@rangojs/router 0.0.0-experimental.e9c0b2f2 → 0.0.0-experimental.ea9f40f2

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.
Files changed (222) hide show
  1. package/AGENTS.md +6 -10
  2. package/README.md +289 -938
  3. package/dist/bin/rango.js +271 -46
  4. package/dist/vite/index.js +673 -193
  5. package/package.json +10 -8
  6. package/skills/api-client/SKILL.md +1 -1
  7. package/skills/breadcrumbs/SKILL.md +31 -14
  8. package/skills/cache-guide/SKILL.md +5 -2
  9. package/skills/caching/SKILL.md +59 -4
  10. package/skills/catalog.json +271 -0
  11. package/skills/comparison/SKILL.md +50 -0
  12. package/skills/comparison/agents/openai.yaml +4 -0
  13. package/skills/comparison/references/framework-comparison.md +837 -0
  14. package/skills/composability/SKILL.md +83 -2
  15. package/skills/debug-manifest/SKILL.md +1 -1
  16. package/skills/defer-hydration/SKILL.md +235 -0
  17. package/skills/document-cache/SKILL.md +9 -1
  18. package/skills/fonts/SKILL.md +1 -1
  19. package/skills/handler-use/SKILL.md +8 -8
  20. package/skills/hooks/SKILL.md +54 -892
  21. package/skills/hooks/data.md +273 -0
  22. package/skills/hooks/handle-and-actions.md +103 -0
  23. package/skills/hooks/navigation.md +110 -0
  24. package/skills/hooks/outlets.md +41 -0
  25. package/skills/hooks/state.md +228 -0
  26. package/skills/hooks/urls.md +135 -0
  27. package/skills/host-router/SKILL.md +4 -4
  28. package/skills/i18n/SKILL.md +1 -1
  29. package/skills/intercept/SKILL.md +46 -14
  30. package/skills/layout/SKILL.md +27 -10
  31. package/skills/links/SKILL.md +1 -1
  32. package/skills/loader/SKILL.md +23 -1
  33. package/skills/middleware/SKILL.md +7 -3
  34. package/skills/migrate-nextjs/SKILL.md +167 -6
  35. package/skills/migrate-react-router/SKILL.md +59 -677
  36. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  37. package/skills/migrate-react-router/component-migration.md +196 -0
  38. package/skills/migrate-react-router/data-and-actions.md +225 -0
  39. package/skills/migrate-react-router/route-mapping.md +271 -0
  40. package/skills/mime-routes/SKILL.md +1 -1
  41. package/skills/observability/SKILL.md +9 -1
  42. package/skills/parallel/SKILL.md +23 -4
  43. package/skills/ppr/SKILL.md +622 -0
  44. package/skills/prerender/SKILL.md +28 -18
  45. package/skills/rango/SKILL.md +84 -25
  46. package/skills/response-routes/SKILL.md +15 -1
  47. package/skills/route/SKILL.md +71 -4
  48. package/skills/router-setup/SKILL.md +14 -3
  49. package/skills/scripts/SKILL.md +1 -1
  50. package/skills/server-actions/SKILL.md +3 -2
  51. package/skills/shell-manifest/SKILL.md +185 -0
  52. package/skills/streams-and-websockets/SKILL.md +1 -1
  53. package/skills/tailwind/SKILL.md +1 -1
  54. package/skills/testing/SKILL.md +2 -1
  55. package/skills/testing/handles.md +4 -2
  56. package/skills/testing/render-handler.md +15 -14
  57. package/skills/testing/reverse-and-types.md +8 -7
  58. package/skills/theme/SKILL.md +1 -1
  59. package/skills/typesafety/SKILL.md +45 -919
  60. package/skills/typesafety/env-and-bindings.md +254 -0
  61. package/skills/typesafety/generated-files-and-cli.md +335 -0
  62. package/skills/typesafety/params-and-search.md +153 -0
  63. package/skills/typesafety/route-types.md +209 -0
  64. package/skills/use-cache/SKILL.md +30 -3
  65. package/skills/vercel/SKILL.md +1 -1
  66. package/skills/view-transitions/SKILL.md +44 -1
  67. package/src/browser/event-controller.ts +62 -10
  68. package/src/browser/logging.ts +28 -0
  69. package/src/browser/merge-segment-loaders.ts +6 -4
  70. package/src/browser/navigation-bridge.ts +65 -16
  71. package/src/browser/navigation-client.ts +32 -2
  72. package/src/browser/navigation-store.ts +128 -14
  73. package/src/browser/network-error-handler.ts +34 -7
  74. package/src/browser/partial-update.ts +76 -17
  75. package/src/browser/prefetch/cache.ts +51 -11
  76. package/src/browser/prefetch/fetch.ts +59 -21
  77. package/src/browser/prefetch/queue.ts +19 -4
  78. package/src/browser/react/Link.tsx +13 -3
  79. package/src/browser/react/NavigationProvider.tsx +108 -4
  80. package/src/browser/response-adapter.ts +38 -9
  81. package/src/browser/rsc-router.tsx +54 -4
  82. package/src/browser/scroll-restoration.ts +7 -5
  83. package/src/browser/segment-reconciler.ts +31 -21
  84. package/src/browser/server-action-bridge.ts +22 -10
  85. package/src/browser/types.ts +54 -1
  86. package/src/build/generate-manifest.ts +155 -131
  87. package/src/build/index.ts +3 -1
  88. package/src/build/route-trie.ts +35 -7
  89. package/src/build/route-types/include-resolution.ts +347 -47
  90. package/src/build/runtime-discovery.ts +4 -1
  91. package/src/cache/cache-key-utils.ts +29 -0
  92. package/src/cache/cache-runtime.ts +262 -71
  93. package/src/cache/cache-scope.ts +2 -17
  94. package/src/cache/cache-tag.ts +60 -14
  95. package/src/cache/cf/cf-cache-store.ts +243 -20
  96. package/src/cache/document-cache.ts +54 -21
  97. package/src/cache/index.ts +1 -0
  98. package/src/cache/memory-segment-store.ts +110 -3
  99. package/src/cache/profile-registry.ts +15 -0
  100. package/src/cache/read-through-swr.ts +15 -1
  101. package/src/cache/segment-codec.ts +4 -4
  102. package/src/cache/shell-snapshot.ts +417 -0
  103. package/src/cache/types.ts +158 -0
  104. package/src/cache/vercel/vercel-cache-store.ts +401 -124
  105. package/src/client.rsc.tsx +0 -3
  106. package/src/client.tsx +0 -3
  107. package/src/cloudflare/tracing.ts +7 -8
  108. package/src/defer.ts +11 -22
  109. package/src/handle.ts +37 -15
  110. package/src/handles/MetaTags.tsx +16 -82
  111. package/src/handles/breadcrumbs.ts +12 -14
  112. package/src/handles/deferred-resolution.ts +127 -0
  113. package/src/handles/is-thenable.ts +7 -8
  114. package/src/handles/meta.ts +7 -44
  115. package/src/host/errors.ts +15 -0
  116. package/src/host/index.ts +1 -0
  117. package/src/index.rsc.ts +8 -2
  118. package/src/index.ts +19 -13
  119. package/src/internal-debug.ts +11 -8
  120. package/src/prerender.ts +17 -4
  121. package/src/redirect-origin.ts +14 -0
  122. package/src/render-error-thrower.tsx +20 -0
  123. package/src/route-content-wrapper.tsx +12 -5
  124. package/src/route-definition/dsl-helpers.ts +21 -32
  125. package/src/route-definition/helper-factories.ts +0 -2
  126. package/src/route-definition/helpers-types.ts +43 -43
  127. package/src/route-definition/index.ts +1 -2
  128. package/src/route-definition/resolve-handler-use.ts +0 -1
  129. package/src/route-definition/use-item-types.ts +3 -6
  130. package/src/route-map-builder.ts +41 -4
  131. package/src/route-types.ts +0 -5
  132. package/src/router/find-match.ts +86 -8
  133. package/src/router/instrument.ts +9 -4
  134. package/src/router/lazy-includes.ts +72 -12
  135. package/src/router/loader-resolution.ts +14 -2
  136. package/src/router/manifest.ts +56 -11
  137. package/src/router/match-api.ts +76 -32
  138. package/src/router/match-handlers.ts +181 -135
  139. package/src/router/match-middleware/background-revalidation.ts +40 -23
  140. package/src/router/match-middleware/cache-store.ts +39 -24
  141. package/src/router/match-result.ts +35 -15
  142. package/src/router/middleware.ts +64 -38
  143. package/src/router/navigation-snapshot.ts +7 -5
  144. package/src/router/parse-pattern.ts +115 -0
  145. package/src/router/pattern-matching.ts +53 -64
  146. package/src/router/prefetch-limits.ts +37 -0
  147. package/src/router/prerender-match.ts +11 -5
  148. package/src/router/preview-match.ts +3 -1
  149. package/src/router/request-classification.ts +23 -8
  150. package/src/router/route-snapshot.ts +14 -2
  151. package/src/router/router-context.ts +3 -1
  152. package/src/router/router-interfaces.ts +32 -1
  153. package/src/router/router-options.ts +30 -0
  154. package/src/router/segment-resolution/fresh.ts +39 -3
  155. package/src/router/segment-resolution/loader-cache.ts +93 -2
  156. package/src/router/segment-resolution/loader-mask.ts +60 -0
  157. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  158. package/src/router/segment-resolution/mask-nested.ts +83 -0
  159. package/src/router/segment-resolution/revalidation.ts +3 -0
  160. package/src/router/segment-resolution/view-transition-default.ts +35 -15
  161. package/src/router/substitute-pattern-params.ts +54 -35
  162. package/src/router/telemetry-otel.ts +6 -8
  163. package/src/router/telemetry.ts +9 -1
  164. package/src/router/tracing.ts +14 -5
  165. package/src/router/trie-matching.ts +19 -11
  166. package/src/router/url-params.ts +13 -0
  167. package/src/router.ts +47 -16
  168. package/src/rsc/full-payload.ts +70 -0
  169. package/src/rsc/handler.ts +60 -33
  170. package/src/rsc/manifest-init.ts +1 -1
  171. package/src/rsc/nonce.ts +10 -1
  172. package/src/rsc/progressive-enhancement.ts +61 -4
  173. package/src/rsc/redirect-guard.ts +2 -1
  174. package/src/rsc/rsc-rendering.ts +429 -37
  175. package/src/rsc/server-action.ts +25 -2
  176. package/src/rsc/shell-capture.ts +1190 -0
  177. package/src/rsc/shell-serve.ts +181 -0
  178. package/src/rsc/transition-gate.ts +89 -0
  179. package/src/rsc/types.ts +30 -0
  180. package/src/segment-loader-promise.ts +18 -0
  181. package/src/segment-system.tsx +149 -14
  182. package/src/server/context.ts +67 -9
  183. package/src/server/cookie-store.ts +73 -1
  184. package/src/server/loader-registry.ts +13 -1
  185. package/src/server/request-context.ts +169 -10
  186. package/src/ssr/index.tsx +462 -178
  187. package/src/ssr/inject-rsc-eager.ts +167 -0
  188. package/src/ssr/ssr-root.tsx +228 -0
  189. package/src/testing/collect-handle.ts +14 -8
  190. package/src/testing/dispatch.ts +152 -40
  191. package/src/testing/generated-routes.ts +27 -11
  192. package/src/testing/index.ts +6 -0
  193. package/src/testing/render-handler.ts +14 -0
  194. package/src/testing/render-route.tsx +13 -10
  195. package/src/testing/run-transition-when.ts +164 -0
  196. package/src/theme/ThemeProvider.tsx +36 -26
  197. package/src/types/handler-context.ts +1 -1
  198. package/src/types/index.ts +2 -0
  199. package/src/types/route-config.ts +19 -7
  200. package/src/types/segments.ts +100 -0
  201. package/src/urls/include-helper.ts +10 -8
  202. package/src/urls/include-provider.ts +71 -0
  203. package/src/urls/index.ts +1 -0
  204. package/src/urls/path-helper-types.ts +44 -12
  205. package/src/urls/path-helper.ts +5 -0
  206. package/src/urls/pattern-types.ts +36 -0
  207. package/src/urls/type-extraction.ts +43 -18
  208. package/src/urls/urls-function.ts +0 -1
  209. package/src/vercel/tracing.ts +7 -7
  210. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  211. package/src/vite/discovery/discover-routers.ts +1 -1
  212. package/src/vite/discovery/discovery-errors.ts +61 -0
  213. package/src/vite/index.ts +7 -0
  214. package/src/vite/inject-client-debug.ts +88 -0
  215. package/src/vite/plugins/vercel-output.ts +114 -25
  216. package/src/vite/plugins/version-injector.ts +22 -7
  217. package/src/vite/plugins/virtual-entries.ts +80 -22
  218. package/src/vite/rango.ts +29 -19
  219. package/src/vite/router-discovery.ts +171 -43
  220. package/src/vite/utils/prerender-utils.ts +17 -4
  221. package/src/vite/utils/shared-utils.ts +47 -0
  222. package/src/network-error-thrower.tsx +0 -18
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: streams-and-websockets
3
- description: Long-lived Response handlers — Server-Sent Events (SSE) via path.stream and WebSocket upgrades via path.any on Cloudflare Workers, including middleware interaction and runtime caveats.
3
+ description: Long-lived Response handlers — Server-Sent Events (SSE) via path.stream and WebSocket upgrades via path.any on Cloudflare Workers, including middleware interaction and runtime caveats. Use when streaming live updates to the browser, or opening a WebSocket connection from a route on Cloudflare Workers.
4
4
  argument-hint: "[sse | websocket | agents]"
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tailwind
3
- description: Set up Tailwind CSS v4 with the Document component and CSS imports
3
+ description: Set up Tailwind CSS v4 with the Document component and CSS imports. Use when adding Tailwind CSS to a Rango app, or Tailwind classes aren't being applied or generated.
4
4
  argument-hint: [setup]
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: testing
3
- description: Test @rangojs/router apps — unit (loaders/middleware/reverse/components), integration (dispatch/Flight), and e2e (dev+prod parity, progressive enhancement)
3
+ description: Test @rangojs/router apps — unit (loaders/middleware/reverse/components), integration (dispatch/Flight), and e2e (dev+prod parity, progressive enhancement). Use when writing a unit test for a loader or middleware, or asking how to test a route end-to-end in dev and production.
4
4
  argument-hint: [layer]
