@rangojs/router 0.0.0-experimental.a769fbe7 → 0.0.0-experimental.ac99d918
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 +211 -43
- package/dist/bin/rango.js +279 -102
- package/dist/testing/vitest.js +82 -0
- package/dist/vite/index.js +3313 -1160
- package/dist/vite/index.js.bak +5448 -0
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +62 -11
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +63 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +222 -30
- package/skills/caching/SKILL.md +263 -8
- 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 +250 -30
- package/skills/host-router/SKILL.md +124 -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 +223 -9
- 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 +77 -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 +66 -5
- package/skills/router-setup/SKILL.md +38 -3
- 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 +319 -27
- package/skills/use-cache/SKILL.md +36 -5
- package/skills/vercel/SKILL.md +107 -0
- 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 +1 -66
- 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 +81 -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/navigation-bridge.ts +66 -14
- package/src/browser/navigation-client.ts +172 -109
- package/src/browser/navigation-store-handle.ts +38 -0
- package/src/browser/navigation-store.ts +76 -67
- package/src/browser/navigation-transaction.ts +9 -59
- package/src/browser/partial-update.ts +79 -93
- package/src/browser/prefetch/cache.ts +180 -62
- package/src/browser/prefetch/fetch.ts +252 -39
- package/src/browser/prefetch/queue.ts +42 -8
- package/src/browser/rango-state.ts +158 -76
- package/src/browser/react/Link.tsx +72 -10
- package/src/browser/react/NavigationProvider.tsx +83 -31
- 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 +111 -24
- package/src/browser/scroll-restoration.ts +29 -19
- package/src/browser/segment-reconciler.ts +36 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +176 -62
- package/src/browser/types.ts +60 -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 +6 -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 +333 -94
- 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 +134 -32
- package/src/cache/cache-scope.ts +100 -74
- package/src/cache/cache-tag.ts +98 -0
- package/src/cache/cf/cf-cache-store.ts +2256 -241
- package/src/cache/cf/index.ts +6 -16
- package/src/cache/document-cache.ts +61 -20
- package/src/cache/handle-snapshot.ts +63 -0
- package/src/cache/index.ts +22 -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/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 +17 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/defer.ts +196 -0
- package/src/deps/browser.ts +0 -1
- 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 +69 -10
- package/src/index.ts +112 -14
- 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 +413 -275
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +107 -32
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +50 -8
- 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 +0 -16
- 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 +44 -23
- package/src/router/handler-context.ts +46 -30
- package/src/router/intercept-resolution.ts +23 -23
- package/src/router/lazy-includes.ts +15 -52
- package/src/router/loader-resolution.ts +207 -30
- package/src/router/logging.ts +0 -6
- package/src/router/manifest.ts +40 -42
- 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 +0 -7
- package/src/router/match-middleware/cache-lookup.ts +161 -262
- package/src/router/match-middleware/cache-store.ts +3 -33
- package/src/router/match-middleware/intercept-resolution.ts +0 -22
- package/src/router/match-middleware/segment-resolution.ts +45 -14
- package/src/router/match-pipelines.ts +1 -42
- package/src/router/match-result.ts +87 -39
- package/src/router/metrics.ts +0 -34
- package/src/router/middleware-types.ts +7 -140
- package/src/router/middleware.ts +169 -140
- 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 +0 -27
- 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 +119 -65
- package/src/router/segment-resolution/helpers.ts +34 -0
- package/src/router/segment-resolution/loader-cache.ts +40 -37
- package/src/router/segment-resolution/revalidation.ts +329 -305
- 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 +0 -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 +9 -63
- package/src/router/url-params.ts +44 -0
- package/src/router.ts +128 -42
- package/src/rsc/handler-context.ts +3 -2
- package/src/rsc/handler.ts +492 -409
- 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 +18 -3
- package/src/rsc/manifest-init.ts +33 -42
- package/src/rsc/origin-guard.ts +39 -25
- package/src/rsc/progressive-enhancement.ts +28 -4
- 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 +53 -56
- package/src/rsc/runtime-warnings.ts +23 -10
- package/src/rsc/server-action.ts +74 -69
- package/src/rsc/ssr-setup.ts +18 -2
- package/src/rsc/types.ts +22 -9
- 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 +208 -201
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +211 -52
- 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 +289 -124
- package/src/ssr/index.tsx +22 -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 +124 -70
- package/src/types/index.ts +1 -10
- package/src/types/loader-types.ts +40 -11
- package/src/types/request-scope.ts +107 -0
- package/src/types/route-config.ts +6 -50
- package/src/types/route-entry.ts +12 -7
- package/src/types/segments.ts +36 -15
- 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 -4
- package/src/vite/discovery/virtual-module-codegen.ts +14 -34
- package/src/vite/index.ts +8 -0
- package/src/vite/plugin-types.ts +236 -6
- 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 +48 -95
- 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 +65 -207
- 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 +32 -23
- package/src/vite/plugins/virtual-entries.ts +46 -17
- package/src/vite/rango.ts +207 -125
- package/src/vite/router-discovery.ts +931 -133
- package/src/vite/utils/ast-handler-extract.ts +15 -31
- package/src/vite/utils/banner.ts +1 -1
- 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 +97 -44
- package/src/browser/action-response-classifier.ts +0 -99
- package/src/browser/debug-channel.ts +0 -93
- 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
package/src/cache/cache-scope.ts
CHANGED
|
@@ -16,14 +16,31 @@ import {
|
|
|
16
16
|
getRequestContext,
|
|
17
17
|
_getRequestContext,
|
|
18
18
|
} from "../server/request-context.js";
|
|
19
|
+
import { recordRequestTags } from "./cache-tag.js";
|
|
20
|
+
import { reportCacheError } from "./cache-error.js";
|
|
19
21
|
import { serializeSegments, deserializeSegments } from "./segment-codec.js";
|
|
20
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
captureHandles,
|
|
24
|
+
restoreHandles,
|
|
25
|
+
encodeHandles,
|
|
26
|
+
decodeHandles,
|
|
27
|
+
} from "./handle-snapshot.js";
|
|
21
28
|
import { sortedSearchString, sortedRouteParams } from "./cache-key-utils.js";
|
|
22
29
|
import {
|
|
23
30
|
DEFAULT_ROUTE_TTL,
|
|
24
31
|
resolveCacheKey,
|
|
25
32
|
resolveCacheStore,
|
|
33
|
+
resolveTagsOption,
|
|
26
34
|
} from "./cache-policy.js";
|
|
35
|
+
import type { RequestContext } from "../server/request-context.js";
|
|
36
|
+
|
|
37
|
+
export function resolveCacheTags(
|
|
38
|
+
config: PartialCacheOptions | false,
|
|
39
|
+
ctx: RequestContext | undefined,
|
|
40
|
+
): string[] | undefined {
|
|
41
|
+
if (config === false) return undefined;
|
|
42
|
+
return resolveTagsOption(config.tags, ctx, "CacheScope");
|
|
43
|
+
}
|
|
27
44
|
|
|
28
45
|
function debugCacheLog(message: string): void {
|
|
29
46
|
if (INTERNAL_RANGO_DEBUG) {
|
|
@@ -31,17 +48,6 @@ function debugCacheLog(message: string): void {
|
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Key Generation (internal)
|
|
36
|
-
// ============================================================================
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Generate cache key base from host, pathname, route params, and search params.
|
|
40
|
-
* Host is included to prevent cross-host cache collisions on shared stores.
|
|
41
|
-
* Route params and search params are sorted alphabetically for deterministic keys.
|
|
42
|
-
* Internal _rsc* and __* query params are excluded.
|
|
43
|
-
* @internal
|
|
44
|
-
*/
|
|
45
51
|
function getCacheKeyBase(
|
|
46
52
|
host: string,
|
|
47
53
|
pathname: string,
|
|
@@ -57,16 +63,6 @@ function getCacheKeyBase(
|
|
|
57
63
|
return key;
|
|
58
64
|
}
|
|
59
65
|
|
|
60
|
-
/**
|
|
61
|
-
* Generate default cache key for a route request.
|
|
62
|
-
* Includes pathname, route params, and user-facing search params for
|
|
63
|
-
* correct scoping. Internal _rsc* params are excluded.
|
|
64
|
-
* Includes request type prefix since they produce different segment sets:
|
|
65
|
-
* - doc: document requests (full page load)
|
|
66
|
-
* - partial: navigation requests (client-side navigation)
|
|
67
|
-
* - intercept: intercept navigation (modal/overlay routes)
|
|
68
|
-
* @internal
|
|
69
|
-
*/
|
|
70
66
|
function getDefaultRouteCacheKey(
|
|
71
67
|
pathname: string,
|
|
72
68
|
params?: Record<string, string>,
|
|
@@ -187,6 +183,32 @@ export class CacheScope {
|
|
|
187
183
|
return resolveCacheKey(keyFn, this.getStore(), defaultKey, "CacheScope");
|
|
188
184
|
}
|
|
189
185
|
|
|
186
|
+
/**
|
|
187
|
+
* Evaluate the cache `condition` predicate. Returns false (skip the cache
|
|
188
|
+
* operation) when the predicate returns false or throws; returns true when
|
|
189
|
+
* there is no condition or no request context to evaluate it against.
|
|
190
|
+
*/
|
|
191
|
+
private conditionAllows(op: "read" | "write"): boolean {
|
|
192
|
+
if (this.config === false || !this.config.condition) return true;
|
|
193
|
+
const requestCtx = getRequestContext();
|
|
194
|
+
if (!requestCtx) return true;
|
|
195
|
+
try {
|
|
196
|
+
if (!this.config.condition(requestCtx)) {
|
|
197
|
+
debugCacheLog(
|
|
198
|
+
`[CacheScope] condition returned false, skipping cache ${op}`,
|
|
199
|
+
);
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
} catch (error) {
|
|
204
|
+
console.error(
|
|
205
|
+
`[CacheScope] condition function threw, skipping cache ${op}:`,
|
|
206
|
+
error,
|
|
207
|
+
);
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
190
212
|
/**
|
|
191
213
|
* Lookup cached segments for a route (single cache entry per request).
|
|
192
214
|
* Returns { segments, shouldRevalidate } or null if cache miss.
|
|
@@ -204,27 +226,7 @@ export class CacheScope {
|
|
|
204
226
|
shouldRevalidate: boolean;
|
|
205
227
|
} | null> {
|
|
206
228
|
if (!this.enabled) return null;
|
|
207
|
-
|
|
208
|
-
// Evaluate condition — skip cache read when condition returns false
|
|
209
|
-
if (this.config !== false && this.config.condition) {
|
|
210
|
-
const requestCtx = getRequestContext();
|
|
211
|
-
if (requestCtx) {
|
|
212
|
-
try {
|
|
213
|
-
if (!this.config.condition(requestCtx)) {
|
|
214
|
-
debugCacheLog(
|
|
215
|
-
`[CacheScope] condition returned false, skipping cache read`,
|
|
216
|
-
);
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
} catch (error) {
|
|
220
|
-
console.error(
|
|
221
|
-
`[CacheScope] condition function threw, skipping cache read:`,
|
|
222
|
-
error,
|
|
223
|
-
);
|
|
224
|
-
return null;
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
}
|
|
229
|
+
if (!this.conditionAllows("read")) return null;
|
|
228
230
|
|
|
229
231
|
const store = this.getStore();
|
|
230
232
|
if (!store) return null;
|
|
@@ -242,13 +244,43 @@ export class CacheScope {
|
|
|
242
244
|
|
|
243
245
|
const { data: cached, shouldRevalidate } = result;
|
|
244
246
|
|
|
245
|
-
// Deserialize segments
|
|
246
|
-
|
|
247
|
+
// Deserialize segments. A failure means the cached segments are corrupt/
|
|
248
|
+
// partial: evict the entry (self-heal - the re-render re-caches under the
|
|
249
|
+
// same key) and report it as corruption, distinct from a transient infra
|
|
250
|
+
// error (handled by the outer catch).
|
|
251
|
+
let segments: ResolvedSegment[];
|
|
252
|
+
try {
|
|
253
|
+
segments = await deserializeSegments(cached.segments);
|
|
254
|
+
} catch (error) {
|
|
255
|
+
reportCacheError(
|
|
256
|
+
error,
|
|
257
|
+
"cache-corrupt",
|
|
258
|
+
`[CacheScope] ${key}: corrupt cached segments, evicting`,
|
|
259
|
+
);
|
|
260
|
+
await store
|
|
261
|
+
.delete(key)
|
|
262
|
+
.catch((e) =>
|
|
263
|
+
reportCacheError(e, "cache-delete", `[CacheScope] ${key}: evict`),
|
|
264
|
+
);
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
247
267
|
|
|
248
|
-
//
|
|
268
|
+
// A hit serves content that was tagged at write time, so the document
|
|
269
|
+
// tag union must include this entry's tags for updateTag()/revalidateTag()
|
|
270
|
+
// to invalidate any full-page entry built on top of it. The write path
|
|
271
|
+
// records via cacheRoute (resolveCacheTags); the hit path records here.
|
|
272
|
+
recordRequestTags(cached.tags);
|
|
273
|
+
|
|
274
|
+
// Replay handle data. An empty string means the route pushed no handles —
|
|
275
|
+
// skip the decode entirely (the common case). Otherwise decode the
|
|
276
|
+
// Flight-encoded blob; a decode failure skips handle restore but keeps the
|
|
277
|
+
// valid cached segments.
|
|
249
278
|
const handleStore = _getRequestContext()?._handleStore;
|
|
250
|
-
if (handleStore) {
|
|
251
|
-
|
|
279
|
+
if (handleStore && cached.handles) {
|
|
280
|
+
const handlesRecord = await decodeHandles(cached.handles);
|
|
281
|
+
if (handlesRecord) {
|
|
282
|
+
restoreHandles(handlesRecord, handleStore);
|
|
283
|
+
}
|
|
252
284
|
}
|
|
253
285
|
|
|
254
286
|
if (INTERNAL_RANGO_DEBUG) {
|
|
@@ -262,7 +294,7 @@ export class CacheScope {
|
|
|
262
294
|
|
|
263
295
|
return { segments, shouldRevalidate };
|
|
264
296
|
} catch (error) {
|
|
265
|
-
|
|
297
|
+
reportCacheError(error, "cache-read", `[CacheScope] lookup ${key}`);
|
|
266
298
|
return null;
|
|
267
299
|
}
|
|
268
300
|
}
|
|
@@ -284,27 +316,7 @@ export class CacheScope {
|
|
|
284
316
|
isIntercept?: boolean,
|
|
285
317
|
): Promise<void> {
|
|
286
318
|
if (!this.enabled || segments.length === 0) return;
|
|
287
|
-
|
|
288
|
-
// Evaluate condition — skip cache write when condition returns false
|
|
289
|
-
if (this.config !== false && this.config.condition) {
|
|
290
|
-
const conditionCtx = getRequestContext();
|
|
291
|
-
if (conditionCtx) {
|
|
292
|
-
try {
|
|
293
|
-
if (!this.config.condition(conditionCtx)) {
|
|
294
|
-
debugCacheLog(
|
|
295
|
-
`[CacheScope] condition returned false, skipping cache write`,
|
|
296
|
-
);
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
} catch (error) {
|
|
300
|
-
console.error(
|
|
301
|
-
`[CacheScope] condition function threw, skipping cache write:`,
|
|
302
|
-
error,
|
|
303
|
-
);
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
319
|
+
if (!this.conditionAllows("write")) return;
|
|
308
320
|
|
|
309
321
|
const store = this.getStore();
|
|
310
322
|
if (!store) return;
|
|
@@ -325,6 +337,10 @@ export class CacheScope {
|
|
|
325
337
|
// Resolve cache key early (while request context is available)
|
|
326
338
|
const key = await this.resolveKey(pathname, params, isIntercept);
|
|
327
339
|
|
|
340
|
+
// Resolve tags early (while request context is available, before waitUntil)
|
|
341
|
+
const tags = resolveCacheTags(this.config, requestCtx);
|
|
342
|
+
recordRequestTags(tags, requestCtx);
|
|
343
|
+
|
|
328
344
|
// Check if this is a partial request (navigation) vs document request
|
|
329
345
|
const isPartial = requestCtx.originalUrl.searchParams.has("_rsc_partial");
|
|
330
346
|
|
|
@@ -381,13 +397,19 @@ export class CacheScope {
|
|
|
381
397
|
);
|
|
382
398
|
}
|
|
383
399
|
|
|
384
|
-
// Serialize
|
|
385
|
-
|
|
400
|
+
// Serialize segments and Flight-encode handles in parallel. Handles go
|
|
401
|
+
// through the codec (not raw into the entry) so Promise/ReactNode handle
|
|
402
|
+
// values survive a JSON-serializing store — see encodeHandles.
|
|
403
|
+
const [serializedSegments, encodedHandles] = await Promise.all([
|
|
404
|
+
serializeSegments(nonLoaderSegments),
|
|
405
|
+
encodeHandles(handles),
|
|
406
|
+
]);
|
|
386
407
|
|
|
387
408
|
const data: CachedEntryData = {
|
|
388
409
|
segments: serializedSegments,
|
|
389
|
-
handles,
|
|
410
|
+
handles: encodedHandles,
|
|
390
411
|
expiresAt: Date.now() + ttl * 1000,
|
|
412
|
+
tags,
|
|
391
413
|
};
|
|
392
414
|
|
|
393
415
|
if (INTERNAL_RANGO_DEBUG) {
|
|
@@ -405,7 +427,11 @@ export class CacheScope {
|
|
|
405
427
|
);
|
|
406
428
|
}
|
|
407
429
|
} catch (error) {
|
|
408
|
-
|
|
430
|
+
reportCacheError(
|
|
431
|
+
error,
|
|
432
|
+
"cache-write",
|
|
433
|
+
`[CacheScope] Failed to cache ${key}`,
|
|
434
|
+
);
|
|
409
435
|
}
|
|
410
436
|
});
|
|
411
437
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Tag API
|
|
3
|
+
*
|
|
4
|
+
* Provides cacheTag() for tagging cached entries at runtime inside "use cache"
|
|
5
|
+
* functions. Tags are scoped via AsyncLocalStorage; calling cacheTag() outside
|
|
6
|
+
* a "use cache" execution throws.
|
|
7
|
+
*
|
|
8
|
+
* The runtime (cache-runtime.ts) wraps "use cache" execution in
|
|
9
|
+
* runWithCacheTagScope(), collects the runtime tags, and merges them with the
|
|
10
|
+
* profile/DSL tags before storing.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
14
|
+
import {
|
|
15
|
+
_getRequestContext,
|
|
16
|
+
type RequestContext,
|
|
17
|
+
} from "../server/request-context.js";
|
|
18
|
+
|
|
19
|
+
const cacheTagStorage = new AsyncLocalStorage<Set<string>>();
|
|
20
|
+
|
|
21
|
+
export function normalizeTag(tag: string): string | null {
|
|
22
|
+
if (!tag || !tag.trim()) return null;
|
|
23
|
+
return tag;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function normalizeTags(tags: Iterable<string>): string[] {
|
|
27
|
+
const out: string[] = [];
|
|
28
|
+
for (const tag of tags) {
|
|
29
|
+
const normalized = normalizeTag(tag);
|
|
30
|
+
if (normalized !== null) out.push(normalized);
|
|
31
|
+
}
|
|
32
|
+
return out;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Tag the current "use cache" entry for later invalidation via
|
|
37
|
+
* updateTag() / revalidateTag().
|
|
38
|
+
*
|
|
39
|
+
* Must be called inside a function marked with "use cache".
|
|
40
|
+
* Tags are additive - multiple calls accumulate.
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* async function getProduct(ctx) {
|
|
45
|
+
* "use cache";
|
|
46
|
+
* cacheTag(`product:${ctx.params.id}`, "products");
|
|
47
|
+
* return db.getProduct(ctx.params.id);
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
export function cacheTag(...tags: string[]): void {
|
|
52
|
+
const store = cacheTagStorage.getStore();
|
|
53
|
+
if (!store) {
|
|
54
|
+
throw new Error('cacheTag() must be called inside a "use cache" function.');
|
|
55
|
+
}
|
|
56
|
+
for (const tag of tags) {
|
|
57
|
+
const normalized = normalizeTag(tag);
|
|
58
|
+
if (normalized === null) {
|
|
59
|
+
if (process.env.NODE_ENV !== "production") {
|
|
60
|
+
console.warn(`[cacheTag] Ignoring empty or whitespace-only tag.`);
|
|
61
|
+
}
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
store.add(normalized);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function recordRequestTags(
|
|
69
|
+
tags: Iterable<string> | undefined,
|
|
70
|
+
ctx: RequestContext | undefined = _getRequestContext(),
|
|
71
|
+
): void {
|
|
72
|
+
if (!tags) return;
|
|
73
|
+
const set = ctx?._requestTags;
|
|
74
|
+
if (!set) return;
|
|
75
|
+
for (const tag of tags) {
|
|
76
|
+
const normalized = normalizeTag(tag);
|
|
77
|
+
if (normalized !== null) set.add(normalized);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Run a function within a cache tag scope. Any cacheTag() calls inside `fn`
|
|
83
|
+
* accumulate into the returned Set.
|
|
84
|
+
*
|
|
85
|
+
* The returned Set is the LIVE reference - the caller must await `result`
|
|
86
|
+
* before reading `tags`, because an async cached function may call cacheTag()
|
|
87
|
+
* after an await boundary.
|
|
88
|
+
*
|
|
89
|
+
* @internal Used by cache-runtime.ts to wrap "use cache" execution.
|
|
90
|
+
*/
|
|
91
|
+
export function runWithCacheTagScope<T>(fn: () => T): {
|
|
92
|
+
result: T;
|
|
93
|
+
tags: Set<string>;
|
|
94
|
+
} {
|
|
95
|
+
const tagSet = new Set<string>();
|
|
96
|
+
const result = cacheTagStorage.run(tagSet, fn);
|
|
97
|
+
return { result, tags: tagSet };
|
|
98
|
+
}
|