react-router 8.2.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 (169) hide show
  1. package/CHANGELOG.md +67 -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 +106 -17
  5. package/dist/development/index-react-server.js +168 -42
  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.js +1 -1
  34. package/dist/development/lib/href.d.ts +24 -8
  35. package/dist/development/lib/href.js +28 -11
  36. package/dist/development/lib/router/history.d.ts +6 -0
  37. package/dist/development/lib/router/history.js +7 -1
  38. package/dist/development/lib/router/instrumentation.js +1 -1
  39. package/dist/development/lib/router/router.js +1 -1
  40. package/dist/development/lib/router/url.js +1 -1
  41. package/dist/development/lib/router/utils.d.ts +12 -0
  42. package/dist/development/lib/router/utils.js +57 -3
  43. package/dist/development/lib/rsc/browser.js +34 -24
  44. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  45. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  46. package/dist/development/lib/rsc/html-stream/server.js +29 -10
  47. package/dist/development/lib/rsc/route-modules.js +1 -1
  48. package/dist/development/lib/rsc/server.rsc.d.ts +5 -1
  49. package/dist/development/lib/rsc/server.ssr.d.ts +29 -6
  50. package/dist/development/lib/rsc/server.ssr.js +27 -12
  51. package/dist/development/lib/server-runtime/cookies.d.ts +28 -2
  52. package/dist/development/lib/server-runtime/cookies.js +17 -3
  53. package/dist/development/lib/server-runtime/crypto.js +1 -1
  54. package/dist/development/lib/server-runtime/data.js +1 -1
  55. package/dist/development/lib/server-runtime/dev.js +1 -1
  56. package/dist/development/lib/server-runtime/entry.js +1 -1
  57. package/dist/development/lib/server-runtime/errors.js +1 -1
  58. package/dist/development/lib/server-runtime/headers.js +1 -1
  59. package/dist/development/lib/server-runtime/invariant.js +1 -1
  60. package/dist/development/lib/server-runtime/mode.js +1 -1
  61. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  62. package/dist/development/lib/server-runtime/routes.js +1 -1
  63. package/dist/development/lib/server-runtime/server.d.ts +12 -0
  64. package/dist/development/lib/server-runtime/server.js +13 -1
  65. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  66. package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  67. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +9 -1
  68. package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  69. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +12 -6
  70. package/dist/development/lib/server-runtime/sessions.d.ts +31 -2
  71. package/dist/development/lib/server-runtime/sessions.js +20 -3
  72. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  73. package/dist/development/lib/server-runtime/urls.js +1 -1
  74. package/dist/development/lib/server-runtime/warnings.js +1 -1
  75. package/dist/development/lib/types/internal.js +1 -1
  76. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  77. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  78. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  79. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  80. package/dist/production/dom-export.js +1 -1
  81. package/dist/production/index-react-server-client.js +1 -1
  82. package/dist/production/index-react-server.d.ts +106 -17
  83. package/dist/production/index-react-server.js +168 -42
  84. package/dist/production/index.js +1 -1
  85. package/dist/production/lib/actions.js +1 -1
  86. package/dist/production/lib/components.js +1 -1
  87. package/dist/production/lib/context.js +1 -1
  88. package/dist/production/lib/dom/dom.d.ts +24 -24
  89. package/dist/production/lib/dom/dom.js +25 -25
  90. package/dist/production/lib/dom/lib.d.ts +2 -1
  91. package/dist/production/lib/dom/lib.js +5 -4
  92. package/dist/production/lib/dom/server.js +1 -1
  93. package/dist/production/lib/dom/ssr/components.js +1 -1
  94. package/dist/production/lib/dom/ssr/data.js +1 -1
  95. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  96. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  97. package/dist/production/lib/dom/ssr/fog-of-war.js +26 -21
  98. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  99. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  100. package/dist/production/lib/dom/ssr/links.js +1 -1
  101. package/dist/production/lib/dom/ssr/markup.js +1 -1
  102. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  103. package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  104. package/dist/production/lib/dom/ssr/routes-test-stub.js +14 -1
  105. package/dist/production/lib/dom/ssr/routes.js +1 -1
  106. package/dist/production/lib/dom/ssr/server.js +1 -1
  107. package/dist/production/lib/dom/ssr/single-fetch.js +1 -1
  108. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  109. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  110. package/dist/production/lib/errors.js +1 -1
  111. package/dist/production/lib/hooks.js +1 -1
  112. package/dist/production/lib/href.d.ts +24 -8
  113. package/dist/production/lib/href.js +28 -11
  114. package/dist/production/lib/router/history.d.ts +6 -0
  115. package/dist/production/lib/router/history.js +7 -1
  116. package/dist/production/lib/router/instrumentation.js +1 -1
  117. package/dist/production/lib/router/router.js +1 -1
  118. package/dist/production/lib/router/url.js +1 -1
  119. package/dist/production/lib/router/utils.d.ts +12 -0
  120. package/dist/production/lib/router/utils.js +57 -3
  121. package/dist/production/lib/rsc/browser.js +34 -24
  122. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  123. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  124. package/dist/production/lib/rsc/html-stream/server.js +29 -10
  125. package/dist/production/lib/rsc/route-modules.js +1 -1
  126. package/dist/production/lib/rsc/server.rsc.d.ts +5 -1
  127. package/dist/production/lib/rsc/server.ssr.d.ts +29 -6
  128. package/dist/production/lib/rsc/server.ssr.js +27 -12
  129. package/dist/production/lib/server-runtime/cookies.d.ts +28 -2
  130. package/dist/production/lib/server-runtime/cookies.js +17 -3
  131. package/dist/production/lib/server-runtime/crypto.js +1 -1
  132. package/dist/production/lib/server-runtime/data.js +1 -1
  133. package/dist/production/lib/server-runtime/dev.js +1 -1
  134. package/dist/production/lib/server-runtime/entry.js +1 -1
  135. package/dist/production/lib/server-runtime/errors.js +1 -1
  136. package/dist/production/lib/server-runtime/headers.js +1 -1
  137. package/dist/production/lib/server-runtime/invariant.js +1 -1
  138. package/dist/production/lib/server-runtime/mode.js +1 -1
  139. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  140. package/dist/production/lib/server-runtime/routes.js +1 -1
  141. package/dist/production/lib/server-runtime/server.d.ts +12 -0
  142. package/dist/production/lib/server-runtime/server.js +13 -1
  143. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  144. package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  145. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +9 -1
  146. package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  147. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +12 -6
  148. package/dist/production/lib/server-runtime/sessions.d.ts +31 -2
  149. package/dist/production/lib/server-runtime/sessions.js +20 -3
  150. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  151. package/dist/production/lib/server-runtime/urls.js +1 -1
  152. package/dist/production/lib/server-runtime/warnings.js +1 -1
  153. package/dist/production/lib/types/internal.js +1 -1
  154. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  155. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  156. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  157. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  158. package/docs/explanation/sessions-and-cookies.md +13 -13
  159. package/docs/explanation/state-management.md +2 -2
  160. package/docs/how-to/presets.md +2 -2
  161. package/docs/how-to/react-server-components.md +53 -1
  162. package/docs/how-to/security.md +8 -1
  163. package/docs/how-to/server-bundles.md +2 -2
  164. package/docs/start/data/route-object.md +2 -2
  165. package/docs/start/data/routing.md +1 -1
  166. package/docs/start/framework/pending-ui.md +1 -1
  167. package/docs/start/framework/route-module.md +8 -8
  168. package/docs/start/framework/routing.md +1 -1
  169. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.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 = {};
