@rangojs/router 0.0.0-experimental.32 → 0.0.0-experimental.3232cd17
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 +4 -0
- package/README.md +198 -44
- package/dist/bin/rango.js +287 -105
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3248 -1117
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +73 -21
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +107 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +245 -21
- package/skills/caching/SKILL.md +302 -6
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +364 -0
- package/skills/hooks/SKILL.md +270 -30
- package/skills/host-router/SKILL.md +82 -22
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +49 -5
- package/skills/layout/SKILL.md +35 -9
- package/skills/links/SKILL.md +249 -17
- package/skills/loader/SKILL.md +294 -30
- package/skills/middleware/SKILL.md +52 -13
- package/skills/migrate-nextjs/SKILL.md +584 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +203 -7
- package/skills/prerender/SKILL.md +123 -100
- package/skills/rango/SKILL.md +250 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +97 -5
- package/skills/router-setup/SKILL.md +90 -5
- package/skills/server-actions/SKILL.md +775 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/testing/SKILL.md +129 -0
- package/skills/testing/bindings.md +89 -0
- package/skills/testing/cache-prerender.md +124 -0
- package/skills/testing/client-components.md +122 -0
- package/skills/testing/e2e-parity.md +125 -0
- package/skills/testing/flight.md +92 -0
- package/skills/testing/handles.md +129 -0
- package/skills/testing/loader.md +128 -0
- package/skills/testing/middleware.md +99 -0
- package/skills/testing/render-handler.md +121 -0
- package/skills/testing/response-routes.md +95 -0
- package/skills/testing/reverse-and-types.md +84 -0
- package/skills/testing/server-actions.md +107 -0
- package/skills/testing/server-tree.md +128 -0
- package/skills/testing/setup.md +120 -0
- package/skills/typesafety/SKILL.md +329 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/__internal.ts +67 -40
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/action-fence.ts +47 -0
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/cookie-name.ts +140 -0
- package/src/browser/event-controller.ts +86 -147
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/invalidate-client-cache.ts +52 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +148 -19
- package/src/browser/navigation-client.ts +187 -67
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +18 -66
- package/src/browser/partial-update.ts +123 -94
- package/src/browser/prefetch/cache.ts +214 -36
- package/src/browser/prefetch/fetch.ts +260 -38
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +126 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +93 -11
- package/src/browser/react/NavigationProvider.tsx +115 -34
- package/src/browser/react/ScrollRestoration.tsx +10 -6
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/filter-segment-order.ts +49 -7
- package/src/browser/react/index.ts +0 -48
- package/src/browser/react/location-state-shared.ts +166 -8
- package/src/browser/react/location-state.ts +39 -14
- package/src/browser/react/use-action.ts +6 -15
- package/src/browser/react/use-handle.ts +23 -69
- package/src/browser/react/use-link-status.ts +0 -4
- package/src/browser/react/use-navigation.ts +22 -5
- package/src/browser/react/use-params.ts +20 -10
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +46 -11
- package/src/browser/react/use-search-params.ts +0 -5
- package/src/browser/react/use-segments.ts +11 -21
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +215 -76
- package/src/browser/scroll-restoration.ts +46 -39
- package/src/browser/segment-reconciler.ts +36 -9
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -50
- package/src/browser/types.ts +95 -11
- package/src/browser/validate-redirect-origin.ts +43 -16
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +65 -40
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/index.ts +8 -2
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +137 -32
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +9 -2
- package/src/build/route-types/param-extraction.ts +6 -3
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +278 -96
- package/src/build/route-types/scan-filter.ts +9 -2
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-error.ts +104 -0
- package/src/cache/cache-policy.ts +68 -28
- package/src/cache/cache-runtime.ts +149 -43
- package/src/cache/cache-scope.ts +148 -81
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2550 -93
- package/src/cache/cf/index.ts +11 -17
- package/src/cache/document-cache.ts +78 -27
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +23 -20
- package/src/cache/memory-segment-store.ts +136 -37
- package/src/cache/profile-registry.ts +6 -30
- package/src/cache/read-through-swr.ts +41 -11
- package/src/cache/segment-codec.ts +0 -16
- package/src/cache/tag-invalidation.ts +230 -0
- package/src/cache/taint.ts +55 -0
- package/src/cache/types.ts +33 -100
- package/src/cache/vercel/index.ts +11 -0
- package/src/cache/vercel/vercel-cache-store.ts +799 -0
- package/src/client.rsc.tsx +6 -21
- package/src/client.tsx +108 -290
- package/src/component-utils.ts +19 -0
- package/src/context-var.ts +84 -2
- package/src/debug.ts +2 -2
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/ssr.ts +0 -1
- package/src/errors.ts +30 -4
- package/src/handle.ts +70 -22
- package/src/handles/MetaTags.tsx +0 -14
- package/src/handles/breadcrumbs.ts +16 -5
- package/src/handles/meta.ts +0 -39
- package/src/host/cookie-handler.ts +0 -36
- package/src/host/errors.ts +0 -24
- package/src/host/index.ts +8 -2
- package/src/host/pattern-matcher.ts +7 -50
- package/src/host/router.ts +107 -99
- package/src/host/testing.ts +40 -27
- package/src/host/types.ts +37 -4
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +137 -22
- package/src/index.rsc.ts +52 -26
- package/src/index.ts +100 -38
- package/src/internal-debug.ts +2 -4
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +20 -13
- package/src/loader.ts +12 -11
- package/src/missing-id-error.ts +68 -0
- package/src/network-error-thrower.tsx +1 -6
- package/src/outlet-context.ts +1 -1
- package/src/outlet-provider.tsx +1 -5
- package/src/prerender/param-hash.ts +10 -11
- package/src/prerender/store.ts +37 -41
- package/src/prerender.ts +198 -82
- package/src/redirect-origin.ts +100 -0
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -15
- package/src/root-error-boundary.tsx +1 -19
- package/src/route-content-wrapper.tsx +7 -72
- package/src/route-definition/dsl-helpers.ts +437 -274
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +113 -37
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +52 -10
- package/src/route-definition/resolve-handler-use.ts +161 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-map-builder.ts +7 -17
- package/src/route-types.ts +37 -41
- package/src/router/basename.ts +14 -0
- package/src/router/content-negotiation.ts +108 -9
- package/src/router/error-handling.ts +13 -17
- package/src/router/find-match.ts +45 -22
- package/src/router/handler-context.ts +83 -41
- package/src/router/intercept-resolution.ts +25 -23
- package/src/router/lazy-includes.ts +19 -53
- package/src/router/loader-resolution.ts +213 -30
- package/src/router/logging.ts +5 -8
- package/src/router/manifest.ts +49 -45
- package/src/router/match-api.ts +120 -204
- package/src/router/match-context.ts +0 -22
- package/src/router/match-handlers.ts +58 -58
- package/src/router/match-middleware/background-revalidation.ts +27 -6
- package/src/router/match-middleware/cache-lookup.ts +205 -249
- package/src/router/match-middleware/cache-store.ts +45 -32
- package/src/router/match-middleware/intercept-resolution.ts +8 -28
- package/src/router/match-middleware/segment-resolution.ts +52 -18
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +104 -40
- package/src/router/metrics.ts +5 -34
- package/src/router/middleware-types.ts +13 -142
- package/src/router/middleware.ts +173 -143
- package/src/router/navigation-snapshot.ts +131 -0
- package/src/router/params-util.ts +23 -0
- package/src/router/pattern-matching.ts +109 -63
- package/src/router/prerender-match.ts +190 -54
- package/src/router/preview-match.ts +32 -102
- package/src/router/request-classification.ts +276 -0
- package/src/router/revalidation.ts +63 -55
- package/src/router/route-snapshot.ts +244 -0
- package/src/router/router-context.ts +6 -28
- package/src/router/router-interfaces.ts +100 -35
- package/src/router/router-options.ts +91 -11
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +242 -75
- package/src/router/segment-resolution/helpers.ts +63 -24
- package/src/router/segment-resolution/loader-cache.ts +41 -37
- package/src/router/segment-resolution/revalidation.ts +456 -372
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/segment-resolution.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -3
- package/src/router/state-cookie-name.ts +33 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/telemetry-otel.ts +0 -20
- package/src/router/telemetry.ts +96 -19
- package/src/router/timeout.ts +0 -20
- package/src/router/trie-matching.ts +91 -46
- package/src/router/types.ts +10 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +134 -43
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -383
- package/src/rsc/helpers.ts +162 -46
- package/src/rsc/index.ts +1 -1
- package/src/rsc/json-route-result.ts +38 -0
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +30 -3
- package/src/rsc/redirect-guard.ts +99 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +90 -63
- package/src/rsc/rsc-rendering.ts +56 -54
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -67
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +25 -6
- package/src/runtime-env.ts +18 -0
- package/src/search-params.ts +4 -20
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +134 -0
- package/src/segment-system.tsx +272 -129
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +309 -61
- package/src/server/cookie-store.ts +80 -5
- package/src/server/handle-store.ts +26 -24
- package/src/server/loader-registry.ts +10 -28
- package/src/server/request-context.ts +338 -126
- package/src/ssr/index.tsx +23 -15
- package/src/static-handler.ts +27 -18
- package/src/testing/cache-status.ts +162 -0
- package/src/testing/collect-handle.ts +40 -0
- package/src/testing/dispatch.ts +618 -0
- package/src/testing/dom.entry.ts +22 -0
- package/src/testing/e2e/fixture.ts +188 -0
- package/src/testing/e2e/index.ts +128 -0
- package/src/testing/e2e/matchers.ts +35 -0
- package/src/testing/e2e/page-helpers.ts +272 -0
- package/src/testing/e2e/parity.ts +387 -0
- package/src/testing/e2e/server.ts +195 -0
- package/src/testing/flight-matchers.ts +97 -0
- package/src/testing/flight-normalize.ts +11 -0
- package/src/testing/flight-runtime.d.ts +57 -0
- package/src/testing/flight-tree.ts +682 -0
- package/src/testing/flight.entry.ts +52 -0
- package/src/testing/flight.ts +232 -0
- package/src/testing/generated-routes.ts +183 -0
- package/src/testing/index.ts +99 -0
- package/src/testing/internal/context.ts +348 -0
- package/src/testing/internal/flight-client-globals.ts +30 -0
- package/src/testing/internal/seed-vars.ts +54 -0
- package/src/testing/render-handler.ts +330 -0
- package/src/testing/render-route.tsx +566 -0
- package/src/testing/run-loader.ts +378 -0
- package/src/testing/run-middleware.ts +205 -0
- package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
- package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
- package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
- package/src/testing/vitest-stubs/version.ts +5 -0
- package/src/testing/vitest.ts +305 -0
- package/src/theme/ThemeProvider.tsx +0 -52
- package/src/theme/ThemeScript.tsx +0 -6
- package/src/theme/constants.ts +0 -12
- package/src/theme/index.ts +0 -7
- package/src/theme/theme-context.ts +1 -5
- package/src/theme/theme-script.ts +0 -14
- package/src/theme/use-theme.ts +0 -3
- package/src/types/boundaries.ts +0 -35
- package/src/types/cache-types.ts +17 -8
- package/src/types/error-types.ts +30 -90
- package/src/types/global-namespace.ts +54 -41
- package/src/types/handler-context.ts +233 -81
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +44 -15
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +19 -7
- package/src/types/segments.ts +37 -14
- package/src/urls/include-helper.ts +33 -70
- package/src/urls/index.ts +1 -11
- package/src/urls/path-helper-types.ts +58 -11
- package/src/urls/path-helper.ts +57 -111
- package/src/urls/pattern-types.ts +48 -19
- package/src/urls/response-types.ts +25 -22
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -18
- package/src/use-loader.tsx +346 -89
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +36 -38
- package/src/vite/discovery/discover-routers.ts +130 -85
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +192 -99
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +51 -6
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +187 -69
- package/src/vite/plugins/cjs-to-esm.ts +8 -18
- package/src/vite/plugins/client-ref-dedup.ts +16 -11
- package/src/vite/plugins/client-ref-hashing.ts +28 -15
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
- package/src/vite/plugins/expose-action-id.ts +49 -98
- package/src/vite/plugins/expose-id-utils.ts +11 -50
- package/src/vite/plugins/expose-ids/export-analysis.ts +76 -34
- package/src/vite/plugins/expose-ids/handler-transform.ts +10 -48
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -20
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -16
- package/src/vite/plugins/expose-internal-ids.ts +554 -317
- package/src/vite/plugins/performance-tracks.ts +89 -0
- package/src/vite/plugins/refresh-cmd.ts +89 -27
- package/src/vite/plugins/use-cache-transform.ts +73 -83
- package/src/vite/plugins/vercel-output.ts +258 -0
- package/src/vite/plugins/version-injector.ts +21 -25
- package/src/vite/plugins/version-plugin.ts +41 -20
- package/src/vite/plugins/virtual-entries.ts +2 -17
- package/src/vite/rango.ts +257 -289
- package/src/vite/router-discovery.ts +930 -140
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +4 -4
- package/src/vite/utils/bundle-analysis.ts +10 -15
- package/src/vite/utils/client-chunks.ts +184 -0
- package/src/vite/utils/forward-user-plugins.ts +171 -0
- package/src/vite/utils/manifest-utils.ts +4 -59
- package/src/vite/utils/package-resolution.ts +20 -52
- package/src/vite/utils/prerender-utils.ts +27 -29
- package/src/vite/utils/shared-utils.ts +92 -42
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/react/use-client-cache.ts +0 -58
- package/src/browser/shallow.ts +0 -40
- package/src/handles/index.ts +0 -7
- package/src/router/middleware-cookies.ts +0 -55
|
@@ -104,7 +104,8 @@ import type { ResolvedSegment } from "../../types.js";
|
|
|
104
104
|
import { getRequestContext } from "../../server/request-context.js";
|
|
105
105
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
106
106
|
import { getRouterContext } from "../router-context.js";
|
|
107
|
-
import { debugLog, debugWarn } from "../logging.js";
|
|
107
|
+
import { debugLog, debugWarn, getOrCreateRequestId } from "../logging.js";
|
|
108
|
+
import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
|
|
108
109
|
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
109
110
|
|
|
110
111
|
/**
|
|
@@ -120,21 +121,16 @@ export function withCacheStore<TEnv>(
|
|
|
120
121
|
return async function* (
|
|
121
122
|
source: AsyncGenerator<ResolvedSegment>,
|
|
122
123
|
): AsyncGenerator<ResolvedSegment> {
|
|
123
|
-
const pipelineStart = performance.now();
|
|
124
124
|
const ms = ctx.metricsStore;
|
|
125
125
|
|
|
126
|
-
// Collect all segments while passing them through
|
|
127
126
|
const allSegments: ResolvedSegment[] = [];
|
|
128
127
|
for await (const segment of source) {
|
|
129
128
|
allSegments.push(segment);
|
|
130
129
|
yield segment;
|
|
131
130
|
}
|
|
132
131
|
|
|
133
|
-
|
|
134
|
-
|
|
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)
|
|
132
|
+
const ownStart = performance.now();
|
|
133
|
+
|
|
138
134
|
if (
|
|
139
135
|
!ctx.cacheScope?.enabled ||
|
|
140
136
|
ctx.isAction ||
|
|
@@ -144,8 +140,8 @@ export function withCacheStore<TEnv>(
|
|
|
144
140
|
if (ms) {
|
|
145
141
|
ms.metrics.push({
|
|
146
142
|
label: "pipeline:cache-store",
|
|
147
|
-
duration: performance.now() -
|
|
148
|
-
startTime:
|
|
143
|
+
duration: performance.now() - ownStart,
|
|
144
|
+
startTime: ownStart - ms.requestStart,
|
|
149
145
|
});
|
|
150
146
|
}
|
|
151
147
|
return;
|
|
@@ -159,24 +155,24 @@ export function withCacheStore<TEnv>(
|
|
|
159
155
|
createHandleStore,
|
|
160
156
|
} = getRouterContext<TEnv>();
|
|
161
157
|
|
|
162
|
-
// Combine main segments with intercept segments
|
|
163
158
|
const allSegmentsToCache = [...allSegments, ...state.interceptSegments];
|
|
164
159
|
|
|
165
|
-
// Check if any non-loader segments have null components
|
|
166
|
-
// This happens when client already had those segments (partial navigation)
|
|
167
160
|
const hasNullComponents = allSegmentsToCache.some(
|
|
168
|
-
(s) =>
|
|
161
|
+
(s) =>
|
|
162
|
+
s.component === null &&
|
|
163
|
+
s.type !== "loader" &&
|
|
164
|
+
ctx.clientSegmentSet.has(s.id),
|
|
169
165
|
);
|
|
170
166
|
|
|
171
167
|
const requestCtx = getRequestContext();
|
|
172
168
|
if (!requestCtx) return;
|
|
173
169
|
|
|
174
170
|
const cacheScope = ctx.cacheScope;
|
|
171
|
+
const reqId = INTERNAL_RANGO_DEBUG
|
|
172
|
+
? getOrCreateRequestId(ctx.request)
|
|
173
|
+
: undefined;
|
|
175
174
|
|
|
176
|
-
// Register onResponse callback to skip caching for non-200 responses
|
|
177
|
-
// Note: error/notFound status codes are set elsewhere (not caching-specific)
|
|
178
175
|
requestCtx.onResponse((response) => {
|
|
179
|
-
// Only cache successful responses
|
|
180
176
|
if (response.status !== 200) {
|
|
181
177
|
debugLog("cacheStore", "skipping cache for non-200 response", {
|
|
182
178
|
status: response.status,
|
|
@@ -186,21 +182,17 @@ export function withCacheStore<TEnv>(
|
|
|
186
182
|
}
|
|
187
183
|
|
|
188
184
|
if (hasNullComponents) {
|
|
189
|
-
// Proactive caching: render all segments fresh in background
|
|
190
|
-
// This ensures cache has complete components for future requests
|
|
191
185
|
requestCtx.waitUntil(async () => {
|
|
186
|
+
const savedMetrics = ctx.Store.metrics;
|
|
187
|
+
ctx.Store.metrics = undefined;
|
|
188
|
+
|
|
189
|
+
const start = performance.now();
|
|
192
190
|
debugLog("cacheStore", "proactive caching started", {
|
|
193
191
|
pathname: ctx.pathname,
|
|
194
192
|
});
|
|
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
193
|
const originalHandleStore = requestCtx._handleStore;
|
|
201
194
|
requestCtx._handleStore = createHandleStore();
|
|
202
195
|
try {
|
|
203
|
-
// Create fresh context for proactive caching
|
|
204
196
|
const proactiveHandlerContext = createHandlerContext(
|
|
205
197
|
ctx.matched.params,
|
|
206
198
|
ctx.request,
|
|
@@ -215,10 +207,8 @@ export function withCacheStore<TEnv>(
|
|
|
215
207
|
);
|
|
216
208
|
const proactiveLoaderPromises = new Map<string, Promise<any>>();
|
|
217
209
|
|
|
218
|
-
// Use normal loader access so handle data is captured
|
|
219
210
|
setupLoaderAccess(proactiveHandlerContext, proactiveLoaderPromises);
|
|
220
211
|
|
|
221
|
-
// Re-resolve ALL segments without revalidation
|
|
222
212
|
const Store = ctx.Store;
|
|
223
213
|
const freshSegments = await Store.run(() =>
|
|
224
214
|
resolveAllSegments(
|
|
@@ -227,10 +217,10 @@ export function withCacheStore<TEnv>(
|
|
|
227
217
|
ctx.matched.params,
|
|
228
218
|
proactiveHandlerContext,
|
|
229
219
|
proactiveLoaderPromises,
|
|
220
|
+
{ skipLoaders: true },
|
|
230
221
|
),
|
|
231
222
|
);
|
|
232
223
|
|
|
233
|
-
// Also resolve intercept segments fresh if applicable
|
|
234
224
|
let freshInterceptSegments: ResolvedSegment[] = [];
|
|
235
225
|
if (ctx.interceptResult) {
|
|
236
226
|
freshInterceptSegments = await Store.run(() =>
|
|
@@ -256,28 +246,51 @@ export function withCacheStore<TEnv>(
|
|
|
256
246
|
completeSegments,
|
|
257
247
|
ctx.isIntercept,
|
|
258
248
|
);
|
|
249
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
250
|
+
const dur = performance.now() - start;
|
|
251
|
+
console.log(
|
|
252
|
+
`[RSC Background][req:${reqId}] Proactive cache ${ctx.pathname} (${dur.toFixed(2)}ms) segments=${completeSegments.length}`,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
259
255
|
debugLog("cacheStore", "proactive caching complete", {
|
|
260
256
|
pathname: ctx.pathname,
|
|
261
257
|
});
|
|
262
258
|
} catch (error) {
|
|
259
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
260
|
+
const dur = performance.now() - start;
|
|
261
|
+
console.log(
|
|
262
|
+
`[RSC Background][req:${reqId}] Proactive cache ${ctx.pathname} FAILED (${dur.toFixed(2)}ms) error=${String(error)}`,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
263
265
|
debugWarn("cacheStore", "proactive caching failed", {
|
|
264
266
|
pathname: ctx.pathname,
|
|
265
267
|
error: String(error),
|
|
266
268
|
});
|
|
267
269
|
} finally {
|
|
268
270
|
requestCtx._handleStore = originalHandleStore;
|
|
271
|
+
ctx.Store.metrics = savedMetrics;
|
|
269
272
|
}
|
|
270
273
|
});
|
|
271
274
|
} else {
|
|
272
|
-
|
|
273
|
-
|
|
275
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
276
|
+
console.log(
|
|
277
|
+
`[RSC CacheStore][req:${reqId}] Direct cache path: scheduling cacheRoute for ${ctx.pathname} (${allSegmentsToCache.length} segments, hasNullComponents=${hasNullComponents})`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
274
280
|
requestCtx.waitUntil(async () => {
|
|
281
|
+
const start = performance.now();
|
|
275
282
|
await cacheScope.cacheRoute(
|
|
276
283
|
ctx.pathname,
|
|
277
284
|
ctx.matched.params,
|
|
278
285
|
allSegmentsToCache,
|
|
279
286
|
ctx.isIntercept,
|
|
280
287
|
);
|
|
288
|
+
if (INTERNAL_RANGO_DEBUG) {
|
|
289
|
+
const dur = performance.now() - start;
|
|
290
|
+
console.log(
|
|
291
|
+
`[RSC Background][req:${reqId}] Cache store ${ctx.pathname} (${dur.toFixed(2)}ms) segments=${allSegmentsToCache.length}`,
|
|
292
|
+
);
|
|
293
|
+
}
|
|
281
294
|
});
|
|
282
295
|
}
|
|
283
296
|
|
|
@@ -287,8 +300,8 @@ export function withCacheStore<TEnv>(
|
|
|
287
300
|
if (ms) {
|
|
288
301
|
ms.metrics.push({
|
|
289
302
|
label: "pipeline:cache-store",
|
|
290
|
-
duration: performance.now() -
|
|
291
|
-
startTime:
|
|
303
|
+
duration: performance.now() - ownStart,
|
|
304
|
+
startTime: ownStart - ms.requestStart,
|
|
292
305
|
});
|
|
293
306
|
}
|
|
294
307
|
};
|
|
@@ -123,54 +123,46 @@ export function withInterceptResolution<TEnv>(
|
|
|
123
123
|
return async function* (
|
|
124
124
|
source: AsyncGenerator<ResolvedSegment>,
|
|
125
125
|
): AsyncGenerator<ResolvedSegment> {
|
|
126
|
-
const pipelineStart = performance.now();
|
|
127
126
|
const ms = ctx.metricsStore;
|
|
128
127
|
|
|
129
|
-
// First, yield all segments from the source (main segment resolution or cache)
|
|
130
128
|
const segments: ResolvedSegment[] = [];
|
|
131
129
|
for await (const segment of source) {
|
|
132
130
|
segments.push(segment);
|
|
133
131
|
yield segment;
|
|
134
132
|
}
|
|
135
133
|
|
|
136
|
-
|
|
134
|
+
const ownStart = performance.now();
|
|
135
|
+
|
|
137
136
|
if (ctx.isFullMatch) {
|
|
138
137
|
if (ms) {
|
|
139
138
|
ms.metrics.push({
|
|
140
139
|
label: "pipeline:intercept",
|
|
141
|
-
duration: performance.now() -
|
|
142
|
-
startTime:
|
|
140
|
+
duration: performance.now() - ownStart,
|
|
141
|
+
startTime: ownStart - ms.requestStart,
|
|
143
142
|
});
|
|
144
143
|
}
|
|
145
144
|
return;
|
|
146
145
|
}
|
|
147
146
|
|
|
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
147
|
const skipInterceptResolution =
|
|
153
148
|
!ctx.interceptResult ||
|
|
154
149
|
state.interceptSegments.length > 0 ||
|
|
155
150
|
(state.cacheHit && ctx.isIntercept);
|
|
156
151
|
|
|
157
152
|
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
153
|
if (ctx.interceptResult && state.cacheHit && ctx.isIntercept) {
|
|
161
154
|
await handleCacheHitIntercept(ctx, state, segments);
|
|
162
155
|
}
|
|
163
156
|
if (ms) {
|
|
164
157
|
ms.metrics.push({
|
|
165
158
|
label: "pipeline:intercept",
|
|
166
|
-
duration: performance.now() -
|
|
167
|
-
startTime:
|
|
159
|
+
duration: performance.now() - ownStart,
|
|
160
|
+
startTime: ownStart - ms.requestStart,
|
|
168
161
|
});
|
|
169
162
|
}
|
|
170
163
|
return;
|
|
171
164
|
}
|
|
172
165
|
|
|
173
|
-
// Resolve intercept segments
|
|
174
166
|
const { resolveInterceptEntry } = getRouterContext<TEnv>();
|
|
175
167
|
|
|
176
168
|
const slotName = ctx.interceptResult!.intercept.slotName;
|
|
@@ -179,7 +171,6 @@ export function withInterceptResolution<TEnv>(
|
|
|
179
171
|
slotName,
|
|
180
172
|
});
|
|
181
173
|
|
|
182
|
-
// Resolve intercept entry (middleware, loaders, handler)
|
|
183
174
|
const Store = ctx.Store;
|
|
184
175
|
const interceptSegments = await Store.run(() =>
|
|
185
176
|
resolveInterceptEntry(
|
|
@@ -201,14 +192,12 @@ export function withInterceptResolution<TEnv>(
|
|
|
201
192
|
),
|
|
202
193
|
);
|
|
203
194
|
|
|
204
|
-
// Update state
|
|
205
195
|
state.interceptSegments = interceptSegments;
|
|
206
196
|
state.slots[slotName] = {
|
|
207
197
|
active: true,
|
|
208
198
|
segments: interceptSegments,
|
|
209
199
|
};
|
|
210
200
|
|
|
211
|
-
// Yield intercept segments
|
|
212
201
|
for (const segment of interceptSegments) {
|
|
213
202
|
yield segment;
|
|
214
203
|
}
|
|
@@ -216,18 +205,13 @@ export function withInterceptResolution<TEnv>(
|
|
|
216
205
|
if (ms) {
|
|
217
206
|
ms.metrics.push({
|
|
218
207
|
label: "pipeline:intercept",
|
|
219
|
-
duration: performance.now() -
|
|
220
|
-
startTime:
|
|
208
|
+
duration: performance.now() - ownStart,
|
|
209
|
+
startTime: ownStart - ms.requestStart,
|
|
221
210
|
});
|
|
222
211
|
}
|
|
223
212
|
};
|
|
224
213
|
}
|
|
225
214
|
|
|
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
215
|
async function handleCacheHitIntercept<TEnv>(
|
|
232
216
|
ctx: MatchContext<TEnv>,
|
|
233
217
|
state: MatchPipelineState,
|
|
@@ -239,14 +223,11 @@ async function handleCacheHitIntercept<TEnv>(
|
|
|
239
223
|
|
|
240
224
|
const slotName = ctx.interceptResult.intercept.slotName;
|
|
241
225
|
|
|
242
|
-
// Find intercept segments from cached segments (namespace starts with "intercept:")
|
|
243
226
|
const interceptSegments = segments.filter((s) =>
|
|
244
227
|
s.namespace?.startsWith("intercept:"),
|
|
245
228
|
);
|
|
246
229
|
state.interceptSegments = interceptSegments;
|
|
247
230
|
|
|
248
|
-
// Re-resolve intercept loaders for fresh data on cache hit
|
|
249
|
-
// This keeps cached component/layout but fetches fresh loader data
|
|
250
231
|
if (resolveInterceptLoadersOnly) {
|
|
251
232
|
const Store = ctx.Store;
|
|
252
233
|
const freshLoaderResult = await Store.run(() =>
|
|
@@ -269,7 +250,6 @@ async function handleCacheHitIntercept<TEnv>(
|
|
|
269
250
|
),
|
|
270
251
|
);
|
|
271
252
|
|
|
272
|
-
// Update intercept segment's loaderDataPromise with fresh data
|
|
273
253
|
if (freshLoaderResult) {
|
|
274
254
|
const interceptMainSegment = interceptSegments.find(
|
|
275
255
|
(s) => s.type === "parallel" && s.slot,
|
|
@@ -87,16 +87,43 @@
|
|
|
87
87
|
* if (state.cacheHit) return; // Now we can check
|
|
88
88
|
*/
|
|
89
89
|
import type { ResolvedSegment } from "../../types.js";
|
|
90
|
+
import type { EntryData } from "../../server/context.js";
|
|
91
|
+
import { _getRequestContext } from "../../server/request-context.js";
|
|
90
92
|
import type { MatchContext, MatchPipelineState } from "../match-context.js";
|
|
91
93
|
import { getRouterContext } from "../router-context.js";
|
|
92
94
|
import type { GeneratorMiddleware } from "./cache-lookup.js";
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
export function treeHasStreaming(entries: EntryData[]): boolean {
|
|
97
|
+
for (const entry of entries) {
|
|
98
|
+
if (
|
|
99
|
+
"loading" in entry &&
|
|
100
|
+
entry.loading !== undefined &&
|
|
101
|
+
entry.loading !== false
|
|
102
|
+
)
|
|
103
|
+
return true;
|
|
104
|
+
if (entry.layout) {
|
|
105
|
+
if (treeHasStreaming(entry.layout)) return true;
|
|
106
|
+
}
|
|
107
|
+
if (entry.parallel) {
|
|
108
|
+
for (const key in entry.parallel) {
|
|
109
|
+
const parallelEntry = entry.parallel[key as `@${string}`];
|
|
110
|
+
if (parallelEntry) {
|
|
111
|
+
if (
|
|
112
|
+
"loading" in parallelEntry &&
|
|
113
|
+
parallelEntry.loading !== undefined &&
|
|
114
|
+
parallelEntry.loading !== false
|
|
115
|
+
)
|
|
116
|
+
return true;
|
|
117
|
+
if (parallelEntry.layout) {
|
|
118
|
+
if (treeHasStreaming(parallelEntry.layout)) return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
|
|
100
127
|
export function withSegmentResolution<TEnv>(
|
|
101
128
|
ctx: MatchContext<TEnv>,
|
|
102
129
|
state: MatchPipelineState,
|
|
@@ -104,34 +131,36 @@ export function withSegmentResolution<TEnv>(
|
|
|
104
131
|
return async function* (
|
|
105
132
|
source: AsyncGenerator<ResolvedSegment>,
|
|
106
133
|
): AsyncGenerator<ResolvedSegment> {
|
|
107
|
-
const pipelineStart = performance.now();
|
|
108
134
|
const ms = ctx.metricsStore;
|
|
109
135
|
|
|
110
|
-
// IMPORTANT: Always iterate source first to give cache-lookup a chance
|
|
111
|
-
// to run and set state.cacheHit. Without this, cache-lookup never executes!
|
|
112
136
|
for await (const segment of source) {
|
|
113
137
|
yield segment;
|
|
114
138
|
}
|
|
115
139
|
|
|
116
|
-
|
|
140
|
+
const ownStart = performance.now();
|
|
141
|
+
|
|
117
142
|
if (state.cacheHit) {
|
|
118
143
|
if (ms) {
|
|
119
144
|
ms.metrics.push({
|
|
120
145
|
label: "pipeline:segment-resolve",
|
|
121
|
-
duration: performance.now() -
|
|
122
|
-
startTime:
|
|
146
|
+
duration: performance.now() - ownStart,
|
|
147
|
+
startTime: ownStart - ms.requestStart,
|
|
123
148
|
});
|
|
124
149
|
}
|
|
125
150
|
return;
|
|
126
151
|
}
|
|
127
152
|
|
|
153
|
+
const reqCtx = _getRequestContext();
|
|
154
|
+
if (reqCtx && reqCtx._treeHasStreaming === undefined) {
|
|
155
|
+
reqCtx._treeHasStreaming = treeHasStreaming(ctx.entries);
|
|
156
|
+
}
|
|
157
|
+
|
|
128
158
|
const { resolveAllSegmentsWithRevalidation, resolveAllSegments } =
|
|
129
159
|
getRouterContext<TEnv>();
|
|
130
160
|
|
|
131
161
|
const Store = ctx.Store;
|
|
132
162
|
|
|
133
163
|
if (ctx.isFullMatch) {
|
|
134
|
-
// Full match (document request) - simple resolution without revalidation
|
|
135
164
|
const segments = await Store.run(() =>
|
|
136
165
|
resolveAllSegments(
|
|
137
166
|
ctx.entries,
|
|
@@ -142,11 +171,12 @@ export function withSegmentResolution<TEnv>(
|
|
|
142
171
|
),
|
|
143
172
|
);
|
|
144
173
|
|
|
145
|
-
// Update state with resolved segments
|
|
146
174
|
state.segments = segments;
|
|
147
175
|
state.matchedIds = segments.map((s: { id: string }) => s.id);
|
|
148
176
|
|
|
149
|
-
|
|
177
|
+
if (reqCtx) {
|
|
178
|
+
reqCtx._resolveRenderBarrier(segments);
|
|
179
|
+
}
|
|
150
180
|
for (const segment of segments) {
|
|
151
181
|
yield segment;
|
|
152
182
|
}
|
|
@@ -163,11 +193,11 @@ export function withSegmentResolution<TEnv>(
|
|
|
163
193
|
ctx.request,
|
|
164
194
|
ctx.prevUrl,
|
|
165
195
|
ctx.url,
|
|
166
|
-
ctx.loaderPromises,
|
|
167
196
|
ctx.actionContext,
|
|
168
197
|
ctx.interceptResult,
|
|
169
198
|
ctx.localRouteName,
|
|
170
199
|
ctx.pathname,
|
|
200
|
+
ctx.stale,
|
|
171
201
|
),
|
|
172
202
|
);
|
|
173
203
|
|
|
@@ -175,6 +205,10 @@ export function withSegmentResolution<TEnv>(
|
|
|
175
205
|
state.segments = result.segments;
|
|
176
206
|
state.matchedIds = result.matchedIds;
|
|
177
207
|
|
|
208
|
+
if (reqCtx) {
|
|
209
|
+
reqCtx._resolveRenderBarrier(result.segments);
|
|
210
|
+
}
|
|
211
|
+
|
|
178
212
|
// Yield all resolved segments
|
|
179
213
|
for (const segment of result.segments) {
|
|
180
214
|
yield segment;
|
|
@@ -184,8 +218,8 @@ export function withSegmentResolution<TEnv>(
|
|
|
184
218
|
if (ms) {
|
|
185
219
|
ms.metrics.push({
|
|
186
220
|
label: "pipeline:segment-resolve",
|
|
187
|
-
duration: performance.now() -
|
|
188
|
-
startTime:
|
|
221
|
+
duration: performance.now() - ownStart,
|
|
222
|
+
startTime: ownStart - ms.requestStart,
|
|
189
223
|
});
|
|
190
224
|
}
|
|
191
225
|
};
|
|
@@ -106,16 +106,6 @@ import {
|
|
|
106
106
|
withSegmentResolution,
|
|
107
107
|
} from "./match-middleware/index.js";
|
|
108
108
|
|
|
109
|
-
/**
|
|
110
|
-
* Compose multiple async generator middleware into a single middleware
|
|
111
|
-
*
|
|
112
|
-
* Middleware are applied in reverse order (rightmost runs first, innermost).
|
|
113
|
-
* For the pipeline:
|
|
114
|
-
* compose(A, B, C)(source)
|
|
115
|
-
*
|
|
116
|
-
* The flow is: source -> C -> B -> A -> output
|
|
117
|
-
* Where C is the innermost (runs first on input) and A is outermost (runs last).
|
|
118
|
-
*/
|
|
119
109
|
export function compose<T>(
|
|
120
110
|
...middleware: GeneratorMiddleware<T>[]
|
|
121
111
|
): GeneratorMiddleware<T> {
|
|
@@ -126,54 +116,23 @@ export function compose<T>(
|
|
|
126
116
|
return middleware[0];
|
|
127
117
|
}
|
|
128
118
|
return (source) => {
|
|
129
|
-
// Apply middleware in reverse order (rightmost first)
|
|
130
119
|
return middleware.reduceRight((prev, fn) => fn(prev), source);
|
|
131
120
|
};
|
|
132
121
|
}
|
|
133
122
|
|
|
134
|
-
|
|
135
|
-
* Create an empty async generator (source for pipeline)
|
|
136
|
-
*/
|
|
137
|
-
export async function* empty<T>(): AsyncGenerator<T> {
|
|
138
|
-
// Yields nothing - used as the initial source for the pipeline
|
|
139
|
-
}
|
|
123
|
+
export async function* empty<T>(): AsyncGenerator<T> {}
|
|
140
124
|
|
|
141
|
-
/**
|
|
142
|
-
* Create the match partial pipeline
|
|
143
|
-
*
|
|
144
|
-
* Pipeline order (innermost to outermost):
|
|
145
|
-
* 1. cache-lookup - Check cache first, yield cached segments if hit
|
|
146
|
-
* 2. segment-resolution - Resolve segments if cache miss
|
|
147
|
-
* 3. intercept-resolution - Resolve intercept segments
|
|
148
|
-
* 4. cache-store - Store segments in cache
|
|
149
|
-
* 5. background-revalidation - Trigger SWR if cache was stale
|
|
150
|
-
*
|
|
151
|
-
* Data flow:
|
|
152
|
-
* - empty() produces no segments
|
|
153
|
-
* - cache-lookup either yields cached segments OR passes through to segment-resolution
|
|
154
|
-
* - segment-resolution resolves fresh segments on cache miss
|
|
155
|
-
* - intercept-resolution adds intercept segments
|
|
156
|
-
* - cache-store observes and caches segments
|
|
157
|
-
* - background-revalidation triggers SWR revalidation if needed
|
|
158
|
-
*/
|
|
159
125
|
export function createMatchPartialPipeline<TEnv>(
|
|
160
126
|
ctx: MatchContext<TEnv>,
|
|
161
127
|
state: MatchPipelineState,
|
|
162
128
|
): AsyncGenerator<ResolvedSegment> {
|
|
163
|
-
// Build the middleware chain
|
|
164
129
|
const pipeline = compose<ResolvedSegment>(
|
|
165
|
-
// Outermost - observes segments and triggers background revalidation
|
|
166
130
|
withBackgroundRevalidation(ctx, state),
|
|
167
|
-
// Observes and stores segments in cache
|
|
168
131
|
withCacheStore(ctx, state),
|
|
169
|
-
// Adds intercept segments after main segments
|
|
170
132
|
withInterceptResolution(ctx, state),
|
|
171
|
-
// Resolves segments on cache miss
|
|
172
133
|
withSegmentResolution(ctx, state),
|
|
173
|
-
// Innermost - checks cache first
|
|
174
134
|
withCacheLookup(ctx, state),
|
|
175
135
|
);
|
|
176
136
|
|
|
177
|
-
// Start with empty source - cache lookup or segment resolution will produce segments
|
|
178
137
|
return pipeline(empty());
|
|
179
138
|
}
|