@rangojs/router 0.18.0 → 0.19.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/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +2 -0
- package/dist/types/browser/navigation-transaction.d.ts +20 -2
- package/dist/types/browser/partial-update.d.ts +16 -1
- package/dist/types/browser/prefetch/cache.d.ts +6 -6
- package/dist/types/browser/react/Link.d.ts +19 -9
- package/dist/types/browser/react/context.d.ts +2 -2
- package/dist/types/browser/react/location-state-shared.d.ts +95 -56
- package/dist/types/browser/react/location-state.d.ts +1 -1
- package/dist/types/browser/transition-when.d.ts +80 -0
- package/dist/types/browser/types.d.ts +104 -9
- package/dist/types/build/generate-manifest.d.ts +8 -0
- package/dist/types/cache/background-task.d.ts +24 -2
- package/dist/types/cache/cache-exec-scope.d.ts +31 -0
- package/dist/types/cache/cache-key-utils.d.ts +44 -13
- package/dist/types/cache/cache-policy.d.ts +1 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +117 -35
- package/dist/types/cache/cache-tag.d.ts +24 -4
- package/dist/types/cache/cf/cf-cache-store.d.ts +55 -44
- package/dist/types/cache/cf/cf-shell-frame.d.ts +1 -1
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +0 -15
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +40 -8
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-snapshot.d.ts +88 -141
- package/dist/types/cache/tag-invalidation.d.ts +38 -14
- package/dist/types/cache/types.d.ts +68 -61
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +23 -16
- package/dist/types/client-urls/navigation.d.ts +28 -2
- package/dist/types/client-urls/server-projection.d.ts +5 -2
- package/dist/types/client-urls/types.d.ts +5 -5
- package/dist/types/client.d.ts +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +1 -1
- package/dist/types/route-definition/helpers-types.d.ts +11 -11
- package/dist/types/route-name.d.ts +7 -0
- package/dist/types/router/error-handling.d.ts +1 -1
- package/dist/types/router/intercept-resolution.d.ts +2 -0
- package/dist/types/router/match-context.d.ts +5 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +18 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +14 -40
- package/dist/types/router/segment-resolution/mask-nested.d.ts +11 -3
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
- package/dist/types/router/transition-when.d.ts +24 -10
- package/dist/types/rsc/attach-transition-when.d.ts +18 -0
- package/dist/types/rsc/full-payload.d.ts +14 -7
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +4 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +77 -20
- package/dist/types/rsc/shell-capture.d.ts +78 -81
- package/dist/types/rsc/shell-serve.d.ts +19 -48
- package/dist/types/rsc/types.d.ts +2 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +89 -61
- package/dist/types/server/cookie-store.d.ts +30 -18
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +66 -45
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/index.d.ts +3 -2
- package/dist/types/testing/internal/action-id.d.ts +7 -0
- package/dist/types/testing/location-state-key.d.ts +24 -0
- package/dist/types/testing/render-route.d.ts +17 -1
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +7 -6
- package/dist/types/testing/shell-status.d.ts +34 -22
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/index.d.ts +1 -1
- package/dist/types/types/segments.d.ts +86 -71
- package/dist/types/urls/path-helper-types.d.ts +3 -4
- package/dist/types/urls/pattern-types.d.ts +3 -4
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/vite/index.js +960 -97
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +76 -49
- package/skills/caching/SKILL.md +167 -69
- package/skills/client-urls/SKILL.md +1 -1
- package/skills/comparison/references/framework-comparison.md +8 -5
- package/skills/document-cache/SKILL.md +24 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +98 -6
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +52 -21
- package/skills/middleware/SKILL.md +18 -0
- package/skills/migrate-nextjs/SKILL.md +3 -3
- package/skills/observability/SKILL.md +6 -5
- package/skills/parallel/SKILL.md +1 -1
- package/skills/ppr/SKILL.md +129 -77
- package/skills/prerender/SKILL.md +3 -3
- package/skills/rango/SKILL.md +25 -6
- package/skills/route/SKILL.md +2 -0
- package/skills/testing/SKILL.md +27 -25
- package/skills/testing/cache-prerender.md +9 -9
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +3 -2
- package/skills/testing/middleware.md +1 -0
- package/skills/testing/predicates.md +49 -49
- package/skills/testing/render-handler.md +1 -0
- package/skills/testing/serve-shell-request.md +14 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +8 -4
- package/skills/typesafety/env-and-bindings.md +8 -0
- package/skills/use-cache/SKILL.md +64 -19
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +52 -1
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +77 -18
- package/src/browser/navigation-client.ts +4 -3
- package/src/browser/navigation-store.ts +43 -1
- package/src/browser/navigation-transaction.ts +31 -11
- package/src/browser/partial-update.ts +146 -35
- package/src/browser/prefetch/cache.ts +6 -6
- package/src/browser/prefetch/fetch.ts +2 -2
- package/src/browser/react/Link.tsx +38 -33
- package/src/browser/react/NavigationProvider.tsx +2 -2
- package/src/browser/react/context.ts +2 -2
- package/src/browser/react/location-state-shared.ts +241 -49
- package/src/browser/react/location-state.ts +5 -2
- package/src/browser/rsc-router.tsx +4 -0
- package/src/browser/server-action-bridge.ts +84 -15
- package/src/browser/transition-when.ts +205 -0
- package/src/browser/types.ts +124 -10
- package/src/build/generate-manifest.ts +77 -1
- package/src/cache/background-task.ts +55 -2
- package/src/cache/cache-exec-scope.ts +48 -1
- package/src/cache/cache-key-utils.ts +56 -16
- package/src/cache/cache-policy.ts +9 -4
- package/src/cache/cache-runtime.ts +197 -144
- package/src/cache/cache-scope.ts +304 -112
- package/src/cache/cache-tag.ts +105 -13
- package/src/cache/cf/cf-cache-store.ts +152 -76
- package/src/cache/cf/cf-shell-frame.ts +2 -2
- package/src/cache/cf/cf-tag-marker-memo.ts +11 -100
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +52 -9
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-snapshot.ts +130 -348
- package/src/cache/tag-invalidation.ts +57 -51
- package/src/cache/types.ts +71 -67
- package/src/cache/vercel/vercel-cache-store.ts +61 -58
- package/src/client-urls/client-root.tsx +8 -4
- package/src/client-urls/client-urls.ts +8 -6
- package/src/client-urls/navigation.ts +55 -4
- package/src/client-urls/server-projection.ts +31 -10
- package/src/client-urls/types.ts +12 -5
- package/src/client.tsx +2 -0
- package/src/host/cookie-handler.ts +2 -1
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/loader-redirect.tsx +7 -3
- package/src/prerender/build-shell-capture.ts +155 -146
- package/src/route-definition/dsl-helpers.ts +2 -0
- package/src/route-definition/helpers-types.ts +11 -11
- package/src/route-name.ts +12 -0
- package/src/router/content-negotiation.ts +4 -1
- package/src/router/error-handling.ts +5 -2
- package/src/router/handler-context.ts +10 -7
- package/src/router/intercept-resolution.ts +11 -1
- package/src/router/loader-resolution.ts +28 -3
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +62 -34
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +9 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +35 -19
- package/src/router/match-middleware/cache-store.ts +15 -9
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware.ts +2 -5
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +4 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +0 -4
- package/src/router/segment-resolution/loader-cache.ts +150 -25
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +15 -78
- package/src/router/segment-resolution/mask-nested.ts +47 -22
- package/src/router/segment-resolution/revalidation.ts +0 -3
- package/src/router/segment-resolution/view-transition-default.ts +6 -21
- package/src/router/telemetry.ts +6 -3
- package/src/router/transition-when.ts +34 -52
- package/src/router.ts +2 -1
- package/src/rsc/attach-transition-when.ts +47 -0
- package/src/rsc/full-payload.ts +30 -10
- package/src/rsc/handler.ts +12 -10
- package/src/rsc/loader-fetch.ts +2 -1
- package/src/rsc/manifest-init.ts +4 -0
- package/src/rsc/origin-guard.ts +8 -5
- package/src/rsc/progressive-enhancement.ts +7 -30
- package/src/rsc/response-cache-serve.ts +68 -80
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +206 -252
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +22 -20
- package/src/rsc/shell-capture-constants.ts +136 -20
- package/src/rsc/shell-capture.ts +352 -589
- package/src/rsc/shell-serve.ts +73 -109
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +2 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +189 -124
- package/src/server/cookie-store.ts +153 -127
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +123 -84
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/index.ts +5 -0
- package/src/testing/internal/action-id.ts +25 -0
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/location-state-key.ts +45 -0
- package/src/testing/render-handler.ts +1 -1
- package/src/testing/render-route.tsx +109 -16
- package/src/testing/run-client-revalidate.ts +3 -19
- package/src/testing/run-loader.ts +5 -1
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +10 -7
- package/src/testing/shell-status.ts +60 -56
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/index.ts +3 -0
- package/src/types/segments.ts +94 -91
- package/src/urls/path-helper-types.ts +3 -4
- package/src/urls/pattern-types.ts +3 -4
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +0 -2
- package/src/vite/discovery/virtual-module-codegen.ts +16 -6
- package/src/vite/plugins/transition-when-hoist.ts +1064 -0
- package/src/vite/rango.ts +2 -0
- package/src/vite/router-discovery.ts +75 -5
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
|
@@ -489,7 +489,7 @@ layout(<ShopLayout />, () => [
|
|
|
489
489
|
"@modal",
|
|
490
490
|
"product.detail",
|
|
491
491
|
ProductQuickView,
|
|
492
|
-
{ when: ({ from }) => from.pathname === "/products" },
|
|
492
|
+
{ when: ({ from }) => from.url.pathname === "/products" },
|
|
493
493
|
() => [
|
|
494
494
|
loader(ProductLoader),
|
|
495
495
|
loading(<QuickViewSkeleton />),
|
|
@@ -508,8 +508,10 @@ middleware, layout, loaders, loading state, and loader-level caching and
|
|
|
508
508
|
`intercept()`) can choose by navigation source.
|
|
509
509
|
|
|
510
510
|
This is integrated with the rest of the runtime rather than implemented as URL
|
|
511
|
-
masking plus local component state.
|
|
512
|
-
|
|
511
|
+
masking plus local component state. A route an intercept targets gets
|
|
512
|
+
source-scoped prefetch entries whether or not the intercept applied, so neither a
|
|
513
|
+
modal Flight payload nor a full page prefetched from another page is reused where
|
|
514
|
+
the intercept decides differently. A prerendered target
|
|
513
515
|
can store a separate intercept variant while its loaders still run live at
|
|
514
516
|
request time. An action can revalidate the open intercept without remounting its
|
|
515
517
|
subtree or forcing the preserved background to render. See the
|
|
@@ -578,7 +580,8 @@ the bounded cache uses a configurable TTL. `Save-Data`,
|
|
|
578
580
|
Correctness is part of the prefetch contract. Keys include Rango state, destination
|
|
579
581
|
URL, and the mounted segment set used to compute the diff. Server actions rotate
|
|
580
582
|
state and abort stale speculative work; a generation check prevents a late result
|
|
581
|
-
from repopulating an invalidated cache.
|
|
583
|
+
from repopulating an invalidated cache. Routes an intercept targets are automatically
|
|
584
|
+
source-scoped, matched or not,
|
|
582
585
|
and malformed, cross-origin, foreign-router, redirect, and reload responses are
|
|
583
586
|
dropped rather than warmed. `useLinkStatus()` exposes `{ pending }` for the owning
|
|
584
587
|
link.
|
|
@@ -754,7 +757,7 @@ from two hosts, route scopes, param sets, or query variants therefore does not
|
|
|
754
757
|
collapse into one entry just because both calls received a `ctx` object.
|
|
755
758
|
|
|
756
759
|
The same boundary guards operations whose meaning cannot survive a hit. Direct
|
|
757
|
-
`cookies()`/`headers()` reads and request/response mutations such as `ctx.set()`,
|
|
760
|
+
`cookies()`/`headers()`/`ctx.request.headers` reads and request/response mutations such as `ctx.set()`,
|
|
758
761
|
`ctx.header()`, status, theme, and location-state writes throw inside
|
|
759
762
|
`"use cache"`. If a cached function pushes typed handles through
|
|
760
763
|
`ctx.use(Breadcrumbs)` or `ctx.use(Meta)`, Rango captures those pushes on a miss and
|
|
@@ -219,6 +219,30 @@ the variant suffixes are still appended.
|
|
|
219
219
|
error during the render. The current request still gets the errored render,
|
|
220
220
|
the next request renders fresh, and on a stale refresh the stale entry keeps
|
|
221
221
|
serving.
|
|
222
|
+
- A page rendered with a visitor's stored theme other than `defaultTheme`.
|
|
223
|
+
That happens only when `Cache-Control` was set after the render (see
|
|
224
|
+
"Theme" below).
|
|
225
|
+
|
|
226
|
+
## Theme
|
|
227
|
+
|
|
228
|
+
With `theme` enabled, the page payload carries the theme `useTheme()` starts
|
|
229
|
+
at (`initialTheme`). A page whose response opted in to this cache before
|
|
230
|
+
`next()` starts it at the no-cookie default (`defaultTheme`), whoever rendered
|
|
231
|
+
it (#978), so a visitor with no stored theme never inherits the theme of the
|
|
232
|
+
visitor who warmed the entry. A visitor with a stored theme sees theirs after
|
|
233
|
+
hydration, and the `<html>` class is right before paint either way (the theme
|
|
234
|
+
script reads the cookie). Any other render, a 404 included, starts at the
|
|
235
|
+
visitor's own theme.
|
|
236
|
+
|
|
237
|
+
The render decides from the `Cache-Control` the handler or middleware set
|
|
238
|
+
before `await next()`. A header set after `next()` (or in `onResponse`) arrives
|
|
239
|
+
once the page is rendered, so the cache stores that response only when it was
|
|
240
|
+
rendered with the default theme. Set `Cache-Control` in the handler or before
|
|
241
|
+
`next()` so every visitor's miss can fill the entry.
|
|
242
|
+
|
|
243
|
+
A handler that reads `ctx.theme` on a stored route is not guarded: the stored
|
|
244
|
+
document shows that visitor's theme to everyone. Read the theme with
|
|
245
|
+
`useTheme()` instead (see `/theme`).
|
|
222
246
|
|
|
223
247
|
## Invalidation
|
|
224
248
|
|
package/skills/hooks/SKILL.md
CHANGED
|
@@ -66,7 +66,9 @@ the one for your case.
|
|
|
66
66
|
Rango-specific hook for tracking actions called outside a
|
|
67
67
|
`<form action={...}>` flow.
|
|
68
68
|
- [`./state.md`](./state.md) — `useLocationState` (persistent + flash state,
|
|
69
|
-
`.read()`/`.write()`/`.delete()`
|
|
69
|
+
`.read()`/`.read(location)`/`.write()`/`.delete()`, typed `state` on `router.push()` /
|
|
70
|
+
`router.replace()`, serializability errors that name the failing field),
|
|
71
|
+
`invalidateClientCache()`.
|
|
70
72
|
- [`./outlets.md`](./outlets.md) — `Outlet`, `ParallelOutlet`, `useOutlet`.
|
|
71
73
|
- [`./urls.md`](./urls.md) — `useParams`, `usePathname`, `useSearchParams`,
|
|
72
74
|
`useHref`, `useMount`, `useReverse`.
|
|
@@ -67,8 +67,10 @@ function NavigationControls() {
|
|
|
67
67
|
| `forward()` | `history.forward()`. |
|
|
68
68
|
|
|
69
69
|
`push` / `replace` options: `scroll` (`false` keeps the current scroll
|
|
70
|
-
position), `revalidate` (default `true`, see below),
|
|
71
|
-
|
|
70
|
+
position), `revalidate` (default `true`, see below), `transition` (`false`
|
|
71
|
+
skips the route's `transition()` for this navigation, see below), and `state`
|
|
72
|
+
(location state: `[MyState(value)]` for typed entries, or plain
|
|
73
|
+
structured-clone-safe data; see [`./state.md`](./state.md)).
|
|
72
74
|
|
|
73
75
|
Target resolution: a path starting with `/` is app-absolute and gets the
|
|
74
76
|
router `basename` prefixed. A relative path (`"cart"`, `"./cart"`) resolves
|
|
@@ -95,6 +97,25 @@ If the pathname changes, `revalidate: false` is silently ignored and a full navi
|
|
|
95
97
|
|
|
96
98
|
Plain `<a>` tags can opt in via `data-revalidate="false"`.
|
|
97
99
|
|
|
100
|
+
#### Skipping the transition
|
|
101
|
+
|
|
102
|
+
Pass `transition: false` to present one navigation without the route's
|
|
103
|
+
`transition()`: the commit is urgent (the `loading()` skeleton streams) and no
|
|
104
|
+
view transition runs, the same result as a `transition({ when })` predicate
|
|
105
|
+
returning `false`. No predicate is called. Back/forward, actions and
|
|
106
|
+
revalidation are unaffected.
|
|
107
|
+
|
|
108
|
+
```tsx
|
|
109
|
+
router.push("/photos/2", { transition: false });
|
|
110
|
+
|
|
111
|
+
<Link to="/photos/2" transition={false}>
|
|
112
|
+
Next
|
|
113
|
+
</Link>;
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Plain `<a>` tags can opt in via `data-transition="false"`. See
|
|
117
|
+
`/view-transitions` → "Conditional transitions".
|
|
118
|
+
|
|
98
119
|
### useSegments()
|
|
99
120
|
|
|
100
121
|
Access current URL path and matched route segments:
|
package/skills/hooks/state.md
CHANGED
|
@@ -12,6 +12,9 @@ import { useLocationState, createLocationState } from "@rangojs/router/client";
|
|
|
12
12
|
|
|
13
13
|
// Define typed state (all export patterns supported)
|
|
14
14
|
// Keys are auto-injected by the Vite plugin -- no manual key needed.
|
|
15
|
+
// In a unit test (no plugin), key it with withLocationStateKey(ProductState,
|
|
16
|
+
// "ProductState") from @rangojs/router/testing: outside production, an
|
|
17
|
+
// unkeyed definition throws on first use.
|
|
15
18
|
export const ProductState = createLocationState<{
|
|
16
19
|
name: string;
|
|
17
20
|
price: number;
|
|
@@ -81,8 +84,87 @@ definition:
|
|
|
81
84
|
const state = useLocationState<{ from?: string }>(); // { from?: string } | undefined
|
|
82
85
|
```
|
|
83
86
|
|
|
87
|
+
### State on router.push() / router.replace()
|
|
88
|
+
|
|
84
89
|
The same `state` option exists on `router.push()` / `router.replace()` (see
|
|
85
|
-
[`./navigation.md`](./navigation.md))
|
|
90
|
+
[`./navigation.md`](./navigation.md)), with the same type as `<Link state>`
|
|
91
|
+
(minus the click-time getter): `HistoryState`, which is
|
|
92
|
+
`readonly LocationStateEntry[]` (typed entries) or `PlainHistoryState` (plain,
|
|
93
|
+
structured-clone-safe data). Both types are exported from
|
|
94
|
+
`@rangojs/router/client`.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
"use client";
|
|
98
|
+
import { useLocationState, useRouter } from "@rangojs/router/client";
|
|
99
|
+
import { GridState } from "./state"; // createLocationState<{ count: number }>()
|
|
100
|
+
|
|
101
|
+
function LoadMore() {
|
|
102
|
+
const router = useRouter();
|
|
103
|
+
const grid = useLocationState(GridState);
|
|
104
|
+
const count = grid?.count ?? 20;
|
|
105
|
+
return (
|
|
106
|
+
<button
|
|
107
|
+
onClick={() =>
|
|
108
|
+
router.replace("?page=2", { state: [GridState({ count: count + 20 })] })
|
|
109
|
+
}
|
|
110
|
+
>
|
|
111
|
+
Load more
|
|
112
|
+
</button>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Typed entries always go in an array, with nothing else in it. Three mistakes
|
|
118
|
+
are compile errors:
|
|
119
|
+
|
|
120
|
+
```tsx
|
|
121
|
+
router.push(url, { state: GridState({ count: 3 }) }); // entry without the array
|
|
122
|
+
router.push(url, { state: [GridState] }); // definition, not called
|
|
123
|
+
router.push(url, { state: [GridState({ count: 3 }), { from: "list" }] }); // mixed
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Without the array, the entry's own `__rsc_ls_*` fields would land on
|
|
127
|
+
`history.state` and `useLocationState(GridState)` would read `undefined`; an
|
|
128
|
+
uncalled definition throws `DataCloneError` in `history.pushState`; in a mixed
|
|
129
|
+
array only the first element picks the format, so the rest are lost. For
|
|
130
|
+
untyped (JS) callers, development builds throw an error for each that names
|
|
131
|
+
the definition's key (or the offending index) and the fix.
|
|
132
|
+
|
|
133
|
+
Plain state is checked at the top level only. It rejects functions, symbols,
|
|
134
|
+
`Promise`, `WeakMap`, `WeakSet`, and values typed `unknown` (narrow an
|
|
135
|
+
`unknown` value, or annotate it as `PlainHistoryState`). Array, `Map`, and
|
|
136
|
+
`Set` elements are checked the same way, but plain object members are not.
|
|
137
|
+
These still compile and still throw `DataCloneError` at `history.pushState`:
|
|
138
|
+
|
|
139
|
+
- a function, symbol, or React element inside a plain object
|
|
140
|
+
(`{ onClose: () => {} }`);
|
|
141
|
+
- a top-level `ReactElement` or DOM node (`HTMLElement`), which look like
|
|
142
|
+
plain objects to the type.
|
|
143
|
+
|
|
144
|
+
Store plain data and rebuild the rest on arrival, or use a typed
|
|
145
|
+
`createLocationState<T>()` definition, whose `T` is checked all the way down.
|
|
146
|
+
|
|
147
|
+
### When a type fails the serializability check
|
|
148
|
+
|
|
149
|
+
`createLocationState<T>()` rejects values that cannot survive
|
|
150
|
+
`history.state`'s structured clone: functions, class constructors, symbols,
|
|
151
|
+
React/RSC content, and fields typed `unknown`. The compile error names the
|
|
152
|
+
failing field and the reason:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
Argument of type '{ items: never[]; cursor: string; }' is not assignable to
|
|
156
|
+
parameter of type '... & LocationStateUnsafe<"`unknown` cannot be verified as
|
|
157
|
+
serializable; give it a concrete type", "items[].info.values">'.
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The path uses `.` for fields, `[string]` / `[number]` for index signatures
|
|
161
|
+
(`Record<string, T>`), `[]` for array and `Set` elements, `[0]` for tuple
|
|
162
|
+
elements, `<key>` / `<value>` for `Map` keys and values, and `<root>` when the
|
|
163
|
+
value itself is unsafe. A field typed `any` is never reported. When several fields fail, the error lists one `LocationStateUnsafe` per
|
|
164
|
+
path. An object's own unsafe fields are reported before anything nested
|
|
165
|
+
deeper, so a class instance or DOM node stops at its methods
|
|
166
|
+
(`row.el.click`); fix those and the next compile names the rest. The walk
|
|
167
|
+
stops 8 levels deep, reporting the deepest path it reached.
|
|
86
168
|
|
|
87
169
|
### Flash State (read-once)
|
|
88
170
|
|
|
@@ -160,6 +242,15 @@ const flash = FlashMessage.read();
|
|
|
160
242
|
const product = ProductState.read();
|
|
161
243
|
```
|
|
162
244
|
|
|
245
|
+
`.read(location)` reads this definition's slot from any `{ state }` snapshot
|
|
246
|
+
instead of the current entry, such as the `from` / `to` of a
|
|
247
|
+
`transition({ when })` context. It works without `window` and never clears
|
|
248
|
+
flash state:
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
transition({ when: ({ to }) => ProductState.read(to) !== undefined });
|
|
252
|
+
```
|
|
253
|
+
|
|
163
254
|
> **Hydration:** `.read()` returns `undefined` on the server but may return
|
|
164
255
|
> a real value on the first client render (history state survives reload).
|
|
165
256
|
> Do not call `.read()` directly during the initial render of a component;
|
|
@@ -191,11 +282,12 @@ const current = ProductState.read();
|
|
|
191
282
|
ProductState.delete();
|
|
192
283
|
```
|
|
193
284
|
|
|
194
|
-
| Method
|
|
195
|
-
|
|
|
196
|
-
| `.read()`
|
|
197
|
-
| `.
|
|
198
|
-
| `.
|
|
285
|
+
| Method | Updates `history.state` | Fires `useLocationState` rerender | SSR behavior |
|
|
286
|
+
| ----------------- | ----------------------- | --------------------------------- | ------------------- |
|
|
287
|
+
| `.read()` | no | n/a (returns snapshot) | returns `undefined` |
|
|
288
|
+
| `.read(location)` | no | n/a (reads the given snapshot) | reads the snapshot |
|
|
289
|
+
| `.write()` | yes (replace this slot) | no | throws |
|
|
290
|
+
| `.delete()` | yes (remove this slot) | no | throws |
|
|
199
291
|
|
|
200
292
|
## Cache Control
|
|
201
293
|
|
|
@@ -214,24 +214,41 @@ intercept(
|
|
|
214
214
|
"product",
|
|
215
215
|
<ProductModal />,
|
|
216
216
|
// Only intercept when coming from a different section
|
|
217
|
-
{ when: ({ from }) => !from.pathname.startsWith("/shop/product/") },
|
|
217
|
+
{ when: ({ from }) => !from.url.pathname.startsWith("/shop/product/") },
|
|
218
218
|
() => [
|
|
219
219
|
loader(ProductLoader),
|
|
220
220
|
]
|
|
221
221
|
)
|
|
222
222
|
```
|
|
223
223
|
|
|
224
|
-
`when` is a synchronous match-time selector
|
|
225
|
-
`
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
224
|
+
`when` is a synchronous match-time selector that runs on the server. It
|
|
225
|
+
receives `from` and `to` (the destination), each `{ url, params, routeName }`
|
|
226
|
+
— the shape `transition({ when })` sees, without `state`, because history
|
|
227
|
+
state never reaches the server — plus `segments` (the client's current
|
|
228
|
+
segment path and ids), `request`, and `env`. `from` is the page the
|
|
229
|
+
navigation leaves, except while an intercept is open: then it is the
|
|
230
|
+
intercept's source page, the page under the modal (`match-api.ts`,
|
|
231
|
+
`navigation-snapshot.ts` `effectiveFromUrl`). Opening `/item/1` as a modal
|
|
232
|
+
over `/list/open` and then navigating to `/item/2` from inside the modal
|
|
233
|
+
gives `from.url.pathname === "/list/open"`, not `/item/1`. That differs from
|
|
234
|
+
`transition({ when })`'s `from`, which is always the committed location on
|
|
235
|
+
screen. `routeName` is set for named routes only. Pass an array of predicates
|
|
236
|
+
for AND logic (all must return true). Omit `when` entirely and the intercept
|
|
237
|
+
always activates. `when` is not re-evaluated during action revalidation, so
|
|
238
|
+
an open modal stays open after an action. A selector that throws does not
|
|
239
|
+
intercept: the full page renders and the error is logged with the route name.
|
|
240
|
+
|
|
241
|
+
A prefetch of a route an intercept targets is stored per source page, whether
|
|
242
|
+
or not the intercept applies from that page (`rsc-rendering.ts`
|
|
243
|
+
`x-rsc-prefetch-scope: source`, set from `MatchResult.interceptTargeted`). A
|
|
244
|
+
full page prefetched from a page where `when` is false never serves a click
|
|
245
|
+
from a page where it is true. Routes no intercept targets keep one shared
|
|
246
|
+
prefetch entry.
|
|
230
247
|
|
|
231
248
|
```typescript
|
|
232
249
|
// Intercept only when opened from the shop index
|
|
233
250
|
intercept("@modal", "product", <ProductModal />, {
|
|
234
|
-
when: ({
|
|
251
|
+
when: ({ from }) => from.routeName === "index",
|
|
235
252
|
})
|
|
236
253
|
```
|
|
237
254
|
|
|
@@ -242,8 +259,8 @@ intercept(
|
|
|
242
259
|
<ProductModal />,
|
|
243
260
|
{
|
|
244
261
|
when: [
|
|
245
|
-
({ from }) => from.pathname.startsWith("/shop"),
|
|
246
|
-
({
|
|
262
|
+
({ from }) => from.url.pathname.startsWith("/shop"),
|
|
263
|
+
({ to }) => to.params.slug !== "featured",
|
|
247
264
|
],
|
|
248
265
|
},
|
|
249
266
|
() => [
|
|
@@ -401,7 +418,7 @@ layout(ShopLayout, () => [
|
|
|
401
418
|
"@modal",
|
|
402
419
|
".detail",
|
|
403
420
|
<ProductModal />,
|
|
404
|
-
{ when: ({ from }) => from.pathname.startsWith("/shop") },
|
|
421
|
+
{ when: ({ from }) => from.url.pathname.startsWith("/shop") },
|
|
405
422
|
() => [loader(ProductLoader)],
|
|
406
423
|
),
|
|
407
424
|
])
|
|
@@ -482,7 +499,7 @@ export const shopPatterns = urls(({
|
|
|
482
499
|
"@modal",
|
|
483
500
|
".product", // dot-local: resolves to "shop.product" in this include
|
|
484
501
|
<ProductModalContent />,
|
|
485
|
-
{ when: ({ from }) => !from.pathname.startsWith("/shop/product/") },
|
|
502
|
+
{ when: ({ from }) => !from.url.pathname.startsWith("/shop/product/") },
|
|
486
503
|
() => [
|
|
487
504
|
layout(<ModalWrapper />),
|
|
488
505
|
loading(<ProductModalSkeleton />),
|
package/skills/loader/SKILL.md
CHANGED
|
@@ -139,11 +139,13 @@ A DSL loader without `ssr: false` can read `createVar({ cache: false })`
|
|
|
139
139
|
variables: it runs on every request, `cache()` and PPR shell HITs included, so
|
|
140
140
|
the read guard is bypassed for it. On a `ppr` route the capture runs an
|
|
141
141
|
`ssr: false` loader, and a loader a handler awaits, and bakes their values into
|
|
142
|
-
the shell: a `cookies()`, `headers()
|
|
143
|
-
the capture, so the route keeps
|
|
142
|
+
the shell: a `cookies()`, `headers()`, `ctx.request.headers` or
|
|
143
|
+
`{ cache: false }` read there refuses the capture, so the route keeps
|
|
144
|
+
rendering without a shell (warned once per key).
|
|
144
145
|
A loader bound with its own `cache()` is the other exception: its value is
|
|
145
|
-
stored and shared, so a miss whose body read `cookies()`, `headers()`,
|
|
146
|
-
non-cacheable variable or the theme fails unless the
|
|
146
|
+
stored and shared, so a miss whose body read `cookies()`, `headers()`,
|
|
147
|
+
`ctx.request.headers`, a non-cacheable variable or the theme fails unless the
|
|
148
|
+
binding has a `key()` or a
|
|
147
149
|
store `keyGenerator`, which must then include them (see "Cache Key"). A
|
|
148
150
|
`"use cache"` function the loader calls is not a loader body: a non-cacheable
|
|
149
151
|
read inside it throws, also through the loader's `ctx` passed in, so read the
|
|
@@ -191,8 +193,9 @@ the tree).
|
|
|
191
193
|
capture-time copy served on every HIT (a HIT never runs the handler);
|
|
192
194
|
`useLoader` of a loader without `ssr: false` is the live lane (an `ssr: false`
|
|
193
195
|
loader is the bake lane however it is read). Unlike `cache()`, an identity
|
|
194
|
-
read inside that loader (`cookies()`, `headers()`,
|
|
195
|
-
variable) refuses the capture, so the route stays
|
|
196
|
+
read inside that loader (`cookies()`, `headers()`, `ctx.request.headers`, a
|
|
197
|
+
`{ cache: false }` variable) refuses the capture, so the route stays
|
|
198
|
+
uncached. One rule across
|
|
196
199
|
`cache()`, `"use cache"`, and PPR: the consumption-lane rule (`/rango` →
|
|
197
200
|
Invariants).
|
|
198
201
|
- Non-cacheable variable reads (`createVar({ cache: false })`) inside the
|
|
@@ -256,7 +259,8 @@ export const ProductLoader = createLoader(async (ctx) => {
|
|
|
256
259
|
// Platform bindings (DB, KV, etc.) — plain bindings from createRouter<TEnv>()
|
|
257
260
|
const db = ctx.env.DB;
|
|
258
261
|
|
|
259
|
-
// Request headers
|
|
262
|
+
// Request headers (guarded like headers(): an unkeyed loader cache() fill
|
|
263
|
+
// or a ppr capture of this loader refuses the read)
|
|
260
264
|
const auth = ctx.request.headers.get("Authorization");
|
|
261
265
|
|
|
262
266
|
// Variables set by middleware (from Rango.Vars augmentation)
|
|
@@ -654,6 +658,7 @@ at two levels:
|
|
|
654
658
|
import { cookies } from "@rangojs/router";
|
|
655
659
|
|
|
656
660
|
// Full override — key function replaces the default entirely
|
|
661
|
+
// Stored as "loader:{loaderId}:key:product%3Ashoe%3Aen"
|
|
657
662
|
loader(ProductLoader, () => [
|
|
658
663
|
cache({
|
|
659
664
|
ttl: 300,
|
|
@@ -665,12 +670,22 @@ loader(ProductLoader, () => [
|
|
|
665
670
|
// Set in the store configuration, applies to all entries in that store
|
|
666
671
|
```
|
|
667
672
|
|
|
668
|
-
Resolution priority (same as route-level `cache()`):
|
|
673
|
+
Resolution priority (same order as route-level `cache()`):
|
|
669
674
|
|
|
670
|
-
1. `key(ctx)` from cache options — full override
|
|
671
|
-
|
|
675
|
+
1. `key(ctx)` from cache options — full override, stored namespaced as
|
|
676
|
+
`loader:{loaderId}:key:` plus the result's URI encoding
|
|
677
|
+
2. `store.keyGenerator(ctx, defaultKey)` — store-level modification, stored
|
|
678
|
+
as returned
|
|
672
679
|
3. Default key — `loader:{loaderId}:{host}{pathname}:{sortedParams}`
|
|
673
680
|
|
|
681
|
+
Because the `key()` result is namespaced by the loader id, no value it
|
|
682
|
+
returns, request input included, can name another loader's entry or a
|
|
683
|
+
default key: two loaders whose `key()` returns the same value keep separate
|
|
684
|
+
entries. A readable prefix is still good practice. A store `keyGenerator`
|
|
685
|
+
result is stored as returned, so build it on the `defaultKey` it receives
|
|
686
|
+
(which carries the loader id): a generator that returns raw request input
|
|
687
|
+
can still name another entry.
|
|
688
|
+
|
|
674
689
|
A `key` function (or store `keyGenerator`) that throws is **not** caught: the
|
|
675
690
|
loader fails as if its body threw. There is no silent fallback to the default
|
|
676
691
|
key, because a personalised key collapsing onto the broad default would share
|
|
@@ -680,9 +695,10 @@ one user's data with everyone.
|
|
|
680
695
|
names no user, so one entry serves everyone who requests the same loader, host,
|
|
681
696
|
path and params. A route `cache()` around the route does not partition it
|
|
682
697
|
either: a loader's own `cache()` is an independent layer, keyed only by what it
|
|
683
|
-
declares. On a miss, a body that reads `cookies()`, `headers()
|
|
684
|
-
non-cacheable variable (`createVar({ cache: false })`,
|
|
685
|
-
`ctx.set(..., { cache: false })`) therefore **fails**,
|
|
698
|
+
declares. On a miss, a body that reads `cookies()`, `headers()`,
|
|
699
|
+
`ctx.request.headers` or a non-cacheable variable (`createVar({ cache: false })`,
|
|
700
|
+
or a value written with `ctx.set(..., { cache: false })`) therefore **fails**,
|
|
701
|
+
and nothing is stored,
|
|
686
702
|
unless the binding declares identity with a `key()` or a store `keyGenerator`.
|
|
687
703
|
The check tests only that one of them is there, not what it contains: any
|
|
688
704
|
`key()` or store `keyGenerator` switches it off, so it must itself include the
|
|
@@ -720,11 +736,25 @@ stored, and `onError` gets the same error. A push still pending after the
|
|
|
720
736
|
timeout drops the entry's handles, and the value is stored without them. A loader with no `cache()` of its own
|
|
721
737
|
reads request data freely.
|
|
722
738
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
739
|
+
The raw reads count the same: `ctx.request.headers` (the loader's
|
|
740
|
+
`ctx.request` is the request's own `Request`) and `getRequestContext().cookie()`
|
|
741
|
+
/ `.cookies()` fail an unkeyed miss exactly like `cookies()`. A `key()` or a
|
|
742
|
+
store `keyGenerator` reads them freely, since that is how the entry is
|
|
743
|
+
partitioned:
|
|
744
|
+
|
|
745
|
+
```typescript
|
|
746
|
+
loader(LocaleLoader, () => [
|
|
747
|
+
cache({
|
|
748
|
+
ttl: 60,
|
|
749
|
+
key: (ctx) => `lang:${ctx.request.headers.get("accept-language") ?? "en"}`,
|
|
750
|
+
}),
|
|
751
|
+
]),
|
|
752
|
+
```
|
|
753
|
+
|
|
754
|
+
One kind of read is not seen, the same as in `"use cache"`: a value computed
|
|
755
|
+
outside the loader and handed in, such as a per-request memo a handler filled
|
|
756
|
+
from `cookies()` before the loader awaited it. Key what it carries too. If the
|
|
757
|
+
value must stay per request, drop the loader's `cache()` instead.
|
|
728
758
|
|
|
729
759
|
### Tags for Invalidation
|
|
730
760
|
|
|
@@ -797,7 +827,8 @@ loader(ProductLoader, () => [
|
|
|
797
827
|
```
|
|
798
828
|
|
|
799
829
|
When `condition` returns false, the loader runs fresh and the cache is bypassed
|
|
800
|
-
entirely (no read, no write).
|
|
830
|
+
entirely (no read, no write). Like `key()`, a `condition()` reads request
|
|
831
|
+
identity freely: it decides whether the entry is used, it is not stored in it.
|
|
801
832
|
|
|
802
833
|
### Per-Loader Store Override
|
|
803
834
|
|
|
@@ -1035,8 +1066,8 @@ The costs and constraints:
|
|
|
1035
1066
|
loader lane rule. On a HIT, a flagged loader whose return has no promises is
|
|
1036
1067
|
served from the shell and does not run (unless the capture marked it to run:
|
|
1037
1068
|
see `/ppr` → On a shell HIT); one whose return carries promises
|
|
1038
|
-
runs on every HIT, with its baked parts overlaid. A `cookies()`, `headers()
|
|
1039
|
-
or `{ cache: false }` read in it refuses the capture. The
|
|
1069
|
+
runs on every HIT, with its baked parts overlaid. A `cookies()`, `headers()`,
|
|
1070
|
+
`ctx.request.headers` or `{ cache: false }` read in it refuses the capture. The
|
|
1040
1071
|
`progressiveChunkSize` auto-raise is live-document only; captured shells
|
|
1041
1072
|
outline per the explicit option or React's default.
|
|
1042
1073
|
|
|
@@ -274,6 +274,24 @@ export const myMiddleware: Middleware = async (ctx, next) => {
|
|
|
274
274
|
Cookies and request headers are not `ctx` members; use the free functions
|
|
275
275
|
`cookies()` and `headers()` from `@rangojs/router`.
|
|
276
276
|
|
|
277
|
+
Middleware reads request identity freely: `cookies()`, `headers()` and
|
|
278
|
+
`ctx.request.headers` alike. The same `Request` reaches handlers and loaders
|
|
279
|
+
as `ctx.request`, and there `ctx.request.headers` answers like `headers()`: it
|
|
280
|
+
throws inside a `cache()` boundary or a `"use cache"` function and refuses a
|
|
281
|
+
`ppr` shell capture (#976). To render a header on a cached or `ppr` route,
|
|
282
|
+
copy it here with `ctx.set()` (a cacheable variable) and include it in the
|
|
283
|
+
route's `cache()` `key()`, so each value gets its own record and shell:
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
const Tier = createVar<string>();
|
|
287
|
+
|
|
288
|
+
export const tierMiddleware: Middleware = async (ctx, next) => {
|
|
289
|
+
ctx.set(Tier, ctx.request.headers.get("x-tier") ?? "free");
|
|
290
|
+
return next();
|
|
291
|
+
};
|
|
292
|
+
// cache({ key: (ctx) => `tier:${ctx.request.headers.get("x-tier") ?? "free"}` }, ...)
|
|
293
|
+
```
|
|
294
|
+
|
|
277
295
|
### Changing the response after `next()`
|
|
278
296
|
|
|
279
297
|
After `await next()`, `ctx.header()` and `ctx.headers` write to the real
|
|
@@ -303,7 +303,7 @@ The main content always goes through `<Outlet />` via the `path()` handler.
|
|
|
303
303
|
layout(<ShopLayout />, () => [
|
|
304
304
|
path("/product/:id", ProductPage, { name: "product" }),
|
|
305
305
|
intercept("@modal", ".product", <ProductModal />, {
|
|
306
|
-
when: ({ from }) => from.pathname.startsWith("/shop"),
|
|
306
|
+
when: ({ from }) => from.url.pathname.startsWith("/shop"),
|
|
307
307
|
}),
|
|
308
308
|
])
|
|
309
309
|
```
|
|
@@ -504,8 +504,8 @@ Differences that matter during migration:
|
|
|
504
504
|
gives one shell per key value, and `cache(false)` or a false `condition()`
|
|
505
505
|
means no shell.
|
|
506
506
|
- **Request-scoped reads in shell material refuse the capture** (in Next they
|
|
507
|
-
silently force dynamic rendering). `cookies()`, `headers()`,
|
|
508
|
-
`{ cache: false }` variable, and `ctx.dynamic()` refuse it anywhere the
|
|
507
|
+
silently force dynamic rendering). `cookies()`, `headers()`,
|
|
508
|
+
`ctx.request.headers`, a `{ cache: false }` variable, and `ctx.dynamic()` refuse it anywhere the
|
|
509
509
|
capture waits: a handler, a promise it passes or pushes, an async component,
|
|
510
510
|
a loader it awaits. Per-user reads must move into a live loader (no
|
|
511
511
|
`ssr: false`; a nested promise does not help). A normal `ctx.get()` value is
|
|
@@ -97,7 +97,7 @@ and, when the tail finishes, one console line (wrapped here):
|
|
|
97
97
|
```
|
|
98
98
|
[RSC Perf] GET /ppr-large/holes shell tail: complete snapshot=4ms
|
|
99
99
|
snapshot-read=2ms snapshot-bytes=1019145b snapshot-parse-cpu=2ms
|
|
100
|
-
records=segment:1
|
|
100
|
+
records=segment:1 seed=4ms seed-cpu=0ms match=4ms handover=4ms
|
|
101
101
|
first-html=5ms complete=55ms prelude=628949b tail=1033762b
|
|
102
102
|
```
|
|
103
103
|
|
|
@@ -116,10 +116,11 @@ first-html=5ms complete=55ms prelude=628949b tail=1033762b
|
|
|
116
116
|
hinted marker reads ran before the entry named its tags), and `fresh-reads`
|
|
117
117
|
when the request carried the cookie.
|
|
118
118
|
- `records=` counts the snapshot records the tail was seeded with, by
|
|
119
|
-
family
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
family. Here every HIT tail replays the handler layer from the one segment
|
|
120
|
+
record; a bake-lane loader adds `loader:N` pins. `pruned=` appears only when
|
|
121
|
+
a navigation-only entry drops the loader pins it has no reader for
|
|
122
|
+
(`pruned=loader:N`): a document entry records only the doc record and the
|
|
123
|
+
loader pins, so there is nothing else to prune.
|
|
123
124
|
- `snapshot=`, `seed=`, `match=`, `first-html=` are offsets from the commit,
|
|
124
125
|
not durations; `snapshot-read=`, `snapshot-parse-cpu=` and `seed-cpu=` are
|
|
125
126
|
durations.
|
package/skills/parallel/SKILL.md
CHANGED
|
@@ -280,7 +280,7 @@ layout(<AccountLayout />, () => [
|
|
|
280
280
|
|
|
281
281
|
A slot's `loading()` (whether from `handler.use` or explicit) makes that slot an independent streaming unit, exactly as in the **Streaming Behavior** section above.
|
|
282
282
|
|
|
283
|
-
Under a shared artifact (`cache()`, `"use cache"`, a PPR shell), the server-side `await ctx.use(CartLoader)` above is the BAKED lane — the capture-time value freezes into the artifact.
|
|
283
|
+
Under a shared artifact (`cache()`, `"use cache"`, a PPR shell), the server-side `await ctx.use(CartLoader)` above is the BAKED lane — the capture-time value freezes into the artifact. An identity read (`cookies()`, `headers()`, `ctx.request.headers`) inside the loader bakes too under `cache()`; inside `"use cache"` it throws when the cached function is the first to read the loader in the request (an already-started loader's value is stored unchecked); under a PPR shell it refuses the capture, so the route stays uncached. Consume the loader client-side (`useLoader` in a `"use client"` component, the slot's `loading()` as its boundary) to keep the slot live per request. One rule, stated once: `/rango` → Invariants ("the consumption-lane rule").
|
|
284
284
|
|
|
285
285
|
The `parallel` mount site has the narrowest allow-list for `handler.use` items — slots cannot bring their own middleware or layout, only `revalidate`, `loader`, `loading`, `errorBoundary`, `notFoundBoundary`, and `transition`. See [skills/handler-use](../handler-use/SKILL.md) for the full table and merge rules.
|
|
286
286
|
|