5
5
  ---
6
6
 
@@ -88,6 +88,7 @@ Each primitive links to its sub-file (API + recipe + caveats).
88
88
  | a loader's cookie / header / redirect output (auth-loader pattern) | unit (node) | [`runLoaderResult`](./loader.md) | `@rangojs/router/testing` |
89
89
  | one middleware's ordering / short-circuit / cookie+header merge | unit (node) | [`runMiddleware`](./middleware.md) | `@rangojs/router/testing` |
90
90
  | a `"use server"` action's cookie / header / flash output (even on `throw redirect()`) | unit (node) | [`runInRequestContext`](./server-actions.md) | `@rangojs/router/testing` |
91
+ | a `transition({ when })` gate (keep/drop) against nav source / target / action metadata | unit (node) | `runTransitionWhen` (`{ kept, whenContext }`) | `@rangojs/router/testing` |
91
92
  | a handle's `collect`/accumulator, or a seeded handle read | unit | [`collectHandle` / seeded `handles`](./handles.md) | `@rangojs/router/testing` |
92
93
  | a CLIENT component reading router context (`useParams`/`useReverse`/`Outlet`/`useNavigation`/`useLoader`) | unit (DOM) | [`renderRoute`](./client-components.md) | `@rangojs/router/testing/dom` |
93
94
  | a redirect / status / headers / cookies / **response route** (json/text/html/xml/md), no Flight | integration | [`dispatch`](./response-routes.md) | `@rangojs/router/testing` |
@@ -13,7 +13,7 @@ A handle's `collect`/accumulator (the `createHandle(collect)` argument that maps
13
13
  | `handle` | `Handle<TData, TAccumulated>` | The handle whose registered collect to run. |
14
14
  | `segments` | `ReadonlyArray<ReadonlyArray<TData>>` | Per-segment pushed values, one inner array per route segment, in **parent -> child** order. Empty inner arrays are filtered before the collect runs (matching production `collectHandleData` — a segment that pushed nothing is not passed through). |
15
15
 
16
- **Returns** `TAccumulated` — exactly what the handle's collect produces (a default-flatten array, or a custom accumulator's value). If the handle's module was never imported (collect unregistered), it warns and falls back to `segments.flat()`.
16
+ **Returns** `TAccumulated` — exactly what the handle's collect produces (the default identity collect's per-segment `TData[][]`, or a custom accumulator's value). If the handle's module was never imported (collect unregistered), it falls back to that same identity default and **warns** a handle with a custom collect that failed to register would otherwise return the wrong shape silently.
17
17
 
18
18
  ### runLoader option — `handles` — `src/testing/run-loader.ts`
