11151 lines
506 KiB
JavaScript
11151 lines
506 KiB
JavaScript
module.exports = [
|
|
"[project]/node_modules/lucide-react/dist/esm/icons/clock.mjs [app-ssr] (ecmascript)", ((__turbopack_context__) => {
|
|
"use strict";
|
|
|
|
__turbopack_context__.s([
|
|
"__iconNode",
|
|
()=>__iconNode,
|
|
"default",
|
|
()=>Clock
|
|
]);
|
|
/**
|
|
* @license lucide-react v1.14.0 - ISC
|
|
*
|
|
* This source code is licensed under the ISC license.
|
|
* See the LICENSE file in the root directory of this source tree.
|
|
*/ var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$createLucideIcon$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/lucide-react/dist/esm/createLucideIcon.mjs [app-ssr] (ecmascript)");
|
|
;
|
|
const __iconNode = [
|
|
[
|
|
"circle",
|
|
{
|
|
cx: "12",
|
|
cy: "12",
|
|
r: "10",
|
|
key: "1mglay"
|
|
}
|
|
],
|
|
[
|
|
"path",
|
|
{
|
|
d: "M12 6v6l4 2",
|
|
key: "mmk7yg"
|
|
}
|
|
]
|
|
];
|
|
const Clock = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$createLucideIcon$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__["default"])("clock", __iconNode);
|
|
;
|
|
}),
|
|
"[project]/node_modules/lucide-react/dist/esm/icons/clock.mjs [app-ssr] (ecmascript) <export default as Clock>", ((__turbopack_context__) => {
|
|
"use strict";
|
|
|
|
__turbopack_context__.s([
|
|
"Clock",
|
|
()=>__TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$icons$2f$clock$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__["default"]
|
|
]);
|
|
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$icons$2f$clock$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/lucide-react/dist/esm/icons/clock.mjs [app-ssr] (ecmascript)");
|
|
}),
|
|
"[project]/node_modules/lucide-react/dist/esm/icons/activity.mjs [app-ssr] (ecmascript)", ((__turbopack_context__) => {
|
|
"use strict";
|
|
|
|
__turbopack_context__.s([
|
|
"__iconNode",
|
|
()=>__iconNode,
|
|
"default",
|
|
()=>Activity
|
|
]);
|
|
/**
|
|
* @license lucide-react v1.14.0 - ISC
|
|
*
|
|
* This source code is licensed under the ISC license.
|
|
* See the LICENSE file in the root directory of this source tree.
|
|
*/ var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$createLucideIcon$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/lucide-react/dist/esm/createLucideIcon.mjs [app-ssr] (ecmascript)");
|
|
;
|
|
const __iconNode = [
|
|
[
|
|
"path",
|
|
{
|
|
d: "M22 12h-2.48a2 2 0 0 0-1.93 1.46l-2.35 8.36a.25.25 0 0 1-.48 0L9.24 2.18a.25.25 0 0 0-.48 0l-2.35 8.36A2 2 0 0 1 4.49 12H2",
|
|
key: "169zse"
|
|
}
|
|
]
|
|
];
|
|
const Activity = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$createLucideIcon$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__["default"])("activity", __iconNode);
|
|
;
|
|
}),
|
|
"[project]/node_modules/lucide-react/dist/esm/icons/activity.mjs [app-ssr] (ecmascript) <export default as Activity>", ((__turbopack_context__) => {
|
|
"use strict";
|
|
|
|
__turbopack_context__.s([
|
|
"Activity",
|
|
()=>__TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$icons$2f$activity$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__["default"]
|
|
]);
|
|
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$icons$2f$activity$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/lucide-react/dist/esm/icons/activity.mjs [app-ssr] (ecmascript)");
|
|
}),
|
|
"[project]/node_modules/lucide-react/dist/esm/icons/arrow-right.mjs [app-ssr] (ecmascript)", ((__turbopack_context__) => {
|
|
"use strict";
|
|
|
|
__turbopack_context__.s([
|
|
"__iconNode",
|
|
()=>__iconNode,
|
|
"default",
|
|
()=>ArrowRight
|
|
]);
|
|
/**
|
|
* @license lucide-react v1.14.0 - ISC
|
|
*
|
|
* This source code is licensed under the ISC license.
|
|
* See the LICENSE file in the root directory of this source tree.
|
|
*/ var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$createLucideIcon$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/lucide-react/dist/esm/createLucideIcon.mjs [app-ssr] (ecmascript)");
|
|
;
|
|
const __iconNode = [
|
|
[
|
|
"path",
|
|
{
|
|
d: "M5 12h14",
|
|
key: "1ays0h"
|
|
}
|
|
],
|
|
[
|
|
"path",
|
|
{
|
|
d: "m12 5 7 7-7 7",
|
|
key: "xquz4c"
|
|
}
|
|
]
|
|
];
|
|
const ArrowRight = (0, __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$createLucideIcon$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__["default"])("arrow-right", __iconNode);
|
|
;
|
|
}),
|
|
"[project]/node_modules/lucide-react/dist/esm/icons/arrow-right.mjs [app-ssr] (ecmascript) <export default as ArrowRight>", ((__turbopack_context__) => {
|
|
"use strict";
|
|
|
|
__turbopack_context__.s([
|
|
"ArrowRight",
|
|
()=>__TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$icons$2f$arrow$2d$right$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__["default"]
|
|
]);
|
|
var __TURBOPACK__imported__module__$5b$project$5d2f$node_modules$2f$lucide$2d$react$2f$dist$2f$esm$2f$icons$2f$arrow$2d$right$2e$mjs__$5b$app$2d$ssr$5d$__$28$ecmascript$29$__ = __turbopack_context__.i("[project]/node_modules/lucide-react/dist/esm/icons/arrow-right.mjs [app-ssr] (ecmascript)");
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/querystring.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
assign: null,
|
|
searchParamsToUrlQuery: null,
|
|
urlQueryToSearchParams: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
assign: function() {
|
|
return assign;
|
|
},
|
|
searchParamsToUrlQuery: function() {
|
|
return searchParamsToUrlQuery;
|
|
},
|
|
urlQueryToSearchParams: function() {
|
|
return urlQueryToSearchParams;
|
|
}
|
|
});
|
|
function searchParamsToUrlQuery(searchParams) {
|
|
const query = {};
|
|
for (const [key, value] of searchParams.entries()){
|
|
const existing = query[key];
|
|
if (typeof existing === 'undefined') {
|
|
query[key] = value;
|
|
} else if (Array.isArray(existing)) {
|
|
existing.push(value);
|
|
} else {
|
|
query[key] = [
|
|
existing,
|
|
value
|
|
];
|
|
}
|
|
}
|
|
return query;
|
|
}
|
|
function stringifyUrlQueryParam(param) {
|
|
if (typeof param === 'string') {
|
|
return param;
|
|
}
|
|
if (typeof param === 'number' && !isNaN(param) || typeof param === 'boolean') {
|
|
return String(param);
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
function urlQueryToSearchParams(query) {
|
|
const searchParams = new URLSearchParams();
|
|
for (const [key, value] of Object.entries(query)){
|
|
if (Array.isArray(value)) {
|
|
for (const item of value){
|
|
searchParams.append(key, stringifyUrlQueryParam(item));
|
|
}
|
|
} else {
|
|
searchParams.set(key, stringifyUrlQueryParam(value));
|
|
}
|
|
}
|
|
return searchParams;
|
|
}
|
|
function assign(target, ...searchParamsList) {
|
|
for (const searchParams of searchParamsList){
|
|
for (const key of searchParams.keys()){
|
|
target.delete(key);
|
|
}
|
|
for (const [key, value] of searchParams.entries()){
|
|
target.append(key, value);
|
|
}
|
|
}
|
|
return target;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/format-url.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
// Format function modified from nodejs
|
|
// Copyright Joyent, Inc. and other Node contributors.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to permit
|
|
// persons to whom the Software is furnished to do so, subject to the
|
|
// following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included
|
|
// in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
|
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
|
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
|
// USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
formatUrl: null,
|
|
formatWithValidation: null,
|
|
urlObjectKeys: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
formatUrl: function() {
|
|
return formatUrl;
|
|
},
|
|
formatWithValidation: function() {
|
|
return formatWithValidation;
|
|
},
|
|
urlObjectKeys: function() {
|
|
return urlObjectKeys;
|
|
}
|
|
});
|
|
const _interop_require_wildcard = __turbopack_context__.r("[project]/node_modules/@swc/helpers/cjs/_interop_require_wildcard.cjs [app-ssr] (ecmascript)");
|
|
const _querystring = /*#__PURE__*/ _interop_require_wildcard._(__turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/querystring.js [app-ssr] (ecmascript)"));
|
|
const slashedProtocols = /https?|ftp|gopher|file/;
|
|
function formatUrl(urlObj) {
|
|
let { auth, hostname } = urlObj;
|
|
let protocol = urlObj.protocol || '';
|
|
let pathname = urlObj.pathname || '';
|
|
let hash = urlObj.hash || '';
|
|
let query = urlObj.query || '';
|
|
let host = false;
|
|
auth = auth ? encodeURIComponent(auth).replace(/%3A/i, ':') + '@' : '';
|
|
if (urlObj.host) {
|
|
host = auth + urlObj.host;
|
|
} else if (hostname) {
|
|
host = auth + (~hostname.indexOf(':') ? `[${hostname}]` : hostname);
|
|
if (urlObj.port) {
|
|
host += ':' + urlObj.port;
|
|
}
|
|
}
|
|
if (query && typeof query === 'object') {
|
|
query = String(_querystring.urlQueryToSearchParams(query));
|
|
}
|
|
let search = urlObj.search || query && `?${query}` || '';
|
|
if (protocol && !protocol.endsWith(':')) protocol += ':';
|
|
if (urlObj.slashes || (!protocol || slashedProtocols.test(protocol)) && host !== false) {
|
|
host = '//' + (host || '');
|
|
if (pathname && pathname[0] !== '/') pathname = '/' + pathname;
|
|
} else if (!host) {
|
|
host = '';
|
|
}
|
|
if (hash && hash[0] !== '#') hash = '#' + hash;
|
|
if (search && search[0] !== '?') search = '?' + search;
|
|
pathname = pathname.replace(/[?#]/g, encodeURIComponent);
|
|
search = search.replace('#', '%23');
|
|
return `${protocol}${host}${pathname}${search}${hash}`;
|
|
}
|
|
const urlObjectKeys = [
|
|
'auth',
|
|
'hash',
|
|
'host',
|
|
'hostname',
|
|
'href',
|
|
'path',
|
|
'pathname',
|
|
'port',
|
|
'protocol',
|
|
'query',
|
|
'search',
|
|
'slashes'
|
|
];
|
|
function formatWithValidation(url) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (url !== null && typeof url === 'object') {
|
|
Object.keys(url).forEach((key)=>{
|
|
if (!urlObjectKeys.includes(key)) {
|
|
console.warn(`Unknown key passed via urlObject into url.format: ${key}`);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return formatUrl(url);
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/use-merged-ref.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "useMergedRef", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return useMergedRef;
|
|
}
|
|
});
|
|
const _react = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react.js [app-ssr] (ecmascript)");
|
|
function useMergedRef(refA, refB) {
|
|
const cleanupA = (0, _react.useRef)(null);
|
|
const cleanupB = (0, _react.useRef)(null);
|
|
// NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
|
|
// (this happens often if the user doesn't pass a ref to Link/Form/Image)
|
|
// But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
|
|
// and the user might pass that ref into ref-merging library that doesn't support cleanup refs
|
|
// (because it hasn't been updated for React 19)
|
|
// which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
|
|
// So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
|
|
return (0, _react.useCallback)((current)=>{
|
|
if (current === null) {
|
|
const cleanupFnA = cleanupA.current;
|
|
if (cleanupFnA) {
|
|
cleanupA.current = null;
|
|
cleanupFnA();
|
|
}
|
|
const cleanupFnB = cleanupB.current;
|
|
if (cleanupFnB) {
|
|
cleanupB.current = null;
|
|
cleanupFnB();
|
|
}
|
|
} else {
|
|
if (refA) {
|
|
cleanupA.current = applyRef(refA, current);
|
|
}
|
|
if (refB) {
|
|
cleanupB.current = applyRef(refB, current);
|
|
}
|
|
}
|
|
}, [
|
|
refA,
|
|
refB
|
|
]);
|
|
}
|
|
function applyRef(refA, current) {
|
|
if (typeof refA === 'function') {
|
|
const cleanup = refA(current);
|
|
if (typeof cleanup === 'function') {
|
|
return cleanup;
|
|
} else {
|
|
return ()=>refA(null);
|
|
}
|
|
} else {
|
|
refA.current = current;
|
|
return ()=>{
|
|
refA.current = null;
|
|
};
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/utils.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
DecodeError: null,
|
|
MiddlewareNotFoundError: null,
|
|
MissingStaticPage: null,
|
|
NormalizeError: null,
|
|
PageNotFoundError: null,
|
|
SP: null,
|
|
ST: null,
|
|
WEB_VITALS: null,
|
|
execOnce: null,
|
|
getDisplayName: null,
|
|
getLocationOrigin: null,
|
|
getURL: null,
|
|
isAbsoluteUrl: null,
|
|
isResSent: null,
|
|
loadGetInitialProps: null,
|
|
normalizeRepeatedSlashes: null,
|
|
stringifyError: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
DecodeError: function() {
|
|
return DecodeError;
|
|
},
|
|
MiddlewareNotFoundError: function() {
|
|
return MiddlewareNotFoundError;
|
|
},
|
|
MissingStaticPage: function() {
|
|
return MissingStaticPage;
|
|
},
|
|
NormalizeError: function() {
|
|
return NormalizeError;
|
|
},
|
|
PageNotFoundError: function() {
|
|
return PageNotFoundError;
|
|
},
|
|
SP: function() {
|
|
return SP;
|
|
},
|
|
ST: function() {
|
|
return ST;
|
|
},
|
|
WEB_VITALS: function() {
|
|
return WEB_VITALS;
|
|
},
|
|
execOnce: function() {
|
|
return execOnce;
|
|
},
|
|
getDisplayName: function() {
|
|
return getDisplayName;
|
|
},
|
|
getLocationOrigin: function() {
|
|
return getLocationOrigin;
|
|
},
|
|
getURL: function() {
|
|
return getURL;
|
|
},
|
|
isAbsoluteUrl: function() {
|
|
return isAbsoluteUrl;
|
|
},
|
|
isResSent: function() {
|
|
return isResSent;
|
|
},
|
|
loadGetInitialProps: function() {
|
|
return loadGetInitialProps;
|
|
},
|
|
normalizeRepeatedSlashes: function() {
|
|
return normalizeRepeatedSlashes;
|
|
},
|
|
stringifyError: function() {
|
|
return stringifyError;
|
|
}
|
|
});
|
|
const WEB_VITALS = [
|
|
'CLS',
|
|
'FCP',
|
|
'FID',
|
|
'INP',
|
|
'LCP',
|
|
'TTFB'
|
|
];
|
|
function execOnce(fn) {
|
|
let used = false;
|
|
let result;
|
|
return (...args)=>{
|
|
if (!used) {
|
|
used = true;
|
|
result = fn(...args);
|
|
}
|
|
return result;
|
|
};
|
|
}
|
|
// Scheme: https://tools.ietf.org/html/rfc3986#section-3.1
|
|
// Absolute URL: https://tools.ietf.org/html/rfc3986#section-4.3
|
|
const ABSOLUTE_URL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*?:/;
|
|
const isAbsoluteUrl = (url)=>ABSOLUTE_URL_REGEX.test(url);
|
|
function getLocationOrigin() {
|
|
const { protocol, hostname, port } = window.location;
|
|
return `${protocol}//${hostname}${port ? ':' + port : ''}`;
|
|
}
|
|
function getURL() {
|
|
const { href } = window.location;
|
|
const origin = getLocationOrigin();
|
|
return href.substring(origin.length);
|
|
}
|
|
function getDisplayName(Component) {
|
|
return typeof Component === 'string' ? Component : Component.displayName || Component.name || 'Unknown';
|
|
}
|
|
function isResSent(res) {
|
|
return res.finished || res.headersSent;
|
|
}
|
|
function normalizeRepeatedSlashes(url) {
|
|
const urlParts = url.split('?');
|
|
const urlNoQuery = urlParts[0];
|
|
return urlNoQuery // first we replace any non-encoded backslashes with forward
|
|
// then normalize repeated forward slashes
|
|
.replace(/\\/g, '/').replace(/\/\/+/g, '/') + (urlParts[1] ? `?${urlParts.slice(1).join('?')}` : '');
|
|
}
|
|
async function loadGetInitialProps(App, ctx) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (App.prototype?.getInitialProps) {
|
|
const message = `"${getDisplayName(App)}.getInitialProps()" is defined as an instance method - visit https://nextjs.org/docs/messages/get-initial-props-as-an-instance-method for more information.`;
|
|
throw Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
|
|
value: "E1035",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
// when called from _app `ctx` is nested in `ctx`
|
|
const res = ctx.res || ctx.ctx && ctx.ctx.res;
|
|
if (!App.getInitialProps) {
|
|
if (ctx.ctx && ctx.Component) {
|
|
// @ts-ignore pageProps default
|
|
return {
|
|
pageProps: await loadGetInitialProps(ctx.Component, ctx.ctx)
|
|
};
|
|
}
|
|
return {};
|
|
}
|
|
const props = await App.getInitialProps(ctx);
|
|
if (res && isResSent(res)) {
|
|
return props;
|
|
}
|
|
if (!props) {
|
|
const message = `"${getDisplayName(App)}.getInitialProps()" should resolve to an object. But found "${props}" instead.`;
|
|
throw Object.defineProperty(new Error(message), "__NEXT_ERROR_CODE", {
|
|
value: "E1025",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (Object.keys(props).length === 0 && !ctx.ctx) {
|
|
console.warn(`${getDisplayName(App)} returned an empty object from \`getInitialProps\`. This de-optimizes and prevents automatic static optimization. https://nextjs.org/docs/messages/empty-object-getInitialProps`);
|
|
}
|
|
}
|
|
return props;
|
|
}
|
|
const SP = typeof performance !== 'undefined';
|
|
const ST = SP && [
|
|
'mark',
|
|
'measure',
|
|
'getEntriesByName'
|
|
].every((method)=>typeof performance[method] === 'function');
|
|
class DecodeError extends Error {
|
|
}
|
|
class NormalizeError extends Error {
|
|
}
|
|
class PageNotFoundError extends Error {
|
|
constructor(page){
|
|
super();
|
|
this.code = 'ENOENT';
|
|
this.name = 'PageNotFoundError';
|
|
this.message = `Cannot find module for page: ${page}`;
|
|
}
|
|
}
|
|
class MissingStaticPage extends Error {
|
|
constructor(page, message){
|
|
super();
|
|
this.message = `Failed to load static file for page: ${page} ${message}`;
|
|
}
|
|
}
|
|
class MiddlewareNotFoundError extends Error {
|
|
constructor(){
|
|
super();
|
|
this.code = 'ENOENT';
|
|
this.message = `Cannot find the middleware module`;
|
|
}
|
|
}
|
|
function stringifyError(error) {
|
|
return JSON.stringify({
|
|
message: error.message,
|
|
stack: error.stack
|
|
});
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/parse-path.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Given a path this function will find the pathname, query and hash and return
|
|
* them. This is useful to parse full paths on the client side.
|
|
* @param path A path to parse e.g. /foo/bar?id=1#hash
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "parsePath", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return parsePath;
|
|
}
|
|
});
|
|
function parsePath(path) {
|
|
const hashIndex = path.indexOf('#');
|
|
const queryIndex = path.indexOf('?');
|
|
const hasQuery = queryIndex > -1 && (hashIndex < 0 || queryIndex < hashIndex);
|
|
if (hasQuery || hashIndex > -1) {
|
|
return {
|
|
pathname: path.substring(0, hasQuery ? queryIndex : hashIndex),
|
|
query: hasQuery ? path.substring(queryIndex, hashIndex > -1 ? hashIndex : undefined) : '',
|
|
hash: hashIndex > -1 ? path.slice(hashIndex) : ''
|
|
};
|
|
}
|
|
return {
|
|
pathname: path,
|
|
query: '',
|
|
hash: ''
|
|
};
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/add-path-prefix.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "addPathPrefix", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return addPathPrefix;
|
|
}
|
|
});
|
|
const _parsepath = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/parse-path.js [app-ssr] (ecmascript)");
|
|
function addPathPrefix(path, prefix) {
|
|
if (!path.startsWith('/') || !prefix) {
|
|
return path;
|
|
}
|
|
const { pathname, query, hash } = (0, _parsepath.parsePath)(path);
|
|
return `${prefix}${pathname}${query}${hash}`;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/remove-trailing-slash.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Removes the trailing slash for a given route or page path. Preserves the
|
|
* root page. Examples:
|
|
* - `/foo/bar/` -> `/foo/bar`
|
|
* - `/foo/bar` -> `/foo/bar`
|
|
* - `/` -> `/`
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "removeTrailingSlash", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return removeTrailingSlash;
|
|
}
|
|
});
|
|
function removeTrailingSlash(route) {
|
|
return route.replace(/\/$/, '') || '/';
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/normalize-trailing-slash.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "normalizePathTrailingSlash", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return normalizePathTrailingSlash;
|
|
}
|
|
});
|
|
const _removetrailingslash = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/remove-trailing-slash.js [app-ssr] (ecmascript)");
|
|
const _parsepath = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/parse-path.js [app-ssr] (ecmascript)");
|
|
const normalizePathTrailingSlash = (path)=>{
|
|
if (!path.startsWith('/') || ("TURBOPACK compile-time value", void 0)) {
|
|
return path;
|
|
}
|
|
const { pathname, query, hash } = (0, _parsepath.parsePath)(path);
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
return `${(0, _removetrailingslash.removeTrailingSlash)(pathname)}${query}${hash}`;
|
|
};
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/add-base-path.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "addBasePath", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return addBasePath;
|
|
}
|
|
});
|
|
const _addpathprefix = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/add-path-prefix.js [app-ssr] (ecmascript)");
|
|
const _normalizetrailingslash = __turbopack_context__.r("[project]/node_modules/next/dist/client/normalize-trailing-slash.js [app-ssr] (ecmascript)");
|
|
const basePath = ("TURBOPACK compile-time value", "") || '';
|
|
function addBasePath(path, required) {
|
|
return (0, _normalizetrailingslash.normalizePathTrailingSlash)(("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : (0, _addpathprefix.addPathPrefix)(path, basePath));
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/utils/warn-once.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "warnOnce", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return warnOnce;
|
|
}
|
|
});
|
|
let warnOnce = (_)=>{};
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
const warnings = new Set();
|
|
warnOnce = (msg)=>{
|
|
if (!warnings.has(msg)) {
|
|
console.warn(msg);
|
|
}
|
|
warnings.add(msg);
|
|
};
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/router-reducer-types.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
ACTION_HMR_REFRESH: null,
|
|
ACTION_NAVIGATE: null,
|
|
ACTION_REFRESH: null,
|
|
ACTION_RESTORE: null,
|
|
ACTION_SERVER_ACTION: null,
|
|
ACTION_SERVER_PATCH: null,
|
|
PrefetchKind: null,
|
|
ScrollBehavior: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
ACTION_HMR_REFRESH: function() {
|
|
return ACTION_HMR_REFRESH;
|
|
},
|
|
ACTION_NAVIGATE: function() {
|
|
return ACTION_NAVIGATE;
|
|
},
|
|
ACTION_REFRESH: function() {
|
|
return ACTION_REFRESH;
|
|
},
|
|
ACTION_RESTORE: function() {
|
|
return ACTION_RESTORE;
|
|
},
|
|
ACTION_SERVER_ACTION: function() {
|
|
return ACTION_SERVER_ACTION;
|
|
},
|
|
ACTION_SERVER_PATCH: function() {
|
|
return ACTION_SERVER_PATCH;
|
|
},
|
|
PrefetchKind: function() {
|
|
return PrefetchKind;
|
|
},
|
|
ScrollBehavior: function() {
|
|
return ScrollBehavior;
|
|
}
|
|
});
|
|
const ACTION_REFRESH = 'refresh';
|
|
const ACTION_NAVIGATE = 'navigate';
|
|
const ACTION_RESTORE = 'restore';
|
|
const ACTION_SERVER_PATCH = 'server-patch';
|
|
const ACTION_HMR_REFRESH = 'hmr-refresh';
|
|
const ACTION_SERVER_ACTION = 'server-action';
|
|
var PrefetchKind = /*#__PURE__*/ function(PrefetchKind) {
|
|
PrefetchKind["AUTO"] = "auto";
|
|
PrefetchKind["FULL"] = "full";
|
|
return PrefetchKind;
|
|
}({});
|
|
var ScrollBehavior = /*#__PURE__*/ function(ScrollBehavior) {
|
|
/** Use per-node ScrollRef to decide whether to scroll. */ ScrollBehavior[ScrollBehavior["Default"] = 0] = "Default";
|
|
/** Suppress scroll entirely (e.g. scroll={false} on Link or router.push). */ ScrollBehavior[ScrollBehavior["NoScroll"] = 1] = "NoScroll";
|
|
return ScrollBehavior;
|
|
}({});
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Shared types and constants for the Segment Cache.
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
FetchStrategy: null,
|
|
NavigationResultTag: null,
|
|
PrefetchPriority: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
FetchStrategy: function() {
|
|
return FetchStrategy;
|
|
},
|
|
NavigationResultTag: function() {
|
|
return NavigationResultTag;
|
|
},
|
|
PrefetchPriority: function() {
|
|
return PrefetchPriority;
|
|
}
|
|
});
|
|
var NavigationResultTag = /*#__PURE__*/ function(NavigationResultTag) {
|
|
NavigationResultTag[NavigationResultTag["MPA"] = 0] = "MPA";
|
|
NavigationResultTag[NavigationResultTag["Success"] = 1] = "Success";
|
|
NavigationResultTag[NavigationResultTag["NoOp"] = 2] = "NoOp";
|
|
NavigationResultTag[NavigationResultTag["Async"] = 3] = "Async";
|
|
return NavigationResultTag;
|
|
}({});
|
|
var PrefetchPriority = /*#__PURE__*/ function(PrefetchPriority) {
|
|
/**
|
|
* Assigned to the most recently hovered/touched link. Special network
|
|
* bandwidth is reserved for this task only. There's only ever one Intent-
|
|
* priority task at a time; when a new Intent task is scheduled, the previous
|
|
* one is bumped down to Default.
|
|
*/ PrefetchPriority[PrefetchPriority["Intent"] = 2] = "Intent";
|
|
/**
|
|
* The default priority for prefetch tasks.
|
|
*/ PrefetchPriority[PrefetchPriority["Default"] = 1] = "Default";
|
|
/**
|
|
* Assigned to tasks when they spawn non-blocking background work, like
|
|
* revalidating a partially cached entry to see if more data is available.
|
|
*/ PrefetchPriority[PrefetchPriority["Background"] = 0] = "Background";
|
|
return PrefetchPriority;
|
|
}({});
|
|
var FetchStrategy = /*#__PURE__*/ function(FetchStrategy) {
|
|
// Deliberately ordered so we can easily compare two segments
|
|
// and determine if one segment is "more specific" than another
|
|
// (i.e. if it's likely that it contains more data)
|
|
FetchStrategy[FetchStrategy["LoadingBoundary"] = 0] = "LoadingBoundary";
|
|
FetchStrategy[FetchStrategy["PPR"] = 1] = "PPR";
|
|
FetchStrategy[FetchStrategy["PPRRuntime"] = 2] = "PPRRuntime";
|
|
FetchStrategy[FetchStrategy["Full"] = 3] = "Full";
|
|
return FetchStrategy;
|
|
}({});
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/cache-key.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
// TypeScript trick to simulate opaque types, like in Flow.
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "createCacheKey", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return createCacheKey;
|
|
}
|
|
});
|
|
function createCacheKey(originalHref, nextUrl) {
|
|
const originalUrl = new URL(originalHref);
|
|
const cacheKey = {
|
|
pathname: originalUrl.pathname,
|
|
search: originalUrl.search,
|
|
nextUrl: nextUrl
|
|
};
|
|
return cacheKey;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/app-router-types.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* App Router types - Client-safe types for the Next.js App Router
|
|
*
|
|
* This file contains type definitions that can be safely imported
|
|
* by both client-side and server-side code without circular dependencies.
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "PrefetchHint", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return PrefetchHint;
|
|
}
|
|
});
|
|
var PrefetchHint = /*#__PURE__*/ function(PrefetchHint) {
|
|
// This segment has a runtime prefetch enabled (via unstable_instant with
|
|
// prefetch: 'runtime'). Per-segment only, does not propagate to ancestors.
|
|
PrefetchHint[PrefetchHint["HasRuntimePrefetch"] = 1] = "HasRuntimePrefetch";
|
|
// This segment or one of its descendants has an instant config defined
|
|
// (any truthy unstable_instant, regardless of prefetch mode). Propagates
|
|
// upward so the root segment reflects the entire subtree.
|
|
PrefetchHint[PrefetchHint["SubtreeHasInstant"] = 2] = "SubtreeHasInstant";
|
|
// This segment itself has a loading.tsx boundary.
|
|
PrefetchHint[PrefetchHint["SegmentHasLoadingBoundary"] = 4] = "SegmentHasLoadingBoundary";
|
|
// A descendant segment (but not this one) has a loading.tsx boundary.
|
|
// Propagates upward so the root reflects the entire subtree.
|
|
PrefetchHint[PrefetchHint["SubtreeHasLoadingBoundary"] = 8] = "SubtreeHasLoadingBoundary";
|
|
// This segment is the root layout of the application.
|
|
PrefetchHint[PrefetchHint["IsRootLayout"] = 16] = "IsRootLayout";
|
|
// This segment's response includes its parent's data inlined into it.
|
|
// Set at build time by the segment size measurement pass.
|
|
PrefetchHint[PrefetchHint["ParentInlinedIntoSelf"] = 32] = "ParentInlinedIntoSelf";
|
|
// This segment's data is inlined into one of its children — don't fetch
|
|
// it separately. Set at build time by the segment size measurement pass.
|
|
PrefetchHint[PrefetchHint["InlinedIntoChild"] = 64] = "InlinedIntoChild";
|
|
// On a __PAGE__: this page's response includes the head (metadata/viewport)
|
|
// at the end of its SegmentPrefetch[] array.
|
|
PrefetchHint[PrefetchHint["HeadInlinedIntoSelf"] = 128] = "HeadInlinedIntoSelf";
|
|
// On the root hint node: the head was NOT inlined into any page — fetch
|
|
// it separately. Absence of this bit means the head is bundled into a page.
|
|
PrefetchHint[PrefetchHint["HeadOutlined"] = 256] = "HeadOutlined";
|
|
return PrefetchHint;
|
|
}({});
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/match-segments.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "matchSegment", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return matchSegment;
|
|
}
|
|
});
|
|
const matchSegment = (existingSegment, segment)=>{
|
|
// segment is either Array or string
|
|
if (typeof existingSegment === 'string') {
|
|
if (typeof segment === 'string') {
|
|
// Common case: segment is just a string
|
|
return existingSegment === segment;
|
|
}
|
|
return false;
|
|
}
|
|
if (typeof segment === 'string') {
|
|
return false;
|
|
}
|
|
return existingSegment[0] === segment[0] && existingSegment[1] === segment[1];
|
|
};
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Vary Params Decoding
|
|
*
|
|
* This module is shared between server and client.
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "readVaryParams", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return readVaryParams;
|
|
}
|
|
});
|
|
function readVaryParams(thenable) {
|
|
// Attach a no-op listener to force Flight to synchronously resolve the
|
|
// thenable. When a thenable arrives from the Flight stream, it may be in an
|
|
// intermediate 'resolved_model' state (data received but not unwrapped).
|
|
// Calling .then() triggers Flight to transition it to 'fulfilled', making
|
|
// the value available synchronously. React uses this same optimization
|
|
// internally to avoid unnecessary microtasks.
|
|
thenable.then(noop);
|
|
// If the thenable is still not 'fulfilled' after calling .then(), the server
|
|
// failed to resolve it before the stream ended. Treat as unknown.
|
|
if (thenable.status !== 'fulfilled') {
|
|
return null;
|
|
}
|
|
return thenable.value;
|
|
}
|
|
const noop = ()=>{};
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
ACTION_HEADER: null,
|
|
FLIGHT_HEADERS: null,
|
|
NEXT_ACTION_NOT_FOUND_HEADER: null,
|
|
NEXT_ACTION_REVALIDATED_HEADER: null,
|
|
NEXT_DID_POSTPONE_HEADER: null,
|
|
NEXT_HMR_REFRESH_HASH_COOKIE: null,
|
|
NEXT_HMR_REFRESH_HEADER: null,
|
|
NEXT_HTML_REQUEST_ID_HEADER: null,
|
|
NEXT_INSTANT_PREFETCH_HEADER: null,
|
|
NEXT_INSTANT_TEST_COOKIE: null,
|
|
NEXT_IS_PRERENDER_HEADER: null,
|
|
NEXT_REQUEST_ID_HEADER: null,
|
|
NEXT_REWRITTEN_PATH_HEADER: null,
|
|
NEXT_REWRITTEN_QUERY_HEADER: null,
|
|
NEXT_ROUTER_PREFETCH_HEADER: null,
|
|
NEXT_ROUTER_SEGMENT_PREFETCH_HEADER: null,
|
|
NEXT_ROUTER_STALE_TIME_HEADER: null,
|
|
NEXT_ROUTER_STATE_TREE_HEADER: null,
|
|
NEXT_RSC_UNION_QUERY: null,
|
|
NEXT_URL: null,
|
|
RSC_CONTENT_TYPE_HEADER: null,
|
|
RSC_HEADER: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
ACTION_HEADER: function() {
|
|
return ACTION_HEADER;
|
|
},
|
|
FLIGHT_HEADERS: function() {
|
|
return FLIGHT_HEADERS;
|
|
},
|
|
NEXT_ACTION_NOT_FOUND_HEADER: function() {
|
|
return NEXT_ACTION_NOT_FOUND_HEADER;
|
|
},
|
|
NEXT_ACTION_REVALIDATED_HEADER: function() {
|
|
return NEXT_ACTION_REVALIDATED_HEADER;
|
|
},
|
|
NEXT_DID_POSTPONE_HEADER: function() {
|
|
return NEXT_DID_POSTPONE_HEADER;
|
|
},
|
|
NEXT_HMR_REFRESH_HASH_COOKIE: function() {
|
|
return NEXT_HMR_REFRESH_HASH_COOKIE;
|
|
},
|
|
NEXT_HMR_REFRESH_HEADER: function() {
|
|
return NEXT_HMR_REFRESH_HEADER;
|
|
},
|
|
NEXT_HTML_REQUEST_ID_HEADER: function() {
|
|
return NEXT_HTML_REQUEST_ID_HEADER;
|
|
},
|
|
NEXT_INSTANT_PREFETCH_HEADER: function() {
|
|
return NEXT_INSTANT_PREFETCH_HEADER;
|
|
},
|
|
NEXT_INSTANT_TEST_COOKIE: function() {
|
|
return NEXT_INSTANT_TEST_COOKIE;
|
|
},
|
|
NEXT_IS_PRERENDER_HEADER: function() {
|
|
return NEXT_IS_PRERENDER_HEADER;
|
|
},
|
|
NEXT_REQUEST_ID_HEADER: function() {
|
|
return NEXT_REQUEST_ID_HEADER;
|
|
},
|
|
NEXT_REWRITTEN_PATH_HEADER: function() {
|
|
return NEXT_REWRITTEN_PATH_HEADER;
|
|
},
|
|
NEXT_REWRITTEN_QUERY_HEADER: function() {
|
|
return NEXT_REWRITTEN_QUERY_HEADER;
|
|
},
|
|
NEXT_ROUTER_PREFETCH_HEADER: function() {
|
|
return NEXT_ROUTER_PREFETCH_HEADER;
|
|
},
|
|
NEXT_ROUTER_SEGMENT_PREFETCH_HEADER: function() {
|
|
return NEXT_ROUTER_SEGMENT_PREFETCH_HEADER;
|
|
},
|
|
NEXT_ROUTER_STALE_TIME_HEADER: function() {
|
|
return NEXT_ROUTER_STALE_TIME_HEADER;
|
|
},
|
|
NEXT_ROUTER_STATE_TREE_HEADER: function() {
|
|
return NEXT_ROUTER_STATE_TREE_HEADER;
|
|
},
|
|
NEXT_RSC_UNION_QUERY: function() {
|
|
return NEXT_RSC_UNION_QUERY;
|
|
},
|
|
NEXT_URL: function() {
|
|
return NEXT_URL;
|
|
},
|
|
RSC_CONTENT_TYPE_HEADER: function() {
|
|
return RSC_CONTENT_TYPE_HEADER;
|
|
},
|
|
RSC_HEADER: function() {
|
|
return RSC_HEADER;
|
|
}
|
|
});
|
|
const RSC_HEADER = 'rsc';
|
|
const ACTION_HEADER = 'next-action';
|
|
const NEXT_ROUTER_STATE_TREE_HEADER = 'next-router-state-tree';
|
|
const NEXT_ROUTER_PREFETCH_HEADER = 'next-router-prefetch';
|
|
const NEXT_ROUTER_SEGMENT_PREFETCH_HEADER = 'next-router-segment-prefetch';
|
|
const NEXT_HMR_REFRESH_HEADER = 'next-hmr-refresh';
|
|
const NEXT_HMR_REFRESH_HASH_COOKIE = '__next_hmr_refresh_hash__';
|
|
const NEXT_URL = 'next-url';
|
|
const RSC_CONTENT_TYPE_HEADER = 'text/x-component';
|
|
const NEXT_INSTANT_PREFETCH_HEADER = 'next-instant-navigation-testing-prefetch';
|
|
const NEXT_INSTANT_TEST_COOKIE = 'next-instant-navigation-testing';
|
|
const FLIGHT_HEADERS = [
|
|
RSC_HEADER,
|
|
NEXT_ROUTER_STATE_TREE_HEADER,
|
|
NEXT_ROUTER_PREFETCH_HEADER,
|
|
NEXT_HMR_REFRESH_HEADER,
|
|
NEXT_ROUTER_SEGMENT_PREFETCH_HEADER
|
|
];
|
|
const NEXT_RSC_UNION_QUERY = '_rsc';
|
|
const NEXT_ROUTER_STALE_TIME_HEADER = 'x-nextjs-stale-time';
|
|
const NEXT_DID_POSTPONE_HEADER = 'x-nextjs-postponed';
|
|
const NEXT_REWRITTEN_PATH_HEADER = 'x-nextjs-rewritten-path';
|
|
const NEXT_REWRITTEN_QUERY_HEADER = 'x-nextjs-rewritten-query';
|
|
const NEXT_IS_PRERENDER_HEADER = 'x-nextjs-prerender';
|
|
const NEXT_ACTION_NOT_FOUND_HEADER = 'x-nextjs-action-not-found';
|
|
const NEXT_REQUEST_ID_HEADER = 'x-nextjs-request-id';
|
|
const NEXT_HTML_REQUEST_ID_HEADER = 'x-nextjs-html-request-id';
|
|
const NEXT_ACTION_REVALIDATED_HEADER = 'x-action-revalidated';
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/is-thenable.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Check to see if a value is Thenable.
|
|
*
|
|
* @param promise the maybe-thenable value
|
|
* @returns true if the value is thenable
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "isThenable", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return isThenable;
|
|
}
|
|
});
|
|
function isThenable(promise) {
|
|
return promise !== null && typeof promise === 'object' && 'then' in promise && typeof promise.then === 'function';
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/next-devtools/userspace/use-app-dev-rendering-indicator.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "useAppDevRenderingIndicator", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return useAppDevRenderingIndicator;
|
|
}
|
|
});
|
|
const _react = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react.js [app-ssr] (ecmascript)");
|
|
const _nextdevtools = __turbopack_context__.r("[project]/node_modules/next/dist/next-devtools/dev-overlay.shim.js [app-ssr] (ecmascript)");
|
|
const useAppDevRenderingIndicator = ()=>{
|
|
const [isPending, startTransition] = (0, _react.useTransition)();
|
|
(0, _react.useEffect)(()=>{
|
|
if (isPending) {
|
|
_nextdevtools.dispatcher.renderingIndicatorShow();
|
|
} else {
|
|
_nextdevtools.dispatcher.renderingIndicatorHide();
|
|
}
|
|
}, [
|
|
isPending
|
|
]);
|
|
return startTransition;
|
|
};
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/use-action-queue.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
dispatchAppRouterAction: null,
|
|
dispatchGestureState: null,
|
|
refreshOnInstantNavigationUnlock: null,
|
|
useActionQueue: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
dispatchAppRouterAction: function() {
|
|
return dispatchAppRouterAction;
|
|
},
|
|
dispatchGestureState: function() {
|
|
return dispatchGestureState;
|
|
},
|
|
refreshOnInstantNavigationUnlock: function() {
|
|
return refreshOnInstantNavigationUnlock;
|
|
},
|
|
useActionQueue: function() {
|
|
return useActionQueue;
|
|
}
|
|
});
|
|
const _interop_require_wildcard = __turbopack_context__.r("[project]/node_modules/@swc/helpers/cjs/_interop_require_wildcard.cjs [app-ssr] (ecmascript)");
|
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(__turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react.js [app-ssr] (ecmascript)"));
|
|
const _isthenable = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/is-thenable.js [app-ssr] (ecmascript)");
|
|
const _routerreducertypes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/router-reducer-types.js [app-ssr] (ecmascript)");
|
|
// The app router state lives outside of React, so we can import the dispatch
|
|
// method directly wherever we need it, rather than passing it around via props
|
|
// or context.
|
|
let dispatch = null;
|
|
function refreshOnInstantNavigationUnlock() {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (dispatch !== null) {
|
|
dispatch({
|
|
type: _routerreducertypes.ACTION_REFRESH,
|
|
bypassCacheInvalidation: true
|
|
});
|
|
} else {
|
|
window.location.reload();
|
|
}
|
|
}
|
|
}
|
|
function dispatchAppRouterAction(action) {
|
|
if (dispatch === null) {
|
|
throw Object.defineProperty(new Error('Internal Next.js error: Router action dispatched before initialization.'), "__NEXT_ERROR_CODE", {
|
|
value: "E668",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
dispatch(action);
|
|
}
|
|
// Optimistic state setter for experimental_gesturePush. Only should be used
|
|
// during a gesture transition.
|
|
let setGestureRouterState = null;
|
|
function dispatchGestureState(state) {
|
|
if (setGestureRouterState === null) {
|
|
throw Object.defineProperty(new Error('Internal Next.js error: Router action dispatched before initialization.'), "__NEXT_ERROR_CODE", {
|
|
value: "E668",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
setGestureRouterState(state);
|
|
}
|
|
const __DEV__ = ("TURBOPACK compile-time value", "development") !== 'production';
|
|
const promisesWithDebugInfo = ("TURBOPACK compile-time truthy", 1) ? new WeakMap() : "TURBOPACK unreachable";
|
|
function useActionQueue(actionQueue) {
|
|
const [canonicalState, setState] = _react.default.useState(actionQueue.state);
|
|
// Wrap the canonical state in useOptimistic to support
|
|
// experimental_gesturePush. During a gesture transition, this returns a fork
|
|
// of the router state that represents the eventual target if/when the gesture
|
|
// completes. Otherwise it returns the canonical state.
|
|
const [state, setGesture] = (0, _react.useOptimistic)(canonicalState);
|
|
if (("TURBOPACK compile-time value", "undefined") !== 'undefined') {
|
|
setGestureRouterState = setGesture;
|
|
}
|
|
// Because of a known issue that requires to decode Flight streams inside the
|
|
// render phase, we have to be a bit clever and assign the dispatch method to
|
|
// a module-level variable upon initialization. The useState hook in this
|
|
// module only exists to synchronize state that lives outside of React.
|
|
// Ideally, what we'd do instead is pass the state as a prop to root.render;
|
|
// this is conceptually how we're modeling the app router state, despite the
|
|
// weird implementation details.
|
|
let nextDispatch;
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
const { useAppDevRenderingIndicator } = __turbopack_context__.r("[project]/node_modules/next/dist/next-devtools/userspace/use-app-dev-rendering-indicator.js [app-ssr] (ecmascript)");
|
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
const appDevRenderingIndicator = useAppDevRenderingIndicator();
|
|
nextDispatch = (action)=>{
|
|
appDevRenderingIndicator(()=>{
|
|
actionQueue.dispatch(action, setState);
|
|
});
|
|
};
|
|
} else //TURBOPACK unreachable
|
|
;
|
|
if (("TURBOPACK compile-time value", "undefined") !== 'undefined') {
|
|
dispatch = nextDispatch;
|
|
}
|
|
// When navigating to a non-prefetched route, then App Router state will be
|
|
// blocked until the server responds. We need to transfer the `_debugInfo`
|
|
// from the underlying Flight response onto the top-level promise that is
|
|
// passed to React (via `use`) so that the latency is accurately represented
|
|
// in the React DevTools.
|
|
const stateWithDebugInfo = (0, _react.useMemo)(()=>{
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
if ((0, _isthenable.isThenable)(state)) {
|
|
// useMemo can't be used to cache a Promise since the memoized value is thrown
|
|
// away when we suspend. So we use a WeakMap to cache the Promise with debug info.
|
|
let promiseWithDebugInfo = promisesWithDebugInfo.get(state);
|
|
if (promiseWithDebugInfo === undefined) {
|
|
const debugInfo = [];
|
|
promiseWithDebugInfo = Promise.resolve(state).then((asyncState)=>{
|
|
if (asyncState.debugInfo !== null) {
|
|
debugInfo.push(...asyncState.debugInfo);
|
|
}
|
|
return asyncState;
|
|
});
|
|
promiseWithDebugInfo._debugInfo = debugInfo;
|
|
promisesWithDebugInfo.set(state, promiseWithDebugInfo);
|
|
}
|
|
return promiseWithDebugInfo;
|
|
}
|
|
return state;
|
|
}, [
|
|
state
|
|
]);
|
|
return (0, _isthenable.isThenable)(stateWithDebugInfo) ? (0, _react.use)(stateWithDebugInfo) : stateWithDebugInfo;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/app-call-server.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "callServer", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return callServer;
|
|
}
|
|
});
|
|
const _react = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react.js [app-ssr] (ecmascript)");
|
|
const _routerreducertypes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/router-reducer-types.js [app-ssr] (ecmascript)");
|
|
const _useactionqueue = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/use-action-queue.js [app-ssr] (ecmascript)");
|
|
async function callServer(actionId, actionArgs) {
|
|
return new Promise((resolve, reject)=>{
|
|
(0, _react.startTransition)(()=>{
|
|
(0, _useactionqueue.dispatchAppRouterAction)({
|
|
type: _routerreducertypes.ACTION_SERVER_ACTION,
|
|
actionId,
|
|
actionArgs,
|
|
resolve,
|
|
reject
|
|
});
|
|
});
|
|
});
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/app-find-source-map-url.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "findSourceMapURL", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return findSourceMapURL;
|
|
}
|
|
});
|
|
const basePath = ("TURBOPACK compile-time value", "") || '';
|
|
const pathname = `${basePath}/__nextjs_source-map`;
|
|
const findSourceMapURL = ("TURBOPACK compile-time truthy", 1) ? function findSourceMapURL(filename) {
|
|
if (filename === '') {
|
|
return null;
|
|
}
|
|
if (filename.startsWith(document.location.origin) && filename.includes('/_next/static')) {
|
|
// This is a request for a client chunk. This can only happen when
|
|
// using Turbopack. In this case, since we control how those source
|
|
// maps are generated, we can safely assume that the sourceMappingURL
|
|
// is relative to the filename, with an added `.map` extension. The
|
|
// browser can just request this file, and it gets served through the
|
|
// normal dev server, without the need to route this through
|
|
// the `/__nextjs_source-map` dev middleware.
|
|
return `${filename}.map`;
|
|
}
|
|
const url = new URL(pathname, document.location.origin);
|
|
url.searchParams.set('filename', filename);
|
|
return url.href;
|
|
} : "TURBOPACK unreachable";
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
HEAD_REQUEST_KEY: null,
|
|
ROOT_SEGMENT_REQUEST_KEY: null,
|
|
appendSegmentRequestKeyPart: null,
|
|
convertSegmentPathToStaticExportFilename: null,
|
|
createSegmentRequestKeyPart: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
HEAD_REQUEST_KEY: function() {
|
|
return HEAD_REQUEST_KEY;
|
|
},
|
|
ROOT_SEGMENT_REQUEST_KEY: function() {
|
|
return ROOT_SEGMENT_REQUEST_KEY;
|
|
},
|
|
appendSegmentRequestKeyPart: function() {
|
|
return appendSegmentRequestKeyPart;
|
|
},
|
|
convertSegmentPathToStaticExportFilename: function() {
|
|
return convertSegmentPathToStaticExportFilename;
|
|
},
|
|
createSegmentRequestKeyPart: function() {
|
|
return createSegmentRequestKeyPart;
|
|
}
|
|
});
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const ROOT_SEGMENT_REQUEST_KEY = '';
|
|
const HEAD_REQUEST_KEY = '/_head';
|
|
function createSegmentRequestKeyPart(segment) {
|
|
if (typeof segment === 'string') {
|
|
if (segment.startsWith(_segment.PAGE_SEGMENT_KEY)) {
|
|
// The Flight Router State type sometimes includes the search params in
|
|
// the page segment. However, the Segment Cache tracks this as a separate
|
|
// key. So, we strip the search params here, and then add them back when
|
|
// the cache entry is turned back into a FlightRouterState. This is an
|
|
// unfortunate consequence of the FlightRouteState being used both as a
|
|
// transport type and as a cache key; we'll address this once more of the
|
|
// Segment Cache implementation has settled.
|
|
// TODO: We should hoist the search params out of the FlightRouterState
|
|
// type entirely, This is our plan for dynamic route params, too.
|
|
return _segment.PAGE_SEGMENT_KEY;
|
|
}
|
|
const safeName = // But params typically don't include the leading slash. We should use
|
|
// a different encoding to avoid this special case.
|
|
segment === '/_not-found' ? '_not-found' : encodeToFilesystemAndURLSafeString(segment);
|
|
// Since this is not a dynamic segment, it's fully encoded. It does not
|
|
// need to be "hydrated" with a param value.
|
|
return safeName;
|
|
}
|
|
const name = segment[0];
|
|
const paramType = segment[2];
|
|
const safeName = encodeToFilesystemAndURLSafeString(name);
|
|
const encodedName = '$' + paramType + '$' + safeName;
|
|
return encodedName;
|
|
}
|
|
function appendSegmentRequestKeyPart(parentRequestKey, parallelRouteKey, childRequestKeyPart) {
|
|
// Aside from being filesystem safe, segment keys are also designed so that
|
|
// each segment and parallel route creates its own subdirectory. Roughly in
|
|
// the same shape as the source app directory. This is mostly just for easier
|
|
// debugging (you can open up the build folder and navigate the output); if
|
|
// we wanted to do we could just use a flat structure.
|
|
// Omit the parallel route key for children, since this is the most
|
|
// common case. Saves some bytes (and it's what the app directory does).
|
|
const slotKey = parallelRouteKey === 'children' ? childRequestKeyPart : `@${encodeToFilesystemAndURLSafeString(parallelRouteKey)}/${childRequestKeyPart}`;
|
|
return parentRequestKey + '/' + slotKey;
|
|
}
|
|
// Define a regex pattern to match the most common characters found in a route
|
|
// param. It excludes anything that might not be cross-platform filesystem
|
|
// compatible, like |. It does not need to be precise because the fallback is to
|
|
// just base64url-encode the whole parameter, which is fine; we just don't do it
|
|
// by default for compactness, and for easier debugging.
|
|
const simpleParamValueRegex = /^[a-zA-Z0-9\-_@]+$/;
|
|
function encodeToFilesystemAndURLSafeString(value) {
|
|
if (simpleParamValueRegex.test(value)) {
|
|
return value;
|
|
}
|
|
// If there are any unsafe characters, base64url-encode the entire value.
|
|
// We also add a ! prefix so it doesn't collide with the simple case.
|
|
const base64url = btoa(value).replace(/\+/g, '-') // Replace '+' with '-'
|
|
.replace(/\//g, '_') // Replace '/' with '_'
|
|
.replace(/=+$/, '') // Remove trailing '='
|
|
;
|
|
return '!' + base64url;
|
|
}
|
|
function convertSegmentPathToStaticExportFilename(segmentPath) {
|
|
return `__next${segmentPath.replace(/\//g, '.')}.txt`;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/route-params.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
doesStaticSegmentAppearInURL: null,
|
|
getCacheKeyForDynamicParam: null,
|
|
getParamValueFromCacheKey: null,
|
|
getRenderedPathname: null,
|
|
getRenderedSearch: null,
|
|
parseDynamicParamFromURLPart: null,
|
|
urlSearchParamsToParsedUrlQuery: null,
|
|
urlToUrlWithoutFlightMarker: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
doesStaticSegmentAppearInURL: function() {
|
|
return doesStaticSegmentAppearInURL;
|
|
},
|
|
getCacheKeyForDynamicParam: function() {
|
|
return getCacheKeyForDynamicParam;
|
|
},
|
|
getParamValueFromCacheKey: function() {
|
|
return getParamValueFromCacheKey;
|
|
},
|
|
getRenderedPathname: function() {
|
|
return getRenderedPathname;
|
|
},
|
|
getRenderedSearch: function() {
|
|
return getRenderedSearch;
|
|
},
|
|
parseDynamicParamFromURLPart: function() {
|
|
return parseDynamicParamFromURLPart;
|
|
},
|
|
urlSearchParamsToParsedUrlQuery: function() {
|
|
return urlSearchParamsToParsedUrlQuery;
|
|
},
|
|
urlToUrlWithoutFlightMarker: function() {
|
|
return urlToUrlWithoutFlightMarker;
|
|
}
|
|
});
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const _segmentvalueencoding = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.js [app-ssr] (ecmascript)");
|
|
const _approuterheaders = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)");
|
|
function getRenderedSearch(response) {
|
|
// If the server performed a rewrite, the search params used to render the
|
|
// page will be different from the params in the request URL. In this case,
|
|
// the response will include a header that gives the rewritten search query.
|
|
const rewrittenQuery = response.headers.get(_approuterheaders.NEXT_REWRITTEN_QUERY_HEADER);
|
|
if (rewrittenQuery !== null) {
|
|
return rewrittenQuery === '' ? '' : '?' + rewrittenQuery;
|
|
}
|
|
// If the header is not present, there was no rewrite, so we use the search
|
|
// query of the response URL.
|
|
return urlToUrlWithoutFlightMarker(new URL(response.url)).search;
|
|
}
|
|
function getRenderedPathname(response) {
|
|
// If the server performed a rewrite, the pathname used to render the
|
|
// page will be different from the pathname in the request URL. In this case,
|
|
// the response will include a header that gives the rewritten pathname.
|
|
const rewrittenPath = response.headers.get(_approuterheaders.NEXT_REWRITTEN_PATH_HEADER);
|
|
return rewrittenPath ?? urlToUrlWithoutFlightMarker(new URL(response.url)).pathname;
|
|
}
|
|
// Pathname parts come from `URL.pathname.split('/')`, so they are already
|
|
// in the encoded form the URL parser produces. The server-side equivalent
|
|
// (`get-dynamic-param.ts`) starts from a decoded param value and applies
|
|
// `encodeURIComponent` once. The two encodings are not the same — for
|
|
// example, the URL parser leaves `,` and `:` untouched while
|
|
// `encodeURIComponent` percent-encodes them. To produce the same canonical
|
|
// form on the client (and avoid double-encoding `%xx` sequences such as
|
|
// `%2F` → `%252F`), we decode the URL part first and re-encode it.
|
|
function canonicalizeURLPart(part) {
|
|
try {
|
|
return encodeURIComponent(decodeURIComponent(part));
|
|
} catch {
|
|
// `decodeURIComponent` throws on malformed sequences. Fall back to the
|
|
// already-encoded form rather than failing the navigation.
|
|
return part;
|
|
}
|
|
}
|
|
function parseDynamicParamFromURLPart(paramType, pathnameParts, partIndex) {
|
|
// This needs to match the behavior in get-dynamic-param.ts.
|
|
switch(paramType){
|
|
// Catchalls
|
|
case 'c':
|
|
{
|
|
// Catchalls receive all the remaining URL parts. If there are no
|
|
// remaining pathname parts, return an empty array.
|
|
return partIndex < pathnameParts.length ? pathnameParts.slice(partIndex).map((s)=>canonicalizeURLPart(s)) : [];
|
|
}
|
|
// Catchall intercepted
|
|
case 'ci(..)(..)':
|
|
case 'ci(.)':
|
|
case 'ci(..)':
|
|
case 'ci(...)':
|
|
{
|
|
const prefix = paramType.length - 2;
|
|
return partIndex < pathnameParts.length ? pathnameParts.slice(partIndex).map((s, i)=>{
|
|
if (i === 0) {
|
|
return canonicalizeURLPart(s.slice(prefix));
|
|
}
|
|
return canonicalizeURLPart(s);
|
|
}) : [];
|
|
}
|
|
// Optional catchalls
|
|
case 'oc':
|
|
{
|
|
// Optional catchalls receive all the remaining URL parts, unless this is
|
|
// the end of the pathname, in which case they return null.
|
|
return partIndex < pathnameParts.length ? pathnameParts.slice(partIndex).map((s)=>canonicalizeURLPart(s)) : null;
|
|
}
|
|
// Dynamic
|
|
case 'd':
|
|
{
|
|
if (partIndex >= pathnameParts.length) {
|
|
// The route tree expected there to be more parts in the URL than there
|
|
// actually are. This could happen if the x-nextjs-rewritten-path header
|
|
// is incorrectly set, or potentially due to bug in Next.js. TODO:
|
|
// Should this be a hard error? During a prefetch, we can just abort.
|
|
// During a client navigation, we could trigger a hard refresh. But if
|
|
// it happens during initial render, we don't really have any
|
|
// recovery options.
|
|
return '';
|
|
}
|
|
return canonicalizeURLPart(pathnameParts[partIndex]);
|
|
}
|
|
// Dynamic intercepted
|
|
case 'di(..)(..)':
|
|
case 'di(.)':
|
|
case 'di(..)':
|
|
case 'di(...)':
|
|
{
|
|
const prefix = paramType.length - 2;
|
|
if (partIndex >= pathnameParts.length) {
|
|
// The route tree expected there to be more parts in the URL than there
|
|
// actually are. This could happen if the x-nextjs-rewritten-path header
|
|
// is incorrectly set, or potentially due to bug in Next.js. TODO:
|
|
// Should this be a hard error? During a prefetch, we can just abort.
|
|
// During a client navigation, we could trigger a hard refresh. But if
|
|
// it happens during initial render, we don't really have any
|
|
// recovery options.
|
|
return '';
|
|
}
|
|
return canonicalizeURLPart(pathnameParts[partIndex].slice(prefix));
|
|
}
|
|
default:
|
|
paramType;
|
|
return '';
|
|
}
|
|
}
|
|
function doesStaticSegmentAppearInURL(segment) {
|
|
// This is not a parameterized segment; however, we need to determine
|
|
// whether or not this segment appears in the URL. For example, this route
|
|
// groups do not appear in the URL, so they should be skipped. Any other
|
|
// special cases must be handled here.
|
|
// TODO: Consider encoding this directly into the router tree instead of
|
|
// inferring it on the client based on the segment type. Something like
|
|
// a `doesAppearInURL` flag in FlightRouterState.
|
|
if (segment === _segmentvalueencoding.ROOT_SEGMENT_REQUEST_KEY || // For some reason, the loader tree sometimes includes extra __PAGE__
|
|
// "layouts" when part of a parallel route. But it's not a leaf node.
|
|
// Otherwise, we wouldn't need this special case because pages are
|
|
// always leaf nodes.
|
|
// TODO: Investigate why the loader produces these fake page segments.
|
|
segment.startsWith(_segment.PAGE_SEGMENT_KEY) || // Route groups.
|
|
segment[0] === '(' && segment.endsWith(')') || segment === _segment.DEFAULT_SEGMENT_KEY || segment === '/_not-found') {
|
|
return false;
|
|
} else {
|
|
// All other segment types appear in the URL
|
|
return true;
|
|
}
|
|
}
|
|
function getCacheKeyForDynamicParam(paramValue, renderedSearch) {
|
|
// This needs to match the logic in get-dynamic-param.ts, until we're able to
|
|
// unify the various implementations so that these are always computed on
|
|
// the client.
|
|
if (typeof paramValue === 'string') {
|
|
// TODO: Refactor or remove this helper function to accept a string rather
|
|
// than the whole segment type. Also we can probably just append the
|
|
// search string instead of turning it into JSON.
|
|
const pageSegmentWithSearchParams = (0, _segment.addSearchParamsIfPageSegment)(paramValue, Object.fromEntries(new URLSearchParams(renderedSearch)));
|
|
return pageSegmentWithSearchParams;
|
|
} else if (paramValue === null) {
|
|
return '';
|
|
} else {
|
|
return paramValue.join('/');
|
|
}
|
|
}
|
|
function urlToUrlWithoutFlightMarker(url) {
|
|
const urlWithoutFlightParameters = new URL(url);
|
|
urlWithoutFlightParameters.searchParams.delete(_approuterheaders.NEXT_RSC_UNION_QUERY);
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
return urlWithoutFlightParameters;
|
|
}
|
|
function getParamValueFromCacheKey(paramCacheKey, paramType) {
|
|
// Turn the cache key string sent by the server (as part of FlightRouterState)
|
|
// into a value that can be passed to `useParams` and client components.
|
|
const isCatchAll = paramType === 'c' || paramType === 'oc';
|
|
if (isCatchAll) {
|
|
// Catch-all param keys are a concatenation of the path segments.
|
|
// See equivalent logic in `getSelectedParams`.
|
|
// TODO: We should just pass the array directly, rather than concatenate
|
|
// it to a string and then split it back to an array. It needs to be an
|
|
// array in some places, like when passing a key React, but we can convert
|
|
// it at runtime in those places.
|
|
return paramCacheKey.split('/');
|
|
}
|
|
return paramCacheKey;
|
|
}
|
|
function urlSearchParamsToParsedUrlQuery(searchParams) {
|
|
// Converts a URLSearchParams object to the same type used by the server when
|
|
// creating search params props, i.e. the type returned by Node's
|
|
// "querystring" module.
|
|
const result = {};
|
|
for (const [key, value] of searchParams.entries()){
|
|
if (result[key] === undefined) {
|
|
result[key] = value;
|
|
} else if (Array.isArray(result[key])) {
|
|
result[key].push(value);
|
|
} else {
|
|
result[key] = [
|
|
result[key],
|
|
value
|
|
];
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/create-href-from-url.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "createHrefFromUrl", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return createHrefFromUrl;
|
|
}
|
|
});
|
|
function createHrefFromUrl(url, includeHash = true) {
|
|
return url.pathname + url.search + (includeHash ? url.hash : '');
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/flight-data-helpers.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
createInitialRSCPayloadFromFallbackPrerender: null,
|
|
getFlightDataPartsFromPath: null,
|
|
getNextFlightSegmentPath: null,
|
|
normalizeFlightData: null,
|
|
prepareFlightRouterStateForRequest: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
createInitialRSCPayloadFromFallbackPrerender: function() {
|
|
return createInitialRSCPayloadFromFallbackPrerender;
|
|
},
|
|
getFlightDataPartsFromPath: function() {
|
|
return getFlightDataPartsFromPath;
|
|
},
|
|
getNextFlightSegmentPath: function() {
|
|
return getNextFlightSegmentPath;
|
|
},
|
|
normalizeFlightData: function() {
|
|
return normalizeFlightData;
|
|
},
|
|
prepareFlightRouterStateForRequest: function() {
|
|
return prepareFlightRouterStateForRequest;
|
|
}
|
|
});
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const _routeparams = __turbopack_context__.r("[project]/node_modules/next/dist/client/route-params.js [app-ssr] (ecmascript)");
|
|
const _createhreffromurl = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/create-href-from-url.js [app-ssr] (ecmascript)");
|
|
function getFlightDataPartsFromPath(flightDataPath) {
|
|
// Pick the last 4 items from the `FlightDataPath` to get the [tree, seedData, viewport, isHeadPartial].
|
|
const flightDataPathLength = 4;
|
|
// tree, seedData, and head are *always* the last three items in the `FlightDataPath`.
|
|
const [tree, seedData, head, isHeadPartial] = flightDataPath.slice(-flightDataPathLength);
|
|
// The `FlightSegmentPath` is everything except the last three items. For a root render, it won't be present.
|
|
const segmentPath = flightDataPath.slice(0, -flightDataPathLength);
|
|
return {
|
|
// TODO: Unify these two segment path helpers. We are inconsistently pushing an empty segment ("")
|
|
// to the start of the segment path in some places which makes it hard to use solely the segment path.
|
|
// Look for "// TODO-APP: remove ''" in the codebase.
|
|
pathToSegment: segmentPath.slice(0, -1),
|
|
segmentPath,
|
|
// if the `FlightDataPath` corresponds with the root, there'll be no segment path,
|
|
// in which case we default to ''.
|
|
segment: segmentPath[segmentPath.length - 1] ?? '',
|
|
tree,
|
|
seedData,
|
|
head,
|
|
isHeadPartial,
|
|
isRootRender: flightDataPath.length === flightDataPathLength
|
|
};
|
|
}
|
|
function createInitialRSCPayloadFromFallbackPrerender(response, fallbackInitialRSCPayload) {
|
|
// This is a static fallback page. In order to hydrate the page, we need to
|
|
// parse the client params from the URL, but to account for the possibility
|
|
// that the page was rewritten, we need to check the response headers
|
|
// for x-nextjs-rewritten-path or x-nextjs-rewritten-query headers. Since
|
|
// we can't access the headers of the initial document response, the client
|
|
// performs a fetch request to the current location. Since it's possible that
|
|
// the fetch request will be dynamically rewritten to a different path than
|
|
// the initial document, this fetch request delivers _all_ the hydration data
|
|
// for the page; it was not inlined into the document, like it normally
|
|
// would be.
|
|
//
|
|
// TODO: Consider treating the case where fetch is rewritten to a different
|
|
// path from the document as a special deopt case. We should optimistically
|
|
// assume this won't happen, inline the data into the document, and perform
|
|
// a minimal request (like a HEAD or range request) to verify that the
|
|
// response matches. Tricky to get right because we need to account for
|
|
// all the different deployment environments we support, like output:
|
|
// "export" mode, where we currently don't assume that custom response
|
|
// headers are present.
|
|
// Patch the Flight data sent by the server with the correct params parsed
|
|
// from the URL + response object.
|
|
const renderedPathname = (0, _routeparams.getRenderedPathname)(response);
|
|
const renderedSearch = (0, _routeparams.getRenderedSearch)(response);
|
|
const canonicalUrl = (0, _createhreffromurl.createHrefFromUrl)(new URL(location.href));
|
|
const originalFlightDataPath = fallbackInitialRSCPayload.f[0];
|
|
const originalFlightRouterState = originalFlightDataPath[0];
|
|
const payload = {
|
|
c: canonicalUrl.split('/'),
|
|
q: renderedSearch,
|
|
i: fallbackInitialRSCPayload.i,
|
|
f: [
|
|
[
|
|
fillInFallbackFlightRouterState(originalFlightRouterState, renderedPathname, renderedSearch),
|
|
originalFlightDataPath[1],
|
|
originalFlightDataPath[2],
|
|
originalFlightDataPath[2]
|
|
]
|
|
],
|
|
m: fallbackInitialRSCPayload.m,
|
|
G: fallbackInitialRSCPayload.G,
|
|
S: fallbackInitialRSCPayload.S,
|
|
h: fallbackInitialRSCPayload.h
|
|
};
|
|
if (fallbackInitialRSCPayload.b) {
|
|
payload.b = fallbackInitialRSCPayload.b;
|
|
}
|
|
return payload;
|
|
}
|
|
function fillInFallbackFlightRouterState(flightRouterState, renderedPathname, renderedSearch) {
|
|
const pathnameParts = renderedPathname.split('/').filter((p)=>p !== '');
|
|
const index = 0;
|
|
return fillInFallbackFlightRouterStateImpl(flightRouterState, renderedSearch, pathnameParts, index);
|
|
}
|
|
function fillInFallbackFlightRouterStateImpl(flightRouterState, renderedSearch, pathnameParts, pathnamePartsIndex) {
|
|
const originalSegment = flightRouterState[0];
|
|
let newSegment;
|
|
let doesAppearInURL;
|
|
if (typeof originalSegment === 'string') {
|
|
newSegment = originalSegment;
|
|
doesAppearInURL = (0, _routeparams.doesStaticSegmentAppearInURL)(originalSegment);
|
|
} else {
|
|
const paramName = originalSegment[0];
|
|
const paramType = originalSegment[2];
|
|
const staticSiblings = originalSegment[3];
|
|
const paramValue = (0, _routeparams.parseDynamicParamFromURLPart)(paramType, pathnameParts, pathnamePartsIndex);
|
|
const cacheKey = (0, _routeparams.getCacheKeyForDynamicParam)(paramValue, renderedSearch);
|
|
newSegment = [
|
|
paramName,
|
|
cacheKey,
|
|
paramType,
|
|
staticSiblings
|
|
];
|
|
doesAppearInURL = true;
|
|
}
|
|
// Only increment the index if the segment appears in the URL. If it's a
|
|
// "virtual" segment, like a route group, it remains the same.
|
|
const childPathnamePartsIndex = doesAppearInURL ? pathnamePartsIndex + 1 : pathnamePartsIndex;
|
|
const children = flightRouterState[1];
|
|
const newChildren = {};
|
|
for(let key in children){
|
|
const childFlightRouterState = children[key];
|
|
newChildren[key] = fillInFallbackFlightRouterStateImpl(childFlightRouterState, renderedSearch, pathnameParts, childPathnamePartsIndex);
|
|
}
|
|
const newState = [
|
|
newSegment,
|
|
newChildren,
|
|
null,
|
|
flightRouterState[3],
|
|
flightRouterState[4]
|
|
];
|
|
return newState;
|
|
}
|
|
function getNextFlightSegmentPath(flightSegmentPath) {
|
|
// Since `FlightSegmentPath` is a repeated tuple of `Segment` and `ParallelRouteKey`, we slice off two items
|
|
// to get the next segment path.
|
|
return flightSegmentPath.slice(2);
|
|
}
|
|
function normalizeFlightData(flightData) {
|
|
// FlightData can be a string when the server didn't respond with a proper flight response,
|
|
// or when a redirect happens, to signal to the client that it needs to perform an MPA navigation.
|
|
if (typeof flightData === 'string') {
|
|
return flightData;
|
|
}
|
|
return flightData.map((flightDataPath)=>getFlightDataPartsFromPath(flightDataPath));
|
|
}
|
|
function prepareFlightRouterStateForRequest(flightRouterState, isHmrRefresh) {
|
|
// HMR requests need the complete, unmodified state for proper functionality
|
|
if (isHmrRefresh) {
|
|
return encodeURIComponent(JSON.stringify(flightRouterState));
|
|
}
|
|
return encodeURIComponent(JSON.stringify(stripClientOnlyDataFromFlightRouterState(flightRouterState)));
|
|
}
|
|
/**
|
|
* Recursively strips client-only data from FlightRouterState while preserving
|
|
* server-needed information for proper rendering decisions.
|
|
*/ function stripClientOnlyDataFromFlightRouterState(flightRouterState) {
|
|
const [segment, parallelRoutes, _refreshState, refreshMarker, prefetchHints] = flightRouterState;
|
|
// Strip client-only data from the segment
|
|
const cleanedSegment = stripClientOnlyDataFromSegment(segment);
|
|
// Recursively process parallel routes
|
|
const cleanedParallelRoutes = {};
|
|
for (const [key, childState] of Object.entries(parallelRoutes)){
|
|
cleanedParallelRoutes[key] = stripClientOnlyDataFromFlightRouterState(childState);
|
|
}
|
|
const result = [
|
|
cleanedSegment,
|
|
cleanedParallelRoutes
|
|
];
|
|
if (refreshMarker) {
|
|
result[2] = null // null slightly more compact than undefined
|
|
;
|
|
result[3] = refreshMarker;
|
|
}
|
|
// Append optional fields if present
|
|
if (prefetchHints !== undefined) {
|
|
result[4] = prefetchHints;
|
|
}
|
|
// Everything else is used only by the client and is not needed for requests.
|
|
return result;
|
|
}
|
|
/**
|
|
* Strips client-only data from segments:
|
|
* - Search parameters from __PAGE__ segments
|
|
* - staticSiblings from dynamic segment tuples (only needed for client-side
|
|
* prefetch reuse decisions)
|
|
*/ function stripClientOnlyDataFromSegment(segment) {
|
|
if (typeof segment === 'string') {
|
|
// Strip search params from __PAGE__ segments
|
|
if (segment.startsWith(_segment.PAGE_SEGMENT_KEY + '?')) {
|
|
return _segment.PAGE_SEGMENT_KEY;
|
|
}
|
|
return segment;
|
|
}
|
|
// Dynamic segment tuple: [paramName, paramCacheKey, paramType, staticSiblings]
|
|
// Strip staticSiblings (4th element) since server doesn't need it
|
|
const [paramName, paramCacheKey, paramType] = segment;
|
|
return [
|
|
paramName,
|
|
paramCacheKey,
|
|
paramType,
|
|
null
|
|
];
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/hash.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
// http://www.cse.yorku.ca/~oz/hash.html
|
|
// More specifically, 32-bit hash via djbxor
|
|
// (ref: https://gist.github.com/eplawless/52813b1d8ad9af510d85?permalink_comment_id=3367765#gistcomment-3367765)
|
|
// This is due to number type differences between rust for turbopack to js number types,
|
|
// where rust does not have easy way to repreesnt js's 53-bit float number type for the matching
|
|
// overflow behavior. This is more `correct` in terms of having canonical hash across different runtime / implementation
|
|
// as can gaurantee determinstic output from 32bit hash.
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
djb2Hash: null,
|
|
hexHash: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
djb2Hash: function() {
|
|
return djb2Hash;
|
|
},
|
|
hexHash: function() {
|
|
return hexHash;
|
|
}
|
|
});
|
|
function djb2Hash(str) {
|
|
let hash = 5381;
|
|
for(let i = 0; i < str.length; i++){
|
|
const char = str.charCodeAt(i);
|
|
hash = (hash << 5) + hash + char & 0xffffffff;
|
|
}
|
|
return hash >>> 0;
|
|
}
|
|
function hexHash(str) {
|
|
return djb2Hash(str).toString(36).slice(0, 5);
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/cache-busting-search-param.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
computeCacheBustingSearchParam: null,
|
|
computeLegacyCacheBustingSearchParam: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
computeCacheBustingSearchParam: function() {
|
|
return computeCacheBustingSearchParam;
|
|
},
|
|
computeLegacyCacheBustingSearchParam: function() {
|
|
return computeLegacyCacheBustingSearchParam;
|
|
}
|
|
});
|
|
const _hash = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/hash.js [app-ssr] (ecmascript)");
|
|
const CACHE_BUSTING_SEARCH_PARAM_DIGEST_BYTES = 12;
|
|
const textEncoder = new TextEncoder();
|
|
function encodeCacheBustingSearchParam(bytes) {
|
|
let binary = '';
|
|
for(let i = 0; i < bytes.length; i++){
|
|
binary += String.fromCharCode(bytes[i]);
|
|
}
|
|
return btoa(binary).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
|
|
}
|
|
function normalizeCacheBustingInput(value) {
|
|
if (value === undefined) {
|
|
return '0';
|
|
}
|
|
return Array.isArray(value) ? value.join(',') : value;
|
|
}
|
|
function createCacheBustingSearchParamInput(prefetchHeader, segmentPrefetchHeader, stateTreeHeader, nextUrlHeader) {
|
|
if ((prefetchHeader === undefined || prefetchHeader === '0') && segmentPrefetchHeader === undefined && stateTreeHeader === undefined && nextUrlHeader === undefined) {
|
|
return null;
|
|
}
|
|
return [
|
|
prefetchHeader ?? '0',
|
|
normalizeCacheBustingInput(segmentPrefetchHeader),
|
|
normalizeCacheBustingInput(stateTreeHeader),
|
|
normalizeCacheBustingInput(nextUrlHeader)
|
|
].join(',');
|
|
}
|
|
async function computeCacheBustingSearchParamFromInput(input) {
|
|
// Truncate SHA-256 to 96 bits to keep `_rsc` compact
|
|
const digest = await globalThis.crypto.subtle.digest('SHA-256', textEncoder.encode(input));
|
|
return encodeCacheBustingSearchParam(new Uint8Array(digest).subarray(0, CACHE_BUSTING_SEARCH_PARAM_DIGEST_BYTES));
|
|
}
|
|
async function computeCacheBustingSearchParam(prefetchHeader, segmentPrefetchHeader, stateTreeHeader, nextUrlHeader) {
|
|
const input = createCacheBustingSearchParamInput(prefetchHeader, segmentPrefetchHeader, stateTreeHeader, nextUrlHeader);
|
|
if (input === null) {
|
|
return '';
|
|
}
|
|
return computeCacheBustingSearchParamFromInput(input);
|
|
}
|
|
function computeLegacyCacheBustingSearchParam(prefetchHeader, segmentPrefetchHeader, stateTreeHeader, nextUrlHeader) {
|
|
const input = createCacheBustingSearchParamInput(prefetchHeader, segmentPrefetchHeader, stateTreeHeader, nextUrlHeader);
|
|
if (input === null) {
|
|
return '';
|
|
}
|
|
return (0, _hash.hexHash)(input);
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/set-cache-busting-search-param.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
setCacheBustingSearchParam: null,
|
|
setCacheBustingSearchParamWithHash: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
setCacheBustingSearchParam: function() {
|
|
return setCacheBustingSearchParam;
|
|
},
|
|
setCacheBustingSearchParamWithHash: function() {
|
|
return setCacheBustingSearchParamWithHash;
|
|
}
|
|
});
|
|
const _cachebustingsearchparam = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/cache-busting-search-param.js [app-ssr] (ecmascript)");
|
|
const _approuterheaders = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)");
|
|
async function computeClientCacheBustingSearchParam(headers) {
|
|
if (typeof globalThis.crypto?.subtle?.digest === 'function') {
|
|
return (0, _cachebustingsearchparam.computeCacheBustingSearchParam)(headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER], headers[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER], headers[_approuterheaders.NEXT_ROUTER_STATE_TREE_HEADER], headers[_approuterheaders.NEXT_URL]);
|
|
}
|
|
return (0, _cachebustingsearchparam.computeLegacyCacheBustingSearchParam)(headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER], headers[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER], headers[_approuterheaders.NEXT_ROUTER_STATE_TREE_HEADER], headers[_approuterheaders.NEXT_URL]);
|
|
}
|
|
const setCacheBustingSearchParam = async (url, headers)=>{
|
|
const uniqueCacheKey = await computeClientCacheBustingSearchParam(headers);
|
|
setCacheBustingSearchParamWithHash(url, uniqueCacheKey);
|
|
};
|
|
const setCacheBustingSearchParamWithHash = (url, hash)=>{
|
|
/**
|
|
* Note that we intentionally do not use `url.searchParams.set` here:
|
|
*
|
|
* const url = new URL('https://example.com/search?q=custom%20spacing');
|
|
* url.searchParams.set('_rsc', 'abc123');
|
|
* console.log(url.toString()); // Outputs: https://example.com/search?q=custom+spacing&_rsc=abc123
|
|
* ^ <--- this is causing confusion
|
|
* This is in fact intended based on https://url.spec.whatwg.org/#interface-urlsearchparams, but
|
|
* we want to preserve the %20 as %20 if that's what the user passed in, hence the custom
|
|
* logic below.
|
|
*/ const existingSearch = url.search;
|
|
const rawQuery = existingSearch.startsWith('?') ? existingSearch.slice(1) : existingSearch;
|
|
// Always remove any existing cache busting param and add a fresh one to ensure
|
|
// we have the correct value based on current request headers
|
|
const pairs = rawQuery.split('&').filter((pair)=>pair && !pair.startsWith(`${_approuterheaders.NEXT_RSC_UNION_QUERY}=`));
|
|
if (hash.length > 0) {
|
|
pairs.push(`${_approuterheaders.NEXT_RSC_UNION_QUERY}=${hash}`);
|
|
} else {
|
|
pairs.push(`${_approuterheaders.NEXT_RSC_UNION_QUERY}`);
|
|
}
|
|
url.search = pairs.length ? `?${pairs.join('&')}` : '';
|
|
};
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/deployment-id.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
getAssetToken: null,
|
|
getAssetTokenQuery: null,
|
|
getDeploymentId: null,
|
|
getDeploymentIdQuery: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
getAssetToken: function() {
|
|
return getAssetToken;
|
|
},
|
|
getAssetTokenQuery: function() {
|
|
return getAssetTokenQuery;
|
|
},
|
|
getDeploymentId: function() {
|
|
return getDeploymentId;
|
|
},
|
|
getDeploymentIdQuery: function() {
|
|
return getDeploymentIdQuery;
|
|
}
|
|
});
|
|
let deploymentId;
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
else {
|
|
// Client side: replaced with globalThis.NEXT_DEPLOYMENT_ID
|
|
// Server side: left as is or replaced with a string or replaced with false
|
|
deploymentId = ("TURBOPACK compile-time value", false) || undefined;
|
|
}
|
|
function getDeploymentId() {
|
|
return deploymentId;
|
|
}
|
|
function getDeploymentIdQuery(ampersand = false) {
|
|
let id = getDeploymentId();
|
|
if (id) {
|
|
return `${ampersand ? '&' : '?'}dpl=${id}`;
|
|
}
|
|
return '';
|
|
}
|
|
function getAssetToken() {
|
|
return ("TURBOPACK compile-time value", "") || ("TURBOPACK compile-time value", false);
|
|
}
|
|
function getAssetTokenQuery(ampersand = false) {
|
|
let id = getAssetToken();
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
return '';
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/navigation-build-id.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
// This gets assigned as a side-effect during app initialization. Because it
|
|
// represents the build used to create the JS bundle, it should never change
|
|
// after being set, so we store it in a global variable.
|
|
//
|
|
// When performing RSC requests, if the incoming data has a different build ID,
|
|
// we perform an MPA navigation/refresh to load the updated build and ensure
|
|
// that the client and server in sync.
|
|
//
|
|
// Starts as an empty string. In practice, because setNavigationBuildId is called during initialization
|
|
// before hydration starts, this will always get reassigned to the actual ID before it's ever needed
|
|
// by a navigation. If for some reasons it didn't, due to a bug or race condition, then on
|
|
// navigation the build comparision would fail and trigger an MPA navigation.
|
|
//
|
|
// Note that this can also be initialized with the deployment id instead (if available). So it's not
|
|
// the same as "the build id", but we are running out of alternative names for "build id or
|
|
// deployment id".
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
getNavigationBuildId: null,
|
|
setNavigationBuildId: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
getNavigationBuildId: function() {
|
|
return getNavigationBuildId;
|
|
},
|
|
setNavigationBuildId: function() {
|
|
return setNavigationBuildId;
|
|
}
|
|
});
|
|
let globalBuildId = '';
|
|
function setNavigationBuildId(buildId) {
|
|
globalBuildId = buildId;
|
|
}
|
|
function getNavigationBuildId() {
|
|
return globalBuildId;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/lib/constants.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
ACTION_SUFFIX: null,
|
|
APP_DIR_ALIAS: null,
|
|
CACHE_ONE_YEAR_SECONDS: null,
|
|
DOT_NEXT_ALIAS: null,
|
|
ESLINT_DEFAULT_DIRS: null,
|
|
GSP_NO_RETURNED_VALUE: null,
|
|
GSSP_COMPONENT_MEMBER_ERROR: null,
|
|
GSSP_NO_RETURNED_VALUE: null,
|
|
HTML_CONTENT_TYPE_HEADER: null,
|
|
INFINITE_CACHE: null,
|
|
INSTRUMENTATION_HOOK_FILENAME: null,
|
|
JSON_CONTENT_TYPE_HEADER: null,
|
|
MATCHED_PATH_HEADER: null,
|
|
MIDDLEWARE_FILENAME: null,
|
|
MIDDLEWARE_LOCATION_REGEXP: null,
|
|
NEXT_BODY_SUFFIX: null,
|
|
NEXT_CACHE_IMPLICIT_TAG_ID: null,
|
|
NEXT_CACHE_REVALIDATED_TAGS_HEADER: null,
|
|
NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER: null,
|
|
NEXT_CACHE_ROOT_PARAM_TAG_ID: null,
|
|
NEXT_CACHE_SOFT_TAG_MAX_LENGTH: null,
|
|
NEXT_CACHE_TAGS_HEADER: null,
|
|
NEXT_CACHE_TAG_MAX_ITEMS: null,
|
|
NEXT_CACHE_TAG_MAX_LENGTH: null,
|
|
NEXT_DATA_SUFFIX: null,
|
|
NEXT_INTERCEPTION_MARKER_PREFIX: null,
|
|
NEXT_META_SUFFIX: null,
|
|
NEXT_NAV_DEPLOYMENT_ID_HEADER: null,
|
|
NEXT_QUERY_PARAM_PREFIX: null,
|
|
NEXT_RESUME_HEADER: null,
|
|
NEXT_RESUME_STATE_LENGTH_HEADER: null,
|
|
NON_STANDARD_NODE_ENV: null,
|
|
PAGES_DIR_ALIAS: null,
|
|
PRERENDER_REVALIDATE_HEADER: null,
|
|
PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER: null,
|
|
PROXY_FILENAME: null,
|
|
PROXY_LOCATION_REGEXP: null,
|
|
PUBLIC_DIR_MIDDLEWARE_CONFLICT: null,
|
|
ROOT_DIR_ALIAS: null,
|
|
RSC_ACTION_CLIENT_WRAPPER_ALIAS: null,
|
|
RSC_ACTION_ENCRYPTION_ALIAS: null,
|
|
RSC_ACTION_PROXY_ALIAS: null,
|
|
RSC_ACTION_VALIDATE_ALIAS: null,
|
|
RSC_CACHE_WRAPPER_ALIAS: null,
|
|
RSC_DYNAMIC_IMPORT_WRAPPER_ALIAS: null,
|
|
RSC_MOD_REF_PROXY_ALIAS: null,
|
|
RSC_SEGMENTS_DIR_SUFFIX: null,
|
|
RSC_SEGMENT_SUFFIX: null,
|
|
RSC_SUFFIX: null,
|
|
SERVER_PROPS_EXPORT_ERROR: null,
|
|
SERVER_PROPS_GET_INIT_PROPS_CONFLICT: null,
|
|
SERVER_PROPS_SSG_CONFLICT: null,
|
|
SERVER_RUNTIME: null,
|
|
SSG_FALLBACK_EXPORT_ERROR: null,
|
|
SSG_GET_INITIAL_PROPS_CONFLICT: null,
|
|
STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR: null,
|
|
TEXT_PLAIN_CONTENT_TYPE_HEADER: null,
|
|
UNSTABLE_REVALIDATE_RENAME_ERROR: null,
|
|
WEBPACK_LAYERS: null,
|
|
WEBPACK_RESOURCE_QUERIES: null,
|
|
WEB_SOCKET_MAX_RECONNECTIONS: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
ACTION_SUFFIX: function() {
|
|
return ACTION_SUFFIX;
|
|
},
|
|
APP_DIR_ALIAS: function() {
|
|
return APP_DIR_ALIAS;
|
|
},
|
|
CACHE_ONE_YEAR_SECONDS: function() {
|
|
return CACHE_ONE_YEAR_SECONDS;
|
|
},
|
|
DOT_NEXT_ALIAS: function() {
|
|
return DOT_NEXT_ALIAS;
|
|
},
|
|
ESLINT_DEFAULT_DIRS: function() {
|
|
return ESLINT_DEFAULT_DIRS;
|
|
},
|
|
GSP_NO_RETURNED_VALUE: function() {
|
|
return GSP_NO_RETURNED_VALUE;
|
|
},
|
|
GSSP_COMPONENT_MEMBER_ERROR: function() {
|
|
return GSSP_COMPONENT_MEMBER_ERROR;
|
|
},
|
|
GSSP_NO_RETURNED_VALUE: function() {
|
|
return GSSP_NO_RETURNED_VALUE;
|
|
},
|
|
HTML_CONTENT_TYPE_HEADER: function() {
|
|
return HTML_CONTENT_TYPE_HEADER;
|
|
},
|
|
INFINITE_CACHE: function() {
|
|
return INFINITE_CACHE;
|
|
},
|
|
INSTRUMENTATION_HOOK_FILENAME: function() {
|
|
return INSTRUMENTATION_HOOK_FILENAME;
|
|
},
|
|
JSON_CONTENT_TYPE_HEADER: function() {
|
|
return JSON_CONTENT_TYPE_HEADER;
|
|
},
|
|
MATCHED_PATH_HEADER: function() {
|
|
return MATCHED_PATH_HEADER;
|
|
},
|
|
MIDDLEWARE_FILENAME: function() {
|
|
return MIDDLEWARE_FILENAME;
|
|
},
|
|
MIDDLEWARE_LOCATION_REGEXP: function() {
|
|
return MIDDLEWARE_LOCATION_REGEXP;
|
|
},
|
|
NEXT_BODY_SUFFIX: function() {
|
|
return NEXT_BODY_SUFFIX;
|
|
},
|
|
NEXT_CACHE_IMPLICIT_TAG_ID: function() {
|
|
return NEXT_CACHE_IMPLICIT_TAG_ID;
|
|
},
|
|
NEXT_CACHE_REVALIDATED_TAGS_HEADER: function() {
|
|
return NEXT_CACHE_REVALIDATED_TAGS_HEADER;
|
|
},
|
|
NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER: function() {
|
|
return NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER;
|
|
},
|
|
NEXT_CACHE_ROOT_PARAM_TAG_ID: function() {
|
|
return NEXT_CACHE_ROOT_PARAM_TAG_ID;
|
|
},
|
|
NEXT_CACHE_SOFT_TAG_MAX_LENGTH: function() {
|
|
return NEXT_CACHE_SOFT_TAG_MAX_LENGTH;
|
|
},
|
|
NEXT_CACHE_TAGS_HEADER: function() {
|
|
return NEXT_CACHE_TAGS_HEADER;
|
|
},
|
|
NEXT_CACHE_TAG_MAX_ITEMS: function() {
|
|
return NEXT_CACHE_TAG_MAX_ITEMS;
|
|
},
|
|
NEXT_CACHE_TAG_MAX_LENGTH: function() {
|
|
return NEXT_CACHE_TAG_MAX_LENGTH;
|
|
},
|
|
NEXT_DATA_SUFFIX: function() {
|
|
return NEXT_DATA_SUFFIX;
|
|
},
|
|
NEXT_INTERCEPTION_MARKER_PREFIX: function() {
|
|
return NEXT_INTERCEPTION_MARKER_PREFIX;
|
|
},
|
|
NEXT_META_SUFFIX: function() {
|
|
return NEXT_META_SUFFIX;
|
|
},
|
|
NEXT_NAV_DEPLOYMENT_ID_HEADER: function() {
|
|
return NEXT_NAV_DEPLOYMENT_ID_HEADER;
|
|
},
|
|
NEXT_QUERY_PARAM_PREFIX: function() {
|
|
return NEXT_QUERY_PARAM_PREFIX;
|
|
},
|
|
NEXT_RESUME_HEADER: function() {
|
|
return NEXT_RESUME_HEADER;
|
|
},
|
|
NEXT_RESUME_STATE_LENGTH_HEADER: function() {
|
|
return NEXT_RESUME_STATE_LENGTH_HEADER;
|
|
},
|
|
NON_STANDARD_NODE_ENV: function() {
|
|
return NON_STANDARD_NODE_ENV;
|
|
},
|
|
PAGES_DIR_ALIAS: function() {
|
|
return PAGES_DIR_ALIAS;
|
|
},
|
|
PRERENDER_REVALIDATE_HEADER: function() {
|
|
return PRERENDER_REVALIDATE_HEADER;
|
|
},
|
|
PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER: function() {
|
|
return PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER;
|
|
},
|
|
PROXY_FILENAME: function() {
|
|
return PROXY_FILENAME;
|
|
},
|
|
PROXY_LOCATION_REGEXP: function() {
|
|
return PROXY_LOCATION_REGEXP;
|
|
},
|
|
PUBLIC_DIR_MIDDLEWARE_CONFLICT: function() {
|
|
return PUBLIC_DIR_MIDDLEWARE_CONFLICT;
|
|
},
|
|
ROOT_DIR_ALIAS: function() {
|
|
return ROOT_DIR_ALIAS;
|
|
},
|
|
RSC_ACTION_CLIENT_WRAPPER_ALIAS: function() {
|
|
return RSC_ACTION_CLIENT_WRAPPER_ALIAS;
|
|
},
|
|
RSC_ACTION_ENCRYPTION_ALIAS: function() {
|
|
return RSC_ACTION_ENCRYPTION_ALIAS;
|
|
},
|
|
RSC_ACTION_PROXY_ALIAS: function() {
|
|
return RSC_ACTION_PROXY_ALIAS;
|
|
},
|
|
RSC_ACTION_VALIDATE_ALIAS: function() {
|
|
return RSC_ACTION_VALIDATE_ALIAS;
|
|
},
|
|
RSC_CACHE_WRAPPER_ALIAS: function() {
|
|
return RSC_CACHE_WRAPPER_ALIAS;
|
|
},
|
|
RSC_DYNAMIC_IMPORT_WRAPPER_ALIAS: function() {
|
|
return RSC_DYNAMIC_IMPORT_WRAPPER_ALIAS;
|
|
},
|
|
RSC_MOD_REF_PROXY_ALIAS: function() {
|
|
return RSC_MOD_REF_PROXY_ALIAS;
|
|
},
|
|
RSC_SEGMENTS_DIR_SUFFIX: function() {
|
|
return RSC_SEGMENTS_DIR_SUFFIX;
|
|
},
|
|
RSC_SEGMENT_SUFFIX: function() {
|
|
return RSC_SEGMENT_SUFFIX;
|
|
},
|
|
RSC_SUFFIX: function() {
|
|
return RSC_SUFFIX;
|
|
},
|
|
SERVER_PROPS_EXPORT_ERROR: function() {
|
|
return SERVER_PROPS_EXPORT_ERROR;
|
|
},
|
|
SERVER_PROPS_GET_INIT_PROPS_CONFLICT: function() {
|
|
return SERVER_PROPS_GET_INIT_PROPS_CONFLICT;
|
|
},
|
|
SERVER_PROPS_SSG_CONFLICT: function() {
|
|
return SERVER_PROPS_SSG_CONFLICT;
|
|
},
|
|
SERVER_RUNTIME: function() {
|
|
return SERVER_RUNTIME;
|
|
},
|
|
SSG_FALLBACK_EXPORT_ERROR: function() {
|
|
return SSG_FALLBACK_EXPORT_ERROR;
|
|
},
|
|
SSG_GET_INITIAL_PROPS_CONFLICT: function() {
|
|
return SSG_GET_INITIAL_PROPS_CONFLICT;
|
|
},
|
|
STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR: function() {
|
|
return STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR;
|
|
},
|
|
TEXT_PLAIN_CONTENT_TYPE_HEADER: function() {
|
|
return TEXT_PLAIN_CONTENT_TYPE_HEADER;
|
|
},
|
|
UNSTABLE_REVALIDATE_RENAME_ERROR: function() {
|
|
return UNSTABLE_REVALIDATE_RENAME_ERROR;
|
|
},
|
|
WEBPACK_LAYERS: function() {
|
|
return WEBPACK_LAYERS;
|
|
},
|
|
WEBPACK_RESOURCE_QUERIES: function() {
|
|
return WEBPACK_RESOURCE_QUERIES;
|
|
},
|
|
WEB_SOCKET_MAX_RECONNECTIONS: function() {
|
|
return WEB_SOCKET_MAX_RECONNECTIONS;
|
|
}
|
|
});
|
|
const TEXT_PLAIN_CONTENT_TYPE_HEADER = 'text/plain';
|
|
const HTML_CONTENT_TYPE_HEADER = 'text/html; charset=utf-8';
|
|
const JSON_CONTENT_TYPE_HEADER = 'application/json; charset=utf-8';
|
|
const NEXT_QUERY_PARAM_PREFIX = 'nxtP';
|
|
const NEXT_INTERCEPTION_MARKER_PREFIX = 'nxtI';
|
|
const MATCHED_PATH_HEADER = 'x-matched-path';
|
|
const PRERENDER_REVALIDATE_HEADER = 'x-prerender-revalidate';
|
|
const PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER = 'x-prerender-revalidate-if-generated';
|
|
const RSC_SEGMENTS_DIR_SUFFIX = '.segments';
|
|
const RSC_SEGMENT_SUFFIX = '.segment.rsc';
|
|
const RSC_SUFFIX = '.rsc';
|
|
const ACTION_SUFFIX = '.action';
|
|
const NEXT_DATA_SUFFIX = '.json';
|
|
const NEXT_META_SUFFIX = '.meta';
|
|
const NEXT_BODY_SUFFIX = '.body';
|
|
const NEXT_NAV_DEPLOYMENT_ID_HEADER = 'x-nextjs-deployment-id';
|
|
const NEXT_CACHE_TAGS_HEADER = 'x-next-cache-tags';
|
|
const NEXT_CACHE_REVALIDATED_TAGS_HEADER = 'x-next-revalidated-tags';
|
|
const NEXT_CACHE_REVALIDATE_TAG_TOKEN_HEADER = 'x-next-revalidate-tag-token';
|
|
const NEXT_RESUME_HEADER = 'next-resume';
|
|
const NEXT_RESUME_STATE_LENGTH_HEADER = 'x-next-resume-state-length';
|
|
const NEXT_CACHE_TAG_MAX_ITEMS = 128;
|
|
const NEXT_CACHE_TAG_MAX_LENGTH = 256;
|
|
const NEXT_CACHE_SOFT_TAG_MAX_LENGTH = 1024;
|
|
const NEXT_CACHE_IMPLICIT_TAG_ID = '_N_T_';
|
|
const NEXT_CACHE_ROOT_PARAM_TAG_ID = '_N_RP_';
|
|
const CACHE_ONE_YEAR_SECONDS = 31536000;
|
|
const INFINITE_CACHE = 0xfffffffe;
|
|
const MIDDLEWARE_FILENAME = 'middleware';
|
|
const MIDDLEWARE_LOCATION_REGEXP = `(?:src/)?${MIDDLEWARE_FILENAME}`;
|
|
const PROXY_FILENAME = 'proxy';
|
|
const PROXY_LOCATION_REGEXP = `(?:src/)?${PROXY_FILENAME}`;
|
|
const INSTRUMENTATION_HOOK_FILENAME = 'instrumentation';
|
|
const PAGES_DIR_ALIAS = 'private-next-pages';
|
|
const DOT_NEXT_ALIAS = 'private-dot-next';
|
|
const ROOT_DIR_ALIAS = 'private-next-root-dir';
|
|
const APP_DIR_ALIAS = 'private-next-app-dir';
|
|
const RSC_MOD_REF_PROXY_ALIAS = 'private-next-rsc-mod-ref-proxy';
|
|
const RSC_ACTION_VALIDATE_ALIAS = 'private-next-rsc-action-validate';
|
|
const RSC_ACTION_PROXY_ALIAS = 'private-next-rsc-server-reference';
|
|
const RSC_CACHE_WRAPPER_ALIAS = 'private-next-rsc-cache-wrapper';
|
|
const RSC_DYNAMIC_IMPORT_WRAPPER_ALIAS = 'private-next-rsc-track-dynamic-import';
|
|
const RSC_ACTION_ENCRYPTION_ALIAS = 'private-next-rsc-action-encryption';
|
|
const RSC_ACTION_CLIENT_WRAPPER_ALIAS = 'private-next-rsc-action-client-wrapper';
|
|
const PUBLIC_DIR_MIDDLEWARE_CONFLICT = `You can not have a '_next' folder inside of your public folder. This conflicts with the internal '/_next' route. https://nextjs.org/docs/messages/public-next-folder-conflict`;
|
|
const SSG_GET_INITIAL_PROPS_CONFLICT = `You can not use getInitialProps with getStaticProps. To use SSG, please remove your getInitialProps`;
|
|
const SERVER_PROPS_GET_INIT_PROPS_CONFLICT = `You can not use getInitialProps with getServerSideProps. Please remove getInitialProps.`;
|
|
const SERVER_PROPS_SSG_CONFLICT = `You can not use getStaticProps or getStaticPaths with getServerSideProps. To use SSG, please remove getServerSideProps`;
|
|
const STATIC_STATUS_PAGE_GET_INITIAL_PROPS_ERROR = `can not have getInitialProps/getServerSideProps, https://nextjs.org/docs/messages/404-get-initial-props`;
|
|
const SERVER_PROPS_EXPORT_ERROR = `pages with \`getServerSideProps\` can not be exported. See more info here: https://nextjs.org/docs/messages/gssp-export`;
|
|
const GSP_NO_RETURNED_VALUE = 'Your `getStaticProps` function did not return an object. Did you forget to add a `return`?';
|
|
const GSSP_NO_RETURNED_VALUE = 'Your `getServerSideProps` function did not return an object. Did you forget to add a `return`?';
|
|
const UNSTABLE_REVALIDATE_RENAME_ERROR = 'The `unstable_revalidate` property is available for general use.\n' + 'Please use `revalidate` instead.';
|
|
const GSSP_COMPONENT_MEMBER_ERROR = `can not be attached to a page's component and must be exported from the page. See more info here: https://nextjs.org/docs/messages/gssp-component-member`;
|
|
const NON_STANDARD_NODE_ENV = `You are using a non-standard "NODE_ENV" value in your environment. This creates inconsistencies in the project and is strongly advised against. Read more: https://nextjs.org/docs/messages/non-standard-node-env`;
|
|
const SSG_FALLBACK_EXPORT_ERROR = `Pages with \`fallback\` enabled in \`getStaticPaths\` can not be exported. See more info here: https://nextjs.org/docs/messages/ssg-fallback-true-export`;
|
|
const ESLINT_DEFAULT_DIRS = [
|
|
'app',
|
|
'pages',
|
|
'components',
|
|
'lib',
|
|
'src'
|
|
];
|
|
const SERVER_RUNTIME = {
|
|
edge: 'edge',
|
|
experimentalEdge: 'experimental-edge',
|
|
nodejs: 'nodejs'
|
|
};
|
|
const WEB_SOCKET_MAX_RECONNECTIONS = 12;
|
|
/**
|
|
* The names of the webpack layers. These layers are the primitives for the
|
|
* webpack chunks.
|
|
*/ const WEBPACK_LAYERS_NAMES = {
|
|
/**
|
|
* The layer for the shared code between the client and server bundles.
|
|
*/ shared: 'shared',
|
|
/**
|
|
* The layer for server-only runtime and picking up `react-server` export conditions.
|
|
* Including app router RSC pages and app router custom routes and metadata routes.
|
|
*/ reactServerComponents: 'rsc',
|
|
/**
|
|
* Server Side Rendering layer for app (ssr).
|
|
*/ serverSideRendering: 'ssr',
|
|
/**
|
|
* The browser client bundle layer for actions.
|
|
*/ actionBrowser: 'action-browser',
|
|
/**
|
|
* The Node.js bundle layer for the API routes.
|
|
*/ apiNode: 'api-node',
|
|
/**
|
|
* The Edge Lite bundle layer for the API routes.
|
|
*/ apiEdge: 'api-edge',
|
|
/**
|
|
* The layer for the middleware code.
|
|
*/ middleware: 'middleware',
|
|
/**
|
|
* The layer for the instrumentation hooks.
|
|
*/ instrument: 'instrument',
|
|
/**
|
|
* The layer for assets on the edge.
|
|
*/ edgeAsset: 'edge-asset',
|
|
/**
|
|
* The browser client bundle layer for App directory.
|
|
*/ appPagesBrowser: 'app-pages-browser',
|
|
/**
|
|
* The browser client bundle layer for Pages directory.
|
|
*/ pagesDirBrowser: 'pages-dir-browser',
|
|
/**
|
|
* The Edge Lite bundle layer for Pages directory.
|
|
*/ pagesDirEdge: 'pages-dir-edge',
|
|
/**
|
|
* The Node.js bundle layer for Pages directory.
|
|
*/ pagesDirNode: 'pages-dir-node'
|
|
};
|
|
const WEBPACK_LAYERS = {
|
|
...WEBPACK_LAYERS_NAMES,
|
|
GROUP: {
|
|
builtinReact: [
|
|
WEBPACK_LAYERS_NAMES.reactServerComponents,
|
|
WEBPACK_LAYERS_NAMES.actionBrowser
|
|
],
|
|
serverOnly: [
|
|
WEBPACK_LAYERS_NAMES.reactServerComponents,
|
|
WEBPACK_LAYERS_NAMES.actionBrowser,
|
|
WEBPACK_LAYERS_NAMES.instrument,
|
|
WEBPACK_LAYERS_NAMES.middleware
|
|
],
|
|
neutralTarget: [
|
|
// pages api
|
|
WEBPACK_LAYERS_NAMES.apiNode,
|
|
WEBPACK_LAYERS_NAMES.apiEdge
|
|
],
|
|
clientOnly: [
|
|
WEBPACK_LAYERS_NAMES.serverSideRendering,
|
|
WEBPACK_LAYERS_NAMES.appPagesBrowser
|
|
],
|
|
bundled: [
|
|
WEBPACK_LAYERS_NAMES.reactServerComponents,
|
|
WEBPACK_LAYERS_NAMES.actionBrowser,
|
|
WEBPACK_LAYERS_NAMES.serverSideRendering,
|
|
WEBPACK_LAYERS_NAMES.appPagesBrowser,
|
|
WEBPACK_LAYERS_NAMES.shared,
|
|
WEBPACK_LAYERS_NAMES.instrument,
|
|
WEBPACK_LAYERS_NAMES.middleware
|
|
],
|
|
appPages: [
|
|
// app router pages and layouts
|
|
WEBPACK_LAYERS_NAMES.reactServerComponents,
|
|
WEBPACK_LAYERS_NAMES.serverSideRendering,
|
|
WEBPACK_LAYERS_NAMES.appPagesBrowser,
|
|
WEBPACK_LAYERS_NAMES.actionBrowser
|
|
]
|
|
}
|
|
};
|
|
const WEBPACK_RESOURCE_QUERIES = {
|
|
edgeSSREntry: '__next_edge_ssr_entry__',
|
|
metadata: '__next_metadata__',
|
|
metadataRoute: '__next_metadata_route__',
|
|
metadataImageMeta: '__next_metadata_image_meta__'
|
|
};
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/is-navigating-to-new-root-layout.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "isNavigatingToNewRootLayout", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return isNavigatingToNewRootLayout;
|
|
}
|
|
});
|
|
const _approutertypes = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/app-router-types.js [app-ssr] (ecmascript)");
|
|
function isNavigatingToNewRootLayout(currentTree, nextTree) {
|
|
// Compare segments
|
|
const currentTreeSegment = currentTree[0];
|
|
const nextTreeSegment = nextTree.segment;
|
|
// If any segment is different before we find the root layout, the root layout has changed.
|
|
// E.g. /same/(group1)/layout.js -> /same/(group2)/layout.js
|
|
// First segment is 'same' for both, keep looking. (group1) changed to (group2) before the root layout was found, it must have changed.
|
|
if (Array.isArray(currentTreeSegment) && Array.isArray(nextTreeSegment)) {
|
|
// Compare dynamic param name and type but ignore the value, different values would not affect the current root layout
|
|
// /[name] - /slug1 and /slug2, both values (slug1 & slug2) still has the same layout /[name]/layout.js
|
|
if (currentTreeSegment[0] !== nextTreeSegment[0] || currentTreeSegment[2] !== nextTreeSegment[2]) {
|
|
return true;
|
|
}
|
|
} else if (currentTreeSegment !== nextTreeSegment) {
|
|
return true;
|
|
}
|
|
// Current tree root layout found
|
|
const currentIsRootLayout = ((currentTree[4] ?? 0) & _approutertypes.PrefetchHint.IsRootLayout) !== 0;
|
|
const nextIsRootLayout = (nextTree.prefetchHints & _approutertypes.PrefetchHint.IsRootLayout) !== 0;
|
|
if (currentIsRootLayout) {
|
|
// If the next tree doesn't have the root layout flag, it must have changed.
|
|
return !nextIsRootLayout;
|
|
}
|
|
// Current tree didn't have its root layout here, must have changed.
|
|
if (nextIsRootLayout) {
|
|
return true;
|
|
}
|
|
const slots = nextTree.slots;
|
|
const currentTreeChildren = currentTree[1];
|
|
if (slots !== null) {
|
|
for(const slot in slots){
|
|
const nextTreeChild = slots[slot];
|
|
const currentTreeChild = currentTreeChildren[slot];
|
|
if (currentTreeChild === undefined || isNavigatingToNewRootLayout(currentTreeChild, nextTreeChild)) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/reducers/committed-state.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
getLastCommittedTree: null,
|
|
setLastCommittedTree: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
getLastCommittedTree: function() {
|
|
return getLastCommittedTree;
|
|
},
|
|
setLastCommittedTree: function() {
|
|
return setLastCommittedTree;
|
|
}
|
|
});
|
|
// The tree from the last state that was committed to the browser history
|
|
// (i.e., the last state for which HistoryUpdater's useInsertionEffect ran).
|
|
// This lets the server-patch reducer distinguish between retrying a
|
|
// navigation that already pushed a history entry vs one whose transition
|
|
// suspended and never committed.
|
|
//
|
|
// Currently only used by the server-patch retry logic, but this module is a
|
|
// stepping stone toward a broader refactor of the navigation queue. The
|
|
// existing AppRouter action queue will eventually be replaced by a more
|
|
// reactive model that explicitly tracks pending vs committed navigation
|
|
// state. This file will likely evolve into (or be subsumed by) that new
|
|
// implementation.
|
|
let lastCommittedTree = null;
|
|
function getLastCommittedTree() {
|
|
return lastCommittedTree;
|
|
}
|
|
function setLastCommittedTree(tree) {
|
|
lastCommittedTree = tree;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/lru.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
cleanup: null,
|
|
deleteFromLru: null,
|
|
lruPut: null,
|
|
updateLruSize: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
cleanup: function() {
|
|
return cleanup;
|
|
},
|
|
deleteFromLru: function() {
|
|
return deleteFromLru;
|
|
},
|
|
lruPut: function() {
|
|
return lruPut;
|
|
},
|
|
updateLruSize: function() {
|
|
return updateLruSize;
|
|
}
|
|
});
|
|
const _cachemap = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-map.js [app-ssr] (ecmascript)");
|
|
const _scheduler = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/scheduler.js [app-ssr] (ecmascript)");
|
|
// We use an LRU for memory management. We must update this whenever we add or
|
|
// remove a new cache entry, or when an entry changes size.
|
|
let head = null;
|
|
let lruSize = 0;
|
|
// TODO: I chose the max size somewhat arbitrarily. Consider setting this based
|
|
// on navigator.deviceMemory, or some other heuristic. We should make this
|
|
// customizable via the Next.js config, too.
|
|
const maxLruSize = 50 * 1024 * 1024 // 50 MB
|
|
;
|
|
function lruPut(node) {
|
|
if (head === node) {
|
|
// Already at the head
|
|
return;
|
|
}
|
|
const prev = node.prev;
|
|
const next = node.next;
|
|
if (next === null || prev === null) {
|
|
// This is an insertion
|
|
lruSize += node.size;
|
|
// Whenever we add an entry, we need to check if we've exceeded the
|
|
// max size. We don't evict entries immediately; they're evicted later in
|
|
// an asynchronous task.
|
|
ensureCleanupIsScheduled();
|
|
} else {
|
|
// This is a move. Remove from its current position.
|
|
prev.next = next;
|
|
next.prev = prev;
|
|
}
|
|
// Move to the front of the list
|
|
if (head === null) {
|
|
// This is the first entry
|
|
node.prev = node;
|
|
node.next = node;
|
|
} else {
|
|
// Add to the front of the list
|
|
const tail = head.prev;
|
|
node.prev = tail;
|
|
// In practice, this is never null, but that isn't encoded in the type
|
|
if (tail !== null) {
|
|
tail.next = node;
|
|
}
|
|
node.next = head;
|
|
head.prev = node;
|
|
}
|
|
head = node;
|
|
}
|
|
function updateLruSize(node, newNodeSize) {
|
|
// This is a separate function from `put` so that we can resize the entry
|
|
// regardless of whether it's currently being tracked by the LRU.
|
|
const prevNodeSize = node.size;
|
|
node.size = newNodeSize;
|
|
if (node.next === null) {
|
|
// This entry is not currently being tracked by the LRU.
|
|
return;
|
|
}
|
|
// Update the total LRU size
|
|
lruSize = lruSize - prevNodeSize + newNodeSize;
|
|
ensureCleanupIsScheduled();
|
|
}
|
|
function deleteFromLru(deleted) {
|
|
const next = deleted.next;
|
|
const prev = deleted.prev;
|
|
if (next !== null && prev !== null) {
|
|
lruSize -= deleted.size;
|
|
deleted.next = null;
|
|
deleted.prev = null;
|
|
// Remove from the list
|
|
if (head === deleted) {
|
|
// Update the head
|
|
if (next === head) {
|
|
// This was the last entry
|
|
head = null;
|
|
} else {
|
|
head = next;
|
|
prev.next = next;
|
|
next.prev = prev;
|
|
}
|
|
} else {
|
|
prev.next = next;
|
|
next.prev = prev;
|
|
}
|
|
} else {
|
|
// Already deleted
|
|
}
|
|
}
|
|
function ensureCleanupIsScheduled() {
|
|
if (lruSize <= maxLruSize) {
|
|
return;
|
|
}
|
|
// To schedule cleanup, ping the prefetch scheduler. At the end of its work
|
|
// loop, once there are no queued tasks and no in-progress requests, it will
|
|
// call cleanup().
|
|
(0, _scheduler.pingPrefetchScheduler)();
|
|
}
|
|
function cleanup() {
|
|
if (lruSize <= maxLruSize) {
|
|
return;
|
|
}
|
|
// Evict entries until we're at 90% capacity. We can assume this won't
|
|
// infinite loop because even if `maxLruSize` were 0, eventually
|
|
// `deleteFromLru` sets `head` to `null` when we run out entries.
|
|
const ninetyPercentMax = maxLruSize * 0.9;
|
|
while(lruSize > ninetyPercentMax && head !== null){
|
|
const tail = head.prev;
|
|
// In practice, this is never null, but that isn't encoded in the type
|
|
if (tail !== null) {
|
|
// Delete the entry from the map. In turn, this will remove it from
|
|
// the LRU.
|
|
(0, _cachemap.deleteMapEntry)(tail);
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/cache-map.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
Fallback: null,
|
|
createCacheMap: null,
|
|
deleteFromCacheMap: null,
|
|
deleteMapEntry: null,
|
|
getFromCacheMap: null,
|
|
isValueExpired: null,
|
|
setInCacheMap: null,
|
|
setSizeInCacheMap: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
Fallback: function() {
|
|
return Fallback;
|
|
},
|
|
createCacheMap: function() {
|
|
return createCacheMap;
|
|
},
|
|
deleteFromCacheMap: function() {
|
|
return deleteFromCacheMap;
|
|
},
|
|
deleteMapEntry: function() {
|
|
return deleteMapEntry;
|
|
},
|
|
getFromCacheMap: function() {
|
|
return getFromCacheMap;
|
|
},
|
|
isValueExpired: function() {
|
|
return isValueExpired;
|
|
},
|
|
setInCacheMap: function() {
|
|
return setInCacheMap;
|
|
},
|
|
setSizeInCacheMap: function() {
|
|
return setSizeInCacheMap;
|
|
}
|
|
});
|
|
const _lru = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/lru.js [app-ssr] (ecmascript)");
|
|
const Fallback = {};
|
|
// This is a special internal key that is used for "revalidation" entries. It's
|
|
// an implementation detail that shouldn't leak outside of this module.
|
|
const Revalidation = {};
|
|
function createCacheMap() {
|
|
const cacheMap = {
|
|
parent: null,
|
|
key: null,
|
|
value: null,
|
|
map: null,
|
|
// LRU-related fields
|
|
prev: null,
|
|
next: null,
|
|
size: 0
|
|
};
|
|
return cacheMap;
|
|
}
|
|
function getOrInitialize(cacheMap, keys, isRevalidation) {
|
|
// Go through each level of keys until we find the entry that matches, or
|
|
// create a new entry if one doesn't exist.
|
|
//
|
|
// This function will only return entries that match the keypath _exactly_.
|
|
// Unlike getWithFallback, it will not access fallback entries unless it's
|
|
// explicitly part of the keypath.
|
|
let entry = cacheMap;
|
|
let remainingKeys = keys;
|
|
let key = null;
|
|
while(true){
|
|
const previousKey = key;
|
|
if (remainingKeys !== null) {
|
|
key = remainingKeys.value;
|
|
remainingKeys = remainingKeys.parent;
|
|
} else if (isRevalidation && previousKey !== Revalidation) {
|
|
// During a revalidation, we append an internal "Revalidation" key to
|
|
// the end of the keypath. The "normal" entry is its parent.
|
|
// However, if the parent entry is currently empty, we don't need to store
|
|
// this as a revalidation entry. Just insert the revalidation into the
|
|
// normal slot.
|
|
if (entry.value === null) {
|
|
return entry;
|
|
}
|
|
// Otheriwse, create a child entry.
|
|
key = Revalidation;
|
|
} else {
|
|
break;
|
|
}
|
|
let map = entry.map;
|
|
if (map !== null) {
|
|
const existingEntry = map.get(key);
|
|
if (existingEntry !== undefined) {
|
|
// Found a match. Keep going.
|
|
entry = existingEntry;
|
|
continue;
|
|
}
|
|
} else {
|
|
map = new Map();
|
|
entry.map = map;
|
|
}
|
|
// No entry exists yet at this level. Create a new one.
|
|
const newEntry = {
|
|
parent: entry,
|
|
key,
|
|
value: null,
|
|
map: null,
|
|
// LRU-related fields
|
|
prev: null,
|
|
next: null,
|
|
size: 0
|
|
};
|
|
map.set(key, newEntry);
|
|
entry = newEntry;
|
|
}
|
|
return entry;
|
|
}
|
|
function getFromCacheMap(now, currentCacheVersion, rootEntry, keys, isRevalidation) {
|
|
const entry = getEntryWithFallbackImpl(now, currentCacheVersion, rootEntry, keys, isRevalidation, 0);
|
|
if (entry === null || entry.value === null) {
|
|
return null;
|
|
}
|
|
// This is an LRU access. Move the entry to the front of the list.
|
|
(0, _lru.lruPut)(entry);
|
|
return entry.value;
|
|
}
|
|
function isValueExpired(now, currentCacheVersion, value) {
|
|
return value.staleAt <= now || value.version < currentCacheVersion;
|
|
}
|
|
function lazilyEvictIfNeeded(now, currentCacheVersion, entry) {
|
|
// We have a matching entry, but before we can return it, we need to check if
|
|
// it's still fresh. Otherwise it should be treated the same as a cache miss.
|
|
if (entry.value === null) {
|
|
// This entry has no value, so there's nothing to evict.
|
|
return entry;
|
|
}
|
|
const value = entry.value;
|
|
if (isValueExpired(now, currentCacheVersion, value)) {
|
|
// The value expired. Lazily evict it from the cache, and return null. This
|
|
// is conceptually the same as a cache miss.
|
|
deleteMapEntry(entry);
|
|
return null;
|
|
}
|
|
// The matched entry has not expired. Return it.
|
|
return entry;
|
|
}
|
|
function getEntryWithFallbackImpl(now, currentCacheVersion, entry, keys, isRevalidation, previousKey) {
|
|
// This is similar to getExactEntry, but if an exact match is not found for
|
|
// a key, it will return the fallback entry instead. This is recursive at
|
|
// every level, e.g. an entry with keypath [a, Fallback, c, Fallback] is
|
|
// valid match for [a, b, c, d].
|
|
//
|
|
// It will return the most specific match available.
|
|
let key;
|
|
let remainingKeys;
|
|
if (keys !== null) {
|
|
key = keys.value;
|
|
remainingKeys = keys.parent;
|
|
} else if (isRevalidation && previousKey !== Revalidation) {
|
|
// During a revalidation, we append an internal "Revalidation" key to
|
|
// the end of the keypath.
|
|
key = Revalidation;
|
|
remainingKeys = null;
|
|
} else {
|
|
// There are no more keys. This is the terminal entry.
|
|
// TODO: When performing a lookup during a navigation, as opposed to a
|
|
// prefetch, we may want to skip entries that are Pending if there's also
|
|
// a Fulfilled fallback entry. Tricky to say, though, since if it's
|
|
// already pending, it's likely to stream in soon. Maybe we could do this
|
|
// just on slow connections and offline mode.
|
|
return lazilyEvictIfNeeded(now, currentCacheVersion, entry);
|
|
}
|
|
const map = entry.map;
|
|
if (map !== null) {
|
|
const existingEntry = map.get(key);
|
|
if (existingEntry !== undefined) {
|
|
// Found an exact match for this key. Keep searching.
|
|
const result = getEntryWithFallbackImpl(now, currentCacheVersion, existingEntry, remainingKeys, isRevalidation, key);
|
|
if (result !== null) {
|
|
return result;
|
|
}
|
|
}
|
|
// No match found for this key. Check if there's a fallback.
|
|
const fallbackEntry = map.get(Fallback);
|
|
if (fallbackEntry !== undefined) {
|
|
// Found a fallback for this key. Keep searching.
|
|
return getEntryWithFallbackImpl(now, currentCacheVersion, fallbackEntry, remainingKeys, isRevalidation, key);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function setInCacheMap(cacheMap, keys, value, isRevalidation) {
|
|
// Add a value to the map at the given keypath. If the value is already
|
|
// part of the map, it's removed from its previous keypath. (NOTE: This is
|
|
// unlike a regular JS map, but the behavior is intentional.)
|
|
const entry = getOrInitialize(cacheMap, keys, isRevalidation);
|
|
setMapEntryValue(entry, value);
|
|
// This is an LRU access. Move the entry to the front of the list.
|
|
(0, _lru.lruPut)(entry);
|
|
(0, _lru.updateLruSize)(entry, value.size);
|
|
}
|
|
function setMapEntryValue(entry, value) {
|
|
if (entry.value !== null) {
|
|
// There's already a value at the given keypath. Disconnect the old value
|
|
// from the map. We're not calling `deleteMapEntry` here because the
|
|
// entry itself is still in the map. We just want to overwrite its value.
|
|
dropRef(entry.value);
|
|
entry.value = null;
|
|
}
|
|
// This value may already be in the map at a different keypath.
|
|
// Grab a reference before we overwrite it.
|
|
const oldEntry = value.ref;
|
|
entry.value = value;
|
|
value.ref = entry;
|
|
(0, _lru.updateLruSize)(entry, value.size);
|
|
if (oldEntry !== null && oldEntry !== entry && oldEntry.value === value) {
|
|
// This value is already in the map at a different keypath in the map.
|
|
// Values only exist at a single keypath at a time. Remove it from the
|
|
// previous keypath.
|
|
//
|
|
// Note that only the internal map entry is garbage collected; we don't
|
|
// call `dropRef` here because it's still in the map, just
|
|
// at a new keypath (the one we just set, above).
|
|
deleteMapEntry(oldEntry);
|
|
}
|
|
}
|
|
function deleteFromCacheMap(value) {
|
|
const entry = value.ref;
|
|
if (entry === null) {
|
|
// This value is not a member of any map.
|
|
return;
|
|
}
|
|
dropRef(value);
|
|
deleteMapEntry(entry);
|
|
}
|
|
function dropRef(value) {
|
|
// Drop the value from the map by setting its `ref` backpointer to
|
|
// null. This is a separate operation from `deleteMapEntry` because when
|
|
// re-keying a value we need to be able to delete the old, internal map
|
|
// entry without garbage collecting the value itself.
|
|
value.ref = null;
|
|
}
|
|
function deleteMapEntry(entry) {
|
|
// Delete the entry from the cache.
|
|
entry.value = null;
|
|
(0, _lru.deleteFromLru)(entry);
|
|
// Check if we can garbage collect the entry.
|
|
const map = entry.map;
|
|
if (map === null) {
|
|
// Since this entry has no value, and also no child entries, we can
|
|
// garbage collect it. Remove it from its parent, and keep garbage
|
|
// collecting the parents until we reach a non-empty entry.
|
|
let parent = entry.parent;
|
|
let key = entry.key;
|
|
while(parent !== null){
|
|
const parentMap = parent.map;
|
|
if (parentMap !== null) {
|
|
parentMap.delete(key);
|
|
if (parentMap.size === 0) {
|
|
// We just removed the last entry in the parent map.
|
|
parent.map = null;
|
|
if (parent.value === null) {
|
|
// The parent node has no child entries, nor does it have a value
|
|
// on itself. It can be garbage collected. Keep going.
|
|
key = parent.key;
|
|
parent = parent.parent;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
} else {
|
|
// Check if there's a revalidating entry. If so, promote it to a
|
|
// "normal" entry, since the normal one was just deleted.
|
|
const revalidatingEntry = map.get(Revalidation);
|
|
if (revalidatingEntry !== undefined && revalidatingEntry.value !== null) {
|
|
setMapEntryValue(entry, revalidatingEntry.value);
|
|
}
|
|
}
|
|
}
|
|
function setSizeInCacheMap(value, size) {
|
|
const entry = value.ref;
|
|
if (entry === null) {
|
|
// This value is not a member of any map.
|
|
return;
|
|
}
|
|
// Except during initialization (when the size is set to 0), this is the only
|
|
// place the `size` field should be updated, to ensure it's in sync with the
|
|
// the LRU.
|
|
value.size = size;
|
|
(0, _lru.updateLruSize)(entry, size);
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/vary-path.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
appendLayoutVaryPath: null,
|
|
clonePageVaryPathWithNewSearchParams: null,
|
|
finalizeLayoutVaryPath: null,
|
|
finalizeMetadataVaryPath: null,
|
|
finalizePageVaryPath: null,
|
|
getFulfilledRouteVaryPath: null,
|
|
getFulfilledSegmentVaryPath: null,
|
|
getPartialLayoutVaryPath: null,
|
|
getPartialPageVaryPath: null,
|
|
getRenderedSearchFromVaryPath: null,
|
|
getRouteVaryPath: null,
|
|
getSegmentVaryPathForRequest: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
appendLayoutVaryPath: function() {
|
|
return appendLayoutVaryPath;
|
|
},
|
|
clonePageVaryPathWithNewSearchParams: function() {
|
|
return clonePageVaryPathWithNewSearchParams;
|
|
},
|
|
finalizeLayoutVaryPath: function() {
|
|
return finalizeLayoutVaryPath;
|
|
},
|
|
finalizeMetadataVaryPath: function() {
|
|
return finalizeMetadataVaryPath;
|
|
},
|
|
finalizePageVaryPath: function() {
|
|
return finalizePageVaryPath;
|
|
},
|
|
getFulfilledRouteVaryPath: function() {
|
|
return getFulfilledRouteVaryPath;
|
|
},
|
|
getFulfilledSegmentVaryPath: function() {
|
|
return getFulfilledSegmentVaryPath;
|
|
},
|
|
getPartialLayoutVaryPath: function() {
|
|
return getPartialLayoutVaryPath;
|
|
},
|
|
getPartialPageVaryPath: function() {
|
|
return getPartialPageVaryPath;
|
|
},
|
|
getRenderedSearchFromVaryPath: function() {
|
|
return getRenderedSearchFromVaryPath;
|
|
},
|
|
getRouteVaryPath: function() {
|
|
return getRouteVaryPath;
|
|
},
|
|
getSegmentVaryPathForRequest: function() {
|
|
return getSegmentVaryPathForRequest;
|
|
}
|
|
});
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _cachemap = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-map.js [app-ssr] (ecmascript)");
|
|
const _segmentvalueencoding = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.js [app-ssr] (ecmascript)");
|
|
function getRouteVaryPath(pathname, search, nextUrl) {
|
|
// requestKey -> searchParams -> nextUrl
|
|
const varyPath = {
|
|
id: null,
|
|
value: pathname,
|
|
parent: {
|
|
id: '?',
|
|
value: search,
|
|
parent: {
|
|
id: null,
|
|
value: nextUrl,
|
|
parent: null
|
|
}
|
|
}
|
|
};
|
|
return varyPath;
|
|
}
|
|
function getFulfilledRouteVaryPath(pathname, search, nextUrl, couldBeIntercepted) {
|
|
// This is called when a route's data is fulfilled. The cache entry will be
|
|
// re-keyed based on which inputs the response varies by.
|
|
// requestKey -> searchParams -> nextUrl
|
|
const varyPath = {
|
|
id: null,
|
|
value: pathname,
|
|
parent: {
|
|
id: '?',
|
|
value: search,
|
|
parent: {
|
|
id: null,
|
|
value: couldBeIntercepted ? nextUrl : _cachemap.Fallback,
|
|
parent: null
|
|
}
|
|
}
|
|
};
|
|
return varyPath;
|
|
}
|
|
function appendLayoutVaryPath(parentPath, cacheKey, paramName) {
|
|
const varyPathPart = {
|
|
id: paramName,
|
|
value: cacheKey,
|
|
parent: parentPath
|
|
};
|
|
return varyPathPart;
|
|
}
|
|
function finalizeLayoutVaryPath(requestKey, varyPath) {
|
|
const layoutVaryPath = {
|
|
id: null,
|
|
value: requestKey,
|
|
parent: varyPath
|
|
};
|
|
return layoutVaryPath;
|
|
}
|
|
function getPartialLayoutVaryPath(finalizedVaryPath) {
|
|
// This is the inverse of finalizeLayoutVaryPath.
|
|
return finalizedVaryPath.parent;
|
|
}
|
|
function finalizePageVaryPath(requestKey, renderedSearch, varyPath) {
|
|
// Unlike layouts, a page segment's vary path also includes the search string.
|
|
// requestKey -> searchParams -> pathParams
|
|
const pageVaryPath = {
|
|
id: null,
|
|
value: requestKey,
|
|
parent: {
|
|
id: '?',
|
|
value: renderedSearch,
|
|
parent: varyPath
|
|
}
|
|
};
|
|
return pageVaryPath;
|
|
}
|
|
function getPartialPageVaryPath(finalizedVaryPath) {
|
|
// This is the inverse of finalizePageVaryPath.
|
|
return finalizedVaryPath.parent.parent;
|
|
}
|
|
function finalizeMetadataVaryPath(pageRequestKey, renderedSearch, varyPath) {
|
|
// The metadata "segment" is not a real segment because it doesn't exist in
|
|
// the normal structure of the route tree, but in terms of caching, it
|
|
// behaves like a page segment because it varies by all the same params as
|
|
// a page.
|
|
//
|
|
// To keep the protocol for querying the server simple, the request key for
|
|
// the metadata does not include any path information. It's unnecessary from
|
|
// the server's perspective, because unlike page segments, there's only one
|
|
// metadata response per URL, i.e. there's no need to distinguish multiple
|
|
// parallel pages.
|
|
//
|
|
// However, this means the metadata request key is insufficient for
|
|
// caching the the metadata in the client cache, because on the client we
|
|
// use the request key to distinguish the metadata entry from all other
|
|
// page's metadata entries.
|
|
//
|
|
// So instead we create a simulated request key based on the page segment.
|
|
// Conceptually this is equivalent to the request key the server would have
|
|
// assigned the metadata segment if it treated it as part of the actual
|
|
// route structure.
|
|
// If there are multiple parallel pages, we use whichever is the first one.
|
|
// This is fine because the only difference between request keys for
|
|
// different parallel pages are things like route groups and parallel
|
|
// route slots. As long as it's always the same one, it doesn't matter.
|
|
const pageVaryPath = {
|
|
id: null,
|
|
// Append the actual metadata request key to the page request key. Note
|
|
// that we're not using a separate vary path part; it's unnecessary because
|
|
// these are not conceptually separate inputs.
|
|
value: pageRequestKey + _segmentvalueencoding.HEAD_REQUEST_KEY,
|
|
parent: {
|
|
id: '?',
|
|
value: renderedSearch,
|
|
parent: varyPath
|
|
}
|
|
};
|
|
return pageVaryPath;
|
|
}
|
|
function getSegmentVaryPathForRequest(fetchStrategy, tree) {
|
|
// This is used for storing pending requests in the cache. We want to choose
|
|
// the most generic vary path based on the strategy used to fetch it, i.e.
|
|
// static/PPR versus runtime prefetching, so that it can be reused as much
|
|
// as possible.
|
|
//
|
|
// We may be able to re-key the response to something even more generic once
|
|
// we receive it — for example, if the server tells us that the response
|
|
// doesn't vary on a particular param — but even before we send the request,
|
|
// we know some params are reusable based on the fetch strategy alone. For
|
|
// example, a static prefetch will never vary on search params.
|
|
//
|
|
// The original vary path with all the params filled in is stored on the
|
|
// route tree object. We will clone this one to create a new vary path
|
|
// where certain params are replaced with Fallback.
|
|
//
|
|
// This result of this function is not stored anywhere. It's only used to
|
|
// access the cache a single time.
|
|
//
|
|
// TODO: Rather than create a new list object just to access the cache, the
|
|
// plan is to add the concept of a "vary mask". This will represent all the
|
|
// params that can be treated as Fallback. (Or perhaps the inverse.)
|
|
const originalVaryPath = tree.varyPath;
|
|
// Only page segments (and the special "metadata" segment, which is treated
|
|
// like a page segment for the purposes of caching) may contain search
|
|
// params. There's no reason to include them in the vary path otherwise.
|
|
if (tree.isPage) {
|
|
// Only a runtime prefetch will include search params in the vary path.
|
|
// Static prefetches never include search params, so they can be reused
|
|
// across all possible search param values.
|
|
const doesVaryOnSearchParams = fetchStrategy === _types.FetchStrategy.Full || fetchStrategy === _types.FetchStrategy.PPRRuntime;
|
|
if (!doesVaryOnSearchParams) {
|
|
// The response from the the server will not vary on search params. Clone
|
|
// the end of the original vary path to replace the search params
|
|
// with Fallback.
|
|
//
|
|
// requestKey -> searchParams -> pathParams
|
|
// ^ This part gets replaced with Fallback
|
|
const searchParamsVaryPath = originalVaryPath.parent;
|
|
const pathParamsVaryPath = searchParamsVaryPath.parent;
|
|
const patchedVaryPath = {
|
|
id: null,
|
|
value: originalVaryPath.value,
|
|
parent: {
|
|
id: '?',
|
|
value: _cachemap.Fallback,
|
|
parent: pathParamsVaryPath
|
|
}
|
|
};
|
|
return patchedVaryPath;
|
|
}
|
|
}
|
|
// The request does vary on search params. We don't need to modify anything.
|
|
return originalVaryPath;
|
|
}
|
|
function clonePageVaryPathWithNewSearchParams(originalVaryPath, newSearch) {
|
|
// requestKey -> searchParams -> pathParams
|
|
// ^ This part gets replaced with newSearch
|
|
const searchParamsVaryPath = originalVaryPath.parent;
|
|
const clonedVaryPath = {
|
|
id: null,
|
|
value: originalVaryPath.value,
|
|
parent: {
|
|
id: '?',
|
|
value: newSearch,
|
|
parent: searchParamsVaryPath.parent
|
|
}
|
|
};
|
|
return clonedVaryPath;
|
|
}
|
|
function getRenderedSearchFromVaryPath(varyPath) {
|
|
const searchParams = varyPath.parent.value;
|
|
return typeof searchParams === 'string' ? searchParams : null;
|
|
}
|
|
function getFulfilledSegmentVaryPath(original, varyParams) {
|
|
// Re-keys a segment's vary path based on which params the segment actually
|
|
// depends on. Params that are NOT in the varyParams set are replaced with
|
|
// Fallback, allowing the cache entry to be reused across different values of
|
|
// those params.
|
|
// This is called when a segment is fulfilled with data from the server. The
|
|
// varyParams set comes from the server and indicates which params were
|
|
// accessed during rendering.
|
|
const clone = {
|
|
id: original.id,
|
|
// If the id is null, this node is not a param (e.g., it's a request key).
|
|
// If the id is in the varyParams set, keep the original value.
|
|
// Otherwise, replace with Fallback to make it reusable.
|
|
value: original.id === null || varyParams.has(original.id) ? original.value : _cachemap.Fallback,
|
|
parent: original.parent === null ? null : getFulfilledSegmentVaryPath(original.parent, varyParams)
|
|
};
|
|
return clone;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/optimistic-routes.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Optimistic Routing (Known Routes)
|
|
*
|
|
* This module enables the client to predict route structure for URLs that
|
|
* haven't been prefetched yet, based on previously learned route patterns.
|
|
* When successful, this allows skipping the route tree prefetch request
|
|
* entirely.
|
|
*
|
|
* The core idea is that many URLs map to the same route structure. For example,
|
|
* /blog/post-1 and /blog/post-2 both resolve to /blog/[slug]. Once we've
|
|
* prefetched one, we can predict the structure of the other.
|
|
*
|
|
* However, we can't always make this prediction. Static siblings (like
|
|
* /blog/featured alongside /blog/[slug]) have different route structures.
|
|
* When we learn a dynamic route, we also learn its static siblings so we
|
|
* know when NOT to apply the prediction.
|
|
*
|
|
* Main entry points:
|
|
*
|
|
* 1. discoverKnownRoute: Called after receiving a route tree from the server.
|
|
* Traverses the route tree, compares URL parts to segments, and populates
|
|
* the known route tree if they match. Routes are always inserted into the
|
|
* cache.
|
|
*
|
|
* 2. matchKnownRoute: Called when looking up a route with no cache entry.
|
|
* Matches the candidate URL against learned patterns. Returns a synthetic
|
|
* cache entry if successful, or null to fall back to server resolution.
|
|
*
|
|
* Rewrite detection happens during traversal: if a URL path part doesn't match
|
|
* the corresponding route segment, we stop populating the known route tree
|
|
* (since the mapping is incorrect) but still insert the route into the cache.
|
|
*
|
|
* The known route tree is append-only with no eviction. Route patterns are
|
|
* derived from the filesystem, so they don't become stale within a session.
|
|
* Cache invalidation on deploy clears everything anyway.
|
|
*
|
|
* Current limitations (deopt to server resolution):
|
|
* - Rewrites: Detected during traversal (tree not populated, but route cached)
|
|
* - Intercepted routes: The route tree varies by referrer (Next-Url header),
|
|
* so we can't predict the correct structure from the URL alone. Patterns are
|
|
* still stored during discovery (so the trie stays populated for non-
|
|
* intercepted siblings), but matching bails out when the pattern is marked
|
|
* as interceptable.
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
discoverKnownRoute: null,
|
|
matchKnownRoute: null,
|
|
resetKnownRoutes: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
discoverKnownRoute: function() {
|
|
return discoverKnownRoute;
|
|
},
|
|
matchKnownRoute: function() {
|
|
return matchKnownRoute;
|
|
},
|
|
resetKnownRoutes: function() {
|
|
return resetKnownRoutes;
|
|
}
|
|
});
|
|
const _cache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)");
|
|
const _routeparams = __turbopack_context__.r("[project]/node_modules/next/dist/client/route-params.js [app-ssr] (ecmascript)");
|
|
const _varypath = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/vary-path.js [app-ssr] (ecmascript)");
|
|
function createEmptyPart() {
|
|
return {
|
|
staticChildren: null,
|
|
dynamicChild: null,
|
|
dynamicChildParamName: null,
|
|
dynamicChildParamType: null,
|
|
pattern: null
|
|
};
|
|
}
|
|
// The root of the known route tree.
|
|
let knownRouteTreeRoot = createEmptyPart();
|
|
function discoverKnownRoute(now, pathname, nextUrl, pendingEntry, routeTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching, hasDynamicRewrite) {
|
|
const tree = routeTree;
|
|
const pathnameParts = pathname.split('/').filter((p)=>p !== '');
|
|
const firstPart = pathnameParts.length > 0 ? pathnameParts[0] : null;
|
|
const remainingParts = pathnameParts.length > 0 ? pathnameParts.slice(1) : [];
|
|
if (pendingEntry !== null) {
|
|
// Fulfill the pending entry first
|
|
const fulfilledEntry = (0, _cache.fulfillRouteCacheEntry)(now, pendingEntry, tree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching);
|
|
if (hasDynamicRewrite) {
|
|
fulfilledEntry.hasDynamicRewrite = true;
|
|
}
|
|
// Populate the known route tree (handles rewrite detection internally).
|
|
// The entry is already in the cache; this just stores it as a pattern
|
|
// if the URL matches the route structure.
|
|
discoverKnownRoutePart(knownRouteTreeRoot, tree, firstPart, remainingParts, fulfilledEntry, now, pathname, nextUrl, tree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching, hasDynamicRewrite);
|
|
return fulfilledEntry;
|
|
}
|
|
// No pending entry - discoverKnownRoutePart will create one and insert it
|
|
// into the cache, or return an existing pattern if one exists.
|
|
return discoverKnownRoutePart(knownRouteTreeRoot, tree, firstPart, remainingParts, null, now, pathname, nextUrl, tree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching, hasDynamicRewrite);
|
|
}
|
|
/**
|
|
* Gets or creates the dynamic child node for a KnownRoutePart.
|
|
* A node can have at most one dynamic child (you can't have both [slug] and
|
|
* [id] at the same route level), so we either return existing or create new.
|
|
*/ function discoverDynamicChild(part, paramName, paramType) {
|
|
if (part.dynamicChild !== null) {
|
|
return part.dynamicChild;
|
|
}
|
|
const newChild = createEmptyPart();
|
|
// Type assertion needed because we're converting from "without" to "with"
|
|
// dynamic child variant.
|
|
const mutablePart = part;
|
|
mutablePart.dynamicChild = newChild;
|
|
mutablePart.dynamicChildParamName = paramName;
|
|
mutablePart.dynamicChildParamType = paramType;
|
|
return newChild;
|
|
}
|
|
/**
|
|
* Recursive workhorse for discoverKnownRoute.
|
|
*
|
|
* Walks the route tree and URL parts in parallel, building out the known
|
|
* route tree as it goes. At each step:
|
|
* 1. Determines if the current segment appears in the URL (dynamic/static)
|
|
* 2. Validates URL matches route structure (detects rewrites)
|
|
* 3. Creates/updates the corresponding KnownRoutePart node
|
|
* 4. Records static siblings for future matching
|
|
* 5. Recurses into child slots (parallel routes)
|
|
*
|
|
* If a URL/route mismatch is detected (rewrite), we stop building the known
|
|
* route tree but still cache the route entry for direct lookup.
|
|
*/ function discoverKnownRoutePart(parentKnownRoutePart, routeTree, urlPart, remainingParts, existingEntry, now, pathname, nextUrl, fullTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching, hasDynamicRewrite) {
|
|
const segment = routeTree.segment;
|
|
let segmentAppearsInURL;
|
|
let paramName = null;
|
|
let paramType = null;
|
|
let staticSiblings = null;
|
|
if (typeof segment === 'string') {
|
|
segmentAppearsInURL = (0, _routeparams.doesStaticSegmentAppearInURL)(segment);
|
|
} else {
|
|
// Dynamic segment tuple: [paramName, paramCacheKey, paramType, staticSiblings]
|
|
paramName = segment[0];
|
|
paramType = segment[2];
|
|
staticSiblings = segment[3];
|
|
segmentAppearsInURL = true;
|
|
}
|
|
let knownRoutePart = parentKnownRoutePart;
|
|
let nextUrlPart = urlPart;
|
|
let nextRemainingParts = remainingParts;
|
|
if (segmentAppearsInURL) {
|
|
// Check for mismatch: if this is a static segment, the URL part must match
|
|
if (paramName === null && urlPart !== segment) {
|
|
// URL doesn't match route structure (likely a rewrite).
|
|
// Don't populate the known route tree, just write the route into the
|
|
// cache and return immediately.
|
|
if (existingEntry !== null) {
|
|
return existingEntry;
|
|
}
|
|
return (0, _cache.writeRouteIntoCache)(now, pathname, nextUrl, fullTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching);
|
|
}
|
|
// URL matches route structure. Build the known route tree.
|
|
if (paramName !== null && paramType !== null) {
|
|
// Dynamic segment
|
|
knownRoutePart = discoverDynamicChild(parentKnownRoutePart, paramName, paramType);
|
|
// Record static siblings as placeholder parts.
|
|
// IMPORTANT: We use the null vs Map distinction to track whether
|
|
// siblings are known at this level:
|
|
// - staticChildren: null = siblings unknown (can't safely match dynamic)
|
|
// - staticChildren: Map = siblings known (even if empty)
|
|
// This matters in dev mode where webpack may not know all siblings yet.
|
|
if (staticSiblings !== null) {
|
|
// Siblings are known - ensure we have a Map (even if empty)
|
|
if (parentKnownRoutePart.staticChildren === null) {
|
|
parentKnownRoutePart.staticChildren = new Map();
|
|
}
|
|
for (const sibling of staticSiblings){
|
|
if (!parentKnownRoutePart.staticChildren.has(sibling)) {
|
|
parentKnownRoutePart.staticChildren.set(sibling, createEmptyPart());
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// Static segment
|
|
if (parentKnownRoutePart.staticChildren === null) {
|
|
parentKnownRoutePart.staticChildren = new Map();
|
|
}
|
|
let existingChild = parentKnownRoutePart.staticChildren.get(urlPart);
|
|
if (existingChild === undefined) {
|
|
existingChild = createEmptyPart();
|
|
parentKnownRoutePart.staticChildren.set(urlPart, existingChild);
|
|
}
|
|
knownRoutePart = existingChild;
|
|
}
|
|
// Advance to next URL part
|
|
nextUrlPart = remainingParts.length > 0 ? remainingParts[0] : null;
|
|
nextRemainingParts = remainingParts.length > 0 ? remainingParts.slice(1) : [];
|
|
}
|
|
// else: Transparent segment (route group, __PAGE__, etc.)
|
|
// Stay at the same known route part, don't advance URL parts
|
|
// Recurse into child routes. A route tree can have multiple parallel routes
|
|
// (e.g., @modal alongside children). Each parallel route is a separate
|
|
// branch, but they all share the same URL - we just need to traverse all
|
|
// branches to build out the known route tree.
|
|
const slots = routeTree.slots;
|
|
let resultFromChildren = null;
|
|
if (slots !== null) {
|
|
for(const parallelRouteKey in slots){
|
|
const childRouteTree = slots[parallelRouteKey];
|
|
// Skip branches with refreshState set - these were reused from a
|
|
// different route (e.g., a "default" parallel slot) and don't represent
|
|
// the actual route structure for this URL.
|
|
if (childRouteTree.refreshState !== null) {
|
|
continue;
|
|
}
|
|
const result = discoverKnownRoutePart(knownRoutePart, childRouteTree, nextUrlPart, nextRemainingParts, existingEntry, now, pathname, nextUrl, fullTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching, hasDynamicRewrite);
|
|
// All parallel route branches share the same URL, so they should all
|
|
// reach compatible leaf nodes. We capture any result.
|
|
resultFromChildren = result;
|
|
}
|
|
if (resultFromChildren !== null) {
|
|
return resultFromChildren;
|
|
}
|
|
// Defensive fallback: no children returned a result. This shouldn't happen
|
|
// for valid route trees, but handle it gracefully.
|
|
if (existingEntry !== null) {
|
|
return existingEntry;
|
|
}
|
|
return (0, _cache.writeRouteIntoCache)(now, pathname, nextUrl, fullTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching);
|
|
}
|
|
// Reached a page node. Create/get the route cache entry and store as a
|
|
// pattern. First, check if there's already a pattern for this route.
|
|
if (knownRoutePart.pattern !== null) {
|
|
// If this route has a dynamic rewrite, mark the existing pattern.
|
|
if (hasDynamicRewrite) {
|
|
knownRoutePart.pattern.hasDynamicRewrite = true;
|
|
}
|
|
return knownRoutePart.pattern;
|
|
}
|
|
// Get or create the entry
|
|
let entry;
|
|
if (existingEntry !== null) {
|
|
// Already have a fulfilled entry, use it directly. It's already in the
|
|
// route cache map.
|
|
entry = existingEntry;
|
|
} else {
|
|
// Create the entry and insert it into the route cache map.
|
|
entry = (0, _cache.writeRouteIntoCache)(now, pathname, nextUrl, fullTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching);
|
|
}
|
|
if (hasDynamicRewrite) {
|
|
entry.hasDynamicRewrite = true;
|
|
}
|
|
// Store as pattern
|
|
knownRoutePart.pattern = entry;
|
|
return entry;
|
|
}
|
|
function matchKnownRoute(pathname, search) {
|
|
const pathnameParts = pathname.split('/').filter((p)=>p !== '');
|
|
const resolvedParams = new Map();
|
|
const match = matchKnownRoutePart(knownRouteTreeRoot, pathnameParts, 0, resolvedParams);
|
|
if (match === null) {
|
|
return null;
|
|
}
|
|
const matchedPart = match.part;
|
|
const pattern = match.pattern;
|
|
// If the pattern could be intercepted, we can't safely use it for prediction.
|
|
// Interception routes resolve to different route trees depending on the
|
|
// referrer (the Next-Url header), which means the same URL can map to
|
|
// different page components depending on where the navigation originated.
|
|
// Since the known route tree only stores a single pattern per URL shape, we
|
|
// can't distinguish between the intercepted and non-intercepted cases, so we
|
|
// bail out to server resolution.
|
|
//
|
|
// TODO: We could store interception behavior in the known route tree itself
|
|
// (e.g., which segments use interception markers and what they resolve to).
|
|
// With enough information embedded in the trie, we could match interception
|
|
// routes entirely on the client without a server round-trip.
|
|
if (pattern.couldBeIntercepted) {
|
|
return null;
|
|
}
|
|
// "Reify" the pattern: clone the template tree with concrete param values.
|
|
// This substitutes resolved params (e.g., slug: "hello") into dynamic
|
|
// segments and recomputes vary paths for correct segment cache keying.
|
|
const acc = {
|
|
metadataVaryPath: null
|
|
};
|
|
const reifiedTree = reifyRouteTree(pattern.tree, resolvedParams, search, null, acc);
|
|
// The metadata tree is a flat page node without the intermediate layout
|
|
// structure. Clone it with the updated metadata vary path collected during
|
|
// the main tree traversal.
|
|
const metadataVaryPath = acc.metadataVaryPath;
|
|
if (metadataVaryPath === null) {
|
|
// This shouldn't be reachable for a valid route tree.
|
|
return null;
|
|
}
|
|
const reifiedMetadata = (0, _cache.createMetadataRouteTree)(metadataVaryPath);
|
|
// Create a synthetic (predicted) entry and store it as the new pattern.
|
|
//
|
|
// Why replace the pattern? We intentionally update the pattern with this
|
|
// synthetic entry so that if our prediction was wrong (server returns a
|
|
// different pathname due to dynamic rewrite), the entry gets marked with
|
|
// hasDynamicRewrite. Future predictions for this route will see the flag
|
|
// and bail out to server resolution instead of making the same mistake.
|
|
const syntheticEntry = {
|
|
canonicalUrl: pathname + search,
|
|
status: _cache.EntryStatus.Fulfilled,
|
|
blockedTasks: null,
|
|
tree: reifiedTree,
|
|
metadata: reifiedMetadata,
|
|
couldBeIntercepted: pattern.couldBeIntercepted,
|
|
supportsPerSegmentPrefetching: pattern.supportsPerSegmentPrefetching,
|
|
hasDynamicRewrite: false,
|
|
renderedSearch: search,
|
|
ref: null,
|
|
size: pattern.size,
|
|
staleAt: pattern.staleAt,
|
|
version: pattern.version
|
|
};
|
|
matchedPart.pattern = syntheticEntry;
|
|
return syntheticEntry;
|
|
}
|
|
/**
|
|
* Recursively matches a URL against the known route tree.
|
|
*
|
|
* Matching priority (most specific first):
|
|
* 1. Static children - exact path segment match
|
|
* 2. Dynamic child - [param], [...param], [[...param]]
|
|
* 3. Direct pattern - when no more URL parts remain
|
|
*
|
|
* Collects resolved param values in resolvedParams as it traverses.
|
|
* Returns null if no match found (caller should fall back to server).
|
|
*/ function matchKnownRoutePart(part, pathnameParts, partIndex, resolvedParams) {
|
|
const urlPart = partIndex < pathnameParts.length ? pathnameParts[partIndex] : null;
|
|
// If staticChildren is null, we don't know what static routes exist at this
|
|
// level. This happens in webpack dev mode where routes are compiled
|
|
// on-demand. We can't safely match a dynamicChild because the URL part might
|
|
// be a static sibling we haven't discovered yet. Example: We know
|
|
// /blog/[slug] exists, but haven't compiled /blog/featured. A request for
|
|
// /blog/featured would incorrectly match /blog/[slug].
|
|
if (part.staticChildren === null) {
|
|
// The only safe match is a direct pattern when no URL parts remain.
|
|
if (urlPart === null) {
|
|
const pattern = part.pattern;
|
|
if (pattern !== null && !pattern.hasDynamicRewrite) {
|
|
return {
|
|
part,
|
|
pattern
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
// Static children take priority over dynamic. This ensures /blog/featured
|
|
// matches its own route rather than /blog/[slug].
|
|
if (urlPart !== null) {
|
|
const staticChild = part.staticChildren.get(urlPart);
|
|
if (staticChild !== undefined) {
|
|
// Check if this is an "unknown" placeholder part. These are created when
|
|
// we learn about static siblings (from the route tree's staticSiblings
|
|
// field) but haven't prefetched them yet. We know the path exists but
|
|
// don't know its structure, so we can't predict it.
|
|
if (staticChild.pattern === null && staticChild.dynamicChild === null && staticChild.staticChildren === null) {
|
|
// Bail out - server must resolve this route.
|
|
return null;
|
|
}
|
|
const match = matchKnownRoutePart(staticChild, pathnameParts, partIndex + 1, resolvedParams);
|
|
if (match !== null) {
|
|
return match;
|
|
}
|
|
// Static child is a real node (not a placeholder) but its subtree
|
|
// didn't match the remaining URL parts. This means the route exists
|
|
// in the static subtree but hasn't been fully discovered yet. Do not
|
|
// fall through to try the dynamic child — the static match is
|
|
// authoritative. Bail out to server resolution.
|
|
return null;
|
|
}
|
|
}
|
|
// Try dynamic child
|
|
if (part.dynamicChild !== null) {
|
|
const dynamicPart = part.dynamicChild;
|
|
const paramName = part.dynamicChildParamName;
|
|
const paramType = part.dynamicChildParamType;
|
|
const dynamicPattern = dynamicPart.pattern;
|
|
switch(paramType){
|
|
case 'c':
|
|
// Required catch-all [...param]: consumes 1+ URL parts
|
|
if (dynamicPattern !== null && !dynamicPattern.hasDynamicRewrite && urlPart !== null) {
|
|
resolvedParams.set(paramName, pathnameParts.slice(partIndex));
|
|
return {
|
|
part: dynamicPart,
|
|
pattern: dynamicPattern
|
|
};
|
|
}
|
|
break;
|
|
case 'oc':
|
|
// Optional catch-all [[...param]]: consumes 0+ URL parts
|
|
if (dynamicPattern !== null && !dynamicPattern.hasDynamicRewrite) {
|
|
if (urlPart !== null) {
|
|
resolvedParams.set(paramName, pathnameParts.slice(partIndex));
|
|
return {
|
|
part: dynamicPart,
|
|
pattern: dynamicPattern
|
|
};
|
|
}
|
|
// urlPart is null - can match with zero parts, but a direct pattern
|
|
// (e.g., page.tsx alongside [[...param]]) takes precedence.
|
|
if (part.pattern === null || part.pattern.hasDynamicRewrite) {
|
|
resolvedParams.set(paramName, []);
|
|
return {
|
|
part: dynamicPart,
|
|
pattern: dynamicPattern
|
|
};
|
|
}
|
|
}
|
|
break;
|
|
case 'd':
|
|
// Regular dynamic [param]: consumes exactly 1 URL part.
|
|
// Unlike catch-all which terminates here, regular dynamic must
|
|
// continue recursing to find the leaf pattern.
|
|
if (urlPart !== null) {
|
|
resolvedParams.set(paramName, urlPart);
|
|
return matchKnownRoutePart(dynamicPart, pathnameParts, partIndex + 1, resolvedParams);
|
|
}
|
|
break;
|
|
// Intercepted routes use relative path markers like (.), (..), (...)
|
|
// Their behavior depends on navigation context (soft vs hard nav),
|
|
// so we can't predict them client-side. Defer to server.
|
|
case 'ci(..)(..)':
|
|
case 'ci(.)':
|
|
case 'ci(..)':
|
|
case 'ci(...)':
|
|
case 'di(..)(..)':
|
|
case 'di(.)':
|
|
case 'di(..)':
|
|
case 'di(...)':
|
|
return null;
|
|
default:
|
|
paramType;
|
|
}
|
|
}
|
|
// No children matched. If we've consumed all URL parts, check for a direct
|
|
// pattern at this node (the route terminates here).
|
|
if (urlPart === null) {
|
|
const pattern = part.pattern;
|
|
if (pattern !== null && !pattern.hasDynamicRewrite) {
|
|
return {
|
|
part,
|
|
pattern
|
|
};
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
/**
|
|
* "Reify" means to make concrete - we take an abstract pattern (the template
|
|
* route tree) and produce a concrete instance with actual param values.
|
|
*
|
|
* This function clones a RouteTree, substituting dynamic segment values from
|
|
* resolvedParams and computing new vary paths. The vary path encodes param
|
|
* values so segment cache entries can be correctly keyed.
|
|
*
|
|
* Example: Pattern for /blog/[slug] with resolvedParams { slug: "hello" }
|
|
* produces a tree where segment [slug] has cacheKey "hello".
|
|
*/ function reifyRouteTree(pattern, resolvedParams, search, parentPartialVaryPath, acc) {
|
|
const originalSegment = pattern.segment;
|
|
let newSegment = originalSegment;
|
|
let partialVaryPath;
|
|
if (typeof originalSegment !== 'string') {
|
|
// Dynamic segment: compute new cache key and append to partial vary path
|
|
const paramName = originalSegment[0];
|
|
const paramType = originalSegment[2];
|
|
const staticSiblings = originalSegment[3];
|
|
const newValue = resolvedParams.get(paramName);
|
|
if (newValue !== undefined) {
|
|
const newCacheKey = Array.isArray(newValue) ? newValue.join('/') : newValue;
|
|
newSegment = [
|
|
paramName,
|
|
newCacheKey,
|
|
paramType,
|
|
staticSiblings
|
|
];
|
|
partialVaryPath = (0, _varypath.appendLayoutVaryPath)(parentPartialVaryPath, newCacheKey, paramName);
|
|
} else {
|
|
// Param not found in resolvedParams - keep original and inherit partial
|
|
// TODO: This should never happen. Bail out with null.
|
|
partialVaryPath = parentPartialVaryPath;
|
|
}
|
|
} else {
|
|
// Static segment: inherit partial vary path from parent
|
|
partialVaryPath = parentPartialVaryPath;
|
|
}
|
|
// Recurse into children with the (possibly updated) partial vary path
|
|
let newSlots = null;
|
|
if (pattern.slots !== null) {
|
|
newSlots = {};
|
|
for(const key in pattern.slots){
|
|
newSlots[key] = reifyRouteTree(pattern.slots[key], resolvedParams, search, partialVaryPath, acc);
|
|
}
|
|
}
|
|
if (pattern.isPage) {
|
|
// Page segment: finalize with search params
|
|
const newVaryPath = (0, _varypath.finalizePageVaryPath)(pattern.requestKey, search, partialVaryPath);
|
|
// Collect metadata vary path (first page wins, same as original algorithm)
|
|
if (acc.metadataVaryPath === null) {
|
|
acc.metadataVaryPath = (0, _varypath.finalizeMetadataVaryPath)(pattern.requestKey, search, partialVaryPath);
|
|
}
|
|
return {
|
|
requestKey: pattern.requestKey,
|
|
segment: newSegment,
|
|
refreshState: pattern.refreshState,
|
|
slots: newSlots,
|
|
prefetchHints: pattern.prefetchHints,
|
|
isPage: true,
|
|
varyPath: newVaryPath
|
|
};
|
|
} else {
|
|
// Layout segment: finalize without search params
|
|
const newVaryPath = (0, _varypath.finalizeLayoutVaryPath)(pattern.requestKey, partialVaryPath);
|
|
return {
|
|
requestKey: pattern.requestKey,
|
|
segment: newSegment,
|
|
refreshState: pattern.refreshState,
|
|
slots: newSlots,
|
|
prefetchHints: pattern.prefetchHints,
|
|
isPage: false,
|
|
varyPath: newVaryPath
|
|
};
|
|
}
|
|
}
|
|
function resetKnownRoutes() {
|
|
knownRouteTreeRoot = createEmptyPart();
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/navigation-testing-lock.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* Navigation lock for the Instant Navigation Testing API.
|
|
*
|
|
* Manages the in-memory lock (a promise) that gates dynamic data writes
|
|
* during instant navigation captures, and owns all cookie state
|
|
* transitions (pending → captured-MPA, pending → captured-SPA).
|
|
*
|
|
* External actors (Playwright, devtools) set [0] to start a lock scope
|
|
* and delete the cookie to end one. Next.js writes captured values.
|
|
* The CookieStore handler distinguishes them by value: pending = external,
|
|
* captured = self-write (ignored).
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
isNavigationLocked: null,
|
|
startListeningForInstantNavigationCookie: null,
|
|
transitionToCapturedSPA: null,
|
|
updateCapturedSPAToTree: null,
|
|
waitForNavigationLockIfActive: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
isNavigationLocked: function() {
|
|
return isNavigationLocked;
|
|
},
|
|
startListeningForInstantNavigationCookie: function() {
|
|
return startListeningForInstantNavigationCookie;
|
|
},
|
|
transitionToCapturedSPA: function() {
|
|
return transitionToCapturedSPA;
|
|
},
|
|
updateCapturedSPAToTree: function() {
|
|
return updateCapturedSPAToTree;
|
|
},
|
|
waitForNavigationLockIfActive: function() {
|
|
return waitForNavigationLockIfActive;
|
|
}
|
|
});
|
|
const _approuterheaders = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)");
|
|
const _useactionqueue = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/use-action-queue.js [app-ssr] (ecmascript)");
|
|
function parseCookieValue(raw) {
|
|
try {
|
|
const parsed = JSON.parse(raw);
|
|
if (Array.isArray(parsed) && parsed.length >= 3) {
|
|
const rawState = parsed[2];
|
|
return rawState === null ? 'mpa' : 'spa';
|
|
}
|
|
} catch {}
|
|
return 'pending';
|
|
}
|
|
function writeCookieValue(value) {
|
|
if (typeof cookieStore === 'undefined') {
|
|
return;
|
|
}
|
|
// Read the existing cookie to preserve its attributes (domain, path),
|
|
// then write back with the new value. This updates the same cookie
|
|
// entry that the external actor created, regardless of how it was
|
|
// scoped.
|
|
cookieStore.get(_approuterheaders.NEXT_INSTANT_TEST_COOKIE).then((existing)=>{
|
|
if (existing) {
|
|
const options = {
|
|
name: _approuterheaders.NEXT_INSTANT_TEST_COOKIE,
|
|
value: JSON.stringify(value),
|
|
path: existing.path ?? '/'
|
|
};
|
|
if (existing.domain) {
|
|
options.domain = existing.domain;
|
|
}
|
|
cookieStore.set(options);
|
|
}
|
|
});
|
|
}
|
|
let lockState = null;
|
|
function acquireLock() {
|
|
if (lockState !== null) {
|
|
return;
|
|
}
|
|
let resolve;
|
|
const promise = new Promise((r)=>{
|
|
resolve = r;
|
|
});
|
|
lockState = {
|
|
promise,
|
|
resolve: resolve
|
|
};
|
|
}
|
|
function releaseLock() {
|
|
if (lockState !== null) {
|
|
lockState.resolve();
|
|
lockState = null;
|
|
}
|
|
}
|
|
function startListeningForInstantNavigationCookie() {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
// If the server served a static shell, this is an MPA page load
|
|
// while the lock is held. Transition to captured-MPA and acquire.
|
|
if (self.__next_instant_test) {
|
|
if (typeof cookieStore !== 'undefined') {
|
|
// If the cookie was already cleared during the MPA page
|
|
// transition, reload to get the full dynamic page.
|
|
cookieStore.get(_approuterheaders.NEXT_INSTANT_TEST_COOKIE).then((cookie)=>{
|
|
if (!cookie) {
|
|
window.location.reload();
|
|
}
|
|
});
|
|
}
|
|
writeCookieValue([
|
|
1,
|
|
`c${Math.random()}`,
|
|
null
|
|
]);
|
|
acquireLock();
|
|
}
|
|
if (typeof cookieStore === 'undefined') {
|
|
return;
|
|
}
|
|
cookieStore.addEventListener('change', (event)=>{
|
|
for (const cookie of event.changed){
|
|
if (cookie.name === _approuterheaders.NEXT_INSTANT_TEST_COOKIE) {
|
|
const state = parseCookieValue(cookie.value ?? '');
|
|
if (state !== 'pending') {
|
|
// Captured value — our own transition. Ignore.
|
|
return;
|
|
}
|
|
// Pending value — external actor starting a new lock scope.
|
|
if (lockState !== null) {
|
|
releaseLock();
|
|
}
|
|
acquireLock();
|
|
return;
|
|
}
|
|
}
|
|
for (const cookie of event.deleted){
|
|
if (cookie.name === _approuterheaders.NEXT_INSTANT_TEST_COOKIE) {
|
|
releaseLock();
|
|
(0, _useactionqueue.refreshOnInstantNavigationUnlock)();
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
function transitionToCapturedSPA(fromTree, toTree) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
writeCookieValue([
|
|
1,
|
|
`c${Math.random()}`,
|
|
{
|
|
from: fromTree,
|
|
to: toTree
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
function updateCapturedSPAToTree(fromTree, toTree) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
writeCookieValue([
|
|
1,
|
|
`c${Math.random()}`,
|
|
{
|
|
from: fromTree,
|
|
to: toTree
|
|
}
|
|
]);
|
|
}
|
|
}
|
|
function isNavigationLocked() {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return lockState !== null;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
}
|
|
async function waitForNavigationLockIfActive() {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (lockState !== null) {
|
|
await lockState.promise;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/ppr-navigations.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
FreshnessPolicy: null,
|
|
createInitialCacheNodeForHydration: null,
|
|
isDeferredRsc: null,
|
|
spawnDynamicRequests: null,
|
|
startPPRNavigation: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
FreshnessPolicy: function() {
|
|
return FreshnessPolicy;
|
|
},
|
|
createInitialCacheNodeForHydration: function() {
|
|
return createInitialCacheNodeForHydration;
|
|
},
|
|
isDeferredRsc: function() {
|
|
return isDeferredRsc;
|
|
},
|
|
spawnDynamicRequests: function() {
|
|
return spawnDynamicRequests;
|
|
},
|
|
startPPRNavigation: function() {
|
|
return startPPRNavigation;
|
|
}
|
|
});
|
|
const _approutertypes = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/app-router-types.js [app-ssr] (ecmascript)");
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const _matchsegments = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/match-segments.js [app-ssr] (ecmascript)");
|
|
const _createhreffromurl = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/create-href-from-url.js [app-ssr] (ecmascript)");
|
|
const _fetchserverresponse = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/fetch-server-response.js [app-ssr] (ecmascript)");
|
|
const _useactionqueue = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/use-action-queue.js [app-ssr] (ecmascript)");
|
|
const _routerreducertypes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/router-reducer-types.js [app-ssr] (ecmascript)");
|
|
const _isnavigatingtonewrootlayout = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/is-navigating-to-new-root-layout.js [app-ssr] (ecmascript)");
|
|
const _committedstate = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/reducers/committed-state.js [app-ssr] (ecmascript)");
|
|
const _navigation = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation.js [app-ssr] (ecmascript)");
|
|
const _cache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)");
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _optimisticroutes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/optimistic-routes.js [app-ssr] (ecmascript)");
|
|
const _constants = __turbopack_context__.r("[project]/node_modules/next/dist/lib/constants.js [app-ssr] (ecmascript)");
|
|
const _varypath = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/vary-path.js [app-ssr] (ecmascript)");
|
|
const _bfcache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/bfcache.js [app-ssr] (ecmascript)");
|
|
var FreshnessPolicy = /*#__PURE__*/ function(FreshnessPolicy) {
|
|
FreshnessPolicy[FreshnessPolicy["Default"] = 0] = "Default";
|
|
FreshnessPolicy[FreshnessPolicy["Hydration"] = 1] = "Hydration";
|
|
FreshnessPolicy[FreshnessPolicy["HistoryTraversal"] = 2] = "HistoryTraversal";
|
|
FreshnessPolicy[FreshnessPolicy["RefreshAll"] = 3] = "RefreshAll";
|
|
FreshnessPolicy[FreshnessPolicy["HMRRefresh"] = 4] = "HMRRefresh";
|
|
FreshnessPolicy[FreshnessPolicy["Gesture"] = 5] = "Gesture";
|
|
return FreshnessPolicy;
|
|
}({});
|
|
const noop = ()=>{};
|
|
function createInitialCacheNodeForHydration(navigatedAt, initialTree, seedData, seedHead, seedDynamicStaleAt) {
|
|
// Create the initial cache node tree, using the data embedded into the
|
|
// HTML document.
|
|
const accumulation = {
|
|
separateRefreshUrls: null,
|
|
scrollRef: null
|
|
};
|
|
const task = createCacheNodeOnNavigation(navigatedAt, initialTree, null, 1, seedData, seedHead, seedDynamicStaleAt, false, accumulation);
|
|
return task;
|
|
}
|
|
function startPPRNavigation(navigatedAt, oldUrl, oldRenderedSearch, oldCacheNode, oldRouterState, newRouteTree, newMetadataVaryPath, freshness, seedData, seedHead, seedDynamicStaleAt, isSamePageNavigation, accumulation) {
|
|
const didFindRootLayout = false;
|
|
const parentNeedsDynamicRequest = false;
|
|
const parentRefreshState = null;
|
|
const oldRootRefreshState = {
|
|
canonicalUrl: (0, _createhreffromurl.createHrefFromUrl)(oldUrl),
|
|
renderedSearch: oldRenderedSearch
|
|
};
|
|
return updateCacheNodeOnNavigation(navigatedAt, oldUrl, oldCacheNode !== null ? oldCacheNode : undefined, oldRouterState, newRouteTree, newMetadataVaryPath, freshness, didFindRootLayout, seedData, seedHead, seedDynamicStaleAt, isSamePageNavigation, parentNeedsDynamicRequest, oldRootRefreshState, parentRefreshState, accumulation);
|
|
}
|
|
function updateCacheNodeOnNavigation(navigatedAt, oldUrl, oldCacheNode, oldRouterState, newRouteTree, newMetadataVaryPath, freshness, didFindRootLayout, seedData, seedHead, seedDynamicStaleAt, isSamePageNavigation, parentNeedsDynamicRequest, oldRootRefreshState, parentRefreshState, accumulation) {
|
|
// Check if this segment matches the one in the previous route.
|
|
const oldSegment = oldRouterState[0];
|
|
const newSegment = createSegmentFromRouteTree(newRouteTree);
|
|
if (!(0, _matchsegments.matchSegment)(newSegment, oldSegment)) {
|
|
// This segment does not match the previous route. We're now entering the
|
|
// new part of the target route. Switch to the "create" path.
|
|
if (// highest-level layout in a route tree is referred to as the "root"
|
|
// layout.) This could mean that we're navigating between two different
|
|
// root layouts. When this happens, we perform a full-page (MPA-style)
|
|
// navigation.
|
|
//
|
|
// However, the algorithm for deciding where to start rendering a route
|
|
// (i.e. the one performed in order to reach this function) is stricter
|
|
// than the one used to detect a change in the root layout. So just
|
|
// because we're re-rendering a segment outside of the root layout does
|
|
// not mean we should trigger a full-page navigation.
|
|
//
|
|
// Specifically, we handle dynamic parameters differently: two segments
|
|
// are considered the same even if their parameter values are different.
|
|
//
|
|
// Refer to isNavigatingToNewRootLayout for details.
|
|
//
|
|
// Note that we only have to perform this extra traversal if we didn't
|
|
// already discover a root layout in the part of the tree that is
|
|
// unchanged. We also only need to compare the subtree that is not
|
|
// shared. In the common case, this branch is skipped completely.
|
|
!didFindRootLayout && (0, _isnavigatingtonewrootlayout.isNavigatingToNewRootLayout)(oldRouterState, newRouteTree) || // The global Not Found route (app/global-not-found.tsx) is a special
|
|
// case, because it acts like a root layout, but in the router tree, it
|
|
// is rendered in the same position as app/layout.tsx.
|
|
//
|
|
// Any navigation to the global Not Found route should trigger a
|
|
// full-page navigation.
|
|
//
|
|
// TODO: We should probably model this by changing the key of the root
|
|
// segment when this happens. Then the root layout check would work
|
|
// as expected, without a special case.
|
|
newSegment === _segment.NOT_FOUND_SEGMENT_KEY) {
|
|
return null;
|
|
}
|
|
return createCacheNodeOnNavigation(navigatedAt, newRouteTree, newMetadataVaryPath, freshness, seedData, seedHead, seedDynamicStaleAt, parentNeedsDynamicRequest, accumulation);
|
|
}
|
|
const newSlots = newRouteTree.slots;
|
|
const oldRouterStateChildren = oldRouterState[1];
|
|
const seedDataChildren = seedData !== null ? seedData[1] : null;
|
|
// We're currently traversing the part of the tree that was also part of
|
|
// the previous route. If we discover a root layout, then we don't need to
|
|
// trigger an MPA navigation.
|
|
const childDidFindRootLayout = didFindRootLayout || (newRouteTree.prefetchHints & _approutertypes.PrefetchHint.IsRootLayout) !== 0;
|
|
let shouldRefreshDynamicData = false;
|
|
switch(freshness){
|
|
case 0:
|
|
case 2:
|
|
case 1:
|
|
case 5:
|
|
shouldRefreshDynamicData = false;
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
shouldRefreshDynamicData = true;
|
|
break;
|
|
default:
|
|
freshness;
|
|
break;
|
|
}
|
|
// TODO: We're not consistent about how we do this check. Some places
|
|
// check if the segment starts with PAGE_SEGMENT_KEY, but most seem to
|
|
// check if there any any children, which is why I'm doing it here. We
|
|
// should probably encode an empty children set as `null` though. Either
|
|
// way, we should update all the checks to be consistent.
|
|
const isLeafSegment = newSlots === null;
|
|
// Get the data for this segment. Since it was part of the previous route,
|
|
// usually we just clone the data from the old CacheNode. However, during a
|
|
// refresh or a revalidation, there won't be any existing CacheNode. So we
|
|
// may need to consult the prefetch cache, like we would for a new segment.
|
|
let newCacheNode;
|
|
let needsDynamicRequest;
|
|
if (oldCacheNode !== undefined && !shouldRefreshDynamicData && // During a same-page navigation, we always refetch the page segments
|
|
!(isLeafSegment && isSamePageNavigation)) {
|
|
// Reuse the existing CacheNode
|
|
const dropPrefetchRsc = false;
|
|
newCacheNode = reuseSharedCacheNode(dropPrefetchRsc, oldCacheNode);
|
|
needsDynamicRequest = false;
|
|
} else {
|
|
// If this is part of a refresh, ignore the existing CacheNode and create a
|
|
// new one.
|
|
const seedRsc = seedData !== null ? seedData[0] : null;
|
|
const result = createCacheNodeForSegment(navigatedAt, newRouteTree, seedRsc, newMetadataVaryPath, seedHead, freshness, seedDynamicStaleAt);
|
|
newCacheNode = result.cacheNode;
|
|
needsDynamicRequest = result.needsDynamicRequest;
|
|
// Carry forward the old node's scrollRef. This preserves scroll
|
|
// intent when a prior navigation's cache node is replaced by a
|
|
// refresh before the scroll handler has had a chance to fire —
|
|
// e.g. when router.push() and router.refresh() are called in the
|
|
// same startTransition batch.
|
|
if (oldCacheNode !== undefined) {
|
|
newCacheNode.scrollRef = oldCacheNode.scrollRef;
|
|
}
|
|
}
|
|
// During a refresh navigation, there's a special case that happens when
|
|
// entering a "default" slot. The default slot may not be part of the
|
|
// current route; it may have been reused from an older route. If so,
|
|
// we need to fetch its data from the old route's URL rather than current
|
|
// route's URL. Keep track of this as we traverse the tree.
|
|
const maybeRefreshState = newRouteTree.refreshState;
|
|
const refreshState = maybeRefreshState !== undefined && maybeRefreshState !== null ? maybeRefreshState : parentRefreshState;
|
|
// If this segment itself needs to fetch new data from the server, then by
|
|
// definition it is being refreshed. Track its refresh URL so we know which
|
|
// URL to request the data from.
|
|
if (needsDynamicRequest && refreshState !== null) {
|
|
accumulateRefreshUrl(accumulation, refreshState);
|
|
}
|
|
// As we diff the trees, we may sometimes modify (copy-on-write, not mutate)
|
|
// the Route Tree that was returned by the server — for example, in the case
|
|
// of default parallel routes, we preserve the currently active segment. To
|
|
// avoid mutating the original tree, we clone the router state children along
|
|
// the return path.
|
|
let patchedRouterStateChildren = {};
|
|
let taskChildren = null;
|
|
// Most navigations require a request to fetch additional data from the
|
|
// server, either because the data was not already prefetched, or because the
|
|
// target route contains dynamic data that cannot be prefetched.
|
|
//
|
|
// However, if the target route is fully static, and it's already completely
|
|
// loaded into the segment cache, then we can skip the server request.
|
|
//
|
|
// This starts off as `false`, and is set to `true` if any of the child
|
|
// routes requires a dynamic request.
|
|
let childNeedsDynamicRequest = false;
|
|
// As we traverse the children, we'll construct a FlightRouterState that can
|
|
// be sent to the server to request the dynamic data. If it turns out that
|
|
// nothing in the subtree is dynamic (i.e. childNeedsDynamicRequest is false
|
|
// at the end), then this will be discarded.
|
|
// TODO: We can probably optimize the format of this data structure to only
|
|
// include paths that are dynamic. Instead of reusing the
|
|
// FlightRouterState type.
|
|
let dynamicRequestTreeChildren = {};
|
|
let newCacheNodeSlots = null;
|
|
if (newSlots !== null) {
|
|
const oldCacheNodeSlots = oldCacheNode !== undefined ? oldCacheNode.slots : null;
|
|
newCacheNode.slots = newCacheNodeSlots = {};
|
|
taskChildren = new Map();
|
|
for(let parallelRouteKey in newSlots){
|
|
let newRouteTreeChild = newSlots[parallelRouteKey];
|
|
const oldRouterStateChild = oldRouterStateChildren[parallelRouteKey];
|
|
if (oldRouterStateChild === undefined) {
|
|
// This should never happen, but if it does, it suggests a malformed
|
|
// server response. Trigger a full-page navigation.
|
|
return null;
|
|
}
|
|
let seedDataChild = seedDataChildren !== null ? seedDataChildren[parallelRouteKey] : null;
|
|
const oldSegmentChild = oldRouterStateChild[0];
|
|
let newSegmentChild = createSegmentFromRouteTree(newRouteTreeChild);
|
|
let seedHeadChild = seedHead;
|
|
if (// was stashed in the history entry as-is.
|
|
freshness !== 2 && newSegmentChild === _segment.DEFAULT_SEGMENT_KEY && oldSegmentChild !== _segment.DEFAULT_SEGMENT_KEY) {
|
|
// This is a "default" segment. These are never sent by the server during
|
|
// a soft navigation; instead, the client reuses whatever segment was
|
|
// already active in that slot on the previous route.
|
|
newRouteTreeChild = reuseActiveSegmentInDefaultSlot(newRouteTree, parallelRouteKey, oldRootRefreshState, oldRouterStateChild);
|
|
newSegmentChild = createSegmentFromRouteTree(newRouteTreeChild);
|
|
// Since we're switching to a different route tree, these are no
|
|
// longer valid, because they correspond to the outer tree.
|
|
seedDataChild = null;
|
|
seedHeadChild = null;
|
|
}
|
|
const oldCacheNodeChild = oldCacheNodeSlots !== null ? oldCacheNodeSlots[parallelRouteKey] : undefined;
|
|
const taskChild = updateCacheNodeOnNavigation(navigatedAt, oldUrl, oldCacheNodeChild, oldRouterStateChild, newRouteTreeChild, newMetadataVaryPath, freshness, childDidFindRootLayout, seedDataChild ?? null, seedHeadChild, seedDynamicStaleAt, isSamePageNavigation, parentNeedsDynamicRequest || needsDynamicRequest, oldRootRefreshState, refreshState, accumulation);
|
|
if (taskChild === null) {
|
|
// One of the child tasks discovered a change to the root layout.
|
|
// Immediately unwind from this recursive traversal. This will trigger a
|
|
// full-page navigation.
|
|
return null;
|
|
}
|
|
// Recursively propagate up the child tasks.
|
|
taskChildren.set(parallelRouteKey, taskChild);
|
|
newCacheNodeSlots[parallelRouteKey] = taskChild.node;
|
|
// The child tree's route state may be different from the prefetched
|
|
// route sent by the server. We need to clone it as we traverse back up
|
|
// the tree.
|
|
const taskChildRoute = taskChild.route;
|
|
patchedRouterStateChildren[parallelRouteKey] = taskChildRoute;
|
|
const dynamicRequestTreeChild = taskChild.dynamicRequestTree;
|
|
if (dynamicRequestTreeChild !== null) {
|
|
// Something in the child tree is dynamic.
|
|
childNeedsDynamicRequest = true;
|
|
dynamicRequestTreeChildren[parallelRouteKey] = dynamicRequestTreeChild;
|
|
} else {
|
|
dynamicRequestTreeChildren[parallelRouteKey] = taskChildRoute;
|
|
}
|
|
}
|
|
}
|
|
const newFlightRouterState = [
|
|
createSegmentFromRouteTree(newRouteTree),
|
|
patchedRouterStateChildren,
|
|
refreshState !== null ? [
|
|
refreshState.canonicalUrl,
|
|
refreshState.renderedSearch
|
|
] : null,
|
|
null,
|
|
newRouteTree.prefetchHints
|
|
];
|
|
return {
|
|
status: needsDynamicRequest ? 0 : 1,
|
|
route: newFlightRouterState,
|
|
node: newCacheNode,
|
|
dynamicRequestTree: createDynamicRequestTree(newFlightRouterState, dynamicRequestTreeChildren, needsDynamicRequest, childNeedsDynamicRequest, parentNeedsDynamicRequest),
|
|
refreshState,
|
|
children: taskChildren
|
|
};
|
|
}
|
|
/**
|
|
* Assigns a ScrollRef to a new leaf CacheNode so the scroll handler
|
|
* knows to scroll to it after navigation. All leaves in the same
|
|
* navigation share the same ScrollRef — the first segment to scroll
|
|
* consumes it, preventing others from also scrolling.
|
|
*
|
|
* This is only called inside `createCacheNodeOnNavigation`, which only
|
|
* runs when segments diverge from the previous route. So for a refresh
|
|
* where the route structure stays the same, segments match, the update
|
|
* path is taken, and this function is never called — no scroll ref is
|
|
* assigned. A scroll ref is only assigned when the route actually
|
|
* changed (e.g. a redirect, or a dynamic condition on the server that
|
|
* produces a different route).
|
|
*
|
|
* Skipped during hydration (initial render should not scroll) and
|
|
* history traversal (scroll restoration is handled separately).
|
|
*/ function accumulateScrollRef(freshness, cacheNode, accumulation) {
|
|
switch(freshness){
|
|
case 0:
|
|
case 5:
|
|
case 3:
|
|
case 4:
|
|
if (accumulation.scrollRef === null) {
|
|
accumulation.scrollRef = {
|
|
current: true
|
|
};
|
|
}
|
|
cacheNode.scrollRef = accumulation.scrollRef;
|
|
break;
|
|
case 1:
|
|
break;
|
|
case 2:
|
|
break;
|
|
default:
|
|
freshness;
|
|
break;
|
|
}
|
|
}
|
|
function createCacheNodeOnNavigation(navigatedAt, newRouteTree, newMetadataVaryPath, freshness, seedData, seedHead, seedDynamicStaleAt, parentNeedsDynamicRequest, accumulation) {
|
|
// Same traversal as updateCacheNodeNavigation, but simpler. We switch to this
|
|
// path once we reach the part of the tree that was not in the previous route.
|
|
// We don't need to diff against the old tree, we just need to create a new
|
|
// one. We also don't need to worry about any refresh-related logic.
|
|
//
|
|
// For the most part, this is a subset of updateCacheNodeOnNavigation, so any
|
|
// change that happens in this function likely needs to be applied to that
|
|
// one, too. However there are some places where the behavior intentionally
|
|
// diverges, which is why we keep them separate.
|
|
const newSegment = createSegmentFromRouteTree(newRouteTree);
|
|
const newSlots = newRouteTree.slots;
|
|
const seedDataChildren = seedData !== null ? seedData[1] : null;
|
|
const seedRsc = seedData !== null ? seedData[0] : null;
|
|
const result = createCacheNodeForSegment(navigatedAt, newRouteTree, seedRsc, newMetadataVaryPath, seedHead, freshness, seedDynamicStaleAt);
|
|
const newCacheNode = result.cacheNode;
|
|
const needsDynamicRequest = result.needsDynamicRequest;
|
|
const isLeafSegment = newSlots === null;
|
|
if (isLeafSegment) {
|
|
accumulateScrollRef(freshness, newCacheNode, accumulation);
|
|
}
|
|
let patchedRouterStateChildren = {};
|
|
let taskChildren = null;
|
|
let childNeedsDynamicRequest = false;
|
|
let dynamicRequestTreeChildren = {};
|
|
let newCacheNodeSlots = null;
|
|
if (newSlots !== null) {
|
|
newCacheNode.slots = newCacheNodeSlots = {};
|
|
taskChildren = new Map();
|
|
for(let parallelRouteKey in newSlots){
|
|
const newRouteTreeChild = newSlots[parallelRouteKey];
|
|
const seedDataChild = seedDataChildren !== null ? seedDataChildren[parallelRouteKey] : null;
|
|
const taskChild = createCacheNodeOnNavigation(navigatedAt, newRouteTreeChild, newMetadataVaryPath, freshness, seedDataChild ?? null, seedHead, seedDynamicStaleAt, parentNeedsDynamicRequest || needsDynamicRequest, accumulation);
|
|
taskChildren.set(parallelRouteKey, taskChild);
|
|
newCacheNodeSlots[parallelRouteKey] = taskChild.node;
|
|
const taskChildRoute = taskChild.route;
|
|
patchedRouterStateChildren[parallelRouteKey] = taskChildRoute;
|
|
const dynamicRequestTreeChild = taskChild.dynamicRequestTree;
|
|
if (dynamicRequestTreeChild !== null) {
|
|
childNeedsDynamicRequest = true;
|
|
dynamicRequestTreeChildren[parallelRouteKey] = dynamicRequestTreeChild;
|
|
} else {
|
|
dynamicRequestTreeChildren[parallelRouteKey] = taskChildRoute;
|
|
}
|
|
}
|
|
}
|
|
const newFlightRouterState = [
|
|
newSegment,
|
|
patchedRouterStateChildren,
|
|
null,
|
|
null,
|
|
newRouteTree.prefetchHints
|
|
];
|
|
return {
|
|
status: needsDynamicRequest ? 0 : 1,
|
|
route: newFlightRouterState,
|
|
node: newCacheNode,
|
|
dynamicRequestTree: createDynamicRequestTree(newFlightRouterState, dynamicRequestTreeChildren, needsDynamicRequest, childNeedsDynamicRequest, parentNeedsDynamicRequest),
|
|
// This route is not part of the current tree, so there's no reason to
|
|
// track the refresh URL.
|
|
refreshState: null,
|
|
children: taskChildren
|
|
};
|
|
}
|
|
function createSegmentFromRouteTree(newRouteTree) {
|
|
if (newRouteTree.isPage) {
|
|
// In a dynamic server response, the server embeds the search params into
|
|
// the segment key, but in a static one it's omitted. The client handles
|
|
// this inconsistency by adding the search params back right at the end.
|
|
//
|
|
// TODO: The only thing this is used for is to create a cache key for
|
|
// ChildSegmentMap. But we already track the `renderedSearch` everywhere as
|
|
// part of the varyPath. The plan is get rid of ChildSegmentMap and
|
|
// store the page data in a CacheMap using the varyPath, like we do
|
|
// for prefetches. Then we can remove it from the segment key.
|
|
//
|
|
// As an incremental step, we can grab the search params from the varyPath.
|
|
const renderedSearch = (0, _varypath.getRenderedSearchFromVaryPath)(newRouteTree.varyPath);
|
|
if (renderedSearch === null) {
|
|
return _segment.PAGE_SEGMENT_KEY;
|
|
}
|
|
// This is based on equivalent logic in addSearchParamsIfPageSegment, used
|
|
// on the server.
|
|
const stringifiedQuery = JSON.stringify(Object.fromEntries(new URLSearchParams(renderedSearch)));
|
|
return stringifiedQuery !== '{}' ? _segment.PAGE_SEGMENT_KEY + '?' + stringifiedQuery : _segment.PAGE_SEGMENT_KEY;
|
|
}
|
|
return newRouteTree.segment;
|
|
}
|
|
function patchRouterStateWithNewChildren(baseRouterState, newChildren) {
|
|
const clone = [
|
|
baseRouterState[0],
|
|
newChildren
|
|
];
|
|
// Based on equivalent logic in apply-router-state-patch-to-tree, but should
|
|
// confirm whether we need to copy all of these fields. Not sure the server
|
|
// ever sends, e.g. the refetch marker.
|
|
if (2 in baseRouterState) {
|
|
clone[2] = baseRouterState[2];
|
|
}
|
|
if (3 in baseRouterState) {
|
|
clone[3] = baseRouterState[3];
|
|
}
|
|
if (4 in baseRouterState) {
|
|
clone[4] = baseRouterState[4];
|
|
}
|
|
return clone;
|
|
}
|
|
function createDynamicRequestTree(newRouterState, dynamicRequestTreeChildren, needsDynamicRequest, childNeedsDynamicRequest, parentNeedsDynamicRequest) {
|
|
// Create a FlightRouterState that instructs the server how to render the
|
|
// requested segment.
|
|
//
|
|
// Or, if neither this segment nor any of the children require a new data,
|
|
// then we return `null` to skip the request.
|
|
let dynamicRequestTree = null;
|
|
if (needsDynamicRequest) {
|
|
dynamicRequestTree = patchRouterStateWithNewChildren(newRouterState, dynamicRequestTreeChildren);
|
|
// The "refetch" marker is set on the top-most segment that requires new
|
|
// data. We can omit it if a parent was already marked.
|
|
if (!parentNeedsDynamicRequest) {
|
|
dynamicRequestTree[3] = 'refetch';
|
|
}
|
|
} else if (childNeedsDynamicRequest) {
|
|
// This segment does not request new data, but at least one of its
|
|
// children does.
|
|
dynamicRequestTree = patchRouterStateWithNewChildren(newRouterState, dynamicRequestTreeChildren);
|
|
} else {
|
|
dynamicRequestTree = null;
|
|
}
|
|
return dynamicRequestTree;
|
|
}
|
|
function accumulateRefreshUrl(accumulation, refreshState) {
|
|
// This is a refresh navigation, and we're inside a "default" slot that's
|
|
// not part of the current route; it was reused from an older route. In
|
|
// order to get fresh data for this reused route, we need to issue a
|
|
// separate request using the old route's URL.
|
|
//
|
|
// Track these extra URLs in the accumulated result. Later, we'll construct
|
|
// an appropriate request for each unique URL in the final set. The reason
|
|
// we don't do it immediately here is so we can deduplicate multiple
|
|
// instances of the same URL into a single request. See
|
|
// listenForDynamicRequest for more details.
|
|
const refreshUrl = refreshState.canonicalUrl;
|
|
const separateRefreshUrls = accumulation.separateRefreshUrls;
|
|
if (separateRefreshUrls === null) {
|
|
accumulation.separateRefreshUrls = new Set([
|
|
refreshUrl
|
|
]);
|
|
} else {
|
|
separateRefreshUrls.add(refreshUrl);
|
|
}
|
|
}
|
|
function reuseActiveSegmentInDefaultSlot(parentRouteTree, parallelRouteKey, oldRootRefreshState, oldRouterState) {
|
|
// This is a "default" segment. These are never sent by the server during a
|
|
// soft navigation; instead, the client reuses whatever segment was already
|
|
// active in that slot on the previous route. This means if we later need to
|
|
// refresh the segment, it will have to be refetched from the previous route's
|
|
// URL. We store it in the Flight Router State.
|
|
let reusedUrl;
|
|
let reusedRenderedSearch;
|
|
const oldRefreshState = oldRouterState[2];
|
|
if (oldRefreshState !== undefined && oldRefreshState !== null) {
|
|
// This segment was already reused from an even older route. Keep its
|
|
// existing URL and refresh state.
|
|
reusedUrl = oldRefreshState[0];
|
|
reusedRenderedSearch = oldRefreshState[1];
|
|
} else {
|
|
// Since this route didn't already have a refresh state, it must have been
|
|
// reachable from the root of the old route. So we use the refresh state
|
|
// that represents the old route.
|
|
reusedUrl = oldRootRefreshState.canonicalUrl;
|
|
reusedRenderedSearch = oldRootRefreshState.renderedSearch;
|
|
}
|
|
const acc = {
|
|
metadataVaryPath: null
|
|
};
|
|
const reusedRouteTree = (0, _cache.convertReusedFlightRouterStateToRouteTree)(parentRouteTree, parallelRouteKey, oldRouterState, reusedRenderedSearch, acc);
|
|
reusedRouteTree.refreshState = {
|
|
canonicalUrl: reusedUrl,
|
|
renderedSearch: reusedRenderedSearch
|
|
};
|
|
return reusedRouteTree;
|
|
}
|
|
function reuseSharedCacheNode(dropPrefetchRsc, existingCacheNode) {
|
|
// Clone the CacheNode that was already present in the previous tree.
|
|
// Carry forward the scrollRef so scroll intent from a prior navigation
|
|
// survives tree rebuilds (e.g. push + refresh in the same batch).
|
|
return createCacheNode(existingCacheNode.rsc, dropPrefetchRsc ? null : existingCacheNode.prefetchRsc, existingCacheNode.head, dropPrefetchRsc ? null : existingCacheNode.prefetchHead, existingCacheNode.scrollRef);
|
|
}
|
|
function createCacheNodeForSegment(now, tree, seedRsc, metadataVaryPath, seedHead, freshness, dynamicStaleAt) {
|
|
// Construct a new CacheNode using data from the BFCache, the client's
|
|
// Segment Cache, or seeded from a server response.
|
|
//
|
|
// If there's a cache miss, or if we only have a partial hit, we'll render
|
|
// the partial state immediately, and spawn a request to the server to fill
|
|
// in the missing data.
|
|
//
|
|
// If the segment is fully cached on the client already, we can omit this
|
|
// segment from the server request.
|
|
//
|
|
// If we already have a dynamic data response associated with this navigation,
|
|
// as in the case of a Server Action-initiated redirect or refresh, we may
|
|
// also be able to use that data without spawning a new request. (This is
|
|
// referred to as the "seed" data.)
|
|
const isPage = tree.isPage;
|
|
// During certain kinds of navigations, we may be able to render from
|
|
// the BFCache.
|
|
switch(freshness){
|
|
case 0:
|
|
{
|
|
// Check BFCache during regular navigations. The entry's staleAt
|
|
// determines whether it's still fresh. This is used when
|
|
// staleTimes.dynamic is configured globally or when a page exports
|
|
// unstable_dynamicStaleTime for per-page control.
|
|
const bfcacheEntry = (0, _bfcache.readFromBFCacheDuringRegularNavigation)(now, tree.varyPath);
|
|
if (bfcacheEntry !== null) {
|
|
return {
|
|
cacheNode: createCacheNode(bfcacheEntry.rsc, bfcacheEntry.prefetchRsc, bfcacheEntry.head, bfcacheEntry.prefetchHead),
|
|
needsDynamicRequest: false
|
|
};
|
|
}
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
// This is not related to the BFCache but it is a special case.
|
|
//
|
|
// We should never spawn network requests during hydration. We must treat
|
|
// the initial payload as authoritative, because the initial page load is
|
|
// used as a last-ditch mechanism for recovering the app.
|
|
//
|
|
// This is also an important safety check because if this leaks into the
|
|
// server rendering path (which theoretically it never should because the
|
|
// server payload should be consistent), the server would hang because these
|
|
// promises would never resolve.
|
|
//
|
|
// TODO: There is an existing case where the global "not found" boundary
|
|
// triggers this path. But it does render correctly despite that. That's an
|
|
// unusual render path so it's not surprising, but we should look into
|
|
// modeling it in a more consistent way. See also the /_notFound special
|
|
// case in updateCacheNodeOnNavigation.
|
|
const rsc = seedRsc;
|
|
const prefetchRsc = null;
|
|
const head = isPage ? seedHead : null;
|
|
const prefetchHead = null;
|
|
(0, _bfcache.writeToBFCache)(now, tree.varyPath, rsc, prefetchRsc, head, prefetchHead, dynamicStaleAt);
|
|
if (isPage && metadataVaryPath !== null) {
|
|
(0, _bfcache.writeHeadToBFCache)(now, metadataVaryPath, head, prefetchHead, dynamicStaleAt);
|
|
}
|
|
return {
|
|
cacheNode: createCacheNode(rsc, prefetchRsc, head, prefetchHead),
|
|
needsDynamicRequest: false
|
|
};
|
|
}
|
|
case 2:
|
|
const bfcacheEntry = (0, _bfcache.readFromBFCache)(tree.varyPath);
|
|
if (bfcacheEntry !== null) {
|
|
// Only show prefetched data if the dynamic data is still pending. This
|
|
// avoids a flash back to the prefetch state in a case where it's highly
|
|
// likely to have already streamed in.
|
|
//
|
|
// Tehnically, what we're actually checking is whether the dynamic
|
|
// network response was received. But since it's a streaming response,
|
|
// this does not mean that all the dynamic data has fully streamed in.
|
|
// It just means that _some_ of the dynamic data was received. But as a
|
|
// heuristic, we assume that the rest dynamic data will stream in
|
|
// quickly, so it's still better to skip the prefetch state.
|
|
const oldRsc = bfcacheEntry.rsc;
|
|
const oldRscDidResolve = !isDeferredRsc(oldRsc) || oldRsc.status !== 'pending';
|
|
const dropPrefetchRsc = oldRscDidResolve;
|
|
return {
|
|
cacheNode: createCacheNode(bfcacheEntry.rsc, dropPrefetchRsc ? null : bfcacheEntry.prefetchRsc, bfcacheEntry.head, dropPrefetchRsc ? null : bfcacheEntry.prefetchHead),
|
|
needsDynamicRequest: false
|
|
};
|
|
}
|
|
break;
|
|
case 3:
|
|
case 4:
|
|
case 5:
|
|
break;
|
|
default:
|
|
freshness;
|
|
break;
|
|
}
|
|
let cachedRsc = null;
|
|
let isCachedRscPartial = true;
|
|
const segmentEntry = (0, _cache.readSegmentCacheEntry)(now, tree.varyPath);
|
|
if (segmentEntry !== null) {
|
|
switch(segmentEntry.status){
|
|
case _cache.EntryStatus.Fulfilled:
|
|
{
|
|
// Happy path: a cache hit
|
|
cachedRsc = segmentEntry.rsc;
|
|
isCachedRscPartial = segmentEntry.isPartial;
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Pending:
|
|
{
|
|
// We haven't received data for this segment yet, but there's already
|
|
// an in-progress request. Since it's extremely likely to arrive
|
|
// before the dynamic data response, we might as well use it.
|
|
const promiseForFulfilledEntry = (0, _cache.waitForSegmentCacheEntry)(segmentEntry);
|
|
cachedRsc = promiseForFulfilledEntry.then((entry)=>entry !== null ? entry.rsc : null);
|
|
// Because the request is still pending, we typically don't know yet
|
|
// whether the response will be partial. We shouldn't skip this segment
|
|
// during the dynamic navigation request. Otherwise, we might need to
|
|
// do yet another request to fill in the remaining data, creating
|
|
// a waterfall.
|
|
//
|
|
// The one exception is if this segment is being fetched with via
|
|
// prefetch={true} (i.e. the "force stale" or "full" strategy). If so,
|
|
// we can assume the response will be full. This field is set to `false`
|
|
// for such segments.
|
|
isCachedRscPartial = segmentEntry.isPartial;
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Empty:
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
segmentEntry;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Now combine the cached data with the seed data to determine what we can
|
|
// render immediately, versus what needs to stream in later.
|
|
// A partial state to show immediately while we wait for the final data to
|
|
// arrive. If `rsc` is already a complete value (not partial), or if we
|
|
// don't have any useful partial state, this will be `null`.
|
|
let prefetchRsc;
|
|
// The final, resolved segment data. If the data is missing, this will be a
|
|
// promise that resolves to the eventual data. A resolved value of `null`
|
|
// means the data failed to load; the LayoutRouter will suspend indefinitely
|
|
// until the router updates again (refer to finishNavigationTask).
|
|
let rsc;
|
|
let doesSegmentNeedDynamicRequest;
|
|
if (seedRsc !== null) {
|
|
// We already have a dynamic server response for this segment.
|
|
if (isCachedRscPartial) {
|
|
// The seed data may still be streaming in, so it's worth showing the
|
|
// partial cached state in the meantime.
|
|
prefetchRsc = cachedRsc;
|
|
rsc = seedRsc;
|
|
} else {
|
|
// We already have a completely cached segment. Ignore the seed data,
|
|
// which may still be streaming in. This shouldn't happen in the normal
|
|
// case because the client will inform the server which segments are
|
|
// already fully cached, and the server will skip rendering them.
|
|
prefetchRsc = null;
|
|
rsc = cachedRsc;
|
|
}
|
|
doesSegmentNeedDynamicRequest = false;
|
|
} else {
|
|
if (isCachedRscPartial) {
|
|
// The cached data contains dynamic holes, or it's missing entirely. We'll
|
|
// show the partial state immediately (if available), and stream in the
|
|
// final data.
|
|
//
|
|
// Create a pending promise that we can later write to when the
|
|
// data arrives from the server.
|
|
prefetchRsc = cachedRsc;
|
|
rsc = createDeferredRsc();
|
|
} else {
|
|
// The data is fully cached.
|
|
prefetchRsc = null;
|
|
rsc = cachedRsc;
|
|
}
|
|
doesSegmentNeedDynamicRequest = isCachedRscPartial;
|
|
}
|
|
// If this is a page segment, we need to do the same for the head. This
|
|
// follows analogous logic to the segment data above.
|
|
// TODO: We don't need to store the head on the page segment's CacheNode; we
|
|
// can lift it to the main state object. Then we can also delete
|
|
// findHeadCache.
|
|
let prefetchHead = null;
|
|
let head = null;
|
|
let doesHeadNeedDynamicRequest = isPage;
|
|
if (isPage) {
|
|
let cachedHead = null;
|
|
let isCachedHeadPartial = true;
|
|
if (metadataVaryPath !== null) {
|
|
const metadataEntry = (0, _cache.readSegmentCacheEntry)(now, metadataVaryPath);
|
|
if (metadataEntry !== null) {
|
|
switch(metadataEntry.status){
|
|
case _cache.EntryStatus.Fulfilled:
|
|
{
|
|
cachedHead = metadataEntry.rsc;
|
|
isCachedHeadPartial = metadataEntry.isPartial;
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Pending:
|
|
{
|
|
cachedHead = (0, _cache.waitForSegmentCacheEntry)(metadataEntry).then((entry)=>entry !== null ? entry.rsc : null);
|
|
isCachedHeadPartial = metadataEntry.isPartial;
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Empty:
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
metadataEntry;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (("TURBOPACK compile-time value", false) && isCachedHeadPartial) {
|
|
// TODO: When optimistic routing is enabled, don't block on waiting for
|
|
// the viewport to resolve. This is a temporary workaround until Vary
|
|
// Params are tracked when rendering the metadata. We'll fix it before
|
|
// this feature is stable. However, it's not a critical issue because 1)
|
|
// it will stream in eventually anyway 2) metadata is wrapped in an
|
|
// internal Suspense boundary, so is always non-blocking; this only
|
|
// affects the viewport node, which is meant to blocking, however... 3)
|
|
// before Segment Cache landed this wasn't always the case, anyway, so
|
|
// it's unlikely that many people are relying on this behavior. Still,
|
|
// will be fixed before stable. It's the very next step in the sequence of
|
|
// work on this project.
|
|
//
|
|
// This line of code works because the App Router treats `null` as
|
|
// "no renderable head available", rather than an empty head. React treats
|
|
// an empty string as empty.
|
|
cachedHead = '';
|
|
}
|
|
if (seedHead !== null) {
|
|
if (isCachedHeadPartial) {
|
|
prefetchHead = cachedHead;
|
|
head = seedHead;
|
|
} else {
|
|
prefetchHead = null;
|
|
head = cachedHead;
|
|
}
|
|
doesHeadNeedDynamicRequest = false;
|
|
} else {
|
|
if (isCachedHeadPartial) {
|
|
prefetchHead = cachedHead;
|
|
head = createDeferredRsc();
|
|
} else {
|
|
prefetchHead = null;
|
|
head = cachedHead;
|
|
}
|
|
doesHeadNeedDynamicRequest = isCachedHeadPartial;
|
|
}
|
|
}
|
|
// Now that we're creating a new segment, write its data to the BFCache. A
|
|
// subsequent back/forward navigation will reuse this same data, until or
|
|
// unless it's cleared by a refresh/revalidation.
|
|
//
|
|
// Skip BFCache writes for optimistic navigations since they are transient
|
|
// and will be replaced by the canonical navigation.
|
|
if (freshness !== 5) {
|
|
(0, _bfcache.writeToBFCache)(now, tree.varyPath, rsc, prefetchRsc, head, prefetchHead, dynamicStaleAt);
|
|
if (isPage && metadataVaryPath !== null) {
|
|
(0, _bfcache.writeHeadToBFCache)(now, metadataVaryPath, head, prefetchHead, dynamicStaleAt);
|
|
}
|
|
}
|
|
return {
|
|
cacheNode: createCacheNode(rsc, prefetchRsc, head, prefetchHead),
|
|
// TODO: We should store this field on the CacheNode itself. I think we can
|
|
// probably unify NavigationTask, CacheNode, and DeferredRsc into a
|
|
// single type. Or at least CacheNode and DeferredRsc.
|
|
needsDynamicRequest: doesSegmentNeedDynamicRequest || doesHeadNeedDynamicRequest
|
|
};
|
|
}
|
|
function createCacheNode(rsc, prefetchRsc, head, prefetchHead, scrollRef = null) {
|
|
return {
|
|
rsc,
|
|
prefetchRsc,
|
|
head,
|
|
prefetchHead,
|
|
slots: null,
|
|
scrollRef
|
|
};
|
|
}
|
|
// Represents whether the previuos navigation resulted in a route tree mismatch.
|
|
// A mismatch results in a refresh of the page. If there are two successive
|
|
// mismatches, we will fall back to an MPA navigation, to prevent a retry loop.
|
|
let previousNavigationDidMismatch = false;
|
|
function spawnDynamicRequests(task, primaryUrl, nextUrl, freshnessPolicy, accumulation, // prediction. Passed through so it can be marked as having a dynamic rewrite
|
|
// if the server returns a different pathname than expected (indicating
|
|
// dynamic rewrite behavior that varies by param value).
|
|
routeCacheEntry, // server-patch retry logic so it can inherit the intent if the original
|
|
// transition hasn't committed yet.
|
|
navigateType) {
|
|
const dynamicRequestTree = task.dynamicRequestTree;
|
|
if (dynamicRequestTree === null) {
|
|
// This navigation was fully cached. There are no dynamic requests to spawn.
|
|
previousNavigationDidMismatch = false;
|
|
return;
|
|
}
|
|
// This is intentionally not an async function to discourage the caller from
|
|
// awaiting the result. Any subsequent async operations spawned by this
|
|
// function should result in a separate navigation task, rather than
|
|
// block the original one.
|
|
//
|
|
// In this function we spawn (but do not await) all the network requests that
|
|
// block the navigation, and collect the promises. The next function,
|
|
// `finishNavigationTask`, can await the promises in any order without
|
|
// accidentally introducing a network waterfall.
|
|
const primaryRequestPromise = fetchMissingDynamicData(task, dynamicRequestTree, primaryUrl, nextUrl, freshnessPolicy, routeCacheEntry);
|
|
const separateRefreshUrls = accumulation.separateRefreshUrls;
|
|
let refreshRequestPromises = null;
|
|
if (separateRefreshUrls !== null) {
|
|
// There are multiple URLs that we need to request the data from. This
|
|
// happens when a "default" parallel route slot is present in the tree, and
|
|
// its data cannot be fetched from the current route. We need to split the
|
|
// combined dynamic request tree into separate requests per URL.
|
|
// TODO: Create a scoped dynamic request tree that omits anything that
|
|
// is not relevant to the given URL. Without doing this, the server may
|
|
// sometimes render more data than necessary; this is not a regression
|
|
// compared to the pre-Segment Cache implementation, though, just an
|
|
// optimization we can make in the future.
|
|
// Construct a request tree for each additional refresh URL. This will
|
|
// prune away everything except the parts of the tree that match the
|
|
// given refresh URL.
|
|
refreshRequestPromises = [];
|
|
const canonicalUrl = (0, _createhreffromurl.createHrefFromUrl)(primaryUrl);
|
|
for (const refreshUrl of separateRefreshUrls){
|
|
if (refreshUrl === canonicalUrl) {
|
|
continue;
|
|
}
|
|
// TODO: Create a scoped dynamic request tree that omits anything that
|
|
// is not relevant to the given URL. Without doing this, the server may
|
|
// sometimes render more data than necessary; this is not a regression
|
|
// compared to the pre-Segment Cache implementation, though, just an
|
|
// optimization we can make in the future.
|
|
// const scopedDynamicRequestTree = splitTaskByURL(task, refreshUrl)
|
|
const scopedDynamicRequestTree = dynamicRequestTree;
|
|
if (scopedDynamicRequestTree !== null) {
|
|
refreshRequestPromises.push(fetchMissingDynamicData(task, scopedDynamicRequestTree, new URL(refreshUrl, location.origin), // time the refresh URL was set, not the current Next-Url. Need to
|
|
// start tracking this alongside the refresh URL. In the meantime,
|
|
// if a refresh fails due to a mismatch, it will trigger a
|
|
// hard refresh.
|
|
nextUrl, freshnessPolicy, routeCacheEntry));
|
|
}
|
|
}
|
|
}
|
|
// Further async operations are moved into this separate function to
|
|
// discourage sequential network requests.
|
|
const voidPromise = finishNavigationTask(task, nextUrl, primaryRequestPromise, refreshRequestPromises, routeCacheEntry, navigateType);
|
|
// `finishNavigationTask` is responsible for error handling, so we can attach
|
|
// noop callbacks to this promise.
|
|
voidPromise.then(noop, noop);
|
|
}
|
|
async function finishNavigationTask(task, nextUrl, primaryRequestPromise, refreshRequestPromises, routeCacheEntry, navigateType) {
|
|
// Wait for all the requests to finish, or for the first one to fail.
|
|
let exitStatus = await waitForRequestsToFinish(primaryRequestPromise, refreshRequestPromises);
|
|
// Once the all the requests have finished, check the tree for any remaining
|
|
// pending tasks. If anything is still pending, it means the server response
|
|
// does not match the client, and we must refresh to get back to a consistent
|
|
// state. We can skip this step if we already detected a mismatch during the
|
|
// first phase; it doesn't matter in that case because we're going to refresh
|
|
// the whole tree regardless.
|
|
if (exitStatus === 0) {
|
|
exitStatus = abortRemainingPendingTasks(task, null, null);
|
|
}
|
|
switch(exitStatus){
|
|
case 0:
|
|
{
|
|
// The task has completely finished. There's no missing data. Exit.
|
|
previousNavigationDidMismatch = false;
|
|
return;
|
|
}
|
|
case 1:
|
|
{
|
|
// Some data failed to finish loading. Trigger a soft retry.
|
|
// TODO: As an extra precaution against soft retry loops, consider
|
|
// tracking whether a navigation was itself triggered by a retry. If two
|
|
// happen in a row, fall back to a hard retry.
|
|
const isHardRetry = false;
|
|
const primaryRequestResult = await primaryRequestPromise;
|
|
dispatchRetryDueToTreeMismatch(isHardRetry, primaryRequestResult.url, nextUrl, primaryRequestResult.seed, task.route, routeCacheEntry, navigateType);
|
|
return;
|
|
}
|
|
case 2:
|
|
{
|
|
// Some data failed to finish loading in a non-recoverable way, such as a
|
|
// network error. Trigger an MPA navigation.
|
|
//
|
|
// Hard navigating/refreshing is how we prevent an infinite retry loop
|
|
// caused by a network error — when the network fails, we fall back to the
|
|
// browser behavior for offline navigations. In the future, Next.js may
|
|
// introduce its own custom handling of offline navigations, but that
|
|
// doesn't exist yet.
|
|
const isHardRetry = true;
|
|
const primaryRequestResult = await primaryRequestPromise;
|
|
dispatchRetryDueToTreeMismatch(isHardRetry, primaryRequestResult.url, nextUrl, primaryRequestResult.seed, task.route, routeCacheEntry, navigateType);
|
|
return;
|
|
}
|
|
default:
|
|
{
|
|
return exitStatus;
|
|
}
|
|
}
|
|
}
|
|
function waitForRequestsToFinish(primaryRequestPromise, refreshRequestPromises) {
|
|
// Custom async combinator logic. This could be replaced by Promise.any but
|
|
// we don't assume that's available.
|
|
//
|
|
// Each promise resolves once the server responsds and the data is written
|
|
// into the CacheNode tree. Resolve the combined promise once all the
|
|
// requests finish.
|
|
//
|
|
// Or, resolve as soon as one of the requests fails, without waiting for the
|
|
// others to finish.
|
|
return new Promise((resolve)=>{
|
|
const onFulfill = (result)=>{
|
|
if (result.exitStatus === 0) {
|
|
remainingCount--;
|
|
if (remainingCount === 0) {
|
|
// All the requests finished successfully.
|
|
resolve(0);
|
|
}
|
|
} else {
|
|
// One of the requests failed. Exit with a failing status.
|
|
// NOTE: It's possible for one of the requests to fail with SoftRetry
|
|
// and a later one to fail with HardRetry. In this case, we choose to
|
|
// retry immediately, rather than delay the retry until all the requests
|
|
// finish. If it fails again, we will hard retry on the next
|
|
// attempt, anyway.
|
|
resolve(result.exitStatus);
|
|
}
|
|
};
|
|
// onReject shouldn't ever be called because fetchMissingDynamicData's
|
|
// entire body is wrapped in a try/catch. This is just defensive.
|
|
const onReject = ()=>resolve(2);
|
|
// Attach the listeners to the promises.
|
|
let remainingCount = 1;
|
|
primaryRequestPromise.then(onFulfill, onReject);
|
|
if (refreshRequestPromises !== null) {
|
|
remainingCount += refreshRequestPromises.length;
|
|
refreshRequestPromises.forEach((refreshRequestPromise)=>refreshRequestPromise.then(onFulfill, onReject));
|
|
}
|
|
});
|
|
}
|
|
function dispatchRetryDueToTreeMismatch(isHardRetry, retryUrl, retryNextUrl, seed, baseTree, // prediction. If the navigation results in a mismatch, we mark it as having
|
|
// a dynamic rewrite so future predictions bail out.
|
|
routeCacheEntry, originalNavigateType) {
|
|
// If the navigation used a route prediction, mark it as having a dynamic
|
|
// rewrite since it resulted in a mismatch.
|
|
if (routeCacheEntry !== null) {
|
|
(0, _cache.markRouteEntryAsDynamicRewrite)(routeCacheEntry);
|
|
} else if (seed !== null) {
|
|
// Even without a direct reference to the route cache entry, we can still
|
|
// mark the route as having a dynamic rewrite by traversing the known route
|
|
// tree. This handles cases where the navigation didn't originate from a
|
|
// route prediction, but still needs to mark the pattern.
|
|
const metadataVaryPath = seed.metadataVaryPath;
|
|
if (metadataVaryPath !== null) {
|
|
const now = Date.now();
|
|
(0, _optimisticroutes.discoverKnownRoute)(now, retryUrl.pathname, retryNextUrl, null, seed.routeTree, metadataVaryPath, false, (0, _createhreffromurl.createHrefFromUrl)(retryUrl), false, true // hasDynamicRewrite
|
|
);
|
|
}
|
|
}
|
|
// Invalidate all route cache entries. Other entries may have been derived
|
|
// from the template before we knew it had a dynamic rewrite. This also
|
|
// triggers re-prefetching of visible links.
|
|
(0, _cache.invalidateRouteCacheEntries)(retryNextUrl, baseTree);
|
|
// If this is the second time in a row that a navigation resulted in a
|
|
// mismatch, fall back to a hard (MPA) refresh.
|
|
isHardRetry = isHardRetry || previousNavigationDidMismatch;
|
|
previousNavigationDidMismatch = true;
|
|
// If the original navigation hasn't committed to the browser history yet
|
|
// (the transition suspended before React committed), inherit its push/replace
|
|
// intent. Otherwise, the pushState already ran, so use 'replace' to avoid
|
|
// creating a duplicate history entry.
|
|
//
|
|
// This works because React entangles the retry's state update with the
|
|
// original pending transition — they commit together as a single batch,
|
|
// so the navigate type from the retry is what HistoryUpdater ultimately sees.
|
|
//
|
|
// TODO: Ideally this check would happen right before we schedule the React
|
|
// update (i.e., closer to where the action is dispatched into the queue),
|
|
// not here where the action is constructed. But the current action queue
|
|
// doesn't provide a natural place for that. Revisit when we refactor the
|
|
// action queue into a more reactive navigation model.
|
|
const lastCommitted = (0, _committedstate.getLastCommittedTree)();
|
|
const retryNavigateType = lastCommitted !== null && baseTree !== lastCommitted ? originalNavigateType : 'replace';
|
|
const retryAction = {
|
|
type: _routerreducertypes.ACTION_SERVER_PATCH,
|
|
previousTree: baseTree,
|
|
url: retryUrl,
|
|
nextUrl: retryNextUrl,
|
|
seed,
|
|
mpa: isHardRetry,
|
|
navigateType: retryNavigateType
|
|
};
|
|
(0, _useactionqueue.dispatchAppRouterAction)(retryAction);
|
|
}
|
|
async function fetchMissingDynamicData(task, dynamicRequestTree, url, nextUrl, freshnessPolicy, routeCacheEntry) {
|
|
try {
|
|
const result = await (0, _fetchserverresponse.fetchServerResponse)(url, {
|
|
flightRouterState: dynamicRequestTree,
|
|
nextUrl,
|
|
isHmrRefresh: freshnessPolicy === 4
|
|
});
|
|
if (typeof result === 'string') {
|
|
// fetchServerResponse will return an href to indicate that the SPA
|
|
// navigation failed. For example, if the server triggered a hard
|
|
// redirect, or the fetch request errored. Initiate an MPA navigation
|
|
// to the given href.
|
|
return {
|
|
exitStatus: 2,
|
|
url: new URL(result, location.origin),
|
|
seed: null
|
|
};
|
|
}
|
|
const now = Date.now();
|
|
const seed = (0, _navigation.convertServerPatchToFullTree)(now, task.route, result.flightData, result.renderedSearch, result.dynamicStaleTime);
|
|
// If the navigation lock is active, wait for it to be released before
|
|
// writing the dynamic data. This allows tests to assert on the prefetched
|
|
// UI state.
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
await waitForNavigationLock();
|
|
}
|
|
if (routeCacheEntry !== null && result.staticStageData !== null) {
|
|
const { response: staticStageResponse, isResponsePartial } = result.staticStageData;
|
|
(0, _cache.getStaleAt)(now, staticStageResponse.s).then((staleAt)=>{
|
|
const buildId = result.responseHeaders.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? staticStageResponse.b;
|
|
(0, _cache.writeStaticStageResponseIntoCache)(now, staticStageResponse.f, buildId, staticStageResponse.h, staleAt, dynamicRequestTree, result.renderedSearch, isResponsePartial);
|
|
}).catch(()=>{
|
|
// The static stage processing failed. Not fatal — the navigation
|
|
// completed normally, we just won't write into the cache.
|
|
});
|
|
}
|
|
if (routeCacheEntry !== null && result.runtimePrefetchStream !== null) {
|
|
(0, _cache.processRuntimePrefetchStream)(now, result.runtimePrefetchStream, dynamicRequestTree, result.renderedSearch).then((processed)=>{
|
|
if (processed !== null) {
|
|
(0, _cache.writeDynamicRenderResponseIntoCache)(now, _types.FetchStrategy.PPRRuntime, processed.flightDatas, processed.buildId, processed.isResponsePartial, processed.headVaryParams, processed.staleAt, processed.navigationSeed, null);
|
|
}
|
|
}).catch(()=>{
|
|
// The runtime prefetch cache write failed. Not fatal — the
|
|
// navigation completed normally, we just won't cache runtime data.
|
|
});
|
|
}
|
|
// result.dynamicStaleTime is in seconds (from the server's `d` field).
|
|
// Convert to an absolute timestamp using the centralized helper.
|
|
const dynamicStaleAt = (0, _bfcache.computeDynamicStaleAt)(now, result.dynamicStaleTime);
|
|
const didReceiveUnknownParallelRoute = writeDynamicDataIntoNavigationTask(task, seed.routeTree, seed.data, seed.head, dynamicStaleAt, result.debugInfo);
|
|
return {
|
|
exitStatus: didReceiveUnknownParallelRoute ? 1 : 0,
|
|
url: new URL(result.canonicalUrl, location.origin),
|
|
seed
|
|
};
|
|
} catch {
|
|
// This shouldn't happen because fetchServerResponse's entire body is
|
|
// wrapped in a try/catch. If it does, though, it implies the server failed
|
|
// to respond with any tree at all. So we must fall back to a hard retry.
|
|
return {
|
|
exitStatus: 2,
|
|
url: url,
|
|
seed: null
|
|
};
|
|
}
|
|
}
|
|
function writeDynamicDataIntoNavigationTask(task, serverRouteTree, dynamicData, dynamicHead, dynamicStaleAt, debugInfo) {
|
|
if (task.status === 0 && dynamicData !== null) {
|
|
task.status = 1;
|
|
finishPendingCacheNode(task.node, dynamicData, dynamicHead, debugInfo);
|
|
// Update the BFCache entry's staleAt for this segment with the value
|
|
// from the dynamic response. This applies the per-page
|
|
// unstable_dynamicStaleTime if set, or the default DYNAMIC_STALETIME_MS.
|
|
// We only update segments that received dynamic data — static segments
|
|
// are unaffected.
|
|
(0, _bfcache.updateBFCacheEntryStaleAt)(serverRouteTree.varyPath, dynamicStaleAt);
|
|
}
|
|
const taskChildren = task.children;
|
|
const serverChildren = serverRouteTree.slots;
|
|
const dynamicDataChildren = dynamicData !== null ? dynamicData[1] : null;
|
|
// Detect whether the server sends a parallel route slot that the client
|
|
// doesn't know about.
|
|
let didReceiveUnknownParallelRoute = false;
|
|
if (taskChildren !== null) {
|
|
if (serverChildren !== null) {
|
|
for(const parallelRouteKey in serverChildren){
|
|
const serverRouteTreeChild = serverChildren[parallelRouteKey];
|
|
const dynamicDataChild = dynamicDataChildren !== null ? dynamicDataChildren[parallelRouteKey] : null;
|
|
const taskChild = taskChildren.get(parallelRouteKey);
|
|
if (taskChild === undefined) {
|
|
// The server sent a child segment that the client doesn't know about.
|
|
//
|
|
// When we receive an unknown parallel route, we must consider it a
|
|
// mismatch. This is unlike the case where the segment itself
|
|
// mismatches, because multiple routes can be active simultaneously.
|
|
// But a given layout should never have a mismatching set of
|
|
// child slots.
|
|
//
|
|
// Theoretically, this should only happen in development during an HMR
|
|
// refresh, because the set of parallel routes for a layout does not
|
|
// change over the lifetime of a build/deployment. In production, we
|
|
// should have already mismatched on either the build id or the segment
|
|
// path. But as an extra precaution, we validate in prod, too.
|
|
didReceiveUnknownParallelRoute = true;
|
|
} else {
|
|
const taskSegment = taskChild.route[0];
|
|
const serverSegment = createSegmentFromRouteTree(serverRouteTreeChild);
|
|
if ((0, _matchsegments.matchSegment)(serverSegment, taskSegment) && dynamicDataChild !== null && dynamicDataChild !== undefined) {
|
|
// Found a match for this task. Keep traversing down the task tree.
|
|
const childDidReceiveUnknownParallelRoute = writeDynamicDataIntoNavigationTask(taskChild, serverRouteTreeChild, dynamicDataChild, dynamicHead, dynamicStaleAt, debugInfo);
|
|
if (childDidReceiveUnknownParallelRoute) {
|
|
didReceiveUnknownParallelRoute = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
if (serverChildren !== null) {
|
|
// The server sent a child segment that the client doesn't know about.
|
|
didReceiveUnknownParallelRoute = true;
|
|
}
|
|
}
|
|
}
|
|
return didReceiveUnknownParallelRoute;
|
|
}
|
|
function finishPendingCacheNode(cacheNode, dynamicData, dynamicHead, debugInfo) {
|
|
// Writes a dynamic response into an existing Cache Node tree. This does _not_
|
|
// create a new tree, it updates the existing tree in-place. So it must follow
|
|
// the Suspense rules of cache safety — it can resolve pending promises, but
|
|
// it cannot overwrite existing data. It can add segments to the tree (because
|
|
// a missing segment will cause the layout router to suspend).
|
|
// but it cannot delete them.
|
|
//
|
|
// We must resolve every promise in the tree, or else it will suspend
|
|
// indefinitely. If we did not receive data for a segment, we will resolve its
|
|
// data promise to `null` to trigger a lazy fetch during render.
|
|
// Use the dynamic data from the server to fulfill the deferred RSC promise
|
|
// on the Cache Node.
|
|
const rsc = cacheNode.rsc;
|
|
const dynamicSegmentData = dynamicData[0];
|
|
if (dynamicSegmentData === null) {
|
|
// This is an empty CacheNode; this particular server request did not
|
|
// render this segment. There may be a separate pending request that will,
|
|
// though, so we won't abort the task until all pending requests finish.
|
|
return;
|
|
}
|
|
if (rsc === null) {
|
|
// This is a lazy cache node. We can overwrite it. This is only safe
|
|
// because we know that the LayoutRouter suspends if `rsc` is `null`.
|
|
cacheNode.rsc = dynamicSegmentData;
|
|
} else if (isDeferredRsc(rsc)) {
|
|
// This is a deferred RSC promise. We can fulfill it with the data we just
|
|
// received from the server. If it was already resolved by a different
|
|
// navigation, then this does nothing because we can't overwrite data.
|
|
rsc.resolve(dynamicSegmentData, debugInfo);
|
|
} else {
|
|
// This is not a deferred RSC promise, nor is it empty, so it must have
|
|
// been populated by a different navigation. We must not overwrite it.
|
|
}
|
|
// Check if this is a leaf segment. If so, it will have a `head` property with
|
|
// a pending promise that needs to be resolved with the dynamic head from
|
|
// the server.
|
|
const head = cacheNode.head;
|
|
if (isDeferredRsc(head)) {
|
|
head.resolve(dynamicHead, debugInfo);
|
|
}
|
|
}
|
|
function abortRemainingPendingTasks(task, error, debugInfo) {
|
|
let exitStatus;
|
|
if (task.status === 0) {
|
|
// The data for this segment is still missing.
|
|
task.status = 2;
|
|
abortPendingCacheNode(task.node, error, debugInfo);
|
|
// If the server failed to fulfill the data for this segment, it implies
|
|
// that the route tree received from the server mismatched the tree that
|
|
// was previously prefetched.
|
|
//
|
|
// In an app with fully static routes and no proxy-driven redirects or
|
|
// rewrites, this should never happen, because the route for a URL would
|
|
// always be the same across multiple requests. So, this implies that some
|
|
// runtime routing condition changed, likely in a proxy, without being
|
|
// pushed to the client.
|
|
//
|
|
// When this happens, we treat this the same as a refresh(). The entire
|
|
// tree will be re-rendered from the root.
|
|
if (task.refreshState === null) {
|
|
// Trigger a "soft" refresh. Essentially the same as calling `refresh()`
|
|
// in a Server Action.
|
|
exitStatus = 1;
|
|
} else {
|
|
// The mismatch was discovered inside an inactive parallel route. This
|
|
// implies the inactive parallel route is no longer reachable at the URL
|
|
// that originally rendered it. Fall back to an MPA refresh.
|
|
// TODO: An alternative could be to trigger a soft refresh but to _not_
|
|
// re-use the inactive parallel routes this time. Similar to what would
|
|
// happen if were to do a hard refrehs, but without the HTML page.
|
|
exitStatus = 2;
|
|
}
|
|
} else {
|
|
// This segment finished. (An error here is treated as Done because they are
|
|
// surfaced to the application during render.)
|
|
exitStatus = 0;
|
|
}
|
|
const taskChildren = task.children;
|
|
if (taskChildren !== null) {
|
|
for (const [, taskChild] of taskChildren){
|
|
const childExitStatus = abortRemainingPendingTasks(taskChild, error, debugInfo);
|
|
// Propagate the exit status up the tree. The statuses are ordered by
|
|
// their precedence.
|
|
if (childExitStatus > exitStatus) {
|
|
exitStatus = childExitStatus;
|
|
}
|
|
}
|
|
}
|
|
return exitStatus;
|
|
}
|
|
function abortPendingCacheNode(cacheNode, error, debugInfo) {
|
|
const rsc = cacheNode.rsc;
|
|
if (isDeferredRsc(rsc)) {
|
|
if (error === null) {
|
|
// This will trigger a lazy fetch during render.
|
|
rsc.resolve(null, debugInfo);
|
|
} else {
|
|
// This will trigger an error during rendering.
|
|
rsc.reject(error, debugInfo);
|
|
}
|
|
}
|
|
// Check if this is a leaf segment. If so, it will have a `head` property with
|
|
// a pending promise that needs to be resolved. If an error was provided, we
|
|
// will not resolve it with an error, since this is rendered at the root of
|
|
// the app. We want the segment to error, not the entire app.
|
|
const head = cacheNode.head;
|
|
if (isDeferredRsc(head)) {
|
|
head.resolve(null, debugInfo);
|
|
}
|
|
}
|
|
const DEFERRED = Symbol();
|
|
function isDeferredRsc(value) {
|
|
return value && typeof value === 'object' && value.tag === DEFERRED;
|
|
}
|
|
function createDeferredRsc() {
|
|
// Create an unresolved promise that represents data derived from a Flight
|
|
// response. The promise will be resolved later as soon as we start receiving
|
|
// data from the server, i.e. as soon as the Flight client decodes and returns
|
|
// the top-level response object.
|
|
// The `_debugInfo` field contains profiling information. Promises that are
|
|
// created by Flight already have this info added by React; for any derived
|
|
// promise created by the router, we need to transfer the Flight debug info
|
|
// onto the derived promise.
|
|
//
|
|
// The debug info represents the latency between the start of the navigation
|
|
// and the start of rendering. (It does not represent the time it takes for
|
|
// whole stream to finish.)
|
|
const debugInfo = [];
|
|
let resolve;
|
|
let reject;
|
|
const pendingRsc = new Promise((res, rej)=>{
|
|
resolve = res;
|
|
reject = rej;
|
|
});
|
|
pendingRsc.status = 'pending';
|
|
pendingRsc.resolve = (value, responseDebugInfo)=>{
|
|
if (pendingRsc.status === 'pending') {
|
|
const fulfilledRsc = pendingRsc;
|
|
fulfilledRsc.status = 'fulfilled';
|
|
fulfilledRsc.value = value;
|
|
if (responseDebugInfo !== null) {
|
|
// Transfer the debug info to the derived promise.
|
|
debugInfo.push.apply(debugInfo, responseDebugInfo);
|
|
}
|
|
resolve(value);
|
|
}
|
|
};
|
|
pendingRsc.reject = (error, responseDebugInfo)=>{
|
|
if (pendingRsc.status === 'pending') {
|
|
const rejectedRsc = pendingRsc;
|
|
rejectedRsc.status = 'rejected';
|
|
rejectedRsc.reason = error;
|
|
if (responseDebugInfo !== null) {
|
|
// Transfer the debug info to the derived promise.
|
|
debugInfo.push.apply(debugInfo, responseDebugInfo);
|
|
}
|
|
reject(error);
|
|
}
|
|
};
|
|
pendingRsc.tag = DEFERRED;
|
|
pendingRsc._debugInfo = debugInfo;
|
|
return pendingRsc;
|
|
}
|
|
/**
|
|
* Helper for the Instant Navigation Testing API. Waits for the navigation lock
|
|
* to be released before returning. The network request has already completed by
|
|
* the time this is called, so this only delays writing the dynamic data.
|
|
*
|
|
* Not exposed in production builds by default.
|
|
*/ async function waitForNavigationLock() {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
const { waitForNavigationLockIfActive } = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation-testing-lock.js [app-ssr] (ecmascript)");
|
|
await waitForNavigationLockIfActive();
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/page-path/ensure-leading-slash.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
/**
|
|
* For a given page path, this function ensures that there is a leading slash.
|
|
* If there is not a leading slash, one is added, otherwise it is noop.
|
|
*/ Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "ensureLeadingSlash", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return ensureLeadingSlash;
|
|
}
|
|
});
|
|
function ensureLeadingSlash(path) {
|
|
return path.startsWith('/') ? path : `/${path}`;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/app-paths.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
compareAppPaths: null,
|
|
normalizeAppPath: null,
|
|
normalizeRscURL: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
compareAppPaths: function() {
|
|
return compareAppPaths;
|
|
},
|
|
normalizeAppPath: function() {
|
|
return normalizeAppPath;
|
|
},
|
|
normalizeRscURL: function() {
|
|
return normalizeRscURL;
|
|
}
|
|
});
|
|
const _ensureleadingslash = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/page-path/ensure-leading-slash.js [app-ssr] (ecmascript)");
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
function normalizeAppPath(route) {
|
|
return (0, _ensureleadingslash.ensureLeadingSlash)(route.split('/').reduce((pathname, segment, index, segments)=>{
|
|
// Empty segments are ignored.
|
|
if (!segment) {
|
|
return pathname;
|
|
}
|
|
// Groups are ignored.
|
|
if ((0, _segment.isGroupSegment)(segment)) {
|
|
return pathname;
|
|
}
|
|
// Parallel segments are ignored.
|
|
if (segment[0] === '@') {
|
|
return pathname;
|
|
}
|
|
// The last segment (if it's a leaf) should be ignored.
|
|
if ((segment === 'page' || segment === 'route') && index === segments.length - 1) {
|
|
return pathname;
|
|
}
|
|
return `${pathname}/${segment}`;
|
|
}, ''));
|
|
}
|
|
function compareAppPaths(a, b) {
|
|
const aHasSlot = a.includes('/@');
|
|
const bHasSlot = b.includes('/@');
|
|
if (aHasSlot && !bHasSlot) return -1;
|
|
if (!aHasSlot && bHasSlot) return 1;
|
|
return a.localeCompare(b);
|
|
}
|
|
function normalizeRscURL(url) {
|
|
return url.replace(/\.rsc($|\?)/, '$1');
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/interception-routes.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
INTERCEPTION_ROUTE_MARKERS: null,
|
|
extractInterceptionRouteInformation: null,
|
|
isInterceptionRouteAppPath: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
INTERCEPTION_ROUTE_MARKERS: function() {
|
|
return INTERCEPTION_ROUTE_MARKERS;
|
|
},
|
|
extractInterceptionRouteInformation: function() {
|
|
return extractInterceptionRouteInformation;
|
|
},
|
|
isInterceptionRouteAppPath: function() {
|
|
return isInterceptionRouteAppPath;
|
|
}
|
|
});
|
|
const _apppaths = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/app-paths.js [app-ssr] (ecmascript)");
|
|
const INTERCEPTION_ROUTE_MARKERS = [
|
|
'(..)(..)',
|
|
'(.)',
|
|
'(..)',
|
|
'(...)'
|
|
];
|
|
function isInterceptionRouteAppPath(path) {
|
|
// TODO-APP: add more serious validation
|
|
return path.split('/').find((segment)=>INTERCEPTION_ROUTE_MARKERS.find((m)=>segment.startsWith(m))) !== undefined;
|
|
}
|
|
function extractInterceptionRouteInformation(path) {
|
|
let interceptingRoute;
|
|
let marker;
|
|
let interceptedRoute;
|
|
for (const segment of path.split('/')){
|
|
marker = INTERCEPTION_ROUTE_MARKERS.find((m)=>segment.startsWith(m));
|
|
if (marker) {
|
|
;
|
|
[interceptingRoute, interceptedRoute] = path.split(marker, 2);
|
|
break;
|
|
}
|
|
}
|
|
if (!interceptingRoute || !marker || !interceptedRoute) {
|
|
throw Object.defineProperty(new Error(`Invalid interception route: ${path}. Must be in the format /<intercepting route>/(..|...|..)(..)/<intercepted route>`), "__NEXT_ERROR_CODE", {
|
|
value: "E269",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
interceptingRoute = (0, _apppaths.normalizeAppPath)(interceptingRoute) // normalize the path, e.g. /(blog)/feed -> /feed
|
|
;
|
|
switch(marker){
|
|
case '(.)':
|
|
// (.) indicates that we should match with sibling routes, so we just need to append the intercepted route to the intercepting route
|
|
if (interceptingRoute === '/') {
|
|
interceptedRoute = `/${interceptedRoute}`;
|
|
} else {
|
|
interceptedRoute = interceptingRoute + '/' + interceptedRoute;
|
|
}
|
|
break;
|
|
case '(..)':
|
|
// (..) indicates that we should match at one level up, so we need to remove the last segment of the intercepting route
|
|
if (interceptingRoute === '/') {
|
|
throw Object.defineProperty(new Error(`Invalid interception route: ${path}. Cannot use (..) marker at the root level, use (.) instead.`), "__NEXT_ERROR_CODE", {
|
|
value: "E207",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
interceptedRoute = interceptingRoute.split('/').slice(0, -1).concat(interceptedRoute).join('/');
|
|
break;
|
|
case '(...)':
|
|
// (...) will match the route segment in the root directory, so we need to use the root directory to prepend the intercepted route
|
|
interceptedRoute = '/' + interceptedRoute;
|
|
break;
|
|
case '(..)(..)':
|
|
// (..)(..) indicates that we should match at two levels up, so we need to remove the last two segments of the intercepting route
|
|
const splitInterceptingRoute = interceptingRoute.split('/');
|
|
if (splitInterceptingRoute.length <= 2) {
|
|
throw Object.defineProperty(new Error(`Invalid interception route: ${path}. Cannot use (..)(..) marker at the root level or one level up.`), "__NEXT_ERROR_CODE", {
|
|
value: "E486",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
interceptedRoute = splitInterceptingRoute.slice(0, -2).concat(interceptedRoute).join('/');
|
|
break;
|
|
default:
|
|
throw Object.defineProperty(new Error('Invariant: unexpected marker'), "__NEXT_ERROR_CODE", {
|
|
value: "E112",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
return {
|
|
interceptingRoute,
|
|
interceptedRoute
|
|
};
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/compute-changed-path.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
computeChangedPath: null,
|
|
extractPathFromFlightRouterState: null,
|
|
extractSourcePageFromFlightRouterState: null,
|
|
getSelectedParams: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
computeChangedPath: function() {
|
|
return computeChangedPath;
|
|
},
|
|
extractPathFromFlightRouterState: function() {
|
|
return extractPathFromFlightRouterState;
|
|
},
|
|
extractSourcePageFromFlightRouterState: function() {
|
|
return extractSourcePageFromFlightRouterState;
|
|
},
|
|
getSelectedParams: function() {
|
|
return getSelectedParams;
|
|
}
|
|
});
|
|
const _interceptionroutes = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/interception-routes.js [app-ssr] (ecmascript)");
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const _matchsegments = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/match-segments.js [app-ssr] (ecmascript)");
|
|
const removeLeadingSlash = (segment)=>{
|
|
return segment[0] === '/' ? segment.slice(1) : segment;
|
|
};
|
|
const segmentToPathname = (segment)=>{
|
|
if (typeof segment === 'string') {
|
|
// 'children' is not a valid path -- it's technically a parallel route that corresponds with the current segment's page
|
|
// if we don't skip it, then the computed pathname might be something like `/children` which doesn't make sense.
|
|
if (segment === 'children') return '';
|
|
return segment;
|
|
}
|
|
return segment[1];
|
|
};
|
|
const segmentToSourcePagePathname = (segment)=>{
|
|
if (typeof segment === 'string') {
|
|
if (segment === 'children') return '';
|
|
if (segment.startsWith(_segment.PAGE_SEGMENT_KEY)) return 'page';
|
|
return segment;
|
|
}
|
|
const [paramName, , dynamicParamType] = segment;
|
|
switch(dynamicParamType){
|
|
case 'c':
|
|
return `[...${paramName}]`;
|
|
case 'ci(..)(..)':
|
|
return `(..)(..)[...${paramName}]`;
|
|
case 'ci(.)':
|
|
return `(.)[...${paramName}]`;
|
|
case 'ci(..)':
|
|
return `(..)[...${paramName}]`;
|
|
case 'ci(...)':
|
|
return `(...)[...${paramName}]`;
|
|
case 'oc':
|
|
return `[[...${paramName}]]`;
|
|
case 'd':
|
|
return `[${paramName}]`;
|
|
case 'di(..)(..)':
|
|
return `(..)(..)[${paramName}]`;
|
|
case 'di(.)':
|
|
return `(.)[${paramName}]`;
|
|
case 'di(..)':
|
|
return `(..)[${paramName}]`;
|
|
case 'di(...)':
|
|
return `(...)[${paramName}]`;
|
|
default:
|
|
dynamicParamType;
|
|
return `[${paramName}]`;
|
|
}
|
|
};
|
|
function normalizeSegments(segments) {
|
|
return segments.reduce((acc, segment)=>{
|
|
segment = removeLeadingSlash(segment);
|
|
if (segment === '' || (0, _segment.isGroupSegment)(segment)) {
|
|
return acc;
|
|
}
|
|
return `${acc}/${segment}`;
|
|
}, '') || '/';
|
|
}
|
|
function extractPathFromFlightRouterState(flightRouterState) {
|
|
const segment = Array.isArray(flightRouterState[0]) ? flightRouterState[0][1] : flightRouterState[0];
|
|
if (segment === _segment.DEFAULT_SEGMENT_KEY || _interceptionroutes.INTERCEPTION_ROUTE_MARKERS.some((m)=>segment.startsWith(m))) return undefined;
|
|
if (segment.startsWith(_segment.PAGE_SEGMENT_KEY)) return '';
|
|
const segments = [
|
|
segmentToPathname(segment)
|
|
];
|
|
const parallelRoutes = flightRouterState[1] ?? {};
|
|
const childrenPath = parallelRoutes.children ? extractPathFromFlightRouterState(parallelRoutes.children) : undefined;
|
|
if (childrenPath !== undefined) {
|
|
segments.push(childrenPath);
|
|
} else {
|
|
for (const [key, value] of Object.entries(parallelRoutes)){
|
|
if (key === 'children') continue;
|
|
const childPath = extractPathFromFlightRouterState(value);
|
|
if (childPath !== undefined) {
|
|
segments.push(childPath);
|
|
}
|
|
}
|
|
}
|
|
return normalizeSegments(segments);
|
|
}
|
|
function extractSourcePageSegmentsFromFlightRouterState(flightRouterState) {
|
|
const segment = segmentToSourcePagePathname(flightRouterState[0]);
|
|
if (segment === _segment.DEFAULT_SEGMENT_KEY) {
|
|
return undefined;
|
|
}
|
|
if (segment === 'page') {
|
|
return [
|
|
segment
|
|
];
|
|
}
|
|
const parallelRoutes = flightRouterState[1] ?? {};
|
|
const childrenPath = parallelRoutes.children ? extractSourcePageSegmentsFromFlightRouterState(parallelRoutes.children) : undefined;
|
|
if (childrenPath !== undefined) {
|
|
return segment === '' ? childrenPath : [
|
|
removeLeadingSlash(segment),
|
|
...childrenPath
|
|
];
|
|
}
|
|
for (const [key, value] of Object.entries(parallelRoutes)){
|
|
if (key === 'children') continue;
|
|
const childPath = extractSourcePageSegmentsFromFlightRouterState(value);
|
|
if (childPath !== undefined) {
|
|
return segment === '' ? childPath : [
|
|
removeLeadingSlash(segment),
|
|
...childPath
|
|
];
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
function extractSourcePageFromFlightRouterState(flightRouterState) {
|
|
const sourcePageSegments = extractSourcePageSegmentsFromFlightRouterState(flightRouterState);
|
|
return sourcePageSegments ? `/${sourcePageSegments.join('/')}` : undefined;
|
|
}
|
|
function computeChangedPathImpl(treeA, treeB) {
|
|
const [segmentA, parallelRoutesA] = treeA;
|
|
const [segmentB, parallelRoutesB] = treeB;
|
|
const normalizedSegmentA = segmentToPathname(segmentA);
|
|
const normalizedSegmentB = segmentToPathname(segmentB);
|
|
if (_interceptionroutes.INTERCEPTION_ROUTE_MARKERS.some((m)=>normalizedSegmentA.startsWith(m) || normalizedSegmentB.startsWith(m))) {
|
|
return '';
|
|
}
|
|
if (!(0, _matchsegments.matchSegment)(segmentA, segmentB)) {
|
|
// once we find where the tree changed, we compute the rest of the path by traversing the tree
|
|
return extractPathFromFlightRouterState(treeB) ?? '';
|
|
}
|
|
for(const parallelRouterKey in parallelRoutesA){
|
|
if (parallelRoutesB[parallelRouterKey]) {
|
|
const changedPath = computeChangedPathImpl(parallelRoutesA[parallelRouterKey], parallelRoutesB[parallelRouterKey]);
|
|
if (changedPath !== null) {
|
|
return `${segmentToPathname(segmentB)}/${changedPath}`;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function computeChangedPath(treeA, treeB) {
|
|
const changedPath = computeChangedPathImpl(treeA, treeB);
|
|
if (changedPath == null || changedPath === '/') {
|
|
return changedPath;
|
|
}
|
|
// lightweight normalization to remove route groups
|
|
return normalizeSegments(changedPath.split('/'));
|
|
}
|
|
function getSelectedParams(currentTree, params = {}) {
|
|
const parallelRoutes = currentTree[1];
|
|
for (const parallelRoute of Object.values(parallelRoutes)){
|
|
const segment = parallelRoute[0];
|
|
const isDynamicParameter = Array.isArray(segment);
|
|
const segmentValue = isDynamicParameter ? segment[1] : segment;
|
|
if (!segmentValue || segmentValue.startsWith(_segment.PAGE_SEGMENT_KEY)) continue;
|
|
// Ensure catchAll and optional catchall are turned into an array
|
|
const isCatchAll = isDynamicParameter && (segment[2] === 'c' || segment[2] === 'oc');
|
|
if (isCatchAll) {
|
|
params[segment[0]] = segment[1].split('/');
|
|
} else if (isDynamicParameter) {
|
|
params[segment[0]] = segment[1];
|
|
}
|
|
params = getSelectedParams(parallelRoute, params);
|
|
}
|
|
return params;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/lib/javascript-url.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
// Adapted from React's sanitizeURL function found here: https://github.com/facebook/react/blob/b565373afd0cc1988497e1107106e851e8cfb261/packages/react-dom-bindings/src/shared/sanitizeURL.js
|
|
// A javascript: URL can contain leading C0 control or \u0020 SPACE,
|
|
// and any newline or tab are filtered out as if they're not part of the URL.
|
|
// https://url.spec.whatwg.org/#url-parsing
|
|
// Tab or newline are defined as \r\n\t:
|
|
// https://infra.spec.whatwg.org/#ascii-tab-or-newline
|
|
// A C0 control is a code point in the range \u0000 NULL to \u001F
|
|
// INFORMATION SEPARATOR ONE, inclusive:
|
|
// https://infra.spec.whatwg.org/#c0-control-or-space
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "isJavaScriptURLString", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return isJavaScriptURLString;
|
|
}
|
|
});
|
|
const isJavaScriptProtocol = /^[\u0000-\u001F ]*j[\r\n\t]*a[\r\n\t]*v[\r\n\t]*a[\r\n\t]*s[\r\n\t]*c[\r\n\t]*r[\r\n\t]*i[\r\n\t]*p[\r\n\t]*t[\r\n\t]*:/i;
|
|
function isJavaScriptURLString(url) {
|
|
return isJavaScriptProtocol.test('' + url);
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/navigation.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
completeHardNavigation: null,
|
|
completeSoftNavigation: null,
|
|
completeTraverseNavigation: null,
|
|
convertServerPatchToFullTree: null,
|
|
navigate: null,
|
|
navigateToKnownRoute: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
completeHardNavigation: function() {
|
|
return completeHardNavigation;
|
|
},
|
|
completeSoftNavigation: function() {
|
|
return completeSoftNavigation;
|
|
},
|
|
completeTraverseNavigation: function() {
|
|
return completeTraverseNavigation;
|
|
},
|
|
convertServerPatchToFullTree: function() {
|
|
return convertServerPatchToFullTree;
|
|
},
|
|
navigate: function() {
|
|
return navigate;
|
|
},
|
|
navigateToKnownRoute: function() {
|
|
return navigateToKnownRoute;
|
|
}
|
|
});
|
|
const _fetchserverresponse = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/fetch-server-response.js [app-ssr] (ecmascript)");
|
|
const _pprnavigations = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/ppr-navigations.js [app-ssr] (ecmascript)");
|
|
const _createhreffromurl = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/create-href-from-url.js [app-ssr] (ecmascript)");
|
|
const _constants = __turbopack_context__.r("[project]/node_modules/next/dist/lib/constants.js [app-ssr] (ecmascript)");
|
|
const _cache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)");
|
|
const _optimisticroutes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/optimistic-routes.js [app-ssr] (ecmascript)");
|
|
const _cachekey = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-key.js [app-ssr] (ecmascript)");
|
|
const _scheduler = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/scheduler.js [app-ssr] (ecmascript)");
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _links = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/links.js [app-ssr] (ecmascript)");
|
|
const _routerreducertypes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/router-reducer-types.js [app-ssr] (ecmascript)");
|
|
const _computechangedpath = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/compute-changed-path.js [app-ssr] (ecmascript)");
|
|
const _javascripturl = __turbopack_context__.r("[project]/node_modules/next/dist/client/lib/javascript-url.js [app-ssr] (ecmascript)");
|
|
const _bfcache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/bfcache.js [app-ssr] (ecmascript)");
|
|
function navigate(state, url, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, nextUrl, freshnessPolicy, scrollBehavior, navigateType) {
|
|
// Instant Navigation Testing API: when the lock is active, ensure a
|
|
// prefetch task has been initiated before proceeding with the navigation.
|
|
// This guarantees that segment data requests are at least pending, even
|
|
// for routes that already have a cached route tree. Without this, the
|
|
// static shell might be incomplete because some segments were never
|
|
// requested.
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
const { isNavigationLocked } = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation-testing-lock.js [app-ssr] (ecmascript)");
|
|
if (isNavigationLocked()) {
|
|
return ensurePrefetchThenNavigate(state, url, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, nextUrl, freshnessPolicy, scrollBehavior, navigateType);
|
|
}
|
|
}
|
|
return navigateImpl(state, url, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, nextUrl, freshnessPolicy, scrollBehavior, navigateType);
|
|
}
|
|
function navigateImpl(state, url, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, nextUrl, freshnessPolicy, scrollBehavior, navigateType) {
|
|
const now = Date.now();
|
|
const href = url.href;
|
|
const cacheKey = (0, _cachekey.createCacheKey)(href, nextUrl);
|
|
const route = (0, _cache.readRouteCacheEntry)(now, cacheKey);
|
|
if (route !== null && route.status === _cache.EntryStatus.Fulfilled) {
|
|
// We have a matching prefetch.
|
|
return navigateUsingPrefetchedRouteTree(now, state, url, currentUrl, currentRenderedSearch, nextUrl, currentCacheNode, currentFlightRouterState, freshnessPolicy, scrollBehavior, navigateType, route);
|
|
}
|
|
// There was no matching route tree in the cache. Let's see if we can
|
|
// construct an "optimistic" route tree using the deprecated search-params
|
|
// based matching. This is only used when the new optimisticRouting flag is
|
|
// disabled.
|
|
//
|
|
// Do not construct an optimistic route tree if there was a cache hit, but
|
|
// the entry has a rejected status, since it may have been rejected due to a
|
|
// rewrite or redirect based on the search params.
|
|
//
|
|
// TODO: There are multiple reasons a prefetch might be rejected; we should
|
|
// track them explicitly and choose what to do here based on that.
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (route === null || route.status !== _cache.EntryStatus.Rejected) {
|
|
const optimisticRoute = (0, _cache.deprecated_requestOptimisticRouteCacheEntry)(now, url, nextUrl);
|
|
if (optimisticRoute !== null) {
|
|
// We have an optimistic route tree. Proceed with the normal flow.
|
|
return navigateUsingPrefetchedRouteTree(now, state, url, currentUrl, currentRenderedSearch, nextUrl, currentCacheNode, currentFlightRouterState, freshnessPolicy, scrollBehavior, navigateType, optimisticRoute);
|
|
}
|
|
}
|
|
}
|
|
// There's no matching prefetch for this route in the cache. We must lazily
|
|
// fetch it from the server before we can perform the navigation.
|
|
//
|
|
// TODO: If this is a gesture navigation, instead of performing a
|
|
// dynamic request, we should do a runtime prefetch.
|
|
return navigateToUnknownRoute(now, state, url, currentUrl, currentRenderedSearch, nextUrl, currentCacheNode, currentFlightRouterState, freshnessPolicy, scrollBehavior, navigateType).catch(()=>{
|
|
// If the navigation fails, return the current state
|
|
return state;
|
|
});
|
|
}
|
|
function navigateToKnownRoute(now, state, url, canonicalUrl, navigationSeed, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, freshnessPolicy, nextUrl, scrollBehavior, navigateType, debugInfo, // prediction. Passed through so it can be marked as having a dynamic rewrite
|
|
// if the server returns a different pathname (indicating dynamic rewrite
|
|
// behavior).
|
|
//
|
|
// When null, the navigation did not use route prediction - either because
|
|
// the route was already fully cached, or it's a navigation that doesn't
|
|
// involve prediction (refresh, history traversal, server action, etc.).
|
|
// In these cases, if a mismatch occurs, we still mark the route as having a
|
|
// dynamic rewrite by traversing the known route tree (see
|
|
// dispatchRetryDueToTreeMismatch).
|
|
routeCacheEntry) {
|
|
// A version of navigate() that accepts the target route tree as an argument
|
|
// rather than reading it from the prefetch cache.
|
|
const accumulation = {
|
|
separateRefreshUrls: null,
|
|
scrollRef: null
|
|
};
|
|
// We special case navigations to the exact same URL as the current location.
|
|
// It's a common UI pattern for apps to refresh when you click a link to the
|
|
// current page. So when this happens, we refresh the dynamic data in the page
|
|
// segments.
|
|
//
|
|
// Note that this does not apply if the any part of the hash or search query
|
|
// has changed. This might feel a bit weird but it makes more sense when you
|
|
// consider that the way to trigger this behavior is to click the same link
|
|
// multiple times.
|
|
//
|
|
// TODO: We should probably refresh the *entire* route when this case occurs,
|
|
// not just the page segments. Essentially treating it the same as a refresh()
|
|
// triggered by an action, which is the more explicit way of modeling the UI
|
|
// pattern described above.
|
|
//
|
|
// Also note that this only refreshes the dynamic data, not static/ cached
|
|
// data. If the page segment is fully static and prefetched, the request is
|
|
// skipped. (This is also how refresh() works.)
|
|
const isSamePageNavigation = url.href === currentUrl.href;
|
|
const task = (0, _pprnavigations.startPPRNavigation)(now, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, navigationSeed.routeTree, navigationSeed.metadataVaryPath, freshnessPolicy, navigationSeed.data, navigationSeed.head, navigationSeed.dynamicStaleAt, isSamePageNavigation, accumulation);
|
|
if (task !== null) {
|
|
if (freshnessPolicy !== _pprnavigations.FreshnessPolicy.Gesture) {
|
|
(0, _pprnavigations.spawnDynamicRequests)(task, url, nextUrl, freshnessPolicy, accumulation, routeCacheEntry, navigateType);
|
|
}
|
|
return completeSoftNavigation(state, url, nextUrl, task.route, task.node, navigationSeed.renderedSearch, canonicalUrl, navigateType, scrollBehavior, accumulation.scrollRef, debugInfo);
|
|
}
|
|
// Could not perform a SPA navigation. Revert to a full-page (MPA) navigation.
|
|
return completeHardNavigation(state, url, navigateType);
|
|
}
|
|
function navigateUsingPrefetchedRouteTree(now, state, url, currentUrl, currentRenderedSearch, nextUrl, currentCacheNode, currentFlightRouterState, freshnessPolicy, scrollBehavior, navigateType, route) {
|
|
const routeTree = route.tree;
|
|
const canonicalUrl = route.canonicalUrl + url.hash;
|
|
const renderedSearch = route.renderedSearch;
|
|
const prefetchSeed = {
|
|
renderedSearch,
|
|
routeTree,
|
|
metadataVaryPath: route.metadata.varyPath,
|
|
data: null,
|
|
head: null,
|
|
dynamicStaleAt: (0, _bfcache.computeDynamicStaleAt)(now, _bfcache.UnknownDynamicStaleTime)
|
|
};
|
|
return navigateToKnownRoute(now, state, url, canonicalUrl, prefetchSeed, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, freshnessPolicy, nextUrl, scrollBehavior, navigateType, null, route);
|
|
}
|
|
// Used to request all the dynamic data for a route, rather than just a subset,
|
|
// e.g. during a refresh or a revalidation. Typically this gets constructed
|
|
// during the normal flow when diffing the route tree, but for an unprefetched
|
|
// navigation, where we don't know the structure of the target route, we use
|
|
// this instead.
|
|
const DynamicRequestTreeForEntireRoute = [
|
|
'',
|
|
{},
|
|
null,
|
|
'refetch'
|
|
];
|
|
async function navigateToUnknownRoute(now, state, url, currentUrl, currentRenderedSearch, nextUrl, currentCacheNode, currentFlightRouterState, freshnessPolicy, scrollBehavior, navigateType) {
|
|
// Runs when a navigation happens but there's no cached prefetch we can use.
|
|
// Don't bother to wait for a prefetch response; go straight to a full
|
|
// navigation that contains both static and dynamic data in a single stream.
|
|
// (This is unlike the old navigation implementation, which instead blocks
|
|
// the dynamic request until a prefetch request is received.)
|
|
//
|
|
// To avoid duplication of logic, we're going to pretend that the tree
|
|
// returned by the dynamic request is, in fact, a prefetch tree. Then we can
|
|
// use the same server response to write the actual data into the CacheNode
|
|
// tree. So it's the same flow as the "happy path" (prefetch, then
|
|
// navigation), except we use a single server response for both stages.
|
|
let dynamicRequestTree;
|
|
switch(freshnessPolicy){
|
|
case _pprnavigations.FreshnessPolicy.Default:
|
|
case _pprnavigations.FreshnessPolicy.HistoryTraversal:
|
|
case _pprnavigations.FreshnessPolicy.Gesture:
|
|
dynamicRequestTree = currentFlightRouterState;
|
|
break;
|
|
case _pprnavigations.FreshnessPolicy.Hydration:
|
|
case _pprnavigations.FreshnessPolicy.RefreshAll:
|
|
case _pprnavigations.FreshnessPolicy.HMRRefresh:
|
|
dynamicRequestTree = DynamicRequestTreeForEntireRoute;
|
|
break;
|
|
default:
|
|
freshnessPolicy;
|
|
dynamicRequestTree = currentFlightRouterState;
|
|
break;
|
|
}
|
|
const promiseForDynamicServerResponse = (0, _fetchserverresponse.fetchServerResponse)(url, {
|
|
flightRouterState: dynamicRequestTree,
|
|
nextUrl
|
|
});
|
|
const result = await promiseForDynamicServerResponse;
|
|
if (typeof result === 'string') {
|
|
// This is an MPA navigation.
|
|
const redirectUrl = new URL(result, location.origin);
|
|
return completeHardNavigation(state, redirectUrl, navigateType);
|
|
}
|
|
const { flightData, canonicalUrl, renderedSearch, couldBeIntercepted, supportsPerSegmentPrefetching, dynamicStaleTime, staticStageData, runtimePrefetchStream, responseHeaders, debugInfo } = result;
|
|
// Since the response format of dynamic requests and prefetches is slightly
|
|
// different, we'll need to massage the data a bit. Create FlightRouterState
|
|
// tree that simulates what we'd receive as the result of a prefetch.
|
|
const navigationSeed = convertServerPatchToFullTree(now, currentFlightRouterState, flightData, renderedSearch, dynamicStaleTime);
|
|
// Learn the route pattern so we can predict it for future navigations.
|
|
// hasDynamicRewrite is false because this is a fresh navigation to an
|
|
// unknown route - any rewrite detection happens during the traversal inside
|
|
// discoverKnownRoute. The hasDynamicRewrite param is only set to true when
|
|
// retrying after a tree mismatch (see dispatchRetryDueToTreeMismatch).
|
|
const metadataVaryPath = navigationSeed.metadataVaryPath;
|
|
if (metadataVaryPath !== null) {
|
|
(0, _optimisticroutes.discoverKnownRoute)(now, url.pathname, nextUrl, null, navigationSeed.routeTree, metadataVaryPath, couldBeIntercepted, (0, _createhreffromurl.createHrefFromUrl)(canonicalUrl), supportsPerSegmentPrefetching, false // hasDynamicRewrite - not a retry, rewrite detection happens during traversal
|
|
);
|
|
if (staticStageData !== null) {
|
|
const { response: staticStageResponse, isResponsePartial } = staticStageData;
|
|
// Write the static stage of the response into the segment cache so that
|
|
// subsequent navigations can serve cached static segments instantly.
|
|
(0, _cache.getStaleAt)(now, staticStageResponse.s).then((staleAt)=>{
|
|
const buildId = responseHeaders.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? staticStageResponse.b;
|
|
(0, _cache.writeStaticStageResponseIntoCache)(now, staticStageResponse.f, buildId, staticStageResponse.h, staleAt, currentFlightRouterState, renderedSearch, isResponsePartial);
|
|
}).catch(()=>{
|
|
// The static stage processing failed. Not fatal — the navigation
|
|
// completed normally, we just won't write into the cache.
|
|
});
|
|
}
|
|
if (runtimePrefetchStream !== null) {
|
|
(0, _cache.processRuntimePrefetchStream)(now, runtimePrefetchStream, currentFlightRouterState, renderedSearch).then((processed)=>{
|
|
if (processed !== null) {
|
|
(0, _cache.writeDynamicRenderResponseIntoCache)(now, _types.FetchStrategy.PPRRuntime, processed.flightDatas, processed.buildId, processed.isResponsePartial, processed.headVaryParams, processed.staleAt, processed.navigationSeed, null);
|
|
}
|
|
}).catch(()=>{
|
|
// The runtime prefetch cache write failed. Not fatal — the
|
|
// navigation completed normally, we just won't cache runtime data.
|
|
});
|
|
}
|
|
}
|
|
return navigateToKnownRoute(now, state, url, (0, _createhreffromurl.createHrefFromUrl)(canonicalUrl), navigationSeed, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, freshnessPolicy, nextUrl, scrollBehavior, navigateType, debugInfo, // came directly from the server. If a mismatch occurs during dynamic data
|
|
// fetch, the retry handler will traverse the known route tree to mark the
|
|
// entry as having a dynamic rewrite.
|
|
null);
|
|
}
|
|
function completeHardNavigation(state, url, navigateType) {
|
|
if ((0, _javascripturl.isJavaScriptURLString)(url.href)) {
|
|
console.error('Next.js has blocked a javascript: URL as a security precaution.');
|
|
return state;
|
|
}
|
|
const newState = {
|
|
canonicalUrl: url.origin === location.origin ? (0, _createhreffromurl.createHrefFromUrl)(url) : url.href,
|
|
pushRef: {
|
|
pendingPush: navigateType === 'push',
|
|
mpaNavigation: true,
|
|
preserveCustomHistoryState: false
|
|
},
|
|
// TODO: None of the rest of these values are consistent with the incoming
|
|
// navigation. We rely on the fact that AppRouter will suspend and trigger
|
|
// a hard navigation before it accesses any of these values. But instead
|
|
// we should trigger the hard navigation and blocking any subsequent
|
|
// router updates without updating React.
|
|
renderedSearch: state.renderedSearch,
|
|
focusAndScrollRef: state.focusAndScrollRef,
|
|
cache: state.cache,
|
|
tree: state.tree,
|
|
nextUrl: state.nextUrl,
|
|
previousNextUrl: state.previousNextUrl,
|
|
debugInfo: null
|
|
};
|
|
return newState;
|
|
}
|
|
function completeSoftNavigation(oldState, url, referringNextUrl, tree, cache, renderedSearch, canonicalUrl, navigateType, scrollBehavior, scrollRef, collectedDebugInfo) {
|
|
// The "Next-Url" is a special representation of the URL that Next.js
|
|
// uses to implement interception routes.
|
|
// TODO: Get rid of this extra traversal by computing this during the
|
|
// same traversal that computes the tree itself. We should also figure out
|
|
// what is the minimum information needed for the server to correctly
|
|
// intercept the route.
|
|
const changedPath = (0, _computechangedpath.computeChangedPath)(oldState.tree, tree);
|
|
const nextUrlForNewRoute = changedPath ? changedPath : oldState.nextUrl;
|
|
// This value is stored on the state as `previousNextUrl`; the naming is
|
|
// confusing. What it represents is the "Next-Url" header that was used to
|
|
// fetch the incoming route. It's essentially the refererer URL, but in a
|
|
// Next.js specific format. During refreshes, this is sent back to the server
|
|
// instead of the current route's "Next-Url" so that the same interception
|
|
// logic is applied as during the original navigation.
|
|
const previousNextUrl = referringNextUrl;
|
|
// Check if the only thing that changed was the hash fragment.
|
|
const oldUrl = new URL(oldState.canonicalUrl, url);
|
|
const onlyHashChange = // navigations are always same-origin.
|
|
url.pathname === oldUrl.pathname && url.search === oldUrl.search && url.hash !== oldUrl.hash;
|
|
// Determine whether and how the page should scroll after this
|
|
// navigation.
|
|
//
|
|
// By default, we scroll to the segments that were navigated to — i.e.
|
|
// segments in the new part of the route, as opposed to shared segments
|
|
// that were already part of the previous route. All newly navigated
|
|
// segments share a single ScrollRef. When they mount, the first one
|
|
// to mount initiates the scroll. They share a ref so that only one
|
|
// scroll happens per navigation.
|
|
//
|
|
// If a subsequent navigation produces new segments, those supersede
|
|
// any pending scroll from the previous navigation by invalidating its
|
|
// ScrollRef. If a navigation doesn't produce any new segments (e.g.
|
|
// a refresh where the route structure didn't change), any pending
|
|
// scrolls from previous navigations are unaffected.
|
|
//
|
|
// The branches below handle special cases layered on top of this
|
|
// default model.
|
|
let activeScrollRef;
|
|
let forceScroll;
|
|
if (scrollBehavior === _routerreducertypes.ScrollBehavior.NoScroll) {
|
|
// The user explicitly opted out of scrolling (e.g. scroll={false}
|
|
// on a Link or router.push).
|
|
//
|
|
// If this navigation created new scroll targets (scrollRef !== null),
|
|
// neutralize them. If it didn't, any prior scroll targets carried
|
|
// forward on the cache nodes via reuseSharedCacheNode remain active.
|
|
if (scrollRef !== null) {
|
|
scrollRef.current = false;
|
|
}
|
|
activeScrollRef = oldState.focusAndScrollRef.scrollRef;
|
|
forceScroll = false;
|
|
} else if (onlyHashChange) {
|
|
// Hash-only navigations should scroll regardless of per-node state.
|
|
// Create a fresh ref so the first segment to scroll consumes it.
|
|
//
|
|
// Invalidate any scroll ref from a prior navigation that hasn't
|
|
// been consumed yet.
|
|
const oldScrollRef = oldState.focusAndScrollRef.scrollRef;
|
|
if (oldScrollRef !== null) {
|
|
oldScrollRef.current = false;
|
|
}
|
|
// Also invalidate any per-node refs that were accumulated during
|
|
// this navigation's tree construction — the hash-only ref
|
|
// supersedes them.
|
|
if (scrollRef !== null) {
|
|
scrollRef.current = false;
|
|
}
|
|
activeScrollRef = {
|
|
current: true
|
|
};
|
|
forceScroll = true;
|
|
} else {
|
|
// Default case. Use the accumulated scrollRef (may be null if no
|
|
// new segments were created). The handler checks per-node refs, so
|
|
// unchanged parallel route slots won't scroll.
|
|
activeScrollRef = scrollRef;
|
|
// If this navigation created new scroll targets, invalidate any
|
|
// pending scroll from a previous navigation.
|
|
if (scrollRef !== null) {
|
|
const oldScrollRef = oldState.focusAndScrollRef.scrollRef;
|
|
if (oldScrollRef !== null) {
|
|
oldScrollRef.current = false;
|
|
}
|
|
}
|
|
forceScroll = false;
|
|
}
|
|
const newState = {
|
|
canonicalUrl,
|
|
renderedSearch,
|
|
pushRef: {
|
|
pendingPush: navigateType === 'push',
|
|
mpaNavigation: false,
|
|
preserveCustomHistoryState: false
|
|
},
|
|
focusAndScrollRef: {
|
|
scrollRef: activeScrollRef,
|
|
forceScroll,
|
|
onlyHashChange,
|
|
hashFragment: //
|
|
// Empty hash should trigger default behavior of scrolling layout into
|
|
// view. #top is handled in layout-router.
|
|
//
|
|
// Refer to `ScrollAndFocusHandler` for details on how this is used.
|
|
scrollBehavior !== _routerreducertypes.ScrollBehavior.NoScroll && url.hash !== '' ? decodeURIComponent(url.hash.slice(1)) : oldState.focusAndScrollRef.hashFragment
|
|
},
|
|
cache,
|
|
tree,
|
|
nextUrl: nextUrlForNewRoute,
|
|
previousNextUrl,
|
|
debugInfo: collectedDebugInfo
|
|
};
|
|
return newState;
|
|
}
|
|
function completeTraverseNavigation(state, url, renderedSearch, cache, tree, nextUrl) {
|
|
return {
|
|
// Set canonical url
|
|
canonicalUrl: (0, _createhreffromurl.createHrefFromUrl)(url),
|
|
renderedSearch,
|
|
pushRef: {
|
|
pendingPush: false,
|
|
mpaNavigation: false,
|
|
// Ensures that the custom history state that was set is preserved when applying this update.
|
|
preserveCustomHistoryState: true
|
|
},
|
|
focusAndScrollRef: state.focusAndScrollRef,
|
|
cache,
|
|
// Restore provided tree
|
|
tree,
|
|
nextUrl,
|
|
// TODO: We need to restore previousNextUrl, too, which represents the
|
|
// Next-Url that was used to fetch the data. Anywhere we fetch using the
|
|
// canonical URL, there should be a corresponding Next-Url.
|
|
previousNextUrl: null,
|
|
debugInfo: null
|
|
};
|
|
}
|
|
function convertServerPatchToFullTree(now, currentTree, flightData, renderedSearch, dynamicStaleTimeSeconds) {
|
|
// During a client navigation or prefetch, the server sends back only a patch
|
|
// for the parts of the tree that have changed.
|
|
//
|
|
// This applies the patch to the base tree to create a full representation of
|
|
// the resulting tree.
|
|
//
|
|
// The return type includes a full FlightRouterState tree and a full
|
|
// CacheNodeSeedData tree. (Conceptually these are the same tree, and should
|
|
// eventually be unified, but there's still lots of existing code that
|
|
// operates on FlightRouterState trees alone without the CacheNodeSeedData.)
|
|
//
|
|
// TODO: This similar to what apply-router-state-patch-to-tree does. It
|
|
// will eventually fully replace it. We should get rid of all the remaining
|
|
// places where we iterate over the server patch format. This should also
|
|
// eventually replace normalizeFlightData.
|
|
let baseTree = currentTree;
|
|
let baseData = null;
|
|
let head = null;
|
|
if (flightData !== null) {
|
|
for (const { segmentPath, tree: treePatch, seedData: dataPatch, head: headPatch } of flightData){
|
|
const result = convertServerPatchToFullTreeImpl(baseTree, baseData, treePatch, dataPatch, segmentPath, renderedSearch, 0);
|
|
baseTree = result.tree;
|
|
baseData = result.data;
|
|
// This is the same for all patches per response, so just pick an
|
|
// arbitrary one
|
|
head = headPatch;
|
|
}
|
|
}
|
|
const finalFlightRouterState = baseTree;
|
|
// Convert the final FlightRouterState into a RouteTree type.
|
|
//
|
|
// TODO: Eventually, FlightRouterState will evolve to being a transport format
|
|
// only. The RouteTree type will become the main type used for dealing with
|
|
// routes on the client, and we'll store it in the state directly.
|
|
const acc = {
|
|
metadataVaryPath: null
|
|
};
|
|
const routeTree = (0, _cache.convertRootFlightRouterStateToRouteTree)(finalFlightRouterState, renderedSearch, acc);
|
|
return {
|
|
routeTree,
|
|
metadataVaryPath: acc.metadataVaryPath,
|
|
data: baseData,
|
|
renderedSearch,
|
|
head,
|
|
dynamicStaleAt: (0, _bfcache.computeDynamicStaleAt)(now, dynamicStaleTimeSeconds)
|
|
};
|
|
}
|
|
function convertServerPatchToFullTreeImpl(baseRouterState, baseData, treePatch, dataPatch, segmentPath, renderedSearch, index) {
|
|
if (index === segmentPath.length) {
|
|
// We reached the part of the tree that we need to patch.
|
|
return {
|
|
tree: treePatch,
|
|
data: dataPatch
|
|
};
|
|
}
|
|
// segmentPath represents the parent path of subtree. It's a repeating
|
|
// pattern of parallel route key and segment:
|
|
//
|
|
// [string, Segment, string, Segment, string, Segment, ...]
|
|
//
|
|
// This path tells us which part of the base tree to apply the tree patch.
|
|
//
|
|
// NOTE: We receive the FlightRouterState patch in the same request as the
|
|
// seed data patch. Therefore we don't need to worry about diffing the segment
|
|
// values; we can assume the server sent us a correct result.
|
|
const updatedParallelRouteKey = segmentPath[index];
|
|
// const segment: Segment = segmentPath[index + 1] <-- Not used, see note above
|
|
const baseTreeChildren = baseRouterState[1];
|
|
const baseSeedDataChildren = baseData !== null ? baseData[1] : null;
|
|
const newTreeChildren = {};
|
|
const newSeedDataChildren = {};
|
|
for(const parallelRouteKey in baseTreeChildren){
|
|
const childBaseRouterState = baseTreeChildren[parallelRouteKey];
|
|
const childBaseSeedData = baseSeedDataChildren !== null ? baseSeedDataChildren[parallelRouteKey] ?? null : null;
|
|
if (parallelRouteKey === updatedParallelRouteKey) {
|
|
const result = convertServerPatchToFullTreeImpl(childBaseRouterState, childBaseSeedData, treePatch, dataPatch, segmentPath, renderedSearch, // the end of the segment path.
|
|
index + 2);
|
|
newTreeChildren[parallelRouteKey] = result.tree;
|
|
newSeedDataChildren[parallelRouteKey] = result.data;
|
|
} else {
|
|
// This child is not being patched. Copy it over as-is.
|
|
newTreeChildren[parallelRouteKey] = childBaseRouterState;
|
|
newSeedDataChildren[parallelRouteKey] = childBaseSeedData;
|
|
}
|
|
}
|
|
let clonedTree;
|
|
let clonedSeedData;
|
|
// Clone all the fields except the children.
|
|
// Clone the FlightRouterState tree. Based on equivalent logic in
|
|
// apply-router-state-patch-to-tree, but should confirm whether we need to
|
|
// copy all of these fields. Not sure the server ever sends, e.g. the
|
|
// refetch marker.
|
|
clonedTree = [
|
|
baseRouterState[0],
|
|
newTreeChildren
|
|
];
|
|
if (2 in baseRouterState) {
|
|
const compressedRefreshState = baseRouterState[2];
|
|
if (compressedRefreshState !== undefined && compressedRefreshState !== null) {
|
|
// Since this part of the tree was patched with new data, any parent
|
|
// refresh states should be updated to reflect the new rendered search
|
|
// value. (The refresh state acts like a "context provider".) All pages
|
|
// within the same server response share the same renderedSearch value,
|
|
// but the same RouteTree could be composed from multiple different
|
|
// routes, and multiple responses.
|
|
clonedTree[2] = [
|
|
compressedRefreshState[0],
|
|
renderedSearch
|
|
];
|
|
}
|
|
}
|
|
if (3 in baseRouterState) {
|
|
clonedTree[3] = baseRouterState[3];
|
|
}
|
|
if (4 in baseRouterState) {
|
|
clonedTree[4] = baseRouterState[4];
|
|
}
|
|
// Clone the CacheNodeSeedData tree.
|
|
const isEmptySeedDataPartial = true;
|
|
clonedSeedData = [
|
|
null,
|
|
newSeedDataChildren,
|
|
null,
|
|
isEmptySeedDataPartial,
|
|
null
|
|
];
|
|
return {
|
|
tree: clonedTree,
|
|
data: clonedSeedData
|
|
};
|
|
}
|
|
/**
|
|
* Instant Navigation Testing API: ensures a prefetch task has been initiated
|
|
* and completed before proceeding with the navigation. This guarantees that
|
|
* segment data requests are at least pending, even for routes whose route
|
|
* tree is already cached.
|
|
*
|
|
* After the prefetch completes, delegates to the normal navigation flow.
|
|
*/ async function ensurePrefetchThenNavigate(state, url, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, nextUrl, freshnessPolicy, scrollBehavior, navigateType) {
|
|
const link = (0, _links.getLinkForCurrentNavigation)();
|
|
const fetchStrategy = link !== null ? link.fetchStrategy : _types.FetchStrategy.PPR;
|
|
// Transition the cookie to captured-SPA immediately, before waiting
|
|
// for the prefetch. This ensures the devtools panel can update its UI
|
|
// right away, even if the prefetch takes time (e.g. dev compilation).
|
|
// The "to" tree starts as null and is filled in after the prefetch
|
|
// resolves and the navigation produces a new router state.
|
|
const { transitionToCapturedSPA, updateCapturedSPAToTree } = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation-testing-lock.js [app-ssr] (ecmascript)");
|
|
transitionToCapturedSPA(currentFlightRouterState, null);
|
|
const cacheKey = (0, _cachekey.createCacheKey)(url.href, nextUrl);
|
|
await new Promise((resolve)=>{
|
|
(0, _scheduler.schedulePrefetchTask)(cacheKey, currentFlightRouterState, fetchStrategy, _types.PrefetchPriority.Default, null, resolve // _onComplete callback
|
|
);
|
|
});
|
|
// Prefetch is complete. Proceed with the normal navigation flow, which
|
|
// will now find the route in the cache.
|
|
const result = await navigateImpl(state, url, currentUrl, currentRenderedSearch, currentCacheNode, currentFlightRouterState, nextUrl, freshnessPolicy, scrollBehavior, navigateType);
|
|
// Update the cookie with the resolved "to" tree so the devtools
|
|
// panel can display both routes immediately.
|
|
updateCapturedSPAToTree(currentFlightRouterState, result.tree);
|
|
return result;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/reducers/navigate-reducer.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
DYNAMIC_STALETIME_MS: null,
|
|
STATIC_STALETIME_MS: null,
|
|
navigateReducer: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
DYNAMIC_STALETIME_MS: function() {
|
|
return DYNAMIC_STALETIME_MS;
|
|
},
|
|
STATIC_STALETIME_MS: function() {
|
|
return STATIC_STALETIME_MS;
|
|
},
|
|
navigateReducer: function() {
|
|
return navigateReducer;
|
|
}
|
|
});
|
|
const _navigation = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation.js [app-ssr] (ecmascript)");
|
|
const _cache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)");
|
|
const _pprnavigations = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/ppr-navigations.js [app-ssr] (ecmascript)");
|
|
const DYNAMIC_STALETIME_MS = Number(("TURBOPACK compile-time value", "0")) * 1000;
|
|
const STATIC_STALETIME_MS = (0, _cache.getStaleTimeMs)(Number(("TURBOPACK compile-time value", "300")));
|
|
function navigateReducer(state, action) {
|
|
const { url, isExternalUrl, navigateType, scrollBehavior } = action;
|
|
if (isExternalUrl) {
|
|
return (0, _navigation.completeHardNavigation)(state, url, navigateType);
|
|
}
|
|
// Handles case where `<meta http-equiv="refresh">` tag is present,
|
|
// which will trigger an MPA navigation.
|
|
if (document.getElementById('__next-page-redirect')) {
|
|
return (0, _navigation.completeHardNavigation)(state, url, navigateType);
|
|
}
|
|
// Temporary glue code between the router reducer and the new navigation
|
|
// implementation. Eventually we'll rewrite the router reducer to a
|
|
// state machine.
|
|
const currentUrl = new URL(state.canonicalUrl, location.origin);
|
|
const currentRenderedSearch = state.renderedSearch;
|
|
return (0, _navigation.navigate)(state, url, currentUrl, currentRenderedSearch, state.cache, state.tree, state.nextUrl, _pprnavigations.FreshnessPolicy.Default, scrollBehavior, navigateType);
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/bfcache.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
UnknownDynamicStaleTime: null,
|
|
computeDynamicStaleAt: null,
|
|
invalidateBfCache: null,
|
|
readFromBFCache: null,
|
|
readFromBFCacheDuringRegularNavigation: null,
|
|
updateBFCacheEntryStaleAt: null,
|
|
writeHeadToBFCache: null,
|
|
writeToBFCache: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
UnknownDynamicStaleTime: function() {
|
|
return UnknownDynamicStaleTime;
|
|
},
|
|
computeDynamicStaleAt: function() {
|
|
return computeDynamicStaleAt;
|
|
},
|
|
invalidateBfCache: function() {
|
|
return invalidateBfCache;
|
|
},
|
|
readFromBFCache: function() {
|
|
return readFromBFCache;
|
|
},
|
|
readFromBFCacheDuringRegularNavigation: function() {
|
|
return readFromBFCacheDuringRegularNavigation;
|
|
},
|
|
updateBFCacheEntryStaleAt: function() {
|
|
return updateBFCacheEntryStaleAt;
|
|
},
|
|
writeHeadToBFCache: function() {
|
|
return writeHeadToBFCache;
|
|
},
|
|
writeToBFCache: function() {
|
|
return writeToBFCache;
|
|
}
|
|
});
|
|
const _navigatereducer = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/reducers/navigate-reducer.js [app-ssr] (ecmascript)");
|
|
const _cachemap = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-map.js [app-ssr] (ecmascript)");
|
|
const UnknownDynamicStaleTime = -1;
|
|
function computeDynamicStaleAt(now, dynamicStaleTimeSeconds) {
|
|
return dynamicStaleTimeSeconds !== UnknownDynamicStaleTime ? now + dynamicStaleTimeSeconds * 1000 : now + _navigatereducer.DYNAMIC_STALETIME_MS;
|
|
}
|
|
const bfcacheMap = (0, _cachemap.createCacheMap)();
|
|
let currentBfCacheVersion = 0;
|
|
function invalidateBfCache() {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
}
|
|
function writeToBFCache(now, varyPath, rsc, prefetchRsc, head, prefetchHead, dynamicStaleAt) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
const entry = undefined;
|
|
const isRevalidation = undefined;
|
|
}
|
|
function writeHeadToBFCache(now, varyPath, head, prefetchHead, dynamicStaleAt) {
|
|
// Read the special "segment" that represents the head data.
|
|
writeToBFCache(now, varyPath, head, prefetchHead, null, null, dynamicStaleAt);
|
|
}
|
|
function updateBFCacheEntryStaleAt(varyPath, newStaleAt) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
const isRevalidation = undefined;
|
|
// Read with staleness bypass (-1) so we can update even stale entries
|
|
const entry = undefined;
|
|
}
|
|
function readFromBFCache(varyPath) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return null;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
const isRevalidation = undefined;
|
|
}
|
|
function readFromBFCacheDuringRegularNavigation(now, varyPath) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return null;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
const isRevalidation = undefined;
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/dev/debug-channel.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
createDebugChannel: null,
|
|
getOrCreateDebugChannelReadableWriterPair: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
createDebugChannel: function() {
|
|
return createDebugChannel;
|
|
},
|
|
getOrCreateDebugChannelReadableWriterPair: function() {
|
|
return getOrCreateDebugChannelReadableWriterPair;
|
|
}
|
|
});
|
|
const _approuterheaders = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)");
|
|
const _invarianterror = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/invariant-error.js [app-ssr] (ecmascript)");
|
|
const pairs = new Map();
|
|
function getOrCreateDebugChannelReadableWriterPair(requestId) {
|
|
let pair = pairs.get(requestId);
|
|
if (!pair) {
|
|
const { readable, writable } = new TransformStream();
|
|
pair = {
|
|
readable,
|
|
writer: writable.getWriter()
|
|
};
|
|
pairs.set(requestId, pair);
|
|
pair.writer.closed.finally(()=>pairs.delete(requestId));
|
|
}
|
|
return pair;
|
|
}
|
|
function createDebugChannel(requestHeaders) {
|
|
let requestId;
|
|
if (requestHeaders) {
|
|
requestId = requestHeaders[_approuterheaders.NEXT_REQUEST_ID_HEADER] ?? undefined;
|
|
if (!requestId) {
|
|
throw Object.defineProperty(new _invarianterror.InvariantError(`Expected a ${JSON.stringify(_approuterheaders.NEXT_REQUEST_ID_HEADER)} request header.`), "__NEXT_ERROR_CODE", {
|
|
value: "E854",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
} else {
|
|
requestId = self.__next_r;
|
|
if (!requestId) {
|
|
throw Object.defineProperty(new _invarianterror.InvariantError(`Expected a request ID to be defined for the document via self.__next_r.`), "__NEXT_ERROR_CODE", {
|
|
value: "E806",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
const { readable } = getOrCreateDebugChannelReadableWriterPair(requestId);
|
|
return {
|
|
readable
|
|
};
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/router-reducer/fetch-server-response.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
createFetch: null,
|
|
createFromNextReadableStream: null,
|
|
decodeStaticStage: null,
|
|
fetchServerResponse: null,
|
|
processFetch: 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;
|
|
},
|
|
decodeStaticStage: function() {
|
|
return decodeStaticStage;
|
|
},
|
|
fetchServerResponse: function() {
|
|
return fetchServerResponse;
|
|
},
|
|
processFetch: function() {
|
|
return processFetch;
|
|
},
|
|
resolveStaticStageData: function() {
|
|
return resolveStaticStageData;
|
|
}
|
|
});
|
|
const _client = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react-server-dom-turbopack-client.js [app-ssr] (ecmascript)");
|
|
const _invarianterror = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/invariant-error.js [app-ssr] (ecmascript)");
|
|
const _approuterheaders = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)");
|
|
const _appcallserver = __turbopack_context__.r("[project]/node_modules/next/dist/client/app-call-server.js [app-ssr] (ecmascript)");
|
|
const _appfindsourcemapurl = __turbopack_context__.r("[project]/node_modules/next/dist/client/app-find-source-map-url.js [app-ssr] (ecmascript)");
|
|
const _flightdatahelpers = __turbopack_context__.r("[project]/node_modules/next/dist/client/flight-data-helpers.js [app-ssr] (ecmascript)");
|
|
const _setcachebustingsearchparam = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/set-cache-busting-search-param.js [app-ssr] (ecmascript)");
|
|
const _routeparams = __turbopack_context__.r("[project]/node_modules/next/dist/client/route-params.js [app-ssr] (ecmascript)");
|
|
const _deploymentid = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/deployment-id.js [app-ssr] (ecmascript)");
|
|
const _navigationbuildid = __turbopack_context__.r("[project]/node_modules/next/dist/client/navigation-build-id.js [app-ssr] (ecmascript)");
|
|
const _constants = __turbopack_context__.r("[project]/node_modules/next/dist/lib/constants.js [app-ssr] (ecmascript)");
|
|
const _cache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)");
|
|
const _bfcache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/bfcache.js [app-ssr] (ecmascript)");
|
|
const createFromReadableStream = _client.createFromReadableStream;
|
|
const createFromFetch = _client.createFromFetch;
|
|
let createDebugChannel;
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
createDebugChannel = __turbopack_context__.r("[project]/node_modules/next/dist/client/dev/debug-channel.js [app-ssr] (ecmascript)").createDebugChannel;
|
|
}
|
|
function doMpaNavigation(url) {
|
|
return (0, _routeparams.urlToUrlWithoutFlightMarker)(new URL(url, location.origin)).toString();
|
|
}
|
|
let isPageUnloading = false;
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
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 (("TURBOPACK compile-time value", "development") === '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 ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
// 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 = ("TURBOPACK compile-time value", false) && !("TURBOPACK compile-time value", false);
|
|
const shouldImmediatelyDecode = !isLegacyPPR;
|
|
const res = await createFetch(url, headers, 'auto', shouldImmediatelyDecode);
|
|
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 ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
// 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 ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
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
|
|
};
|
|
} catch (err) {
|
|
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 ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
return {
|
|
response,
|
|
cacheData: null
|
|
};
|
|
}
|
|
async function resolveStaticStageData(cacheData, flightResponse, headers) {
|
|
const { isResponsePartial, responseBodyClone } = cacheData;
|
|
if (responseBodyClone) {
|
|
if (!isResponsePartial) {
|
|
// Fully static — cache the entire decoded response as-is.
|
|
responseBodyClone.cancel();
|
|
return {
|
|
response: flightResponse,
|
|
isResponsePartial: false
|
|
};
|
|
}
|
|
if (flightResponse.l !== undefined) {
|
|
// Partially static — truncate the body clone at the byte boundary and
|
|
// decode it.
|
|
const response = await decodeStaticStage(responseBodyClone, flightResponse.l, headers);
|
|
return {
|
|
response,
|
|
isResponsePartial: true
|
|
};
|
|
}
|
|
// No caching — cancel the unused clone.
|
|
responseBodyClone.cancel();
|
|
}
|
|
return null;
|
|
}
|
|
async function decodeStaticStage(responseBodyClone, staticStageByteLengthPromise, headers) {
|
|
const staticStageByteLength = await staticStageByteLengthPromise;
|
|
const truncatedStream = truncateStream(responseBodyClone, staticStageByteLength);
|
|
return createFromNextReadableStream(truncatedStream, headers, {
|
|
allowPartialStream: true
|
|
});
|
|
}
|
|
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 ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
const deploymentId = (0, _deploymentid.getDeploymentId)();
|
|
if (deploymentId) {
|
|
headers['x-deployment-id'] = deploymentId;
|
|
}
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
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 = 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 ? createFromNextFetch(fetchPromise, headers) : 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 ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
// 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)
|
|
});
|
|
}
|
|
function truncateStream(stream, byteLength) {
|
|
const reader = stream.getReader();
|
|
let remaining = byteLength;
|
|
return new ReadableStream({
|
|
async pull (controller) {
|
|
if (remaining <= 0) {
|
|
reader.cancel();
|
|
controller.close();
|
|
return;
|
|
}
|
|
const { done, value } = await reader.read();
|
|
if (done) {
|
|
controller.close();
|
|
return;
|
|
}
|
|
if (value.byteLength <= remaining) {
|
|
controller.enqueue(value);
|
|
remaining -= value.byteLength;
|
|
} else {
|
|
controller.enqueue(value.subarray(0, remaining));
|
|
remaining = 0;
|
|
reader.cancel();
|
|
controller.close();
|
|
}
|
|
},
|
|
cancel () {
|
|
reader.cancel();
|
|
}
|
|
});
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
EntryStatus: null,
|
|
attemptToFulfillDynamicSegmentFromBFCache: null,
|
|
attemptToUpgradeSegmentFromBFCache: null,
|
|
canNewFetchStrategyProvideMoreContent: null,
|
|
convertReusedFlightRouterStateToRouteTree: null,
|
|
convertRootFlightRouterStateToRouteTree: null,
|
|
convertRouteTreeToFlightRouterState: null,
|
|
createDetachedSegmentCacheEntry: null,
|
|
createMetadataRouteTree: null,
|
|
deprecated_requestOptimisticRouteCacheEntry: null,
|
|
fetchInlinedSegmentsOnCacheMiss: null,
|
|
fetchRouteOnCacheMiss: null,
|
|
fetchSegmentOnCacheMiss: null,
|
|
fetchSegmentPrefetchesUsingDynamicRequest: null,
|
|
fulfillRouteCacheEntry: null,
|
|
getCurrentRouteCacheVersion: null,
|
|
getCurrentSegmentCacheVersion: null,
|
|
getStaleAt: null,
|
|
getStaleTimeMs: null,
|
|
invalidateEntirePrefetchCache: null,
|
|
invalidateRouteCacheEntries: null,
|
|
invalidateSegmentCacheEntries: null,
|
|
markRouteEntryAsDynamicRewrite: null,
|
|
overwriteRevalidatingSegmentCacheEntry: null,
|
|
pingInvalidationListeners: null,
|
|
processRuntimePrefetchStream: null,
|
|
readOrCreateRevalidatingSegmentEntry: null,
|
|
readOrCreateRouteCacheEntry: null,
|
|
readOrCreateSegmentCacheEntry: null,
|
|
readRouteCacheEntry: null,
|
|
readSegmentCacheEntry: null,
|
|
stripIsPartialByte: null,
|
|
upgradeToPendingSegment: null,
|
|
upsertSegmentEntry: null,
|
|
waitForSegmentCacheEntry: null,
|
|
writeDynamicRenderResponseIntoCache: null,
|
|
writeRouteIntoCache: null,
|
|
writeStaticStageResponseIntoCache: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
EntryStatus: function() {
|
|
return EntryStatus;
|
|
},
|
|
attemptToFulfillDynamicSegmentFromBFCache: function() {
|
|
return attemptToFulfillDynamicSegmentFromBFCache;
|
|
},
|
|
attemptToUpgradeSegmentFromBFCache: function() {
|
|
return attemptToUpgradeSegmentFromBFCache;
|
|
},
|
|
canNewFetchStrategyProvideMoreContent: function() {
|
|
return canNewFetchStrategyProvideMoreContent;
|
|
},
|
|
convertReusedFlightRouterStateToRouteTree: function() {
|
|
return convertReusedFlightRouterStateToRouteTree;
|
|
},
|
|
convertRootFlightRouterStateToRouteTree: function() {
|
|
return convertRootFlightRouterStateToRouteTree;
|
|
},
|
|
convertRouteTreeToFlightRouterState: function() {
|
|
return convertRouteTreeToFlightRouterState;
|
|
},
|
|
createDetachedSegmentCacheEntry: function() {
|
|
return createDetachedSegmentCacheEntry;
|
|
},
|
|
createMetadataRouteTree: function() {
|
|
return createMetadataRouteTree;
|
|
},
|
|
deprecated_requestOptimisticRouteCacheEntry: function() {
|
|
return deprecated_requestOptimisticRouteCacheEntry;
|
|
},
|
|
fetchInlinedSegmentsOnCacheMiss: function() {
|
|
return fetchInlinedSegmentsOnCacheMiss;
|
|
},
|
|
fetchRouteOnCacheMiss: function() {
|
|
return fetchRouteOnCacheMiss;
|
|
},
|
|
fetchSegmentOnCacheMiss: function() {
|
|
return fetchSegmentOnCacheMiss;
|
|
},
|
|
fetchSegmentPrefetchesUsingDynamicRequest: function() {
|
|
return fetchSegmentPrefetchesUsingDynamicRequest;
|
|
},
|
|
fulfillRouteCacheEntry: function() {
|
|
return fulfillRouteCacheEntry;
|
|
},
|
|
getCurrentRouteCacheVersion: function() {
|
|
return getCurrentRouteCacheVersion;
|
|
},
|
|
getCurrentSegmentCacheVersion: function() {
|
|
return getCurrentSegmentCacheVersion;
|
|
},
|
|
getStaleAt: function() {
|
|
return getStaleAt;
|
|
},
|
|
getStaleTimeMs: function() {
|
|
return getStaleTimeMs;
|
|
},
|
|
invalidateEntirePrefetchCache: function() {
|
|
return invalidateEntirePrefetchCache;
|
|
},
|
|
invalidateRouteCacheEntries: function() {
|
|
return invalidateRouteCacheEntries;
|
|
},
|
|
invalidateSegmentCacheEntries: function() {
|
|
return invalidateSegmentCacheEntries;
|
|
},
|
|
markRouteEntryAsDynamicRewrite: function() {
|
|
return markRouteEntryAsDynamicRewrite;
|
|
},
|
|
overwriteRevalidatingSegmentCacheEntry: function() {
|
|
return overwriteRevalidatingSegmentCacheEntry;
|
|
},
|
|
pingInvalidationListeners: function() {
|
|
return pingInvalidationListeners;
|
|
},
|
|
processRuntimePrefetchStream: function() {
|
|
return processRuntimePrefetchStream;
|
|
},
|
|
readOrCreateRevalidatingSegmentEntry: function() {
|
|
return readOrCreateRevalidatingSegmentEntry;
|
|
},
|
|
readOrCreateRouteCacheEntry: function() {
|
|
return readOrCreateRouteCacheEntry;
|
|
},
|
|
readOrCreateSegmentCacheEntry: function() {
|
|
return readOrCreateSegmentCacheEntry;
|
|
},
|
|
readRouteCacheEntry: function() {
|
|
return readRouteCacheEntry;
|
|
},
|
|
readSegmentCacheEntry: function() {
|
|
return readSegmentCacheEntry;
|
|
},
|
|
stripIsPartialByte: function() {
|
|
return stripIsPartialByte;
|
|
},
|
|
upgradeToPendingSegment: function() {
|
|
return upgradeToPendingSegment;
|
|
},
|
|
upsertSegmentEntry: function() {
|
|
return upsertSegmentEntry;
|
|
},
|
|
waitForSegmentCacheEntry: function() {
|
|
return waitForSegmentCacheEntry;
|
|
},
|
|
writeDynamicRenderResponseIntoCache: function() {
|
|
return writeDynamicRenderResponseIntoCache;
|
|
},
|
|
writeRouteIntoCache: function() {
|
|
return writeRouteIntoCache;
|
|
},
|
|
writeStaticStageResponseIntoCache: function() {
|
|
return writeStaticStageResponseIntoCache;
|
|
}
|
|
});
|
|
const _varyparamsdecoding = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment-cache/vary-params-decoding.js [app-ssr] (ecmascript)");
|
|
const _approuterheaders = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/app-router-headers.js [app-ssr] (ecmascript)");
|
|
const _fetchserverresponse = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/fetch-server-response.js [app-ssr] (ecmascript)");
|
|
const _scheduler = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/scheduler.js [app-ssr] (ecmascript)");
|
|
const _varypath = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/vary-path.js [app-ssr] (ecmascript)");
|
|
const _createhreffromurl = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/create-href-from-url.js [app-ssr] (ecmascript)");
|
|
const _cachekey = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-key.js [app-ssr] (ecmascript)");
|
|
const _routeparams = __turbopack_context__.r("[project]/node_modules/next/dist/client/route-params.js [app-ssr] (ecmascript)");
|
|
const _cachemap = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-map.js [app-ssr] (ecmascript)");
|
|
const _segmentvalueencoding = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment-cache/segment-value-encoding.js [app-ssr] (ecmascript)");
|
|
const _flightdatahelpers = __turbopack_context__.r("[project]/node_modules/next/dist/client/flight-data-helpers.js [app-ssr] (ecmascript)");
|
|
const _navigatereducer = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/reducers/navigate-reducer.js [app-ssr] (ecmascript)");
|
|
const _links = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/links.js [app-ssr] (ecmascript)");
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _promisewithresolvers = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/promise-with-resolvers.js [app-ssr] (ecmascript)");
|
|
const _bfcache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/bfcache.js [app-ssr] (ecmascript)");
|
|
const _optimisticroutes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/optimistic-routes.js [app-ssr] (ecmascript)");
|
|
const _navigation = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation.js [app-ssr] (ecmascript)");
|
|
const _navigationbuildid = __turbopack_context__.r("[project]/node_modules/next/dist/client/navigation-build-id.js [app-ssr] (ecmascript)");
|
|
const _constants = __turbopack_context__.r("[project]/node_modules/next/dist/lib/constants.js [app-ssr] (ecmascript)");
|
|
function getStaleTimeMs(staleTimeSeconds) {
|
|
return Math.max(staleTimeSeconds, 30) * 1000;
|
|
}
|
|
var EntryStatus = /*#__PURE__*/ function(EntryStatus) {
|
|
EntryStatus[EntryStatus["Empty"] = 0] = "Empty";
|
|
EntryStatus[EntryStatus["Pending"] = 1] = "Pending";
|
|
EntryStatus[EntryStatus["Fulfilled"] = 2] = "Fulfilled";
|
|
EntryStatus[EntryStatus["Rejected"] = 3] = "Rejected";
|
|
return EntryStatus;
|
|
}({});
|
|
const isOutputExportMode = ("TURBOPACK compile-time value", "development") === 'production' && ("TURBOPACK compile-time value", void 0) === 'export';
|
|
const MetadataOnlyRequestTree = [
|
|
'',
|
|
{},
|
|
null,
|
|
'metadata-only'
|
|
];
|
|
let routeCacheMap = (0, _cachemap.createCacheMap)();
|
|
let segmentCacheMap = (0, _cachemap.createCacheMap)();
|
|
// All invalidation listeners for the whole cache are tracked in single set.
|
|
// Since we don't yet support tag or path-based invalidation, there's no point
|
|
// tracking them any more granularly than this. Once we add granular
|
|
// invalidation, that may change, though generally the model is to just notify
|
|
// the listeners and allow the caller to poll the prefetch cache with a new
|
|
// prefetch task if desired.
|
|
let invalidationListeners = null;
|
|
// Incrementing counters used to track cache invalidations. Route and segment
|
|
// caches have separate versions so they can be invalidated independently.
|
|
// Invalidation does not eagerly evict anything from the cache; entries are
|
|
// lazily evicted when read.
|
|
let currentRouteCacheVersion = 0;
|
|
let currentSegmentCacheVersion = 0;
|
|
function getCurrentRouteCacheVersion() {
|
|
return currentRouteCacheVersion;
|
|
}
|
|
function getCurrentSegmentCacheVersion() {
|
|
return currentSegmentCacheVersion;
|
|
}
|
|
function invalidateEntirePrefetchCache(nextUrl, tree) {
|
|
currentRouteCacheVersion++;
|
|
currentSegmentCacheVersion++;
|
|
(0, _links.pingVisibleLinks)(nextUrl, tree);
|
|
pingInvalidationListeners(nextUrl, tree);
|
|
}
|
|
function invalidateRouteCacheEntries(nextUrl, tree) {
|
|
currentRouteCacheVersion++;
|
|
(0, _links.pingVisibleLinks)(nextUrl, tree);
|
|
pingInvalidationListeners(nextUrl, tree);
|
|
}
|
|
function invalidateSegmentCacheEntries(nextUrl, tree) {
|
|
currentSegmentCacheVersion++;
|
|
(0, _links.pingVisibleLinks)(nextUrl, tree);
|
|
pingInvalidationListeners(nextUrl, tree);
|
|
}
|
|
function attachInvalidationListener(task) {
|
|
// This function is called whenever a prefetch task reads a cache entry. If
|
|
// the task has an onInvalidate function associated with it — i.e. the one
|
|
// optionally passed to router.prefetch(onInvalidate) — then we attach that
|
|
// listener to the every cache entry that the task reads. Then, if an entry
|
|
// is invalidated, we call the function.
|
|
if (task.onInvalidate !== null) {
|
|
if (invalidationListeners === null) {
|
|
invalidationListeners = new Set([
|
|
task
|
|
]);
|
|
} else {
|
|
invalidationListeners.add(task);
|
|
}
|
|
}
|
|
}
|
|
function notifyInvalidationListener(task) {
|
|
const onInvalidate = task.onInvalidate;
|
|
if (onInvalidate !== null) {
|
|
// Clear the callback from the task object to guarantee it's not called more
|
|
// than once.
|
|
task.onInvalidate = null;
|
|
// This is a user-space function, so we must wrap in try/catch.
|
|
try {
|
|
onInvalidate();
|
|
} catch (error) {
|
|
if (typeof reportError === 'function') {
|
|
reportError(error);
|
|
} else {
|
|
console.error(error);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function pingInvalidationListeners(nextUrl, tree) {
|
|
// The rough equivalent of pingVisibleLinks, but for onInvalidate callbacks.
|
|
// This is called when the Next-Url or the base tree changes, since those
|
|
// may affect the result of a prefetch task. It's also called after a
|
|
// cache invalidation.
|
|
if (invalidationListeners !== null) {
|
|
const tasks = invalidationListeners;
|
|
invalidationListeners = null;
|
|
for (const task of tasks){
|
|
if ((0, _scheduler.isPrefetchTaskDirty)(task, nextUrl, tree)) {
|
|
notifyInvalidationListener(task);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function readRouteCacheEntry(now, key) {
|
|
const varyPath = (0, _varypath.getRouteVaryPath)(key.pathname, key.search, key.nextUrl);
|
|
const isRevalidation = false;
|
|
const existingEntry = (0, _cachemap.getFromCacheMap)(now, getCurrentRouteCacheVersion(), routeCacheMap, varyPath, isRevalidation);
|
|
if (existingEntry !== null) {
|
|
return existingEntry;
|
|
}
|
|
// No cache hit. Attempt to construct from template using the new
|
|
// optimistic routing mechanism (pattern-based matching).
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
return null;
|
|
}
|
|
function readSegmentCacheEntry(now, varyPath) {
|
|
const isRevalidation = false;
|
|
return (0, _cachemap.getFromCacheMap)(now, getCurrentSegmentCacheVersion(), segmentCacheMap, varyPath, isRevalidation);
|
|
}
|
|
function readRevalidatingSegmentCacheEntry(now, varyPath) {
|
|
const isRevalidation = true;
|
|
return (0, _cachemap.getFromCacheMap)(now, getCurrentSegmentCacheVersion(), segmentCacheMap, varyPath, isRevalidation);
|
|
}
|
|
function waitForSegmentCacheEntry(pendingEntry) {
|
|
// Because the entry is pending, there's already a in-progress request.
|
|
// Attach a promise to the entry that will resolve when the server responds.
|
|
let promiseWithResolvers = pendingEntry.promise;
|
|
if (promiseWithResolvers === null) {
|
|
promiseWithResolvers = pendingEntry.promise = (0, _promisewithresolvers.createPromiseWithResolvers)();
|
|
} else {
|
|
// There's already a promise we can use
|
|
}
|
|
return promiseWithResolvers.promise;
|
|
}
|
|
function createDetachedRouteCacheEntry() {
|
|
return {
|
|
canonicalUrl: null,
|
|
status: 0,
|
|
blockedTasks: null,
|
|
tree: null,
|
|
metadata: null,
|
|
// This is initialized to true because we don't know yet whether the route
|
|
// could be intercepted. It's only set to false once we receive a response
|
|
// from the server.
|
|
couldBeIntercepted: true,
|
|
// Similarly, we don't yet know if the route supports PPR.
|
|
supportsPerSegmentPrefetching: false,
|
|
renderedSearch: null,
|
|
// Map-related fields
|
|
ref: null,
|
|
size: 0,
|
|
// Since this is an empty entry, there's no reason to ever evict it. It will
|
|
// be updated when the data is populated.
|
|
staleAt: Infinity,
|
|
version: getCurrentRouteCacheVersion()
|
|
};
|
|
}
|
|
function readOrCreateRouteCacheEntry(now, task, key) {
|
|
attachInvalidationListener(task);
|
|
const existingEntry = readRouteCacheEntry(now, key);
|
|
if (existingEntry !== null) {
|
|
return existingEntry;
|
|
}
|
|
// Create a pending entry and add it to the cache.
|
|
const pendingEntry = createDetachedRouteCacheEntry();
|
|
const varyPath = (0, _varypath.getRouteVaryPath)(key.pathname, key.search, key.nextUrl);
|
|
const isRevalidation = false;
|
|
(0, _cachemap.setInCacheMap)(routeCacheMap, varyPath, pendingEntry, isRevalidation);
|
|
return pendingEntry;
|
|
}
|
|
function deprecated_requestOptimisticRouteCacheEntry(now, requestedUrl, nextUrl) {
|
|
// This function is called during a navigation when there was no matching
|
|
// route tree in the prefetch cache. Before de-opting to a blocking,
|
|
// unprefetched navigation, we will first attempt to construct an "optimistic"
|
|
// route tree by checking the cache for similar routes.
|
|
//
|
|
// Check if there's a route with the same pathname, but with different
|
|
// search params. We can then base our optimistic route tree on this entry.
|
|
//
|
|
// Conceptually, we are simulating what would happen if we did perform a
|
|
// prefetch the requested URL, under the assumption that the server will
|
|
// not redirect or rewrite the request in a different manner than the
|
|
// base route tree. This assumption might not hold, in which case we'll have
|
|
// to recover when we perform the dynamic navigation request. However, this
|
|
// is what would happen if a route were dynamically rewritten/redirected
|
|
// in between the prefetch and the navigation. So the logic needs to exist
|
|
// to handle this case regardless.
|
|
// Look for a route with the same pathname, but with an empty search string.
|
|
// TODO: There's nothing inherently special about the empty search string;
|
|
// it's chosen somewhat arbitrarily, with the rationale that it's the most
|
|
// likely one to exist. But we should update this to match _any_ search
|
|
// string. The plan is to generalize this logic alongside other improvements
|
|
// related to "fallback" cache entries.
|
|
const requestedSearch = requestedUrl.search;
|
|
if (requestedSearch === '') {
|
|
// The caller would have already checked if a route with an empty search
|
|
// string is in the cache. So we can bail out here.
|
|
return null;
|
|
}
|
|
const urlWithoutSearchParams = new URL(requestedUrl);
|
|
urlWithoutSearchParams.search = '';
|
|
const routeWithNoSearchParams = readRouteCacheEntry(now, (0, _cachekey.createCacheKey)(urlWithoutSearchParams.href, nextUrl));
|
|
if (routeWithNoSearchParams === null || routeWithNoSearchParams.status !== 2) {
|
|
// Bail out of constructing an optimistic route tree. This will result in
|
|
// a blocking, unprefetched navigation.
|
|
return null;
|
|
}
|
|
// Now we have a base route tree we can "patch" with our optimistic values.
|
|
// Optimistically assume that redirects for the requested pathname do
|
|
// not vary on the search string. Therefore, if the base route was
|
|
// redirected to a different search string, then the optimistic route
|
|
// should be redirected to the same search string. Otherwise, we use
|
|
// the requested search string.
|
|
const canonicalUrlForRouteWithNoSearchParams = new URL(routeWithNoSearchParams.canonicalUrl, requestedUrl.origin);
|
|
const optimisticCanonicalSearch = canonicalUrlForRouteWithNoSearchParams.search !== '' ? canonicalUrlForRouteWithNoSearchParams.search : requestedSearch;
|
|
// Similarly, optimistically assume that rewrites for the requested
|
|
// pathname do not vary on the search string. Therefore, if the base
|
|
// route was rewritten to a different search string, then the optimistic
|
|
// route should be rewritten to the same search string. Otherwise, we use
|
|
// the requested search string.
|
|
const optimisticRenderedSearch = routeWithNoSearchParams.renderedSearch !== '' ? routeWithNoSearchParams.renderedSearch : requestedSearch;
|
|
const optimisticUrl = new URL(routeWithNoSearchParams.canonicalUrl, location.origin);
|
|
optimisticUrl.search = optimisticCanonicalSearch;
|
|
const optimisticCanonicalUrl = (0, _createhreffromurl.createHrefFromUrl)(optimisticUrl);
|
|
const optimisticRouteTree = deprecated_createOptimisticRouteTree(routeWithNoSearchParams.tree, optimisticRenderedSearch);
|
|
const optimisticMetadataTree = deprecated_createOptimisticRouteTree(routeWithNoSearchParams.metadata, optimisticRenderedSearch);
|
|
// Clone the base route tree, and override the relevant fields with our
|
|
// optimistic values.
|
|
const optimisticEntry = {
|
|
canonicalUrl: optimisticCanonicalUrl,
|
|
status: 2,
|
|
// This isn't cloned because it's instance-specific
|
|
blockedTasks: null,
|
|
tree: optimisticRouteTree,
|
|
metadata: optimisticMetadataTree,
|
|
couldBeIntercepted: routeWithNoSearchParams.couldBeIntercepted,
|
|
supportsPerSegmentPrefetching: routeWithNoSearchParams.supportsPerSegmentPrefetching,
|
|
hasDynamicRewrite: routeWithNoSearchParams.hasDynamicRewrite,
|
|
// Override the rendered search with the optimistic value.
|
|
renderedSearch: optimisticRenderedSearch,
|
|
// Map-related fields
|
|
ref: null,
|
|
size: 0,
|
|
staleAt: routeWithNoSearchParams.staleAt,
|
|
version: routeWithNoSearchParams.version
|
|
};
|
|
// Do not insert this entry into the cache. It only exists so we can
|
|
// perform the current navigation. Just return it to the caller.
|
|
return optimisticEntry;
|
|
}
|
|
function deprecated_createOptimisticRouteTree(tree, newRenderedSearch) {
|
|
// Create a new route tree that identical to the original one except for
|
|
// the rendered search string, which is contained in the vary path.
|
|
let clonedSlots = null;
|
|
const originalSlots = tree.slots;
|
|
if (originalSlots !== null) {
|
|
clonedSlots = {};
|
|
for(const parallelRouteKey in originalSlots){
|
|
const childTree = originalSlots[parallelRouteKey];
|
|
clonedSlots[parallelRouteKey] = deprecated_createOptimisticRouteTree(childTree, newRenderedSearch);
|
|
}
|
|
}
|
|
// We only need to clone the vary path if the route is a page.
|
|
if (tree.isPage) {
|
|
return {
|
|
requestKey: tree.requestKey,
|
|
segment: tree.segment,
|
|
refreshState: tree.refreshState,
|
|
varyPath: (0, _varypath.clonePageVaryPathWithNewSearchParams)(tree.varyPath, newRenderedSearch),
|
|
isPage: true,
|
|
slots: clonedSlots,
|
|
prefetchHints: tree.prefetchHints
|
|
};
|
|
}
|
|
return {
|
|
requestKey: tree.requestKey,
|
|
segment: tree.segment,
|
|
refreshState: tree.refreshState,
|
|
varyPath: tree.varyPath,
|
|
isPage: false,
|
|
slots: clonedSlots,
|
|
prefetchHints: tree.prefetchHints
|
|
};
|
|
}
|
|
function readOrCreateSegmentCacheEntry(now, fetchStrategy, tree) {
|
|
const existingEntry = readSegmentCacheEntry(now, tree.varyPath);
|
|
if (existingEntry !== null) {
|
|
return existingEntry;
|
|
}
|
|
// Create a pending entry and add it to the cache. The stale time is set to a
|
|
// default value; the actual stale time will be set when the entry is
|
|
// fulfilled with data from the server response.
|
|
const varyPathForRequest = (0, _varypath.getSegmentVaryPathForRequest)(fetchStrategy, tree);
|
|
const pendingEntry = createDetachedSegmentCacheEntry(now);
|
|
const isRevalidation = false;
|
|
(0, _cachemap.setInCacheMap)(segmentCacheMap, varyPathForRequest, pendingEntry, isRevalidation);
|
|
return pendingEntry;
|
|
}
|
|
function readOrCreateRevalidatingSegmentEntry(now, fetchStrategy, tree) {
|
|
// This function is called when we've already confirmed that a particular
|
|
// segment is cached, but we want to perform another request anyway in case it
|
|
// returns more complete and/or fresher data than we already have. The logic
|
|
// for deciding whether to replace the existing entry is handled elsewhere;
|
|
// this function just handles retrieving a cache entry that we can use to
|
|
// track the revalidation.
|
|
//
|
|
// The reason revalidations are stored in the cache is because we need to be
|
|
// able to dedupe multiple revalidation requests. The reason they have to be
|
|
// handled specially is because we shouldn't overwrite a "normal" entry if
|
|
// one exists at the same keypath. So, for each internal cache location, there
|
|
// is a special "revalidation" slot that is used solely for this purpose.
|
|
//
|
|
// You can think of it as if all the revalidation entries were stored in a
|
|
// separate cache map from the canonical entries, and then transfered to the
|
|
// canonical cache map once the request is complete — this isn't how it's
|
|
// actually implemented, since it's more efficient to store them in the same
|
|
// data structure as the normal entries, but that's how it's modeled
|
|
// conceptually.
|
|
// TODO: Once we implement Fallback behavior for params, where an entry is
|
|
// re-keyed based on response information, we'll need to account for the
|
|
// possibility that the keypath of the previous entry is more generic than
|
|
// the keypath of the revalidating entry. In other words, the server could
|
|
// return a less generic entry upon revalidation. For now, though, this isn't
|
|
// a concern because the keypath is based solely on the prefetch strategy,
|
|
// not on data contained in the response.
|
|
const existingEntry = readRevalidatingSegmentCacheEntry(now, tree.varyPath);
|
|
if (existingEntry !== null) {
|
|
return existingEntry;
|
|
}
|
|
// Create a pending entry and add it to the cache. The stale time is set to a
|
|
// default value; the actual stale time will be set when the entry is
|
|
// fulfilled with data from the server response.
|
|
const varyPathForRequest = (0, _varypath.getSegmentVaryPathForRequest)(fetchStrategy, tree);
|
|
const pendingEntry = createDetachedSegmentCacheEntry(now);
|
|
const isRevalidation = true;
|
|
(0, _cachemap.setInCacheMap)(segmentCacheMap, varyPathForRequest, pendingEntry, isRevalidation);
|
|
return pendingEntry;
|
|
}
|
|
function overwriteRevalidatingSegmentCacheEntry(now, fetchStrategy, tree) {
|
|
// This function is called when we've already decided to replace an existing
|
|
// revalidation entry. Create a new entry and write it into the cache,
|
|
// overwriting the previous value. The stale time is set to a default value;
|
|
// the actual stale time will be set when the entry is fulfilled with data
|
|
// from the server response.
|
|
const varyPathForRequest = (0, _varypath.getSegmentVaryPathForRequest)(fetchStrategy, tree);
|
|
const pendingEntry = createDetachedSegmentCacheEntry(now);
|
|
const isRevalidation = true;
|
|
(0, _cachemap.setInCacheMap)(segmentCacheMap, varyPathForRequest, pendingEntry, isRevalidation);
|
|
return pendingEntry;
|
|
}
|
|
function upsertSegmentEntry(now, varyPath, candidateEntry) {
|
|
// We have a new entry that has not yet been inserted into the cache. Before
|
|
// we do so, we need to confirm whether it takes precedence over the existing
|
|
// entry (if one exists).
|
|
// TODO: We should not upsert an entry if its key was invalidated in the time
|
|
// since the request was made. We can do that by passing the "owner" entry to
|
|
// this function and confirming it's the same as `existingEntry`.
|
|
if ((0, _cachemap.isValueExpired)(now, getCurrentSegmentCacheVersion(), candidateEntry)) {
|
|
// The entry is expired. We cannot upsert it.
|
|
return null;
|
|
}
|
|
const existingEntry = readSegmentCacheEntry(now, varyPath);
|
|
if (existingEntry !== null) {
|
|
// Don't replace a more specific segment with a less-specific one. A case where this
|
|
// might happen is if the existing segment was fetched via
|
|
// `<Link prefetch={true}>`.
|
|
if (// than the segment we already have in the cache, so it can't have more content.
|
|
candidateEntry.fetchStrategy !== existingEntry.fetchStrategy && !canNewFetchStrategyProvideMoreContent(existingEntry.fetchStrategy, candidateEntry.fetchStrategy) || // The existing entry isn't partial, but the new one is.
|
|
// (TODO: can this be true if `candidateEntry.fetchStrategy >= existingEntry.fetchStrategy`?)
|
|
!existingEntry.isPartial && candidateEntry.isPartial) {
|
|
// We're going to leave revalidating entry in the cache so that it doesn't
|
|
// get revalidated again unnecessarily. Downgrade the Fulfilled entry to
|
|
// Rejected and null out the data so it can be garbage collected. We leave
|
|
// `staleAt` intact to prevent subsequent revalidation attempts only until
|
|
// the entry expires.
|
|
const rejectedEntry = candidateEntry;
|
|
rejectedEntry.status = 3;
|
|
rejectedEntry.rsc = null;
|
|
return null;
|
|
}
|
|
// Evict the existing entry from the cache.
|
|
(0, _cachemap.deleteFromCacheMap)(existingEntry);
|
|
}
|
|
const isRevalidation = false;
|
|
(0, _cachemap.setInCacheMap)(segmentCacheMap, varyPath, candidateEntry, isRevalidation);
|
|
return candidateEntry;
|
|
}
|
|
function createDetachedSegmentCacheEntry(now) {
|
|
// Default stale time for pending segment cache entries. The actual stale time
|
|
// is set when the entry is fulfilled with data from the server response.
|
|
const staleAt = now + 30 * 1000;
|
|
const emptyEntry = {
|
|
status: 0,
|
|
// Default to assuming the fetch strategy will be PPR. This will be updated
|
|
// when a fetch is actually initiated.
|
|
fetchStrategy: _types.FetchStrategy.PPR,
|
|
rsc: null,
|
|
isPartial: true,
|
|
promise: null,
|
|
// Map-related fields
|
|
ref: null,
|
|
size: 0,
|
|
staleAt,
|
|
version: 0
|
|
};
|
|
return emptyEntry;
|
|
}
|
|
function upgradeToPendingSegment(emptyEntry, fetchStrategy) {
|
|
const pendingEntry = emptyEntry;
|
|
pendingEntry.status = 1;
|
|
pendingEntry.fetchStrategy = fetchStrategy;
|
|
if (fetchStrategy === _types.FetchStrategy.Full) {
|
|
// We can assume the response will contain the full segment data. Set this
|
|
// to false so we know it's OK to omit this segment from any navigation
|
|
// requests that may happen while the data is still pending.
|
|
pendingEntry.isPartial = false;
|
|
}
|
|
// Set the version here, since this is right before the request is initiated.
|
|
// The next time the segment cache version is incremented, the entry will
|
|
// effectively be evicted. This happens before initiating the request, rather
|
|
// than when receiving the response, because it's guaranteed to happen
|
|
// before the data is read on the server.
|
|
pendingEntry.version = getCurrentSegmentCacheVersion();
|
|
return pendingEntry;
|
|
}
|
|
function attemptToFulfillDynamicSegmentFromBFCache(now, segment, tree) {
|
|
// Attempts to fulfill an empty segment cache entry using data from the
|
|
// bfcache. This is only valid during a Full prefetch (i.e. one that includes
|
|
// dynamic data), because the bfcache stores data from navigations which
|
|
// always include dynamic data.
|
|
// We always use the canonical vary path when checking the bfcache. This is
|
|
// the same operation we'd use to access the cache during a
|
|
// regular navigation.
|
|
const varyPath = tree.varyPath;
|
|
// Read from the BFCache without expiring it (pass -1). We check freshness
|
|
// ourselves using navigatedAt, because the BFCache's staleAt may have been
|
|
// overridden by a per-page unstable_dynamicStaleTime and can't be used to
|
|
// derive the original request time.
|
|
const bfcacheEntry = (0, _bfcache.readFromBFCache)(varyPath);
|
|
if (bfcacheEntry !== null) {
|
|
// The stale time for dynamic prefetches (default: 5 mins) is different
|
|
// from the stale time for regular navigations (default: 0 secs). Use
|
|
// navigatedAt to compute the correct expiry for prefetch purposes.
|
|
const dynamicPrefetchStaleAt = bfcacheEntry.navigatedAt + _navigatereducer.STATIC_STALETIME_MS;
|
|
if (now > dynamicPrefetchStaleAt) {
|
|
return null;
|
|
}
|
|
const pendingSegment = upgradeToPendingSegment(segment, _types.FetchStrategy.Full);
|
|
const isPartial = false;
|
|
return fulfillSegmentCacheEntry(pendingSegment, bfcacheEntry.rsc, dynamicPrefetchStaleAt, isPartial);
|
|
}
|
|
return null;
|
|
}
|
|
function attemptToUpgradeSegmentFromBFCache(now, tree) {
|
|
const varyPath = tree.varyPath;
|
|
const bfcacheEntry = (0, _bfcache.readFromBFCache)(varyPath);
|
|
if (bfcacheEntry !== null) {
|
|
const dynamicPrefetchStaleAt = bfcacheEntry.navigatedAt + _navigatereducer.STATIC_STALETIME_MS;
|
|
if (now > dynamicPrefetchStaleAt) {
|
|
return null;
|
|
}
|
|
const pendingSegment = upgradeToPendingSegment(createDetachedSegmentCacheEntry(now), _types.FetchStrategy.Full);
|
|
const isPartial = false;
|
|
const newEntry = fulfillSegmentCacheEntry(pendingSegment, bfcacheEntry.rsc, dynamicPrefetchStaleAt, isPartial);
|
|
const segmentVaryPath = (0, _varypath.getSegmentVaryPathForRequest)(_types.FetchStrategy.Full, tree);
|
|
const upserted = upsertSegmentEntry(now, segmentVaryPath, newEntry);
|
|
if (upserted !== null && upserted.status === 2) {
|
|
return upserted;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
function pingBlockedTasks(entry) {
|
|
const blockedTasks = entry.blockedTasks;
|
|
if (blockedTasks !== null) {
|
|
for (const task of blockedTasks){
|
|
(0, _scheduler.pingPrefetchTask)(task);
|
|
}
|
|
entry.blockedTasks = null;
|
|
}
|
|
}
|
|
function createMetadataRouteTree(metadataVaryPath) {
|
|
// The Head is not actually part of the route tree, but other than that, it's
|
|
// fetched and cached like a segment. Some functions expect a RouteTree
|
|
// object, so rather than fork the logic in all those places, we use this
|
|
// "fake" one.
|
|
const metadata = {
|
|
requestKey: _segmentvalueencoding.HEAD_REQUEST_KEY,
|
|
segment: _segmentvalueencoding.HEAD_REQUEST_KEY,
|
|
refreshState: null,
|
|
varyPath: metadataVaryPath,
|
|
// The metadata isn't really a "page" (though it isn't really a "segment"
|
|
// either) but for the purposes of how this field is used, it behaves like
|
|
// one. If this logic ever gets more complex we can change this to an enum.
|
|
isPage: true,
|
|
slots: null,
|
|
prefetchHints: 0
|
|
};
|
|
return metadata;
|
|
}
|
|
function fulfillRouteCacheEntry(now, entry, tree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching) {
|
|
// Get the rendered search from the vary path
|
|
const renderedSearch = (0, _varypath.getRenderedSearchFromVaryPath)(metadataVaryPath) ?? '';
|
|
const fulfilledEntry = entry;
|
|
fulfilledEntry.status = 2;
|
|
fulfilledEntry.tree = tree;
|
|
fulfilledEntry.metadata = createMetadataRouteTree(metadataVaryPath);
|
|
// Route structure is essentially static — it only changes on deploy.
|
|
// Always use the static stale time.
|
|
// NOTE: An exception is rewrites/redirects in middleware or proxy, which can
|
|
// change routes dynamically. We have other strategies for handling those.
|
|
fulfilledEntry.staleAt = now + _navigatereducer.STATIC_STALETIME_MS;
|
|
fulfilledEntry.couldBeIntercepted = couldBeIntercepted;
|
|
fulfilledEntry.canonicalUrl = canonicalUrl;
|
|
fulfilledEntry.renderedSearch = renderedSearch;
|
|
fulfilledEntry.supportsPerSegmentPrefetching = supportsPerSegmentPrefetching;
|
|
fulfilledEntry.hasDynamicRewrite = false;
|
|
pingBlockedTasks(entry);
|
|
return fulfilledEntry;
|
|
}
|
|
function writeRouteIntoCache(now, pathname, nextUrl, tree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching) {
|
|
const pendingEntry = createDetachedRouteCacheEntry();
|
|
const fulfilledEntry = fulfillRouteCacheEntry(now, pendingEntry, tree, metadataVaryPath, couldBeIntercepted, canonicalUrl, supportsPerSegmentPrefetching);
|
|
const renderedSearch = fulfilledEntry.renderedSearch;
|
|
const varyPath = (0, _varypath.getFulfilledRouteVaryPath)(pathname, renderedSearch, nextUrl, couldBeIntercepted);
|
|
const isRevalidation = false;
|
|
(0, _cachemap.setInCacheMap)(routeCacheMap, varyPath, fulfilledEntry, isRevalidation);
|
|
return fulfilledEntry;
|
|
}
|
|
function markRouteEntryAsDynamicRewrite(entry) {
|
|
entry.hasDynamicRewrite = true;
|
|
// Note: The caller is responsible for also calling invalidateRouteCacheEntries
|
|
// to invalidate other entries that may have been derived from this template
|
|
// before we knew it had a dynamic rewrite.
|
|
}
|
|
function fulfillSegmentCacheEntry(segmentCacheEntry, rsc, staleAt, isPartial) {
|
|
const fulfilledEntry = segmentCacheEntry;
|
|
fulfilledEntry.status = 2;
|
|
fulfilledEntry.rsc = rsc;
|
|
fulfilledEntry.staleAt = staleAt;
|
|
fulfilledEntry.isPartial = isPartial;
|
|
// Resolve any listeners that were waiting for this data.
|
|
if (segmentCacheEntry.promise !== null) {
|
|
segmentCacheEntry.promise.resolve(fulfilledEntry);
|
|
// Free the promise for garbage collection.
|
|
fulfilledEntry.promise = null;
|
|
}
|
|
return fulfilledEntry;
|
|
}
|
|
function rejectRouteCacheEntry(entry, staleAt) {
|
|
const rejectedEntry = entry;
|
|
rejectedEntry.status = 3;
|
|
rejectedEntry.staleAt = staleAt;
|
|
pingBlockedTasks(entry);
|
|
}
|
|
function rejectSegmentCacheEntry(entry, staleAt) {
|
|
const rejectedEntry = entry;
|
|
rejectedEntry.status = 3;
|
|
rejectedEntry.staleAt = staleAt;
|
|
if (entry.promise !== null) {
|
|
// NOTE: We don't currently propagate the reason the prefetch was canceled
|
|
// but we could by accepting a `reason` argument.
|
|
entry.promise.resolve(null);
|
|
entry.promise = null;
|
|
}
|
|
}
|
|
function convertRootTreePrefetchToRouteTree(rootTree, renderedPathname, renderedSearch, acc) {
|
|
// Remove trailing and leading slashes
|
|
const pathnameParts = renderedPathname.split('/').filter((p)=>p !== '');
|
|
const index = 0;
|
|
const rootSegment = _segmentvalueencoding.ROOT_SEGMENT_REQUEST_KEY;
|
|
return convertTreePrefetchToRouteTree(rootTree.tree, rootSegment, null, _segmentvalueencoding.ROOT_SEGMENT_REQUEST_KEY, pathnameParts, index, renderedSearch, acc);
|
|
}
|
|
function convertTreePrefetchToRouteTree(prefetch, segment, partialVaryPath, requestKey, pathnameParts, pathnamePartsIndex, renderedSearch, acc) {
|
|
// Converts the route tree sent by the server into the format used by the
|
|
// cache. The cached version of the tree includes additional fields, such as a
|
|
// cache key for each segment. Since this is frequently accessed, we compute
|
|
// it once instead of on every access. This same cache key is also used to
|
|
// request the segment from the server.
|
|
let slots = null;
|
|
let isPage;
|
|
let varyPath;
|
|
const prefetchSlots = prefetch.slots;
|
|
if (prefetchSlots !== null) {
|
|
isPage = false;
|
|
varyPath = (0, _varypath.finalizeLayoutVaryPath)(requestKey, partialVaryPath);
|
|
slots = {};
|
|
for(let parallelRouteKey in prefetchSlots){
|
|
const childPrefetch = prefetchSlots[parallelRouteKey];
|
|
const childSegmentName = childPrefetch.name;
|
|
const childParam = childPrefetch.param;
|
|
let childDoesAppearInURL;
|
|
let childSegment;
|
|
let childPartialVaryPath;
|
|
if (childParam !== null) {
|
|
// This segment is parameterized. Get the param from the pathname.
|
|
const childParamValue = (0, _routeparams.parseDynamicParamFromURLPart)(childParam.type, pathnameParts, pathnamePartsIndex);
|
|
// Assign a cache key to the segment, based on the param value. In the
|
|
// pre-Segment Cache implementation, the server computes this and sends
|
|
// it in the body of the response. In the Segment Cache implementation,
|
|
// the server sends an empty string and we fill it in here.
|
|
// TODO: We're intentionally not adding the search param to page
|
|
// segments here; it's tracked separately and added back during a read.
|
|
// This would clearer if we waited to construct the segment until it's
|
|
// read from the cache, since that's effectively what we're
|
|
// doing anyway.
|
|
const childParamKey = // cacheComponents is enabled.
|
|
childParam.key !== null ? childParam.key : (0, _routeparams.getCacheKeyForDynamicParam)(childParamValue, '');
|
|
childPartialVaryPath = (0, _varypath.appendLayoutVaryPath)(partialVaryPath, childParamKey, childSegmentName);
|
|
childSegment = [
|
|
childSegmentName,
|
|
childParamKey,
|
|
childParam.type,
|
|
childParam.siblings
|
|
];
|
|
childDoesAppearInURL = true;
|
|
} else {
|
|
// This segment does not have a param. Inherit the partial vary path of
|
|
// the parent.
|
|
childPartialVaryPath = partialVaryPath;
|
|
childSegment = childSegmentName;
|
|
childDoesAppearInURL = (0, _routeparams.doesStaticSegmentAppearInURL)(childSegmentName);
|
|
}
|
|
// Only increment the index if the segment appears in the URL. If it's a
|
|
// "virtual" segment, like a route group, it remains the same.
|
|
const childPathnamePartsIndex = childDoesAppearInURL ? pathnamePartsIndex + 1 : pathnamePartsIndex;
|
|
const childRequestKeyPart = (0, _segmentvalueencoding.createSegmentRequestKeyPart)(childSegment);
|
|
const childRequestKey = (0, _segmentvalueencoding.appendSegmentRequestKeyPart)(requestKey, parallelRouteKey, childRequestKeyPart);
|
|
slots[parallelRouteKey] = convertTreePrefetchToRouteTree(childPrefetch, childSegment, childPartialVaryPath, childRequestKey, pathnameParts, childPathnamePartsIndex, renderedSearch, acc);
|
|
}
|
|
} else {
|
|
if (requestKey.endsWith(_segment.PAGE_SEGMENT_KEY)) {
|
|
// This is a page segment.
|
|
isPage = true;
|
|
varyPath = (0, _varypath.finalizePageVaryPath)(requestKey, renderedSearch, partialVaryPath);
|
|
// The metadata "segment" is not part the route tree, but it has the same
|
|
// conceptual params as a page segment. Write the vary path into the
|
|
// accumulator object. If there are multiple parallel pages, we use the
|
|
// first one. Which page we choose is arbitrary as long as it's
|
|
// consistently the same one every time every time. See
|
|
// finalizeMetadataVaryPath for more details.
|
|
if (acc.metadataVaryPath === null) {
|
|
acc.metadataVaryPath = (0, _varypath.finalizeMetadataVaryPath)(requestKey, renderedSearch, partialVaryPath);
|
|
}
|
|
} else {
|
|
// This is a layout segment.
|
|
isPage = false;
|
|
varyPath = (0, _varypath.finalizeLayoutVaryPath)(requestKey, partialVaryPath);
|
|
}
|
|
}
|
|
return {
|
|
requestKey,
|
|
segment,
|
|
refreshState: null,
|
|
// TODO: Cheating the type system here a bit because TypeScript can't tell
|
|
// that the type of isPage and varyPath are consistent. The fix would be to
|
|
// create separate constructors and call the appropriate one from each of
|
|
// the branches above. Just seems a bit overkill only for one field so I'll
|
|
// leave it as-is for now. If isPage were wrong it would break the behavior
|
|
// and we'd catch it quickly, anyway.
|
|
varyPath: varyPath,
|
|
isPage: isPage,
|
|
slots,
|
|
prefetchHints: prefetch.prefetchHints
|
|
};
|
|
}
|
|
function convertRootFlightRouterStateToRouteTree(flightRouterState, renderedSearch, acc) {
|
|
return convertFlightRouterStateToRouteTree(flightRouterState, _segmentvalueencoding.ROOT_SEGMENT_REQUEST_KEY, null, renderedSearch, acc);
|
|
}
|
|
function convertReusedFlightRouterStateToRouteTree(parentRouteTree, parallelRouteKey, flightRouterState, renderedSearch, acc) {
|
|
// Create a RouteTree for a FlightRouterState that was reused from an older
|
|
// route. This happens during a navigation when a parallel route slot does not
|
|
// match the target route; we reuse whatever slot was already active.
|
|
// Unlike a FlightRouterState, the RouteTree type contains backreferences to
|
|
// the parent segments. Append the vary path to the parent's vary path.
|
|
const parentPartialVaryPath = parentRouteTree.isPage ? (0, _varypath.getPartialPageVaryPath)(parentRouteTree.varyPath) : (0, _varypath.getPartialLayoutVaryPath)(parentRouteTree.varyPath);
|
|
const segment = flightRouterState[0];
|
|
// And the request key.
|
|
const parentRequestKey = parentRouteTree.requestKey;
|
|
const requestKeyPart = (0, _segmentvalueencoding.createSegmentRequestKeyPart)(segment);
|
|
const requestKey = (0, _segmentvalueencoding.appendSegmentRequestKeyPart)(parentRequestKey, parallelRouteKey, requestKeyPart);
|
|
return convertFlightRouterStateToRouteTree(flightRouterState, requestKey, parentPartialVaryPath, renderedSearch, acc);
|
|
}
|
|
function convertFlightRouterStateToRouteTree(flightRouterState, requestKey, parentPartialVaryPath, parentRenderedSearch, acc) {
|
|
const originalSegment = flightRouterState[0];
|
|
// If the FlightRouterState has a refresh state, then this segment is part of
|
|
// an inactive parallel route. It has a different rendered search query than
|
|
// the outer parent route. In order to construct the inactive route correctly,
|
|
// we must restore the query that was originally used to render it.
|
|
const compressedRefreshState = flightRouterState[2] ?? null;
|
|
const refreshState = compressedRefreshState !== null ? {
|
|
canonicalUrl: compressedRefreshState[0],
|
|
renderedSearch: compressedRefreshState[1]
|
|
} : null;
|
|
const renderedSearch = refreshState !== null ? refreshState.renderedSearch : parentRenderedSearch;
|
|
let segment;
|
|
let partialVaryPath;
|
|
let isPage;
|
|
let varyPath;
|
|
if (Array.isArray(originalSegment)) {
|
|
isPage = false;
|
|
const paramCacheKey = originalSegment[1];
|
|
const paramName = originalSegment[0];
|
|
partialVaryPath = (0, _varypath.appendLayoutVaryPath)(parentPartialVaryPath, paramCacheKey, paramName);
|
|
varyPath = (0, _varypath.finalizeLayoutVaryPath)(requestKey, partialVaryPath);
|
|
segment = originalSegment;
|
|
} else {
|
|
// This segment does not have a param. Inherit the partial vary path of
|
|
// the parent.
|
|
partialVaryPath = parentPartialVaryPath;
|
|
if (requestKey.endsWith(_segment.PAGE_SEGMENT_KEY)) {
|
|
// This is a page segment.
|
|
isPage = true;
|
|
// The navigation implementation expects the search params to be included
|
|
// in the segment. However, in the case of a static response, the search
|
|
// params are omitted. So the client needs to add them back in when reading
|
|
// from the Segment Cache.
|
|
//
|
|
// For consistency, we'll do this for dynamic responses, too.
|
|
//
|
|
// TODO: We should move search params out of FlightRouterState and handle
|
|
// them entirely on the client, similar to our plan for dynamic params.
|
|
segment = _segment.PAGE_SEGMENT_KEY;
|
|
varyPath = (0, _varypath.finalizePageVaryPath)(requestKey, renderedSearch, partialVaryPath);
|
|
// The metadata "segment" is not part the route tree, but it has the same
|
|
// conceptual params as a page segment. Write the vary path into the
|
|
// accumulator object. If there are multiple parallel pages, we use the
|
|
// first one. Which page we choose is arbitrary as long as it's
|
|
// consistently the same one every time every time. See
|
|
// finalizeMetadataVaryPath for more details.
|
|
if (acc.metadataVaryPath === null) {
|
|
acc.metadataVaryPath = (0, _varypath.finalizeMetadataVaryPath)(requestKey, renderedSearch, partialVaryPath);
|
|
}
|
|
} else {
|
|
// This is a layout segment.
|
|
isPage = false;
|
|
segment = originalSegment;
|
|
varyPath = (0, _varypath.finalizeLayoutVaryPath)(requestKey, partialVaryPath);
|
|
}
|
|
}
|
|
let slots = null;
|
|
const parallelRoutes = flightRouterState[1];
|
|
for(let parallelRouteKey in parallelRoutes){
|
|
const childRouterState = parallelRoutes[parallelRouteKey];
|
|
const childSegment = childRouterState[0];
|
|
// TODO: Eventually, the param values will not be included in the response
|
|
// from the server. We'll instead fill them in on the client by parsing
|
|
// the URL. This is where we'll do that.
|
|
const childRequestKeyPart = (0, _segmentvalueencoding.createSegmentRequestKeyPart)(childSegment);
|
|
const childRequestKey = (0, _segmentvalueencoding.appendSegmentRequestKeyPart)(requestKey, parallelRouteKey, childRequestKeyPart);
|
|
const childTree = convertFlightRouterStateToRouteTree(childRouterState, childRequestKey, partialVaryPath, renderedSearch, acc);
|
|
if (slots === null) {
|
|
slots = {
|
|
[parallelRouteKey]: childTree
|
|
};
|
|
} else {
|
|
slots[parallelRouteKey] = childTree;
|
|
}
|
|
}
|
|
return {
|
|
requestKey,
|
|
segment,
|
|
refreshState,
|
|
// TODO: Cheating the type system here a bit because TypeScript can't tell
|
|
// that the type of isPage and varyPath are consistent. The fix would be to
|
|
// create separate constructors and call the appropriate one from each of
|
|
// the branches above. Just seems a bit overkill only for one field so I'll
|
|
// leave it as-is for now. If isPage were wrong it would break the behavior
|
|
// and we'd catch it quickly, anyway.
|
|
varyPath: varyPath,
|
|
isPage: isPage,
|
|
slots,
|
|
prefetchHints: flightRouterState[4] ?? 0
|
|
};
|
|
}
|
|
function convertRouteTreeToFlightRouterState(routeTree) {
|
|
const parallelRoutes = {};
|
|
if (routeTree.slots !== null) {
|
|
for(const parallelRouteKey in routeTree.slots){
|
|
parallelRoutes[parallelRouteKey] = convertRouteTreeToFlightRouterState(routeTree.slots[parallelRouteKey]);
|
|
}
|
|
}
|
|
const flightRouterState = [
|
|
routeTree.segment,
|
|
parallelRoutes,
|
|
null,
|
|
null
|
|
];
|
|
return flightRouterState;
|
|
}
|
|
async function fetchRouteOnCacheMiss(entry, key) {
|
|
// This function is allowed to use async/await because it contains the actual
|
|
// fetch that gets issued on a cache miss. Notice it writes the result to the
|
|
// cache entry directly, rather than return data that is then written by
|
|
// the caller.
|
|
const pathname = key.pathname;
|
|
const search = key.search;
|
|
const nextUrl = key.nextUrl;
|
|
const segmentPath = '/_tree';
|
|
const headers = {
|
|
[_approuterheaders.RSC_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER]: segmentPath
|
|
};
|
|
if (nextUrl !== null) {
|
|
headers[_approuterheaders.NEXT_URL] = nextUrl;
|
|
}
|
|
// Tell the server to perform a static pre-render for the Instant Navigation
|
|
// Testing API. Static pre-renders don't normally happen during development.
|
|
addInstantPrefetchHeaderIfLocked(headers);
|
|
try {
|
|
const url = new URL(pathname + search, location.origin);
|
|
let response;
|
|
let urlAfterRedirects;
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
else {
|
|
// "Server" mode. We can use request headers instead of the pathname.
|
|
// TODO: The eventual plan is to get rid of our custom request headers and
|
|
// encode everything into the URL, using a similar strategy to the
|
|
// "output: export" block above.
|
|
response = await fetchPrefetchResponse(url, headers);
|
|
urlAfterRedirects = response !== null && response.redirected ? new URL(response.url) : url;
|
|
}
|
|
if (!response || !response.ok || // 204 is a Cache miss. Though theoretically this shouldn't happen when
|
|
// PPR is enabled, because we always respond to route tree requests, even
|
|
// if it needs to be blockingly generated on demand.
|
|
response.status === 204 || !response.body) {
|
|
// Server responded with an error, or with a miss. We should still cache
|
|
// the response, but we can try again after 10 seconds.
|
|
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
// TODO: The canonical URL is the href without the origin. I think
|
|
// historically the reason for this is because the initial canonical URL
|
|
// gets passed as a prop to the top-level React component, which means it
|
|
// needs to be computed during SSR. If it were to include the origin, it
|
|
// would need to always be same as location.origin on the client, to prevent
|
|
// a hydration mismatch. To sidestep this complexity, we omit the origin.
|
|
//
|
|
// However, since this is neither a native URL object nor a fully qualified
|
|
// URL string, we need to be careful about how we use it. To prevent subtle
|
|
// mistakes, we should create a special type for it, instead of just string.
|
|
// Or, we should just use a (readonly) URL object instead. The type of the
|
|
// prop that we pass to seed the initial state does not need to be the same
|
|
// type as the state itself.
|
|
const canonicalUrl = (0, _createhreffromurl.createHrefFromUrl)(urlAfterRedirects);
|
|
// Check whether the response varies based on the Next-Url header.
|
|
const varyHeader = response.headers.get('vary');
|
|
const couldBeIntercepted = varyHeader !== null && varyHeader.includes(_approuterheaders.NEXT_URL);
|
|
// TODO: The `closed` promise was originally used to track when a streaming
|
|
// network connection closes, so the scheduler could limit concurrent
|
|
// connections. Now that prefetch responses are buffered, `closed` is
|
|
// resolved immediately after buffering — before the outer function even
|
|
// returns. This mechanism is only still meaningful for dynamic (Full)
|
|
// prefetches, which use incremental streaming. Consider removing the
|
|
// `closed` plumbing for buffered prefetch paths.
|
|
const closed = (0, _promisewithresolvers.createPromiseWithResolvers)();
|
|
// This checks whether the response was served from the per-segment cache,
|
|
// rather than the old prefetching flow. If it fails, it implies that PPR
|
|
// is disabled on this route.
|
|
const routeIsPPREnabled = response.headers.get(_approuterheaders.NEXT_DID_POSTPONE_HEADER) === '2' || // In output: "export" mode, we can't rely on response headers. But if we
|
|
// receive a well-formed response, we can assume it's a static response,
|
|
// because all data is static in this mode.
|
|
isOutputExportMode;
|
|
if (routeIsPPREnabled) {
|
|
const { stream: prefetchStream, size: responseSize } = await createNonTaskyPrefetchResponseStream(response.body);
|
|
closed.resolve();
|
|
(0, _cachemap.setSizeInCacheMap)(entry, responseSize);
|
|
const serverData = await (0, _fetchserverresponse.createFromNextReadableStream)(prefetchStream, headers, {
|
|
allowPartialStream: true
|
|
});
|
|
if ((response.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.buildId) !== (0, _navigationbuildid.getNavigationBuildId)()) {
|
|
// The server build does not match the client. Treat as a 404. During
|
|
// an actual navigation, the router will trigger an MPA navigation.
|
|
// TODO: We should cache the fact that this is an MPA navigation.
|
|
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
// Get the params that were used to render the target page. These may
|
|
// be different from the params in the request URL, if the page
|
|
// was rewritten.
|
|
const renderedPathname = (0, _routeparams.getRenderedPathname)(response);
|
|
const renderedSearch = (0, _routeparams.getRenderedSearch)(response);
|
|
// Convert the server-sent data into the RouteTree format used by the
|
|
// client cache.
|
|
//
|
|
// During this traversal, we accumulate additional data into this
|
|
// "accumulator" object.
|
|
const acc = {
|
|
metadataVaryPath: null
|
|
};
|
|
const routeTree = convertRootTreePrefetchToRouteTree(serverData, renderedPathname, renderedSearch, acc);
|
|
const metadataVaryPath = acc.metadataVaryPath;
|
|
if (metadataVaryPath === null) {
|
|
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
(0, _optimisticroutes.discoverKnownRoute)(Date.now(), pathname, nextUrl, entry, routeTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, routeIsPPREnabled, false // hasDynamicRewrite
|
|
);
|
|
} else {
|
|
// PPR is not enabled for this route. The server responds with a
|
|
// different format (FlightRouterState) that we need to convert.
|
|
// TODO: We will unify the responses eventually. I'm keeping the types
|
|
// separate for now because FlightRouterState has so many
|
|
// overloaded concerns.
|
|
const { stream: prefetchStream, size: responseSize } = await createNonTaskyPrefetchResponseStream(response.body);
|
|
closed.resolve();
|
|
(0, _cachemap.setSizeInCacheMap)(entry, responseSize);
|
|
const serverData = await (0, _fetchserverresponse.createFromNextReadableStream)(prefetchStream, headers, {
|
|
allowPartialStream: true
|
|
});
|
|
if ((response.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.b) !== (0, _navigationbuildid.getNavigationBuildId)()) {
|
|
// The server build does not match the client. Treat as a 404. During
|
|
// an actual navigation, the router will trigger an MPA navigation.
|
|
// TODO: We should cache the fact that this is an MPA navigation.
|
|
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
// Read head vary params synchronously. Individual segments carry their
|
|
// own thenables in CacheNodeSeedData.
|
|
const headVaryParamsThenable = serverData.h;
|
|
const headVaryParams = headVaryParamsThenable !== null ? (0, _varyparamsdecoding.readVaryParams)(headVaryParamsThenable) : null;
|
|
writeDynamicTreeResponseIntoCache(Date.now(), // using the LoadingBoundary fetch strategy, so mark their cache entries accordingly.
|
|
_types.FetchStrategy.LoadingBoundary, response, serverData, entry, couldBeIntercepted, canonicalUrl, routeIsPPREnabled, headVaryParams, pathname, nextUrl);
|
|
}
|
|
if (!couldBeIntercepted) {
|
|
// This route will never be intercepted. So we can use this entry for all
|
|
// requests to this route, regardless of the Next-Url header. This works
|
|
// because when reading the cache we always check for a valid
|
|
// non-intercepted entry first.
|
|
// Re-key the entry. The `set` implementation handles removing it from
|
|
// its previous position in the cache. We don't need to do anything to
|
|
// update the LRU, because the entry is already in it.
|
|
// TODO: Treat this as an upsert — should check if an entry already
|
|
// exists at the new keypath, and if so, whether we should keep that
|
|
// one instead.
|
|
const fulfilledVaryPath = (0, _varypath.getFulfilledRouteVaryPath)(pathname, search, nextUrl, couldBeIntercepted);
|
|
const isRevalidation = false;
|
|
(0, _cachemap.setInCacheMap)(routeCacheMap, fulfilledVaryPath, entry, isRevalidation);
|
|
}
|
|
// Return a promise that resolves when the network connection closes, so
|
|
// the scheduler can track the number of concurrent network connections.
|
|
return {
|
|
value: null,
|
|
closed: closed.promise
|
|
};
|
|
} catch (error) {
|
|
// Either the connection itself failed, or something bad happened while
|
|
// decoding the response.
|
|
rejectRouteCacheEntry(entry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
}
|
|
async function fetchSegmentOnCacheMiss(route, segmentCacheEntry, routeKey, tree) {
|
|
// This function is allowed to use async/await because it contains the actual
|
|
// fetch that gets issued on a cache miss. Notice it writes the result to the
|
|
// cache entry directly, rather than return data that is then written by
|
|
// the caller.
|
|
//
|
|
// Segment fetches are non-blocking so we don't need to ping the scheduler
|
|
// on completion.
|
|
// Use the canonical URL to request the segment, not the original URL. These
|
|
// are usually the same, but the canonical URL will be different if the route
|
|
// tree response was redirected. To avoid an extra waterfall on every segment
|
|
// request, we pass the redirected URL instead of the original one.
|
|
const url = new URL(route.canonicalUrl, location.origin);
|
|
const nextUrl = routeKey.nextUrl;
|
|
const requestKey = tree.requestKey;
|
|
const normalizedRequestKey = requestKey === _segmentvalueencoding.ROOT_SEGMENT_REQUEST_KEY ? // `_index` instead of as an empty string. This should be treated as
|
|
// an implementation detail and not as a stable part of the protocol.
|
|
// It just needs to match the equivalent logic that happens when
|
|
// prerendering the responses. It should not leak outside of Next.js.
|
|
'/_index' : requestKey;
|
|
const headers = {
|
|
[_approuterheaders.RSC_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER]: normalizedRequestKey
|
|
};
|
|
if (nextUrl !== null) {
|
|
headers[_approuterheaders.NEXT_URL] = nextUrl;
|
|
}
|
|
// Tell the server to perform a static pre-render for the Instant Navigation
|
|
// Testing API. Static pre-renders don't normally happen during development.
|
|
addInstantPrefetchHeaderIfLocked(headers);
|
|
const requestUrl = ("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : url;
|
|
try {
|
|
const response = await fetchPrefetchResponse(requestUrl, headers);
|
|
if (!response || !response.ok || response.status === 204 || // Cache miss
|
|
// This checks whether the response was served from the per-segment cache,
|
|
// rather than the old prefetching flow. If it fails, it implies that PPR
|
|
// is disabled on this route. Theoretically this should never happen
|
|
// because we only issue requests for segments once we've verified that
|
|
// the route supports PPR.
|
|
response.headers.get(_approuterheaders.NEXT_DID_POSTPONE_HEADER) !== '2' && // In output: "export" mode, we can't rely on response headers. But if
|
|
// we receive a well-formed response, we can assume it's a static
|
|
// response, because all data is static in this mode.
|
|
!isOutputExportMode || !response.body) {
|
|
// Server responded with an error, or with a miss. We should still cache
|
|
// the response, but we can try again after 10 seconds.
|
|
rejectSegmentCacheEntry(segmentCacheEntry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
// See TODO in fetchRouteOnCacheMiss about removing `closed` for
|
|
// buffered prefetch paths.
|
|
const closed = (0, _promisewithresolvers.createPromiseWithResolvers)();
|
|
const { stream: prefetchStream, size: responseSize } = await createNonTaskyPrefetchResponseStream(response.body);
|
|
closed.resolve();
|
|
(0, _cachemap.setSizeInCacheMap)(segmentCacheEntry, responseSize);
|
|
const serverData = await (0, _fetchserverresponse.createFromNextReadableStream)(prefetchStream, headers, {
|
|
allowPartialStream: true
|
|
});
|
|
if ((response.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.buildId) !== (0, _navigationbuildid.getNavigationBuildId)()) {
|
|
// The server build does not match the client. Treat as a 404. During
|
|
// an actual navigation, the router will trigger an MPA navigation.
|
|
rejectSegmentCacheEntry(segmentCacheEntry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
const now = Date.now();
|
|
const staleAt = now + getStaleTimeMs(serverData.staleTime);
|
|
const fulfilledEntry = fulfillSegmentCacheEntry(segmentCacheEntry, serverData.rsc, staleAt, serverData.isPartial);
|
|
// If the server tells us which params the segment varies by, we can re-key
|
|
// the entry to a more generic vary path. This allows the entry to be reused
|
|
// across different param values for params that the segment doesn't
|
|
// actually depend on.
|
|
const varyParams = serverData.varyParams;
|
|
const fulfilledVaryPath = ("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : (0, _varypath.getSegmentVaryPathForRequest)(segmentCacheEntry.fetchStrategy, tree);
|
|
// Re-key and upsert the entry at the fulfilled vary path. This ensures
|
|
// the entry is stored at the most generic path possible based on which
|
|
// params the segment actually depends on.
|
|
upsertSegmentEntry(now, fulfilledVaryPath, fulfilledEntry);
|
|
return {
|
|
value: fulfilledEntry,
|
|
// Return a promise that resolves when the network connection closes, so
|
|
// the scheduler can track the number of concurrent network connections.
|
|
closed: closed.promise
|
|
};
|
|
} catch (error) {
|
|
// Either the connection itself failed, or something bad happened while
|
|
// decoding the response.
|
|
rejectSegmentCacheEntry(segmentCacheEntry, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
}
|
|
async function fetchInlinedSegmentsOnCacheMiss(route, routeKey, tree, spawnedEntries) {
|
|
// When prefetch inlining is enabled, all segment data for a route is bundled
|
|
// into a single /_inlined response instead of individual per-segment
|
|
// requests. This function fetches that response and walks the tree to fill
|
|
// all segment cache entries at once.
|
|
const url = new URL(route.canonicalUrl, location.origin);
|
|
const nextUrl = routeKey.nextUrl;
|
|
const headers = {
|
|
[_approuterheaders.RSC_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_SEGMENT_PREFETCH_HEADER]: '/' + _segment.PAGE_SEGMENT_KEY
|
|
};
|
|
if (nextUrl !== null) {
|
|
headers[_approuterheaders.NEXT_URL] = nextUrl;
|
|
}
|
|
addInstantPrefetchHeaderIfLocked(headers);
|
|
try {
|
|
const response = await fetchPrefetchResponse(url, headers);
|
|
if (!response || !response.ok || response.status === 204 || response.headers.get(_approuterheaders.NEXT_DID_POSTPONE_HEADER) !== '2' && !isOutputExportMode || !response.body) {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
// See TODO in fetchRouteOnCacheMiss about removing `closed` for
|
|
// buffered prefetch paths.
|
|
const closed = (0, _promisewithresolvers.createPromiseWithResolvers)();
|
|
const { stream: prefetchStream } = await createNonTaskyPrefetchResponseStream(response.body);
|
|
closed.resolve();
|
|
const serverData = await (0, _fetchserverresponse.createFromNextReadableStream)(prefetchStream, headers, {
|
|
allowPartialStream: true
|
|
});
|
|
if ((response.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.tree.segment.buildId) !== (0, _navigationbuildid.getNavigationBuildId)()) {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
const now = Date.now();
|
|
// Walk the inlined tree in parallel with the RouteTree and fill
|
|
// segment cache entries.
|
|
fillInlinedSegmentEntries(now, route, tree, serverData.tree, spawnedEntries);
|
|
// Fill the head entry.
|
|
const headStaleAt = now + getStaleTimeMs(serverData.head.staleTime);
|
|
const headKey = route.metadata.requestKey;
|
|
const ownedHeadEntry = spawnedEntries.get(headKey);
|
|
if (ownedHeadEntry !== undefined) {
|
|
fulfillSegmentCacheEntry(ownedHeadEntry, serverData.head.rsc, headStaleAt, serverData.head.isPartial);
|
|
} else {
|
|
// The head was already cached. Try to upsert if the entry is empty.
|
|
const existingEntry = readOrCreateSegmentCacheEntry(now, _types.FetchStrategy.PPR, route.metadata);
|
|
if (existingEntry.status === 0) {
|
|
fulfillSegmentCacheEntry(upgradeToPendingSegment(existingEntry, _types.FetchStrategy.PPR), serverData.head.rsc, headStaleAt, serverData.head.isPartial);
|
|
}
|
|
}
|
|
// Reject any remaining entries that were not fulfilled by the response.
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return {
|
|
value: null,
|
|
closed: closed.promise
|
|
};
|
|
} catch (error) {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
}
|
|
function fillInlinedSegmentEntries(now, route, tree, inlinedNode, spawnedEntries) {
|
|
// Check if the spawned entries map has an entry for this segment's key.
|
|
const segment = inlinedNode.segment;
|
|
const staleAt = now + getStaleTimeMs(segment.staleTime);
|
|
const ownedEntry = spawnedEntries.get(tree.requestKey);
|
|
if (ownedEntry !== undefined) {
|
|
// We own this entry. Fulfill it directly.
|
|
fulfillSegmentCacheEntry(ownedEntry, segment.rsc, staleAt, segment.isPartial);
|
|
} else {
|
|
// Not owned by us — this is extra data from the inlined response for a
|
|
// segment that was already cached. Try to upsert if the entry is empty.
|
|
const existingEntry = readOrCreateSegmentCacheEntry(now, _types.FetchStrategy.PPR, tree);
|
|
if (existingEntry.status === 0) {
|
|
fulfillSegmentCacheEntry(upgradeToPendingSegment(existingEntry, _types.FetchStrategy.PPR), segment.rsc, staleAt, segment.isPartial);
|
|
}
|
|
}
|
|
// Recurse into children.
|
|
if (tree.slots !== null && inlinedNode.slots !== null) {
|
|
for(const parallelRouteKey in tree.slots){
|
|
const childTree = tree.slots[parallelRouteKey];
|
|
const childInlinedNode = inlinedNode.slots[parallelRouteKey];
|
|
if (childInlinedNode !== undefined) {
|
|
fillInlinedSegmentEntries(now, route, childTree, childInlinedNode, spawnedEntries);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
async function fetchSegmentPrefetchesUsingDynamicRequest(task, route, fetchStrategy, dynamicRequestTree, spawnedEntries) {
|
|
const key = task.key;
|
|
const url = new URL(route.canonicalUrl, location.origin);
|
|
const nextUrl = key.nextUrl;
|
|
if (spawnedEntries.size === 1 && spawnedEntries.has(route.metadata.requestKey)) {
|
|
// The only thing pending is the head. Instruct the server to
|
|
// skip over everything else.
|
|
dynamicRequestTree = MetadataOnlyRequestTree;
|
|
}
|
|
const headers = {
|
|
[_approuterheaders.RSC_HEADER]: '1',
|
|
[_approuterheaders.NEXT_ROUTER_STATE_TREE_HEADER]: (0, _flightdatahelpers.prepareFlightRouterStateForRequest)(dynamicRequestTree)
|
|
};
|
|
if (nextUrl !== null) {
|
|
headers[_approuterheaders.NEXT_URL] = nextUrl;
|
|
}
|
|
switch(fetchStrategy){
|
|
case _types.FetchStrategy.Full:
|
|
{
|
|
break;
|
|
}
|
|
case _types.FetchStrategy.PPRRuntime:
|
|
{
|
|
headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER] = '2';
|
|
break;
|
|
}
|
|
case _types.FetchStrategy.LoadingBoundary:
|
|
{
|
|
headers[_approuterheaders.NEXT_ROUTER_PREFETCH_HEADER] = '1';
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
fetchStrategy;
|
|
}
|
|
}
|
|
try {
|
|
const response = await fetchPrefetchResponse(url, headers);
|
|
if (!response || !response.ok || !response.body) {
|
|
// Server responded with an error, or with a miss. We should still cache
|
|
// the response, but we can try again after 10 seconds.
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
const renderedSearch = (0, _routeparams.getRenderedSearch)(response);
|
|
if (renderedSearch !== route.renderedSearch) {
|
|
// The search params that were used to render the target page are
|
|
// different from the search params in the request URL. This only happens
|
|
// when there's a dynamic rewrite in between the tree prefetch and the
|
|
// data prefetch.
|
|
// TODO: For now, since this is an edge case, we reject the prefetch, but
|
|
// the proper way to handle this is to evict the stale route tree entry
|
|
// then fill the cache with the new response.
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
// Track when the network connection closes. Only meaningful for Full
|
|
// (dynamic) prefetches which use incremental streaming. For buffered
|
|
// paths, this is resolved immediately — see TODO in fetchRouteOnCacheMiss.
|
|
const closed = (0, _promisewithresolvers.createPromiseWithResolvers)();
|
|
let fulfilledEntries = null;
|
|
let prefetchStream;
|
|
let bufferedResponseSize = null;
|
|
if (fetchStrategy === _types.FetchStrategy.Full) {
|
|
// Full prefetches are dynamic responses stored in the prefetch cache.
|
|
// They don't carry vary params or other cache metadata, so there's no
|
|
// need to buffer them. Use the incremental version to allow data to be
|
|
// processed as it arrives.
|
|
prefetchStream = createIncrementalPrefetchResponseStream(response.body, closed.resolve, function onResponseSizeUpdate(totalBytesReceivedSoFar) {
|
|
// When processing a dynamic response, we don't know how large each
|
|
// individual segment is, so approximate by assigning each segment
|
|
// the average of the total response size.
|
|
if (fulfilledEntries === null) {
|
|
// Haven't received enough data yet to know which segments
|
|
// were included.
|
|
return;
|
|
}
|
|
const averageSize = totalBytesReceivedSoFar / fulfilledEntries.length;
|
|
for (const entry of fulfilledEntries){
|
|
(0, _cachemap.setSizeInCacheMap)(entry, averageSize);
|
|
}
|
|
});
|
|
} else {
|
|
const { stream, size } = await createNonTaskyPrefetchResponseStream(response.body);
|
|
closed.resolve();
|
|
prefetchStream = stream;
|
|
bufferedResponseSize = size;
|
|
}
|
|
const [serverData, cacheData] = await Promise.all([
|
|
(0, _fetchserverresponse.createFromNextReadableStream)(prefetchStream, headers, {
|
|
allowPartialStream: true
|
|
}),
|
|
response.cacheData
|
|
]);
|
|
// Read head vary params synchronously. Individual segments carry their
|
|
// own thenables in CacheNodeSeedData.
|
|
const headVaryParamsThenable = serverData.h;
|
|
const headVaryParams = headVaryParamsThenable !== null ? (0, _varyparamsdecoding.readVaryParams)(headVaryParamsThenable) : null;
|
|
const now = Date.now();
|
|
const staleAt = await getStaleAt(now, serverData.s, response);
|
|
// PPRRuntime prefetches are partial when the server marks the response
|
|
// as '~' (Partial). Full/LoadingBoundary prefetches are always complete.
|
|
const isResponsePartial = fetchStrategy === _types.FetchStrategy.PPRRuntime && (cacheData?.isResponsePartial ?? false);
|
|
// Aside from writing the data into the cache, this function also returns
|
|
// the entries that were fulfilled, so we can streamingly update their sizes
|
|
// in the LRU as more data comes in.
|
|
const buildId = response.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.b;
|
|
const flightDatas = (0, _flightdatahelpers.normalizeFlightData)(serverData.f);
|
|
if (typeof flightDatas === 'string') {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
const navigationSeed = (0, _navigation.convertServerPatchToFullTree)(now, dynamicRequestTree, flightDatas, renderedSearch, _bfcache.UnknownDynamicStaleTime);
|
|
fulfilledEntries = writeDynamicRenderResponseIntoCache(now, fetchStrategy, flightDatas, buildId, isResponsePartial, headVaryParams, staleAt, navigationSeed, spawnedEntries);
|
|
// For buffered responses, update LRU sizes now that we know which
|
|
// entries were fulfilled.
|
|
if (bufferedResponseSize !== null && fulfilledEntries !== null && fulfilledEntries.length > 0) {
|
|
const averageSize = bufferedResponseSize / fulfilledEntries.length;
|
|
for (const entry of fulfilledEntries){
|
|
(0, _cachemap.setSizeInCacheMap)(entry, averageSize);
|
|
}
|
|
}
|
|
// Return a promise that resolves when the network connection closes, so
|
|
// the scheduler can track the number of concurrent network connections.
|
|
return {
|
|
value: null,
|
|
closed: closed.promise
|
|
};
|
|
} catch (error) {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, Date.now() + 10 * 1000);
|
|
return null;
|
|
}
|
|
}
|
|
function writeDynamicTreeResponseIntoCache(now, fetchStrategy, response, serverData, entry, couldBeIntercepted, canonicalUrl, routeIsPPREnabled, headVaryParams, originalPathname, nextUrl) {
|
|
const renderedSearch = (0, _routeparams.getRenderedSearch)(response);
|
|
const normalizedFlightDataResult = (0, _flightdatahelpers.normalizeFlightData)(serverData.f);
|
|
if (// MPA navigation.
|
|
typeof normalizedFlightDataResult === 'string' || normalizedFlightDataResult.length !== 1) {
|
|
rejectRouteCacheEntry(entry, now + 10 * 1000);
|
|
return;
|
|
}
|
|
const flightData = normalizedFlightDataResult[0];
|
|
if (!flightData.isRootRender) {
|
|
// Unexpected response format.
|
|
rejectRouteCacheEntry(entry, now + 10 * 1000);
|
|
return;
|
|
}
|
|
const flightRouterState = flightData.tree;
|
|
// If the response was postponed, segments may contain dynamic holes.
|
|
// The head has its own partiality flag (flightDataEntry.isHeadPartial)
|
|
// which is handled separately in writeDynamicRenderResponseIntoCache.
|
|
const isResponsePartial = response.headers.get(_approuterheaders.NEXT_DID_POSTPONE_HEADER) === '1';
|
|
// Convert the server-sent data into the RouteTree format used by the
|
|
// client cache.
|
|
//
|
|
// During this traversal, we accumulate additional data into this
|
|
// "accumulator" object.
|
|
const acc = {
|
|
metadataVaryPath: null
|
|
};
|
|
const routeTree = convertRootFlightRouterStateToRouteTree(flightRouterState, renderedSearch, acc);
|
|
const metadataVaryPath = acc.metadataVaryPath;
|
|
if (metadataVaryPath === null) {
|
|
rejectRouteCacheEntry(entry, now + 10 * 1000);
|
|
return;
|
|
}
|
|
(0, _optimisticroutes.discoverKnownRoute)(now, originalPathname, nextUrl, entry, routeTree, metadataVaryPath, couldBeIntercepted, canonicalUrl, routeIsPPREnabled, false // hasDynamicRewrite
|
|
);
|
|
// If the server sent segment data as part of the response, we should write
|
|
// it into the cache to prevent a second, redundant prefetch request.
|
|
// TODO: This is a leftover branch from before Client Segment Cache was
|
|
// enabled everywhere. Tree prefetches should never include segment data. We
|
|
// can delete it. Leaving for a subsequent PR.
|
|
const navigationSeed = (0, _navigation.convertServerPatchToFullTree)(now, flightRouterState, normalizedFlightDataResult, renderedSearch, _bfcache.UnknownDynamicStaleTime);
|
|
const buildId = response.headers.get(_constants.NEXT_NAV_DEPLOYMENT_ID_HEADER) ?? serverData.b;
|
|
writeDynamicRenderResponseIntoCache(now, fetchStrategy, normalizedFlightDataResult, buildId, isResponsePartial, headVaryParams, getStaleAtFromHeader(now, response), navigationSeed, null);
|
|
}
|
|
function rejectSegmentEntriesIfStillPending(entries, staleAt) {
|
|
const fulfilledEntries = [];
|
|
for (const entry of entries.values()){
|
|
if (entry.status === 1) {
|
|
rejectSegmentCacheEntry(entry, staleAt);
|
|
} else if (entry.status === 2) {
|
|
fulfilledEntries.push(entry);
|
|
}
|
|
}
|
|
return fulfilledEntries;
|
|
}
|
|
function writeDynamicRenderResponseIntoCache(now, fetchStrategy, flightDatas, buildId, isResponsePartial, headVaryParams, staleAt, navigationSeed, spawnedEntries) {
|
|
if (buildId && buildId !== (0, _navigationbuildid.getNavigationBuildId)()) {
|
|
// The server build does not match the client. Treat as a 404. During
|
|
// an actual navigation, the router will trigger an MPA navigation.
|
|
if (spawnedEntries !== null) {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, now + 10 * 1000);
|
|
}
|
|
return null;
|
|
}
|
|
const routeTree = navigationSeed.routeTree;
|
|
const metadataTree = navigationSeed.metadataVaryPath !== null ? createMetadataRouteTree(navigationSeed.metadataVaryPath) : null;
|
|
for (const flightDataEntry of flightDatas){
|
|
const seedData = flightDataEntry.seedData;
|
|
if (seedData !== null) {
|
|
// The data sent by the server represents only a subtree of the app. We
|
|
// need to find the part of the task tree that matches the response.
|
|
//
|
|
// segmentPath represents the parent path of subtree. It's a repeating
|
|
// pattern of parallel route key and segment:
|
|
//
|
|
// [string, Segment, string, Segment, string, Segment, ...]
|
|
const segmentPath = flightDataEntry.segmentPath;
|
|
let tree = routeTree;
|
|
for(let i = 0; i < segmentPath.length; i += 2){
|
|
const parallelRouteKey = segmentPath[i];
|
|
if (tree?.slots?.[parallelRouteKey] !== undefined) {
|
|
tree = tree.slots[parallelRouteKey];
|
|
} else {
|
|
if (spawnedEntries !== null) {
|
|
rejectSegmentEntriesIfStillPending(spawnedEntries, now + 10 * 1000);
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
writeSeedDataIntoCache(now, fetchStrategy, tree, staleAt, seedData, isResponsePartial, spawnedEntries);
|
|
}
|
|
const head = flightDataEntry.head;
|
|
if (head !== null && metadataTree !== null) {
|
|
// When Cache Components is enabled, the server conservatively marks
|
|
// the head as partial during static generation (isPossiblyPartialHead
|
|
// in app-render.tsx), even for fully static pages where the head is
|
|
// actually complete. When the response is non-partial, we override
|
|
// this since the server confirmed no dynamic content exists.
|
|
//
|
|
// Without Cache Components, the server always sends the correct
|
|
// isHeadPartial value, so no override is needed.
|
|
const isHeadPartial = ("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : flightDataEntry.isHeadPartial;
|
|
fulfillEntrySpawnedByRuntimePrefetch(now, fetchStrategy, head, isHeadPartial, staleAt, // parameter.
|
|
headVaryParams, metadataTree, spawnedEntries);
|
|
}
|
|
}
|
|
// Any entry that's still pending was intentionally not rendered by the
|
|
// server, because it was inside the loading boundary. Mark them as rejected
|
|
// so we know not to fetch them again.
|
|
// TODO: If PPR is enabled on some routes but not others, then it's possible
|
|
// that a different page is able to do a per-segment prefetch of one of the
|
|
// segments we're marking as rejected here. We should mark on the segment
|
|
// somehow that the reason for the rejection is because of a non-PPR prefetch.
|
|
// That way a per-segment prefetch knows to disregard the rejection.
|
|
if (spawnedEntries !== null) {
|
|
const fulfilledEntries = rejectSegmentEntriesIfStillPending(spawnedEntries, now + 10 * 1000);
|
|
return fulfilledEntries;
|
|
}
|
|
return null;
|
|
}
|
|
function writeSeedDataIntoCache(now, fetchStrategy, tree, staleAt, seedData, isResponsePartial, entriesOwnedByCurrentTask) {
|
|
// This function is used to write the result of a runtime server request
|
|
// (CacheNodeSeedData) into the prefetch cache.
|
|
const rsc = seedData[0];
|
|
const isPartial = rsc === null || isResponsePartial;
|
|
const varyParamsThenable = seedData[4];
|
|
// Each segment carries its own vary params thenable in the seed data. The
|
|
// thenable resolves to the set of params the segment accessed during render.
|
|
// A null thenable means tracking was not enabled (not a prerender).
|
|
const varyParams = varyParamsThenable !== null ? (0, _varyparamsdecoding.readVaryParams)(varyParamsThenable) : null;
|
|
fulfillEntrySpawnedByRuntimePrefetch(now, fetchStrategy, rsc, isPartial, staleAt, varyParams, tree, entriesOwnedByCurrentTask);
|
|
// Recursively write the child data into the cache.
|
|
const slots = tree.slots;
|
|
if (slots !== null) {
|
|
const seedDataChildren = seedData[1];
|
|
for(const parallelRouteKey in slots){
|
|
const childTree = slots[parallelRouteKey];
|
|
const childSeedData = seedDataChildren[parallelRouteKey];
|
|
if (childSeedData !== null && childSeedData !== undefined) {
|
|
writeSeedDataIntoCache(now, fetchStrategy, childTree, staleAt, childSeedData, isResponsePartial, entriesOwnedByCurrentTask);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function fulfillEntrySpawnedByRuntimePrefetch(now, fetchStrategy, rsc, isPartial, staleAt, segmentVaryParams, tree, entriesOwnedByCurrentTask) {
|
|
// We should only write into cache entries that are owned by us. Or create
|
|
// a new one and write into that. We must never write over an entry that was
|
|
// created by a different task, because that causes data races.
|
|
const ownedEntry = entriesOwnedByCurrentTask !== null ? entriesOwnedByCurrentTask.get(tree.requestKey) : undefined;
|
|
if (ownedEntry !== undefined) {
|
|
const fulfilledEntry = fulfillSegmentCacheEntry(ownedEntry, rsc, staleAt, isPartial);
|
|
// Re-key the entry based on which params the segment actually depends on.
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
} else {
|
|
// There's no matching entry. Attempt to create a new one.
|
|
const possiblyNewEntry = readOrCreateSegmentCacheEntry(now, fetchStrategy, tree);
|
|
if (possiblyNewEntry.status === 0) {
|
|
// Confirmed this is a new entry. We can fulfill it.
|
|
const newEntry = possiblyNewEntry;
|
|
const fulfilledEntry = fulfillSegmentCacheEntry(upgradeToPendingSegment(newEntry, fetchStrategy), rsc, staleAt, isPartial);
|
|
// Re-key the entry based on which params the segment actually depends on.
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
} else {
|
|
// There was already an entry in the cache. But we may be able to
|
|
// replace it with the new one from the server.
|
|
const newEntry = fulfillSegmentCacheEntry(upgradeToPendingSegment(createDetachedSegmentCacheEntry(now), fetchStrategy), rsc, staleAt, isPartial);
|
|
// Use the fulfilled vary path if available, otherwise fall back to
|
|
// the request vary path.
|
|
const varyPath = ("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : (0, _varypath.getSegmentVaryPathForRequest)(fetchStrategy, tree);
|
|
upsertSegmentEntry(now, varyPath, newEntry);
|
|
}
|
|
}
|
|
}
|
|
async function fetchPrefetchResponse(url, headers) {
|
|
const fetchPriority = 'low';
|
|
// When issuing a prefetch request, don't immediately decode the response; we
|
|
// use the lower level `createFromResponse` API instead because we need to do
|
|
// some extra processing of the response stream. See
|
|
// `createNonTaskyPrefetchResponseStream` for more details.
|
|
const shouldImmediatelyDecode = false;
|
|
const response = await (0, _fetchserverresponse.createFetch)(url, headers, fetchPriority, shouldImmediatelyDecode);
|
|
if (!response.ok) {
|
|
return null;
|
|
}
|
|
// Check the content type
|
|
if ("TURBOPACK compile-time falsy", 0) {
|
|
// In output: "export" mode, we relaxed about the content type, since it's
|
|
// not Next.js that's serving the response. If the status is OK, assume the
|
|
// response is valid. If it's not a valid response, the Flight client won't
|
|
// be able to decode it, and we'll treat it as a miss.
|
|
} else {
|
|
const contentType = response.headers.get('content-type');
|
|
const isFlightResponse = contentType && contentType.startsWith(_approuterheaders.RSC_CONTENT_TYPE_HEADER);
|
|
if (!isFlightResponse) {
|
|
return null;
|
|
}
|
|
}
|
|
return response;
|
|
}
|
|
async function createNonTaskyPrefetchResponseStream(body) {
|
|
// Buffer the entire response before passing it to the Flight client. This
|
|
// ensures that when Flight processes the stream, all model data is available
|
|
// synchronously. This is important for readVaryParams, which synchronously
|
|
// checks the thenable status — if data arrived in multiple network chunks,
|
|
// the thenables might not yet be fulfilled.
|
|
//
|
|
// TODO: There are too many intermediate stream transformations in the
|
|
// prefetch response pipeline (e.g. stripIsPartialByte, this function).
|
|
// These could all be consolidated into a single transformation. Refactor
|
|
// once the cached navigations experiment lands.
|
|
//
|
|
// Read the entire response from the network.
|
|
const reader = body.getReader();
|
|
const chunks = [];
|
|
let size = 0;
|
|
while(true){
|
|
const { done, value } = await reader.read();
|
|
if (done) break;
|
|
chunks.push(value);
|
|
size += value.byteLength;
|
|
}
|
|
// Concatenate into a single chunk so that Flight's processBinaryChunk
|
|
// processes all rows synchronously in one call. Multiple chunks would not
|
|
// be sufficient: even though reader.read() resolves as a microtask for
|
|
// already-enqueued data, the `await` continuation from
|
|
// createFromReadableStream can interleave between chunks. If the root
|
|
// model row isn't the first row (e.g. outlined values come first), the
|
|
// PromiseResolveThenableJob from `await` can cause the root to initialize
|
|
// eagerly, scheduling the continuation before remaining chunks (including
|
|
// promise value rows) are processed. A single chunk avoids this.
|
|
let buffer;
|
|
if (chunks.length === 1) {
|
|
buffer = chunks[0];
|
|
} else if (chunks.length > 1) {
|
|
buffer = new Uint8Array(size);
|
|
let offset = 0;
|
|
for (const chunk of chunks){
|
|
buffer.set(chunk, offset);
|
|
offset += chunk.byteLength;
|
|
}
|
|
} else {
|
|
buffer = new Uint8Array(0);
|
|
}
|
|
const stream = new ReadableStream({
|
|
start (controller) {
|
|
controller.enqueue(buffer);
|
|
controller.close();
|
|
}
|
|
});
|
|
return {
|
|
stream,
|
|
size
|
|
};
|
|
}
|
|
/**
|
|
* Creates a streaming (non-buffered) prefetch response stream for dynamic/Full
|
|
* prefetches. These are essentially dynamic responses that get stored in the
|
|
* prefetch cache — they don't carry vary params or other cache metadata that
|
|
* requires synchronous thenable resolution, so there's no need to buffer them.
|
|
* They should continue to stream so consumers can process data as it arrives.
|
|
*/ function createIncrementalPrefetchResponseStream(originalFlightStream, onStreamClose, onResponseSizeUpdate) {
|
|
// While processing the original stream, we incrementally update the size
|
|
// of the cache entry in the LRU.
|
|
let totalByteLength = 0;
|
|
const reader = originalFlightStream.getReader();
|
|
return new ReadableStream({
|
|
async pull (controller) {
|
|
while(true){
|
|
const { done, value } = await reader.read();
|
|
if (!done) {
|
|
// Pass to the target stream and keep consuming the Flight response
|
|
// from the server.
|
|
controller.enqueue(value);
|
|
// Incrementally update the size of the cache entry in the LRU.
|
|
totalByteLength += value.byteLength;
|
|
onResponseSizeUpdate(totalByteLength);
|
|
continue;
|
|
}
|
|
controller.close();
|
|
onStreamClose();
|
|
return;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
function addSegmentPathToUrlInOutputExportMode(url, segmentPath) {
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
return url;
|
|
}
|
|
function canNewFetchStrategyProvideMoreContent(currentStrategy, newStrategy) {
|
|
return currentStrategy < newStrategy;
|
|
}
|
|
/**
|
|
* Adds the instant prefetch header if the navigation lock is active.
|
|
* Uses a lazy require to ensure dead code elimination.
|
|
*/ function addInstantPrefetchHeaderIfLocked(headers) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
const { isNavigationLocked } = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/navigation-testing-lock.js [app-ssr] (ecmascript)");
|
|
if (isNavigationLocked()) {
|
|
headers[_approuterheaders.NEXT_INSTANT_PREFETCH_HEADER] = '1';
|
|
}
|
|
}
|
|
}
|
|
function getStaleAtFromHeader(now, response) {
|
|
const staleTimeSeconds = parseInt(response.headers.get(_approuterheaders.NEXT_ROUTER_STALE_TIME_HEADER) ?? '', 10);
|
|
const staleTimeMs = !isNaN(staleTimeSeconds) ? getStaleTimeMs(staleTimeSeconds) : _navigatereducer.STATIC_STALETIME_MS;
|
|
return now + staleTimeMs;
|
|
}
|
|
async function getStaleAt(now, staleTimeIterable, response) {
|
|
if (staleTimeIterable !== undefined) {
|
|
// Iterate the async iterable and take the last yielded value. The server
|
|
// yields updated staleTime values during the render; the last one is the
|
|
// final staleTime.
|
|
let staleTimeSeconds;
|
|
for await (const value of staleTimeIterable){
|
|
staleTimeSeconds = value;
|
|
}
|
|
if (staleTimeSeconds !== undefined) {
|
|
const staleTimeMs = isNaN(staleTimeSeconds) ? _navigatereducer.STATIC_STALETIME_MS : getStaleTimeMs(staleTimeSeconds);
|
|
return now + staleTimeMs;
|
|
}
|
|
}
|
|
if (response !== undefined) {
|
|
return getStaleAtFromHeader(now, response);
|
|
}
|
|
return now + _navigatereducer.STATIC_STALETIME_MS;
|
|
}
|
|
function writeStaticStageResponseIntoCache(now, flightData, buildId, headVaryParamsThenable, staleAt, baseTree, renderedSearch, isResponsePartial) {
|
|
const fetchStrategy = isResponsePartial ? _types.FetchStrategy.PPR : _types.FetchStrategy.Full;
|
|
const headVaryParams = headVaryParamsThenable !== null ? (0, _varyparamsdecoding.readVaryParams)(headVaryParamsThenable) : null;
|
|
const flightDatas = (0, _flightdatahelpers.normalizeFlightData)(flightData);
|
|
if (typeof flightDatas === 'string') {
|
|
return;
|
|
}
|
|
const navigationSeed = (0, _navigation.convertServerPatchToFullTree)(now, baseTree, flightDatas, renderedSearch, _bfcache.UnknownDynamicStaleTime);
|
|
writeDynamicRenderResponseIntoCache(now, fetchStrategy, flightDatas, buildId, isResponsePartial, headVaryParams, staleAt, navigationSeed, null // spawnedEntries — no pre-created entries; will create or upsert
|
|
);
|
|
}
|
|
async function processRuntimePrefetchStream(now, runtimePrefetchStream, baseTree, renderedSearch) {
|
|
const { stream, isPartial } = await stripIsPartialByte(runtimePrefetchStream);
|
|
const serverData = await (0, _fetchserverresponse.createFromNextReadableStream)(stream, undefined, {
|
|
allowPartialStream: true
|
|
});
|
|
const headVaryParamsThenable = serverData.h;
|
|
const headVaryParams = headVaryParamsThenable !== null ? (0, _varyparamsdecoding.readVaryParams)(headVaryParamsThenable) : null;
|
|
const staleAt = await getStaleAt(now, serverData.s);
|
|
const flightDatas = (0, _flightdatahelpers.normalizeFlightData)(serverData.f);
|
|
if (typeof flightDatas === 'string') {
|
|
return null;
|
|
}
|
|
const navigationSeed = (0, _navigation.convertServerPatchToFullTree)(now, baseTree, flightDatas, renderedSearch, _bfcache.UnknownDynamicStaleTime);
|
|
return {
|
|
flightDatas,
|
|
navigationSeed,
|
|
buildId: serverData.b,
|
|
isResponsePartial: isPartial,
|
|
headVaryParams,
|
|
staleAt
|
|
};
|
|
}
|
|
async function stripIsPartialByte(stream) {
|
|
// When there is no recognized marker byte, the fallback depends on whether
|
|
// Cached Navigations is enabled. When enabled, dynamic navigation responses
|
|
// don't have a marker but may contain dynamic holes, so they are treated as
|
|
// partial. When disabled, unmarked responses are treated as non-partial.
|
|
const defaultIsPartial = !!("TURBOPACK compile-time value", false);
|
|
const reader = stream.getReader();
|
|
const { done, value } = await reader.read();
|
|
if (done || !value || value.byteLength === 0) {
|
|
return {
|
|
stream: new ReadableStream({
|
|
start: (c)=>c.close()
|
|
}),
|
|
isPartial: defaultIsPartial
|
|
};
|
|
}
|
|
const firstByte = value[0];
|
|
const hasMarker = firstByte === 0x23 || firstByte === 0x7e;
|
|
const isPartial = hasMarker ? firstByte === 0x7e : defaultIsPartial;
|
|
const remainder = hasMarker ? value.byteLength > 1 ? value.subarray(1) : null : value;
|
|
return {
|
|
isPartial,
|
|
stream: new ReadableStream({
|
|
start (controller) {
|
|
if (remainder) {
|
|
controller.enqueue(remainder);
|
|
}
|
|
},
|
|
async pull (controller) {
|
|
const result = await reader.read();
|
|
if (result.done) {
|
|
controller.close();
|
|
} else {
|
|
controller.enqueue(result.value);
|
|
}
|
|
}
|
|
})
|
|
};
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/segment-cache/scheduler.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
cancelPrefetchTask: null,
|
|
isPrefetchTaskDirty: null,
|
|
pingPrefetchScheduler: null,
|
|
pingPrefetchTask: null,
|
|
reschedulePrefetchTask: null,
|
|
schedulePrefetchTask: null,
|
|
startRevalidationCooldown: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
cancelPrefetchTask: function() {
|
|
return cancelPrefetchTask;
|
|
},
|
|
isPrefetchTaskDirty: function() {
|
|
return isPrefetchTaskDirty;
|
|
},
|
|
pingPrefetchScheduler: function() {
|
|
return pingPrefetchScheduler;
|
|
},
|
|
pingPrefetchTask: function() {
|
|
return pingPrefetchTask;
|
|
},
|
|
reschedulePrefetchTask: function() {
|
|
return reschedulePrefetchTask;
|
|
},
|
|
schedulePrefetchTask: function() {
|
|
return schedulePrefetchTask;
|
|
},
|
|
startRevalidationCooldown: function() {
|
|
return startRevalidationCooldown;
|
|
}
|
|
});
|
|
const _approutertypes = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/app-router-types.js [app-ssr] (ecmascript)");
|
|
const _matchsegments = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/match-segments.js [app-ssr] (ecmascript)");
|
|
const _cache = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache.js [app-ssr] (ecmascript)");
|
|
const _cachekey = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-key.js [app-ssr] (ecmascript)");
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _segment = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/segment.js [app-ssr] (ecmascript)");
|
|
const _lru = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/lru.js [app-ssr] (ecmascript)");
|
|
const scheduleMicrotask = typeof queueMicrotask === 'function' ? queueMicrotask : (fn)=>Promise.resolve().then(fn).catch((error)=>setTimeout(()=>{
|
|
throw error;
|
|
}));
|
|
const taskHeap = [];
|
|
let inProgressRequests = 0;
|
|
let sortIdCounter = 0;
|
|
let didScheduleMicrotask = false;
|
|
// The most recently hovered (or touched, etc) link, i.e. the most recent task
|
|
// scheduled at Intent priority. There's only ever a single task at Intent
|
|
// priority at a time. We reserve special network bandwidth for this task only.
|
|
let mostRecentlyHoveredLink = null;
|
|
// CDN cache propagation delay after revalidation (in milliseconds)
|
|
const REVALIDATION_COOLDOWN_MS = 300;
|
|
// Timeout handle for the revalidation cooldown. When non-null, prefetch
|
|
// requests are blocked to allow CDN cache propagation.
|
|
let revalidationCooldownTimeoutHandle = null;
|
|
function startRevalidationCooldown() {
|
|
// Clear any existing timeout in case multiple revalidations happen
|
|
// in quick succession.
|
|
if (revalidationCooldownTimeoutHandle !== null) {
|
|
clearTimeout(revalidationCooldownTimeoutHandle);
|
|
}
|
|
// Schedule the cooldown to expire after the delay.
|
|
revalidationCooldownTimeoutHandle = setTimeout(()=>{
|
|
revalidationCooldownTimeoutHandle = null;
|
|
// Retry the prefetch queue now that the cooldown has expired.
|
|
pingPrefetchScheduler();
|
|
}, REVALIDATION_COOLDOWN_MS);
|
|
}
|
|
function schedulePrefetchTask(key, treeAtTimeOfPrefetch, fetchStrategy, priority, onInvalidate, _onComplete) {
|
|
// Spawn a new prefetch task
|
|
const task = {
|
|
key,
|
|
treeAtTimeOfPrefetch,
|
|
routeCacheVersion: (0, _cache.getCurrentRouteCacheVersion)(),
|
|
segmentCacheVersion: (0, _cache.getCurrentSegmentCacheVersion)(),
|
|
priority,
|
|
phase: 1,
|
|
hasBackgroundWork: false,
|
|
spawnedRuntimePrefetches: null,
|
|
fetchStrategy,
|
|
sortId: sortIdCounter++,
|
|
isCanceled: false,
|
|
onInvalidate,
|
|
_heapIndex: -1
|
|
};
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
task._onComplete = _onComplete;
|
|
}
|
|
trackMostRecentlyHoveredLink(task);
|
|
heapPush(taskHeap, task);
|
|
// Schedule an async task to process the queue.
|
|
//
|
|
// The main reason we process the queue in an async task is for batching.
|
|
// It's common for a single JS task/event to trigger multiple prefetches.
|
|
// By deferring to a microtask, we only process the queue once per JS task.
|
|
// If they have different priorities, it also ensures they are processed in
|
|
// the optimal order.
|
|
pingPrefetchScheduler();
|
|
return task;
|
|
}
|
|
function cancelPrefetchTask(task) {
|
|
// Remove the prefetch task from the queue. If the task already completed,
|
|
// then this is a no-op.
|
|
//
|
|
// We must also explicitly mark the task as canceled so that a blocked task
|
|
// does not get added back to the queue when it's pinged by the network.
|
|
task.isCanceled = true;
|
|
heapDelete(taskHeap, task);
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
// Call completion callback. In practice this shouldn't be reached for
|
|
// test-initiated prefetches since cancellation is only used by the Link
|
|
// component when elements scroll out of viewport.
|
|
const onComplete = task._onComplete;
|
|
task._onComplete = undefined;
|
|
onComplete?.();
|
|
}
|
|
}
|
|
function reschedulePrefetchTask(task, treeAtTimeOfPrefetch, fetchStrategy, priority) {
|
|
// Bump the prefetch task to the top of the queue, as if it were a fresh
|
|
// task. This is essentially the same as canceling the task and scheduling
|
|
// a new one, except it reuses the original object.
|
|
//
|
|
// The primary use case is to increase the priority of a Link-initated
|
|
// prefetch on hover.
|
|
//
|
|
// Note: _onComplete is not reset here because it's preserved on the same
|
|
// task object. When the rescheduled task completes, the original callback
|
|
// will still be invoked.
|
|
// Un-cancel the task, in case it was previously canceled.
|
|
task.isCanceled = false;
|
|
task.phase = 1;
|
|
// Assign a new sort ID to move it ahead of all other tasks at the same
|
|
// priority level. (Higher sort IDs are processed first.)
|
|
task.sortId = sortIdCounter++;
|
|
task.priority = // Intent priority, even if the rescheduled priority is lower.
|
|
task === mostRecentlyHoveredLink ? _types.PrefetchPriority.Intent : priority;
|
|
task.treeAtTimeOfPrefetch = treeAtTimeOfPrefetch;
|
|
task.fetchStrategy = fetchStrategy;
|
|
trackMostRecentlyHoveredLink(task);
|
|
if (task._heapIndex !== -1) {
|
|
// The task is already in the queue.
|
|
heapResift(taskHeap, task);
|
|
} else {
|
|
heapPush(taskHeap, task);
|
|
}
|
|
pingPrefetchScheduler();
|
|
}
|
|
function isPrefetchTaskDirty(task, nextUrl, tree) {
|
|
// This is used to quickly bail out of a prefetch task if the result is
|
|
// guaranteed to not have changed since the task was initiated. This is
|
|
// strictly an optimization — theoretically, if it always returned true, no
|
|
// behavior should change because a full prefetch task will effectively
|
|
// perform the same checks.
|
|
return task.routeCacheVersion !== (0, _cache.getCurrentRouteCacheVersion)() || task.segmentCacheVersion !== (0, _cache.getCurrentSegmentCacheVersion)() || task.treeAtTimeOfPrefetch !== tree || task.key.nextUrl !== nextUrl;
|
|
}
|
|
function trackMostRecentlyHoveredLink(task) {
|
|
// Track the mostly recently hovered link, i.e. the most recently scheduled
|
|
// task at Intent priority. There must only be one such task at a time.
|
|
if (task.priority === _types.PrefetchPriority.Intent && task !== mostRecentlyHoveredLink) {
|
|
if (mostRecentlyHoveredLink !== null) {
|
|
// Bump the previously hovered link's priority down to Default.
|
|
if (mostRecentlyHoveredLink.priority !== _types.PrefetchPriority.Background) {
|
|
mostRecentlyHoveredLink.priority = _types.PrefetchPriority.Default;
|
|
heapResift(taskHeap, mostRecentlyHoveredLink);
|
|
}
|
|
}
|
|
mostRecentlyHoveredLink = task;
|
|
}
|
|
}
|
|
function pingPrefetchScheduler() {
|
|
if (didScheduleMicrotask) {
|
|
// Already scheduled a task to process the queue
|
|
return;
|
|
}
|
|
didScheduleMicrotask = true;
|
|
scheduleMicrotask(processQueueInMicrotask);
|
|
}
|
|
/**
|
|
* Checks if we've exceeded the maximum number of concurrent prefetch requests,
|
|
* to avoid saturating the browser's internal network queue. This is a
|
|
* cooperative limit — prefetch tasks should check this before issuing
|
|
* new requests.
|
|
*
|
|
* Also checks if we're within the revalidation cooldown window, during which
|
|
* prefetch requests are delayed to allow CDN cache propagation.
|
|
*/ function hasNetworkBandwidth(task) {
|
|
// Check if we're within the revalidation cooldown window
|
|
if (revalidationCooldownTimeoutHandle !== null) {
|
|
// We're within the cooldown window. Return false to prevent prefetching.
|
|
// When the cooldown expires, the timeout will call ensureWorkIsScheduled()
|
|
// to retry the queue.
|
|
return false;
|
|
}
|
|
// TODO: Also check if there's an in-progress navigation. We should never
|
|
// add prefetch requests to the network queue if an actual navigation is
|
|
// taking place, to ensure there's sufficient bandwidth for render-blocking
|
|
// data and resources.
|
|
// TODO: Consider reserving some amount of bandwidth for static prefetches.
|
|
if (task.priority === _types.PrefetchPriority.Intent) {
|
|
// The most recently hovered link is allowed to exceed the default limit.
|
|
//
|
|
// The goal is to always have enough bandwidth to start a new prefetch
|
|
// request when hovering over a link.
|
|
//
|
|
// However, because we don't abort in-progress requests, it's still possible
|
|
// we'll run out of bandwidth. When links are hovered in quick succession,
|
|
// there could be multiple hover requests running simultaneously.
|
|
return inProgressRequests < 12;
|
|
}
|
|
// The default limit is lower than the limit for a hovered link.
|
|
return inProgressRequests < 4;
|
|
}
|
|
function spawnPrefetchSubtask(prefetchSubtask) {
|
|
// When the scheduler spawns an async task, we don't await its result.
|
|
// Instead, the async task writes its result directly into the cache, then
|
|
// pings the scheduler to continue.
|
|
//
|
|
// We process server responses streamingly, so the prefetch subtask will
|
|
// likely resolve before we're finished receiving all the data. The subtask
|
|
// result includes a promise that resolves once the network connection is
|
|
// closed. The scheduler uses this to control network bandwidth by tracking
|
|
// and limiting the number of concurrent requests.
|
|
inProgressRequests++;
|
|
return prefetchSubtask.then((result)=>{
|
|
if (result === null) {
|
|
// The prefetch task errored before it could start processing the
|
|
// network stream. Assume the connection is closed.
|
|
onPrefetchConnectionClosed();
|
|
return null;
|
|
}
|
|
// Wait for the connection to close before freeing up more bandwidth.
|
|
result.closed.then(onPrefetchConnectionClosed);
|
|
return result.value;
|
|
});
|
|
}
|
|
function onPrefetchConnectionClosed() {
|
|
inProgressRequests--;
|
|
// Notify the scheduler that we have more bandwidth, and can continue
|
|
// processing tasks.
|
|
pingPrefetchScheduler();
|
|
}
|
|
function pingPrefetchTask(task) {
|
|
// "Ping" a prefetch that's already in progress to notify it of new data.
|
|
if (task.isCanceled || // Check if prefetch is already queued.
|
|
task._heapIndex !== -1) {
|
|
return;
|
|
}
|
|
// Add the task back to the queue.
|
|
heapPush(taskHeap, task);
|
|
pingPrefetchScheduler();
|
|
}
|
|
function processQueueInMicrotask() {
|
|
didScheduleMicrotask = false;
|
|
// We aim to minimize how often we read the current time. Since nearly all
|
|
// functions in the prefetch scheduler are synchronous, we can read the time
|
|
// once and pass it as an argument wherever it's needed.
|
|
const now = Date.now();
|
|
// Process the task queue until we run out of network bandwidth.
|
|
let task = heapPeek(taskHeap);
|
|
while(task !== null && hasNetworkBandwidth(task)){
|
|
task.routeCacheVersion = (0, _cache.getCurrentRouteCacheVersion)();
|
|
task.segmentCacheVersion = (0, _cache.getCurrentSegmentCacheVersion)();
|
|
const exitStatus = pingRoute(now, task);
|
|
// These fields are only valid for a single attempt. Reset them after each
|
|
// iteration of the task queue.
|
|
const hasBackgroundWork = task.hasBackgroundWork;
|
|
task.hasBackgroundWork = false;
|
|
task.spawnedRuntimePrefetches = null;
|
|
switch(exitStatus){
|
|
case 0:
|
|
// The task yielded because there are too many requests in progress.
|
|
// Stop processing tasks until we have more bandwidth.
|
|
return;
|
|
case 1:
|
|
// The task is blocked. It needs more data before it can proceed.
|
|
// Keep the task out of the queue until the server responds.
|
|
heapPop(taskHeap);
|
|
// Continue to the next task
|
|
task = heapPeek(taskHeap);
|
|
continue;
|
|
case 2:
|
|
if (task.phase === 1) {
|
|
// Finished prefetching the route tree. Proceed to prefetching
|
|
// the segments.
|
|
task.phase = 0;
|
|
heapResift(taskHeap, task);
|
|
} else if (hasBackgroundWork) {
|
|
// The task spawned additional background work. Reschedule the task
|
|
// at background priority.
|
|
task.priority = _types.PrefetchPriority.Background;
|
|
heapResift(taskHeap, task);
|
|
} else {
|
|
// The prefetch is complete. Continue to the next task.
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
// Notify the Instant Navigation Testing API that the prefetch has
|
|
// completed, so it can proceed with navigation.
|
|
const onComplete = task._onComplete;
|
|
task._onComplete = undefined;
|
|
onComplete?.();
|
|
}
|
|
heapPop(taskHeap);
|
|
}
|
|
task = heapPeek(taskHeap);
|
|
continue;
|
|
default:
|
|
exitStatus;
|
|
}
|
|
}
|
|
// Run LRU cleanup only when the scheduler is fully idle: no queued tasks and
|
|
// no in-progress requests. At that point, all active prefetch tasks have
|
|
// finished reading from the cache (moving recently used entries to the front
|
|
// of the list), so only genuinely stale data gets evicted.
|
|
if (task === null && inProgressRequests === 0) {
|
|
(0, _lru.cleanup)();
|
|
}
|
|
}
|
|
/**
|
|
* Check this during a prefetch task to determine if background work can be
|
|
* performed. If so, it evaluates to `true`. Otherwise, it returns `false`,
|
|
* while also scheduling a background task to run later. Usage:
|
|
*
|
|
* @example
|
|
* if (background(task)) {
|
|
* // Perform background-pri work
|
|
* }
|
|
*/ function background(task) {
|
|
if (task.priority === _types.PrefetchPriority.Background) {
|
|
return true;
|
|
}
|
|
task.hasBackgroundWork = true;
|
|
return false;
|
|
}
|
|
function pingRoute(now, task) {
|
|
const key = task.key;
|
|
const route = (0, _cache.readOrCreateRouteCacheEntry)(now, task, key);
|
|
const exitStatus = pingRootRouteTree(now, task, route);
|
|
if (exitStatus !== 0 && key.search !== '') {
|
|
// If the URL has a non-empty search string, also prefetch the pathname
|
|
// without the search string. We use the searchless route tree as a base for
|
|
// optimistic routing; see requestOptimisticRouteCacheEntry for details.
|
|
//
|
|
// Note that we don't need to prefetch any of the segment data. Just the
|
|
// route tree.
|
|
//
|
|
// TODO: This is a temporary solution; the plan is to replace this by adding
|
|
// a wildcard lookup method to the TupleMap implementation. This is
|
|
// non-trivial to implement because it needs to account for things like
|
|
// fallback route entries, hence this temporary workaround.
|
|
const url = new URL(key.pathname, location.origin);
|
|
const keyWithoutSearch = (0, _cachekey.createCacheKey)(url.href, key.nextUrl);
|
|
const routeWithoutSearch = (0, _cache.readOrCreateRouteCacheEntry)(now, task, keyWithoutSearch);
|
|
switch(routeWithoutSearch.status){
|
|
case _cache.EntryStatus.Empty:
|
|
{
|
|
if (background(task)) {
|
|
routeWithoutSearch.status = _cache.EntryStatus.Pending;
|
|
spawnPrefetchSubtask((0, _cache.fetchRouteOnCacheMiss)(routeWithoutSearch, keyWithoutSearch));
|
|
}
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Pending:
|
|
case _cache.EntryStatus.Fulfilled:
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
routeWithoutSearch;
|
|
}
|
|
}
|
|
return exitStatus;
|
|
}
|
|
function pingRootRouteTree(now, task, route) {
|
|
switch(route.status){
|
|
case _cache.EntryStatus.Empty:
|
|
{
|
|
// Route is not yet cached, and there's no request already in progress.
|
|
// Spawn a task to request the route, load it into the cache, and ping
|
|
// the task to continue.
|
|
// TODO: There are multiple strategies in the <Link> API for prefetching
|
|
// a route. Currently we've only implemented the main one: per-segment,
|
|
// static-data only.
|
|
//
|
|
// There's also `<Link prefetch={true}>`
|
|
// which prefetch both static *and* dynamic data.
|
|
// Similarly, we need to fallback to the old, per-page
|
|
// behavior if PPR is disabled for a route (via the incremental opt-in).
|
|
//
|
|
// Those cases will be handled here.
|
|
spawnPrefetchSubtask((0, _cache.fetchRouteOnCacheMiss)(route, task.key));
|
|
// If the request takes longer than a minute, a subsequent request should
|
|
// retry instead of waiting for this one. When the response is received,
|
|
// this value will be replaced by a new value based on the stale time sent
|
|
// from the server.
|
|
// TODO: We should probably also manually abort the fetch task, to reclaim
|
|
// server bandwidth.
|
|
route.staleAt = now + 60 * 1000;
|
|
// Upgrade to Pending so we know there's already a request in progress
|
|
route.status = _cache.EntryStatus.Pending;
|
|
// Intentional fallthrough to the Pending branch
|
|
}
|
|
case _cache.EntryStatus.Pending:
|
|
{
|
|
// Still pending. We can't start prefetching the segments until the route
|
|
// tree has loaded. Add the task to the set of blocked tasks so that it
|
|
// is notified when the route tree is ready.
|
|
const blockedTasks = route.blockedTasks;
|
|
if (blockedTasks === null) {
|
|
route.blockedTasks = new Set([
|
|
task
|
|
]);
|
|
} else {
|
|
blockedTasks.add(task);
|
|
}
|
|
return 1;
|
|
}
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
// Route tree failed to load. Treat as a 404.
|
|
return 2;
|
|
}
|
|
case _cache.EntryStatus.Fulfilled:
|
|
{
|
|
if (task.phase !== 0) {
|
|
// Do not prefetch segment data until we've entered the segment phase.
|
|
return 2;
|
|
}
|
|
// Recursively fill in the segment tree.
|
|
if (!hasNetworkBandwidth(task)) {
|
|
// Stop prefetching segments until there's more bandwidth.
|
|
return 0;
|
|
}
|
|
const tree = route.tree;
|
|
// A task's fetch strategy gets set to `PPR` for any "auto" prefetch.
|
|
// If it turned out that the route isn't PPR-enabled, we need to use `LoadingBoundary` instead.
|
|
// We don't need to do this for runtime prefetches, because those are only available in
|
|
// `cacheComponents`, where every route is PPR.
|
|
let fetchStrategy;
|
|
if (tree.prefetchHints & _approutertypes.PrefetchHint.SubtreeHasInstant) {
|
|
// If `instant` is defined anywhere on the target route, ignore the
|
|
// fetch strategy and switch to unified strategy used by Cache
|
|
// Components (called `PPR` for now, will likely be renamed).
|
|
//
|
|
// In practice, this just means that a "full" prefetch (<Link
|
|
// prefetch={true}>) has no effect. You're meant to use Runtime
|
|
// Prefetching instead — that's the new pattern that replaces
|
|
// prefetch={true}.
|
|
//
|
|
// The reason we check for `instant` rather than the `cacheComponents`
|
|
// flag is to support incremental adoption. `prefetch={true}` will
|
|
// continue to work until you opt into `instant`.
|
|
fetchStrategy = _types.FetchStrategy.PPR;
|
|
} else if (task.fetchStrategy === _types.FetchStrategy.PPR) {
|
|
fetchStrategy = route.supportsPerSegmentPrefetching ? _types.FetchStrategy.PPR : _types.FetchStrategy.LoadingBoundary;
|
|
} else {
|
|
fetchStrategy = task.fetchStrategy;
|
|
}
|
|
switch(fetchStrategy){
|
|
case _types.FetchStrategy.PPR:
|
|
{
|
|
// For Cache Components pages, each segment may be prefetched
|
|
// statically or using a runtime request, based on various
|
|
// configurations and heuristics. We'll do this in two passes: first
|
|
// traverse the tree and perform all the static prefetches.
|
|
//
|
|
// Then, if there are any segments that need a runtime request,
|
|
// do another pass to perform a runtime prefetch.
|
|
pingStaticHead(now, task, route);
|
|
const exitStatus = pingSharedPartOfCacheComponentsTree(now, task, route, task.treeAtTimeOfPrefetch, tree);
|
|
if (exitStatus === 0) {
|
|
// Child yielded without finishing.
|
|
return 0;
|
|
}
|
|
const spawnedRuntimePrefetches = task.spawnedRuntimePrefetches;
|
|
if (spawnedRuntimePrefetches !== null) {
|
|
// During the first pass, we discovered segments that require a
|
|
// runtime prefetch. Do a second pass to construct a request tree.
|
|
const spawnedEntries = new Map();
|
|
pingRuntimeHead(now, task, route, spawnedEntries, _types.FetchStrategy.PPRRuntime);
|
|
const requestTree = pingRuntimePrefetches(now, task, route, tree, spawnedRuntimePrefetches, spawnedEntries);
|
|
let needsDynamicRequest = spawnedEntries.size > 0;
|
|
if (needsDynamicRequest) {
|
|
// Perform a dynamic prefetch request and populate the cache with
|
|
// the result.
|
|
spawnPrefetchSubtask((0, _cache.fetchSegmentPrefetchesUsingDynamicRequest)(task, route, _types.FetchStrategy.PPRRuntime, requestTree, spawnedEntries));
|
|
}
|
|
}
|
|
return 2;
|
|
}
|
|
case _types.FetchStrategy.Full:
|
|
case _types.FetchStrategy.PPRRuntime:
|
|
case _types.FetchStrategy.LoadingBoundary:
|
|
{
|
|
// Prefetch multiple segments using a single dynamic request.
|
|
// TODO: We can consolidate this branch with previous one by modeling
|
|
// it as if the first segment in the new tree has runtime prefetching
|
|
// enabled. Will do this as a follow-up refactor. Might want to remove
|
|
// the special metatdata case below first. In the meantime, it's not
|
|
// really that much duplication, just would be nice to remove one of
|
|
// these codepaths.
|
|
const spawnedEntries = new Map();
|
|
pingRuntimeHead(now, task, route, spawnedEntries, fetchStrategy);
|
|
const dynamicRequestTree = diffRouteTreeAgainstCurrent(now, task, route, task.treeAtTimeOfPrefetch, tree, spawnedEntries, fetchStrategy);
|
|
let needsDynamicRequest = spawnedEntries.size > 0;
|
|
if (needsDynamicRequest) {
|
|
spawnPrefetchSubtask((0, _cache.fetchSegmentPrefetchesUsingDynamicRequest)(task, route, fetchStrategy, dynamicRequestTree, spawnedEntries));
|
|
}
|
|
return 2;
|
|
}
|
|
default:
|
|
fetchStrategy;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
route;
|
|
}
|
|
}
|
|
return 2;
|
|
}
|
|
function pingStaticHead(now, task, route) {
|
|
// The Head data for a page (metadata, viewport) is not really a route
|
|
// segment, in the sense that it doesn't appear in the route tree. But we
|
|
// store it in the cache as if it were, using a special key.
|
|
pingStaticSegmentData(now, task, route, (0, _cache.readOrCreateSegmentCacheEntry)(now, _types.FetchStrategy.PPR, route.metadata), task.key, route.metadata);
|
|
}
|
|
function pingRuntimeHead(now, task, route, spawnedEntries, fetchStrategy) {
|
|
pingRouteTreeAndIncludeDynamicData(now, task, route, route.metadata, false, spawnedEntries, // and LoadingBoundary
|
|
fetchStrategy === _types.FetchStrategy.LoadingBoundary ? _types.FetchStrategy.Full : fetchStrategy);
|
|
}
|
|
// TODO: Rename dynamic -> runtime throughout this module
|
|
function pingSharedPartOfCacheComponentsTree(now, task, route, oldTree, newTree) {
|
|
// When Cache Components is enabled (or PPR, or a fully static route when PPR
|
|
// is disabled; those cases are treated equivalently to Cache Components), we
|
|
// start by prefetching each segment individually. Once we reach the "new"
|
|
// part of the tree — the part that doesn't exist on the current page — we
|
|
// may choose to switch to a runtime prefetch instead, based on the
|
|
// information sent by the server in the route tree.
|
|
//
|
|
// The traversal starts in the "shared" part of the tree. Once we reach the
|
|
// "new" part of the tree, we switch to a different traversal,
|
|
// pingNewPartOfCacheComponentsTree.
|
|
// Prefetch this segment's static data.
|
|
const segment = (0, _cache.readOrCreateSegmentCacheEntry)(now, task.fetchStrategy, newTree);
|
|
pingStaticSegmentData(now, task, route, segment, task.key, newTree);
|
|
// Recursively ping the children.
|
|
const oldTreeChildren = oldTree[1];
|
|
const newTreeChildren = newTree.slots;
|
|
if (newTreeChildren !== null) {
|
|
for(const parallelRouteKey in newTreeChildren){
|
|
if (!hasNetworkBandwidth(task)) {
|
|
// Stop prefetching segments until there's more bandwidth.
|
|
return 0;
|
|
}
|
|
const newTreeChild = newTreeChildren[parallelRouteKey];
|
|
const newTreeChildSegment = newTreeChild.segment;
|
|
const oldTreeChild = oldTreeChildren[parallelRouteKey];
|
|
const oldTreeChildSegment = oldTreeChild?.[0];
|
|
let childExitStatus;
|
|
if (oldTreeChildSegment !== undefined && doesCurrentSegmentMatchCachedSegment(route, newTreeChildSegment, oldTreeChildSegment)) {
|
|
// We're still in the "shared" part of the tree.
|
|
childExitStatus = pingSharedPartOfCacheComponentsTree(now, task, route, oldTreeChild, newTreeChild);
|
|
} else {
|
|
// We've entered the "new" part of the tree. Switch
|
|
// traversal functions.
|
|
childExitStatus = pingNewPartOfCacheComponentsTree(now, task, route, newTreeChild);
|
|
}
|
|
if (childExitStatus === 0) {
|
|
// Child yielded without finishing.
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
return 2;
|
|
}
|
|
function pingNewPartOfCacheComponentsTree(now, task, route, tree) {
|
|
// We're now prefetching in the "new" part of the tree, the part that doesn't
|
|
// exist on the current page. (In other words, we're deeper than the
|
|
// shared layouts.) Segments in here default to being prefetched statically.
|
|
// However, if the server instructs us to, we may switch to a runtime
|
|
// prefetch instead. Traverse the tree and check at each segment.
|
|
if (tree.prefetchHints & _approutertypes.PrefetchHint.HasRuntimePrefetch) {
|
|
// This route has a runtime prefetch response. Since we're below the shared
|
|
// layout, everything from this point should be prefetched using a single,
|
|
// combined runtime request, rather than using per-segment static requests.
|
|
// This is true even if some of the child segments are known to be fully
|
|
// static — once we've decided to perform a runtime prefetch, we might as
|
|
// well respond with the static segments in the same roundtrip. (That's how
|
|
// regular navigations work, too.) We'll still skip over segments that are
|
|
// already cached, though.
|
|
//
|
|
// It's the server's responsibility to set a reasonable value of
|
|
// `hasRuntimePrefetch`. Currently it's user-defined, but eventually, the
|
|
// server may send a value of `false` even if the user opts in, if it
|
|
// determines during build that the route is always fully static. There are
|
|
// more optimizations we can do once we implement fallback param
|
|
// tracking, too.
|
|
//
|
|
// Use the task object to collect the segments that need a runtime prefetch.
|
|
// This will signal to the outer task queue that a second traversal is
|
|
// required to construct a request tree.
|
|
if (task.spawnedRuntimePrefetches === null) {
|
|
task.spawnedRuntimePrefetches = new Set([
|
|
tree.requestKey
|
|
]);
|
|
} else {
|
|
task.spawnedRuntimePrefetches.add(tree.requestKey);
|
|
}
|
|
// Then exit the traversal without prefetching anything further.
|
|
return 2;
|
|
}
|
|
// This segment should not be runtime prefetched. Prefetch its static data.
|
|
const segment = (0, _cache.readOrCreateSegmentCacheEntry)(now, task.fetchStrategy, tree);
|
|
pingStaticSegmentData(now, task, route, segment, task.key, tree);
|
|
if (tree.slots !== null) {
|
|
if (!hasNetworkBandwidth(task)) {
|
|
// Stop prefetching segments until there's more bandwidth.
|
|
return 0;
|
|
}
|
|
// Recursively ping the children.
|
|
for(const parallelRouteKey in tree.slots){
|
|
const childTree = tree.slots[parallelRouteKey];
|
|
const childExitStatus = pingNewPartOfCacheComponentsTree(now, task, route, childTree);
|
|
if (childExitStatus === 0) {
|
|
// Child yielded without finishing.
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
// This segment and all its children have finished prefetching.
|
|
return 2;
|
|
}
|
|
function diffRouteTreeAgainstCurrent(now, task, route, oldTree, newTree, spawnedEntries, fetchStrategy) {
|
|
// This is a single recursive traversal that does multiple things:
|
|
// - Finds the parts of the target route (newTree) that are not part of
|
|
// of the current page (oldTree) by diffing them, using the same algorithm
|
|
// as a real navigation.
|
|
// - Constructs a request tree (FlightRouterState) that describes which
|
|
// segments need to be prefetched and which ones are already cached.
|
|
// - Creates a set of pending cache entries for the segments that need to
|
|
// be prefetched, so that a subsequent prefetch task does not request the
|
|
// same segments again.
|
|
const oldTreeChildren = oldTree[1];
|
|
const newTreeChildren = newTree.slots;
|
|
let requestTreeChildren = {};
|
|
if (newTreeChildren !== null) {
|
|
for(const parallelRouteKey in newTreeChildren){
|
|
const newTreeChild = newTreeChildren[parallelRouteKey];
|
|
const newTreeChildSegment = newTreeChild.segment;
|
|
const oldTreeChild = oldTreeChildren[parallelRouteKey];
|
|
const oldTreeChildSegment = oldTreeChild?.[0];
|
|
if (oldTreeChildSegment !== undefined && doesCurrentSegmentMatchCachedSegment(route, newTreeChildSegment, oldTreeChildSegment)) {
|
|
// This segment is already part of the current route. Keep traversing.
|
|
const requestTreeChild = diffRouteTreeAgainstCurrent(now, task, route, oldTreeChild, newTreeChild, spawnedEntries, fetchStrategy);
|
|
requestTreeChildren[parallelRouteKey] = requestTreeChild;
|
|
} else {
|
|
// This segment is not part of the current route. We're entering a
|
|
// part of the tree that we need to prefetch (unless everything is
|
|
// already cached).
|
|
switch(fetchStrategy){
|
|
case _types.FetchStrategy.LoadingBoundary:
|
|
{
|
|
// When PPR is disabled, we can't prefetch per segment. We must
|
|
// fallback to the old prefetch behavior and send a dynamic request.
|
|
// Only routes that include a loading boundary can be prefetched in
|
|
// this way.
|
|
//
|
|
// This is simlar to a "full" prefetch, but we're much more
|
|
// conservative about which segments to include in the request.
|
|
//
|
|
// The server will only render up to the first loading boundary
|
|
// inside new part of the tree. If there's no loading boundary
|
|
// anywhere in the tree, the server will never return any data, so
|
|
// we can skip the request.
|
|
const subtreeHasLoadingBoundary = (newTreeChild.prefetchHints & (_approutertypes.PrefetchHint.SegmentHasLoadingBoundary | _approutertypes.PrefetchHint.SubtreeHasLoadingBoundary)) !== 0;
|
|
const requestTreeChild = subtreeHasLoadingBoundary ? pingPPRDisabledRouteTreeUpToLoadingBoundary(now, task, route, newTreeChild, null, spawnedEntries) : (0, _cache.convertRouteTreeToFlightRouterState)(newTreeChild);
|
|
requestTreeChildren[parallelRouteKey] = requestTreeChild;
|
|
break;
|
|
}
|
|
case _types.FetchStrategy.PPRRuntime:
|
|
{
|
|
// This is a runtime prefetch. Fetch all cacheable data in the tree,
|
|
// not just the static PPR shell.
|
|
const requestTreeChild = pingRouteTreeAndIncludeDynamicData(now, task, route, newTreeChild, false, spawnedEntries, fetchStrategy);
|
|
requestTreeChildren[parallelRouteKey] = requestTreeChild;
|
|
break;
|
|
}
|
|
case _types.FetchStrategy.Full:
|
|
{
|
|
// This is a "full" prefetch. Fetch all the data in the tree, both
|
|
// static and dynamic. We issue roughly the same request that we
|
|
// would during a real navigation. The goal is that once the
|
|
// navigation occurs, the router should not have to fetch any
|
|
// additional data.
|
|
//
|
|
// Although the response will include dynamic data, opting into a
|
|
// Full prefetch — via <Link prefetch={true}> — implicitly
|
|
// instructs the cache to treat the response as "static", or non-
|
|
// dynamic, since the whole point is to cache it for
|
|
// future navigations.
|
|
//
|
|
// Construct a tree (currently a FlightRouterState) that represents
|
|
// which segments need to be prefetched and which ones are already
|
|
// cached. If the tree is empty, then we can exit. Otherwise, we'll
|
|
// send the request tree to the server and use the response to
|
|
// populate the segment cache.
|
|
const requestTreeChild = pingRouteTreeAndIncludeDynamicData(now, task, route, newTreeChild, false, spawnedEntries, fetchStrategy);
|
|
requestTreeChildren[parallelRouteKey] = requestTreeChild;
|
|
break;
|
|
}
|
|
default:
|
|
fetchStrategy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const requestTree = [
|
|
newTree.segment,
|
|
requestTreeChildren,
|
|
null,
|
|
null
|
|
];
|
|
return requestTree;
|
|
}
|
|
function pingPPRDisabledRouteTreeUpToLoadingBoundary(now, task, route, tree, refetchMarkerContext, spawnedEntries) {
|
|
// This function is similar to pingRouteTreeAndIncludeDynamicData, except the
|
|
// server is only going to return a minimal loading state — it will stop
|
|
// rendering at the first loading boundary. Whereas a Full prefetch is
|
|
// intentionally aggressive and tries to pretfetch all the data that will be
|
|
// needed for a navigation, a LoadingBoundary prefetch is much more
|
|
// conservative. For example, it will omit from the request tree any segment
|
|
// that is already cached, regardles of whether it's partial or full. By
|
|
// contrast, a Full prefetch will refetch partial segments.
|
|
// "inside-shared-layout" tells the server where to start looking for a
|
|
// loading boundary.
|
|
let refetchMarker = refetchMarkerContext === null ? 'inside-shared-layout' : null;
|
|
const segment = (0, _cache.readOrCreateSegmentCacheEntry)(now, task.fetchStrategy, tree);
|
|
switch(segment.status){
|
|
case _cache.EntryStatus.Empty:
|
|
{
|
|
// This segment is not cached. Add a refetch marker so the server knows
|
|
// to start rendering here.
|
|
// TODO: Instead of a "refetch" marker, we could just omit this subtree's
|
|
// FlightRouterState from the request tree. I think this would probably
|
|
// already work even without any updates to the server. For consistency,
|
|
// though, I'll send the full tree and we'll look into this later as part
|
|
// of a larger redesign of the request protocol.
|
|
// Add the pending cache entry to the result map.
|
|
spawnedEntries.set(tree.requestKey, (0, _cache.upgradeToPendingSegment)(segment, // might not include it in the pending response. If another route is able
|
|
// to issue a per-segment request, we'll do that in the background.
|
|
_types.FetchStrategy.LoadingBoundary));
|
|
if (refetchMarkerContext !== 'refetch') {
|
|
refetchMarker = refetchMarkerContext = 'refetch';
|
|
} else {
|
|
// There's already a parent with a refetch marker, so we don't need
|
|
// to add another one.
|
|
}
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Fulfilled:
|
|
{
|
|
// The segment is already cached.
|
|
const segmentHasLoadingBoundary = (tree.prefetchHints & _approutertypes.PrefetchHint.SegmentHasLoadingBoundary) !== 0;
|
|
if (segmentHasLoadingBoundary) {
|
|
// This segment has a loading boundary, which means the server won't
|
|
// render its children. So there's nothing left to prefetch along this
|
|
// path. We can bail out.
|
|
return (0, _cache.convertRouteTreeToFlightRouterState)(tree);
|
|
}
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Pending:
|
|
{
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
break;
|
|
}
|
|
default:
|
|
segment;
|
|
}
|
|
const requestTreeChildren = {};
|
|
if (tree.slots !== null) {
|
|
for(const parallelRouteKey in tree.slots){
|
|
const childTree = tree.slots[parallelRouteKey];
|
|
requestTreeChildren[parallelRouteKey] = pingPPRDisabledRouteTreeUpToLoadingBoundary(now, task, route, childTree, refetchMarkerContext, spawnedEntries);
|
|
}
|
|
}
|
|
const requestTree = [
|
|
tree.segment,
|
|
requestTreeChildren,
|
|
null,
|
|
refetchMarker
|
|
];
|
|
return requestTree;
|
|
}
|
|
function pingRouteTreeAndIncludeDynamicData(now, task, route, tree, isInsideRefetchingParent, spawnedEntries, fetchStrategy) {
|
|
// The tree we're constructing is the same shape as the tree we're navigating
|
|
// to. But even though this is a "new" tree, some of the individual segments
|
|
// may be cached as a result of other route prefetches.
|
|
//
|
|
// So we need to find the first uncached segment along each path add an
|
|
// explicit "refetch" marker so the server knows where to start rendering.
|
|
// Once the server starts rendering along a path, it keeps rendering the
|
|
// entire subtree.
|
|
const segment = (0, _cache.readOrCreateSegmentCacheEntry)(now, // and we have to use the former here.
|
|
// We can have a task with `FetchStrategy.PPR` where some of its segments are configured to
|
|
// always use runtime prefetching (via `export const prefetch`), and those should check for
|
|
// entries that include search params.
|
|
fetchStrategy, tree);
|
|
let spawnedSegment = null;
|
|
switch(segment.status){
|
|
case _cache.EntryStatus.Empty:
|
|
{
|
|
// This segment is not cached.
|
|
if (fetchStrategy === _types.FetchStrategy.Full) {
|
|
// Check if there's a matching entry in the bfcache. If so, fulfill the
|
|
// segment using the bfcache entry instead of issuing a new request.
|
|
const fulfilled = (0, _cache.attemptToFulfillDynamicSegmentFromBFCache)(now, segment, tree);
|
|
if (fulfilled !== null) {
|
|
break;
|
|
}
|
|
}
|
|
// Include it in the request.
|
|
spawnedSegment = (0, _cache.upgradeToPendingSegment)(segment, fetchStrategy);
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Fulfilled:
|
|
{
|
|
// The segment is already cached.
|
|
if (segment.isPartial && (0, _cache.canNewFetchStrategyProvideMoreContent)(segment.fetchStrategy, fetchStrategy)) {
|
|
// The cached segment contains dynamic holes, and was prefetched using a
|
|
// less specific strategy than the current one. This means we're in one
|
|
// of these cases:
|
|
// - we have a static prefetch, and we're doing a runtime prefetch
|
|
// - we have a static or runtime prefetch, and we're doing a Full
|
|
// prefetch (or a navigation).
|
|
// In either case, we need to include it in the request to get a more
|
|
// specific (or full) version. However, if there's a non-stale bfcache
|
|
// entry from a previous navigation, prefer that over making a new
|
|
// request.
|
|
if (fetchStrategy === _types.FetchStrategy.Full) {
|
|
const fulfilled = (0, _cache.attemptToUpgradeSegmentFromBFCache)(now, tree);
|
|
if (fulfilled !== null) {
|
|
break;
|
|
}
|
|
}
|
|
spawnedSegment = pingFullSegmentRevalidation(now, tree, fetchStrategy);
|
|
}
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Pending:
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
// There's either another prefetch currently in progress, or the previous
|
|
// attempt failed. If the new strategy can provide more content, fetch it again.
|
|
if ((0, _cache.canNewFetchStrategyProvideMoreContent)(segment.fetchStrategy, fetchStrategy)) {
|
|
spawnedSegment = pingFullSegmentRevalidation(now, tree, fetchStrategy);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
segment;
|
|
}
|
|
const requestTreeChildren = {};
|
|
if (tree.slots !== null) {
|
|
for(const parallelRouteKey in tree.slots){
|
|
const childTree = tree.slots[parallelRouteKey];
|
|
requestTreeChildren[parallelRouteKey] = pingRouteTreeAndIncludeDynamicData(now, task, route, childTree, isInsideRefetchingParent || spawnedSegment !== null, spawnedEntries, fetchStrategy);
|
|
}
|
|
}
|
|
if (spawnedSegment !== null) {
|
|
// Add the pending entry to the result map.
|
|
spawnedEntries.set(tree.requestKey, spawnedSegment);
|
|
}
|
|
// Don't bother to add a refetch marker if one is already present in a parent.
|
|
const refetchMarker = !isInsideRefetchingParent && spawnedSegment !== null ? 'refetch' : null;
|
|
const requestTree = [
|
|
tree.segment,
|
|
requestTreeChildren,
|
|
null,
|
|
refetchMarker
|
|
];
|
|
return requestTree;
|
|
}
|
|
function pingRuntimePrefetches(now, task, route, tree, spawnedRuntimePrefetches, spawnedEntries) {
|
|
// Construct a request tree (FlightRouterState) for a runtime prefetch. If
|
|
// a segment is part of the runtime prefetch, the tree is constructed by
|
|
// diffing against what's already in the prefetch cache. Otherwise, we send
|
|
// a regular FlightRouterState with no special markers.
|
|
//
|
|
// See pingRouteTreeAndIncludeDynamicData for details.
|
|
if (spawnedRuntimePrefetches.has(tree.requestKey)) {
|
|
// This segment needs a runtime prefetch.
|
|
return pingRouteTreeAndIncludeDynamicData(now, task, route, tree, false, spawnedEntries, _types.FetchStrategy.PPRRuntime);
|
|
}
|
|
let requestTreeChildren = {};
|
|
const slots = tree.slots;
|
|
if (slots !== null) {
|
|
for(const parallelRouteKey in slots){
|
|
const childTree = slots[parallelRouteKey];
|
|
requestTreeChildren[parallelRouteKey] = pingRuntimePrefetches(now, task, route, childTree, spawnedRuntimePrefetches, spawnedEntries);
|
|
}
|
|
}
|
|
// This segment is not part of the runtime prefetch. Clone the base tree.
|
|
const requestTree = [
|
|
tree.segment,
|
|
requestTreeChildren,
|
|
null,
|
|
null
|
|
];
|
|
return requestTree;
|
|
}
|
|
function pingStaticSegmentData(now, task, route, segment, routeKey, tree) {
|
|
switch(segment.status){
|
|
case _cache.EntryStatus.Empty:
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
// Upgrade to Pending so we know there's already a request in progress
|
|
spawnPrefetchSubtask((0, _cache.fetchSegmentOnCacheMiss)(route, (0, _cache.upgradeToPendingSegment)(segment, _types.FetchStrategy.PPR), routeKey, tree));
|
|
break;
|
|
case _cache.EntryStatus.Pending:
|
|
{
|
|
// There's already a request in progress. Depending on what kind of
|
|
// request it is, we may want to revalidate it.
|
|
switch(segment.fetchStrategy){
|
|
case _types.FetchStrategy.PPR:
|
|
case _types.FetchStrategy.PPRRuntime:
|
|
case _types.FetchStrategy.Full:
|
|
break;
|
|
case _types.FetchStrategy.LoadingBoundary:
|
|
// There's a pending request, but because it's using the old
|
|
// prefetching strategy, we can't be sure if it will be fulfilled by
|
|
// the response — it might be inside the loading boundary. Perform
|
|
// a revalidation, but because it's speculative, wait to do it at
|
|
// background priority.
|
|
if (background(task)) {
|
|
// TODO: Instead of speculatively revalidating, consider including
|
|
// `hasLoading` in the route tree prefetch response.
|
|
pingPPRSegmentRevalidation(now, route, routeKey, tree);
|
|
}
|
|
break;
|
|
default:
|
|
segment.fetchStrategy;
|
|
}
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Rejected:
|
|
{
|
|
// The existing entry in the cache was rejected. Depending on how it
|
|
// was originally fetched, we may or may not want to revalidate it.
|
|
switch(segment.fetchStrategy){
|
|
case _types.FetchStrategy.PPR:
|
|
case _types.FetchStrategy.PPRRuntime:
|
|
case _types.FetchStrategy.Full:
|
|
break;
|
|
case _types.FetchStrategy.LoadingBoundary:
|
|
// There's a rejected entry, but it was fetched using the loading
|
|
// boundary strategy. So the reason it wasn't returned by the server
|
|
// might just be because it was inside a loading boundary. Or because
|
|
// there was a dynamic rewrite. Revalidate it using the per-
|
|
// segment strategy.
|
|
//
|
|
// Because a rejected segment will definitely prevent the segment (and
|
|
// all of its children) from rendering, we perform this revalidation
|
|
// immediately instead of deferring it to a background task.
|
|
pingPPRSegmentRevalidation(now, route, routeKey, tree);
|
|
break;
|
|
default:
|
|
segment.fetchStrategy;
|
|
}
|
|
break;
|
|
}
|
|
case _cache.EntryStatus.Fulfilled:
|
|
break;
|
|
default:
|
|
segment;
|
|
}
|
|
// Segments do not have dependent tasks, so once the prefetch is initiated,
|
|
// there's nothing else for us to do (except write the server data into the
|
|
// entry, which is handled by `fetchSegmentOnCacheMiss`).
|
|
}
|
|
/**
|
|
* Walks the RouteTree (including the head metadata) and collects any segments
|
|
* that are still Empty into a Map, upgrading them to Pending. These entries
|
|
* will be fulfilled by the inlined prefetch response.
|
|
*/ function collectInlinedEntries(now, route) {
|
|
const entries = new Map();
|
|
collectInlinedEntriesImpl(now, route.tree, entries);
|
|
// Also collect the head/metadata entry.
|
|
const headEntry = (0, _cache.readOrCreateSegmentCacheEntry)(now, _types.FetchStrategy.PPR, route.metadata);
|
|
if (headEntry.status === _cache.EntryStatus.Empty) {
|
|
entries.set(route.metadata.requestKey, (0, _cache.upgradeToPendingSegment)(headEntry, _types.FetchStrategy.PPR));
|
|
}
|
|
return entries;
|
|
}
|
|
function collectInlinedEntriesImpl(now, tree, entries) {
|
|
const entry = (0, _cache.readOrCreateSegmentCacheEntry)(now, _types.FetchStrategy.PPR, tree);
|
|
if (entry.status === _cache.EntryStatus.Empty) {
|
|
entries.set(tree.requestKey, (0, _cache.upgradeToPendingSegment)(entry, _types.FetchStrategy.PPR));
|
|
}
|
|
if (tree.slots !== null) {
|
|
for(const parallelRouteKey in tree.slots){
|
|
collectInlinedEntriesImpl(now, tree.slots[parallelRouteKey], entries);
|
|
}
|
|
}
|
|
}
|
|
function pingPPRSegmentRevalidation(now, route, routeKey, tree) {
|
|
const revalidatingSegment = (0, _cache.readOrCreateRevalidatingSegmentEntry)(now, _types.FetchStrategy.PPR, tree);
|
|
switch(revalidatingSegment.status){
|
|
case _cache.EntryStatus.Empty:
|
|
// Spawn a prefetch request. The fetch function handles upserting
|
|
// the entry at the correct fulfilled vary path upon completion.
|
|
spawnPrefetchSubtask((0, _cache.fetchSegmentOnCacheMiss)(route, (0, _cache.upgradeToPendingSegment)(revalidatingSegment, _types.FetchStrategy.PPR), routeKey, tree));
|
|
break;
|
|
case _cache.EntryStatus.Pending:
|
|
break;
|
|
case _cache.EntryStatus.Fulfilled:
|
|
case _cache.EntryStatus.Rejected:
|
|
break;
|
|
default:
|
|
revalidatingSegment;
|
|
}
|
|
}
|
|
function pingFullSegmentRevalidation(now, tree, fetchStrategy) {
|
|
const revalidatingSegment = (0, _cache.readOrCreateRevalidatingSegmentEntry)(now, fetchStrategy, tree);
|
|
if (revalidatingSegment.status === _cache.EntryStatus.Empty) {
|
|
// During a Full/PPRRuntime prefetch, a single dynamic request is made for all the
|
|
// segments that we need. So we don't initiate a request here directly. By
|
|
// returning a pending entry from this function, it signals to the caller
|
|
// that this segment should be included in the request that's sent to
|
|
// the server.
|
|
const pendingSegment = (0, _cache.upgradeToPendingSegment)(revalidatingSegment, fetchStrategy);
|
|
// The upsert is handled by fulfillEntrySpawnedByRuntimePrefetch
|
|
// when the dynamic prefetch response is written into the cache.
|
|
return pendingSegment;
|
|
} else {
|
|
// There's already a revalidation in progress.
|
|
const nonEmptyRevalidatingSegment = revalidatingSegment;
|
|
if ((0, _cache.canNewFetchStrategyProvideMoreContent)(nonEmptyRevalidatingSegment.fetchStrategy, fetchStrategy)) {
|
|
// The existing revalidation was fetched using a less specific strategy.
|
|
// Reset it and start a new revalidation.
|
|
const emptySegment = (0, _cache.overwriteRevalidatingSegmentCacheEntry)(now, fetchStrategy, tree);
|
|
const pendingSegment = (0, _cache.upgradeToPendingSegment)(emptySegment, fetchStrategy);
|
|
// The upsert is handled by fulfillEntrySpawnedByRuntimePrefetch
|
|
// when the dynamic prefetch response is written into the cache.
|
|
return pendingSegment;
|
|
}
|
|
switch(nonEmptyRevalidatingSegment.status){
|
|
case _cache.EntryStatus.Pending:
|
|
// There's already an in-progress prefetch that includes this segment.
|
|
return null;
|
|
case _cache.EntryStatus.Fulfilled:
|
|
case _cache.EntryStatus.Rejected:
|
|
// A previous revalidation attempt finished, but we chose not to replace
|
|
// the existing entry in the cache. Don't try again until or unless the
|
|
// revalidation entry expires.
|
|
return null;
|
|
default:
|
|
nonEmptyRevalidatingSegment;
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
function doesCurrentSegmentMatchCachedSegment(route, currentSegment, cachedSegment) {
|
|
if (cachedSegment === _segment.PAGE_SEGMENT_KEY) {
|
|
// In the FlightRouterState stored by the router, the page segment has the
|
|
// rendered search params appended to the name of the segment. In the
|
|
// prefetch cache, however, this is stored separately. So, when comparing
|
|
// the router's current FlightRouterState to the cached FlightRouterState,
|
|
// we need to make sure we compare both parts of the segment.
|
|
// TODO: This is not modeled clearly. We use the same type,
|
|
// FlightRouterState, for both the CacheNode tree _and_ the prefetch cache
|
|
// _and_ the server response format, when conceptually those are three
|
|
// different things and treated in different ways. We should encode more of
|
|
// this information into the type design so mistakes are less likely.
|
|
return currentSegment === (0, _segment.addSearchParamsIfPageSegment)(_segment.PAGE_SEGMENT_KEY, Object.fromEntries(new URLSearchParams(route.renderedSearch)));
|
|
}
|
|
// Non-page segments are compared using the same function as the server
|
|
return (0, _matchsegments.matchSegment)(cachedSegment, currentSegment);
|
|
}
|
|
// -----------------------------------------------------------------------------
|
|
// The remainder of the module is a MinHeap implementation. Try not to put any
|
|
// logic below here unless it's related to the heap algorithm. We can extract
|
|
// this to a separate module if/when we need multiple kinds of heaps.
|
|
// -----------------------------------------------------------------------------
|
|
function compareQueuePriority(a, b) {
|
|
// Since the queue is a MinHeap, this should return a positive number if b is
|
|
// higher priority than a, and a negative number if a is higher priority
|
|
// than b.
|
|
// `priority` is an integer, where higher numbers are higher priority.
|
|
const priorityDiff = b.priority - a.priority;
|
|
if (priorityDiff !== 0) {
|
|
return priorityDiff;
|
|
}
|
|
// If the priority is the same, check which phase the prefetch is in — is it
|
|
// prefetching the route tree, or the segments? Route trees are prioritized.
|
|
const phaseDiff = b.phase - a.phase;
|
|
if (phaseDiff !== 0) {
|
|
return phaseDiff;
|
|
}
|
|
// Finally, check the insertion order. `sortId` is an incrementing counter
|
|
// assigned to prefetches. We want to process the newest prefetches first.
|
|
return b.sortId - a.sortId;
|
|
}
|
|
function heapPush(heap, node) {
|
|
const index = heap.length;
|
|
heap.push(node);
|
|
node._heapIndex = index;
|
|
heapSiftUp(heap, node, index);
|
|
}
|
|
function heapPeek(heap) {
|
|
return heap.length === 0 ? null : heap[0];
|
|
}
|
|
function heapPop(heap) {
|
|
if (heap.length === 0) {
|
|
return null;
|
|
}
|
|
const first = heap[0];
|
|
first._heapIndex = -1;
|
|
const last = heap.pop();
|
|
if (last !== first) {
|
|
heap[0] = last;
|
|
last._heapIndex = 0;
|
|
heapSiftDown(heap, last, 0);
|
|
}
|
|
return first;
|
|
}
|
|
function heapDelete(heap, node) {
|
|
const index = node._heapIndex;
|
|
if (index !== -1) {
|
|
node._heapIndex = -1;
|
|
if (heap.length !== 0) {
|
|
const last = heap.pop();
|
|
if (last !== node) {
|
|
heap[index] = last;
|
|
last._heapIndex = index;
|
|
heapSiftDown(heap, last, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function heapResift(heap, node) {
|
|
const index = node._heapIndex;
|
|
if (index !== -1) {
|
|
if (index === 0) {
|
|
heapSiftDown(heap, node, 0);
|
|
} else {
|
|
const parentIndex = index - 1 >>> 1;
|
|
const parent = heap[parentIndex];
|
|
if (compareQueuePriority(parent, node) > 0) {
|
|
// The parent is larger. Sift up.
|
|
heapSiftUp(heap, node, index);
|
|
} else {
|
|
// The parent is smaller (or equal). Sift down.
|
|
heapSiftDown(heap, node, index);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
function heapSiftUp(heap, node, i) {
|
|
let index = i;
|
|
while(index > 0){
|
|
const parentIndex = index - 1 >>> 1;
|
|
const parent = heap[parentIndex];
|
|
if (compareQueuePriority(parent, node) > 0) {
|
|
// The parent is larger. Swap positions.
|
|
heap[parentIndex] = node;
|
|
node._heapIndex = parentIndex;
|
|
heap[index] = parent;
|
|
parent._heapIndex = index;
|
|
index = parentIndex;
|
|
} else {
|
|
// The parent is smaller. Exit.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
function heapSiftDown(heap, node, i) {
|
|
let index = i;
|
|
const length = heap.length;
|
|
const halfLength = length >>> 1;
|
|
while(index < halfLength){
|
|
const leftIndex = (index + 1) * 2 - 1;
|
|
const left = heap[leftIndex];
|
|
const rightIndex = leftIndex + 1;
|
|
const right = heap[rightIndex];
|
|
// If the left or right node is smaller, swap with the smaller of those.
|
|
if (compareQueuePriority(left, node) < 0) {
|
|
if (rightIndex < length && compareQueuePriority(right, left) < 0) {
|
|
heap[index] = right;
|
|
right._heapIndex = index;
|
|
heap[rightIndex] = node;
|
|
node._heapIndex = rightIndex;
|
|
index = rightIndex;
|
|
} else {
|
|
heap[index] = left;
|
|
left._heapIndex = index;
|
|
heap[leftIndex] = node;
|
|
node._heapIndex = leftIndex;
|
|
index = leftIndex;
|
|
}
|
|
} else if (rightIndex < length && compareQueuePriority(right, node) < 0) {
|
|
heap[index] = right;
|
|
right._heapIndex = index;
|
|
heap[rightIndex] = node;
|
|
node._heapIndex = rightIndex;
|
|
index = rightIndex;
|
|
} else {
|
|
// Neither child is smaller. Exit.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/components/links.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
IDLE_LINK_STATUS: null,
|
|
PENDING_LINK_STATUS: null,
|
|
getLinkForCurrentNavigation: null,
|
|
mountFormInstance: null,
|
|
mountLinkInstance: null,
|
|
onLinkVisibilityChanged: null,
|
|
onNavigationIntent: null,
|
|
pingVisibleLinks: null,
|
|
setLinkForCurrentNavigation: null,
|
|
unmountLinkForCurrentNavigation: null,
|
|
unmountPrefetchableInstance: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
IDLE_LINK_STATUS: function() {
|
|
return IDLE_LINK_STATUS;
|
|
},
|
|
PENDING_LINK_STATUS: function() {
|
|
return PENDING_LINK_STATUS;
|
|
},
|
|
getLinkForCurrentNavigation: function() {
|
|
return getLinkForCurrentNavigation;
|
|
},
|
|
mountFormInstance: function() {
|
|
return mountFormInstance;
|
|
},
|
|
mountLinkInstance: function() {
|
|
return mountLinkInstance;
|
|
},
|
|
onLinkVisibilityChanged: function() {
|
|
return onLinkVisibilityChanged;
|
|
},
|
|
onNavigationIntent: function() {
|
|
return onNavigationIntent;
|
|
},
|
|
pingVisibleLinks: function() {
|
|
return pingVisibleLinks;
|
|
},
|
|
setLinkForCurrentNavigation: function() {
|
|
return setLinkForCurrentNavigation;
|
|
},
|
|
unmountLinkForCurrentNavigation: function() {
|
|
return unmountLinkForCurrentNavigation;
|
|
},
|
|
unmountPrefetchableInstance: function() {
|
|
return unmountPrefetchableInstance;
|
|
}
|
|
});
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _cachekey = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/cache-key.js [app-ssr] (ecmascript)");
|
|
const _scheduler = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/scheduler.js [app-ssr] (ecmascript)");
|
|
const _react = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react.js [app-ssr] (ecmascript)");
|
|
// Tracks the most recently navigated link instance. When null, indicates
|
|
// the current navigation was not initiated by a link click.
|
|
let linkForMostRecentNavigation = null;
|
|
const PENDING_LINK_STATUS = {
|
|
pending: true
|
|
};
|
|
const IDLE_LINK_STATUS = {
|
|
pending: false
|
|
};
|
|
function setLinkForCurrentNavigation(link) {
|
|
(0, _react.startTransition)(()=>{
|
|
linkForMostRecentNavigation?.setOptimisticLinkStatus(IDLE_LINK_STATUS);
|
|
link?.setOptimisticLinkStatus(PENDING_LINK_STATUS);
|
|
linkForMostRecentNavigation = link;
|
|
});
|
|
}
|
|
function unmountLinkForCurrentNavigation(link) {
|
|
if (linkForMostRecentNavigation === link) {
|
|
linkForMostRecentNavigation = null;
|
|
}
|
|
}
|
|
function getLinkForCurrentNavigation() {
|
|
return linkForMostRecentNavigation;
|
|
}
|
|
// Use a WeakMap to associate a Link instance with its DOM element. This is
|
|
// used by the IntersectionObserver to track the link's visibility.
|
|
const prefetchable = typeof WeakMap === 'function' ? new WeakMap() : new Map();
|
|
// A Set of the currently visible links. We re-prefetch visible links after a
|
|
// cache invalidation, or when the current URL changes. It's a separate data
|
|
// structure from the WeakMap above because only the visible links need to
|
|
// be enumerated.
|
|
const prefetchableAndVisible = new Set();
|
|
// A single IntersectionObserver instance shared by all <Link> components.
|
|
const observer = typeof IntersectionObserver === 'function' ? new IntersectionObserver(handleIntersect, {
|
|
rootMargin: '200px'
|
|
}) : null;
|
|
function observeVisibility(element, instance) {
|
|
const existingInstance = prefetchable.get(element);
|
|
if (existingInstance !== undefined) {
|
|
// This shouldn't happen because each <Link> component should have its own
|
|
// anchor tag instance, but it's defensive coding to avoid a memory leak in
|
|
// case there's a logical error somewhere else.
|
|
unmountPrefetchableInstance(element);
|
|
}
|
|
// Only track prefetchable links that have a valid prefetch URL
|
|
prefetchable.set(element, instance);
|
|
if (observer !== null) {
|
|
observer.observe(element);
|
|
}
|
|
}
|
|
function coercePrefetchableUrl(href) {
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
function mountLinkInstance(element, href, router, fetchStrategy, prefetchEnabled, setOptimisticLinkStatus) {
|
|
if (prefetchEnabled) {
|
|
const prefetchURL = coercePrefetchableUrl(href);
|
|
if (prefetchURL !== null) {
|
|
const instance = {
|
|
router,
|
|
fetchStrategy,
|
|
isVisible: false,
|
|
prefetchTask: null,
|
|
prefetchHref: prefetchURL.href,
|
|
setOptimisticLinkStatus
|
|
};
|
|
// We only observe the link's visibility if it's prefetchable. For
|
|
// example, this excludes links to external URLs.
|
|
observeVisibility(element, instance);
|
|
return instance;
|
|
}
|
|
}
|
|
// If the link is not prefetchable, we still create an instance so we can
|
|
// track its optimistic state (i.e. useLinkStatus).
|
|
const instance = {
|
|
router,
|
|
fetchStrategy,
|
|
isVisible: false,
|
|
prefetchTask: null,
|
|
prefetchHref: null,
|
|
setOptimisticLinkStatus
|
|
};
|
|
return instance;
|
|
}
|
|
function mountFormInstance(element, href, router, fetchStrategy) {
|
|
const prefetchURL = coercePrefetchableUrl(href);
|
|
if (prefetchURL === null) {
|
|
// This href is not prefetchable, so we don't track it.
|
|
// TODO: We currently observe/unobserve a form every time its href changes.
|
|
// For Links, this isn't a big deal because the href doesn't usually change,
|
|
// but for forms it's extremely common. We should optimize this.
|
|
return;
|
|
}
|
|
const instance = {
|
|
router,
|
|
fetchStrategy,
|
|
isVisible: false,
|
|
prefetchTask: null,
|
|
prefetchHref: prefetchURL.href,
|
|
setOptimisticLinkStatus: null
|
|
};
|
|
observeVisibility(element, instance);
|
|
}
|
|
function unmountPrefetchableInstance(element) {
|
|
const instance = prefetchable.get(element);
|
|
if (instance !== undefined) {
|
|
prefetchable.delete(element);
|
|
prefetchableAndVisible.delete(instance);
|
|
const prefetchTask = instance.prefetchTask;
|
|
if (prefetchTask !== null) {
|
|
(0, _scheduler.cancelPrefetchTask)(prefetchTask);
|
|
}
|
|
}
|
|
if (observer !== null) {
|
|
observer.unobserve(element);
|
|
}
|
|
}
|
|
function handleIntersect(entries) {
|
|
for (const entry of entries){
|
|
// Some extremely old browsers or polyfills don't reliably support
|
|
// isIntersecting so we check intersectionRatio instead. (Do we care? Not
|
|
// really. But whatever this is fine.)
|
|
const isVisible = entry.intersectionRatio > 0;
|
|
onLinkVisibilityChanged(entry.target, isVisible);
|
|
}
|
|
}
|
|
function onLinkVisibilityChanged(element, isVisible) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
// Prefetching on viewport is disabled in development for performance
|
|
// reasons, because it requires compiling the target page.
|
|
// TODO: Investigate re-enabling this.
|
|
return;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
const instance = undefined;
|
|
}
|
|
function onNavigationIntent(element, unstable_upgradeToDynamicPrefetch) {
|
|
const instance = prefetchable.get(element);
|
|
if (instance === undefined) {
|
|
return;
|
|
}
|
|
// Prefetch the link on hover/touchstart.
|
|
if (instance !== undefined) {
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
rescheduleLinkPrefetch(instance, _types.PrefetchPriority.Intent);
|
|
}
|
|
}
|
|
function rescheduleLinkPrefetch(instance, priority) {
|
|
// Ensures that app-router-instance is not compiled in the server bundle
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
}
|
|
function pingVisibleLinks(nextUrl, tree) {
|
|
// For each currently visible link, cancel the existing prefetch task (if it
|
|
// exists) and schedule a new one. This is effectively the same as if all the
|
|
// visible links left and then re-entered the viewport.
|
|
//
|
|
// This is called when the Next-Url or the base tree changes, since those
|
|
// may affect the result of a prefetch task. It's also called after a
|
|
// cache invalidation.
|
|
for (const instance of prefetchableAndVisible){
|
|
const task = instance.prefetchTask;
|
|
if (task !== null && !(0, _scheduler.isPrefetchTaskDirty)(task, nextUrl, tree)) {
|
|
continue;
|
|
}
|
|
// Something changed. Cancel the existing prefetch task and schedule a
|
|
// new one.
|
|
if (task !== null) {
|
|
(0, _scheduler.cancelPrefetchTask)(task);
|
|
}
|
|
const cacheKey = (0, _cachekey.createCacheKey)(instance.prefetchHref, nextUrl);
|
|
instance.prefetchTask = (0, _scheduler.schedulePrefetchTask)(cacheKey, tree, instance.fetchStrategy, _types.PrefetchPriority.Default, null);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/path-has-prefix.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "pathHasPrefix", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return pathHasPrefix;
|
|
}
|
|
});
|
|
const _parsepath = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/parse-path.js [app-ssr] (ecmascript)");
|
|
function pathHasPrefix(path, prefix) {
|
|
if (typeof path !== 'string') {
|
|
return false;
|
|
}
|
|
const { pathname } = (0, _parsepath.parsePath)(path);
|
|
return pathname === prefix || pathname.startsWith(prefix + '/');
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/has-base-path.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "hasBasePath", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return hasBasePath;
|
|
}
|
|
});
|
|
const _pathhasprefix = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/path-has-prefix.js [app-ssr] (ecmascript)");
|
|
const basePath = ("TURBOPACK compile-time value", "") || '';
|
|
function hasBasePath(path) {
|
|
return (0, _pathhasprefix.pathHasPrefix)(path, basePath);
|
|
}
|
|
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;
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/router/utils/is-local-url.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "isLocalURL", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return isLocalURL;
|
|
}
|
|
});
|
|
const _utils = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/utils.js [app-ssr] (ecmascript)");
|
|
const _hasbasepath = __turbopack_context__.r("[project]/node_modules/next/dist/client/has-base-path.js [app-ssr] (ecmascript)");
|
|
function isLocalURL(url) {
|
|
// prevent a hydration mismatch on href for url with anchor refs
|
|
if (!(0, _utils.isAbsoluteUrl)(url)) return true;
|
|
try {
|
|
// absolute urls can be local if they are on the same origin
|
|
const locationOrigin = (0, _utils.getLocationOrigin)();
|
|
const resolved = new URL(url, locationOrigin);
|
|
return resolved.origin === locationOrigin && (0, _hasbasepath.hasBasePath)(resolved.pathname);
|
|
} catch (_) {
|
|
return false;
|
|
}
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/shared/lib/utils/error-once.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
Object.defineProperty(exports, "errorOnce", {
|
|
enumerable: true,
|
|
get: function() {
|
|
return errorOnce;
|
|
}
|
|
});
|
|
let errorOnce = (_)=>{};
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
const errors = new Set();
|
|
errorOnce = (msg)=>{
|
|
if (!errors.has(msg)) {
|
|
console.error(msg);
|
|
}
|
|
errors.add(msg);
|
|
};
|
|
}
|
|
}),
|
|
"[project]/node_modules/next/dist/client/app-dir/link.js [app-ssr] (ecmascript)", ((__turbopack_context__, module, exports) => {
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
0 && (module.exports = {
|
|
default: null,
|
|
useLinkStatus: null
|
|
});
|
|
function _export(target, all) {
|
|
for(var name in all)Object.defineProperty(target, name, {
|
|
enumerable: true,
|
|
get: all[name]
|
|
});
|
|
}
|
|
_export(exports, {
|
|
/**
|
|
* A React component that extends the HTML `<a>` element to provide
|
|
* [prefetching](https://nextjs.org/docs/app/building-your-application/routing/linking-and-navigating#2-prefetching)
|
|
* and client-side navigation. This is the primary way to navigate between routes in Next.js.
|
|
*
|
|
* @remarks
|
|
* - Prefetching is only enabled in production.
|
|
*
|
|
* @see https://nextjs.org/docs/app/api-reference/components/link
|
|
*/ default: function() {
|
|
return LinkComponent;
|
|
},
|
|
useLinkStatus: function() {
|
|
return useLinkStatus;
|
|
}
|
|
});
|
|
const _interop_require_wildcard = __turbopack_context__.r("[project]/node_modules/@swc/helpers/cjs/_interop_require_wildcard.cjs [app-ssr] (ecmascript)");
|
|
const _jsxruntime = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react-jsx-runtime.js [app-ssr] (ecmascript)");
|
|
const _react = /*#__PURE__*/ _interop_require_wildcard._(__turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/ssr/react.js [app-ssr] (ecmascript)"));
|
|
const _formaturl = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/format-url.js [app-ssr] (ecmascript)");
|
|
const _approutercontextsharedruntime = __turbopack_context__.r("[project]/node_modules/next/dist/server/route-modules/app-page/vendored/contexts/app-router-context.js [app-ssr] (ecmascript)");
|
|
const _usemergedref = __turbopack_context__.r("[project]/node_modules/next/dist/client/use-merged-ref.js [app-ssr] (ecmascript)");
|
|
const _utils = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/utils.js [app-ssr] (ecmascript)");
|
|
const _addbasepath = __turbopack_context__.r("[project]/node_modules/next/dist/client/add-base-path.js [app-ssr] (ecmascript)");
|
|
const _warnonce = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/utils/warn-once.js [app-ssr] (ecmascript)");
|
|
const _routerreducertypes = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/router-reducer/router-reducer-types.js [app-ssr] (ecmascript)");
|
|
const _links = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/links.js [app-ssr] (ecmascript)");
|
|
const _islocalurl = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/router/utils/is-local-url.js [app-ssr] (ecmascript)");
|
|
const _types = __turbopack_context__.r("[project]/node_modules/next/dist/client/components/segment-cache/types.js [app-ssr] (ecmascript)");
|
|
const _erroronce = __turbopack_context__.r("[project]/node_modules/next/dist/shared/lib/utils/error-once.js [app-ssr] (ecmascript)");
|
|
function isModifiedEvent(event) {
|
|
const eventTarget = event.currentTarget;
|
|
const target = eventTarget.getAttribute('target');
|
|
return target && target !== '_self' || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || // triggers resource download
|
|
event.nativeEvent && event.nativeEvent.which === 2;
|
|
}
|
|
function linkClicked(e, href, linkInstanceRef, replace, scroll, onNavigate, transitionTypes) {
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
}
|
|
function formatStringOrUrl(urlObjOrString) {
|
|
if (typeof urlObjOrString === 'string') {
|
|
return urlObjOrString;
|
|
}
|
|
return (0, _formaturl.formatUrl)(urlObjOrString);
|
|
}
|
|
function LinkComponent(props) {
|
|
const [linkStatus, setOptimisticLinkStatus] = (0, _react.useOptimistic)(_links.IDLE_LINK_STATUS);
|
|
let children;
|
|
const linkInstanceRef = (0, _react.useRef)(null);
|
|
const { href: hrefProp, as: asProp, children: childrenProp, prefetch: prefetchProp = null, passHref, replace, shallow, scroll, onClick, onMouseEnter: onMouseEnterProp, onTouchStart: onTouchStartProp, legacyBehavior = false, onNavigate, transitionTypes, ref: forwardedRef, unstable_dynamicOnHover, ...restProps } = props;
|
|
children = childrenProp;
|
|
if (legacyBehavior && (typeof children === 'string' || typeof children === 'number')) {
|
|
children = /*#__PURE__*/ (0, _jsxruntime.jsx)("a", {
|
|
children: children
|
|
});
|
|
}
|
|
const router = _react.default.useContext(_approutercontextsharedruntime.AppRouterContext);
|
|
const prefetchEnabled = prefetchProp !== false;
|
|
const fetchStrategy = prefetchProp !== false ? getFetchStrategyFromPrefetchProp(prefetchProp) : _types.FetchStrategy.PPR;
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
function createPropError(args) {
|
|
return Object.defineProperty(new Error(`Failed prop type: The prop \`${args.key}\` expects a ${args.expected} in \`<Link>\`, but got \`${args.actual}\` instead.` + (("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : '')), "__NEXT_ERROR_CODE", {
|
|
value: "E319",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
// TypeScript trick for type-guarding:
|
|
const requiredPropsGuard = {
|
|
href: true
|
|
};
|
|
const requiredProps = Object.keys(requiredPropsGuard);
|
|
requiredProps.forEach((key)=>{
|
|
if (key === 'href') {
|
|
if (props[key] == null || typeof props[key] !== 'string' && typeof props[key] !== 'object') {
|
|
throw createPropError({
|
|
key,
|
|
expected: '`string` or `object`',
|
|
actual: props[key] === null ? 'null' : typeof props[key]
|
|
});
|
|
}
|
|
} else {
|
|
// TypeScript trick for type-guarding:
|
|
const _ = key;
|
|
}
|
|
});
|
|
// TypeScript trick for type-guarding:
|
|
const optionalPropsGuard = {
|
|
as: true,
|
|
replace: true,
|
|
scroll: true,
|
|
shallow: true,
|
|
passHref: true,
|
|
prefetch: true,
|
|
unstable_dynamicOnHover: true,
|
|
onClick: true,
|
|
onMouseEnter: true,
|
|
onTouchStart: true,
|
|
legacyBehavior: true,
|
|
onNavigate: true,
|
|
transitionTypes: true
|
|
};
|
|
const optionalProps = Object.keys(optionalPropsGuard);
|
|
optionalProps.forEach((key)=>{
|
|
const valType = typeof props[key];
|
|
if (key === 'as') {
|
|
if (props[key] && valType !== 'string' && valType !== 'object') {
|
|
throw createPropError({
|
|
key,
|
|
expected: '`string` or `object`',
|
|
actual: valType
|
|
});
|
|
}
|
|
} else if (key === 'onClick' || key === 'onMouseEnter' || key === 'onTouchStart' || key === 'onNavigate') {
|
|
if (props[key] && valType !== 'function') {
|
|
throw createPropError({
|
|
key,
|
|
expected: '`function`',
|
|
actual: valType
|
|
});
|
|
}
|
|
} else if (key === 'replace' || key === 'scroll' || key === 'shallow' || key === 'passHref' || key === 'legacyBehavior' || key === 'unstable_dynamicOnHover') {
|
|
if (props[key] != null && valType !== 'boolean') {
|
|
throw createPropError({
|
|
key,
|
|
expected: '`boolean`',
|
|
actual: valType
|
|
});
|
|
}
|
|
} else if (key === 'prefetch') {
|
|
if (props[key] != null && valType !== 'boolean' && props[key] !== 'auto') {
|
|
throw createPropError({
|
|
key,
|
|
expected: '`boolean | "auto"`',
|
|
actual: valType
|
|
});
|
|
}
|
|
} else if (key === 'transitionTypes') {
|
|
if (props[key] != null && !Array.isArray(props[key])) {
|
|
throw createPropError({
|
|
key,
|
|
expected: '`string[]`',
|
|
actual: valType
|
|
});
|
|
}
|
|
} else {
|
|
// TypeScript trick for type-guarding:
|
|
const _ = key;
|
|
}
|
|
});
|
|
}
|
|
const resolvedHref = asProp || hrefProp;
|
|
const formattedHref = formatStringOrUrl(resolvedHref);
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (props.locale) {
|
|
(0, _warnonce.warnOnce)('The `locale` prop is not supported in `next/link` while using the `app` router. Read more about app router internalization: https://nextjs.org/docs/app/building-your-application/routing/internationalization');
|
|
}
|
|
if (!asProp) {
|
|
let href;
|
|
if (typeof resolvedHref === 'string') {
|
|
href = resolvedHref;
|
|
} else if (typeof resolvedHref === 'object' && typeof resolvedHref.pathname === 'string') {
|
|
href = resolvedHref.pathname;
|
|
}
|
|
if (href) {
|
|
const hasDynamicSegment = href.split('/').some((segment)=>segment.startsWith('[') && segment.endsWith(']'));
|
|
if (hasDynamicSegment) {
|
|
throw Object.defineProperty(new Error(`Dynamic href \`${href}\` found in <Link> while using the \`/app\` router, this is not supported. Read more: https://nextjs.org/docs/messages/app-dir-dynamic-href`), "__NEXT_ERROR_CODE", {
|
|
value: "E267",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// This will return the first child, if multiple are provided it will throw an error
|
|
let child;
|
|
if (legacyBehavior) {
|
|
if (children?.$$typeof === Symbol.for('react.lazy')) {
|
|
throw Object.defineProperty(new Error(`\`<Link legacyBehavior>\` received a direct child that is either a Server Component, or JSX that was loaded with React.lazy(). This is not supported. Either remove legacyBehavior, or make the direct child a Client Component that renders the Link's \`<a>\` tag.`), "__NEXT_ERROR_CODE", {
|
|
value: "E863",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (onClick) {
|
|
console.warn(`"onClick" was passed to <Link> with \`href\` of \`${formattedHref}\` but "legacyBehavior" was set. The legacy behavior requires onClick be set on the child of next/link`);
|
|
}
|
|
if (onMouseEnterProp) {
|
|
console.warn(`"onMouseEnter" was passed to <Link> with \`href\` of \`${formattedHref}\` but "legacyBehavior" was set. The legacy behavior requires onMouseEnter be set on the child of next/link`);
|
|
}
|
|
try {
|
|
child = _react.default.Children.only(children);
|
|
} catch (err) {
|
|
if (!children) {
|
|
throw Object.defineProperty(new Error(`No children were passed to <Link> with \`href\` of \`${formattedHref}\` but one child is required https://nextjs.org/docs/messages/link-no-children`), "__NEXT_ERROR_CODE", {
|
|
value: "E320",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
throw Object.defineProperty(new Error(`Multiple children were passed to <Link> with \`href\` of \`${formattedHref}\` but only one child is supported https://nextjs.org/docs/messages/link-multiple-children` + (("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : '')), "__NEXT_ERROR_CODE", {
|
|
value: "E266",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
} else //TURBOPACK unreachable
|
|
;
|
|
} else {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (children?.type === 'a') {
|
|
throw Object.defineProperty(new Error('Invalid <Link> with <a> child. Please remove <a> or use <Link legacyBehavior>.\nLearn more: https://nextjs.org/docs/messages/invalid-new-link-with-extra-anchor'), "__NEXT_ERROR_CODE", {
|
|
value: "E209",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
const childRef = legacyBehavior ? child && typeof child === 'object' && child.ref : forwardedRef;
|
|
// Use a callback ref to attach an IntersectionObserver to the anchor tag on
|
|
// mount. In the future we will also use this to keep track of all the
|
|
// currently mounted <Link> instances, e.g. so we can re-prefetch them after
|
|
// a revalidation or refresh.
|
|
const observeLinkVisibilityOnMount = _react.default.useCallback((element)=>{
|
|
if (router !== null) {
|
|
linkInstanceRef.current = (0, _links.mountLinkInstance)(element, formattedHref, router, fetchStrategy, prefetchEnabled, setOptimisticLinkStatus);
|
|
}
|
|
return ()=>{
|
|
if (linkInstanceRef.current) {
|
|
(0, _links.unmountLinkForCurrentNavigation)(linkInstanceRef.current);
|
|
linkInstanceRef.current = null;
|
|
}
|
|
(0, _links.unmountPrefetchableInstance)(element);
|
|
};
|
|
}, [
|
|
prefetchEnabled,
|
|
formattedHref,
|
|
router,
|
|
fetchStrategy,
|
|
setOptimisticLinkStatus
|
|
]);
|
|
const mergedRef = (0, _usemergedref.useMergedRef)(observeLinkVisibilityOnMount, childRef);
|
|
const childProps = {
|
|
ref: mergedRef,
|
|
onClick (e) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
if (!e) {
|
|
throw Object.defineProperty(new Error(`Component rendered inside next/link has to pass click event to "onClick" prop.`), "__NEXT_ERROR_CODE", {
|
|
value: "E312",
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
}
|
|
}
|
|
if (!legacyBehavior && typeof onClick === 'function') {
|
|
onClick(e);
|
|
}
|
|
if (legacyBehavior && child.props && typeof child.props.onClick === 'function') {
|
|
child.props.onClick(e);
|
|
}
|
|
if (!router) {
|
|
return;
|
|
}
|
|
if (e.defaultPrevented) {
|
|
return;
|
|
}
|
|
linkClicked(e, formattedHref, linkInstanceRef, replace, scroll, onNavigate, transitionTypes);
|
|
},
|
|
onMouseEnter (e) {
|
|
if (!legacyBehavior && typeof onMouseEnterProp === 'function') {
|
|
onMouseEnterProp(e);
|
|
}
|
|
if (legacyBehavior && child.props && typeof child.props.onMouseEnter === 'function') {
|
|
child.props.onMouseEnter(e);
|
|
}
|
|
if (!router) {
|
|
return;
|
|
}
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
return;
|
|
}
|
|
//TURBOPACK unreachable
|
|
;
|
|
const upgradeToDynamicPrefetch = undefined;
|
|
},
|
|
onTouchStart: ("TURBOPACK compile-time falsy", 0) ? "TURBOPACK unreachable" : function onTouchStart(e) {
|
|
if (!legacyBehavior && typeof onTouchStartProp === 'function') {
|
|
onTouchStartProp(e);
|
|
}
|
|
if (legacyBehavior && child.props && typeof child.props.onTouchStart === 'function') {
|
|
child.props.onTouchStart(e);
|
|
}
|
|
if (!router) {
|
|
return;
|
|
}
|
|
if (!prefetchEnabled) {
|
|
return;
|
|
}
|
|
const upgradeToDynamicPrefetch = unstable_dynamicOnHover === true;
|
|
(0, _links.onNavigationIntent)(e.currentTarget, upgradeToDynamicPrefetch);
|
|
}
|
|
};
|
|
// If the url is absolute, we can bypass the logic to prepend the basePath.
|
|
if ((0, _utils.isAbsoluteUrl)(formattedHref)) {
|
|
childProps.href = formattedHref;
|
|
} else if (!legacyBehavior || passHref || child.type === 'a' && !('href' in child.props)) {
|
|
childProps.href = (0, _addbasepath.addBasePath)(formattedHref);
|
|
}
|
|
let link;
|
|
if (legacyBehavior) {
|
|
if ("TURBOPACK compile-time truthy", 1) {
|
|
(0, _erroronce.errorOnce)('`legacyBehavior` is deprecated and will be removed in a future ' + 'release. A codemod is available to upgrade your components:\n\n' + 'npx @next/codemod@latest new-link .\n\n' + 'Learn more: https://nextjs.org/docs/app/building-your-application/upgrading/codemods#remove-a-tags-from-link-components');
|
|
}
|
|
link = /*#__PURE__*/ _react.default.cloneElement(child, childProps);
|
|
} else {
|
|
link = /*#__PURE__*/ (0, _jsxruntime.jsx)("a", {
|
|
...restProps,
|
|
...childProps,
|
|
children: children
|
|
});
|
|
}
|
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(LinkStatusContext.Provider, {
|
|
value: linkStatus,
|
|
children: link
|
|
});
|
|
}
|
|
const LinkStatusContext = /*#__PURE__*/ (0, _react.createContext)(_links.IDLE_LINK_STATUS);
|
|
const useLinkStatus = ()=>{
|
|
return (0, _react.useContext)(LinkStatusContext);
|
|
};
|
|
function getFetchStrategyFromPrefetchProp(prefetchProp) {
|
|
if ("TURBOPACK compile-time falsy", 0) //TURBOPACK unreachable
|
|
;
|
|
else {
|
|
return prefetchProp === null || prefetchProp === 'auto' ? _types.FetchStrategy.PPR : // (although invalid values should've been filtered out by prop validation in dev)
|
|
_types.FetchStrategy.Full;
|
|
}
|
|
}
|
|
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=node_modules_0c7w9ve._.js.map
|