@rangojs/router 0.17.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/README.md +3 -2
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/event-controller.d.ts +3 -3
- package/dist/types/browser/history-state.d.ts +18 -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 +48 -2
- package/dist/types/cache/cache-policy.d.ts +18 -1
- package/dist/types/cache/cache-runtime.d.ts +7 -0
- package/dist/types/cache/cache-scope.d.ts +192 -26
- package/dist/types/cache/cache-tag.d.ts +116 -11
- 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 +224 -72
- 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 +19 -0
- package/dist/types/cache/document-cache.d.ts +10 -0
- package/dist/types/cache/handle-snapshot.d.ts +41 -8
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/invalidation-order.d.ts +23 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/request-tag-mask.d.ts +54 -0
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +141 -105
- package/dist/types/cache/tag-invalidation.d.ts +54 -3
- package/dist/types/cache/types.d.ts +267 -96
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
- 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/prerender/build-shell-capture.d.ts +11 -4
- 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/instrument.d.ts +8 -0
- 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/middleware-types.d.ts +6 -1
- package/dist/types/router/router-options.d.ts +4 -1
- package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
- package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
- package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
- 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 +21 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-cache-serve.d.ts +15 -12
- package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
- package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
- package/dist/types/rsc/shell-capture.d.ts +202 -74
- package/dist/types/rsc/shell-serve.d.ts +144 -50
- package/dist/types/rsc/types.d.ts +9 -0
- package/dist/types/segment-system.d.ts +7 -0
- package/dist/types/server/context.d.ts +155 -33
- package/dist/types/server/cookie-store.d.ts +33 -0
- package/dist/types/server/handle-store.d.ts +52 -1
- package/dist/types/server/request-context.d.ts +210 -63
- package/dist/types/server/request-headers.d.ts +44 -0
- package/dist/types/server.d.ts +1 -0
- package/dist/types/ssr/index.d.ts +9 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- 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-loader.d.ts +18 -3
- package/dist/types/testing/run-transition-when.d.ts +44 -69
- package/dist/types/testing/serve-shell-request.d.ts +133 -0
- package/dist/types/testing/shell-status.d.ts +44 -19
- package/dist/types/transition-when-ref.d.ts +79 -0
- package/dist/types/types/cache-types.d.ts +15 -3
- package/dist/types/types/handler-context.d.ts +14 -1
- 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 +21 -18
- package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1005 -105
- package/package.json +1 -1
- package/skills/breadcrumbs/SKILL.md +17 -7
- package/skills/cache-guide/SKILL.md +175 -76
- package/skills/caching/SKILL.md +361 -33
- package/skills/catalog.json +1 -1
- package/skills/client-urls/SKILL.md +12 -7
- package/skills/cloudflare/SKILL.md +15 -0
- package/skills/comparison/references/framework-comparison.md +52 -37
- package/skills/defer-hydration/SKILL.md +50 -30
- package/skills/deployment-caching/SKILL.md +5 -2
- package/skills/document-cache/SKILL.md +32 -2
- package/skills/handler-use/SKILL.md +2 -0
- package/skills/hooks/SKILL.md +3 -1
- package/skills/hooks/handle-and-actions.md +3 -1
- package/skills/hooks/navigation.md +23 -2
- package/skills/hooks/state.md +101 -7
- package/skills/intercept/SKILL.md +29 -12
- package/skills/loader/SKILL.md +197 -44
- package/skills/middleware/SKILL.md +34 -9
- package/skills/migrate-nextjs/SKILL.md +68 -37
- package/skills/migrate-react-router/SKILL.md +2 -1
- package/skills/migrate-react-router/component-migration.md +3 -1
- package/skills/migrate-react-router/data-and-actions.md +3 -1
- package/skills/migrate-react-router/route-mapping.md +7 -4
- package/skills/observability/SKILL.md +65 -0
- package/skills/parallel/SKILL.md +13 -8
- package/skills/ppr/SKILL.md +715 -371
- package/skills/prerender/SKILL.md +30 -17
- package/skills/rango/SKILL.md +72 -34
- package/skills/route/SKILL.md +14 -7
- package/skills/router-setup/SKILL.md +4 -2
- package/skills/scripts/SKILL.md +8 -1
- package/skills/server-actions/SKILL.md +11 -0
- package/skills/shell-manifest/SKILL.md +21 -12
- package/skills/testing/SKILL.md +49 -43
- package/skills/testing/cache-prerender.md +15 -14
- package/skills/testing/client-components.md +45 -3
- package/skills/testing/loader.md +19 -1
- 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 +202 -0
- package/skills/testing/server-actions.md +9 -3
- package/skills/theme/SKILL.md +56 -4
- package/skills/typesafety/env-and-bindings.md +18 -2
- package/skills/use-cache/SKILL.md +91 -23
- package/skills/vercel/SKILL.md +14 -2
- package/skills/view-transitions/SKILL.md +150 -29
- package/src/__augment-tests__/augmented.check.ts +23 -0
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/event-controller.ts +4 -4
- package/src/browser/history-state.ts +75 -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 +9 -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 +62 -2
- package/src/cache/cache-policy.ts +31 -4
- package/src/cache/cache-runtime.ts +209 -129
- package/src/cache/cache-scope.ts +573 -69
- package/src/cache/cache-tag.ts +475 -19
- 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 +1094 -376
- 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 +55 -40
- package/src/cache/document-cache.ts +53 -7
- package/src/cache/handle-snapshot.ts +53 -9
- package/src/cache/index.ts +2 -0
- package/src/cache/invalidation-order.ts +84 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/request-tag-mask.ts +147 -0
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +249 -277
- package/src/cache/tag-invalidation.ts +109 -18
- package/src/cache/types.ts +274 -102
- package/src/cache/vercel/vercel-cache-store.ts +561 -74
- 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 +173 -110
- package/src/route-definition/dsl-helpers.ts +6 -3
- 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 +42 -5
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +30 -8
- package/src/router/loader-resolution.ts +89 -33
- package/src/router/logging.ts +5 -3
- package/src/router/match-api.ts +89 -42
- package/src/router/match-context.ts +5 -0
- package/src/router/match-handlers.ts +14 -29
- package/src/router/match-middleware/background-revalidation.ts +3 -5
- package/src/router/match-middleware/cache-lookup.ts +52 -19
- package/src/router/match-middleware/cache-store.ts +36 -10
- package/src/router/match-result.ts +1 -0
- package/src/router/middleware-types.ts +6 -1
- package/src/router/middleware.ts +20 -4
- package/src/router/navigation-snapshot.ts +6 -5
- package/src/router/prerender-match.ts +10 -0
- package/src/router/request-classification.ts +7 -4
- package/src/router/router-options.ts +4 -1
- package/src/router/segment-resolution/fresh.ts +18 -13
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +366 -70
- package/src/router/segment-resolution/loader-mask.ts +3 -4
- package/src/router/segment-resolution/loader-snapshot.ts +215 -126
- package/src/router/segment-resolution/mask-nested.ts +161 -42
- package/src/router/segment-resolution/revalidation.ts +27 -13
- 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 +47 -3
- package/src/rsc/handler.ts +24 -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 +10 -32
- package/src/rsc/response-cache-serve.ts +69 -78
- package/src/rsc/response-route-handler.ts +2 -2
- package/src/rsc/rsc-rendering.ts +712 -338
- package/src/rsc/server-action.ts +8 -20
- package/src/rsc/shell-build-manifest.ts +49 -29
- package/src/rsc/shell-capture-constants.ts +182 -12
- package/src/rsc/shell-capture.ts +1126 -612
- package/src/rsc/shell-serve.ts +331 -85
- package/src/rsc/ssr-setup.ts +3 -2
- package/src/rsc/types.ts +9 -0
- package/src/segment-system.tsx +40 -7
- package/src/server/context.ts +328 -72
- package/src/server/cookie-store.ts +184 -133
- package/src/server/handle-store.ts +245 -27
- package/src/server/request-context.ts +364 -100
- package/src/server/request-headers.ts +134 -0
- package/src/server.ts +4 -0
- package/src/ssr/index.tsx +96 -37
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/testing/dispatch.ts +4 -7
- package/src/testing/e2e/index.ts +1 -0
- package/src/testing/flight.entry.ts +14 -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 +67 -5
- package/src/testing/run-transition-when.ts +109 -172
- package/src/testing/serve-shell-request.ts +381 -0
- package/src/testing/shell-status.ts +73 -47
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/transition-when-ref.ts +220 -0
- package/src/types/cache-types.ts +15 -3
- package/src/types/handler-context.ts +14 -1
- 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 +21 -18
- package/src/vite/discovery/discover-routers.ts +3 -0
- package/src/vite/discovery/shell-prerender-phase.ts +9 -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 +89 -9
- package/dist/types/rsc/transition-gate.d.ts +0 -23
- package/src/rsc/transition-gate.ts +0 -86
|
@@ -20,8 +20,9 @@ The handler code and its imports are removed from the production bundle.
|
|
|
20
20
|
|
|
21
21
|
## Not this skill if…
|
|
22
22
|
|
|
23
|
-
- You want a cached HTML shell captured at runtime, with holes
|
|
24
|
-
|
|
23
|
+
- You want a cached HTML shell captured at runtime, with holes staying live
|
|
24
|
+
per request (loaders without `ssr: false` run per request; `ssr: false`
|
|
25
|
+
loaders bake) — see `/ppr`.
|
|
25
26
|
- You want runtime segment caching with TTL/SWR — that is the `cache()` DSL:
|
|
26
27
|
see `/caching`. Prerender is the same cache filled at build time.
|
|
27
28
|
- You are unsure which cache layer you need — start at `/cache-guide`.
|
|
@@ -244,15 +245,14 @@ In client and SSR environments, ALL prerender handlers are always stubbed.
|
|
|
244
245
|
## Sub-use Semantics
|
|
245
246
|
|
|
246
247
|
Everything inside the path's use() callback is part of the pre-rendered route
|
|
247
|
-
subtree
|
|
248
|
-
the exception — they stay live:
|
|
248
|
+
subtree and gets pre-rendered. Loaders are the exception — they stay live:
|
|
249
249
|
|
|
250
250
|
```typescript
|
|
251
251
|
path("/blog/:slug", BlogPost, { name: "blog.post" }, () => [
|
|
252
|
-
layout(<PostLayout />, () => [ // inside
|
|
252
|
+
layout(<PostLayout />, () => [ // inside the route -> pre-rendered
|
|
253
253
|
loader(PostMetaLoader), // live at runtime, bundled normally
|
|
254
254
|
]),
|
|
255
|
-
parallel({ "@sidebar": BlogSidebar }), // inside
|
|
255
|
+
parallel({ "@sidebar": BlogSidebar }), // inside the route -> pre-rendered
|
|
256
256
|
])
|
|
257
257
|
```
|
|
258
258
|
|
|
@@ -327,9 +327,16 @@ implicitly re-run outer prerender-derived handlers/layouts.
|
|
|
327
327
|
## Prerender + PPR Build Shells
|
|
328
328
|
|
|
329
329
|
A `Prerender` page may also declare `ppr` on the path option. The build still
|
|
330
|
-
stores the Flight payload first. After that,
|
|
331
|
-
|
|
332
|
-
|
|
330
|
+
stores the Flight payload first. After that, the build-time shell capture
|
|
331
|
+
tries to bake the HTML shell for each generated URL so the first document
|
|
332
|
+
request can be an `x-rango-shell: HIT`. A route partitioned by
|
|
333
|
+
`cache({ key })` or a store `keyGenerator` never serves that build shell (the
|
|
334
|
+
build captured one partition); each partition captures at runtime, a
|
|
335
|
+
once-per-route warning says so, and a `keyGenerator` that returns the default
|
|
336
|
+
key unchanged keeps it. When the route's `cache()` scope (its own, or one
|
|
337
|
+
inherited from a layout) refuses a request — `cache(false)`, or a
|
|
338
|
+
`condition()` that returns false — that request gets no shell at all, the
|
|
339
|
+
build shell included: it renders like a cache miss.
|
|
333
340
|
|
|
334
341
|
That shell capture is request-shaped enough to run middleware safely:
|
|
335
342
|
|
|
@@ -362,14 +369,15 @@ entry STILL serves and a runtime recapture is scheduled that upgrades it in plac
|
|
|
362
369
|
(SWR is the upgrade path from build entry → fresher runtime entry). Because the
|
|
363
370
|
`Prerender` handler is evicted from the production bundle, that recapture never
|
|
364
371
|
re-runs the handler — it replays the same build-time segments and only refreshes
|
|
365
|
-
`cache()`-scoped data baked into the shell
|
|
366
|
-
`
|
|
372
|
+
what the capture reads again: `cache()`-scoped data baked into the shell, and
|
|
373
|
+
the data of `ssr: false` loaders, which run at every capture. **If the shell
|
|
374
|
+
bakes neither, `ttl` has nothing to refresh** — reach for `updateTag` (or a
|
|
367
375
|
redeploy) instead of a shorter `ttl`.
|
|
368
376
|
|
|
369
|
-
`ctx.dynamic()` opts a request off the shell
|
|
377
|
+
`ctx.dynamic()` opts a request off the shell ONLY. A `Prerender` route has
|
|
370
378
|
no live handler to fall back to (it was evicted), so a `dynamic()` request still
|
|
371
|
-
serves the build-baked segments — fresh loaders
|
|
372
|
-
|
|
379
|
+
serves the build-baked segments — fresh loaders, not a fresh handler render.
|
|
380
|
+
There is no "fully dynamic" render for a prerendered route.
|
|
373
381
|
|
|
374
382
|
## Dev Mode
|
|
375
383
|
|
|
@@ -635,6 +643,11 @@ Passthrough entries are logged distinctly:
|
|
|
635
643
|
Loaders on pre-rendered routes run at request time. They are bundled normally
|
|
636
644
|
and need `cache()` for caching. Do not use build-only APIs in loaders.
|
|
637
645
|
|
|
646
|
+
The exception is an `ssr: false` loader on a `Prerender` + `ppr` route: the
|
|
647
|
+
build-time shell capture runs it and bakes its settled data into the shell,
|
|
648
|
+
and a shell HIT runs it only when its return carries promises (`/ppr` → "The
|
|
649
|
+
bake lane"). The Flight payload collection itself still skips loaders.
|
|
650
|
+
|
|
638
651
|
### Build-time handle data is frozen
|
|
639
652
|
|
|
640
653
|
Handle values pushed via `ctx.use()` DURING pre-rendering (handler pushes at
|
|
@@ -782,8 +795,8 @@ At runtime, the cache-lookup middleware checks `ctx.isIntercept`:
|
|
|
782
795
|
intercept-resolution middleware runs live. This handles `when` config conditions
|
|
783
796
|
that prevented pre-rendering.
|
|
784
797
|
|
|
785
|
-
The `when` config selector receives an `InterceptSelectorContext`
|
|
786
|
-
|
|
798
|
+
The `when` config selector receives an `InterceptSelectorContext` whose `from`
|
|
799
|
+
location (the page being left) is unknown at build time. All intercepts are pre-rendered unconditionally;
|
|
787
800
|
`when` is evaluated at runtime by the intercept-resolution middleware.
|
|
788
801
|
|
|
789
802
|
### Example: Pre-rendered route with intercept
|
|
@@ -807,7 +820,7 @@ layout(ShopLayout, () => [
|
|
|
807
820
|
"@modal",
|
|
808
821
|
".detail",
|
|
809
822
|
<ProductModal />,
|
|
810
|
-
{ when: ({ from }) => from.pathname === "/shop" },
|
|
823
|
+
{ when: ({ from }) => from.url.pathname === "/shop" },
|
|
811
824
|
() => [loader(ProductLoader)],
|
|
812
825
|
),
|
|
813
826
|
])
|
package/skills/rango/SKILL.md
CHANGED
|
@@ -26,7 +26,9 @@ with the shape, then pick a primitive.
|
|
|
26
26
|
inside a cached render. They run **in parallel** right after middleware and
|
|
27
27
|
**stream**, so data latency overlaps first paint instead of blocking it (a
|
|
28
28
|
cache hit streams UI instantly while loaders resolve fresh alongside). Opt into
|
|
29
|
-
caching explicitly. See `/loader` → "Parallel and streaming".
|
|
29
|
+
caching explicitly. See `/loader` → "Parallel and streaming". The one
|
|
30
|
+
exception is a `ppr` route's `ssr: false` loader: it bakes into the shell
|
|
31
|
+
and runs on a shell HIT only when its return carries promises (`/ppr`).
|
|
30
32
|
- **One identity, one store** — loaders, handles, cached fns, and actions are all
|
|
31
33
|
`path#export`; all caches share one store. Entries expire by TTL/SWR, and are
|
|
32
34
|
tagged via `cache({ tags })` or runtime `cacheTag(...tags)`; built-in stores
|
|
@@ -68,8 +70,9 @@ Reach for the next rung only when the one above doesn't fit — the higher rungs
|
|
|
68
70
|
are immune to partial-revalidation staleness by construction.
|
|
69
71
|
|
|
70
72
|
1. **A loader** (`loader()` + `useLoader()`). Loaders resolve fresh on every
|
|
71
|
-
pass — full renders, action revalidations, cache hits
|
|
72
|
-
|
|
73
|
+
pass — full renders, action revalidations, cache hits, PPR shell HITs (on a
|
|
74
|
+
`ppr` route, keep it off `ssr: false`, which bakes it into the shell).
|
|
75
|
+
Nothing to keep in sync. If the data can be a loader, make it a loader.
|
|
73
76
|
2. **Middleware `ctx.set()`**. Route middleware wraps every render pass,
|
|
74
77
|
including post-action revalidation and PE re-renders, so its variables are
|
|
75
78
|
never stale. Right for request-shaped context: auth, session, locale.
|
|
@@ -114,7 +117,7 @@ stated, greppable contract.
|
|
|
114
117
|
| guarantee loader output in the SSR HTML | `loader(L, { ssr: false })` | /loader |
|
|
115
118
|
| pre-render a route at build time | `Prerender(...)` wrapper | /prerender |
|
|
116
119
|
| feed live loaders from a cached shell | replayed handle + `ctx.rendered()` | /shell-manifest |
|
|
117
|
-
| cache
|
|
120
|
+
| cache HTML shell, live loaders as holes | `ppr` path option | /ppr |
|
|
118
121
|
| choose in-function vs CDN caching | deployment cache boundary | /deployment-caching |
|
|
119
122
|
| stream SSE / upgrade a WebSocket | `path.stream()` / `path.any()` | /streams-and-websockets |
|
|
120
123
|
|
|
@@ -123,16 +126,29 @@ stated, greppable contract.
|
|
|
123
126
|
- `path()`/`include()` are always visible in `urls()`; config helpers are extractable.
|
|
124
127
|
- **Cache decides freshness; `revalidate()` decides client-update.** Orthogonal; compose.
|
|
125
128
|
- Loaders resolve fresh every request (even inside `cache()`) and never run twice/request.
|
|
129
|
+
The exception is a `ppr` route's `ssr: false` loader: it bakes into the shell
|
|
130
|
+
and runs on a shell HIT only when its return carries promises.
|
|
126
131
|
- **The consumption-lane rule.** For every shared artifact (`cache()`,
|
|
127
132
|
`"use cache"`, the PPR shell): server-side handler consumption
|
|
128
|
-
(`await ctx.use(loader)`) yields a BAKED copy —
|
|
129
|
-
(`cookies()`/`headers()
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
(`await ctx.use(loader)`) yields a BAKED copy — the capture-time value
|
|
134
|
+
freezes into the shared artifact. Identity reads (`cookies()`/`headers()`/
|
|
135
|
+
`ctx.request.headers`, a `{ cache: false }` variable) inside that loader
|
|
136
|
+
are permitted under a route `cache()` (a documented footgun; see
|
|
137
|
+
`/caching` → "Cache purity & tainted objects"). They throw inside
|
|
138
|
+
`"use cache"` (when the cached function is the first to read the loader in
|
|
139
|
+
the request; an already-started loader's value is stored unchecked) and REFUSE a PPR capture: the route serves uncached until
|
|
140
|
+
the read moves to a live loader. Client-side consumption
|
|
141
|
+
(`useLoader` in a `"use client"` component) is the LIVE lane. DSL
|
|
142
|
+
`loader()` segments follow their PPR lane (only `ssr: false` bakes; see
|
|
143
|
+
`/ppr` → The loader lane rule).
|
|
144
|
+
- **A PPR shell HIT never runs a handler.** Everything a handler produces
|
|
145
|
+
(promises it passes under `<Suspense>`, async server components, handle
|
|
146
|
+
pushes, loaders it awaits) is shell material, as under `cache()`; a HIT
|
|
147
|
+
runs middleware and live loaders only (an `ssr: false` loader runs there
|
|
148
|
+
only when its return carries promises). Live data belongs in a loader without
|
|
149
|
+
`ssr: false`, read with `useLoader` under `loading()` or an inline
|
|
150
|
+
`<Suspense>`.
|
|
151
|
+
- Inside `"use cache"`: `cookies()`/`headers()`/`ctx.request.headers` and `ctx` side-effects
|
|
136
152
|
(`set`/`header`/`setTheme`/`onResponse`/`setLocationState`) throw; `ctx.use(Handle)`
|
|
137
153
|
is captured on miss and replayed on hit. A non-cacheable variable read
|
|
138
154
|
(`createVar({ cache: false })`) throws too — see the correctness bullet below.
|
|
@@ -149,6 +165,23 @@ stated, greppable contract.
|
|
|
149
165
|
- Nested caches: the outer cache window bounds the inner — an inner shorter TTL
|
|
150
166
|
only applies when the enclosing cache recomputes; put a value in a loader if it
|
|
151
167
|
must be fresher. See `/cache-guide` → "Combining Both".
|
|
168
|
+
- **A predicate runs where its effect lands.** A server-tree `revalidate()`
|
|
169
|
+
decides what the server re-sends, so it runs on the server; an `intercept({
|
|
170
|
+
when })` selector decides what the server renders, so it runs on the server;
|
|
171
|
+
a `clientUrls()` `revalidate()` and `transition({ when })` decide a browser
|
|
172
|
+
commit, so they run in the browser. A browser predicate sees URLs, params,
|
|
173
|
+
route names, history state and action data, never `get()`, `env` or handler
|
|
174
|
+
state; in a server `urls()` it is written inline (the build hoists it into a
|
|
175
|
+
client module) or imported from a `"use client"` module (`/view-transitions`).
|
|
176
|
+
- **Vocabulary.** `revalidate()` keeps React Router's `shouldRevalidate`
|
|
177
|
+
dialect (`currentUrl`/`nextUrl`, `currentParams`/`nextParams`). Navigation
|
|
178
|
+
selectors (`intercept({ when })`, `transition({ when })`) use `from`/`to`
|
|
179
|
+
locations (`{ url, params, routeName }`, plus `state` in the browser).
|
|
180
|
+
- **A throwing predicate yields the conservative default.** For
|
|
181
|
+
`transition({ when })` that is no hold (an urgent commit) plus
|
|
182
|
+
`console.error`; for a `revalidate()` it is the default decision it would
|
|
183
|
+
have deferred to, logged; for an `intercept({ when })` selector it is no
|
|
184
|
+
intercept (the full page renders), logged with the route name.
|
|
152
185
|
|
|
153
186
|
## Don't confuse
|
|
154
187
|
|
|
@@ -162,17 +195,22 @@ stated, greppable contract.
|
|
|
162
195
|
Same words, different jobs — this is the most common source of the
|
|
163
196
|
`revalidate()`-is-caching misread.
|
|
164
197
|
|
|
165
|
-
| You may know | Maps to Rango
|
|
166
|
-
| --------------------------------------- |
|
|
167
|
-
| Next.js `export const revalidate = N` | **
|
|
168
|
-
| Next.js `revalidateTag` / `updateTag` | **
|
|
169
|
-
| React Router / Remix `shouldRevalidate` | **
|
|
170
|
-
| HTTP `Cache-Control` / ISR | Deployment layer
|
|
171
|
-
| Next.js PPR (partial prerendering) | HTML shell layer
|
|
172
|
-
| Remix/RR `loader` | live data
|
|
198
|
+
| You may know | Maps to Rango | Watch out |
|
|
199
|
+
| --------------------------------------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
200
|
+
| Next.js `export const revalidate = N` | **Value freshness** (cache) | Same word, opposite meaning. Next's `revalidate` is time-based cache expiry; Rango's `revalidate()` is **partial-render selection**. Use `cache({ ttl })` for the Next behavior. |
|
|
201
|
+
| Next.js `revalidateTag` / `updateTag` | **Value freshness** (cache) | Cache busting by tag. Tag via `cache({ tags })` / `cacheTag(...tags)`; invalidate with `updateTag(...tags)` (awaitable, read-your-own-writes) or `revalidateTag(...tags)` (background, non-blocking). Built-in stores index by tag. No `revalidatePath` (path-based busting); use tags. |
|
|
202
|
+
| React Router / Remix `shouldRevalidate` | **Partial-render selection** | This is the correct mental model for Rango's `revalidate()`. |
|
|
203
|
+
| HTTP `Cache-Control` / ISR | Deployment layer | Complete-response deployment layer. A CDN hit bypasses Rango entirely; the store-backed middleware does not. See `/deployment-caching` and `/document-cache`. |
|
|
204
|
+
| Next.js PPR (partial prerendering) | HTML shell layer | Same React primitive, different transport: Rango serves shells in-function after middleware. Ordinary `ppr` captures at runtime; `Prerender + ppr` captures at build. See `/ppr`, `/prerender`, and `/deployment-caching`. |
|
|
205
|
+
| Remix/RR `loader` | live data | Like Rango loaders, fresh per request — but Rango loaders run in parallel and stream (latency overlaps first paint), and can opt into caching on demand. |
|
|
173
206
|
|
|
174
|
-
|
|
175
|
-
|
|
207
|
+
Next.js PPR and Rango's `ppr` also differ in the hole model: in Rango only
|
|
208
|
+
loaders make holes. A handler's own promise under `<Suspense>` bakes into the
|
|
209
|
+
shell, as under `cache()`.
|
|
210
|
+
|
|
211
|
+
See `/cache-guide` for the cache decision guide, `/loader` and `/route` for
|
|
212
|
+
`revalidate()` (partial-render selection), and `/document-cache` for the edge
|
|
213
|
+
layer.
|
|
176
214
|
|
|
177
215
|
## Canonical shape
|
|
178
216
|
|
|
@@ -277,18 +315,18 @@ Grouped by concern — read when you need to…
|
|
|
277
315
|
|
|
278
316
|
**Data & caching** — fetch, mutate, and cache:
|
|
279
317
|
|
|
280
|
-
| Skill | Description
|
|
281
|
-
| --------------------- |
|
|
282
|
-
| `/loader` | Data loaders with `createLoader()` and `revalidate()`
|
|
283
|
-
| `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation
|
|
284
|
-
| `/caching` | Segment caching with memory or KV stores
|
|
285
|
-
| `/use-cache` | Function-level caching with `"use cache"` directive
|
|
286
|
-
| `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide
|
|
287
|
-
| `/document-cache` | Store-backed complete-response middleware using Cache-Control policy
|
|
288
|
-
| `/deployment-caching` | Choose between in-function caches, store-backed responses, and an external CDN cache
|
|
289
|
-
| `/ppr` | PPR shell caching: cached shell served instantly, live holes resumed — a hole is a `loading()`
|
|
290
|
-
| `/prerender` | Pre-render route segments at build time (Passthrough live fallback)
|
|
291
|
-
| `/shell-manifest` | Replayed handles as cache metadata read by live loaders (frozen shell, batched live holes)
|
|
318
|
+
| Skill | Description |
|
|
319
|
+
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
320
|
+
| `/loader` | Data loaders with `createLoader()` and `revalidate()` |
|
|
321
|
+
| `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
|
|
322
|
+
| `/caching` | Segment caching with memory or KV stores |
|
|
323
|
+
| `/use-cache` | Function-level caching with `"use cache"` directive |
|
|
324
|
+
| `/cache-guide` | When to use `cache()` vs `"use cache"` — differences and decision guide |
|
|
325
|
+
| `/document-cache` | Store-backed complete-response middleware using Cache-Control policy |
|
|
326
|
+
| `/deployment-caching` | Choose between in-function caches, store-backed responses, and an external CDN cache |
|
|
327
|
+
| `/ppr` | PPR shell caching: cached shell served instantly, live holes resumed — a hole is a live loader read under `loading()` or an inline `<Suspense>`; handler output bakes |
|
|
328
|
+
| `/prerender` | Pre-render route segments at build time (Passthrough live fallback) |
|
|
329
|
+
| `/shell-manifest` | Replayed handles as cache metadata read by live loaders (frozen shell, batched live holes) |
|
|
292
330
|
|
|
293
331
|
**Client & presentation** — build the client-side UX:
|
|
294
332
|
|
package/skills/route/SKILL.md
CHANGED
|
@@ -329,12 +329,17 @@ cache({ ttl: 60 }, () => [
|
|
|
329
329
|
]);
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
-
The same read throws inside a `"use cache"` function body, where the value
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
332
|
+
The same read throws inside a `"use cache"` function body, where the value would
|
|
333
|
+
be stored without being part of the key — pass it in as an argument instead. The
|
|
334
|
+
guard does not follow derived values (a string copied out of `Session` and
|
|
335
|
+
cached elsewhere is not tracked). Under `cache()`, loaders are exempt — a DSL
|
|
336
|
+
loader re-runs on every hit — but a `"use cache"` function a loader calls is
|
|
337
|
+
not, and neither is a loader bound with its own `cache()` without a `key()` (any
|
|
338
|
+
`key()` switches the check off, so it must include the value; `/loader` → "Cache
|
|
339
|
+
Key"). On a `ppr` route the exemption does not cover a loader the shell capture
|
|
340
|
+
waits for (`ssr: false`, or awaited by a handler via `ctx.use()`): the read
|
|
341
|
+
refuses the capture. Cacheable vars (the default) can be read freely inside
|
|
342
|
+
cache scopes. See `/cache-guide` → "Context Variable Cache Safety".
|
|
338
343
|
|
|
339
344
|
### Revalidation Contracts for Handler Data
|
|
340
345
|
|
|
@@ -543,7 +548,7 @@ interface HandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch = {}> {
|
|
|
543
548
|
build: boolean; // true while pre-rendering at build time
|
|
544
549
|
dev: boolean; // true under Vite dev
|
|
545
550
|
dynamic(): void; // opt this request out of PPR shell capture (see /ppr)
|
|
546
|
-
theme?: Theme; // plus setTheme?() — only when the router enables themes (/theme)
|
|
551
|
+
readonly theme?: Theme; // plus setTheme?() — only when the router enables themes; a read is guarded like cookies() (/theme)
|
|
547
552
|
}
|
|
548
553
|
```
|
|
549
554
|
|
|
@@ -603,6 +608,8 @@ whether the routes mount.
|
|
|
603
608
|
|
|
604
609
|
A route can configure its own `transition()` — the wrap goes around the route's component itself (routes are leaves; they have no separate default outlet channel). If the route component renders a `<ParallelOutlet />` directly, that slot remains inside the route's VT subtree, so prefer mounting parallel slots in a layout when combining intercept modals with route-level transitions. See [skills/view-transitions](../view-transitions/SKILL.md) for examples and the wrap-location rules across layouts, routes, and slots.
|
|
605
610
|
|
|
611
|
+
`transition({ when })` and `revalidate()` decide different things: `when` (a browser predicate) gates the hold, `revalidate()` decides whether the route segment is re-sent. A `revalidate()` returning false keeps the client's segment on a navigation whatever `when` returns, including a `ppr` navigation replayed from the shell.
|
|
612
|
+
|
|
606
613
|
## Handler-attached `.use`
|
|
607
614
|
|
|
608
615
|
Page handlers can carry their own loader, middleware, error boundaries, parallels, and other defaults via a `.use` callback — so the page is self-contained and reusable across mount sites without re-wiring the same items.
|
|
@@ -163,7 +163,7 @@ interface RangoOptions<TEnv> {
|
|
|
163
163
|
viewTransition?: "auto" | false; // Router-placed <ViewTransition> default (/view-transitions)
|
|
164
164
|
warmup?: boolean; // Connection warmup after idle (default true)
|
|
165
165
|
strictMode?: boolean; // Hydrate inside <React.StrictMode> (default true)
|
|
166
|
-
stateCookiePrefix?: string; // Prefix of the state cookie `{prefix}_{routerId}` (default "rango-state")
|
|
166
|
+
stateCookiePrefix?: string; // Prefix of the state cookie `{prefix}_{routerId}` and the fresh-reads cookie `{prefix}-fresh` (default "rango-state")
|
|
167
167
|
|
|
168
168
|
// --- Request handling ---
|
|
169
169
|
nonce?: (
|
|
@@ -752,7 +752,9 @@ the check.
|
|
|
752
752
|
render counts in development. Production behavior is unchanged.
|
|
753
753
|
- `stateCookiePrefix`: rename the state cookie that keys the client caches. The
|
|
754
754
|
`_{routerId}` suffix is always kept so sibling apps on one origin do not
|
|
755
|
-
collide.
|
|
755
|
+
collide. The prefix also names the fresh-reads cookie `{prefix}-fresh`
|
|
756
|
+
(`rango-state-fresh` by default) that `updateTag()`/`revalidateTag()` set
|
|
757
|
+
on a store with per-isolate memos; a consent-manager list needs both names.
|
|
756
758
|
- `nonce: () => true`: generate a CSP nonce per request and apply it to the
|
|
757
759
|
router's inline scripts. Return a string to supply your own, or
|
|
758
760
|
`false`/`""` to skip it for one request. Middleware reads it with
|
package/skills/scripts/SKILL.md
CHANGED
|
@@ -114,7 +114,10 @@ during navigation.
|
|
|
114
114
|
> means it is silently dropped. If a loader must contribute an inline script
|
|
115
115
|
> to the document, register it `loader(Def, { ssr: false })` so the
|
|
116
116
|
> document render awaits the push; otherwise push from a handler (or use an
|
|
117
|
-
> `async` config, which stays reactive).
|
|
117
|
+
> `async` config, which stays reactive). On a `ppr` route a loader without
|
|
118
|
+
> `ssr: false` never runs during the shell capture, so its inline/ordered
|
|
119
|
+
> script is dropped on every shell HIT; with `ssr: false` the push bakes into
|
|
120
|
+
> the shell.
|
|
118
121
|
|
|
119
122
|
**Nonce caveat for soft-nav async.** The "nonce is applied automatically" claim
|
|
120
123
|
holds for DOCUMENT-RENDERED scripts (they carry the nonce in the SSR HTML). An
|
|
@@ -185,6 +188,10 @@ Otherwise allow the vendor hosts. For GTM/GA4 (Google's wildcards): `script-src
|
|
|
185
188
|
`frame-src https://*.googletagmanager.com` for the GTM `<noscript>` iframe. See
|
|
186
189
|
[Google's CSP guide](https://developers.google.com/tag-platform/security/guides/csp).
|
|
187
190
|
|
|
191
|
+
A per-request nonce also takes a `ppr` route off its shell: a shared shell
|
|
192
|
+
cannot carry one request's nonce, so the route renders normally (no shell) and
|
|
193
|
+
warns once per key. See `/ppr` → "What always renders without a shell".
|
|
194
|
+
|
|
188
195
|
## Not covered (do it yourself)
|
|
189
196
|
|
|
190
197
|
- **`onLoad` / `onReady` / `onError`** — callbacks can't cross the server handle
|
|
@@ -37,6 +37,17 @@ Actions mutate state; route handlers and loaders read the latest state. After
|
|
|
37
37
|
an action finishes, Rango performs a server-side revalidation render for the
|
|
38
38
|
matched route so the UI receives fresh segment output and loader data.
|
|
39
39
|
|
|
40
|
+
Segments under a route `cache()` re-render fresh on that render (actions skip
|
|
41
|
+
the route cache lookup and write). A loader with its own `cache()` and a
|
|
42
|
+
`"use cache"` read return their stored entry, and a `ppr` route's stored shell
|
|
43
|
+
is not evicted, unless the action invalidates their tags.
|
|
44
|
+
`await updateTag(...tags)` purges them before the revalidation render, so the
|
|
45
|
+
action's own response reads fresh (`revalidateTag()` gives that render fresh
|
|
46
|
+
reads too; only its durable write runs in the background). On a store that keeps
|
|
47
|
+
per-isolate memos (`CFCacheStore`, `VercelCacheStore`) the response also sets
|
|
48
|
+
the fresh-reads cookie (`rango-state-fresh` by default), so the same user's next
|
|
49
|
+
requests skip those memos (see `/caching` → "The fresh-reads cookie").
|
|
50
|
+
|
|
40
51
|
The main control point is `revalidate((ctx) => ...)` on the segment that owns
|
|
41
52
|
the data. Match specific actions by imported reference with `ctx.isAction()`;
|
|
42
53
|
use raw `actionId` only when you intentionally need path or directory matching.
|
|
@@ -9,8 +9,8 @@ argument-hint:
|
|
|
9
9
|
Use this when a cached or prerendered shell has dynamic holes, and the live
|
|
10
10
|
data layer needs to know **what the shell actually contains** — which
|
|
11
11
|
products, which slots, which keys. The frozen render describes itself
|
|
12
|
-
through a handle; loaders (
|
|
13
|
-
exactly the dynamic data the shell needs, in one batch.
|
|
12
|
+
through a handle; live loaders (no `ssr: false`) read that description and
|
|
13
|
+
fetch exactly the dynamic data the shell needs, in one batch.
|
|
14
14
|
|
|
15
15
|
Canonical case: a prerendered product list where prices must stay live.
|
|
16
16
|
|
|
@@ -32,16 +32,19 @@ batched.
|
|
|
32
32
|
## The mechanism (three features composed)
|
|
33
33
|
|
|
34
34
|
1. **Handles record data at render time.** The handler pushes to a handle
|
|
35
|
-
(`ctx.use(Handle)`) while it renders — at build time for `Prerender`, on
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
35
|
+
(`ctx.use(Handle)`) while it renders — at build time for `Prerender`, on the
|
|
36
|
+
cache miss for `cache()`. (Loader bodies can push handles too — see `/loader`
|
|
37
|
+
— but a live loader's push is request-time and is NOT part of the replayed
|
|
38
|
+
artifact; a manifest handle must be pushed by the code that gets frozen with
|
|
39
|
+
the shell. An `ssr: false` loader on a `ppr` route is the exception: its
|
|
40
|
+
settled pushes are recorded with the shell. On a HIT, one whose return has no
|
|
41
|
+
promises does not run (unless the capture marked it to run), so the recorded
|
|
42
|
+
pushes are what the page shows; one whose return carries promises runs on
|
|
43
|
+
every HIT, and its pushes replace the recorded ones — `/ppr` "On a shell
|
|
44
|
+
HIT".)
|
|
42
45
|
2. **Replay on every hit.** Handle data is stored with the Flight payload
|
|
43
|
-
and replayed into the handle store on cache
|
|
44
|
-
does not re-run, but its pushes do.
|
|
46
|
+
and replayed into the handle store on cache, prerender and `ppr` shell
|
|
47
|
+
hits — handler code does not re-run, but its pushes do.
|
|
45
48
|
3. **Loaders read after the render barrier.** A DSL loader can
|
|
46
49
|
`await ctx.rendered()` (waits for all non-loader segments to settle —
|
|
47
50
|
fresh render or replay alike), then `ctx.get(Handle)` returns the
|
|
@@ -185,7 +188,13 @@ const prices = await runLoader(PriceLoader, {
|
|
|
185
188
|
|
|
186
189
|
This tests the loader's post-barrier logic. The real
|
|
187
190
|
push → store → replay → barrier wiring is covered at the e2e tier (dev +
|
|
188
|
-
production), like every cache-path behavior.
|
|
191
|
+
production), like every cache-path behavior. On a `ppr` route,
|
|
192
|
+
`serveShellRequest(router, url, { cacheStore })` from
|
|
193
|
+
`@rangojs/router/testing/flight` runs the real capture and HIT in a unit test
|
|
194
|
+
(react-server Vitest): serve the URL twice with the same store; the second
|
|
195
|
+
response is a HIT, and its `flight` carries the live loader's output for the
|
|
196
|
+
replayed ids. Call `resetShellTestState()` in `beforeEach`. Recipe:
|
|
197
|
+
`/testing` → `serve-shell-request.md`.
|
|
189
198
|
|
|
190
199
|
## Related
|
|
191
200
|
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: testing
|
|
3
|
-
description: Test @rangojs/router apps — unit (loaders, middleware, server actions, handles, transition/clientUrls predicates), client components (renderRoute), RSC/Flight (renderServerTree, renderHandler), integration (dispatch), and e2e (dev+prod parity, progressive enhancement). Use when writing a unit test for a loader, middleware, action, or handler, setting up vitest for a Rango app, or testing a route end-to-end in dev and production.
|
|
3
|
+
description: Test @rangojs/router apps — unit (loaders, middleware, server actions, handles, transition/clientUrls predicates), client components (renderRoute), RSC/Flight (renderServerTree, renderHandler, serveShellRequest for PPR shells), integration (dispatch), and e2e (dev+prod parity, progressive enhancement). Use when writing a unit test for a loader, middleware, action, or handler, setting up vitest for a Rango app, or testing a route end-to-end in dev and production.
|
|
4
4
|
argument-hint: [layer]
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -89,29 +89,32 @@ The single rule that drives everything:
|
|
|
89
89
|
|
|
90
90
|
Each primitive links to its sub-file (API + recipe + caveats).
|
|
91
91
|
|
|
92
|
-
| The behavior is… | Layer | Primitive
|
|
93
|
-
| ---------------------------------------------------------------------------------------------------------------------------------- | ------------ |
|
|
94
|
-
| a `reverse` / `href` / params / env TYPE contract (a wrong name or missing param must not compile) | types (tsc) | [`@ts-expect-error` / `expectTypeOf`](./reverse-and-types.md)
|
|
95
|
-
| one loader's data logic | unit (node) | [`runLoader`](./loader.md)
|
|
96
|
-
| a loader's cookie / header / thrown `redirect()`/`notFound()` / handle-push output | unit (node) | [`runLoaderResult`](./loader.md)
|
|
97
|
-
| one middleware's ordering / short-circuit / cookie+header merge | unit (node) | [`runMiddleware`](./middleware.md)
|
|
98
|
-
| a `"use server"` action's cookie / header / flash output (even on `throw redirect()`) | unit (node) | [`runInRequestContext`](./server-actions.md)
|
|
99
|
-
| a `transition({ when })`
|
|
100
|
-
| a `clientUrls()` `revalidate()` / `isAction(ref)` predicate | unit (node) | [`runClientRevalidate`](./predicates.md) (production chain evaluator + locked defaults)
|
|
101
|
-
| a handle's `collect`/accumulator, a seeded handle read, or a loader handle write | unit | [`collectHandle` / seeded `handles` / `handlePushes`](./handles.md)
|
|
102
|
-
| a CLIENT component reading router context (`useParams`/`useReverse`/`Outlet`/`useNavigation`/`useLoader`) | unit (DOM) | [`renderRoute`](./client-components.md)
|
|
103
|
-
| a stale indicator from `useLoader().isLoading` while a `transition()` navigation re-runs the loader | unit (DOM) | [`renderRoute` + `navigate(url, { loaders })`](./client-components.md#held-navigation)
|
|
104
|
-
| a
|
|
105
|
-
| a
|
|
106
|
-
|
|
|
107
|
-
|
|
|
108
|
-
|
|
|
109
|
-
|
|
|
110
|
-
| a `"use cache"` hit in a
|
|
111
|
-
| cache hit
|
|
112
|
-
|
|
|
113
|
-
|
|
|
114
|
-
| a
|
|
92
|
+
| The behavior is… | Layer | Primitive | Import root |
|
|
93
|
+
| ---------------------------------------------------------------------------------------------------------------------------------- | ------------ | ----------------------------------------------------------------------------------------------------- | -------------------------------- |
|
|
94
|
+
| a `reverse` / `href` / params / env TYPE contract (a wrong name or missing param must not compile) | types (tsc) | [`@ts-expect-error` / `expectTypeOf`](./reverse-and-types.md) | `@rangojs/router/client` + `tsc` |
|
|
95
|
+
| one loader's data logic | unit (node) | [`runLoader`](./loader.md) | `@rangojs/router/testing` |
|
|
96
|
+
| a loader's cookie / header / thrown `redirect()`/`notFound()` / handle-push output | unit (node) | [`runLoaderResult`](./loader.md) | `@rangojs/router/testing` |
|
|
97
|
+
| one middleware's ordering / short-circuit / cookie+header merge | unit (node) | [`runMiddleware`](./middleware.md) | `@rangojs/router/testing` |
|
|
98
|
+
| a `"use server"` action's cookie / header / flash output (even on `throw redirect()`) | unit (node) | [`runInRequestContext`](./server-actions.md) | `@rangojs/router/testing` |
|
|
99
|
+
| a `transition({ when })` browser predicate against `{ kind, from, to, action }` | unit (node) | [`runTransitionWhen`](./predicates.md) (`{ applied, gatedOff, context }`) | `@rangojs/router/testing` |
|
|
100
|
+
| a `clientUrls()` `revalidate()` / `isAction(ref)` predicate | unit (node) | [`runClientRevalidate`](./predicates.md) (production chain evaluator + locked defaults) | `@rangojs/router/testing` |
|
|
101
|
+
| a handle's `collect`/accumulator, a seeded handle read, or a loader handle write | unit | [`collectHandle` / seeded `handles` / `handlePushes`](./handles.md) | `@rangojs/router/testing` |
|
|
102
|
+
| a CLIENT component reading router context (`useParams`/`useReverse`/`Outlet`/`useNavigation`/`useLoader`) | unit (DOM) | [`renderRoute`](./client-components.md) | `@rangojs/router/testing/dom` |
|
|
103
|
+
| a stale indicator from `useLoader().isLoading` while a `transition()` navigation re-runs the loader | unit (DOM) | [`renderRoute` + `navigate(url, { loaders })`](./client-components.md#held-navigation) | `@rangojs/router/testing/dom` |
|
|
104
|
+
| a component reading `useLocationState(Def)` after `router.push(url, { state: [Def(value)] })` | unit (DOM) | [`renderRoute` + `withLocationStateKey`](./client-components.md#location-state) | `@rangojs/router/testing[/dom]` |
|
|
105
|
+
| a redirect / status / headers / cookies / **response route** (json/text/html/xml/md), no Flight | integration | [`dispatch`](./response-routes.md) | `@rangojs/router/testing` |
|
|
106
|
+
| a real async **Server Component** (assert what it rendered: typed boundary props, server-rendered host content, inlined-vs-island) | RSC unit | [`renderServerTree` + `findClientBoundaries`/`findElements`](./server-tree.md) | `@rangojs/router/testing/flight` |
|
|
107
|
+
| the exact Flight **wire payload** shape (a drift snapshot) | RSC unit | [`renderToFlightString` + `toMatchFlightSnapshot`](./flight.md) | `@rangojs/router/testing/flight` |
|
|
108
|
+
| a real route **handler** `(ctx) => rsc` (params/loaders/vars -> rendered RSC + effects) | RSC unit | [`renderHandler`](./render-handler.md) | `@rangojs/router/testing/flight` |
|
|
109
|
+
| navigation, hydration, PE parity, view transitions, real SSR | e2e | [`createRangoE2E` -> `parityDescribe`/`expectParity`](./e2e-parity.md) | `@rangojs/router/testing/e2e` |
|
|
110
|
+
| a `"use cache"` hit in a handler (the body runs once across renders; the value round-trips) | RSC unit | [`renderHandler` + seeded `cacheStore`](./render-handler.md#a-use-cache-hit) | `@rangojs/router/testing/flight` |
|
|
111
|
+
| a `"use cache"` hit in a loader (the same, in the react-server project) | RSC unit | [`runLoader` + seeded `cacheStore`](./loader.md) | `@rangojs/router/testing` |
|
|
112
|
+
| a loader's own `cache()` (HIT, `updateTag()` of a tag its body recorded) | RSC unit | [`runLoader` + `cache` + seeded `cacheStore`](./loader.md) | `@rangojs/router/testing` |
|
|
113
|
+
| cache hit/miss/stale, prerender (= a cache hit by design) | e2e + signal | [`assertCacheStatus` (header) / `assertCacheDecision` (telemetry sink)](./cache-prerender.md) | `@rangojs/router/testing[/e2e]` |
|
|
114
|
+
| a `ppr` route's shell: MISS -> capture -> HIT, what the shell froze vs what a HIT renders live, tag eviction, navigation replay | RSC unit | [`serveShellRequest`](./serve-shell-request.md) | `@rangojs/router/testing/flight` |
|
|
115
|
+
| PPR shell HIT/MISS in real HTML (`ppr` path option), shell store key / getShell after flush | e2e + signal | [`assertShellStatus` / `shellCacheKey` + store](./cache-prerender.md#ppr-shell-and-navigation-replay) | `@rangojs/router/testing[/e2e]` |
|
|
116
|
+
| generated route map drift vs runtime | unit (node) | [`assertGeneratedRoutesMatch`](./reverse-and-types.md) | `@rangojs/router/testing` |
|
|
117
|
+
| a platform binding (`env.DB` / Durable Object / `env.R2`) | unit/integr. | [your own double via `env`](./bindings.md) | (any primitive's `env` option) |
|
|
115
118
|
|
|
116
119
|
Cross-references to the DSL skills: `/loader`, `/middleware`, `/server-actions`,
|
|
117
120
|
`/handler-use`, `/hooks`, `/response-routes`, `/route`, `/caching`, `/prerender`,
|
|
@@ -125,7 +128,8 @@ Cross-references to the DSL skills: `/loader`, `/middleware`, `/server-actions`,
|
|
|
125
128
|
[`predicates.md`](./predicates.md), [`reverse-and-types.md`](./reverse-and-types.md)
|
|
126
129
|
- Unit (DOM): [`client-components.md`](./client-components.md)
|
|
127
130
|
- RSC unit: [`flight.md`](./flight.md), [`server-tree.md`](./server-tree.md),
|
|
128
|
-
[`render-handler.md`](./render-handler.md)
|
|
131
|
+
[`render-handler.md`](./render-handler.md),
|
|
132
|
+
[`serve-shell-request.md`](./serve-shell-request.md)
|
|
129
133
|
- Integration: [`response-routes.md`](./response-routes.md)
|
|
130
134
|
- E2E: [`e2e-parity.md`](./e2e-parity.md), [`cache-prerender.md`](./cache-prerender.md)
|
|
131
135
|
|
|
@@ -134,24 +138,26 @@ Cross-references to the DSL skills: `/loader`, `/middleware`, `/server-actions`,
|
|
|
134
138
|
Where each runtime export is documented (types such as `RunLoaderOptions` sit
|
|
135
139
|
beside their function):
|
|
136
140
|
|
|
137
|
-
| Entry | Exports | Documented in
|
|
138
|
-
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
139
|
-
| `@rangojs/router/testing` | `runLoader`, `runLoaderResult` | [`loader.md`](./loader.md)
|
|
140
|
-
| | `runMiddleware` | [`middleware.md`](./middleware.md)
|
|
141
|
-
| | `runInRequestContext`, `createTestRequestContext`, `runWithRequestContext`, `toRequest`, `seedVariables` | [`server-actions.md`](./server-actions.md)
|
|
142
|
-
| | `runTransitionWhen`, `runClientRevalidate` | [`predicates.md`](./predicates.md)
|
|
143
|
-
| | `collectHandle` | [`handles.md`](./handles.md)
|
|
144
|
-
| | `
|
|
145
|
-
| | `
|
|
146
|
-
| | `
|
|
147
|
-
| | `
|
|
148
|
-
|
|
|
149
|
-
| `@rangojs/router/testing/
|
|
150
|
-
|
|
|
151
|
-
| | `
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
| `@rangojs/router/testing/
|
|
141
|
+
| Entry | Exports | Documented in |
|
|
142
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- |
|
|
143
|
+
| `@rangojs/router/testing` | `runLoader`, `runLoaderResult` | [`loader.md`](./loader.md) |
|
|
144
|
+
| | `runMiddleware` | [`middleware.md`](./middleware.md) |
|
|
145
|
+
| | `runInRequestContext`, `createTestRequestContext`, `runWithRequestContext`, `toRequest`, `seedVariables` | [`server-actions.md`](./server-actions.md) |
|
|
146
|
+
| | `runTransitionWhen`, `runClientRevalidate` | [`predicates.md`](./predicates.md) |
|
|
147
|
+
| | `collectHandle` | [`handles.md`](./handles.md) |
|
|
148
|
+
| | `withLocationStateKey` | [`client-components.md`](./client-components.md) |
|
|
149
|
+
| | `dispatch` | [`response-routes.md`](./response-routes.md) |
|
|
150
|
+
| | `assertCacheStatus`, `assertCacheDecision`, `parseCacheHeader`, `createCacheSink`, `filterCacheDecisions` | [`cache-prerender.md`](./cache-prerender.md) |
|
|
151
|
+
| | `assertShellStatus`, `parseShellStatus`, `shellCacheKey`, `SHELL_STATUS_HEADER`, `assertPprReplayStatus`, `parsePprReplayStatus`, `PPR_REPLAY_STATUS_HEADER` | [`cache-prerender.md`](./cache-prerender.md) |
|
|
152
|
+
| | `assertGeneratedRoutesMatch`, `diffGeneratedRoutes` | [`reverse-and-types.md`](./reverse-and-types.md) |
|
|
153
|
+
| `@rangojs/router/testing/dom` | `renderRoute` | [`client-components.md`](./client-components.md) |
|
|
154
|
+
| `@rangojs/router/testing/flight` | `renderToFlightString`, `normalizeFlight`, `assertFlightRuntimeAvailable` | [`flight.md`](./flight.md) |
|
|
155
|
+
| | `renderServerTree`, `findClientBoundaries`, `findElements`, `textContent`, `assertFlightTreeRuntimeAvailable` | [`server-tree.md`](./server-tree.md) |
|
|
156
|
+
| | `renderHandler` | [`render-handler.md`](./render-handler.md) |
|
|
157
|
+
| | `serveShellRequest`, `resetShellTestState` | [`serve-shell-request.md`](./serve-shell-request.md) |
|
|
158
|
+
| `@rangojs/router/testing/flight-matchers` | `flightMatchers` (`toMatchFlight`, `toMatchFlightSnapshot`) | [`flight.md`](./flight.md) |
|
|
159
|
+
| `@rangojs/router/testing/vitest` | `rangoTestConfig`, `rangoTestAliases`, `rangoInlineDeps`, `rangoUseClientTransform`, `rangoUseCacheTransform` | [`setup.md`](./setup.md) |
|
|
160
|
+
| `@rangojs/router/testing/e2e` | `createRangoE2E` and its standalone helpers; the cache/shell status helpers above are re-exported here | [`e2e-parity.md`](./e2e-parity.md) |
|
|
155
161
|
|
|
156
162
|
## Pre-push checklist
|
|
157
163
|
|