@tanstack/router-core 1.171.15 → 1.171.16-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +3 -12
- package/dist/cjs/await-signal.cjs +21 -0
- package/dist/cjs/await-signal.cjs.map +1 -0
- package/dist/cjs/await-signal.d.cts +1 -0
- package/dist/cjs/index.cjs +3 -1
- package/dist/cjs/index.d.cts +3 -2
- package/dist/cjs/isServer/client.cjs +2 -0
- package/dist/cjs/isServer/client.cjs.map +1 -1
- package/dist/cjs/isServer/client.d.cts +1 -0
- package/dist/cjs/isServer/development.cjs +2 -0
- package/dist/cjs/isServer/development.cjs.map +1 -1
- package/dist/cjs/isServer/development.d.cts +1 -0
- package/dist/cjs/isServer/server.cjs +2 -0
- package/dist/cjs/isServer/server.cjs.map +1 -1
- package/dist/cjs/isServer/server.d.cts +1 -0
- package/dist/cjs/load-client.cjs +1470 -0
- package/dist/cjs/load-client.cjs.map +1 -0
- package/dist/cjs/load-client.d.cts +147 -0
- package/dist/cjs/load-server.cjs +565 -0
- package/dist/cjs/load-server.cjs.map +1 -0
- package/dist/cjs/load-server.d.cts +16 -0
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/redirect.d.cts +0 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/router.cjs +187 -442
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +61 -51
- package/dist/cjs/ssr/client.cjs +2 -2
- package/dist/cjs/ssr/createRequestHandler.cjs +60 -9
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.d.cts +1 -0
- package/dist/cjs/ssr/handlerCallback.cjs +49 -4
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
- package/dist/cjs/ssr/handlerCallback.d.cts +3 -0
- package/dist/cjs/ssr/server.cjs +4 -0
- package/dist/cjs/ssr/server.d.cts +2 -2
- package/dist/cjs/ssr/ssr-client.d.cts +1 -10
- package/dist/cjs/ssr/ssr-match-id.cjs +2 -2
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +6 -6
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +27 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +2 -0
- package/dist/cjs/ssr/types.d.cts +0 -1
- package/dist/cjs/stores.cjs +30 -104
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/stores.d.cts +7 -32
- package/dist/cjs/utils.cjs +0 -4
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.d.ts +3 -12
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/await-signal.d.ts +1 -0
- package/dist/esm/await-signal.js +21 -0
- package/dist/esm/await-signal.js.map +1 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/isServer/client.d.ts +1 -0
- package/dist/esm/isServer/client.js +2 -1
- package/dist/esm/isServer/client.js.map +1 -1
- package/dist/esm/isServer/development.d.ts +1 -0
- package/dist/esm/isServer/development.js +2 -1
- package/dist/esm/isServer/development.js.map +1 -1
- package/dist/esm/isServer/server.d.ts +1 -0
- package/dist/esm/isServer/server.js +2 -1
- package/dist/esm/isServer/server.js.map +1 -1
- package/dist/esm/load-client.d.ts +147 -0
- package/dist/esm/load-client.js +1462 -0
- package/dist/esm/load-client.js.map +1 -0
- package/dist/esm/load-server.d.ts +16 -0
- package/dist/esm/load-server.js +565 -0
- package/dist/esm/load-server.js.map +1 -0
- package/dist/esm/redirect.d.ts +0 -1
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +61 -51
- package/dist/esm/router.js +188 -444
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/ssr/client.js +1 -1
- package/dist/esm/ssr/createRequestHandler.d.ts +1 -0
- package/dist/esm/ssr/createRequestHandler.js +61 -11
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/handlerCallback.d.ts +3 -0
- package/dist/esm/ssr/handlerCallback.js +47 -5
- package/dist/esm/ssr/handlerCallback.js.map +1 -1
- package/dist/esm/ssr/server.d.ts +2 -2
- package/dist/esm/ssr/server.js +3 -3
- package/dist/esm/ssr/ssr-client.d.ts +1 -10
- package/dist/esm/ssr/ssr-match-id.js +2 -2
- package/dist/esm/ssr/ssr-match-id.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js +6 -6
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +2 -0
- package/dist/esm/ssr/transformStreamWithRouter.js +27 -1
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/ssr/types.d.ts +0 -1
- package/dist/esm/stores.d.ts +7 -32
- package/dist/esm/stores.js +30 -104
- package/dist/esm/stores.js.map +1 -1
- package/dist/esm/utils.js +1 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/Matches.ts +6 -21
- package/src/await-signal.ts +27 -0
- package/src/index.ts +1 -3
- package/src/isServer/client.ts +1 -0
- package/src/isServer/development.ts +1 -0
- package/src/isServer/server.ts +1 -0
- package/src/load-client.ts +2701 -0
- package/src/load-server.ts +849 -0
- package/src/redirect.ts +0 -1
- package/src/route.ts +2 -11
- package/src/router.ts +379 -769
- package/src/ssr/createRequestHandler.ts +109 -16
- package/src/ssr/handlerCallback.ts +76 -10
- package/src/ssr/server.ts +4 -1
- package/src/ssr/ssr-client.ts +1 -310
- package/src/ssr/ssr-match-id.ts +11 -2
- package/src/ssr/ssr-server.ts +9 -8
- package/src/ssr/transformStreamWithRouter.ts +37 -1
- package/src/ssr/types.ts +0 -1
- package/src/stores.ts +46 -208
- package/dist/cjs/load-matches.cjs +0 -658
- package/dist/cjs/load-matches.cjs.map +0 -1
- package/dist/cjs/load-matches.d.cts +0 -18
- package/dist/cjs/ssr/ssr-client.cjs +0 -183
- package/dist/cjs/ssr/ssr-client.cjs.map +0 -1
- package/dist/esm/load-matches.d.ts +0 -18
- package/dist/esm/load-matches.js +0 -656
- package/dist/esm/load-matches.js.map +0 -1
- package/dist/esm/ssr/ssr-client.js +0 -183
- package/dist/esm/ssr/ssr-client.js.map +0 -1
- package/src/load-matches.ts +0 -1278
|
@@ -0,0 +1,1462 @@
|
|
|
1
|
+
import { deepEqual } from "./utils.js";
|
|
2
|
+
import { isNotFound } from "./not-found.js";
|
|
3
|
+
import { isRedirect } from "./redirect.js";
|
|
4
|
+
import { hydrateSsrMatchId } from "./ssr/ssr-match-id.js";
|
|
5
|
+
import { _getUserHistoryState, getLocationChangeInfo, runRouteLifecycle } from "./router.js";
|
|
6
|
+
//#region src/load-client.ts
|
|
7
|
+
function replaceRouteChunk(route, lazyFn) {
|
|
8
|
+
route.lazyFn = lazyFn ?? route.lazyFn;
|
|
9
|
+
route._lazy = void 0;
|
|
10
|
+
}
|
|
11
|
+
function preloadComponent(route, type) {
|
|
12
|
+
return route.options[type]?.preload?.();
|
|
13
|
+
}
|
|
14
|
+
function loadComponents(route) {
|
|
15
|
+
const component = preloadComponent(route, "component");
|
|
16
|
+
const pending = preloadComponent(route, "pendingComponent");
|
|
17
|
+
if (component && pending) return Promise.all([component, pending]).then(() => {});
|
|
18
|
+
return component ?? pending;
|
|
19
|
+
}
|
|
20
|
+
function loadRouteChunk(route, componentType) {
|
|
21
|
+
const afterLazy = () => componentType === false ? void 0 : componentType ? preloadComponent(route, componentType) : loadComponents(route);
|
|
22
|
+
const current = route._lazy;
|
|
23
|
+
if (current) return current === true ? afterLazy() : current.then(afterLazy);
|
|
24
|
+
if (!route.lazyFn) return afterLazy();
|
|
25
|
+
const promise = route.lazyFn().then((lazyRoute) => {
|
|
26
|
+
if (process.env.NODE_ENV === "production" || route._lazy === promise) {
|
|
27
|
+
const { id: _id, ...options } = lazyRoute.options;
|
|
28
|
+
Object.assign(route.options, options);
|
|
29
|
+
route._lazy = true;
|
|
30
|
+
}
|
|
31
|
+
}, (error) => {
|
|
32
|
+
if (process.env.NODE_ENV === "production" || route._lazy === promise) route._lazy = void 0;
|
|
33
|
+
throw error;
|
|
34
|
+
});
|
|
35
|
+
route._lazy = promise;
|
|
36
|
+
return promise.then(afterLazy);
|
|
37
|
+
}
|
|
38
|
+
/** Return the structural lane through the first terminal render boundary. */
|
|
39
|
+
function _getRenderedMatches(matches) {
|
|
40
|
+
const end = matches.findIndex((match) => match.status !== "success" || match._notFound) + 1;
|
|
41
|
+
return end && end < matches.length ? matches.slice(0, end) : matches;
|
|
42
|
+
}
|
|
43
|
+
/** Return the lane whose document assets belong to the current presentation. */
|
|
44
|
+
function _getAssetMatches(matches) {
|
|
45
|
+
let end = matches.length;
|
|
46
|
+
for (let index = 0; index < end; index++) {
|
|
47
|
+
const match = matches[index];
|
|
48
|
+
if (match._assetEnd !== void 0) {
|
|
49
|
+
end = Math.min(end, Math.max(index + 1, match._assetEnd));
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (match.status !== "success" || match._notFound) {
|
|
53
|
+
end = index + 1;
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return end < matches.length ? matches.slice(0, end) : matches;
|
|
58
|
+
}
|
|
59
|
+
const SUCCESS = 0;
|
|
60
|
+
const ERROR = 1;
|
|
61
|
+
const NOT_FOUND = 2;
|
|
62
|
+
const REDIRECTED = 3;
|
|
63
|
+
const CANCELED = 4;
|
|
64
|
+
function isControl(result) {
|
|
65
|
+
return typeof result[0] === "number";
|
|
66
|
+
}
|
|
67
|
+
function waitFor(value, signal) {
|
|
68
|
+
if (signal.aborted) return Promise.race([Promise.reject(signal), value]);
|
|
69
|
+
return new Promise((resolve, reject) => {
|
|
70
|
+
const abort = () => reject(signal);
|
|
71
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
72
|
+
Promise.resolve(value).then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
function getRoute(router, match) {
|
|
76
|
+
return router.routesById[match.routeId];
|
|
77
|
+
}
|
|
78
|
+
function normalize(value, rejected, routeId) {
|
|
79
|
+
if (isRedirect(value)) return [REDIRECTED, value];
|
|
80
|
+
if (isNotFound(value)) {
|
|
81
|
+
value.routeId ||= routeId;
|
|
82
|
+
return [NOT_FOUND, value];
|
|
83
|
+
}
|
|
84
|
+
if (rejected && typeof value?.then === "function") value = new Error("A Promise was thrown", { cause: value });
|
|
85
|
+
return rejected ? [ERROR, value] : [SUCCESS, value];
|
|
86
|
+
}
|
|
87
|
+
function normalizeError(route, cause) {
|
|
88
|
+
let outcome = normalize(cause, true, route.id);
|
|
89
|
+
if (outcome[0] !== ERROR) return outcome;
|
|
90
|
+
try {
|
|
91
|
+
route.options.onError?.(outcome[1]);
|
|
92
|
+
} catch (onErrorCause) {
|
|
93
|
+
outcome = normalize(onErrorCause, true, route.id);
|
|
94
|
+
}
|
|
95
|
+
return outcome;
|
|
96
|
+
}
|
|
97
|
+
function normalizeLaneError(route, cause, options) {
|
|
98
|
+
if (options[0].signal.aborted || !options[2]()) {
|
|
99
|
+
options[0].abort();
|
|
100
|
+
return [CANCELED];
|
|
101
|
+
}
|
|
102
|
+
return normalizeError(route, cause);
|
|
103
|
+
}
|
|
104
|
+
function navigateFrom(router, location) {
|
|
105
|
+
return (opts) => router.navigate({
|
|
106
|
+
...opts,
|
|
107
|
+
_fromLocation: location
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
async function contextualize(router, lane, options, end) {
|
|
111
|
+
const [location, matches] = lane;
|
|
112
|
+
const signal = options[0].signal;
|
|
113
|
+
const preload = !!options[4];
|
|
114
|
+
for (let index = options[7] ?? 0; index < end; index++) {
|
|
115
|
+
const match = matches[index];
|
|
116
|
+
const route = getRoute(router, match);
|
|
117
|
+
match.abortController = options[0];
|
|
118
|
+
const parentContext = matches[index - 1]?.context ?? router.options.context ?? {};
|
|
119
|
+
const common = {
|
|
120
|
+
params: match.params,
|
|
121
|
+
location,
|
|
122
|
+
navigate: navigateFrom(router, location),
|
|
123
|
+
buildLocation: router.buildLocation,
|
|
124
|
+
cause: preload ? "preload" : match.cause,
|
|
125
|
+
abortController: options[0],
|
|
126
|
+
preload,
|
|
127
|
+
matches,
|
|
128
|
+
routeId: route.id
|
|
129
|
+
};
|
|
130
|
+
let context = parentContext;
|
|
131
|
+
try {
|
|
132
|
+
let routeContext = match._ctx;
|
|
133
|
+
if (!routeContext && route.options.context) routeContext = match._ctx = route.options.context({
|
|
134
|
+
...common,
|
|
135
|
+
deps: match.loaderDeps,
|
|
136
|
+
context: parentContext
|
|
137
|
+
}) || {};
|
|
138
|
+
context = {
|
|
139
|
+
...parentContext,
|
|
140
|
+
...routeContext
|
|
141
|
+
};
|
|
142
|
+
match.context = context;
|
|
143
|
+
} catch (cause) {
|
|
144
|
+
releaseFlight(router, match);
|
|
145
|
+
return [index, normalizeLaneError(route, cause, options)];
|
|
146
|
+
}
|
|
147
|
+
if (signal.aborted || !options[2]()) {
|
|
148
|
+
options[0].abort();
|
|
149
|
+
return [index, [CANCELED]];
|
|
150
|
+
}
|
|
151
|
+
const validationError = match.paramsError ?? match.searchError;
|
|
152
|
+
if (validationError !== void 0) {
|
|
153
|
+
releaseFlight(router, match);
|
|
154
|
+
return [index, normalizeLaneError(route, validationError, options)];
|
|
155
|
+
}
|
|
156
|
+
const beforeLoad = route.options.beforeLoad;
|
|
157
|
+
if (!beforeLoad) continue;
|
|
158
|
+
const beforeLoadContext = {
|
|
159
|
+
...common,
|
|
160
|
+
search: match.search,
|
|
161
|
+
context,
|
|
162
|
+
...router.options.additionalContext
|
|
163
|
+
};
|
|
164
|
+
const previousStatus = match.status;
|
|
165
|
+
if (previousStatus === "success") match.status = "pending";
|
|
166
|
+
options[8]?.();
|
|
167
|
+
try {
|
|
168
|
+
setFetching(router, match, "beforeLoad", options[0]);
|
|
169
|
+
const result = await waitFor(beforeLoad(beforeLoadContext), signal);
|
|
170
|
+
if (!options[2]()) {
|
|
171
|
+
options[0].abort();
|
|
172
|
+
return [index, [CANCELED]];
|
|
173
|
+
}
|
|
174
|
+
const outcome = normalize(result, false, route.id);
|
|
175
|
+
if (outcome[0] !== SUCCESS) {
|
|
176
|
+
releaseFlight(router, match);
|
|
177
|
+
return [index, outcome];
|
|
178
|
+
}
|
|
179
|
+
match.context = {
|
|
180
|
+
...context,
|
|
181
|
+
...result
|
|
182
|
+
};
|
|
183
|
+
} catch (cause) {
|
|
184
|
+
releaseFlight(router, match);
|
|
185
|
+
return [index, normalizeLaneError(route, cause, options)];
|
|
186
|
+
} finally {
|
|
187
|
+
if (previousStatus === "success" && match.status === "pending") match.status = "success";
|
|
188
|
+
setFetching(router, match, false, options[0]);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
function releaseOwnedFlight(router, id, flight) {
|
|
193
|
+
if (!flight || --flight[2]) return;
|
|
194
|
+
if (router._flights?.get(id) === flight) router._flights.delete(id);
|
|
195
|
+
return flight[1];
|
|
196
|
+
}
|
|
197
|
+
function releaseFlight(router, match) {
|
|
198
|
+
const flight = match._flight;
|
|
199
|
+
match._flight = void 0;
|
|
200
|
+
releaseOwnedFlight(router, match.id, flight)?.abort();
|
|
201
|
+
}
|
|
202
|
+
function laneInputs(router, location) {
|
|
203
|
+
const masked = location.maskedLocation;
|
|
204
|
+
return [
|
|
205
|
+
router.routeTree,
|
|
206
|
+
router.options.context ?? {},
|
|
207
|
+
router.options.additionalContext,
|
|
208
|
+
_getUserHistoryState(location.state),
|
|
209
|
+
location.search,
|
|
210
|
+
masked && [
|
|
211
|
+
masked.href,
|
|
212
|
+
_getUserHistoryState(masked.state),
|
|
213
|
+
masked.search,
|
|
214
|
+
masked.unmaskOnReload
|
|
215
|
+
]
|
|
216
|
+
];
|
|
217
|
+
}
|
|
218
|
+
function samePreloadLane(preload, router, location, redirects) {
|
|
219
|
+
return preload[3] === router._committed && preload[5] === redirects && deepEqual(preload[4], laneInputs(router, location)) && !preload[0].some((match) => getRoute(router, match).options.preload === false);
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Not passing in a `next` ownership recipient
|
|
223
|
+
* is equivalent to discarding the match resources
|
|
224
|
+
*/
|
|
225
|
+
function transferMatchResources(router, previous, next) {
|
|
226
|
+
const abort = [];
|
|
227
|
+
for (const match of previous) if (!next?.includes(match)) {
|
|
228
|
+
const flight = match._flight;
|
|
229
|
+
match._flight = void 0;
|
|
230
|
+
const controller = releaseOwnedFlight(router, match.id, flight);
|
|
231
|
+
if (controller) abort.push(controller);
|
|
232
|
+
}
|
|
233
|
+
for (const controller of abort) controller.abort();
|
|
234
|
+
}
|
|
235
|
+
function discardPreload(router, preload) {
|
|
236
|
+
preload[1].abort();
|
|
237
|
+
transferMatchResources(router, preload[0]);
|
|
238
|
+
}
|
|
239
|
+
function acquireMatchResources(matches) {
|
|
240
|
+
for (const match of matches) {
|
|
241
|
+
const flight = match._flight;
|
|
242
|
+
if (flight) flight[2]++;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function setFetching(router, match, value, owner) {
|
|
246
|
+
match.isFetching = value;
|
|
247
|
+
if (owner && router._tx?.[0] !== owner) return;
|
|
248
|
+
const store = router.stores.byRoute.get(match.routeId);
|
|
249
|
+
const presented = store?.get();
|
|
250
|
+
if (presented?.id === match.id) store.set({
|
|
251
|
+
...presented,
|
|
252
|
+
isFetching: value
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
function getLoaderContext(router, lane, match, route, controller, parentMatchPromise, preload) {
|
|
256
|
+
const location = lane[0];
|
|
257
|
+
return {
|
|
258
|
+
params: match.params,
|
|
259
|
+
location,
|
|
260
|
+
navigate: navigateFrom(router, location),
|
|
261
|
+
cause: preload ? "preload" : match.cause,
|
|
262
|
+
abortController: controller,
|
|
263
|
+
preload,
|
|
264
|
+
deps: match.loaderDeps,
|
|
265
|
+
parentMatchPromise,
|
|
266
|
+
context: match.context,
|
|
267
|
+
route,
|
|
268
|
+
...router.options.additionalContext
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
async function loadResource(router, lane, match, route, loader, parentMatchPromise, preload, owner) {
|
|
272
|
+
const signal = owner.signal;
|
|
273
|
+
if (signal.aborted) return [CANCELED];
|
|
274
|
+
if (!loader) return [SUCCESS, void 0];
|
|
275
|
+
let flight = match._flight;
|
|
276
|
+
let joined = !!flight;
|
|
277
|
+
setFetching(router, match, "loader", owner);
|
|
278
|
+
try {
|
|
279
|
+
for (;;) {
|
|
280
|
+
if (!flight) {
|
|
281
|
+
const controller = new AbortController();
|
|
282
|
+
flight = [
|
|
283
|
+
Promise.resolve().then(() => loader(getLoaderContext(router, lane, match, route, controller, parentMatchPromise, preload))).then((value) => normalize(value, false, route.id), (cause) => normalize(cause, true, route.id)).then((result) => {
|
|
284
|
+
return result[0] === ERROR && match._flight === flight ? normalizeError(route, result[1]) : result;
|
|
285
|
+
}),
|
|
286
|
+
controller,
|
|
287
|
+
1
|
|
288
|
+
];
|
|
289
|
+
(router._flights ??= /* @__PURE__ */ new Map()).set(match.id, flight);
|
|
290
|
+
}
|
|
291
|
+
match._flight = flight;
|
|
292
|
+
match.abortController = flight[1];
|
|
293
|
+
try {
|
|
294
|
+
const outcome = await waitFor(flight[0], signal);
|
|
295
|
+
if (!joined || outcome[0] === SUCCESS || outcome[0] === REDIRECTED) return outcome;
|
|
296
|
+
} catch (cause) {
|
|
297
|
+
if (cause === signal) {
|
|
298
|
+
releaseFlight(router, match);
|
|
299
|
+
return [CANCELED];
|
|
300
|
+
}
|
|
301
|
+
throw cause;
|
|
302
|
+
}
|
|
303
|
+
releaseFlight(router, match);
|
|
304
|
+
if (signal.aborted) return [CANCELED];
|
|
305
|
+
flight = void 0;
|
|
306
|
+
joined = false;
|
|
307
|
+
}
|
|
308
|
+
} finally {
|
|
309
|
+
setFetching(router, match, false, owner);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
function settleInto(match, result, preload) {
|
|
313
|
+
if (result[0] === SUCCESS) {
|
|
314
|
+
match.loaderData = result[1];
|
|
315
|
+
match.error = void 0;
|
|
316
|
+
match.status = "success";
|
|
317
|
+
match.invalid = false;
|
|
318
|
+
match.updatedAt = Date.now();
|
|
319
|
+
match.preload = preload;
|
|
320
|
+
} else if (result[0] !== REDIRECTED) {
|
|
321
|
+
match.status = "success";
|
|
322
|
+
match.error = void 0;
|
|
323
|
+
match.invalid = true;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function cacheLoaderMatch(router, match, planned) {
|
|
327
|
+
const current = router._cache.get(match.id);
|
|
328
|
+
if (current !== planned || router._committed.some((candidate) => candidate.id === match.id && candidate._flight === match._flight)) return;
|
|
329
|
+
const cached = {
|
|
330
|
+
...match,
|
|
331
|
+
_notFound: void 0,
|
|
332
|
+
context: {}
|
|
333
|
+
};
|
|
334
|
+
if (cached._flight) cached._flight[2]++;
|
|
335
|
+
router._cache.set(match.id, cached);
|
|
336
|
+
if (current) releaseFlight(router, current);
|
|
337
|
+
}
|
|
338
|
+
function getParentSnapshot(match, outcome) {
|
|
339
|
+
if (outcome[0] === ERROR || outcome[0] === NOT_FOUND) return {
|
|
340
|
+
...match,
|
|
341
|
+
status: outcome[0] === ERROR ? "error" : "notFound",
|
|
342
|
+
error: outcome[1],
|
|
343
|
+
_flight: void 0
|
|
344
|
+
};
|
|
345
|
+
return match;
|
|
346
|
+
}
|
|
347
|
+
function createLoaderTask(router, lane, index, tasks, semanticParent, options) {
|
|
348
|
+
const match = lane[1][index];
|
|
349
|
+
const route = getRoute(router, match);
|
|
350
|
+
const preload = !!options[4];
|
|
351
|
+
const plannedCacheMatch = preload ? router._cache.get(match.id) : void 0;
|
|
352
|
+
let reload = false;
|
|
353
|
+
let reloadFailure;
|
|
354
|
+
try {
|
|
355
|
+
let configured;
|
|
356
|
+
if (match.status === "success") {
|
|
357
|
+
configured = route.options.shouldReload;
|
|
358
|
+
if (typeof configured === "function") configured = configured(getLoaderContext(router, lane, match, route, options[0], semanticParent, preload));
|
|
359
|
+
if (!options[2]()) {
|
|
360
|
+
options[0].abort();
|
|
361
|
+
reloadFailure = [CANCELED];
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (!reloadFailure) if (match.status !== "success") reload = true;
|
|
365
|
+
else {
|
|
366
|
+
const staleAge = options[4] || match.preload ? route.options.preloadStaleTime ?? router.options.defaultPreloadStaleTime ?? 3e4 : route.options.staleTime ?? router.options.defaultStaleTime ?? 0;
|
|
367
|
+
reload = !!(match.invalid || configured || configured === void 0 && Date.now() - match.updatedAt >= staleAge && (options[6] || match.cause === "enter" || options[3].some((candidate) => candidate.routeId === match.routeId && candidate.id !== match.id)));
|
|
368
|
+
}
|
|
369
|
+
} catch (cause) {
|
|
370
|
+
match.invalid = true;
|
|
371
|
+
releaseFlight(router, match);
|
|
372
|
+
reloadFailure = normalizeLaneError(route, cause, options);
|
|
373
|
+
}
|
|
374
|
+
const routeLoader = route.options.loader;
|
|
375
|
+
const loader = typeof routeLoader === "function" ? routeLoader : routeLoader?.handler;
|
|
376
|
+
const background = !!(routeLoader && reload && match.status === "success" && !preload && !options[5] && ((typeof routeLoader === "function" ? void 0 : routeLoader?.staleReloadMode) ?? router.options.defaultStaleReloadMode) !== "blocking");
|
|
377
|
+
const loaded = reload && (!preload || route.options.preload !== false);
|
|
378
|
+
const blocking = loaded && !background && (match.status !== "success" || !!routeLoader);
|
|
379
|
+
if (loaded && !routeLoader) {
|
|
380
|
+
match.invalid = false;
|
|
381
|
+
match.updatedAt = Date.now();
|
|
382
|
+
}
|
|
383
|
+
let donor = loaded && routeLoader ? router._flights?.get(match.id) : void 0;
|
|
384
|
+
if (donor === match._flight) donor = void 0;
|
|
385
|
+
else if (donor) donor[2]++;
|
|
386
|
+
if (blocking) {
|
|
387
|
+
const acceptedFlight = match._flight;
|
|
388
|
+
match._flight = donor;
|
|
389
|
+
releaseOwnedFlight(router, match.id, acceptedFlight)?.abort();
|
|
390
|
+
if (match.status === "success") match.status = "pending";
|
|
391
|
+
options[8]?.();
|
|
392
|
+
}
|
|
393
|
+
if (!loaded) match.isFetching = false;
|
|
394
|
+
const outcome = (reloadFailure ? Promise.resolve(reloadFailure) : !blocking ? Promise.resolve([SUCCESS, match.loaderData]) : loadResource(router, lane, match, route, loader, semanticParent, preload, options[0])).then((result) => {
|
|
395
|
+
if (blocking) {
|
|
396
|
+
settleInto(match, result, preload);
|
|
397
|
+
if (result[0] === SUCCESS) {
|
|
398
|
+
if (preload && routeLoader) cacheLoaderMatch(router, match, plannedCacheMatch);
|
|
399
|
+
match.status = "pending";
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return result;
|
|
403
|
+
});
|
|
404
|
+
const chunkFailure = waitFor(Promise.resolve().then(() => loadRouteChunk(route)), options[0].signal).then(() => {
|
|
405
|
+
options[8]?.();
|
|
406
|
+
}, (cause) => [index, normalizeLaneError(route, cause, options)]).then((failure) => outcome.then((result) => {
|
|
407
|
+
if (blocking && !failure && result[0] === SUCCESS && match.status === "pending" && options[2]()) {
|
|
408
|
+
match.status = "success";
|
|
409
|
+
options[8]?.();
|
|
410
|
+
}
|
|
411
|
+
return failure;
|
|
412
|
+
}));
|
|
413
|
+
tasks.push([
|
|
414
|
+
index,
|
|
415
|
+
outcome,
|
|
416
|
+
chunkFailure
|
|
417
|
+
]);
|
|
418
|
+
if (!background) return outcome.then((result) => getParentSnapshot(match, result));
|
|
419
|
+
const candidate = {
|
|
420
|
+
...match,
|
|
421
|
+
status: "pending",
|
|
422
|
+
preload: false,
|
|
423
|
+
_flight: donor
|
|
424
|
+
};
|
|
425
|
+
match.invalid = false;
|
|
426
|
+
match.isFetching = "loader";
|
|
427
|
+
const backgroundOutcome = loadResource(router, lane, candidate, route, loader, semanticParent, false, options[0]).then((result) => {
|
|
428
|
+
match.isFetching = false;
|
|
429
|
+
settleInto(candidate, result, false);
|
|
430
|
+
return result;
|
|
431
|
+
});
|
|
432
|
+
(lane[2] ??= []).push([
|
|
433
|
+
index,
|
|
434
|
+
backgroundOutcome,
|
|
435
|
+
chunkFailure,
|
|
436
|
+
candidate
|
|
437
|
+
]);
|
|
438
|
+
return backgroundOutcome.then((result) => getParentSnapshot(candidate, result));
|
|
439
|
+
}
|
|
440
|
+
async function getNotFoundBoundary(router, matches, indexed, signal, fallback = 0) {
|
|
441
|
+
const cause = indexed?.[1][1];
|
|
442
|
+
let index = cause?.routeId ? matches.findIndex((match) => match.routeId === cause.routeId) : indexed?.[0] ?? matches.length - 1;
|
|
443
|
+
if (index < 0) index = 0;
|
|
444
|
+
for (let i = index; i >= 0; i--) {
|
|
445
|
+
const route = getRoute(router, matches[i]);
|
|
446
|
+
const loading = loadRouteChunk(route, false);
|
|
447
|
+
if (loading) try {
|
|
448
|
+
await waitFor(loading, signal);
|
|
449
|
+
} catch (cause) {
|
|
450
|
+
if (cause === signal) throw cause;
|
|
451
|
+
}
|
|
452
|
+
if (route.options.notFoundComponent) return i;
|
|
453
|
+
}
|
|
454
|
+
return cause?.routeId ? index : fallback;
|
|
455
|
+
}
|
|
456
|
+
function discardBackground(router, lane) {
|
|
457
|
+
if (lane[2]) {
|
|
458
|
+
transferMatchResources(router, lane[2].map((task) => task[3]));
|
|
459
|
+
lane[2] = void 0;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
async function settleTasks(tasks, serialFailure, redirectTasks, gate) {
|
|
463
|
+
let loaderFailure;
|
|
464
|
+
try {
|
|
465
|
+
await Promise.all(tasks.map((task) => task[1].then(async (outcome) => {
|
|
466
|
+
const taskIndex = task[0];
|
|
467
|
+
if (gate && taskIndex >= await gate) return;
|
|
468
|
+
if (outcome[0] >= REDIRECTED) throw [taskIndex, outcome];
|
|
469
|
+
if (!loaderFailure && outcome[0] !== SUCCESS) {
|
|
470
|
+
loaderFailure = [taskIndex, outcome];
|
|
471
|
+
await Promise.all((redirectTasks ?? []).map((nextTask) => {
|
|
472
|
+
if (nextTask[0] <= taskIndex) return;
|
|
473
|
+
return nextTask[1].then((nextOutcome) => {
|
|
474
|
+
if (nextOutcome[0] === REDIRECTED) throw [nextTask[0], nextOutcome];
|
|
475
|
+
});
|
|
476
|
+
}));
|
|
477
|
+
}
|
|
478
|
+
})));
|
|
479
|
+
} catch (cause) {
|
|
480
|
+
return cause;
|
|
481
|
+
}
|
|
482
|
+
return serialFailure ?? loaderFailure;
|
|
483
|
+
}
|
|
484
|
+
async function reduceLane(router, lane, tasks, controller, redirects, settlement, onReady) {
|
|
485
|
+
const matches = lane[1];
|
|
486
|
+
let failure = await settlement;
|
|
487
|
+
let redirectLimitExceeded = false;
|
|
488
|
+
const plannedBoundary = matches.findIndex((match) => match._notFound);
|
|
489
|
+
const boundaryOf = (found) => found[1][0] === NOT_FOUND ? getNotFoundBoundary(router, matches, found, controller.signal) : found[0];
|
|
490
|
+
let readinessEnd = plannedBoundary < 0 ? matches.length : plannedBoundary;
|
|
491
|
+
if ((failure?.[1][0] ?? 0) >= REDIRECTED) readinessEnd = 0;
|
|
492
|
+
else if (failure) {
|
|
493
|
+
readinessEnd = failure[2] ??= await boundaryOf(failure);
|
|
494
|
+
for (const task of tasks) {
|
|
495
|
+
if (task[0] >= readinessEnd) break;
|
|
496
|
+
const outcome = await task[1];
|
|
497
|
+
if (outcome[0] !== SUCCESS && outcome[0] < REDIRECTED && !("loaderData" in matches[task[0]])) {
|
|
498
|
+
failure = [task[0], outcome];
|
|
499
|
+
readinessEnd = failure[2] = await boundaryOf(failure);
|
|
500
|
+
break;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
for (const task of tasks) {
|
|
505
|
+
if (task[0] >= readinessEnd) break;
|
|
506
|
+
const chunkFailure = await task[2];
|
|
507
|
+
if (!chunkFailure) continue;
|
|
508
|
+
failure = chunkFailure;
|
|
509
|
+
break;
|
|
510
|
+
}
|
|
511
|
+
if ((failure?.[1][0] ?? 0) >= REDIRECTED) {
|
|
512
|
+
const outcome = failure[1];
|
|
513
|
+
if (outcome[0] !== REDIRECTED || outcome[1].options.reloadDocument || redirects < 20) {
|
|
514
|
+
discardBackground(router, lane);
|
|
515
|
+
return outcome;
|
|
516
|
+
}
|
|
517
|
+
redirectLimitExceeded = true;
|
|
518
|
+
failure = [0, [ERROR, /* @__PURE__ */ new Error("Too many redirects")]];
|
|
519
|
+
}
|
|
520
|
+
const boundary = failure ? failure[2] ?? await boundaryOf(failure) : plannedBoundary;
|
|
521
|
+
if (boundary >= 0) {
|
|
522
|
+
const outcome = failure?.[1];
|
|
523
|
+
const kind = outcome?.[0];
|
|
524
|
+
const match = matches[boundary];
|
|
525
|
+
const cause = outcome?.[1];
|
|
526
|
+
const install = () => {
|
|
527
|
+
if (outcome) {
|
|
528
|
+
match._notFound = void 0;
|
|
529
|
+
if (kind === ERROR) match.status = "error";
|
|
530
|
+
else {
|
|
531
|
+
cause.routeId = match.routeId;
|
|
532
|
+
if (match.routeId === router.routeTree.id) {
|
|
533
|
+
match.status = "success";
|
|
534
|
+
match._notFound = true;
|
|
535
|
+
} else match.status = "notFound";
|
|
536
|
+
}
|
|
537
|
+
match.error = cause;
|
|
538
|
+
match.isFetching = false;
|
|
539
|
+
}
|
|
540
|
+
};
|
|
541
|
+
install();
|
|
542
|
+
try {
|
|
543
|
+
await waitFor(outcome ? Promise.resolve().then(() => loadRouteChunk(getRoute(router, match), kind === ERROR ? "errorComponent" : "notFoundComponent")) : Promise.all([loadRouteChunk(getRoute(router, match)), loadRouteChunk(getRoute(router, match), "notFoundComponent")]), controller.signal);
|
|
544
|
+
} catch (cause) {
|
|
545
|
+
if (cause === controller.signal) {
|
|
546
|
+
discardBackground(router, lane);
|
|
547
|
+
return [CANCELED];
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
if (!outcome) {
|
|
551
|
+
match.status = "success";
|
|
552
|
+
onReady?.();
|
|
553
|
+
} else if (redirectLimitExceeded) {
|
|
554
|
+
controller.abort();
|
|
555
|
+
await Promise.all([
|
|
556
|
+
...tasks.map((task) => task[1]),
|
|
557
|
+
...tasks.map((task) => task[2]),
|
|
558
|
+
...(lane[2] ?? []).map((task) => task[1])
|
|
559
|
+
]);
|
|
560
|
+
discardBackground(router, lane);
|
|
561
|
+
transferMatchResources(router, matches);
|
|
562
|
+
install();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return lane;
|
|
566
|
+
}
|
|
567
|
+
async function projectLane(router, lane, signal, start = 0, end = lane[1].length) {
|
|
568
|
+
const matches = lane[1];
|
|
569
|
+
for (let index = start; index < end; index++) {
|
|
570
|
+
const match = matches[index];
|
|
571
|
+
const routeOptions = getRoute(router, match).options;
|
|
572
|
+
if (routeOptions.head || routeOptions.scripts) try {
|
|
573
|
+
const context = {
|
|
574
|
+
ssr: router.options.ssr,
|
|
575
|
+
matches,
|
|
576
|
+
match,
|
|
577
|
+
params: match.params,
|
|
578
|
+
loaderData: match.loaderData
|
|
579
|
+
};
|
|
580
|
+
const [head, scripts] = await waitFor(Promise.all([routeOptions.head?.(context), routeOptions.scripts?.(context)]), signal);
|
|
581
|
+
match.meta = head?.meta;
|
|
582
|
+
match.links = head?.links;
|
|
583
|
+
match.headScripts = head?.scripts;
|
|
584
|
+
match.styles = head?.styles;
|
|
585
|
+
match.scripts = scripts;
|
|
586
|
+
} catch (cause) {
|
|
587
|
+
if (cause === signal) break;
|
|
588
|
+
console.error(cause);
|
|
589
|
+
}
|
|
590
|
+
if (match.status !== "success" || match._notFound) break;
|
|
591
|
+
}
|
|
592
|
+
return lane;
|
|
593
|
+
}
|
|
594
|
+
async function executeClientLane(router, location, matches, options) {
|
|
595
|
+
const matched = [location, matches];
|
|
596
|
+
let plannedBoundary = matches.findIndex((match) => match._notFound);
|
|
597
|
+
if (router.options.notFoundMode !== "root" && plannedBoundary >= 0) {
|
|
598
|
+
const boundary = await getNotFoundBoundary(router, matched[1], void 0, options[0].signal, plannedBoundary);
|
|
599
|
+
if (boundary !== plannedBoundary) {
|
|
600
|
+
matches[plannedBoundary]._notFound = void 0;
|
|
601
|
+
matches[boundary]._notFound = true;
|
|
602
|
+
}
|
|
603
|
+
plannedBoundary = boundary;
|
|
604
|
+
}
|
|
605
|
+
let end = plannedBoundary < 0 ? matches.length : plannedBoundary + 1;
|
|
606
|
+
const failure = await contextualize(router, matched, options, end);
|
|
607
|
+
if (failure) options[5] = true;
|
|
608
|
+
const tasks = [];
|
|
609
|
+
const start = options[7] ?? 0;
|
|
610
|
+
let semanticParent = start ? Promise.resolve(matched[1][start - 1]) : void 0;
|
|
611
|
+
end = failure?.[0] ?? end;
|
|
612
|
+
if (failure?.[1][0] === NOT_FOUND) {
|
|
613
|
+
failure[2] = await getNotFoundBoundary(router, matched[1], failure, options[0].signal);
|
|
614
|
+
end = Math.min(end, failure[2] + 1);
|
|
615
|
+
} else if ((failure?.[1][0] ?? 0) >= REDIRECTED) end = 0;
|
|
616
|
+
for (let index = start; index < end; index++) {
|
|
617
|
+
if (options[0].signal.aborted) break;
|
|
618
|
+
semanticParent = createLoaderTask(router, matched, index, tasks, semanticParent, options);
|
|
619
|
+
}
|
|
620
|
+
let reduced;
|
|
621
|
+
try {
|
|
622
|
+
const reduction = reduceLane(router, matched, tasks, options[0], options[1], settleTasks(tasks, failure, matched[2]), options[8]);
|
|
623
|
+
if (matched[2]?.length) matched[3] = settleTasks(matched[2], void 0, void 0, reduction.then((foreground) => isControl(foreground) ? 0 : _getRenderedMatches(foreground[1]).length, () => 0));
|
|
624
|
+
reduced = await reduction;
|
|
625
|
+
} catch (cause) {
|
|
626
|
+
discardBackground(router, matched);
|
|
627
|
+
throw cause;
|
|
628
|
+
}
|
|
629
|
+
if (isControl(reduced)) return reduced;
|
|
630
|
+
return projectLane(router, reduced, options[0].signal, options[7] === reduced[1].length ? options[7] : 0);
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Finds the first route that should show pending UI and its two timing values.
|
|
634
|
+
* A fallback already on screen remains selected after its route loads, so we
|
|
635
|
+
* do not jump to a child fallback. Matches put back into pending by invalidation
|
|
636
|
+
* skip pendingMs, and a route without a usable fallback blocks pending UI for deeper routes.
|
|
637
|
+
*/
|
|
638
|
+
function pendingConfig(router, matches) {
|
|
639
|
+
const presented = router.stores.matches.get();
|
|
640
|
+
for (let index = 0; index < matches.length; index++) {
|
|
641
|
+
const match = matches[index];
|
|
642
|
+
const success = match.status === "success";
|
|
643
|
+
const visible = success && presented[index]?.id === match.id && presented[index]?.status === "pending";
|
|
644
|
+
if (success && !visible) continue;
|
|
645
|
+
const route = getRoute(router, match);
|
|
646
|
+
const delay = visible || match.invalid ? 0 : route.options.pendingMs ?? router.options.defaultPendingMs;
|
|
647
|
+
const component = route.options.pendingComponent ?? router.options.defaultPendingComponent;
|
|
648
|
+
return component && typeof delay === "number" && delay !== Infinity ? [
|
|
649
|
+
delay,
|
|
650
|
+
index,
|
|
651
|
+
route.options.pendingMinMs ?? router.options.defaultPendingMinMs ?? 0,
|
|
652
|
+
component
|
|
653
|
+
] : void 0;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Waits for `pendingMs`, then presents the complete lane. Rendering applies the
|
|
658
|
+
* selected boundary cutoff while retaining every match's structural state.
|
|
659
|
+
* A replacement load for the same match keeps the timer; choosing a different
|
|
660
|
+
* match resets it. `pendingMinMs` starts after the fallback renders.
|
|
661
|
+
*/
|
|
662
|
+
function offerPending(router, tx) {
|
|
663
|
+
if (router._tx !== tx) return;
|
|
664
|
+
let session = router._pending;
|
|
665
|
+
let tookOver = false;
|
|
666
|
+
const sessionMatchId = session?.[0][3][session[1]]?.id;
|
|
667
|
+
if (session?.[0] !== tx) if (session && tx[3][session[1]]?.id === sessionMatchId) {
|
|
668
|
+
session[0] = tx;
|
|
669
|
+
tookOver = true;
|
|
670
|
+
} else {
|
|
671
|
+
clearTimeout(session?.[3]);
|
|
672
|
+
router._pending = session = void 0;
|
|
673
|
+
}
|
|
674
|
+
const config = pendingConfig(router, tx[3]);
|
|
675
|
+
if (!config) return;
|
|
676
|
+
const [delay, boundary, min, component] = config;
|
|
677
|
+
const matchId = tx[3][boundary].id;
|
|
678
|
+
if (!session || session[1] !== boundary || sessionMatchId !== matchId) {
|
|
679
|
+
clearTimeout(session?.[3]);
|
|
680
|
+
const presented = router.stores.matches.get()[boundary];
|
|
681
|
+
const visible = presented?.id === matchId && presented.status === "pending";
|
|
682
|
+
router._pending = session = [
|
|
683
|
+
tx,
|
|
684
|
+
boundary,
|
|
685
|
+
visible ? Date.now() + min : tx[4] + delay,
|
|
686
|
+
void 0,
|
|
687
|
+
visible ? Promise.resolve(true) : void 0,
|
|
688
|
+
component
|
|
689
|
+
];
|
|
690
|
+
}
|
|
691
|
+
if (session[4] && !tookOver && session[5] === component) return;
|
|
692
|
+
session[5] = component;
|
|
693
|
+
if (!session[4]) {
|
|
694
|
+
clearTimeout(session[3]);
|
|
695
|
+
const remaining = session[2] - Date.now();
|
|
696
|
+
if (remaining > 0) {
|
|
697
|
+
session[3] = setTimeout(() => {
|
|
698
|
+
offerPending(router, tx);
|
|
699
|
+
}, remaining);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
session[2] = 0;
|
|
703
|
+
}
|
|
704
|
+
const offered = tx[3].map((match) => ({
|
|
705
|
+
...match,
|
|
706
|
+
_flight: void 0
|
|
707
|
+
}));
|
|
708
|
+
offered[boundary].status = "pending";
|
|
709
|
+
const ack = router.startTransition(() => router.stores.setMatches(offered), offered, true).then((rendered) => {
|
|
710
|
+
if (rendered && router._pending === session && session[4] === ack && !session[2]) session[2] = Date.now() + min;
|
|
711
|
+
return rendered;
|
|
712
|
+
});
|
|
713
|
+
session[4] = ack;
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Cancels pending UI timing when its load ends. The ownership check prevents
|
|
717
|
+
* an older, superseded load from clearing pending UI that a newer load took over.
|
|
718
|
+
*/
|
|
719
|
+
function finishPending(router, tx) {
|
|
720
|
+
const session = router._pending;
|
|
721
|
+
if (session?.[0] === tx) {
|
|
722
|
+
clearTimeout(session[3]);
|
|
723
|
+
router._pending = void 0;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
function publishMatches(router, matches) {
|
|
727
|
+
router._committed = matches;
|
|
728
|
+
router.stores.setMatches(matches);
|
|
729
|
+
}
|
|
730
|
+
function discardLane(router, lane) {
|
|
731
|
+
transferMatchResources(router, lane[1]);
|
|
732
|
+
discardBackground(router, lane);
|
|
733
|
+
}
|
|
734
|
+
function commitMatches(router, tx, matches, resolvedPrefix) {
|
|
735
|
+
const previous = router._committed;
|
|
736
|
+
const previousCached = router._cache;
|
|
737
|
+
for (const match of matches) {
|
|
738
|
+
match.preload = false;
|
|
739
|
+
if (resolvedPrefix) match._assetEnd = void 0;
|
|
740
|
+
}
|
|
741
|
+
const cut = _getRenderedMatches(matches).length;
|
|
742
|
+
const cached = /* @__PURE__ */ new Map();
|
|
743
|
+
const now = Date.now();
|
|
744
|
+
for (const match of [...previous, ...previousCached.values()]) {
|
|
745
|
+
if (match.status !== "success" || matches.some((candidate, index) => candidate.id === match.id && (index < cut || candidate.status === "success"))) continue;
|
|
746
|
+
const route = getRoute(router, match);
|
|
747
|
+
if (!route.options.loader || now - match.updatedAt >= (match.preload ? route.options.preloadGcTime ?? router.options.defaultPreloadGcTime ?? 3e5 : route.options.gcTime ?? router.options.defaultGcTime ?? 3e5)) continue;
|
|
748
|
+
cached.set(match.id, previousCached.get(match.id) === match ? match : {
|
|
749
|
+
...match,
|
|
750
|
+
_flight: void 0,
|
|
751
|
+
isFetching: false,
|
|
752
|
+
context: {}
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
tx[3] = [];
|
|
756
|
+
router._cache = cached;
|
|
757
|
+
publishMatches(router, matches);
|
|
758
|
+
transferMatchResources(router, [...previousCached.values(), ...previous], [...matches, ...cached.values()]);
|
|
759
|
+
runRouteLifecycle(router, previous, matches, () => router._tx === tx);
|
|
760
|
+
}
|
|
761
|
+
function commitRefreshMatches(router, tx, matches, checkpoint) {
|
|
762
|
+
const previous = router._committed;
|
|
763
|
+
const previousCached = router._cache;
|
|
764
|
+
for (const match of matches) match.preload = false;
|
|
765
|
+
const cached = /* @__PURE__ */ new Map();
|
|
766
|
+
tx[3] = [];
|
|
767
|
+
router._cache = cached;
|
|
768
|
+
checkpoint.previousMatches = previous;
|
|
769
|
+
checkpoint.previousCache = previousCached;
|
|
770
|
+
checkpoint.published = true;
|
|
771
|
+
publishMatches(router, matches);
|
|
772
|
+
if (!checkpoint.published || router._tx !== tx) return;
|
|
773
|
+
runRouteLifecycle(router, previous, matches, () => router._tx === tx);
|
|
774
|
+
}
|
|
775
|
+
function settlePublication(router, checkpoint) {
|
|
776
|
+
if (!checkpoint.published) return;
|
|
777
|
+
checkpoint.published = false;
|
|
778
|
+
transferMatchResources(router, [...checkpoint.previousCache.values(), ...checkpoint.previousMatches], [...router._cache.values(), ...router._committed]);
|
|
779
|
+
}
|
|
780
|
+
function rollbackPublication(router, tx, lane, checkpoint) {
|
|
781
|
+
if (!checkpoint.published || router._tx !== tx || router._committed !== lane[1]) {
|
|
782
|
+
settlePublication(router, checkpoint);
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
const discarded = [...router._cache.values(), ...router._committed];
|
|
786
|
+
const restored = [...checkpoint.previousCache.values(), ...checkpoint.previousMatches];
|
|
787
|
+
router._cache = checkpoint.previousCache;
|
|
788
|
+
router._committed = checkpoint.previousMatches;
|
|
789
|
+
checkpoint.published = false;
|
|
790
|
+
for (const match of discarded) if (!restored.includes(match) && match._flight && router._flights?.get(match.id) === match._flight) router._flights.delete(match.id);
|
|
791
|
+
finishPending(router, tx);
|
|
792
|
+
router.batch(() => {
|
|
793
|
+
router.stores.status.set("idle");
|
|
794
|
+
router.stores.setMatches(checkpoint.previousPresentation);
|
|
795
|
+
});
|
|
796
|
+
tx[0].abort();
|
|
797
|
+
transferMatchResources(router, discarded, restored);
|
|
798
|
+
discardBackground(router, lane);
|
|
799
|
+
if (router._tx === tx && router._commitPromise === checkpoint.commitPromise) {
|
|
800
|
+
router._commitPromise?.resolve();
|
|
801
|
+
router._commitPromise = void 0;
|
|
802
|
+
}
|
|
803
|
+
return true;
|
|
804
|
+
}
|
|
805
|
+
async function transitionRefresh(router, tx, lane, changeInfo) {
|
|
806
|
+
const checkpoint = {
|
|
807
|
+
previousMatches: router._committed,
|
|
808
|
+
previousPresentation: tx[6]?.[0] ?? router.stores.matches.get(),
|
|
809
|
+
previousCache: router._cache,
|
|
810
|
+
commitPromise: router._commitPromise,
|
|
811
|
+
published: false
|
|
812
|
+
};
|
|
813
|
+
const commit = () => {
|
|
814
|
+
finishPending(router, tx);
|
|
815
|
+
router._rollbackRefresh = rollback;
|
|
816
|
+
commitRefreshMatches(router, tx, lane[1], checkpoint);
|
|
817
|
+
if (!checkpoint.published || router._tx !== tx) return;
|
|
818
|
+
router.emit({
|
|
819
|
+
type: "onLoad",
|
|
820
|
+
...changeInfo
|
|
821
|
+
});
|
|
822
|
+
if (router._tx === tx) router.emit({
|
|
823
|
+
type: "onBeforeRouteMount",
|
|
824
|
+
...changeInfo
|
|
825
|
+
});
|
|
826
|
+
};
|
|
827
|
+
const rollback = () => {
|
|
828
|
+
if (router._rollbackRefresh === rollback) router._rollbackRefresh = void 0;
|
|
829
|
+
const restored = rollbackPublication(router, tx, lane, checkpoint);
|
|
830
|
+
router._cancelTransition?.();
|
|
831
|
+
return restored;
|
|
832
|
+
};
|
|
833
|
+
try {
|
|
834
|
+
const rendered = await router.startTransition(commit, lane[1]);
|
|
835
|
+
if (router._rollbackRefresh === rollback) router._rollbackRefresh = void 0;
|
|
836
|
+
if (checkpoint.published) {
|
|
837
|
+
const handoff = tx[6]?.[1];
|
|
838
|
+
if (handoff && router._handoff === handoff) handoff[1]();
|
|
839
|
+
if (router._tx === tx) tx[6] = void 0;
|
|
840
|
+
}
|
|
841
|
+
settlePublication(router, checkpoint);
|
|
842
|
+
return rendered;
|
|
843
|
+
} catch (cause) {
|
|
844
|
+
if (rollback()) return;
|
|
845
|
+
throw cause;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
async function awaitCurrent(router, owner) {
|
|
849
|
+
let current = router._tx;
|
|
850
|
+
while (current && current !== owner) {
|
|
851
|
+
await current[5];
|
|
852
|
+
if (router._tx === current) return;
|
|
853
|
+
current = router._tx;
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
async function followRedirect(router, tx, redirect) {
|
|
857
|
+
await router.navigate({
|
|
858
|
+
...redirect.options,
|
|
859
|
+
replace: true,
|
|
860
|
+
ignoreBlocker: true,
|
|
861
|
+
_redirects: tx[1] + 1
|
|
862
|
+
});
|
|
863
|
+
}
|
|
864
|
+
function restoreCommitted(router, tx) {
|
|
865
|
+
finishPending(router, tx);
|
|
866
|
+
tx[0].abort();
|
|
867
|
+
transferMatchResources(router, tx[3]);
|
|
868
|
+
tx[3] = [];
|
|
869
|
+
if (router._tx !== tx) return;
|
|
870
|
+
router.batch(() => {
|
|
871
|
+
router.stores.status.set("idle");
|
|
872
|
+
router.stores.setMatches(router._committed);
|
|
873
|
+
});
|
|
874
|
+
if (router._tx === tx) {
|
|
875
|
+
router._commitPromise?.resolve();
|
|
876
|
+
router._commitPromise = void 0;
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
async function runBackground(router, tx, base, tasks, settlement) {
|
|
880
|
+
const next = base.map((match) => ({ ...match }));
|
|
881
|
+
acquireMatchResources(next);
|
|
882
|
+
for (const task of tasks) {
|
|
883
|
+
releaseFlight(router, next[task[0]]);
|
|
884
|
+
next[task[0]] = task[3];
|
|
885
|
+
}
|
|
886
|
+
const lane = [tx[2], next];
|
|
887
|
+
let reduced;
|
|
888
|
+
try {
|
|
889
|
+
reduced = await reduceLane(router, lane, tasks, tx[0], tx[1], settlement);
|
|
890
|
+
} catch (cause) {
|
|
891
|
+
transferMatchResources(router, next);
|
|
892
|
+
throw cause;
|
|
893
|
+
}
|
|
894
|
+
if (isControl(reduced)) {
|
|
895
|
+
transferMatchResources(router, next);
|
|
896
|
+
if (reduced[0] === REDIRECTED && router._tx === tx && router._committed === base) await followRedirect(router, tx, reduced[1]);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
const projected = await projectLane(router, reduced, tx[0].signal);
|
|
900
|
+
if (router._tx !== tx || router._committed !== base) {
|
|
901
|
+
transferMatchResources(router, projected[1]);
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
for (const match of projected[1]) {
|
|
905
|
+
const cached = router._cache.get(match.id);
|
|
906
|
+
if (cached?._flight && cached._flight === match._flight) {
|
|
907
|
+
router._cache.delete(match.id);
|
|
908
|
+
releaseFlight(router, cached);
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
publishMatches(router, projected[1]);
|
|
912
|
+
transferMatchResources(router, base, projected[1]);
|
|
913
|
+
}
|
|
914
|
+
async function runClientTransaction(router, tx, forceStaleReload, onReady, sync, resolvedPrefix, adopted, retained) {
|
|
915
|
+
const options = [
|
|
916
|
+
tx[0],
|
|
917
|
+
tx[1],
|
|
918
|
+
() => router._tx === tx && !!tx[3].length,
|
|
919
|
+
router._committed,
|
|
920
|
+
void 0,
|
|
921
|
+
sync,
|
|
922
|
+
forceStaleReload,
|
|
923
|
+
resolvedPrefix,
|
|
924
|
+
onReady
|
|
925
|
+
];
|
|
926
|
+
let result;
|
|
927
|
+
try {
|
|
928
|
+
result = adopted ? await adopted[2] : await executeClientLane(router, tx[2], tx[3], options);
|
|
929
|
+
} finally {
|
|
930
|
+
if (retained) discardPreload(router, retained);
|
|
931
|
+
}
|
|
932
|
+
if (adopted && router._tx === tx && (isControl(result) && result[0] === CANCELED || !isControl(result) && result[1].some((match) => match.status !== "success" || match._notFound))) {
|
|
933
|
+
const donors = tx[3];
|
|
934
|
+
tx[3] = [];
|
|
935
|
+
transferMatchResources(router, donors);
|
|
936
|
+
tx[0].abort();
|
|
937
|
+
if (router._tx !== tx) return;
|
|
938
|
+
const controller = new AbortController();
|
|
939
|
+
tx[0] = options[0] = controller;
|
|
940
|
+
tx[3] = router.matchRoutes(tx[2], { _controller: controller });
|
|
941
|
+
acquireMatchResources(tx[3]);
|
|
942
|
+
result = await executeClientLane(router, tx[2], tx[3], options);
|
|
943
|
+
}
|
|
944
|
+
if (isControl(result)) {
|
|
945
|
+
if (result[0] === REDIRECTED && router._tx === tx) {
|
|
946
|
+
finishPending(router, tx);
|
|
947
|
+
transferMatchResources(router, tx[3]);
|
|
948
|
+
tx[3] = [];
|
|
949
|
+
if (router._tx === tx) {
|
|
950
|
+
if (process.env.NODE_ENV !== "production" && tx[6]) router._refreshNextLoad = true;
|
|
951
|
+
await followRedirect(router, tx, result[1]);
|
|
952
|
+
}
|
|
953
|
+
} else restoreCommitted(router, tx);
|
|
954
|
+
return;
|
|
955
|
+
}
|
|
956
|
+
const pending = router._pending;
|
|
957
|
+
if (pending?.[0] === tx) {
|
|
958
|
+
/**
|
|
959
|
+
* Loading finished, so cancel any pending reveal. If the fallback rendered,
|
|
960
|
+
* wait out the rest of `pendingMinMs` before replacing it. If it never
|
|
961
|
+
* rendered, there is no minimum wait; if another load took it over, that
|
|
962
|
+
* load owns the deadline.
|
|
963
|
+
*/
|
|
964
|
+
clearTimeout(pending[3]);
|
|
965
|
+
if (pending[4]) {
|
|
966
|
+
const signal = tx[0].signal;
|
|
967
|
+
let rendered = false;
|
|
968
|
+
try {
|
|
969
|
+
rendered = await waitFor(pending[4], signal);
|
|
970
|
+
} catch (cause) {
|
|
971
|
+
if (cause !== signal) throw cause;
|
|
972
|
+
}
|
|
973
|
+
if (rendered && router._pending === pending && pending[0] === tx) {
|
|
974
|
+
const remaining = pending[2] - Date.now();
|
|
975
|
+
if (remaining > 0) {
|
|
976
|
+
try {
|
|
977
|
+
await waitFor(new Promise((resolve) => {
|
|
978
|
+
pending[3] = setTimeout(resolve, remaining);
|
|
979
|
+
}), signal);
|
|
980
|
+
} catch {}
|
|
981
|
+
clearTimeout(pending[3]);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
if (router._tx !== tx) {
|
|
987
|
+
finishPending(router, tx);
|
|
988
|
+
discardLane(router, result);
|
|
989
|
+
return;
|
|
990
|
+
}
|
|
991
|
+
const toLocation = tx[2];
|
|
992
|
+
const changeInfo = getLocationChangeInfo(toLocation, router.stores.resolvedLocation.get());
|
|
993
|
+
const background = result[2];
|
|
994
|
+
await router.startViewTransition(async () => {
|
|
995
|
+
if (router._tx !== tx) {
|
|
996
|
+
discardLane(router, result);
|
|
997
|
+
return;
|
|
998
|
+
}
|
|
999
|
+
const commit = () => {
|
|
1000
|
+
finishPending(router, tx);
|
|
1001
|
+
commitMatches(router, tx, result[1], resolvedPrefix);
|
|
1002
|
+
if (router._tx !== tx) return;
|
|
1003
|
+
router.emit({
|
|
1004
|
+
type: "onLoad",
|
|
1005
|
+
...changeInfo
|
|
1006
|
+
});
|
|
1007
|
+
if (router._tx === tx) router.emit({
|
|
1008
|
+
type: "onBeforeRouteMount",
|
|
1009
|
+
...changeInfo
|
|
1010
|
+
});
|
|
1011
|
+
};
|
|
1012
|
+
const rendered = process.env.NODE_ENV !== "production" && tx[6] ? await transitionRefresh(router, tx, result, changeInfo) : await router.startTransition(commit, result[1]);
|
|
1013
|
+
if (process.env.NODE_ENV !== "production" && tx[6] && rendered === void 0) return;
|
|
1014
|
+
if (router._tx !== tx) {
|
|
1015
|
+
discardBackground(router, result);
|
|
1016
|
+
return;
|
|
1017
|
+
}
|
|
1018
|
+
if (background?.length) runBackground(router, tx, result[1], background, result[3]).catch(console.error);
|
|
1019
|
+
router.batch(() => {
|
|
1020
|
+
router.stores.resolvedLocation.set(toLocation);
|
|
1021
|
+
router.stores.status.set("idle");
|
|
1022
|
+
if (router._tx === tx) router.emit({
|
|
1023
|
+
type: "onResolved",
|
|
1024
|
+
...changeInfo
|
|
1025
|
+
});
|
|
1026
|
+
if (rendered && router._tx === tx) router.emit({
|
|
1027
|
+
type: "onRendered",
|
|
1028
|
+
...changeInfo
|
|
1029
|
+
});
|
|
1030
|
+
});
|
|
1031
|
+
if (router._tx !== tx) return;
|
|
1032
|
+
router._commitPromise?.resolve();
|
|
1033
|
+
router._commitPromise = void 0;
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
async function loadClientRoute(router, opts) {
|
|
1037
|
+
let rematerialize = false;
|
|
1038
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1039
|
+
router._rollbackRefresh?.();
|
|
1040
|
+
rematerialize = !!router._refreshNextLoad || !!router._tx?.[6];
|
|
1041
|
+
}
|
|
1042
|
+
const refreshPresentation = rematerialize ? router.stores.matches.get() : void 0;
|
|
1043
|
+
const previousOwner = router._tx;
|
|
1044
|
+
const resolvedLocation = router.stores.resolvedLocation.get();
|
|
1045
|
+
const previousLocation = resolvedLocation ?? router.stores.location.get();
|
|
1046
|
+
const location = router.latestLocation;
|
|
1047
|
+
const pendingLocation = router._pendingLocation;
|
|
1048
|
+
const redirects = pendingLocation?.href === location.href ? pendingLocation._redirects ?? 0 : 0;
|
|
1049
|
+
if (opts?._dedupe && !redirects && previousOwner && !rematerialize && previousOwner[2].href === location.href && router.stores.status.get() === "pending") {
|
|
1050
|
+
await awaitCurrent(router);
|
|
1051
|
+
return;
|
|
1052
|
+
}
|
|
1053
|
+
const handoff = router._handoff;
|
|
1054
|
+
const hydrationController = rematerialize ? void 0 : handoff?.[0]();
|
|
1055
|
+
const preflight = new AbortController();
|
|
1056
|
+
const previousPreflight = router._preflight;
|
|
1057
|
+
router._preflight = preflight;
|
|
1058
|
+
if (!rematerialize && !hydrationController) handoff?.[1]();
|
|
1059
|
+
previousPreflight?.abort();
|
|
1060
|
+
if (preflight.signal.aborted || router._tx !== previousOwner) {
|
|
1061
|
+
await awaitCurrent(router, previousOwner);
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
const changeInfo = getLocationChangeInfo(location, resolvedLocation);
|
|
1065
|
+
router.emit({
|
|
1066
|
+
type: "onBeforeNavigate",
|
|
1067
|
+
...changeInfo
|
|
1068
|
+
});
|
|
1069
|
+
if (!preflight.signal.aborted && router._tx === previousOwner) router.emit({
|
|
1070
|
+
type: "onBeforeLoad",
|
|
1071
|
+
...changeInfo
|
|
1072
|
+
});
|
|
1073
|
+
if (preflight.signal.aborted || router._tx !== previousOwner) {
|
|
1074
|
+
preflight.abort();
|
|
1075
|
+
await awaitCurrent(router, previousOwner);
|
|
1076
|
+
return;
|
|
1077
|
+
}
|
|
1078
|
+
const sameHref = previousLocation.href === location.href;
|
|
1079
|
+
let adopted = router._preloads?.get(location.href);
|
|
1080
|
+
let retained;
|
|
1081
|
+
if (rematerialize && adopted) {
|
|
1082
|
+
router._preloads.delete(location.href);
|
|
1083
|
+
discardPreload(router, adopted);
|
|
1084
|
+
adopted = void 0;
|
|
1085
|
+
if (preflight.signal.aborted || router._tx !== previousOwner) {
|
|
1086
|
+
preflight.abort();
|
|
1087
|
+
await awaitCurrent(router, previousOwner);
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
}
|
|
1091
|
+
if (adopted && (hydrationController || !samePreloadLane(adopted, router, pendingLocation?.href === location.href ? pendingLocation : location, redirects))) {
|
|
1092
|
+
router._preloads.delete(location.href);
|
|
1093
|
+
retained = adopted;
|
|
1094
|
+
adopted = void 0;
|
|
1095
|
+
}
|
|
1096
|
+
let matches;
|
|
1097
|
+
let controller = preflight;
|
|
1098
|
+
let resolvedPrefix;
|
|
1099
|
+
if (adopted) {
|
|
1100
|
+
controller = adopted[1];
|
|
1101
|
+
matches = adopted[0];
|
|
1102
|
+
router._preloads.delete(location.href);
|
|
1103
|
+
} else {
|
|
1104
|
+
try {
|
|
1105
|
+
matches = process.env.NODE_ENV !== "production" && rematerialize ? router.matchRoutes(location, {
|
|
1106
|
+
_controller: preflight,
|
|
1107
|
+
_rematerialize: true
|
|
1108
|
+
}) : router.matchRoutes(location, { _controller: preflight });
|
|
1109
|
+
acquireMatchResources(matches);
|
|
1110
|
+
} catch (cause) {
|
|
1111
|
+
preflight.abort();
|
|
1112
|
+
if (retained) discardPreload(router, retained);
|
|
1113
|
+
if (!isRedirect(cause)) {
|
|
1114
|
+
if (process.env.NODE_ENV !== "production" && rematerialize) router._refreshNextLoad = void 0;
|
|
1115
|
+
await awaitCurrent(router);
|
|
1116
|
+
router._commitPromise?.resolve();
|
|
1117
|
+
router._commitPromise = void 0;
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1120
|
+
await router.navigate({
|
|
1121
|
+
...cause.options,
|
|
1122
|
+
replace: true,
|
|
1123
|
+
ignoreBlocker: true
|
|
1124
|
+
});
|
|
1125
|
+
await awaitCurrent(router, previousOwner);
|
|
1126
|
+
return;
|
|
1127
|
+
}
|
|
1128
|
+
resolvedPrefix = hydrationController ? handoff[1](matches) : void 0;
|
|
1129
|
+
if (resolvedPrefix) controller = hydrationController;
|
|
1130
|
+
else hydrationController?.abort();
|
|
1131
|
+
}
|
|
1132
|
+
if (router._preflight !== preflight || router._tx !== previousOwner) {
|
|
1133
|
+
preflight.abort();
|
|
1134
|
+
transferMatchResources(router, matches);
|
|
1135
|
+
await awaitCurrent(router, previousOwner);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
router._preflight = void 0;
|
|
1139
|
+
const tx = [
|
|
1140
|
+
controller,
|
|
1141
|
+
redirects,
|
|
1142
|
+
location,
|
|
1143
|
+
matches,
|
|
1144
|
+
Date.now(),
|
|
1145
|
+
Promise.resolve().then(() => runClientTransaction(router, tx, sameHref, () => offerPending(router, tx), opts?.sync, resolvedPrefix, adopted, retained)).catch(() => {
|
|
1146
|
+
if (router._tx === tx) restoreCommitted(router, tx);
|
|
1147
|
+
})
|
|
1148
|
+
];
|
|
1149
|
+
if (process.env.NODE_ENV !== "production" && rematerialize) {
|
|
1150
|
+
tx[6] = [refreshPresentation, handoff];
|
|
1151
|
+
router._refreshNextLoad = void 0;
|
|
1152
|
+
}
|
|
1153
|
+
router._tx = tx;
|
|
1154
|
+
if (!rematerialize && router._handoff === handoff) router._handoff = void 0;
|
|
1155
|
+
if (previousOwner) {
|
|
1156
|
+
for (const match of router.stores.matches.get()) {
|
|
1157
|
+
if (router._tx !== tx) break;
|
|
1158
|
+
if (match.isFetching) setFetching(router, match, false);
|
|
1159
|
+
}
|
|
1160
|
+
previousOwner[0].abort();
|
|
1161
|
+
transferMatchResources(router, previousOwner[3]);
|
|
1162
|
+
}
|
|
1163
|
+
if (router._tx !== tx) {
|
|
1164
|
+
transferMatchResources(router, tx[3]);
|
|
1165
|
+
tx[3] = [];
|
|
1166
|
+
await awaitCurrent(router, tx);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
router.batch(() => {
|
|
1170
|
+
router.stores.status.set("pending");
|
|
1171
|
+
router.stores.location.set(location);
|
|
1172
|
+
});
|
|
1173
|
+
offerPending(router, tx);
|
|
1174
|
+
try {
|
|
1175
|
+
await tx[5];
|
|
1176
|
+
} finally {
|
|
1177
|
+
await awaitCurrent(router, tx);
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
async function refreshClientRoute(router) {
|
|
1181
|
+
router._rollbackRefresh?.();
|
|
1182
|
+
const pending = router._tx;
|
|
1183
|
+
if (pending && !pending[6] && router.stores.status.get() === "pending") {
|
|
1184
|
+
await pending[5];
|
|
1185
|
+
if (router._tx !== pending) await awaitCurrent(router, pending);
|
|
1186
|
+
}
|
|
1187
|
+
router._flights?.clear();
|
|
1188
|
+
router.clearCache();
|
|
1189
|
+
router._refreshNextLoad = true;
|
|
1190
|
+
await loadClientRoute(router, { sync: true });
|
|
1191
|
+
}
|
|
1192
|
+
function followPreloadRedirect(router, result, location, owner, redirects) {
|
|
1193
|
+
if (result[0] === REDIRECTED && !result[1].options.reloadDocument && router._tx === owner) return preloadClientRoute(router, {
|
|
1194
|
+
...result[1].options,
|
|
1195
|
+
_fromLocation: location
|
|
1196
|
+
}, redirects + 1);
|
|
1197
|
+
}
|
|
1198
|
+
async function preloadClientRoute(router, opts, redirects = 0) {
|
|
1199
|
+
if (redirects > 20) return;
|
|
1200
|
+
const owner = router._tx;
|
|
1201
|
+
if (process.env.NODE_ENV !== "production" && (router._refreshNextLoad || owner?.[6])) return;
|
|
1202
|
+
const location = opts._builtLocation ?? router.buildLocation(opts);
|
|
1203
|
+
const base = router._committed;
|
|
1204
|
+
const controller = new AbortController();
|
|
1205
|
+
let matches;
|
|
1206
|
+
let preload;
|
|
1207
|
+
let replaced;
|
|
1208
|
+
try {
|
|
1209
|
+
const pending = router._preloads?.get(location.href);
|
|
1210
|
+
if (pending) {
|
|
1211
|
+
if (samePreloadLane(pending, router, location, redirects)) {
|
|
1212
|
+
const result = await pending[2];
|
|
1213
|
+
return isControl(result) ? followPreloadRedirect(router, result, location, owner, redirects) : result[1];
|
|
1214
|
+
}
|
|
1215
|
+
router._preloads.delete(location.href);
|
|
1216
|
+
replaced = pending;
|
|
1217
|
+
}
|
|
1218
|
+
matches = router.matchRoutes(location, { _controller: controller });
|
|
1219
|
+
acquireMatchResources(matches);
|
|
1220
|
+
const promise = Promise.resolve().then(() => executeClientLane(router, location, matches, [
|
|
1221
|
+
controller,
|
|
1222
|
+
redirects,
|
|
1223
|
+
() => true,
|
|
1224
|
+
base,
|
|
1225
|
+
true
|
|
1226
|
+
])).finally(() => {
|
|
1227
|
+
if (replaced) discardPreload(router, replaced);
|
|
1228
|
+
});
|
|
1229
|
+
preload = [
|
|
1230
|
+
matches,
|
|
1231
|
+
controller,
|
|
1232
|
+
promise,
|
|
1233
|
+
base,
|
|
1234
|
+
laneInputs(router, location),
|
|
1235
|
+
redirects
|
|
1236
|
+
];
|
|
1237
|
+
(router._preloads ??= /* @__PURE__ */ new Map()).set(location.href, preload);
|
|
1238
|
+
const result = await promise;
|
|
1239
|
+
if (router._preloads?.get(location.href) !== preload) return isControl(result) ? void 0 : result[1];
|
|
1240
|
+
router._preloads.delete(location.href);
|
|
1241
|
+
if (isControl(result)) {
|
|
1242
|
+
controller.abort();
|
|
1243
|
+
transferMatchResources(router, matches);
|
|
1244
|
+
return followPreloadRedirect(router, result, location, owner, redirects);
|
|
1245
|
+
}
|
|
1246
|
+
transferMatchResources(router, result[1]);
|
|
1247
|
+
controller.abort();
|
|
1248
|
+
return result[1];
|
|
1249
|
+
} catch (cause) {
|
|
1250
|
+
if (!preload || router._preloads?.get(location.href) === preload) {
|
|
1251
|
+
if (preload) router._preloads.delete(location.href);
|
|
1252
|
+
controller.abort();
|
|
1253
|
+
if (matches) transferMatchResources(router, matches);
|
|
1254
|
+
}
|
|
1255
|
+
if (router._tx !== owner) return;
|
|
1256
|
+
if (!isNotFound(cause)) console.error(cause);
|
|
1257
|
+
return;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
async function hydrate(router) {
|
|
1261
|
+
if (process.env.NODE_ENV !== "production" && !window.$_TSR) throw new Error("Invariant failed: Expected to find bootstrap data on window.$_TSR, but we did not. Please file an issue!");
|
|
1262
|
+
const tsr = window.$_TSR;
|
|
1263
|
+
const adapters = router.options.serializationAdapters;
|
|
1264
|
+
if (adapters?.length) {
|
|
1265
|
+
tsr.t = new Map(adapters.map((adapter) => [adapter.key, adapter.fromSerializable]));
|
|
1266
|
+
tsr.buffer.forEach((script) => script());
|
|
1267
|
+
}
|
|
1268
|
+
tsr.initialized = true;
|
|
1269
|
+
const dehydratedRouter = tsr.router;
|
|
1270
|
+
if (process.env.NODE_ENV !== "production" && !dehydratedRouter) throw new Error("Invariant failed: Expected to find a dehydrated data on window.$_TSR.router, but we did not. Please file an issue!");
|
|
1271
|
+
router.ssr = { manifest: dehydratedRouter.manifest };
|
|
1272
|
+
const nonce = document.querySelector("meta[property=\"csp-nonce\"]")?.content;
|
|
1273
|
+
router.options.ssr = { nonce };
|
|
1274
|
+
const dehydratedMatches = dehydratedRouter.matches;
|
|
1275
|
+
const controller = new AbortController();
|
|
1276
|
+
const previousPreflight = router._preflight;
|
|
1277
|
+
router._preflight = controller;
|
|
1278
|
+
previousPreflight?.abort();
|
|
1279
|
+
const retire = (cause) => {
|
|
1280
|
+
if (router._preflight === controller) router._preflight = void 0;
|
|
1281
|
+
controller.abort(cause);
|
|
1282
|
+
return false;
|
|
1283
|
+
};
|
|
1284
|
+
const isCurrent = () => !router._tx && router._preflight === controller && !controller.signal.aborted || retire();
|
|
1285
|
+
let location;
|
|
1286
|
+
let candidates;
|
|
1287
|
+
let handoffInputs;
|
|
1288
|
+
try {
|
|
1289
|
+
await waitFor(router.options.hydrate?.(dehydratedRouter.dehydratedData), controller.signal);
|
|
1290
|
+
if (!isCurrent()) return;
|
|
1291
|
+
router.updateLatestLocation();
|
|
1292
|
+
location = router.latestLocation;
|
|
1293
|
+
router.stores.location.set(location);
|
|
1294
|
+
handoffInputs = laneInputs(router, location);
|
|
1295
|
+
candidates = router.matchRoutes(location, { _controller: controller });
|
|
1296
|
+
} catch (cause) {
|
|
1297
|
+
retire(cause);
|
|
1298
|
+
if (cause !== controller.signal) throw cause;
|
|
1299
|
+
}
|
|
1300
|
+
if (!isCurrent()) return;
|
|
1301
|
+
const committed = [];
|
|
1302
|
+
let pendingBoundary;
|
|
1303
|
+
let verifiedAssetEnd = 0;
|
|
1304
|
+
const retryFrom = (index) => {
|
|
1305
|
+
verifiedAssetEnd = Math.min(verifiedAssetEnd, index + 1);
|
|
1306
|
+
const removed = committed.splice(index);
|
|
1307
|
+
for (const match of removed) if (getRoute(router, match).options.loader && (match.status === "success" || !match.invalid && "loaderData" in match)) cacheLoaderMatch(router, {
|
|
1308
|
+
...match,
|
|
1309
|
+
status: "success",
|
|
1310
|
+
error: void 0,
|
|
1311
|
+
preload: true
|
|
1312
|
+
}, router._cache.get(match.id));
|
|
1313
|
+
transferMatchResources(router, removed);
|
|
1314
|
+
};
|
|
1315
|
+
const shared = dehydratedMatches.length > candidates.length ? candidates.findIndex((match) => match._notFound) + 1 : dehydratedMatches.length;
|
|
1316
|
+
let isTerminal = false;
|
|
1317
|
+
for (let index = 0; index < shared; index++) {
|
|
1318
|
+
const candidate = candidates[index];
|
|
1319
|
+
const dehydrated = dehydratedMatches[index];
|
|
1320
|
+
if (typeof dehydrated.i !== "string" || hydrateSsrMatchId(dehydrated.i) !== candidate.id) {
|
|
1321
|
+
pendingBoundary ??= index;
|
|
1322
|
+
break;
|
|
1323
|
+
}
|
|
1324
|
+
verifiedAssetEnd = index + 1;
|
|
1325
|
+
const route = getRoute(router, candidate);
|
|
1326
|
+
if ("l" in dehydrated || dehydrated.s === "success" && dehydrated.e === void 0 && route.options.loader) candidate.loaderData = dehydrated.l;
|
|
1327
|
+
candidate.status = dehydrated.s;
|
|
1328
|
+
candidate.ssr = dehydrated.ssr;
|
|
1329
|
+
route.options.ssr = candidate.ssr;
|
|
1330
|
+
candidate.updatedAt = dehydrated.u;
|
|
1331
|
+
candidate.error = dehydrated.e;
|
|
1332
|
+
candidate._notFound ||= dehydrated.g;
|
|
1333
|
+
if (candidate.status === "error" || candidate.status === "notFound" || candidate._notFound) {
|
|
1334
|
+
isTerminal = true;
|
|
1335
|
+
committed.push(candidate);
|
|
1336
|
+
if (candidate.ssr === false || candidate.ssr === "data-only") pendingBoundary ??= index;
|
|
1337
|
+
break;
|
|
1338
|
+
}
|
|
1339
|
+
if (candidate.status === "pending") {
|
|
1340
|
+
pendingBoundary ??= index;
|
|
1341
|
+
break;
|
|
1342
|
+
}
|
|
1343
|
+
committed.push(candidate);
|
|
1344
|
+
if (candidate.ssr === "data-only") pendingBoundary ??= index;
|
|
1345
|
+
}
|
|
1346
|
+
let verifiedContextEnd = verifiedAssetEnd;
|
|
1347
|
+
if (!isTerminal && committed.length === shared && shared < candidates.length) pendingBoundary = shared;
|
|
1348
|
+
const chunks = committed.map(async (match) => {
|
|
1349
|
+
try {
|
|
1350
|
+
const route = getRoute(router, match);
|
|
1351
|
+
if (match._notFound) await Promise.all([loadRouteChunk(route), loadRouteChunk(route, "notFoundComponent")]);
|
|
1352
|
+
else await loadRouteChunk(route, match.status === "error" ? "errorComponent" : match.status === "notFound" ? "notFoundComponent" : void 0);
|
|
1353
|
+
return true;
|
|
1354
|
+
} catch {
|
|
1355
|
+
return false;
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
let chunkFailure = 0;
|
|
1359
|
+
try {
|
|
1360
|
+
while (chunkFailure < chunks.length && await waitFor(chunks[chunkFailure], controller.signal)) chunkFailure++;
|
|
1361
|
+
} catch {
|
|
1362
|
+
isCurrent();
|
|
1363
|
+
return;
|
|
1364
|
+
}
|
|
1365
|
+
if (!isCurrent()) return;
|
|
1366
|
+
if (chunkFailure < committed.length) {
|
|
1367
|
+
verifiedContextEnd = Math.min(verifiedContextEnd, chunkFailure);
|
|
1368
|
+
retryFrom(chunkFailure);
|
|
1369
|
+
}
|
|
1370
|
+
const contextEnd = Math.max(pendingBoundary === committed.length ? committed.length + 1 : committed.length, verifiedContextEnd);
|
|
1371
|
+
for (let index = 0; index < contextEnd; index++) {
|
|
1372
|
+
const match = candidates[index];
|
|
1373
|
+
const route = getRoute(router, match);
|
|
1374
|
+
const parentContext = candidates[index - 1]?.context ?? router.options.context ?? {};
|
|
1375
|
+
let routeContext;
|
|
1376
|
+
if (route.options.context) {
|
|
1377
|
+
try {
|
|
1378
|
+
routeContext = match._ctx = route.options.context({
|
|
1379
|
+
deps: match.loaderDeps,
|
|
1380
|
+
params: match.params,
|
|
1381
|
+
context: parentContext,
|
|
1382
|
+
location,
|
|
1383
|
+
navigate: navigateFrom(router, location),
|
|
1384
|
+
buildLocation: router.buildLocation,
|
|
1385
|
+
cause: match.cause,
|
|
1386
|
+
abortController: controller,
|
|
1387
|
+
preload: false,
|
|
1388
|
+
matches: candidates,
|
|
1389
|
+
routeId: route.id
|
|
1390
|
+
}) || {};
|
|
1391
|
+
} catch {
|
|
1392
|
+
if (!isCurrent()) return;
|
|
1393
|
+
if (match.status !== "error" && match.status !== "notFound" && !match._notFound) {
|
|
1394
|
+
retryFrom(index);
|
|
1395
|
+
break;
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
if (!isCurrent()) return;
|
|
1399
|
+
}
|
|
1400
|
+
match.context = {
|
|
1401
|
+
...parentContext,
|
|
1402
|
+
...routeContext,
|
|
1403
|
+
...committed[index] && dehydratedMatches[index].b
|
|
1404
|
+
};
|
|
1405
|
+
}
|
|
1406
|
+
await projectLane(router, [location, candidates], controller.signal, 0, verifiedAssetEnd);
|
|
1407
|
+
if (!isCurrent()) return;
|
|
1408
|
+
const needsClientLoad = pendingBoundary !== void 0 || committed.length < shared;
|
|
1409
|
+
const committedMatches = isTerminal && committed.length === shared ? candidates : committed;
|
|
1410
|
+
let presented = needsClientLoad ? candidates : committedMatches;
|
|
1411
|
+
let dataOnlyAssetEnd;
|
|
1412
|
+
if (needsClientLoad && pendingBoundary !== void 0) {
|
|
1413
|
+
const boundary = presented[pendingBoundary];
|
|
1414
|
+
dataOnlyAssetEnd = boundary.status === "success" && boundary.ssr === "data-only" && boundary.error === void 0 && !boundary._notFound && verifiedAssetEnd > pendingBoundary + 1 ? verifiedAssetEnd : void 0;
|
|
1415
|
+
presented = presented.slice();
|
|
1416
|
+
presented[pendingBoundary] = {
|
|
1417
|
+
...boundary,
|
|
1418
|
+
status: "pending",
|
|
1419
|
+
ssr: boundary.ssr === "data-only" ? "data-only" : false,
|
|
1420
|
+
_assetEnd: dataOnlyAssetEnd
|
|
1421
|
+
};
|
|
1422
|
+
}
|
|
1423
|
+
const claim = () => needsClientLoad && !router._tx && router.latestLocation.state === location.state && deepEqual(handoffInputs, laneInputs(router, router.latestLocation)) && router._committed === committedMatches && committedMatches.length && !controller.signal.aborted ? controller : void 0;
|
|
1424
|
+
const handoff = [claim, (matches) => {
|
|
1425
|
+
if (router._handoff !== handoff) return;
|
|
1426
|
+
const prefix = committedMatches.length;
|
|
1427
|
+
if (!matches || !claim() || committedMatches.some((match, index) => match.id !== matches[index]?.id)) {
|
|
1428
|
+
router._handoff = void 0;
|
|
1429
|
+
controller.abort();
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
let handoffAssetEnd = dataOnlyAssetEnd;
|
|
1433
|
+
if (handoffAssetEnd !== void 0) {
|
|
1434
|
+
for (let index = prefix; index < handoffAssetEnd; index++) if (candidates[index]?.id !== matches[index]?.id) {
|
|
1435
|
+
handoffAssetEnd = index > (pendingBoundary ?? -1) + 1 ? index : void 0;
|
|
1436
|
+
break;
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
const clones = committedMatches.map((match) => ({ ...match }));
|
|
1440
|
+
if (handoffAssetEnd !== void 0) clones[pendingBoundary]._assetEnd = handoffAssetEnd;
|
|
1441
|
+
transferMatchResources(router, matches.splice(0, prefix, ...clones));
|
|
1442
|
+
for (let index = prefix; index < matches.length; index++) {
|
|
1443
|
+
const match = matches[index];
|
|
1444
|
+
const hydrated = candidates[index];
|
|
1445
|
+
if (hydrated?.id === match.id && hydrated._ctx) match._ctx = hydrated._ctx;
|
|
1446
|
+
match.abortController = controller;
|
|
1447
|
+
}
|
|
1448
|
+
return prefix;
|
|
1449
|
+
}];
|
|
1450
|
+
router._committed = committedMatches;
|
|
1451
|
+
router._handoff = handoff;
|
|
1452
|
+
router._preflight = void 0;
|
|
1453
|
+
router.batch(() => {
|
|
1454
|
+
router.stores.setMatches(presented);
|
|
1455
|
+
router.stores.status.set("idle");
|
|
1456
|
+
if (!needsClientLoad) router.stores.resolvedLocation.set(router.stores.location.get());
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
//#endregion
|
|
1460
|
+
export { _getAssetMatches, _getRenderedMatches, hydrate, loadClientRoute, loadRouteChunk, preloadClientRoute, refreshClientRoute, replaceRouteChunk, transferMatchResources };
|
|
1461
|
+
|
|
1462
|
+
//# sourceMappingURL=load-client.js.map
|