@rangojs/router 0.26.6 → 0.28.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 (234) hide show
  1. package/dist/bin/rango.js +1 -1
  2. package/dist/types/__internal.d.ts +7 -1
  3. package/dist/types/browser/action-coordinator.d.ts +0 -4
  4. package/dist/types/browser/action-fence.d.ts +11 -3
  5. package/dist/types/browser/event-controller.d.ts +20 -1
  6. package/dist/types/browser/history-state.d.ts +7 -9
  7. package/dist/types/browser/intercept-utils.d.ts +23 -0
  8. package/dist/types/browser/navigation-store.d.ts +8 -6
  9. package/dist/types/browser/react/filter-segment-order.d.ts +2 -2
  10. package/dist/types/browser/rsc-router.d.ts +1 -1
  11. package/dist/types/browser/segment-reconciler.d.ts +6 -4
  12. package/dist/types/browser/types.d.ts +2 -1
  13. package/dist/types/browser/wire-protocol.d.ts +25 -0
  14. package/dist/types/cache/cache-key-utils.d.ts +11 -5
  15. package/dist/types/cache/cache-scope.d.ts +34 -22
  16. package/dist/types/cache/cf/cf-cache-constants.d.ts +2 -2
  17. package/dist/types/cache/cf/cf-cache-store.d.ts +47 -65
  18. package/dist/types/cache/cf/cf-kv-utils.d.ts +1 -1
  19. package/dist/types/cache/handle-snapshot.d.ts +9 -4
  20. package/dist/types/cache/segment-codec.d.ts +14 -0
  21. package/dist/types/cache/shell-snapshot.d.ts +1 -1
  22. package/dist/types/cache/tag-invalidation.d.ts +10 -0
  23. package/dist/types/cache/taint.d.ts +5 -3
  24. package/dist/types/cache/types.d.ts +7 -3
  25. package/dist/types/cache/vercel/vercel-cache-store.d.ts +8 -6
  26. package/dist/types/prefetch-markers.d.ts +4 -6
  27. package/dist/types/prerender/store.d.ts +31 -0
  28. package/dist/types/prerender/warm-request.d.ts +3 -3
  29. package/dist/types/prerender.d.ts +2 -2
  30. package/dist/types/route-name.d.ts +5 -0
  31. package/dist/types/router/error-handling.d.ts +4 -4
  32. package/dist/types/router/instrument.d.ts +4 -3
  33. package/dist/types/router/intercept-resolution.d.ts +25 -9
  34. package/dist/types/router/match-context.d.ts +12 -11
  35. package/dist/types/router/match-handlers.d.ts +2 -6
  36. package/dist/types/router/match-middleware/background-revalidation.d.ts +10 -20
  37. package/dist/types/router/match-middleware/cache-lookup.d.ts +35 -87
  38. package/dist/types/router/match-middleware/cache-store.d.ts +2 -2
  39. package/dist/types/router/match-middleware/segment-resolution.d.ts +8 -6
  40. package/dist/types/router/prerender-match.d.ts +21 -6
  41. package/dist/types/router/preview-match.d.ts +2 -7
  42. package/dist/types/router/request-classification.d.ts +5 -0
  43. package/dist/types/router/revalidation.d.ts +6 -1
  44. package/dist/types/router/router-context.d.ts +9 -63
  45. package/dist/types/router/router-interfaces.d.ts +8 -14
  46. package/dist/types/router/segment-resolution/loader-cache.d.ts +3 -3
  47. package/dist/types/router/segment-resolution/loader-mask.d.ts +4 -4
  48. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +20 -18
  49. package/dist/types/router/segment-resolution/resolution-pass.d.ts +93 -0
  50. package/dist/types/router/segment-resolution/revalidation.d.ts +82 -31
  51. package/dist/types/router/segment-resolution.d.ts +1 -2
  52. package/dist/types/router/segment-wrappers.d.ts +11 -48
  53. package/dist/types/router/telemetry.d.ts +2 -0
  54. package/dist/types/router/types.d.ts +10 -0
  55. package/dist/types/rsc/full-payload.d.ts +2 -2
  56. package/dist/types/rsc/handler-context.d.ts +6 -6
  57. package/dist/types/rsc/json-route-result.d.ts +5 -9
  58. package/dist/types/rsc/request-stages.d.ts +203 -0
  59. package/dist/types/rsc/response-cache-serve.d.ts +7 -12
  60. package/dist/types/rsc/response-route-handler.d.ts +2 -2
  61. package/dist/types/rsc/routine-plan.d.ts +1 -1
  62. package/dist/types/rsc/server-action.d.ts +2 -2
  63. package/dist/types/rsc/shell-capture-constants.d.ts +2 -2
  64. package/dist/types/rsc/shell-capture.d.ts +132 -15
  65. package/dist/types/rsc/shell-serve.d.ts +37 -12
  66. package/dist/types/server/context.d.ts +9 -8
  67. package/dist/types/server/handle-store.d.ts +16 -0
  68. package/dist/types/server/render-pass.d.ts +72 -0
  69. package/dist/types/server/request-context.d.ts +24 -104
  70. package/dist/types/server/rewrite-trail.d.ts +2 -2
  71. package/dist/types/testing/dispatch.d.ts +35 -18
  72. package/dist/types/testing/serve-shell-request.d.ts +7 -0
  73. package/dist/types/types/cache-types.d.ts +6 -3
  74. package/dist/types/types/error-types.d.ts +9 -1
  75. package/dist/types/types/handler-context.d.ts +18 -7
  76. package/dist/types/types/segments.d.ts +1 -1
  77. package/dist/types/vite/discovery/build-discovery-phase.d.ts +40 -0
  78. package/dist/types/vite/discovery/build-env.d.ts +22 -0
  79. package/dist/types/vite/discovery/bundle-scan-phase.d.ts +12 -0
  80. package/dist/types/vite/discovery/dev-prerender-cache.d.ts +3 -2
  81. package/dist/types/vite/discovery/dev-server-phase.d.ts +26 -0
  82. package/dist/types/vite/discovery/discovery-errors.d.ts +1 -1
  83. package/dist/types/vite/discovery/state.d.ts +12 -0
  84. package/dist/types/vite/plugins/version-plugin.d.ts +1 -1
  85. package/dist/types/vite/plugins/virtual-entries.d.ts +1 -1
  86. package/dist/types/vite/router-discovery.d.ts +8 -47
  87. package/dist/types/vite/temp-rsc-server.d.ts +39 -0
  88. package/dist/types/vite/utils/prerender-utils.d.ts +9 -0
  89. package/dist/types/wire-params.d.ts +34 -0
  90. package/dist/vite/index.js +3809 -3710
  91. package/package.json +1 -1
  92. package/skills/cache-guide/SKILL.md +8 -5
  93. package/skills/caching/SKILL.md +37 -18
  94. package/skills/client-urls/SKILL.md +2 -1
  95. package/skills/document-cache/SKILL.md +18 -1
  96. package/skills/handler-use/SKILL.md +1 -1
  97. package/skills/intercept/SKILL.md +53 -13
  98. package/skills/layout/SKILL.md +11 -7
  99. package/skills/loader/SKILL.md +30 -13
  100. package/skills/middleware/SKILL.md +19 -1
  101. package/skills/migrate-nextjs/SKILL.md +5 -4
  102. package/skills/parallel/SKILL.md +5 -4
  103. package/skills/ppr/SKILL.md +19 -3
  104. package/skills/prerender/SKILL.md +39 -15
  105. package/skills/rango/SKILL.md +23 -11
  106. package/skills/server-actions/SKILL.md +51 -6
  107. package/skills/testing/predicates.md +1 -1
  108. package/skills/testing/response-routes.md +6 -4
  109. package/skills/testing/serve-shell-request.md +7 -7
  110. package/skills/use-cache/SKILL.md +46 -8
  111. package/skills/ux-architecture/SKILL.md +3 -2
  112. package/src/__internal.ts +7 -1
  113. package/src/browser/action-coordinator.ts +2 -13
  114. package/src/browser/action-fence.ts +11 -3
  115. package/src/browser/connection-warmup.ts +3 -1
  116. package/src/browser/event-controller.ts +42 -3
  117. package/src/browser/history-state.ts +15 -13
  118. package/src/browser/intercept-utils.ts +51 -0
  119. package/src/browser/navigation-bridge.ts +34 -30
  120. package/src/browser/navigation-client.ts +21 -33
  121. package/src/browser/navigation-store.ts +25 -27
  122. package/src/browser/navigation-transaction.ts +7 -11
  123. package/src/browser/partial-update.ts +8 -0
  124. package/src/browser/prefetch/cache.ts +2 -1
  125. package/src/browser/prefetch/fetch.ts +8 -16
  126. package/src/browser/prefetch/queue.ts +4 -4
  127. package/src/browser/react/NavigationProvider.tsx +2 -1
  128. package/src/browser/react/filter-segment-order.ts +2 -2
  129. package/src/browser/rsc-router.tsx +17 -2
  130. package/src/browser/segment-reconciler.ts +66 -53
  131. package/src/browser/server-action-bridge.ts +265 -127
  132. package/src/browser/types.ts +2 -1
  133. package/src/browser/wire-protocol.ts +41 -0
  134. package/src/cache/cache-key-utils.ts +11 -5
  135. package/src/cache/cache-runtime.ts +110 -50
  136. package/src/cache/cache-scope.ts +110 -64
  137. package/src/cache/cf/cf-cache-constants.ts +2 -2
  138. package/src/cache/cf/cf-cache-store.ts +486 -874
  139. package/src/cache/cf/cf-kv-utils.ts +1 -1
  140. package/src/cache/document-cache.ts +42 -15
  141. package/src/cache/handle-snapshot.ts +19 -7
  142. package/src/cache/segment-codec.ts +55 -0
  143. package/src/cache/shell-snapshot.ts +2 -2
  144. package/src/cache/tag-invalidation.ts +21 -2
  145. package/src/cache/taint.ts +5 -3
  146. package/src/cache/types.ts +7 -3
  147. package/src/cache/vercel/vercel-cache-store.ts +243 -310
  148. package/src/handles/pushed-by.ts +1 -1
  149. package/src/prefetch-markers.ts +4 -7
  150. package/src/prerender/store.ts +76 -0
  151. package/src/prerender/warm-request.ts +8 -4
  152. package/src/prerender.ts +2 -2
  153. package/src/route-name.ts +8 -0
  154. package/src/router/error-handling.ts +4 -4
  155. package/src/router/instrument.ts +4 -3
  156. package/src/router/intercept-resolution.ts +77 -75
  157. package/src/router/loader-resolution.ts +5 -4
  158. package/src/router/match-api.ts +4 -5
  159. package/src/router/match-context.ts +33 -12
  160. package/src/router/match-handlers.ts +8 -20
  161. package/src/router/match-middleware/background-revalidation.ts +41 -53
  162. package/src/router/match-middleware/cache-lookup.ts +680 -720
  163. package/src/router/match-middleware/cache-store.ts +14 -12
  164. package/src/router/match-middleware/intercept-resolution.ts +3 -23
  165. package/src/router/match-middleware/segment-resolution.ts +34 -61
  166. package/src/router/match-result.ts +1 -20
  167. package/src/router/middleware.ts +69 -11
  168. package/src/router/navigation-snapshot.ts +4 -3
  169. package/src/router/prerender-match.ts +100 -44
  170. package/src/router/preview-match.ts +6 -41
  171. package/src/router/request-classification.ts +13 -7
  172. package/src/router/revalidation.ts +33 -7
  173. package/src/router/route-snapshot.ts +2 -5
  174. package/src/router/router-context.ts +14 -117
  175. package/src/router/router-interfaces.ts +8 -14
  176. package/src/router/segment-resolution/loader-cache.ts +14 -9
  177. package/src/router/segment-resolution/loader-mask.ts +5 -5
  178. package/src/router/segment-resolution/prefetch-deferral.ts +30 -27
  179. package/src/router/segment-resolution/resolution-pass.ts +137 -0
  180. package/src/router/segment-resolution/revalidation.ts +897 -776
  181. package/src/router/segment-resolution.ts +4 -13
  182. package/src/router/segment-wrappers.ts +51 -269
  183. package/src/router/telemetry.ts +5 -0
  184. package/src/router/types.ts +10 -0
  185. package/src/router.ts +12 -13
  186. package/src/rsc/full-payload.ts +6 -5
  187. package/src/rsc/handler-context.ts +8 -14
  188. package/src/rsc/handler.ts +126 -507
  189. package/src/rsc/json-route-result.ts +5 -9
  190. package/src/rsc/loader-fetch.ts +6 -5
  191. package/src/rsc/progressive-enhancement.ts +5 -0
  192. package/src/rsc/request-stages.ts +757 -0
  193. package/src/rsc/response-cache-serve.ts +10 -14
  194. package/src/rsc/response-route-handler.ts +13 -5
  195. package/src/rsc/rewrite-passes.ts +2 -1
  196. package/src/rsc/routine-plan.ts +1 -1
  197. package/src/rsc/rsc-rendering.ts +172 -349
  198. package/src/rsc/server-action.ts +87 -113
  199. package/src/rsc/shell-capture-constants.ts +3 -1
  200. package/src/rsc/shell-capture.ts +212 -169
  201. package/src/rsc/shell-serve.ts +121 -18
  202. package/src/rsc/ssr-setup.ts +5 -4
  203. package/src/segment-system.tsx +3 -3
  204. package/src/server/context.ts +22 -25
  205. package/src/server/handle-store.ts +55 -0
  206. package/src/server/render-pass.ts +291 -0
  207. package/src/server/request-context.ts +26 -115
  208. package/src/server/rewrite-trail.ts +6 -4
  209. package/src/ssr/index.tsx +1 -1
  210. package/src/ssr/ssr-root.tsx +1 -0
  211. package/src/testing/dispatch.ts +223 -710
  212. package/src/testing/e2e/page-helpers.ts +2 -2
  213. package/src/testing/serve-shell-request.ts +37 -32
  214. package/src/types/cache-types.ts +6 -3
  215. package/src/types/error-types.ts +9 -1
  216. package/src/types/handler-context.ts +21 -7
  217. package/src/types/segments.ts +1 -1
  218. package/src/vite/discovery/build-discovery-phase.ts +204 -0
  219. package/src/vite/discovery/build-env.ts +148 -0
  220. package/src/vite/discovery/bundle-scan-phase.ts +128 -0
  221. package/src/vite/discovery/dev-prerender-cache.ts +8 -3
  222. package/src/vite/discovery/dev-server-phase.ts +1776 -0
  223. package/src/vite/discovery/discover-routers.ts +1 -1
  224. package/src/vite/discovery/discovery-errors.ts +1 -1
  225. package/src/vite/discovery/prerender-collection.ts +24 -39
  226. package/src/vite/discovery/state.ts +15 -0
  227. package/src/vite/plugins/version-plugin.ts +1 -1
  228. package/src/vite/plugins/virtual-entries.ts +1 -1
  229. package/src/vite/router-discovery.ts +29 -2428
  230. package/src/vite/temp-rsc-server.ts +223 -0
  231. package/src/vite/utils/prerender-utils.ts +47 -0
  232. package/src/wire-params.ts +35 -0
  233. package/dist/types/router/segment-resolution/fresh.d.ts +0 -72
  234. package/src/router/segment-resolution/fresh.ts +0 -945
