@rangojs/router 0.22.1 → 0.24.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/dist/bin/rango.js +10 -4
- package/dist/testing/vitest.js +31 -1
- package/dist/types/browser/defer.d.ts +48 -0
- package/dist/types/browser/document-revealed.d.ts +17 -0
- package/dist/types/browser/event-controller.d.ts +7 -1
- package/dist/types/browser/merge-segment-loaders.d.ts +5 -2
- package/dist/types/browser/navigation-transaction.d.ts +21 -7
- package/dist/types/browser/partial-update.d.ts +39 -8
- package/dist/types/browser/react/NavigationProvider.d.ts +19 -5
- package/dist/types/browser/react/context.d.ts +3 -2
- package/dist/types/browser/react/filter-segment-order.d.ts +7 -0
- package/dist/types/browser/response-adapter.d.ts +7 -0
- package/dist/types/browser/types.d.ts +24 -10
- package/dist/types/browser/validate-redirect-origin.d.ts +11 -0
- package/dist/types/build/generate-route-types.d.ts +1 -1
- package/dist/types/build/route-types/router-processing.d.ts +1 -1
- package/dist/types/build/route-types/scan-filter.d.ts +22 -2
- package/dist/types/cache/cf/cf-cache-store.d.ts +16 -1
- package/dist/types/cache/cf/cf-cache-types.d.ts +23 -2
- package/dist/types/cache/document-cache-policy.d.ts +24 -0
- package/dist/types/cache/document-cache.d.ts +2 -10
- package/dist/types/client-urls/client-urls.d.ts +1 -1
- package/dist/types/client-urls/hoisted-revalidate.d.ts +13 -0
- package/dist/types/client-urls/navigation.d.ts +0 -37
- package/dist/types/client-urls/revalidate-registry.d.ts +4 -0
- package/dist/types/client-urls/server-projection.d.ts +29 -1
- package/dist/types/client-urls/types.d.ts +34 -69
- package/dist/types/client.d.ts +0 -1
- package/dist/types/context-var.d.ts +12 -0
- package/dist/types/errors.d.ts +15 -0
- package/dist/types/handles/deferred-resolution.d.ts +2 -1
- package/dist/types/handles/pushed-by.d.ts +31 -0
- package/dist/types/index.d.ts +4 -0
- package/dist/types/index.rsc.d.ts +2 -0
- package/dist/types/loader-store.d.ts +8 -0
- package/dist/types/prefetch-markers.d.ts +11 -0
- package/dist/types/response-utils.d.ts +7 -0
- package/dist/types/reverse.d.ts +8 -8
- package/dist/types/route-definition/helpers-types.d.ts +27 -0
- package/dist/types/route-definition/rewrite.d.ts +56 -0
- package/dist/types/route-map-builder.d.ts +7 -2
- package/dist/types/router/basename.d.ts +7 -0
- package/dist/types/router/handler-context.d.ts +1 -5
- package/dist/types/router/internal-params.d.ts +8 -0
- package/dist/types/router/is-action.d.ts +2 -1
- package/dist/types/router/middleware-types.d.ts +15 -0
- package/dist/types/router/revalidation.d.ts +1 -2
- package/dist/types/router/route-trie-builder.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -2
- package/dist/types/router/segment-resolution/mask-nested.d.ts +4 -2
- package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +264 -0
- package/dist/types/rsc/full-payload.d.ts +11 -0
- package/dist/types/rsc/handler.d.ts +33 -0
- package/dist/types/rsc/helpers.d.ts +8 -0
- package/dist/types/rsc/loader-fetch.d.ts +16 -0
- package/dist/types/rsc/rewrite-passes.d.ts +74 -0
- package/dist/types/rsc/ssr-setup.d.ts +3 -1
- package/dist/types/server/context.d.ts +21 -1
- package/dist/types/server/handle-store.d.ts +9 -1
- package/dist/types/server/request-context.d.ts +52 -2
- package/dist/types/server/rewrite-trail.d.ts +49 -0
- package/dist/types/server/slot-accessors.d.ts +29 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/testing/flight.entry.d.ts +1 -1
- package/dist/types/testing/index.d.ts +4 -4
- package/dist/types/testing/internal/action-id.d.ts +10 -1
- package/dist/types/testing/internal/client-urls-projection.d.ts +16 -0
- package/dist/types/testing/internal/test-action.d.ts +8 -0
- package/dist/types/testing/render-route.d.ts +8 -0
- package/dist/types/testing/run-loader.d.ts +2 -1
- package/dist/types/testing/run-middleware.d.ts +20 -0
- package/dist/types/testing/run-revalidate.d.ts +74 -0
- package/dist/types/testing/run-transition-when.d.ts +2 -3
- package/dist/types/testing/serve-shell-request.d.ts +40 -1
- package/dist/types/testing/vitest.d.ts +11 -0
- package/dist/types/thenable-status.d.ts +6 -0
- package/dist/types/types/boundaries.d.ts +9 -0
- package/dist/types/types/handler-context.d.ts +14 -0
- package/dist/types/types/loader-types.d.ts +30 -0
- package/dist/types/types/request-scope.d.ts +7 -0
- package/dist/types/types/segments.d.ts +21 -4
- package/dist/types/urls/path-helper-types.d.ts +25 -4
- package/dist/types/urls/type-extraction.d.ts +6 -2
- package/dist/types/vite/discovery/discover-routers.d.ts +9 -0
- package/dist/types/vite/discovery/state.d.ts +14 -4
- package/dist/types/vite/plugins/client-urls-revalidate-hoist.d.ts +54 -0
- package/dist/types/vite/plugins/hoist/literal.d.ts +83 -0
- package/dist/types/vite/plugins/hoist/scope.d.ts +40 -0
- package/dist/types/vite/plugins/hoist/source.d.ts +41 -0
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +8 -11
- package/dist/types/vite/router-discovery.d.ts +10 -0
- package/dist/vite/index.js +974 -379
- package/package.json +1 -1
- package/skills/caching/SKILL.md +54 -5
- package/skills/catalog.json +3 -3
- package/skills/client-urls/SKILL.md +197 -24
- package/skills/cloudflare/SKILL.md +14 -9
- package/skills/hooks/SKILL.md +16 -3
- package/skills/hooks/data.md +3 -1
- package/skills/links/SKILL.md +24 -0
- package/skills/loader/SKILL.md +199 -2
- package/skills/middleware/SKILL.md +183 -2
- package/skills/migrate-nextjs/SKILL.md +2 -2
- package/skills/migrate-react-router/SKILL.md +10 -7
- package/skills/migrate-react-router/data-and-actions.md +1 -1
- package/skills/ppr/SKILL.md +22 -0
- package/skills/prerender/SKILL.md +22 -8
- package/skills/rango/SKILL.md +13 -7
- package/skills/route/SKILL.md +139 -0
- package/skills/server-actions/SKILL.md +27 -0
- package/skills/testing/SKILL.md +3 -2
- package/skills/testing/client-components.md +2 -1
- package/skills/testing/middleware.md +31 -10
- package/skills/testing/predicates.md +67 -32
- package/skills/testing/response-routes.md +1 -0
- package/skills/testing/serve-shell-request.md +107 -19
- package/skills/testing/setup.md +2 -2
- package/skills/typesafety/route-types.md +2 -2
- package/skills/use-cache/SKILL.md +4 -2
- package/skills/view-transitions/SKILL.md +26 -0
- package/src/__augment-tests__/augmented.check.ts +20 -0
- package/src/browser/defer.ts +82 -0
- package/src/browser/document-revealed.ts +52 -0
- package/src/browser/event-controller.ts +46 -32
- package/src/browser/merge-segment-loaders.ts +31 -18
- package/src/browser/navigation-bridge.ts +82 -16
- package/src/browser/navigation-client.ts +48 -36
- package/src/browser/navigation-store.ts +13 -0
- package/src/browser/navigation-transaction.ts +25 -8
- package/src/browser/partial-update.ts +554 -146
- package/src/browser/prefetch/fetch.ts +2 -1
- package/src/browser/react/Link.tsx +7 -9
- package/src/browser/react/NavigationProvider.tsx +88 -49
- package/src/browser/react/context.ts +3 -2
- package/src/browser/react/filter-segment-order.ts +13 -2
- package/src/browser/react/use-router.ts +2 -4
- package/src/browser/response-adapter.ts +20 -0
- package/src/browser/rsc-router.tsx +17 -45
- package/src/browser/server-action-bridge.ts +25 -63
- package/src/browser/types.ts +24 -10
- package/src/browser/validate-redirect-origin.ts +16 -0
- package/src/build/generate-route-types.ts +1 -0
- package/src/build/route-types/router-processing.ts +3 -6
- package/src/build/route-types/scan-filter.ts +45 -11
- package/src/cache/cf/cf-cache-store.ts +59 -21
- package/src/cache/cf/cf-cache-types.ts +24 -2
- package/src/cache/document-cache-policy.ts +131 -0
- package/src/cache/document-cache.ts +21 -107
- package/src/client-urls/client-urls.ts +22 -6
- package/src/client-urls/hoisted-revalidate.ts +25 -0
- package/src/client-urls/navigation.ts +0 -96
- package/src/client-urls/revalidate-registry.ts +30 -0
- package/src/client-urls/server-projection.ts +151 -65
- package/src/client-urls/types.ts +39 -71
- package/src/client.tsx +9 -5
- package/src/context-var.ts +32 -2
- package/src/errors.ts +34 -0
- package/src/handles/deferred-resolution.ts +28 -5
- package/src/handles/pushed-by.ts +148 -0
- package/src/index.rsc.ts +8 -0
- package/src/index.ts +7 -0
- package/src/loader-store.ts +18 -7
- package/src/prefetch-markers.ts +13 -0
- package/src/response-utils.ts +19 -0
- package/src/reverse.ts +17 -14
- package/src/route-content-wrapper.tsx +18 -4
- package/src/route-definition/dsl-helpers.ts +27 -4
- package/src/route-definition/helpers-types.ts +27 -1
- package/src/route-definition/redirect.ts +2 -15
- package/src/route-definition/rewrite.ts +146 -0
- package/src/route-map-builder.ts +14 -11
- package/src/router/basename.ts +20 -0
- package/src/router/handler-context.ts +3 -13
- package/src/router/intercept-resolution.ts +35 -16
- package/src/router/internal-params.ts +16 -0
- package/src/router/is-action.ts +2 -1
- package/src/router/loader-resolution.ts +85 -4
- package/src/router/match-api.ts +36 -1
- package/src/router/match-middleware/background-revalidation.ts +16 -4
- package/src/router/match-middleware/cache-lookup.ts +35 -7
- package/src/router/match-middleware/cache-store.ts +15 -1
- package/src/router/match-middleware/intercept-resolution.ts +4 -2
- package/src/router/match-result.ts +14 -2
- package/src/router/middleware-types.ts +16 -0
- package/src/router/middleware.ts +127 -49
- package/src/router/navigation-snapshot.ts +2 -1
- package/src/router/revalidation.ts +1 -1
- package/src/router/route-trie-builder.ts +28 -1
- package/src/router/segment-resolution/fresh.ts +22 -18
- package/src/router/segment-resolution/loader-cache.ts +2 -1
- package/src/router/segment-resolution/loader-mask.ts +5 -3
- package/src/router/segment-resolution/loader-snapshot.ts +18 -4
- package/src/router/segment-resolution/mask-nested.ts +8 -4
- package/src/router/segment-resolution/prefetch-deferral.ts +583 -0
- package/src/router/segment-resolution/revalidation.ts +215 -82
- package/src/rsc/full-payload.ts +37 -5
- package/src/rsc/handler.ts +82 -55
- package/src/rsc/helpers.ts +24 -0
- package/src/rsc/loader-fetch.ts +79 -24
- package/src/rsc/response-cache-serve.ts +18 -2
- package/src/rsc/response-route-handler.ts +4 -7
- package/src/rsc/rewrite-passes.ts +203 -0
- package/src/rsc/rsc-rendering.ts +54 -9
- package/src/rsc/shell-capture.ts +2 -1
- package/src/rsc/ssr-setup.ts +11 -5
- package/src/segment-loader-promise.ts +23 -20
- package/src/segment-system.tsx +60 -26
- package/src/server/context.ts +39 -5
- package/src/server/cookie-parse.ts +36 -12
- package/src/server/cookie-store.ts +7 -3
- package/src/server/handle-store.ts +152 -25
- package/src/server/request-context.ts +175 -64
- package/src/server/request-headers.ts +4 -14
- package/src/server/rewrite-trail.ts +190 -0
- package/src/server/slot-accessors.ts +57 -0
- package/src/server.ts +4 -0
- package/src/ssr/index.tsx +118 -16
- package/src/testing/action-form.ts +2 -11
- package/src/testing/dispatch.ts +138 -67
- package/src/testing/e2e/page-helpers.ts +3 -1
- package/src/testing/flight.entry.ts +1 -0
- package/src/testing/index.ts +7 -3
- package/src/testing/internal/action-id.ts +16 -1
- package/src/testing/internal/client-urls-projection.ts +70 -0
- package/src/testing/internal/test-action.ts +25 -0
- package/src/testing/render-route.tsx +16 -3
- package/src/testing/run-loader.ts +4 -1
- package/src/testing/run-middleware.ts +56 -11
- package/src/testing/run-revalidate.ts +142 -0
- package/src/testing/run-transition-when.ts +7 -8
- package/src/testing/serve-shell-request.ts +131 -8
- package/src/testing/vitest.ts +67 -1
- package/src/thenable-status.ts +11 -0
- package/src/types/boundaries.ts +12 -0
- package/src/types/handler-context.ts +14 -0
- package/src/types/loader-types.ts +30 -0
- package/src/types/request-scope.ts +8 -0
- package/src/types/segments.ts +26 -4
- package/src/urls/path-helper-types.ts +27 -5
- package/src/urls/type-extraction.ts +30 -10
- package/src/use-loader.tsx +18 -13
- package/src/vite/discovery/client-urls-projection.ts +20 -21
- package/src/vite/discovery/discover-routers.ts +94 -26
- package/src/vite/discovery/state.ts +28 -4
- package/src/vite/discovery/virtual-module-codegen.ts +22 -17
- package/src/vite/plugins/client-urls-revalidate-hoist.ts +564 -0
- package/src/vite/plugins/expose-internal-ids.ts +2 -3
- package/src/vite/plugins/hoist/literal.ts +417 -0
- package/src/vite/plugins/hoist/scope.ts +370 -0
- package/src/vite/plugins/hoist/source.ts +84 -0
- package/src/vite/plugins/transition-when-hoist.ts +113 -779
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +116 -53
- package/dist/types/client-urls/revalidate-chain.d.ts +0 -33
- package/dist/types/client-urls/revalidation-protocol.d.ts +0 -25
- package/dist/types/testing/run-client-revalidate.d.ts +0 -43
- package/src/client-urls/revalidate-chain.ts +0 -83
- package/src/client-urls/revalidation-protocol.ts +0 -56
- package/src/testing/run-client-revalidate.ts +0 -92
|
@@ -9,11 +9,15 @@
|
|
|
9
9
|
import type { ReactNode } from "react";
|
|
10
10
|
import { invariant } from "../../errors";
|
|
11
11
|
import { revalidate } from "../loader-resolution.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
evaluateRevalidation,
|
|
14
|
+
type EvaluateRevalidationOptions,
|
|
15
|
+
} from "../revalidation.js";
|
|
13
16
|
import {
|
|
14
17
|
getParallelEntries,
|
|
15
18
|
getParallelSlotEntries,
|
|
16
19
|
type EntryData,
|
|
20
|
+
type LoaderEntry,
|
|
17
21
|
type ParallelEntryData,
|
|
18
22
|
} from "../../server/context";
|
|
19
23
|
import type {
|
|
@@ -34,6 +38,13 @@ import {
|
|
|
34
38
|
} from "../logging.js";
|
|
35
39
|
import { resolveLoaderData } from "./loader-cache.js";
|
|
36
40
|
import { entryLoadingMasksLoaders } from "./loader-mask.js";
|
|
41
|
+
import {
|
|
42
|
+
defersLoader,
|
|
43
|
+
deferUnit,
|
|
44
|
+
fillSends,
|
|
45
|
+
getPrefetchDeferral,
|
|
46
|
+
registerDeferredLoader,
|
|
47
|
+
} from "./prefetch-deferral.js";
|
|
37
48
|
import {
|
|
38
49
|
handleHandlerResult,
|
|
39
50
|
warnOnStreamedResponse,
|
|
@@ -102,6 +113,67 @@ function emitRevalidationDecision(
|
|
|
102
113
|
});
|
|
103
114
|
}
|
|
104
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Whether a segment the client holds (`held`, the set resolution works on)
|
|
118
|
+
* renders again. A fill keeps it (prefetch-deferral.ts fillSends): no
|
|
119
|
+
* revalidate() predicate runs and nothing is traced or observed. Any other
|
|
120
|
+
* request asks the segment's revalidate() chain, and `onEvaluated` sees that
|
|
121
|
+
* answer.
|
|
122
|
+
*/
|
|
123
|
+
async function revalidateHeld<TEnv>({
|
|
124
|
+
held,
|
|
125
|
+
onEvaluated,
|
|
126
|
+
...options
|
|
127
|
+
}: EvaluateRevalidationOptions<TEnv> & {
|
|
128
|
+
held: ReadonlySet<string>;
|
|
129
|
+
onEvaluated?: (shouldRevalidate: boolean) => void;
|
|
130
|
+
}): Promise<boolean> {
|
|
131
|
+
const fill = fillSends(options.context, options.segment.id, held);
|
|
132
|
+
if (fill !== undefined) return fill;
|
|
133
|
+
const shouldRevalidate = await evaluateRevalidation(options);
|
|
134
|
+
onEvaluated?.(shouldRevalidate);
|
|
135
|
+
return shouldRevalidate;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
// prefetch: false (prefetch-deferral.ts)
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* How to deliver a loader a prefetch deferred, kept so deliverStartedLoaders
|
|
144
|
+
* can deliver it after all. Keyed by the emitted segment object.
|
|
145
|
+
*/
|
|
146
|
+
const deferredLoaderRuns = new WeakMap<ResolvedSegment, () => unknown>();
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* A deferred loader something started in this request (a handler that awaits
|
|
150
|
+
* it with ctx.use(), or another loader) ran in the prefetch whatever its flag
|
|
151
|
+
* says: deliver that run instead of having the fill run it a second time. A
|
|
152
|
+
* reader starts it through its registration (prefetch-deferral.ts
|
|
153
|
+
* startDeferredLoader), so the delivery's resolveLoaderData finds the same
|
|
154
|
+
* run or cache() binding. Called once resolution has awaited every handler it
|
|
155
|
+
* awaits; a streamed handler that reads the loader later is not seen, and
|
|
156
|
+
* that loader runs again in the fill.
|
|
157
|
+
*/
|
|
158
|
+
function deliverStartedLoaders<TEnv>(
|
|
159
|
+
ctx: HandlerContext<any, TEnv>,
|
|
160
|
+
segments: ResolvedSegment[],
|
|
161
|
+
): void {
|
|
162
|
+
const plan = getPrefetchDeferral(ctx);
|
|
163
|
+
if (plan?.mode !== "prefetch") return;
|
|
164
|
+
const internal = ctx as InternalHandlerContext<any, TEnv>;
|
|
165
|
+
const started = (loaderId: string): boolean =>
|
|
166
|
+
plan.deferredLoaders?.get(loaderId)?.started === true ||
|
|
167
|
+
internal._loaderStarted?.(loaderId) === true;
|
|
168
|
+
for (const segment of segments) {
|
|
169
|
+
if (!segment.deferred || segment.type !== "loader") continue;
|
|
170
|
+
const run = deferredLoaderRuns.get(segment);
|
|
171
|
+
if (!run || !started(segment.loaderId!)) continue;
|
|
172
|
+
delete segment.deferred;
|
|
173
|
+
segment.loaderData = run();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
105
177
|
// ---------------------------------------------------------------------------
|
|
106
178
|
// Revalidation path (partial match)
|
|
107
179
|
// ---------------------------------------------------------------------------
|
|
@@ -185,7 +257,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
185
257
|
belongsToRoute,
|
|
186
258
|
};
|
|
187
259
|
|
|
188
|
-
return await
|
|
260
|
+
return await revalidateHeld({
|
|
189
261
|
segment: dummySegment,
|
|
190
262
|
prevParams,
|
|
191
263
|
getPrevSegment: null,
|
|
@@ -201,6 +273,7 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
201
273
|
actionContext,
|
|
202
274
|
stale,
|
|
203
275
|
traceSource: "loader",
|
|
276
|
+
held: clientSegmentIds,
|
|
204
277
|
});
|
|
205
278
|
},
|
|
206
279
|
async () => true,
|
|
@@ -226,35 +299,72 @@ export async function resolveLoadersWithRevalidation<TEnv>(
|
|
|
226
299
|
// (matchPartialWithPprReplay) seeds once its doc record hits.
|
|
227
300
|
const bakeLane = !entryLoadingMasksLoaders(entry.loading);
|
|
228
301
|
|
|
302
|
+
const kickoff = (
|
|
303
|
+
loaderEntry: LoaderEntry,
|
|
304
|
+
segmentId: string,
|
|
305
|
+
loaderId: string,
|
|
306
|
+
): Promise<unknown> =>
|
|
307
|
+
runInsideLoaderScope(
|
|
308
|
+
() =>
|
|
309
|
+
resolveLoaderData(
|
|
310
|
+
loaderEntry,
|
|
311
|
+
ctx,
|
|
312
|
+
ctx.pathname,
|
|
313
|
+
// `bake`, not awaitBeforeFlush: a navigation evaluation never
|
|
314
|
+
// carries awaitBeforeFlush, and an ssr: false loader must pin
|
|
315
|
+
// here exactly as on the document HIT.
|
|
316
|
+
bakeLane || loaderEntry.bake === true ? segmentId : null,
|
|
317
|
+
),
|
|
318
|
+
loaderId,
|
|
319
|
+
);
|
|
320
|
+
const start = (
|
|
321
|
+
loaderEntry: LoaderEntry,
|
|
322
|
+
segmentId: string,
|
|
323
|
+
loaderId: string,
|
|
324
|
+
) =>
|
|
325
|
+
deps.wrapLoaderPromise(
|
|
326
|
+
kickoff(loaderEntry, segmentId, loaderId),
|
|
327
|
+
entry,
|
|
328
|
+
segmentId,
|
|
329
|
+
ctx.pathname,
|
|
330
|
+
errorContext,
|
|
331
|
+
);
|
|
332
|
+
|
|
229
333
|
const loadersToRun = revalidationChecks.filter((c) => c.shouldRun);
|
|
230
334
|
const segments: ResolvedSegment[] = loadersToRun.map(
|
|
231
|
-
({ loaderEntry, loader, segmentId, index }) =>
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
335
|
+
({ loaderEntry, loader, segmentId, index }) => {
|
|
336
|
+
const base = {
|
|
337
|
+
id: segmentId,
|
|
338
|
+
namespace: entry.id,
|
|
339
|
+
type: "loader" as const,
|
|
340
|
+
index,
|
|
341
|
+
component: null,
|
|
342
|
+
params: ctx.params,
|
|
343
|
+
loaderId: loader.$$id,
|
|
344
|
+
};
|
|
345
|
+
// Only a loader on a segment the client lacks (defersLoader). It
|
|
346
|
+
// would have run. Registered, not started: it runs, and reads its
|
|
347
|
+
// cache(), only if a reader starts it (startDeferredLoader).
|
|
348
|
+
if (defersLoader(ctx, loaderEntry, segmentId)) {
|
|
349
|
+
registerDeferredLoader(ctx, loader.$$id, shortCode, () =>
|
|
350
|
+
kickoff(loaderEntry, segmentId, loader.$$id),
|
|
351
|
+
);
|
|
352
|
+
const deferred: ResolvedSegment = {
|
|
353
|
+
...base,
|
|
354
|
+
belongsToRoute,
|
|
355
|
+
deferred: true,
|
|
356
|
+
};
|
|
357
|
+
deferredLoaderRuns.set(deferred, () =>
|
|
358
|
+
start(loaderEntry, segmentId, loader.$$id),
|
|
359
|
+
);
|
|
360
|
+
return deferred;
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
...base,
|
|
364
|
+
loaderData: start(loaderEntry, segmentId, loader.$$id),
|
|
365
|
+
belongsToRoute,
|
|
366
|
+
};
|
|
367
|
+
},
|
|
258
368
|
);
|
|
259
369
|
|
|
260
370
|
return { segments, matchedIds };
|
|
@@ -372,6 +482,7 @@ export async function resolveLoadersOnlyWithRevalidation<TEnv>(
|
|
|
372
482
|
await collectEntryLoaders(entry, entry.type === "route");
|
|
373
483
|
}
|
|
374
484
|
|
|
485
|
+
deliverStartedLoaders(context, allLoaderSegments);
|
|
375
486
|
return { segments: allLoaderSegments, matchedIds: allMatchedIds };
|
|
376
487
|
}
|
|
377
488
|
|
|
@@ -612,8 +723,19 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
612
723
|
// on the next request, perpetuating the staleness).
|
|
613
724
|
matchedIds.push(parallelId);
|
|
614
725
|
|
|
726
|
+
// A slot below a unit a prefetch skipped goes with it, and no predicate
|
|
727
|
+
// runs: a prefetch sends what the slot declares instead of running its
|
|
728
|
+
// handler.
|
|
729
|
+
let deferred = deferUnit(context, parallelId, "below");
|
|
730
|
+
// A fill sends exactly the slots the client lacks, ahead of every rule
|
|
731
|
+
// below (fillSends).
|
|
732
|
+
const fill = fillSends(context, parallelId, clientSegmentIds);
|
|
615
733
|
let shouldResolve: boolean;
|
|
616
|
-
if (
|
|
734
|
+
if (fill !== undefined) {
|
|
735
|
+
shouldResolve = fill;
|
|
736
|
+
} else if (deferred) {
|
|
737
|
+
shouldResolve = true;
|
|
738
|
+
} else if (isFullRefetch) {
|
|
617
739
|
// Client has nothing cached — slot MUST render. User revalidate fns are
|
|
618
740
|
// bypassed here because returning false would leave the segment blank
|
|
619
741
|
// with no client-side fallback.
|
|
@@ -687,18 +809,22 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
687
809
|
shouldResolve,
|
|
688
810
|
);
|
|
689
811
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
812
|
+
// A slot with its own flagged loading() is its own unit.
|
|
813
|
+
if (shouldResolve) deferred ??= deferUnit(context, parallelId, "slot");
|
|
814
|
+
const { component, handlerRan } = deferred
|
|
815
|
+
? { component: null, handlerRan: false }
|
|
816
|
+
: await resolveParallelSlotComponent({
|
|
817
|
+
shouldResolve,
|
|
818
|
+
parallelEntry,
|
|
819
|
+
slot,
|
|
820
|
+
parallelId,
|
|
821
|
+
handler,
|
|
822
|
+
context,
|
|
823
|
+
deps,
|
|
824
|
+
routeKey,
|
|
825
|
+
params,
|
|
826
|
+
boundaryEntry: options?.boundaryEntry ?? entry,
|
|
827
|
+
});
|
|
702
828
|
|
|
703
829
|
segments.push({
|
|
704
830
|
id: parallelId,
|
|
@@ -719,6 +845,7 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
719
845
|
...(parallelEntry.mountPath
|
|
720
846
|
? { mountPath: parallelEntry.mountPath }
|
|
721
847
|
: {}),
|
|
848
|
+
...deferred,
|
|
722
849
|
});
|
|
723
850
|
|
|
724
851
|
if (loaderOrder === "after") {
|
|
@@ -750,6 +877,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
750
877
|
const matchedId = entry.shortCode;
|
|
751
878
|
|
|
752
879
|
let handlerRan = false;
|
|
880
|
+
let deferred: { readonly deferred: true } | undefined;
|
|
753
881
|
const component = await revalidate(
|
|
754
882
|
async () => {
|
|
755
883
|
const hasSegment = clientSegmentIds.has(entry.shortCode);
|
|
@@ -795,7 +923,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
795
923
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
796
924
|
};
|
|
797
925
|
|
|
798
|
-
|
|
926
|
+
return revalidateHeld({
|
|
799
927
|
segment: dummySegment,
|
|
800
928
|
prevParams,
|
|
801
929
|
getPrevSegment: null,
|
|
@@ -812,20 +940,26 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
812
940
|
stale,
|
|
813
941
|
traceSource:
|
|
814
942
|
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
943
|
+
held: clientSegmentIds,
|
|
944
|
+
onEvaluated: (shouldRevalidate) => {
|
|
945
|
+
emitRevalidationDecision(
|
|
946
|
+
entry.shortCode,
|
|
947
|
+
context.pathname,
|
|
948
|
+
routeKey,
|
|
949
|
+
shouldRevalidate,
|
|
950
|
+
);
|
|
951
|
+
debugLog("segment.revalidate", "entry revalidation decision", {
|
|
952
|
+
segmentId: entry.shortCode,
|
|
953
|
+
shouldRevalidate,
|
|
954
|
+
});
|
|
955
|
+
},
|
|
815
956
|
});
|
|
816
|
-
emitRevalidationDecision(
|
|
817
|
-
entry.shortCode,
|
|
818
|
-
context.pathname,
|
|
819
|
-
routeKey,
|
|
820
|
-
shouldRevalidate,
|
|
821
|
-
);
|
|
822
|
-
debugLog("segment.revalidate", "entry revalidation decision", {
|
|
823
|
-
segmentId: entry.shortCode,
|
|
824
|
-
shouldRevalidate,
|
|
825
|
-
});
|
|
826
|
-
return shouldRevalidate;
|
|
827
957
|
},
|
|
828
958
|
async () => {
|
|
959
|
+
// Only an entry the client lacks (defersUnit). Its handler would
|
|
960
|
+
// have run, and a prefetch sends the entry's fallback in its place.
|
|
961
|
+
deferred = deferUnit(context, entry.shortCode, "chain");
|
|
962
|
+
if (deferred) return null;
|
|
829
963
|
handlerRan = true;
|
|
830
964
|
const doneHandler = track(`handler:${entry.id}`, 2);
|
|
831
965
|
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
@@ -928,6 +1062,7 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
928
1062
|
: {}),
|
|
929
1063
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
930
1064
|
_handlerRan: handlerRan,
|
|
1065
|
+
...deferred,
|
|
931
1066
|
};
|
|
932
1067
|
|
|
933
1068
|
return { segment, matchedId };
|
|
@@ -1020,10 +1155,8 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
1020
1155
|
}
|
|
1021
1156
|
}
|
|
1022
1157
|
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
// (handler data visible) then push handler segment last for tree order.
|
|
1026
|
-
const parallelResult = await resolveParallelSegmentsWithRevalidation(
|
|
1158
|
+
const resolveSlots = () =>
|
|
1159
|
+
resolveParallelSegmentsWithRevalidation(
|
|
1027
1160
|
entry,
|
|
1028
1161
|
params,
|
|
1029
1162
|
context,
|
|
@@ -1038,6 +1171,11 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
1038
1171
|
actionContext,
|
|
1039
1172
|
stale,
|
|
1040
1173
|
);
|
|
1174
|
+
|
|
1175
|
+
if (routeHandlerResult) {
|
|
1176
|
+
// Route entry: handler already executed above; resolve parallels
|
|
1177
|
+
// (handler data visible) then push handler segment last for tree order.
|
|
1178
|
+
const parallelResult = await resolveSlots();
|
|
1041
1179
|
segments.push(...parallelResult.segments);
|
|
1042
1180
|
matchedIds.push(...parallelResult.matchedIds);
|
|
1043
1181
|
|
|
@@ -1064,21 +1202,7 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
1064
1202
|
segments.push(handlerResult.segment);
|
|
1065
1203
|
matchedIds.push(handlerResult.matchedId);
|
|
1066
1204
|
|
|
1067
|
-
const parallelResult = await
|
|
1068
|
-
entry,
|
|
1069
|
-
params,
|
|
1070
|
-
context,
|
|
1071
|
-
belongsToRoute,
|
|
1072
|
-
clientSegmentIds,
|
|
1073
|
-
prevParams,
|
|
1074
|
-
request,
|
|
1075
|
-
prevUrl,
|
|
1076
|
-
nextUrl,
|
|
1077
|
-
routeKey,
|
|
1078
|
-
deps,
|
|
1079
|
-
actionContext,
|
|
1080
|
-
stale,
|
|
1081
|
-
);
|
|
1205
|
+
const parallelResult = await resolveSlots();
|
|
1082
1206
|
segments.push(...parallelResult.segments);
|
|
1083
1207
|
matchedIds.push(...parallelResult.matchedIds);
|
|
1084
1208
|
|
|
@@ -1192,6 +1316,7 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1192
1316
|
// so ctx.set() values are visible to parallel children.
|
|
1193
1317
|
matchedIds.push(orphan.shortCode);
|
|
1194
1318
|
|
|
1319
|
+
let deferred: { readonly deferred: true } | undefined;
|
|
1195
1320
|
const component = await revalidate(
|
|
1196
1321
|
async () => {
|
|
1197
1322
|
if (!clientSegmentIds.has(orphan.shortCode)) {
|
|
@@ -1221,7 +1346,7 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1221
1346
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
1222
1347
|
};
|
|
1223
1348
|
|
|
1224
|
-
|
|
1349
|
+
return revalidateHeld({
|
|
1225
1350
|
segment: dummySegment,
|
|
1226
1351
|
prevParams,
|
|
1227
1352
|
getPrevSegment: null,
|
|
@@ -1237,16 +1362,22 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1237
1362
|
actionContext,
|
|
1238
1363
|
stale,
|
|
1239
1364
|
traceSource: "orphan-layout",
|
|
1365
|
+
held: clientSegmentIds,
|
|
1366
|
+
onEvaluated: (shouldRevalidate) =>
|
|
1367
|
+
emitRevalidationDecision(
|
|
1368
|
+
orphan.shortCode,
|
|
1369
|
+
context.pathname,
|
|
1370
|
+
routeKey,
|
|
1371
|
+
shouldRevalidate,
|
|
1372
|
+
),
|
|
1240
1373
|
});
|
|
1241
|
-
emitRevalidationDecision(
|
|
1242
|
-
orphan.shortCode,
|
|
1243
|
-
context.pathname,
|
|
1244
|
-
routeKey,
|
|
1245
|
-
shouldRevalidate,
|
|
1246
|
-
);
|
|
1247
|
-
return shouldRevalidate;
|
|
1248
1374
|
},
|
|
1249
|
-
async () =>
|
|
1375
|
+
async () => {
|
|
1376
|
+
// Below a unit a prefetch skipped: the orphan goes with it.
|
|
1377
|
+
deferred = deferUnit(context, orphan.shortCode, "below");
|
|
1378
|
+
if (deferred) return null;
|
|
1379
|
+
return resolveLayoutComponent(orphan, context);
|
|
1380
|
+
},
|
|
1250
1381
|
() => null,
|
|
1251
1382
|
);
|
|
1252
1383
|
|
|
@@ -1265,6 +1396,7 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1265
1396
|
deps.viewTransitionDefault,
|
|
1266
1397
|
),
|
|
1267
1398
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
1399
|
+
...deferred,
|
|
1268
1400
|
});
|
|
1269
1401
|
|
|
1270
1402
|
// Resolve the orphan layout's parallel slots through the shared main-path
|
|
@@ -1425,5 +1557,6 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1425
1557
|
}
|
|
1426
1558
|
}
|
|
1427
1559
|
|
|
1560
|
+
deliverStartedLoaders(context, allSegments);
|
|
1428
1561
|
return { segments: allSegments, matchedIds };
|
|
1429
1562
|
}
|
package/src/rsc/full-payload.ts
CHANGED
|
@@ -14,7 +14,11 @@ import type { HandlerContext } from "./handler-context.js";
|
|
|
14
14
|
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
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
rendersForSharedAudience,
|
|
19
|
+
rewriteTrail,
|
|
20
|
+
visitorUrl,
|
|
21
|
+
} from "../server/rewrite-trail.js";
|
|
18
22
|
import { attachTransitionWhen } from "./attach-transition-when.js";
|
|
19
23
|
import { publicRouteName } from "../route-name.js";
|
|
20
24
|
import { resolvedHandleStream } from "../handles/deferred-resolution.js";
|
|
@@ -38,7 +42,7 @@ export function buildFullPayload(
|
|
|
38
42
|
): RscPayload {
|
|
39
43
|
return {
|
|
40
44
|
metadata: {
|
|
41
|
-
pathname: url.pathname,
|
|
45
|
+
pathname: renderedLocation(reqCtx, url).pathname,
|
|
42
46
|
routerId: ctx.router.id,
|
|
43
47
|
basename: ctx.router.basename,
|
|
44
48
|
segments: attachTransitionWhen(m.segments, reqCtx),
|
|
@@ -84,6 +88,36 @@ export function buildFullPayload(
|
|
|
84
88
|
};
|
|
85
89
|
}
|
|
86
90
|
|
|
91
|
+
/**
|
|
92
|
+
* The URL a render shows as the page's location (payload `pathname`, the SSR
|
|
93
|
+
* search and origin seed). For a request middleware rewrote, the visitor's
|
|
94
|
+
* URL, so the server markup and the hydrating client (which reads the address
|
|
95
|
+
* bar) agree on `usePathname()` and `useSearchParams()`. A render other
|
|
96
|
+
* visitors may be served shows `url`, the target, as a direct visit does: a
|
|
97
|
+
* shell capture, and a render the document cache stores (its key is the
|
|
98
|
+
* target). A render that shows the visitor's URL marks
|
|
99
|
+
* `_payloadVisitorLocation`, and the document cache does not store it.
|
|
100
|
+
*/
|
|
101
|
+
export function renderedLocation(
|
|
102
|
+
reqCtx:
|
|
103
|
+
| Pick<
|
|
104
|
+
RequestContext<any>,
|
|
105
|
+
| "_variables"
|
|
106
|
+
| "_shellCaptureRun"
|
|
107
|
+
| "_documentCacheRender"
|
|
108
|
+
| "_payloadVisitorLocation"
|
|
109
|
+
| "res"
|
|
110
|
+
>
|
|
111
|
+
| undefined,
|
|
112
|
+
url: URL,
|
|
113
|
+
): URL {
|
|
114
|
+
if (!reqCtx || !rewriteTrail(reqCtx) || rendersForSharedAudience(reqCtx)) {
|
|
115
|
+
return url;
|
|
116
|
+
}
|
|
117
|
+
reqCtx._payloadVisitorLocation = true;
|
|
118
|
+
return visitorUrl(reqCtx, url);
|
|
119
|
+
}
|
|
120
|
+
|
|
87
121
|
/**
|
|
88
122
|
* The theme a document payload carries as `metadata.initialTheme` (the
|
|
89
123
|
* ThemeProvider's first state), read with no identity guard: it is the
|
|
@@ -116,9 +150,7 @@ export function payloadInitialTheme(
|
|
|
116
150
|
): Theme | undefined {
|
|
117
151
|
const config = reqCtx._themeConfig;
|
|
118
152
|
if (!config) return undefined;
|
|
119
|
-
if (
|
|
120
|
-
return config.defaultTheme;
|
|
121
|
-
}
|
|
153
|
+
if (rendersForSharedAudience(reqCtx)) return config.defaultTheme;
|
|
122
154
|
const theme = reqCtx._readTheme();
|
|
123
155
|
if (theme !== config.defaultTheme) reqCtx._payloadVisitorTheme = true;
|
|
124
156
|
return theme;
|