UNPKG

129 kB JavaScript View Raw
1/**
2 * react-router v8.5.1
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { PROTOCOL_RELATIVE_URL_REGEX, normalizeProtocolRelativeUrl } from "./url.js";
12import { createBrowserURLImpl, createLocation, createPath, invariant, parsePath, warning } from "./history.js";
13import { DiscoveryCancelledError, ErrorResponseImpl, RouterContextProvider, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createDataFunctionUrl, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, prependBasename, removeDoubleSlashes, resolveTo, stripBasename } from "./utils.js";
14import { consumeInstrumentationClientResultMetaReceiver, getRouteInstrumentationUpdates, instrumentClientSideRouter } from "./instrumentation.js";
15import { V6RegExMatcher } from "./matcher.js";
16import { getRoutePatternMatcher } from "./matcher-route-pattern.preload.js";
17import { validateNavigationTarget } from "./navigation.js";
18//#region lib/router/router.ts
19const validMutationMethodsArr = [
20 "POST",
21 "PUT",
22 "PATCH",
23 "DELETE"
24];
25const validMutationMethods = new Set(validMutationMethodsArr);
26const validRequestMethodsArr = ["GET", ...validMutationMethodsArr];
27const validRequestMethods = new Set(validRequestMethodsArr);
28const redirectStatusCodes = new Set([
29 301,
30 302,
31 303,
32 307,
33 308
34]);
35const redirectPreserveMethodStatusCodes = new Set([307, 308]);
36const IDLE_NAVIGATION = {
37 state: "idle",
38 location: void 0,
39 matches: void 0,
40 historyAction: void 0,
41 formMethod: void 0,
42 formAction: void 0,
43 formEncType: void 0,
44 formData: void 0,
45 json: void 0,
46 text: void 0
47};
48const IDLE_FETCHER = {
49 state: "idle",
50 data: void 0,
51 formMethod: void 0,
52 formAction: void 0,
53 formEncType: void 0,
54 formData: void 0,
55 json: void 0,
56 text: void 0
57};
58const IDLE_BLOCKER = {
59 state: "unblocked",
60 proceed: void 0,
61 reset: void 0,
62 location: void 0
63};
64const TRANSITIONS_STORAGE_KEY = "remix-router-transitions";
65const ResetLoaderDataSymbol = Symbol("ResetLoaderData");
66function createDataRouteMatcher(future, basename) {
67 if (future.unstable_routePatternMatching) {
68 let RoutePatternMatcher = getRoutePatternMatcher();
69 invariant(RoutePatternMatcher, "You must call unstable_preloadRoutePattern() from \"react-router/route-pattern\" before enabling future.unstable_routePatternMatching.");
70 return new RoutePatternMatcher(basename);
71 }
72 return new V6RegExMatcher(basename);
73}
74/**
75* Encapsulates the stable and in-flight route trees together with their
76* pre-computed branch caches so the structures always stay in sync.
77*/
78var DataRoutes = class {
79 #routes;
80 #hmrRoutes;
81 #matcher;
82 constructor(routes, matcher) {
83 this.#routes = routes;
84 this.#matcher = matcher;
85 this.#matcher.update(routes);
86 }
87 /** The stable route tree */
88 get stableRoutes() {
89 return this.#routes;
90 }
91 /** The in-flight route tree if one is active, otherwise the stable tree */
92 get activeRoutes() {
93 return this.#hmrRoutes ?? this.#routes;
94 }
95 get hasHMRRoutes() {
96 return this.#hmrRoutes != null;
97 }
98 /** Replace the stable route tree and recompute its branches */
99 setRoutes(routes) {
100 this.#routes = routes;
101 if (!this.#hmrRoutes) this.#matcher.update(routes);
102 }
103 /** Set a new in-flight route tree and recompute its branches */
104 setHmrRoutes(routes) {
105 this.#hmrRoutes = routes;
106 this.#matcher.update(routes);
107 }
108 /** Commit in-flight routes/branches to the stable slot and clear in-flight */
109 commitHmrRoutes() {
110 if (this.#hmrRoutes) {
111 this.#routes = this.#hmrRoutes;
112 this.#hmrRoutes = void 0;
113 this.#matcher.update(this.#routes);
114 }
115 }
116};
117const clientErrors = /* @__PURE__ */ new WeakMap();
118/**
119* Create a router and listen to history POP navigations
120*/
121function createRouter(init) {
122 const routerWindow = init.window ? init.window : typeof window !== "undefined" ? window : void 0;
123 const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined";
124 invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
125 let hydrationRouteProperties = init.hydrationRouteProperties || [];
126 let _mapRouteProperties = init.mapRouteProperties;
127 let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
128 if (init.instrumentations) {
129 let instrumentations = init.instrumentations;
130 mapRouteProperties = (route) => {
131 return {
132 ..._mapRouteProperties?.(route),
133 ...getRouteInstrumentationUpdates(instrumentations.map((i) => i.route).filter(Boolean), route)
134 };
135 };
136 }
137 let future = { ...init.future };
138 let basename = init.basename || "/";
139 if (!basename.startsWith("/")) basename = `/${basename}`;
140 let dataRouteMatcher = createDataRouteMatcher(future, basename);
141 let manifest = {};
142 let dataRoutes = new DataRoutes(convertRoutesToDataRoutes(init.routes, mapRouteProperties, void 0, manifest), dataRouteMatcher);
143 let dataStrategyImpl = init.dataStrategy || defaultDataStrategyWithMiddleware;
144 let unlistenHistory = null;
145 let subscribers = /* @__PURE__ */ new Set();
146 let savedScrollPositions = null;
147 let getScrollRestorationKey = null;
148 let getScrollPosition = null;
149 let initialScrollRestored = init.hydrationData != null;
150 let initialMatches = dataRouteMatcher.match(init.history.location);
151 let initialMatchesIsFOW = false;
152 let initialErrors = null;
153 let initialized;
154 let renderFallback;
155 if (initialMatches == null && !init.patchRoutesOnNavigation) {
156 let error = getInternalRouterError(404, { pathname: init.history.location.pathname });
157 let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
158 initialized = true;
159 renderFallback = !initialized;
160 initialMatches = matches;
161 initialErrors = { [route.id]: error };
162 } else {
163 if (initialMatches && !init.hydrationData) {
164 if (checkFogOfWar(initialMatches, init.history.location.pathname).active) initialMatches = null;
165 }
166 if (!initialMatches) {
167 initialized = false;
168 renderFallback = !initialized;
169 initialMatches = [];
170 let fogOfWar = checkFogOfWar(null, init.history.location.pathname);
171 if (fogOfWar.active && fogOfWar.matches) {
172 initialMatchesIsFOW = true;
173 initialMatches = fogOfWar.matches;
174 }
175 } else if (initialMatches.some((m) => m.route.lazy)) {
176 initialized = false;
177 renderFallback = !initialized;
178 } else if (!initialMatches.some((m) => routeHasLoaderOrMiddleware(m.route))) {
179 initialized = true;
180 renderFallback = !initialized;
181 } else {
182 let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
183 let errors = init.hydrationData ? init.hydrationData.errors : null;
184 let relevantMatches = initialMatches;
185 if (errors) {
186 let idx = initialMatches.findIndex((m) => errors[m.route.id] !== void 0);
187 relevantMatches = relevantMatches.slice(0, idx + 1);
188 }
189 renderFallback = false;
190 initialized = true;
191 relevantMatches.forEach((m) => {
192 let status = getRouteHydrationStatus(m.route, loaderData, errors);
193 renderFallback = renderFallback || status.renderFallback;
194 initialized = initialized && !status.shouldLoad;
195 });
196 }
197 }
198 let router;
199 let state = {
200 historyAction: init.history.action,
201 location: init.history.location,
202 matches: initialMatches,
203 initialized,
204 renderFallback,
205 navigation: IDLE_NAVIGATION,
206 restoreScrollPosition: init.hydrationData != null ? false : null,
207 preventScrollReset: false,
208 revalidation: "idle",
209 loaderData: init.hydrationData && init.hydrationData.loaderData || {},
210 actionData: init.hydrationData && init.hydrationData.actionData || null,
211 errors: init.hydrationData && init.hydrationData.errors || initialErrors,
212 fetchers: /* @__PURE__ */ new Map(),
213 blockers: /* @__PURE__ */ new Map()
214 };
215 let pendingAction = "POP";
216 let pendingPopstateDelta = 0;
217 let pendingDiscoveryCancellation;
218 let pendingPopstateNavigationDfd = null;
219 let pendingPreventScrollReset = false;
220 let pendingNavigationController;
221 let pendingViewTransitionEnabled = false;
222 let appliedViewTransitions = /* @__PURE__ */ new Map();
223 let removePageHideEventListener = null;
224 let isUninterruptedRevalidation = false;
225 let isRevalidationRequired = false;
226 let cancelledFetcherLoads = /* @__PURE__ */ new Set();
227 let fetchControllers = /* @__PURE__ */ new Map();
228 let incrementingLoadId = 0;
229 let pendingNavigationLoadId = -1;
230 let fetchReloadIds = /* @__PURE__ */ new Map();
231 let fetchRedirectIds = /* @__PURE__ */ new Set();
232 let fetchLoadMatches = /* @__PURE__ */ new Map();
233 let activeFetchers = /* @__PURE__ */ new Map();
234 let fetchersQueuedForDeletion = /* @__PURE__ */ new Set();
235 let blockerFunctions = /* @__PURE__ */ new Map();
236 let unblockBlockerHistoryUpdate = void 0;
237 let pendingRevalidationDfd = null;
238 function initialize() {
239 unlistenHistory = init.history.listen(({ action: historyAction, location, delta }) => {
240 if (unblockBlockerHistoryUpdate) {
241 unblockBlockerHistoryUpdate();
242 unblockBlockerHistoryUpdate = void 0;
243 return;
244 }
245 warning(blockerFunctions.size === 0 || delta != null, "You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL.");
246 let blockerKey = shouldBlockNavigation({
247 currentLocation: state.location,
248 nextLocation: location,
249 historyAction
250 });
251 if (blockerKey && delta != null) {
252 let nextHistoryUpdatePromise = new Promise((resolve) => {
253 unblockBlockerHistoryUpdate = resolve;
254 });
255 init.history.go(delta * -1);
256 updateBlocker(blockerKey, {
257 state: "blocked",
258 location,
259 proceed() {
260 updateBlocker(blockerKey, {
261 state: "proceeding",
262 proceed: void 0,
263 reset: void 0,
264 location
265 });
266 nextHistoryUpdatePromise.then(() => init.history.go(delta));
267 },
268 reset() {
269 let blockers = new Map(state.blockers);
270 blockers.set(blockerKey, IDLE_BLOCKER);
271 updateState({ blockers });
272 }
273 });
274 pendingPopstateNavigationDfd?.resolve();
275 pendingPopstateNavigationDfd = null;
276 return;
277 }
278 if (future.unstable_customRouteDiscovery) pendingPopstateDelta += delta || 0;
279 return startNavigation(historyAction, location);
280 });
281 if (isBrowser) {
282 restoreAppliedTransitions(routerWindow, appliedViewTransitions);
283 let _saveAppliedTransitions = () => persistAppliedTransitions(routerWindow, appliedViewTransitions);
284 routerWindow.addEventListener("pagehide", _saveAppliedTransitions);
285 removePageHideEventListener = () => routerWindow.removeEventListener("pagehide", _saveAppliedTransitions);
286 }
287 if (!state.initialized) startNavigation("POP", state.location, { initialHydration: true });
288 return router;
289 }
290 function dispose() {
291 if (unlistenHistory) unlistenHistory();
292 if (removePageHideEventListener) removePageHideEventListener();
293 subscribers.clear();
294 pendingNavigationController && pendingNavigationController.abort();
295 state.fetchers.forEach((_, key) => deleteFetcher(state.fetchers, key));
296 state.blockers.forEach((_, key) => deleteBlocker(key));
297 }
298 function subscribe(fn) {
299 subscribers.add(fn);
300 return () => subscribers.delete(fn);
301 }
302 function updateState(newState, opts = {}) {
303 let newErrors = newState.errors ?? null;
304 let cachedErrors = newErrors ? clientErrors.get(newErrors) : void 0;
305 if (cachedErrors !== void 0) newErrors = cachedErrors;
306 if (newState.errors !== void 0 || newState.initialized) {
307 if (state.errors) clientErrors.delete(state.errors);
308 if (newState.errors) clientErrors.set(newState.errors, newErrors);
309 }
310 if (newState.matches) newState.matches = newState.matches.map((m) => {
311 let route = manifest[m.route.id];
312 let matchRoute = m.route;
313 if (matchRoute.element !== route.element || matchRoute.errorElement !== route.errorElement || matchRoute.hydrateFallbackElement !== route.hydrateFallbackElement) return {
314 ...m,
315 route
316 };
317 return m;
318 });
319 state = {
320 ...state,
321 ...newState
322 };
323 let unmountedFetchers = [];
324 let mountedFetchers = [];
325 state.fetchers.forEach((fetcher, key) => {
326 if (fetcher.state === "idle") if (fetchersQueuedForDeletion.has(key)) unmountedFetchers.push(key);
327 else mountedFetchers.push(key);
328 });
329 fetchersQueuedForDeletion.forEach((key) => {
330 if (!state.fetchers.has(key) && !fetchControllers.has(key)) unmountedFetchers.push(key);
331 });
332 [...subscribers].forEach((subscriber) => subscriber(state, {
333 deletedFetchers: unmountedFetchers,
334 newErrors,
335 viewTransitionOpts: opts.viewTransitionOpts,
336 flushSync: opts.flushSync === true
337 }));
338 unmountedFetchers.forEach((key) => deleteFetcher(state.fetchers, key));
339 mountedFetchers.forEach((key) => state.fetchers.delete(key));
340 }
341 function completeNavigation(location, newState, { flushSync } = {}) {
342 let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === "loading" && location.state?._isRedirect !== true;
343 let actionData;
344 if (newState.actionData) if (Object.keys(newState.actionData).length > 0) actionData = newState.actionData;
345 else actionData = null;
346 else if (isActionReload) actionData = state.actionData;
347 else actionData = null;
348 let loaderData = newState.loaderData ? mergeLoaderData(state.loaderData, newState.loaderData, newState.matches || [], newState.errors) : state.loaderData;
349 let blockers = state.blockers;
350 if (blockers.size > 0 && !isUninterruptedRevalidation) {
351 blockers = new Map(blockers);
352 blockers.forEach((_, k) => blockers.set(k, IDLE_BLOCKER));
353 }
354 let restoreScrollPosition = isUninterruptedRevalidation ? false : getSavedScrollPosition(location, newState.matches || state.matches);
355 let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && location.state?._isRedirect !== true;
356 dataRoutes.commitHmrRoutes();
357 if (isUninterruptedRevalidation) {} else if (pendingAction === "POP") {} else if (pendingAction === "PUSH") init.history.push(location, location.state);
358 else if (pendingAction === "REPLACE") init.history.replace(location, location.state);
359 let viewTransitionOpts;
360 if (pendingAction === "POP" && !isUninterruptedRevalidation && location !== state.location) {
361 let priorPaths = appliedViewTransitions.get(state.location.pathname);
362 if (priorPaths && priorPaths.has(location.pathname)) viewTransitionOpts = {
363 currentLocation: state.location,
364 nextLocation: location
365 };
366 else if (appliedViewTransitions.has(location.pathname)) viewTransitionOpts = {
367 currentLocation: location,
368 nextLocation: state.location
369 };
370 } else if (pendingViewTransitionEnabled) {
371 let toPaths = appliedViewTransitions.get(state.location.pathname);
372 if (toPaths) toPaths.add(location.pathname);
373 else {
374 toPaths = new Set([location.pathname]);
375 appliedViewTransitions.set(state.location.pathname, toPaths);
376 }
377 viewTransitionOpts = {
378 currentLocation: state.location,
379 nextLocation: location
380 };
381 }
382 updateState({
383 ...newState,
384 actionData,
385 loaderData,
386 historyAction: pendingAction,
387 location,
388 initialized: true,
389 renderFallback: false,
390 navigation: IDLE_NAVIGATION,
391 revalidation: "idle",
392 restoreScrollPosition,
393 preventScrollReset,
394 blockers
395 }, {
396 viewTransitionOpts,
397 flushSync: flushSync === true
398 });
399 resetPendingNavigation();
400 pendingPopstateNavigationDfd?.resolve();
401 pendingPopstateNavigationDfd = null;
402 pendingRevalidationDfd?.resolve();
403 pendingRevalidationDfd = null;
404 }
405 function resetPendingNavigation() {
406 pendingPopstateDelta = 0;
407 pendingAction = "POP";
408 pendingPreventScrollReset = false;
409 pendingViewTransitionEnabled = false;
410 isUninterruptedRevalidation = false;
411 isRevalidationRequired = fetchReloadIds.size > 0;
412 }
413 async function navigate(to, opts) {
414 if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
415 pendingPopstateNavigationDfd?.resolve();
416 pendingPopstateNavigationDfd = null;
417 if (typeof to === "number") {
418 if (!pendingPopstateNavigationDfd) pendingPopstateNavigationDfd = createDeferred();
419 let promise = pendingPopstateNavigationDfd.promise;
420 init.history.go(to);
421 return promise;
422 }
423 let instrumentationNavigateMetaReceiver = consumeInstrumentationClientResultMetaReceiver(router);
424 let { path, submission, error } = normalizeNavigateOptions(false, normalizeTo(state.location, state.matches, basename, to, opts?.fromRouteId, opts?.relative), opts);
425 let maskPath;
426 if (opts?.mask) {
427 let partialPath = typeof opts.mask === "string" ? parsePath(opts.mask) : {
428 ...state.location.mask,
429 ...opts.mask
430 };
431 maskPath = {
432 pathname: partialPath.pathname ?? "",
433 search: partialPath.search ?? "",
434 hash: partialPath.hash ?? ""
435 };
436 if (PROTOCOL_RELATIVE_URL_REGEX.test(maskPath.pathname)) throw new Error("External navigation is not allowed");
437 else if (maskPath.pathname.startsWith("\\")) maskPath.pathname = maskPath.pathname.replace(/^\\+/, "/");
438 validateNavigationTarget(typeof opts.mask === "string" ? opts.mask : createPath(opts.mask), createPath(maskPath), init.history.createURL("/"), "reject");
439 }
440 let currentLocation = state.location;
441 let nextLocation = createLocation(currentLocation, path, opts && opts.state, void 0, maskPath);
442 nextLocation = {
443 ...nextLocation,
444 ...init.history.encodeLocation(nextLocation)
445 };
446 validateNavigationTarget(to == null ? init.history.createHref(state.location) : typeof to === "string" ? to : createPath(to), init.history.createHref(nextLocation.mask || nextLocation), init.history.createURL("/"), "reject");
447 let userReplace = opts && opts.replace != null ? opts.replace : void 0;
448 let historyAction = "PUSH";
449 let currentPath = state.location.pathname + state.location.search;
450 if (userReplace === true) historyAction = "REPLACE";
451 else if (userReplace === false) {} else if (submission != null && isMutationMethod(submission.formMethod) && (submission.formAction === currentPath || nextLocation.pathname + nextLocation.search === currentPath)) historyAction = "REPLACE";
452 let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : void 0;
453 let flushSync = (opts && opts.flushSync) === true;
454 let blockerKey = shouldBlockNavigation({
455 currentLocation,
456 nextLocation,
457 historyAction
458 });
459 if (blockerKey) {
460 updateBlocker(blockerKey, {
461 state: "blocked",
462 location: nextLocation,
463 proceed() {
464 updateBlocker(blockerKey, {
465 state: "proceeding",
466 proceed: void 0,
467 reset: void 0,
468 location: nextLocation
469 });
470 navigate(to, opts);
471 },
472 reset() {
473 let blockers = new Map(state.blockers);
474 blockers.set(blockerKey, IDLE_BLOCKER);
475 updateState({ blockers });
476 }
477 });
478 return;
479 }
480 await startNavigation(historyAction, nextLocation, {
481 submission,
482 pendingError: error,
483 preventScrollReset,
484 replace: opts && opts.replace,
485 enableViewTransition: opts && opts.viewTransition,
486 flushSync,
487 callSiteDefaultShouldRevalidate: opts && opts.defaultShouldRevalidate,
488 instrumentationNavigateMetaReceiver
489 });
490 }
491 function revalidate() {
492 if (pendingDiscoveryCancellation) return pendingDiscoveryCancellation.then(revalidate);
493 if (!pendingRevalidationDfd) pendingRevalidationDfd = createDeferred();
494 interruptActiveLoads();
495 updateState({ revalidation: "loading" });
496 let promise = pendingRevalidationDfd.promise;
497 if (state.navigation.state === "submitting") return promise;
498 if (state.navigation.state === "idle") {
499 startNavigation(state.historyAction, state.location, {
500 startUninterruptedRevalidation: true,
501 dataStrategyInitiator: "revalidation"
502 });
503 return promise;
504 }
505 startNavigation(pendingAction || state.historyAction, state.navigation.location, {
506 overrideNavigation: state.navigation,
507 dataStrategyInitiator: "revalidation",
508 enableViewTransition: pendingViewTransitionEnabled === true
509 });
510 return promise;
511 }
512 async function startNavigation(historyAction, location, opts) {
513 if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
514 pendingNavigationController && pendingNavigationController.abort();
515 pendingNavigationController = null;
516 pendingAction = historyAction;
517 isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true;
518 saveScrollPosition(state.location, state.matches);
519 pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
520 pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
521 let matches = opts?.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? state.matches : dataRouteMatcher.match(location);
522 let flushSync = (opts && opts.flushSync) === true;
523 if (matches && state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
524 completeNavigation(location, { matches }, { flushSync });
525 return;
526 }
527 let fogOfWar = checkFogOfWar(matches, location.pathname);
528 if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
529 if (opts?.instrumentationNavigateMetaReceiver) {
530 let meta = getInstrumentationNavigateMeta(init.history, location, matches);
531 opts.instrumentationNavigateMetaReceiver(meta);
532 }
533 if (!matches) {
534 let { error, notFoundMatches, route } = handleNavigational404(location.pathname);
535 completeNavigation(location, {
536 matches: notFoundMatches,
537 loaderData: {},
538 errors: { [route.id]: error }
539 }, { flushSync });
540 return;
541 }
542 let loadingNavigation = opts && opts.overrideNavigation ? {
543 ...opts.overrideNavigation,
544 matches,
545 historyAction
546 } : void 0;
547 pendingNavigationController = new AbortController();
548 let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
549 let dataStrategyInitiator = opts?.initialHydration ? "initialization" : opts?.dataStrategyInitiator ?? "navigation";
550 let scopedContext = init.getContext ? await init.getContext() : new RouterContextProvider();
551 let pendingActionResult;
552 if (opts && opts.pendingError) pendingActionResult = [findNearestBoundary(matches).route.id, {
553 type: "error",
554 error: opts.pendingError
555 }];
556 else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
557 let actionResult = await handleAction(request, dataStrategyInitiator, location, opts.submission, matches, historyAction, scopedContext, fogOfWar.active, opts && opts.initialHydration === true, {
558 replace: opts.replace,
559 flushSync
560 });
561 if (actionResult.shortCircuited) return;
562 if (actionResult.pendingActionResult) {
563 let [routeId, result] = actionResult.pendingActionResult;
564 if (isErrorResult(result) && isRouteErrorResponse(result.error) && result.error.status === 404) {
565 pendingNavigationController = null;
566 completeNavigation(location, {
567 matches: actionResult.matches,
568 loaderData: {},
569 errors: { [routeId]: result.error }
570 });
571 return;
572 }
573 }
574 matches = actionResult.matches || matches;
575 pendingActionResult = actionResult.pendingActionResult;
576 loadingNavigation = getLoadingNavigation(location, matches, historyAction, opts.submission);
577 flushSync = false;
578 fogOfWar.active = false;
579 request = createClientSideRequest(init.history, request.url, request.signal);
580 }
581 let { shortCircuited, matches: updatedMatches, loaderData, errors, workingFetchers } = await handleLoaders(request, dataStrategyInitiator, location, matches, historyAction, scopedContext, fogOfWar.active, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionResult, opts && opts.callSiteDefaultShouldRevalidate);
582 if (shortCircuited) return;
583 pendingNavigationController = null;
584 completeNavigation(location, {
585 matches: updatedMatches || matches,
586 ...getActionDataForCommit(pendingActionResult),
587 loaderData,
588 errors,
589 ...workingFetchers ? { fetchers: workingFetchers } : {}
590 });
591 }
592 function cancelDiscoveryNavigation() {
593 let cancellation = createDeferred();
594 pendingDiscoveryCancellation = cancellation.promise;
595 let delta = pendingPopstateDelta;
596 let popstateNavigationDfd = pendingPopstateNavigationDfd;
597 pendingPopstateNavigationDfd = null;
598 pendingNavigationController?.abort();
599 pendingNavigationController = null;
600 resetPendingNavigation();
601 pendingRevalidationDfd?.resolve();
602 pendingRevalidationDfd = null;
603 let blockers = new Map(state.blockers);
604 blockers.forEach((blocker, key) => {
605 if (blocker.state === "proceeding") blockers.set(key, IDLE_BLOCKER);
606 });
607 let fetchers = new Map(state.fetchers);
608 let didUpdateFetchers = markFetchRedirectsDone(fetchers);
609 fetchers.forEach((fetcher, key) => {
610 if (fetcher.state === "loading" && !fetchControllers.has(key)) {
611 fetchers.set(key, getDoneFetcher(fetcher.data));
612 didUpdateFetchers = true;
613 }
614 });
615 cancelledFetcherLoads.clear();
616 updateState({
617 navigation: IDLE_NAVIGATION,
618 revalidation: "idle",
619 blockers,
620 ...didUpdateFetchers ? { fetchers } : {}
621 });
622 function finishCancellation() {
623 pendingDiscoveryCancellation = void 0;
624 popstateNavigationDfd?.resolve();
625 cancellation.resolve();
626 }
627 if (delta) {
628 unblockBlockerHistoryUpdate = finishCancellation;
629 init.history.go(-delta);
630 } else finishCancellation();
631 return cancellation.promise;
632 }
633 async function handleAction(request, dataStrategyInitiator, location, submission, matches, historyAction, scopedContext, isFogOfWar, initialHydration, opts = {}) {
634 if (!future.unstable_customRouteDiscovery) interruptActiveLoads();
635 updateState({ navigation: getSubmittingNavigation(location, matches, historyAction, submission) }, { flushSync: opts.flushSync === true });
636 if (isFogOfWar) {
637 let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
638 if (future.unstable_customRouteDiscovery && request.signal.aborted || discoverResult.type === "aborted") return { shortCircuited: true };
639 else if (discoverResult.type === "cancelled") {
640 await cancelDiscoveryNavigation();
641 return { shortCircuited: true };
642 } else if (discoverResult.type === "error") {
643 if (discoverResult.partialMatches.length === 0) {
644 let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
645 return {
646 matches,
647 pendingActionResult: [route.id, {
648 type: "error",
649 error: discoverResult.error
650 }]
651 };
652 }
653 let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
654 return {
655 matches: discoverResult.partialMatches,
656 pendingActionResult: [boundaryId, {
657 type: "error",
658 error: discoverResult.error
659 }]
660 };
661 } else if (!discoverResult.matches) {
662 let { notFoundMatches, error, route } = handleNavigational404(location.pathname);
663 return {
664 matches: notFoundMatches,
665 pendingActionResult: [route.id, {
666 type: "error",
667 error
668 }]
669 };
670 } else matches = discoverResult.matches;
671 }
672 if (future.unstable_customRouteDiscovery) interruptActiveLoads();
673 let result;
674 let actionMatch = getTargetMatch(matches, location);
675 if (!actionMatch.route.action && !actionMatch.route.lazy) result = {
676 type: "error",
677 error: getInternalRouterError(405, {
678 method: request.method,
679 pathname: location.pathname,
680 routeId: actionMatch.route.id
681 })
682 };
683 else {
684 let results = await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, initialHydration ? [] : hydrationRouteProperties, scopedContext), scopedContext, null, dataStrategyInitiator, historyAction);
685 result = results[actionMatch.route.id];
686 if (!result) {
687 for (let match of matches) if (results[match.route.id]) {
688 result = results[match.route.id];
689 break;
690 }
691 }
692 if (request.signal.aborted) return { shortCircuited: true };
693 }
694 if (isRedirectResult(result)) {
695 let replace;
696 if (opts && opts.replace != null) replace = opts.replace;
697 else replace = normalizeRedirectLocation(result.response.headers.get("Location"), new URL(request.url), basename, init.history) === state.location.pathname + state.location.search;
698 await startRedirectNavigation(request, result, true, dataStrategyInitiator, {
699 submission,
700 replace
701 });
702 return { shortCircuited: true };
703 }
704 if (isErrorResult(result)) {
705 let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
706 if ((opts && opts.replace) !== true) pendingAction = "PUSH";
707 return {
708 matches,
709 pendingActionResult: [
710 boundaryMatch.route.id,
711 result,
712 actionMatch.route.id
713 ]
714 };
715 }
716 return {
717 matches,
718 pendingActionResult: [actionMatch.route.id, result]
719 };
720 }
721 async function handleLoaders(request, dataStrategyInitiator, location, matches, historyAction, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult, callSiteDefaultShouldRevalidate) {
722 let loadingNavigation = overrideNavigation || getLoadingNavigation(location, matches, historyAction, submission);
723 let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
724 let shouldUpdateNavigationState = !isUninterruptedRevalidation && !initialHydration;
725 if (isFogOfWar) {
726 if (shouldUpdateNavigationState) {
727 let actionData = getUpdatedActionData(pendingActionResult);
728 updateState({
729 navigation: loadingNavigation,
730 ...actionData !== void 0 ? { actionData } : {}
731 }, { flushSync });
732 }
733 let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
734 if (future.unstable_customRouteDiscovery && request.signal.aborted || discoverResult.type === "aborted") return { shortCircuited: true };
735 else if (discoverResult.type === "cancelled") {
736 await cancelDiscoveryNavigation();
737 return { shortCircuited: true };
738 } else if (discoverResult.type === "error") {
739 if (discoverResult.partialMatches.length === 0) {
740 let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
741 return {
742 matches,
743 loaderData: {},
744 errors: { [route.id]: discoverResult.error }
745 };
746 }
747 let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id;
748 return {
749 matches: discoverResult.partialMatches,
750 loaderData: {},
751 errors: { [boundaryId]: discoverResult.error }
752 };
753 } else if (!discoverResult.matches) {
754 let { error, notFoundMatches, route } = handleNavigational404(location.pathname);
755 return {
756 matches: notFoundMatches,
757 loaderData: {},
758 errors: { [route.id]: error }
759 };
760 } else {
761 matches = discoverResult.matches;
762 if (initialHydration && !request.signal.aborted) updateState({ matches });
763 }
764 }
765 let { dsMatches, revalidatingFetchers } = getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, init.history, state, matches, activeSubmission, location, initialHydration ? [] : hydrationRouteProperties, initialHydration === true, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, pendingActionResult, callSiteDefaultShouldRevalidate);
766 pendingNavigationLoadId = ++incrementingLoadId;
767 if (!init.dataStrategy && !dsMatches.some((m) => m.shouldLoad) && !dsMatches.some((m) => m.route.middleware && m.route.middleware.length > 0) && revalidatingFetchers.length === 0) {
768 let workingFetchers = new Map(state.fetchers);
769 let didUpdateFetcherRedirects = markFetchRedirectsDone(workingFetchers);
770 completeNavigation(location, {
771 matches,
772 loaderData: {},
773 errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
774 ...getActionDataForCommit(pendingActionResult),
775 ...didUpdateFetcherRedirects ? { fetchers: workingFetchers } : {}
776 }, { flushSync });
777 return { shortCircuited: true };
778 }
779 if (shouldUpdateNavigationState) {
780 let updates = {};
781 if (!isFogOfWar) {
782 updates.navigation = loadingNavigation;
783 let actionData = getUpdatedActionData(pendingActionResult);
784 if (actionData !== void 0) updates.actionData = actionData;
785 }
786 if (revalidatingFetchers.length > 0) updates.fetchers = getUpdatedRevalidatingFetchers(revalidatingFetchers);
787 updateState(updates, { flushSync });
788 }
789 revalidatingFetchers.forEach((rf) => {
790 abortFetcher(rf.key);
791 if (rf.controller) fetchControllers.set(rf.key, rf.controller);
792 });
793 let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => {
794 if (fetchControllers.get(rf.key) === rf.controller) abortFetcher(rf.key);
795 });
796 if (pendingNavigationController) pendingNavigationController.signal.addEventListener("abort", abortPendingFetchRevalidations);
797 let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, request, location, scopedContext, dataStrategyInitiator, historyAction);
798 if (request.signal.aborted) return { shortCircuited: true };
799 if (pendingNavigationController) pendingNavigationController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
800 revalidatingFetchers.forEach((rf) => {
801 if (fetchControllers.get(rf.key) === rf.controller) fetchControllers.delete(rf.key);
802 });
803 let redirect = findRedirect(loaderResults);
804 if (redirect) {
805 await startRedirectNavigation(request, redirect.result, true, dataStrategyInitiator, { replace });
806 return { shortCircuited: true };
807 }
808 redirect = findRedirect(fetcherResults);
809 if (redirect) {
810 fetchRedirectIds.add(redirect.key);
811 await startRedirectNavigation(request, redirect.result, true, dataStrategyInitiator, { replace });
812 return { shortCircuited: true };
813 }
814 let workingFetchers = new Map(state.fetchers);
815 let { loaderData, errors } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers);
816 if (initialHydration && state.errors) {
817 let newClientErrors = errors ?? null;
818 errors = {
819 ...state.errors,
820 ...errors
821 };
822 clientErrors.set(errors, newClientErrors);
823 }
824 let didUpdateFetcherRedirects = markFetchRedirectsDone(workingFetchers);
825 let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId, workingFetchers);
826 let shouldUpdateFetchers = didUpdateFetcherRedirects || didAbortFetchLoads || revalidatingFetchers.length > 0;
827 return {
828 matches,
829 loaderData,
830 errors,
831 ...shouldUpdateFetchers ? { workingFetchers } : {}
832 };
833 }
834 function getUpdatedActionData(pendingActionResult) {
835 if (pendingActionResult && !isErrorResult(pendingActionResult[1])) return { [pendingActionResult[0]]: pendingActionResult[1].data };
836 else if (state.actionData) if (Object.keys(state.actionData).length === 0) return null;
837 else return state.actionData;
838 }
839 function getUpdatedRevalidatingFetchers(revalidatingFetchers) {
840 let workingFetchers = new Map(state.fetchers);
841 revalidatingFetchers.forEach((rf) => {
842 let fetcher = workingFetchers.get(rf.key);
843 let revalidatingFetcher = getLoadingFetcher(void 0, fetcher ? fetcher.data : void 0);
844 workingFetchers.set(rf.key, revalidatingFetcher);
845 });
846 return workingFetchers;
847 }
848 async function fetch(key, routeId, href, opts) {
849 if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
850 abortFetcher(key);
851 let flushSync = (opts && opts.flushSync) === true;
852 let instrumentationResultMetaReceiver = consumeInstrumentationClientResultMetaReceiver(router);
853 let normalizedPath = normalizeTo(state.location, state.matches, basename, href, routeId, opts?.relative);
854 let matches = dataRouteMatcher.match(normalizedPath);
855 let fogOfWar = checkFogOfWar(matches, normalizedPath);
856 if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
857 if (instrumentationResultMetaReceiver) instrumentationResultMetaReceiver(getInstrumentationNavigateMeta(init.history, normalizedPath, matches));
858 if (!matches) {
859 setFetcherError(key, routeId, getInternalRouterError(404, { pathname: normalizedPath }), { flushSync });
860 return;
861 }
862 let { path, submission, error } = normalizeNavigateOptions(true, normalizedPath, opts);
863 if (error) {
864 setFetcherError(key, routeId, error, { flushSync });
865 return;
866 }
867 let scopedContext = init.getContext ? await init.getContext() : new RouterContextProvider();
868 let preventScrollReset = (opts && opts.preventScrollReset) === true;
869 if (submission && isMutationMethod(submission.formMethod)) {
870 await handleFetcherAction(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission, opts && opts.defaultShouldRevalidate);
871 return;
872 }
873 let loadMatch = {
874 routeId,
875 path,
876 isDiscovering: fogOfWar.active
877 };
878 fetchLoadMatches.set(key, loadMatch);
879 await handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission);
880 }
881 async function handleFetcherAction(key, routeId, path, requestMatches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission, callSiteDefaultShouldRevalidate) {
882 if (!future.unstable_customRouteDiscovery) interruptActiveLoads();
883 fetchLoadMatches.delete(key);
884 let existingFetcher = state.fetchers.get(key);
885 updateFetcherState(key, getSubmittingFetcher(submission, existingFetcher), { flushSync });
886 let abortController = new AbortController();
887 if (future.unstable_customRouteDiscovery) fetchControllers.set(key, abortController);
888 let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
889 if (isFogOfWar) {
890 let discoverResult = await discoverRoutes(requestMatches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
891 if (future.unstable_customRouteDiscovery && fetchRequest.signal.aborted || discoverResult.type === "aborted") return;
892 else if (discoverResult.type === "cancelled") {
893 fetchControllers.delete(key);
894 fetchLoadMatches.delete(key);
895 updateFetcherState(key, getDoneFetcher(existingFetcher?.data), { flushSync });
896 return;
897 } else if (discoverResult.type === "error") {
898 setFetcherError(key, routeId, discoverResult.error, { flushSync });
899 return;
900 } else if (!discoverResult.matches) {
901 setFetcherError(key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync });
902 return;
903 } else requestMatches = discoverResult.matches;
904 }
905 let match = getTargetMatch(requestMatches, path);
906 if (!match.route.action && !match.route.lazy) {
907 setFetcherError(key, routeId, getInternalRouterError(405, {
908 method: submission.formMethod,
909 pathname: path,
910 routeId
911 }), { flushSync });
912 return;
913 }
914 if (future.unstable_customRouteDiscovery) interruptActiveLoads();
915 fetchControllers.set(key, abortController);
916 let originatingLoadId = incrementingLoadId;
917 let fetchMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, requestMatches, match, hydrationRouteProperties, scopedContext);
918 let actionResults = await callDataStrategy(fetchRequest, path, fetchMatches, scopedContext, key, "fetcher");
919 let actionResult = actionResults[match.route.id];
920 if (!actionResult) {
921 for (let match of fetchMatches) if (actionResults[match.route.id]) {
922 actionResult = actionResults[match.route.id];
923 break;
924 }
925 }
926 if (fetchRequest.signal.aborted) {
927 if (fetchControllers.get(key) === abortController) fetchControllers.delete(key);
928 return;
929 }
930 if (fetchersQueuedForDeletion.has(key)) {
931 if (isErrorResult(actionResult)) {
932 updateFetcherState(key, getDoneFetcher(void 0));
933 return;
934 }
935 if (isRedirectResult(actionResult)) {
936 isRevalidationRequired = true;
937 actionResult = {
938 type: "data",
939 data: void 0
940 };
941 }
942 } else {
943 if (isRedirectResult(actionResult)) if (pendingNavigationLoadId > originatingLoadId) {
944 isRevalidationRequired = true;
945 actionResult = {
946 type: "data",
947 data: void 0
948 };
949 } else {
950 fetchControllers.delete(key);
951 fetchRedirectIds.add(key);
952 updateFetcherState(key, getLoadingFetcher(submission));
953 return startRedirectNavigation(fetchRequest, actionResult, false, "fetcher", {
954 fetcherSubmission: submission,
955 preventScrollReset
956 });
957 }
958 if (isErrorResult(actionResult)) {
959 setFetcherError(key, routeId, actionResult.error);
960 return;
961 }
962 }
963 isRevalidationRequired = true;
964 let nextLocation = state.navigation.location || state.location;
965 let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
966 let matches = state.navigation.state !== "idle" ? dataRouteMatcher.match(state.navigation.location) : state.matches;
967 invariant(matches, "Didn't find any matches after fetcher action");
968 let loadId = ++incrementingLoadId;
969 fetchReloadIds.set(key, loadId);
970 let { dsMatches, revalidatingFetchers } = getMatchesToLoad(revalidationRequest, scopedContext, mapRouteProperties, manifest, init.history, state, matches, submission, nextLocation, hydrationRouteProperties, false, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, [match.route.id, actionResult], callSiteDefaultShouldRevalidate);
971 let loadFetcher = getLoadingFetcher(submission, actionResult.data);
972 let workingFetchers = new Map(state.fetchers);
973 workingFetchers.set(key, loadFetcher);
974 revalidatingFetchers.filter((rf) => rf.key !== key).forEach((rf) => {
975 let staleKey = rf.key;
976 let existingFetcher = workingFetchers.get(staleKey);
977 let revalidatingFetcher = getLoadingFetcher(void 0, existingFetcher ? existingFetcher.data : void 0);
978 workingFetchers.set(staleKey, revalidatingFetcher);
979 abortFetcher(staleKey);
980 if (rf.controller) fetchControllers.set(staleKey, rf.controller);
981 });
982 updateState({ fetchers: workingFetchers });
983 let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => {
984 if (fetchControllers.get(rf.key) === rf.controller) abortFetcher(rf.key);
985 });
986 abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
987 let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext, "fetcher");
988 if (abortController.signal.aborted) {
989 if (fetchReloadIds.get(key) === loadId) fetchReloadIds.delete(key);
990 return;
991 }
992 abortController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
993 fetchReloadIds.delete(key);
994 fetchControllers.delete(key);
995 revalidatingFetchers.forEach((r) => {
996 if (fetchControllers.get(r.key) === r.controller) fetchControllers.delete(r.key);
997 });
998 let fetcherIsMounted = state.fetchers.has(key);
999 let getRedirectStateWithDoneFetcher = (s) => {
1000 if (!fetcherIsMounted) return s;
1001 let workingFetchers = new Map(s.fetchers);
1002 workingFetchers.set(key, getDoneFetcher(actionResult.data));
1003 return {
1004 ...s,
1005 fetchers: workingFetchers
1006 };
1007 };
1008 let redirect = findRedirect(loaderResults);
1009 if (redirect) {
1010 state = getRedirectStateWithDoneFetcher(state);
1011 return startRedirectNavigation(revalidationRequest, redirect.result, false, "fetcher", { preventScrollReset });
1012 }
1013 redirect = findRedirect(fetcherResults);
1014 if (redirect) {
1015 fetchRedirectIds.add(redirect.key);
1016 state = getRedirectStateWithDoneFetcher(state);
1017 return startRedirectNavigation(revalidationRequest, redirect.result, false, "fetcher", { preventScrollReset });
1018 }
1019 let finalFetchers = new Map(state.fetchers);
1020 if (fetcherIsMounted) finalFetchers.set(key, getDoneFetcher(actionResult.data));
1021 let { loaderData, errors } = processLoaderData(state, matches, loaderResults, void 0, revalidatingFetchers, fetcherResults, finalFetchers);
1022 abortStaleFetchLoads(loadId, finalFetchers);
1023 if (state.navigation.state === "loading" && loadId > pendingNavigationLoadId) {
1024 invariant(pendingAction, "Expected pending action");
1025 pendingNavigationController && pendingNavigationController.abort();
1026 markFetchRedirectsDone(finalFetchers);
1027 completeNavigation(state.navigation.location, {
1028 matches,
1029 loaderData,
1030 errors,
1031 fetchers: finalFetchers
1032 });
1033 } else {
1034 updateState({
1035 errors,
1036 loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors),
1037 fetchers: finalFetchers
1038 });
1039 isRevalidationRequired = fetchReloadIds.size > 0;
1040 }
1041 }
1042 async function handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission) {
1043 let { routeId, path } = loadMatch;
1044 let existingFetcher = state.fetchers.get(key);
1045 updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : void 0), { flushSync });
1046 let abortController = new AbortController();
1047 if (future.unstable_customRouteDiscovery) fetchControllers.set(key, abortController);
1048 let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
1049 if (loadMatch.isDiscovering) {
1050 let discoverResult = await discoverRoutes(matches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
1051 if (future.unstable_customRouteDiscovery && fetchRequest.signal.aborted || discoverResult.type === "aborted") return;
1052 else if (discoverResult.type === "cancelled") {
1053 fetchControllers.delete(key);
1054 fetchLoadMatches.delete(key);
1055 updateFetcherState(key, getDoneFetcher(existingFetcher?.data), { flushSync });
1056 return;
1057 } else if (discoverResult.type === "error") {
1058 setFetcherError(key, routeId, discoverResult.error, { flushSync });
1059 return;
1060 } else if (!discoverResult.matches) {
1061 setFetcherError(key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync });
1062 return;
1063 } else {
1064 matches = discoverResult.matches;
1065 loadMatch.isDiscovering = false;
1066 }
1067 }
1068 let match = getTargetMatch(matches, path);
1069 fetchControllers.set(key, abortController);
1070 let originatingLoadId = incrementingLoadId;
1071 let results = await callDataStrategy(fetchRequest, path, getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, matches, match, hydrationRouteProperties, scopedContext), scopedContext, key, "fetcher");
1072 let result = results[match.route.id];
1073 if (!result) {
1074 for (let match of matches) if (results[match.route.id]) {
1075 result = results[match.route.id];
1076 break;
1077 }
1078 }
1079 if (fetchControllers.get(key) === abortController) fetchControllers.delete(key);
1080 if (fetchRequest.signal.aborted) return;
1081 if (fetchersQueuedForDeletion.has(key)) {
1082 updateFetcherState(key, getDoneFetcher(void 0));
1083 return;
1084 }
1085 if (isRedirectResult(result)) if (pendingNavigationLoadId > originatingLoadId) {
1086 updateFetcherState(key, getDoneFetcher(void 0));
1087 return;
1088 } else {
1089 fetchRedirectIds.add(key);
1090 await startRedirectNavigation(fetchRequest, result, false, "fetcher", { preventScrollReset });
1091 return;
1092 }
1093 if (isErrorResult(result)) {
1094 setFetcherError(key, routeId, result.error);
1095 return;
1096 }
1097 updateFetcherState(key, getDoneFetcher(result.data));
1098 }
1099 /**
1100 * Utility function to handle redirects returned from an action or loader.
1101 * Normally, a redirect "replaces" the navigation that triggered it. So, for
1102 * example:
1103 *
1104 * - user is on /a
1105 * - user clicks a link to /b
1106 * - loader for /b redirects to /c
1107 *
1108 * In a non-JS app the browser would track the in-flight navigation to /b and
1109 * then replace it with /c when it encountered the redirect response. In
1110 * the end it would only ever update the URL bar with /c.
1111 *
1112 * In client-side routing using pushState/replaceState, we aim to emulate
1113 * this behavior and we also do not update history until the end of the
1114 * navigation (including processed redirects). This means that we never
1115 * actually touch history until we've processed redirects, so we just use
1116 * the history action from the original navigation (PUSH or REPLACE).
1117 */
1118 async function startRedirectNavigation(request, redirect, isNavigation, dataStrategyInitiator, { submission, fetcherSubmission, preventScrollReset, replace } = {}) {
1119 if (!isNavigation) {
1120 pendingPopstateNavigationDfd?.resolve();
1121 pendingPopstateNavigationDfd = null;
1122 }
1123 if (redirect.response.headers.has("X-Remix-Revalidate")) isRevalidationRequired = true;
1124 let location = redirect.response.headers.get("Location");
1125 invariant(location, "Expected a Location header on the redirect Response");
1126 let originalLocation = location;
1127 let currentUrl = new URL(request.url);
1128 location = normalizeRedirectLocation(location, currentUrl, basename, init.history);
1129 validateNavigationTarget(originalLocation, location, currentUrl, "allow-explicit");
1130 let redirectLocation = createLocation(state.location, location, { _isRedirect: true });
1131 if (isBrowser) {
1132 let isDocumentReload = false;
1133 if (redirect.response.headers.has("X-Remix-Reload-Document")) isDocumentReload = true;
1134 else if (isAbsoluteUrl(location)) {
1135 const url = createBrowserURLImpl(routerWindow, location, true);
1136 isDocumentReload = url.origin !== routerWindow.location.origin || stripBasename(url.pathname, basename) == null;
1137 }
1138 if (isDocumentReload) {
1139 if (replace) routerWindow.location.replace(location);
1140 else routerWindow.location.assign(location);
1141 return;
1142 }
1143 }
1144 pendingNavigationController = null;
1145 let redirectNavigationType = replace === true || redirect.response.headers.has("X-Remix-Replace") ? "REPLACE" : "PUSH";
1146 let { formMethod, formAction, formEncType } = state.navigation;
1147 if (!submission && !fetcherSubmission && formMethod && formAction && formEncType) submission = getSubmissionFromNavigation(state.navigation);
1148 let activeSubmission = submission || fetcherSubmission;
1149 if (redirectPreserveMethodStatusCodes.has(redirect.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) await startNavigation(redirectNavigationType, redirectLocation, {
1150 submission: {
1151 ...activeSubmission,
1152 formAction: location
1153 },
1154 preventScrollReset: preventScrollReset || pendingPreventScrollReset,
1155 enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0,
1156 dataStrategyInitiator
1157 });
1158 else await startNavigation(redirectNavigationType, redirectLocation, {
1159 overrideNavigation: getLoadingNavigation(redirectLocation, [], redirectNavigationType, submission),
1160 fetcherSubmission,
1161 preventScrollReset: preventScrollReset || pendingPreventScrollReset,
1162 enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0,
1163 dataStrategyInitiator
1164 });
1165 }
1166 async function callDataStrategy(request, path, matches, scopedContext, fetcherKey, initiator, navigationType = null) {
1167 let results;
1168 let dataResults = {};
1169 try {
1170 results = await callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, initiator, scopedContext, false, navigationType);
1171 } catch (e) {
1172 matches.filter((m) => m.shouldLoad).forEach((m) => {
1173 dataResults[m.route.id] = {
1174 type: "error",
1175 error: e
1176 };
1177 });
1178 return dataResults;
1179 }
1180 if (request.signal.aborted) return dataResults;
1181 if (!isMutationMethod(request.method)) for (let match of matches) {
1182 if (results[match.route.id]?.type === "error") break;
1183 if (!results.hasOwnProperty(match.route.id) && !state.loaderData.hasOwnProperty(match.route.id) && (!state.errors || !state.errors.hasOwnProperty(match.route.id)) && match.shouldCallHandler()) results[match.route.id] = {
1184 type: "error",
1185 result: /* @__PURE__ */ new Error(`No result returned from dataStrategy for route ${match.route.id}`)
1186 };
1187 }
1188 for (let [routeId, result] of Object.entries(results)) if (isRedirectDataStrategyResult(result)) {
1189 let response = result.result;
1190 dataResults[routeId] = {
1191 type: "redirect",
1192 response: normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename)
1193 };
1194 } else dataResults[routeId] = await convertDataStrategyResultToDataResult(result);
1195 return dataResults;
1196 }
1197 async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, location, scopedContext, initiator, navigationType = null) {
1198 let loaderResultsPromise = callDataStrategy(request, location, matches, scopedContext, null, initiator, navigationType);
1199 let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async (f) => {
1200 if (f.matches && f.match && f.request && f.controller) {
1201 let result = (await callDataStrategy(f.request, f.path, f.matches, scopedContext, f.key, initiator, navigationType))[f.match.route.id];
1202 return { [f.key]: result };
1203 } else return Promise.resolve({ [f.key]: {
1204 type: "error",
1205 error: getInternalRouterError(404, { pathname: f.path })
1206 } });
1207 }));
1208 return {
1209 loaderResults: await loaderResultsPromise,
1210 fetcherResults: (await fetcherResultsPromise).reduce((acc, r) => Object.assign(acc, r), {})
1211 };
1212 }
1213 function interruptActiveLoads() {
1214 isRevalidationRequired = true;
1215 fetchLoadMatches.forEach((loadMatch, key) => {
1216 if (future.unstable_customRouteDiscovery && loadMatch.isDiscovering) return;
1217 if (fetchControllers.has(key)) cancelledFetcherLoads.add(key);
1218 abortFetcher(key);
1219 });
1220 }
1221 function updateFetcherState(key, fetcher, opts = {}) {
1222 let workingFetchers = new Map(state.fetchers);
1223 workingFetchers.set(key, fetcher);
1224 updateState({ fetchers: workingFetchers }, { flushSync: (opts && opts.flushSync) === true });
1225 }
1226 function setFetcherError(key, routeId, error, opts = {}) {
1227 let boundaryMatch = findNearestBoundary(state.matches, routeId);
1228 let workingFetchers = new Map(state.fetchers);
1229 deleteFetcher(workingFetchers, key);
1230 updateState({
1231 errors: { [boundaryMatch.route.id]: error },
1232 fetchers: workingFetchers
1233 }, { flushSync: (opts && opts.flushSync) === true });
1234 }
1235 function getFetcher(key) {
1236 activeFetchers.set(key, (activeFetchers.get(key) || 0) + 1);
1237 if (fetchersQueuedForDeletion.has(key)) fetchersQueuedForDeletion.delete(key);
1238 return state.fetchers.get(key) || IDLE_FETCHER;
1239 }
1240 function resetFetcher(key, opts) {
1241 abortFetcher(key, opts?.reason);
1242 fetchLoadMatches.delete(key);
1243 cancelledFetcherLoads.delete(key);
1244 updateFetcherState(key, getDoneFetcher(null));
1245 }
1246 function deleteFetcher(fetchers, key) {
1247 let fetcher = state.fetchers.get(key);
1248 if (fetchControllers.has(key) && !(fetcher && fetcher.state === "loading" && fetchReloadIds.has(key))) abortFetcher(key);
1249 fetchLoadMatches.delete(key);
1250 fetchReloadIds.delete(key);
1251 fetchRedirectIds.delete(key);
1252 fetchersQueuedForDeletion.delete(key);
1253 cancelledFetcherLoads.delete(key);
1254 fetchers.delete(key);
1255 }
1256 function queueFetcherForDeletion(key) {
1257 let count = (activeFetchers.get(key) || 0) - 1;
1258 if (count <= 0) {
1259 activeFetchers.delete(key);
1260 fetchersQueuedForDeletion.add(key);
1261 } else activeFetchers.set(key, count);
1262 updateState({ fetchers: new Map(state.fetchers) });
1263 }
1264 function abortFetcher(key, reason) {
1265 let controller = fetchControllers.get(key);
1266 if (controller) {
1267 controller.abort(reason);
1268 fetchControllers.delete(key);
1269 }
1270 }
1271 function markFetchersDone(keys, fetchers) {
1272 for (let key of keys) {
1273 let fetcher = fetchers.get(key);
1274 invariant(fetcher, `Expected fetcher: ${key}`);
1275 let doneFetcher = getDoneFetcher(fetcher.data);
1276 fetchers.set(key, doneFetcher);
1277 }
1278 }
1279 function markFetchRedirectsDone(fetchers) {
1280 let doneKeys = [];
1281 let didUpdateFetchers = false;
1282 for (let key of fetchRedirectIds) {
1283 let fetcher = fetchers.get(key);
1284 invariant(fetcher, `Expected fetcher: ${key}`);
1285 if (fetcher.state === "loading") {
1286 fetchRedirectIds.delete(key);
1287 doneKeys.push(key);
1288 didUpdateFetchers = true;
1289 }
1290 }
1291 markFetchersDone(doneKeys, fetchers);
1292 return didUpdateFetchers;
1293 }
1294 function abortStaleFetchLoads(landedId, fetchers) {
1295 let yeetedKeys = [];
1296 for (let [key, id] of fetchReloadIds) if (id < landedId) {
1297 let fetcher = fetchers.get(key);
1298 invariant(fetcher, `Expected fetcher: ${key}`);
1299 if (fetcher.state === "loading") {
1300 abortFetcher(key);
1301 fetchReloadIds.delete(key);
1302 yeetedKeys.push(key);
1303 }
1304 }
1305 markFetchersDone(yeetedKeys, fetchers);
1306 return yeetedKeys.length > 0;
1307 }
1308 function getBlocker(key, fn) {
1309 let blocker = state.blockers.get(key) || IDLE_BLOCKER;
1310 if (blockerFunctions.get(key) !== fn) blockerFunctions.set(key, fn);
1311 return blocker;
1312 }
1313 function deleteBlocker(key) {
1314 state.blockers.delete(key);
1315 blockerFunctions.delete(key);
1316 }
1317 function updateBlocker(key, newBlocker) {
1318 let blocker = state.blockers.get(key) || IDLE_BLOCKER;
1319 invariant(blocker.state === "unblocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "blocked" || blocker.state === "blocked" && newBlocker.state === "proceeding" || blocker.state === "blocked" && newBlocker.state === "unblocked" || blocker.state === "proceeding" && newBlocker.state === "unblocked", `Invalid blocker state transition: ${blocker.state} -> ${newBlocker.state}`);
1320 let blockers = new Map(state.blockers);
1321 blockers.set(key, newBlocker);
1322 updateState({ blockers });
1323 }
1324 function shouldBlockNavigation({ currentLocation, nextLocation, historyAction }) {
1325 if (blockerFunctions.size === 0) return;
1326 if (blockerFunctions.size > 1) warning(false, "A router only supports one blocker at a time");
1327 let entries = Array.from(blockerFunctions.entries());
1328 let [blockerKey, blockerFunction] = entries[entries.length - 1];
1329 let blocker = state.blockers.get(blockerKey);
1330 if (blocker && blocker.state === "proceeding") return;
1331 if (blockerFunction({
1332 currentLocation,
1333 nextLocation,
1334 historyAction
1335 })) return blockerKey;
1336 }
1337 function handleNavigational404(pathname) {
1338 let error = getInternalRouterError(404, { pathname });
1339 let routesToUse = dataRoutes.activeRoutes;
1340 let { matches, route } = getShortCircuitMatches(routesToUse);
1341 return {
1342 notFoundMatches: matches,
1343 route,
1344 error
1345 };
1346 }
1347 function enableScrollRestoration(positions, getPosition, getKey) {
1348 savedScrollPositions = positions;
1349 getScrollPosition = getPosition;
1350 getScrollRestorationKey = getKey || null;
1351 if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) {
1352 initialScrollRestored = true;
1353 let y = getSavedScrollPosition(state.location, state.matches);
1354 if (y != null) updateState({ restoreScrollPosition: y });
1355 }
1356 return () => {
1357 savedScrollPositions = null;
1358 getScrollPosition = null;
1359 getScrollRestorationKey = null;
1360 };
1361 }
1362 function getScrollKey(location, matches) {
1363 if (getScrollRestorationKey) return getScrollRestorationKey(location, matches.map((m) => convertRouteMatchToUiMatch(m, state.loaderData))) || location.key;
1364 return location.key;
1365 }
1366 function saveScrollPosition(location, matches) {
1367 if (savedScrollPositions && getScrollPosition) {
1368 let key = getScrollKey(location, matches);
1369 savedScrollPositions[key] = getScrollPosition();
1370 }
1371 }
1372 function getSavedScrollPosition(location, matches) {
1373 if (savedScrollPositions) {
1374 let key = getScrollKey(location, matches);
1375 let y = savedScrollPositions[key];
1376 if (typeof y === "number") return y;
1377 }
1378 return null;
1379 }
1380 function checkFogOfWar(matches, pathname) {
1381 if (init.patchRoutesOnNavigation) {
1382 if (!matches) return {
1383 active: true,
1384 matches: dataRouteMatcher.match(pathname, true) || []
1385 };
1386 else if (Object.keys(matches[0].params).length > 0) return {
1387 active: true,
1388 matches: dataRouteMatcher.match(pathname, true)
1389 };
1390 }
1391 return {
1392 active: false,
1393 matches: null
1394 };
1395 }
1396 async function discoverRoutes(matches, pathname, signal, fetcherKey) {
1397 if (!init.patchRoutesOnNavigation) return {
1398 type: "success",
1399 matches
1400 };
1401 let partialMatches = matches;
1402 while (true) {
1403 let localManifest = manifest;
1404 try {
1405 await init.patchRoutesOnNavigation({
1406 signal,
1407 path: pathname,
1408 matches: partialMatches,
1409 fetcherKey,
1410 patch: (routeId, children) => {
1411 if (signal.aborted) return;
1412 patchRoutesImpl(routeId, children, dataRoutes, localManifest, mapRouteProperties, false);
1413 }
1414 });
1415 } catch (e) {
1416 if (future.unstable_customRouteDiscovery) {
1417 if (signal.aborted) return { type: "aborted" };
1418 if (e instanceof DiscoveryCancelledError) return { type: "cancelled" };
1419 }
1420 return {
1421 type: "error",
1422 error: e,
1423 partialMatches
1424 };
1425 }
1426 if (signal.aborted) return { type: "aborted" };
1427 let newMatches = dataRouteMatcher.match(pathname);
1428 let newPartialMatches = null;
1429 if (newMatches) if (Object.keys(newMatches[0].params).length === 0) return {
1430 type: "success",
1431 matches: newMatches
1432 };
1433 else {
1434 newPartialMatches = dataRouteMatcher.match(pathname, true);
1435 if (!(newPartialMatches && partialMatches.length < newPartialMatches.length && compareMatches(partialMatches, newPartialMatches.slice(0, partialMatches.length)))) return {
1436 type: "success",
1437 matches: newMatches
1438 };
1439 }
1440 if (!newPartialMatches) newPartialMatches = dataRouteMatcher.match(pathname, true);
1441 if (!newPartialMatches || compareMatches(partialMatches, newPartialMatches)) return {
1442 type: "success",
1443 matches: null
1444 };
1445 partialMatches = newPartialMatches;
1446 }
1447 }
1448 function compareMatches(a, b) {
1449 return a.length === b.length && a.every((m, i) => m.route.id === b[i].route.id);
1450 }
1451 function _internalSetRoutes(newRoutes) {
1452 manifest = {};
1453 dataRoutes.setHmrRoutes(convertRoutesToDataRoutes(newRoutes, mapRouteProperties, void 0, manifest));
1454 }
1455 function patchRoutes(routeId, children, unstable_allowElementMutations = false) {
1456 patchRoutesImpl(routeId, children, dataRoutes, manifest, mapRouteProperties, unstable_allowElementMutations);
1457 if (!dataRoutes.hasHMRRoutes) updateState({});
1458 }
1459 router = {
1460 get basename() {
1461 return basename;
1462 },
1463 get future() {
1464 return future;
1465 },
1466 get state() {
1467 return state;
1468 },
1469 get routes() {
1470 return dataRoutes.stableRoutes;
1471 },
1472 match(locationArg) {
1473 return dataRouteMatcher.match(locationArg);
1474 },
1475 get manifest() {
1476 return manifest;
1477 },
1478 get window() {
1479 return routerWindow;
1480 },
1481 initialize,
1482 subscribe,
1483 enableScrollRestoration,
1484 navigate,
1485 fetch,
1486 revalidate,
1487 createHref: (to) => init.history.createHref(to),
1488 createURL: (to) => init.history.createURL(to),
1489 encodeLocation: (to) => init.history.encodeLocation(to),
1490 getFetcher,
1491 resetFetcher,
1492 deleteFetcher: queueFetcherForDeletion,
1493 dispose,
1494 getBlocker,
1495 deleteBlocker,
1496 patchRoutes,
1497 _internalFetchControllers: fetchControllers,
1498 _internalSetRoutes,
1499 _internalSetStateDoNotUseOrYouWillBreakYourApp(newState) {
1500 updateState(newState);
1501 }
1502 };
1503 if (init.instrumentations) router = instrumentClientSideRouter(router, init.instrumentations.map((i) => i.router).filter(Boolean));
1504 return router;
1505}
1506/**
1507* Create a static handler to perform server-side data loading
1508*
1509* @example
1510* export async function handleRequest(request: Request) {
1511* let { query, dataRoutes } = createStaticHandler(routes);
1512* let context = await query(request);
1513*
1514* if (context instanceof Response) {
1515* return context;
1516* }
1517*
1518* let router = createStaticRouter(dataRoutes, context);
1519* return new Response(
1520* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
1521* { headers: { "Content-Type": "text/html" } }
1522* );
1523* }
1524*
1525* @public
1526* @category Data Routers
1527* @mode data
1528* @param routes The {@link RouteObject | route objects} to create a static
1529* handler for
1530* @param opts Options
1531* @param opts.basename The base URL for the static handler (default: `/`)
1532* @param opts.future Future flags for the static handler
1533* @returns A static handler that can be used to query data for the provided
1534* routes
1535*/
1536function createStaticHandler(routes, opts) {
1537 invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
1538 let manifest = {};
1539 let basename = (opts ? opts.basename : null) || "/";
1540 let _mapRouteProperties = opts?.mapRouteProperties;
1541 let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
1542 let dataRouteMatcher = createDataRouteMatcher({ ...opts?.future }, basename);
1543 if (opts?.instrumentations) {
1544 let instrumentations = opts.instrumentations;
1545 mapRouteProperties = (route) => {
1546 return {
1547 ..._mapRouteProperties?.(route),
1548 ...getRouteInstrumentationUpdates(instrumentations.map((i) => i.route).filter(Boolean), route)
1549 };
1550 };
1551 }
1552 let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, void 0, manifest);
1553 dataRouteMatcher.update(dataRoutes);
1554 let match = (locationArg) => dataRouteMatcher.match(locationArg);
1555 /**
1556 * The query() method is intended for document requests, in which we want to
1557 * call an optional action and potentially multiple loaders for all nested
1558 * routes. It returns a StaticHandlerContext object, which is very similar
1559 * to the router state (location, loaderData, actionData, errors, etc.) and
1560 * also adds SSR-specific information such as the statusCode and headers
1561 * from action/loaders Responses.
1562 *
1563 * It _should_ never throw and should report all errors through the
1564 * returned handlerContext.errors object, properly associating errors to
1565 * their error boundary. Additionally, it tracks _deepestRenderedBoundaryId
1566 * which can be used to emulate React error boundaries during SSR by performing
1567 * a second pass only down to the boundaryId.
1568 *
1569 * The one exception where we do not return a StaticHandlerContext is when a
1570 * redirect response is returned or thrown from any action/loader. We
1571 * propagate that out and return the raw Response so the HTTP server can
1572 * return it directly.
1573 *
1574 * - `opts.requestContext` is an optional server context that will be passed
1575 * to actions/loaders in the `context` parameter
1576 * - `opts.skipLoaderErrorBubbling` is an optional parameter that will prevent
1577 * the bubbling of errors which allows single-fetch-type implementations
1578 * where the client will handle the bubbling and we may need to return data
1579 * for the handling route
1580 */
1581 async function query(request, { requestContext, filterMatchesToLoad, skipLoaderErrorBubbling, skipRevalidation, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
1582 let normalizePathImpl = normalizePath || defaultNormalizePath;
1583 let method = request.method;
1584 let location = createLocation("", normalizePathImpl(request), null, "default");
1585 let matches = dataRouteMatcher.match(location);
1586 requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1587 if (!isValidMethod(method) && method !== "HEAD") {
1588 let error = getInternalRouterError(405, { method });
1589 let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
1590 let staticContext = {
1591 basename,
1592 location,
1593 matches: methodNotAllowedMatches,
1594 loaderData: {},
1595 actionData: null,
1596 errors: { [route.id]: error },
1597 statusCode: error.status,
1598 loaderHeaders: {},
1599 actionHeaders: {},
1600 _match: match
1601 };
1602 return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1603 } else if (!matches) {
1604 let error = getInternalRouterError(404, { pathname: location.pathname });
1605 let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
1606 let staticContext = {
1607 basename,
1608 location,
1609 matches: notFoundMatches,
1610 loaderData: {},
1611 actionData: null,
1612 errors: { [route.id]: error },
1613 statusCode: error.status,
1614 loaderHeaders: {},
1615 actionHeaders: {},
1616 _match: match
1617 };
1618 return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1619 }
1620 if (generateMiddlewareResponse) {
1621 invariant(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
1622 try {
1623 await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
1624 let renderedStaticContext;
1625 let response = await runServerMiddlewarePipeline({
1626 request,
1627 url: createDataFunctionUrl(request, location),
1628 pattern: getRoutePattern(matches),
1629 matches,
1630 params: matches[0].params,
1631 context: requestContext
1632 }, async () => {
1633 return await generateMiddlewareResponse(async (revalidationRequest, opts = {}) => {
1634 let result = await queryImpl(revalidationRequest, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, "filterMatchesToLoad" in opts ? opts.filterMatchesToLoad ?? null : filterMatchesToLoad ?? null, skipRevalidation === true);
1635 if (isResponse(result)) return result;
1636 renderedStaticContext = {
1637 location,
1638 basename,
1639 ...result,
1640 _match: match
1641 };
1642 return renderedStaticContext;
1643 });
1644 }, async (error, routeId) => {
1645 if (isRedirectResponse(error)) return error;
1646 if (isResponse(error)) try {
1647 error = new ErrorResponseImpl(error.status, error.statusText, await parseResponseBody(error));
1648 } catch (e) {
1649 error = e;
1650 }
1651 if (isDataWithResponseInit(error)) error = dataWithResponseInitToErrorResponse(error);
1652 if (renderedStaticContext) {
1653 if (routeId in renderedStaticContext.loaderData) renderedStaticContext.loaderData[routeId] = void 0;
1654 let staticContext = getStaticContextFromError(dataRoutes, renderedStaticContext, error, skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id);
1655 return generateMiddlewareResponse(() => Promise.resolve(staticContext));
1656 } else {
1657 let staticContext = {
1658 matches,
1659 location,
1660 basename,
1661 loaderData: {},
1662 actionData: null,
1663 errors: { [skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, matches.find((m) => m.route.id === routeId || m.route.loader)?.route.id || routeId).route.id]: error },
1664 statusCode: isRouteErrorResponse(error) ? error.status : 500,
1665 actionHeaders: {},
1666 loaderHeaders: {},
1667 _match: match
1668 };
1669 return generateMiddlewareResponse(() => Promise.resolve(staticContext));
1670 }
1671 });
1672 invariant(isResponse(response), "Expected a response in query()");
1673 return response;
1674 } catch (e) {
1675 if (isResponse(e)) return e;
1676 throw e;
1677 }
1678 }
1679 let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, filterMatchesToLoad || null, skipRevalidation === true);
1680 if (isResponse(result)) return result;
1681 return {
1682 location,
1683 basename,
1684 ...result,
1685 _match: match
1686 };
1687 }
1688 /**
1689 * The queryRoute() method is intended for targeted route requests, either
1690 * for fetch ?_data requests or resource route requests. In this case, we
1691 * are only ever calling a single action or loader, and we are returning the
1692 * returned value directly. In most cases, this will be a Response returned
1693 * from the action/loader, but it may be a primitive or other value as well -
1694 * and in such cases the calling context should handle that accordingly.
1695 *
1696 * We do respect the throw/return differentiation, so if an action/loader
1697 * throws, then this method will throw the value. This is important so we
1698 * can do proper boundary identification in Remix where a thrown Response
1699 * must go to the Catch Boundary but a returned Response is happy-path.
1700 *
1701 * One thing to note is that any Router-initiated Errors that make sense
1702 * to associate with a status code will be thrown as an ErrorResponse
1703 * instance which include the raw Error, such that the calling context can
1704 * serialize the error as they see fit while including the proper response
1705 * code. Examples here are 404 and 405 errors that occur prior to reaching
1706 * any user-defined loaders.
1707 *
1708 * - `opts.routeId` allows you to specify the specific route handler to call.
1709 * If not provided the handler will determine the proper route by matching
1710 * against `request.url`
1711 * - `opts.requestContext` is an optional server context that will be passed
1712 * to actions/loaders in the `context` parameter
1713 */
1714 async function queryRoute(request, { routeId, requestContext, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
1715 let normalizePathImpl = normalizePath || defaultNormalizePath;
1716 let method = request.method;
1717 let location = createLocation("", normalizePathImpl(request), null, "default");
1718 let matches = dataRouteMatcher.match(location);
1719 requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1720 if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") throw getInternalRouterError(405, { method });
1721 else if (!matches) throw getInternalRouterError(404, { pathname: location.pathname });
1722 let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
1723 if (routeId && !match) throw getInternalRouterError(403, {
1724 pathname: location.pathname,
1725 routeId
1726 });
1727 else if (!match) throw getInternalRouterError(404, { pathname: location.pathname });
1728 if (generateMiddlewareResponse) {
1729 invariant(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
1730 await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
1731 return await runServerMiddlewarePipeline({
1732 request,
1733 url: createDataFunctionUrl(request, location),
1734 pattern: getRoutePattern(matches),
1735 matches,
1736 params: matches[0].params,
1737 context: requestContext
1738 }, async () => {
1739 return await generateMiddlewareResponse(async (innerRequest) => {
1740 let processed = handleQueryResult(await queryImpl(innerRequest, location, matches, requestContext, dataStrategy || null, false, match, null, false));
1741 return isResponse(processed) ? processed : typeof processed === "string" ? new Response(processed) : Response.json(processed);
1742 });
1743 }, (error) => {
1744 if (isDataWithResponseInit(error)) return Promise.resolve(dataWithResponseInitToResponse(error));
1745 if (isResponse(error)) return Promise.resolve(error);
1746 throw error;
1747 });
1748 }
1749 return handleQueryResult(await queryImpl(request, location, matches, requestContext, dataStrategy || null, false, match, null, false));
1750 function handleQueryResult(result) {
1751 if (isResponse(result)) return result;
1752 let error = result.errors ? Object.values(result.errors)[0] : void 0;
1753 if (error !== void 0) throw error;
1754 if (result.actionData) return Object.values(result.actionData)[0];
1755 if (result.loaderData) return Object.values(result.loaderData)[0];
1756 }
1757 }
1758 async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
1759 invariant(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
1760 try {
1761 if (isMutationMethod(request.method)) return await submit(request, location, matches, routeMatch || getTargetMatch(matches, location), requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch != null, filterMatchesToLoad, skipRevalidation);
1762 let result = await loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad);
1763 return isResponse(result) ? result : {
1764 ...result,
1765 actionData: null,
1766 actionHeaders: {}
1767 };
1768 } catch (e) {
1769 if (isDataStrategyResult(e) && isResponse(e.result)) {
1770 if (e.type === "error") throw e.result;
1771 return e.result;
1772 }
1773 if (isRedirectResponse(e)) return e;
1774 throw e;
1775 }
1776 }
1777 async function submit(request, location, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
1778 let result;
1779 if (!actionMatch.route.action && !actionMatch.route.lazy) {
1780 let error = getInternalRouterError(405, {
1781 method: request.method,
1782 pathname: new URL(request.url).pathname,
1783 routeId: actionMatch.route.id
1784 });
1785 if (isRouteRequest) throw error;
1786 result = {
1787 type: "error",
1788 error
1789 };
1790 } else {
1791 result = (await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, [], requestContext), isRouteRequest, requestContext, dataStrategy))[actionMatch.route.id];
1792 if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
1793 }
1794 if (isRedirectResult(result)) throw new Response(null, {
1795 status: result.response.status,
1796 headers: { Location: result.response.headers.get("Location") }
1797 });
1798 if (isRouteRequest) {
1799 if (isErrorResult(result)) throw result.error;
1800 return {
1801 matches: [actionMatch],
1802 loaderData: {},
1803 actionData: { [actionMatch.route.id]: result.data },
1804 errors: null,
1805 statusCode: 200,
1806 loaderHeaders: {},
1807 actionHeaders: {}
1808 };
1809 }
1810 if (skipRevalidation) if (isErrorResult(result)) {
1811 let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
1812 return {
1813 statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
1814 actionData: null,
1815 actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} },
1816 matches,
1817 loaderData: {},
1818 errors: { [boundaryMatch.route.id]: result.error },
1819 loaderHeaders: {}
1820 };
1821 } else return {
1822 actionData: { [actionMatch.route.id]: result.data },
1823 actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
1824 matches,
1825 loaderData: {},
1826 errors: null,
1827 statusCode: result.statusCode || 200,
1828 loaderHeaders: {}
1829 };
1830 let loaderRequest = new Request(request.url, {
1831 headers: request.headers,
1832 redirect: request.redirect,
1833 signal: request.signal
1834 });
1835 if (isErrorResult(result)) return {
1836 ...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad, [(skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id)).route.id, result]),
1837 statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
1838 actionData: null,
1839 actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} }
1840 };
1841 return {
1842 ...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad),
1843 actionData: { [actionMatch.route.id]: result.data },
1844 ...result.statusCode ? { statusCode: result.statusCode } : {},
1845 actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
1846 };
1847 }
1848 async function loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
1849 let isRouteRequest = routeMatch != null;
1850 if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) throw getInternalRouterError(400, {
1851 method: request.method,
1852 pathname: new URL(request.url).pathname,
1853 routeId: routeMatch?.route.id
1854 });
1855 let dsMatches;
1856 if (routeMatch) dsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, routeMatch, [], requestContext);
1857 else {
1858 let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1 : void 0;
1859 let pattern = getRoutePattern(matches);
1860 dsMatches = matches.map((match, index) => {
1861 if (maxIdx != null && index > maxIdx) return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, false);
1862 return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, (match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match)));
1863 });
1864 }
1865 if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) return {
1866 matches,
1867 loaderData: {},
1868 errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
1869 statusCode: 200,
1870 loaderHeaders: {}
1871 };
1872 let results = await callDataStrategy(request, location, dsMatches, isRouteRequest, requestContext, dataStrategy);
1873 if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
1874 return {
1875 ...processRouteLoaderData(matches, results, pendingActionResult, true, skipLoaderErrorBubbling),
1876 matches
1877 };
1878 }
1879 async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
1880 let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, "static", requestContext, true);
1881 let dataResults = {};
1882 await Promise.all(matches.map(async (match) => {
1883 if (!(match.route.id in results)) return;
1884 let result = results[match.route.id];
1885 if (isRedirectDataStrategyResult(result)) {
1886 let response = result.result;
1887 throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename);
1888 }
1889 if (isRouteRequest) {
1890 if (isResponse(result.result)) throw result;
1891 else if (isDataWithResponseInit(result.result)) throw dataWithResponseInitToResponse(result.result);
1892 }
1893 dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
1894 }));
1895 return dataResults;
1896 }
1897 return {
1898 dataRoutes,
1899 get _matchCost() {
1900 return dataRouteMatcher instanceof V6RegExMatcher ? dataRouteMatcher.matchCost : void 0;
1901 },
1902 match,
1903 query,
1904 queryRoute
1905 };
1906}
1907/**
1908* Given an existing StaticHandlerContext and an error thrown at render time,
1909* provide an updated StaticHandlerContext suitable for a second SSR render
1910*
1911* @category Utils
1912*/
1913function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
1914 let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
1915 return {
1916 ...handlerContext,
1917 statusCode: isRouteErrorResponse(error) ? error.status : 500,
1918 errors: { [errorBoundaryId]: error }
1919 };
1920}
1921function throwStaticHandlerAbortedError(request, isRouteRequest) {
1922 if (request.signal.reason !== void 0) throw request.signal.reason;
1923 throw new Error(`${isRouteRequest ? "queryRoute" : "query"}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`);
1924}
1925function isSubmissionNavigation(opts) {
1926 return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== void 0);
1927}
1928function defaultNormalizePath(request) {
1929 let url = new URL(request.url);
1930 return {
1931 pathname: url.pathname,
1932 search: url.search,
1933 hash: url.hash
1934 };
1935}
1936function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
1937 let contextualMatches;
1938 let activeRouteMatch;
1939 if (fromRouteId) {
1940 contextualMatches = [];
1941 for (let match of matches) {
1942 contextualMatches.push(match);
1943 if (match.route.id === fromRouteId) {
1944 activeRouteMatch = match;
1945 break;
1946 }
1947 }
1948 } else {
1949 contextualMatches = matches;
1950 activeRouteMatch = matches[matches.length - 1];
1951 }
1952 let path = resolveTo(to ? to : ".", getResolveToMatches(contextualMatches), stripBasename(location.pathname, basename) || location.pathname, relative === "path");
1953 if (to == null) {
1954 path.search = location.search;
1955 path.hash = location.hash;
1956 }
1957 if ((to == null || to === "" || to === ".") && activeRouteMatch) {
1958 let nakedIndex = hasNakedIndexQuery(path.search);
1959 if (activeRouteMatch.route.index && !nakedIndex) path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
1960 else if (!activeRouteMatch.route.index && nakedIndex) {
1961 let params = new URLSearchParams(path.search);
1962 let indexValues = params.getAll("index");
1963 params.delete("index");
1964 indexValues.filter((v) => v).forEach((v) => params.append("index", v));
1965 let qs = params.toString();
1966 path.search = qs ? `?${qs}` : "";
1967 }
1968 }
1969 if (basename !== "/") path.pathname = prependBasename({
1970 basename,
1971 pathname: path.pathname
1972 });
1973 return createPath(path);
1974}
1975function normalizeNavigateOptions(isFetcher, path, opts) {
1976 if (!opts || !isSubmissionNavigation(opts)) return { path };
1977 if (opts.formMethod && !isValidMethod(opts.formMethod)) return {
1978 path,
1979 error: getInternalRouterError(405, { method: opts.formMethod })
1980 };
1981 let getInvalidBodyError = () => ({
1982 path,
1983 error: getInternalRouterError(400, { type: "invalid-body" })
1984 });
1985 let formMethod = (opts.formMethod || "get").toUpperCase();
1986 let formAction = stripHashFromPath(path);
1987 if (opts.body !== void 0) {
1988 if (opts.formEncType === "text/plain") {
1989 if (!isMutationMethod(formMethod)) return getInvalidBodyError();
1990 let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ? Array.from(opts.body.entries()).reduce((acc, [name, value]) => `${acc}${name}=${value}\n`, "") : String(opts.body);
1991 return {
1992 path,
1993 submission: {
1994 formMethod,
1995 formAction,
1996 formEncType: opts.formEncType,
1997 formData: void 0,
1998 json: void 0,
1999 text
2000 }
2001 };
2002 } else if (opts.formEncType === "application/json") {
2003 if (!isMutationMethod(formMethod)) return getInvalidBodyError();
2004 try {
2005 let json = typeof opts.body === "string" ? JSON.parse(opts.body) : opts.body;
2006 return {
2007 path,
2008 submission: {
2009 formMethod,
2010 formAction,
2011 formEncType: opts.formEncType,
2012 formData: void 0,
2013 json,
2014 text: void 0
2015 }
2016 };
2017 } catch {
2018 return getInvalidBodyError();
2019 }
2020 }
2021 }
2022 invariant(typeof FormData === "function", "FormData is not available in this environment");
2023 let searchParams;
2024 let formData;
2025 if (opts.formData) {
2026 searchParams = convertFormDataToSearchParams(opts.formData);
2027 formData = opts.formData;
2028 } else if (opts.body instanceof FormData) {
2029 searchParams = convertFormDataToSearchParams(opts.body);
2030 formData = opts.body;
2031 } else if (opts.body instanceof URLSearchParams) {
2032 searchParams = opts.body;
2033 formData = convertSearchParamsToFormData(searchParams);
2034 } else if (opts.body == null) {
2035 searchParams = new URLSearchParams();
2036 formData = new FormData();
2037 } else try {
2038 searchParams = new URLSearchParams(opts.body);
2039 formData = convertSearchParamsToFormData(searchParams);
2040 } catch {
2041 return getInvalidBodyError();
2042 }
2043 let submission = {
2044 formMethod,
2045 formAction,
2046 formEncType: opts && opts.formEncType || "application/x-www-form-urlencoded",
2047 formData,
2048 json: void 0,
2049 text: void 0
2050 };
2051 if (isMutationMethod(submission.formMethod)) return {
2052 path,
2053 submission
2054 };
2055 let parsedPath = parsePath(path);
2056 if (isFetcher && parsedPath.search && hasNakedIndexQuery(parsedPath.search)) searchParams.append("index", "");
2057 parsedPath.search = `?${searchParams}`;
2058 return {
2059 path: createPath(parsedPath),
2060 submission
2061 };
2062}
2063function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, history, state, matches, submission, location, lazyRoutePropertiesToSkip, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, pendingActionResult, callSiteDefaultShouldRevalidate) {
2064 let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : void 0;
2065 let currentUrl = history.createURL(state.location);
2066 let nextUrl = history.createURL(location);
2067 let maxIdx;
2068 if (initialHydration && state.errors) {
2069 let boundaryId = Object.keys(state.errors)[0];
2070 maxIdx = matches.findIndex((m) => m.route.id === boundaryId);
2071 } else if (pendingActionResult && isErrorResult(pendingActionResult[1])) {
2072 let boundaryId = pendingActionResult[0];
2073 maxIdx = matches.findIndex((m) => m.route.id === boundaryId) - 1;
2074 }
2075 let actionStatus = pendingActionResult ? pendingActionResult[1].statusCode : void 0;
2076 let shouldSkipRevalidation = actionStatus && actionStatus >= 400;
2077 let baseShouldRevalidateArgs = {
2078 currentUrl,
2079 currentParams: state.matches[0]?.params || {},
2080 nextUrl,
2081 nextParams: matches[0].params,
2082 ...submission,
2083 actionResult,
2084 actionStatus
2085 };
2086 let pattern = getRoutePattern(matches);
2087 let dsMatches = matches.map((match, index) => {
2088 let { route } = match;
2089 let forceShouldLoad = null;
2090 if (maxIdx != null && index > maxIdx) forceShouldLoad = false;
2091 else if (route.lazy) forceShouldLoad = true;
2092 else if (!routeHasLoaderOrMiddleware(route)) forceShouldLoad = false;
2093 else if (initialHydration) {
2094 let { shouldLoad } = getRouteHydrationStatus(route, state.loaderData, state.errors);
2095 forceShouldLoad = shouldLoad;
2096 } else if (isNewLoader(state.loaderData, state.matches[index], match)) forceShouldLoad = true;
2097 if (forceShouldLoad !== null) return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, lazyRoutePropertiesToSkip, scopedContext, forceShouldLoad);
2098 let defaultShouldRevalidate = false;
2099 if (typeof callSiteDefaultShouldRevalidate === "boolean") defaultShouldRevalidate = callSiteDefaultShouldRevalidate;
2100 else if (shouldSkipRevalidation) defaultShouldRevalidate = false;
2101 else if (isRevalidationRequired) defaultShouldRevalidate = true;
2102 else if (currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search) defaultShouldRevalidate = true;
2103 else if (currentUrl.search !== nextUrl.search) defaultShouldRevalidate = true;
2104 else if (isNewRouteInstance(state.matches[index], match)) defaultShouldRevalidate = true;
2105 let shouldRevalidateArgs = {
2106 ...baseShouldRevalidateArgs,
2107 defaultShouldRevalidate
2108 };
2109 return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateLoader(match, shouldRevalidateArgs), shouldRevalidateArgs, callSiteDefaultShouldRevalidate);
2110 });
2111 let revalidatingFetchers = [];
2112 fetchLoadMatches.forEach((f, key) => {
2113 if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key) || f.isDiscovering) return;
2114 let fetcher = state.fetchers.get(key);
2115 let isMidInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === void 0;
2116 let fetcherMatches = dataRouteMatcher.match(f.path);
2117 if (!fetcherMatches) {
2118 revalidatingFetchers.push({
2119 key,
2120 routeId: f.routeId,
2121 path: f.path,
2122 matches: null,
2123 match: null,
2124 request: null,
2125 controller: null
2126 });
2127 return;
2128 }
2129 if (fetchRedirectIds.has(key)) return;
2130 let fetcherMatch = getTargetMatch(fetcherMatches, f.path);
2131 let fetchController = new AbortController();
2132 let fetchRequest = createClientSideRequest(history, f.path, fetchController.signal);
2133 let fetcherDsMatches = null;
2134 if (cancelledFetcherLoads.has(key)) {
2135 cancelledFetcherLoads.delete(key);
2136 fetcherDsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, f.path, fetcherMatches, fetcherMatch, lazyRoutePropertiesToSkip, scopedContext);
2137 } else if (isMidInitialLoad) {
2138 if (isRevalidationRequired) fetcherDsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, f.path, fetcherMatches, fetcherMatch, lazyRoutePropertiesToSkip, scopedContext);
2139 } else {
2140 let defaultShouldRevalidate;
2141 if (typeof callSiteDefaultShouldRevalidate === "boolean") defaultShouldRevalidate = callSiteDefaultShouldRevalidate;
2142 else if (shouldSkipRevalidation) defaultShouldRevalidate = false;
2143 else defaultShouldRevalidate = isRevalidationRequired;
2144 let shouldRevalidateArgs = {
2145 ...baseShouldRevalidateArgs,
2146 defaultShouldRevalidate
2147 };
2148 if (shouldRevalidateLoader(fetcherMatch, shouldRevalidateArgs)) fetcherDsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, f.path, fetcherMatches, fetcherMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs);
2149 }
2150 if (fetcherDsMatches) revalidatingFetchers.push({
2151 key,
2152 routeId: f.routeId,
2153 path: f.path,
2154 matches: fetcherDsMatches,
2155 match: fetcherMatch,
2156 request: fetchRequest,
2157 controller: fetchController
2158 });
2159 });
2160 return {
2161 dsMatches,
2162 revalidatingFetchers
2163 };
2164}
2165function routeHasLoaderOrMiddleware(route) {
2166 return route.loader != null || route.middleware != null && route.middleware.length > 0;
2167}
2168function getRouteHydrationStatus(route, loaderData, errors) {
2169 if (route.lazy) return {
2170 shouldLoad: true,
2171 renderFallback: true
2172 };
2173 if (!routeHasLoaderOrMiddleware(route)) return {
2174 shouldLoad: false,
2175 renderFallback: false
2176 };
2177 let hasData = loaderData != null && route.id in loaderData;
2178 let hasError = errors != null && errors[route.id] !== void 0;
2179 if (!hasData && hasError) return {
2180 shouldLoad: false,
2181 renderFallback: false
2182 };
2183 if (typeof route.loader === "function" && route.loader.hydrate === true) return {
2184 shouldLoad: true,
2185 renderFallback: !hasData
2186 };
2187 let shouldLoad = !hasData && !hasError;
2188 return {
2189 shouldLoad,
2190 renderFallback: shouldLoad
2191 };
2192}
2193function isNewLoader(currentLoaderData, currentMatch, match) {
2194 let isNew = !currentMatch || match.route.id !== currentMatch.route.id;
2195 let isMissingData = !currentLoaderData.hasOwnProperty(match.route.id);
2196 return isNew || isMissingData;
2197}
2198function isNewRouteInstance(currentMatch, match) {
2199 let currentPath = currentMatch.route.path;
2200 return currentMatch.pathname !== match.pathname || currentPath != null && currentPath.endsWith("*") && currentMatch.params["*"] !== match.params["*"];
2201}
2202function shouldRevalidateLoader(loaderMatch, arg) {
2203 if (loaderMatch.route.shouldRevalidate) {
2204 let routeChoice = loaderMatch.route.shouldRevalidate(arg);
2205 if (typeof routeChoice === "boolean") return routeChoice;
2206 }
2207 return arg.defaultShouldRevalidate;
2208}
2209function patchRoutesImpl(routeId, children, dataRoutes, manifest, mapRouteProperties, allowElementMutations) {
2210 let childrenToPatch;
2211 if (routeId) {
2212 let route = manifest[routeId];
2213 invariant(route, `No route found to patch children into: routeId = ${routeId}`);
2214 if (!route.children) route.children = [];
2215 childrenToPatch = route.children;
2216 } else childrenToPatch = dataRoutes.activeRoutes;
2217 let uniqueChildren = [];
2218 let existingChildren = [];
2219 children.forEach((newRoute) => {
2220 let existingRoute = childrenToPatch.find((existingRoute) => isSameRoute(newRoute, existingRoute));
2221 if (existingRoute) existingChildren.push({
2222 existingRoute,
2223 newRoute
2224 });
2225 else uniqueChildren.push(newRoute);
2226 });
2227 if (uniqueChildren.length > 0) {
2228 let newRoutes = convertRoutesToDataRoutes(uniqueChildren, mapRouteProperties, [
2229 routeId || "_",
2230 "patch",
2231 String(childrenToPatch?.length || "0")
2232 ], manifest);
2233 childrenToPatch.push(...newRoutes);
2234 }
2235 if (allowElementMutations && existingChildren.length > 0) for (let i = 0; i < existingChildren.length; i++) {
2236 let { existingRoute, newRoute } = existingChildren[i];
2237 let existingRouteTyped = existingRoute;
2238 let [newRouteTyped] = convertRoutesToDataRoutes([newRoute], mapRouteProperties, [], {}, true);
2239 Object.assign(existingRouteTyped, {
2240 element: newRouteTyped.element ? newRouteTyped.element : existingRouteTyped.element,
2241 errorElement: newRouteTyped.errorElement ? newRouteTyped.errorElement : existingRouteTyped.errorElement,
2242 hydrateFallbackElement: newRouteTyped.hydrateFallbackElement ? newRouteTyped.hydrateFallbackElement : existingRouteTyped.hydrateFallbackElement
2243 });
2244 }
2245 if (!dataRoutes.hasHMRRoutes) dataRoutes.setRoutes([...dataRoutes.activeRoutes]);
2246}
2247function isSameRoute(newRoute, existingRoute) {
2248 if ("id" in newRoute && "id" in existingRoute && newRoute.id === existingRoute.id) return true;
2249 if (!(newRoute.index === existingRoute.index && newRoute.path === existingRoute.path && newRoute.caseSensitive === existingRoute.caseSensitive)) return false;
2250 if ((!newRoute.children || newRoute.children.length === 0) && (!existingRoute.children || existingRoute.children.length === 0)) return true;
2251 return newRoute.children?.every((aChild, i) => existingRoute.children?.some((bChild) => isSameRoute(aChild, bChild))) ?? false;
2252}
2253const lazyRoutePropertyCache = /* @__PURE__ */ new WeakMap();
2254const loadLazyRouteProperty = ({ key, route, manifest, mapRouteProperties }) => {
2255 let routeToUpdate = manifest[route.id];
2256 invariant(routeToUpdate, "No route found in manifest");
2257 if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") return;
2258 let lazyFn = routeToUpdate.lazy[key];
2259 if (!lazyFn) return;
2260 let cache = lazyRoutePropertyCache.get(routeToUpdate);
2261 if (!cache) {
2262 cache = {};
2263 lazyRoutePropertyCache.set(routeToUpdate, cache);
2264 }
2265 let cachedPromise = cache[key];
2266 if (cachedPromise) return cachedPromise;
2267 let propertyPromise = (async () => {
2268 let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
2269 let isStaticallyDefined = routeToUpdate[key] !== void 0;
2270 if (isUnsupported) {
2271 warning(!isUnsupported, "Route property " + key + " is not a supported lazy route property. This property will be ignored.");
2272 cache[key] = Promise.resolve();
2273 } else if (isStaticallyDefined) warning(false, `Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`);
2274 else {
2275 let value = await lazyFn();
2276 if (value != null) {
2277 Object.assign(routeToUpdate, { [key]: value });
2278 Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
2279 }
2280 }
2281 if (typeof routeToUpdate.lazy === "object") {
2282 routeToUpdate.lazy[key] = void 0;
2283 if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) routeToUpdate.lazy = void 0;
2284 }
2285 })();
2286 cache[key] = propertyPromise;
2287 propertyPromise.catch(() => {
2288 if (cache[key] === propertyPromise) delete cache[key];
2289 });
2290 return propertyPromise;
2291};
2292const lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
2293/**
2294* Execute route.lazy functions to lazily load route modules (loader, action,
2295* shouldRevalidate) and update the routeManifest in place which shares objects
2296* with dataRoutes so those get updated as well.
2297*/
2298function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
2299 let routeToUpdate = manifest[route.id];
2300 invariant(routeToUpdate, "No route found in manifest");
2301 if (!route.lazy) return {
2302 lazyRoutePromise: void 0,
2303 lazyHandlerPromise: void 0
2304 };
2305 if (typeof route.lazy === "function") {
2306 let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
2307 if (cachedPromise) return {
2308 lazyRoutePromise: cachedPromise,
2309 lazyHandlerPromise: cachedPromise
2310 };
2311 let lazyRoutePromise = (async () => {
2312 invariant(typeof route.lazy === "function", "No lazy route function found");
2313 let lazyRoute = await route.lazy();
2314 let routeUpdates = {};
2315 for (let lazyRouteProperty in lazyRoute) {
2316 let lazyValue = lazyRoute[lazyRouteProperty];
2317 if (lazyValue === void 0) continue;
2318 let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
2319 let isStaticallyDefined = routeToUpdate[lazyRouteProperty] !== void 0;
2320 if (isUnsupported) warning(!isUnsupported, "Route property " + lazyRouteProperty + " is not a supported property to be returned from a lazy route function. This property will be ignored.");
2321 else if (isStaticallyDefined) warning(!isStaticallyDefined, `Route "${routeToUpdate.id}" has a static property "${lazyRouteProperty}" defined but its lazy function is also returning a value for this property. The lazy route property "${lazyRouteProperty}" will be ignored.`);
2322 else routeUpdates[lazyRouteProperty] = lazyValue;
2323 }
2324 Object.assign(routeToUpdate, routeUpdates);
2325 Object.assign(routeToUpdate, {
2326 ...mapRouteProperties(routeToUpdate),
2327 lazy: void 0
2328 });
2329 })();
2330 lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise);
2331 lazyRoutePromise.catch(() => {
2332 if (lazyRouteFunctionCache.get(routeToUpdate) === lazyRoutePromise) lazyRouteFunctionCache.delete(routeToUpdate);
2333 });
2334 return {
2335 lazyRoutePromise,
2336 lazyHandlerPromise: lazyRoutePromise
2337 };
2338 }
2339 let lazyKeys = Object.keys(route.lazy);
2340 let lazyPropertyPromises = [];
2341 let lazyHandlerPromise = void 0;
2342 for (let key of lazyKeys) {
2343 if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) continue;
2344 let promise = loadLazyRouteProperty({
2345 key,
2346 route,
2347 manifest,
2348 mapRouteProperties
2349 });
2350 if (promise) {
2351 lazyPropertyPromises.push(promise);
2352 if (key === type) lazyHandlerPromise = promise;
2353 }
2354 }
2355 let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {}) : void 0;
2356 lazyRoutePromise?.catch(() => {});
2357 lazyHandlerPromise?.catch(() => {});
2358 return {
2359 lazyRoutePromise,
2360 lazyHandlerPromise
2361 };
2362}
2363function isNonNullable(value) {
2364 return value !== void 0;
2365}
2366function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
2367 let promises = matches.map(({ route }) => {
2368 if (typeof route.lazy !== "object" || !route.lazy.middleware) return;
2369 return loadLazyRouteProperty({
2370 key: "middleware",
2371 route,
2372 manifest,
2373 mapRouteProperties
2374 });
2375 }).filter(isNonNullable);
2376 return promises.length > 0 ? Promise.all(promises) : void 0;
2377}
2378async function defaultDataStrategy(args) {
2379 let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
2380 let keyedResults = {};
2381 (await Promise.all(matchesToLoad.map((m) => m.resolve()))).forEach((result, i) => {
2382 keyedResults[matchesToLoad[i].route.id] = result;
2383 });
2384 return keyedResults;
2385}
2386async function defaultDataStrategyWithMiddleware(args) {
2387 if (!args.matches.some((m) => m.route.middleware)) return defaultDataStrategy(args);
2388 return runClientMiddlewarePipeline(args, () => defaultDataStrategy(args));
2389}
2390function runServerMiddlewarePipeline(args, handler, errorHandler) {
2391 return runMiddlewarePipeline(args, handler, processResult, isResponse, errorHandler);
2392 function processResult(result) {
2393 return isDataWithResponseInit(result) ? dataWithResponseInitToResponse(result) : result;
2394 }
2395}
2396function runClientMiddlewarePipeline(args, handler) {
2397 return runMiddlewarePipeline(args, handler, (r) => {
2398 if (isRedirectResponse(r)) throw r;
2399 return r;
2400 }, isDataStrategyResults, errorHandler);
2401 async function errorHandler(error, routeId, nextResult) {
2402 if (nextResult) return Object.assign(nextResult.value, { [routeId]: {
2403 type: "error",
2404 result: error
2405 } });
2406 else {
2407 let { matches } = args;
2408 let maxBoundaryIdx = Math.min(Math.max(matches.findIndex((m) => m.route.id === routeId), 0), Math.max(matches.findIndex((m) => m.shouldCallHandler()), 0));
2409 let deepestRouteId = matches[maxBoundaryIdx].route.id;
2410 for (let match of matches.slice(0, maxBoundaryIdx + 1)) try {
2411 await match._lazyPromises?.route;
2412 } catch {
2413 deepestRouteId = match.route.id;
2414 break;
2415 }
2416 return { [findNearestBoundary(matches, deepestRouteId).route.id]: {
2417 type: "error",
2418 result: error
2419 } };
2420 }
2421 }
2422}
2423async function runMiddlewarePipeline(args, handler, processResult, isResult, errorHandler) {
2424 let { matches, ...dataFnArgs } = args;
2425 return await callRouteMiddleware(dataFnArgs, matches.flatMap((m) => m.route.middleware ? m.route.middleware.map((fn) => [m.route.id, fn]) : []), handler, processResult, isResult, errorHandler);
2426}
2427async function callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx = 0) {
2428 let { request } = args;
2429 if (request.signal.aborted) throw request.signal.reason ?? /* @__PURE__ */ new Error(`Request aborted: ${request.method} ${request.url}`);
2430 let tuple = middlewares[idx];
2431 if (!tuple) return await handler();
2432 let [routeId, middleware] = tuple;
2433 let nextResult;
2434 let next = async () => {
2435 if (nextResult) throw new Error("You may only call `next()` once per middleware");
2436 try {
2437 nextResult = { value: await callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx + 1) };
2438 return nextResult.value;
2439 } catch (error) {
2440 nextResult = { value: await errorHandler(error, routeId, nextResult) };
2441 return nextResult.value;
2442 }
2443 };
2444 try {
2445 let value = await middleware(args, next);
2446 let result = value != null ? processResult(value) : void 0;
2447 if (isResult(result)) return result;
2448 else if (nextResult) return result ?? nextResult.value;
2449 else {
2450 nextResult = { value: await next() };
2451 return nextResult.value;
2452 }
2453 } catch (error) {
2454 return await errorHandler(error, routeId, nextResult);
2455 }
2456}
2457function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
2458 let lazyMiddlewarePromise = loadLazyRouteProperty({
2459 key: "middleware",
2460 route: match.route,
2461 manifest,
2462 mapRouteProperties
2463 });
2464 let lazyRoutePromises = loadLazyRoute(match.route, isMutationMethod(request.method) ? "action" : "loader", manifest, mapRouteProperties, lazyRoutePropertiesToSkip);
2465 return {
2466 middleware: lazyMiddlewarePromise,
2467 route: lazyRoutePromises.lazyRoutePromise,
2468 handler: lazyRoutePromises.lazyHandlerPromise
2469 };
2470}
2471function getDataStrategyMatch(mapRouteProperties, manifest, request, path, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, shouldRevalidateArgs = null, callSiteDefaultShouldRevalidate) {
2472 let isUsingNewApi = false;
2473 let _lazyPromises = getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip);
2474 return {
2475 ...match,
2476 _lazyPromises,
2477 shouldLoad,
2478 shouldRevalidateArgs,
2479 shouldCallHandler(defaultShouldRevalidate) {
2480 isUsingNewApi = true;
2481 if (!shouldRevalidateArgs) return shouldLoad;
2482 if (typeof callSiteDefaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
2483 ...shouldRevalidateArgs,
2484 defaultShouldRevalidate: callSiteDefaultShouldRevalidate
2485 });
2486 if (typeof defaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
2487 ...shouldRevalidateArgs,
2488 defaultShouldRevalidate
2489 });
2490 return shouldRevalidateLoader(match, shouldRevalidateArgs);
2491 },
2492 resolve(handlerOverride) {
2493 let { lazy, loader, middleware } = match.route;
2494 let callHandler = isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (lazy || loader);
2495 let isMiddlewareOnlyRoute = middleware && middleware.length > 0 && !loader && !lazy;
2496 if (callHandler && (isMutationMethod(request.method) || !isMiddlewareOnlyRoute)) return callLoaderOrAction({
2497 request,
2498 path,
2499 pattern,
2500 match,
2501 lazyHandlerPromise: _lazyPromises?.handler,
2502 lazyRoutePromise: _lazyPromises?.route,
2503 handlerOverride,
2504 scopedContext
2505 });
2506 return Promise.resolve({
2507 type: "data",
2508 result: void 0
2509 });
2510 }
2511 };
2512}
2513function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, path, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
2514 return matches.map((match) => {
2515 if (match.route.id !== targetMatch.route.id) return {
2516 ...match,
2517 shouldLoad: false,
2518 shouldRevalidateArgs,
2519 shouldCallHandler: () => false,
2520 _lazyPromises: getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip),
2521 resolve: () => Promise.resolve({
2522 type: "data",
2523 result: void 0
2524 })
2525 };
2526 return getDataStrategyMatch(mapRouteProperties, manifest, request, path, getRoutePattern(matches), match, lazyRoutePropertiesToSkip, scopedContext, true, shouldRevalidateArgs);
2527 });
2528}
2529async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, initiator, scopedContext, isStaticHandler, navigationType = null) {
2530 if (matches.some((m) => m._lazyPromises?.middleware)) await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
2531 let dataStrategyArgs = {
2532 request,
2533 url: createDataFunctionUrl(request, path),
2534 pattern: getRoutePattern(matches),
2535 params: matches[0].params,
2536 context: scopedContext,
2537 matches,
2538 unstable_initiator: initiator,
2539 unstable_navigationType: initiator === "navigation" ? navigationType : null
2540 };
2541 let runClientMiddleware = isStaticHandler ? () => {
2542 throw new Error("You cannot call `runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`");
2543 } : (cb) => {
2544 let typedDataStrategyArgs = dataStrategyArgs;
2545 return runClientMiddlewarePipeline(typedDataStrategyArgs, () => {
2546 return cb({
2547 ...typedDataStrategyArgs,
2548 fetcherKey,
2549 runClientMiddleware: () => {
2550 throw new Error("Cannot call `runClientMiddleware()` from within an `runClientMiddleware` handler");
2551 }
2552 });
2553 });
2554 };
2555 let results = await dataStrategyImpl({
2556 ...dataStrategyArgs,
2557 fetcherKey,
2558 runClientMiddleware
2559 });
2560 try {
2561 await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
2562 } catch {}
2563 return results;
2564}
2565async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
2566 let result;
2567 let onReject;
2568 let isAction = isMutationMethod(request.method);
2569 let type = isAction ? "action" : "loader";
2570 let runHandler = (handler) => {
2571 let reject;
2572 let abortPromise = new Promise((_, r) => reject = r);
2573 onReject = () => reject();
2574 request.signal.addEventListener("abort", onReject);
2575 let actualHandler = (ctx) => {
2576 if (typeof handler !== "function") return Promise.reject(/* @__PURE__ */ new Error(`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`));
2577 return handler({
2578 request,
2579 url: createDataFunctionUrl(request, path),
2580 pattern,
2581 params: match.params,
2582 context: scopedContext
2583 }, ...ctx !== void 0 ? [ctx] : []);
2584 };
2585 let handlerPromise = (async () => {
2586 try {
2587 return {
2588 type: "data",
2589 result: await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler())
2590 };
2591 } catch (e) {
2592 return {
2593 type: "error",
2594 result: e
2595 };
2596 }
2597 })();
2598 return Promise.race([handlerPromise, abortPromise]);
2599 };
2600 try {
2601 let handler = isAction ? match.route.action : match.route.loader;
2602 if (lazyHandlerPromise || lazyRoutePromise) if (handler) {
2603 let handlerError;
2604 let [value] = await Promise.all([
2605 runHandler(handler).catch((e) => {
2606 handlerError = e;
2607 }),
2608 lazyHandlerPromise,
2609 lazyRoutePromise
2610 ]);
2611 if (handlerError !== void 0) throw handlerError;
2612 result = value;
2613 } else {
2614 await lazyHandlerPromise;
2615 let handler = isAction ? match.route.action : match.route.loader;
2616 if (handler) [result] = await Promise.all([runHandler(handler), lazyRoutePromise]);
2617 else if (type === "action") {
2618 let url = new URL(request.url);
2619 let pathname = url.pathname + url.search;
2620 throw getInternalRouterError(405, {
2621 method: request.method,
2622 pathname,
2623 routeId: match.route.id
2624 });
2625 } else {
2626 await lazyRoutePromise;
2627 return {
2628 type: "data",
2629 result: void 0
2630 };
2631 }
2632 }
2633 else if (!handler) {
2634 let url = new URL(request.url);
2635 throw getInternalRouterError(404, { pathname: url.pathname + url.search });
2636 } else result = await runHandler(handler);
2637 } catch (e) {
2638 return {
2639 type: "error",
2640 result: e
2641 };
2642 } finally {
2643 if (onReject) request.signal.removeEventListener("abort", onReject);
2644 }
2645 return result;
2646}
2647async function parseResponseBody(response) {
2648 let contentType = response.headers.get("Content-Type");
2649 if (contentType && /\bapplication\/json\b/.test(contentType)) return response.body == null ? null : response.json();
2650 return response.text();
2651}
2652async function convertDataStrategyResultToDataResult(dataStrategyResult) {
2653 let { result, type } = dataStrategyResult;
2654 if (isResponse(result)) {
2655 let data;
2656 try {
2657 data = await parseResponseBody(result);
2658 } catch (e) {
2659 return {
2660 type: "error",
2661 error: e
2662 };
2663 }
2664 if (type === "error") return {
2665 type: "error",
2666 error: new ErrorResponseImpl(result.status, result.statusText, data),
2667 statusCode: result.status,
2668 headers: result.headers
2669 };
2670 return {
2671 type: "data",
2672 data,
2673 statusCode: result.status,
2674 headers: result.headers
2675 };
2676 }
2677 if (type === "error") {
2678 if (isDataWithResponseInit(result)) {
2679 if (result.data instanceof Error) return {
2680 type: "error",
2681 error: result.data,
2682 statusCode: result.init?.status,
2683 headers: result.init?.headers ? new Headers(result.init.headers) : void 0
2684 };
2685 return {
2686 type: "error",
2687 error: dataWithResponseInitToErrorResponse(result),
2688 statusCode: isRouteErrorResponse(result) ? result.status : void 0,
2689 headers: result.init?.headers ? new Headers(result.init.headers) : void 0
2690 };
2691 }
2692 return {
2693 type: "error",
2694 error: result,
2695 statusCode: isRouteErrorResponse(result) ? result.status : void 0
2696 };
2697 }
2698 if (isDataWithResponseInit(result)) return {
2699 type: "data",
2700 data: result.data,
2701 statusCode: result.init?.status,
2702 headers: result.init?.headers ? new Headers(result.init.headers) : void 0
2703 };
2704 return {
2705 type: "data",
2706 data: result
2707 };
2708}
2709function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
2710 let location = response.headers.get("Location");
2711 invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
2712 if (!isAbsoluteUrl(location)) {
2713 let trimmedMatches = matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1);
2714 location = normalizeTo(new URL(request.url), trimmedMatches, basename, location);
2715 response.headers.set("Location", location);
2716 }
2717 return response;
2718}
2719const invalidProtocols = [
2720 "about:",
2721 "blob:",
2722 "chrome:",
2723 "chrome-untrusted:",
2724 "content:",
2725 "data:",
2726 "devtools:",
2727 "file:",
2728 "filesystem:",
2729 "javascript:"
2730];
2731function hasInvalidProtocol(location) {
2732 try {
2733 return invalidProtocols.includes(new URL(location).protocol);
2734 } catch {
2735 return false;
2736 }
2737}
2738function normalizeRedirectLocation(location, currentUrl, basename, historyInstance) {
2739 if (isAbsoluteUrl(location)) {
2740 let normalizedLocation = location;
2741 let url = PROTOCOL_RELATIVE_URL_REGEX.test(normalizedLocation) ? new URL(normalizeProtocolRelativeUrl(normalizedLocation, currentUrl.protocol)) : new URL(normalizedLocation);
2742 if (hasInvalidProtocol(url.toString())) throw new Error("Invalid redirect location");
2743 let isSameBasename = stripBasename(url.pathname, basename) != null;
2744 if (url.origin === currentUrl.origin && isSameBasename) return removeDoubleSlashes(url.pathname) + url.search + url.hash;
2745 }
2746 try {
2747 if (hasInvalidProtocol(historyInstance.createURL(location).toString())) throw new Error("Invalid redirect location");
2748 } catch {}
2749 return location;
2750}
2751function createClientSideRequest(history, location, signal, submission) {
2752 let url = history.createURL(stripHashFromPath(location)).toString();
2753 let init = { signal };
2754 if (submission && isMutationMethod(submission.formMethod)) {
2755 let { formMethod, formEncType } = submission;
2756 init.method = formMethod.toUpperCase();
2757 if (formEncType === "application/json") {
2758 init.headers = new Headers({ "Content-Type": formEncType });
2759 init.body = JSON.stringify(submission.json);
2760 } else if (formEncType === "text/plain") init.body = submission.text;
2761 else if (formEncType === "application/x-www-form-urlencoded" && submission.formData) init.body = convertFormDataToSearchParams(submission.formData);
2762 else init.body = submission.formData;
2763 }
2764 return new Request(url, init);
2765}
2766function convertFormDataToSearchParams(formData) {
2767 let searchParams = new URLSearchParams();
2768 for (let [key, value] of formData.entries()) searchParams.append(key, typeof value === "string" ? value : value.name);
2769 return searchParams;
2770}
2771function convertSearchParamsToFormData(searchParams) {
2772 let formData = new FormData();
2773 for (let [key, value] of searchParams.entries()) formData.append(key, value);
2774 return formData;
2775}
2776function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
2777 let loaderData = {};
2778 let errors = null;
2779 let statusCode;
2780 let foundError = false;
2781 let loaderHeaders = {};
2782 let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
2783 matches.forEach((match) => {
2784 if (!(match.route.id in results)) return;
2785 let id = match.route.id;
2786 let result = results[id];
2787 invariant(!isRedirectResult(result), "Cannot handle redirect results in processLoaderData");
2788 if (isErrorResult(result)) {
2789 let error = result.error;
2790 if (pendingError !== void 0) {
2791 error = pendingError;
2792 pendingError = void 0;
2793 }
2794 errors = errors || {};
2795 if (skipLoaderErrorBubbling) errors[id] = error;
2796 else {
2797 let boundaryMatch = findNearestBoundary(matches, id);
2798 if (errors[boundaryMatch.route.id] == null) errors[boundaryMatch.route.id] = error;
2799 }
2800 if (!isStaticHandler) loaderData[id] = ResetLoaderDataSymbol;
2801 if (!foundError) {
2802 foundError = true;
2803 statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
2804 }
2805 if (result.headers) loaderHeaders[id] = result.headers;
2806 } else {
2807 loaderData[id] = result.data;
2808 if (result.statusCode && result.statusCode !== 200 && !foundError) statusCode = result.statusCode;
2809 if (result.headers) loaderHeaders[id] = result.headers;
2810 }
2811 });
2812 if (pendingError !== void 0 && pendingActionResult) {
2813 errors = { [pendingActionResult[0]]: pendingError };
2814 if (pendingActionResult[2]) loaderData[pendingActionResult[2]] = void 0;
2815 }
2816 return {
2817 loaderData,
2818 errors,
2819 statusCode: statusCode || 200,
2820 loaderHeaders
2821 };
2822}
2823function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers) {
2824 let { loaderData, errors } = processRouteLoaderData(matches, results, pendingActionResult);
2825 revalidatingFetchers.filter((f) => !f.matches || f.matches.some((m) => m.shouldLoad)).forEach((rf) => {
2826 let { key, routeId, controller } = rf;
2827 if (controller && controller.signal.aborted) return;
2828 let result = fetcherResults[key];
2829 invariant(result, "Did not find corresponding fetcher result");
2830 if (isErrorResult(result)) {
2831 let boundaryMatch = findNearestBoundary(state.matches, routeId);
2832 if (!(errors && errors[boundaryMatch.route.id])) errors = {
2833 ...errors,
2834 [boundaryMatch.route.id]: result.error
2835 };
2836 workingFetchers.delete(key);
2837 } else if (isRedirectResult(result)) invariant(false, "Unhandled fetcher revalidation redirect");
2838 else {
2839 let doneFetcher = getDoneFetcher(result.data);
2840 workingFetchers.set(key, doneFetcher);
2841 }
2842 });
2843 return {
2844 loaderData,
2845 errors
2846 };
2847}
2848function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
2849 let mergedLoaderData = Object.entries(newLoaderData).filter(([, v]) => v !== ResetLoaderDataSymbol).reduce((merged, [k, v]) => {
2850 merged[k] = v;
2851 return merged;
2852 }, {});
2853 let preservedCount = 0;
2854 for (let match of matches) {
2855 let id = match.route.id;
2856 if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) {
2857 mergedLoaderData[id] = loaderData[id];
2858 preservedCount++;
2859 }
2860 if (errors && errors.hasOwnProperty(id)) break;
2861 }
2862 return Object.keys(newLoaderData).length === 0 && preservedCount === Object.keys(loaderData).length ? loaderData : mergedLoaderData;
2863}
2864function getActionDataForCommit(pendingActionResult) {
2865 if (!pendingActionResult) return {};
2866 return isErrorResult(pendingActionResult[1]) ? { actionData: {} } : { actionData: { [pendingActionResult[0]]: pendingActionResult[1].data } };
2867}
2868function findNearestBoundary(matches, routeId) {
2869 return (routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches]).reverse().find((m) => m.route.ErrorBoundary != null || m.route.errorElement != null) || matches[0];
2870}
2871function getShortCircuitMatches(routes) {
2872 let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || { id: `__shim-error-route__` };
2873 return {
2874 matches: [{
2875 params: {},
2876 pathname: "",
2877 pathnameBase: "",
2878 route
2879 }],
2880 route
2881 };
2882}
2883function getInternalRouterError(status, { pathname, routeId, method, type, message } = {}) {
2884 let statusText = "Unknown Server Error";
2885 let errorMessage = "Unknown @remix-run/router error";
2886 if (status === 400) {
2887 statusText = "Bad Request";
2888 if (method && pathname && routeId) errorMessage = `You made a ${method} request to "${pathname}" but did not provide a \`loader\` for route "${routeId}", so there is no way to handle the request.`;
2889 else if (type === "invalid-body") errorMessage = "Unable to encode submission body";
2890 } else if (status === 403) {
2891 statusText = "Forbidden";
2892 errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
2893 } else if (status === 404) {
2894 statusText = "Not Found";
2895 errorMessage = `No route matches URL "${pathname}"`;
2896 } else if (status === 405) {
2897 statusText = "Method Not Allowed";
2898 if (method && pathname && routeId) errorMessage = `You made a ${method.toUpperCase()} request to "${pathname}" but did not provide an \`action\` for route "${routeId}", so there is no way to handle the request.`;
2899 else if (method) errorMessage = `Invalid request method "${method.toUpperCase()}"`;
2900 }
2901 return new ErrorResponseImpl(status || 500, statusText, new Error(errorMessage), true);
2902}
2903function findRedirect(results) {
2904 let entries = Object.entries(results);
2905 for (let i = entries.length - 1; i >= 0; i--) {
2906 let [key, result] = entries[i];
2907 if (isRedirectResult(result)) return {
2908 key,
2909 result
2910 };
2911 }
2912}
2913function stripHashFromPath(path) {
2914 return createPath({
2915 ...typeof path === "string" ? parsePath(path) : path,
2916 hash: ""
2917 });
2918}
2919function isHashChangeOnly(a, b) {
2920 if (a.pathname !== b.pathname || a.search !== b.search) return false;
2921 if (a.hash === "") return b.hash !== "";
2922 else if (a.hash === b.hash) return true;
2923 else if (b.hash !== "") return true;
2924 return false;
2925}
2926function dataWithResponseInitToResponse(data) {
2927 return Response.json(data.data, data.init ?? void 0);
2928}
2929function dataWithResponseInitToErrorResponse(data) {
2930 return new ErrorResponseImpl(data.init?.status ?? 500, data.init?.statusText ?? "Internal Server Error", data.data);
2931}
2932function isDataStrategyResults(result) {
2933 return result != null && typeof result === "object" && Object.entries(result).every(([key, value]) => typeof key === "string" && isDataStrategyResult(value));
2934}
2935function isDataStrategyResult(result) {
2936 return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" || result.type === "error");
2937}
2938function isRedirectDataStrategyResult(result) {
2939 return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
2940}
2941function isErrorResult(result) {
2942 return result.type === "error";
2943}
2944function isRedirectResult(result) {
2945 return (result && result.type) === "redirect";
2946}
2947function isDataWithResponseInit(value) {
2948 return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
2949}
2950function isResponse(value) {
2951 return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
2952}
2953function isRedirectStatusCode(statusCode) {
2954 return redirectStatusCodes.has(statusCode);
2955}
2956function isRedirectResponse(result) {
2957 return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
2958}
2959function isValidMethod(method) {
2960 return validRequestMethods.has(method.toUpperCase());
2961}
2962function isMutationMethod(method) {
2963 return validMutationMethods.has(method.toUpperCase());
2964}
2965function hasNakedIndexQuery(search) {
2966 return new URLSearchParams(search).getAll("index").some((v) => v === "");
2967}
2968function getTargetMatch(matches, location) {
2969 let search = typeof location === "string" ? parsePath(location).search : location.search;
2970 if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) return matches[matches.length - 1];
2971 let pathMatches = getPathContributingMatches(matches);
2972 return pathMatches[pathMatches.length - 1];
2973}
2974function getInstrumentationNavigateMeta(history, location, matches) {
2975 return {
2976 url: createDataFunctionUrl(history.createURL(location), location),
2977 pattern: matches ? getRoutePattern(matches) : "",
2978 params: matches?.[0]?.params ? { ...matches[0].params } : {}
2979 };
2980}
2981function getSubmissionFromNavigation(navigation) {
2982 let { formMethod, formAction, formEncType, text, formData, json } = navigation;
2983 if (!formMethod || !formAction || !formEncType) return;
2984 if (text != null) return {
2985 formMethod,
2986 formAction,
2987 formEncType,
2988 formData: void 0,
2989 json: void 0,
2990 text
2991 };
2992 else if (formData != null) return {
2993 formMethod,
2994 formAction,
2995 formEncType,
2996 formData,
2997 json: void 0,
2998 text: void 0
2999 };
3000 else if (json !== void 0) return {
3001 formMethod,
3002 formAction,
3003 formEncType,
3004 formData: void 0,
3005 json,
3006 text: void 0
3007 };
3008}
3009function getLoadingNavigation(location, matches, historyAction, submission) {
3010 if (submission) return {
3011 state: "loading",
3012 location,
3013 matches,
3014 historyAction,
3015 formMethod: submission.formMethod,
3016 formAction: submission.formAction,
3017 formEncType: submission.formEncType,
3018 formData: submission.formData,
3019 json: submission.json,
3020 text: submission.text
3021 };
3022 else return {
3023 state: "loading",
3024 location,
3025 matches,
3026 historyAction,
3027 formMethod: void 0,
3028 formAction: void 0,
3029 formEncType: void 0,
3030 formData: void 0,
3031 json: void 0,
3032 text: void 0
3033 };
3034}
3035function getSubmittingNavigation(location, matches, historyAction, submission) {
3036 return {
3037 state: "submitting",
3038 location,
3039 matches,
3040 historyAction,
3041 formMethod: submission.formMethod,
3042 formAction: submission.formAction,
3043 formEncType: submission.formEncType,
3044 formData: submission.formData,
3045 json: submission.json,
3046 text: submission.text
3047 };
3048}
3049function getLoadingFetcher(submission, data) {
3050 if (submission) return {
3051 state: "loading",
3052 formMethod: submission.formMethod,
3053 formAction: submission.formAction,
3054 formEncType: submission.formEncType,
3055 formData: submission.formData,
3056 json: submission.json,
3057 text: submission.text,
3058 data
3059 };
3060 else return {
3061 state: "loading",
3062 formMethod: void 0,
3063 formAction: void 0,
3064 formEncType: void 0,
3065 formData: void 0,
3066 json: void 0,
3067 text: void 0,
3068 data
3069 };
3070}
3071function getSubmittingFetcher(submission, existingFetcher) {
3072 return {
3073 state: "submitting",
3074 formMethod: submission.formMethod,
3075 formAction: submission.formAction,
3076 formEncType: submission.formEncType,
3077 formData: submission.formData,
3078 json: submission.json,
3079 text: submission.text,
3080 data: existingFetcher ? existingFetcher.data : void 0
3081 };
3082}
3083function getDoneFetcher(data) {
3084 return {
3085 state: "idle",
3086 formMethod: void 0,
3087 formAction: void 0,
3088 formEncType: void 0,
3089 formData: void 0,
3090 json: void 0,
3091 text: void 0,
3092 data
3093 };
3094}
3095function restoreAppliedTransitions(_window, transitions) {
3096 try {
3097 let sessionPositions = _window.sessionStorage.getItem(TRANSITIONS_STORAGE_KEY);
3098 if (sessionPositions) {
3099 let json = JSON.parse(sessionPositions);
3100 for (let [k, v] of Object.entries(json || {})) if (v && Array.isArray(v)) transitions.set(k, new Set(v || []));
3101 }
3102 } catch {}
3103}
3104function persistAppliedTransitions(_window, transitions) {
3105 if (transitions.size > 0) {
3106 let json = {};
3107 for (let [k, v] of transitions) json[k] = [...v];
3108 try {
3109 _window.sessionStorage.setItem(TRANSITIONS_STORAGE_KEY, JSON.stringify(json));
3110 } catch (error) {
3111 warning(false, `Failed to save applied view transitions in sessionStorage (${error}).`);
3112 }
3113 }
3114}
3115function createDeferred() {
3116 let resolve;
3117 let reject;
3118 let promise = new Promise((res, rej) => {
3119 resolve = async (val) => {
3120 res(val);
3121 try {
3122 await promise;
3123 } catch {}
3124 };
3125 reject = async (error) => {
3126 rej(error);
3127 try {
3128 await promise;
3129 } catch {}
3130 };
3131 });
3132 return {
3133 promise,
3134 resolve,
3135 reject
3136 };
3137}
3138//#endregion
3139export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, clientErrors, createDataRouteMatcher, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };