react-router 7.11.0 → 7.12.0

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.
Files changed (68) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/dist/development/{browser-Cv4JZyZ5.d.mts → browser-BEPxnEBW.d.mts} +4 -2
  3. package/dist/{production/browser-Cv4JZyZ5.d.mts → development/browser-CJ9_du-U.d.ts} +4 -2
  4. package/dist/development/{chunk-SZQUWNVJ.js → chunk-2BEI23B2.js} +117 -35
  5. package/dist/development/{chunk-2JY4UAJA.js → chunk-7ZCBDOCZ.js} +98 -108
  6. package/dist/development/{chunk-JMJ3UQ3L.mjs → chunk-EPOLDU6W.mjs} +122 -50
  7. package/dist/development/{chunk-YNUBSHFH.mjs → chunk-FNSCYPCZ.mjs} +111 -11
  8. package/dist/{production/chunk-BEXOWXJO.js → development/chunk-G2I2SRFA.js} +7 -7
  9. package/dist/development/dom-export.d.mts +2 -2
  10. package/dist/development/dom-export.d.ts +2 -2
  11. package/dist/development/dom-export.js +35 -30
  12. package/dist/development/dom-export.mjs +12 -7
  13. package/dist/development/{index-react-server-client-P7VgYu6T.d.mts → index-react-server-client-IoJGLOqV.d.mts} +3 -1
  14. package/dist/{production/index-react-server-client-Cv5Q9lf0.d.ts → development/index-react-server-client-gGyf-7Xp.d.ts} +3 -1
  15. package/dist/development/index-react-server-client.d.mts +2 -2
  16. package/dist/development/index-react-server-client.d.ts +2 -2
  17. package/dist/development/index-react-server-client.js +4 -4
  18. package/dist/development/index-react-server-client.mjs +2 -2
  19. package/dist/development/index-react-server.d.mts +3 -1
  20. package/dist/development/index-react-server.d.ts +3 -1
  21. package/dist/development/index-react-server.js +92 -7
  22. package/dist/development/index-react-server.mjs +92 -7
  23. package/dist/development/index.d.mts +9 -9
  24. package/dist/development/index.d.ts +9 -9
  25. package/dist/development/index.js +205 -101
  26. package/dist/development/index.mjs +7 -3
  27. package/dist/{production/instrumentation-BlrVzjbg.d.ts → development/instrumentation-DvHY1sgY.d.ts} +45 -1
  28. package/dist/development/lib/types/internal.d.mts +2 -2
  29. package/dist/development/lib/types/internal.d.ts +2 -2
  30. package/dist/development/lib/types/internal.js +1 -1
  31. package/dist/development/lib/types/internal.mjs +1 -1
  32. package/dist/development/{register-BGQUMCK4.d.ts → register-Bm80E9qL.d.ts} +1 -1
  33. package/dist/development/{register-DTJJbt1o.d.mts → register-CS_tiXsm.d.mts} +1 -1
  34. package/dist/development/{router-5fbeEIMQ.d.mts → router-5iOvts3c.d.mts} +45 -1
  35. package/dist/{development/browser-o-qhcuhA.d.ts → production/browser-BEPxnEBW.d.mts} +4 -2
  36. package/dist/production/{browser-o-qhcuhA.d.ts → browser-CJ9_du-U.d.ts} +4 -2
  37. package/dist/{development/chunk-GNDLROV6.js → production/chunk-337Z4Y65.js} +7 -7
  38. package/dist/production/{chunk-TINMVEA2.mjs → chunk-DZM3VO5F.mjs} +122 -50
  39. package/dist/production/{chunk-E6GYEQUT.mjs → chunk-QCD7HIN2.mjs} +111 -11
  40. package/dist/production/{chunk-ZMYPVUNZ.js → chunk-Y62AB6TB.js} +98 -108
  41. package/dist/production/{chunk-2HFJAX7U.js → chunk-Z47B263N.js} +117 -35
  42. package/dist/production/dom-export.d.mts +2 -2
  43. package/dist/production/dom-export.d.ts +2 -2
  44. package/dist/production/dom-export.js +35 -30
  45. package/dist/production/dom-export.mjs +12 -7
  46. package/dist/production/{index-react-server-client-P7VgYu6T.d.mts → index-react-server-client-IoJGLOqV.d.mts} +3 -1
  47. package/dist/{development/index-react-server-client-Cv5Q9lf0.d.ts → production/index-react-server-client-gGyf-7Xp.d.ts} +3 -1
  48. package/dist/production/index-react-server-client.d.mts +2 -2
  49. package/dist/production/index-react-server-client.d.ts +2 -2
  50. package/dist/production/index-react-server-client.js +4 -4
  51. package/dist/production/index-react-server-client.mjs +2 -2
  52. package/dist/production/index-react-server.d.mts +3 -1
  53. package/dist/production/index-react-server.d.ts +3 -1
  54. package/dist/production/index-react-server.js +92 -7
  55. package/dist/production/index-react-server.mjs +92 -7
  56. package/dist/production/index.d.mts +9 -9
  57. package/dist/production/index.d.ts +9 -9
  58. package/dist/production/index.js +205 -101
  59. package/dist/production/index.mjs +7 -3
  60. package/dist/{development/instrumentation-BlrVzjbg.d.ts → production/instrumentation-DvHY1sgY.d.ts} +45 -1
  61. package/dist/production/lib/types/internal.d.mts +2 -2
  62. package/dist/production/lib/types/internal.d.ts +2 -2
  63. package/dist/production/lib/types/internal.js +1 -1
  64. package/dist/production/lib/types/internal.mjs +1 -1
  65. package/dist/production/{register-BGQUMCK4.d.ts → register-Bm80E9qL.d.ts} +1 -1
  66. package/dist/production/{register-DTJJbt1o.d.mts → register-CS_tiXsm.d.mts} +1 -1
  67. package/dist/production/{router-5fbeEIMQ.d.mts → router-5iOvts3c.d.mts} +45 -1
  68. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # `react-router`
2
2
 
3
+ ## 7.12.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Add additional layer of CSRF protection by rejecting submissions to UI routes from external origins. If you need to permit access to specific external origins, you can specify them in the `react-router.config.ts` config `allowedActionOrigins` field. ([#14708](https://github.com/remix-run/react-router/pull/14708))
8
+
9
+ ### Patch Changes
10
+
11
+ - Fix `generatePath` when used with suffixed params (i.e., "/books/:id.json") ([#14269](https://github.com/remix-run/react-router/pull/14269))
12
+
13
+ - Export `UNSAFE_createMemoryHistory` and `UNSAFE_createHashHistory` alongside `UNSAFE_createBrowserHistory` for consistency. These are not intended to be used for new apps but intended to help apps usiong `unstable_HistoryRouter` migrate from v6->v7 so they can adopt the newer APIs. ([#14663](https://github.com/remix-run/react-router/pull/14663))
14
+
15
+ - Escape HTML in scroll restoration keys ([#14705](https://github.com/remix-run/react-router/pull/14705))
16
+
17
+ - Validate redirect locations ([#14706](https://github.com/remix-run/react-router/pull/14706))
18
+
19
+ - \[UNSTABLE] Pass `<Scripts nonce>` value through to the underlying `importmap` `script` tag when using `future.unstable_subResourceIntegrity` ([#14675](https://github.com/remix-run/react-router/pull/14675))
20
+
21
+ - \[UNSTABLE] Add a new `future.unstable_trailingSlashAwareDataRequests` flag to provide consistent behavior of `request.pathname` inside `middleware`, `loader`, and `action` functions on document and data requests when a trailing slash is present in the browser URL. ([#14644](https://github.com/remix-run/react-router/pull/14644))
22
+
23
+ Currently, your HTTP and `request` pathnames would be as follows for `/a/b/c` and `/a/b/c/`
24
+
25
+ | URL `/a/b/c` | **HTTP pathname** | **`request` pathname\`** |
26
+ | ------------ | ----------------- | ------------------------ |
27
+ | **Document** | `/a/b/c` | `/a/b/c` ✅ |
28
+ | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
29
+
30
+ | URL `/a/b/c/` | **HTTP pathname** | **`request` pathname\`** |
31
+ | ------------- | ----------------- | ------------------------ |
32
+ | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
33
+ | **Data** | `/a/b/c.data` | `/a/b/c` ⚠️ |
34
+
35
+ With this flag enabled, these pathnames will be made consistent though a new `_.data` format for client-side `.data` requests:
36
+
37
+ | URL `/a/b/c` | **HTTP pathname** | **`request` pathname\`** |
38
+ | ------------ | ----------------- | ------------------------ |
39
+ | **Document** | `/a/b/c` | `/a/b/c` ✅ |
40
+ | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
41
+
42
+ | URL `/a/b/c/` | **HTTP pathname** | **`request` pathname\`** |
43
+ | ------------- | ------------------ | ------------------------ |
44
+ | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
45
+ | **Data** | `/a/b/c/_.data` ⬅️ | `/a/b/c/` ✅ |
46
+
47
+ This a bug fix but we are putting it behind an opt-in flag because it has the potential to be a "breaking bug fix" if you are relying on the URL format for any other application or caching logic.
48
+
49
+ Enabling this flag also changes the format of client side `.data` requests from `/_root.data` to `/_.data` when navigating to `/` to align with the new format. This does not impact the `request` pathname which is still `/` in all cases.
50
+
51
+ - Preserve `clientLoader.hydrate=true` when using `<HydratedRouter unstable_instrumentations>` ([#14674](https://github.com/remix-run/react-router/pull/14674))
52
+
3
53
  ## 7.11.0
4
54
 
5
55
  ### Minor Changes
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { L as Location, C as ClientActionFunction, a as ClientLoaderFunction, b as LinksFunction, M as MetaFunction, S as ShouldRevalidateFunction, P as Params, c as RouterContextProvider, A as ActionFunction, H as HeadersFunction, d as LoaderFunction, e as RouterInit } from './router-5fbeEIMQ.mjs';
2
+ import { L as Location, C as ClientActionFunction, a as ClientLoaderFunction, b as LinksFunction, M as MetaFunction, S as ShouldRevalidateFunction, P as Params, c as RouterContextProvider, A as ActionFunction, H as HeadersFunction, d as LoaderFunction, e as RouterInit } from './router-5iOvts3c.mjs';
3
3
 
4
4
  type RSCRouteConfigEntryBase = {
5
5
  action?: ActionFunction;
@@ -138,6 +138,7 @@ type LoadServerActionFunction = (id: string) => Promise<Function>;
138
138
  * @category RSC
139
139
  * @mode data
140
140
  * @param opts Options
141
+ * @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
141
142
  * @param opts.basename The basename to use when matching the request.
142
143
  * @param opts.createTemporaryReferenceSet A function that returns a temporary
143
144
  * reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
@@ -167,7 +168,8 @@ type LoadServerActionFunction = (id: string) => Promise<Function>;
167
168
  * that contains the [RSC](https://react.dev/reference/rsc/server-components)
168
169
  * data for hydration.
169
170
  */
170
- declare function matchRSCServerRequest({ createTemporaryReferenceSet, basename, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
171
+ declare function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
172
+ allowedActionOrigins?: string[];
171
173
  createTemporaryReferenceSet: () => unknown;
172
174
  basename?: string;
173
175
  decodeReply?: DecodeReplyFunction;
@@ -1,5 +1,5 @@
1
1
  import * as React from 'react';
2
- import { L as Location, C as ClientActionFunction, a as ClientLoaderFunction, b as LinksFunction, M as MetaFunction, S as ShouldRevalidateFunction, P as Params, c as RouterContextProvider, A as ActionFunction, H as HeadersFunction, d as LoaderFunction, e as RouterInit } from './router-5fbeEIMQ.mjs';
2
+ import { L as Location, C as ClientActionFunction, a as ClientLoaderFunction, b as LinksFunction, M as MetaFunction, S as ShouldRevalidateFunction, P as Params, c as RouterContextProvider, A as ActionFunction, H as HeadersFunction, d as LoaderFunction, e as RouterInit } from './instrumentation-DvHY1sgY.js';
3
3
 
4
4
  type RSCRouteConfigEntryBase = {
5
5
  action?: ActionFunction;
@@ -138,6 +138,7 @@ type LoadServerActionFunction = (id: string) => Promise<Function>;
138
138
  * @category RSC
139
139
  * @mode data
140
140
  * @param opts Options
141
+ * @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
141
142
  * @param opts.basename The basename to use when matching the request.
142
143
  * @param opts.createTemporaryReferenceSet A function that returns a temporary
143
144
  * reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
@@ -167,7 +168,8 @@ type LoadServerActionFunction = (id: string) => Promise<Function>;
167
168
  * that contains the [RSC](https://react.dev/reference/rsc/server-components)
168
169
  * data for hydration.
169
170
  */
170
- declare function matchRSCServerRequest({ createTemporaryReferenceSet, basename, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
171
+ declare function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
172
+ allowedActionOrigins?: string[];
171
173
  createTemporaryReferenceSet: () => unknown;
172
174
  basename?: string;
173
175
  decodeReply?: DecodeReplyFunction;
@@ -1,5 +1,5 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/**
2
- * react-router v7.11.0
2
+ * react-router v7.12.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -709,12 +709,12 @@ function generatePath(originalPath, params = {}) {
709
709
  const star = "*";
710
710
  return stringify2(params[star]);
711
711
  }
712
- const keyMatch = segment.match(/^:([\w-]+)(\??)$/);
712
+ const keyMatch = segment.match(/^:([\w-]+)(\??)(.*)/);
713
713
  if (keyMatch) {
714
- const [, key, optional] = keyMatch;
714
+ const [, key, optional, suffix] = keyMatch;
715
715
  let param = params[key];
716
716
  invariant(optional === "?" || param != null, `Missing ":${key}" param`);
717
- return encodeURIComponent(stringify2(param));
717
+ return encodeURIComponent(stringify2(param)) + suffix;
718
718
  }
719
719
  return segment.replace(/\?$/g, "");
720
720
  }).filter((segment) => !!segment);
@@ -1071,6 +1071,9 @@ function getRouteInstrumentationUpdates(fns, route) {
1071
1071
  (...args) => getHandlerInfo(args[0])
1072
1072
  );
1073
1073
  if (instrumented) {
1074
+ if (key === "loader" && original.hydrate === true) {
1075
+ instrumented.hydrate = true;
1076
+ }
1074
1077
  instrumented[UninstrumentedSymbol] = original;
1075
1078
  updates[key] = instrumented;
1076
1079
  }
@@ -2014,7 +2017,8 @@ function createRouter(init) {
2014
2017
  let location2 = normalizeRedirectLocation(
2015
2018
  result.response.headers.get("Location"),
2016
2019
  new URL(request.url),
2017
- basename
2020
+ basename,
2021
+ init.history
2018
2022
  );
2019
2023
  replace2 = location2 === state.location.pathname + state.location.search;
2020
2024
  }
@@ -2648,7 +2652,8 @@ function createRouter(init) {
2648
2652
  location = normalizeRedirectLocation(
2649
2653
  location,
2650
2654
  new URL(request.url),
2651
- basename
2655
+ basename,
2656
+ init.history
2652
2657
  );
2653
2658
  let redirectLocation = createLocation(state.location, location, {
2654
2659
  _isRedirect: true
@@ -4907,15 +4912,38 @@ function normalizeRelativeRoutingRedirectResponse(response, request, routeId, ma
4907
4912
  }
4908
4913
  return response;
4909
4914
  }
4910
- function normalizeRedirectLocation(location, currentUrl, basename) {
4915
+ function normalizeRedirectLocation(location, currentUrl, basename, historyInstance) {
4916
+ let invalidProtocols = [
4917
+ "about:",
4918
+ "blob:",
4919
+ "chrome:",
4920
+ "chrome-untrusted:",
4921
+ "content:",
4922
+ "data:",
4923
+ "devtools:",
4924
+ "file:",
4925
+ "filesystem:",
4926
+ // eslint-disable-next-line no-script-url
4927
+ "javascript:"
4928
+ ];
4911
4929
  if (isAbsoluteUrl(location)) {
4912
4930
  let normalizedLocation = location;
4913
4931
  let url = normalizedLocation.startsWith("//") ? new URL(currentUrl.protocol + normalizedLocation) : new URL(normalizedLocation);
4932
+ if (invalidProtocols.includes(url.protocol)) {
4933
+ throw new Error("Invalid redirect location");
4934
+ }
4914
4935
  let isSameBasename = stripBasename(url.pathname, basename) != null;
4915
4936
  if (url.origin === currentUrl.origin && isSameBasename) {
4916
4937
  return url.pathname + url.search + url.hash;
4917
4938
  }
4918
4939
  }
4940
+ try {
4941
+ let url = historyInstance.createURL(location);
4942
+ if (invalidProtocols.includes(url.protocol)) {
4943
+ throw new Error("Invalid redirect location");
4944
+ }
4945
+ } catch (e) {
4946
+ }
4919
4947
  return location;
4920
4948
  }
4921
4949
  function createClientSideRequest(history, location, signal, submission) {
@@ -6187,7 +6215,7 @@ function StreamTransfer({
6187
6215
  )));
6188
6216
  }
6189
6217
  }
6190
- function getTurboStreamSingleFetchDataStrategy(getRouter, manifest, routeModules, ssr, basename) {
6218
+ function getTurboStreamSingleFetchDataStrategy(getRouter, manifest, routeModules, ssr, basename, trailingSlashAware) {
6191
6219
  let dataStrategy = getSingleFetchDataStrategyImpl(
6192
6220
  getRouter,
6193
6221
  (match) => {
@@ -6202,26 +6230,43 @@ function getTurboStreamSingleFetchDataStrategy(getRouter, manifest, routeModules
6202
6230
  },
6203
6231
  fetchAndDecodeViaTurboStream,
6204
6232
  ssr,
6205
- basename
6233
+ basename,
6234
+ trailingSlashAware
6206
6235
  );
6207
6236
  return async (args) => args.runClientMiddleware(dataStrategy);
6208
6237
  }
6209
- function getSingleFetchDataStrategyImpl(getRouter, getRouteInfo, fetchAndDecode, ssr, basename, shouldAllowOptOut = () => true) {
6238
+ function getSingleFetchDataStrategyImpl(getRouter, getRouteInfo, fetchAndDecode, ssr, basename, trailingSlashAware, shouldAllowOptOut = () => true) {
6210
6239
  return async (args) => {
6211
6240
  let { request, matches, fetcherKey } = args;
6212
6241
  let router = getRouter();
6213
6242
  if (request.method !== "GET") {
6214
- return singleFetchActionStrategy(args, fetchAndDecode, basename);
6243
+ return singleFetchActionStrategy(
6244
+ args,
6245
+ fetchAndDecode,
6246
+ basename,
6247
+ trailingSlashAware
6248
+ );
6215
6249
  }
6216
6250
  let foundRevalidatingServerLoader = matches.some((m) => {
6217
6251
  let { hasLoader, hasClientLoader } = getRouteInfo(m);
6218
6252
  return m.shouldCallHandler() && hasLoader && !hasClientLoader;
6219
6253
  });
6220
6254
  if (!ssr && !foundRevalidatingServerLoader) {
6221
- return nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename);
6255
+ return nonSsrStrategy(
6256
+ args,
6257
+ getRouteInfo,
6258
+ fetchAndDecode,
6259
+ basename,
6260
+ trailingSlashAware
6261
+ );
6222
6262
  }
6223
6263
  if (fetcherKey) {
6224
- return singleFetchLoaderFetcherStrategy(args, fetchAndDecode, basename);
6264
+ return singleFetchLoaderFetcherStrategy(
6265
+ args,
6266
+ fetchAndDecode,
6267
+ basename,
6268
+ trailingSlashAware
6269
+ );
6225
6270
  }
6226
6271
  return singleFetchLoaderNavigationStrategy(
6227
6272
  args,
@@ -6230,19 +6275,23 @@ function getSingleFetchDataStrategyImpl(getRouter, getRouteInfo, fetchAndDecode,
6230
6275
  fetchAndDecode,
6231
6276
  ssr,
6232
6277
  basename,
6278
+ trailingSlashAware,
6233
6279
  shouldAllowOptOut
6234
6280
  );
6235
6281
  };
6236
6282
  }
6237
- async function singleFetchActionStrategy(args, fetchAndDecode, basename) {
6283
+ async function singleFetchActionStrategy(args, fetchAndDecode, basename, trailingSlashAware) {
6238
6284
  let actionMatch = args.matches.find((m) => m.shouldCallHandler());
6239
6285
  invariant2(actionMatch, "No action match found");
6240
6286
  let actionStatus = void 0;
6241
6287
  let result = await actionMatch.resolve(async (handler) => {
6242
6288
  let result2 = await handler(async () => {
6243
- let { data: data2, status } = await fetchAndDecode(args, basename, [
6244
- actionMatch.route.id
6245
- ]);
6289
+ let { data: data2, status } = await fetchAndDecode(
6290
+ args,
6291
+ basename,
6292
+ trailingSlashAware,
6293
+ [actionMatch.route.id]
6294
+ );
6246
6295
  actionStatus = status;
6247
6296
  return unwrapSingleFetchResult(data2, actionMatch.route.id);
6248
6297
  });
@@ -6258,7 +6307,7 @@ async function singleFetchActionStrategy(args, fetchAndDecode, basename) {
6258
6307
  }
6259
6308
  };
6260
6309
  }
6261
- async function nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename) {
6310
+ async function nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename, trailingSlashAware) {
6262
6311
  let matchesToLoad = args.matches.filter((m) => m.shouldCallHandler());
6263
6312
  let results = {};
6264
6313
  await Promise.all(
@@ -6268,7 +6317,12 @@ async function nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename) {
6268
6317
  let { hasClientLoader } = getRouteInfo(m);
6269
6318
  let routeId = m.route.id;
6270
6319
  let result = hasClientLoader ? await handler(async () => {
6271
- let { data: data2 } = await fetchAndDecode(args, basename, [routeId]);
6320
+ let { data: data2 } = await fetchAndDecode(
6321
+ args,
6322
+ basename,
6323
+ trailingSlashAware,
6324
+ [routeId]
6325
+ );
6272
6326
  return unwrapSingleFetchResult(data2, routeId);
6273
6327
  }) : await handler();
6274
6328
  results[m.route.id] = { type: "data", result };
@@ -6280,7 +6334,7 @@ async function nonSsrStrategy(args, getRouteInfo, fetchAndDecode, basename) {
6280
6334
  );
6281
6335
  return results;
6282
6336
  }
6283
- async function singleFetchLoaderNavigationStrategy(args, router, getRouteInfo, fetchAndDecode, ssr, basename, shouldAllowOptOut = () => true) {
6337
+ async function singleFetchLoaderNavigationStrategy(args, router, getRouteInfo, fetchAndDecode, ssr, basename, trailingSlashAware, shouldAllowOptOut = () => true) {
6284
6338
  let routesParams = /* @__PURE__ */ new Set();
6285
6339
  let foundOptOutRoute = false;
6286
6340
  let routeDfds = args.matches.map(() => createDeferred2());
@@ -6306,7 +6360,12 @@ async function singleFetchLoaderNavigationStrategy(args, router, getRouteInfo, f
6306
6360
  }
6307
6361
  try {
6308
6362
  let result = await handler(async () => {
6309
- let { data: data2 } = await fetchAndDecode(args, basename, [routeId]);
6363
+ let { data: data2 } = await fetchAndDecode(
6364
+ args,
6365
+ basename,
6366
+ trailingSlashAware,
6367
+ [routeId]
6368
+ );
6310
6369
  return unwrapSingleFetchResult(data2, routeId);
6311
6370
  });
6312
6371
  results[routeId] = { type: "data", result };
@@ -6337,7 +6396,12 @@ async function singleFetchLoaderNavigationStrategy(args, router, getRouteInfo, f
6337
6396
  } else {
6338
6397
  let targetRoutes = ssr && foundOptOutRoute && routesParams.size > 0 ? [...routesParams.keys()] : void 0;
6339
6398
  try {
6340
- let data2 = await fetchAndDecode(args, basename, targetRoutes);
6399
+ let data2 = await fetchAndDecode(
6400
+ args,
6401
+ basename,
6402
+ trailingSlashAware,
6403
+ targetRoutes
6404
+ );
6341
6405
  singleFetchDfd.resolve(data2.data);
6342
6406
  } catch (e) {
6343
6407
  singleFetchDfd.reject(e);
@@ -6383,13 +6447,15 @@ async function bubbleMiddlewareErrors(singleFetchPromise, matches, routesParams,
6383
6447
  } catch (e) {
6384
6448
  }
6385
6449
  }
6386
- async function singleFetchLoaderFetcherStrategy(args, fetchAndDecode, basename) {
6450
+ async function singleFetchLoaderFetcherStrategy(args, fetchAndDecode, basename, trailingSlashAware) {
6387
6451
  let fetcherMatch = args.matches.find((m) => m.shouldCallHandler());
6388
6452
  invariant2(fetcherMatch, "No fetcher match found");
6389
6453
  let routeId = fetcherMatch.route.id;
6390
6454
  let result = await fetcherMatch.resolve(
6391
6455
  async (handler) => handler(async () => {
6392
- let { data: data2 } = await fetchAndDecode(args, basename, [routeId]);
6456
+ let { data: data2 } = await fetchAndDecode(args, basename, trailingSlashAware, [
6457
+ routeId
6458
+ ]);
6393
6459
  return unwrapSingleFetchResult(data2, routeId);
6394
6460
  })
6395
6461
  );
@@ -6409,25 +6475,33 @@ function stripIndexParam(url) {
6409
6475
  }
6410
6476
  return url;
6411
6477
  }
6412
- function singleFetchUrl(reqUrl, basename, extension) {
6478
+ function singleFetchUrl(reqUrl, basename, trailingSlashAware, extension) {
6413
6479
  let url = typeof reqUrl === "string" ? new URL(
6414
6480
  reqUrl,
6415
6481
  // This can be called during the SSR flow via PrefetchPageLinksImpl so
6416
6482
  // don't assume window is available
6417
6483
  typeof window === "undefined" ? "server://singlefetch/" : window.location.origin
6418
6484
  ) : reqUrl;
6419
- if (url.pathname === "/") {
6420
- url.pathname = `_root.${extension}`;
6421
- } else if (basename && stripBasename(url.pathname, basename) === "/") {
6422
- url.pathname = `${basename.replace(/\/$/, "")}/_root.${extension}`;
6485
+ if (trailingSlashAware) {
6486
+ if (url.pathname.endsWith("/")) {
6487
+ url.pathname = `${url.pathname}_.${extension}`;
6488
+ } else {
6489
+ url.pathname = `${url.pathname}.${extension}`;
6490
+ }
6423
6491
  } else {
6424
- url.pathname = `${url.pathname.replace(/\/$/, "")}.${extension}`;
6492
+ if (url.pathname === "/") {
6493
+ url.pathname = `_root.${extension}`;
6494
+ } else if (basename && stripBasename(url.pathname, basename) === "/") {
6495
+ url.pathname = `${basename.replace(/\/$/, "")}/_root.${extension}`;
6496
+ } else {
6497
+ url.pathname = `${url.pathname.replace(/\/$/, "")}.${extension}`;
6498
+ }
6425
6499
  }
6426
6500
  return url;
6427
6501
  }
6428
- async function fetchAndDecodeViaTurboStream(args, basename, targetRoutes) {
6502
+ async function fetchAndDecodeViaTurboStream(args, basename, trailingSlashAware, targetRoutes) {
6429
6503
  let { request } = args;
6430
- let url = singleFetchUrl(request.url, basename, "data");
6504
+ let url = singleFetchUrl(request.url, basename, trailingSlashAware, "data");
6431
6505
  if (request.method === "GET") {
6432
6506
  url = stripIndexParam(url);
6433
6507
  if (targetRoutes) {
@@ -8358,7 +8432,7 @@ function PrefetchPageLinksImpl({
8358
8432
  ...linkProps
8359
8433
  }) {
8360
8434
  let location = useLocation();
8361
- let { manifest, routeModules } = useFrameworkContext();
8435
+ let { future, manifest, routeModules } = useFrameworkContext();
8362
8436
  let { basename } = useDataRouterContext2();
8363
8437
  let { loaderData, matches } = useDataRouterStateContext();
8364
8438
  let newMatchesForData = React7.useMemo(
@@ -8405,7 +8479,12 @@ function PrefetchPageLinksImpl({
8405
8479
  if (routesParams.size === 0) {
8406
8480
  return [];
8407
8481
  }
8408
- let url = singleFetchUrl(page, basename, "data");
8482
+ let url = singleFetchUrl(
8483
+ page,
8484
+ basename,
8485
+ future.unstable_trailingSlashAwareDataRequests,
8486
+ "data"
8487
+ );
8409
8488
  if (foundOptOutRoute && routesParams.size > 0) {
8410
8489
  url.searchParams.set(
8411
8490
  "_routes",
@@ -8415,6 +8494,7 @@ function PrefetchPageLinksImpl({
8415
8494
  return [url.pathname + url.search];
8416
8495
  }, [
8417
8496
  basename,
8497
+ future.unstable_trailingSlashAwareDataRequests,
8418
8498
  loaderData,
8419
8499
  location,
8420
8500
  manifest,
@@ -8658,6 +8738,7 @@ import(${JSON.stringify(manifest.entry.module)});`;
8658
8738
  return isHydrated || isRSCRouterContext ? null : /* @__PURE__ */ React7.createElement(React7.Fragment, null, typeof manifest.sri === "object" ? /* @__PURE__ */ React7.createElement(
8659
8739
  "script",
8660
8740
  {
8741
+ ...scriptProps,
8661
8742
  "rr-importmap": "",
8662
8743
  type: "importmap",
8663
8744
  suppressHydrationWarning: true,
@@ -9636,4 +9717,5 @@ function withErrorBoundaryProps(ErrorBoundary) {
9636
9717
 
9637
9718
 
9638
9719
 
9639
- exports.Action = Action; exports.createBrowserHistory = createBrowserHistory; exports.createHashHistory = createHashHistory; exports.invariant = invariant; exports.warning = warning; exports.createPath = createPath; exports.parsePath = parsePath; exports.createContext = createContext; exports.RouterContextProvider = RouterContextProvider; exports.convertRoutesToDataRoutes = convertRoutesToDataRoutes; exports.matchRoutes = matchRoutes; exports.generatePath = generatePath; exports.matchPath = matchPath; exports.stripBasename = stripBasename; exports.resolvePath = resolvePath; exports.joinPaths = joinPaths; exports.data = data; exports.redirect = redirect; exports.redirectDocument = redirectDocument; exports.replace = replace; exports.ErrorResponseImpl = ErrorResponseImpl; exports.isRouteErrorResponse = isRouteErrorResponse; exports.parseToInfo = parseToInfo; exports.escapeHtml = escapeHtml; exports.encode = encode; exports.instrumentHandler = instrumentHandler; exports.IDLE_NAVIGATION = IDLE_NAVIGATION; exports.IDLE_FETCHER = IDLE_FETCHER; exports.IDLE_BLOCKER = IDLE_BLOCKER; exports.createRouter = createRouter; exports.createStaticHandler = createStaticHandler; exports.getStaticContextFromError = getStaticContextFromError; exports.isDataWithResponseInit = isDataWithResponseInit; exports.isResponse = isResponse; exports.isRedirectStatusCode = isRedirectStatusCode; exports.isRedirectResponse = isRedirectResponse; exports.isMutationMethod = isMutationMethod; exports.createRequestInit = createRequestInit; exports.SingleFetchRedirectSymbol = SingleFetchRedirectSymbol; exports.SINGLE_FETCH_REDIRECT_STATUS = SINGLE_FETCH_REDIRECT_STATUS; exports.NO_BODY_STATUS_CODES = NO_BODY_STATUS_CODES; exports.StreamTransfer = StreamTransfer; exports.getTurboStreamSingleFetchDataStrategy = getTurboStreamSingleFetchDataStrategy; exports.getSingleFetchDataStrategyImpl = getSingleFetchDataStrategyImpl; exports.stripIndexParam = stripIndexParam; exports.singleFetchUrl = singleFetchUrl; exports.decodeViaTurboStream = decodeViaTurboStream; exports.DataRouterContext = DataRouterContext; exports.DataRouterStateContext = DataRouterStateContext; exports.RSCRouterContext = RSCRouterContext; exports.ViewTransitionContext = ViewTransitionContext; exports.FetchersContext = FetchersContext; exports.AwaitContextProvider = AwaitContextProvider; exports.NavigationContext = NavigationContext; exports.LocationContext = LocationContext; exports.RouteContext = RouteContext; exports.ENABLE_DEV_WARNINGS = ENABLE_DEV_WARNINGS; exports.warnOnce = warnOnce; exports.decodeRedirectErrorDigest = decodeRedirectErrorDigest; exports.decodeRouteErrorResponseDigest = decodeRouteErrorResponseDigest; exports.useHref = useHref; exports.useInRouterContext = useInRouterContext; exports.useLocation = useLocation; exports.useNavigationType = useNavigationType; exports.useMatch = useMatch; exports.useNavigate = useNavigate; exports.useOutletContext = useOutletContext; exports.useOutlet = useOutlet; exports.useParams = useParams; exports.useResolvedPath = useResolvedPath; exports.useRoutes = useRoutes; exports.useRoutesImpl = useRoutesImpl; exports.useRouteId = useRouteId; exports.useNavigation = useNavigation; exports.useRevalidator = useRevalidator; exports.useMatches = useMatches; exports.useLoaderData = useLoaderData; exports.useRouteLoaderData = useRouteLoaderData; exports.useActionData = useActionData; exports.useRouteError = useRouteError; exports.useAsyncValue = useAsyncValue; exports.useAsyncError = useAsyncError; exports.useBlocker = useBlocker; exports.useRoute = useRoute; exports.RemixErrorBoundary = RemixErrorBoundary; exports.createServerRoutes = createServerRoutes; exports.createClientRoutesWithHMRRevalidationOptOut = createClientRoutesWithHMRRevalidationOptOut; exports.noActionDefinedError = noActionDefinedError; exports.createClientRoutes = createClientRoutes; exports.shouldHydrateRouteLoader = shouldHydrateRouteLoader; exports.getPatchRoutesOnNavigationFunction = getPatchRoutesOnNavigationFunction; exports.useFogOFWarDiscovery = useFogOFWarDiscovery; exports.getManifestPath = getManifestPath; exports.FrameworkContext = FrameworkContext; exports.usePrefetchBehavior = usePrefetchBehavior; exports.CRITICAL_CSS_DATA_ATTRIBUTE = CRITICAL_CSS_DATA_ATTRIBUTE; exports.Links = Links; exports.PrefetchPageLinks = PrefetchPageLinks; exports.Meta = Meta; exports.setIsHydrated = setIsHydrated; exports.Scripts = Scripts; exports.mergeRefs = mergeRefs; exports.mapRouteProperties = mapRouteProperties; exports.hydrationRouteProperties = hydrationRouteProperties; exports.createMemoryRouter = createMemoryRouter; exports.RouterProvider = RouterProvider; exports.MemoryRouter = MemoryRouter; exports.Navigate = Navigate; exports.Outlet = Outlet; exports.Route = Route; exports.Router = Router; exports.Routes = Routes; exports.Await = Await; exports.createRoutesFromChildren = createRoutesFromChildren; exports.createRoutesFromElements = createRoutesFromElements; exports.renderMatches = renderMatches; exports.WithComponentProps = WithComponentProps; exports.withComponentProps = withComponentProps; exports.WithHydrateFallbackProps = WithHydrateFallbackProps; exports.withHydrateFallbackProps = withHydrateFallbackProps; exports.WithErrorBoundaryProps = WithErrorBoundaryProps; exports.withErrorBoundaryProps = withErrorBoundaryProps;
9720
+
9721
+ exports.Action = Action; exports.createMemoryHistory = createMemoryHistory; exports.createBrowserHistory = createBrowserHistory; exports.createHashHistory = createHashHistory; exports.invariant = invariant; exports.warning = warning; exports.createPath = createPath; exports.parsePath = parsePath; exports.createContext = createContext; exports.RouterContextProvider = RouterContextProvider; exports.convertRoutesToDataRoutes = convertRoutesToDataRoutes; exports.matchRoutes = matchRoutes; exports.generatePath = generatePath; exports.matchPath = matchPath; exports.stripBasename = stripBasename; exports.resolvePath = resolvePath; exports.joinPaths = joinPaths; exports.data = data; exports.redirect = redirect; exports.redirectDocument = redirectDocument; exports.replace = replace; exports.ErrorResponseImpl = ErrorResponseImpl; exports.isRouteErrorResponse = isRouteErrorResponse; exports.parseToInfo = parseToInfo; exports.escapeHtml = escapeHtml; exports.encode = encode; exports.instrumentHandler = instrumentHandler; exports.IDLE_NAVIGATION = IDLE_NAVIGATION; exports.IDLE_FETCHER = IDLE_FETCHER; exports.IDLE_BLOCKER = IDLE_BLOCKER; exports.createRouter = createRouter; exports.createStaticHandler = createStaticHandler; exports.getStaticContextFromError = getStaticContextFromError; exports.isDataWithResponseInit = isDataWithResponseInit; exports.isResponse = isResponse; exports.isRedirectStatusCode = isRedirectStatusCode; exports.isRedirectResponse = isRedirectResponse; exports.isMutationMethod = isMutationMethod; exports.createRequestInit = createRequestInit; exports.SingleFetchRedirectSymbol = SingleFetchRedirectSymbol; exports.SINGLE_FETCH_REDIRECT_STATUS = SINGLE_FETCH_REDIRECT_STATUS; exports.NO_BODY_STATUS_CODES = NO_BODY_STATUS_CODES; exports.StreamTransfer = StreamTransfer; exports.getTurboStreamSingleFetchDataStrategy = getTurboStreamSingleFetchDataStrategy; exports.getSingleFetchDataStrategyImpl = getSingleFetchDataStrategyImpl; exports.stripIndexParam = stripIndexParam; exports.singleFetchUrl = singleFetchUrl; exports.decodeViaTurboStream = decodeViaTurboStream; exports.DataRouterContext = DataRouterContext; exports.DataRouterStateContext = DataRouterStateContext; exports.RSCRouterContext = RSCRouterContext; exports.ViewTransitionContext = ViewTransitionContext; exports.FetchersContext = FetchersContext; exports.AwaitContextProvider = AwaitContextProvider; exports.NavigationContext = NavigationContext; exports.LocationContext = LocationContext; exports.RouteContext = RouteContext; exports.ENABLE_DEV_WARNINGS = ENABLE_DEV_WARNINGS; exports.warnOnce = warnOnce; exports.decodeRedirectErrorDigest = decodeRedirectErrorDigest; exports.decodeRouteErrorResponseDigest = decodeRouteErrorResponseDigest; exports.useHref = useHref; exports.useInRouterContext = useInRouterContext; exports.useLocation = useLocation; exports.useNavigationType = useNavigationType; exports.useMatch = useMatch; exports.useNavigate = useNavigate; exports.useOutletContext = useOutletContext; exports.useOutlet = useOutlet; exports.useParams = useParams; exports.useResolvedPath = useResolvedPath; exports.useRoutes = useRoutes; exports.useRoutesImpl = useRoutesImpl; exports.useRouteId = useRouteId; exports.useNavigation = useNavigation; exports.useRevalidator = useRevalidator; exports.useMatches = useMatches; exports.useLoaderData = useLoaderData; exports.useRouteLoaderData = useRouteLoaderData; exports.useActionData = useActionData; exports.useRouteError = useRouteError; exports.useAsyncValue = useAsyncValue; exports.useAsyncError = useAsyncError; exports.useBlocker = useBlocker; exports.useRoute = useRoute; exports.RemixErrorBoundary = RemixErrorBoundary; exports.createServerRoutes = createServerRoutes; exports.createClientRoutesWithHMRRevalidationOptOut = createClientRoutesWithHMRRevalidationOptOut; exports.noActionDefinedError = noActionDefinedError; exports.createClientRoutes = createClientRoutes; exports.shouldHydrateRouteLoader = shouldHydrateRouteLoader; exports.getPatchRoutesOnNavigationFunction = getPatchRoutesOnNavigationFunction; exports.useFogOFWarDiscovery = useFogOFWarDiscovery; exports.getManifestPath = getManifestPath; exports.FrameworkContext = FrameworkContext; exports.usePrefetchBehavior = usePrefetchBehavior; exports.CRITICAL_CSS_DATA_ATTRIBUTE = CRITICAL_CSS_DATA_ATTRIBUTE; exports.Links = Links; exports.PrefetchPageLinks = PrefetchPageLinks; exports.Meta = Meta; exports.setIsHydrated = setIsHydrated; exports.Scripts = Scripts; exports.mergeRefs = mergeRefs; exports.mapRouteProperties = mapRouteProperties; exports.hydrationRouteProperties = hydrationRouteProperties; exports.createMemoryRouter = createMemoryRouter; exports.RouterProvider = RouterProvider; exports.MemoryRouter = MemoryRouter; exports.Navigate = Navigate; exports.Outlet = Outlet; exports.Route = Route; exports.Router = Router; exports.Routes = Routes; exports.Await = Await; exports.createRoutesFromChildren = createRoutesFromChildren; exports.createRoutesFromElements = createRoutesFromElements; exports.renderMatches = renderMatches; exports.WithComponentProps = WithComponentProps; exports.withComponentProps = withComponentProps; exports.WithHydrateFallbackProps = WithHydrateFallbackProps; exports.withHydrateFallbackProps = withHydrateFallbackProps; exports.WithErrorBoundaryProps = WithErrorBoundaryProps; exports.withErrorBoundaryProps = withErrorBoundaryProps;