@rangojs/router 0.0.0-experimental.39 → 0.0.0-experimental.3b1deca8
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/dist/bin/rango.js +8 -3
- package/dist/vite/index.js +292 -204
- package/package.json +1 -1
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +45 -4
- package/skills/loader/SKILL.md +53 -43
- package/skills/parallel/SKILL.md +126 -0
- package/skills/route/SKILL.md +31 -0
- package/skills/router-setup/SKILL.md +52 -2
- package/skills/typesafety/SKILL.md +10 -0
- package/src/browser/debug-channel.ts +93 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/navigation-bridge.ts +1 -5
- package/src/browser/navigation-client.ts +84 -27
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +50 -9
- package/src/browser/prefetch/cache.ts +57 -5
- package/src/browser/prefetch/fetch.ts +30 -21
- package/src/browser/prefetch/queue.ts +92 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +9 -1
- package/src/browser/react/NavigationProvider.tsx +32 -3
- package/src/browser/rsc-router.tsx +109 -57
- package/src/browser/scroll-restoration.ts +31 -34
- package/src/browser/segment-reconciler.ts +6 -1
- package/src/browser/server-action-bridge.ts +12 -0
- package/src/browser/types.ts +17 -1
- package/src/build/route-types/router-processing.ts +12 -2
- package/src/cache/cache-runtime.ts +15 -11
- package/src/cache/cache-scope.ts +48 -7
- package/src/cache/cf/cf-cache-store.ts +453 -11
- package/src/cache/cf/index.ts +5 -1
- package/src/cache/document-cache.ts +17 -7
- package/src/cache/index.ts +1 -0
- package/src/cache/taint.ts +55 -0
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/deps/browser.ts +1 -0
- package/src/route-definition/dsl-helpers.ts +32 -7
- package/src/route-definition/helpers-types.ts +6 -5
- package/src/route-definition/redirect.ts +2 -2
- package/src/route-map-builder.ts +7 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +31 -8
- package/src/router/intercept-resolution.ts +2 -0
- package/src/router/lazy-includes.ts +4 -1
- package/src/router/loader-resolution.ts +7 -1
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +9 -3
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +66 -9
- package/src/router/match-middleware/cache-store.ts +53 -10
- package/src/router/match-middleware/intercept-resolution.ts +9 -7
- package/src/router/match-middleware/segment-resolution.ts +8 -5
- package/src/router/match-result.ts +22 -6
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +6 -2
- package/src/router/middleware.ts +4 -3
- package/src/router/router-context.ts +6 -1
- package/src/router/segment-resolution/fresh.ts +130 -17
- package/src/router/segment-resolution/helpers.ts +29 -24
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +352 -290
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/types.ts +1 -0
- package/src/router.ts +6 -1
- package/src/rsc/handler.ts +28 -2
- package/src/rsc/loader-fetch.ts +7 -2
- package/src/rsc/progressive-enhancement.ts +4 -1
- package/src/rsc/rsc-rendering.ts +4 -1
- package/src/rsc/server-action.ts +2 -0
- package/src/rsc/types.ts +7 -1
- package/src/segment-system.tsx +140 -4
- package/src/server/context.ts +102 -13
- package/src/server/request-context.ts +59 -12
- package/src/ssr/index.tsx +1 -0
- package/src/types/handler-context.ts +120 -22
- package/src/types/loader-types.ts +4 -4
- package/src/types/route-entry.ts +7 -0
- package/src/types/segments.ts +2 -0
- package/src/urls/path-helper.ts +1 -1
- package/src/vite/discovery/state.ts +0 -2
- package/src/vite/plugin-types.ts +0 -83
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/performance-tracks.ts +235 -0
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +148 -209
- package/src/vite/router-discovery.ts +0 -8
- package/src/vite/utils/banner.ts +3 -3
|
@@ -20,7 +20,12 @@ import type {
|
|
|
20
20
|
DefaultRouteName,
|
|
21
21
|
} from "../types/global-namespace.js";
|
|
22
22
|
import type { Handle } from "../handle.js";
|
|
23
|
-
import {
|
|
23
|
+
import {
|
|
24
|
+
type ContextVar,
|
|
25
|
+
contextGet,
|
|
26
|
+
contextSet,
|
|
27
|
+
isNonCacheable,
|
|
28
|
+
} from "../context-var.js";
|
|
24
29
|
import { createHandleStore, type HandleStore } from "./handle-store.js";
|
|
25
30
|
import { isHandle } from "../handle.js";
|
|
26
31
|
import { track, type MetricsStore } from "./context.js";
|
|
@@ -30,7 +35,11 @@ import type { Theme, ResolvedThemeConfig } from "../theme/types.js";
|
|
|
30
35
|
import { THEME_COOKIE } from "../theme/constants.js";
|
|
31
36
|
import type { LocationStateEntry } from "../browser/react/location-state-shared.js";
|
|
32
37
|
import { NOCACHE_SYMBOL, assertNotInsideCacheExec } from "../cache/taint.js";
|
|
33
|
-
import {
|
|
38
|
+
import { isInsideCacheScope } from "./context.js";
|
|
39
|
+
import {
|
|
40
|
+
createReverseFunction,
|
|
41
|
+
stripInternalParams,
|
|
42
|
+
} from "../router/handler-context.js";
|
|
34
43
|
import { getGlobalRouteMap, isRouteRootScoped } from "../route-map-builder.js";
|
|
35
44
|
import { invariant } from "../errors.js";
|
|
36
45
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
@@ -58,7 +67,7 @@ export interface RequestContext<
|
|
|
58
67
|
originalUrl: URL;
|
|
59
68
|
/** URL pathname */
|
|
60
69
|
pathname: string;
|
|
61
|
-
/** URL search params (
|
|
70
|
+
/** URL search params (with internal `_rsc*` params stripped, same as `url.searchParams`) */
|
|
62
71
|
searchParams: URLSearchParams;
|
|
63
72
|
/** Variables set by middleware (same as ctx.var) */
|
|
64
73
|
var: Record<string, any>;
|
|
@@ -69,8 +78,12 @@ export interface RequestContext<
|
|
|
69
78
|
};
|
|
70
79
|
/** Set a variable (shared with middleware and handlers) */
|
|
71
80
|
set: {
|
|
72
|
-
<T>(
|
|
73
|
-
|
|
81
|
+
<T>(
|
|
82
|
+
contextVar: ContextVar<T>,
|
|
83
|
+
value: T,
|
|
84
|
+
options?: { cache?: boolean },
|
|
85
|
+
): void;
|
|
86
|
+
<K extends string>(key: K, value: any, options?: { cache?: boolean }): void;
|
|
74
87
|
};
|
|
75
88
|
/**
|
|
76
89
|
* Route params (populated after route matching)
|
|
@@ -274,6 +287,12 @@ export interface RequestContext<
|
|
|
274
287
|
|
|
275
288
|
/** @internal Request-scoped performance metrics store */
|
|
276
289
|
_metricsStore?: MetricsStore;
|
|
290
|
+
|
|
291
|
+
/** @internal Dev-only: debug channel for React Performance Tracks */
|
|
292
|
+
_debugChannel?: {
|
|
293
|
+
readable: ReadableStream;
|
|
294
|
+
writable: WritableStream;
|
|
295
|
+
};
|
|
277
296
|
}
|
|
278
297
|
|
|
279
298
|
/**
|
|
@@ -303,6 +322,7 @@ export type PublicRequestContext<
|
|
|
303
322
|
| "_reportBackgroundError"
|
|
304
323
|
| "_debugPerformance"
|
|
305
324
|
| "_metricsStore"
|
|
325
|
+
| "_debugChannel"
|
|
306
326
|
| "_setStatus"
|
|
307
327
|
| "res"
|
|
308
328
|
>;
|
|
@@ -503,6 +523,18 @@ export function createRequestContext<TEnv>(
|
|
|
503
523
|
responseCookieCache = null;
|
|
504
524
|
};
|
|
505
525
|
|
|
526
|
+
// Guard: throw if a response-level side effect is called inside a cache() scope.
|
|
527
|
+
// Uses ALS to detect the scope (set during segment resolution).
|
|
528
|
+
function assertNotInsideCacheScopeALS(methodName: string): void {
|
|
529
|
+
if (isInsideCacheScope()) {
|
|
530
|
+
throw new Error(
|
|
531
|
+
`ctx.${methodName}() cannot be called inside a cache() boundary. ` +
|
|
532
|
+
`On cache hit the handler is skipped, so this side effect would be lost. ` +
|
|
533
|
+
`Move ctx.${methodName}() to a middleware or layout outside the cache() scope.`,
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
506
538
|
// Effective cookie read: response stub Set-Cookie wins, then original header.
|
|
507
539
|
// The stub IS the source of truth for same-request mutations.
|
|
508
540
|
const effectiveCookie = (name: string): string | undefined => {
|
|
@@ -555,20 +587,31 @@ export function createRequestContext<TEnv>(
|
|
|
555
587
|
invalidateResponseCookieCache();
|
|
556
588
|
};
|
|
557
589
|
|
|
590
|
+
// Strip internal _rsc* params so userland sees a clean URL.
|
|
591
|
+
const cleanUrl = stripInternalParams(url);
|
|
592
|
+
|
|
558
593
|
// Build the context object first (without use), then add use
|
|
559
594
|
const ctx: RequestContext<TEnv> = {
|
|
560
595
|
env,
|
|
561
596
|
request,
|
|
562
|
-
url,
|
|
597
|
+
url: cleanUrl,
|
|
563
598
|
originalUrl: new URL(request.url),
|
|
564
599
|
pathname: url.pathname,
|
|
565
|
-
searchParams:
|
|
600
|
+
searchParams: cleanUrl.searchParams,
|
|
566
601
|
var: variables,
|
|
567
|
-
get: ((keyOrVar: any) =>
|
|
568
|
-
|
|
569
|
-
|
|
602
|
+
get: ((keyOrVar: any) => {
|
|
603
|
+
if (isNonCacheable(variables, keyOrVar) && isInsideCacheScope()) {
|
|
604
|
+
throw new Error(
|
|
605
|
+
`ctx.get() for a non-cacheable variable cannot be called inside a cache() boundary. ` +
|
|
606
|
+
`The variable was created with { cache: false } or set with { cache: false }, ` +
|
|
607
|
+
`and its value would be stale on cache hit. Move the read outside the cached scope.`,
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
return contextGet(variables, keyOrVar);
|
|
611
|
+
}) as RequestContext<TEnv>["get"],
|
|
612
|
+
set: ((keyOrVar: any, value: any, options?: any) => {
|
|
570
613
|
assertNotInsideCacheExec(ctx, "set");
|
|
571
|
-
contextSet(variables, keyOrVar, value);
|
|
614
|
+
contextSet(variables, keyOrVar, value, options);
|
|
572
615
|
}) as RequestContext<TEnv>["set"],
|
|
573
616
|
params: {} as Record<string, string>,
|
|
574
617
|
|
|
@@ -606,6 +649,7 @@ export function createRequestContext<TEnv>(
|
|
|
606
649
|
|
|
607
650
|
setCookie(name: string, value: string, options?: CookieOptions): void {
|
|
608
651
|
assertNotInsideCacheExec(ctx, "setCookie");
|
|
652
|
+
assertNotInsideCacheScopeALS("setCookie");
|
|
609
653
|
stubResponse.headers.append(
|
|
610
654
|
"Set-Cookie",
|
|
611
655
|
serializeCookieValue(name, value, options),
|
|
@@ -618,6 +662,7 @@ export function createRequestContext<TEnv>(
|
|
|
618
662
|
options?: Pick<CookieOptions, "domain" | "path">,
|
|
619
663
|
): void {
|
|
620
664
|
assertNotInsideCacheExec(ctx, "deleteCookie");
|
|
665
|
+
assertNotInsideCacheScopeALS("deleteCookie");
|
|
621
666
|
stubResponse.headers.append(
|
|
622
667
|
"Set-Cookie",
|
|
623
668
|
serializeCookieValue(name, "", { ...options, maxAge: 0 }),
|
|
@@ -627,11 +672,13 @@ export function createRequestContext<TEnv>(
|
|
|
627
672
|
|
|
628
673
|
header(name: string, value: string): void {
|
|
629
674
|
assertNotInsideCacheExec(ctx, "header");
|
|
675
|
+
assertNotInsideCacheScopeALS("header");
|
|
630
676
|
stubResponse.headers.set(name, value);
|
|
631
677
|
},
|
|
632
678
|
|
|
633
679
|
setStatus(status: number): void {
|
|
634
680
|
assertNotInsideCacheExec(ctx, "setStatus");
|
|
681
|
+
assertNotInsideCacheScopeALS("setStatus");
|
|
635
682
|
stubResponse = new Response(null, {
|
|
636
683
|
status,
|
|
637
684
|
headers: stubResponse.headers,
|
|
@@ -670,6 +717,7 @@ export function createRequestContext<TEnv>(
|
|
|
670
717
|
|
|
671
718
|
onResponse(callback: (response: Response) => Response): void {
|
|
672
719
|
assertNotInsideCacheExec(ctx, "onResponse");
|
|
720
|
+
assertNotInsideCacheScopeALS("onResponse");
|
|
673
721
|
this._onResponseCallbacks.push(callback);
|
|
674
722
|
},
|
|
675
723
|
|
|
@@ -900,7 +948,6 @@ export function createUseFunction<TEnv>(
|
|
|
900
948
|
),
|
|
901
949
|
};
|
|
902
950
|
|
|
903
|
-
// Start loader execution with tracking
|
|
904
951
|
const doneLoader = track(`loader:${loader.$$id}`, 2);
|
|
905
952
|
const promise = Promise.resolve(loaderFn(loaderCtx)).finally(() => {
|
|
906
953
|
doneLoader();
|
package/src/ssr/index.tsx
CHANGED
|
@@ -272,8 +272,16 @@ export type HandlerContext<
|
|
|
272
272
|
* ```
|
|
273
273
|
*/
|
|
274
274
|
set: {
|
|
275
|
-
<T>(
|
|
276
|
-
|
|
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
|
|
302
|
-
* route("
|
|
303
|
-
* const
|
|
304
|
-
*
|
|
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
|
-
|
|
574
|
+
|
|
575
|
+
// ── Segment metadata (which segment is being evaluated) ──────────────
|
|
576
|
+
|
|
577
|
+
/** The type of segment being revalidated. */
|
|
530
578
|
segmentType: "layout" | "route" | "parallel";
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
* //
|
|
170
|
-
* const
|
|
169
|
+
* // Client usage (preferred — cache-safe, always fresh)
|
|
170
|
+
* const { data } = useLoader(CartLoader);
|
|
171
171
|
*
|
|
172
|
-
* //
|
|
173
|
-
* const cart =
|
|
172
|
+
* // Server escape hatch (handler needs data directly)
|
|
173
|
+
* const cart = await ctx.use(CartLoader);
|
|
174
174
|
* ```
|
|
175
175
|
*/
|
|
176
176
|
export type LoaderDefinition<
|
package/src/types/route-entry.ts
CHANGED
|
@@ -55,6 +55,13 @@ export interface RouteEntry<TEnv = any> {
|
|
|
55
55
|
| Promise<() => Array<AllUseItems>>;
|
|
56
56
|
mountIndex: number;
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Router ID that owns this entry. Used to namespace the manifest cache
|
|
60
|
+
* so multi-router setups (host routing) don't share cached EntryData
|
|
61
|
+
* across routers with overlapping mountIndex + routeKey combinations.
|
|
62
|
+
*/
|
|
63
|
+
routerId?: string;
|
|
64
|
+
|
|
58
65
|
/**
|
|
59
66
|
* Route keys in this entry that have pre-render handlers.
|
|
60
67
|
* Used by the non-trie match path to set the `pr` flag.
|
package/src/types/segments.ts
CHANGED
|
@@ -51,9 +51,11 @@ export interface ResolvedSegment {
|
|
|
51
51
|
// Loader-specific fields
|
|
52
52
|
loaderId?: string; // For loaders: the loader $$id identifier
|
|
53
53
|
loaderData?: any; // For loaders: the resolved data from loader execution
|
|
54
|
+
parallelLoading?: ReactNode; // For parallel-owned loaders: the parallel's loading fallback
|
|
54
55
|
// Intercept loader fields (for streaming loader data in parallel segments)
|
|
55
56
|
loaderDataPromise?: Promise<any[]> | any[]; // Loader data promise or resolved array
|
|
56
57
|
loaderIds?: string[]; // IDs ($$id) of loaders for this segment
|
|
58
|
+
parallelLoaderSources?: any[]; // Internal: preserves stable aggregate promise across renders
|
|
57
59
|
// Error-specific fields
|
|
58
60
|
error?: ErrorInfo; // For error segments: the error information
|
|
59
61
|
// NotFound-specific fields
|
package/src/urls/path-helper.ts
CHANGED
|
@@ -13,8 +13,6 @@ export const VIRTUAL_ROUTES_MANIFEST_ID = "virtual:rsc-router/routes-manifest";
|
|
|
13
13
|
export interface PluginOptions {
|
|
14
14
|
enableBuildPrerender?: boolean;
|
|
15
15
|
staticRouteTypesGeneration?: boolean;
|
|
16
|
-
include?: string[];
|
|
17
|
-
exclude?: string[];
|
|
18
16
|
// Mutable ref for deferred auto-discovery (node preset).
|
|
19
17
|
// The auto-discover config() hook populates this before configResolved.
|
|
20
18
|
routerPathRef?: { path?: string };
|
package/src/vite/plugin-types.ts
CHANGED
|
@@ -1,39 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* RSC plugin entry points configuration.
|
|
3
|
-
* All entries use virtual modules by default. Specify a path to use a custom entry file.
|
|
4
|
-
*/
|
|
5
|
-
export interface RscEntries {
|
|
6
|
-
/**
|
|
7
|
-
* Path to a custom browser/client entry file.
|
|
8
|
-
* If not specified, a default virtual entry is used.
|
|
9
|
-
*/
|
|
10
|
-
client?: string;
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Path to a custom SSR entry file.
|
|
14
|
-
* If not specified, a default virtual entry is used.
|
|
15
|
-
*/
|
|
16
|
-
ssr?: string;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Path to a custom RSC entry file.
|
|
20
|
-
* If not specified, a default virtual entry is used that imports the router from the `entry` option.
|
|
21
|
-
*/
|
|
22
|
-
rsc?: string;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Options for @vitejs/plugin-rsc integration
|
|
27
|
-
*/
|
|
28
|
-
export interface RscPluginOptions {
|
|
29
|
-
/**
|
|
30
|
-
* Entry points for client, ssr, and rsc environments.
|
|
31
|
-
* All entries use virtual modules by default.
|
|
32
|
-
* Specify paths only when you need custom entry files.
|
|
33
|
-
*/
|
|
34
|
-
entries?: RscEntries;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
1
|
/**
|
|
38
2
|
* Base options shared by all presets
|
|
39
3
|
*/
|
|
@@ -51,21 +15,6 @@ interface RangoBaseOptions {
|
|
|
51
15
|
* @default true
|
|
52
16
|
*/
|
|
53
17
|
staticRouteTypesGeneration?: boolean;
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Glob patterns for files to include in route type scanning.
|
|
57
|
-
* Only files matching at least one pattern will be scanned.
|
|
58
|
-
* Patterns are relative to the project root.
|
|
59
|
-
* When unset, all .ts/.tsx files are scanned.
|
|
60
|
-
*/
|
|
61
|
-
include?: string[];
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Glob patterns for files to exclude from route type scanning.
|
|
65
|
-
* Takes precedence over `include`. Patterns are relative to the project root.
|
|
66
|
-
* Defaults to common test/build directories.
|
|
67
|
-
*/
|
|
68
|
-
exclude?: string[];
|
|
69
18
|
}
|
|
70
19
|
|
|
71
20
|
/**
|
|
@@ -76,38 +25,6 @@ export interface RangoNodeOptions extends RangoBaseOptions {
|
|
|
76
25
|
* Deployment preset. Defaults to 'node' when not specified.
|
|
77
26
|
*/
|
|
78
27
|
preset?: "node";
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Path to your router configuration file that exports the route tree.
|
|
82
|
-
* This file must export a `router` object created with `createRouter()`.
|
|
83
|
-
*
|
|
84
|
-
* When omitted, auto-discovers the router by scanning for files containing
|
|
85
|
-
* `createRouter`. If exactly one is found, it is used automatically.
|
|
86
|
-
* If multiple are found, an error is thrown with the list of candidates.
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* ```ts
|
|
90
|
-
* rango({ router: './src/router.tsx' })
|
|
91
|
-
* // or simply:
|
|
92
|
-
* rango()
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
router?: string;
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* RSC plugin configuration. By default, rsc-router includes @vitejs/plugin-rsc
|
|
99
|
-
* with sensible defaults.
|
|
100
|
-
*
|
|
101
|
-
* Entry files (browser, ssr, rsc) are optional - if they don't exist,
|
|
102
|
-
* virtual defaults are used.
|
|
103
|
-
*
|
|
104
|
-
* - Omit or pass `true`/`{}` to use defaults (recommended)
|
|
105
|
-
* - Pass `{ entries: {...} }` to customize entry paths
|
|
106
|
-
* - Pass `false` to disable (for manual @vitejs/plugin-rsc configuration)
|
|
107
|
-
*
|
|
108
|
-
* @default true
|
|
109
|
-
*/
|
|
110
|
-
rsc?: boolean | RscPluginOptions;
|
|
111
28
|
}
|
|
112
29
|
|
|
113
30
|
/**
|
|
@@ -278,9 +278,7 @@ export function exposeActionId(): Plugin {
|
|
|
278
278
|
if (!rscPluginApi) {
|
|
279
279
|
throw new Error(
|
|
280
280
|
"[rsc-router] Could not find @vitejs/plugin-rsc. " +
|
|
281
|
-
"@rangojs/router requires the Vite RSC plugin
|
|
282
|
-
"The RSC plugin should be included automatically. If you disabled it with\n" +
|
|
283
|
-
"rango({ rsc: false }), add rsc() before rango() in your config.",
|
|
281
|
+
"@rangojs/router requires the Vite RSC plugin, which is included automatically by rango().",
|
|
284
282
|
);
|
|
285
283
|
}
|
|
286
284
|
|