@remix-run/router 0.0.0-experimental-e960cf1a → 0.0.0-experimental-bc2c864b

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @remix-run/router v0.0.0-experimental-e960cf1a
2
+ * @remix-run/router v0.0.0-experimental-bc2c864b
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -573,6 +573,10 @@ let ResultType = /*#__PURE__*/function (ResultType) {
573
573
  * Result from a loader or action - potentially successful or unsuccessful
574
574
  */
575
575
 
576
+ /**
577
+ * Result from a loader or action called via dataStrategy
578
+ */
579
+
576
580
  /**
577
581
  * Users can specify either lowercase or uppercase form methods on `<Form>`,
578
582
  * useSubmit(), `<fetcher.Form>`, etc.
@@ -889,7 +893,7 @@ function rankRouteBranches(branches) {
889
893
  branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
890
894
  : compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex)));
891
895
  }
892
- const paramRe = /^:\w+$/;
896
+ const paramRe = /^:[\w-]+$/;
893
897
  const dynamicSegmentValue = 3;
894
898
  const indexRouteValue = 2;
895
899
  const emptySegmentValue = 1;
@@ -979,7 +983,7 @@ function generatePath(originalPath, params) {
979
983
  // Apply the splat
980
984
  return stringify(params[star]);
981
985
  }
982
- const keyMatch = segment.match(/^:(\w+)(\??)$/);
986
+ const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
983
987
  if (keyMatch) {
984
988
  const [, key, optional] = keyMatch;
985
989
  let param = params[key];
@@ -1061,7 +1065,7 @@ function compilePath(path, caseSensitive, end) {
1061
1065
  let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
1062
1066
  .replace(/^\/*/, "/") // Make sure it has a leading /
1063
1067
  .replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars
1064
- .replace(/\/:(\w+)(\?)?/g, (_, paramName, isOptional) => {
1068
+ .replace(/\/:([\w-]+)(\?)?/g, (_, paramName, isOptional) => {
1065
1069
  params.push({
1066
1070
  paramName,
1067
1071
  isOptional: isOptional != null
@@ -1597,11 +1601,6 @@ function isRouteErrorResponse(error) {
1597
1601
  /**
1598
1602
  * Identified fetcher.load() calls that need to be revalidated
1599
1603
  */
1600
- /**
1601
- * Wrapper object to allow us to throw any response out from callLoaderOrAction
1602
- * for queryRouter while preserving whether or not it was thrown or returned
1603
- * from the loader/action
1604
- */
1605
1604
  const validMutationMethodsArr = ["post", "put", "patch", "delete"];
1606
1605
  const validMutationMethods = new Set(validMutationMethodsArr);
1607
1606
  const validRequestMethodsArr = ["get", ...validMutationMethodsArr];
@@ -1654,8 +1653,6 @@ function createRouter(init) {
1654
1653
  const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined";
1655
1654
  const isServer = !isBrowser;
1656
1655
  invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
1657
- const dataStrategy = init.unstable_dataStrategy || defaultDataStrategy;
1658
- const callLoaderOrAction = createCallLoaderOrAction(dataStrategy);
1659
1656
  let mapRouteProperties;
1660
1657
  if (init.mapRouteProperties) {
1661
1658
  mapRouteProperties = init.mapRouteProperties;
@@ -1675,6 +1672,7 @@ function createRouter(init) {
1675
1672
  let dataRoutes = convertRoutesToDataRoutes(init.routes, mapRouteProperties, undefined, manifest);
1676
1673
  let inFlightDataRoutes;
1677
1674
  let basename = init.basename || "/";
1675
+ let dataStrategyImpl = init.unstable_dataStrategy || defaultDataStrategy;
1678
1676
  // Config driven behavior flags
1679
1677
  let future = _extends({
1680
1678
  v7_fetcherPersist: false,
@@ -1717,18 +1715,28 @@ function createRouter(init) {
1717
1715
  [route.id]: error
1718
1716
  };
1719
1717
  }
1720
-
1721
- // "Initialized" here really means "Can `RouterProvider` render my route tree?"
1722
- // Prior to `route.HydrateFallback`, we only had a root `fallbackElement` so we used
1723
- // `state.initialized` to render that instead of `<DataRoutes>`. Now that we
1724
- // support route level fallbacks we can always render and we'll just render
1725
- // as deep as we have data for and detect the nearest ancestor HydrateFallback
1726
- let initialized = future.v7_partialHydration ||
1727
- // All initialMatches need to be loaded before we're ready. If we have lazy
1728
- // functions around still then we'll need to run them in initialize()
1729
- !initialMatches.some(m => m.route.lazy) && (
1730
- // And we have to either have no loaders or have been provided hydrationData
1731
- !initialMatches.some(m => m.route.loader) || init.hydrationData != null);
1718
+ let initialized;
1719
+ let hasLazyRoutes = initialMatches.some(m => m.route.lazy);
1720
+ let hasLoaders = initialMatches.some(m => m.route.loader);
1721
+ if (hasLazyRoutes) {
1722
+ // All initialMatches need to be loaded before we're ready. If we have lazy
1723
+ // functions around still then we'll need to run them in initialize()
1724
+ initialized = false;
1725
+ } else if (!hasLoaders) {
1726
+ // If we've got no loaders to run, then we're good to go
1727
+ initialized = true;
1728
+ } else if (future.v7_partialHydration) {
1729
+ // If partial hydration is enabled, we're initialized so long as we were
1730
+ // provided with hydrationData for every route with a loader, and no loaders
1731
+ // were marked for explicit hydration
1732
+ let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1733
+ let errors = init.hydrationData ? init.hydrationData.errors : null;
1734
+ initialized = initialMatches.every(m => m.route.loader && m.route.loader.hydrate !== true && (loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined));
1735
+ } else {
1736
+ // Without partial hydration - we're initialized if we were provided any
1737
+ // hydrationData - which is expected to be complete
1738
+ initialized = init.hydrationData != null;
1739
+ }
1732
1740
  let router;
1733
1741
  let state = {
1734
1742
  historyAction: init.history.action,
@@ -1895,7 +1903,7 @@ function createRouter(init) {
1895
1903
  // in the normal navigation flow. For SSR it's expected that lazy modules are
1896
1904
  // resolved prior to router creation since we can't go into a fallbackElement
1897
1905
  // UI for SSR'd apps
1898
- if (!state.initialized || future.v7_partialHydration && state.matches.some(m => isUnhydratedRoute(state, m.route))) {
1906
+ if (!state.initialized) {
1899
1907
  startNavigation(Action.Pop, state.location, {
1900
1908
  initialHydration: true
1901
1909
  });
@@ -2336,7 +2344,8 @@ function createRouter(init) {
2336
2344
  })
2337
2345
  };
2338
2346
  } else {
2339
- result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath);
2347
+ let results = await callDataStrategy("action", request, [actionMatch], matches);
2348
+ result = results[0];
2340
2349
  if (request.signal.aborted) {
2341
2350
  return {
2342
2351
  shortCircuited: true
@@ -2351,9 +2360,9 @@ function createRouter(init) {
2351
2360
  // If the user didn't explicity indicate replace behavior, replace if
2352
2361
  // we redirected to the exact same location we're currently at to avoid
2353
2362
  // double back-buttons
2354
- replace = result.location === state.location.pathname + state.location.search;
2363
+ replace = result.response.headers.get("Location") === state.location.pathname + state.location.search;
2355
2364
  }
2356
- await startRedirectNavigation(state, result, {
2365
+ await startRedirectNavigation(request, result, {
2357
2366
  submission,
2358
2367
  replace
2359
2368
  });
@@ -2476,7 +2485,7 @@ function createRouter(init) {
2476
2485
  let {
2477
2486
  loaderResults,
2478
2487
  fetcherResults
2479
- } = await loadDataAndMaybeResolveDeferred(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
2488
+ } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
2480
2489
  if (request.signal.aborted) {
2481
2490
  return {
2482
2491
  shortCircuited: true
@@ -2501,7 +2510,7 @@ function createRouter(init) {
2501
2510
  let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2502
2511
  fetchRedirectIds.add(fetcherKey);
2503
2512
  }
2504
- await startRedirectNavigation(state, redirect.result, {
2513
+ await startRedirectNavigation(request, redirect.result, {
2505
2514
  replace
2506
2515
  });
2507
2516
  return {
@@ -2610,7 +2619,8 @@ function createRouter(init) {
2610
2619
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2611
2620
  fetchControllers.set(key, abortController);
2612
2621
  let originatingLoadId = incrementingLoadId;
2613
- let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath);
2622
+ let actionResults = await callDataStrategy("action", fetchRequest, [match], requestMatches);
2623
+ let actionResult = actionResults[0];
2614
2624
  if (fetchRequest.signal.aborted) {
2615
2625
  // We can delete this so long as we weren't aborted by our own fetcher
2616
2626
  // re-submit which would have put _new_ controller is in fetchControllers
@@ -2642,7 +2652,7 @@ function createRouter(init) {
2642
2652
  } else {
2643
2653
  fetchRedirectIds.add(key);
2644
2654
  updateFetcherState(key, getLoadingFetcher(submission));
2645
- return startRedirectNavigation(state, actionResult, {
2655
+ return startRedirectNavigation(fetchRequest, actionResult, {
2646
2656
  fetcherSubmission: submission
2647
2657
  });
2648
2658
  }
@@ -2699,7 +2709,7 @@ function createRouter(init) {
2699
2709
  let {
2700
2710
  loaderResults,
2701
2711
  fetcherResults
2702
- } = await loadDataAndMaybeResolveDeferred(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
2712
+ } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
2703
2713
  if (abortController.signal.aborted) {
2704
2714
  return;
2705
2715
  }
@@ -2716,7 +2726,7 @@ function createRouter(init) {
2716
2726
  let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2717
2727
  fetchRedirectIds.add(fetcherKey);
2718
2728
  }
2719
- return startRedirectNavigation(state, redirect.result);
2729
+ return startRedirectNavigation(revalidationRequest, redirect.result);
2720
2730
  }
2721
2731
 
2722
2732
  // Process and commit output from loaders
@@ -2770,7 +2780,8 @@ function createRouter(init) {
2770
2780
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2771
2781
  fetchControllers.set(key, abortController);
2772
2782
  let originatingLoadId = incrementingLoadId;
2773
- let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath);
2783
+ let results = await callDataStrategy("loader", fetchRequest, [match], matches);
2784
+ let result = results[0];
2774
2785
 
2775
2786
  // Deferred isn't supported for fetcher loads, await everything and treat it
2776
2787
  // as a normal load. resolveDeferredData will return undefined if this
@@ -2805,7 +2816,7 @@ function createRouter(init) {
2805
2816
  return;
2806
2817
  } else {
2807
2818
  fetchRedirectIds.add(key);
2808
- await startRedirectNavigation(state, result);
2819
+ await startRedirectNavigation(fetchRequest, result);
2809
2820
  return;
2810
2821
  }
2811
2822
  }
@@ -2840,26 +2851,40 @@ function createRouter(init) {
2840
2851
  * actually touch history until we've processed redirects, so we just use
2841
2852
  * the history action from the original navigation (PUSH or REPLACE).
2842
2853
  */
2843
- async function startRedirectNavigation(state, redirect, _temp2) {
2854
+ async function startRedirectNavigation(request, redirect, _temp2) {
2844
2855
  let {
2845
2856
  submission,
2846
2857
  fetcherSubmission,
2847
2858
  replace
2848
2859
  } = _temp2 === void 0 ? {} : _temp2;
2849
- if (redirect.revalidate) {
2860
+ if (redirect.response.headers.has("X-Remix-Revalidate")) {
2850
2861
  isRevalidationRequired = true;
2851
2862
  }
2852
- let redirectLocation = createLocation(state.location, redirect.location, {
2863
+ let location = redirect.response.headers.get("Location");
2864
+ invariant(location, "Expected a Location header on the redirect Response");
2865
+ let redirectLocation = createLocation(state.location, location, {
2853
2866
  _isRedirect: true
2854
2867
  });
2855
- invariant(redirectLocation, "Expected a location on the redirect navigation");
2868
+ if (ABSOLUTE_URL_REGEX.test(location)) {
2869
+ // Strip off the protocol+origin for same-origin + same-basename absolute redirects
2870
+ let normalizedLocation = location;
2871
+ let currentUrl = new URL(request.url);
2872
+ let url = normalizedLocation.startsWith("//") ? new URL(currentUrl.protocol + normalizedLocation) : new URL(normalizedLocation);
2873
+ let isSameBasename = stripBasename(url.pathname, basename) != null;
2874
+ if (url.origin === currentUrl.origin && isSameBasename) {
2875
+ normalizedLocation = url.pathname + url.search + url.hash;
2876
+ redirectLocation = createLocation(state.location, normalizedLocation, {
2877
+ _isRedirect: true
2878
+ });
2879
+ }
2880
+ }
2856
2881
  if (isBrowser) {
2857
2882
  let isDocumentReload = false;
2858
- if (redirect.reloadDocument) {
2883
+ if (redirect.response.headers.has("X-Remix-Reload-Document")) {
2859
2884
  // Hard reload if the response contained X-Remix-Reload-Document
2860
2885
  isDocumentReload = true;
2861
- } else if (ABSOLUTE_URL_REGEX.test(redirect.location)) {
2862
- const url = init.history.createURL(redirect.location);
2886
+ } else if (ABSOLUTE_URL_REGEX.test(location)) {
2887
+ const url = init.history.createURL(location);
2863
2888
  isDocumentReload =
2864
2889
  // Hard reload if it's an absolute URL to a new origin
2865
2890
  url.origin !== routerWindow.location.origin ||
@@ -2868,9 +2893,9 @@ function createRouter(init) {
2868
2893
  }
2869
2894
  if (isDocumentReload) {
2870
2895
  if (replace) {
2871
- routerWindow.location.replace(redirect.location);
2896
+ routerWindow.location.replace(location);
2872
2897
  } else {
2873
- routerWindow.location.assign(redirect.location);
2898
+ routerWindow.location.assign(location);
2874
2899
  }
2875
2900
  return;
2876
2901
  }
@@ -2896,10 +2921,10 @@ function createRouter(init) {
2896
2921
  // re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
2897
2922
  // redirected location
2898
2923
  let activeSubmission = submission || fetcherSubmission;
2899
- if (redirectPreserveMethodStatusCodes.has(redirect.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
2924
+ if (redirectPreserveMethodStatusCodes.has(redirect.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
2900
2925
  await startNavigation(redirectHistoryAction, redirectLocation, {
2901
2926
  submission: _extends({}, activeSubmission, {
2902
- formAction: redirect.location
2927
+ formAction: location
2903
2928
  }),
2904
2929
  // Preserve this flag across redirects
2905
2930
  preventScrollReset: pendingPreventScrollReset
@@ -2917,16 +2942,36 @@ function createRouter(init) {
2917
2942
  });
2918
2943
  }
2919
2944
  }
2920
- async function loadDataAndMaybeResolveDeferred(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
2921
- let [loaderResults, ...fetcherResults] = await Promise.all([matchesToLoad.length ? dataStrategy({
2922
- matches: matchesToLoad.map(m => finesseToAgnosticDataStrategyMatch(m, mapRouteProperties, manifest)),
2923
- request,
2924
- type: "loader",
2925
- defaultStrategy(match) {
2926
- return callLoaderOrActionImplementation("loader", request, match, matches, basename, future.v7_relativeSplatPath);
2927
- }
2928
- }) : [], ...fetchersToLoad.map(f => {
2929
- if (!f.matches || !f.match || !f.controller) {
2945
+
2946
+ // Utility wrapper for calling dataStrategy client-side without having to
2947
+ // pass around the manifest, mapRouteProperties, etc.
2948
+ async function callDataStrategy(type, request, matchesToLoad, matches) {
2949
+ try {
2950
+ let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties);
2951
+ return await Promise.all(results.map((result, i) => {
2952
+ if (isRedirectHandlerResult(result)) {
2953
+ return {
2954
+ type: ResultType.redirect,
2955
+ response: normalizeRelativeRoutingRedirectResponse(result.result, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath)
2956
+ };
2957
+ }
2958
+ return convertHandlerResultToDataResult(result);
2959
+ }));
2960
+ } catch (e) {
2961
+ // If the outer dataStrategy method throws, just return the error for all
2962
+ // matches - and it'll naturally bubble to the root
2963
+ return matchesToLoad.map(() => ({
2964
+ type: ResultType.error,
2965
+ error: e
2966
+ }));
2967
+ }
2968
+ }
2969
+ async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
2970
+ let [loaderResults, ...fetcherResults] = await Promise.all([matchesToLoad.length ? callDataStrategy("loader", request, matchesToLoad, matches) : [], ...fetchersToLoad.map(f => {
2971
+ if (f.matches && f.match && f.controller) {
2972
+ let fetcherRequest = createClientSideRequest(init.history, f.path, f.controller.signal);
2973
+ return callDataStrategy("loader", fetcherRequest, [f.match], f.matches).then(r => r[0]);
2974
+ } else {
2930
2975
  return Promise.resolve({
2931
2976
  type: ResultType.error,
2932
2977
  error: getInternalRouterError(404, {
@@ -2934,16 +2979,6 @@ function createRouter(init) {
2934
2979
  })
2935
2980
  });
2936
2981
  }
2937
- return dataStrategy({
2938
- matches: [finesseToAgnosticDataStrategyMatch(f.match, mapRouteProperties, manifest)],
2939
- request,
2940
- type: "loader",
2941
- defaultStrategy(match) {
2942
- invariant(f.controller, "Expected controller for fetcher in defaultStrategy");
2943
- invariant(f.matches, "Expected matches for fetcher in defaultStrategy");
2944
- return callLoaderOrActionImplementation("loader", createClientSideRequest(init.history, f.path, f.controller.signal), match, f.matches, basename, future.v7_relativeSplatPath);
2945
- }
2946
- }).then(r => r[0]);
2947
2982
  })]);
2948
2983
  await Promise.all([resolveDeferredResults(currentMatches, matchesToLoad, loaderResults, loaderResults.map(() => request.signal), false, state.loaderData), resolveDeferredResults(currentMatches, fetchersToLoad.map(f => f.match), fetcherResults, fetchersToLoad.map(f => f.controller ? f.controller.signal : null), true)]);
2949
2984
  return {
@@ -3257,10 +3292,9 @@ const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
3257
3292
 
3258
3293
  function createStaticHandler(routes, opts) {
3259
3294
  invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
3260
- const dataStrategy = (opts == null ? void 0 : opts.dataStrategy) || defaultDataStrategy;
3261
- const callLoaderOrAction = createCallLoaderOrAction(dataStrategy);
3262
3295
  let manifest = {};
3263
3296
  let basename = (opts ? opts.basename : null) || "/";
3297
+ let dataStrategyImpl = (opts == null ? void 0 : opts.unstable_dataStrategy) || defaultDataStrategy;
3264
3298
  let mapRouteProperties;
3265
3299
  if (opts != null && opts.mapRouteProperties) {
3266
3300
  mapRouteProperties = opts.mapRouteProperties;
@@ -3275,7 +3309,8 @@ function createStaticHandler(routes, opts) {
3275
3309
  }
3276
3310
  // Config driven behavior flags
3277
3311
  let future = _extends({
3278
- v7_relativeSplatPath: false
3312
+ v7_relativeSplatPath: false,
3313
+ v7_throwAbortReason: false
3279
3314
  }, opts ? opts.future : null);
3280
3315
  let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, undefined, manifest);
3281
3316
 
@@ -3459,14 +3494,14 @@ function createStaticHandler(routes, opts) {
3459
3494
  actionHeaders: {}
3460
3495
  });
3461
3496
  } catch (e) {
3462
- // If the user threw/returned a Response in callLoaderOrAction, we throw
3463
- // it to bail out and then return or throw here based on whether the user
3464
- // returned or threw
3465
- if (isQueryRouteResponse(e)) {
3497
+ // If the user threw/returned a Response in callLoaderOrAction for a
3498
+ // `queryRoute` call, we throw the `HandlerResult` to bail out early
3499
+ // and then return or throw the raw Response here accordingly
3500
+ if (isHandlerResult(e) && isResponse(e.result)) {
3466
3501
  if (e.type === ResultType.error) {
3467
- throw e.response;
3502
+ throw e.result;
3468
3503
  }
3469
- return e.response;
3504
+ return e.result;
3470
3505
  }
3471
3506
  // Redirects are always returned since they don't propagate to catch
3472
3507
  // boundaries
@@ -3492,14 +3527,10 @@ function createStaticHandler(routes, opts) {
3492
3527
  error
3493
3528
  };
3494
3529
  } else {
3495
- result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath, {
3496
- isStaticRequest: true,
3497
- isRouteRequest,
3498
- requestContext
3499
- });
3530
+ let results = await callDataStrategy("action", request, [actionMatch], matches, isRouteRequest, requestContext);
3531
+ result = results[0];
3500
3532
  if (request.signal.aborted) {
3501
- let method = isRouteRequest ? "queryRoute" : "query";
3502
- throw new Error(method + "() call aborted: " + request.method + " " + request.url);
3533
+ throwStaticHandlerAbortedError(request, isRouteRequest, future);
3503
3534
  }
3504
3535
  }
3505
3536
  if (isRedirectResult(result)) {
@@ -3508,9 +3539,9 @@ function createStaticHandler(routes, opts) {
3508
3539
  // can get back on the "throw all redirect responses" train here should
3509
3540
  // this ever happen :/
3510
3541
  throw new Response(null, {
3511
- status: result.status,
3542
+ status: result.response.status,
3512
3543
  headers: {
3513
- Location: result.location
3544
+ Location: result.response.headers.get("Location")
3514
3545
  }
3515
3546
  });
3516
3547
  }
@@ -3559,8 +3590,8 @@ function createStaticHandler(routes, opts) {
3559
3590
  return _extends({}, context, {
3560
3591
  statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500,
3561
3592
  actionData: null,
3562
- actionHeaders: _extends({}, result.headers ? {
3563
- [actionMatch.route.id]: result.headers
3593
+ actionHeaders: _extends({}, result.response ? {
3594
+ [actionMatch.route.id]: result.response.headers
3564
3595
  } : {})
3565
3596
  });
3566
3597
  }
@@ -3572,15 +3603,17 @@ function createStaticHandler(routes, opts) {
3572
3603
  signal: request.signal
3573
3604
  });
3574
3605
  let context = await loadRouteData(loaderRequest, matches, requestContext);
3575
- return _extends({}, context, result.statusCode ? {
3576
- statusCode: result.statusCode
3577
- } : {}, {
3606
+ return _extends({}, context, {
3578
3607
  actionData: {
3579
3608
  [actionMatch.route.id]: result.data
3580
- },
3581
- actionHeaders: _extends({}, result.headers ? {
3582
- [actionMatch.route.id]: result.headers
3583
- } : {})
3609
+ }
3610
+ }, result.response ? {
3611
+ statusCode: result.response.status,
3612
+ actionHeaders: {
3613
+ [actionMatch.route.id]: result.response.headers
3614
+ }
3615
+ } : {
3616
+ actionHeaders: {}
3584
3617
  });
3585
3618
  }
3586
3619
  async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
@@ -3611,21 +3644,9 @@ function createStaticHandler(routes, opts) {
3611
3644
  activeDeferreds: null
3612
3645
  };
3613
3646
  }
3614
- let results = await dataStrategy({
3615
- matches: matchesToLoad.map(m => finesseToAgnosticDataStrategyMatch(m, mapRouteProperties, manifest)),
3616
- request,
3617
- type: "loader",
3618
- defaultStrategy(match) {
3619
- return callLoaderOrActionImplementation("loader", request, match, matches, basename, future.v7_relativeSplatPath, {
3620
- isStaticRequest: true,
3621
- isRouteRequest,
3622
- requestContext
3623
- });
3624
- }
3625
- });
3647
+ let results = await callDataStrategy("loader", request, matchesToLoad, matches, isRouteRequest, requestContext);
3626
3648
  if (request.signal.aborted) {
3627
- let method = isRouteRequest ? "queryRoute" : "query";
3628
- throw new Error(method + "() call aborted: " + request.method + " " + request.url);
3649
+ throwStaticHandlerAbortedError(request, isRouteRequest, future);
3629
3650
  }
3630
3651
 
3631
3652
  // Process and commit output from loaders
@@ -3644,6 +3665,25 @@ function createStaticHandler(routes, opts) {
3644
3665
  activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
3645
3666
  });
3646
3667
  }
3668
+
3669
+ // Utility wrapper for calling dataStrategy server-side without having to
3670
+ // pass around the manifest, mapRouteProperties, etc.
3671
+ async function callDataStrategy(type, request, matchesToLoad, matches, isRouteRequest, requestContext) {
3672
+ let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext);
3673
+ return await Promise.all(results.map((result, i) => {
3674
+ if (isRedirectHandlerResult(result)) {
3675
+ // Throw redirects and let the server handle them with an HTTP redirect
3676
+ throw normalizeRelativeRoutingRedirectResponse(result.result, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath);
3677
+ }
3678
+ if (isResponse(result.result) && isRouteRequest) {
3679
+ // For SSR single-route requests, we want to hand Responses back directly
3680
+ // without unwrapping. We do this with the QueryRouteResponse wrapper
3681
+ // interface so we can know whether it was returned or thrown
3682
+ throw result;
3683
+ }
3684
+ return convertHandlerResultToDataResult(result);
3685
+ }));
3686
+ }
3647
3687
  return {
3648
3688
  dataRoutes,
3649
3689
  query,
@@ -3657,27 +3697,26 @@ function createStaticHandler(routes, opts) {
3657
3697
  //#region Helpers
3658
3698
  ////////////////////////////////////////////////////////////////////////////////
3659
3699
 
3660
- function defaultDataStrategy(_ref3) {
3661
- let {
3662
- defaultStrategy,
3663
- matches
3664
- } = _ref3;
3665
- return Promise.all(matches.map(match => defaultStrategy(match)));
3666
- }
3667
-
3668
3700
  /**
3669
3701
  * Given an existing StaticHandlerContext and an error thrown at render time,
3670
3702
  * provide an updated StaticHandlerContext suitable for a second SSR render
3671
3703
  */
3672
3704
  function getStaticContextFromError(routes, context, error) {
3673
3705
  let newContext = _extends({}, context, {
3674
- statusCode: 500,
3706
+ statusCode: isRouteErrorResponse(error) ? error.status : 500,
3675
3707
  errors: {
3676
3708
  [context._deepestRenderedBoundaryId || routes[0].id]: error
3677
3709
  }
3678
3710
  });
3679
3711
  return newContext;
3680
3712
  }
3713
+ function throwStaticHandlerAbortedError(request, isRouteRequest, future) {
3714
+ if (future.v7_throwAbortReason && request.signal.reason !== undefined) {
3715
+ throw request.signal.reason;
3716
+ }
3717
+ let method = isRouteRequest ? "queryRoute" : "query";
3718
+ throw new Error(method + "() call aborted: " + request.method + " " + request.url);
3719
+ }
3681
3720
  function isSubmissionNavigation(opts) {
3682
3721
  return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== undefined);
3683
3722
  }
@@ -3762,8 +3801,8 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
3762
3801
  }
3763
3802
  let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
3764
3803
  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
3765
- Array.from(opts.body.entries()).reduce((acc, _ref4) => {
3766
- let [name, value] = _ref4;
3804
+ Array.from(opts.body.entries()).reduce((acc, _ref3) => {
3805
+ let [name, value] = _ref3;
3767
3806
  return "" + acc + name + "=" + value + "\n";
3768
3807
  }, "") : String(opts.body);
3769
3808
  return {
@@ -3874,18 +3913,24 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3874
3913
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3875
3914
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3876
3915
  let navigationMatches = boundaryMatches.filter((match, index) => {
3877
- if (isInitialLoad) {
3878
- // On initial hydration we don't do any shouldRevalidate stuff - we just
3879
- // call the unhydrated loaders
3880
- return isUnhydratedRoute(state, match.route);
3881
- }
3882
- if (match.route.lazy) {
3916
+ let {
3917
+ route
3918
+ } = match;
3919
+ if (route.lazy) {
3883
3920
  // We haven't loaded this route yet so we don't know if it's got a loader!
3884
3921
  return true;
3885
3922
  }
3886
- if (match.route.loader == null) {
3923
+ if (route.loader == null) {
3887
3924
  return false;
3888
3925
  }
3926
+ if (isInitialLoad) {
3927
+ if (route.loader.hydrate) {
3928
+ return true;
3929
+ }
3930
+ return state.loaderData[route.id] === undefined && (
3931
+ // Don't re-run if the loader ran and threw an error
3932
+ !state.errors || state.errors[route.id] === undefined);
3933
+ }
3889
3934
 
3890
3935
  // Always call the loader on new route instances and pending defer cancellations
3891
3936
  if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
@@ -3987,20 +4032,6 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3987
4032
  });
3988
4033
  return [navigationMatches, revalidatingFetchers];
3989
4034
  }
3990
-
3991
- // Is this route unhydrated (when v7_partialHydration=true) such that we need
3992
- // to call it's loader on the initial router creation
3993
- function isUnhydratedRoute(state, route) {
3994
- if (!route.loader) {
3995
- return false;
3996
- }
3997
- if (route.loader.hydrate) {
3998
- return true;
3999
- }
4000
- return state.loaderData[route.id] === undefined && (!state.errors ||
4001
- // Loader ran but errored - don't re-run
4002
- state.errors[route.id] === undefined);
4003
- }
4004
4035
  function isNewLoader(currentLoaderData, currentMatch, match) {
4005
4036
  let isNew =
4006
4037
  // [a] -> [a, b]
@@ -4088,44 +4119,57 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
4088
4119
  }));
4089
4120
  return routeToUpdate;
4090
4121
  }
4091
- function createCallLoaderOrAction(dataStrategy) {
4092
- return async function (type, request, match, matches, manifest, mapRouteProperties, basename, v7_relativeSplatPath, opts) {
4093
- if (opts === void 0) {
4094
- opts = {};
4095
- }
4096
- let [result] = await dataStrategy({
4097
- matches: [finesseToAgnosticDataStrategyMatch(match, mapRouteProperties, manifest)],
4098
- request,
4099
- type,
4100
- defaultStrategy(match) {
4101
- return callLoaderOrActionImplementation(type, request, match, matches, basename, v7_relativeSplatPath, opts);
4102
- }
4103
- });
4104
- return result;
4105
- };
4122
+
4123
+ // Default implementation of `dataStrategy` which fetches all loaders in parallel
4124
+ function defaultDataStrategy(opts) {
4125
+ return Promise.all(opts.matches.map(m => m.bikeshed_loadRoute()));
4106
4126
  }
4107
- function finesseToAgnosticDataStrategyMatch(match, mapRouteProperties, manifest) {
4108
- let loadRoutePromise;
4109
- if (match.route.lazy) {
4110
- try {
4111
- loadRoutePromise = loadLazyRouteModule(match.route, mapRouteProperties, manifest);
4112
- } catch (error) {
4113
- loadRoutePromise = Promise.reject(error);
4114
- }
4115
- }
4116
- if (!loadRoutePromise) {
4117
- loadRoutePromise = Promise.resolve(match.route);
4118
- }
4119
- loadRoutePromise.catch(() => {});
4120
- return _extends({}, match, {
4121
- route: Object.assign(loadRoutePromise, match.route)
4127
+ async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext) {
4128
+ let routeIdsToLoad = matchesToLoad.reduce((acc, m) => acc.add(m.route.id), new Set());
4129
+ let loadedMatches = new Set();
4130
+
4131
+ // Send all matches here to allow for a middleware-type implementation.
4132
+ // handler will be a no-op for unneeded routes and we filter those results
4133
+ // back out below.
4134
+ let results = await dataStrategyImpl({
4135
+ matches: matches.map(match => {
4136
+ // `bikeshed_loadRoute` encapsulates the route.lazy, executing the
4137
+ // loader/action, and mapping return values/thrown errors to a
4138
+ // HandlerResult. Users can pass a callback to take fine-grained control
4139
+ // over the execution of the loader/action
4140
+ let bikeshed_loadRoute = bikeshed_handlerOverride => {
4141
+ loadedMatches.add(match.route.id);
4142
+ return routeIdsToLoad.has(match.route.id) ? callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, requestContext) :
4143
+ // TODO: What's the best thing to do here - return an empty "success" result?
4144
+ // Or return a success result with the current route loader/action data?
4145
+ // We strip these results out if the route didn't need to be revalidated in
4146
+ // `callDataStrategy` so it doesn't matter for us. It's more of a question
4147
+ // of whether exposing the current data to the user is useful?
4148
+ Promise.resolve({
4149
+ type: ResultType.data,
4150
+ result: undefined
4151
+ });
4152
+ };
4153
+ return _extends({}, match, {
4154
+ bikeshed_loadRoute
4155
+ });
4156
+ }),
4157
+ request,
4158
+ params: matches[0].params
4122
4159
  });
4160
+
4161
+ // Throw if any loadRoute implementations not called since they are what
4162
+ // ensures a route is fully loaded
4163
+ // Throw if any loadRoute implementations not called since they are what
4164
+ // ensure a route is fully loaded
4165
+ matches.forEach(m => invariant(loadedMatches.has(m.route.id), "`match.bikeshed_loadRoute()` was not called for route id \"" + m.route.id + "\". " + "You must call `match.bikeshed_loadRoute()` on every match passed to " + "`dataStrategy` to ensure all routes are properly loaded."));
4166
+
4167
+ // Filter out any middleware-only matches for which we didn't need to run handlers
4168
+ return results.filter((_, i) => routeIdsToLoad.has(matches[i].route.id));
4123
4169
  }
4124
- async function callLoaderOrActionImplementation(type, request, match, matches, basename, v7_relativeSplatPath, opts) {
4125
- if (opts === void 0) {
4126
- opts = {};
4127
- }
4128
- let resultType;
4170
+
4171
+ // Default logic for calling a loader/action is the user has no specified a dataStrategy
4172
+ async function callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, staticContext) {
4129
4173
  let result;
4130
4174
  let onReject;
4131
4175
  let runHandler = handler => {
@@ -4134,11 +4178,13 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
4134
4178
  let abortPromise = new Promise((_, r) => reject = r);
4135
4179
  onReject = () => reject();
4136
4180
  request.signal.addEventListener("abort", onReject);
4137
- return Promise.race([handler({
4181
+ let handlerArg = {
4138
4182
  request,
4139
4183
  params: match.params,
4140
- context: opts.requestContext
4141
- }), abortPromise]);
4184
+ context: staticContext
4185
+ };
4186
+ let handlerPromise = bikeshed_handlerOverride ? bikeshed_handlerOverride(async ctx => ctx !== undefined ? handler(handlerArg, ctx) : handler(handlerArg)) : handler(handlerArg);
4187
+ return Promise.race([handlerPromise, abortPromise]);
4142
4188
  };
4143
4189
  try {
4144
4190
  let handler = match.route[type];
@@ -4152,17 +4198,17 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
4152
4198
  // route has a boundary that can handle the error
4153
4199
  runHandler(handler).catch(e => {
4154
4200
  handlerError = e;
4155
- }), match.route]);
4201
+ }), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]);
4156
4202
  if (handlerError) {
4157
4203
  throw handlerError;
4158
4204
  }
4159
4205
  result = values[0];
4160
4206
  } else {
4161
4207
  // Load lazy route module, then run any returned handler
4162
- let route = await match.route;
4163
- handler = route[type];
4208
+ await loadLazyRouteModule(match.route, mapRouteProperties, manifest);
4209
+ handler = match.route[type];
4164
4210
  if (handler) {
4165
- // Handler still run even if we got interrupted to maintain consistency
4211
+ // Handler still runs even if we got interrupted to maintain consistency
4166
4212
  // with un-abortable behavior of handler execution on non-lazy or
4167
4213
  // previously-lazy-loaded routes
4168
4214
  result = await runHandler(handler);
@@ -4179,7 +4225,7 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
4179
4225
  // hit the invariant below that errors on returning undefined.
4180
4226
  return {
4181
4227
  type: ResultType.data,
4182
- data: undefined
4228
+ result: undefined
4183
4229
  };
4184
4230
  }
4185
4231
  }
@@ -4194,70 +4240,37 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
4194
4240
  }
4195
4241
  invariant(result !== undefined, "You defined " + (type === "action" ? "an action" : "a loader") + " for route " + ("\"" + match.route.id + "\" but didn't return anything from your `" + type + "` ") + "function. Please return a value or `null`.");
4196
4242
  } catch (e) {
4197
- resultType = ResultType.error;
4198
- result = e;
4243
+ return {
4244
+ type: ResultType.error,
4245
+ result: e
4246
+ };
4199
4247
  } finally {
4200
4248
  if (onReject) {
4201
4249
  request.signal.removeEventListener("abort", onReject);
4202
4250
  }
4203
4251
  }
4252
+ return {
4253
+ type: ResultType.data,
4254
+ result
4255
+ };
4256
+ }
4257
+ async function convertHandlerResultToDataResult(handlerResult) {
4258
+ let {
4259
+ result,
4260
+ type
4261
+ } = handlerResult;
4204
4262
  if (isResponse(result)) {
4205
- let status = result.status;
4206
-
4207
- // Process redirects
4208
- if (redirectStatusCodes.has(status)) {
4209
- let location = result.headers.get("Location");
4210
- invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
4211
-
4212
- // Support relative routing in internal redirects
4213
- if (!ABSOLUTE_URL_REGEX.test(location)) {
4214
- location = normalizeTo(new URL(request.url), matches.slice(0, matches.findIndex(m => m.route.id === match.route.id) + 1), basename, true, location, v7_relativeSplatPath);
4215
- } else if (!opts.isStaticRequest) {
4216
- // Strip off the protocol+origin for same-origin + same-basename absolute
4217
- // redirects. If this is a static request, we can let it go back to the
4218
- // browser as-is
4219
- let currentUrl = new URL(request.url);
4220
- let url = location.startsWith("//") ? new URL(currentUrl.protocol + location) : new URL(location);
4221
- let isSameBasename = stripBasename(url.pathname, basename) != null;
4222
- if (url.origin === currentUrl.origin && isSameBasename) {
4223
- location = url.pathname + url.search + url.hash;
4224
- }
4225
- }
4226
-
4227
- // Don't process redirects in the router during static requests requests.
4228
- // Instead, throw the Response and let the server handle it with an HTTP
4229
- // redirect. We also update the Location header in place in this flow so
4230
- // basename and relative routing is taken into account
4231
- if (opts.isStaticRequest) {
4232
- result.headers.set("Location", location);
4233
- throw result;
4234
- }
4235
- return {
4236
- type: ResultType.redirect,
4237
- status,
4238
- location,
4239
- revalidate: result.headers.get("X-Remix-Revalidate") !== null,
4240
- reloadDocument: result.headers.get("X-Remix-Reload-Document") !== null
4241
- };
4242
- }
4243
-
4244
- // For SSR single-route requests, we want to hand Responses back directly
4245
- // without unwrapping. We do this with the QueryRouteResponse wrapper
4246
- // interface so we can know whether it was returned or thrown
4247
- if (opts.isRouteRequest) {
4248
- let queryRouteResponse = {
4249
- type: resultType === ResultType.error ? ResultType.error : ResultType.data,
4250
- response: result
4251
- };
4252
- throw queryRouteResponse;
4253
- }
4254
4263
  let data;
4255
4264
  try {
4256
4265
  let contentType = result.headers.get("Content-Type");
4257
4266
  // Check between word boundaries instead of startsWith() due to the last
4258
4267
  // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
4259
4268
  if (contentType && /\bapplication\/json\b/.test(contentType)) {
4260
- data = await result.json();
4269
+ if (result.body == null) {
4270
+ data = null;
4271
+ } else {
4272
+ data = await result.json();
4273
+ }
4261
4274
  } else {
4262
4275
  data = await result.text();
4263
4276
  }
@@ -4267,23 +4280,22 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
4267
4280
  error: e
4268
4281
  };
4269
4282
  }
4270
- if (resultType === ResultType.error) {
4283
+ if (type === ResultType.error) {
4271
4284
  return {
4272
- type: resultType,
4273
- error: new ErrorResponseImpl(status, result.statusText, data),
4274
- headers: result.headers
4285
+ type,
4286
+ error: new ErrorResponseImpl(result.status, result.statusText, data),
4287
+ response: result
4275
4288
  };
4276
4289
  }
4277
4290
  return {
4278
4291
  type: ResultType.data,
4279
4292
  data,
4280
- statusCode: result.status,
4281
- headers: result.headers
4293
+ response: result
4282
4294
  };
4283
4295
  }
4284
- if (resultType === ResultType.error) {
4296
+ if (type === ResultType.error) {
4285
4297
  return {
4286
- type: resultType,
4298
+ type,
4287
4299
  error: result
4288
4300
  };
4289
4301
  }
@@ -4302,6 +4314,18 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
4302
4314
  };
4303
4315
  }
4304
4316
 
4317
+ // Support relative routing in internal redirects
4318
+ function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename, v7_relativeSplatPath) {
4319
+ let location = response.headers.get("Location");
4320
+ invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
4321
+ if (!ABSOLUTE_URL_REGEX.test(location)) {
4322
+ let trimmedMatches = matches.slice(0, matches.findIndex(m => m.route.id === routeId) + 1);
4323
+ location = normalizeTo(new URL(request.url), trimmedMatches, basename, true, location, v7_relativeSplatPath);
4324
+ response.headers.set("Location", location);
4325
+ }
4326
+ return response;
4327
+ }
4328
+
4305
4329
  // Utility method for creating the Request instances for loaders/actions during
4306
4330
  // client-side navigations and fetches. During SSR we will always have a
4307
4331
  // Request instance from the static handler (query/queryRoute)
@@ -4392,24 +4416,31 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
4392
4416
  foundError = true;
4393
4417
  statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
4394
4418
  }
4395
- if (result.headers) {
4396
- loaderHeaders[id] = result.headers;
4419
+ if (result.response) {
4420
+ loaderHeaders[id] = result.response.headers;
4397
4421
  }
4398
4422
  } else {
4399
4423
  if (isDeferredResult(result)) {
4400
4424
  activeDeferreds.set(id, result.deferredData);
4401
4425
  loaderData[id] = result.deferredData.data;
4426
+ // Error status codes always override success status codes, but if all
4427
+ // loaders are successful we take the deepest status code.
4428
+ if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
4429
+ statusCode = result.statusCode;
4430
+ }
4431
+ if (result.headers) {
4432
+ loaderHeaders[id] = result.headers;
4433
+ }
4402
4434
  } else {
4403
4435
  loaderData[id] = result.data;
4404
- }
4405
-
4406
- // Error status codes always override success status codes, but if all
4407
- // loaders are successful we take the deepest status code.
4408
- if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
4409
- statusCode = result.statusCode;
4410
- }
4411
- if (result.headers) {
4412
- loaderHeaders[id] = result.headers;
4436
+ // Error status codes always override success status codes, but if all
4437
+ // loaders are successful we take the deepest status code.
4438
+ if (result.response) {
4439
+ if (result.response.status !== 200 && !foundError) {
4440
+ statusCode = result.response.status;
4441
+ }
4442
+ loaderHeaders[id] = result.response.headers;
4443
+ }
4413
4444
  }
4414
4445
  }
4415
4446
  });
@@ -4589,6 +4620,12 @@ function isHashChangeOnly(a, b) {
4589
4620
  // /page#hash -> /page
4590
4621
  return false;
4591
4622
  }
4623
+ function isHandlerResult(result) {
4624
+ return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
4625
+ }
4626
+ function isRedirectHandlerResult(result) {
4627
+ return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
4628
+ }
4592
4629
  function isDeferredResult(result) {
4593
4630
  return result.type === ResultType.deferred;
4594
4631
  }
@@ -4613,9 +4650,6 @@ function isRedirectResponse(result) {
4613
4650
  let location = result.headers.get("Location");
4614
4651
  return status >= 300 && status <= 399 && location != null;
4615
4652
  }
4616
- function isQueryRouteResponse(obj) {
4617
- return obj && isResponse(obj.response) && (obj.type === ResultType.data || obj.type === ResultType.error);
4618
- }
4619
4653
  function isValidMethod(method) {
4620
4654
  return validRequestMethods.has(method.toLowerCase());
4621
4655
  }
@@ -4859,7 +4893,6 @@ exports.Action = Action;
4859
4893
  exports.IDLE_BLOCKER = IDLE_BLOCKER;
4860
4894
  exports.IDLE_FETCHER = IDLE_FETCHER;
4861
4895
  exports.IDLE_NAVIGATION = IDLE_NAVIGATION;
4862
- exports.ResultType = ResultType;
4863
4896
  exports.UNSAFE_DEFERRED_SYMBOL = UNSAFE_DEFERRED_SYMBOL;
4864
4897
  exports.UNSAFE_DeferredData = DeferredData;
4865
4898
  exports.UNSAFE_ErrorResponseImpl = ErrorResponseImpl;