@rangojs/router 0.16.0 → 0.18.0
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 +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +132 -19
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +75 -57
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +26 -6
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +6 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
|
@@ -7,7 +7,16 @@
|
|
|
7
7
|
import type { ReactNode } from "react";
|
|
8
8
|
import type { EntryData } from "../server/context";
|
|
9
9
|
import { observePhase, PHASES } from "./instrument.js";
|
|
10
|
-
import { contextGet } from "../context-var.js";
|
|
10
|
+
import { contextGet, isNonCacheable } from "../context-var.js";
|
|
11
|
+
import { NOCACHE_SYMBOL } from "../cache/taint.js";
|
|
12
|
+
import {
|
|
13
|
+
linkLoaderTags,
|
|
14
|
+
linkRecordedTags,
|
|
15
|
+
linkValueTags,
|
|
16
|
+
loaderTagSetsArmed,
|
|
17
|
+
readValueTags,
|
|
18
|
+
tagLoaderValue,
|
|
19
|
+
} from "../cache/cache-tag.js";
|
|
11
20
|
import type {
|
|
12
21
|
ResolvedSegment,
|
|
13
22
|
HandlerContext,
|
|
@@ -43,6 +52,7 @@ import {
|
|
|
43
52
|
import { getFetchableLoader } from "../server/fetchable-loader-store.js";
|
|
44
53
|
import { _getRequestContext } from "../server/request-context.js";
|
|
45
54
|
import {
|
|
55
|
+
assertNonCacheableReadAllowed,
|
|
46
56
|
isInsideLoaderScope,
|
|
47
57
|
runInsideLoaderBodyScope,
|
|
48
58
|
isInsidePushCallbackScope,
|
|
@@ -386,6 +396,12 @@ function createLoaderExecutor<TEnv>(
|
|
|
386
396
|
* payload's handle-stream lifetime.
|
|
387
397
|
*/
|
|
388
398
|
silentHandles?: boolean;
|
|
399
|
+
/**
|
|
400
|
+
* loaderId -> the cache() tags of that loader's binding in this request
|
|
401
|
+
* (setupLoaderAccess _bindLoaderCacheTags): an execution of it outside
|
|
402
|
+
* the binding answers for them too (#964).
|
|
403
|
+
*/
|
|
404
|
+
bindingTags?: ReadonlyMap<string, Set<string>>;
|
|
389
405
|
},
|
|
390
406
|
): (
|
|
391
407
|
loader: LoaderDefinition<any, any>,
|
|
@@ -394,6 +410,7 @@ function createLoaderExecutor<TEnv>(
|
|
|
394
410
|
// Capture RequestContext eagerly for cookie access (ALS protection on Cloudflare)
|
|
395
411
|
const reqCtxRef = _getRequestContext();
|
|
396
412
|
const silentHandles = executorOptions?.silentHandles === true;
|
|
413
|
+
const internal = ctx as InternalHandlerContext<any, TEnv>;
|
|
397
414
|
|
|
398
415
|
// Dependency graph: loaderId -> set of loader IDs it directly depends on.
|
|
399
416
|
const dependsOn = new Map<string, Set<string>>();
|
|
@@ -429,9 +446,30 @@ function createLoaderExecutor<TEnv>(
|
|
|
429
446
|
deps.add(loader.$$id);
|
|
430
447
|
}
|
|
431
448
|
|
|
432
|
-
//
|
|
433
|
-
|
|
434
|
-
|
|
449
|
+
// Every loader execution funnels here (handler and loader-to-loader
|
|
450
|
+
// reads, uncached DSL bindings, cache misses): memoized per request, so a
|
|
451
|
+
// loader runs at most once however many reads, with no store cache. A
|
|
452
|
+
// loader's cache() read-through lives in loader-cache.ts
|
|
453
|
+
// (executeLoaderData), whose ctx.use override serves a later handler read
|
|
454
|
+
// the binding's result.
|
|
455
|
+
//
|
|
456
|
+
// A read is a consumption: the value bakes into the reader's output, so
|
|
457
|
+
// the loader's tags follow the reader onto a cache() record (#957) and
|
|
458
|
+
// into the reader's own loader-cache entry (#964, readValueTags; armed
|
|
459
|
+
// only where a loader cache() can read them).
|
|
460
|
+
const reqCtx = reqCtxRef ?? _getRequestContext();
|
|
461
|
+
linkLoaderTags(loader.$$id, reqCtx);
|
|
462
|
+
const recordTags = loaderTagSetsArmed(reqCtx);
|
|
463
|
+
// A loader body reads a cache()-bound loader through its binding too, so
|
|
464
|
+
// every reader in the request gets the one value a HIT served. The
|
|
465
|
+
// binding's own MISS (callerLoaderId null) runs the loader below.
|
|
466
|
+
const bound =
|
|
467
|
+
callerLoaderId !== null &&
|
|
468
|
+
internal._loaderCacheOverrides?.get(loader.$$id);
|
|
469
|
+
const memo = bound || loaderPromises.get(loader.$$id);
|
|
470
|
+
if (memo) {
|
|
471
|
+
if (recordTags) readValueTags(memo);
|
|
472
|
+
return memo;
|
|
435
473
|
}
|
|
436
474
|
|
|
437
475
|
// Get loader function - either from loader object or fetchable registry
|
|
@@ -452,7 +490,7 @@ function createLoaderExecutor<TEnv>(
|
|
|
452
490
|
pendingLoaders.add(loader.$$id);
|
|
453
491
|
|
|
454
492
|
const currentLoaderId = loader.$$id;
|
|
455
|
-
const variables =
|
|
493
|
+
const variables = internal._variables;
|
|
456
494
|
|
|
457
495
|
// Capture whether this loader is being started from a DSL loader scope
|
|
458
496
|
// (runInsideLoaderScope in fresh.ts). Handler-invoked loaders are NOT
|
|
@@ -466,18 +504,26 @@ function createLoaderExecutor<TEnv>(
|
|
|
466
504
|
// stale. Attributing to the owning route/layout segment gives handle
|
|
467
505
|
// pushes the same granularity handler pushes have (segmentOrder-correct
|
|
468
506
|
// for breadcrumb accumulation).
|
|
469
|
-
const owningSegmentId =
|
|
470
|
-
._currentSegmentId;
|
|
507
|
+
const owningSegmentId = internal._currentSegmentId;
|
|
471
508
|
|
|
472
509
|
let renderedResolved = false;
|
|
473
510
|
let renderedPromise: Promise<void> | null = null;
|
|
474
511
|
|
|
475
|
-
//
|
|
476
|
-
//
|
|
477
|
-
//
|
|
478
|
-
//
|
|
479
|
-
// (
|
|
480
|
-
|
|
512
|
+
// Request-scoped like the handler ctx it derives from (issue #940): the
|
|
513
|
+
// NOCACHE_SYMBOL brand below makes a "use cache" function taking it key by
|
|
514
|
+
// the route fields (_routeName, _responseType) and replay its handle
|
|
515
|
+
// pushes into the owning segment, claimed per loader like a handler ctx
|
|
516
|
+
// (_claimLoaderPushes, _runLoaderIsolated). Non-cacheable ctx.get reads
|
|
517
|
+
// run the shared guard; the body itself stays exempt (loader body scope).
|
|
518
|
+
const loaderCtx: LoaderContext<Record<string, string | undefined>, TEnv> &
|
|
519
|
+
Pick<
|
|
520
|
+
InternalHandlerContext<any, TEnv>,
|
|
521
|
+
| "_routeName"
|
|
522
|
+
| "_responseType"
|
|
523
|
+
| "_currentSegmentId"
|
|
524
|
+
| "_claimLoaderPushes"
|
|
525
|
+
| "_runLoaderIsolated"
|
|
526
|
+
> = {
|
|
481
527
|
params: ctx.params,
|
|
482
528
|
routeParams: (ctx.params ?? {}) as Record<string, string>,
|
|
483
529
|
request: ctx.request,
|
|
@@ -517,6 +563,9 @@ function createLoaderExecutor<TEnv>(
|
|
|
517
563
|
buildHandleSnapshot(reqCtx._handleStore, segmentOrder);
|
|
518
564
|
return collectHandleData(keyOrVar, snapshot, segmentOrder);
|
|
519
565
|
}
|
|
566
|
+
if (isNonCacheable(variables, keyOrVar)) {
|
|
567
|
+
assertNonCacheableReadAllowed(keyOrVar, _getRequestContext());
|
|
568
|
+
}
|
|
520
569
|
return contextGet(variables, keyOrVar);
|
|
521
570
|
}) as typeof ctx.get,
|
|
522
571
|
use: ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
|
|
@@ -552,6 +601,11 @@ function createLoaderExecutor<TEnv>(
|
|
|
552
601
|
method: "GET",
|
|
553
602
|
body: undefined,
|
|
554
603
|
reverse: ctx.reverse as LoaderContext["reverse"],
|
|
604
|
+
_routeName: internal._routeName,
|
|
605
|
+
_responseType: internal._responseType,
|
|
606
|
+
_currentSegmentId: owningSegmentId,
|
|
607
|
+
_claimLoaderPushes: internal._claimLoaderPushes,
|
|
608
|
+
_runLoaderIsolated: internal._runLoaderIsolated,
|
|
555
609
|
rendered: (): Promise<void> => {
|
|
556
610
|
// Guard: only DSL loaders may use rendered()
|
|
557
611
|
if (!isDslLoader) {
|
|
@@ -639,6 +693,7 @@ function createLoaderExecutor<TEnv>(
|
|
|
639
693
|
return renderedPromise;
|
|
640
694
|
},
|
|
641
695
|
};
|
|
696
|
+
(loaderCtx as any)[NOCACHE_SYMBOL] = true;
|
|
642
697
|
|
|
643
698
|
// Meter this loader once via observePhase (loader:<id> perf metric +
|
|
644
699
|
// rango.loader span); loaderFn runs inside the span callback so its KV/D1/
|
|
@@ -655,42 +710,29 @@ function createLoaderExecutor<TEnv>(
|
|
|
655
710
|
// does not grant rendered() to handler-invoked loaders. Uses a body-only
|
|
656
711
|
// scope, so isInsideLoaderScope() / barrier / deadlock gating is unchanged.
|
|
657
712
|
//
|
|
658
|
-
//
|
|
659
|
-
//
|
|
660
|
-
//
|
|
661
|
-
//
|
|
662
|
-
// cache-purity guards give it. DSL segment loaders keep their lane
|
|
663
|
-
// machinery (live = masked at capture, bake = guarded). A DSL loader's
|
|
664
|
-
// nested deps inherit isDslLoader=false only when the CHAIN started in a
|
|
665
|
-
// handler; a chain started by the segment funnel stays DSL (the loader
|
|
666
|
-
// scope ALS survives the body's awaits).
|
|
667
|
-
// Shell fast path eligibility: a HANDLER-invoked loader executing during a
|
|
668
|
-
// capture is handler-layer dynamism — on a handler-free (replayed) HIT it
|
|
669
|
-
// would never re-run, freezing its consumption-lane value (#672's "fresh
|
|
670
|
-
// per serve" slot shape). Mark the capture; the entry declines the fast
|
|
671
|
-
// path and keeps the full tail. DSL loaders re-run on every HIT and never
|
|
672
|
-
// set this.
|
|
673
|
-
if (!isDslLoader) {
|
|
674
|
-
const captureCtx = _getRequestContext();
|
|
675
|
-
if (
|
|
676
|
-
captureCtx?._shellCaptureRun &&
|
|
677
|
-
captureCtx._shellCaptureHandleLiveness
|
|
678
|
-
) {
|
|
679
|
-
captureCtx._shellCaptureHandleLiveness.handlerInvokedLoader = true;
|
|
680
|
-
}
|
|
681
|
-
}
|
|
713
|
+
// The CONSUMPTION-LANE RULE: a handler-consumed loader's value is a
|
|
714
|
+
// BAKED copy in every shared artifact (cache(), "use cache", the PPR
|
|
715
|
+
// shell). The cache-purity guards permit its identity reads; the PPR
|
|
716
|
+
// capture guard does not (server/context.ts assertNotInsideShellCapture).
|
|
682
717
|
|
|
718
|
+
const recordedTags = recordTags ? new Set<string>() : undefined;
|
|
683
719
|
const promise = observePhase(PHASES.loader(loader.$$id), () =>
|
|
684
720
|
Promise.resolve(
|
|
685
721
|
runInsideLoaderBodyScope(
|
|
686
722
|
() => loaderFn(loaderCtx as LoaderContext<any, TEnv>),
|
|
687
723
|
loader.$$id,
|
|
688
|
-
|
|
724
|
+
recordedTags,
|
|
689
725
|
),
|
|
690
726
|
).finally(() => {
|
|
691
727
|
pendingLoaders.delete(loader.$$id);
|
|
692
728
|
}),
|
|
693
729
|
);
|
|
730
|
+
if (recordedTags) {
|
|
731
|
+
tagLoaderValue(promise, recordedTags);
|
|
732
|
+
readValueTags(promise);
|
|
733
|
+
const bindingTags = executorOptions?.bindingTags?.get(loader.$$id);
|
|
734
|
+
if (bindingTags) linkRecordedTags(recordedTags, bindingTags);
|
|
735
|
+
}
|
|
694
736
|
|
|
695
737
|
// Auxiliary-lane tracking: keeps the handle store open for this body's
|
|
696
738
|
// ctx.handle() pushes without joining `settled` (the handler barrier that
|
|
@@ -734,7 +776,41 @@ export function setupLoaderAccess<TEnv>(
|
|
|
734
776
|
const reqCtxRef = _getRequestContext();
|
|
735
777
|
const handleStoreRef = reqCtxRef?._handleStore;
|
|
736
778
|
|
|
737
|
-
|
|
779
|
+
// Loader-cache HIT replay (loader-cache.ts replayLoaderHandles): a cached
|
|
780
|
+
// entry records its ctx.use dependencies' pushes too, and a dependency is
|
|
781
|
+
// memoized here and shared with live readers. A loader that already ran
|
|
782
|
+
// here keeps its live pushes; otherwise one replay delivers its recorded
|
|
783
|
+
// pushes, and a later run here replaces them (HandleStore.pushReplayed).
|
|
784
|
+
const claimed = new Set<string>();
|
|
785
|
+
const internal = ctx as InternalHandlerContext<any, TEnv>;
|
|
786
|
+
internal._claimLoaderPushes = (loaderId) => {
|
|
787
|
+
if (loaderPromises.has(loaderId) || claimed.has(loaderId)) return false;
|
|
788
|
+
claimed.add(loaderId);
|
|
789
|
+
return true;
|
|
790
|
+
};
|
|
791
|
+
// A loader cache() binding's tags reach every execution of that loader
|
|
792
|
+
// outside the binding: one a reader started before the binding (linked
|
|
793
|
+
// here), a later one, or a refresh's (bindingTags).
|
|
794
|
+
const executor: { bindingTags?: Map<string, Set<string>> } = {};
|
|
795
|
+
internal._bindLoaderCacheTags = (loaderId, tags) => {
|
|
796
|
+
(executor.bindingTags ??= new Map()).set(loaderId, tags);
|
|
797
|
+
const started = loaderPromises.get(loaderId);
|
|
798
|
+
if (started) linkValueTags(started, tags);
|
|
799
|
+
};
|
|
800
|
+
const useLoader = createLoaderExecutor(ctx, loaderPromises, executor);
|
|
801
|
+
// A refresh's pushes are diverted, so its loader ctxs never claim
|
|
802
|
+
// (cache-runtime.ts refreshView). It reads each loader fresh, not through
|
|
803
|
+
// the page's bindings: a stale refresh must not rebuild its entry from
|
|
804
|
+
// another stale entry.
|
|
805
|
+
internal._runLoaderIsolated = (loader) =>
|
|
806
|
+
createLoaderExecutor(
|
|
807
|
+
Object.create(ctx, {
|
|
808
|
+
_claimLoaderPushes: { value: undefined },
|
|
809
|
+
_loaderCacheOverrides: { value: undefined },
|
|
810
|
+
}),
|
|
811
|
+
new Map(),
|
|
812
|
+
executor,
|
|
813
|
+
)(loader, null);
|
|
738
814
|
|
|
739
815
|
ctx.use = ((item: LoaderDefinition<any, any> | Handle<any, any>) => {
|
|
740
816
|
if (isHandle(item)) {
|
package/src/router/match-api.ts
CHANGED
|
@@ -3,11 +3,15 @@ import {
|
|
|
3
3
|
createCacheScope,
|
|
4
4
|
resolveShellImplicitCacheScope,
|
|
5
5
|
} from "../cache/cache-scope.js";
|
|
6
|
+
import { armLoaderTagSets, armRecordTagOwners } from "../cache/cache-tag.js";
|
|
7
|
+
import { bindsLoaderCache } from "./segment-resolution/loader-cache.js";
|
|
6
8
|
import { RouteNotFoundError } from "../errors";
|
|
7
9
|
import {
|
|
8
10
|
createErrorInfo,
|
|
9
11
|
createErrorSegment,
|
|
12
|
+
findInOrphans,
|
|
10
13
|
findNearestErrorBoundary as findErrorBoundary,
|
|
14
|
+
withErrorBoundary,
|
|
11
15
|
} from "./error-handling.js";
|
|
12
16
|
import {
|
|
13
17
|
createHandlerContext,
|
|
@@ -148,6 +152,20 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
148
152
|
Store.metrics = metricsStore;
|
|
149
153
|
}
|
|
150
154
|
|
|
155
|
+
// A shell capture's implicit doc scope, or a document HIT tail's (which
|
|
156
|
+
// replaces any route-derived scope: the tail replays the shell's doc
|
|
157
|
+
// record), or else the route's own scope.
|
|
158
|
+
const cacheScope = resolveShellImplicitCacheScope(snapshot.cacheScope);
|
|
159
|
+
// A scope means a route cache() record may be written: arm its tag owners
|
|
160
|
+
// before any handler runs (#957). A document HIT tail writes no record and
|
|
161
|
+
// runs no handler, so it arms nothing. A loader bound with its own cache()
|
|
162
|
+
// needs the per-execution loader tag sets before any loader runs (#964):
|
|
163
|
+
// loaders still run on a HIT.
|
|
164
|
+
if (cacheScope && !_getRequestContext()?._shellImplicitCache?.docTail) {
|
|
165
|
+
armRecordTagOwners();
|
|
166
|
+
}
|
|
167
|
+
if (bindsLoaderCache(snapshot.entries)) armLoaderTagSets();
|
|
168
|
+
|
|
151
169
|
return {
|
|
152
170
|
request,
|
|
153
171
|
url: cleanUrl,
|
|
@@ -184,9 +202,7 @@ export async function createMatchContextForFull<TEnv>(
|
|
|
184
202
|
},
|
|
185
203
|
isSameRouteNavigation: false,
|
|
186
204
|
interceptResult: null,
|
|
187
|
-
|
|
188
|
-
// substitute an implicit doc-level scope (a route-derived scope wins).
|
|
189
|
-
cacheScope: resolveShellImplicitCacheScope(snapshot.cacheScope),
|
|
205
|
+
cacheScope,
|
|
190
206
|
isIntercept: false,
|
|
191
207
|
actionContext: undefined,
|
|
192
208
|
isAction: false,
|
|
@@ -399,6 +415,16 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
399
415
|
}
|
|
400
416
|
|
|
401
417
|
const isIntercept = !!interceptResult;
|
|
418
|
+
// A normal-route navigation may replay a PPR shell's canonical segment
|
|
419
|
+
// snapshot. Intercepts remain source-dependent and always use their normal
|
|
420
|
+
// cache path.
|
|
421
|
+
const cacheScope = isIntercept
|
|
422
|
+
? snapshot.cacheScope
|
|
423
|
+
: resolveShellImplicitCacheScope(snapshot.cacheScope);
|
|
424
|
+
// A scope means a route cache() record may be written: arm its tag owners
|
|
425
|
+
// before any handler runs (#957); see the full-match site for #964's arm.
|
|
426
|
+
if (cacheScope) armRecordTagOwners();
|
|
427
|
+
if (bindsLoaderCache(snapshot.entries)) armLoaderTagSets();
|
|
402
428
|
|
|
403
429
|
return {
|
|
404
430
|
request,
|
|
@@ -425,12 +451,7 @@ export async function createMatchContextForPartial<TEnv>(
|
|
|
425
451
|
interceptSelectorContext,
|
|
426
452
|
isSameRouteNavigation: nav.isSameRouteNavigation,
|
|
427
453
|
interceptResult,
|
|
428
|
-
|
|
429
|
-
// snapshot. Intercepts remain source-dependent and always use their normal
|
|
430
|
-
// cache path.
|
|
431
|
-
cacheScope: isIntercept
|
|
432
|
-
? snapshot.cacheScope
|
|
433
|
-
: resolveShellImplicitCacheScope(snapshot.cacheScope),
|
|
454
|
+
cacheScope,
|
|
434
455
|
isIntercept,
|
|
435
456
|
actionContext,
|
|
436
457
|
isAction,
|
|
@@ -481,24 +502,21 @@ export async function matchError<TEnv>(
|
|
|
481
502
|
segmentType,
|
|
482
503
|
);
|
|
483
504
|
|
|
505
|
+
// Same search order as findNearestErrorBoundary, so the fallback above and
|
|
506
|
+
// the entry that positions it agree.
|
|
484
507
|
let entryWithBoundary: EntryData | null = null;
|
|
485
508
|
let current: EntryData | null = manifestEntry;
|
|
509
|
+
let prev: EntryData | null = null;
|
|
486
510
|
while (current) {
|
|
487
511
|
if (current.errorBoundary && current.errorBoundary.length > 0) {
|
|
488
512
|
entryWithBoundary = current;
|
|
489
513
|
break;
|
|
490
514
|
}
|
|
491
515
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
if (orphan.errorBoundary && orphan.errorBoundary.length > 0) {
|
|
495
|
-
entryWithBoundary = orphan;
|
|
496
|
-
break;
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
if (entryWithBoundary) break;
|
|
500
|
-
}
|
|
516
|
+
entryWithBoundary = findInOrphans(current, withErrorBoundary, prev) ?? null;
|
|
517
|
+
if (entryWithBoundary) break;
|
|
501
518
|
|
|
519
|
+
prev = current;
|
|
502
520
|
current = current.parent;
|
|
503
521
|
}
|
|
504
522
|
|
|
@@ -507,23 +525,19 @@ export async function matchError<TEnv>(
|
|
|
507
525
|
|
|
508
526
|
if (entryWithBoundary) {
|
|
509
527
|
boundaryEntry = entryWithBoundary;
|
|
528
|
+
// The chain entry holding the boundary, itself or in its orphan tree: the
|
|
529
|
+
// error segment takes the place of its child in the chain.
|
|
530
|
+
const boundaryOwner = current;
|
|
510
531
|
|
|
511
532
|
outletEntry = manifestEntry;
|
|
512
533
|
current = manifestEntry;
|
|
513
534
|
|
|
514
535
|
while (current) {
|
|
515
|
-
if (current.parent ===
|
|
536
|
+
if (current.parent === boundaryOwner) {
|
|
516
537
|
outletEntry = current;
|
|
517
538
|
break;
|
|
518
539
|
}
|
|
519
540
|
|
|
520
|
-
if (current.parent && current.parent.layout) {
|
|
521
|
-
if (current.parent.layout.includes(boundaryEntry)) {
|
|
522
|
-
outletEntry = current;
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
|
|
527
541
|
current = current.parent;
|
|
528
542
|
}
|
|
529
543
|
} else {
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
buildCacheSignalSegments,
|
|
48
48
|
} from "./telemetry.js";
|
|
49
49
|
import { _getRequestContext } from "../server/request-context.js";
|
|
50
|
+
import { ShellRecordUnavailableError } from "../cache/shell-snapshot.js";
|
|
50
51
|
import { evaluatePprTransitionWhen } from "./transition-when.js";
|
|
51
52
|
|
|
52
53
|
/**
|
|
@@ -380,6 +381,10 @@ export function createMatchHandlers<TEnv = any>(
|
|
|
380
381
|
emitter.end(0, false, error.status);
|
|
381
382
|
throw error;
|
|
382
383
|
}
|
|
384
|
+
// A document shell HIT tail's doc record could not supply the
|
|
385
|
+
// handler layer: a serve-path signal serveShellHit degrades, not a
|
|
386
|
+
// routing error (the record's decode failure was already reported).
|
|
387
|
+
if (error instanceof ShellRecordUnavailableError) throw error;
|
|
383
388
|
emitter.error(
|
|
384
389
|
error instanceof Error ? error : new Error(String(error)),
|
|
385
390
|
"routing",
|