@remix-run/router 0.0.0-experimental-2272fa73 → 0.0.0-experimental-cbcd94b7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export type { ActionFunction, ActionFunctionArgs, AgnosticDataIndexRouteObject, AgnosticDataNonIndexRouteObject, AgnosticDataRouteMatch, AgnosticDataRouteObject, AgnosticIndexRouteObject, AgnosticNonIndexRouteObject, AgnosticRouteMatch, AgnosticRouteObject, DataStrategyFunction, DataStrategyFunctionArgs, DataStrategyMatch, ErrorResponse, FormEncType, FormMethod, HTMLFormMethod, JsonFunction, LazyRouteFunction, LoaderFunction, LoaderFunctionArgs, ParamParseKey, Params, PathMatch, PathParam, PathPattern, RedirectFunction, ShouldRevalidateFunction, ShouldRevalidateFunctionArgs, TrackedPromise, UIMatch, V7_FormMethod, } from "./utils";
2
- export { AbortedDeferredError, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, redirectDocument, resolvePath, resolveTo, stripBasename, } from "./utils";
2
+ export { AbortedDeferredError, DecodedResponse, defer, generatePath, getToPathname, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, redirect, redirectDocument, resolvePath, resolveTo, stripBasename, } from "./utils";
3
3
  export type { BrowserHistory, BrowserHistoryOptions, HashHistory, HashHistoryOptions, History, InitialEntry, Location, MemoryHistory, MemoryHistoryOptions, Path, To, } from "./history";
4
4
  export { Action, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, parsePath, } from "./history";
5
5
  export * from "./router";
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v0.0.0-experimental-2272fa73
2
+ * @remix-run/router v0.0.0-experimental-cbcd94b7
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1519,6 +1519,22 @@ class ErrorResponseImpl {
1519
1519
  }
1520
1520
  }
1521
1521
 
1522
+ /**
1523
+ * Utility class we use to hold unwrapped Responses while preserving status
1524
+ * codes and headers. This is most useful for dataStrategy implementations
1525
+ * where implementors want to use a custom decoding mechanism and return
1526
+ * pre-decoded data while also preserving response meta information
1527
+ */
1528
+ class DecodedResponse {
1529
+ constructor(status, statusText, headers, data) {
1530
+ this._isDecodedResponse = true;
1531
+ this.status = status;
1532
+ this.statusText = statusText;
1533
+ this.headers = headers;
1534
+ this.data = data;
1535
+ }
1536
+ }
1537
+
1522
1538
  /**
1523
1539
  * Check if the given error is an ErrorResponse generated from a 4xx/5xx
1524
1540
  * Response thrown from an action/loader
@@ -1527,6 +1543,13 @@ function isRouteErrorResponse(error) {
1527
1543
  return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
1528
1544
  }
1529
1545
 
1546
+ /**
1547
+ * Check if the given error is an DecodedResponse
1548
+ */
1549
+ function isDecodedResponse(value) {
1550
+ return value != null && value._isDecodedResponse === true && typeof value.status === "number" && typeof value.statusText === "string" && value.headers != null && "data" in value;
1551
+ }
1552
+
1530
1553
  ////////////////////////////////////////////////////////////////////////////////
1531
1554
  //#region Types and Constants
1532
1555
  ////////////////////////////////////////////////////////////////////////////////
@@ -1675,7 +1698,8 @@ function createRouter(init) {
1675
1698
  v7_normalizeFormMethod: false,
1676
1699
  v7_partialHydration: false,
1677
1700
  v7_prependBasename: false,
1678
- v7_relativeSplatPath: false
1701
+ v7_relativeSplatPath: false,
1702
+ v7_skipActionErrorRevalidation: true
1679
1703
  }, init.future);
1680
1704
  // Cleanup function for history
1681
1705
  let unlistenHistory = null;
