@rangojs/router 0.0.0-experimental.b000c598 → 0.0.0-experimental.b30bbf02

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 (51) hide show
  1. package/README.md +65 -0
  2. package/dist/vite/index.js +1160 -443
  3. package/package.json +3 -1
  4. package/skills/hooks/SKILL.md +5 -0
  5. package/skills/links/SKILL.md +2 -0
  6. package/skills/loader/SKILL.md +35 -1
  7. package/skills/middleware/SKILL.md +2 -0
  8. package/skills/migrate-nextjs/SKILL.md +3 -1
  9. package/skills/migrate-react-router/SKILL.md +4 -0
  10. package/skills/parallel/SKILL.md +7 -0
  11. package/skills/rango/SKILL.md +1 -0
  12. package/skills/server-actions/SKILL.md +739 -0
  13. package/src/browser/event-controller.ts +44 -4
  14. package/src/browser/react/NavigationProvider.tsx +20 -7
  15. package/src/browser/react/filter-segment-order.ts +51 -7
  16. package/src/browser/react/use-segments.ts +11 -8
  17. package/src/browser/types.ts +6 -0
  18. package/src/route-definition/helpers-types.ts +6 -1
  19. package/src/router/match-api.ts +1 -0
  20. package/src/router/match-handlers.ts +1 -0
  21. package/src/router/match-result.ts +21 -2
  22. package/src/router/middleware.ts +22 -3
  23. package/src/router/pattern-matching.ts +30 -11
  24. package/src/router/revalidation.ts +15 -1
  25. package/src/router/segment-resolution/fresh.ts +8 -0
  26. package/src/router/segment-resolution/revalidation.ts +128 -100
  27. package/src/router/trie-matching.ts +8 -9
  28. package/src/rsc/progressive-enhancement.ts +2 -0
  29. package/src/rsc/rsc-rendering.ts +3 -0
  30. package/src/rsc/server-action.ts +2 -0
  31. package/src/rsc/types.ts +6 -0
  32. package/src/ssr/index.tsx +5 -1
  33. package/src/types/handler-context.ts +10 -5
  34. package/src/types/segments.ts +17 -0
  35. package/src/vite/debug.ts +184 -0
  36. package/src/vite/discovery/discover-routers.ts +31 -3
  37. package/src/vite/discovery/gate-state.ts +171 -0
  38. package/src/vite/discovery/prerender-collection.ts +48 -1
  39. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  40. package/src/vite/plugins/cjs-to-esm.ts +5 -0
  41. package/src/vite/plugins/client-ref-dedup.ts +16 -0
  42. package/src/vite/plugins/client-ref-hashing.ts +16 -4
  43. package/src/vite/plugins/expose-action-id.ts +52 -28
  44. package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
  45. package/src/vite/plugins/expose-internal-ids.ts +516 -486
  46. package/src/vite/plugins/performance-tracks.ts +17 -9
  47. package/src/vite/plugins/use-cache-transform.ts +56 -43
  48. package/src/vite/plugins/version-injector.ts +37 -11
  49. package/src/vite/rango.ts +19 -5
  50. package/src/vite/router-discovery.ts +498 -52
  51. package/src/vite/utils/package-resolution.ts +8 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rangojs/router",
3
- "version": "0.0.0-experimental.b000c598",
3
+ "version": "0.0.0-experimental.b30bbf02",
4
4
  "description": "Django-inspired RSC router with composable URL patterns",
5
5
  "keywords": [
6
6
  "react",
@@ -142,7 +142,9 @@
142
142
  "test:unit:watch": "vitest"
143
143
  },
