@rangojs/router 0.16.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +255 -89
- package/dist/testing/vitest.js +252 -0
- package/dist/types/browser/cookie-name.d.ts +6 -0
- package/dist/types/browser/history-state.d.ts +16 -0
- package/dist/types/browser/navigation-transaction.d.ts +6 -0
- package/dist/types/browser/partial-update.d.ts +11 -0
- package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
- package/dist/types/browser/rsc-router.d.ts +1 -1
- package/dist/types/browser/scroll-restoration.d.ts +7 -0
- package/dist/types/cache/cache-exec-scope.d.ts +18 -1
- package/dist/types/cache/cache-key-utils.d.ts +17 -2
- package/dist/types/cache/cache-policy.d.ts +17 -0
- package/dist/types/cache/cache-scope.d.ts +132 -19
- package/dist/types/cache/cache-tag.d.ts +92 -7
- package/dist/types/cache/cf/cf-base64.d.ts +6 -0
- package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
- package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
- package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
- package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
- package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
- package/dist/types/cache/handle-capture.d.ts +41 -3
- package/dist/types/cache/handle-snapshot.d.ts +54 -13
- package/dist/types/cache/index.d.ts +1 -0
- package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
- package/dist/types/cache/segment-codec.d.ts +10 -3
- package/dist/types/cache/shell-memo.d.ts +178 -0
- package/dist/types/cache/shell-snapshot.d.ts +110 -21
- package/dist/types/cache/tag-invalidation.d.ts +30 -3
- package/dist/types/cache/taint.d.ts +1 -1
- package/dist/types/cache/types.d.ts +220 -44
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
- package/dist/types/client.d.ts +4 -4
- package/dist/types/client.rsc.d.ts +4 -5
- package/dist/types/components/DefaultDocument.d.ts +5 -2
- package/dist/types/context-var.d.ts +6 -5
- package/dist/types/escape-script.d.ts +1 -1
- package/dist/types/handles/MetaTags.d.ts +5 -4
- package/dist/types/handles/Scripts.d.ts +8 -7
- package/dist/types/handles/meta.d.ts +2 -2
- package/dist/types/handles/script.d.ts +17 -16
- package/dist/types/href-client.d.ts +1 -1
- package/dist/types/html.d.ts +12 -0
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.rsc.d.ts +3 -2
- package/dist/types/loader-store.d.ts +7 -4
- package/dist/types/prerender/build-shell-capture.d.ts +11 -4
- package/dist/types/reverse.d.ts +36 -4
- package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
- package/dist/types/route-definition/helpers-types.d.ts +33 -9
- package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
- package/dist/types/route-types.d.ts +1 -1
- package/dist/types/router/debug-manifest.d.ts +3 -2
- package/dist/types/router/error-handling.d.ts +20 -0
- package/dist/types/router/handler-context.d.ts +6 -0
- package/dist/types/router/instrument.d.ts +8 -0
- package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
- package/dist/types/router/middleware-types.d.ts +9 -3
- package/dist/types/router/router-context.d.ts +3 -1
- package/dist/types/router/router-interfaces.d.ts +24 -2
- package/dist/types/router/router-options.d.ts +8 -5
- package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
- package/dist/types/router/segment-resolution/loader-cache.d.ts +66 -1
- package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
- package/dist/types/router/segment-resolution/loader-snapshot.d.ts +23 -12
- package/dist/types/router/segment-resolution/mask-nested.d.ts +67 -26
- package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
- package/dist/types/router/segment-wrappers.d.ts +1 -1
- package/dist/types/rsc/full-payload.d.ts +14 -0
- package/dist/types/rsc/handler.d.ts +1 -1
- package/dist/types/rsc/response-route-handler.d.ts +1 -1
- package/dist/types/rsc/rsc-rendering.d.ts +1 -2
- package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
- package/dist/types/rsc/shell-capture-constants.d.ts +61 -6
- package/dist/types/rsc/shell-capture.d.ts +151 -19
- package/dist/types/rsc/shell-serve.d.ts +140 -17
- package/dist/types/rsc/ssr-setup.d.ts +6 -6
- package/dist/types/rsc/types.d.ts +17 -0
- package/dist/types/server/context.d.ts +139 -15
- package/dist/types/server/cookie-store.d.ts +21 -0
- package/dist/types/server/handle-store.d.ts +26 -2
- package/dist/types/server/request-context.d.ts +182 -53
- package/dist/types/ssr/index.d.ts +24 -0
- package/dist/types/ssr/ssr-root.d.ts +9 -8
- package/dist/types/static-handler.d.ts +4 -1
- package/dist/types/testing/flight.entry.d.ts +7 -0
- package/dist/types/testing/generated-routes.d.ts +2 -2
- package/dist/types/testing/internal/context.d.ts +1 -1
- package/dist/types/testing/render-route.d.ts +32 -6
- package/dist/types/testing/run-loader.d.ts +18 -3
- package/dist/types/testing/serve-shell-request.d.ts +132 -0
- package/dist/types/testing/shell-status.d.ts +23 -10
- package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
- package/dist/types/testing/vitest.d.ts +35 -1
- package/dist/types/theme/ThemeScript.d.ts +3 -3
- package/dist/types/types/cache-types.d.ts +14 -10
- package/dist/types/types/handler-context.d.ts +28 -1
- package/dist/types/types/loader-types.d.ts +8 -6
- package/dist/types/urls/pattern-types.d.ts +22 -18
- package/dist/types/urls/response-types.d.ts +3 -12
- package/dist/types/use-loader.d.ts +12 -3
- package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
- package/dist/types/vite/rango.d.ts +1 -1
- package/dist/types/vite/router-discovery.d.ts +29 -2
- package/dist/vite/index.js +1420 -1348
- package/package.json +2 -2
- package/skills/api-client/SKILL.md +22 -10
- package/skills/breadcrumbs/SKILL.md +75 -22
- package/skills/bundle-analysis/SKILL.md +22 -22
- package/skills/cache-guide/SKILL.md +306 -124
- package/skills/caching/SKILL.md +530 -98
- package/skills/catalog.json +16 -16
- package/skills/client-urls/SKILL.md +65 -38
- package/skills/cloudflare/SKILL.md +87 -20
- package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
- package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
- package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
- package/skills/comparison/SKILL.md +5 -0
- package/skills/comparison/references/framework-comparison.md +75 -57
- package/skills/composability/SKILL.md +38 -29
- package/skills/css/SKILL.md +10 -13
- package/skills/debug-manifest/SKILL.md +74 -31
- package/skills/defer-hydration/SKILL.md +52 -31
- package/skills/deployment-caching/SKILL.md +11 -3
- package/skills/document-cache/SKILL.md +98 -28
- package/skills/fonts/SKILL.md +16 -8
- package/skills/handler-use/SKILL.md +55 -17
- package/skills/hooks/SKILL.md +28 -20
- package/skills/hooks/data.md +23 -3
- package/skills/hooks/handle-and-actions.md +34 -17
- package/skills/hooks/navigation.md +39 -10
- package/skills/hooks/outlets.md +13 -6
- package/skills/hooks/state.md +39 -15
- package/skills/hooks/urls.md +7 -5
- package/skills/host-router/SKILL.md +64 -12
- package/skills/i18n/SKILL.md +60 -34
- package/skills/intercept/SKILL.md +161 -26
- package/skills/layout/SKILL.md +106 -50
- package/skills/links/SKILL.md +51 -19
- package/skills/loader/SKILL.md +386 -128
- package/skills/middleware/SKILL.md +129 -26
- package/skills/migrate-nextjs/SKILL.md +156 -70
- package/skills/migrate-nextjs/backend-host-swap.md +5 -2
- package/skills/migrate-react-router/SKILL.md +51 -39
- package/skills/migrate-react-router/component-migration.md +28 -7
- package/skills/migrate-react-router/data-and-actions.md +21 -6
- package/skills/migrate-react-router/route-mapping.md +19 -10
- package/skills/mime-routes/SKILL.md +12 -7
- package/skills/observability/SKILL.md +92 -14
- package/skills/parallel/SKILL.md +88 -47
- package/skills/ppr/SKILL.md +866 -379
- package/skills/prerender/SKILL.md +210 -108
- package/skills/rango/SKILL.md +115 -65
- package/skills/react-compiler/SKILL.md +6 -5
- package/skills/response-routes/SKILL.md +133 -25
- package/skills/route/SKILL.md +170 -60
- package/skills/router-setup/SKILL.md +420 -240
- package/skills/scripts/SKILL.md +39 -21
- package/skills/server-actions/SKILL.md +231 -59
- package/skills/shell-manifest/SKILL.md +28 -14
- package/skills/streams-and-websockets/SKILL.md +32 -14
- package/skills/tailwind/SKILL.md +37 -33
- package/skills/testing/SKILL.md +73 -33
- package/skills/testing/cache-prerender.md +60 -33
- package/skills/testing/client-components.md +59 -6
- package/skills/testing/e2e-parity.md +12 -2
- package/skills/testing/flight.md +19 -11
- package/skills/testing/handles.md +8 -5
- package/skills/testing/loader.md +26 -6
- package/skills/testing/middleware.md +8 -3
- package/skills/testing/predicates.md +120 -0
- package/skills/testing/render-handler.md +70 -22
- package/skills/testing/response-routes.md +11 -4
- package/skills/testing/reverse-and-types.md +20 -14
- package/skills/testing/serve-shell-request.md +188 -0
- package/skills/testing/server-actions.md +12 -3
- package/skills/testing/server-tree.md +9 -0
- package/skills/testing/setup.md +9 -5
- package/skills/theme/SKILL.md +155 -30
- package/skills/typesafety/SKILL.md +20 -9
- package/skills/typesafety/env-and-bindings.md +137 -50
- package/skills/typesafety/generated-files-and-cli.md +26 -14
- package/skills/typesafety/params-and-search.md +44 -19
- package/skills/typesafety/route-types.md +50 -14
- package/skills/use-cache/SKILL.md +224 -83
- package/skills/vercel/SKILL.md +35 -13
- package/skills/view-transitions/SKILL.md +42 -21
- package/src/__augment-tests__/augmented.check.ts +26 -1
- package/src/browser/cookie-name.ts +23 -0
- package/src/browser/history-state.ts +23 -0
- package/src/browser/index.ts +2 -2
- package/src/browser/navigation-bridge.ts +3 -4
- package/src/browser/navigation-transaction.ts +22 -2
- package/src/browser/partial-update.ts +35 -25
- package/src/browser/react/ScrollRestoration.tsx +6 -6
- package/src/browser/rsc-router.tsx +6 -1
- package/src/browser/scroll-restoration.ts +53 -6
- package/src/cache/cache-exec-scope.ts +37 -4
- package/src/cache/cache-key-utils.ts +23 -8
- package/src/cache/cache-policy.ts +22 -0
- package/src/cache/cache-runtime.ts +264 -68
- package/src/cache/cache-scope.ts +419 -43
- package/src/cache/cache-tag.ts +375 -11
- package/src/cache/cf/cf-base64.ts +36 -4
- package/src/cache/cf/cf-cache-constants.ts +12 -0
- package/src/cache/cf/cf-cache-store.ts +990 -348
- package/src/cache/cf/cf-cache-types.ts +32 -4
- package/src/cache/cf/cf-shell-frame.ts +258 -0
- package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
- package/src/cache/document-cache.ts +15 -0
- package/src/cache/handle-capture.ts +90 -10
- package/src/cache/handle-snapshot.ts +133 -28
- package/src/cache/index.ts +2 -0
- package/src/cache/isolate-tag-memo.ts +308 -0
- package/src/cache/segment-codec.ts +26 -7
- package/src/cache/shell-memo.ts +341 -0
- package/src/cache/shell-snapshot.ts +217 -27
- package/src/cache/tag-invalidation.ts +108 -22
- package/src/cache/taint.ts +1 -1
- package/src/cache/types.ts +225 -44
- package/src/cache/vercel/vercel-cache-store.ts +545 -61
- package/src/client.rsc.tsx +10 -9
- package/src/client.tsx +5 -4
- package/src/components/DefaultDocument.tsx +7 -2
- package/src/context-var.ts +6 -5
- package/src/escape-script.ts +1 -1
- package/src/handles/MetaTags.tsx +13 -11
- package/src/handles/Scripts.tsx +11 -10
- package/src/handles/meta.ts +2 -2
- package/src/handles/script.ts +17 -16
- package/src/href-client.ts +1 -1
- package/src/html.ts +12 -0
- package/src/index.rsc.ts +5 -0
- package/src/index.ts +1 -0
- package/src/loader-store.ts +7 -4
- package/src/prerender/build-shell-capture.ts +73 -22
- package/src/reverse.ts +44 -4
- package/src/route-definition/dsl-helpers.ts +121 -33
- package/src/route-definition/helpers-types.ts +33 -9
- package/src/route-definition/resolve-handler-use.ts +121 -6
- package/src/route-types.ts +0 -3
- package/src/router/debug-manifest.ts +10 -2
- package/src/router/error-handling.ts +51 -15
- package/src/router/handler-context.ts +71 -28
- package/src/router/instrument.ts +54 -2
- package/src/router/intercept-resolution.ts +141 -43
- package/src/router/loader-resolution.ts +115 -39
- package/src/router/match-api.ts +40 -26
- package/src/router/match-handlers.ts +5 -0
- package/src/router/match-middleware/background-revalidation.ts +161 -96
- package/src/router/match-middleware/cache-lookup.ts +110 -10
- package/src/router/match-middleware/cache-store.ts +48 -101
- package/src/router/middleware-types.ts +9 -6
- package/src/router/middleware.ts +75 -13
- package/src/router/prerender-match.ts +59 -19
- package/src/router/route-snapshot.ts +11 -1
- package/src/router/router-context.ts +2 -0
- package/src/router/router-interfaces.ts +25 -2
- package/src/router/router-options.ts +8 -5
- package/src/router/segment-resolution/fresh.ts +62 -21
- package/src/router/segment-resolution/helpers.ts +4 -2
- package/src/router/segment-resolution/loader-cache.ts +407 -76
- package/src/router/segment-resolution/loader-mask.ts +12 -22
- package/src/router/segment-resolution/loader-snapshot.ts +208 -56
- package/src/router/segment-resolution/mask-nested.ts +138 -44
- package/src/router/segment-resolution/revalidation.ts +61 -16
- package/src/router/segment-wrappers.ts +4 -0
- package/src/rsc/full-payload.ts +25 -1
- package/src/rsc/handler.ts +14 -2
- package/src/rsc/progressive-enhancement.ts +3 -2
- package/src/rsc/render-pipeline.ts +3 -0
- package/src/rsc/response-cache-serve.ts +9 -6
- package/src/rsc/response-route-handler.ts +11 -3
- package/src/rsc/rsc-rendering.ts +608 -225
- package/src/rsc/shell-build-manifest.ts +27 -9
- package/src/rsc/shell-capture-constants.ts +73 -6
- package/src/rsc/shell-capture.ts +1068 -296
- package/src/rsc/shell-serve.ts +316 -34
- package/src/rsc/ssr-setup.ts +14 -8
- package/src/rsc/types.ts +18 -0
- package/src/server/context.ts +300 -32
- package/src/server/cookie-store.ts +87 -61
- package/src/server/handle-store.ts +232 -7
- package/src/server/request-context.ts +297 -67
- package/src/ssr/index.tsx +136 -44
- package/src/ssr/ssr-root.tsx +9 -8
- package/src/static-handler.ts +4 -1
- package/src/testing/flight-runtime.d.ts +21 -0
- package/src/testing/flight.entry.ts +14 -0
- package/src/testing/generated-routes.ts +2 -2
- package/src/testing/internal/context.ts +1 -1
- package/src/testing/internal/flight-client-globals.ts +7 -5
- package/src/testing/render-route.tsx +85 -24
- package/src/testing/run-loader.ts +68 -6
- package/src/testing/serve-shell-request.ts +378 -0
- package/src/testing/shell-status.ts +34 -12
- package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
- package/src/testing/vitest.ts +62 -1
- package/src/theme/ThemeProvider.tsx +7 -6
- package/src/theme/ThemeScript.tsx +3 -3
- package/src/types/cache-types.ts +14 -10
- package/src/types/handler-context.ts +28 -1
- package/src/types/loader-types.ts +8 -6
- package/src/urls/pattern-types.ts +22 -18
- package/src/urls/response-types.ts +3 -23
- package/src/use-loader.tsx +12 -3
- package/src/vite/discovery/shell-prerender-phase.ts +9 -0
- package/src/vite/plugins/use-cache-transform.ts +118 -74
- package/src/vite/rango.ts +7 -3
- package/src/vite/router-discovery.ts +14 -4
|
@@ -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,14 +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
|
-
|
|
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".)
|
|
40
45
|
2. **Replay on every hit.** Handle data is stored with the Flight payload
|
|
41
|
-
and replayed into the handle store on cache
|
|
42
|
-
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.
|
|
43
48
|
3. **Loaders read after the render barrier.** A DSL loader can
|
|
44
49
|
`await ctx.rendered()` (waits for all non-loader segments to settle —
|
|
45
50
|
fresh render or replay alike), then `ctx.get(Handle)` returns the
|
|
@@ -94,7 +99,7 @@ import { RenderedProducts } from "../handles/rendered-products";
|
|
|
94
99
|
export const PriceLoader = createLoader(async (ctx) => {
|
|
95
100
|
await ctx.rendered();
|
|
96
101
|
const ids = ctx.get(RenderedProducts);
|
|
97
|
-
return db.pricesFor(ids); //
|
|
102
|
+
return db.pricesFor(ids); // Record<string, number> keyed by product id
|
|
98
103
|
});
|
|
99
104
|
```
|
|
100
105
|
|
|
@@ -138,9 +143,11 @@ cache({ ttl: 600, tags: ["products"] }, () => [
|
|
|
138
143
|
## Contract and gotchas
|
|
139
144
|
|
|
140
145
|
- **The manifest is exactly as fresh as the shell.** Replayed handle data is
|
|
141
|
-
frozen with the payload. To change _which_ products render,
|
|
142
|
-
shell (`updateTag("products")
|
|
143
|
-
|
|
146
|
+
frozen with the payload. To change _which_ products render, refresh the
|
|
147
|
+
shell — for `cache()`, `updateTag("products")` or TTL expiry; for
|
|
148
|
+
`Prerender`, a rebuild (tag invalidation does not reach build-time payloads,
|
|
149
|
+
see `/prerender`). Never treat the loader as the refresh path for the list
|
|
150
|
+
itself. This is the point:
|
|
144
151
|
shell and holes cannot desync because they share one artifact.
|
|
145
152
|
- **No request-scoped data in a manifest handle.** The handle data is baked
|
|
146
153
|
into a shared artifact — the same cross-user rule as any cached content.
|
|
@@ -181,12 +188,19 @@ const prices = await runLoader(PriceLoader, {
|
|
|
181
188
|
|
|
182
189
|
This tests the loader's post-barrier logic. The real
|
|
183
190
|
push → store → replay → barrier wiring is covered at the e2e tier (dev +
|
|
184
|
-
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`.
|
|
185
198
|
|
|
186
199
|
## Related
|
|
187
200
|
|
|
188
201
|
- `/prerender` — `Prerender`/`Passthrough`, build flow, passthrough fallback
|
|
189
202
|
- `/caching` — segment `cache()`, stores, tags
|
|
203
|
+
- `/ppr` — cached HTML shells; its holes are the same live loaders
|
|
190
204
|
- `/loader` — loader context, `ctx.rendered()`, streaming
|
|
191
205
|
- `/hooks` — `useHandle` for reading handle data in client components
|
|
192
206
|
- `/rango` → "Passing data down the tree" — this pattern is the frozen→live
|
|
@@ -38,11 +38,12 @@ export const urlpatterns = urls(({ path }) => [
|
|
|
38
38
|
"/events/ticks",
|
|
39
39
|
(ctx) => {
|
|
40
40
|
const encoder = new TextEncoder();
|
|
41
|
+
let interval: ReturnType<typeof setInterval> | undefined;
|
|
41
42
|
|
|
42
43
|
const stream = new ReadableStream({
|
|
43
|
-
|
|
44
|
+
start(controller) {
|
|
44
45
|
let count = 0;
|
|
45
|
-
|
|
46
|
+
interval = setInterval(() => {
|
|
46
47
|
controller.enqueue(
|
|
47
48
|
encoder.encode(`event: tick\ndata: ${++count}\n\n`),
|
|
48
49
|
);
|
|
@@ -51,9 +52,17 @@ export const urlpatterns = urls(({ path }) => [
|
|
|
51
52
|
// Honor client disconnect — signal comes from ctx.request.signal
|
|
52
53
|
ctx.request.signal.addEventListener("abort", () => {
|
|
53
54
|
clearInterval(interval);
|
|
54
|
-
|
|
55
|
+
try {
|
|
56
|
+
controller.close();
|
|
57
|
+
} catch {
|
|
58
|
+
// already closed or cancelled
|
|
59
|
+
}
|
|
55
60
|
});
|
|
56
61
|
},
|
|
62
|
+
// The runtime cancels the stream when the reader goes away
|
|
63
|
+
cancel() {
|
|
64
|
+
clearInterval(interval);
|
|
65
|
+
},
|
|
57
66
|
});
|
|
58
67
|
|
|
59
68
|
return new Response(stream, {
|
|
@@ -80,18 +89,21 @@ source.addEventListener("tick", (e) => console.log("tick", e.data));
|
|
|
80
89
|
|
|
81
90
|
### SSE caveats
|
|
82
91
|
|
|
83
|
-
- **Never
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
cache()
|
|
92
|
+
- **Never put an SSE route inside a `cache()` scope.** Response-route
|
|
93
|
+
`cache()` does not special-case `path.stream`: an SSE response is a `200`
|
|
94
|
+
like any other, so a `cache()` above it would try to store the stream. Keep
|
|
95
|
+
SSE routes outside every `cache()` boundary, and don't layer a custom
|
|
96
|
+
caching middleware on top either.
|
|
87
97
|
- **Middleware is fine.** Global/route middleware rewraps the SSE `Response`
|
|
88
98
|
as `new Response(response.body, { status, headers })` to merge stub headers.
|
|
89
99
|
The `ReadableStream` body is passed by reference, not consumed, so the
|
|
90
100
|
client sees the stream unchanged. (WebSocket upgrades are the exception —
|
|
91
101
|
those bypass rewrap entirely; see below.)
|
|
92
|
-
- **Honor `ctx.request.signal`.** Without wiring
|
|
93
|
-
(timer, DB cursor, upstream fetch), the stream leaks when the
|
|
94
|
-
|
|
102
|
+
- **Honor `ctx.request.signal` and `cancel()`.** Without wiring disconnect to
|
|
103
|
+
your source (timer, DB cursor, upstream fetch), the stream leaks when the
|
|
104
|
+
client goes away. Clean up in both places: depending on the runtime, a
|
|
105
|
+
disconnect surfaces as the request signal aborting, the stream being
|
|
106
|
+
cancelled, or both.
|
|
95
107
|
- **Disable Nginx/CDN buffering** via `x-accel-buffering: no` and ensure
|
|
96
108
|
no intermediate proxy rebuffers. On Cloudflare Workers this is a non-issue.
|
|
97
109
|
|
|
@@ -210,6 +222,8 @@ HTTP request instead (e.g. during login), then read them at upgrade time
|
|
|
210
222
|
via `ctx.request.headers.get("cookie")`.
|
|
211
223
|
|
|
212
224
|
```typescript
|
|
225
|
+
import { cookies } from "@rangojs/router";
|
|
226
|
+
|
|
213
227
|
// Avoid: this cookie may not land on the upgrade response, and the client
|
|
214
228
|
// never reads it during the handshake regardless.
|
|
215
229
|
router.use(async (ctx, next) => {
|
|
@@ -219,7 +233,7 @@ router.use(async (ctx, next) => {
|
|
|
219
233
|
|
|
220
234
|
// Prefer: authenticate by reading a cookie set on a prior HTTP request.
|
|
221
235
|
path.any("/ws", (ctx) => {
|
|
222
|
-
const session =
|
|
236
|
+
const session = cookies().get("session")?.value;
|
|
223
237
|
if (!verify(session)) return new Response("unauthorized", { status: 401 });
|
|
224
238
|
// ...upgrade
|
|
225
239
|
});
|
|
@@ -240,7 +254,8 @@ router.use(async (ctx, next) => {
|
|
|
240
254
|
|
|
241
255
|
## Caching
|
|
242
256
|
|
|
243
|
-
- **SSE** — do not
|
|
257
|
+
- **SSE** — do not place under `cache()`: it is a cacheable `200`, and an
|
|
258
|
+
endless stream cannot be stored or replayed.
|
|
244
259
|
- **WebSocket** — `cache()` is inert because only `status === 200` is cacheable.
|
|
245
260
|
|
|
246
261
|
## Runtime caveats
|
|
@@ -279,5 +294,8 @@ Object.defineProperty(upgrade, "webSocket", {
|
|
|
279
294
|
|
|
280
295
|
## See also
|
|
281
296
|
|
|
282
|
-
-
|
|
283
|
-
|
|
297
|
+
- `/response-routes` — the parent skill for `path.json/text/html/stream/any`
|
|
298
|
+
and `ResponseHandlerContext` (`ctx.header()`, `ctx.waitUntil()`,
|
|
299
|
+
`ctx.executionContext`).
|
|
300
|
+
- `/middleware` — how global and route-level middleware compose with handlers.
|
|
301
|
+
- `/cloudflare` — Workers bindings and Durable Object setup.
|
package/skills/tailwind/SKILL.md
CHANGED
|
@@ -6,7 +6,9 @@ argument-hint: [setup]
|
|
|
6
6
|
|
|
7
7
|
# Tailwind CSS
|
|
8
8
|
|
|
9
|
-
Set up Tailwind CSS v4
|
|
9
|
+
Set up Tailwind CSS v4 in a Rango app: the Vite plugin, the CSS entry, loading
|
|
10
|
+
it from the Document with Vite's `?url` import, theme tokens, class-based dark
|
|
11
|
+
mode, and fonts. The general stylesheet pattern is explained in `/css`.
|
|
10
12
|
|
|
11
13
|
## Install
|
|
12
14
|
|
|
@@ -18,11 +20,14 @@ pnpm add -D tailwindcss @tailwindcss/vite
|
|
|
18
20
|
|
|
19
21
|
```typescript
|
|
20
22
|
// vite.config.ts
|
|
23
|
+
import { defineConfig } from "vite";
|
|
21
24
|
import tailwindcss from "@tailwindcss/vite";
|
|
25
|
+
import { rango } from "@rangojs/router/vite";
|
|
22
26
|
|
|
23
27
|
export default defineConfig({
|
|
24
28
|
plugins: [
|
|
25
29
|
tailwindcss(),
|
|
30
|
+
rango(),
|
|
26
31
|
// ... other plugins
|
|
27
32
|
],
|
|
28
33
|
});
|
|
@@ -37,18 +42,17 @@ export default defineConfig({
|
|
|
37
42
|
|
|
38
43
|
## Document Component
|
|
39
44
|
|
|
40
|
-
Import the CSS file with `?url` to get
|
|
41
|
-
`<head>`. Give the `<link rel="stylesheet">` a `precedence` prop so React 19
|
|
42
|
-
manages it as a resource
|
|
43
|
-
(no flash of unstyled content).
|
|
44
|
-
[Stylesheets and cross-app navigation](#stylesheets-and-cross-app-navigation):
|
|
45
|
+
Import the CSS file with `?url` to get its hashed URL, then preload and link it
|
|
46
|
+
in `<head>`. Give the `<link rel="stylesheet">` a `precedence` prop so React 19
|
|
47
|
+
manages it as a resource: de-duped by `href`, ordered, and loaded before paint
|
|
48
|
+
(no flash of unstyled content).
|
|
45
49
|
|
|
46
50
|
```tsx
|
|
47
51
|
// src/document.tsx
|
|
48
52
|
"use client";
|
|
49
53
|
|
|
50
54
|
import type { ReactNode } from "react";
|
|
51
|
-
import {
|
|
55
|
+
import { Html } from "@rangojs/router/client";
|
|
52
56
|
import styles from "./index.css?url";
|
|
53
57
|
|
|
54
58
|
export function Document({ children }: { children: ReactNode }) {
|
|
@@ -57,7 +61,7 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
57
61
|
<head>
|
|
58
62
|
<link rel="preload" href={styles} as="style" precedence="default" />
|
|
59
63
|
<link rel="stylesheet" href={styles} precedence="default" />
|
|
60
|
-
<
|
|
64
|
+
<Html.Meta />
|
|
61
65
|
</head>
|
|
62
66
|
<body className="font-sans antialiased text-slate-900 bg-slate-50">
|
|
63
67
|
{children}
|
|
@@ -67,27 +71,15 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
67
71
|
}
|
|
68
72
|
```
|
|
69
73
|
|
|
70
|
-
|
|
74
|
+
The `?url` suffix tells Vite to return the processed CSS file's URL instead of
|
|
75
|
+
injecting it as a side effect, giving a stable, hashed asset path in both
|
|
76
|
+
development and production. A bare `import "./index.css"` also produces managed
|
|
77
|
+
CSS, but it is not guaranteed to be in the initial streamed `<head>`; prefer
|
|
78
|
+
`?url` + `<link precedence>` for document CSS (see `/css`).
|
|
71
79
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
recommended way to render a stylesheet link, which is why the example above uses
|
|
76
|
-
it. (A bare side-effect `import "./index.css"` also produces managed CSS via
|
|
77
|
-
`@vitejs/plugin-rsc`, but carries an SSR-streaming caveat — prefer the `?url` +
|
|
78
|
-
`<link precedence>` form for document CSS. See `/css`.)
|
|
79
|
-
|
|
80
|
-
For **host-router** apps (`/host-router`), a client-side navigation that crosses
|
|
81
|
-
an app boundary is a **full document load**, not a soft swap — the framework
|
|
82
|
-
redirects on an app switch. So each app's document (its stylesheets, theme, meta)
|
|
83
|
-
is always re-established cleanly by the target app's own load; you do not have to
|
|
84
|
-
coordinate stylesheet `href`s or `precedence` across apps. (This replaced an
|
|
85
|
-
earlier soft cross-app swap, under which a stylesheet shared across apps — every
|
|
86
|
-
app's `@import "tailwindcss"` compiles to one hashed asset — could be dropped by
|
|
87
|
-
React's by-`href` resource dedup if the apps disagreed on `precedence`. The full
|
|
88
|
-
reload removes that footgun entirely.)
|
|
89
|
-
|
|
90
|
-
The `?url` suffix tells Vite to return the processed CSS file's URL instead of injecting it as a side effect. This gives you a stable, hashed asset path that works in both development and production.
|
|
80
|
+
For **host-router** apps (`/host-router`), navigating between apps is a full
|
|
81
|
+
document load, so each app's Tailwind stylesheet is loaded by its own Document;
|
|
82
|
+
there is nothing to coordinate across apps.
|
|
91
83
|
|
|
92
84
|
## Customizing the Theme
|
|
93
85
|
|
|
@@ -107,17 +99,29 @@ Tailwind v4 uses CSS `@theme` for customization:
|
|
|
107
99
|
|
|
108
100
|
## Dark Mode
|
|
109
101
|
|
|
110
|
-
Combine with the Rango theme system (see `/theme`):
|
|
102
|
+
Combine with the Rango theme system (see `/theme`). With `attribute: "class"`
|
|
103
|
+
(the default) the theme script puts `class="dark"` or `class="light"` on
|
|
104
|
+
`<html>` before first paint; add `suppressHydrationWarning` to the Document's
|
|
105
|
+
`<html>` so React accepts that change:
|
|
111
106
|
|
|
112
107
|
```typescript
|
|
113
|
-
const router = createRouter({
|
|
108
|
+
export const router = createRouter({
|
|
114
109
|
document: Document,
|
|
115
110
|
urls: urlpatterns,
|
|
116
111
|
theme: { attribute: "class" },
|
|
117
112
|
});
|
|
118
113
|
```
|
|
119
114
|
|
|
120
|
-
|
|
115
|
+
Tailwind v4's `dark:` variant follows `prefers-color-scheme` by default. Point
|
|
116
|
+
it at the class instead, so the user's stored choice wins over the OS setting:
|
|
117
|
+
|
|
118
|
+
```css
|
|
119
|
+
/* src/index.css */
|
|
120
|
+
@import "tailwindcss";
|
|
121
|
+
@custom-variant dark (&:where(.dark, .dark *));
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Then use the `dark:` variant as usual:
|
|
121
125
|
|
|
122
126
|
```tsx
|
|
123
127
|
<div className="bg-white dark:bg-slate-900 text-slate-900 dark:text-white">
|
|
@@ -147,7 +151,7 @@ No extra `<link>` tags needed in the Document -- Vite bundles the font files fro
|
|
|
147
151
|
|
|
148
152
|
## Notes
|
|
149
153
|
|
|
150
|
-
- `?url` import
|
|
151
|
-
- `<link rel="preload" as="style">`
|
|
154
|
+
- Use the `?url` import for the document stylesheet: a bare side-effect import is not guaranteed to be in the initial streamed `<head>` (see `/css`)
|
|
155
|
+
- `<link rel="preload" as="style">` starts the stylesheet download early
|
|
152
156
|
- Tailwind v4 does not need a `tailwind.config.js` -- use `@theme` in CSS instead
|
|
153
157
|
- The `@tailwindcss/vite` plugin handles content detection automatically
|
package/skills/testing/SKILL.md
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: testing
|
|
3
|
-
description: Test @rangojs/router apps — unit (loaders
|
|
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
|
|
|
7
7
|
# Testing @rangojs/router apps
|
|
8
8
|
|
|
9
|
+
This skill picks the right test layer for a behavior in a Rango app and gives
|
|
10
|
+
the API, a recipe, and the caveats for each testing primitive. Use it when
|
|
11
|
+
adding or changing tests, or when wiring the vitest/Playwright setup.
|
|
12
|
+
|
|
9
13
|
Rango ships six consumer-facing testing entries, one per test runtime/dependency:
|
|
10
14
|
`@rangojs/router/testing` (unit + integration, under a Vite-driven Vitest
|
|
11
15
|
project), `@rangojs/router/testing/vitest` (the `rangoTestConfig`/`rangoTestAliases`
|
|
12
16
|
setup preset), `@rangojs/router/testing/dom` (`renderRoute`, needs RTL + a DOM
|
|
13
17
|
env), `@rangojs/router/testing/e2e` (the Playwright harness),
|
|
14
18
|
`@rangojs/router/testing/flight` (real Flight, react-server condition only), and
|
|
15
|
-
`@rangojs/router/testing/flight-matchers` (the Flight matchers).
|
|
19
|
+
`@rangojs/router/testing/flight-matchers` (the Flight matchers). Host-router
|
|
20
|
+
pattern helpers live separately at `@rangojs/router/host/testing` (see
|
|
21
|
+
`/host-router`).
|
|
16
22
|
|
|
17
23
|
The hard problem in an RSC app is that the layer you reach for is dictated by
|
|
18
24
|
**what the behavior touches** — a pure predicate is a one-line vitest test; a real
|
|
@@ -42,8 +48,8 @@ Use this skill when adding or changing tests for a Rango app: a loader,
|
|
|
42
48
|
middleware, a server action, a route map, a client component, a response route,
|
|
43
49
|
cache/SWR behavior, prerender, or a navigation/PE flow.
|
|
44
50
|
|
|
45
|
-
Two non-negotiable mandates (
|
|
46
|
-
consumer apps too):
|
|
51
|
+
Two non-negotiable mandates (the Rango repo holds itself to these, and they
|
|
52
|
+
apply to consumer apps too):
|
|
47
53
|
|
|
48
54
|
- **Every e2e covers BOTH dev and production.** A dev-only e2e is not acceptable.
|
|
49
55
|
Use `parityDescribe` — it generates the dev and production describes from one
|
|
@@ -54,8 +60,10 @@ consumer apps too):
|
|
|
54
60
|
|
|
55
61
|
## The read-first shape
|
|
56
62
|
|
|
57
|
-
Four
|
|
58
|
-
split is forced by hard walls, not
|
|
63
|
+
Four of the six entries hold the test primitives, each matched to the
|
|
64
|
+
dependency/runtime that can load it — this split is forced by hard walls, not
|
|
65
|
+
preference. (The other two are helpers: `/testing/vitest` is config-only, and
|
|
66
|
+
`/testing/flight-matchers` holds `toMatchFlight`/`toMatchFlightSnapshot`.)
|
|
59
67
|
|
|
60
68
|
- `@rangojs/router/testing` — unit + integration primitives. Run these under a
|
|
61
69
|
**Vite-driven Vitest** project with the rango Vite plugin active (the router
|
|
@@ -81,26 +89,31 @@ The single rule that drives everything:
|
|
|
81
89
|
|
|
82
90
|
Each primitive links to its sub-file (API + recipe + caveats).
|
|
83
91
|
|
|
84
|
-
| The behavior is… | Layer | Primitive
|
|
85
|
-
| ---------------------------------------------------------------------------------------------------------------------------------- | ------------ |
|
|
86
|
-
| a
|
|
87
|
-
| one loader's data logic | unit (node) | [`runLoader`](./loader.md)
|
|
88
|
-
| a loader's cookie / header / thrown `redirect()`/`notFound()` / handle-push output | unit (node) | [`runLoaderResult`](./loader.md)
|
|
89
|
-
| one middleware's ordering / short-circuit / cookie+header merge | unit (node) | [`runMiddleware`](./middleware.md)
|
|
90
|
-
| a `"use server"` action's cookie / header / flash output (even on `throw redirect()`) | unit (node) | [`runInRequestContext`](./server-actions.md)
|
|
91
|
-
| a `transition({ when })` gate (keep/drop) against nav source / target / action metadata | unit (node) | `runTransitionWhen` (`{ kept, whenContext }`;
|
|
92
|
-
| a `clientUrls()` `revalidate()` / `isAction(ref)` predicate | unit (node) | `runClientRevalidate` (production
|
|
93
|
-
| a handle's `collect`/accumulator, a seeded handle read, or a loader handle write | unit | [`collectHandle` / seeded `handles` / `handlePushes`](./handles.md)
|
|
94
|
-
| a CLIENT component reading router context (`useParams`/`useReverse`/`Outlet`/`useNavigation`/`useLoader`) | unit (DOM) | [`renderRoute`](./client-components.md)
|
|
95
|
-
| a
|
|
96
|
-
| a
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
|
101
|
-
|
|
|
102
|
-
|
|
|
103
|
-
| 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 })` gate (keep/drop) against nav source / target / action metadata | unit (node) | [`runTransitionWhen`](./predicates.md) (`{ kept, whenContext }`; `{ ppr: true }` for pre-handler timing) | `@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 redirect / status / headers / cookies / **response route** (json/text/html/xml/md), no Flight | integration | [`dispatch`](./response-routes.md) | `@rangojs/router/testing` |
|
|
105
|
+
| 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` |
|
|
106
|
+
| the exact Flight **wire payload** shape (a drift snapshot) | RSC unit | [`renderToFlightString` + `toMatchFlightSnapshot`](./flight.md) | `@rangojs/router/testing/flight` |
|
|
107
|
+
| a real route **handler** `(ctx) => rsc` (params/loaders/vars -> rendered RSC + effects) | RSC unit | [`renderHandler`](./render-handler.md) | `@rangojs/router/testing/flight` |
|
|
108
|
+
| navigation, hydration, PE parity, view transitions, real SSR | e2e | [`createRangoE2E` -> `parityDescribe`/`expectParity`](./e2e-parity.md) | `@rangojs/router/testing/e2e` |
|
|
109
|
+
| 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` |
|
|
110
|
+
| a `"use cache"` hit in a loader (the same, in the react-server project) | RSC unit | [`runLoader` + seeded `cacheStore`](./loader.md) | `@rangojs/router/testing` |
|
|
111
|
+
| a loader's own `cache()` (HIT, `updateTag()` of a tag its body recorded) | RSC unit | [`runLoader` + `cache` + seeded `cacheStore`](./loader.md) | `@rangojs/router/testing` |
|
|
112
|
+
| cache hit/miss/stale, prerender (= a cache hit by design) | e2e + signal | [`assertCacheStatus` (header) / `assertCacheDecision` (telemetry sink)](./cache-prerender.md) | `@rangojs/router/testing[/e2e]` |
|
|
113
|
+
| 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` |
|
|
114
|
+
| 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]` |
|
|
115
|
+
| generated route map drift vs runtime | unit (node) | [`assertGeneratedRoutesMatch`](./reverse-and-types.md) | `@rangojs/router/testing` |
|
|
116
|
+
| a platform binding (`env.DB` / Durable Object / `env.R2`) | unit/integr. | [your own double via `env`](./bindings.md) | (any primitive's `env` option) |
|
|
104
117
|
|
|
105
118
|
Cross-references to the DSL skills: `/loader`, `/middleware`, `/server-actions`,
|
|
106
119
|
`/handler-use`, `/hooks`, `/response-routes`, `/route`, `/caching`, `/prerender`,
|
|
@@ -111,22 +124,49 @@ Cross-references to the DSL skills: `/loader`, `/middleware`, `/server-actions`,
|
|
|
111
124
|
- Cross-cutting: [`setup.md`](./setup.md), [`bindings.md`](./bindings.md)
|
|
112
125
|
- Unit (node): [`loader.md`](./loader.md), [`middleware.md`](./middleware.md),
|
|
113
126
|
[`server-actions.md`](./server-actions.md), [`handles.md`](./handles.md),
|
|
114
|
-
[`reverse-and-types.md`](./reverse-and-types.md)
|
|
127
|
+
[`predicates.md`](./predicates.md), [`reverse-and-types.md`](./reverse-and-types.md)
|
|
115
128
|
- Unit (DOM): [`client-components.md`](./client-components.md)
|
|
116
129
|
- RSC unit: [`flight.md`](./flight.md), [`server-tree.md`](./server-tree.md),
|
|
117
|
-
[`render-handler.md`](./render-handler.md)
|
|
130
|
+
[`render-handler.md`](./render-handler.md),
|
|
131
|
+
[`serve-shell-request.md`](./serve-shell-request.md)
|
|
118
132
|
- Integration: [`response-routes.md`](./response-routes.md)
|
|
119
133
|
- E2E: [`e2e-parity.md`](./e2e-parity.md), [`cache-prerender.md`](./cache-prerender.md)
|
|
120
134
|
|
|
121
|
-
##
|
|
122
|
-
|
|
123
|
-
|
|
135
|
+
## Every export, by entry
|
|
136
|
+
|
|
137
|
+
Where each runtime export is documented (types such as `RunLoaderOptions` sit
|
|
138
|
+
beside their function):
|
|
139
|
+
|
|
140
|
+
| Entry | Exports | Documented in |
|
|
141
|
+
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------- |
|
|
142
|
+
| `@rangojs/router/testing` | `runLoader`, `runLoaderResult` | [`loader.md`](./loader.md) |
|
|
143
|
+
| | `runMiddleware` | [`middleware.md`](./middleware.md) |
|
|
144
|
+
| | `runInRequestContext`, `createTestRequestContext`, `runWithRequestContext`, `toRequest`, `seedVariables` | [`server-actions.md`](./server-actions.md) |
|
|
145
|
+
| | `runTransitionWhen`, `runClientRevalidate` | [`predicates.md`](./predicates.md) |
|
|
146
|
+
| | `collectHandle` | [`handles.md`](./handles.md) |
|
|
147
|
+
| | `dispatch` | [`response-routes.md`](./response-routes.md) |
|
|
148
|
+
| | `assertCacheStatus`, `assertCacheDecision`, `parseCacheHeader`, `createCacheSink`, `filterCacheDecisions` | [`cache-prerender.md`](./cache-prerender.md) |
|
|
149
|
+
| | `assertShellStatus`, `parseShellStatus`, `shellCacheKey`, `SHELL_STATUS_HEADER`, `assertPprReplayStatus`, `parsePprReplayStatus`, `PPR_REPLAY_STATUS_HEADER` | [`cache-prerender.md`](./cache-prerender.md) |
|
|
150
|
+
| | `assertGeneratedRoutesMatch`, `diffGeneratedRoutes` | [`reverse-and-types.md`](./reverse-and-types.md) |
|
|
151
|
+
| `@rangojs/router/testing/dom` | `renderRoute` | [`client-components.md`](./client-components.md) |
|
|
152
|
+
| `@rangojs/router/testing/flight` | `renderToFlightString`, `normalizeFlight`, `assertFlightRuntimeAvailable` | [`flight.md`](./flight.md) |
|
|
153
|
+
| | `renderServerTree`, `findClientBoundaries`, `findElements`, `textContent`, `assertFlightTreeRuntimeAvailable` | [`server-tree.md`](./server-tree.md) |
|
|
154
|
+
| | `renderHandler` | [`render-handler.md`](./render-handler.md) |
|
|
155
|
+
| | `serveShellRequest`, `resetShellTestState` | [`serve-shell-request.md`](./serve-shell-request.md) |
|
|
156
|
+
| `@rangojs/router/testing/flight-matchers` | `flightMatchers` (`toMatchFlight`, `toMatchFlightSnapshot`) | [`flight.md`](./flight.md) |
|
|
157
|
+
| `@rangojs/router/testing/vitest` | `rangoTestConfig`, `rangoTestAliases`, `rangoInlineDeps`, `rangoUseClientTransform`, `rangoUseCacheTransform` | [`setup.md`](./setup.md) |
|
|
158
|
+
| `@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) |
|
|
159
|
+
|
|
160
|
+
## Pre-push checklist
|
|
161
|
+
|
|
162
|
+
Before pushing, run all of these and fix any failure (script names follow
|
|
163
|
+
[`./setup.md`](./setup.md); adjust to your `package.json`):
|
|
124
164
|
|
|
125
165
|
1. `pnpm run typecheck` (or `pnpm exec tsc --noEmit`)
|
|
126
166
|
2. `pnpm run test:unit` (node + DOM vitest)
|
|
127
167
|
3. `pnpm run test:unit:rsc` (the react-server Flight project)
|
|
128
|
-
4.
|
|
129
|
-
5.
|
|
168
|
+
4. your lint and format scripts
|
|
169
|
+
5. your Playwright suite (dev AND production projects)
|
|
130
170
|
|
|
131
171
|
And: **every e2e has a production counterpart.** `parityDescribe` makes this
|
|
132
172
|
automatic — if you wrote a plain `test.describe` for a behavior, convert it.
|