@rangojs/router 0.0.0-experimental.fa8a383a → 0.0.0-experimental.ffbe1b7f

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 (37) hide show
  1. package/dist/vite/index.js +17 -2
  2. package/package.json +1 -1
  3. package/skills/cache-guide/SKILL.md +32 -0
  4. package/skills/caching/SKILL.md +8 -0
  5. package/skills/loader/SKILL.md +52 -42
  6. package/skills/parallel/SKILL.md +67 -0
  7. package/skills/route/SKILL.md +31 -0
  8. package/skills/typesafety/SKILL.md +10 -0
  9. package/src/browser/partial-update.ts +11 -0
  10. package/src/browser/prefetch/queue.ts +61 -29
  11. package/src/browser/prefetch/resource-ready.ts +77 -0
  12. package/src/browser/react/NavigationProvider.tsx +5 -3
  13. package/src/cache/cache-runtime.ts +15 -11
  14. package/src/cache/cache-scope.ts +46 -5
  15. package/src/cache/taint.ts +55 -0
  16. package/src/context-var.ts +72 -2
  17. package/src/route-definition/helpers-types.ts +6 -5
  18. package/src/router/handler-context.ts +31 -8
  19. package/src/router/loader-resolution.ts +7 -1
  20. package/src/router/match-middleware/background-revalidation.ts +12 -1
  21. package/src/router/match-middleware/cache-lookup.ts +46 -6
  22. package/src/router/match-middleware/cache-store.ts +21 -4
  23. package/src/router/match-result.ts +11 -5
  24. package/src/router/metrics.ts +6 -1
  25. package/src/router/middleware-types.ts +6 -2
  26. package/src/router/middleware.ts +2 -2
  27. package/src/router/router-context.ts +1 -0
  28. package/src/router/segment-resolution/fresh.ts +37 -14
  29. package/src/router/segment-resolution/helpers.ts +29 -24
  30. package/src/router/segment-resolution/revalidation.ts +43 -19
  31. package/src/router/types.ts +1 -0
  32. package/src/router.ts +1 -0
  33. package/src/rsc/handler.ts +0 -9
  34. package/src/server/context.ts +12 -0
  35. package/src/server/request-context.ts +42 -8
  36. package/src/types/handler-context.ts +120 -22
  37. package/src/types/loader-types.ts +4 -4
@@ -272,8 +272,16 @@ export type HandlerContext<
272
272
  * ```
273
273
  */
274
274
  set: {
275
- <T>(contextVar: ContextVar<T>, value: T): void;
276
- } & (<K extends keyof DefaultVars>(key: K, value: DefaultVars[K]) => void);
275
+ <T>(
276
+ contextVar: ContextVar<T>,
277
+ value: T,
278
+ options?: { cache?: boolean },
279
+ ): void;
280
+ } & (<K extends keyof DefaultVars>(
281
+ key: K,
282
+ value: DefaultVars[K],
283
+ options?: { cache?: boolean },
284
+ ) => void);
277
285
  /**
278
286
  * Response headers. Headers set here are merged into the final response.
279
287
  *
@@ -289,8 +297,15 @@ export type HandlerContext<
289
297
  /**
290
298
  * Access loader data or push handle data.
291
299
  *
300
+ * Available in route handlers, layout handlers, middleware, server actions,
301
+ * and server components rendered within the request context.
302
+ *
292
303
  * For loaders: Returns a promise that resolves to the loader data.
293
304
  * Loaders are executed in parallel and memoized per request.
305
+ * Prefer DSL `loader()` + client `useLoader()` over `ctx.use(Loader)` —
306
+ * DSL loaders are always fresh and cache-safe. Use `ctx.use(Loader)` only
307
+ * when you need loader data in the handler itself (e.g., to set context
308
+ * variables or make routing decisions).
294
309
  *
295
310
  * For handles: Returns a push function to add data for this segment.
296
311
  * Handle data accumulates across all matched route segments.
@@ -298,10 +313,11 @@ export type HandlerContext<
298
313
  *
299
314
  * @example
300
315
  * ```typescript
301
- * // Loader usage
302
- * route("cart", async (ctx) => {
303
- * const cart = await ctx.use(CartLoader);
304
- * return <CartPage cart={cart} />;
316
+ * // Loader escape hatch — use when handler needs the data directly
317
+ * route("product", async (ctx) => {
318
+ * const { product } = await ctx.use(ProductLoader);
319
+ * ctx.set(Product, product); // make available to children
320
+ * return <ProductPage />;
305
321
  * });
306
322
  *
307
323
  * // Handle usage - direct value
@@ -519,30 +535,112 @@ export type RevalidateParams<TParams = GenericParams, TEnv = any> = Parameters<
519
535
  * })
