react-router 8.1.0 → 8.3.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 (178) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/development/dom-export.js +1 -1
  3. package/dist/development/index-react-server-client.js +1 -1
  4. package/dist/development/index-react-server.d.ts +109 -19
  5. package/dist/development/index-react-server.js +176 -45
  6. package/dist/development/index.js +1 -1
  7. package/dist/development/lib/actions.js +1 -1
  8. package/dist/development/lib/components.js +1 -1
  9. package/dist/development/lib/context.js +1 -1
  10. package/dist/development/lib/dom/dom.d.ts +24 -24
  11. package/dist/development/lib/dom/dom.js +25 -25
  12. package/dist/development/lib/dom/lib.d.ts +2 -1
  13. package/dist/development/lib/dom/lib.js +5 -4
  14. package/dist/development/lib/dom/server.js +1 -1
  15. package/dist/development/lib/dom/ssr/components.js +1 -1
  16. package/dist/development/lib/dom/ssr/data.js +1 -1
  17. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  18. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  19. package/dist/development/lib/dom/ssr/fog-of-war.js +26 -21
  20. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  21. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  22. package/dist/development/lib/dom/ssr/links.js +1 -1
  23. package/dist/development/lib/dom/ssr/markup.js +1 -1
  24. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  25. package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  26. package/dist/development/lib/dom/ssr/routes-test-stub.js +14 -1
  27. package/dist/development/lib/dom/ssr/routes.js +1 -1
  28. package/dist/development/lib/dom/ssr/server.js +1 -1
  29. package/dist/development/lib/dom/ssr/single-fetch.js +1 -1
  30. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  31. package/dist/development/lib/dom-export/hydrated-router.js +1 -1
  32. package/dist/development/lib/errors.js +1 -1
  33. package/dist/development/lib/hooks.d.ts +33 -6
  34. package/dist/development/lib/hooks.js +34 -7
  35. package/dist/development/lib/href.d.ts +24 -8
  36. package/dist/development/lib/href.js +31 -11
  37. package/dist/development/lib/router/history.d.ts +6 -0
  38. package/dist/development/lib/router/history.js +7 -1
  39. package/dist/development/lib/router/instrumentation.js +1 -1
  40. package/dist/development/lib/router/router.js +2 -2
  41. package/dist/development/lib/router/url.js +1 -1
  42. package/dist/development/lib/router/utils.d.ts +12 -0
  43. package/dist/development/lib/router/utils.js +61 -5
  44. package/dist/development/lib/rsc/browser.js +34 -24
  45. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  46. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  47. package/dist/development/lib/rsc/html-stream/server.js +29 -10
  48. package/dist/development/lib/rsc/route-modules.js +1 -1
  49. package/dist/development/lib/rsc/server.rsc.d.ts +8 -3
  50. package/dist/development/lib/rsc/server.ssr.d.ts +29 -6
  51. package/dist/development/lib/rsc/server.ssr.js +27 -12
  52. package/dist/development/lib/server-runtime/cookies.d.ts +28 -2
  53. package/dist/development/lib/server-runtime/cookies.js +17 -3
  54. package/dist/development/lib/server-runtime/crypto.js +1 -1
  55. package/dist/development/lib/server-runtime/data.js +1 -1
  56. package/dist/development/lib/server-runtime/dev.js +1 -1
  57. package/dist/development/lib/server-runtime/entry.js +1 -1
  58. package/dist/development/lib/server-runtime/errors.js +1 -1
  59. package/dist/development/lib/server-runtime/headers.js +1 -1
  60. package/dist/development/lib/server-runtime/invariant.js +1 -1
  61. package/dist/development/lib/server-runtime/mode.js +1 -1
  62. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  63. package/dist/development/lib/server-runtime/routes.js +1 -1
  64. package/dist/development/lib/server-runtime/server.d.ts +12 -0
  65. package/dist/development/lib/server-runtime/server.js +13 -1
  66. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  67. package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  68. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +9 -1
  69. package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  70. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +12 -6
  71. package/dist/development/lib/server-runtime/sessions.d.ts +31 -2
  72. package/dist/development/lib/server-runtime/sessions.js +20 -3
  73. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  74. package/dist/development/lib/server-runtime/urls.js +1 -1
  75. package/dist/development/lib/server-runtime/warnings.js +1 -1
  76. package/dist/development/lib/types/internal.js +1 -1
  77. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  78. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  79. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  80. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  81. package/dist/production/dom-export.js +1 -1
  82. package/dist/production/index-react-server-client.js +1 -1
  83. package/dist/production/index-react-server.d.ts +109 -19
  84. package/dist/production/index-react-server.js +176 -45
  85. package/dist/production/index.js +1 -1
  86. package/dist/production/lib/actions.js +1 -1
  87. package/dist/production/lib/components.js +1 -1
  88. package/dist/production/lib/context.js +1 -1
  89. package/dist/production/lib/dom/dom.d.ts +24 -24
  90. package/dist/production/lib/dom/dom.js +25 -25
  91. package/dist/production/lib/dom/lib.d.ts +2 -1
  92. package/dist/production/lib/dom/lib.js +5 -4
  93. package/dist/production/lib/dom/server.js +1 -1
  94. package/dist/production/lib/dom/ssr/components.js +1 -1
  95. package/dist/production/lib/dom/ssr/data.js +1 -1
  96. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  97. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  98. package/dist/production/lib/dom/ssr/fog-of-war.js +26 -21
  99. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  100. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  101. package/dist/production/lib/dom/ssr/links.js +1 -1
  102. package/dist/production/lib/dom/ssr/markup.js +1 -1
  103. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  104. package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  105. package/dist/production/lib/dom/ssr/routes-test-stub.js +14 -1
  106. package/dist/production/lib/dom/ssr/routes.js +1 -1
  107. package/dist/production/lib/dom/ssr/server.js +1 -1
  108. package/dist/production/lib/dom/ssr/single-fetch.js +1 -1
  109. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  110. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  111. package/dist/production/lib/errors.js +1 -1
  112. package/dist/production/lib/hooks.d.ts +33 -6
  113. package/dist/production/lib/hooks.js +34 -7
  114. package/dist/production/lib/href.d.ts +24 -8
  115. package/dist/production/lib/href.js +31 -11
  116. package/dist/production/lib/router/history.d.ts +6 -0
  117. package/dist/production/lib/router/history.js +7 -1
  118. package/dist/production/lib/router/instrumentation.js +1 -1
  119. package/dist/production/lib/router/router.js +2 -2
  120. package/dist/production/lib/router/url.js +1 -1
  121. package/dist/production/lib/router/utils.d.ts +12 -0
  122. package/dist/production/lib/router/utils.js +61 -5
  123. package/dist/production/lib/rsc/browser.js +34 -24
  124. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  125. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  126. package/dist/production/lib/rsc/html-stream/server.js +29 -10
  127. package/dist/production/lib/rsc/route-modules.js +1 -1
  128. package/dist/production/lib/rsc/server.rsc.d.ts +8 -3
  129. package/dist/production/lib/rsc/server.ssr.d.ts +29 -6
  130. package/dist/production/lib/rsc/server.ssr.js +27 -12
  131. package/dist/production/lib/server-runtime/cookies.d.ts +28 -2
  132. package/dist/production/lib/server-runtime/cookies.js +17 -3
  133. package/dist/production/lib/server-runtime/crypto.js +1 -1
  134. package/dist/production/lib/server-runtime/data.js +1 -1
  135. package/dist/production/lib/server-runtime/dev.js +1 -1
  136. package/dist/production/lib/server-runtime/entry.js +1 -1
  137. package/dist/production/lib/server-runtime/errors.js +1 -1
  138. package/dist/production/lib/server-runtime/headers.js +1 -1
  139. package/dist/production/lib/server-runtime/invariant.js +1 -1
  140. package/dist/production/lib/server-runtime/mode.js +1 -1
  141. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  142. package/dist/production/lib/server-runtime/routes.js +1 -1
  143. package/dist/production/lib/server-runtime/server.d.ts +12 -0
  144. package/dist/production/lib/server-runtime/server.js +13 -1
  145. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  146. package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  147. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +9 -1
  148. package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  149. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +12 -6
  150. package/dist/production/lib/server-runtime/sessions.d.ts +31 -2
  151. package/dist/production/lib/server-runtime/sessions.js +20 -3
  152. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  153. package/dist/production/lib/server-runtime/urls.js +1 -1
  154. package/dist/production/lib/server-runtime/warnings.js +1 -1
  155. package/dist/production/lib/types/internal.js +1 -1
  156. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  157. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  158. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  159. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  160. package/docs/explanation/hot-module-replacement.md +1 -1
  161. package/docs/explanation/sessions-and-cookies.md +13 -13
  162. package/docs/explanation/state-management.md +2 -2
  163. package/docs/how-to/data-strategy.md +1 -1
  164. package/docs/how-to/presets.md +2 -2
  165. package/docs/how-to/react-server-components.md +53 -1
  166. package/docs/how-to/security.md +8 -1
  167. package/docs/how-to/server-bundles.md +2 -2
  168. package/docs/start/data/route-object.md +2 -2
  169. package/docs/start/data/routing.md +1 -1
  170. package/docs/start/framework/deploying.md +4 -0
  171. package/docs/start/framework/pending-ui.md +1 -1
  172. package/docs/start/framework/route-module.md +8 -8
  173. package/docs/start/framework/routing.md +1 -1
  174. package/docs/upgrading/component-routes.md +1 -1
  175. package/docs/upgrading/future.md +33 -0
  176. package/docs/upgrading/router-provider.md +5 -5
  177. package/docs/upgrading/v7.md +39 -22
  178. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -47,7 +47,10 @@ function createLocation(current, to, state = null, key, mask) {
47
47
  /**
48
48
  * Creates a string URL path from the given pathname, search, and hash components.
49
49
  *
50
+ * @public
50
51
  * @category Utils
52
+ * @param path The pathname, search, and hash components to combine.
53
+ * @returns The combined URL path.
51
54
  */
52
55
  function createPath({ pathname = "/", search = "", hash = "" }) {
53
56
  if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search;
@@ -57,7 +60,10 @@ function createPath({ pathname = "/", search = "", hash = "" }) {
57
60
  /**
58
61
  * Parses a string URL path into its separate pathname, search, and hash components.
59
62
  *
63
+ * @public
60
64
  * @category Utils
65
+ * @param path The URL path to parse.
66
+ * @returns The parsed pathname, search, and hash components.
61
67
  */
62
68
  function parsePath(path) {
63
69
  let parsedPath = {};
@@ -380,6 +386,8 @@ function rankRouteBranches(branches) {
380
386
  branches.sort((a, b) => a.score !== b.score ? b.score - a.score : compareIndexes(a.routesMeta.map((meta) => meta.childrenIndex), b.routesMeta.map((meta) => meta.childrenIndex)));
381
387
  }
382
388
  const paramRe = /^:[\w-]+$/;
389
+ const partialParamRe = /^:[\w-]+/;
390
+ const partialDynamicSegmentValue = 3.5;
383
391
  const dynamicSegmentValue = 3;
384
392
  const indexRouteValue = 2;
385
393
  const emptySegmentValue = 1;
@@ -391,7 +399,7 @@ function computeScore(path, index) {
391
399
  let initialScore = segments.length;
392
400
  if (segments.some(isSplat)) initialScore += splatPenalty;
393
401
  if (index) initialScore += indexRouteValue;
394
- return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
402
+ return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : partialParamRe.test(segment) ? partialDynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
395
403
  }
396
404
  function compareIndexes(a, b) {
397
405
  return a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]) ? a[a.length - 1] - b[b.length - 1] : 0;
@@ -430,6 +438,48 @@ function matchRouteBranch(branch, pathname, allowPartial = false) {
430
438
  return matches;
431
439
  }
432
440
  /**
441
+ * Characters that `encodeURIComponent` escapes but that are valid literally in
442
+ * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`:
443
+ *
444
+ * ```
445
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
446
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
447
+ * ```
448
+ *
449
+ * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @`
450
+ * are delimiters and must be escaped — but in a path segment they carry no
451
+ * special meaning, and browsers keep them literal in `location.pathname`.
452
+ * (`! ' ( ) *` and the unreserved set are already left alone by
453
+ * `encodeURIComponent`, so they need no restoring.)
454
+ */
455
+ const PATH_PARAM_OVERESCAPED = {
456
+ "%24": "$",
457
+ "%26": "&",
458
+ "%2B": "+",
459
+ "%2C": ",",
460
+ "%3A": ":",
461
+ "%3B": ";",
462
+ "%3D": "=",
463
+ "%40": "@"
464
+ };
465
+ /**
466
+ * Encodes a param value for interpolation into a single URL path segment.
467
+ *
468
+ * Escapes characters that would break the path (`/ ? # %`, whitespace,
469
+ * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a
470
+ * path segment untouched. Escaping those would needlessly rewrite URLs — e.g.
471
+ * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers
472
+ * display and match the `+` literally in `location.pathname`.
473
+ *
474
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3))
475
+ *
476
+ * @param value The param value to encode.
477
+ * @returns The encoded value, safe for use as a single path segment.
478
+ */
479
+ function encodePathParam(value) {
480
+ return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]);
481
+ }
482
+ /**
433
483
  * Performs pattern matching on a URL pathname and returns information about
434
484
  * the match.
435
485
  *
@@ -488,7 +538,7 @@ function compilePath(path, caseSensitive = false, end = true) {
488
538
  return "(?:/([^\\/]*))?";
489
539
  }
490
540
  return "/([^\\/]+)";
491
- }).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
541
+ }).replace(/\/([\w-]+)\?(?=\/|$|\()/g, "(?:/$1)?");
492
542
  if (path.endsWith("*")) {
493
543
  params.push({ paramName: "*" });
494
544
  regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
@@ -2321,6 +2371,8 @@ const Await = (async ({ children, resolve, errorElement }) => {
2321
2371
  * encoding the {@link unstable_RSCPayload}.
2322
2372
  * @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
2323
2373
  * `loadServerAction` function, used to load a server action by ID.
2374
+ * @param opts.clientVersion A version derived from the client build output used
2375
+ * to detect stale clients during lazy route discovery.
2324
2376
  * @param opts.onError An optional error handler that will be called with any
2325
2377
  * errors that occur during the request processing.
2326
2378
  * @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
@@ -2334,7 +2386,7 @@ const Await = (async ({ children, resolve, errorElement }) => {
2334
2386
  * that contains the [RSC](https://react.dev/reference/rsc/server-components)
2335
2387
  * data for hydration.
2336
2388
  */
2337
- async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse }) {
2389
+ async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, clientVersion, onError, request, routes, generateResponse }) {
2338
2390
  let url = new URL(request.url);
2339
2391
  basename = basename || "/";
2340
2392
  let normalizedPath = url.pathname;
@@ -2352,22 +2404,26 @@ async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryRefe
2352
2404
  });
