@rangojs/router 0.0.0-experimental.13 → 0.0.0-experimental.13221847
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/AGENTS.md +9 -0
- package/README.md +884 -4
- package/dist/bin/rango.js +1531 -212
- package/dist/vite/index.js +3995 -2489
- package/package.json +57 -52
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +85 -23
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +12 -8
- package/skills/document-cache/SKILL.md +18 -16
- package/skills/fonts/SKILL.md +6 -4
- package/skills/hooks/SKILL.md +328 -70
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +131 -8
- package/skills/layout/SKILL.md +100 -3
- package/skills/links/SKILL.md +62 -15
- package/skills/loader/SKILL.md +368 -42
- package/skills/middleware/SKILL.md +171 -34
- package/skills/mime-routes/SKILL.md +14 -10
- package/skills/parallel/SKILL.md +137 -1
- package/skills/prerender/SKILL.md +366 -28
- package/skills/rango/SKILL.md +85 -21
- package/skills/response-routes/SKILL.md +136 -83
- package/skills/route/SKILL.md +195 -21
- package/skills/router-setup/SKILL.md +123 -30
- package/skills/theme/SKILL.md +9 -8
- package/skills/typesafety/SKILL.md +240 -102
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +102 -4
- package/src/bin/rango.ts +312 -15
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +92 -64
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +24 -4
- package/src/browser/logging.ts +11 -0
- package/src/browser/merge-segment-loaders.ts +20 -12
- package/src/browser/navigation-bridge.ts +266 -558
- package/src/browser/navigation-client.ts +132 -75
- package/src/browser/navigation-store.ts +33 -50
- package/src/browser/navigation-transaction.ts +297 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +303 -309
- package/src/browser/prefetch/cache.ts +206 -0
- package/src/browser/prefetch/fetch.ts +144 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +48 -0
- package/src/browser/prefetch/queue.ts +128 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +190 -70
- package/src/browser/react/NavigationProvider.tsx +78 -11
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +12 -12
- package/src/browser/react/location-state-shared.ts +95 -53
- package/src/browser/react/location-state.ts +60 -15
- package/src/browser/react/mount-context.ts +6 -1
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +29 -51
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +29 -70
- package/src/browser/react/use-link-status.ts +6 -5
- package/src/browser/react/use-navigation.ts +22 -63
- package/src/browser/react/use-params.ts +65 -0
- package/src/browser/react/use-pathname.ts +47 -0
- package/src/browser/react/use-router.ts +63 -0
- package/src/browser/react/use-search-params.ts +56 -0
- package/src/browser/react/use-segments.ts +80 -97
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +188 -57
- package/src/browser/scroll-restoration.ts +117 -44
- package/src/browser/segment-reconciler.ts +221 -0
- package/src/browser/segment-structure-assert.ts +16 -0
- package/src/browser/server-action-bridge.ts +488 -606
- package/src/browser/shallow.ts +6 -1
- package/src/browser/types.ts +116 -47
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +63 -21
- package/src/build/generate-route-types.ts +36 -1038
- package/src/build/index.ts +2 -5
- package/src/build/route-trie.ts +38 -12
- package/src/build/route-types/ast-helpers.ts +25 -0
- package/src/build/route-types/ast-route-extraction.ts +98 -0
- package/src/build/route-types/codegen.ts +102 -0
- package/src/build/route-types/include-resolution.ts +411 -0
- package/src/build/route-types/param-extraction.ts +48 -0
- package/src/build/route-types/per-module-writer.ts +128 -0
- package/src/build/route-types/router-processing.ts +479 -0
- package/src/build/route-types/scan-filter.ts +78 -0
- package/src/build/runtime-discovery.ts +231 -0
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +342 -0
- package/src/cache/cache-scope.ts +122 -303
- package/src/cache/cf/cf-cache-store.ts +571 -17
- package/src/cache/cf/index.ts +13 -3
- package/src/cache/document-cache.ts +116 -77
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +1 -15
- package/src/cache/memory-segment-store.ts +191 -13
- package/src/cache/profile-registry.ts +73 -0
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +256 -0
- package/src/cache/taint.ts +98 -0
- package/src/cache/types.ts +72 -122
- package/src/client.rsc.tsx +3 -1
- package/src/client.tsx +84 -126
- package/src/component-utils.ts +4 -4
- package/src/components/DefaultDocument.tsx +5 -1
- package/src/context-var.ts +86 -0
- package/src/debug.ts +19 -9
- package/src/errors.ts +77 -7
- package/src/handle.ts +12 -7
- package/src/handles/MetaTags.tsx +73 -20
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/handles/meta.ts +30 -13
- package/src/host/cookie-handler.ts +21 -15
- package/src/host/errors.ts +8 -8
- package/src/host/index.ts +4 -7
- package/src/host/pattern-matcher.ts +27 -27
- package/src/host/router.ts +61 -39
- package/src/host/testing.ts +8 -8
- package/src/host/types.ts +15 -7
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +65 -45
- package/src/index.rsc.ts +104 -40
- package/src/index.ts +122 -67
- package/src/internal-debug.ts +9 -3
- package/src/loader.rsc.ts +18 -93
- package/src/loader.ts +26 -9
- package/src/network-error-thrower.tsx +3 -1
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +4 -2
- package/src/prerender/store.ts +121 -17
- package/src/prerender.ts +325 -20
- package/src/reverse.ts +144 -124
- package/src/root-error-boundary.tsx +41 -29
- package/src/route-content-wrapper.tsx +7 -4
- package/src/route-definition/dsl-helpers.ts +959 -0
- package/src/route-definition/helper-factories.ts +200 -0
- package/src/route-definition/helpers-types.ts +430 -0
- package/src/route-definition/index.ts +52 -0
- package/src/route-definition/redirect.ts +93 -0
- package/src/route-definition.ts +1 -1450
- package/src/route-map-builder.ts +87 -133
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +41 -6
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +9 -9
- package/src/router/find-match.ts +160 -0
- package/src/router/handler-context.ts +324 -116
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +237 -0
- package/src/router/loader-resolution.ts +179 -133
- package/src/router/logging.ts +112 -6
- package/src/router/manifest.ts +58 -19
- package/src/router/match-api.ts +89 -88
- package/src/router/match-context.ts +4 -2
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +86 -89
- package/src/router/match-middleware/cache-lookup.ts +295 -49
- package/src/router/match-middleware/cache-store.ts +56 -13
- package/src/router/match-middleware/intercept-resolution.ts +45 -22
- package/src/router/match-middleware/segment-resolution.ts +20 -9
- package/src/router/match-pipelines.ts +10 -45
- package/src/router/match-result.ts +44 -21
- package/src/router/metrics.ts +240 -15
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +327 -369
- package/src/router/pattern-matching.ts +169 -31
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +105 -14
- package/src/router/router-context.ts +40 -21
- package/src/router/router-interfaces.ts +452 -0
- package/src/router/router-options.ts +592 -0
- package/src/router/router-registry.ts +24 -0
- package/src/router/segment-resolution/fresh.ts +677 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +199 -0
- package/src/router/segment-resolution/revalidation.ts +1296 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -1354
- package/src/router/segment-wrappers.ts +291 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/trie-matching.ts +96 -29
- package/src/router/types.ts +15 -9
- package/src/router.ts +642 -2366
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +639 -1027
- package/src/rsc/helpers.ts +140 -6
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +14 -0
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +379 -0
- package/src/rsc/response-error.ts +37 -0
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +237 -0
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +348 -0
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +38 -11
- package/src/search-params.ts +66 -54
- package/src/segment-system.tsx +165 -17
- package/src/server/context.ts +237 -54
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +94 -15
- package/src/server/loader-registry.ts +15 -56
- package/src/server/request-context.ts +438 -71
- package/src/server.ts +26 -164
- package/src/ssr/index.tsx +101 -31
- package/src/static-handler.ts +22 -4
- package/src/theme/ThemeProvider.tsx +21 -15
- package/src/theme/ThemeScript.tsx +5 -5
- package/src/theme/constants.ts +5 -2
- package/src/theme/index.ts +4 -14
- package/src/theme/theme-context.ts +4 -30
- package/src/theme/theme-script.ts +21 -18
- package/src/types/boundaries.ts +158 -0
- package/src/types/cache-types.ts +198 -0
- package/src/types/error-types.ts +192 -0
- package/src/types/global-namespace.ts +100 -0
- package/src/types/handler-context.ts +773 -0
- package/src/types/index.ts +88 -0
- package/src/types/loader-types.ts +183 -0
- package/src/types/route-config.ts +170 -0
- package/src/types/route-entry.ts +109 -0
- package/src/types/segments.ts +150 -0
- package/src/types.ts +1 -1795
- package/src/urls/include-helper.ts +197 -0
- package/src/urls/index.ts +53 -0
- package/src/urls/path-helper-types.ts +339 -0
- package/src/urls/path-helper.ts +329 -0
- package/src/urls/pattern-types.ts +95 -0
- package/src/urls/response-types.ts +106 -0
- package/src/urls/type-extraction.ts +372 -0
- package/src/urls/urls-function.ts +98 -0
- package/src/urls.ts +1 -1323
- package/src/use-loader.tsx +85 -77
- package/src/vite/discovery/bundle-postprocess.ts +184 -0
- package/src/vite/discovery/discover-routers.ts +344 -0
- package/src/vite/discovery/prerender-collection.ts +385 -0
- package/src/vite/discovery/route-types-writer.ts +258 -0
- package/src/vite/discovery/self-gen-tracking.ts +47 -0
- package/src/vite/discovery/state.ts +108 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +11 -2259
- package/src/vite/plugin-types.ts +48 -0
- package/src/vite/plugins/cjs-to-esm.ts +93 -0
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +105 -0
- package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -47
- package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +8 -43
- package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
- package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
- package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
- package/src/vite/plugins/expose-ids/types.ts +45 -0
- package/src/vite/plugins/expose-internal-ids.ts +569 -0
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +323 -0
- package/src/vite/plugins/version-injector.ts +83 -0
- package/src/vite/plugins/version-plugin.ts +266 -0
- package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +445 -0
- package/src/vite/router-discovery.ts +777 -0
- package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +181 -9
- package/src/vite/utils/banner.ts +36 -0
- package/src/vite/utils/bundle-analysis.ts +137 -0
- package/src/vite/utils/manifest-utils.ts +70 -0
- package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
- package/CLAUDE.md +0 -43
- package/dist/vite/index.named-routes.gen.ts +0 -103
- package/src/browser/lru-cache.ts +0 -69
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- package/src/vite/expose-internal-ids.ts +0 -1167
- /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
|
@@ -70,9 +70,11 @@
|
|
|
70
70
|
* - No segments yielded from this middleware
|
|
71
71
|
*
|
|
72
72
|
* Loaders:
|
|
73
|
-
* - NEVER cached
|
|
73
|
+
* - NEVER cached in the segment cache
|
|
74
74
|
* - Always resolved fresh on every request
|
|
75
75
|
* - Ensures data freshness even with cached UI components
|
|
76
|
+
* - Segment cache staleness does NOT propagate to loader revalidation;
|
|
77
|
+
* loaders use their own revalidation rules (actionId, user-defined)
|
|
76
78
|
*
|
|
77
79
|
*
|
|
78
80
|
* REVALIDATION RULES
|
|
@@ -92,27 +94,61 @@
|
|
|
92
94
|
import type { ResolvedSegment } from "../../types.js";
|
|
93
95
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
94
96
|
import { getRouterContext } from "../router-context.js";
|
|
97
|
+
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
98
|
+
import { pushRevalidationTraceEntry, isTraceActive } from "../logging.js";
|
|
95
99
|
import type { PrerenderStore, PrerenderEntry } from "../../prerender/store.js";
|
|
100
|
+
import type { HandleStore } from "../../server/handle-store.js";
|
|
101
|
+
import {
|
|
102
|
+
getRequestContext,
|
|
103
|
+
_getRequestContext,
|
|
104
|
+
} from "../../server/request-context.js";
|
|
96
105
|
|
|
97
106
|
// Lazily initialized prerender store singleton and dynamically imported deps.
|
|
98
107
|
// Dynamic imports prevent pulling in @vitejs/plugin-rsc/rsc virtual module at
|
|
99
108
|
// top-level, which breaks vitest (only URLs with file:, data:, node: schemes).
|
|
100
109
|
let prerenderStoreInstance: PrerenderStore | null | undefined;
|
|
101
|
-
let _deserializeSegments:
|
|
102
|
-
|
|
103
|
-
|
|
110
|
+
let _deserializeSegments:
|
|
111
|
+
| typeof import("../../cache/segment-codec.js").deserializeSegments
|
|
112
|
+
| undefined;
|
|
113
|
+
let _restoreHandles:
|
|
114
|
+
| typeof import("../../cache/handle-snapshot.js").restoreHandles
|
|
115
|
+
| undefined;
|
|
116
|
+
let _hashParams:
|
|
117
|
+
| typeof import("../../prerender/param-hash.js").hashParams
|
|
118
|
+
| undefined;
|
|
119
|
+
let _lazyGetRequestContext:
|
|
120
|
+
| typeof import("../../server/request-context.js").getRequestContext
|
|
121
|
+
| undefined;
|
|
122
|
+
|
|
123
|
+
function paramsEqual(
|
|
124
|
+
a: Record<string, string>,
|
|
125
|
+
b: Record<string, string>,
|
|
126
|
+
): boolean {
|
|
127
|
+
if (a === b) return true;
|
|
128
|
+
|
|
129
|
+
const keysA = Object.keys(a);
|
|
130
|
+
if (keysA.length !== Object.keys(b).length) return false;
|
|
131
|
+
|
|
132
|
+
for (const key of keysA) {
|
|
133
|
+
if (a[key] !== b[key]) return false;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
104
138
|
|
|
105
139
|
async function ensurePrerenderDeps() {
|
|
106
140
|
if (!_deserializeSegments) {
|
|
107
|
-
const [
|
|
108
|
-
import("../../cache/
|
|
141
|
+
const [codec, snapshot, paramHash, reqCtx, store] = await Promise.all([
|
|
142
|
+
import("../../cache/segment-codec.js"),
|
|
143
|
+
import("../../cache/handle-snapshot.js"),
|
|
109
144
|
import("../../prerender/param-hash.js"),
|
|
110
145
|
import("../../server/request-context.js"),
|
|
111
146
|
import("../../prerender/store.js"),
|
|
112
147
|
]);
|
|
113
|
-
_deserializeSegments =
|
|
148
|
+
_deserializeSegments = codec.deserializeSegments;
|
|
149
|
+
_restoreHandles = snapshot.restoreHandles;
|
|
114
150
|
_hashParams = paramHash.hashParams;
|
|
115
|
-
|
|
151
|
+
_lazyGetRequestContext = reqCtx.getRequestContext;
|
|
116
152
|
if (prerenderStoreInstance === undefined) {
|
|
117
153
|
prerenderStoreInstance = store.createPrerenderStore();
|
|
118
154
|
}
|
|
@@ -129,29 +165,31 @@ async function* yieldFromStore<TEnv>(
|
|
|
129
165
|
ctx: MatchContext<TEnv>,
|
|
130
166
|
state: MatchPipelineState,
|
|
131
167
|
pipelineStart: number,
|
|
168
|
+
handleStoreRef?: HandleStore,
|
|
132
169
|
): AsyncGenerator<ResolvedSegment> {
|
|
133
|
-
const {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
170
|
+
const { resolveLoadersOnlyWithRevalidation, resolveLoadersOnly } =
|
|
171
|
+
getRouterContext<TEnv>();
|
|
172
|
+
|
|
173
|
+
if (
|
|
174
|
+
!_deserializeSegments ||
|
|
175
|
+
!_restoreHandles ||
|
|
176
|
+
!_hashParams ||
|
|
177
|
+
!_lazyGetRequestContext
|
|
178
|
+
) {
|
|
139
179
|
throw new Error("yieldFromStore called before ensurePrerenderDeps");
|
|
140
180
|
}
|
|
141
181
|
|
|
142
182
|
const segments = await _deserializeSegments(entry.segments);
|
|
143
183
|
|
|
144
|
-
// Replay handle data (same as runtime cache hit path)
|
|
145
|
-
|
|
184
|
+
// Replay handle data (same as runtime cache hit path).
|
|
185
|
+
// Prefer the eagerly-captured handleStoreRef to avoid ALS disruption in workerd.
|
|
186
|
+
const handleStore = handleStoreRef ?? _lazyGetRequestContext()?._handleStore;
|
|
146
187
|
if (handleStore) {
|
|
147
|
-
|
|
148
|
-
if (Object.keys(segHandles).length > 0) {
|
|
149
|
-
handleStore.replaySegmentData(segId, segHandles);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
188
|
+
_restoreHandles(entry.handles, handleStore);
|
|
152
189
|
}
|
|
153
190
|
|
|
154
191
|
state.cacheHit = true;
|
|
192
|
+
state.cacheSource = "prerender";
|
|
155
193
|
state.cachedSegments = segments;
|
|
156
194
|
state.cachedMatchedIds = segments.map((s) => s.id);
|
|
157
195
|
|
|
@@ -159,10 +197,14 @@ async function* yieldFromStore<TEnv>(
|
|
|
159
197
|
// so parent layouts stay live (client keeps its existing versions).
|
|
160
198
|
// When params changed (e.g., different guide slug), the segments have
|
|
161
199
|
// different content, so we must NOT nullify.
|
|
162
|
-
const paramsChanged =
|
|
163
|
-
|
|
200
|
+
const paramsChanged =
|
|
201
|
+
!ctx.isFullMatch && !paramsEqual(ctx.matched.params, ctx.prevParams);
|
|
164
202
|
for (const segment of segments) {
|
|
165
|
-
if (
|
|
203
|
+
if (
|
|
204
|
+
!ctx.isFullMatch &&
|
|
205
|
+
!paramsChanged &&
|
|
206
|
+
ctx.clientSegmentSet.has(segment.id)
|
|
207
|
+
) {
|
|
166
208
|
segment.component = null;
|
|
167
209
|
segment.loading = undefined;
|
|
168
210
|
}
|
|
@@ -170,6 +212,9 @@ async function* yieldFromStore<TEnv>(
|
|
|
170
212
|
}
|
|
171
213
|
|
|
172
214
|
// Resolve loaders fresh (loaders are never pre-rendered/cached)
|
|
215
|
+
const ms = ctx.metricsStore;
|
|
216
|
+
const loaderStart = performance.now();
|
|
217
|
+
|
|
173
218
|
if (ctx.isFullMatch) {
|
|
174
219
|
if (resolveLoadersOnly) {
|
|
175
220
|
const loaderSegments = await ctx.Store.run(() =>
|
|
@@ -209,9 +254,19 @@ async function* yieldFromStore<TEnv>(
|
|
|
209
254
|
}
|
|
210
255
|
}
|
|
211
256
|
|
|
212
|
-
const ms = ctx.metricsStore;
|
|
213
257
|
if (ms) {
|
|
214
|
-
|
|
258
|
+
const loaderEnd = performance.now();
|
|
259
|
+
ms.metrics.push({
|
|
260
|
+
label: "pipeline:loader-resolve",
|
|
261
|
+
duration: loaderEnd - loaderStart,
|
|
262
|
+
startTime: loaderStart - ms.requestStart,
|
|
263
|
+
depth: 1,
|
|
264
|
+
});
|
|
265
|
+
ms.metrics.push({
|
|
266
|
+
label: "pipeline:cache-hit",
|
|
267
|
+
duration: loaderEnd - pipelineStart,
|
|
268
|
+
startTime: pipelineStart - ms.requestStart,
|
|
269
|
+
});
|
|
215
270
|
}
|
|
216
271
|
}
|
|
217
272
|
|
|
@@ -219,7 +274,7 @@ async function* yieldFromStore<TEnv>(
|
|
|
219
274
|
* Async generator middleware type
|
|
220
275
|
*/
|
|
221
276
|
export type GeneratorMiddleware<T> = (
|
|
222
|
-
source: AsyncGenerator<T
|
|
277
|
+
source: AsyncGenerator<T>,
|
|
223
278
|
) => AsyncGenerator<T>;
|
|
224
279
|
|
|
225
280
|
/**
|
|
@@ -237,14 +292,21 @@ export type GeneratorMiddleware<T> = (
|
|
|
237
292
|
*/
|
|
238
293
|
export function withCacheLookup<TEnv>(
|
|
239
294
|
ctx: MatchContext<TEnv>,
|
|
240
|
-
state: MatchPipelineState
|
|
295
|
+
state: MatchPipelineState,
|
|
241
296
|
): GeneratorMiddleware<ResolvedSegment> {
|
|
242
297
|
return async function* (
|
|
243
|
-
source: AsyncGenerator<ResolvedSegment
|
|
298
|
+
source: AsyncGenerator<ResolvedSegment>,
|
|
244
299
|
): AsyncGenerator<ResolvedSegment> {
|
|
245
300
|
const pipelineStart = performance.now();
|
|
246
301
|
const ms = ctx.metricsStore;
|
|
247
302
|
|
|
303
|
+
// Eagerly capture the HandleStore before any async operations.
|
|
304
|
+
// In workerd/Cloudflare, dynamic imports and fetch() inside the pipeline
|
|
305
|
+
// can disrupt AsyncLocalStorage, causing getRequestContext() to return
|
|
306
|
+
// undefined afterward. Capturing the reference early ensures handle replay
|
|
307
|
+
// and handler handle-push work regardless of ALS state.
|
|
308
|
+
const handleStoreRef = _getRequestContext()?._handleStore;
|
|
309
|
+
|
|
248
310
|
const {
|
|
249
311
|
evaluateRevalidation,
|
|
250
312
|
buildEntryRevalidateMap,
|
|
@@ -258,12 +320,54 @@ export function withCacheLookup<TEnv>(
|
|
|
258
320
|
await ensurePrerenderDeps();
|
|
259
321
|
if (prerenderStoreInstance) {
|
|
260
322
|
const paramHash = _hashParams!(ctx.matched.params);
|
|
261
|
-
const
|
|
262
|
-
|
|
323
|
+
const isPassthroughPrerenderRoute = ctx.entries.some(
|
|
324
|
+
(entry) =>
|
|
325
|
+
entry.type === "route" &&
|
|
326
|
+
entry.prerenderDef?.options?.passthrough === true,
|
|
263
327
|
);
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
328
|
+
|
|
329
|
+
if (ctx.isIntercept) {
|
|
330
|
+
// Intercept navigation: try intercept-specific prerender entry
|
|
331
|
+
const entry = await prerenderStoreInstance.get(
|
|
332
|
+
ctx.matched.routeKey,
|
|
333
|
+
paramHash + "/i",
|
|
334
|
+
{
|
|
335
|
+
pathname: ctx.pathname,
|
|
336
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
337
|
+
},
|
|
338
|
+
);
|
|
339
|
+
if (entry) {
|
|
340
|
+
yield* yieldFromStore(
|
|
341
|
+
entry,
|
|
342
|
+
ctx,
|
|
343
|
+
state,
|
|
344
|
+
pipelineStart,
|
|
345
|
+
handleStoreRef,
|
|
346
|
+
);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
// No intercept prerender -- fall through to normal pipeline
|
|
350
|
+
// (skip non-intercept prerender to let intercept-resolution run)
|
|
351
|
+
} else {
|
|
352
|
+
// Normal navigation: existing behavior
|
|
353
|
+
const entry = await prerenderStoreInstance.get(
|
|
354
|
+
ctx.matched.routeKey,
|
|
355
|
+
paramHash,
|
|
356
|
+
{
|
|
357
|
+
pathname: ctx.pathname,
|
|
358
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
359
|
+
},
|
|
360
|
+
);
|
|
361
|
+
if (entry) {
|
|
362
|
+
yield* yieldFromStore(
|
|
363
|
+
entry,
|
|
364
|
+
ctx,
|
|
365
|
+
state,
|
|
366
|
+
pipelineStart,
|
|
367
|
+
handleStoreRef,
|
|
368
|
+
);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
267
371
|
}
|
|
268
372
|
}
|
|
269
373
|
}
|
|
@@ -278,18 +382,61 @@ export function withCacheLookup<TEnv>(
|
|
|
278
382
|
// in-process where Node APIs work, so no interception is needed.
|
|
279
383
|
if (!ctx.isAction && !ctx.matched.pr && globalThis.__PRERENDER_DEV_URL) {
|
|
280
384
|
const hasStatic = ctx.entries.some(
|
|
281
|
-
(e) =>
|
|
385
|
+
(e) =>
|
|
386
|
+
(e.type === "layout" ||
|
|
387
|
+
e.type === "route" ||
|
|
388
|
+
e.type === "parallel") &&
|
|
389
|
+
e.isStaticPrerender,
|
|
282
390
|
);
|
|
283
391
|
if (hasStatic) {
|
|
284
392
|
await ensurePrerenderDeps();
|
|
285
393
|
if (prerenderStoreInstance) {
|
|
286
394
|
const paramHash = _hashParams!(ctx.matched.params);
|
|
287
|
-
const
|
|
288
|
-
|
|
395
|
+
const isPassthroughPrerenderRoute = ctx.entries.some(
|
|
396
|
+
(entry) =>
|
|
397
|
+
entry.type === "route" &&
|
|
398
|
+
entry.prerenderDef?.options?.passthrough === true,
|
|
289
399
|
);
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
400
|
+
|
|
401
|
+
if (ctx.isIntercept) {
|
|
402
|
+
const entry = await prerenderStoreInstance.get(
|
|
403
|
+
ctx.matched.routeKey,
|
|
404
|
+
paramHash + "/i",
|
|
405
|
+
{
|
|
406
|
+
pathname: ctx.pathname,
|
|
407
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
408
|
+
},
|
|
409
|
+
);
|
|
410
|
+
if (entry) {
|
|
411
|
+
yield* yieldFromStore(
|
|
412
|
+
entry,
|
|
413
|
+
ctx,
|
|
414
|
+
state,
|
|
415
|
+
pipelineStart,
|
|
416
|
+
handleStoreRef,
|
|
417
|
+
);
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
// No intercept prerender -- fall through to normal pipeline
|
|
421
|
+
} else {
|
|
422
|
+
const entry = await prerenderStoreInstance.get(
|
|
423
|
+
ctx.matched.routeKey,
|
|
424
|
+
paramHash,
|
|
425
|
+
{
|
|
426
|
+
pathname: ctx.pathname,
|
|
427
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
428
|
+
},
|
|
429
|
+
);
|
|
430
|
+
if (entry) {
|
|
431
|
+
yield* yieldFromStore(
|
|
432
|
+
entry,
|
|
433
|
+
ctx,
|
|
434
|
+
state,
|
|
435
|
+
pipelineStart,
|
|
436
|
+
handleStoreRef,
|
|
437
|
+
);
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
293
440
|
}
|
|
294
441
|
}
|
|
295
442
|
}
|
|
@@ -300,7 +447,11 @@ export function withCacheLookup<TEnv>(
|
|
|
300
447
|
// Cache miss - pass through to segment resolution
|
|
301
448
|
yield* source;
|
|
302
449
|
if (ms) {
|
|
303
|
-
ms.metrics.push({
|
|
450
|
+
ms.metrics.push({
|
|
451
|
+
label: "pipeline:cache-miss",
|
|
452
|
+
duration: performance.now() - pipelineStart,
|
|
453
|
+
startTime: pipelineStart - ms.requestStart,
|
|
454
|
+
});
|
|
304
455
|
}
|
|
305
456
|
return;
|
|
306
457
|
}
|
|
@@ -309,30 +460,54 @@ export function withCacheLookup<TEnv>(
|
|
|
309
460
|
const cacheResult = await ctx.cacheScope.lookupRoute(
|
|
310
461
|
ctx.pathname,
|
|
311
462
|
ctx.matched.params,
|
|
312
|
-
ctx.isIntercept
|
|
463
|
+
ctx.isIntercept,
|
|
313
464
|
);
|
|
314
465
|
|
|
315
466
|
if (!cacheResult) {
|
|
316
467
|
// Cache miss - pass through to segment resolution
|
|
317
468
|
yield* source;
|
|
318
469
|
if (ms) {
|
|
319
|
-
ms.metrics.push({
|
|
470
|
+
ms.metrics.push({
|
|
471
|
+
label: "pipeline:cache-miss",
|
|
472
|
+
duration: performance.now() - pipelineStart,
|
|
473
|
+
startTime: pipelineStart - ms.requestStart,
|
|
474
|
+
});
|
|
320
475
|
}
|
|
321
476
|
return;
|
|
322
477
|
}
|
|
323
478
|
|
|
324
479
|
// Cache HIT
|
|
325
480
|
state.cacheHit = true;
|
|
481
|
+
state.cacheSource = "runtime";
|
|
326
482
|
state.shouldRevalidate = cacheResult.shouldRevalidate;
|
|
327
483
|
state.cachedSegments = cacheResult.segments;
|
|
328
484
|
state.cachedMatchedIds = cacheResult.segments.map((s) => s.id);
|
|
329
485
|
|
|
330
|
-
// Apply revalidation to cached segments
|
|
331
|
-
|
|
486
|
+
// Apply revalidation to cached segments.
|
|
487
|
+
// For full matches or empty client segment sets, this map is unnecessary:
|
|
488
|
+
// we never run segment-level revalidation and can stream segments directly.
|
|
489
|
+
const canCheckSegmentRevalidation =
|
|
490
|
+
!ctx.isFullMatch &&
|
|
491
|
+
ctx.clientSegmentSet.size > 0 &&
|
|
492
|
+
!!buildEntryRevalidateMap;
|
|
493
|
+
const entryRevalidateMap = canCheckSegmentRevalidation
|
|
494
|
+
? buildEntryRevalidateMap(ctx.entries)
|
|
495
|
+
: undefined;
|
|
332
496
|
|
|
333
497
|
for (const segment of cacheResult.segments) {
|
|
334
498
|
// Skip segments client doesn't have - they need their component
|
|
335
499
|
if (!ctx.clientSegmentSet.has(segment.id)) {
|
|
500
|
+
if (isTraceActive()) {
|
|
501
|
+
pushRevalidationTraceEntry({
|
|
502
|
+
segmentId: segment.id,
|
|
503
|
+
segmentType: segment.type,
|
|
504
|
+
belongsToRoute: segment.belongsToRoute ?? false,
|
|
505
|
+
source: "cache-hit",
|
|
506
|
+
defaultShouldRevalidate: true,
|
|
507
|
+
finalShouldRevalidate: true,
|
|
508
|
+
reason: "new-segment",
|
|
509
|
+
});
|
|
510
|
+
}
|
|
336
511
|
yield segment;
|
|
337
512
|
continue;
|
|
338
513
|
}
|
|
@@ -345,8 +520,46 @@ export function withCacheLookup<TEnv>(
|
|
|
345
520
|
|
|
346
521
|
// Look up revalidation rules for this segment
|
|
347
522
|
const entryInfo = entryRevalidateMap?.get(segment.id);
|
|
523
|
+
|
|
524
|
+
// Even without explicit revalidation rules, route segments and their
|
|
525
|
+
// children must re-render when search params change — the handler reads
|
|
526
|
+
// ctx.searchParams so different ?page= values produce different content.
|
|
527
|
+
const searchChanged = ctx.prevUrl.search !== ctx.url.search;
|
|
528
|
+
const shouldDefaultRevalidate =
|
|
529
|
+
searchChanged &&
|
|
530
|
+
(segment.type === "route" ||
|
|
531
|
+
(segment.belongsToRoute &&
|
|
532
|
+
(segment.type === "layout" || segment.type === "parallel")));
|
|
533
|
+
|
|
348
534
|
if (!entryInfo || entryInfo.revalidate.length === 0) {
|
|
535
|
+
if (shouldDefaultRevalidate) {
|
|
536
|
+
// Search params changed — must re-render even without custom rules
|
|
537
|
+
if (isTraceActive()) {
|
|
538
|
+
pushRevalidationTraceEntry({
|
|
539
|
+
segmentId: segment.id,
|
|
540
|
+
segmentType: segment.type,
|
|
541
|
+
belongsToRoute: segment.belongsToRoute ?? false,
|
|
542
|
+
source: "cache-hit",
|
|
543
|
+
defaultShouldRevalidate: true,
|
|
544
|
+
finalShouldRevalidate: true,
|
|
545
|
+
reason: "cached-search-changed",
|
|
546
|
+
});
|
|
547
|
+
}
|
|
548
|
+
yield segment;
|
|
549
|
+
continue;
|
|
550
|
+
}
|
|
349
551
|
// No revalidation rules, use default behavior (skip if client has)
|
|
552
|
+
if (isTraceActive()) {
|
|
553
|
+
pushRevalidationTraceEntry({
|
|
554
|
+
segmentId: segment.id,
|
|
555
|
+
segmentType: segment.type,
|
|
556
|
+
belongsToRoute: segment.belongsToRoute ?? false,
|
|
557
|
+
source: "cache-hit",
|
|
558
|
+
defaultShouldRevalidate: false,
|
|
559
|
+
finalShouldRevalidate: false,
|
|
560
|
+
reason: "cached-no-rules",
|
|
561
|
+
});
|
|
562
|
+
}
|
|
350
563
|
segment.component = null;
|
|
351
564
|
segment.loading = undefined;
|
|
352
565
|
yield segment;
|
|
@@ -368,8 +581,24 @@ export function withCacheLookup<TEnv>(
|
|
|
368
581
|
routeKey: ctx.routeKey,
|
|
369
582
|
context: ctx.handlerContext,
|
|
370
583
|
actionContext: ctx.actionContext,
|
|
584
|
+
stale: cacheResult.shouldRevalidate || undefined,
|
|
585
|
+
traceSource: "cache-hit",
|
|
371
586
|
});
|
|
372
587
|
|
|
588
|
+
const routerCtx = getRouterContext<TEnv>();
|
|
589
|
+
if (routerCtx.telemetry) {
|
|
590
|
+
const tSink = resolveSink(routerCtx.telemetry);
|
|
591
|
+
safeEmit(tSink, {
|
|
592
|
+
type: "revalidation.decision",
|
|
593
|
+
timestamp: performance.now(),
|
|
594
|
+
requestId: routerCtx.requestId,
|
|
595
|
+
segmentId: segment.id,
|
|
596
|
+
pathname: ctx.pathname,
|
|
597
|
+
routeKey: ctx.routeKey,
|
|
598
|
+
shouldRevalidate,
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
|
|
373
602
|
if (!shouldRevalidate) {
|
|
374
603
|
// Client has it, no revalidation needed
|
|
375
604
|
segment.component = null;
|
|
@@ -382,12 +611,13 @@ export function withCacheLookup<TEnv>(
|
|
|
382
611
|
// Resolve loaders fresh (loaders are NOT cached by default)
|
|
383
612
|
// This ensures fresh data even on cache hit
|
|
384
613
|
const Store = ctx.Store;
|
|
614
|
+
const loaderStart = performance.now();
|
|
385
615
|
|
|
386
616
|
if (ctx.isFullMatch) {
|
|
387
617
|
// Full match (document request) - simple loader resolution without revalidation
|
|
388
618
|
if (resolveLoadersOnly) {
|
|
389
619
|
const loaderSegments = await Store.run(() =>
|
|
390
|
-
resolveLoadersOnly(ctx.entries, ctx.handlerContext)
|
|
620
|
+
resolveLoadersOnly(ctx.entries, ctx.handlerContext),
|
|
391
621
|
);
|
|
392
622
|
|
|
393
623
|
// Update state - full match doesn't track matchedIds separately
|
|
@@ -413,8 +643,13 @@ export function withCacheLookup<TEnv>(
|
|
|
413
643
|
ctx.prevUrl,
|
|
414
644
|
ctx.url,
|
|
415
645
|
ctx.routeKey,
|
|
416
|
-
ctx.actionContext
|
|
417
|
-
|
|
646
|
+
ctx.actionContext,
|
|
647
|
+
// Loaders are never cached in the segment cache, so segment
|
|
648
|
+
// staleness (cacheResult.shouldRevalidate) must not propagate.
|
|
649
|
+
// But browser-sent staleness (ctx.stale) — indicating an action
|
|
650
|
+
// happened in this or another tab — must still reach loaders.
|
|
651
|
+
ctx.stale || undefined,
|
|
652
|
+
),
|
|
418
653
|
);
|
|
419
654
|
|
|
420
655
|
// Update state with fresh loader matchedIds
|
|
@@ -432,7 +667,18 @@ export function withCacheLookup<TEnv>(
|
|
|
432
667
|
}
|
|
433
668
|
}
|
|
434
669
|
if (ms) {
|
|
435
|
-
|
|
670
|
+
const loaderEnd = performance.now();
|
|
671
|
+
ms.metrics.push({
|
|
672
|
+
label: "pipeline:loader-resolve",
|
|
673
|
+
duration: loaderEnd - loaderStart,
|
|
674
|
+
startTime: loaderStart - ms.requestStart,
|
|
675
|
+
depth: 1,
|
|
676
|
+
});
|
|
677
|
+
ms.metrics.push({
|
|
678
|
+
label: "pipeline:cache-hit",
|
|
679
|
+
duration: loaderEnd - pipelineStart,
|
|
680
|
+
startTime: pipelineStart - ms.requestStart,
|
|
681
|
+
});
|
|
436
682
|
}
|
|
437
683
|
};
|
|
438
684
|
}
|