19
19
 
@@ -40,7 +40,9 @@ import { describe, it, expect } from "vitest";
40
40
  import { collectHandle } from "@rangojs/router/testing";
41
41
  import { createHandle } from "@rangojs/router";
42
42
 
43
- const Breadcrumbs = createHandle<{ label: string; href: string }>(); // default flatten
43
+ // Opt into a flat list; the default collect groups per segment (TData[][]).
44
+ type Crumb = { label: string; href: string };
45
+ const Breadcrumbs = createHandle<Crumb, Crumb[]>((segments) => segments.flat());
44
46
 
45
47
  it("flattens per-segment crumbs in parent->child order", () => {
46
48
  const home = { label: "Home", href: "/" };
@@ -8,20 +8,21 @@ A Rango route handler is a pure function `(ctx) => rsc` — the function you pas
8
8
 
9
9
  ### Options — `RenderHandlerOptions`
10
10
 
11
- | Field | Type | Meaning |
12
- | ------------------ | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
13
- | `params` | `Record<string, string>` | Route params surfaced as `ctx.params`. |
14
- | `env` | `TEnv` | Environment bindings surfaced as `ctx.env`. |
15
- | `request` | `Request \| string` | Backing Request (string or `Request`); defaults to a localhost GET. |
16
- | `headers` | `HeadersInit` | Request headers (e.g. `Cookie`) the handler reads via `cookies()`. |
17
- | `vars` | `VarsInit` (object or `[[Var, value]]` tuples) | Variables a prior middleware set, read via `ctx.get(...)`. |
18
- | `routeName` | `string` | Matched route name (drives `ctx.routeName` and scoped reverse). |
19
- | `routeMap` | `Record<string, string>` | Route name -> pattern map enabling `ctx.reverse()`. |
20
- | `loaders` | `ReadonlyArray<readonly [LoaderDefinition, unknown]>` | Seed the data `ctx.use(SomeLoader)` returns. Matched by loader reference; NO real loader runs. |
21
- | `clientComponents` | `Record<string, unknown>` | `"use client"` components in the handler's RSC, so they serialize as real boundaries when `rangoUseClientTransform()` is not wired. Keyed by name. |
22
- | `stateCookie` | `StateCookieSeed` (`{ prefix?, routerId?, version? }`) | Customize the rango state cookie a handler calling `invalidateClientCache()` rotates. The name is ALWAYS seeded (default `rango-state_router_0`) so the rotation `Set-Cookie` fires like production rather than no-opping; override `prefix`/`routerId` to match your `createRouter({ stateCookiePrefix, id })`, or `version` (the value is `{version}:{timestamp}`, default `"0"`). |
23
- | `cacheStore` | `SegmentCacheStore` | Segment cache store backing a `"use cache"` function the handler invokes (e.g. `new MemorySegmentCacheStore()`). WITHOUT it, `registerCachedFunction` takes the uncached bypass and the cached path is NOT exercised (the runtime emits a one-time warning under the test runner). Pair with `cacheProfiles`. |
24
- | `cacheProfiles` | `Record<string, CacheProfile>` | Cache profiles in the `createRouter({ cacheProfiles })` shape, required for `"use cache: profileName"` resolution once a `cacheStore` is wired. |
11
+ | Field | Type | Meaning |
12
+ | ---------------------- | ------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
13
+ | `params` | `Record<string, string>` | Route params surfaced as `ctx.params`. |
14
+ | `env` | `TEnv` | Environment bindings surfaced as `ctx.env`. |
15
+ | `request` | `Request \| string` | Backing Request (string or `Request`); defaults to a localhost GET. |
16
+ | `headers` | `HeadersInit` | Request headers (e.g. `Cookie`) the handler reads via `cookies()`. |
17
+ | `vars` | `VarsInit` (object or `[[Var, value]]` tuples) | Variables a prior middleware set, read via `ctx.get(...)`. |
18
+ | `routeName` | `string` | Matched route name (drives `ctx.routeName` and scoped reverse). |
19
+ | `routeMap` | `Record<string, string>` | Route name -> pattern map enabling `ctx.reverse()`. |
20
+ | `loaders` | `ReadonlyArray<readonly [LoaderDefinition, unknown]>` | Seed the data `ctx.use(SomeLoader)` returns. Matched by loader reference; NO real loader runs. |
21
+ | `clientComponents` | `Record<string, unknown>` | `"use client"` components in the handler's RSC, so they serialize as real boundaries when `rangoUseClientTransform()` is not wired. Keyed by name. |
22
+ | `stateCookie` | `StateCookieSeed` (`{ prefix?, routerId?, version? }`) | Customize the rango state cookie a handler calling `invalidateClientCache()` rotates. The name is ALWAYS seeded (default `rango-state_router_0`) so the rotation `Set-Cookie` fires like production rather than no-opping; override `prefix`/`routerId` to match your `createRouter({ stateCookiePrefix, id })`, or `version` (the value is `{version}:{timestamp}`, default `"0"`). |
23
+ | `cacheStore` | `SegmentCacheStore` | Segment cache store backing a `"use cache"` function the handler invokes (e.g. `new MemorySegmentCacheStore()`). WITHOUT it, `registerCachedFunction` takes the uncached bypass and the cached path is NOT exercised (the runtime emits a one-time warning under the test runner). Pair with `cacheProfiles`. |
24
+ | `cacheProfiles` | `Record<string, CacheProfile>` | Cache profiles in the `createRouter({ cacheProfiles })` shape, required for `"use cache: profileName"` resolution once a `cacheStore` is wired. |
25
+ | `inActionRevalidation` | `boolean` | Render as if inside a server action's revalidation render (production sets this in `revalidateAfterAction`). A stale `"use cache"` entry whose profile sets `foregroundOnAction: true` then re-executes in the FOREGROUND (fresh result in this render) instead of SWR. Pair with `cacheStore` + `cacheProfiles` to exercise the opt-in. |
25
26
 
26
27
  ### Context — `HandlerContext` (what your handler receives)
27
28
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Layer:** unit (node) + typecheck · **Import:** `@rangojs/router/client` (useReverse), `@rangojs/router/testing` (assertGeneratedRoutesMatch) · **DSL it tests:** `reverse`/`href`/`useReverse` (see `/typesafety`, `/links`)
4
4
 
5
- The reverse/href/params/env types are a real contract: a wrong route name, a missing param, or an unknown env binding should be a COMPILE error, not a runtime surprise. The type-test recipes have no runtime API — `tsc --noEmit` IS the assertion. `assertGeneratedRoutesMatch` is the one runtime helper here: it runs the router's real matching to expand lazy includes, then diffs the live `routeMap` against the generated named-routes map you seed.
5
+ The reverse/href/params/env types are a real contract: a wrong route name, a missing param, or an unknown env binding should be a COMPILE error, not a runtime surprise. The type-test recipes have no runtime API — `tsc --noEmit` IS the assertion. `assertGeneratedRoutesMatch` is the one runtime helper here: it runs the router's real matching to expand lazy includes, then diffs the live `routeMap` against the generated named-routes map you seed. It (and `diffGeneratedRoutes`) are **async — `await` them** — because expanding an async `include(prefix, () => import("./routes"))` group means importing that module first.
6
6
 
7
7
  ## API
8
8
 
@@ -13,7 +13,7 @@ The reverse/href/params/env types are a real contract: a wrong route name, a mis
13
13
  | `router` | `{ routeMap; findMatch? }` | Your router (real impl). `routeMap` is the live name→pattern map; `findMatch` (when present) is called to force-expand lazy `include()`d routes. |
14
14
  | `generatedMap` | `Record<string, unknown>` (optional) | The imported `*.named-routes.gen.ts` map (name→pattern, or `{ path }` objects). Omit to diff against the global route map (`getGlobalRouteMap()`) instead. |
15
15
 
16
- ### Context — `GeneratedRoutesDiff` (what `diffGeneratedRoutes` returns)
16
+ ### Context — `GeneratedRoutesDiff` (what `diffGeneratedRoutes` resolves to)
17
17
 
18
18
  | Field | Type | Meaning |
19
19
  | ---------- | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -24,7 +24,7 @@ The reverse/href/params/env types are a real contract: a wrong route name, a mis
24
24
 
25
25
  ### Returns — `assertGeneratedRoutesMatch`
26
26
 
27
- `void` on match. On drift, throws an `Error` listing every missing, extra, and mismatched route plus a "regenerate the `*.named-routes.gen.ts` file" hint. (`diffGeneratedRoutes` returns the `GeneratedRoutesDiff` above without throwing.)
27
+ `Promise<void>` — **await it**. Resolves on match; on drift the promise REJECTS with an `Error` listing every missing, extra, and mismatched route plus a "regenerate the `*.named-routes.gen.ts` file" hint. (`diffGeneratedRoutes` resolves to the `GeneratedRoutesDiff` above and never rejects.) Both are async so an async `include(() => import())` group is imported and spliced into `routeMap` before the diff — call them without `await` and every route in that group reads as a false `missing`.
28
28
 
29
29
  ## Recipe
30
30
 
@@ -57,14 +57,15 @@ expectTypeOf<
57
57
 
58
58
  ```ts
59
59
  // 3. assertGeneratedRoutesMatch — a one-liner whole-app drift test. Real
60
- // matching expands lazy include()d routes before the diff.
60
+ // matching expands lazy include()d routes before the diff. It is async
61
+ // (it awaits findMatch to import async include groups), so await it.
61
62
  import { it } from "vitest";
62
63
  import { assertGeneratedRoutesMatch } from "@rangojs/router/testing";
63
64
  import { router } from "../src/router";
64
65
  import generated from "../src/router.named-routes.gen";
65
66
 
66
- it("generated named-routes map is in sync with the router", () => {
67
- assertGeneratedRoutesMatch(router, generated);
67
+ it("generated named-routes map is in sync with the router", async () => {
68
+ await assertGeneratedRoutesMatch(router, generated);
68
69
  });
69
70
  ```
70
71
 
@@ -74,7 +75,7 @@ For a large type-only suite, collect recipe-1/2 assertions in `*.test-d.ts` file
74
75
 
75
76
  - Type tests run at TYPECHECK time (`tsc --noEmit`), NOT in the vitest runner. They are their own layer — wire them into CI as a real step (`pnpm run typecheck`). A type test nobody runs is just a comment.
76
77
  - `@ts-expect-error` ERRORS if the line below it ever starts compiling, so a regressed guard fails the typecheck. A runtime test cannot assert "this should not type-check".
77
- - `assertGeneratedRoutesMatch` force-expands lazy `include()`d routes (calls `findMatch` on a concrete path derived from each generated pattern) before diffing — otherwise every included route reads as a false `missing`. This makes the whole-app drift check work in a plain unit test. Routers without `findMatch` (a bare `{ routeMap }`) are left as-is.
78
+ - `assertGeneratedRoutesMatch` force-expands lazy `include()`d routes (awaits `findMatch` on a concrete path derived from each generated pattern) before diffing — otherwise every included route reads as a false `missing`. It is **async** so it can await the module import behind an async `include(() => import())` group; `await` the call (or the async group's routes read as `missing`). This makes the whole-app drift check work in a plain unit test. Routers without `findMatch` (a bare `{ routeMap }`) are left as-is.
78
79
  - MULTI-APP route-map isolation. `href()`/`reverse()` typing is GLOBAL — each app's generated file augments the one `Rango.GeneratedRouteMap` interface. A `renderRoute` suite that imports a client component from app B (which calls `href("/b-route")`) won't typecheck if the same tsconfig program also carries app A's augmentation: A's route union rejects B's name. `renderRoute` is app-agnostic at RUNTIME; the collision is purely the global `href` typing. Keep a `renderRoute` suite single-app, or give each app its OWN tsconfig program (see `/typesafety`); a quick sidestep is to probe `useMount`/`useHref` inline instead of importing the cross-app component.
79
80
 
80
81
  ## See also
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: theme
3
- description: Opt-in theme system with FOUC prevention for light/dark mode
3
+ description: Opt-in theme system with FOUC prevention for light/dark mode. Use when adding a light/dark mode toggle, or the page flashes the wrong theme on load (FOUC).
4
4
  argument-hint: [setup]
5
5
  ---
6
6