@@ -441,6 +447,48 @@ function matchRouteBranch(branch, pathname, allowPartial = false) {
441
447
  return matches;
442
448
  }
443
449
  /**
450
+ * Characters that `encodeURIComponent` escapes but that are valid literally in
451
+ * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`:
452
+ *
453
+ * ```
454
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
455
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
456
+ * ```
457
+ *
458
+ * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @`
459
+ * are delimiters and must be escaped — but in a path segment they carry no
460
+ * special meaning, and browsers keep them literal in `location.pathname`.
461
+ * (`! ' ( ) *` and the unreserved set are already left alone by
462
+ * `encodeURIComponent`, so they need no restoring.)
463
+ */
464
+ const PATH_PARAM_OVERESCAPED = {
465
+ "%24": "$",
466
+ "%26": "&",
467
+ "%2B": "+",
468
+ "%2C": ",",
469
+ "%3A": ":",
470
+ "%3B": ";",
471
+ "%3D": "=",
472
+ "%40": "@"
473
+ };
474
+ /**
475
+ * Encodes a param value for interpolation into a single URL path segment.
476
+ *
477
+ * Escapes characters that would break the path (`/ ? # %`, whitespace,
478
+ * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a
479
+ * path segment untouched. Escaping those would needlessly rewrite URLs — e.g.
480
+ * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers
481
+ * display and match the `+` literally in `location.pathname`.
482
+ *
483
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3))
484
+ *
485
+ * @param value The param value to encode.
486
+ * @returns The encoded value, safe for use as a single path segment.
487
+ */
488
+ function encodePathParam(value) {
489
+ return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]);
490
+ }
491
+ /**
444
492
  * Performs pattern matching on a URL pathname and returns information about
445
493
  * the match.
446
494
  *
@@ -2332,6 +2380,8 @@ const Await = (async ({ children, resolve, errorElement }) => {
2332
2380
  * encoding the {@link unstable_RSCPayload}.
2333
2381
  * @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
2334
2382
  * `loadServerAction` function, used to load a server action by ID.
2383
+ * @param opts.clientVersion A version derived from the client build output used
2384
+ * to detect stale clients during lazy route discovery.
2335
2385
  * @param opts.onError An optional error handler that will be called with any
2336
2386
  * errors that occur during the request processing.
2337
2387
  * @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
@@ -2345,7 +2395,7 @@ const Await = (async ({ children, resolve, errorElement }) => {
2345
2395
  * that contains the [RSC](https://react.dev/reference/rsc/server-components)
2346
2396
  * data for hydration.
2347
2397
  */