@@ -24,6 +24,8 @@ import {
24
24
  import { printServerError } from "../server/print-server-error.js";
25
25
  import { observePhase, PHASES } from "../router/instrument.js";
26
26
  import { resolveActionRefId } from "../router/is-action.js";
27
+ import { getInterceptSourceHeader } from "../router/navigation-snapshot.js";
28
+ import type { ActionContext } from "../router/types.js";
27
29
  import type { TraceSpan } from "../router/tracing.js";
28
30
  import { attachTransitionWhen } from "./attach-transition-when.js";
29
31
  import { payloadLocation } from "./full-payload.js";
@@ -50,6 +52,8 @@ import {
50
52
  import { warnNonRedirectActionResponse } from "./runtime-warnings.js";
51
53
  import type { HandlerContext } from "./handler-context.js";
52
54
  import type { MatchResult } from "../types.js";
55
+ import type { HandleData } from "../server/handle-store.js";
56
+ import { isRedirectResponse } from "../response-utils.js";
53
57
  import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
54
58
 
55
59
  /**
@@ -62,8 +66,8 @@ import { INTERNAL_RANGO_DEBUG } from "../internal-debug.js";
62
66
  * the revalidation phase so it runs INSIDE the same route-middleware wrapper as
63
67
  * a successful revalidation — route middleware (context vars, headers, cookies)
64
68
  * must apply to the error render too, matching the module doc's "identical to a
65
- * normal render". When `errorBoundary` is set, `actionContext` is unused (the
66
- * boundary is already matched; no matchPartial is run).
69
+ * normal render". That render is the action's revalidation, cut at the
70
+ * boundary (ActionContext.boundary).
67
71
  */
68
72
  export interface ActionContinuation {
69
73
  returnValue: { ok: boolean; data: unknown };
@@ -195,6 +199,7 @@ export async function executeServerAction<TEnv>(
195
199
  let returnValue: { ok: boolean; data: unknown };
196
200
  let actionStatus = 200;
197
201
  let loadedAction: Function | undefined;
202
+ let errorBoundary: MatchResult | undefined;
198
203
 
199
204
  try {
200
205
  loadedAction = await ctx.loadServerAction(actionId);
@@ -267,14 +272,13 @@ export async function executeServerAction<TEnv>(
267
272
 
268
273
  // Try to render error boundary.
269
274
  // Report the action error first so it is not lost if matchError throws.
270
- let errorResult;
275
+ // The boundary renders in the revalidation phase, so route middleware
276
+ // applies to it as to a success's render (handler.ts
277
+ // executeRenderWithMiddleware).
271
278
  try {
272
- errorResult = await ctx.router.matchError(
273
- request,
274
- { env },
275
- error,
276
- "route",
277
- );
279
+ errorBoundary =
280
+ (await ctx.router.matchError(request, { env }, error, "route")) ??
281
+ undefined;
278
282
  } catch (matchErr) {
279
283
  // matchError failed — report the original action error as unhandled,
280
284
  // then let the matchError failure propagate.
@@ -293,32 +297,8 @@ export async function executeServerAction<TEnv>(
293
297
  url,
294
298
  env,
295
299
  actionId,
296
- handledByBoundary: !!errorResult,
300
+ handledByBoundary: !!errorBoundary,
297
301
  });
298
-
299
- if (errorResult) {
300
- // Defer the error-boundary render to the revalidation phase so it runs
301
- // inside the same route-middleware wrapper as a successful revalidation
302
- // (handler.ts executeRenderWithMiddleware). Building + returning the
303
- // Response here would bypass route middleware: context vars, headers, and
304
- // cookies set by route middleware would NOT apply to the error render,
305
- // diverging from the success path and from the module doc's "identical to
306
- // a normal render". The boundary is already matched; the render in
307
- // revalidateAfterAction uses errorResult directly (no matchPartial).
308
- return {
309
- returnValue,
310
- actionStatus,
311
- temporaryReferences,
312
- // actionContext is unused on the errorBoundary path (no matchPartial).
313
- actionContext: {
314
- actionId,
315
- actionUrl: new URL(url),
316
- actionResult: returnValue.data,
317
- formData: actionFormData,
318
- },
319
- errorBoundary: errorResult,
320
- };
321
- }
322
302
  }
323
303
 
324
304
  // Build continuation for the revalidation phase
@@ -337,6 +317,7 @@ export async function executeServerAction<TEnv>(
337
317
  actionResult: returnValue.data,
338
318
  formData: actionFormData,
339
319
  },
320
+ ...(errorBoundary && { errorBoundary }),
340
321
  };
341
322
  }
342
323
 
@@ -456,9 +437,9 @@ async function revalidateAfterActionInner<TEnv>(
456
437
  }
457
438
 
458
439
  /**
459
- * The action-revalidation plan: render the deferred error boundary if the
460
- * action threw and one matched, otherwise match affected segments (falling
461
- * back to a full match for redirects) and render the partial payload.
440
+ * The action-revalidation plan: match the affected segments, cut at the error
441
+ * boundary when the action threw into one (a full match for a redirect
442
+ * route), and render the partial payload.
462
443
  */
463
444
  function* actionRevalidationPlan<TEnv>(
464
445
  input: ActionRevalidationInput<TEnv>,
@@ -466,12 +447,14 @@ function* actionRevalidationPlan<TEnv>(
466
447
  const { ctx, request, env, url, continuation } = input;
467
448
  const { actionContext, errorBoundary } = continuation;
468
449
 
469
- // Action threw and a boundary matched: render the (already-matched) error
470
- // boundary here so it runs inside the route-middleware wrapper, exactly like
471
- // the success branch below.
450
+ // Action threw and a boundary matched: render it here so it runs inside the
451
+ // route-middleware wrapper, exactly like the success branch below.
472
452
  if (errorBoundary) {
453
+ const boundaryResult = yield* step("match:partial", () =>
454
+ matchBoundaryRevalidation(input, errorBoundary),
455
+ );
473
456
  return yield* step("render", () =>
474
- renderActionBoundaryResponse(input, errorBoundary),
457
+ renderActionResponse(input, boundaryResult),
475
458
  );
476
459
  }
477
460
 
@@ -510,9 +493,7 @@ function* actionRevalidationPlan<TEnv>(
510
493
  );
511
494
  }
512
495
 
513
- return yield* step("render", () =>
514
- renderRevalidationResponse(input, matchResult),
515
- );
496
+ return yield* step("render", () => renderActionResponse(input, matchResult));
516
497
  }
517
498
 
518
499
  /** matchPartial that stamps params/routeName when a result exists. */
@@ -520,7 +501,7 @@ async function matchPartialAndRecord<TEnv>(
520
501
  ctx: HandlerContext<TEnv>,
521
502
  request: Request,
522
503
  env: TEnv,
523
- actionContext: ActionContinuation["actionContext"],
504
+ actionContext: ActionContext,
524
505
  ): Promise<
525
506
  Awaited<ReturnType<HandlerContext<TEnv>["router"]["matchPartial"]>>
526
507
  > {
@@ -536,82 +517,67 @@ async function matchPartialAndRecord<TEnv>(
536
517
  }
537
518
 
538
519
  /**
539
- * Render the deferred action error boundary. setRequestContextParams + the
540
- * payload mirror the pre-deferral render that executeServerAction used to do
541
- * inline.
520
+ * A thrown action's boundary response: its revalidation, cut at the boundary
521
+ * (ActionContext.boundary). matchError's result alone, which streams no
522
+ * handle values, for an action sent from an intercept, and when the cut
523
+ * revalidation returns null, misses the boundary or throws anything but a
524
+ * redirect.
542
525
  */
543
- function renderActionBoundaryResponse<TEnv>(
544
- input: ActionRevalidationInput<TEnv>,
526
+ async function matchBoundaryRevalidation<TEnv>(
527
+ { ctx, request, env, continuation, reqCtx }: ActionRevalidationInput<TEnv>,
545
528
  errorBoundary: MatchResult,
546
- ): Promise<Response> {
547
- const { ctx, request, env, url, handleStore, continuation, reqCtx } = input;
548
- const { returnValue, actionStatus, temporaryReferences, actionContext } =
549
- continuation;
550
-
529
+ ): Promise<MatchResult> {
530
+ // matchError returns the error segment alone.
531
+ const boundary = errorBoundary.segments[0]!;
532
+ if (!getInterceptSourceHeader(request)) {
533
+ let failure: string | undefined;
534
+ try {
535
+ const matchResult = await matchPartialAndRecord(ctx, request, env, {
536
+ ...continuation.actionContext,
537
+ boundary,
538
+ });
539
+ if (
540
+ matchResult?.segments.some(
541
+ (s) => s.id === boundary.id && s.type === "error",
542
+ )
543
+ ) {
544
+ return matchResult;
545
+ }
546
+ if (matchResult) failure = "did not reach the boundary";
547
+ } catch (error) {
548
+ // Redirects as from a success's revalidation (handler.ts
549
+ // executeRenderWithMiddleware). matchError's 500 was the boundary's.
550
+ if (error instanceof Response && isRedirectResponse(error)) {
551
+ reqCtx._setStatus(200);
552
+ throw error;
553
+ }
554
+ failure = `threw ${error instanceof Response ? `a ${error.status} Response` : String(error)}`;
555
+ }
556
+ if (failure && process.env.NODE_ENV !== "production") {
557
+ console.warn(
558
+ `[@rangojs/router] Server action "${continuation.actionContext.actionId}" ` +
559
+ `threw into an error boundary whose revalidation ${failure}. The ` +
560
+ `response carries the error segment alone, without the loaders above it.`,
561
+ );
562
+ }
563
+ }
551
564
  setRequestContextParams(errorBoundary.params, errorBoundary.routeName);
552
-
553
- const errorPayload: RscPayload = {
554
- metadata: {
555
- ...payloadLocation(reqCtx, url),
556
- // routerId exposed for the frontend (current app identity); see
557
- // rsc-rendering.ts partial branch.
558
- routerId: ctx.router.id,
559
- segments: attachTransitionWhen(errorBoundary.segments, reqCtx),
560
- isPartial: true,
561
- matched: errorBoundary.matched,
562
- diff: errorBoundary.diff,
563
- resolvedIds: errorBoundary.resolvedIds,
564
- params: errorBoundary.params,
565
- routeName: publicRouteName(errorBoundary.routeName),
566
- isError: true,
567
- handles: handleStore.stream(),
568
- version: ctx.version,
569
- },
570
- returnValue,
571
- };
572
-
573
- // Intentionally omit attachLocationState for error payloads: location state
574
- // is a success-only semantic. Error boundary responses update the error UI
575
- // but should not mutate browser history state.
576
-
577
- return renderRscResponse({
578
- ctx,
579
- request,
580
- env,
581
- url,
582
- payload: errorPayload,
583
- temporaryReferences,
584
- init: {
585
- status: actionStatus,
586
- headers: {
587
- "content-type": "text/x-component;charset=utf-8",
588
- // Router identity for the client's pre-decode integrity check (the
589
- // action apply path has no post-decode guard). See response-adapter.
590
- "X-RSC-Router-Id": ctx.router.id,
591
- },
592
- },
593
- tracking: {
594
- mode: "action-revalidation",
595
- routeKey: input.reqCtx._routeName,
596
- actionId: actionContext?.actionId,
597
- span: input.renderSpan,
598
- onEvent:
599
- reqCtx._activeRoutine &&
600
- createRenderStageTraceBridge(reqCtx._activeRoutine),
601
- },
602
- });
565
+ return errorBoundary;
603
566
  }
604
567
 
605
- /** Render the updated segments after a successful action. */
606
- function renderRevalidationResponse<TEnv>(
568
+ /**
569
+ * Render the action's revalidation, or a thrown action's boundary response.
570
+ * An error payload attaches no location state: that is a success-only
571
+ * semantic, and the boundary must not mutate history state.
572
+ */
573
+ function renderActionResponse<TEnv>(
607
574
  input: ActionRevalidationInput<TEnv>,
608
- matchResult: NonNullable<
609
- Awaited<ReturnType<HandlerContext<TEnv>["router"]["matchPartial"]>>
610
- >,
575
+ matchResult: MatchResult,
611
576
  ): Promise<Response> {
612
577
  const { ctx, request, env, url, handleStore, continuation, reqCtx } = input;
613
578
  const { returnValue, actionStatus, temporaryReferences, actionContext } =
614
579
  continuation;
580
+ const isError = !!continuation.errorBoundary;
615
581
 
616
582
  const payload: RscPayload = {
617
583
  metadata: {
@@ -627,13 +593,18 @@ function renderRevalidationResponse<TEnv>(
627
593
  params: matchResult.params,
628
594
  routeName: publicRouteName(matchResult.routeName),
629
595
  slots: matchResult.slots,
630
- handles: handleStore.stream(),
596
+ ...(isError && { isError }),
597
+ // matchError's result: the abandoned walk's pushes are not the page's.
598
+ handles:
599
+ matchResult === continuation.errorBoundary
600
+ ? noHandles()
601
+ : handleStore.stream(),
631
602
  version: ctx.version,
632
603
  },
633
604
  returnValue,
634
605
  };
635
606
 
636
- attachLocationStateIfPresent(payload);
607
+ if (!isError) attachLocationStateIfPresent(payload);
637
608
 
638
609
  return renderRscResponse({
639
610
  ctx,
@@ -662,3 +633,6 @@ function renderRevalidationResponse<TEnv>(
662
633
  },
663
634
  });
664
635
  }
636
+
637
+ /** The handle stream of matchBoundaryRevalidation's fallback. */
638
+ async function* noHandles(): AsyncGenerator<HandleData, void, unknown> {}
@@ -52,6 +52,8 @@ export const PPR_REPLAY_STATUS_HEADER: string = "x-rango-ppr-replay";
52
52
  */
53
53
  export const PPR_REPLAY_BYPASS_REASONS = [
54
54
  "method",
55
+ // A stale request (`_rsc_stale`) renders live, as an action does.
56
+ "stale",
55
57
  "dynamic",
56
58
  "nonce",
57
59
  "store-unavailable",
@@ -198,7 +200,7 @@ export const POST_QUIESCE_TASK_HOPS = 16;
198
200
  * short is an acceptable loss — every terminal path backs the key off anyway),
199
201
  * and below workerd's ~30s waitUntil grace so the cap timer still fires in a
200
202
  * live context. When workerd kills the context BEFORE the cap fires, the timer
201
- * dies with it — that stranding is healed by scheduleShellCapture's staleness
203
+ * dies with it — that stranding is healed by planShellCapture's staleness
202
204
  * check on the stampede guard, keyed off the same constant.
203
205
  */
204
206
  export const SHELL_CAPTURE_TASK_HARD_CAP_MS = 25_000;