@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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 +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- 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 +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +170 -18
- package/src/build/runtime-discovery.ts +13 -1
- 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 +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/index.ts +0 -3
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- package/src/router/segment-wrappers.ts +3 -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 +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- 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/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
|
@@ -30,23 +30,15 @@
|
|
|
30
30
|
* |
|
|
31
31
|
* v (async, doesn't block response)
|
|
32
32
|
* +---------------------------+
|
|
33
|
-
* | Create fresh
|
|
33
|
+
* | Create fresh context | Fresh handleStore, handlerContext,
|
|
34
|
+
* | (full isolation) | and loaderPromises map
|
|
34
35
|
* +---------------------------+
|
|
35
36
|
* |
|
|
36
37
|
* v
|
|
37
|
-
*
|
|
38
|
-
* |
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* +-----+-----+
|
|
42
|
-
* | |
|
|
43
|
-
* yes no
|
|
44
|
-
* | |
|
|
45
|
-
* v v
|
|
46
|
-
* resolveAll resolveWithRevalidation
|
|
47
|
-
* Segments + resolveIntercepts
|
|
48
|
-
* | |
|
|
49
|
-
* +-----------+
|
|
38
|
+
* +---------------------------+
|
|
39
|
+
* | resolveAllSegments() | Fresh resolution (no revalidation)
|
|
40
|
+
* | + resolveIntercepts() | Ensures complete components
|
|
41
|
+
* +---------------------------+
|
|
50
42
|
* |
|
|
51
43
|
* v
|
|
52
44
|
* +---------------------------+
|
|
@@ -90,27 +82,22 @@
|
|
|
90
82
|
* ISOLATION FROM RESPONSE
|
|
91
83
|
* =======================
|
|
92
84
|
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
85
|
+
* Background revalidation creates fully isolated context:
|
|
86
|
+
* - Fresh handleStore (prevents polluting the response stream)
|
|
87
|
+
* - Fresh handlerContext + loaderPromises (prevents reusing memoized
|
|
88
|
+
* loader results from the foreground pass)
|
|
89
|
+
* - handleStore is saved/restored in try/finally
|
|
96
90
|
*
|
|
97
|
-
* This
|
|
98
|
-
* - Polluting the current response stream
|
|
99
|
-
* - Causing duplicate data in the client
|
|
100
|
-
* - Creating race conditions
|
|
91
|
+
* This matches the proactive caching pattern in cache-store.ts.
|
|
101
92
|
*
|
|
102
93
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
94
|
+
* FRESH RESOLUTION (NO REVALIDATION)
|
|
95
|
+
* ===================================
|
|
105
96
|
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* Partial Match (navigation):
|
|
111
|
-
* - resolveAllSegmentsWithRevalidation()
|
|
112
|
-
* - Also resolves intercept segments if applicable
|
|
113
|
-
* - More complex but handles all scenarios
|
|
97
|
+
* Both full and partial requests use resolveAllSegments() (without
|
|
98
|
+
* revalidation logic) to ensure all segments have complete components.
|
|
99
|
+
* Using revalidation-aware resolution would produce null components
|
|
100
|
+
* for skipped segments, which would corrupt the cache entry.
|
|
114
101
|
*/
|
|
115
102
|
import type { ResolvedSegment } from "../../types.js";
|
|
116
103
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
@@ -148,7 +135,8 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
148
135
|
const {
|
|
149
136
|
getRequestContext,
|
|
150
137
|
createHandleStore,
|
|
151
|
-
|
|
138
|
+
createHandlerContext,
|
|
139
|
+
setupLoaderAccess,
|
|
152
140
|
resolveAllSegments,
|
|
153
141
|
resolveInterceptEntry,
|
|
154
142
|
} = getRouterContext<TEnv>();
|
|
@@ -161,72 +149,62 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
161
149
|
pathname: ctx.pathname,
|
|
162
150
|
fullMatch: ctx.isFullMatch,
|
|
163
151
|
});
|
|
164
|
-
try {
|
|
165
|
-
// Create a fresh handleStore for background revalidation
|
|
166
|
-
// to avoid polluting the current response's handle stream
|
|
167
|
-
if (requestCtx) {
|
|
168
|
-
requestCtx._handleStore = createHandleStore();
|
|
169
|
-
}
|
|
170
152
|
|
|
171
|
-
|
|
153
|
+
// Save and replace handleStore to avoid polluting the response stream.
|
|
154
|
+
// Restore in finally (same pattern as proactive caching in cache-store).
|
|
155
|
+
const originalHandleStore = requestCtx._handleStore;
|
|
156
|
+
requestCtx._handleStore = createHandleStore();
|
|
172
157
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
158
|
+
try {
|
|
159
|
+
// Create fresh handler context and loader promises to avoid
|
|
160
|
+
// reusing memoized results from the foreground pass
|
|
161
|
+
const freshHandlerContext = createHandlerContext(
|
|
162
|
+
ctx.matched.params,
|
|
163
|
+
ctx.request,
|
|
164
|
+
ctx.url.searchParams,
|
|
165
|
+
ctx.pathname,
|
|
166
|
+
ctx.url,
|
|
167
|
+
ctx.env,
|
|
168
|
+
ctx.routeMap,
|
|
169
|
+
ctx.matched.routeKey,
|
|
170
|
+
ctx.matched.responseType,
|
|
171
|
+
ctx.matched.pt === true,
|
|
172
|
+
);
|
|
173
|
+
const freshLoaderPromises = new Map<string, Promise<any>>();
|
|
174
|
+
setupLoaderAccess(freshHandlerContext, freshLoaderPromises);
|
|
175
|
+
|
|
176
|
+
// Resolve all segments fresh (without revalidation logic)
|
|
177
|
+
// to ensure complete components for caching
|
|
178
|
+
const freshSegments = await ctx.Store.run(() =>
|
|
179
|
+
resolveAllSegments(
|
|
185
180
|
ctx.entries,
|
|
186
181
|
ctx.routeKey,
|
|
187
182
|
ctx.matched.params,
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
ctx.prevUrl,
|
|
193
|
-
ctx.url,
|
|
194
|
-
ctx.loaderPromises,
|
|
195
|
-
ctx.actionContext,
|
|
196
|
-
ctx.interceptResult,
|
|
197
|
-
ctx.localRouteName,
|
|
198
|
-
ctx.pathname,
|
|
199
|
-
);
|
|
200
|
-
|
|
201
|
-
freshSegments = freshResult.segments;
|
|
183
|
+
freshHandlerContext,
|
|
184
|
+
freshLoaderPromises,
|
|
185
|
+
),
|
|
186
|
+
);
|
|
202
187
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
188
|
+
// Also resolve intercept segments fresh if applicable
|
|
189
|
+
let freshInterceptSegments: ResolvedSegment[] = [];
|
|
190
|
+
if (ctx.interceptResult) {
|
|
191
|
+
freshInterceptSegments = await ctx.Store.run(() =>
|
|
192
|
+
resolveInterceptEntry(
|
|
193
|
+
ctx.interceptResult!.intercept,
|
|
194
|
+
ctx.interceptResult!.entry,
|
|
208
195
|
ctx.matched.params,
|
|
209
|
-
|
|
196
|
+
freshHandlerContext,
|
|
210
197
|
true,
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
prevParams: ctx.prevParams,
|
|
214
|
-
request: ctx.request,
|
|
215
|
-
prevUrl: ctx.prevUrl,
|
|
216
|
-
nextUrl: ctx.url,
|
|
217
|
-
routeKey: ctx.routeKey,
|
|
218
|
-
actionContext: ctx.actionContext,
|
|
219
|
-
stale: false,
|
|
220
|
-
},
|
|
221
|
-
);
|
|
222
|
-
freshSegments = [...freshSegments, ...freshInterceptSegments];
|
|
223
|
-
}
|
|
198
|
+
),
|
|
199
|
+
);
|
|
224
200
|
}
|
|
225
201
|
|
|
202
|
+
const completeSegments = [...freshSegments, ...freshInterceptSegments];
|
|
203
|
+
requestCtx._handleStore.seal();
|
|
226
204
|
await cacheScope.cacheRoute(
|
|
227
205
|
ctx.pathname,
|
|
228
206
|
ctx.matched.params,
|
|
229
|
-
|
|
207
|
+
completeSegments,
|
|
230
208
|
ctx.isIntercept,
|
|
231
209
|
);
|
|
232
210
|
debugLog("backgroundRevalidation", "revalidation complete", {
|
|
@@ -237,6 +215,8 @@ export function withBackgroundRevalidation<TEnv>(
|
|
|
237
215
|
pathname: ctx.pathname,
|
|
238
216
|
error: String(error),
|
|
239
217
|
});
|
|
218
|
+
} finally {
|
|
219
|
+
requestCtx._handleStore = originalHandleStore;
|
|
240
220
|
}
|
|
241
221
|
});
|
|
242
222
|
};
|
|
@@ -92,9 +92,14 @@
|
|
|
92
92
|
import type { ResolvedSegment } from "../../types.js";
|
|
93
93
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
94
94
|
import { getRouterContext } from "../router-context.js";
|
|
95
|
+
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
96
|
+
import { pushRevalidationTraceEntry, isTraceActive } from "../logging.js";
|
|
95
97
|
import type { PrerenderStore, PrerenderEntry } from "../../prerender/store.js";
|
|
96
98
|
import type { HandleStore } from "../../server/handle-store.js";
|
|
97
|
-
import {
|
|
99
|
+
import {
|
|
100
|
+
getRequestContext,
|
|
101
|
+
_getRequestContext,
|
|
102
|
+
} from "../../server/request-context.js";
|
|
98
103
|
|
|
99
104
|
// Lazily initialized prerender store singleton and dynamically imported deps.
|
|
100
105
|
// Dynamic imports prevent pulling in @vitejs/plugin-rsc/rsc virtual module at
|
|
@@ -109,7 +114,7 @@ let _restoreHandles:
|
|
|
109
114
|
let _hashParams:
|
|
110
115
|
| typeof import("../../prerender/param-hash.js").hashParams
|
|
111
116
|
| undefined;
|
|
112
|
-
let
|
|
117
|
+
let _lazyGetRequestContext:
|
|
113
118
|
| typeof import("../../server/request-context.js").getRequestContext
|
|
114
119
|
| undefined;
|
|
115
120
|
|
|
@@ -141,7 +146,7 @@ async function ensurePrerenderDeps() {
|
|
|
141
146
|
_deserializeSegments = codec.deserializeSegments;
|
|
142
147
|
_restoreHandles = snapshot.restoreHandles;
|
|
143
148
|
_hashParams = paramHash.hashParams;
|
|
144
|
-
|
|
149
|
+
_lazyGetRequestContext = reqCtx.getRequestContext;
|
|
145
150
|
if (prerenderStoreInstance === undefined) {
|
|
146
151
|
prerenderStoreInstance = store.createPrerenderStore();
|
|
147
152
|
}
|
|
@@ -167,7 +172,7 @@ async function* yieldFromStore<TEnv>(
|
|
|
167
172
|
!_deserializeSegments ||
|
|
168
173
|
!_restoreHandles ||
|
|
169
174
|
!_hashParams ||
|
|
170
|
-
!
|
|
175
|
+
!_lazyGetRequestContext
|
|
171
176
|
) {
|
|
172
177
|
throw new Error("yieldFromStore called before ensurePrerenderDeps");
|
|
173
178
|
}
|
|
@@ -176,12 +181,13 @@ async function* yieldFromStore<TEnv>(
|
|
|
176
181
|
|
|
177
182
|
// Replay handle data (same as runtime cache hit path).
|
|
178
183
|
// Prefer the eagerly-captured handleStoreRef to avoid ALS disruption in workerd.
|
|
179
|
-
const handleStore = handleStoreRef ??
|
|
184
|
+
const handleStore = handleStoreRef ?? _lazyGetRequestContext()?._handleStore;
|
|
180
185
|
if (handleStore) {
|
|
181
186
|
_restoreHandles(entry.handles, handleStore);
|
|
182
187
|
}
|
|
183
188
|
|
|
184
189
|
state.cacheHit = true;
|
|
190
|
+
state.cacheSource = "prerender";
|
|
185
191
|
state.cachedSegments = segments;
|
|
186
192
|
state.cachedMatchedIds = segments.map((s) => s.id);
|
|
187
193
|
|
|
@@ -288,7 +294,7 @@ export function withCacheLookup<TEnv>(
|
|
|
288
294
|
// can disrupt AsyncLocalStorage, causing getRequestContext() to return
|
|
289
295
|
// undefined afterward. Capturing the reference early ensures handle replay
|
|
290
296
|
// and handler handle-push work regardless of ALS state.
|
|
291
|
-
const handleStoreRef =
|
|
297
|
+
const handleStoreRef = _getRequestContext()?._handleStore;
|
|
292
298
|
|
|
293
299
|
const {
|
|
294
300
|
evaluateRevalidation,
|
|
@@ -303,13 +309,21 @@ export function withCacheLookup<TEnv>(
|
|
|
303
309
|
await ensurePrerenderDeps();
|
|
304
310
|
if (prerenderStoreInstance) {
|
|
305
311
|
const paramHash = _hashParams!(ctx.matched.params);
|
|
312
|
+
const isPassthroughPrerenderRoute = ctx.entries.some(
|
|
313
|
+
(entry) =>
|
|
314
|
+
entry.type === "route" &&
|
|
315
|
+
entry.prerenderDef?.options?.passthrough === true,
|
|
316
|
+
);
|
|
306
317
|
|
|
307
318
|
if (ctx.isIntercept) {
|
|
308
319
|
// Intercept navigation: try intercept-specific prerender entry
|
|
309
320
|
const entry = await prerenderStoreInstance.get(
|
|
310
321
|
ctx.matched.routeKey,
|
|
311
322
|
paramHash + "/i",
|
|
312
|
-
{
|
|
323
|
+
{
|
|
324
|
+
pathname: ctx.pathname,
|
|
325
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
326
|
+
},
|
|
313
327
|
);
|
|
314
328
|
if (entry) {
|
|
315
329
|
yield* yieldFromStore(
|
|
@@ -328,7 +342,10 @@ export function withCacheLookup<TEnv>(
|
|
|
328
342
|
const entry = await prerenderStoreInstance.get(
|
|
329
343
|
ctx.matched.routeKey,
|
|
330
344
|
paramHash,
|
|
331
|
-
{
|
|
345
|
+
{
|
|
346
|
+
pathname: ctx.pathname,
|
|
347
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
348
|
+
},
|
|
332
349
|
);
|
|
333
350
|
if (entry) {
|
|
334
351
|
yield* yieldFromStore(
|
|
@@ -364,12 +381,20 @@ export function withCacheLookup<TEnv>(
|
|
|
364
381
|
await ensurePrerenderDeps();
|
|
365
382
|
if (prerenderStoreInstance) {
|
|
366
383
|
const paramHash = _hashParams!(ctx.matched.params);
|
|
384
|
+
const isPassthroughPrerenderRoute = ctx.entries.some(
|
|
385
|
+
(entry) =>
|
|
386
|
+
entry.type === "route" &&
|
|
387
|
+
entry.prerenderDef?.options?.passthrough === true,
|
|
388
|
+
);
|
|
367
389
|
|
|
368
390
|
if (ctx.isIntercept) {
|
|
369
391
|
const entry = await prerenderStoreInstance.get(
|
|
370
392
|
ctx.matched.routeKey,
|
|
371
393
|
paramHash + "/i",
|
|
372
|
-
{
|
|
394
|
+
{
|
|
395
|
+
pathname: ctx.pathname,
|
|
396
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
397
|
+
},
|
|
373
398
|
);
|
|
374
399
|
if (entry) {
|
|
375
400
|
yield* yieldFromStore(
|
|
@@ -386,7 +411,10 @@ export function withCacheLookup<TEnv>(
|
|
|
386
411
|
const entry = await prerenderStoreInstance.get(
|
|
387
412
|
ctx.matched.routeKey,
|
|
388
413
|
paramHash,
|
|
389
|
-
{
|
|
414
|
+
{
|
|
415
|
+
pathname: ctx.pathname,
|
|
416
|
+
isPassthroughRoute: isPassthroughPrerenderRoute,
|
|
417
|
+
},
|
|
390
418
|
);
|
|
391
419
|
if (entry) {
|
|
392
420
|
yield* yieldFromStore(
|
|
@@ -439,6 +467,7 @@ export function withCacheLookup<TEnv>(
|
|
|
439
467
|
|
|
440
468
|
// Cache HIT
|
|
441
469
|
state.cacheHit = true;
|
|
470
|
+
state.cacheSource = "runtime";
|
|
442
471
|
state.shouldRevalidate = cacheResult.shouldRevalidate;
|
|
443
472
|
state.cachedSegments = cacheResult.segments;
|
|
444
473
|
state.cachedMatchedIds = cacheResult.segments.map((s) => s.id);
|
|
@@ -457,6 +486,17 @@ export function withCacheLookup<TEnv>(
|
|
|
457
486
|
for (const segment of cacheResult.segments) {
|
|
458
487
|
// Skip segments client doesn't have - they need their component
|
|
459
488
|
if (!ctx.clientSegmentSet.has(segment.id)) {
|
|
489
|
+
if (isTraceActive()) {
|
|
490
|
+
pushRevalidationTraceEntry({
|
|
491
|
+
segmentId: segment.id,
|
|
492
|
+
segmentType: segment.type,
|
|
493
|
+
belongsToRoute: segment.belongsToRoute ?? false,
|
|
494
|
+
source: "cache-hit",
|
|
495
|
+
defaultShouldRevalidate: true,
|
|
496
|
+
finalShouldRevalidate: true,
|
|
497
|
+
reason: "new-segment",
|
|
498
|
+
});
|
|
499
|
+
}
|
|
460
500
|
yield segment;
|
|
461
501
|
continue;
|
|
462
502
|
}
|
|
@@ -471,6 +511,17 @@ export function withCacheLookup<TEnv>(
|
|
|
471
511
|
const entryInfo = entryRevalidateMap?.get(segment.id);
|
|
472
512
|
if (!entryInfo || entryInfo.revalidate.length === 0) {
|
|
473
513
|
// No revalidation rules, use default behavior (skip if client has)
|
|
514
|
+
if (isTraceActive()) {
|
|
515
|
+
pushRevalidationTraceEntry({
|
|
516
|
+
segmentId: segment.id,
|
|
517
|
+
segmentType: segment.type,
|
|
518
|
+
belongsToRoute: segment.belongsToRoute ?? false,
|
|
519
|
+
source: "cache-hit",
|
|
520
|
+
defaultShouldRevalidate: false,
|
|
521
|
+
finalShouldRevalidate: false,
|
|
522
|
+
reason: "cached-no-rules",
|
|
523
|
+
});
|
|
524
|
+
}
|
|
474
525
|
segment.component = null;
|
|
475
526
|
segment.loading = undefined;
|
|
476
527
|
yield segment;
|
|
@@ -492,8 +543,24 @@ export function withCacheLookup<TEnv>(
|
|
|
492
543
|
routeKey: ctx.routeKey,
|
|
493
544
|
context: ctx.handlerContext,
|
|
494
545
|
actionContext: ctx.actionContext,
|
|
546
|
+
stale: cacheResult.shouldRevalidate || undefined,
|
|
547
|
+
traceSource: "cache-hit",
|
|
495
548
|
});
|
|
496
549
|
|
|
550
|
+
const routerCtx = getRouterContext<TEnv>();
|
|
551
|
+
if (routerCtx.telemetry) {
|
|
552
|
+
const tSink = resolveSink(routerCtx.telemetry);
|
|
553
|
+
safeEmit(tSink, {
|
|
554
|
+
type: "revalidation.decision",
|
|
555
|
+
timestamp: performance.now(),
|
|
556
|
+
requestId: routerCtx.requestId,
|
|
557
|
+
segmentId: segment.id,
|
|
558
|
+
pathname: ctx.pathname,
|
|
559
|
+
routeKey: ctx.routeKey,
|
|
560
|
+
shouldRevalidate,
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
|
|
497
564
|
if (!shouldRevalidate) {
|
|
498
565
|
// Client has it, no revalidation needed
|
|
499
566
|
segment.component = null;
|
|
@@ -538,6 +605,7 @@ export function withCacheLookup<TEnv>(
|
|
|
538
605
|
ctx.url,
|
|
539
606
|
ctx.routeKey,
|
|
540
607
|
ctx.actionContext,
|
|
608
|
+
cacheResult.shouldRevalidate || undefined,
|
|
541
609
|
),
|
|
542
610
|
);
|
|
543
611
|
|
|
@@ -211,6 +211,7 @@ export function withCacheStore<TEnv>(
|
|
|
211
211
|
ctx.routeMap,
|
|
212
212
|
ctx.matched.routeKey,
|
|
213
213
|
ctx.matched.responseType,
|
|
214
|
+
ctx.matched.pt === true,
|
|
214
215
|
);
|
|
215
216
|
const proactiveLoaderPromises = new Map<string, Promise<any>>();
|
|
216
217
|
|
|
@@ -248,6 +249,7 @@ export function withCacheStore<TEnv>(
|
|
|
248
249
|
...freshSegments,
|
|
249
250
|
...freshInterceptSegments,
|
|
250
251
|
];
|
|
252
|
+
requestCtx._handleStore.seal();
|
|
251
253
|
await cacheScope.cacheRoute(
|
|
252
254
|
ctx.pathname,
|
|
253
255
|
ctx.matched.params,
|
|
@@ -86,19 +86,14 @@
|
|
|
86
86
|
* -> output: cached segments + fresh loader data
|
|
87
87
|
*
|
|
88
88
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
* 2. createMatchPartialPipeline (Partial Match)
|
|
98
|
-
* - Used for client-side navigation
|
|
99
|
-
* - Includes revalidation for SWR
|
|
100
|
-
* - Compares with previous params/URL
|
|
101
|
-
* - Supports intercepts (soft navigation modals)
|
|
89
|
+
* PIPELINE VARIANT
|
|
90
|
+
* ================
|
|
91
|
+
*
|
|
92
|
+
* createMatchPartialPipeline handles both full (document) and partial
|
|
93
|
+
* (navigation) requests. The middleware steps adapt based on ctx.isFullMatch:
|
|
94
|
+
* - cache-lookup/store work for both
|
|
95
|
+
* - background-revalidation is a no-op for full matches (no stale state)
|
|
96
|
+
* - intercept-resolution is a no-op for full matches (no previous navigation)
|
|
102
97
|
*/
|
|
103
98
|
import type { ResolvedSegment } from "../types.js";
|
|
104
99
|
import type { MatchContext, MatchPipelineState } from "./match-context.js";
|
|
@@ -182,33 +177,3 @@ export function createMatchPartialPipeline<TEnv>(
|
|
|
182
177
|
// Start with empty source - cache lookup or segment resolution will produce segments
|
|
183
178
|
return pipeline(empty());
|
|
184
179
|
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Create the full match pipeline (simpler, no revalidation)
|
|
188
|
-
*
|
|
189
|
-
* Used for document requests (initial page load) where we don't need
|
|
190
|
-
* revalidation logic since there's no previous state to compare against.
|
|
191
|
-
*/
|
|
192
|
-
export function createMatchPipeline<TEnv>(
|
|
193
|
-
ctx: MatchContext<TEnv>,
|
|
194
|
-
state: MatchPipelineState,
|
|
195
|
-
): AsyncGenerator<ResolvedSegment> {
|
|
196
|
-
// For full match, we only need:
|
|
197
|
-
// 1. Cache lookup
|
|
198
|
-
// 2. Segment resolution (without revalidation)
|
|
199
|
-
// 3. Intercept resolution
|
|
200
|
-
// 4. Cache store
|
|
201
|
-
|
|
202
|
-
// Note: Full match uses different resolution logic (resolveAllSegments instead of
|
|
203
|
-
// resolveAllSegmentsWithRevalidation). This will be handled by the segment resolution
|
|
204
|
-
// middleware checking ctx.isFullMatch or similar flag.
|
|
205
|
-
|
|
206
|
-
const pipeline = compose<ResolvedSegment>(
|
|
207
|
-
withCacheStore(ctx, state),
|
|
208
|
-
withInterceptResolution(ctx, state),
|
|
209
|
-
withSegmentResolution(ctx, state),
|
|
210
|
-
withCacheLookup(ctx, state),
|
|
211
|
-
);
|
|
212
|
-
|
|
213
|
-
return pipeline(empty());
|
|
214
|
-
}
|
|
@@ -108,7 +108,6 @@
|
|
|
108
108
|
*/
|
|
109
109
|
import type { MatchResult, ResolvedSegment } from "../types.js";
|
|
110
110
|
import type { MatchContext, MatchPipelineState } from "./match-context.js";
|
|
111
|
-
import { generateServerTiming, logMetrics } from "./metrics.js";
|
|
112
111
|
import { debugLog } from "./logging.js";
|
|
113
112
|
|
|
114
113
|
/**
|
|
@@ -186,20 +185,12 @@ export function buildMatchResult<TEnv>(
|
|
|
186
185
|
segmentIds: segmentsToRender.map((s) => s.id),
|
|
187
186
|
});
|
|
188
187
|
|
|
189
|
-
// Output metrics if enabled
|
|
190
|
-
let serverTiming: string | undefined;
|
|
191
|
-
if (ctx.metricsStore) {
|
|
192
|
-
logMetrics(ctx.request.method, ctx.pathname, ctx.metricsStore);
|
|
193
|
-
serverTiming = generateServerTiming(ctx.metricsStore);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
188
|
return {
|
|
197
189
|
segments: segmentsToRender,
|
|
198
190
|
matched: allIds,
|
|
199
191
|
diff: segmentsToRender.map((s) => s.id),
|
|
200
192
|
params: ctx.matched.params,
|
|
201
193
|
routeName: ctx.routeKey,
|
|
202
|
-
serverTiming,
|
|
203
194
|
slots: Object.keys(state.slots).length > 0 ? state.slots : undefined,
|
|
204
195
|
routeMiddleware:
|
|
205
196
|
ctx.routeMiddleware.length > 0 ? ctx.routeMiddleware : undefined,
|