@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.
package/dist/router.js CHANGED
@@ -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
  *
@@ -653,7 +653,7 @@ function rankRouteBranches(branches) {
653
653
  branches.sort((a, b) => a.score !== b.score ? b.score - a.score // Higher score first
654
654
  : compareIndexes(a.routesMeta.map(meta => meta.childrenIndex), b.routesMeta.map(meta => meta.childrenIndex)));
655
655
  }
656
- const paramRe = /^:\w+$/;
656
+ const paramRe = /^:[\w-]+$/;
657
657
  const dynamicSegmentValue = 3;
658
658
  const indexRouteValue = 2;
659
659
  const emptySegmentValue = 1;
@@ -740,7 +740,7 @@ function generatePath(originalPath, params) {
740
740
  // Apply the splat
741
741
  return stringify(params[star]);
742
742
  }
743
- const keyMatch = segment.match(/^:(\w+)(\??)$/);
743
+ const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
744
744
  if (keyMatch) {
745
745
  const [, key, optional] = keyMatch;
746
746
  let param = params[key];
@@ -812,7 +812,7 @@ function compilePath(path, caseSensitive, end) {
812
812
  let regexpSource = "^" + path.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
813
813
  .replace(/^\/*/, "/") // Make sure it has a leading /
814
814
  .replace(/[\\.*+^${}|()[\]]/g, "\\$&") // Escape special regex chars
815
- .replace(/\/:(\w+)(\?)?/g, (_, paramName, isOptional) => {
815
+ .replace(/\/:([\w-]+)(\?)?/g, (_, paramName, isOptional) => {
816
816
  params.push({
817
817
  paramName,
818
818
  isOptional: isOptional != null
@@ -1306,8 +1306,6 @@ function createRouter(init) {
1306
1306
  const isBrowser = typeof routerWindow !== "undefined" && typeof routerWindow.document !== "undefined" && typeof routerWindow.document.createElement !== "undefined";
1307
1307
  const isServer = !isBrowser;
1308
1308
  invariant(init.routes.length > 0, "You must provide a non-empty routes array to createRouter");
1309
- const dataStrategy = init.unstable_dataStrategy || defaultDataStrategy;
1310
- const callLoaderOrAction = createCallLoaderOrAction(dataStrategy);
1311
1309
  let mapRouteProperties;
1312
1310
  if (init.mapRouteProperties) {
1313
1311
  mapRouteProperties = init.mapRouteProperties;
@@ -1326,6 +1324,7 @@ function createRouter(init) {
1326
1324
  let dataRoutes = convertRoutesToDataRoutes(init.routes, mapRouteProperties, undefined, manifest);
1327
1325
  let inFlightDataRoutes;
1328
1326
  let basename = init.basename || "/";
1327
+ let dataStrategyImpl = init.unstable_dataStrategy || defaultDataStrategy;
1329
1328
  // Config driven behavior flags
1330
1329
  let future = _extends({
1331
1330
  v7_fetcherPersist: false,
@@ -1368,17 +1367,28 @@ function createRouter(init) {
1368
1367
  [route.id]: error
1369
1368
  };
1370
1369
  }
1371
- // "Initialized" here really means "Can `RouterProvider` render my route tree?"
1372
- // Prior to `route.HydrateFallback`, we only had a root `fallbackElement` so we used
1373
- // `state.initialized` to render that instead of `<DataRoutes>`. Now that we
1374
- // support route level fallbacks we can always render and we'll just render
1375
- // as deep as we have data for and detect the nearest ancestor HydrateFallback
1376
- let initialized = future.v7_partialHydration ||
1377
- // All initialMatches need to be loaded before we're ready. If we have lazy
1378
- // functions around still then we'll need to run them in initialize()
1379
- !initialMatches.some(m => m.route.lazy) && (
1380
- // And we have to either have no loaders or have been provided hydrationData
1381
- !initialMatches.some(m => m.route.loader) || init.hydrationData != null);
1370
+ let initialized;
1371
+ let hasLazyRoutes = initialMatches.some(m => m.route.lazy);
1372
+ let hasLoaders = initialMatches.some(m => m.route.loader);
1373
+ if (hasLazyRoutes) {
1374
+ // All initialMatches need to be loaded before we're ready. If we have lazy
1375
+ // functions around still then we'll need to run them in initialize()
1376
+ initialized = false;
1377
+ } else if (!hasLoaders) {
1378
+ // If we've got no loaders to run, then we're good to go
1379
+ initialized = true;
1380
+ } else if (future.v7_partialHydration) {
1381
+ // If partial hydration is enabled, we're initialized so long as we were
1382
+ // provided with hydrationData for every route with a loader, and no loaders
1383
+ // were marked for explicit hydration
1384
+ let loaderData = init.hydrationData ? init.hydrationData.loaderData : null;
1385
+ let errors = init.hydrationData ? init.hydrationData.errors : null;
1386
+ initialized = initialMatches.every(m => m.route.loader && m.route.loader.hydrate !== true && (loaderData && loaderData[m.route.id] !== undefined || errors && errors[m.route.id] !== undefined));
1387
+ } else {
1388
+ // Without partial hydration - we're initialized if we were provided any
1389
+ // hydrationData - which is expected to be complete
1390
+ initialized = init.hydrationData != null;
1391
+ }
1382
1392
  let router;
1383
1393
  let state = {
1384
1394
  historyAction: init.history.action,
@@ -1521,7 +1531,7 @@ function createRouter(init) {
1521
1531
  // in the normal navigation flow. For SSR it's expected that lazy modules are
1522
1532
  // resolved prior to router creation since we can't go into a fallbackElement
1523
1533
  // UI for SSR'd apps
1524
- if (!state.initialized || future.v7_partialHydration && state.matches.some(m => isUnhydratedRoute(state, m.route))) {
1534
+ if (!state.initialized) {
1525
1535
  startNavigation(Action.Pop, state.location, {
1526
1536
  initialHydration: true
1527
1537
  });
@@ -1932,7 +1942,8 @@ function createRouter(init) {
1932
1942
  })
1933
1943
  };
1934
1944
  } else {
1935
- result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath);
1945
+ let results = await callDataStrategy("action", request, [actionMatch], matches);
1946
+ result = results[0];
1936
1947
  if (request.signal.aborted) {
1937
1948
  return {
1938
1949
  shortCircuited: true
@@ -1947,9 +1958,9 @@ function createRouter(init) {
1947
1958
  // If the user didn't explicity indicate replace behavior, replace if
1948
1959
  // we redirected to the exact same location we're currently at to avoid
1949
1960
  // double back-buttons
1950
- replace = result.location === state.location.pathname + state.location.search;
1961
+ replace = result.response.headers.get("Location") === state.location.pathname + state.location.search;
1951
1962
  }
1952
- await startRedirectNavigation(state, result, {
1963
+ await startRedirectNavigation(request, result, {
1953
1964
  submission,
1954
1965
  replace
1955
1966
  });
@@ -2065,7 +2076,7 @@ function createRouter(init) {
2065
2076
  let {
2066
2077
  loaderResults,
2067
2078
  fetcherResults
2068
- } = await loadDataAndMaybeResolveDeferred(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
2079
+ } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, request);
2069
2080
  if (request.signal.aborted) {
2070
2081
  return {
2071
2082
  shortCircuited: true
@@ -2088,7 +2099,7 @@ function createRouter(init) {
2088
2099
  let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2089
2100
  fetchRedirectIds.add(fetcherKey);
2090
2101
  }
2091
- await startRedirectNavigation(state, redirect.result, {
2102
+ await startRedirectNavigation(request, redirect.result, {
2092
2103
  replace
2093
2104
  });
2094
2105
  return {
@@ -2190,7 +2201,8 @@ function createRouter(init) {
2190
2201
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
2191
2202
  fetchControllers.set(key, abortController);
2192
2203
  let originatingLoadId = incrementingLoadId;
2193
- let actionResult = await callLoaderOrAction("action", fetchRequest, match, requestMatches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath);
2204
+ let actionResults = await callDataStrategy("action", fetchRequest, [match], requestMatches);
2205
+ let actionResult = actionResults[0];
2194
2206
  if (fetchRequest.signal.aborted) {
2195
2207
  // We can delete this so long as we weren't aborted by our own fetcher
2196
2208
  // re-submit which would have put _new_ controller is in fetchControllers
@@ -2221,7 +2233,7 @@ function createRouter(init) {
2221
2233
  } else {
2222
2234
  fetchRedirectIds.add(key);
2223
2235
  updateFetcherState(key, getLoadingFetcher(submission));
2224
- return startRedirectNavigation(state, actionResult, {
2236
+ return startRedirectNavigation(fetchRequest, actionResult, {
2225
2237
  fetcherSubmission: submission
2226
2238
  });
2227
2239
  }
@@ -2275,7 +2287,7 @@ function createRouter(init) {
2275
2287
  let {
2276
2288
  loaderResults,
2277
2289
  fetcherResults
2278
- } = await loadDataAndMaybeResolveDeferred(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
2290
+ } = await callLoadersAndMaybeResolveData(state.matches, matches, matchesToLoad, revalidatingFetchers, revalidationRequest);
2279
2291
  if (abortController.signal.aborted) {
2280
2292
  return;
2281
2293
  }
@@ -2292,7 +2304,7 @@ function createRouter(init) {
2292
2304
  let fetcherKey = revalidatingFetchers[redirect.idx - matchesToLoad.length].key;
2293
2305
  fetchRedirectIds.add(fetcherKey);
2294
2306
  }
2295
- return startRedirectNavigation(state, redirect.result);
2307
+ return startRedirectNavigation(revalidationRequest, redirect.result);
2296
2308
  }
2297
2309
  // Process and commit output from loaders
2298
2310
  let {
@@ -2341,7 +2353,8 @@ function createRouter(init) {
2341
2353
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
2342
2354
  fetchControllers.set(key, abortController);
2343
2355
  let originatingLoadId = incrementingLoadId;
2344
- let result = await callLoaderOrAction("loader", fetchRequest, match, matches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath);
2356
+ let results = await callDataStrategy("loader", fetchRequest, [match], matches);
2357
+ let result = results[0];
2345
2358
  // Deferred isn't supported for fetcher loads, await everything and treat it
2346
2359
  // as a normal load. resolveDeferredData will return undefined if this
2347
2360
  // fetcher gets aborted, so we just leave result untouched and short circuit
@@ -2372,7 +2385,7 @@ function createRouter(init) {
2372
2385
  return;
2373
2386
  } else {
2374
2387
  fetchRedirectIds.add(key);
2375
- await startRedirectNavigation(state, result);
2388
+ await startRedirectNavigation(fetchRequest, result);
2376
2389
  return;
2377
2390
  }
2378
2391
  }
@@ -2404,26 +2417,40 @@ function createRouter(init) {
2404
2417
  * actually touch history until we've processed redirects, so we just use
2405
2418
  * the history action from the original navigation (PUSH or REPLACE).
2406
2419
  */
2407
- async function startRedirectNavigation(state, redirect, _temp2) {
2420
+ async function startRedirectNavigation(request, redirect, _temp2) {
2408
2421
  let {
2409
2422
  submission,
2410
2423
  fetcherSubmission,
2411
2424
  replace
2412
2425
  } = _temp2 === void 0 ? {} : _temp2;
2413
- if (redirect.revalidate) {
2426
+ if (redirect.response.headers.has("X-Remix-Revalidate")) {
2414
2427
  isRevalidationRequired = true;
2415
2428
  }
2416
- let redirectLocation = createLocation(state.location, redirect.location, {
2429
+ let location = redirect.response.headers.get("Location");
2430
+ invariant(location, "Expected a Location header on the redirect Response");
2431
+ let redirectLocation = createLocation(state.location, location, {
2417
2432
  _isRedirect: true
2418
2433
  });
2419
- invariant(redirectLocation, "Expected a location on the redirect navigation");
2434
+ if (ABSOLUTE_URL_REGEX.test(location)) {
2435
+ // Strip off the protocol+origin for same-origin + same-basename absolute redirects
2436
+ let normalizedLocation = location;
2437
+ let currentUrl = new URL(request.url);
2438
+ let url = normalizedLocation.startsWith("//") ? new URL(currentUrl.protocol + normalizedLocation) : new URL(normalizedLocation);
2439
+ let isSameBasename = stripBasename(url.pathname, basename) != null;
2440
+ if (url.origin === currentUrl.origin && isSameBasename) {
2441
+ normalizedLocation = url.pathname + url.search + url.hash;
2442
+ redirectLocation = createLocation(state.location, normalizedLocation, {
2443
+ _isRedirect: true
2444
+ });
2445
+ }
2446
+ }
2420
2447
  if (isBrowser) {
2421
2448
  let isDocumentReload = false;
2422
- if (redirect.reloadDocument) {
2449
+ if (redirect.response.headers.has("X-Remix-Reload-Document")) {
2423
2450
  // Hard reload if the response contained X-Remix-Reload-Document
2424
2451
  isDocumentReload = true;
2425
- } else if (ABSOLUTE_URL_REGEX.test(redirect.location)) {
2426
- const url = init.history.createURL(redirect.location);
2452
+ } else if (ABSOLUTE_URL_REGEX.test(location)) {
2453
+ const url = init.history.createURL(location);
2427
2454
  isDocumentReload =
2428
2455
  // Hard reload if it's an absolute URL to a new origin
2429
2456
  url.origin !== routerWindow.location.origin ||
@@ -2432,9 +2459,9 @@ function createRouter(init) {
2432
2459
  }
2433
2460
  if (isDocumentReload) {
2434
2461
  if (replace) {
2435
- routerWindow.location.replace(redirect.location);
2462
+ routerWindow.location.replace(location);
2436
2463
  } else {
2437
- routerWindow.location.assign(redirect.location);
2464
+ routerWindow.location.assign(location);
2438
2465
  }
2439
2466
  return;
2440
2467
  }
@@ -2457,10 +2484,10 @@ function createRouter(init) {
2457
2484
  // re-submit the GET/POST/PUT/PATCH/DELETE as a submission navigation to the
2458
2485
  // redirected location
2459
2486
  let activeSubmission = submission || fetcherSubmission;
2460
- if (redirectPreserveMethodStatusCodes.has(redirect.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
2487
+ if (redirectPreserveMethodStatusCodes.has(redirect.response.status) && activeSubmission && isMutationMethod(activeSubmission.formMethod)) {
2461
2488
  await startNavigation(redirectHistoryAction, redirectLocation, {
2462
2489
  submission: _extends({}, activeSubmission, {
2463
- formAction: redirect.location
2490
+ formAction: location
2464
2491
  }),
2465
2492
  // Preserve this flag across redirects
2466
2493
  preventScrollReset: pendingPreventScrollReset
@@ -2478,16 +2505,35 @@ function createRouter(init) {
2478
2505
  });
2479
2506
  }
2480
2507
  }
2481
- async function loadDataAndMaybeResolveDeferred(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
2482
- let [loaderResults, ...fetcherResults] = await Promise.all([matchesToLoad.length ? dataStrategy({
2483
- matches: matchesToLoad.map(m => finesseToAgnosticDataStrategyMatch(m, mapRouteProperties, manifest)),
2484
- request,
2485
- type: "loader",
2486
- defaultStrategy(match) {
2487
- return callLoaderOrActionImplementation("loader", request, match, matches, basename, future.v7_relativeSplatPath);
2488
- }
2489
- }) : [], ...fetchersToLoad.map(f => {
2490
- if (!f.matches || !f.match || !f.controller) {
2508
+ // Utility wrapper for calling dataStrategy client-side without having to
2509
+ // pass around the manifest, mapRouteProperties, etc.
2510
+ async function callDataStrategy(type, request, matchesToLoad, matches) {
2511
+ try {
2512
+ let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties);
2513
+ return await Promise.all(results.map((result, i) => {
2514
+ if (isRedirectHandlerResult(result)) {
2515
+ return {
2516
+ type: ResultType.redirect,
2517
+ response: normalizeRelativeRoutingRedirectResponse(result.result, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath)
2518
+ };
2519
+ }
2520
+ return convertHandlerResultToDataResult(result);
2521
+ }));
2522
+ } catch (e) {
2523
+ // If the outer dataStrategy method throws, just return the error for all
2524
+ // matches - and it'll naturally bubble to the root
2525
+ return matchesToLoad.map(() => ({
2526
+ type: ResultType.error,
2527
+ error: e
2528
+ }));
2529
+ }
2530
+ }
2531
+ async function callLoadersAndMaybeResolveData(currentMatches, matches, matchesToLoad, fetchersToLoad, request) {
2532
+ let [loaderResults, ...fetcherResults] = await Promise.all([matchesToLoad.length ? callDataStrategy("loader", request, matchesToLoad, matches) : [], ...fetchersToLoad.map(f => {
2533
+ if (f.matches && f.match && f.controller) {
2534
+ let fetcherRequest = createClientSideRequest(init.history, f.path, f.controller.signal);
2535
+ return callDataStrategy("loader", fetcherRequest, [f.match], f.matches).then(r => r[0]);
2536
+ } else {
2491
2537
  return Promise.resolve({
2492
2538
  type: ResultType.error,
2493
2539
  error: getInternalRouterError(404, {
@@ -2495,16 +2541,6 @@ function createRouter(init) {
2495
2541
  })
2496
2542
  });
2497
2543
  }
2498
- return dataStrategy({
2499
- matches: [finesseToAgnosticDataStrategyMatch(f.match, mapRouteProperties, manifest)],
2500
- request,
2501
- type: "loader",
2502
- defaultStrategy(match) {
2503
- invariant(f.controller, "Expected controller for fetcher in defaultStrategy");
2504
- invariant(f.matches, "Expected matches for fetcher in defaultStrategy");
2505
- return callLoaderOrActionImplementation("loader", createClientSideRequest(init.history, f.path, f.controller.signal), match, f.matches, basename, future.v7_relativeSplatPath);
2506
- }
2507
- }).then(r => r[0]);
2508
2544
  })]);
2509
2545
  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)]);
2510
2546
  return {
@@ -2803,10 +2839,9 @@ function createRouter(init) {
2803
2839
  const UNSAFE_DEFERRED_SYMBOL = Symbol("deferred");
2804
2840
  function createStaticHandler(routes, opts) {
2805
2841
  invariant(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
2806
- const dataStrategy = (opts == null ? void 0 : opts.dataStrategy) || defaultDataStrategy;
2807
- const callLoaderOrAction = createCallLoaderOrAction(dataStrategy);
2808
2842
  let manifest = {};
2809
2843
  let basename = (opts ? opts.basename : null) || "/";
2844
+ let dataStrategyImpl = (opts == null ? void 0 : opts.unstable_dataStrategy) || defaultDataStrategy;
2810
2845
  let mapRouteProperties;
2811
2846
  if (opts != null && opts.mapRouteProperties) {
2812
2847
  mapRouteProperties = opts.mapRouteProperties;
@@ -2821,7 +2856,8 @@ function createStaticHandler(routes, opts) {
2821
2856
  }
2822
2857
  // Config driven behavior flags
2823
2858
  let future = _extends({
2824
- v7_relativeSplatPath: false
2859
+ v7_relativeSplatPath: false,
2860
+ v7_throwAbortReason: false
2825
2861
  }, opts ? opts.future : null);
2826
2862
  let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, undefined, manifest);
2827
2863
  /**
@@ -2999,14 +3035,14 @@ function createStaticHandler(routes, opts) {
2999
3035
  actionHeaders: {}
3000
3036
  });
3001
3037
  } catch (e) {
3002
- // If the user threw/returned a Response in callLoaderOrAction, we throw
3003
- // it to bail out and then return or throw here based on whether the user
3004
- // returned or threw
3005
- if (isQueryRouteResponse(e)) {
3038
+ // If the user threw/returned a Response in callLoaderOrAction for a
3039
+ // `queryRoute` call, we throw the `HandlerResult` to bail out early
3040
+ // and then return or throw the raw Response here accordingly
3041
+ if (isHandlerResult(e) && isResponse(e.result)) {
3006
3042
  if (e.type === ResultType.error) {
3007
- throw e.response;
3043
+ throw e.result;
3008
3044
  }
3009
- return e.response;
3045
+ return e.result;
3010
3046
  }
3011
3047
  // Redirects are always returned since they don't propagate to catch
3012
3048
  // boundaries
@@ -3032,14 +3068,10 @@ function createStaticHandler(routes, opts) {
3032
3068
  error
3033
3069
  };
3034
3070
  } else {
3035
- result = await callLoaderOrAction("action", request, actionMatch, matches, manifest, mapRouteProperties, basename, future.v7_relativeSplatPath, {
3036
- isStaticRequest: true,
3037
- isRouteRequest,
3038
- requestContext
3039
- });
3071
+ let results = await callDataStrategy("action", request, [actionMatch], matches, isRouteRequest, requestContext);
3072
+ result = results[0];
3040
3073
  if (request.signal.aborted) {
3041
- let method = isRouteRequest ? "queryRoute" : "query";
3042
- throw new Error(method + "() call aborted: " + request.method + " " + request.url);
3074
+ throwStaticHandlerAbortedError(request, isRouteRequest, future);
3043
3075
  }
3044
3076
  }
3045
3077
  if (isRedirectResult(result)) {
@@ -3048,9 +3080,9 @@ function createStaticHandler(routes, opts) {
3048
3080
  // can get back on the "throw all redirect responses" train here should
3049
3081
  // this ever happen :/
3050
3082
  throw new Response(null, {
3051
- status: result.status,
3083
+ status: result.response.status,
3052
3084
  headers: {
3053
- Location: result.location
3085
+ Location: result.response.headers.get("Location")
3054
3086
  }
3055
3087
  });
3056
3088
  }
@@ -3098,8 +3130,8 @@ function createStaticHandler(routes, opts) {
3098
3130
  return _extends({}, context, {
3099
3131
  statusCode: isRouteErrorResponse(result.error) ? result.error.status : 500,
3100
3132
  actionData: null,
3101
- actionHeaders: _extends({}, result.headers ? {
3102
- [actionMatch.route.id]: result.headers
3133
+ actionHeaders: _extends({}, result.response ? {
3134
+ [actionMatch.route.id]: result.response.headers
3103
3135
  } : {})
3104
3136
  });
3105
3137
  }
@@ -3110,15 +3142,17 @@ function createStaticHandler(routes, opts) {
3110
3142
  signal: request.signal
3111
3143
  });
3112
3144
  let context = await loadRouteData(loaderRequest, matches, requestContext);
3113
- return _extends({}, context, result.statusCode ? {
3114
- statusCode: result.statusCode
3115
- } : {}, {
3145
+ return _extends({}, context, {
3116
3146
  actionData: {
3117
3147
  [actionMatch.route.id]: result.data
3118
- },
3119
- actionHeaders: _extends({}, result.headers ? {
3120
- [actionMatch.route.id]: result.headers
3121
- } : {})
3148
+ }
3149
+ }, result.response ? {
3150
+ statusCode: result.response.status,
3151
+ actionHeaders: {
3152
+ [actionMatch.route.id]: result.response.headers
3153
+ }
3154
+ } : {
3155
+ actionHeaders: {}
3122
3156
  });
3123
3157
  }
3124
3158
  async function loadRouteData(request, matches, requestContext, routeMatch, pendingActionError) {
@@ -3147,21 +3181,9 @@ function createStaticHandler(routes, opts) {
3147
3181
  activeDeferreds: null
3148
3182
  };
3149
3183
  }
3150
- let results = await dataStrategy({
3151
- matches: matchesToLoad.map(m => finesseToAgnosticDataStrategyMatch(m, mapRouteProperties, manifest)),
3152
- request,
3153
- type: "loader",
3154
- defaultStrategy(match) {
3155
- return callLoaderOrActionImplementation("loader", request, match, matches, basename, future.v7_relativeSplatPath, {
3156
- isStaticRequest: true,
3157
- isRouteRequest,
3158
- requestContext
3159
- });
3160
- }
3161
- });
3184
+ let results = await callDataStrategy("loader", request, matchesToLoad, matches, isRouteRequest, requestContext);
3162
3185
  if (request.signal.aborted) {
3163
- let method = isRouteRequest ? "queryRoute" : "query";
3164
- throw new Error(method + "() call aborted: " + request.method + " " + request.url);
3186
+ throwStaticHandlerAbortedError(request, isRouteRequest, future);
3165
3187
  }
3166
3188
  // Process and commit output from loaders
3167
3189
  let activeDeferreds = new Map();
@@ -3178,6 +3200,24 @@ function createStaticHandler(routes, opts) {
3178
3200
  activeDeferreds: activeDeferreds.size > 0 ? Object.fromEntries(activeDeferreds.entries()) : null
3179
3201
  });
3180
3202
  }
3203
+ // Utility wrapper for calling dataStrategy server-side without having to
3204
+ // pass around the manifest, mapRouteProperties, etc.
3205
+ async function callDataStrategy(type, request, matchesToLoad, matches, isRouteRequest, requestContext) {
3206
+ let results = await callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext);
3207
+ return await Promise.all(results.map((result, i) => {
3208
+ if (isRedirectHandlerResult(result)) {
3209
+ // Throw redirects and let the server handle them with an HTTP redirect
3210
+ throw normalizeRelativeRoutingRedirectResponse(result.result, request, matchesToLoad[i].route.id, matches, basename, future.v7_relativeSplatPath);
3211
+ }
3212
+ if (isResponse(result.result) && isRouteRequest) {
3213
+ // For SSR single-route requests, we want to hand Responses back directly
3214
+ // without unwrapping. We do this with the QueryRouteResponse wrapper
3215
+ // interface so we can know whether it was returned or thrown
3216
+ throw result;
3217
+ }
3218
+ return convertHandlerResultToDataResult(result);
3219
+ }));
3220
+ }
3181
3221
  return {
3182
3222
  dataRoutes,
3183
3223
  query,
@@ -3188,26 +3228,26 @@ function createStaticHandler(routes, opts) {
3188
3228
  ////////////////////////////////////////////////////////////////////////////////
3189
3229
  //#region Helpers
3190
3230
  ////////////////////////////////////////////////////////////////////////////////
3191
- function defaultDataStrategy(_ref3) {
3192
- let {
3193
- defaultStrategy,
3194
- matches
3195
- } = _ref3;
3196
- return Promise.all(matches.map(match => defaultStrategy(match)));
3197
- }
3198
3231
  /**
3199
3232
  * Given an existing StaticHandlerContext and an error thrown at render time,
3200
3233
  * provide an updated StaticHandlerContext suitable for a second SSR render
3201
3234
  */
3202
3235
  function getStaticContextFromError(routes, context, error) {
3203
3236
  let newContext = _extends({}, context, {
3204
- statusCode: 500,
3237
+ statusCode: isRouteErrorResponse(error) ? error.status : 500,
3205
3238
  errors: {
3206
3239
  [context._deepestRenderedBoundaryId || routes[0].id]: error
3207
3240
  }
3208
3241
  });
3209
3242
  return newContext;
3210
3243
  }
3244
+ function throwStaticHandlerAbortedError(request, isRouteRequest, future) {
3245
+ if (future.v7_throwAbortReason && request.signal.reason !== undefined) {
3246
+ throw request.signal.reason;
3247
+ }
3248
+ let method = isRouteRequest ? "queryRoute" : "query";
3249
+ throw new Error(method + "() call aborted: " + request.method + " " + request.url);
3250
+ }
3211
3251
  function isSubmissionNavigation(opts) {
3212
3252
  return opts != null && ("formData" in opts && opts.formData != null || "body" in opts && opts.body !== undefined);
3213
3253
  }
@@ -3286,8 +3326,8 @@ function normalizeNavigateOptions(normalizeFormMethod, isFetcher, path, opts) {
3286
3326
  }
3287
3327
  let text = typeof opts.body === "string" ? opts.body : opts.body instanceof FormData || opts.body instanceof URLSearchParams ?
3288
3328
  // https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#plain-text-form-data
3289
- Array.from(opts.body.entries()).reduce((acc, _ref4) => {
3290
- let [name, value] = _ref4;
3329
+ Array.from(opts.body.entries()).reduce((acc, _ref3) => {
3330
+ let [name, value] = _ref3;
3291
3331
  return "" + acc + name + "=" + value + "\n";
3292
3332
  }, "") : String(opts.body);
3293
3333
  return {
@@ -3395,18 +3435,24 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3395
3435
  let boundaryId = pendingError ? Object.keys(pendingError)[0] : undefined;
3396
3436
  let boundaryMatches = getLoaderMatchesUntilBoundary(matches, boundaryId);
3397
3437
  let navigationMatches = boundaryMatches.filter((match, index) => {
3398
- if (isInitialLoad) {
3399
- // On initial hydration we don't do any shouldRevalidate stuff - we just
3400
- // call the unhydrated loaders
3401
- return isUnhydratedRoute(state, match.route);
3402
- }
3403
- if (match.route.lazy) {
3438
+ let {
3439
+ route
3440
+ } = match;
3441
+ if (route.lazy) {
3404
3442
  // We haven't loaded this route yet so we don't know if it's got a loader!
3405
3443
  return true;
3406
3444
  }
3407
- if (match.route.loader == null) {
3445
+ if (route.loader == null) {
3408
3446
  return false;
3409
3447
  }
3448
+ if (isInitialLoad) {
3449
+ if (route.loader.hydrate) {
3450
+ return true;
3451
+ }
3452
+ return state.loaderData[route.id] === undefined && (
3453
+ // Don't re-run if the loader ran and threw an error
3454
+ !state.errors || state.errors[route.id] === undefined);
3455
+ }
3410
3456
  // Always call the loader on new route instances and pending defer cancellations
3411
3457
  if (isNewLoader(state.loaderData, state.matches[index], match) || cancelledDeferredRoutes.some(id => id === match.route.id)) {
3412
3458
  return true;
@@ -3503,19 +3549,6 @@ function getMatchesToLoad(history, state, matches, submission, location, isIniti
3503
3549
  });
3504
3550
  return [navigationMatches, revalidatingFetchers];
3505
3551
  }
3506
- // Is this route unhydrated (when v7_partialHydration=true) such that we need
3507
- // to call it's loader on the initial router creation
3508
- function isUnhydratedRoute(state, route) {
3509
- if (!route.loader) {
3510
- return false;
3511
- }
3512
- if (route.loader.hydrate) {
3513
- return true;
3514
- }
3515
- return state.loaderData[route.id] === undefined && (!state.errors ||
3516
- // Loader ran but errored - don't re-run
3517
- state.errors[route.id] === undefined);
3518
- }
3519
3552
  function isNewLoader(currentLoaderData, currentMatch, match) {
3520
3553
  let isNew =
3521
3554
  // [a] -> [a, b]
@@ -3596,44 +3629,52 @@ async function loadLazyRouteModule(route, mapRouteProperties, manifest) {
3596
3629
  }));
3597
3630
  return routeToUpdate;
3598
3631
  }
3599
- function createCallLoaderOrAction(dataStrategy) {
3600
- return async function (type, request, match, matches, manifest, mapRouteProperties, basename, v7_relativeSplatPath, opts) {
3601
- if (opts === void 0) {
3602
- opts = {};
3603
- }
3604
- let [result] = await dataStrategy({
3605
- matches: [finesseToAgnosticDataStrategyMatch(match, mapRouteProperties, manifest)],
3606
- request,
3607
- type,
3608
- defaultStrategy(match) {
3609
- return callLoaderOrActionImplementation(type, request, match, matches, basename, v7_relativeSplatPath, opts);
3610
- }
3611
- });
3612
- return result;
3613
- };
3632
+ // Default implementation of `dataStrategy` which fetches all loaders in parallel
3633
+ function defaultDataStrategy(opts) {
3634
+ return Promise.all(opts.matches.map(m => m.bikeshed_loadRoute()));
3614
3635
  }
3615
- function finesseToAgnosticDataStrategyMatch(match, mapRouteProperties, manifest) {
3616
- let loadRoutePromise;
3617
- if (match.route.lazy) {
3618
- try {
3619
- loadRoutePromise = loadLazyRouteModule(match.route, mapRouteProperties, manifest);
3620
- } catch (error) {
3621
- loadRoutePromise = Promise.reject(error);
3622
- }
3623
- }
3624
- if (!loadRoutePromise) {
3625
- loadRoutePromise = Promise.resolve(match.route);
3626
- }
3627
- loadRoutePromise.catch(() => {});
3628
- return _extends({}, match, {
3629
- route: Object.assign(loadRoutePromise, match.route)
3636
+ async function callDataStrategyImpl(dataStrategyImpl, type, request, matchesToLoad, matches, manifest, mapRouteProperties, requestContext) {
3637
+ let routeIdsToLoad = matchesToLoad.reduce((acc, m) => acc.add(m.route.id), new Set());
3638
+ let loadedMatches = new Set();
3639
+ // Send all matches here to allow for a middleware-type implementation.
3640
+ // handler will be a no-op for unneeded routes and we filter those results
3641
+ // back out below.
3642
+ let results = await dataStrategyImpl({
3643
+ matches: matches.map(match => {
3644
+ // `bikeshed_loadRoute` encapsulates the route.lazy, executing the
3645
+ // loader/action, and mapping return values/thrown errors to a
3646
+ // HandlerResult. Users can pass a callback to take fine-grained control
3647
+ // over the execution of the loader/action
3648
+ let bikeshed_loadRoute = bikeshed_handlerOverride => {
3649
+ loadedMatches.add(match.route.id);
3650
+ return routeIdsToLoad.has(match.route.id) ? callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, requestContext) :
3651
+ // TODO: What's the best thing to do here - return an empty "success" result?
3652
+ // Or return a success result with the current route loader/action data?
3653
+ // We strip these results out if the route didn't need to be revalidated in
3654
+ // `callDataStrategy` so it doesn't matter for us. It's more of a question
3655
+ // of whether exposing the current data to the user is useful?
3656
+ Promise.resolve({
3657
+ type: ResultType.data,
3658
+ result: undefined
3659
+ });
3660
+ };
3661
+ return _extends({}, match, {
3662
+ bikeshed_loadRoute
3663
+ });
3664
+ }),
3665
+ request,
3666
+ params: matches[0].params
3630
3667
  });
3668
+ // Throw if any loadRoute implementations not called since they are what
3669
+ // ensures a route is fully loaded
3670
+ // Throw if any loadRoute implementations not called since they are what
3671
+ // ensure a route is fully loaded
3672
+ 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."));
3673
+ // Filter out any middleware-only matches for which we didn't need to run handlers
3674
+ return results.filter((_, i) => routeIdsToLoad.has(matches[i].route.id));
3631
3675
  }
3632
- async function callLoaderOrActionImplementation(type, request, match, matches, basename, v7_relativeSplatPath, opts) {
3633
- if (opts === void 0) {
3634
- opts = {};
3635
- }
3636
- let resultType;
3676
+ // Default logic for calling a loader/action is the user has no specified a dataStrategy
3677
+ async function callLoaderOrAction(type, request, match, manifest, mapRouteProperties, bikeshed_handlerOverride, staticContext) {
3637
3678
  let result;
3638
3679
  let onReject;
3639
3680
  let runHandler = handler => {
@@ -3642,11 +3683,13 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
3642
3683
  let abortPromise = new Promise((_, r) => reject = r);
3643
3684
  onReject = () => reject();
3644
3685
  request.signal.addEventListener("abort", onReject);
3645
- return Promise.race([handler({
3686
+ let handlerArg = {
3646
3687
  request,
3647
3688
  params: match.params,
3648
- context: opts.requestContext
3649
- }), abortPromise]);
3689
+ context: staticContext
3690
+ };
3691
+ let handlerPromise = bikeshed_handlerOverride ? bikeshed_handlerOverride(async ctx => ctx !== undefined ? handler(handlerArg, ctx) : handler(handlerArg)) : handler(handlerArg);
3692
+ return Promise.race([handlerPromise, abortPromise]);
3650
3693
  };
3651
3694
  try {
3652
3695
  let handler = match.route[type];
@@ -3660,17 +3703,17 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
3660
3703
  // route has a boundary that can handle the error
3661
3704
  runHandler(handler).catch(e => {
3662
3705
  handlerError = e;
3663
- }), match.route]);
3706
+ }), loadLazyRouteModule(match.route, mapRouteProperties, manifest)]);
3664
3707
  if (handlerError) {
3665
3708
  throw handlerError;
3666
3709
  }
3667
3710
  result = values[0];
3668
3711
  } else {
3669
3712
  // Load lazy route module, then run any returned handler
3670
- let route = await match.route;
3671
- handler = route[type];
3713
+ await loadLazyRouteModule(match.route, mapRouteProperties, manifest);
3714
+ handler = match.route[type];
3672
3715
  if (handler) {
3673
- // Handler still run even if we got interrupted to maintain consistency
3716
+ // Handler still runs even if we got interrupted to maintain consistency
3674
3717
  // with un-abortable behavior of handler execution on non-lazy or
3675
3718
  // previously-lazy-loaded routes
3676
3719
  result = await runHandler(handler);
@@ -3687,7 +3730,7 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
3687
3730
  // hit the invariant below that errors on returning undefined.
3688
3731
  return {
3689
3732
  type: ResultType.data,
3690
- data: undefined
3733
+ result: undefined
3691
3734
  };
3692
3735
  }
3693
3736
  }
@@ -3702,66 +3745,37 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
3702
3745
  }
3703
3746
  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`.");
3704
3747
  } catch (e) {
3705
- resultType = ResultType.error;
3706
- result = e;
3748
+ return {
3749
+ type: ResultType.error,
3750
+ result: e
3751
+ };
3707
3752
  } finally {
3708
3753
  if (onReject) {
3709
3754
  request.signal.removeEventListener("abort", onReject);
3710
3755
  }
3711
3756
  }
3757
+ return {
3758
+ type: ResultType.data,
3759
+ result
3760
+ };
3761
+ }
3762
+ async function convertHandlerResultToDataResult(handlerResult) {
3763
+ let {
3764
+ result,
3765
+ type
3766
+ } = handlerResult;
3712
3767
  if (isResponse(result)) {
3713
- let status = result.status;
3714
- // Process redirects
3715
- if (redirectStatusCodes.has(status)) {
3716
- let location = result.headers.get("Location");
3717
- invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
3718
- // Support relative routing in internal redirects
3719
- if (!ABSOLUTE_URL_REGEX.test(location)) {
3720
- location = normalizeTo(new URL(request.url), matches.slice(0, matches.findIndex(m => m.route.id === match.route.id) + 1), basename, true, location, v7_relativeSplatPath);
3721
- } else if (!opts.isStaticRequest) {
3722
- // Strip off the protocol+origin for same-origin + same-basename absolute
3723
- // redirects. If this is a static request, we can let it go back to the
3724
- // browser as-is
3725
- let currentUrl = new URL(request.url);
3726
- let url = location.startsWith("//") ? new URL(currentUrl.protocol + location) : new URL(location);
3727
- let isSameBasename = stripBasename(url.pathname, basename) != null;
3728
- if (url.origin === currentUrl.origin && isSameBasename) {
3729
- location = url.pathname + url.search + url.hash;
3730
- }
3731
- }
3732
- // Don't process redirects in the router during static requests requests.
3733
- // Instead, throw the Response and let the server handle it with an HTTP
3734
- // redirect. We also update the Location header in place in this flow so
3735
- // basename and relative routing is taken into account
3736
- if (opts.isStaticRequest) {
3737
- result.headers.set("Location", location);
3738
- throw result;
3739
- }
3740
- return {
3741
- type: ResultType.redirect,
3742
- status,
3743
- location,
3744
- revalidate: result.headers.get("X-Remix-Revalidate") !== null,
3745
- reloadDocument: result.headers.get("X-Remix-Reload-Document") !== null
3746
- };
3747
- }
3748
- // For SSR single-route requests, we want to hand Responses back directly
3749
- // without unwrapping. We do this with the QueryRouteResponse wrapper
3750
- // interface so we can know whether it was returned or thrown
3751
- if (opts.isRouteRequest) {
3752
- let queryRouteResponse = {
3753
- type: resultType === ResultType.error ? ResultType.error : ResultType.data,
3754
- response: result
3755
- };
3756
- throw queryRouteResponse;
3757
- }
3758
3768
  let data;
3759
3769
  try {
3760
3770
  let contentType = result.headers.get("Content-Type");
3761
3771
  // Check between word boundaries instead of startsWith() due to the last
3762
3772
  // paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
3763
3773
  if (contentType && /\bapplication\/json\b/.test(contentType)) {
3764
- data = await result.json();
3774
+ if (result.body == null) {
3775
+ data = null;
3776
+ } else {
3777
+ data = await result.json();
3778
+ }
3765
3779
  } else {
3766
3780
  data = await result.text();
3767
3781
  }
@@ -3771,23 +3785,22 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
3771
3785
  error: e
3772
3786
  };
3773
3787
  }
3774
- if (resultType === ResultType.error) {
3788
+ if (type === ResultType.error) {
3775
3789
  return {
3776
- type: resultType,
3777
- error: new ErrorResponseImpl(status, result.statusText, data),
3778
- headers: result.headers
3790
+ type,
3791
+ error: new ErrorResponseImpl(result.status, result.statusText, data),
3792
+ response: result
3779
3793
  };
3780
3794
  }
3781
3795
  return {
3782
3796
  type: ResultType.data,
3783
3797
  data,
3784
- statusCode: result.status,
3785
- headers: result.headers
3798
+ response: result
3786
3799
  };
3787
3800
  }
3788
- if (resultType === ResultType.error) {
3801
+ if (type === ResultType.error) {
3789
3802
  return {
3790
- type: resultType,
3803
+ type,
3791
3804
  error: result
3792
3805
  };
3793
3806
  }
@@ -3805,6 +3818,17 @@ async function callLoaderOrActionImplementation(type, request, match, matches, b
3805
3818
  data: result
3806
3819
  };
3807
3820
  }
3821
+ // Support relative routing in internal redirects
3822
+ function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename, v7_relativeSplatPath) {
3823
+ let location = response.headers.get("Location");
3824
+ invariant(location, "Redirects returned/thrown from loaders/actions must have a Location header");
3825
+ if (!ABSOLUTE_URL_REGEX.test(location)) {
3826
+ let trimmedMatches = matches.slice(0, matches.findIndex(m => m.route.id === routeId) + 1);
3827
+ location = normalizeTo(new URL(request.url), trimmedMatches, basename, true, location, v7_relativeSplatPath);
3828
+ response.headers.set("Location", location);
3829
+ }
3830
+ return response;
3831
+ }
3808
3832
  // Utility method for creating the Request instances for loaders/actions during
3809
3833
  // client-side navigations and fetches. During SSR we will always have a
3810
3834
  // Request instance from the static handler (query/queryRoute)
@@ -3891,23 +3915,31 @@ function processRouteLoaderData(matches, matchesToLoad, results, pendingError, a
3891
3915
  foundError = true;
3892
3916
  statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
3893
3917
  }
3894
- if (result.headers) {
3895
- loaderHeaders[id] = result.headers;
3918
+ if (result.response) {
3919
+ loaderHeaders[id] = result.response.headers;
3896
3920
  }
3897
3921
  } else {
3898
3922
  if (isDeferredResult(result)) {
3899
3923
  activeDeferreds.set(id, result.deferredData);
3900
3924
  loaderData[id] = result.deferredData.data;
3925
+ // Error status codes always override success status codes, but if all
3926
+ // loaders are successful we take the deepest status code.
3927
+ if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
3928
+ statusCode = result.statusCode;
3929
+ }
3930
+ if (result.headers) {
3931
+ loaderHeaders[id] = result.headers;
3932
+ }
3901
3933
  } else {
3902
3934
  loaderData[id] = result.data;
3903
- }
3904
- // Error status codes always override success status codes, but if all
3905
- // loaders are successful we take the deepest status code.
3906
- if (result.statusCode != null && result.statusCode !== 200 && !foundError) {
3907
- statusCode = result.statusCode;
3908
- }
3909
- if (result.headers) {
3910
- loaderHeaders[id] = result.headers;
3935
+ // Error status codes always override success status codes, but if all
3936
+ // loaders are successful we take the deepest status code.
3937
+ if (result.response) {
3938
+ if (result.response.status !== 200 && !foundError) {
3939
+ statusCode = result.response.status;
3940
+ }
3941
+ loaderHeaders[id] = result.response.headers;
3942
+ }
3911
3943
  }
3912
3944
  }
3913
3945
  });
@@ -4081,6 +4113,12 @@ function isHashChangeOnly(a, b) {
4081
4113
  // /page#hash -> /page
4082
4114
  return false;
4083
4115
  }
4116
+ function isHandlerResult(result) {
4117
+ return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === ResultType.data || result.type === ResultType.error);
4118
+ }
4119
+ function isRedirectHandlerResult(result) {
4120
+ return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
4121
+ }
4084
4122
  function isDeferredResult(result) {
4085
4123
  return result.type === ResultType.deferred;
4086
4124
  }
@@ -4105,9 +4143,6 @@ function isRedirectResponse(result) {
4105
4143
  let location = result.headers.get("Location");
4106
4144
  return status >= 300 && status <= 399 && location != null;
4107
4145
  }
4108
- function isQueryRouteResponse(obj) {
4109
- return obj && isResponse(obj.response) && (obj.type === ResultType.data || obj.type === ResultType.error);
4110
- }
4111
4146
  function isValidMethod(method) {
4112
4147
  return validRequestMethods.has(method.toLowerCase());
4113
4148
  }
@@ -4345,5 +4380,5 @@ function persistAppliedTransitions(_window, transitions) {
4345
4380
  }
4346
4381
  //#endregion
4347
4382
 
4348
- export { AbortedDeferredError, Action, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, ResultType, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getResolveToMatches as UNSAFE_getResolveToMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isDeferredData, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, redirectDocument, resolvePath, resolveTo, stripBasename };
4383
+ export { AbortedDeferredError, Action, IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, UNSAFE_DEFERRED_SYMBOL, DeferredData as UNSAFE_DeferredData, ErrorResponseImpl as UNSAFE_ErrorResponseImpl, convertRouteMatchToUiMatch as UNSAFE_convertRouteMatchToUiMatch, convertRoutesToDataRoutes as UNSAFE_convertRoutesToDataRoutes, getResolveToMatches as UNSAFE_getResolveToMatches, invariant as UNSAFE_invariant, warning as UNSAFE_warning, createBrowserHistory, createHashHistory, createMemoryHistory, createPath, createRouter, createStaticHandler, defer, generatePath, getStaticContextFromError, getToPathname, isDeferredData, isRouteErrorResponse, joinPaths, json, matchPath, matchRoutes, normalizePathname, parsePath, redirect, redirectDocument, resolvePath, resolveTo, stripBasename };
4349
4384
  //# sourceMappingURL=router.js.map