144
144
  "dependencies": {
145
+ "@types/debug": "^4.1.12",
145
146
  "@vitejs/plugin-rsc": "^0.5.23",
147
+ "debug": "^4.4.1",
146
148
  "magic-string": "^0.30.17",
147
149
  "picomatch": "^4.0.3",
148
150
  "rsc-html-stream": "^0.0.7"
@@ -323,6 +323,11 @@ RSC serialization strips the `collect` function via `toJSON()`. On the client,
323
323
 
324
324
  ## Action Hooks
325
325
 
326
+ For the full server-action guide (defining actions, `useActionState`,
327
+ `useOptimistic`, validation, revalidation, error handling, file uploads),
328
+ see `/server-actions`. `useAction()` below is a Rango-specific hook for
329
+ tracking actions called outside a `<form action={...}>` flow.
330
+
326
331
  ### useAction()
327
332
 
328
333
  Track state of server action invocations:
@@ -187,6 +187,8 @@ export async function getProductUrl(slug: string) {
187
187
  }
188
188
  ```
189
189
 
190
+ See `/server-actions` for the full action surface (`getRequestContext()` is the same context middleware and handlers use).
191
+
190
192
  For static path strings (not named routes), client components can use `href()` — see below.
191
193
 
192
194
  ## Client: href()
@@ -6,7 +6,10 @@ argument-hint: [loader]
6
6
 
7
7
  # Data Loaders with loader()
8
8
 
9
- Loaders fetch data on the server and stream it to the client.
9
+ Loaders fetch data on the server and stream it to the client. For mutations
10
+ (writes triggered by forms or buttons), use server actions instead — see
11
+ `/server-actions`. Loaders re-resolve after an action runs, so the typical
12
+ flow is _action mutates → loader re-reads → UI updates_.
10
13
 
11
14
  ## Creating a Loader
12
15
 
@@ -248,6 +251,37 @@ path("/product/:slug", ProductPage, { name: "product" }, () => [
248
251
  ]);
249
252
  ```
250
253
 
254
+ ### `revalidate()` return shapes
255
+
256
+ A `revalidate(fn)` callback can return one of four shapes. The chain
257
+ processes revalidators in order; each call's return controls how the
258
+ chain continues:
259
+
260
+ ```typescript
261
+ // 1) Hard decision — short-circuits the chain, used as the final answer.
262
+ revalidate(() => true);
263
+ revalidate(({ actionId }) => actionId?.includes("Cart") ?? false);
264
+
265
+ // 2) Soft decision — updates the running suggestion for downstream
266
+ // revalidators on the same segment, chain continues.
267
+ revalidate(({ defaultShouldRevalidate }) => ({
268
+ defaultShouldRevalidate: !defaultShouldRevalidate,
269
+ }));
270
+
271
+ // 3) Defer (no opinion) — leaves the running suggestion unchanged and
272
+ // continues to the next revalidator. Implicit return / null /
273
+ // undefined are all equivalent and consumer-friendly.
274
+ revalidate(({ actionId }) => {
275
+ if (actionId?.includes("Cart")) return true; // hard for this branch only
276
+ // implicit return — let downstream revalidators or the segment default decide
277
+ });
278
+ revalidate(() => undefined); // explicit defer
279
+ revalidate(() => null); // explicit defer
280
+ ```
281
+
282
+ If every revalidator on a segment defers, the segment-type default
283
+ (e.g. params-changed for routes, `false` for parallels) is used.
284
+
251
285
  ### Revalidation Contracts for Loader Dependencies
252
286
 
253
287
  If a loader reads `ctx.get()` data produced by an outer handler/layout, share
@@ -32,6 +32,8 @@ When the router has a `basename`, pattern-scoped `.use()` patterns are automatic
32
32
 
33
33
  Registered inside `urls()` callback. Wraps **rendering only** -- it does NOT wrap server action execution. Actions run before route middleware, so when route middleware executes during post-action revalidation, it can observe state that the action set (cookies, context variables, headers).
34
34
 
35
+ > **Implication for auth:** route middleware cannot guard server actions. Use `router.use("/admin/*", requireAuth)` (global, scoped) for action protection, or check inside the action body. See `/server-actions` for action-side auth patterns.
36
+
35
37
  ```
36
38
  Request flow (with action):
37
39
  global mw -> action executes -> route mw -> layout -> handler -> loaders
@@ -225,7 +225,7 @@ Loaders are Rango's live data layer. Use them when you need:
225
225
 
226
226
  - **Client-side data refresh** — `useLoader()` in client components for reactive data
227
227
  - **Per-loader caching** — opt in with `loader(MyLoader, () => [cache({ ttl: 60 })])`; loaders stay live by default
228
- - **Revalidation control** — `revalidate()` targets specific loaders after actions
228
+ - **Revalidation control** — `revalidate()` targets specific segments and loaders after actions
229
229
  - **Loading skeletons** — `loading()` shows a Suspense fallback while loaders resolve
230
230
 
231
231
  ```typescript
@@ -463,6 +463,8 @@ Server actions work the same way — `"use server"` directive, `useActionState`,
463
463
 
464
464
  Key difference: in Rango, route middleware does NOT wrap action execution. Actions only see global middleware context. Use `getRequestContext()` in actions to access `ctx.set()`/`ctx.get()`.
465
465
 
466
+ Next.js's `revalidatePath()` / `revalidateTag()` have no direct equivalent — Rango partially re-renders matched route segments (path/layout/parallel/intercept) and re-resolves their loaders, and you scope re-runs by attaching a `revalidate(({ actionId }) => ...)` rule to any segment or loader registration. See `/server-actions` for the full pattern (validation, error handling, file uploads) and `/loader` for revalidation rule semantics.
467
+
466
468
  ## 8. Metadata / Head
467
469
 
468
470
  Rango uses the `Meta` handle + `<MetaTags />` client component:
@@ -483,6 +483,10 @@ Since Rango uses RSC server actions, all React action patterns work:
483
483
  `useActionState`, `useOptimistic`, `useTransition`, `startTransition`,
484
484
  and plain `<form action={serverAction}>`. No framework-specific hook needed.
485
485
 
486
+ For the full guide — defining actions, validation with Zod, error handling,
487
+ revalidation rules, file uploads, and progressive enhancement — see
488
+ `/server-actions`.
489
+
486
490
  ### clientLoader / clientAction (framework mode)
487
491
 
488
492
  RR7 framework mode's `clientLoader` and `clientAction` run in the browser.
@@ -347,6 +347,13 @@ Revalidating only the parallel does not re-run outer handlers/layouts.
347
347
  If the slot reads `ctx.get()` data established above it, opt the outer
348
348
  segment into revalidation as well.
349
349
 
350
+ A `revalidate()` callback may return a hard `boolean`, a soft
351
+ `{ defaultShouldRevalidate }` object, or nothing (`void` / `null` /
352
+ `undefined`) to defer to the next revalidator. See
353
+ [loader/SKILL.md#revalidate-return-shapes](../loader/SKILL.md#revalidate-return-shapes)
354
+ for the full contract — it's the same across `loader()`, `path()`,
355
+ `layout()`, `parallel()`, and `intercept()`.
356
+
350
357
  ### Revalidation Contracts for Parallel Dependencies
351
358
 
352
359
  Prefer named revalidation contracts shared by both the upstream producer and
@@ -16,6 +16,7 @@ Django-inspired RSC router with composable URL patterns, type-safe href, and ser
16
16
  | `/route` | Define routes with `urls()` and `path()` |
17
17
  | `/layout` | Layouts that wrap child routes |
18
18
  | `/loader` | Data loaders with `createLoader()` |
19
+ | `/server-actions` | Mutations with `"use server"`, useActionState, validation, revalidation |
19
20
  | `/middleware` | Request processing and authentication |
20
21
  | `/intercept` | Modal/slide-over patterns for soft navigation |
21
22
  | `/parallel` | Multi-column layouts and sidebars |