UNPKG

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