@@ -2257,34 +2281,31 @@ function createRouter(init) {
2257
2281
  // Create a controller/Request for this navigation
2258
2282
  pendingNavigationController = new AbortController();
2259
2283
  let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
2260
- let pendingActionData;
2261
- let pendingError;
2284
+ let pendingActionResult;
2262
2285
  if (opts && opts.pendingError) {
2263
2286
  // If we have a pendingError, it means the user attempted a GET submission
2264
2287
  // with binary FormData so assign here and skip to handleLoaders. That
2265
2288
  // way we handle calling loaders above the boundary etc. It's not really
2266
2289
  // different from an actionError in that sense.
2267
- pendingError = {
2268
- [findNearestBoundary(matches).route.id]: opts.pendingError
2269
- };
2290
+ pendingActionResult = [findNearestBoundary(matches).route.id, {
2291
+ type: ResultType.error,
2292
+ error: opts.pendingError
2293
+ }];
2270
2294
  } else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
2271
2295
  // Call action if we received an action submission
2272
- let actionOutput = await handleAction(request, location, opts.submission, matches, {
2296
+ let actionResult = await handleAction(request, location, opts.submission, matches, {
2273
2297
  replace: opts.replace,
2274
2298
  flushSync
2275
2299
  });
2276
- if (actionOutput.shortCircuited) {
2300
+ if (actionResult.shortCircuited) {
2277
2301
  return;
2278
2302
  }
2279
- pendingActionData = actionOutput.pendingActionData;
2280
- pendingError = actionOutput.pendingActionError;
2303
+ pendingActionResult = actionResult.pendingActionResult;
2281
2304
  loadingNavigation = getLoadingNavigation(location, opts.submission);
2282
2305
  flushSync = false;
2283
2306
 
2284
2307
  // Create a GET request for the loaders
2285
- request = new Request(request.url, {
2286
- signal: request.signal
2287
- });
2308
+ request = createClientSideRequest(init.history, request.url, request.signal);
2288
2309
  }
2289
2310
 
2290
2311
  // Call loaders
@@ -2292,7 +2313,7 @@ function createRouter(init) {
2292
2313
  shortCircuited,
2293
2314
  loaderData,
2294
2315
  errors
2295
- } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionData, pendingError);
2316
+ } = await handleLoaders(request, location, matches, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionResult);
2296
2317
  if (shortCircuited) {
2297
2318
  return;
2298
2319
  }
@@ -2303,9 +2324,7 @@ function createRouter(init) {
2303
2324
  pendingNavigationController = null;
2304
2325
  completeNavigation(location, _extends({
2305
2326
  matches
2306
- }, pendingActionData ? {
2307
- actionData: pendingActionData
2308
- } : {}, {
2327
+ }, getActionDataForCommit(pendingActionResult), {
2309
2328
  loaderData,
2310
2329
  errors
2311
2330
  }));
@@ -2366,6 +2385,11 @@ function createRouter(init) {
2366
2385
  shortCircuited: true
2367
2386
  };
2368
2387
  }
2388
+ if (isDeferredResult(result)) {
2389
+ throw getInternalRouterError(400, {
2390
+ type: "defer-action"
2391
+ });
2392
+ }
2369
2393
  if (isErrorResult(result)) {
2370
2394
  // Store off the pending error - we use it to determine which loaders
2371
2395
  // to call and will commit it when we complete the navigation
@@ -2379,28 +2403,17 @@ function createRouter(init) {
2379
2403
  pendingAction = Action.Push;
2380
2404
  }
2381
2405
  return {
2382
- // Send back an empty object we can use to clear out any prior actionData
2383
- pendingActionData: {},
2384
- pendingActionError: {
2385
- [boundaryMatch.route.id]: result.error
2386
- }
2406
+ pendingActionResult: [boundaryMatch.route.id, result]
2387
2407
  };
2388
2408
  }
2389
- if (isDeferredResult(result)) {
2390
- throw getInternalRouterError(400, {
2391
- type: "defer-action"
2392
- });
2393
- }
2394
2409
  return {
2395
- pendingActionData: {
2396
- [actionMatch.route.id]: result.data
2397
- }
2410
+ pendingActionResult: [actionMatch.route.id, result]
2398
2411
  };
2399
2412
  }
2400
2413
 
2401
2414
  // Call all applicable loaders for the given matches, handling redirects,
2402
2415
  // errors, etc.
