UNPKG

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