2353
2405
  const temporaryReferences = createTemporaryReferenceSet();
2354
2406
  const requestUrl = new URL(request.url);
2355
- if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery);
2407
+ if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion);
2356
2408
  let isDataRequest = isReactServerRequest(requestUrl);
2357
2409
  let matches = matchRoutes(routes, url.pathname, basename);
2358
2410
  if (matches) await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
2359
2411
  const leafMatch = matches?.[matches.length - 1];
2360
2412
  if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) return generateResourceResponse(routerRequest, routes, basename, leafMatch.route.id, requestContext, onError);
2361
- let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery);
2413
+ let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion);
2362
2414
  response.headers.set("X-Remix-Response", "yes");
2363
2415
  return response;
2364
2416
  }
2365
- async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
2417
+ async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion) {
2366
2418
  let url = new URL(request.url);
2367
2419
  if (url.toString().length > 7680) return new Response(null, {
2368
2420
  statusText: "Bad Request",
2369
2421
  status: 400
2370
2422
  });
2423
+ if (clientVersion !== void 0 && clientVersion !== url.searchParams.get("version")) return new Response(null, {
2424
+ status: 204,
2425
+ headers: { "X-Remix-Reload-Document": "true" }
2426
+ });
2371
2427
  if (routeDiscovery?.mode === "initial") {
2372
2428
  let payload = {
2373
2429
  type: "manifest",
@@ -2459,7 +2515,7 @@ async function processServerAction(request, basename, decodeReply, loadServerAct
2459
2515
  try {
2460
2516
  let result = await action();
2461
2517
  if (isRedirectResponse(result)) result = prependBasenameToRedirectResponse(result, basename);
2462
- formState = decodeFormState?.(result, formData);
2518
+ formState = await decodeFormState?.(result, formData);
2463
2519
  } catch (error) {
2464
2520
  if (isRedirectResponse(error)) return prependBasenameToRedirectResponse(error, basename);
2465
2521
  if (isResponse(error)) return error;
@@ -2516,7 +2572,7 @@ async function generateResourceResponse(request, routes, basename, routeId, requ
2516
2572
  });
2517
2573
  }
2518
2574
  }
2519
- async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
2575
+ async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion) {
2520
2576
  let statusCode = 200;
2521
2577
  let url = new URL(request.url);
2522
2578
  let isSubmission = isMutationMethod(request.method);
@@ -2537,29 +2593,39 @@ async function generateRenderResponse(request, routes, basename, isDataRequest,
2537
2593
  let formState;
2538
2594
  let skipRevalidation = false;
2539
2595
  let potentialCSRFAttackError;
2540
- if (isMutationMethod(request.method)) try {
2541
- throwIfPotentialCSRFAttack(request, allowedActionOrigins);
2542
- ctx.runningAction = true;
2543
- let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
2544
- ctx.runningAction = false;
2545
- });
2546
- if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2547
- skipRevalidation = result?.skipRevalidation ?? false;
2548
- actionResult = result?.actionResult;
2549
- formState = result?.formState;
2550
- request = result?.revalidationRequest ?? request;
2551
- if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
2552
- } catch (error) {
2553
- potentialCSRFAttackError = error;
2596
+ if (isMutationMethod(request.method)) {
2597
+ try {
2598
+ throwIfPotentialCSRFAttack(request, allowedActionOrigins);
2599
+ } catch (error) {
2600
+ onError?.(error);
2601
+ potentialCSRFAttackError = error;
2602
+ request = new Request(request.url, {
2603
+ method: "GET",
2604
+ headers: request.headers,
2605
+ signal: request.signal
2606
+ });
2607
+ }
2608
+ if (!potentialCSRFAttackError) {
2609
+ ctx.runningAction = true;
2610
+ let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
2611
+ ctx.runningAction = false;
2612
+ });
2613
+ if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2614
+ skipRevalidation = result?.skipRevalidation ?? false;
2615
+ actionResult = result?.actionResult;
2616
+ formState = result?.formState;
2617
+ request = result?.revalidationRequest ?? request;
2618
+ if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
2619
+ }
2554
2620
  }