2403
- async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionData, pendingError) {
2416
+ async function handleLoaders(request, location, matches, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult) {
2404
2417
  // Figure out the right navigation we want to use for data loading
2405
2418
  let loadingNavigation = overrideNavigation || getLoadingNavigation(location, submission);
2406
2419
 
@@ -2408,7 +2421,7 @@ function createRouter(init) {
2408
2421
  // we have it on the loading navigation so use that if available
2409
2422
  let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
2410
2423
  let routesToUse = inFlightDataRoutes || dataRoutes;
2411
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, future.v7_partialHydration && initialHydration === true, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError);
2424
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, activeSubmission, location, future.v7_partialHydration && initialHydration === true, future.v7_skipActionErrorRevalidation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult);
2412
2425
 
2413
2426
  // Cancel pending deferreds for no-longer-matched routes or routes we're
2414
2427
  // about to reload. Note that if this is an action reload we would have
@@ -2423,10 +2436,10 @@ function createRouter(init) {
2423
2436
  matches,
2424
2437
  loaderData: {},
2425
2438
  // Commit pending error if we're short circuiting
2426
- errors: pendingError || null
2427
- }, pendingActionData ? {
2428
- actionData: pendingActionData
2429
- } : {}, updatedFetchers ? {
2439
+ errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? {
2440
+ [pendingActionResult[0]]: pendingActionResult[1].error
2441
+ } : null
2442
+ }, getActionDataForCommit(pendingActionResult), updatedFetchers ? {
2430
2443
  fetchers: new Map(state.fetchers)
2431
2444
  } : {}), {
2432
2445
  flushSync
@@ -2448,12 +2461,24 @@ function createRouter(init) {
2448
2461
  let revalidatingFetcher = getLoadingFetcher(undefined, fetcher ? fetcher.data : undefined);
2449
2462
  state.fetchers.set(rf.key, revalidatingFetcher);
2450
2463
  });
