@rangojs/router 0.0.0-experimental.0f44aca1
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 +5 -0
- package/README.md +899 -0
- package/dist/bin/rango.js +1601 -0
- package/dist/vite/index.js +5214 -0
- package/package.json +176 -0
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +262 -0
- package/skills/caching/SKILL.md +220 -0
- package/skills/composability/SKILL.md +172 -0
- package/skills/debug-manifest/SKILL.md +112 -0
- package/skills/document-cache/SKILL.md +182 -0
- package/skills/fonts/SKILL.md +167 -0
- package/skills/hooks/SKILL.md +704 -0
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +313 -0
- package/skills/layout/SKILL.md +310 -0
- package/skills/links/SKILL.md +239 -0
- package/skills/loader/SKILL.md +596 -0
- package/skills/middleware/SKILL.md +339 -0
- package/skills/mime-routes/SKILL.md +128 -0
- package/skills/parallel/SKILL.md +305 -0
- package/skills/prerender/SKILL.md +643 -0
- package/skills/rango/SKILL.md +118 -0
- package/skills/response-routes/SKILL.md +411 -0
- package/skills/route/SKILL.md +385 -0
- package/skills/router-setup/SKILL.md +439 -0
- package/skills/tailwind/SKILL.md +129 -0
- package/skills/theme/SKILL.md +79 -0
- package/skills/typesafety/SKILL.md +623 -0
- package/skills/use-cache/SKILL.md +324 -0
- package/src/__internal.ts +273 -0
- package/src/bin/rango.ts +321 -0
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/action-response-classifier.ts +99 -0
- package/src/browser/event-controller.ts +899 -0
- package/src/browser/history-state.ts +80 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/intercept-utils.ts +52 -0
- package/src/browser/link-interceptor.ts +141 -0
- package/src/browser/logging.ts +55 -0
- package/src/browser/merge-segment-loaders.ts +134 -0
- package/src/browser/navigation-bridge.ts +645 -0
- package/src/browser/navigation-client.ts +215 -0
- package/src/browser/navigation-store.ts +806 -0
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/network-error-handler.ts +61 -0
- package/src/browser/partial-update.ts +550 -0
- package/src/browser/prefetch/cache.ts +146 -0
- package/src/browser/prefetch/fetch.ts +135 -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 +360 -0
- package/src/browser/react/NavigationProvider.tsx +386 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +59 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +162 -0
- package/src/browser/react/location-state.ts +107 -0
- package/src/browser/react/mount-context.ts +37 -0
- 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 +218 -0
- package/src/browser/react/use-client-cache.ts +58 -0
- package/src/browser/react/use-handle.ts +162 -0
- package/src/browser/react/use-href.tsx +40 -0
- package/src/browser/react/use-link-status.ts +135 -0
- package/src/browser/react/use-mount.ts +31 -0
- package/src/browser/react/use-navigation.ts +99 -0
- 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 +171 -0
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +431 -0
- package/src/browser/scroll-restoration.ts +400 -0
- package/src/browser/segment-reconciler.ts +216 -0
- package/src/browser/segment-structure-assert.ts +83 -0
- package/src/browser/server-action-bridge.ts +667 -0
- package/src/browser/shallow.ts +40 -0
- package/src/browser/types.ts +538 -0
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +438 -0
- package/src/build/generate-route-types.ts +36 -0
- package/src/build/index.ts +35 -0
- package/src/build/route-trie.ts +265 -0
- 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 +469 -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 +338 -0
- package/src/cache/cache-scope.ts +382 -0
- package/src/cache/cf/cf-cache-store.ts +540 -0
- package/src/cache/cf/index.ts +25 -0
- package/src/cache/document-cache.ts +369 -0
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/handle-snapshot.ts +41 -0
- package/src/cache/index.ts +43 -0
- package/src/cache/memory-segment-store.ts +328 -0
- 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 +342 -0
- package/src/client.rsc.tsx +85 -0
- package/src/client.tsx +601 -0
- package/src/component-utils.ts +76 -0
- package/src/components/DefaultDocument.tsx +27 -0
- package/src/context-var.ts +86 -0
- package/src/debug.ts +243 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +365 -0
- package/src/handle.ts +135 -0
- package/src/handles/MetaTags.tsx +246 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +7 -0
- package/src/handles/meta.ts +264 -0
- package/src/host/cookie-handler.ts +165 -0
- package/src/host/errors.ts +97 -0
- package/src/host/index.ts +53 -0
- package/src/host/pattern-matcher.ts +214 -0
- package/src/host/router.ts +352 -0
- package/src/host/testing.ts +79 -0
- package/src/host/types.ts +146 -0
- package/src/host/utils.ts +25 -0
- package/src/href-client.ts +222 -0
- package/src/index.rsc.ts +233 -0
- package/src/index.ts +277 -0
- package/src/internal-debug.ts +11 -0
- package/src/loader.rsc.ts +89 -0
- package/src/loader.ts +64 -0
- package/src/network-error-thrower.tsx +23 -0
- package/src/outlet-context.ts +15 -0
- package/src/outlet-provider.tsx +45 -0
- package/src/prerender/param-hash.ts +37 -0
- package/src/prerender/store.ts +185 -0
- package/src/prerender.ts +463 -0
- package/src/reverse.ts +330 -0
- package/src/root-error-boundary.tsx +289 -0
- package/src/route-content-wrapper.tsx +196 -0
- package/src/route-definition/dsl-helpers.ts +934 -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 -0
- package/src/route-map-builder.ts +275 -0
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +259 -0
- package/src/router/content-negotiation.ts +116 -0
- package/src/router/debug-manifest.ts +72 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/find-match.ts +158 -0
- package/src/router/handler-context.ts +451 -0
- package/src/router/intercept-resolution.ts +395 -0
- package/src/router/lazy-includes.ts +234 -0
- package/src/router/loader-resolution.ts +420 -0
- package/src/router/logging.ts +248 -0
- package/src/router/manifest.ts +267 -0
- package/src/router/match-api.ts +620 -0
- package/src/router/match-context.ts +266 -0
- package/src/router/match-handlers.ts +440 -0
- package/src/router/match-middleware/background-revalidation.ts +223 -0
- package/src/router/match-middleware/cache-lookup.ts +634 -0
- package/src/router/match-middleware/cache-store.ts +295 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +306 -0
- package/src/router/match-middleware/segment-resolution.ts +192 -0
- package/src/router/match-pipelines.ts +179 -0
- package/src/router/match-result.ts +219 -0
- package/src/router/metrics.ts +282 -0
- package/src/router/middleware-cookies.ts +55 -0
- package/src/router/middleware-types.ts +222 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +563 -0
- package/src/router/prerender-match.ts +402 -0
- package/src/router/preview-match.ts +170 -0
- package/src/router/revalidation.ts +289 -0
- package/src/router/router-context.ts +316 -0
- 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 +570 -0
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +198 -0
- package/src/router/segment-resolution/revalidation.ts +1239 -0
- package/src/router/segment-resolution/static-store.ts +67 -0
- package/src/router/segment-resolution.ts +21 -0
- package/src/router/segment-wrappers.ts +289 -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 +239 -0
- package/src/router/types.ts +170 -0
- package/src/router.ts +1002 -0
- package/src/rsc/handler-context.ts +45 -0
- package/src/rsc/handler.ts +1089 -0
- package/src/rsc/helpers.ts +198 -0
- package/src/rsc/index.ts +36 -0
- package/src/rsc/loader-fetch.ts +209 -0
- package/src/rsc/manifest-init.ts +86 -0
- package/src/rsc/nonce.ts +32 -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 +235 -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 +263 -0
- package/src/search-params.ts +230 -0
- package/src/segment-system.tsx +454 -0
- package/src/server/context.ts +591 -0
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +37 -0
- package/src/server/handle-store.ts +308 -0
- package/src/server/loader-registry.ts +133 -0
- package/src/server/request-context.ts +914 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +51 -0
- package/src/ssr/index.tsx +365 -0
- package/src/static-handler.ts +114 -0
- package/src/theme/ThemeProvider.tsx +297 -0
- package/src/theme/ThemeScript.tsx +61 -0
- package/src/theme/constants.ts +62 -0
- package/src/theme/index.ts +48 -0
- package/src/theme/theme-context.ts +44 -0
- package/src/theme/theme-script.ts +155 -0
- package/src/theme/types.ts +182 -0
- package/src/theme/use-theme.ts +44 -0
- 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 +687 -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 +102 -0
- package/src/types/segments.ts +148 -0
- package/src/types.ts +1 -0
- 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 -0
- package/src/use-loader.tsx +354 -0
- 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 +110 -0
- package/src/vite/discovery/virtual-module-codegen.ts +203 -0
- package/src/vite/index.ts +16 -0
- package/src/vite/plugin-types.ts +131 -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/plugins/expose-action-id.ts +365 -0
- package/src/vite/plugins/expose-id-utils.ts +287 -0
- 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 +254 -0
- package/src/vite/plugins/version.d.ts +12 -0
- package/src/vite/plugins/virtual-entries.ts +123 -0
- package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
- package/src/vite/rango.ts +510 -0
- package/src/vite/router-discovery.ts +785 -0
- package/src/vite/utils/ast-handler-extract.ts +517 -0
- 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/utils/package-resolution.ts +121 -0
- package/src/vite/utils/prerender-utils.ts +189 -0
- package/src/vite/utils/shared-utils.ts +169 -0
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Store Middleware
|
|
3
|
+
*
|
|
4
|
+
* Stores resolved segments in cache for future requests.
|
|
5
|
+
* Implements proactive caching for partial navigation scenarios.
|
|
6
|
+
*
|
|
7
|
+
* FLOW DIAGRAM
|
|
8
|
+
* ============
|
|
9
|
+
*
|
|
10
|
+
* source (from intercept-resolution)
|
|
11
|
+
* |
|
|
12
|
+
* v
|
|
13
|
+
* +---------------------------+
|
|
14
|
+
* | Collect + yield all | Observer pattern: pass through
|
|
15
|
+
* | allSegments[] |
|
|
16
|
+
* +---------------------------+
|
|
17
|
+
* |
|
|
18
|
+
* v
|
|
19
|
+
* +---------------------+
|
|
20
|
+
* | Should skip cache? |
|
|
21
|
+
* | - !cacheScope |──yes──> return
|
|
22
|
+
* | - isAction |
|
|
23
|
+
* | - cacheHit |
|
|
24
|
+
* | - method !== GET |
|
|
25
|
+
* +---------------------+
|
|
26
|
+
* | no
|
|
27
|
+
* v
|
|
28
|
+
* +-------------------------------+
|
|
29
|
+
* | Any null components? |
|
|
30
|
+
* | (client already has segment) |
|
|
31
|
+
* +-------------------------------+
|
|
32
|
+
* |
|
|
33
|
+
* +-----+-----+
|
|
34
|
+
* | |
|
|
35
|
+
* yes no
|
|
36
|
+
* | |
|
|
37
|
+
* v v
|
|
38
|
+
* PROACTIVE DIRECT
|
|
39
|
+
* CACHE CACHE
|
|
40
|
+
* | |
|
|
41
|
+
* v v
|
|
42
|
+
* waitUntil() cacheRoute()
|
|
43
|
+
* re-render immediately
|
|
44
|
+
* fresh |
|
|
45
|
+
* | |
|
|
46
|
+
* +-----------+
|
|
47
|
+
* |
|
|
48
|
+
* v
|
|
49
|
+
* next middleware
|
|
50
|
+
*
|
|
51
|
+
*
|
|
52
|
+
* CACHING STRATEGIES
|
|
53
|
+
* ==================
|
|
54
|
+
*
|
|
55
|
+
* 1. Direct Cache (all components present):
|
|
56
|
+
* - Immediate cacheRoute() call
|
|
57
|
+
* - All segments have valid components
|
|
58
|
+
* - Used for fresh full-page renders
|
|
59
|
+
*
|
|
60
|
+
* 2. Proactive Cache (null components present):
|
|
61
|
+
* - Background re-render via waitUntil()
|
|
62
|
+
* - Creates fresh context to avoid polluting response
|
|
63
|
+
* - Re-resolves ALL segments without revalidation
|
|
64
|
+
* - Ensures cache has complete components for future requests
|
|
65
|
+
*
|
|
66
|
+
*
|
|
67
|
+
* WHY PROACTIVE CACHING?
|
|
68
|
+
* ======================
|
|
69
|
+
*
|
|
70
|
+
* During partial navigation, some segments have null components:
|
|
71
|
+
*
|
|
72
|
+
* Request: /products/123 -> /products/456
|
|
73
|
+
* Segments: [ProductLayout(null), ProductPage(component)]
|
|
74
|
+
*
|
|
75
|
+
* The null means "client already has this, don't re-send."
|
|
76
|
+
* But if we cache these null components, future document requests
|
|
77
|
+
* would fail (no component to render).
|
|
78
|
+
*
|
|
79
|
+
* Solution: Background re-render all segments fresh, then cache.
|
|
80
|
+
* This ensures the cache always has complete, renderable segments.
|
|
81
|
+
*
|
|
82
|
+
*
|
|
83
|
+
* PROACTIVE CACHE FLOW
|
|
84
|
+
* ====================
|
|
85
|
+
*
|
|
86
|
+
* 1. Current request returns (fast, with nulls)
|
|
87
|
+
* 2. waitUntil() triggers background work
|
|
88
|
+
* 3. Create fresh handler context (silent, no stream pollution)
|
|
89
|
+
* 4. Re-resolve all entries without revalidation logic
|
|
90
|
+
* 5. Also resolve intercept segments if applicable
|
|
91
|
+
* 6. Store complete segments in cache
|
|
92
|
+
*
|
|
93
|
+
*
|
|
94
|
+
* SKIP CONDITIONS
|
|
95
|
+
* ===============
|
|
96
|
+
*
|
|
97
|
+
* Caching is skipped when:
|
|
98
|
+
* - Cache scope disabled (no caching configured)
|
|
99
|
+
* - This is an action request (mutations shouldn't cache)
|
|
100
|
+
* - Cache was already hit (no need to re-cache same data)
|
|
101
|
+
* - Non-GET request (only GET requests are cacheable)
|
|
102
|
+
*/
|
|
103
|
+
import type { ResolvedSegment } from "../../types.js";
|
|
104
|
+
import { getRequestContext } from "../../server/request-context.js";
|
|
105
|
+
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
106
|
+
import { getRouterContext } from "../router-context.js";
|
|
107
|
+
import { debugLog, debugWarn } from "../logging.js";
|
|
108
|
+
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates cache store middleware
|
|
112
|
+
*
|
|
113
|
+
* Observes all segments passing through and stores them in cache after pipeline completes.
|
|
114
|
+
* Handles proactive caching for null-component segments.
|
|
115
|
+
*/
|
|
116
|
+
export function withCacheStore<TEnv>(
|
|
117
|
+
ctx: MatchContext<TEnv>,
|
|
118
|
+
state: MatchPipelineState,
|
|
119
|
+
): GeneratorMiddleware<ResolvedSegment> {
|
|
120
|
+
return async function* (
|
|
121
|
+
source: AsyncGenerator<ResolvedSegment>,
|
|
122
|
+
): AsyncGenerator<ResolvedSegment> {
|
|
123
|
+
const pipelineStart = performance.now();
|
|
124
|
+
const ms = ctx.metricsStore;
|
|
125
|
+
|
|
126
|
+
// Collect all segments while passing them through
|
|
127
|
+
const allSegments: ResolvedSegment[] = [];
|
|
128
|
+
for await (const segment of source) {
|
|
129
|
+
allSegments.push(segment);
|
|
130
|
+
yield segment;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Skip caching if:
|
|
134
|
+
// 1. Cache miss but cache scope is disabled
|
|
135
|
+
// 2. This is an action (actions don't cache)
|
|
136
|
+
// 3. Cache was already hit (no need to re-cache)
|
|
137
|
+
// 4. Non-GET request (only cache GET requests)
|
|
138
|
+
if (
|
|
139
|
+
!ctx.cacheScope?.enabled ||
|
|
140
|
+
ctx.isAction ||
|
|
141
|
+
state.cacheHit ||
|
|
142
|
+
ctx.request.method !== "GET"
|
|
143
|
+
) {
|
|
144
|
+
if (ms) {
|
|
145
|
+
ms.metrics.push({
|
|
146
|
+
label: "pipeline:cache-store",
|
|
147
|
+
duration: performance.now() - pipelineStart,
|
|
148
|
+
startTime: pipelineStart - ms.requestStart,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const {
|
|
155
|
+
createHandlerContext,
|
|
156
|
+
setupLoaderAccess,
|
|
157
|
+
resolveAllSegments,
|
|
158
|
+
resolveInterceptEntry,
|
|
159
|
+
createHandleStore,
|
|
160
|
+
} = getRouterContext<TEnv>();
|
|
161
|
+
|
|
162
|
+
// Combine main segments with intercept segments
|
|
163
|
+
const allSegmentsToCache = [...allSegments, ...state.interceptSegments];
|
|
164
|
+
|
|
165
|
+
// Check if any non-loader segments have null components
|
|
166
|
+
// This happens when client already had those segments (partial navigation)
|
|
167
|
+
const hasNullComponents = allSegmentsToCache.some(
|
|
168
|
+
(s) => s.component === null && s.type !== "loader",
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
const requestCtx = getRequestContext();
|
|
172
|
+
if (!requestCtx) return;
|
|
173
|
+
|
|
174
|
+
const cacheScope = ctx.cacheScope;
|
|
175
|
+
|
|
176
|
+
// Register onResponse callback to skip caching for non-200 responses
|
|
177
|
+
// Note: error/notFound status codes are set elsewhere (not caching-specific)
|
|
178
|
+
requestCtx.onResponse((response) => {
|
|
179
|
+
// Only cache successful responses
|
|
180
|
+
if (response.status !== 200) {
|
|
181
|
+
debugLog("cacheStore", "skipping cache for non-200 response", {
|
|
182
|
+
status: response.status,
|
|
183
|
+
pathname: ctx.pathname,
|
|
184
|
+
});
|
|
185
|
+
return response;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (hasNullComponents) {
|
|
189
|
+
// Proactive caching: render all segments fresh in background
|
|
190
|
+
// This ensures cache has complete components for future requests
|
|
191
|
+
requestCtx.waitUntil(async () => {
|
|
192
|
+
debugLog("cacheStore", "proactive caching started", {
|
|
193
|
+
pathname: ctx.pathname,
|
|
194
|
+
});
|
|
195
|
+
// Swap to a fresh HandleStore so handle.push() calls from
|
|
196
|
+
// proactive resolution are captured (not silenced). The original
|
|
197
|
+
// store's stream is already sent by waitUntil time.
|
|
198
|
+
// cacheRoute reads from requestCtx._handleStore, so this ensures
|
|
199
|
+
// complete handle data (e.g. breadcrumbs) is cached.
|
|
200
|
+
const originalHandleStore = requestCtx._handleStore;
|
|
201
|
+
requestCtx._handleStore = createHandleStore();
|
|
202
|
+
try {
|
|
203
|
+
// Create fresh context for proactive caching
|
|
204
|
+
const proactiveHandlerContext = createHandlerContext(
|
|
205
|
+
ctx.matched.params,
|
|
206
|
+
ctx.request,
|
|
207
|
+
ctx.url.searchParams,
|
|
208
|
+
ctx.pathname,
|
|
209
|
+
ctx.url,
|
|
210
|
+
ctx.env,
|
|
211
|
+
ctx.routeMap,
|
|
212
|
+
ctx.matched.routeKey,
|
|
213
|
+
ctx.matched.responseType,
|
|
214
|
+
ctx.matched.pt === true,
|
|
215
|
+
);
|
|
216
|
+
const proactiveLoaderPromises = new Map<string, Promise<any>>();
|
|
217
|
+
|
|
218
|
+
// Use normal loader access so handle data is captured
|
|
219
|
+
setupLoaderAccess(proactiveHandlerContext, proactiveLoaderPromises);
|
|
220
|
+
|
|
221
|
+
// Re-resolve ALL segments without revalidation
|
|
222
|
+
const Store = ctx.Store;
|
|
223
|
+
const freshSegments = await Store.run(() =>
|
|
224
|
+
resolveAllSegments(
|
|
225
|
+
ctx.entries,
|
|
226
|
+
ctx.routeKey,
|
|
227
|
+
ctx.matched.params,
|
|
228
|
+
proactiveHandlerContext,
|
|
229
|
+
proactiveLoaderPromises,
|
|
230
|
+
),
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
// Also resolve intercept segments fresh if applicable
|
|
234
|
+
let freshInterceptSegments: ResolvedSegment[] = [];
|
|
235
|
+
if (ctx.interceptResult) {
|
|
236
|
+
freshInterceptSegments = await Store.run(() =>
|
|
237
|
+
resolveInterceptEntry(
|
|
238
|
+
ctx.interceptResult!.intercept,
|
|
239
|
+
ctx.interceptResult!.entry,
|
|
240
|
+
ctx.matched.params,
|
|
241
|
+
proactiveHandlerContext,
|
|
242
|
+
true, // belongsToRoute
|
|
243
|
+
// No revalidationContext = render fresh
|
|
244
|
+
),
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
const completeSegments = [
|
|
249
|
+
...freshSegments,
|
|
250
|
+
...freshInterceptSegments,
|
|
251
|
+
];
|
|
252
|
+
requestCtx._handleStore.seal();
|
|
253
|
+
await cacheScope.cacheRoute(
|
|
254
|
+
ctx.pathname,
|
|
255
|
+
ctx.matched.params,
|
|
256
|
+
completeSegments,
|
|
257
|
+
ctx.isIntercept,
|
|
258
|
+
);
|
|
259
|
+
debugLog("cacheStore", "proactive caching complete", {
|
|
260
|
+
pathname: ctx.pathname,
|
|
261
|
+
});
|
|
262
|
+
} catch (error) {
|
|
263
|
+
debugWarn("cacheStore", "proactive caching failed", {
|
|
264
|
+
pathname: ctx.pathname,
|
|
265
|
+
error: String(error),
|
|
266
|
+
});
|
|
267
|
+
} finally {
|
|
268
|
+
requestCtx._handleStore = originalHandleStore;
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
} else {
|
|
272
|
+
// All segments have components - cache directly
|
|
273
|
+
// Schedule caching in waitUntil since cacheRoute is now async (key resolution)
|
|
274
|
+
requestCtx.waitUntil(async () => {
|
|
275
|
+
await cacheScope.cacheRoute(
|
|
276
|
+
ctx.pathname,
|
|
277
|
+
ctx.matched.params,
|
|
278
|
+
allSegmentsToCache,
|
|
279
|
+
ctx.isIntercept,
|
|
280
|
+
);
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return response;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (ms) {
|
|
288
|
+
ms.metrics.push({
|
|
289
|
+
label: "pipeline:cache-store",
|
|
290
|
+
duration: performance.now() - pipelineStart,
|
|
291
|
+
startTime: pipelineStart - ms.requestStart,
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Match Middleware
|
|
3
|
+
*
|
|
4
|
+
* Async generator middleware for the match pipeline.
|
|
5
|
+
* Each middleware transforms or enriches the segment stream.
|
|
6
|
+
*
|
|
7
|
+
* MIDDLEWARE OVERVIEW
|
|
8
|
+
* ===================
|
|
9
|
+
*
|
|
10
|
+
* The pipeline consists of 5 middleware layers, each with a specific role:
|
|
11
|
+
*
|
|
12
|
+
* +-------------------------------------------------------------------------+
|
|
13
|
+
* | MIDDLEWARE PIPELINE |
|
|
14
|
+
* +-------------------------------------------------------------------------+
|
|
15
|
+
* | |
|
|
16
|
+
* | [1] CACHE LOOKUP (innermost) |
|
|
17
|
+
* | Purpose: Check cache before resolving |
|
|
18
|
+
* | On hit: Yield cached segments + fresh loaders |
|
|
19
|
+
* | On miss: Pass through to segment resolution |
|
|
20
|
+
* | Side effects: Sets state.cacheHit, state.shouldRevalidate |
|
|
21
|
+
* | |
|
|
22
|
+
* | [2] SEGMENT RESOLUTION |
|
|
23
|
+
* | Purpose: Resolve segments when cache misses |
|
|
24
|
+
* | Skips if: state.cacheHit === true |
|
|
25
|
+
* | Produces: All route segments (layouts, routes, loaders) |
|
|
26
|
+
* | Two modes: Full (simple) vs Partial (with revalidation) |
|
|
27
|
+
* | |
|
|
28
|
+
* | [3] INTERCEPT RESOLUTION |
|
|
29
|
+
* | Purpose: Resolve intercept segments (modal slots) |
|
|
30
|
+
* | Triggers: When ctx.interceptResult exists |
|
|
31
|
+
* | Produces: Additional segments for named slots |
|
|
32
|
+
* | Updates: state.slots[slotName] with intercept segments |
|
|
33
|
+
* | |
|
|
34
|
+
* | [4] CACHE STORE |
|
|
35
|
+
* | Purpose: Store segments in cache for future requests |
|
|
36
|
+
* | Skips if: Cache hit, actions, or cache disabled |
|
|
37
|
+
* | Strategy: Direct cache if all components present |
|
|
38
|
+
* | Proactive cache if null components (via waitUntil) |
|
|
39
|
+
* | |
|
|
40
|
+
* | [5] BACKGROUND REVALIDATION (outermost) |
|
|
41
|
+
* | Purpose: SWR - serve stale, revalidate in background |
|
|
42
|
+
* | Triggers: When state.shouldRevalidate === true |
|
|
43
|
+
* | Action: Async resolution via waitUntil(), updates cache |
|
|
44
|
+
* | |
|
|
45
|
+
* +-------------------------------------------------------------------------+
|
|
46
|
+
*
|
|
47
|
+
*
|
|
48
|
+
* MIDDLEWARE INTERACTION PATTERNS
|
|
49
|
+
* ===============================
|
|
50
|
+
*
|
|
51
|
+
* Pattern 1: Producer Middleware (cache-lookup, segment-resolution)
|
|
52
|
+
* - Yields segments into the stream
|
|
53
|
+
* - Creates new data for downstream middleware
|
|
54
|
+
*
|
|
55
|
+
* Pattern 2: Transformer Middleware (intercept-resolution)
|
|
56
|
+
* - Passes through existing segments
|
|
57
|
+
* - Adds additional segments (intercepts)
|
|
58
|
+
*
|
|
59
|
+
* Pattern 3: Observer Middleware (cache-store, background-revalidation)
|
|
60
|
+
* - Passes through all segments unchanged
|
|
61
|
+
* - Triggers side effects based on state
|
|
62
|
+
*
|
|
63
|
+
*
|
|
64
|
+
* STATE FLAGS
|
|
65
|
+
* ===========
|
|
66
|
+
*
|
|
67
|
+
* The middleware communicate through MatchPipelineState:
|
|
68
|
+
*
|
|
69
|
+
* state.cacheHit - Set by cache-lookup, read by others to skip work
|
|
70
|
+
* state.shouldRevalidate - Set by cache-lookup, triggers bg-revalidation
|
|
71
|
+
* state.segments - Accumulated segments from pipeline
|
|
72
|
+
* state.interceptSegments - Segments for intercept slots
|
|
73
|
+
* state.slots - Named slot data for client
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
export { withCacheLookup } from "./cache-lookup.js";
|
|
77
|
+
export { withSegmentResolution } from "./segment-resolution.js";
|
|
78
|
+
export { withInterceptResolution } from "./intercept-resolution.js";
|
|
79
|
+
export { withCacheStore } from "./cache-store.js";
|
|
80
|
+
export { withBackgroundRevalidation } from "./background-revalidation.js";
|
|
81
|
+
export type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Intercept Resolution Middleware
|
|
3
|
+
*
|
|
4
|
+
* Resolves intercept (modal slot) segments for soft navigation.
|
|
5
|
+
* Yields intercept segments after main route segments.
|
|
6
|
+
*
|
|
7
|
+
* FLOW DIAGRAM
|
|
8
|
+
* ============
|
|
9
|
+
*
|
|
10
|
+
* source (from segment-resolution)
|
|
11
|
+
* |
|
|
12
|
+
* v
|
|
13
|
+
* +---------------------------+
|
|
14
|
+
* | Collect + yield source | Pass through main segments
|
|
15
|
+
* | segments[] |
|
|
16
|
+
* +---------------------------+
|
|
17
|
+
* |
|
|
18
|
+
* v
|
|
19
|
+
* +---------------------+
|
|
20
|
+
* | isFullMatch? |──yes──> return (no intercepts on doc requests)
|
|
21
|
+
* +---------------------+
|
|
22
|
+
* | no
|
|
23
|
+
* v
|
|
24
|
+
* +---------------------+
|
|
25
|
+
* | Has interceptResult |──no───> return
|
|
26
|
+
* | AND not cached? |
|
|
27
|
+
* +---------------------+
|
|
28
|
+
* | yes
|
|
29
|
+
* v
|
|
30
|
+
* +----------------------+ +----------------------------+
|
|
31
|
+
* | Fresh intercept? |yes>| resolveInterceptEntry() |
|
|
32
|
+
* | (!cacheHit or | | - middleware, loaders, UI |
|
|
33
|
+
* | no intercept segs) | +----------------------------+
|
|
34
|
+
* +----------------------+ |
|
|
35
|
+
* | no v
|
|
36
|
+
* v yield intercept segments
|
|
37
|
+
* +----------------------------+ |
|
|
38
|
+
* | Cache hit with intercept | |
|
|
39
|
+
* | handleCacheHitIntercept() | |
|
|
40
|
+
* | - Extract from cache | |
|
|
41
|
+
* | - Re-resolve loaders only | |
|
|
42
|
+
* +----------------------------+ |
|
|
43
|
+
* | |
|
|
44
|
+
* +-------------------------------+
|
|
45
|
+
* |
|
|
46
|
+
* v
|
|
47
|
+
* +---------------------------+
|
|
48
|
+
* | Update state: |
|
|
49
|
+
* | - interceptSegments |
|
|
50
|
+
* | - slots[slotName] |
|
|
51
|
+
* +---------------------------+
|
|
52
|
+
* |
|
|
53
|
+
* v
|
|
54
|
+
* next middleware
|
|
55
|
+
*
|
|
56
|
+
*
|
|
57
|
+
* INTERCEPT SCENARIOS
|
|
58
|
+
* ===================
|
|
59
|
+
*
|
|
60
|
+
* 1. Fresh intercept (no cache):
|
|
61
|
+
* - Full resolution of intercept entry
|
|
62
|
+
* - Resolves middleware, loaders, and component
|
|
63
|
+
* - Yields all intercept segments
|
|
64
|
+
*
|
|
65
|
+
* 2. Cache hit with intercept:
|
|
66
|
+
* - Extracts intercept segments from cached data
|
|
67
|
+
* - Re-resolves ONLY loaders for fresh data
|
|
68
|
+
* - Keeps cached component/layout
|
|
69
|
+
*
|
|
70
|
+
* 3. No intercept:
|
|
71
|
+
* - Passes through unchanged
|
|
72
|
+
* - No intercept segments yielded
|
|
73
|
+
*
|
|
74
|
+
*
|
|
75
|
+
* WHAT ARE INTERCEPTS?
|
|
76
|
+
* ====================
|
|
77
|
+
*
|
|
78
|
+
* Intercepts enable "soft navigation" patterns like modals:
|
|
79
|
+
*
|
|
80
|
+
* 1. User clicks a link (e.g., /photos/123)
|
|
81
|
+
* 2. Instead of full navigation, content renders in a modal slot
|
|
82
|
+
* 3. Background page remains visible and interactive
|
|
83
|
+
* 4. Hard navigation (direct URL) shows full page
|
|
84
|
+
*
|
|
85
|
+
* Configuration:
|
|
86
|
+
* intercept("@modal", "photos", <PhotoModal />, () => [...])
|
|
87
|
+
*
|
|
88
|
+
* The intercept resolves to segments that render in the named slot
|
|
89
|
+
* instead of replacing the main content.
|
|
90
|
+
*
|
|
91
|
+
*
|
|
92
|
+
* SLOT STRUCTURE
|
|
93
|
+
* ==============
|
|
94
|
+
*
|
|
95
|
+
* state.slots[slotName] = {
|
|
96
|
+
* active: true,
|
|
97
|
+
* segments: [...intercept segments]
|
|
98
|
+
* }
|
|
99
|
+
*
|
|
100
|
+
* The client uses this to:
|
|
101
|
+
* 1. Keep current page segments
|
|
102
|
+
* 2. Render intercept segments in named <Outlet name="@modal" />
|
|
103
|
+
*/
|
|
104
|
+
import type { ResolvedSegment } from "../../types.js";
|
|
105
|
+
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
106
|
+
import { getRouterContext } from "../router-context.js";
|
|
107
|
+
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
108
|
+
import { debugLog } from "../logging.js";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Creates intercept resolution middleware
|
|
112
|
+
*
|
|
113
|
+
* If ctx.interceptResult exists and we're not in a cache-hit-with-intercept scenario:
|
|
114
|
+
* - Resolves intercept segments
|
|
115
|
+
* - Updates state.interceptSegments
|
|
116
|
+
* - Updates state.slots with the intercept slot
|
|
117
|
+
* - Yields intercept segments after main segments
|
|
118
|
+
*/
|
|
119
|
+
export function withInterceptResolution<TEnv>(
|
|
120
|
+
ctx: MatchContext<TEnv>,
|
|
121
|
+
state: MatchPipelineState,
|
|
122
|
+
): GeneratorMiddleware<ResolvedSegment> {
|
|
123
|
+
return async function* (
|
|
124
|
+
source: AsyncGenerator<ResolvedSegment>,
|
|
125
|
+
): AsyncGenerator<ResolvedSegment> {
|
|
126
|
+
const pipelineStart = performance.now();
|
|
127
|
+
const ms = ctx.metricsStore;
|
|
128
|
+
|
|
129
|
+
// First, yield all segments from the source (main segment resolution or cache)
|
|
130
|
+
const segments: ResolvedSegment[] = [];
|
|
131
|
+
for await (const segment of source) {
|
|
132
|
+
segments.push(segment);
|
|
133
|
+
yield segment;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Skip intercept resolution for full match (document requests don't have intercepts)
|
|
137
|
+
if (ctx.isFullMatch) {
|
|
138
|
+
if (ms) {
|
|
139
|
+
ms.metrics.push({
|
|
140
|
+
label: "pipeline:intercept",
|
|
141
|
+
duration: performance.now() - pipelineStart,
|
|
142
|
+
startTime: pipelineStart - ms.requestStart,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Skip intercept resolution if:
|
|
149
|
+
// 1. No intercept result
|
|
150
|
+
// 2. Already have intercept segments (from cache hit with intercept key)
|
|
151
|
+
// 3. Cache hit with intercept key
|
|
152
|
+
const skipInterceptResolution =
|
|
153
|
+
!ctx.interceptResult ||
|
|
154
|
+
state.interceptSegments.length > 0 ||
|
|
155
|
+
(state.cacheHit && ctx.isIntercept);
|
|
156
|
+
|
|
157
|
+
if (skipInterceptResolution) {
|
|
158
|
+
// For cache hit with intercept, extract intercept segments from cached data for slots
|
|
159
|
+
// and re-resolve loaders for fresh data
|
|
160
|
+
if (ctx.interceptResult && state.cacheHit && ctx.isIntercept) {
|
|
161
|
+
await handleCacheHitIntercept(ctx, state, segments);
|
|
162
|
+
}
|
|
163
|
+
if (ms) {
|
|
164
|
+
ms.metrics.push({
|
|
165
|
+
label: "pipeline:intercept",
|
|
166
|
+
duration: performance.now() - pipelineStart,
|
|
167
|
+
startTime: pipelineStart - ms.requestStart,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Resolve intercept segments
|
|
174
|
+
const { resolveInterceptEntry } = getRouterContext<TEnv>();
|
|
175
|
+
|
|
176
|
+
const slotName = ctx.interceptResult!.intercept.slotName;
|
|
177
|
+
debugLog("matchPartial.intercept", "intercept resolved", {
|
|
178
|
+
routeName: ctx.localRouteName,
|
|
179
|
+
slotName,
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
// Resolve intercept entry (middleware, loaders, handler)
|
|
183
|
+
const Store = ctx.Store;
|
|
184
|
+
const interceptSegments = await Store.run(() =>
|
|
185
|
+
resolveInterceptEntry(
|
|
186
|
+
ctx.interceptResult!.intercept,
|
|
187
|
+
ctx.interceptResult!.entry,
|
|
188
|
+
ctx.matched.params,
|
|
189
|
+
ctx.handlerContext,
|
|
190
|
+
true, // belongsToRoute
|
|
191
|
+
{
|
|
192
|
+
clientSegmentIds: ctx.clientSegmentSet,
|
|
193
|
+
prevParams: ctx.prevParams,
|
|
194
|
+
request: ctx.request,
|
|
195
|
+
prevUrl: ctx.prevUrl,
|
|
196
|
+
nextUrl: ctx.url,
|
|
197
|
+
routeKey: ctx.routeKey,
|
|
198
|
+
actionContext: ctx.actionContext,
|
|
199
|
+
stale: ctx.stale,
|
|
200
|
+
},
|
|
201
|
+
),
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
// Update state
|
|
205
|
+
state.interceptSegments = interceptSegments;
|
|
206
|
+
state.slots[slotName] = {
|
|
207
|
+
active: true,
|
|
208
|
+
segments: interceptSegments,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Yield intercept segments
|
|
212
|
+
for (const segment of interceptSegments) {
|
|
213
|
+
yield segment;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
if (ms) {
|
|
217
|
+
ms.metrics.push({
|
|
218
|
+
label: "pipeline:intercept",
|
|
219
|
+
duration: performance.now() - pipelineStart,
|
|
220
|
+
startTime: pipelineStart - ms.requestStart,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Handle cache hit with intercept scenario
|
|
228
|
+
*
|
|
229
|
+
* Extract intercept segments from cached data and re-resolve loaders for fresh data.
|
|
230
|
+
*/
|
|
231
|
+
async function handleCacheHitIntercept<TEnv>(
|
|
232
|
+
ctx: MatchContext<TEnv>,
|
|
233
|
+
state: MatchPipelineState,
|
|
234
|
+
segments: ResolvedSegment[],
|
|
235
|
+
): Promise<void> {
|
|
236
|
+
if (!ctx.interceptResult) return;
|
|
237
|
+
|
|
238
|
+
const { resolveInterceptLoadersOnly } = getRouterContext<TEnv>();
|
|
239
|
+
|
|
240
|
+
const slotName = ctx.interceptResult.intercept.slotName;
|
|
241
|
+
|
|
242
|
+
// Find intercept segments from cached segments (namespace starts with "intercept:")
|
|
243
|
+
const interceptSegments = segments.filter((s) =>
|
|
244
|
+
s.namespace?.startsWith("intercept:"),
|
|
245
|
+
);
|
|
246
|
+
state.interceptSegments = interceptSegments;
|
|
247
|
+
|
|
248
|
+
// Re-resolve intercept loaders for fresh data on cache hit
|
|
249
|
+
// This keeps cached component/layout but fetches fresh loader data
|
|
250
|
+
if (resolveInterceptLoadersOnly) {
|
|
251
|
+
const Store = ctx.Store;
|
|
252
|
+
const freshLoaderResult = await Store.run(() =>
|
|
253
|
+
resolveInterceptLoadersOnly(
|
|
254
|
+
ctx.interceptResult!.intercept,
|
|
255
|
+
ctx.interceptResult!.entry,
|
|
256
|
+
ctx.matched.params,
|
|
257
|
+
ctx.handlerContext,
|
|
258
|
+
true, // belongsToRoute
|
|
259
|
+
{
|
|
260
|
+
clientSegmentIds: ctx.clientSegmentSet,
|
|
261
|
+
prevParams: ctx.prevParams,
|
|
262
|
+
request: ctx.request,
|
|
263
|
+
prevUrl: ctx.prevUrl,
|
|
264
|
+
nextUrl: ctx.url,
|
|
265
|
+
routeKey: ctx.routeKey,
|
|
266
|
+
actionContext: ctx.actionContext,
|
|
267
|
+
stale: ctx.stale,
|
|
268
|
+
},
|
|
269
|
+
),
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
// Update intercept segment's loaderDataPromise with fresh data
|
|
273
|
+
if (freshLoaderResult) {
|
|
274
|
+
const interceptMainSegment = interceptSegments.find(
|
|
275
|
+
(s) => s.type === "parallel" && s.slot,
|
|
276
|
+
);
|
|
277
|
+
if (interceptMainSegment) {
|
|
278
|
+
interceptMainSegment.loaderDataPromise =
|
|
279
|
+
freshLoaderResult.loaderDataPromise;
|
|
280
|
+
interceptMainSegment.loaderIds = freshLoaderResult.loaderIds;
|
|
281
|
+
debugLog(
|
|
282
|
+
"matchPartial.intercept",
|
|
283
|
+
"cache hit with fresh intercept loaders",
|
|
284
|
+
{
|
|
285
|
+
routeName: ctx.localRouteName,
|
|
286
|
+
slotName,
|
|
287
|
+
},
|
|
288
|
+
);
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
debugLog(
|
|
292
|
+
"matchPartial.intercept",
|
|
293
|
+
"cache hit without intercept loader revalidation",
|
|
294
|
+
{
|
|
295
|
+
routeName: ctx.localRouteName,
|
|
296
|
+
slotName,
|
|
297
|
+
},
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
state.slots[slotName] = {
|
|
303
|
+
active: true,
|
|
304
|
+
segments: interceptSegments,
|
|
305
|
+
};
|
|
306
|
+
}
|