@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
@@ -0,0 +1,271 @@
1
+ # Project Setup and Route Mapping
2
+
3
+ ## 1. Project Setup
4
+
5
+ Replace React Router tooling with Vite + Rango:
6
+
7
+ ```bash
8
+ # Framework mode:
9
+ npm remove react-router @react-router/dev @react-router/node @react-router/serve
10
+ # Library mode:
11
+ npm remove react-router react-router-dom
12
+
13
+ npm install @rangojs/router
14
+ ```
15
+
16
+ Replace the `@react-router/dev` Vite plugin with `rango()`:
17
+
18
+ ```typescript
19
+ // vite.config.ts
20
+ // Before: import { reactRouter } from "@react-router/dev/vite";
21
+ import { defineConfig } from "vite";
22
+ import { rango } from "@rangojs/router/vite";
23
+
24
+ export default defineConfig({
25
+ plugins: [rango()],
26
+ });
27
+ ```
28
+
29
+ Delete `react-router.config.ts` — route configuration moves to the `urls()` DSL.
30
+
31
+ ```typescript
32
+ // src/router.tsx
33
+ import { createRouter } from "@rangojs/router";
34
+ import { Document } from "./document";
35
+ import { urlpatterns } from "./urls";
36
+
37
+ export default createRouter({
38
+ document: Document,
39
+ }).routes(urlpatterns);
40
+ ```
41
+
42
+ ## 2. Route Mapping
43
+
44
+ ### RR7 framework mode: route modules → urls() DSL
45
+
46
+ In framework mode, each route is a file with conventional exports (`loader`,
47
+ `action`, `default`, `meta`, `headers`, `shouldRevalidate`, `handle`,
48
+ `ErrorBoundary`, `HydrateFallback`). In Rango, all of these become part of the
49
+ `urls()` DSL or move into the server component handler:
50
+
51
+ ```text
52
+ RR7 route module export → Rango equivalent
53
+ ─────────────────────────────────────────────────────
54
+ default (Component) → handler in path()
55
+ loader → fetch in handler, or createLoader()
56
+ action → "use server" function
57
+ meta → ctx.use(Meta) in handler
58
+ headers → ctx.header() in handler or middleware
59
+ shouldRevalidate → revalidate() DSL
60
+ ErrorBoundary → errorBoundary() DSL
61
+ HydrateFallback → loading() DSL
62
+ handle → createHandle() for cross-segment data (breadcrumbs, etc.)
63
+ clientLoader / clientAction → "use client" component with React hooks
64
+ ```
65
+
66
+ #### Example: full route module migration
67
+
68
+ ```typescript
69
+ // RR7 framework mode: app/routes/product.$slug.tsx
70
+ import type { Route } from "./+types/product.$slug";
71
+
72
+ export async function loader({ params }: Route.LoaderArgs) {
73
+ const product = await getProduct(params.slug);
74
+ if (!product) throw new Response("Not Found", { status: 404 });
75
+ return { product };
76
+ }
77
+
78
+ export async function action({ request }: Route.ActionArgs) {
79
+ const formData = await request.formData();
80
+ await addToCart(formData.get("productId") as string);
81
+ return { ok: true };
82
+ }
83
+
84
+ export function meta({ data }: Route.MetaArgs) {
85
+ return [{ title: data.product.name }];
86
+ }
87
+
88
+ export function headers() {
89
+ return { "Cache-Control": "max-age=300" };
90
+ }
91
+
92
+ export function shouldRevalidate({ actionResult }) {
93
+ return !!actionResult;
94
+ }
95
+
96
+ export default function ProductPage({ loaderData }: Route.ComponentProps) {
97
+ return <div>{loaderData.product.name}</div>;
98
+ }
99
+
100
+ export function ErrorBoundary() {
101
+ return <div>Product error</div>;
102
+ }
103
+ ```
104
+
105
+ ```typescript
106
+ // Rango: urls.tsx + handler
107
+ import { notFound } from "@rangojs/router";
108
+
109
+ const ProductPage: Handler<"product"> = async (ctx) => {
110
+ const product = await getProduct(ctx.params.slug);
111
+ if (!product) notFound("Product not found");
112
+
113
+ const meta = ctx.use(Meta);
114
+ meta({ title: product.name });
115
+ ctx.header("Cache-Control", "max-age=300");
116
+
117
+ return <div>{product.name}</div>;
118
+ };
119
+
120
+ // In urls.tsx:
121
+ path("/product/:slug", ProductPage, { name: "product" }, () => [
122
+ revalidate(({ actionId }) => !!actionId),
123
+ errorBoundary(() => <div>Product error</div>),
124
+ loading(<ProductSkeleton />),
125
+ ])
126
+ ```
127
+
128
+ Key shift: the route module's scattered exports consolidate into the handler
129
+ (data fetching, meta, headers) and the DSL (revalidation, error boundary, loading).
130
+
131
+ ### RR7 file routing → urls() DSL
132
+
133
+ | RR7 file path | Rango |
134
+ | ---------------------------------------- | ------------------------------------------------------------- |
135
+ | `app/routes/_index.tsx` | `path("/", HomePage, { name: "home" })` |
136
+ | `app/routes/about.tsx` | `path("/about", AboutPage, { name: "about" })` |
137
+ | `app/routes/blog.$slug.tsx` | `path("/blog/:slug", BlogPost, { name: "blogPost" })` |
138
+ | `app/routes/files.$.tsx` (splat) | `path("/files/:path*", FileBrowser, { name: "files" })` |
139
+ | `app/routes/dashboard.tsx` (layout) | `layout(<DashboardLayout />, () => [...])` |
140
+ | `app/routes/dashboard._index.tsx` | `path("/dashboard", DashboardIndex, { name: "dashboard" })` |
141
+ | `app/routes/dashboard.settings.tsx` | `path("/dashboard/settings", Settings, { name: "settings" })` |
142
+ | `app/routes/_auth.tsx` (pathless layout) | `layout(<AuthLayout />, () => [...])` |
143
+ | `app/routes/_auth.login.tsx` | `path("/login", LoginPage, { name: "login" })` |
144
+
145
+ ### Library mode: config routes → urls() DSL
146
+
147
+ | React Router | Rango |
148
+ | -------------------------------------- | ------------------------------------------------------- |
149
+ | `path: "/"` | `path("/", HomePage, { name: "home" })` |
150
+ | `path: "about"` | `path("/about", AboutPage, { name: "about" })` |
151
+ | `path: "blog/:slug"` | `path("/blog/:slug", BlogPost, { name: "blogPost" })` |
152
+ | `path: "files/*"` (splat) | `path("/files/:path*", FileBrowser, { name: "files" })` |
153
+ | `path: "docs/:lang?"` (optional param) | `path("/docs/:lang?", Docs, { name: "docs" })` |
154
+
155
+ The RR splat (`$` / `*`) matches the bare parent too (`/files` binds `""`), so
156
+ it maps to `:path*` (zero-or-more). Use `:path+` only when you require at least
157
+ one trailing segment. RR reads the splat at `params["*"]`; Rango exposes it as a
158
+ named string at `ctx.params.path` with the `/` separators preserved (split to
159
+ recover RR's array):
160
+
161
+ ```typescript
162
+ path("/files/:path*", (ctx) => {
163
+ const parts = ctx.params.path === "" ? [] : ctx.params.path.split("/");
164
+ return <FileBrowser path={parts} />;
165
+ }, { name: "files" });
166
+ ```
167
+
168
+ ### Layouts
169
+
170
+ React Router layouts use `<Outlet />` — same concept in Rango:
171
+
172
+ ```typescript
173
+ // React Router:
174
+ function DashboardLayout() {
175
+ return (
176
+ <div className="dashboard">
177
+ <Outlet />
178
+ </div>
179
+ );
180
+ }
181
+
182
+ // route config:
183
+ { path: "dashboard", element: <DashboardLayout />, children: [...] }
184
+
185
+ // Rango: same <Outlet />, from @rangojs/router/client
186
+ import { Outlet } from "@rangojs/router/client";
187
+
188
+ layout(<DashboardLayout />, () => [
189
+ path("/dashboard", DashboardIndex, { name: "dashboard" }),
190
+ path("/dashboard/settings", Settings, { name: "settings" }),
191
+ ])
192
+ ```
193
+
194
+ ### Dynamic layouts (with data)
195
+
196
+ ```typescript
197
+ // React Router: useLoaderData() in layout component
198
+ function DashboardLayout() {
199
+ const { user } = useLoaderData();
200
+ return <Shell user={user}><Outlet /></Shell>;
201
+ }
202
+
203
+ // Rango: handler function layout (server component)
204
+ layout(async (ctx) => {
205
+ const user = ctx.get("user");
206
+ return (
207
+ <Shell user={user}>
208
+ <Outlet />
209
+ </Shell>
210
+ );
211
+ }, () => [
212
+ path("/dashboard", DashboardIndex, { name: "dashboard" }),
213
+ ])
214
+ ```
215
+
216
+ ### Nested routes
217
+
218
+ React Router's nested route tree maps directly to Rango's `layout()` nesting:
219
+
220
+ ```typescript
221
+ // React Router:
222
+ createBrowserRouter([{
223
+ path: "/",
224
+ element: <RootLayout />,
225
+ children: [
226
+ { path: "dashboard",
227
+ element: <DashboardLayout />,
228
+ children: [
229
+ { index: true, element: <DashboardIndex /> },
230
+ { path: "settings", element: <Settings /> },
231
+ ]
232
+ },
233
+ ]
234
+ }])
235
+
236
+ // Rango:
237
+ urls(({ path, layout }) => [
238
+ layout(<RootLayout />, () => [
239
+ layout(<DashboardLayout />, () => [
240
+ path("/dashboard", DashboardIndex, { name: "dashboard" }),
241
+ path("/dashboard/settings", Settings, { name: "settings" }),
242
+ ]),
243
+ ]),
244
+ ])
245
+ ```
246
+
247
+ ### Route groups / pathless layouts
248
+
249
+ React Router's pathless routes (layout routes without a path) are Rango's
250
+ layouts without a URL prefix:
251
+
252
+ ```typescript
253
+ // React Router: { element: <AuthLayout />, children: [...] }
254
+
255
+ // Rango: layout with no URL segment
256
+ layout(<AuthLayout />, () => [
257
+ path("/login", LoginPage, { name: "login" }),
258
+ path("/register", RegisterPage, { name: "register" }),
259
+ ])
260
+ ```
261
+
262
+ ### Index routes
263
+
264
+ ```typescript
265
+ // React Router: { index: true, element: <Home /> }
266
+
267
+ // Rango: path with "/" inside a layout
268
+ layout(<RootLayout />, () => [
269
+ path("/", HomePage, { name: "home" }),
270
+ ])
271
+ ```
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: mime-routes
3
- description: Content negotiation — serve different response types (RSC, JSON, text, XML) from the same URL based on Accept header
3
+ description: Content negotiation — serve different response types (RSC, JSON, text, XML) from the same URL based on Accept header. Use when the same URL needs to return JSON for API clients and HTML/RSC for browsers, or branching a handler on the Accept header.
4
4
  argument-hint: [negotiate|vary|accept]
5
5
  ---
6
6
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: observability
3
- description: Debug Rango request performance with debugPerformance, Server-Timing, structured telemetry, and tracing
3
+ description: Debug Rango request performance with debugPerformance, Server-Timing, structured telemetry, and tracing. Use when a request feels slow and you need to see where time is spent, or wiring up tracing/telemetry for production requests.
4
4
  argument-hint:
5
5
  ---
6
6
 
@@ -58,6 +58,14 @@ Read the timeline as intervals:
58
58
  - Cache, route matching, middleware pre/post, RSC serialization, and SSR phases
59
59
  appear as separate spans, so the slow phase is visible without guessing.
60
60
 
61
+ **Deployed Cloudflare caveat**: on production Workers, timers are frozen
62
+ during request execution (Spectre mitigation), so `Server-Timing` durations
63
+ read as ~0 on the deployed edge — they only advance across genuine awaited
64
+ I/O. The waterfall is a LOCAL diagnostic (dev, `vite preview`,
65
+ `wrangler dev`); for deployed workers, measure from the client
66
+ (`PerformanceResourceTiming`, TTFB) and use structured telemetry below for
67
+ server-side events.
68
+
61
69
  ## Structured telemetry
62
70
 
63
71
  Use telemetry when you want durable production events rather than a one-request
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: parallel
3
- description: Define parallel routes for multi-column layouts, sidebars, and modal slots in @rangojs/router
3
+ description: Define parallel routes for multi-column layouts, sidebars, and modal slots in @rangojs/router. Use when a layout needs multiple independently-loading regions (e.g. a sidebar and main panel), or rendering more than one route segment at the same URL.
4
4
  argument-hint: [@slot-name]
5
5
  ---
6
6
 
@@ -8,6 +8,13 @@ argument-hint: [@slot-name]
8
8
 
9
9
  Parallel routes render multiple components simultaneously in named slots.
10
10
 
11
+ ## Not this skill if…
12
+
13
+ - You want a modal or slide-over that appears only on soft navigation and shows
14
+ the full page on hard navigation — that is `intercept()`: see `/intercept`.
15
+ - You want a slot rendered conditionally on HOW the user navigated — parallel
16
+ slots ALWAYS render alongside the page; see `/intercept`.
17
+
11
18
  ## Basic Parallel Routes
12
19
 
13
20
  ```typescript
@@ -232,6 +239,8 @@ layout(<AccountLayout />, () => [
232
239
 
233
240
  A slot's `loading()` (whether from `handler.use` or explicit) makes that slot an independent streaming unit, exactly as in the **Streaming Behavior** section above.
234
241
 
242
+ Under a shared artifact (`cache()`, `"use cache"`, a PPR shell), the server-side `await ctx.use(CartLoader)` above is the BAKED lane — the capture-time value (identity reads included) freezes into the artifact; consume the loader client-side (`useLoader` in a `"use client"` component) to keep the slot live per request. One rule, stated once: `/rango` → Invariants ("the consumption-lane rule").
243
+
235
244
  The `parallel` mount site has the narrowest allow-list for `handler.use` items — slots cannot bring their own middleware or layout, only `revalidate`, `loader`, `loading`, `errorBoundary`, `notFoundBoundary`, and `transition`. See [skills/handler-use](../handler-use/SKILL.md) for the full table and merge rules.
236
245
 
237
246
  `transition` is allowed in the slot allow-list, but slot-level rendering does **not** currently apply a `<ViewTransition>` wrapper — only the layout/route wraps take effect at render time. For a modal-only morph today, use an element-level React `<ViewTransition>` inside the slot's component. The reverse direction is the useful guarantee: a layout-level `transition()` fires when the layout's default outlet content changes but **not** when a `<ParallelOutlet />` mounts new content (modal opens are not subtree updates of the layout VT). See [skills/view-transitions](../view-transitions/SKILL.md) for the wrap rules and the intercept caveat.
@@ -264,6 +273,8 @@ parallel(
264
273
 
265
274
  Per-slot merge order is **handler.use → shared use → slot-local use**. Slot-local is the narrowest scope, so it wins for last-write-wins items. See [skills/handler-use § `loading()` is a single-assignment item — scope it correctly](../handler-use/SKILL.md#loading-is-a-single-assignment-item--scope-it-correctly) for the full reasoning.
266
275
 
276
+ Typing note: a BARE arrow slot handler infers its ctx (`"@cart": (ctx) => ...`), but an arrow inside a DESCRIPTOR needs an explicit annotation — `handler: (ctx: HandlerContext) => ...` — because `StaticHandlerDefinition` in the slot union contributes a second callable to the contextual type and TS declines to pick a signature.
277
+
267
278
  ## Slot Override Semantics
268
279
 
269
280
  When multiple `parallel()` calls define the same slot name, **the last
@@ -344,9 +355,17 @@ parallel(
344
355
  )
345
356
  ```
346
357
 
347
- Revalidating only the parallel does not re-run outer handlers/layouts.
348
- If the slot reads `ctx.get()` data established above it, opt the outer
349
- segment into revalidation as well.
358
+ Where the slot sits decides its action default. A parallel under a
359
+ `path()` (or one of its orphan layouts) belongs to the route entry and
360
+ revalidates together with it on every action — handler-set data stays
361
+ consistent with no configuration. A parallel under a standalone
362
+ `layout()` entry follows the parent-chain default instead: skipped on
363
+ actions unless a `revalidate()` opts it in.
364
+
365
+ In either position, revalidating only the parallel does not re-run outer
366
+ handlers/layouts. If the slot reads `ctx.get()` data established above
367
+ it, opt the outer segment into revalidation as well (see `/rango` →
368
+ "Passing data down the tree").
350
369
 
351
370
  A `revalidate()` callback may return a hard `boolean`, a soft
352
371
  `{ defaultShouldRevalidate }` object, or nothing (`void` / `null` /