2451
- let actionData = pendingActionData || state.actionData;
2464
+ let actionData;
2465
+ if (pendingActionResult && !isErrorResult(pendingActionResult[1])) {
2466
+ // This is cast to `any` currently because `RouteData`uses any and it
2467
+ // would be a breaking change to use any.
2468
+ // TODO: v7 - change `RouteData` to use `unknown` instead of `any`
2469
+ actionData = {
2470
+ [pendingActionResult[0]]: pendingActionResult[1].data
2471
+ };
2472
+ } else if (state.actionData) {
2473
+ if (Object.keys(state.actionData).length === 0) {
2474
+ actionData = null;
2475
+ } else {
2476
+ actionData = state.actionData;
2477
+ }
2478
+ }
2452
2479
  updateState(_extends({
2453
2480
  navigation: loadingNavigation
2454
- }, actionData ? Object.keys(actionData).length === 0 ? {
2455
- actionData: null
2456
- } : {
2481
+ }, actionData !== undefined ? {
2457
2482
  actionData
2458
2483
  } : {}, revalidatingFetchers.length > 0 ? {
2459
2484
  fetchers: new Map(state.fetchers)
@@ -2518,7 +2543,7 @@ function createRouter(init) {
2518
2543
  let {
2519
2544
  loaderData,
2520
2545
  errors
2521
- } = processLoaderData(state, matches, matchesToLoad, loaderResults, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds);
2546
+ } = processLoaderData(state, matches, matchesToLoad, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds);
2522
2547
 
2523
2548
  // Wire up subscribers to update loaderData as promises settle
2524
2549
  activeDeferreds.forEach((deferredData, routeId) => {
@@ -2677,10 +2702,7 @@ function createRouter(init) {
2677
2702
  fetchReloadIds.set(key, loadId);
2678
2703
  let loadFetcher = getLoadingFetcher(submission, actionResult.data);
2679
2704
  state.fetchers.set(key, loadFetcher);
2680
- let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, false, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, {
2681
- [match.route.id]: actionResult.data
2682
- }, undefined // No need to send through errors since we short circuit above
2683
- );
2705
+ let [matchesToLoad, revalidatingFetchers] = getMatchesToLoad(init.history, state, matches, submission, nextLocation, false, future.v7_skipActionErrorRevalidation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, [match.route.id, actionResult]);
2684
2706
 
2685
2707
  // Put all revalidating fetchers into the loading state, except for the
2686
2708
  // current fetcher which we want to keep in it's current loading state which
@@ -3597,16 +3619,14 @@ function createStaticHandler(routes, opts) {
3597
3619
  // Store off the pending error - we use it to determine which loaders
3598
3620
  // to call and will commit it when we complete the navigation
3599
3621
  let boundaryMatch = findNearestBoundary(matches, actionMatch.route.id);
3600
- let context = await loadRouteData(loaderRequest, matches, requestContext, loadRouteIds, null, {
3601
- [boundaryMatch.route.id]: result.error
3602
- });
3622
+ let context = await loadRouteData(loaderRequest, matches, requestContext, loadRouteIds, null, [boundaryMatch.route.id, result]);
3603
3623
 
3604
3624
  // action status codes take precedence over loader status codes
3605
3625
  return _extends({}, context, {
3606
3626
  statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500,
3607
3627
  actionData: null,
3608
- actionHeaders: _extends({}, result.response ? {
3609
- [actionMatch.route.id]: result.response.headers
3628
+ actionHeaders: _extends({}, result.headers ? {
3629
+ [actionMatch.route.id]: result.headers
3610
3630
  } : {})
3611
3631
  });
3612
3632
  }
@@ -3615,16 +3635,15 @@ function createStaticHandler(routes, opts) {
3615
3635
  actionData: {
3616
3636
  [actionMatch.route.id]: result.data
3617
3637
  }
3618
- }, result.response ? {
3619
- statusCode: result.response.status,
3620
- actionHeaders: {
3621
- [actionMatch.route.id]: result.response.headers
3622
- }
3623
- } : {
3624
- actionHeaders: {}
3638
+ }, result.statusCode ? {
3639
+ statusCode: result.statusCode
3640
+ } : {}, {
3641
+ actionHeaders: result.headers ? {
3642
+ [actionMatch.route.id]: result.headers
3643
+ } : {}
3625
3644
  });
3626
3645
  }
3627
- async function loadRouteData(request, matches, requestContext, loadRouteIds, routeMatch, pendingActionError) {
3646
+ async function loadRouteData(request, matches, requestContext, loadRouteIds, routeMatch, pendingActionResult) {
3628
3647
  let isRouteRequest = routeMatch != null;
3629
3648
 
3630
3649
  // Short circuit if we have no loaders to run (queryRoute())
@@ -3635,7 +3654,7 @@ function createStaticHandler(routes, opts) {
3635
3654
  routeId: routeMatch == null ? void 0 : routeMatch.route.id
3636
3655
  });
3637
3656
  }
3638
- let requestMatches = routeMatch ? [routeMatch] : getLoaderMatchesUntilBoundary(matches, Object.keys(pendingActionError || {})[0]);
3657
+ let requestMatches = routeMatch ? [routeMatch] : pendingActionResult && isErrorResult(pendingActionResult[1]) ? getLoaderMatchesUntilBoundary(matches, pendingActionResult[0]) : matches;
3639
3658
  let matchesToLoad = requestMatches.filter(m => m.route.loader || m.route.lazy);
3640
3659
  if (loadRouteIds) {
3641
3660
  matchesToLoad = matchesToLoad.filter(m => loadRouteIds.includes(m.route.id));
@@ -3649,7 +3668,9 @@ function createStaticHandler(routes, opts) {
3649
3668
  loaderData: matches.reduce((acc, m) => Object.assign(acc, {
3650
3669
  [m.route.id]: null
3651
3670
  }), {}),
3652
- errors: pendingActionError || null,
3671
+ errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? {
3672
+ [pendingActionResult[0]]: pendingActionResult[1].error
3673
+ } : null,
3653
3674
  statusCode: 200,
3654
3675
  loaderHeaders: {},
3655
3676
  activeDeferreds: null
@@ -3662,7 +3683,7 @@ function createStaticHandler(routes, opts) {
3662
3683
 
3663
3684
  // Process and commit output from loaders
3664
3685
  let activeDeferreds = new Map();
3665
- let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionError, activeDeferreds);
3686
+ let context = processRouteLoaderData(matches, matchesToLoad, results, pendingActionResult, activeDeferreds);
3666
3687
 
3667
3688
  // Add a null for any non-loader matches for proper revalidation on the client
3668
3689
  let executedLoaders = new Set(matchesToLoad.map(match => match.route.id));
@@ -3915,14 +3936,19 @@ function getLoaderMatchesUntilBoundary(matches, boundaryId) {
3915
3936
  }
3916
3937
  return boundaryMatches;
3917
3938
  }
3918
- function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionData, pendingError) {
3919
- let actionResult = pendingError ? Object.values(pendingError)[0] : pendingActionData ? Object.values(pendingActionData)[0] : undefined;
3939
+ function getMatchesToLoad(history, state, matches, submission, location, isInitialLoad, skipActionErrorRevalidation, isRevalidationRequired, cancelledDeferredRoutes, cancelledFetcherLoads, deletedFetchers, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, pendingActionResult) {
3940
+ let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : undefined;
3920
3941
  let currentUrl = history.createURL(state.location);
3921
3942
  let nextUrl = history.createURL(location);
3922
3943
 
3923
3944
  // Pick navigation matches that are net-new or qualify for revalidation
3924
- let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3925
- let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3945
+ let boundaryId = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[0] : undefined;
3946
+ let boundaryMatches = boundaryId ? getLoaderMatchesUntilBoundary(matches, boundaryId) : matches;
3947
+
3948
+ // Don't revalidate loaders by default after action 4xx/5xx responses
3949
+ // when the flag is enabled. They can still opt-into revalidation via
3950
+ // `shouldRevalidate` via `actionResult`
3951
+ let shouldSkipRevalidation = skipActionErrorRevalidation && pendingActionResult && typeof pendingActionResult[1].statusCode === "number" && pendingActionResult[1].statusCode >= 400;
3926
3952
  let navigationMatches = boundaryMatches.filter((match, index) => {
3927
3953
  let {
3928
3954
  route
@@ -3961,11 +3987,9 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3961
3987
  nextParams: nextRouteMatch.params
3962
3988
  }, submission, {
3963
3989
  actionResult,
3964
- defaultShouldRevalidate:
3990
+ defaultShouldRevalidate: shouldSkipRevalidation ? false :
3965
3991
  // Forced revalidation due to submission, useRevalidator, or X-Remix-Revalidate
3966
- isRevalidationRequired ||
3967
- // Clicked the same link, resubmitted a GET form
3968
- currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search ||
3992
+ isRevalidationRequired || currentUrl.pathname + currentUrl.search === nextUrl.pathname + nextUrl.search ||
3969
3993
  // Search params affect all loaders
3970
3994
  currentUrl.search !== nextUrl.search || isNewRouteInstance(currentRouteMatch, nextRouteMatch)
3971
3995
  }));
@@ -4027,7 +4051,7 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
4027
4051
  nextParams: matches[matches.length - 1].params
4028
4052
  }, submission, {
4029
4053
  actionResult,
4030
- defaultShouldRevalidate: isRevalidationRequired
4054
+ defaultShouldRevalidate: shouldSkipRevalidation ? false : isRevalidationRequired
4031
4055
  }));
4032
4056
  }
4033
4057
  if (shouldRevalidate) {
@@ -4302,19 +4326,38 @@ async function convertHandlerResultToDataResult(handlerResult) {
4302
4326
  return {
4303
4327
  type,
4304
4328
  error: new ErrorResponseImpl(result.status, result.statusText, data),
4305
- response: result
4329
+ statusCode: result.status,
4330
+ headers: result.headers
4306
4331
  };
4307
4332
  }
4308
4333
  return {
4309
4334
  type: ResultType.data,
4310
4335
  data,
4311
- response: result
4336
+ statusCode: result.status,
4337
+ headers: result.headers
4338
+ };
4339
+ }
4340
+ if (isDecodedResponse(result)) {
4341
+ if (type === ResultType.error) {
4342
+ return {
4343
+ type,
4344
+ error: new ErrorResponseImpl(result.status, result.statusText, result.data),
4345
+ statusCode: result.status,
4346
+ headers: result.headers
4347
+ };
4348
+ }
4349
+ return {
4350
+ type: ResultType.data,
4351
+ data: result.data,
4352
+ statusCode: result.status,
4353
+ headers: result.headers
4312
4354
  };
4313
4355
  }
4314
4356
  if (type === ResultType.error) {
4315
4357
  return {
4316
4358
  type,
4317
- error: result
4359
+ error: result,
4360
+ statusCode: isRouteErrorResponse(result) ? result.status : undefined
4318
4361
  };
4319
4362
  }
4320
4363
  if (isDeferredData(result)) {
@@ -4394,13 +4437,14 @@ function convertSearchParamsToFormData(searchParams) {
4394
4437
  }
4395
4438
  return formData;
4396
4439
  }
4397
- function processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds) {
4440
+ function processRouteLoaderData(matches, matchesToLoad, results, pendingActionResult, activeDeferreds) {
4398
4441
  // Fill in loaderData/errors from our loaders
4399
4442
  let loaderData = {};
4400
4443
  let errors = null;
4401
4444
  let statusCode;
4402
4445
  let foundError = false;
4403
4446
  let loaderHeaders = {};
4447
+ let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : undefined;
4404
4448
 
4405
4449
  // Process loader results into state.loaderData/state.errors
4406
4450
  results.forEach((result, index) => {
@@ -4414,8 +4458,8 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
4414
4458
  // If we have a pending action error, we report it at the highest-route
4415
4459
  // that throws a loader error, and then clear it out to indicate that
4416
4460
  // it was consumed
4417
- if (pendingError) {
4418
- error = Object.values(pendingError)[0];
4461
+ if (pendingError !== undefined) {
4462
+ error = pendingError;
4419
4463
  pendingError = undefined;
4420
4464
  }
4421
4465
  errors = errors || {};
@@ -4434,8 +4478,8 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
4434
4478
  foundError = true;
4435
4479
  statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
4436
4480
  }
4437
- if (result.response) {
4438
- loaderHeaders[id] = result.response.headers;
4481
+ if (result.headers) {
4482
+ loaderHeaders[id] = result.headers;
4439
4483
  }
4440
4484
  } else {
4441
4485
  if (isDeferredResult(result)) {
@@ -4453,11 +4497,11 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
4453
4497
  loaderData[id] = result.data;
4454
4498
  // Error status codes always override success status codes, but if all
4455
4499
  // loaders are successful we take the deepest status code.
4456
- if (result.response) {
4457
- if (result.response.status !== 200 && !foundError) {
4458
- statusCode = result.response.status;
4459
- }
4460
- loaderHeaders[id] = result.response.headers;
4500
+ if (result.statusCode && result.statusCode !== 200 && !foundError) {
4501
+ statusCode = result.statusCode;
4502
+ }
4503
+ if (result.headers) {
4504
+ loaderHeaders[id] = result.headers;
4461
4505
  }
4462
4506
  }
4463
4507
  }
@@ -4466,9 +4510,11 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
4466
4510
  // If we didn't consume the pending action error (i.e., all loaders
4467
4511
  // resolved), then consume it here. Also clear out any loaderData for the
4468
4512
  // throwing route
4469
- if (pendingError) {
4470
- errors = pendingError;
4471
- loaderData[Object.keys(pendingError)[0]] = undefined;
4513
+ if (pendingError !== undefined && pendingActionResult) {
4514
+ errors = {
4515
+ [pendingActionResult[0]]: pendingError
4516
+ };
4517
+ loaderData[pendingActionResult[0]] = undefined;
4472
4518
  }
4473
4519
  return {
4474
4520
  loaderData,
@@ -4477,11 +4523,11 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
4477
4523
  loaderHeaders
4478
4524
  };
4479
4525
  }
4480
- function processLoaderData(state, matches, matchesToLoad, results, pendingError, revalidatingFetchers, fetcherResults, activeDeferreds) {
4526
+ function processLoaderData(state, matches, matchesToLoad, results, pendingActionResult, revalidatingFetchers, fetcherResults, activeDeferreds) {
4481
4527
  let {
4482
4528
  loaderData,
4483
4529
  errors
4484
- } = processRouteLoaderData(matches, matchesToLoad, results, pendingError, activeDeferreds);
4530
+ } = processRouteLoaderData(matches, matchesToLoad, results, pendingActionResult, activeDeferreds);
4485
4531
 
4486
4532
  // Process results from our revalidating fetchers
4487
4533
  for (let index = 0; index < revalidatingFetchers.length; index++) {
@@ -4543,6 +4589,19 @@ function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
4543
4589
  }
4544
4590
  return mergedLoaderData;
4545
4591
  }
4592
+ function getActionDataForCommit(pendingActionResult) {
4593
+ if (!pendingActionResult) {
4594
+ return {};
4595
+ }
4596
+ return isErrorResult(pendingActionResult[1]) ? {
4597
+ // Clear out prior actionData on errors
4598
+ actionData: {}
4599
+ } : {
4600
+ actionData: {
4601
+ [pendingActionResult[0]]: pendingActionResult[1].data
4602
+ }
4603
+ };
4604
+ }
4546
4605
 
4547
4606
  // Find the nearest error boundary, looking upwards from the leaf route (or the
4548
4607
  // route specified by routeId) for the closest ancestor error boundary,
@@ -4908,6 +4967,7 @@ function persistAppliedTransitions(_window, transitions) {
4908
4967
 
4909
4968
  exports.AbortedDeferredError = AbortedDeferredError;
4910
4969
  exports.Action = Action;
4970
+ exports.DecodedResponse = DecodedResponse;
4911
4971
  exports.IDLE_BLOCKER = IDLE_BLOCKER;
4912
4972
  exports.IDLE_FETCHER = IDLE_FETCHER;
4913
4973
  exports.IDLE_NAVIGATION = IDLE_NAVIGATION;