_ */ new Map(); let activeFetchers = /* @__PURE__ */ new Map(); let fetchersQueuedForDeletion = /* @__PURE__ */ new Set(); let blockerFunctions = /* @__PURE__ */ new Map(); let unblockBlockerHistoryUpdate = void 0; let pendingRevalidationDfd = null; function initialize() { unlistenHistory = init.history.listen( ({ action: historyAction, location, delta }) => { if (unblockBlockerHistoryUpdate) { unblockBlockerHistoryUpdate(); unblockBlockerHistoryUpdate = void 0; return; } 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." ); let blockerKey = shouldBlockNavigation({ currentLocation: state.location, nextLocation: location, historyAction }); if (blockerKey && delta != null) { let nextHistoryUpdatePromise = new Promise((resolve) => { unblockBlockerHistoryUpdate = resolve; }); init.history.go(delta * -1); updateBlocker(blockerKey, { state: "blocked", location, proceed() { updateBlocker(blockerKey, { state: "proceeding", proceed: void 0, reset: void 0, location }); nextHistoryUpdatePromise.then(() => init.history.go(delta)); }, reset() { let blockers = new Map(state.blockers); blockers.set(blockerKey, IDLE_BLOCKER); updateState({ blockers }); } }); return; } return startNavigation(historyAction, location); } ); if (isBrowser2) { restoreAppliedTransitions(routerWindow, appliedViewTransitions); let _saveAppliedTransitions = () => persistAppliedTransitions(routerWindow, appliedViewTransitions); routerWindow.addEventListener("pagehide", _saveAppliedTransitions); removePageHideEventListener = () => routerWindow.removeEventListener("pagehide", _saveAppliedTransitions); } if (!state.initialized) { startNavigation("POP", state.location, { initialHydration: true }); } return router2; } function dispose() { if (unlistenHistory) { unlistenHistory(); } if (removePageHideEventListener) { removePageHideEventListener(); } subscribers.clear(); pendingNavigationController && pendingNavigationController.abort(); state.fetchers.forEach((_, key) => deleteFetcher(key)); state.blockers.forEach((_, key) => deleteBlocker(key)); } function subscribe(fn) { subscribers.add(fn); return () => subscribers.delete(fn); } function updateState(newState, opts = {}) { state = { ...state, ...newState }; let unmountedFetchers = []; let mountedFetchers = []; state.fetchers.forEach((fetcher, key) => { if (fetcher.state === "idle") { if (fetchersQueuedForDeletion.has(key)) { unmountedFetchers.push(key); } else { mountedFetchers.push(key); } } }); fetchersQueuedForDeletion.forEach((key) => { if (!state.fetchers.has(key) && !fetchControllers.has(key)) { unmountedFetchers.push(key); } }); [...subscribers].forEach( (subscriber) => subscriber(state, { deletedFetchers: unmountedFetchers, viewTransitionOpts: opts.viewTransitionOpts, flushSync: opts.flushSync === true }) ); unmountedFetchers.forEach((key) => deleteFetcher(key)); mountedFetchers.forEach((key) => state.fetchers.delete(key)); } function completeNavigation(location, newState, { flushSync: flushSync2 } = {}) { let isActionReload = state.actionData != null && state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && state.navigation.state === "loading" && location.state?._isRedirect !== true; let actionData; if (newState.actionData) { if (Object.keys(newState.actionData).length > 0) { actionData = newState.actionData; } else { actionData = null; } } else if (isActionReload) { actionData = state.actionData; } else { actionData = null; } let loaderData = newState.loaderData ? mergeLoaderData( state.loaderData, newState.loaderData, newState.matches || [], newState.errors ) : state.loaderData; let blockers = state.blockers; if (blockers.size > 0) { blockers = new Map(blockers); blockers.forEach((_, k) => blockers.set(k, IDLE_BLOCKER)); } let preventScrollReset = pendingPreventScrollReset === true || state.navigation.formMethod != null && isMutationMethod(state.navigation.formMethod) && location.state?._isRedirect !== true; if (inFlightDataRoutes) { dataRoutes = inFlightDataRoutes; inFlightDataRoutes = void 0; } if (isUninterruptedRevalidation) { } else if (pendingAction === "POP") { } else if (pendingAction === "PUSH") { init.history.push(location, location.state); } else if (pendingAction === "REPLACE") { init.history.replace(location, location.state); } let viewTransitionOpts; if (pendingAction === "POP") { let priorPaths = appliedViewTransitions.get(state.location.pathname); if (priorPaths && priorPaths.has(location.pathname)) { viewTransitionOpts = { currentLocation: state.location, nextLocation: location }; } else if (appliedViewTransitions.has(location.pathname)) { viewTransitionOpts = { currentLocation: location, nextLocation: state.location }; } } else if (pendingViewTransitionEnabled) { let toPaths = appliedViewTransitions.get(state.location.pathname); if (toPaths) { toPaths.add(location.pathname); } else { toPaths = /* @__PURE__ */ new Set([location.pathname]); appliedViewTransitions.set(state.location.pathname, toPaths); } viewTransitionOpts = { currentLocation: state.location, nextLocation: location }; } updateState( { ...newState, // matches, errors, fetchers go through as-is actionData, loaderData, historyAction: pendingAction, location, initialized: true, navigation: IDLE_NAVIGATION, revalidation: "idle", restoreScrollPosition: getSavedScrollPosition( location, newState.matches || state.matches ), preventScrollReset, blockers }, { viewTransitionOpts, flushSync: flushSync2 === true } ); pendingAction = "POP"; pendingPreventScrollReset = false; pendingViewTransitionEnabled = false; isUninterruptedRevalidation = false; isRevalidationRequired = false; pendingRevalidationDfd?.resolve(); pendingRevalidationDfd = null; } async function navigate(to, opts) { if (typeof to === "number") { init.history.go(to); return; } let normalizedPath = normalizeTo( state.location, state.matches, basename, to, opts?.fromRouteId, opts?.relative ); let { path, submission, error } = normalizeNavigateOptions( false, normalizedPath, opts ); let currentLocation = state.location; let nextLocation = createLocation(state.location, path, opts && opts.state); nextLocation = { ...nextLocation, ...init.history.encodeLocation(nextLocation) }; let userReplace = opts && opts.replace != null ? opts.replace : void 0; let historyAction = "PUSH"; if (userReplace === true) { historyAction = "REPLACE"; } else if (userReplace === false) { } else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) { historyAction = "REPLACE"; } let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : void 0; let flushSync2 = (opts && opts.flushSync) === true; let blockerKey = shouldBlockNavigation({ currentLocation, nextLocation, historyAction }); if (blockerKey) { updateBlocker(blockerKey, { state: "blocked", location: nextLocation, proceed() { updateBlocker(blockerKey, { state: "proceeding", proceed: void 0, reset: void 0, location: nextLocation }); navigate(to, opts); }, reset() { let blockers = new Map(state.blockers); blockers.set(blockerKey, IDLE_BLOCKER); updateState({ blockers }); } }); return; } await startNavigation(historyAction, nextLocation, { submission, // Send through the formData serialization error if we have one so we can // render at the right error boundary after we match routes pendingError: error, preventScrollReset, replace: opts && opts.replace, enableViewTransition: opts && opts.viewTransition, flushSync: flushSync2 }); } function revalidate() { if (!pendingRevalidationDfd) { pendingRevalidationDfd = createDeferred(); } interruptActiveLoads(); updateState({ revalidation: "loading" }); let promise = pendingRevalidationDfd.promise; if (state.navigation.state === "submitting") { return promise; } if (state.navigation.state === "idle") { startNavigation(state.historyAction, state.location, { startUninterruptedRevalidation: true }); return promise; } startNavigation( pendingAction || state.historyAction, state.navigation.location, { overrideNavigation: state.navigation, // Proxy through any rending view transition enableViewTransition: pendingViewTransitionEnabled === true } ); return promise; } async function startNavigation(historyAction, location, opts) { pendingNavigationController && pendingNavigationController.abort(); pendingNavigationController = null; pendingAction = historyAction; isUninterruptedRevalidation = (opts && opts.startUninterruptedRevalidation) === true; saveScrollPosition(state.location, state.matches); pendingPreventScrollReset = (opts && opts.preventScrollReset) === true; pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true; let routesToUse = inFlightDataRoutes || dataRoutes; let loadingNavigation = opts && opts.overrideNavigation; let matches = opts?.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? ( // `matchRoutes()` has already been called if we're in here via `router.initialize()` state.matches ) : matchRoutes(routesToUse, location, basename); let flushSync2 = (opts && opts.flushSync) === true; if (matches && state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) { completeNavigation(location, { matches }, { flushSync: flushSync2 }); return; } let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname); if (fogOfWar.active && fogOfWar.matches) { matches = fogOfWar.matches; } if (!matches) { let { error, notFoundMatches, route } = handleNavigational404( location.pathname ); completeNavigation( location, { matches: notFoundMatches, loaderData: {}, errors: { [route.id]: error } }, { flushSync: flushSync2 } ); return; } pendingNavigationController = new AbortController(); let request = createClientSideRequest( init.history, location, pendingNavigationController.signal, opts && opts.submission ); let scopedContext = new unstable_RouterContextProvider( init.unstable_getContext ? await init.unstable_getContext() : void 0 ); let pendingActionResult; if (opts && opts.pendingError) { pendingActionResult = [ findNearestBoundary(matches).route.id, { type: "error", error: opts.pendingError } ]; } else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) { let actionResult = await handleAction( request, location, opts.submission, matches, scopedContext, fogOfWar.active, { replace: opts.replace, flushSync: flushSync2 } ); if (actionResult.shortCircuited) { return; } if (actionResult.pendingActionResult) { let [routeId, result] = actionResult.pendingActionResult; if (isErrorResult(result) && isRouteErrorResponse(result.error) && result.error.status === 404) { pendingNavigationController = null; completeNavigation(location, { matches: actionResult.matches, loaderData: {}, errors: { [routeId]: result.error } }); return; } } matches = actionResult.matches || matches; pendingActionResult = actionResult.pendingActionResult; loadingNavigation = getLoadingNavigation(location, opts.submission); flushSync2 = false; fogOfWar.active = false; request = createClientSideRequest( init.history, request.url, request.signal ); } let { shortCircuited, matches: updatedMatches, loaderData, errors } = await handleLoaders( request, location, matches, scopedContext, fogOfWar.active, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync2, pendingActionResult ); if (shortCircuited) { return; } pendingNavigationController = null; completeNavigation(location, { matches: updatedMatches || matches, ...getActionDataForCommit(pendingActionResult), loaderData, errors }); } async function handleAction(request, location, submission, matches, scopedContext, isFogOfWar, opts = {}) { interruptActiveLoads(); let navigation = getSubmittingNavigation(location, submission); updateState({ navigation }, { flushSync: opts.flushSync === true }); if (isFogOfWar) { let discoverResult = await discoverRoutes( matches, location.pathname, request.signal ); if (discoverResult.type === "aborted") { return { shortCircuited: true }; } else if (discoverResult.type === "error") { let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id; return { matches: discoverResult.partialMatches, pendingActionResult: [ boundaryId, { type: "error", error: discoverResult.error } ] }; } else if (!discoverResult.matches) { let { notFoundMatches, error, route } = handleNavigational404( location.pathname ); return { matches: notFoundMatches, pendingActionResult: [ route.id, { type: "error", error } ] }; } else { matches = discoverResult.matches; } } let result; let actionMatch = getTargetMatch(matches, location); if (!actionMatch.route.action && !actionMatch.route.lazy) { result = { type: "error", error: getInternalRouterError(405, { method: request.method, pathname: location.pathname, routeId: actionMatch.route.id }) }; } else { let results = await callDataStrategy( "action", request, [actionMatch], matches, scopedContext, null ); result = results[actionMatch.route.id]; if (!result) { for (let match of matches) { if (results[match.route.id]) { result = results[match.route.id]; break; } } } if (request.signal.aborted) { return { shortCircuited: true }; } } if (isRedirectResult(result)) { let replace2; if (opts && opts.replace != null) { replace2 = opts.replace; } else { let location2 = normalizeRedirectLocation( result.response.headers.get("Location"), new URL(request.url), basename ); replace2 = location2 === state.location.pathname + state.location.search; } await startRedirectNavigation(request, result, true, { submission, replace: replace2 }); return { shortCircuited: true }; } if (isErrorResult(result)) { let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id); if ((opts && opts.replace) !== true) { pendingAction = "PUSH"; } return { matches, pendingActionResult: [boundaryMatch.route.id, result] }; } return { matches, pendingActionResult: [actionMatch.route.id, result] }; } async function handleLoaders(request, location, matches, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace2, initialHydration, flushSync2, pendingActionResult) { let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission); let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation); let shouldUpdateNavigationState = !isUninterruptedRevalidation && !initialHydration; if (isFogOfWar) { if (shouldUpdateNavigationState) { let actionData = getUpdatedActionData(pendingActionResult); updateState( { navigation: loadingNavigation, ...actionData !== void 0 ? { actionData } : {} }, { flushSync: flushSync2 } ); } let discoverResult = await discoverRoutes( matches, location.pathname, request.signal ); if (discoverResult.type === "aborted") { return { shortCircuited: true }; } else if (discoverResult.type === "error") { let boundaryId = findNearestBoundary(discoverResult.partialMatches).route.id; return { matches: discoverResult.partialMatches, loaderData: {}, errors: { [boundaryId]: discoverResult.error } }; } else if (!discoverResult.matches) { let { error, notFoundMatches, route } = handleNavigational404( location.pathname ); return { matches: notFoundMatches, loaderData: {}, errors: { [route.id]: error } }; } else { matches = discoverResult.matches; } } let routesToUse = inFlightDataRoutes || dataRoutes; let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad( init.history, state, matches, activeSubmission, location, initialHydration === true, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult ); pendingNavigationLoadId = ++incrementingLoadId; if (matchesToLoad.length === 0 && revalidatingFetchers.length === 0) { let updatedFetchers2 = markFetchRedirectsDone(); completeNavigation( location, { matches, loaderData: {}, // Commit pending error if we're short circuiting errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null, ...getActionDataForCommit(pendingActionResult), ...updatedFetchers2 ? { fetchers: new Map(state.fetchers) } : {} }, { flushSync: flushSync2 } ); return { shortCircuited: true }; } if (shouldUpdateNavigationState) { let updates = {}; if (!isFogOfWar) { updates.navigation = loadingNavigation; let actionData = getUpdatedActionData(pendingActionResult); if (actionData !== void 0) { updates.actionData = actionData; } } if (revalidatingFetchers.length > 0) { updates.fetchers = getUpdatedRevalidatingFetchers(revalidatingFetchers); } updateState(updates, { flushSync: flushSync2 }); } revalidatingFetchers.forEach((rf) => { abortFetcher(rf.key); if (rf.controller) { fetchControllers.set(rf.key, rf.controller); } }); let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((f) => abortFetcher(f.key)); if (pendingNavigationController) { pendingNavigationController.signal.addEventListener( "abort", abortPendingFetchRevalidations ); } let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData( matches, matchesToLoad, revalidatingFetchers, request, scopedContext ); if (request.signal.aborted) { return { shortCircuited: true }; } if (pendingNavigationController) { pendingNavigationController.signal.removeEventListener( "abort", abortPendingFetchRevalidations ); } revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key)); let redirect2 = findRedirect(loaderResults); if (redirect2) { await startRedirectNavigation(request, redirect2.result, true, { replace: replace2 }); return { shortCircuited: true }; } redirect2 = findRedirect(fetcherResults); if (redirect2) { fetchRedirectIds.add(redirect2.key); await startRedirectNavigation(request, redirect2.result, true, { replace: replace2 }); return { shortCircuited: true }; } let { loaderData, errors } = processLoaderData( state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults ); if (initialHydration && state.errors) { errors = { ...state.errors, ...errors }; } let updatedFetchers = markFetchRedirectsDone(); let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId); let shouldUpdateFetchers = updatedFetchers || didAbortFetchLoads || revalidatingFetchers.length > 0; return { matches, loaderData, errors, ...shouldUpdateFetchers ? { fetchers: new Map(state.fetchers) } : {} }; } function getUpdatedActionData(pendingActionResult) { if (pendingActionResult && !isErrorResult(pendingActionResult[1])) { return { [pendingActionResult[0]]: pendingActionResult[1].data }; } else if (state.actionData) { if (Object.keys(state.actionData).length === 0) { return null; } else { return state.actionData; } } } function getUpdatedRevalidatingFetchers(revalidatingFetchers) { revalidatingFetchers.forEach((rf) => { let fetcher = state.fetchers.get(rf.key); let revalidatingFetcher = getLoadingFetcher( void 0, fetcher ? fetcher.data : void 0 ); state.fetchers.set(rf.key, revalidatingFetcher); }); return new Map(state.fetchers); } async function fetch2(key, routeId, href2, opts) { abortFetcher(key); let flushSync2 = (opts && opts.flushSync) === true; let routesToUse = inFlightDataRoutes || dataRoutes; let normalizedPath = normalizeTo( state.location, state.matches, basename, href2, routeId, opts?.relative ); let matches = matchRoutes(routesToUse, normalizedPath, basename); let fogOfWar = checkFogOfWar(matches, routesToUse, normalizedPath); if (fogOfWar.active && fogOfWar.matches) { matches = fogOfWar.matches; } if (!matches) { setFetcherError( key, routeId, getInternalRouterError(404, { pathname: normalizedPath }), { flushSync: flushSync2 } ); return; } let { path, submission, error } = normalizeNavigateOptions( true, normalizedPath, opts ); if (error) { setFetcherError(key, routeId, error, { flushSync: flushSync2 }); return; } let match = getTargetMatch(matches, path); let scopedContext = new unstable_RouterContextProvider( init.unstable_getContext ? await init.unstable_getContext() : void 0 ); let preventScrollReset = (opts && opts.preventScrollReset) === true; if (submission && isMutationMethod(submission.formMethod)) { await handleFetcherAction( key, routeId, path, match, matches, scopedContext, fogOfWar.active, flushSync2, preventScrollReset, submission ); return; } fetchLoadMatches.set(key, { routeId, path }); await handleFetcherLoader( key, routeId, path, match, matches, scopedContext, fogOfWar.active, flushSync2, preventScrollReset, submission ); } async function handleFetcherAction(key, routeId, path, match, requestMatches, scopedContext, isFogOfWar, flushSync2, preventScrollReset, submission) { interruptActiveLoads(); fetchLoadMatches.delete(key); function detectAndHandle405Error(m) { if (!m.route.action && !m.route.lazy) { let error = getInternalRouterError(405, { method: submission.formMethod, pathname: path, routeId }); setFetcherError(key, routeId, error, { flushSync: flushSync2 }); return true; } return false; } if (!isFogOfWar && detectAndHandle405Error(match)) { return; } let existingFetcher = state.fetchers.get(key); updateFetcherState(key, getSubmittingFetcher(submission, existingFetcher), { flushSync: flushSync2 }); let abortController = new AbortController(); let fetchRequest = createClientSideRequest( init.history, path, abortController.signal, submission ); if (isFogOfWar) { let discoverResult = await discoverRoutes( requestMatches, path, fetchRequest.signal, key ); if (discoverResult.type === "aborted") { return; } else if (discoverResult.type === "error") { setFetcherError(key, routeId, discoverResult.error, { flushSync: flushSync2 }); return; } else if (!discoverResult.matches) { setFetcherError( key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync: flushSync2 } ); return; } else { requestMatches = discoverResult.matches; match = getTargetMatch(requestMatches, path); if (detectAndHandle405Error(match)) { return; } } } fetchControllers.set(key, abortController); let originatingLoadId = incrementingLoadId; let actionResults = await callDataStrategy( "action", fetchRequest, [match], requestMatches, scopedContext, key ); let actionResult = actionResults[match.route.id]; if (fetchRequest.signal.aborted) { if (fetchControllers.get(key) === abortController) { fetchControllers.delete(key); } return; } if (fetchersQueuedForDeletion.has(key)) { if (isRedirectResult(actionResult) || isErrorResult(actionResult)) { updateFetcherState(key, getDoneFetcher(void 0)); return; } } else { if (isRedirectResult(actionResult)) { fetchControllers.delete(key); if (pendingNavigationLoadId > originatingLoadId) { updateFetcherState(key, getDoneFetcher(void 0)); return; } else { fetchRedirectIds.add(key); updateFetcherState(key, getLoadingFetcher(submission)); return startRedirectNavigation(fetchRequest, actionResult, false, { fetcherSubmission: submission, preventScrollReset }); } } if (isErrorResult(actionResult)) { setFetcherError(key, routeId, actionResult.error); return; } } let nextLocation = state.navigation.location || state.location; let revalidationRequest = createClientSideRequest( init.history, nextLocation, abortController.signal ); let routesToUse = inFlightDataRoutes || dataRoutes; let matches = state.navigation.state !== "idle" ? matchRoutes(routesToUse, state.navigation.location, basename) : state.matches; invariant(matches, "Didn't find any matches after fetcher action"); let loadId = ++incrementingLoadId; fetchReloadIds.set(key, loadId); let loadFetcher = getLoadingFetcher(submission, actionResult.data); state.fetchers.set(key, loadFetcher); let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad( init.history, state, matches, submission, nextLocation, false, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, [match.route.id, actionResult] ); revalidatingFetchers.filter((rf) => rf.key !== key).forEach((rf) => { let staleKey = rf.key; let existingFetcher2 = state.fetchers.get(staleKey); let revalidatingFetcher = getLoadingFetcher( void 0, existingFetcher2 ? existingFetcher2.data : void 0 ); state.fetchers.set(staleKey, revalidatingFetcher); abortFetcher(staleKey); if (rf.controller) { fetchControllers.set(staleKey, rf.controller); } }); updateState({ fetchers: new Map(state.fetchers) }); let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => abortFetcher(rf.key)); abortController.signal.addEventListener( "abort", abortPendingFetchRevalidations ); let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData( matches, matchesToLoad, revalidatingFetchers, revalidationRequest, scopedContext ); if (abortController.signal.aborted) { return; } abortController.signal.removeEventListener( "abort", abortPendingFetchRevalidations ); fetchReloadIds.delete(key); fetchControllers.delete(key); revalidatingFetchers.forEach((r) => fetchControllers.delete(r.key)); let redirect2 = findRedirect(loaderResults); if (redirect2) { return startRedirectNavigation( revalidationRequest, redirect2.result, false, { preventScrollReset } ); } redirect2 = findRedirect(fetcherResults); if (redirect2) { fetchRedirectIds.add(redirect2.key); return startRedirectNavigation( revalidationRequest, redirect2.result, false, { preventScrollReset } ); } let { loaderData, errors } = processLoaderData( state, matches, loaderResults, void 0, revalidatingFetchers, fetcherResults ); if (state.fetchers.has(key)) { let doneFetcher = getDoneFetcher(actionResult.data); state.fetchers.set(key, doneFetcher); } abortStaleFetchLoads(loadId); if (state.navigation.state === "loading" && loadId > pendingNavigationLoadId) { invariant(pendingAction, "Expected pending action"); pendingNavigationController && pendingNavigationController.abort(); completeNavigation(state.navigation.location, { matches, loaderData, errors, fetchers: new Map(state.fetchers) }); } else { updateState({ errors, loaderData: mergeLoaderData( state.loaderData, loaderData, matches, errors ), fetchers: new Map(state.fetchers) }); isRevalidationRequired = false; } } async function handleFetcherLoader(key, routeId, path, match, matches, scopedContext, isFogOfWar, flushSync2, preventScrollReset, submission) { let existingFetcher = state.fetchers.get(key); updateFetcherState( key, getLoadingFetcher( submission, existingFetcher ? existingFetcher.data : void 0 ), { flushSync: flushSync2 } ); let abortController = new AbortController(); let fetchRequest = createClientSideRequest( init.history, path, abortController.signal ); if (isFogOfWar) { let discoverResult = await discoverRoutes( matches, path, fetchRequest.signal, key ); if (discoverResult.type === "aborted") { return; } else if (discoverResult.type === "error") { setFetcherError(key, routeId, discoverResult.error, { flushSync: flushSync2 }); return; } else if (!discoverResult.matches) { setFetcherError( key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync: flushSync2 } ); return; } else { matches = discoverResult.matches; match = getTargetMatch(matches, path); } } fetchControllers.set(key, abortController); let originatingLoadId = incrementingLoadId; let results = await callDataStrategy( "loader", fetchRequest, [match], matches, scopedContext, key ); let result = results[match.route.id]; if (fetchControllers.get(key) === abortController) { fetchControllers.delete(key); } if (fetchRequest.signal.aborted) { return; } if (fetchersQueuedForDeletion.has(key)) { updateFetcherState(key, getDoneFetcher(void 0)); return; } if (isRedirectResult(result)) { if (pendingNavigationLoadId > originatingLoadId) { updateFetcherState(key, getDoneFetcher(void 0)); return; } else { fetchRedirectIds.add(key); await startRedirectNavigation(fetchRequest, result, false, { preventScrollReset }); return; } } if (isErrorResult(result)) { setFetcherError(key, routeId, result.error); return; } updateFetcherState(key, getDoneFetcher(result.data)); } async function startRedirectNavigation(request, redirect2, isNavigation, { submission, fetcherSubmission, preventScrollReset, replace: replace2 } = {}) { if (redirect2.response.headers.has("X-Remix-Revalidate")) { isRevalidationRequired = true; } let location = redirect2.response.headers.get("Location"); invariant(location, "Expected a Location header on the redirect Response"); location = normalizeRedirectLocation( location, new URL(request.url), basename ); let redirectLocation = createLocation(state.location, location, { _isRedirect: true }); if (isBrowser2) { let isDocumentReload = false; if (redirect2.response.headers.has("X-Remix-Reload-Document")) { isDocumentReload = true; } else if (ABSOLUTE_URL_REGEX.test(location)) { const url = init.history.createURL(location); isDocumentReload = // Hard reload if it's an absolute URL to a new origin url.origin !== routerWindow.location.origin || // Hard reload if it's an absolute URL that does not match our basename stripBasename(url.pathname, basename) == null; } if (isDocumentReload) { if (replace2) { routerWindow.location.replace(location); } else { routerWindow.location.assign(location); } return; } } pendingNavigationController = null; let redirectNavigationType = replace2 === true || redirect2.response.headers.has("X-Remix-Replace") ? "REPLACE" : "PUSH"; let { formMethod, formAction, formEncType } = state.navigation; if (!submission && !fetcherSubmission && formMethod && formAction && formEncType) { submission = getSubmissionFromNavigation(state.navigation); } let activeSubmission = submission || fetcherSubmission; if (redirectPreserveMethodStatusCodes.has(redirect2.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) { await startNavigation(redirectNavigationType, redirectLocation, { submission: { ...activeSubmission, formAction: location }, // Preserve these flags across redirects preventScrollReset: preventScrollReset || pendingPreventScrollReset, enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0 }); } else { let overrideNavigation = getLoadingNavigation( redirectLocation, submission ); await startNavigation(redirectNavigationType, redirectLocation, { overrideNavigation, // Send fetcher submissions through for shouldRevalidate fetcherSubmission, // Preserve these flags across redirects preventScrollReset: preventScrollReset || pendingPreventScrollReset, enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0 }); } } async function callDataStrategy(type, request, matchesToLoad, matches, scopedContext, fetcherKey) { let results; let dataResults = {}; try { results = await callDataStrategyImpl( dataStrategyImpl, type, request, matchesToLoad, matches, fetcherKey, manifest, mapRouteProperties2, scopedContext, future.unstable_middleware ); } catch (e) { matchesToLoad.forEach((m) => { dataResults[m.route.id] = { type: "error", error: e }; }); return dataResults; } for (let [routeId, result] of Object.entries(results)) { if (isRedirectDataStrategyResult(result)) { let response = result.result; dataResults[routeId] = { type: "redirect", response: normalizeRelativeRoutingRedirectResponse( response, request, routeId, matches, basename ) }; } else { dataResults[routeId] = await convertDataStrategyResultToDataResult( result ); } } return dataResults; } async function callLoadersAndMaybeResolveData(matches, matchesToLoad, fetchersToLoad, request, scopedContext) { let loaderResultsPromise = callDataStrategy( "loader", request, matchesToLoad, matches, scopedContext, null ); let fetcherResultsPromise = Promise.all( fetchersToLoad.map(async (f) => { if (f.matches && f.match && f.controller) { let results = await callDataStrategy( "loader", createClientSideRequest(init.history, f.path, f.controller.signal), [f.match], f.matches, scopedContext, f.key ); let result = results[f.match.route.id]; return { [f.key]: result }; } else { return Promise.resolve({ [f.key]: { type: "error", error: getInternalRouterError(404, { pathname: f.path }) } }); } }) ); let loaderResults = await loaderResultsPromise; let fetcherResults = (await fetcherResultsPromise).reduce( (acc, r) => Object.assign(acc, r), {} ); return { loaderResults, fetcherResults }; } function interruptActiveLoads() { isRevalidationRequired = true; fetchLoadMatches.forEach((_, key) => { if (fetchControllers.has(key)) { cancelledFetcherLoads.add(key); } abortFetcher(key); }); } function updateFetcherState(key, fetcher, opts = {}) { state.fetchers.set(key, fetcher); updateState( { fetchers: new Map(state.fetchers) }, { flushSync: (opts && opts.flushSync) === true } ); } function setFetcherError(key, routeId, error, opts = {}) { let boundaryMatch = findNearestBoundary(state.matches, routeId); deleteFetcher(key); updateState( { errors: { [boundaryMatch.route.id]: error }, fetchers: new Map(state.fetchers) }, { flushSync: (opts && opts.flushSync) === true } ); } function getFetcher(key) { activeFetchers.set(key, (activeFetchers.get(key) || 0) + 1); if (fetchersQueuedForDeletion.has(key)) { fetchersQueuedForDeletion.delete(key); } return state.fetchers.get(key) || IDLE_FETCHER; } function deleteFetcher(key) { let fetcher = state.fetchers.get(key); if (fetchControllers.has(key) && !(fetcher && fetcher.state === "loading" && fetchReloadIds.has(key))) { abortFetcher(key); } fetchLoadMatches.delete(key); fetchReloadIds.delete(key); fetchRedirectIds.delete(key); fetchersQueuedForDeletion.delete(key); cancelledFetcherLoads.delete(key); state.fetchers.delete(key); } function queueFetcherForDeletion(key) { let count = (activeFetchers.get(key) || 0) - 1; if (count <= 0) { activeFetchers.delete(key); fetchersQueuedForDeletion.add(key); } else { activeFetchers.set(key, count); } updateState({ fetchers: new Map(state.fetchers) }); } function abortFetcher(key) { let controller = fetchControllers.get(key); if (controller) { controller.abort(); fetchControllers.delete(key); } } function markFetchersDone(keys) { for (let key of keys) { let fetcher = getFetcher(key); let doneFetcher = getDoneFetcher(fetcher.data); state.fetchers.set(key, doneFetcher); } } function markFetchRedirectsDone() { let doneKeys = []; let updatedFetchers = false; for (let key of fetchRedirectIds) { let fetcher = state.fetchers.get(key); invariant(fetcher, `Expected fetcher: ${key}`); if (fetcher.state === "loading") { fetchRedirectIds.delete(key); doneKeys.push(key); updatedFetchers = true; } } markFetchersDone(doneKeys); return updatedFetchers; } function abortStaleFetchLoads(landedId) { let yeetedKeys = []; for (let [key, id] of fetchReloadIds) { if (id < landedId) { let fetcher = state.fetchers.get(key); invariant(fetcher, `Expected fetcher: ${key}`); if (fetcher.state === "loading") { abortFetcher(key); fetchReloadIds.delete(key); yeetedKeys.push(key); } } } markFetchersDone(yeetedKeys); return yeetedKeys.length > 0; } function getBlocker(key, fn) { let blocker = state.blockers.get(key) || IDLE_BLOCKER; if (blockerFunctions.get(key) !== fn) { blockerFunctions.set(key, fn); } return blocker; } function deleteBlocker(key) { state.blockers.delete(key); blockerFunctions.delete(key); } function updateBlocker(key, newBlocker) { let blocker = state.blockers.get(key) || IDLE_BLOCKER; 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}` ); let blockers = new Map(state.blockers); blockers.set(key, newBlocker); updateState({ blockers }); } function shouldBlockNavigation({ currentLocation, nextLocation, historyAction }) { if (blockerFunctions.size === 0) { return; } if (blockerFunctions.size > 1) { warning(false, "A router only supports one blocker at a time"); } let entries = Array.from(blockerFunctions.entries()); let [blockerKey, blockerFunction] = entries[entries.length - 1]; let blocker = state.blockers.get(blockerKey); if (blocker && blocker.state === "proceeding") { return; } if (blockerFunction({ currentLocation, nextLocation, historyAction })) { return blockerKey; } } function handleNavigational404(pathname) { let error = getInternalRouterError(404, { pathname }); let routesToUse = inFlightDataRoutes || dataRoutes; let { matches, route } = getShortCircuitMatches(routesToUse); return { notFoundMatches: matches, route, error }; } function enableScrollRestoration(positions, getPosition, getKey) { savedScrollPositions2 = positions; getScrollPosition = getPosition; getScrollRestorationKey2 = getKey || null; if (!initialScrollRestored && state.navigation === IDLE_NAVIGATION) { initialScrollRestored = true; let y = getSavedScrollPosition(state.location, state.matches); if (y != null) { updateState({ restoreScrollPosition: y }); } } return () => { savedScrollPositions2 = null; getScrollPosition = null; getScrollRestorationKey2 = null; }; } function getScrollKey(location, matches) { if (getScrollRestorationKey2) { let key = getScrollRestorationKey2( location, matches.map((m) => convertRouteMatchToUiMatch(m, state.loaderData)) ); return key || location.key; } return location.key; } function saveScrollPosition(location, matches) { if (savedScrollPositions2 && getScrollPosition) { let key = getScrollKey(location, matches); savedScrollPositions2[key] = getScrollPosition(); } } function getSavedScrollPosition(location, matches) { if (savedScrollPositions2) { let key = getScrollKey(location, matches); let y = savedScrollPositions2[key]; if (typeof y === "number") { return y; } } return null; } function checkFogOfWar(matches, routesToUse, pathname) { if (init.patchRoutesOnNavigation) { if (!matches) { let fogMatches = matchRoutesImpl( routesToUse, pathname, basename, true ); return { active: true, matches: fogMatches || [] }; } else { if (Object.keys(matches[0].params).length > 0) { let partialMatches = matchRoutesImpl( routesToUse, pathname, basename, true ); return { active: true, matches: partialMatches }; } } } return { active: false, matches: null }; } async function discoverRoutes(matches, pathname, signal, fetcherKey) { if (!init.patchRoutesOnNavigation) { return { type: "success", matches }; } let partialMatches = matches; while (true) { let isNonHMR = inFlightDataRoutes == null; let routesToUse = inFlightDataRoutes || dataRoutes; let localManifest = manifest; try { await init.patchRoutesOnNavigation({ signal, path: pathname, matches: partialMatches, fetcherKey, patch: (routeId, children) => { if (signal.aborted) return; patchRoutesImpl( routeId, children, routesToUse, localManifest, mapRouteProperties2 ); } }); } catch (e) { return { type: "error", error: e, partialMatches }; } finally { if (isNonHMR && !signal.aborted) { dataRoutes = [...dataRoutes]; } } if (signal.aborted) { return { type: "aborted" }; } let newMatches = matchRoutes(routesToUse, pathname, basename); if (newMatches) { return { type: "success", matches: newMatches }; } let newPartialMatches = matchRoutesImpl( routesToUse, pathname, basename, true ); if (!newPartialMatches || partialMatches.length === newPartialMatches.length && partialMatches.every( (m, i) => m.route.id === newPartialMatches[i].route.id )) { return { type: "success", matches: null }; } partialMatches = newPartialMatches; } } function _internalSetRoutes(newRoutes) { manifest = {}; inFlightDataRoutes = convertRoutesToDataRoutes( newRoutes, mapRouteProperties2, void 0, manifest ); } function patchRoutes(routeId, children) { let isNonHMR = inFlightDataRoutes == null; let routesToUse = inFlightDataRoutes || dataRoutes; patchRoutesImpl( routeId, children, routesToUse, manifest, mapRouteProperties2 ); if (isNonHMR) { dataRoutes = [...dataRoutes]; updateState({}); } } router2 = { get basename() { return basename; }, get future() { return future; }, get state() { return state; }, get routes() { return dataRoutes; }, get window() { return routerWindow; }, initialize, subscribe, enableScrollRestoration, navigate, fetch: fetch2, revalidate, // Passthrough to history-aware createHref used by useHref so we get proper // hash-aware URLs in DOM paths createHref: (to) => init.history.createHref(to), encodeLocation: (to) => init.history.encodeLocation(to), getFetcher, deleteFetcher: queueFetcherForDeletion, dispose, getBlocker, deleteBlocker, patchRoutes, _internalFetchControllers: fetchControllers, // TODO: Remove setRoutes, it's temporary to avoid dealing with // updating the tree while validating the update algorithm. _internalSetRoutes }; return router2; } function createStaticHandler(routes, opts) { invariant( routes.length > 0, "You must provide a non-empty routes array to createStaticHandler" ); let manifest = {}; let basename = (opts ? opts.basename : null) || "/"; let mapRouteProperties2 = opts?.mapRouteProperties || defaultMapRouteProperties; let dataRoutes = convertRoutesToDataRoutes( routes, mapRouteProperties2, void 0, manifest ); async function query(request, { requestContext, filterMatchesToLoad, skipLoaderErrorBubbling, skipRevalidation, dataStrategy, unstable_respond: respond } = {}) { let url = new URL(request.url); let method = request.method; let location = createLocation("", createPath(url), null, "default"); let matches = matchRoutes(dataRoutes, location, basename); requestContext = requestContext != null ? requestContext : new unstable_RouterContextProvider(); if (!isValidMethod(method) && method !== "HEAD") { let error = getInternalRouterError(405, { method }); let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes); let staticContext = { basename, location, matches: methodNotAllowedMatches, loaderData: {}, actionData: null, errors: { [route.id]: error }, statusCode: error.status, loaderHeaders: {}, actionHeaders: {} }; return respond ? respond(staticContext) : staticContext; } else if (!matches) { let error = getInternalRouterError(404, { pathname: location.pathname }); let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes); let staticContext = { basename, location, matches: notFoundMatches, loaderData: {}, actionData: null, errors: { [route.id]: error }, statusCode: error.status, loaderHeaders: {}, actionHeaders: {} }; return respond ? respond(staticContext) : staticContext; } if (respond && matches.some((m) => m.route.unstable_middleware)) { invariant( requestContext instanceof unstable_RouterContextProvider, "When using middleware in `staticHandler.query()`, any provided `requestContext` must bean instance of `unstable_RouterContextProvider`" ); try { let renderedStaticContext; let response = await runMiddlewarePipeline( { request, matches, params: matches[0].params, // If we're calling middleware then it must be enabled so we can cast // this to the proper type knowing it's not an `AppLoadContext` context: requestContext }, true, async () => { let result2 = await queryImpl( request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, filterMatchesToLoad || null, skipRevalidation === true ); if (isResponse(result2)) { return result2; } renderedStaticContext = { location, basename, ...result2 }; let res = await respond(renderedStaticContext); return res; }, async (e) => { if (isResponse(e.error)) { return e.error; } if (renderedStaticContext) { if (e.routeId in renderedStaticContext.loaderData) { renderedStaticContext.loaderData[e.routeId] = void 0; } return respond( getStaticContextFromError( dataRoutes, renderedStaticContext, e.error, findNearestBoundary(matches, e.routeId).route.id ) ); } else { let loaderIdx = matches.findIndex((m) => m.route.loader); let boundary = loaderIdx >= 0 ? findNearestBoundary(matches, matches[loaderIdx].route.id) : findNearestBoundary(matches); return respond({ matches, location, basename, loaderData: {}, actionData: null, errors: { [boundary.route.id]: e.error }, statusCode: isRouteErrorResponse(e.error) ? e.error.status : 500, actionHeaders: {}, loaderHeaders: {} }); } } ); invariant(isResponse(response), "Expected a response in query()"); return response; } catch (e) { if (isResponse(e)) { return e; } throw e; } } let result = await queryImpl( request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, filterMatchesToLoad || null, skipRevalidation === true ); if (isResponse(result)) { return result; } return { location, basename, ...result }; } async function queryRoute(request, { routeId, requestContext, dataStrategy, unstable_respond: respond } = {}) { let url = new URL(request.url); let method = request.method; let location = createLocation("", createPath(url), null, "default"); let matches = matchRoutes(dataRoutes, location, basename); requestContext = requestContext != null ? requestContext : new unstable_RouterContextProvider(); if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") { throw getInternalRouterError(405, { method }); } else if (!matches) { throw getInternalRouterError(404, { pathname: location.pathname }); } let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location); if (routeId && !match) { throw getInternalRouterError(403, { pathname: location.pathname, routeId }); } else if (!match) { throw getInternalRouterError(404, { pathname: location.pathname }); } if (respond && matches.some((m) => m.route.unstable_middleware)) { invariant( requestContext instanceof unstable_RouterContextProvider, "When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must bean instance of `unstable_RouterContextProvider`" ); let response = await runMiddlewarePipeline( { request, matches, params: matches[0].params, // If we're calling middleware then it must be enabled so we can cast // this to the proper type knowing it's not an `AppLoadContext` context: requestContext }, true, async () => { let result2 = await queryImpl( request, location, matches, requestContext, dataStrategy || null, false, match, null, false ); if (isResponse(result2)) { return respond(result2); } let error2 = result2.errors ? Object.values(result2.errors)[0] : void 0; if (error2 !== void 0) { throw error2; } let value = result2.actionData ? Object.values(result2.actionData)[0] : Object.values(result2.loaderData)[0]; return typeof value === "string" ? new Response(value) : Response.json(value); }, (e) => { if (isResponse(e.error)) { return respond(e.error); } return new Response(String(e.error), { status: 500, statusText: "Unexpected Server Error" }); } ); return response; } let result = await queryImpl( request, location, matches, requestContext, dataStrategy || null, false, match, null, false ); if (isResponse(result)) { return result; } let error = result.errors ? Object.values(result.errors)[0] : void 0; if (error !== void 0) { throw error; } if (result.actionData) { return Object.values(result.actionData)[0]; } if (result.loaderData) { return Object.values(result.loaderData)[0]; } return void 0; } async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) { invariant( request.signal, "query()/queryRoute() requests must contain an AbortController signal" ); try { if (isMutationMethod(request.method)) { let result2 = await submit( request, matches, routeMatch || getTargetMatch(matches, location), requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch != null, filterMatchesToLoad, skipRevalidation ); return result2; } let result = await loadRouteData( request, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad ); return isResponse(result) ? result : { ...result, actionData: null, actionHeaders: {} }; } catch (e) { if (isDataStrategyResult(e) && isResponse(e.result)) { if (e.type === "error") { throw e.result; } return e.result; } if (isRedirectResponse(e)) { return e; } throw e; } } async function submit(request, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) { let result; if (!actionMatch.route.action && !actionMatch.route.lazy) { let error = getInternalRouterError(405, { method: request.method, pathname: new URL(request.url).pathname, routeId: actionMatch.route.id }); if (isRouteRequest) { throw error; } result = { type: "error", error }; } else { let results = await callDataStrategy( "action", request, [actionMatch], matches, isRouteRequest, requestContext, dataStrategy ); result = results[actionMatch.route.id]; if (request.signal.aborted) { throwStaticHandlerAbortedError(request, isRouteRequest); } } if (isRedirectResult(result)) { throw new Response(null, { status: result.response.status, headers: { Location: result.response.headers.get("Location") } }); } if (isRouteRequest) { if (isErrorResult(result)) { throw result.error; } return { matches: [actionMatch], loaderData: {}, actionData: { [actionMatch.route.id]: result.data }, errors: null, // Note: statusCode + headers are unused here since queryRoute will // return the raw Response or value statusCode: 200, loaderHeaders: {}, actionHeaders: {} }; } if (skipRevalidation) { if (isErrorResult(result)) { let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id); return { statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500, actionData: null, actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} }, matches, loaderData: {}, errors: { [boundaryMatch.route.id]: result.error }, loaderHeaders: {} }; } else { return { actionData: { [actionMatch.route.id]: result.data }, actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}, matches, loaderData: {}, errors: null, statusCode: result.statusCode || 200, loaderHeaders: {} }; } } let loaderRequest = new Request(request.url, { headers: request.headers, redirect: request.redirect, signal: request.signal }); if (isErrorResult(result)) { let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id); let handlerContext2 = await loadRouteData( loaderRequest, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad, [boundaryMatch.route.id, result] ); return { ...handlerContext2, statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500, actionData: null, actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} } }; } let handlerContext = await loadRouteData( loaderRequest, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad ); return { ...handlerContext, actionData: { [actionMatch.route.id]: result.data }, // action status codes take precedence over loader status codes ...result.statusCode ? { statusCode: result.statusCode } : {}, actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {} }; } async function loadRouteData(request, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) { let isRouteRequest = routeMatch != null; if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) { throw getInternalRouterError(400, { method: request.method, pathname: new URL(request.url).pathname, routeId: routeMatch?.route.id }); } let requestMatches = routeMatch ? [routeMatch] : pendingActionResult && isErrorResult(pendingActionResult[1]) ? getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]) : matches; let matchesToLoad = requestMatches.fil