UNPKG

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