2348
- async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse }) {
2398
+ async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, clientVersion, onError, request, routes, generateResponse }) {
2349
2399
  let url = new URL(request.url);
2350
2400
  basename = basename || "/";
2351
2401
  let normalizedPath = url.pathname;
@@ -2363,22 +2413,26 @@ async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryRefe
2363
2413
  });
2364
2414
  const temporaryReferences = createTemporaryReferenceSet();
2365
2415
  const requestUrl = new URL(request.url);
2366
- if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery);
2416
+ if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion);
2367
2417
  let isDataRequest = isReactServerRequest(requestUrl);
2368
2418
  let matches = matchRoutes(routes, url.pathname, basename);
2369
2419
  if (matches) await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
2370
2420
  const leafMatch = matches?.[matches.length - 1];
2371
2421
  if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) return generateResourceResponse(routerRequest, routes, basename, leafMatch.route.id, requestContext, onError);
2372
- let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery);
2422
+ let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion);
2373
2423
  response.headers.set("X-Remix-Response", "yes");
2374
2424
  return response;
2375
2425
  }
2376
- async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
2426
+ async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion) {
2377
2427
  let url = new URL(request.url);
2378
2428
  if (url.toString().length > 7680) return new Response(null, {
2379
2429
  statusText: "Bad Request",
2380
2430
  status: 400
2381
2431
  });
2432
+ if (clientVersion !== void 0 && clientVersion !== url.searchParams.get("version")) return new Response(null, {
2433
+ status: 204,
2434
+ headers: { "X-Remix-Reload-Document": "true" }
2435
+ });
2382
2436
  if (routeDiscovery?.mode === "initial") {
2383
2437
  let payload = {
2384
2438
  type: "manifest",
@@ -2527,7 +2581,7 @@ async function generateResourceResponse(request, routes, basename, routeId, requ
2527
2581
  });
2528
2582
  }
2529
2583
  }