520
536
  * ```
521
537
  */
538
+ /**
539
+ * Revalidation function called during client-side navigation to decide whether
540
+ * a segment (layout, route, parallel slot, or loader) should be re-rendered.
541
+ *
542
+ * Return `true` to re-render, `false` to skip (keep client's current version),
543
+ * or `{ defaultShouldRevalidate: boolean }` to override the default for
544
+ * downstream segments.
545
+ *
546
+ * @example
547
+ * ```ts
548
+ * // Re-render only when a cart action happened or browser signals staleness
549
+ * revalidate(({ actionId, stale }) =>
550
+ * actionId?.includes("cart") || stale || false
551
+ * )
552
+ *
553
+ * // Always re-render when params change (default behavior made explicit)
554
+ * revalidate(({ defaultShouldRevalidate }) => defaultShouldRevalidate)
555
+ * ```
556
+ */
522
557
  export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
558
+ /** Route params from the page being navigated away from. */
523
559
  currentParams: TParams;
560
+ /** Full URL of the page being navigated away from. */
524
561
  currentUrl: URL;
562
+ /** Route params for the navigation target. */
525
563
  nextParams: TParams;
564
+ /** Full URL of the navigation target. */
526
565
  nextUrl: URL;
566
+ /**
567
+ * The router's default revalidation decision for this segment.
568
+ * `true` when params changed or the segment is new to the client.
569
+ * Return this when you want default behavior plus your own conditions.
570
+ */
527
571
  defaultShouldRevalidate: boolean;
572
+ /** Full handler context — access to `ctx.use()`, `ctx.env`, `ctx.params`, etc. */
528
573
  context: HandlerContext<TParams, TEnv>;
529
- // Segment metadata (which segment is being evaluated):
574
+
575
+ // ── Segment metadata (which segment is being evaluated) ──────────────
576
+
577
+ /** The type of segment being revalidated. */
530
578
  segmentType: "layout" | "route" | "parallel";
531
- layoutName?: string; // Layout name (e.g., "root", "shop", "auth") - only for layouts
532
- slotName?: string; // Slot name (e.g., "@sidebar", "@modal") - only for parallels
533
- // Action context (populated when revalidation triggered by server action):
534
- actionId?: string; // Action identifier (e.g., "src/actions.ts#addToCart")
535
- actionUrl?: URL; // URL where action was executed
536
- actionResult?: any; // Return value from action execution
537
- formData?: FormData; // FormData from action request
538
- method?: string; // Request method: 'GET' for navigation, 'POST' for actions
539
- routeName?: DefaultRouteName; // Route name of the navigation target (alias for toRouteName)
540
- // Named-route identity for both ends of a navigation transition.
541
- // Undefined for unnamed internal routes (those without a `name` option).
542
- fromRouteName?: DefaultRouteName; // Route name being navigated away from
543
- toRouteName?: DefaultRouteName; // Route name being navigated to
544
- // Stale cache revalidation (SWR pattern):
545
- stale?: boolean; // True if this is a stale cache revalidation request
579
+ /** Layout name (e.g., `"root"`, `"shop"`, `"auth"`). Only set for layout segments. */
580
+ layoutName?: string;
581
+ /** Slot name (e.g., `"@sidebar"`, `"@modal"`). Only set for parallel segments. */
582
+ slotName?: string;
583
+
584
+ // ── Action context (populated when revalidation is triggered by a server action) ──
585
+
586
+ /**
587
+ * Identifier of the server action that triggered revalidation.
588
+ * `undefined` during normal navigation (no action involved).
589
+ *
590
+ * Format: `"src/<path>#<exportName>"` the file path is the source path
591
+ * relative to the project root, followed by `#` and the exported function name.
592
+ *
593
+ * This is stable and can be used for path-based matching to revalidate
594
+ * when any action in a module or directory fires:
595
+ *
596
+ * @example
597
+ * ```ts
598
+ * // Match a specific action
599
+ * revalidate(({ actionId }) => actionId === "src/actions/cart.ts#addToCart")
600
+ *
601
+ * // Match any action in the cart module
602
+ * revalidate(({ actionId }) => actionId?.includes("cart") ?? false)
603
+ *
604
+ * // Match any action under src/apps/store/actions/
605
+ * revalidate(({ actionId }) => actionId?.startsWith("src/apps/store/actions/") ?? false)
606
+ * ```
607
+ */
608
+ actionId?: string;
609
+ /** URL where the action was executed (the page the user was on when they triggered the action). */
610
+ actionUrl?: URL;
611
+ /** Return value from the action execution. Can be used to conditionally revalidate based on the action's outcome. */
612
+ actionResult?: any;
613
+ /** FormData from the action request body. Only set for form-based actions (not inline `"use server"` actions). */
614
+ formData?: FormData;
615
+ /** HTTP method: `"GET"` for navigation, `"POST"` for server actions. */
616
+ method?: string;
617
+
618
+ // ── Route identity ───────────────────────────────────────────────────
619
+
620
+ /** Route name of the navigation target. Alias for `toRouteName`. */
621
+ routeName?: DefaultRouteName;
622
+ /**
623
+ * Route name being navigated away from.
624
+ * `undefined` for unnamed internal routes (those without a `name` option).
625
+ */
626
+ fromRouteName?: DefaultRouteName;
627
+ /**
628
+ * Route name being navigated to.
629
+ * `undefined` for unnamed internal routes (those without a `name` option).
630
+ */
631
+ toRouteName?: DefaultRouteName;
632
+
633
+ // ── Staleness signal ─────────────────────────────────────────────────
634
+
635
+ /**
636
+ * `true` when the browser signals that data may be stale — typically because
637
+ * a server action was executed in this or another tab (`_rsc_stale` header).
638
+ *
639
+ * This is NOT segment cache staleness (loaders are never segment-cached).
640
+ * Use this to decide whether loader data should be re-fetched after an
641
+ * action that may have mutated backend state.
642
+ */
643
+ stale?: boolean;
546
644
  }) => boolean | { defaultShouldRevalidate: boolean };
547
645
 
548
646
  // MiddlewareFn is imported from "../router/middleware.js" and re-exported
@@ -166,11 +166,11 @@ export type LoadOptions =
166
166
  * return await db.products.findBySlug(slug);
167
167
  * });
168
168
  *
169
- * // Server usage
170
- * const cart = ctx.use(CartLoader);
169
+ * // Client usage (preferred — cache-safe, always fresh)
170
+ * const { data } = useLoader(CartLoader);
171
171
  *
172
- * // Client usage (fn is stripped, only name remains)
173
- * const cart = useLoader(CartLoader);
172
+ * // Server escape hatch (handler needs data directly)
173
+ * const cart = await ctx.use(CartLoader);
174
174
  * ```
175
175
  */
176
176
  export type LoaderDefinition<