@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
|
@@ -44,10 +44,10 @@ and multi-region composition without replacing its routing or data model.
|
|
|
44
44
|
|
|
45
45
|
| Dimension | **Rango** | Next.js (App Router) | TanStack Start | Waku |
|
|
46
46
|
| ----------------------- | ---------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | ----------------------------------------------------------------------- |
|
|
47
|
-
| Routing model | Code DSL (`urls()`/`include()`), named + `reverse()`
|
|
47
|
+
| Routing model | Code DSL (`urls()`/`include()`), named + `ctx.reverse()` | File-system convention | File or code, type-first | File-system (+ `createPages`) |
|
|
48
48
|
| Foundation | Vite + plugin-rsc | Turbopack (default in 16; Webpack opt-in) | [Vite or Rsbuild](https://tanstack.com/start/latest/docs/framework/react/overview) | Vite + plugin-rsc |
|
|
49
49
|
| RSC model | RSC-first | RSC-first | SSR/client-first; RSC opt-in (experimental) | RSC-first |
|
|
50
|
-
| Type-safe routes/params | Generated names, params, search, `reverse()`, response MIME
|
|
50
|
+
| Type-safe routes/params | Generated names, params, search, `ctx.reverse()`/`href()`, response MIME | Stable typed links; route-local param types | Best-in-class | Typed path params |
|
|
51
51
|
| Client render selection | Per-segment/loader `revalidate()` policy; typed action and result matching | Automatic segment reuse; refresh/invalidation APIs | Match/loader reload policy | Route refetch/reload |
|
|
52
52
|
| Slots and intercepts | Code-defined named slots with their own loaders/policy; conditional alternate soft-navigation compositions | File-system `@slot` + intercept conventions | Route masking, no parallel RSC slot graph | No equivalent route-slot graph |
|
|
53
53
|
| Caching | One segment store = runtime + build-time + `"use cache"`, tags, SWR | Cache Components plus distinct server/client cache lifecycles | Router loader cache + Query integration + HTTP/CDN policy | Minimal |
|
|
@@ -81,16 +81,16 @@ const router = createRouter().routes(({ path }) => [
|
|
|
81
81
|
As the application grows, each requirement adds one local primitive to that same
|
|
82
82
|
tree:
|
|
83
83
|
|
|
84
|
-
| When you need to… | Add…
|
|
85
|
-
| ------------------------------------------ |
|
|
86
|
-
| make navigation refactor-safe | a route `name` and `reverse()`
|
|
87
|
-
| split the application into modules | `urls()` and `include()`
|
|
88
|
-
| keep request data live beneath cached UI | `loader()`
|
|
89
|
-
| cache or prerender a shared shell | `cache()` or `Prerender()`
|
|
90
|
-
| choose what updates after an action | `revalidate()`
|
|
91
|
-
| render independent regions | `parallel()` and named slots
|
|
92
|
-
| open a route as a modal on soft navigation | `intercept()`
|
|
93
|
-
| inspect where request time went | `debugPerformance` or `telemetry`
|
|
84
|
+
| When you need to… | Add… |
|
|
85
|
+
| ------------------------------------------ | ---------------------------------- |
|
|
86
|
+
| make navigation refactor-safe | a route `name` and `ctx.reverse()` |
|
|
87
|
+
| split the application into modules | `urls()` and `include()` |
|
|
88
|
+
| keep request data live beneath cached UI | `loader()` |
|
|
89
|
+
| cache or prerender a shared shell | `cache()` or `Prerender()` |
|
|
90
|
+
| choose what updates after an action | `revalidate()` |
|
|
91
|
+
| render independent regions | `parallel()` and named slots |
|
|
92
|
+
| open a route as a modal on soft navigation | `intercept()` |
|
|
93
|
+
| inspect where request time went | `debugPerformance` or `telemetry` |
|
|
94
94
|
|
|
95
95
|
Nothing in the first route requires learning the last row. More importantly, the
|
|
96
96
|
last row does not force a migration to a second router, client data library, or
|
|
@@ -140,7 +140,11 @@ configure a node, so they can be imported or composed through small factories:
|
|
|
140
140
|
```tsx
|
|
141
141
|
const withAccountPolicy = () => [
|
|
142
142
|
middleware(requireUser),
|
|
143
|
-
|
|
143
|
+
// After an action, re-run only for account actions (a hard decision that ends
|
|
144
|
+
// the chain for later revalidators); on navigation, keep the default.
|
|
145
|
+
revalidate((ctx) =>
|
|
146
|
+
ctx.isAction() ? ctx.isAction(AccountActions) : undefined,
|
|
147
|
+
),
|
|
144
148
|
];
|
|
145
149
|
|
|
146
150
|
const accountPatterns = urls(({ path }) => [
|
|
@@ -208,7 +212,7 @@ you statically generate your top 1,000 products and serve the long tail
|
|
|
208
212
|
dynamically, from one definition. Next.js approximates this with
|
|
209
213
|
`generateStaticParams` + `dynamicParams`, but that is two separate knobs rather
|
|
210
214
|
than one decision point inside your handler. See
|
|
211
|
-
[prerender-api-design.md](
|
|
215
|
+
[prerender-api-design.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/prerender-api-design.md).
|
|
212
216
|
|
|
213
217
|
### Progressive enhancement is a tested contract
|
|
214
218
|
|
|
@@ -297,8 +301,8 @@ The router gives you guarantees most file-based routers leave implicit:
|
|
|
297
301
|
only; parallel siblings do not bleed into each other.
|
|
298
302
|
- **Dev/prod matching parity** — one trie is used in both dev and production, so
|
|
299
303
|
matching cannot drift between them. See
|
|
300
|
-
[matching-and-lazy-discovery.md](
|
|
301
|
-
and [execution-model.md](
|
|
304
|
+
[matching-and-lazy-discovery.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/internal/matching-and-lazy-discovery.md)
|
|
305
|
+
and [execution-model.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/internal/execution-model.md).
|
|
302
306
|
|
|
303
307
|
### Observability you do not wire yourself
|
|
304
308
|
|
|
@@ -311,7 +315,7 @@ Next.js also ships automatic OTel instrumentation; Rango's distinction is that t
|
|
|
311
315
|
same router-owned phase registry drives its traces, local waterfall, and
|
|
312
316
|
`Server-Timing` output. TanStack's strength is client/data devtools; Waku does not
|
|
313
317
|
ship an equivalent request-phase model. See
|
|
314
|
-
[telemetry.md](
|
|
318
|
+
[telemetry.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/telemetry.md).
|
|
315
319
|
|
|
316
320
|
### Deploy to Node, Cloudflare, and Vercel — and host many apps behind one entry
|
|
317
321
|
|
|
@@ -367,15 +371,19 @@ old-deployment request pinning.
|
|
|
367
371
|
### A shipped testing harness for server code
|
|
368
372
|
|
|
369
373
|
The `@rangojs/router/testing/*` entry points give you `runLoader`,
|
|
370
|
-
`runMiddleware`, `
|
|
371
|
-
rendering (`renderServerTree`,
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
`./testing/
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
374
|
+
`runMiddleware`, `runInRequestContext` (server actions), `dispatch` (base
|
|
375
|
+
`./testing`), `renderHandler` and real Flight rendering (`renderServerTree`,
|
|
376
|
+
`findClientBoundaries`, `findElements`) plus a real PPR shell capture and HIT
|
|
377
|
+
(`serveShellRequest`, `resetShellTestState`) from `./testing/flight`,
|
|
378
|
+
`renderRoute` from `./testing/dom`, a vitest preset from `./testing/vitest`, and
|
|
379
|
+
a Playwright e2e harness with dev/prod parity helpers (`parityDescribe`,
|
|
380
|
+
`expectParity`) from `./testing/e2e`. You can unit-test a loader, a middleware,
|
|
381
|
+
an action, or an RSC handler in isolation. Next.js and Waku ship no official
|
|
382
|
+
primitives for testing server components/handlers; TanStack documents testing
|
|
383
|
+
patterns (build your own harness from `createRouter`/`createMemoryHistory`) but
|
|
384
|
+
ships no testing package, and nothing at the RSC-handler level. See the
|
|
385
|
+
[testing skill](../../testing/SKILL.md) and
|
|
386
|
+
[testing.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/testing.md).
|
|
379
387
|
|
|
380
388
|
### Bundle discipline
|
|
381
389
|
|
|
@@ -386,7 +394,7 @@ the build if React's development bundle leaks into production. Treat those as
|
|
|
386
394
|
Rango baselines, not a normalized cross-framework benchmark: application shape,
|
|
387
395
|
React version, compiler output, and deployment transforms make headline bundle
|
|
388
396
|
comparisons unreliable. Waku deliberately targets a smaller surface. See
|
|
389
|
-
[client-chunking.md](
|
|
397
|
+
[client-chunking.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/client-chunking.md).
|
|
390
398
|
|
|
391
399
|
## Runtime mechanics in depth
|
|
392
400
|
|
|
@@ -495,7 +503,7 @@ layout(<ShopLayout />, () => [
|
|
|
495
503
|
A click from the list renders `ProductQuickView` into `@modal` and preserves the
|
|
496
504
|
list behind it. A direct visit or reload renders the canonical full page. Back
|
|
497
505
|
closes the modal and restores the preserved background. The intercept can own its
|
|
498
|
-
middleware, layout, loaders, loading
|
|
506
|
+
middleware, layout, loaders, loading state, and loader-level caching and
|
|
499
507
|
`revalidate()` rules; the `when` config selector (the 4th argument to
|
|
500
508
|
`intercept()`) can choose by navigation source.
|
|
501
509
|
|
|
@@ -521,17 +529,20 @@ There are two distinct things named "revalidate", and the split is deliberate:
|
|
|
521
529
|
downstream revalidators in the same decision chain. This is surgical
|
|
522
530
|
post-action control: "this widget re-renders only when `addToCart` ran; the
|
|
523
531
|
rest of the tree stays put."
|
|
524
|
-
See [is-action-api-design.md](
|
|
532
|
+
See [is-action-api-design.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/design/is-action-api-design.md).
|
|
525
533
|
2. **`revalidate: false` on `<Link>` / `navigate()`** — shallow navigation. When
|
|
526
534
|
the pathname is unchanged (a search or hash change), it updates the URL and all
|
|
527
535
|
location hooks but skips the server fetch and re-render entirely. For filters,
|
|
528
536
|
tabs, and pagination. See
|
|
529
|
-
[shallow-navigation.md](
|
|
530
|
-
|
|
531
|
-
Both are separate from `revalidateTag()`/`updateTag()`, which
|
|
532
|
-
cache entries
|
|
533
|
-
`
|
|
534
|
-
|
|
537
|
+
[shallow-navigation.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/design/shallow-navigation.md).
|
|
538
|
+
|
|
539
|
+
Both are separate from `revalidateTag()`/`updateTag()`, which evict tagged
|
|
540
|
+
cache entries; the request that calls either reads its own writes.
|
|
541
|
+
`updateTag()` also awaits the durable write; `revalidateTag()` schedules it in
|
|
542
|
+
the background. The mutating user's next requests skip the stores' per-isolate
|
|
543
|
+
PPR shell memos via a fresh-reads cookie, and other users converge within the
|
|
544
|
+
memo window and the platform's own propagation. Neither API selects a client
|
|
545
|
+
segment to render. Next.js offers `router.refresh()` (and
|
|
535
546
|
`refresh()` from `next/cache`) plus path/tag cache invalidation, but no per-segment
|
|
536
547
|
render predicate and no typed action discrimination — and its invalidations clear
|
|
537
548
|
the whole client cache rather than selecting a segment. TanStack's `shouldReload`/`router.invalidate()` is the nearest
|
|
@@ -614,18 +625,22 @@ this resource-aware policy layer.
|
|
|
614
625
|
|
|
615
626
|
`createLoader(fn)` / `loader()` define **live-by-default** data units: they are
|
|
616
627
|
excluded from an enclosing segment cache and resolve on every request unless the
|
|
617
|
-
loader itself explicitly opts into `cache()`.
|
|
618
|
-
`headers()`, request context, and `env` because
|
|
619
|
-
cached shell
|
|
620
|
-
|
|
621
|
-
route
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
628
|
+
loader itself explicitly opts into `cache()`. A loader without `ssr: false` may
|
|
629
|
+
safely read `cookies()`, `headers()`, request context, and `env` because its
|
|
630
|
+
execution is outside the cached shell; a loader with its own `cache()` must put
|
|
631
|
+
those reads in its `key()`, or its miss fails and stores nothing. On a `ppr`
|
|
632
|
+
route an `ssr: false` loader bakes into the shell, so an identity read there
|
|
633
|
+
refuses the capture. Loaders run in parallel, stream independently under
|
|
634
|
+
`loading()` boundaries, compose server-side via `ctx.use(OtherLoader)`, and
|
|
635
|
+
carry route-level authority: they can throw `notFound()`/`redirect()`, WRITE
|
|
636
|
+
handles (`ctx.use(Meta)({ title })` — data-derived page metadata pushed from the
|
|
637
|
+
data's producer), read handle data after the render settles (`ctx.get(handle)`
|
|
638
|
+
behind `await ctx.rendered()`), and opt into `loader(Def, { ssr: false })` so a
|
|
639
|
+
document render awaits them before first flush (deterministic SSR'd data, meta,
|
|
640
|
+
and 404 status). Reads happen through `useLoader` in a client component
|
|
641
|
+
(including its SSR pass) or `useFetchLoader` for standalone client fetches.
|
|
642
|
+
"Fetchable" loaders are callable endpoints with their own middleware and
|
|
643
|
+
GET/POST/PUT/PATCH/DELETE bodies.
|
|
629
644
|
|
|
630
645
|
One sharp edge is worth stating because the distinction matters: a cached handler
|
|
631
646
|
can call `await ctx.use(Loader)`, but if it renders that result inline, it bakes the
|
|
@@ -675,7 +690,7 @@ a fully prerendered route. Caching loader data is a separate, explicit opt-in on
|
|
|
675
690
|
that loader.
|
|
676
691
|
|
|
677
692
|
The mechanism is concrete: a prerendered route serves its baked Flight shell from
|
|
678
|
-
the store, then
|
|
693
|
+
the store, then the router resolves the loaders through their own
|
|
679
694
|
freshness policy at request time and merges their segments into that replayed
|
|
680
695
|
shell. With the default policy you get a cached/prerendered shell plus live data,
|
|
681
696
|
automatically, with no per-route static-vs-dynamic decision. The loader is the
|
|
@@ -686,7 +701,8 @@ designated live data slot in a cache-first RSC tree.** Several properties follow
|
|
|
686
701
|
that no loader-before-render has:
|
|
687
702
|
|
|
688
703
|
- it is the **cache-safety escape hatch** — the only place request-coupled reads
|
|
689
|
-
are allowed inside a cache scope, because it is the part guaranteed to re-run
|
|
704
|
+
are allowed inside a cache scope, because it is the part guaranteed to re-run
|
|
705
|
+
(a loader without `ssr: false` and without its own unkeyed `cache()`);
|
|
690
706
|
- it is **standalone and composable**, not route-coupled — one `createLoader()`
|
|
691
707
|
read by many segments, composed via `ctx.use`, or exposed as a fetchable endpoint;
|
|
692
708
|
- it **streams as a hole, not a gate** — concurrent, Suspense-resolved under
|
|
@@ -767,16 +783,18 @@ safe rule remains simple: read request-specific context at the live point of use
|
|
|
767
783
|
normally a loader, rather than deriving it outside and carrying it into a cached
|
|
768
784
|
shell.
|
|
769
785
|
|
|
770
|
-
This is where the taint and loader designs meet. Loaders are the sanctioned
|
|
771
|
-
holes: they run outside the enclosing segment cache and may read the
|
|
772
|
-
request,
|
|
773
|
-
|
|
774
|
-
|
|
786
|
+
This is where the taint and loader designs meet. Loaders are the sanctioned
|
|
787
|
+
dynamic holes: they run outside the enclosing segment cache and may read the
|
|
788
|
+
current request (except, on a `ppr` route, an `ssr: false` loader or a loader a
|
|
789
|
+
handler awaits, which the capture runs and refuses on such a read), while the
|
|
790
|
+
shared shell remains protected. Next.js also isolates runtime APIs from ordinary
|
|
791
|
+
`"use cache"`, and React's optional taint APIs protect values at the
|
|
792
|
+
server-to-client serialization boundary. Those are useful but different
|
|
775
793
|
contracts; they do not provide Rango's route-aware tainted-argument keying,
|
|
776
794
|
non-cacheable typed context variables, handle capture/replay, and loader escape
|
|
777
795
|
path as one system. TanStack Start and Waku leave this boundary primarily to
|
|
778
796
|
application architecture. See the [cache guide](../../cache-guide/SKILL.md) and
|
|
779
|
-
[`"use cache"` design](
|
|
797
|
+
[`"use cache"` design](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/use-cache-api-design.md).
|
|
780
798
|
|
|
781
799
|
## Performance diagnostics (`debugPerformance`)
|
|
782
800
|
|
|
@@ -809,7 +827,7 @@ Next.js has no equivalent flip-a-boolean per-request phase waterfall: its automa
|
|
|
809
827
|
OTel spans still need an exporter and trace viewer. TanStack's strength is
|
|
810
828
|
client/data devtools, a different axis from server request-phase diagnostics. Waku
|
|
811
829
|
does not ship an equivalent phase model. See
|
|
812
|
-
[telemetry.md](
|
|
830
|
+
[telemetry.md](https://github.com/rangojs/rango/blob/main/packages/rangojs-router/docs/telemetry.md).
|
|
813
831
|
|
|
814
832
|
## Coming from a specific framework
|
|
815
833
|
|
|
@@ -819,7 +837,7 @@ layers for a code-defined, refactor-safe router and one caching model. You gain
|
|
|
819
837
|
Cloudflare as a first-class target and phase spans for free. You give up the
|
|
820
838
|
largest ecosystem and talent pool in the React world — the real cost.
|
|
821
839
|
|
|
822
|
-
**From TanStack Start.** You get comparable type-safe routing (`reverse()`, typed
|
|
840
|
+
**From TanStack Start.** You get comparable type-safe routing (`ctx.reverse()`/`href()`, typed
|
|
823
841
|
params/search, even typed response MIME via `.json()`/`.text()`/`.image()`), plus a
|
|
824
842
|
true RSC-first model (zero-bundle server components, Flight) that TanStack's
|
|
825
843
|
client-first core added later and opt-in. You give up TanStack's best-in-class
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: composability
|
|
3
|
-
description: Reusable composition patterns with globally importable route helpers in @rangojs/router. Use when sharing loaders, middleware, or
|
|
3
|
+
description: Reusable composition patterns with globally importable route helpers and include() modules in @rangojs/router. Use when sharing loaders, middleware, or caching config across multiple route files, splitting routes into eager or code-split include() groups, or avoiding copy-pasting the same route setup everywhere.
|
|
4
4
|
argument-hint: "pattern-name"
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,6 +8,11 @@ argument-hint: "pattern-name"
|
|
|
8
8
|
|
|
9
9
|
Route helpers can be imported directly from `@rangojs/router` and used to build reusable composition factories. This enables sharing common route configurations across multiple routes and modules.
|
|
10
10
|
|
|
11
|
+
Use this skill when the same config (auth middleware, caching, loading and error
|
|
12
|
+
UI, revalidation) repeats across routes, when splitting a route tree into
|
|
13
|
+
modules with `include()`, or when sizing code-split route groups. To attach
|
|
14
|
+
defaults to one handler instead of a factory, see `/handler-use`.
|
|
15
|
+
|
|
11
16
|
## Globally Importable Helpers
|
|
12
17
|
|
|
13
18
|
These helpers can be imported and called outside the `urls()` callback parameter:
|
|
@@ -24,10 +29,11 @@ import {
|
|
|
24
29
|
intercept,
|
|
25
30
|
errorBoundary,
|
|
26
31
|
notFoundBoundary,
|
|
32
|
+
transition,
|
|
27
33
|
} from "@rangojs/router";
|
|
28
34
|
```
|
|
29
35
|
|
|
30
|
-
They work because they
|
|
36
|
+
They work because they read the active `urls()` builder from AsyncLocalStorage at call time, not import time. Calling one outside a running `urls()` builder throws a `DslContextError`.
|
|
31
37
|
|
|
32
38
|
## Why path() and include() Are Not Global
|
|
33
39
|
|
|
@@ -49,26 +55,25 @@ The globally importable helpers (`cache`, `middleware`, `loading`, etc.) are con
|
|
|
49
55
|
Define reusable factories that return arrays of use items:
|
|
50
56
|
|
|
51
57
|
```typescript
|
|
52
|
-
|
|
58
|
+
// route-config.tsx
|
|
59
|
+
import type { ReactNode } from "react";
|
|
60
|
+
import { cache, loading, errorBoundary, middleware } from "@rangojs/router";
|
|
61
|
+
import { authMiddleware, loggingMiddleware } from "./middleware";
|
|
53
62
|
|
|
54
63
|
// Shared caching configuration
|
|
55
|
-
const withCaching = () => [
|
|
56
|
-
cache({ ttl:
|
|
57
|
-
// Defer on navigation (|| undefined) so each route keeps its own param/search
|
|
58
|
-
// revalidation default; only force a re-run when an action ran.
|
|
59
|
-
revalidate(({ actionId }) => (actionId ? true : undefined)),
|
|
64
|
+
export const withCaching = () => [
|
|
65
|
+
cache({ ttl: 600 }), // seconds
|
|
60
66
|
];
|
|
61
67
|
|
|
62
68
|
// Shared loading and error handling
|
|
63
|
-
const withLoadingAndError = (skeleton: ReactNode) => [
|
|
69
|
+
export const withLoadingAndError = (skeleton: ReactNode) => [
|
|
64
70
|
loading(skeleton),
|
|
65
71
|
errorBoundary(() => <div>Something went wrong</div>),
|
|
66
72
|
];
|
|
67
73
|
|
|
68
|
-
// Shared
|
|
69
|
-
const withAuth = () => [
|
|
70
|
-
middleware(authMiddleware),
|
|
71
|
-
middleware(loggingMiddleware),
|
|
74
|
+
// Shared middleware (the array form registers both in order)
|
|
75
|
+
export const withAuth = () => [
|
|
76
|
+
middleware([authMiddleware, loggingMiddleware]),
|
|
72
77
|
];
|
|
73
78
|
```
|
|
74
79
|
|
|
@@ -97,7 +102,7 @@ const withAuth = () => [
|
|
|
97
102
|
|
|
98
103
|
## Using Factories in Routes
|
|
99
104
|
|
|
100
|
-
Place factory calls inside `path()`
|
|
105
|
+
Place factory calls inside any use callback (`path()`, `layout()`, `parallel()`, `intercept()`, `loader()`), as long as every item is valid there. The returned arrays are flattened automatically (up to 3 levels):
|
|
101
106
|
|
|
102
107
|
```typescript
|
|
103
108
|
import { urls } from "@rangojs/router";
|
|
@@ -126,13 +131,10 @@ Factories can be defined in shared modules and reused across separate `urls()` d
|
|
|
126
131
|
|
|
127
132
|
```typescript
|
|
128
133
|
// src/route-config.ts
|
|
129
|
-
import { cache,
|
|
134
|
+
import { cache, middleware } from "@rangojs/router";
|
|
130
135
|
import { authMiddleware } from "./middleware/auth";
|
|
131
136
|
|
|
132
|
-
export const withPublicDefaults = () => [
|
|
133
|
-
cache({ ttl: 300 }),
|
|
134
|
-
revalidate(({ actionId }) => (actionId ? true : undefined)),
|
|
135
|
-
];
|
|
137
|
+
export const withPublicDefaults = () => [cache({ ttl: 300 })];
|
|
136
138
|
|
|
137
139
|
export const withProtectedDefaults = () => [
|
|
138
140
|
middleware(authMiddleware),
|
|
@@ -195,7 +197,10 @@ urls(({ include }) => [
|
|
|
195
197
|
]);
|
|
196
198
|
```
|
|
197
199
|
|
|
198
|
-
The
|
|
200
|
+
The provider must resolve to a `urls()` value — either as the module's `default`
|
|
201
|
+
export (the usual form) or returned directly
|
|
202
|
+
(`() => import("./shop-patterns").then((m) => m.shopPatterns)`). Anything else
|
|
203
|
+
throws when the group is first loaded:
|
|
199
204
|
|
|
200
205
|
```typescript
|
|
201
206
|
// src/shop-patterns.ts
|
|
@@ -219,7 +224,7 @@ splitting a thin group buys little). Both match identically at runtime — only
|
|
|
219
224
|
module's runtime evaluation timing differs.
|
|
220
225
|
|
|
221
226
|
What you do NOT lose by splitting: build-time discovery `await`s the provider, so
|
|
222
|
-
`href()`, `reverse()`, generated route types, and prerender still see every route
|
|
227
|
+
`href()`, `ctx.reverse()`, generated route types, and prerender still see every route
|
|
223
228
|
in the group — including nested `include()`s inside the split module. Only the
|
|
224
229
|
module's runtime evaluation defers. `rango generate` resolves the `() => import()`
|
|
225
230
|
the same way, so a code-split group is still fully typed.
|
|
@@ -227,8 +232,10 @@ the same way, so a code-split group is still fully typed.
|
|
|
227
232
|
### Sizing async include groups (measured)
|
|
228
233
|
|
|
229
234
|
The first request into an async group pays that group's chunk import; every
|
|
230
|
-
request after that is flat.
|
|
231
|
-
26k
|
|
235
|
+
request after that is flat. One cold, single-run measurement right after
|
|
236
|
+
deploying a 26k-route app to Cloudflare Workers (2026-07, warm RTT floor
|
|
237
|
+
~23 ms, see `tests/cloudflare-stress-demo/BENCHMARK-2026-07-04-edge-26k.md`).
|
|
238
|
+
Treat the numbers as orders of magnitude:
|
|
232
239
|
|
|
233
240
|
| Group size | First-hit latency |
|
|
234
241
|
| -------------------------- | ------------------------------------ |
|
|
@@ -261,11 +268,12 @@ erases first-hit cost for the isolate entirely.
|
|
|
261
268
|
For typed factories, import the composition types:
|
|
262
269
|
|
|
263
270
|
```typescript
|
|
271
|
+
import { cache, loading, middleware } from "@rangojs/router";
|
|
264
272
|
import type { RouteUseItem, LayoutUseItem, UseItems } from "@rangojs/router";
|
|
265
273
|
|
|
266
274
|
// Factory for path() use callbacks
|
|
267
275
|
const withCaching = (): RouteUseItem[] => [
|
|
268
|
-
cache({ ttl:
|
|
276
|
+
cache({ ttl: 600 }),
|
|
269
277
|
];
|
|
270
278
|
|
|
271
279
|
// Factory for layout() use callbacks
|
|
@@ -281,12 +289,13 @@ const withEverything = (): UseItems<RouteUseItem> => [
|
|
|
281
289
|
```
|
|
282
290
|
|
|
283
291
|
- `RouteUseItem[]` -- flat array for `path()` use callbacks
|
|
284
|
-
- `LayoutUseItem[]` -- flat array for `layout()` use callbacks
|
|
285
|
-
- `UseItems<T>` -- allows nested arrays from composing factories together
|
|
292
|
+
- `LayoutUseItem[]` -- flat array for `layout()` use callbacks (every item type)
|
|
293
|
+
- `UseItems<T>` -- allows one level of nested arrays from composing factories together
|
|
294
|
+
- `HandlerUseItem` -- items a `handler.use` callback may return (see `/handler-use`)
|
|
286
295
|
|
|
287
296
|
## Rules
|
|
288
297
|
|
|
289
|
-
- Helpers execute lazily -- factory functions are defined anywhere, but only called
|
|
290
|
-
- Calling helpers outside a `urls()`
|
|
298
|
+
- Helpers execute lazily -- factory functions are defined anywhere, but only called while a `urls()` builder runs (inside a use callback)
|
|
299
|
+
- Calling helpers outside a `urls()` builder throws a `DslContextError`
|
|
291
300
|
- Nested arrays from factories are flattened automatically via `.flat(3)`
|
|
292
|
-
- `path()` and `include()`
|
|
301
|
+
- `path()` and `include()` are not exported as standalone helpers -- they define route structure and must remain visible in the `urls()` callback
|
package/skills/css/SKILL.md
CHANGED
|
@@ -7,9 +7,10 @@ argument-hint:
|
|
|
7
7
|
# CSS imports
|
|
8
8
|
|
|
9
9
|
Document/app CSS in Rango lives in the Document `<head>`, loaded with Vite's
|
|
10
|
-
`?url` import and a `precedence`-managed `<link rel="stylesheet">`. This page
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
`?url` import and a `precedence`-managed `<link rel="stylesheet">`. This page
|
|
11
|
+
explains that pattern, when a side-effect `import "./x.css"` is fine, and how CSS
|
|
12
|
+
behaves across host-router apps. `/tailwind` is the concrete Tailwind setup,
|
|
13
|
+
`/theme` is dark mode, `/fonts` is fonts.
|
|
13
14
|
|
|
14
15
|
## The pattern
|
|
15
16
|
|
|
@@ -18,7 +19,7 @@ is dark mode, `/fonts` is fonts.
|
|
|
18
19
|
"use client";
|
|
19
20
|
|
|
20
21
|
import type { ReactNode } from "react";
|
|
21
|
-
import {
|
|
22
|
+
import { Html } from "@rangojs/router/client";
|
|
22
23
|
import styles from "./index.css?url";
|
|
23
24
|
|
|
24
25
|
export function Document({ children }: { children: ReactNode }) {
|
|
@@ -27,7 +28,7 @@ export function Document({ children }: { children: ReactNode }) {
|
|
|
27
28
|
<head>
|
|
28
29
|
<link rel="preload" href={styles} as="style" precedence="default" />
|
|
29
30
|
<link rel="stylesheet" href={styles} precedence="default" />
|
|
30
|
-
<
|
|
31
|
+
<Html.Meta />
|
|
31
32
|
</head>
|
|
32
33
|
<body>{children}</body>
|
|
33
34
|
</html>
|
|
@@ -50,14 +51,10 @@ A client-side navigation that crosses an app boundary is a **full document load*
|
|
|
50
51
|
(the server returns `X-RSC-Reload` on an app switch — see `/host-router`), so the
|
|
51
52
|
target app's entire document — its stylesheets, theme, meta — is re-established
|
|
52
53
|
by the target app's own load. Each app owns its document; how one app renders a
|
|
53
|
-
stylesheet has no effect on another.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
to one hashed asset — could be silently dropped by React's by-`href` resource
|
|
58
|
-
dedup when the apps disagreed on `precedence` (one unmanaged, one managed). The
|
|
59
|
-
full reload removes that footgun entirely, which is the main reason cross-app
|
|
60
|
-
navigation is a hard boundary.)
|
|
54
|
+
stylesheet has no effect on another. In particular, apps may disagree on
|
|
55
|
+
`precedence` for a stylesheet they share by `href` (for example every app's
|
|
56
|
+
`@import "tailwindcss"` compiling to one hashed asset) without React's by-`href`
|
|
57
|
+
dedup dropping it.
|
|
61
58
|
|
|
62
59
|
## Side-effect imports vs `?url`
|
|
63
60
|
|
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: debug-manifest
|
|
3
|
-
description:
|
|
3
|
+
description: Inspect the router's route manifest (route/layout tree, shortCodes, parent links, loader/middleware flags) and diff it between versions. Use when routes aren't matching as expected, you need to see the generated route tree, or a path resolves to the wrong handler.
|
|
4
4
|
argument-hint:
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Debug Manifest
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Dump the route manifest the router builds from your `urls()` tree to verify
|
|
10
|
+
parent relationships, shortCodes, and which segments carry loaders, middleware,
|
|
11
|
+
error boundaries, parallel slots, or intercepts. This is a development aid; the
|
|
12
|
+
diff helpers below are internal and may change between releases.
|
|
10
13
|
|
|
11
|
-
## Programmatic
|
|
14
|
+
## Programmatic access
|
|
12
15
|
|
|
13
|
-
|
|
16
|
+
The router returned by `createRouter()` has an async `debugManifest()` method:
|
|
14
17
|
|
|
15
18
|
```typescript
|
|
16
19
|
import { router } from "./router.js";
|
|
17
20
|
|
|
18
|
-
//
|
|
21
|
+
// Development only
|
|
19
22
|
if (process.env.NODE_ENV !== "production") {
|
|
20
23
|
const manifest = await router.debugManifest();
|
|
21
24
|
console.log(JSON.stringify(manifest, null, 2));
|
|
22
25
|
}
|
|
23
26
|
```
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
Run it on the server side (for example from a dev-only route handler), since
|
|
29
|
+
it evaluates your `urls()` tree. The result type is `SerializedManifest`
|
|
30
|
+
(`import type { SerializedManifest } from "@rangojs/router"`).
|
|
26
31
|
|
|
27
|
-
|
|
32
|
+
### `include()` groups are not expanded
|
|
33
|
+
|
|
34
|
+
`include()` groups are lazy: the router evaluates one on the first request that
|
|
35
|
+
matches its prefix. `debugManifest()` does not evaluate them. It lists the
|
|
36
|
+
routes and layouts declared directly in each `.routes()` / `urls` mount, so the
|
|
37
|
+
routes inside an included group are missing from `routes` and `totalRoutes`.
|
|
38
|
+
For their names and patterns, read the generated `*.named-routes.gen.ts` file,
|
|
39
|
+
which lists every route including those in `include()` groups.
|
|
40
|
+
|
|
41
|
+
## Manifest structure
|
|
42
|
+
|
|
43
|
+
`debugManifest()` returns `{ routes, layouts, totalRoutes, totalLayouts }`.
|
|
44
|
+
Every entry has the same fields; `pattern` is present on routes only. Cache
|
|
45
|
+
boundaries are listed under `layouts` with `type: "cache"`. Shape (values are
|
|
46
|
+
illustrative):
|
|
28
47
|
|
|
29
48
|
```json
|
|
30
49
|
{
|
|
@@ -47,7 +66,12 @@ The programmatic `router.debugManifest()` call returns `{ routes, layouts, total
|
|
|
47
66
|
"id": "debug.M0.$root",
|
|
48
67
|
"shortCode": "M0L0",
|
|
49
68
|
"type": "layout",
|
|
50
|
-
"parentShortCode": null
|
|
69
|
+
"parentShortCode": null,
|
|
70
|
+
"hasLoader": false,
|
|
71
|
+
"hasMiddleware": false,
|
|
72
|
+
"hasErrorBoundary": false,
|
|
73
|
+
"parallelCount": 0,
|
|
74
|
+
"interceptCount": 0
|
|
51
75
|
}
|
|
52
76
|
},
|
|
53
77
|
"totalRoutes": 45,
|
|
@@ -55,30 +79,39 @@ The programmatic `router.debugManifest()` call returns `{ routes, layouts, total
|
|
|
55
79
|
}
|
|
56
80
|
```
|
|
57
81
|
|
|
58
|
-
## ShortCode
|
|
82
|
+
## ShortCode format
|
|
59
83
|
|
|
60
|
-
|
|
61
|
-
| ------ | ---------------------------------------- |
|
|
62
|
-
| **M** | Mount index (multiple `.routes()` calls) |
|
|
63
|
-
| **L** | Layout |
|
|
64
|
-
| **C** | Cache boundary |
|
|
65
|
-
| **R** | Route |
|
|
66
|
-
| **P** | Parallel slot |
|
|
84
|
+
A shortCode is the parent's shortCode plus one segment per level:
|
|
67
85
|
|
|
68
|
-
|
|
86
|
+
| Prefix | Meaning |
|
|
87
|
+
| ------ | ----------------------------------------------------------------------- |
|
|
88
|
+
| **M** | Mount index (`.routes()` / `urls` registrations and `include()` mounts) |
|
|
89
|
+
| **L** | Layout (the router adds a root layout, `M<n>L0`) |
|
|
90
|
+
| **C** | Cache boundary |
|
|
91
|
+
| **R** | Route |
|
|
92
|
+
| **P** | Parallel slot |
|
|
93
|
+
| **I** | `include()` scope token, so included routes never collide with siblings |
|
|
69
94
|
|
|
70
|
-
|
|
95
|
+
Example: `M0L0L1C0R0` = Mount 0 → Root Layout → Nested Layout → Cache → Route.
|
|
96
|
+
At runtime, a route from an `include()` placed under the root layout gets a
|
|
97
|
+
shortCode such as `M0L0I0R0`.
|
|
71
98
|
|
|
72
|
-
|
|
73
|
-
2. **Correct hierarchy**: ShortCode should reflect nesting (e.g., `M0L0R0` not `M0R0`)
|
|
74
|
-
3. **Loaders attached**: Check `hasLoader: true` for routes with data requirements
|
|
75
|
-
4. **Intercepts registered**: `interceptCount > 0` for modal/overlay patterns
|
|
99
|
+
## Debugging checklist
|
|
76
100
|
|
|
77
|
-
|
|
101
|
+
1. **Correct hierarchy**: the shortCode reflects nesting (`M0L0L1R0` for a
|
|
102
|
+
route in a nested layout, not `M0L0R0`).
|
|
103
|
+
2. **Parents**: only a mount's root layout (`M<n>L0`) has
|
|
104
|
+
`parentShortCode: null`.
|
|
105
|
+
3. **Loaders attached**: `hasLoader: true` on routes/layouts that declare
|
|
106
|
+
`loader()`.
|
|
107
|
+
4. **Intercepts registered**: `interceptCount > 0` where you declared
|
|
108
|
+
`intercept()` for modal/overlay patterns.
|
|
109
|
+
5. **Slots**: `parallelCount` matches the number of `parallel()` slots.
|
|
110
|
+
|
|
111
|
+
## Comparing manifests
|
|
78
112
|
|
|
79
113
|
```typescript
|
|
80
114
|
import {
|
|
81
|
-
serializeManifest,
|
|
82
115
|
compareManifests,
|
|
83
116
|
formatManifestDiff,
|
|
84
117
|
} from "@rangojs/router/__internal";
|
|
@@ -91,16 +124,26 @@ const diff = compareManifests(oldManifest, newManifest);
|
|
|
91
124
|
console.log(formatManifestDiff(diff));
|
|
92
125
|
```
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
`compareManifests` reports added, removed, and changed routes and layouts
|
|
128
|
+
(field-level: `old` / `new` per changed field).
|
|
129
|
+
|
|
130
|
+
## Common issues
|
|
95
131
|
|
|
96
|
-
###
|
|
132
|
+
### A route sits under the wrong layout
|
|
97
133
|
|
|
98
|
-
|
|
134
|
+
Check that the `layout()` call wraps the route in its `use` callback
|
|
135
|
+
(`layout(<Shell />, () => [path(...)])`). A `path()` listed next to a
|
|
136
|
+
`layout()` is a sibling, not a child.
|
|
99
137
|
|
|
100
|
-
###
|
|
138
|
+
### Unexpected mount index
|
|
101
139
|
|
|
102
|
-
|
|
140
|
+
Each `.routes()` call (and the `urls` option) registers a separate mount (M0,
|
|
141
|
+
M1, ...), and each `include()` also takes a mount index. Routes registered by
|
|
142
|
+
separate `.routes()` calls do not share layouts or middleware; compose modules
|
|
143
|
+
with `include()` inside one `urls()` tree when they should (`/composability`).
|
|
103
144
|
|
|
104
|
-
###
|
|
145
|
+
### Missing flags
|
|
105
146
|
|
|
106
|
-
|
|
147
|
+
`hasMiddleware` / `hasLoader` / `hasErrorBoundary` are per entry. Middleware or
|
|
148
|
+
a loader declared on a parent layout shows on that layout, not on each child
|
|
149
|
+
route.
|