UNPKG

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