2530
- async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
2584
+ async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion) {
2531
2585
  let statusCode = 200;
2532
2586
  let url = new URL(request.url);
2533
2587
  let isSubmission = isMutationMethod(request.method);
@@ -2548,29 +2602,39 @@ async function generateRenderResponse(request, routes, basename, isDataRequest,
2548
2602
  let formState;
2549
2603
  let skipRevalidation = false;
2550
2604
  let potentialCSRFAttackError;
2551
- if (isMutationMethod(request.method)) try {
2552
- throwIfPotentialCSRFAttack(request, allowedActionOrigins);
2553
- ctx.runningAction = true;
2554
- let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
2555
- ctx.runningAction = false;
2556
- });
2557
- if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2558
- skipRevalidation = result?.skipRevalidation ?? false;
2559
- actionResult = result?.actionResult;
2560
- formState = result?.formState;
2561
- request = result?.revalidationRequest ?? request;
2562
- if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
2563
- } catch (error) {
2564
- potentialCSRFAttackError = error;
2605
+ if (isMutationMethod(request.method)) {
2606
+ try {
2607
+ throwIfPotentialCSRFAttack(request, allowedActionOrigins);
2608
+ } catch (error) {
2609
+ onError?.(error);
2610
+ potentialCSRFAttackError = error;
2611
+ request = new Request(request.url, {
2612
+ method: "GET",
2613
+ headers: request.headers,
2614
+ signal: request.signal
2615
+ });
2616
+ }
2617
+ if (!potentialCSRFAttackError) {
2618
+ ctx.runningAction = true;
2619
+ let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
2620
+ ctx.runningAction = false;
2621
+ });
2622
+ if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2623
+ skipRevalidation = result?.skipRevalidation ?? false;
2624
+ actionResult = result?.actionResult;
2625
+ formState = result?.formState;
2626
+ request = result?.revalidationRequest ?? request;
2627
+ if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
2628
+ }
2565
2629
  }
2566
- let staticContext = await query(request, skipRevalidation || !!potentialCSRFAttackError ? { filterMatchesToLoad: () => false } : void 0);
2630
+ let staticContext = await query(request, skipRevalidation ? { filterMatchesToLoad: () => false } : void 0);
2567
2631
  if (isResponse(staticContext)) return generateRedirectResponse(staticContext, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2568
2632
  if (potentialCSRFAttackError) {
2569
2633
  staticContext.errors ??= {};
2570
2634
  staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
2571
2635
  staticContext.statusCode = 400;
2572
2636
  }
2573
- return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery);
2637
+ return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery, clientVersion);
2574
2638
  }
2575
2639
  }));
2576
2640
  if (isRedirectResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
@@ -2604,7 +2668,7 @@ function generateRedirectResponse(response, actionResult, basename, isDataReques
2604
2668
  onError: defaultOnError
2605
2669
  });
2606
2670
  }