2555
- let staticContext = await query(request, skipRevalidation || !!potentialCSRFAttackError ? { filterMatchesToLoad: () => false } : void 0);
2621
+ let staticContext = await query(request, skipRevalidation ? { filterMatchesToLoad: () => false } : void 0);
2556
2622
  if (isResponse(staticContext)) return generateRedirectResponse(staticContext, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2557
2623
  if (potentialCSRFAttackError) {
2558
2624
  staticContext.errors ??= {};
2559
2625
  staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
2560
2626
  staticContext.statusCode = 400;
2561
2627
  }
2562
- return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery);
2628
+ return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery, clientVersion);
2563
2629
  }
2564
2630
  }));
2565
2631
  if (isRedirectResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
@@ -2593,7 +2659,7 @@ function generateRedirectResponse(response, actionResult, basename, isDataReques
2593
2659
  onError: defaultOnError
2594
2660
  });
2595
2661
  }
2596
- async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
2662
+ async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery, clientVersion) {
2597
2663
  statusCode = staticContext.statusCode ?? statusCode;
2598
2664
  if (staticContext.errors) staticContext.errors = Object.fromEntries(Object.entries(staticContext.errors).map(([key, error]) => [key, isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error]));
2599
2665
  staticContext.matches.forEach((m) => {
@@ -2606,6 +2672,7 @@ async function generateStaticContextResponse(routes, basename, generateResponse,
2606
2672
  const baseRenderPayload = {
2607
2673
  type: "render",
2608
2674
  basename: staticContext.basename,
2675
+ clientVersion,
2609
2676
  routeDiscovery: routeDiscovery ?? { mode: "lazy" },
2610
2677
  actionData: staticContext.actionData,
2611
2678
  errors: staticContext.errors,
@@ -2797,15 +2864,34 @@ function canDecodeWithFormData(contentType) {
2797
2864
  }
2798
2865
  //#endregion
2799
2866
  //#region lib/href.ts
2867
+ function stringify(p) {
2868
+ return p == null ? "" : typeof p === "string" ? p : String(p);
2869
+ }
2800
2870
  /**
2801
- Returns a resolved URL path for the specified route.
2802
-
2803
- ```tsx
2804
- const h = href("/:lang?/about", { lang: "en" })
2805
- // -> `/en/about`
2806
-
2807
- <Link to={href("/products/:id", { id: "abc123" })} />
2808
- ```
2871
+ * Returns a resolved URL path for the specified route.
2872
+ *
2873
+ * Param values are percent-encoded for use in a path segment: characters that
2874
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
2875
+ * are escaped, while characters that RFC 3986 allows literally in a path
2876
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
2877
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
2878
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
2879
+ * preserving `/` separators.
2880
+ *
2881
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
2882
+ *
2883
+ * @example
2884
+ * const h = href("/:lang?/about", { lang: "en" })
2885
+ * // -> `/en/about`
2886
+ *
2887
+ * <Link to={href("/products/:id", { id: "abc123" })} />
2888
+ *
2889
+ * @public
2890
+ * @category Utils
2891
+ * @mode framework
2892
+ * @param path The route path to resolve
2893
+ * @param args The route params to use when resolving the path
2894
+ * @returns The resolved URL path
2809
2895
  */
2810
2896
  function href(path, ...args) {
2811
2897
  let params = args[0];
@@ -2813,11 +2899,11 @@ function href(path, ...args) {
2813
2899
  const isRequired = questionMark === void 0;
2814
2900
  const value = params?.[param];
2815
2901
  if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
2816
- return value === void 0 ? "" : "/" + value;
2902
+ return value == null ? "" : "/" + encodePathParam(stringify(value));
2817
2903
  });
2818
2904
  if (path.endsWith("*")) {
2819
2905
  const value = params?.["*"];
2820
- if (value !== void 0) result += "/" + value;
2906
+ if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/");
2821
2907
  }
2822
2908
  return result || "/";
2823
2909
  }
@@ -2870,6 +2956,14 @@ function byteStringToUint8Array(byteString) {
2870
2956
  //#region lib/server-runtime/cookies.ts
2871
2957
  /**
2872
2958
  * Creates a logical container for managing a browser cookie from the server.
2959
+ *
2960
+ * @public
2961
+ * @category Utils
2962
+ * @mode framework
2963
+ * @mode data
2964
+ * @param name The name of the cookie.
2965
+ * @param cookieOptions Options for parsing and serializing the cookie.
2966
+ * @returns A {@link Cookie} object for parsing and serializing the cookie.
2873
2967
  */
2874
2968
  const createCookie = (name, cookieOptions = {}) => {
2875
2969
  let { secrets = [], ...options } = {
@@ -2909,9 +3003,15 @@ const createCookie = (name, cookieOptions = {}) => {
2909
3003
  };
2910
3004
  };
2911
3005
  /**
2912
- * Returns true if an object is a Remix cookie container.
3006
+ * Returns `true` if a value is a React Router {@link Cookie} object.
2913
3007
  *
2914
- * @see https://remix.run/utils/cookies#iscookie
3008
+ * @public
3009
+ * @category Utils
3010
+ * @mode framework
3011
+ * @mode data
3012
+ * @param object The value to check.
3013
+ * @returns `true` if the value is a React Router {@link Cookie} object;
3014
+ * otherwise, `false`.
2915
3015
  */
2916
3016
  const isCookie = (object) => {
2917
3017
  return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
@@ -3001,6 +3101,12 @@ function flash(name) {
3001
3101
  *
3002
3102
  * Note: This function is typically not invoked directly by application code.
3003
3103
  * Instead, use a `SessionStorage` object's `getSession` method.
3104
+ *
3105
+ * @category Utils
3106
+ * @param initialData The initial data for the session.
3107
+ * @param id The identifier for the session. Defaults to an empty string for a
3108
+ * new session.
3109
+ * @returns A new {@link Session} object.
3004
3110
  */
3005
3111
  const createSession = (initialData = {}, id = "") => {
3006
3112
  let map = new Map(Object.entries(initialData));
@@ -3035,9 +3141,15 @@ const createSession = (initialData = {}, id = "") => {
3035
3141
  };
3036
3142
  };
3037
3143
  /**
3038
- * Returns true if an object is a React Router session.
3144
+ * Returns `true` if a value is a React Router {@link Session} object.
3039
3145
  *
3040
- * @see https://reactrouter.com/api/utils/isSession
3146
+ * @public
3147
+ * @category Utils
3148
+ * @mode framework
3149
+ * @mode data
3150
+ * @param object The value to check.
3151
+ * @returns `true` if the value is a React Router {@link Session} object;
3152
+ * otherwise, `false`.
3041
3153
  */
3042
3154
  const isSession = (object) => {
3043
3155
  return object != null && typeof object.id === "string" && typeof object.data !== "undefined" && typeof object.has === "function" && typeof object.get === "function" && typeof object.set === "function" && typeof object.flash === "function" && typeof object.unset === "function";
@@ -3047,6 +3159,11 @@ const isSession = (object) => {
3047
3159
  *
3048
3160
  * Note: This is a low-level API that should only be used if none of the
3049
3161
  * existing session storage options meet your requirements.
3162
+ *
3163
+ * @category Utils
3164
+ * @param strategy The strategy used to store session identifiers and data.
3165
+ * @returns A {@link SessionStorage} object that persists session data using the
3166
+ * provided strategy.
3050
3167
  */
3051
3168
  function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) {
3052
3169
  let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
@@ -3086,6 +3203,14 @@ function warnOnceAboutSigningSessionCookie(cookie) {
3086
3203
  * needed, and can help to simplify some load-balanced scenarios. However, it
3087
3204
  * also has the limitation that serialized session data may not exceed the
3088
3205
  * browser's maximum cookie size. Trade-offs!
3206
+ *
3207
+ * @public
3208
+ * @category Utils
3209
+ * @mode framework
3210
+ * @mode data
3211
+ * @param options Options for creating the cookie-backed session storage.
3212
+ * @returns A {@link SessionStorage} object that stores all session data in its
3213
+ * cookie.
3089
3214
  */
3090
3215
  function createCookieSessionStorage({ cookie: cookieArg } = {}) {
3091
3216
  let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
@@ -3111,18 +3236,24 @@ function createCookieSessionStorage({ cookie: cookieArg } = {}) {
3111
3236
  //#endregion
3112
3237
  //#region lib/server-runtime/sessions/memoryStorage.ts
3113
3238
  /**
3114
- * Creates and returns a simple in-memory SessionStorage object, mostly useful
3115
- * for testing and as a reference implementation.
3239
+ * Creates and returns a simple in-memory SessionStorage object.
3116
3240
  *
3117
- * Note: This storage does not scale beyond a single process, so it is not
3118
- * suitable for most production scenarios.
3241
+ * Intended for local development and testing. It does not scale beyond a single
3242
+ * process, and all session data is lost when the server process stops/restarts.
3243
+ *
3244
+ * @public
3245
+ * @category Utils
3246
+ * @mode framework
3247
+ * @mode data
3248
+ * @param options Options for creating the in-memory session storage.
3249
+ * @returns A {@link SessionStorage} object that stores session data in memory.
3119
3250
  */
3120
3251
  function createMemorySessionStorage({ cookie } = {}) {
3121
3252
  let map = /* @__PURE__ */ new Map();
3122
3253
  return createSessionStorage({
3123
3254
  cookie,
3124
3255
  async createData(data, expires) {
3125
- let id = Math.random().toString(36).substring(2, 10);
3256
+ let id = crypto.randomUUID();
3126
3257
  map.set(id, {
3127
3258
  data,
3128
3259
  expires
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -6,30 +6,30 @@ import { RelativeRoutingType } from "../router/router.js";
6
6
  type ParamKeyValuePair = [string, string];
7
7
  type URLSearchParamsInit = string | ParamKeyValuePair[] | Record<string, string | string[]> | URLSearchParams;
8
8
  /**
9
- Creates a URLSearchParams object using the given initializer.
10
-
11
- This is identical to `new URLSearchParams(init)` except it also
12
- supports arrays as values in the object form of the initializer
13
- instead of just strings. This is convenient when you need multiple
14
- values for a given key, but don't want to use an array initializer.
15
-
16
- For example, instead of:
17
-
18
- ```tsx
19
- let searchParams = new URLSearchParams([
20
- ['sort', 'name'],
21
- ['sort', 'price']
22
- ]);
23
- ```
24
- you can do:
25
-
26
- ```
27
- let searchParams = createSearchParams({
28
- sort: ['name', 'price']
29
- });
30
- ```
31
-
32
- @category Utils
9
+ * Creates a URLSearchParams object using the given initializer.
10
+ *
11
+ * This is identical to `new URLSearchParams(init)` except it also supports
12
+ * arrays as values in the object form of the initializer instead of just
13
+ * strings. This is convenient when you need multiple values for a given key,
14
+ * but don't want to use an array initializer.
15
+ *
16
+ * @example
17
+ * // Instead of:
18
+ * let searchParams = new URLSearchParams([
19
+ * ["sort", "name"],
20
+ * ["sort", "price"],
21
+ * ]);
22
+ *
23
+ * // You can do:
24
+ * let searchParams = createSearchParams({
25
+ * sort: ["name", "price"],
26
+ * });
27
+ *
28
+ * @public
29
+ * @category Utils
30
+ * @param init The value used to initialize the URL search parameters.
31
+ * @returns A URLSearchParams object containing the initialized search
32
+ * parameters.
33
33
  */
34
34
  declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams;
35
35
  type JsonObject = { [Key in string]: JsonValue } & { [Key in string]?: JsonValue | undefined };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -30,30 +30,30 @@ function shouldProcessLinkClick(event, target) {
30
30
  return event.button === 0 && (!target || target === "_self") && !isModifiedEvent(event);
31
31
  }
32
32
  /**
33
- Creates a URLSearchParams object using the given initializer.
34
-
35
- This is identical to `new URLSearchParams(init)` except it also
36
- supports arrays as values in the object form of the initializer
37
- instead of just strings. This is convenient when you need multiple
38
- values for a given key, but don't want to use an array initializer.
39
-
40
- For example, instead of:
41
-
42
- ```tsx
43
- let searchParams = new URLSearchParams([
44
- ['sort', 'name'],
45
- ['sort', 'price']
46
- ]);
47
- ```
48
- you can do:
49
-
50
- ```
51
- let searchParams = createSearchParams({
52
- sort: ['name', 'price']
53
- });
54
- ```
55
-
56
- @category Utils
33
+ * Creates a URLSearchParams object using the given initializer.
34
+ *
35
+ * This is identical to `new URLSearchParams(init)` except it also supports
36
+ * arrays as values in the object form of the initializer instead of just
37
+ * strings. This is convenient when you need multiple values for a given key,
38
+ * but don't want to use an array initializer.
39
+ *
40
+ * @example
41
+ * // Instead of:
42
+ * let searchParams = new URLSearchParams([
43
+ * ["sort", "name"],
44
+ * ["sort", "price"],
45
+ * ]);
46
+ *
47
+ * // You can do:
48
+ * let searchParams = createSearchParams({
49
+ * sort: ["name", "price"],
50
+ * });
51
+ *
52
+ * @public
53
+ * @category Utils
54
+ * @param init The value used to initialize the URL search parameters.
55
+ * @returns A URLSearchParams object containing the initialized search
56
+ * parameters.
57
57
  */
58
58
  function createSearchParams(init = "") {
59
59
  return new URLSearchParams(typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams ? init : Object.keys(init).reduce((memo, key) => {
@@ -1409,7 +1409,8 @@ declare namespace ScrollRestoration {
1409
1409
  * for this navigation. To apply specific styles during the transition, see
1410
1410
  * {@link useViewTransitionState}. Defaults to `false`.
1411
1411
  * @param options.defaultShouldRevalidate Specify the default revalidation
1412
- * behavior for the navigation. Defaults to `true`.
1412
+ * behavior for the navigation. When not specified, loaders revalidate
1413
+ * according to the router's standard revalidation behavior.
1413
1414
  * @param options.mask Masked location to display in the browser instead
1414
1415
  * of the router location. Defaults to `undefined`.
1415
1416
  * @param options.useTransitions Wraps the navigation in
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -22,7 +22,7 @@ import * as React$1 from "react";
22
22
  //#region lib/dom/lib.tsx
23
23
  const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
24
24
  try {
25
- if (isBrowser) window.__reactRouterVersion = "8.1.0";
25
+ if (isBrowser) window.__reactRouterVersion = "8.3.0";
26
26
  } catch (e) {}
27
27
  /**
28
28
  * Create a new {@link DataRouter| data router} that manages the application
@@ -386,7 +386,7 @@ const NavLink = React$1.forwardRef(function NavLinkWithRef({ "aria-current": ari
386
386
  if (nextLocationPathname && basename) nextLocationPathname = stripBasename(nextLocationPathname, basename) || nextLocationPathname;
387
387
  const endSlashPosition = toPathname !== "/" && toPathname.endsWith("/") ? toPathname.length - 1 : toPathname.length;
388
388
  let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/";
389
- let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/");
389
+ let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(endSlashPosition) === "/");
390
390
  let renderProps = {
391
391
  isActive,
392
392
  isPending,
@@ -609,7 +609,8 @@ function useDataRouterState(hookName) {
609
609
  * for this navigation. To apply specific styles during the transition, see
610
610
  * {@link useViewTransitionState}. Defaults to `false`.
611
611
  * @param options.defaultShouldRevalidate Specify the default revalidation
612
- * behavior for the navigation. Defaults to `true`.
612
+ * behavior for the navigation. When not specified, loaders revalidate
613
+ * according to the router's standard revalidation behavior.
613
614
  * @param options.mask Masked location to display in the browser instead
614
615
  * of the router location. Defaults to `undefined`.
615
616
  * @param options.useTransitions Wraps the navigation in
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.1.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *