INDIE UNPKG

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