@rangojs/router 0.24.4 → 0.25.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.
- package/README.md +1 -0
- package/dist/bin/rango.js +363 -39
- package/dist/testing/vitest.js +37 -4
- package/dist/types/browser/event-controller.d.ts +24 -5
- package/dist/types/browser/intercept-utils.d.ts +5 -2
- package/dist/types/browser/merge-segment-loaders.d.ts +0 -23
- package/dist/types/browser/navigation-transaction.d.ts +5 -5
- package/dist/types/browser/partial-update.d.ts +21 -13
- package/dist/types/browser/prefetch/cache.d.ts +8 -0
- package/dist/types/browser/rango-marker.d.ts +7 -0
- package/dist/types/browser/react/filter-segment-order.d.ts +10 -0
- package/dist/types/browser/react/use-handle.d.ts +20 -0
- package/dist/types/browser/react/use-href.d.ts +6 -0
- package/dist/types/browser/react/use-router.d.ts +6 -1
- package/dist/types/browser/response-adapter.d.ts +5 -3
- package/dist/types/browser/scroll-restoration.d.ts +9 -3
- package/dist/types/browser/segment-reconciler.d.ts +10 -13
- package/dist/types/browser/types.d.ts +38 -8
- package/dist/types/build/route-types/per-module-writer.d.ts +12 -0
- package/dist/types/cache/cf/cf-shell-frame.d.ts +2 -0
- package/dist/types/cache/types.d.ts +8 -0
- package/dist/types/client-urls/client-urls.d.ts +6 -2
- package/dist/types/client-urls/revalidate-registry.d.ts +1 -1
- package/dist/types/client-urls/server-projection.d.ts +9 -5
- package/dist/types/client-urls/types.d.ts +29 -16
- package/dist/types/client.d.ts +2 -2
- package/dist/types/client.rsc.d.ts +2 -2
- package/dist/types/decode-loader-results.d.ts +28 -7
- package/dist/types/href-client.d.ts +3 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/loader-redirect.d.ts +2 -3
- package/dist/types/outlet-context.d.ts +7 -6
- package/dist/types/prefetch-markers.d.ts +14 -5
- package/dist/types/route-content-wrapper.d.ts +17 -14
- package/dist/types/route-definition/helpers-types.d.ts +7 -2
- package/dist/types/route-definition/resolve-handler-use.d.ts +6 -1
- package/dist/types/route-definition/rewrite.d.ts +48 -5
- package/dist/types/route-types.d.ts +2 -2
- package/dist/types/router/handler-context.d.ts +14 -2
- package/dist/types/router/intercept-resolution.d.ts +19 -46
- package/dist/types/router/lazy-includes.d.ts +7 -0
- package/dist/types/router/match-middleware/intercept-resolution.d.ts +2 -2
- package/dist/types/router/middleware.d.ts +0 -16
- package/dist/types/router/navigation-snapshot.d.ts +2 -0
- package/dist/types/router/prefetch-cache-ttl.d.ts +8 -11
- package/dist/types/router/revalidation.d.ts +8 -1
- package/dist/types/router/router-context.d.ts +4 -16
- package/dist/types/router/router-interfaces.d.ts +0 -6
- package/dist/types/router/router-options.d.ts +4 -7
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +31 -8
- package/dist/types/router/segment-resolution/revalidation.d.ts +38 -4
- package/dist/types/router/segment-resolution.d.ts +1 -1
- package/dist/types/router/segment-wrappers.d.ts +3 -5
- package/dist/types/router/substitute-pattern-params.d.ts +3 -2
- package/dist/types/router/tracing.d.ts +2 -2
- package/dist/types/rsc/full-payload.d.ts +14 -2
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/manifest-init.d.ts +20 -0
- package/dist/types/rsc/prefetch-render-error.d.ts +22 -0
- package/dist/types/rsc/progressive-enhancement.d.ts +20 -0
- package/dist/types/rsc/rewrite-passes.d.ts +12 -2
- package/dist/types/rsc/server-action.d.ts +6 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +10 -1
- package/dist/types/rsc/shell-capture.d.ts +7 -0
- package/dist/types/rsc/types.d.ts +5 -0
- package/dist/types/segment-system.d.ts +15 -0
- package/dist/types/server/context.d.ts +64 -4
- package/dist/types/server/request-context.d.ts +41 -4
- package/dist/types/server/request-headers.d.ts +12 -0
- package/dist/types/server/rewrite-trail.d.ts +8 -1
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +1 -1
- package/dist/types/testing/internal/client-urls-projection.d.ts +4 -3
- package/dist/types/testing/render-route.d.ts +21 -11
- package/dist/types/testing/run-loader.d.ts +7 -0
- package/dist/types/testing/run-middleware.d.ts +7 -0
- package/dist/types/testing/run-revalidate.d.ts +5 -4
- package/dist/types/testing/serve-shell-request.d.ts +41 -4
- package/dist/types/testing/shell-status.d.ts +17 -0
- package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
- package/dist/types/testing/vitest.d.ts +10 -1
- package/dist/types/thenable-status.d.ts +0 -6
- package/dist/types/types/handler-context.d.ts +12 -1
- package/dist/types/types/loader-types.d.ts +4 -3
- package/dist/types/types/route-entry.d.ts +2 -1
- package/dist/types/urls/path-helper-types.d.ts +4 -2
- package/dist/types/vite/discovery/client-urls-projection.d.ts +55 -5
- package/dist/types/vite/discovery/state.d.ts +20 -0
- package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +28 -16
- package/dist/types/vite/plugins/hoist/source.d.ts +8 -7
- package/dist/types/vite/plugins/virtual-entries.d.ts +6 -0
- package/dist/types/vite/utils/prerender-utils.d.ts +7 -0
- package/dist/vite/index.js +6816 -6547
- package/package.json +1 -1
- package/skills/app-structure/SKILL.md +255 -0
- package/skills/app-structure/content-site.md +333 -0
- package/skills/app-structure/e-commerce.md +435 -0
- package/skills/app-structure/large-app.md +885 -0
- package/skills/app-structure/multi-app.md +280 -0
- package/skills/breadcrumbs/SKILL.md +6 -1
- package/skills/caching/SKILL.md +3 -1
- package/skills/catalog.json +12 -0
- package/skills/client-urls/SKILL.md +185 -32
- package/skills/composability/SKILL.md +10 -8
- package/skills/handler-use/SKILL.md +48 -9
- package/skills/hooks/handle-and-actions.md +104 -0
- package/skills/hooks/navigation.md +24 -10
- package/skills/hooks/urls.md +1 -1
- package/skills/i18n/SKILL.md +2 -1
- package/skills/intercept/SKILL.md +60 -29
- package/skills/layout/SKILL.md +6 -3
- package/skills/links/SKILL.md +151 -17
- package/skills/loader/SKILL.md +32 -10
- package/skills/middleware/SKILL.md +111 -15
- package/skills/observability/SKILL.md +11 -0
- package/skills/parallel/SKILL.md +5 -5
- package/skills/ppr/SKILL.md +43 -8
- package/skills/prerender/SKILL.md +1 -2
- package/skills/rango/SKILL.md +3 -0
- package/skills/response-routes/SKILL.md +4 -1
- package/skills/scripts/SKILL.md +12 -1
- package/skills/server-actions/SKILL.md +60 -20
- package/skills/testing/SKILL.md +1 -0
- package/skills/testing/client-components.md +13 -10
- package/skills/testing/loader.md +25 -24
- package/skills/testing/middleware.md +11 -11
- package/skills/testing/response-routes.md +4 -1
- package/skills/testing/serve-shell-request.md +85 -21
- package/skills/ux-architecture/SKILL.md +980 -0
- package/skills/view-transitions/SKILL.md +43 -10
- package/src/__augment-tests__/augmented.check.ts +28 -0
- package/src/browser/event-controller.ts +44 -18
- package/src/browser/intercept-utils.ts +3 -2
- package/src/browser/merge-segment-loaders.ts +0 -85
- package/src/browser/navigation-bridge.ts +112 -84
- package/src/browser/navigation-client.ts +20 -15
- package/src/browser/navigation-store.ts +4 -2
- package/src/browser/navigation-transaction.ts +9 -8
- package/src/browser/partial-update.ts +90 -32
- package/src/browser/prefetch/cache.ts +8 -0
- package/src/browser/prefetch/fetch.ts +13 -3
- package/src/browser/rango-marker.ts +13 -0
- package/src/browser/react/NavigationProvider.tsx +25 -7
- package/src/browser/react/filter-segment-order.ts +13 -0
- package/src/browser/react/use-action.ts +9 -15
- package/src/browser/react/use-handle.ts +63 -71
- package/src/browser/react/use-href.tsx +24 -5
- package/src/browser/react/use-link-status.ts +1 -1
- package/src/browser/react/use-navigation.ts +30 -40
- package/src/browser/react/use-router.ts +37 -6
- package/src/browser/react/use-segments.ts +8 -12
- package/src/browser/response-adapter.ts +11 -5
- package/src/browser/rsc-router.tsx +3 -1
- package/src/browser/scroll-restoration.ts +34 -20
- package/src/browser/segment-reconciler.ts +11 -29
- package/src/browser/server-action-bridge.ts +52 -16
- package/src/browser/types.ts +40 -8
- package/src/build/generate-manifest.ts +12 -1
- package/src/build/route-types/codegen.ts +6 -2
- package/src/build/route-types/per-module-writer.ts +94 -61
- package/src/cache/cache-runtime.ts +9 -0
- package/src/cache/cache-scope.ts +2 -1
- package/src/cache/cf/cf-cache-store.ts +2 -0
- package/src/cache/cf/cf-shell-frame.ts +3 -0
- package/src/cache/types.ts +8 -0
- package/src/cache/vercel/vercel-cache-store.ts +6 -1
- package/src/client-urls/client-urls.ts +202 -153
- package/src/client-urls/revalidate-registry.ts +12 -3
- package/src/client-urls/server-projection.ts +137 -66
- package/src/client-urls/types.ts +30 -15
- package/src/client.rsc.tsx +11 -1
- package/src/client.tsx +9 -1
- package/src/decode-loader-results.ts +64 -31
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +1 -0
- package/src/index.ts +12 -4
- package/src/loader-redirect.tsx +2 -3
- package/src/outlet-context.ts +7 -6
- package/src/prefetch-markers.ts +15 -5
- package/src/reverse.ts +3 -3
- package/src/route-content-wrapper.tsx +19 -17
- package/src/route-definition/dsl-helpers.ts +27 -7
- package/src/route-definition/helpers-types.ts +7 -2
- package/src/route-definition/resolve-handler-use.ts +23 -10
- package/src/route-definition/rewrite.ts +86 -19
- package/src/route-types.ts +1 -2
- package/src/router/find-match.ts +37 -1
- package/src/router/handler-context.ts +85 -40
- package/src/router/intercept-resolution.ts +161 -307
- package/src/router/lazy-includes.ts +43 -27
- package/src/router/loader-resolution.ts +21 -1
- package/src/router/manifest.ts +6 -8
- package/src/router/match-api.ts +11 -1
- package/src/router/match-middleware/background-revalidation.ts +2 -4
- package/src/router/match-middleware/cache-lookup.ts +16 -20
- package/src/router/match-middleware/intercept-resolution.ts +41 -60
- package/src/router/match-result.ts +31 -2
- package/src/router/middleware.ts +6 -147
- package/src/router/navigation-snapshot.ts +38 -2
- package/src/router/prefetch-cache-ttl.ts +10 -18
- package/src/router/prerender-match.ts +2 -1
- package/src/router/revalidation.ts +24 -3
- package/src/router/router-context.ts +5 -18
- package/src/router/router-interfaces.ts +0 -7
- package/src/router/router-options.ts +4 -7
- package/src/router/segment-resolution/fresh.ts +6 -2
- package/src/router/segment-resolution/helpers.ts +7 -1
- package/src/router/segment-resolution/prefetch-deferral.ts +62 -12
- package/src/router/segment-resolution/revalidation.ts +75 -35
- package/src/router/segment-resolution.ts +0 -1
- package/src/router/segment-wrappers.ts +8 -13
- package/src/router/substitute-pattern-params.ts +3 -2
- package/src/router/tracing.ts +2 -2
- package/src/router.ts +2 -9
- package/src/rsc/full-payload.ts +26 -4
- package/src/rsc/handler.ts +83 -41
- package/src/rsc/helpers.ts +13 -0
- package/src/rsc/manifest-init.ts +56 -3
- package/src/rsc/prefetch-render-error.ts +38 -0
- package/src/rsc/progressive-enhancement.ts +161 -211
- package/src/rsc/rewrite-passes.ts +56 -9
- package/src/rsc/rsc-rendering.ts +79 -23
- package/src/rsc/server-action.ts +25 -2
- package/src/rsc/shell-capture-constants.ts +22 -0
- package/src/rsc/shell-capture.ts +11 -0
- package/src/rsc/types.ts +5 -0
- package/src/segment-system.tsx +125 -119
- package/src/server/context.ts +96 -5
- package/src/server/handle-store.ts +3 -2
- package/src/server/request-context.ts +42 -3
- package/src/server/request-headers.ts +32 -0
- package/src/server/rewrite-trail.ts +55 -9
- package/src/ssr/ssr-root.tsx +6 -10
- package/src/testing/dispatch.ts +4 -5
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +1 -0
- package/src/testing/internal/client-urls-projection.ts +31 -11
- package/src/testing/render-route.tsx +83 -40
- package/src/testing/run-loader.ts +26 -2
- package/src/testing/run-middleware.ts +10 -0
- package/src/testing/run-revalidate.ts +13 -6
- package/src/testing/serve-shell-request.ts +77 -4
- package/src/testing/shell-status.ts +40 -0
- package/src/testing/vitest-stubs/version.ts +1 -0
- package/src/testing/vitest.ts +74 -7
- package/src/thenable-status.ts +0 -11
- package/src/types/handler-context.ts +12 -1
- package/src/types/loader-types.ts +4 -3
- package/src/types/route-entry.ts +2 -1
- package/src/urls/include-helper.ts +38 -11
- package/src/urls/path-helper-types.ts +4 -2
- package/src/vite/discovery/client-urls-projection.ts +205 -16
- package/src/vite/discovery/discover-routers.ts +2 -2
- package/src/vite/discovery/prerender-collection.ts +2 -6
- package/src/vite/discovery/state.ts +26 -0
- package/src/vite/plugins/client-urls-revalidate-hoist.ts +190 -77
- package/src/vite/plugins/expose-internal-ids.ts +46 -40
- package/src/vite/plugins/hoist/source.ts +8 -7
- package/src/vite/plugins/transition-when-hoist.ts +1 -0
- package/src/vite/plugins/version-plugin.ts +7 -0
- package/src/vite/plugins/version.d.ts +7 -0
- package/src/vite/plugins/virtual-entries.ts +20 -0
- package/src/vite/rango.ts +4 -1
- package/src/vite/router-discovery.ts +39 -7
- package/src/vite/utils/prerender-utils.ts +17 -0
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
deferUnit,
|
|
44
44
|
fillSends,
|
|
45
45
|
getPrefetchDeferral,
|
|
46
|
+
interceptFillSkips,
|
|
46
47
|
registerDeferredLoader,
|
|
47
48
|
} from "./prefetch-deferral.js";
|
|
48
49
|
import {
|
|
@@ -58,6 +59,7 @@ import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
|
58
59
|
import { isLayoutLoaderCopy } from "../../client-urls/layout-loader-copies.js";
|
|
59
60
|
import { getRouterContext } from "../router-context.js";
|
|
60
61
|
import { observeEvent, observeSegmentHandler } from "../instrument.js";
|
|
62
|
+
import { withReverseScope } from "../handler-context.js";
|
|
61
63
|
import { runInSegmentTagScope } from "../../cache/cache-tag.js";
|
|
62
64
|
import { trackStreamedHandler } from "./streamed-handler-boundary.js";
|
|
63
65
|
import {
|
|
@@ -156,7 +158,7 @@ const deferredLoaderRuns = new WeakMap<ResolvedSegment, () => unknown>();
|
|
|
156
158
|
* awaits; a streamed handler that reads the loader later is not seen, and
|
|
157
159
|
* that loader runs again in the fill.
|
|
158
160
|
*/
|
|
159
|
-
function deliverStartedLoaders<TEnv>(
|
|
161
|
+
export function deliverStartedLoaders<TEnv>(
|
|
160
162
|
ctx: HandlerContext<any, TEnv>,
|
|
161
163
|
segments: ResolvedSegment[],
|
|
162
164
|
): void {
|
|
@@ -180,14 +182,51 @@ function deliverStartedLoaders<TEnv>(
|
|
|
180
182
|
// ---------------------------------------------------------------------------
|
|
181
183
|
|
|
182
184
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
+
* Whose loaders a resolveOwnerLoaders call resolves: a route entry's (or the
|
|
186
|
+
* entry its loaders are inherited into), or an intercept's, whose loaders are
|
|
187
|
+
* loader segments of its slot (`${parent}.${slot}D${i}.${$$id}`).
|
|
185
188
|
*/
|
|
186
|
-
export
|
|
189
|
+
export interface LoaderOwner {
|
|
190
|
+
readonly loaders: readonly LoaderEntry[];
|
|
191
|
+
/** The segments' namespace: the entry id, or `intercept:${routeName}`. */
|
|
192
|
+
readonly namespace: string;
|
|
193
|
+
/** The segment id the loader ids extend and the handle pushes file under. */
|
|
194
|
+
readonly segmentPrefix: string;
|
|
195
|
+
/** Where a loader error renders (wrapLoaderPromise). */
|
|
196
|
+
readonly boundary: EntryData;
|
|
197
|
+
/**
|
|
198
|
+
* resolveLoaderData's PPR bake/seed key for every loader (loader-cache.ts).
|
|
199
|
+
* False for an intercept: never a document.
|
|
200
|
+
*/
|
|
201
|
+
readonly bakeLane: boolean;
|
|
202
|
+
readonly traceSource: "loader" | "intercept-loader";
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/** A route entry as a LoaderOwner, under `shortCodeOverride` when inherited. */
|
|
206
|
+
export function ownerOf(
|
|
187
207
|
entry: EntryData,
|
|
208
|
+
shortCodeOverride?: string,
|
|
209
|
+
): LoaderOwner {
|
|
210
|
+
return {
|
|
211
|
+
loaders: entry.loader ?? [],
|
|
212
|
+
namespace: entry.id,
|
|
213
|
+
segmentPrefix: shortCodeOverride ?? entry.shortCode,
|
|
214
|
+
boundary: entry,
|
|
215
|
+
bakeLane: !entryLoadingMasksLoaders(entry.loading),
|
|
216
|
+
traceSource: "loader",
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* The partial lane's one loader funnel: each loader is a segment, run when
|
|
222
|
+
* the client lacks it, asked its revalidate() when held, kept outright in a
|
|
223
|
+
* fill (fillSends), deferred in a prefetch that defers it (defersLoader).
|
|
224
|
+
*/
|
|
225
|
+
export async function resolveOwnerLoaders<TEnv>(
|
|
226
|
+
owner: LoaderOwner,
|
|
188
227
|
ctx: HandlerContext<any, TEnv>,
|
|
189
228
|
belongsToRoute: boolean,
|
|
190
|
-
clientSegmentIds:
|
|
229
|
+
clientSegmentIds: ReadonlySet<string>,
|
|
191
230
|
prevParams: Record<string, string>,
|
|
192
231
|
request: Request,
|
|
193
232
|
prevUrl: URL,
|
|
@@ -195,13 +234,12 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
195
234
|
routeKey: string,
|
|
196
235
|
deps: SegmentResolutionDeps<TEnv>,
|
|
197
236
|
actionContext?: ActionContext,
|
|
198
|
-
shortCodeOverride?: string,
|
|
199
237
|
stale?: boolean,
|
|
200
238
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
201
|
-
const loaderEntries =
|
|
239
|
+
const loaderEntries = owner.loaders;
|
|
202
240
|
if (loaderEntries.length === 0) return { segments: [], matchedIds: [] };
|
|
203
241
|
|
|
204
|
-
const shortCode =
|
|
242
|
+
const shortCode = owner.segmentPrefix;
|
|
205
243
|
|
|
206
244
|
// Pin `_currentSegmentId` to the OWNING entry before the kickoffs below —
|
|
207
245
|
// createLoaderExecutor captures it synchronously for ctx.use(Handle) push
|
|
@@ -238,7 +276,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
238
276
|
segmentId,
|
|
239
277
|
segmentType: "loader",
|
|
240
278
|
belongsToRoute,
|
|
241
|
-
source:
|
|
279
|
+
source: owner.traceSource,
|
|
242
280
|
defaultShouldRevalidate: true,
|
|
243
281
|
finalShouldRevalidate: true,
|
|
244
282
|
reason: "new-segment",
|
|
@@ -249,7 +287,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
249
287
|
|
|
250
288
|
const dummySegment: ResolvedSegment = {
|
|
251
289
|
id: segmentId,
|
|
252
|
-
namespace:
|
|
290
|
+
namespace: owner.namespace,
|
|
253
291
|
type: "loader",
|
|
254
292
|
index,
|
|
255
293
|
component: null,
|
|
@@ -268,14 +306,14 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
268
306
|
prevUrl,
|
|
269
307
|
nextUrl,
|
|
270
308
|
revalidations: loaderRevalidateFns.map((fn, j) => ({
|
|
271
|
-
name:
|
|
309
|
+
name: `${owner.traceSource}-revalidate${j}`,
|
|
272
310
|
fn,
|
|
273
311
|
})),
|
|
274
312
|
routeKey,
|
|
275
313
|
context: ctx,
|
|
276
314
|
actionContext,
|
|
277
315
|
stale,
|
|
278
|
-
traceSource:
|
|
316
|
+
traceSource: owner.traceSource,
|
|
279
317
|
held: clientSegmentIds,
|
|
280
318
|
});
|
|
281
319
|
},
|
|
@@ -300,7 +338,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
300
338
|
// Outside capture the key only activates the _shellLoaderSeed overlay,
|
|
301
339
|
// which serveShellHit seeds for a document HIT and the PPR partial replay
|
|
302
340
|
// (matchPartialWithPprReplay) seeds once its doc record hits.
|
|
303
|
-
const bakeLane =
|
|
341
|
+
const bakeLane = owner.bakeLane;
|
|
304
342
|
|
|
305
343
|
const kickoff = (
|
|
306
344
|
loaderEntry: LoaderEntry,
|
|
@@ -319,6 +357,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
319
357
|
bakeLane || loaderEntry.bake === true ? segmentId : null,
|
|
320
358
|
),
|
|
321
359
|
loaderId,
|
|
360
|
+
loaderEntry.reverseScope,
|
|
322
361
|
);
|
|
323
362
|
const start = (
|
|
324
363
|
loaderEntry: LoaderEntry,
|
|
@@ -327,7 +366,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
327
366
|
) =>
|
|
328
367
|
deps.wrapLoaderPromise(
|
|
329
368
|
kickoff(loaderEntry, segmentId, loaderId),
|
|
330
|
-
|
|
369
|
+
owner.boundary,
|
|
331
370
|
segmentId,
|
|
332
371
|
ctx.pathname,
|
|
333
372
|
errorContext,
|
|
@@ -338,7 +377,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
338
377
|
({ loaderEntry, loader, segmentId, index }) => {
|
|
339
378
|
const base = {
|
|
340
379
|
id: segmentId,
|
|
341
|
-
namespace:
|
|
380
|
+
namespace: owner.namespace,
|
|
342
381
|
type: "loader" as const,
|
|
343
382
|
index,
|
|
344
383
|
component: null,
|
|
@@ -411,8 +450,8 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
411
450
|
seenIds.has(`${sc}D${i}.${le.loader.$$id}`),
|
|
412
451
|
);
|
|
413
452
|
if (!allAlreadySeen) {
|
|
414
|
-
const { segments, matchedIds } = await
|
|
415
|
-
entry,
|
|
453
|
+
const { segments, matchedIds } = await resolveOwnerLoaders(
|
|
454
|
+
ownerOf(entry, shortCodeOverride),
|
|
416
455
|
context,
|
|
417
456
|
belongsToRoute,
|
|
418
457
|
clientSegmentIds,
|
|
@@ -423,7 +462,6 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
423
462
|
routeKey,
|
|
424
463
|
deps,
|
|
425
464
|
actionContext,
|
|
426
|
-
shortCodeOverride,
|
|
427
465
|
stale,
|
|
428
466
|
);
|
|
429
467
|
for (const seg of segments) {
|
|
@@ -454,8 +492,8 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
454
492
|
entry.loader.length > 0 &&
|
|
455
493
|
Object.keys(layoutEntry.parallel).length > 0
|
|
456
494
|
) {
|
|
457
|
-
const inherited = await
|
|
458
|
-
entry,
|
|
495
|
+
const inherited = await resolveOwnerLoaders(
|
|
496
|
+
ownerOf(entry, layoutEntry.shortCode),
|
|
459
497
|
context,
|
|
460
498
|
childBelongsToRoute,
|
|
461
499
|
clientSegmentIds,
|
|
@@ -466,7 +504,6 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
466
504
|
routeKey,
|
|
467
505
|
deps,
|
|
468
506
|
actionContext,
|
|
469
|
-
layoutEntry.shortCode,
|
|
470
507
|
stale,
|
|
471
508
|
);
|
|
472
509
|
for (const seg of inherited.segments) {
|
|
@@ -594,10 +631,11 @@ async function resolveParallelSlotComponent<TEnv>(args: {
|
|
|
594
631
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
595
632
|
parallelId;
|
|
596
633
|
handlerRan = true;
|
|
634
|
+
const slotContext = withReverseScope(context, parallelEntry.reverseScope);
|
|
597
635
|
if (hasLoadingFallback) {
|
|
598
636
|
const result =
|
|
599
637
|
typeof handler === "function"
|
|
600
|
-
? runInSegmentTagScope(parallelId, handler,
|
|
638
|
+
? runInSegmentTagScope(parallelId, handler, slotContext)
|
|
601
639
|
: handler;
|
|
602
640
|
if (result instanceof Promise) {
|
|
603
641
|
warnOnStreamedResponse(result, parallelId);
|
|
@@ -616,7 +654,7 @@ async function resolveParallelSlotComponent<TEnv>(args: {
|
|
|
616
654
|
} else {
|
|
617
655
|
component =
|
|
618
656
|
typeof handler === "function"
|
|
619
|
-
? await runInSegmentTagScope(parallelId, handler,
|
|
657
|
+
? await runInSegmentTagScope(parallelId, handler, slotContext)
|
|
620
658
|
: handler;
|
|
621
659
|
}
|
|
622
660
|
}
|
|
@@ -697,8 +735,8 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
697
735
|
// emitted either before or after the slot segment per loaderOrder.
|
|
698
736
|
const resolveSlotLoaders = async () => {
|
|
699
737
|
if (resolvedParallelEntries.has(parallelEntry.id)) return;
|
|
700
|
-
const loaderResult = await
|
|
701
|
-
parallelEntry,
|
|
738
|
+
const loaderResult = await resolveOwnerLoaders(
|
|
739
|
+
ownerOf(parallelEntry, entry.shortCode),
|
|
702
740
|
context,
|
|
703
741
|
belongsToRoute,
|
|
704
742
|
clientSegmentIds,
|
|
@@ -709,7 +747,6 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
709
747
|
routeKey,
|
|
710
748
|
deps,
|
|
711
749
|
actionContext,
|
|
712
|
-
entry.shortCode,
|
|
713
750
|
stale,
|
|
714
751
|
);
|
|
715
752
|
segments.push(...loaderResult.segments);
|
|
@@ -1095,8 +1132,8 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
1095
1132
|
|
|
1096
1133
|
const belongsToRoute = entry.type === "route";
|
|
1097
1134
|
|
|
1098
|
-
const loaderResult = await
|
|
1099
|
-
entry,
|
|
1135
|
+
const loaderResult = await resolveOwnerLoaders(
|
|
1136
|
+
ownerOf(entry),
|
|
1100
1137
|
context,
|
|
1101
1138
|
belongsToRoute,
|
|
1102
1139
|
clientSegmentIds,
|
|
@@ -1107,7 +1144,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
1107
1144
|
routeKey,
|
|
1108
1145
|
deps,
|
|
1109
1146
|
actionContext,
|
|
1110
|
-
undefined,
|
|
1111
1147
|
stale,
|
|
1112
1148
|
);
|
|
1113
1149
|
segments.push(...loaderResult.segments);
|
|
@@ -1266,8 +1302,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1266
1302
|
const segments: ResolvedSegment[] = [];
|
|
1267
1303
|
const matchedIds: string[] = [];
|
|
1268
1304
|
|
|
1269
|
-
const loaderResult = await
|
|
1270
|
-
orphan,
|
|
1305
|
+
const loaderResult = await resolveOwnerLoaders(
|
|
1306
|
+
ownerOf(orphan),
|
|
1271
1307
|
context,
|
|
1272
1308
|
belongsToRoute,
|
|
1273
1309
|
clientSegmentIds,
|
|
@@ -1278,7 +1314,6 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1278
1314
|
routeKey,
|
|
1279
1315
|
deps,
|
|
1280
1316
|
actionContext,
|
|
1281
|
-
undefined,
|
|
1282
1317
|
stale,
|
|
1283
1318
|
);
|
|
1284
1319
|
segments.push(...loaderResult.segments);
|
|
@@ -1292,8 +1327,8 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1292
1327
|
parentRouteEntry.loader.length > 0 &&
|
|
1293
1328
|
Object.keys(orphan.parallel).length > 0
|
|
1294
1329
|
) {
|
|
1295
|
-
const inheritedResult = await
|
|
1296
|
-
parentRouteEntry,
|
|
1330
|
+
const inheritedResult = await resolveOwnerLoaders(
|
|
1331
|
+
ownerOf(parentRouteEntry, orphan.shortCode),
|
|
1297
1332
|
context,
|
|
1298
1333
|
belongsToRoute,
|
|
1299
1334
|
clientSegmentIds,
|
|
@@ -1304,7 +1339,6 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1304
1339
|
routeKey,
|
|
1305
1340
|
deps,
|
|
1306
1341
|
actionContext,
|
|
1307
|
-
orphan.shortCode,
|
|
1308
1342
|
stale,
|
|
1309
1343
|
);
|
|
1310
1344
|
// Tag as inherited so buildMatchResult can deduplicate when safe
|
|
@@ -1491,6 +1525,11 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1491
1525
|
latchPprHeaderScopeForEntries(entries, routeKey);
|
|
1492
1526
|
|
|
1493
1527
|
const telemetry = getRouterContext()?.telemetry;
|
|
1528
|
+
const fillMatch = {
|
|
1529
|
+
handlerContext: context,
|
|
1530
|
+
isIntercept: !!interceptResult,
|
|
1531
|
+
clientSegmentSet,
|
|
1532
|
+
};
|
|
1494
1533
|
|
|
1495
1534
|
for (const entry of entries) {
|
|
1496
1535
|
if (entry.type === "route" && interceptResult) {
|
|
@@ -1508,6 +1547,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1508
1547
|
}
|
|
1509
1548
|
continue;
|
|
1510
1549
|
}
|
|
1550
|
+
if (interceptFillSkips(fillMatch, entry.shortCode)) continue;
|
|
1511
1551
|
|
|
1512
1552
|
const nonParallelEntry = entry as Exclude<EntryData, { type: "parallel" }>;
|
|
1513
1553
|
// Same latch as resolveAllSegments (fresh.ts), route-level cache() included.
|
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
} from "../types";
|
|
7
7
|
import type { SegmentResolutionDeps } from "./types.js";
|
|
8
8
|
import type { ResolveSegmentOptions } from "./segment-resolution.js";
|
|
9
|
+
import type { RevalidationContext } from "./router-context.js";
|
|
9
10
|
|
|
10
11
|
import {
|
|
11
12
|
resolveAllSegments as _resolveAllSegments,
|
|
@@ -96,19 +97,15 @@ export interface SegmentWrappers<TEnv = any> {
|
|
|
96
97
|
context: HandlerContext<any, TEnv>,
|
|
97
98
|
belongsToRoute?: boolean,
|
|
98
99
|
revalidationContext?: any,
|
|
99
|
-
options?: {
|
|
100
|
+
options?: { background?: boolean },
|
|
100
101
|
) => Promise<ResolvedSegment[]>;
|
|
101
102
|
resolveInterceptLoadersOnly: (
|
|
102
103
|
interceptEntry: InterceptEntry,
|
|
103
104
|
parentEntry: EntryData,
|
|
104
|
-
params: Record<string, string>,
|
|
105
105
|
context: HandlerContext<any, TEnv>,
|
|
106
|
-
belongsToRoute
|
|
107
|
-
revalidationContext
|
|
108
|
-
) => Promise<
|
|
109
|
-
loaderDataPromise: Promise<any[]> | any[];
|
|
110
|
-
loaderIds: string[];
|
|
111
|
-
} | null>;
|
|
106
|
+
belongsToRoute: boolean,
|
|
107
|
+
revalidationContext: RevalidationContext,
|
|
108
|
+
) => Promise<ResolvedSegment[]>;
|
|
112
109
|
}
|
|
113
110
|
|
|
114
111
|
/**
|
|
@@ -250,7 +247,7 @@ export function createSegmentWrappers<TEnv = any>(
|
|
|
250
247
|
context: HandlerContext<any, TEnv>,
|
|
251
248
|
belongsToRoute: boolean = true,
|
|
252
249
|
revalidationContext?: any,
|
|
253
|
-
options?: {
|
|
250
|
+
options?: { background?: boolean },
|
|
254
251
|
): ReturnType<typeof _resolveInterceptEntry> {
|
|
255
252
|
return _resolveInterceptEntry(
|
|
256
253
|
interceptEntry,
|
|
@@ -267,15 +264,13 @@ export function createSegmentWrappers<TEnv = any>(
|
|
|
267
264
|
function resolveInterceptLoadersOnly(
|
|
268
265
|
interceptEntry: InterceptEntry,
|
|
269
266
|
parentEntry: EntryData,
|
|
270
|
-
params: Record<string, string>,
|
|
271
267
|
context: HandlerContext<any, TEnv>,
|
|
272
|
-
belongsToRoute: boolean
|
|
273
|
-
revalidationContext:
|
|
268
|
+
belongsToRoute: boolean,
|
|
269
|
+
revalidationContext: RevalidationContext,
|
|
274
270
|
): ReturnType<typeof _resolveInterceptLoadersOnly> {
|
|
275
271
|
return _resolveInterceptLoadersOnly(
|
|
276
272
|
interceptEntry,
|
|
277
273
|
parentEntry,
|
|
278
|
-
params,
|
|
279
274
|
context,
|
|
280
275
|
belongsToRoute,
|
|
281
276
|
segmentDeps,
|
|
@@ -20,8 +20,9 @@ import { parsePattern } from "./parse-pattern.js";
|
|
|
20
20
|
* omit when absent.
|
|
21
21
|
*
|
|
22
22
|
* Shared by `ctx.reverse()` (server), `createReverse()` (typed runtime
|
|
23
|
-
* helper),
|
|
24
|
-
*
|
|
23
|
+
* helper), `useReverse()` (client hook), and the include() mount fill of
|
|
24
|
+
* `useHref()` and relative `useRouter().push`. The behavior must stay
|
|
25
|
+
* identical across all of them.
|
|
25
26
|
*/
|
|
26
27
|
export function substitutePatternParams(
|
|
27
28
|
pattern: string,
|
package/src/router/tracing.ts
CHANGED
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
* registry; do not call traceSpan directly from new code.
|
|
25
25
|
*
|
|
26
26
|
* Phase coverage (all via observePhase): rango.request (span-only; handler:total
|
|
27
|
-
* metered directly), rango.middleware (span-only
|
|
28
|
-
*
|
|
27
|
+
* metered directly), rango.middleware (span-only; pre/post metered directly),
|
|
28
|
+
* rango.action (action:<id>; server-action
|
|
29
29
|
* execution, JS + no-JS/PE), rango.loader (loader:<id>; single metering site at
|
|
30
30
|
* useLoader, plus the fetchable path), rango.handler (span-only, one per segment
|
|
31
31
|
* route/layout handler execution; the handler:<id> perf metric is owned by the
|
package/src/router.ts
CHANGED
|
@@ -276,14 +276,8 @@ export function createRouter<TEnv = any>(
|
|
|
276
276
|
);
|
|
277
277
|
|
|
278
278
|
// Resolve prefetch cache TTL (default: 300 seconds / 5 minutes). Clamps to a
|
|
279
|
-
// non-negative integer and guards non-finite (NaN/Infinity) inputs
|
|
280
|
-
|
|
281
|
-
const resolvedPrefetchCacheTTL = resolvePrefetchCacheTTL(
|
|
282
|
-
prefetchCacheTTLOption,
|
|
283
|
-
);
|
|
284
|
-
const prefetchCacheTTL = resolvedPrefetchCacheTTL.ms;
|
|
285
|
-
const prefetchCacheControl: string | false =
|
|
286
|
-
resolvedPrefetchCacheTTL.cacheControl;
|
|
279
|
+
// non-negative integer and guards non-finite (NaN/Infinity) inputs.
|
|
280
|
+
const prefetchCacheTTL = resolvePrefetchCacheTTL(prefetchCacheTTLOption).ms;
|
|
287
281
|
|
|
288
282
|
// Resolve client-side prefetch limits (in-memory cache size and queue
|
|
289
283
|
// concurrency). Both are positive-integer counts; sub-1/non-finite inputs
|
|
@@ -1153,7 +1147,6 @@ export function createRouter<TEnv = any>(
|
|
|
1153
1147
|
cacheProfiles: resolvedCacheProfiles,
|
|
1154
1148
|
|
|
1155
1149
|
// Expose prefetch cache settings
|
|
1156
|
-
prefetchCacheControl,
|
|
1157
1150
|
prefetchCacheTTL,
|
|
1158
1151
|
prefetchCacheSize,
|
|
1159
1152
|
prefetchConcurrency,
|
package/src/rsc/full-payload.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type { RequestContext } from "../server/request-context.js";
|
|
|
15
15
|
import type { HandleStore } from "../server/handle-store.js";
|
|
16
16
|
import type { Theme } from "../theme/types.js";
|
|
17
17
|
import {
|
|
18
|
+
pathKey,
|
|
18
19
|
rendersForSharedAudience,
|
|
19
20
|
rewriteTrail,
|
|
20
21
|
visitorUrl,
|
|
@@ -42,7 +43,7 @@ export function buildFullPayload(
|
|
|
42
43
|
): RscPayload {
|
|
43
44
|
return {
|
|
44
45
|
metadata: {
|
|
45
|
-
|
|
46
|
+
...payloadLocation(reqCtx, url),
|
|
46
47
|
routerId: ctx.router.id,
|
|
47
48
|
basename: ctx.router.basename,
|
|
48
49
|
segments: attachTransitionWhen(m.segments, reqCtx),
|
|
@@ -95,7 +96,8 @@ export function buildFullPayload(
|
|
|
95
96
|
* bar) agree on `usePathname()` and `useSearchParams()`. A render other
|
|
96
97
|
* visitors may be served shows `url`, the target, as a direct visit does: a
|
|
97
98
|
* shell capture, and a render the document cache stores (its key is the
|
|
98
|
-
* target)
|
|
99
|
+
* target), except a visitor-keyed shell capture, whose key covers the
|
|
100
|
+
* visitor's URL. Any other render that shows the visitor's URL marks
|
|
99
101
|
* `_payloadVisitorLocation`, and the document cache does not store it.
|
|
100
102
|
*/
|
|
101
103
|
export function renderedLocation(
|
|
@@ -104,6 +106,7 @@ export function renderedLocation(
|
|
|
104
106
|
RequestContext<any>,
|
|
105
107
|
| "_variables"
|
|
106
108
|
| "_shellCaptureRun"
|
|
109
|
+
| "_shellCaptureForVisitor"
|
|
107
110
|
| "_documentCacheRender"
|
|
108
111
|
| "_payloadVisitorLocation"
|
|
109
112
|
| "res"
|
|
@@ -111,13 +114,32 @@ export function renderedLocation(
|
|
|
111
114
|
| undefined,
|
|
112
115
|
url: URL,
|
|
113
116
|
): URL {
|
|
114
|
-
if (!reqCtx || !rewriteTrail(reqCtx)
|
|
115
|
-
|
|
117
|
+
if (!reqCtx || !rewriteTrail(reqCtx)) return url;
|
|
118
|
+
if (rendersForSharedAudience(reqCtx)) {
|
|
119
|
+
return reqCtx._shellCaptureForVisitor ? visitorUrl(reqCtx, url) : url;
|
|
116
120
|
}
|
|
117
121
|
reqCtx._payloadVisitorLocation = true;
|
|
118
122
|
return visitorUrl(reqCtx, url);
|
|
119
123
|
}
|
|
120
124
|
|
|
125
|
+
/**
|
|
126
|
+
* A payload's `pathname` (renderedLocation) and, when middleware rewrote the
|
|
127
|
+
* request, `rewriteTarget`: the path and search the route rendered (the
|
|
128
|
+
* trail's last URL). The browser keeps it per history entry and sends it
|
|
129
|
+
* back as `_rsc_rt`, so the next request from the page diffs against the
|
|
130
|
+
* route that rendered it (navigation-snapshot.ts resolveNavigation, #1144).
|
|
131
|
+
*/
|
|
132
|
+
export function payloadLocation(
|
|
133
|
+
reqCtx: Parameters<typeof renderedLocation>[0],
|
|
134
|
+
url: URL,
|
|
135
|
+
): { pathname: string; rewriteTarget?: string } {
|
|
136
|
+
const pathname = renderedLocation(reqCtx, url).pathname;
|
|
137
|
+
const trail = rewriteTrail(reqCtx);
|
|
138
|
+
return trail
|
|
139
|
+
? { pathname, rewriteTarget: pathKey(new URL(trail[trail.length - 1]!)) }
|
|
140
|
+
: { pathname };
|
|
141
|
+
}
|
|
142
|
+
|
|
121
143
|
/**
|
|
122
144
|
* The theme a document payload carries as `metadata.initialTheme` (the
|
|
123
145
|
* ThemeProvider's first state), read with no identity guard: it is the
|
package/src/rsc/handler.ts
CHANGED
|
@@ -18,7 +18,8 @@ import {
|
|
|
18
18
|
captureRequestEntryContext,
|
|
19
19
|
} from "../server/request-context.js";
|
|
20
20
|
import { REWRITE_HEADER, runRewritePasses } from "./rewrite-passes.js";
|
|
21
|
-
import {
|
|
21
|
+
import { rewriteTrail } from "../server/rewrite-trail.js";
|
|
22
|
+
import { payloadLocation } from "./full-payload.js";
|
|
22
23
|
import * as rscDeps from "@vitejs/plugin-rsc/rsc/server";
|
|
23
24
|
import type {
|
|
24
25
|
RscPayload,
|
|
@@ -27,6 +28,7 @@ import type {
|
|
|
27
28
|
SSRModule,
|
|
28
29
|
} from "./types.js";
|
|
29
30
|
import {
|
|
31
|
+
actionRenderRequest,
|
|
30
32
|
createReloadResponse,
|
|
31
33
|
createResponseWithMergedHeaders,
|
|
32
34
|
finalizeResponse,
|
|
@@ -79,6 +81,7 @@ import type { SegmentCacheStore } from "../cache/types.js";
|
|
|
79
81
|
import { buildRouterTrieFromUrlpatterns } from "./manifest-init.js";
|
|
80
82
|
import { handleProgressiveEnhancement } from "./progressive-enhancement.js";
|
|
81
83
|
import {
|
|
84
|
+
continuationForRewritePass,
|
|
82
85
|
executeServerAction,
|
|
83
86
|
revalidateAfterAction,
|
|
84
87
|
type ActionContinuation,
|
|
@@ -973,6 +976,32 @@ export function createRSCHandler<
|
|
|
973
976
|
// "middleware-short-circuit").
|
|
974
977
|
getRequestContext()._requestMode = plan.mode;
|
|
975
978
|
|
|
979
|
+
// A rewritten pass of an action must reach the branch that runs it: the
|
|
980
|
+
// one that ran it re-renders from its outcome, and a JS action a global
|
|
981
|
+
// rewrite moved before it ran runs only on the action branch. Any other
|
|
982
|
+
// plan would serve the POST as a fresh request or drop the action; a
|
|
983
|
+
// reload (version mismatch, app switch) does neither.
|
|
984
|
+
const passCtx = getRequestContext();
|
|
985
|
+
const actionRan = passCtx._actionRan;
|
|
986
|
+
const actionMode = actionRan
|
|
987
|
+
? actionRan.kind === "action"
|
|
988
|
+
? "action"
|
|
989
|
+
: "pe-render"
|
|
990
|
+
: url.searchParams.has("_rsc_action") && rewriteTrail(passCtx)
|
|
991
|
+
? "action"
|
|
992
|
+
: undefined;
|
|
993
|
+
if (
|
|
994
|
+
actionMode &&
|
|
995
|
+
plan.mode !== actionMode &&
|
|
996
|
+
plan.mode !== "version-mismatch" &&
|
|
997
|
+
plan.mode !== "app-switch"
|
|
998
|
+
) {
|
|
999
|
+
throw new Error(
|
|
1000
|
+
`A rewrite of a server action or form post must target a page ` +
|
|
1001
|
+
`route; ${url.pathname} ${plan.mode === "response" ? "is a response route" : plan.mode === "redirect" ? "redirects" : "is not one"}.`,
|
|
1002
|
+
);
|
|
1003
|
+
}
|
|
1004
|
+
|
|
976
1005
|
// ---- 2. Terminal plans (no execution needed) ----
|
|
977
1006
|
if (plan.mode === "redirect") {
|
|
978
1007
|
// Redirects are handled by the pipeline (match/matchPartial),
|
|
@@ -1224,51 +1253,64 @@ export function createRSCHandler<
|
|
|
1224
1253
|
|
|
1225
1254
|
// ---- Action: execute action, then revalidate wrapped in route middleware ----
|
|
1226
1255
|
if (plan.mode === "action") {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1256
|
+
const reqCtx = getRequestContext();
|
|
1257
|
+
const ran = reqCtx._actionRan;
|
|
1258
|
+
let actionContinuation: ActionContinuation;
|
|
1259
|
+
if (ran?.kind === "action") {
|
|
1260
|
+
// A rewrite pass after the action ran: never run it again.
|
|
1261
|
+
actionContinuation = await continuationForRewritePass(
|
|
1262
|
+
handlerCtx,
|
|
1263
|
+
request,
|
|
1264
|
+
env,
|
|
1265
|
+
ran.continuation,
|
|
1266
|
+
);
|
|
1267
|
+
} else {
|
|
1268
|
+
try {
|
|
1269
|
+
// Instrument the action execution as its own phase (action:<actionId> +
|
|
1270
|
+
// rango.action), so a POST shows the mutation time AND which action ran,
|
|
1271
|
+
// not just the downstream revalidation render. The action's own
|
|
1272
|
+
// loaders/fetches nest under rango.action.
|
|
1273
|
+
const actionOutcome = await withTimeout(
|
|
1274
|
+
observePhase(PHASES.action(plan.actionId), () =>
|
|
1275
|
+
executeServerAction(
|
|
1276
|
+
handlerCtx,
|
|
1277
|
+
request,
|
|
1278
|
+
env,
|
|
1279
|
+
url,
|
|
1280
|
+
plan.actionId,
|
|
1281
|
+
handleStore,
|
|
1282
|
+
),
|
|
1283
|
+
),
|
|
1284
|
+
router.timeouts.actionMs,
|
|
1285
|
+
"action",
|
|
1286
|
+
);
|
|
1287
|
+
if (actionOutcome.timedOut) {
|
|
1288
|
+
return handleTimeoutResponse(
|
|
1237
1289
|
request,
|
|
1238
1290
|
env,
|
|
1239
1291
|
url,
|
|
1292
|
+
"action",
|
|
1293
|
+
actionOutcome.durationMs,
|
|
1294
|
+
plan.route.routeKey,
|
|
1240
1295
|
plan.actionId,
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
const result = actionOutcome.result;
|
|
1299
|
+
// Response means redirect or error boundary — done.
|
|
1300
|
+
if (result instanceof Response) return result;
|
|
1301
|
+
actionContinuation = result;
|
|
1302
|
+
reqCtx._actionRan = { kind: "action", continuation: result };
|
|
1303
|
+
} catch (error) {
|
|
1304
|
+
callOnError(error, "action", {
|
|
1249
1305
|
request,
|
|
1250
|
-
env,
|
|
1251
1306
|
url,
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
);
|
|
1307
|
+
env,
|
|
1308
|
+
actionId: plan.actionId,
|
|
1309
|
+
handledByBoundary: false,
|
|
1310
|
+
});
|
|
1311
|
+
console.error(`[RSC] Action error:`, error);
|
|
1312
|
+
throw error;
|
|
1257
1313
|
}
|
|
1258
|
-
const result = actionOutcome.result;
|
|
1259
|
-
// Response means redirect or error boundary — done.
|
|
1260
|
-
if (result instanceof Response) return result;
|
|
1261
|
-
actionContinuation = result;
|
|
1262
|
-
} catch (error) {
|
|
1263
|
-
callOnError(error, "action", {
|
|
1264
|
-
request,
|
|
1265
|
-
url,
|
|
1266
|
-
env,
|
|
1267
|
-
actionId: plan.actionId,
|
|
1268
|
-
handledByBoundary: false,
|
|
1269
|
-
});
|
|
1270
|
-
console.error(`[RSC] Action error:`, error);
|
|
1271
|
-
throw error;
|
|
1272
1314
|
}
|
|
1273
1315
|
|
|
1274
1316
|
// Revalidation render wrapped in route middleware.
|
|
@@ -1423,7 +1465,7 @@ export function createRSCHandler<
|
|
|
1423
1465
|
|
|
1424
1466
|
const payload: RscPayload = {
|
|
1425
1467
|
metadata: {
|
|
1426
|
-
|
|
1468
|
+
...payloadLocation(getRequestContext(), url),
|
|
1427
1469
|
routerId: router.id,
|
|
1428
1470
|
basename: router.basename,
|
|
1429
1471
|
segments: [notFoundSegment],
|
|
@@ -1527,7 +1569,7 @@ export function createRSCHandler<
|
|
|
1527
1569
|
if (routeMiddleware.length > 0) {
|
|
1528
1570
|
const mwResponse = await executeMiddleware(
|
|
1529
1571
|
buildRouteMiddlewareEntries<TEnv>(routeMiddleware),
|
|
1530
|
-
request,
|
|
1572
|
+
actionContinuation ? actionRenderRequest(request) : request,
|
|
1531
1573
|
env,
|
|
1532
1574
|
variables,
|
|
1533
1575
|
renderHandler,
|