2607
- async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
2671
+ async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery, clientVersion) {
2608
2672
  statusCode = staticContext.statusCode ?? statusCode;
2609
2673
  if (staticContext.errors) staticContext.errors = Object.fromEntries(Object.entries(staticContext.errors).map(([key, error]) => [key, isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error]));
2610
2674
  staticContext.matches.forEach((m) => {
@@ -2617,6 +2681,7 @@ async function generateStaticContextResponse(routes, basename, generateResponse,
2617
2681
  const baseRenderPayload = {
2618
2682
  type: "render",
2619
2683
  basename: staticContext.basename,
2684
+ clientVersion,
2620
2685
  routeDiscovery: routeDiscovery ?? { mode: "lazy" },
2621
2686
  actionData: staticContext.actionData,
2622
2687
  errors: staticContext.errors,
@@ -2812,14 +2877,30 @@ function stringify(p) {
2812
2877
  return p == null ? "" : typeof p === "string" ? p : String(p);
2813
2878
  }
2814
2879
  /**
2815
- Returns a resolved URL path for the specified route.
2816
-
2817
- ```tsx
2818
- const h = href("/:lang?/about", { lang: "en" })
2819
- // -> `/en/about`
2820
-
2821
- <Link to={href("/products/:id", { id: "abc123" })} />
2822
- ```
2880
+ * Returns a resolved URL path for the specified route.
2881
+ *
2882
+ * Param values are percent-encoded for use in a path segment: characters that
2883
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
2884
+ * are escaped, while characters that RFC 3986 allows literally in a path
2885
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
2886
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
2887
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
2888
+ * preserving `/` separators.
2889
+ *
2890
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
2891
+ *
2892
+ * @example
2893
+ * const h = href("/:lang?/about", { lang: "en" })
2894
+ * // -> `/en/about`
2895
+ *
2896
+ * <Link to={href("/products/:id", { id: "abc123" })} />
2897
+ *
2898
+ * @public
2899
+ * @category Utils
2900
+ * @mode framework
2901
+ * @param path The route path to resolve
2902
+ * @param args The route params to use when resolving the path
2903
+ * @returns The resolved URL path
2823
2904
  */
2824
2905
  function href(path, ...args) {
2825
2906
  let params = args[0];
@@ -2827,11 +2908,11 @@ function href(path, ...args) {
2827
2908
  const isRequired = questionMark === void 0;
2828
2909
  const value = params?.[param];
2829
2910
  if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
2830
- return value == null ? "" : "/" + encodeURIComponent(stringify(value));
2911
+ return value == null ? "" : "/" + encodePathParam(stringify(value));
2831
2912
  });
2832
2913
  if (path.endsWith("*")) {
2833
2914
  const value = params?.["*"];
2834
- if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/");
2915
+ if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/");
2835
2916
  }
2836
2917
  return result || "/";
2837
2918
  }
@@ -2884,6 +2965,14 @@ function byteStringToUint8Array(byteString) {
2884
2965
  //#region lib/server-runtime/cookies.ts
2885
2966
  /**
2886
2967
  * Creates a logical container for managing a browser cookie from the server.
2968
+ *
2969
+ * @public
2970
+ * @category Utils
2971
+ * @mode framework
2972
+ * @mode data
2973
+ * @param name The name of the cookie.
2974
+ * @param cookieOptions Options for parsing and serializing the cookie.
2975
+ * @returns A {@link Cookie} object for parsing and serializing the cookie.
2887
2976
  */
2888
2977
  const createCookie = (name, cookieOptions = {}) => {
2889
2978
  let { secrets = [], ...options } = {
@@ -2923,9 +3012,15 @@ const createCookie = (name, cookieOptions = {}) => {
2923
3012
  };
2924
3013
  };
2925
3014
  /**
2926
- * Returns true if an object is a Remix cookie container.
3015
+ * Returns `true` if a value is a React Router {@link Cookie} object.
2927
3016
  *
2928
- * @see https://remix.run/utils/cookies#iscookie
3017
+ * @public
3018
+ * @category Utils
3019
+ * @mode framework
3020
+ * @mode data
3021
+ * @param object The value to check.
3022
+ * @returns `true` if the value is a React Router {@link Cookie} object;
3023
+ * otherwise, `false`.
2929
3024
  */
2930
3025
  const isCookie = (object) => {
2931
3026
  return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
@@ -3015,6 +3110,12 @@ function flash(name) {
3015
3110
  *
3016
3111
  * Note: This function is typically not invoked directly by application code.
3017
3112
  * Instead, use a `SessionStorage` object's `getSession` method.
3113
+ *
3114
+ * @category Utils
3115
+ * @param initialData The initial data for the session.
3116
+ * @param id The identifier for the session. Defaults to an empty string for a
3117
+ * new session.
3118
+ * @returns A new {@link Session} object.
3018
3119
  */
3019
3120
  const createSession = (initialData = {}, id = "") => {
3020
3121
  let map = new Map(Object.entries(initialData));
@@ -3049,9 +3150,15 @@ const createSession = (initialData = {}, id = "") => {
3049
3150
  };
3050
3151
  };
3051
3152
  /**
3052
- * Returns true if an object is a React Router session.
3153
+ * Returns `true` if a value is a React Router {@link Session} object.
3053
3154
  *
3054
- * @see https://reactrouter.com/api/utils/isSession
3155
+ * @public
3156
+ * @category Utils
3157
+ * @mode framework
3158
+ * @mode data
3159
+ * @param object The value to check.
3160
+ * @returns `true` if the value is a React Router {@link Session} object;
3161
+ * otherwise, `false`.
3055
3162
  */
3056
3163
  const isSession = (object) => {
3057
3164
  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";
@@ -3061,6 +3168,11 @@ const isSession = (object) => {
3061
3168
  *
3062
3169
  * Note: This is a low-level API that should only be used if none of the
3063
3170
  * existing session storage options meet your requirements.
3171
+ *
3172
+ * @category Utils
3173
+ * @param strategy The strategy used to store session identifiers and data.
3174
+ * @returns A {@link SessionStorage} object that persists session data using the
3175
+ * provided strategy.
3064
3176
  */
3065
3177
  function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) {
3066
3178
  let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
@@ -3100,6 +3212,14 @@ function warnOnceAboutSigningSessionCookie(cookie) {
3100
3212
  * needed, and can help to simplify some load-balanced scenarios. However, it
3101
3213
  * also has the limitation that serialized session data may not exceed the
3102
3214
  * browser's maximum cookie size. Trade-offs!
3215
+ *
3216
+ * @public
3217
+ * @category Utils
3218
+ * @mode framework
3219
+ * @mode data
3220
+ * @param options Options for creating the cookie-backed session storage.
3221
+ * @returns A {@link SessionStorage} object that stores all session data in its
3222
+ * cookie.
3103
3223
  */
3104
3224
  function createCookieSessionStorage({ cookie: cookieArg } = {}) {
3105
3225
  let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
@@ -3125,18 +3245,24 @@ function createCookieSessionStorage({ cookie: cookieArg } = {}) {
3125
3245
  //#endregion
3126
3246
  //#region lib/server-runtime/sessions/memoryStorage.ts
3127
3247
  /**
3128
- * Creates and returns a simple in-memory SessionStorage object, mostly useful
3129
- * for testing and as a reference implementation.
3248
+ * Creates and returns a simple in-memory SessionStorage object.
3130
3249
  *
3131
- * Note: This storage does not scale beyond a single process, so it is not
3132
- * suitable for most production scenarios.
3250
+ * Intended for local development and testing. It does not scale beyond a single
3251
+ * process, and all session data is lost when the server process stops/restarts.
3252
+ *
3253
+ * @public
3254
+ * @category Utils
3255
+ * @mode framework
3256
+ * @mode data
3257
+ * @param options Options for creating the in-memory session storage.
3258
+ * @returns A {@link SessionStorage} object that stores session data in memory.
3133
3259
  */
3134
3260
  function createMemorySessionStorage({ cookie } = {}) {
3135
3261
  let map = /* @__PURE__ */ new Map();
3136
3262
  return createSessionStorage({
3137
3263
  cookie,
3138
3264
  async createData(data, expires) {
3139
- let id = Math.random().toString(36).substring(2, 10);
3265
+ let id = crypto.randomUUID();
3140
3266
  map.set(id, {
3141
3267
  data,
3142
3268
  expires
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.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.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *