@rangojs/router 0.0.0-experimental.29 → 0.0.0-experimental.2a0dea97
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/AGENTS.md +4 -0
- package/README.md +78 -19
- package/dist/bin/rango.js +138 -50
- package/dist/vite/index.js +853 -435
- package/package.json +17 -16
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +45 -4
- package/skills/handler-use/SKILL.md +362 -0
- package/skills/hooks/SKILL.md +22 -4
- package/skills/intercept/SKILL.md +20 -0
- package/skills/layout/SKILL.md +22 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/loader/SKILL.md +71 -21
- package/skills/middleware/SKILL.md +34 -3
- package/skills/migrate-nextjs/SKILL.md +560 -0
- package/skills/migrate-react-router/SKILL.md +764 -0
- package/skills/parallel/SKILL.md +185 -0
- package/skills/prerender/SKILL.md +110 -68
- package/skills/rango/SKILL.md +24 -22
- package/skills/route/SKILL.md +56 -2
- package/skills/router-setup/SKILL.md +87 -2
- package/skills/typesafety/SKILL.md +33 -21
- package/src/__internal.ts +92 -0
- package/src/browser/app-version.ts +14 -0
- package/src/browser/event-controller.ts +5 -0
- package/src/browser/link-interceptor.ts +4 -0
- package/src/browser/navigation-bridge.ts +125 -16
- package/src/browser/navigation-client.ts +142 -57
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +94 -17
- package/src/browser/prefetch/cache.ts +82 -12
- package/src/browser/prefetch/fetch.ts +98 -27
- package/src/browser/prefetch/policy.ts +6 -0
- package/src/browser/prefetch/queue.ts +92 -20
- package/src/browser/prefetch/resource-ready.ts +77 -0
- package/src/browser/react/Link.tsx +88 -9
- package/src/browser/react/NavigationProvider.tsx +40 -4
- package/src/browser/react/context.ts +7 -2
- package/src/browser/react/use-handle.ts +9 -58
- package/src/browser/react/use-router.ts +21 -8
- package/src/browser/rsc-router.tsx +134 -59
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +72 -10
- package/src/browser/server-action-bridge.ts +8 -6
- package/src/browser/types.ts +55 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +3 -0
- package/src/build/route-trie.ts +50 -24
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +223 -74
- package/src/build/route-types/scan-filter.ts +8 -1
- 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/client.rsc.tsx +2 -0
- package/src/client.tsx +6 -66
- package/src/context-var.ts +72 -2
- package/src/debug.ts +2 -2
- package/src/handle.ts +40 -0
- package/src/handles/breadcrumbs.ts +66 -0
- package/src/handles/index.ts +1 -0
- package/src/index.rsc.ts +6 -36
- package/src/index.ts +50 -43
- package/src/prerender/store.ts +5 -4
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +25 -1
- package/src/route-definition/dsl-helpers.ts +224 -37
- package/src/route-definition/helpers-types.ts +67 -19
- package/src/route-definition/index.ts +3 -0
- package/src/route-definition/redirect.ts +11 -3
- package/src/route-definition/resolve-handler-use.ts +149 -0
- package/src/route-map-builder.ts +7 -1
- package/src/route-types.ts +11 -0
- package/src/router/content-negotiation.ts +100 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/handler-context.ts +111 -25
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +4 -1
- package/src/router/loader-resolution.ts +156 -21
- package/src/router/logging.ts +5 -2
- package/src/router/manifest.ts +9 -3
- package/src/router/match-api.ts +125 -190
- package/src/router/match-middleware/background-revalidation.ts +30 -2
- package/src/router/match-middleware/cache-lookup.ts +94 -17
- 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 +61 -5
- package/src/router/match-result.ts +104 -10
- package/src/router/metrics.ts +6 -1
- package/src/router/middleware-types.ts +16 -22
- package/src/router/middleware.ts +24 -30
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/prerender-match.ts +114 -10
- package/src/router/preview-match.ts +30 -102
- package/src/router/request-classification.ts +310 -0
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +36 -4
- package/src/router/router-options.ts +37 -11
- package/src/router/segment-resolution/fresh.ts +198 -20
- package/src/router/segment-resolution/helpers.ts +30 -25
- package/src/router/segment-resolution/loader-cache.ts +1 -0
- package/src/router/segment-resolution/revalidation.ts +438 -300
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/types.ts +1 -0
- package/src/router.ts +59 -6
- package/src/rsc/handler.ts +472 -372
- package/src/rsc/loader-fetch.ts +23 -3
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +14 -2
- package/src/rsc/rsc-rendering.ts +12 -1
- package/src/rsc/server-action.ts +8 -0
- package/src/rsc/ssr-setup.ts +2 -2
- package/src/rsc/types.ts +9 -1
- package/src/segment-content-promise.ts +33 -0
- package/src/segment-system.tsx +164 -23
- package/src/server/context.ts +140 -14
- package/src/server/handle-store.ts +19 -0
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +204 -28
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/types/cache-types.ts +4 -4
- package/src/types/handler-context.ts +149 -49
- package/src/types/loader-types.ts +36 -9
- package/src/types/route-entry.ts +8 -1
- package/src/types/segments.ts +6 -0
- package/src/urls/path-helper-types.ts +39 -6
- package/src/urls/path-helper.ts +48 -13
- package/src/urls/pattern-types.ts +12 -0
- package/src/urls/response-types.ts +16 -6
- package/src/use-loader.tsx +77 -5
- package/src/vite/discovery/bundle-postprocess.ts +30 -33
- package/src/vite/discovery/discover-routers.ts +5 -1
- package/src/vite/discovery/prerender-collection.ts +128 -74
- package/src/vite/discovery/state.ts +13 -6
- package/src/vite/index.ts +4 -0
- package/src/vite/plugin-types.ts +51 -79
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/expose-id-utils.ts +12 -0
- package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
- package/src/vite/plugins/expose-internal-ids.ts +257 -40
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +88 -26
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +163 -211
- package/src/vite/router-discovery.ts +178 -45
- package/src/vite/utils/banner.ts +3 -3
- package/src/vite/utils/prerender-utils.ts +37 -5
- package/src/vite/utils/shared-utils.ts +3 -2
package/src/static-handler.ts
CHANGED
|
@@ -32,11 +32,21 @@
|
|
|
32
32
|
*/
|
|
33
33
|
import type { ReactNode } from "react";
|
|
34
34
|
import type { Handler } from "./types.js";
|
|
35
|
-
import type {
|
|
35
|
+
import type { StaticBuildContext } from "./prerender.js";
|
|
36
|
+
import type { UseItems, HandlerUseItem } from "./route-types.js";
|
|
36
37
|
import { isCachedFunction } from "./cache/taint.js";
|
|
37
38
|
|
|
38
39
|
// -- Types ------------------------------------------------------------------
|
|
39
40
|
|
|
41
|
+
export interface StaticHandlerOptions {
|
|
42
|
+
/**
|
|
43
|
+
* Keep handler in server bundle for live fallback (default: false).
|
|
44
|
+
* false: handler replaced with stub, source-only APIs excluded from bundle.
|
|
45
|
+
* true: handler stays in bundle, renders live at request time.
|
|
46
|
+
*/
|
|
47
|
+
passthrough?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
40
50
|
export interface StaticHandlerDefinition<
|
|
41
51
|
TParams extends Record<string, any> = any,
|
|
42
52
|
> {
|
|
@@ -46,14 +56,16 @@ export interface StaticHandlerDefinition<
|
|
|
46
56
|
/** In dev mode, the actual handler function that layout/path/parallel can call. */
|
|
47
57
|
handler: Handler<TParams>;
|
|
48
58
|
/** Static handler options (passthrough support). */
|
|
49
|
-
options?:
|
|
59
|
+
options?: StaticHandlerOptions;
|
|
60
|
+
/** Composable default DSL items merged when the handler is mounted. */
|
|
61
|
+
use?: () => UseItems<HandlerUseItem>;
|
|
50
62
|
}
|
|
51
63
|
|
|
52
64
|
// -- Function ---------------------------------------------------------------
|
|
53
65
|
|
|
54
66
|
export function Static<TParams extends Record<string, any> = {}>(
|
|
55
67
|
handler: (ctx: StaticBuildContext) => ReactNode | Promise<ReactNode>,
|
|
56
|
-
options?:
|
|
68
|
+
options?: StaticHandlerOptions,
|
|
57
69
|
__injectedId?: string,
|
|
58
70
|
): StaticHandlerDefinition<TParams>;
|
|
59
71
|
|
|
@@ -61,7 +73,7 @@ export function Static<TParams extends Record<string, any> = {}>(
|
|
|
61
73
|
|
|
62
74
|
export function Static<TParams extends Record<string, any>>(
|
|
63
75
|
handler: Function,
|
|
64
|
-
optionsOrId?:
|
|
76
|
+
optionsOrId?: StaticHandlerOptions | string,
|
|
65
77
|
maybeId?: string,
|
|
66
78
|
): StaticHandlerDefinition<TParams> {
|
|
67
79
|
if (isCachedFunction(handler)) {
|
|
@@ -72,13 +84,13 @@ export function Static<TParams extends Record<string, any>>(
|
|
|
72
84
|
);
|
|
73
85
|
}
|
|
74
86
|
|
|
75
|
-
let options:
|
|
87
|
+
let options: StaticHandlerOptions | undefined;
|
|
76
88
|
let id: string;
|
|
77
89
|
|
|
78
90
|
if (typeof optionsOrId === "string") {
|
|
79
91
|
id = optionsOrId;
|
|
80
92
|
} else {
|
|
81
|
-
options = optionsOrId as
|
|
93
|
+
options = optionsOrId as StaticHandlerOptions | undefined;
|
|
82
94
|
id = maybeId ?? "";
|
|
83
95
|
}
|
|
84
96
|
|
package/src/types/cache-types.ts
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* during cache key generation (before middleware runs).
|
|
6
6
|
*
|
|
7
7
|
* Note: While the full RequestContext is passed, middleware-set variables
|
|
8
|
-
*
|
|
9
|
-
*
|
|
8
|
+
* read via `ctx.get()` may not be populated yet since cache lookup happens
|
|
9
|
+
* before middleware execution.
|
|
10
10
|
*/
|
|
11
11
|
export type { RequestContext as CacheContext } from "../server/request-context.js";
|
|
12
12
|
|
|
@@ -101,7 +101,7 @@ export interface CacheOptions<TEnv = unknown> {
|
|
|
101
101
|
* Return false to skip cache for this request (always fetch fresh).
|
|
102
102
|
*
|
|
103
103
|
* Has access to full RequestContext including env, request, params, cookies, etc.
|
|
104
|
-
* Note: Middleware-set variables
|
|
104
|
+
* Note: Middleware-set variables read via `ctx.get()` may not be populated yet.
|
|
105
105
|
*
|
|
106
106
|
* @example
|
|
107
107
|
* ```typescript
|
|
@@ -123,7 +123,7 @@ export interface CacheOptions<TEnv = unknown> {
|
|
|
123
123
|
* Bypasses default key generation AND store's keyGenerator.
|
|
124
124
|
*
|
|
125
125
|
* Has access to full RequestContext including env, request, params, cookies, etc.
|
|
126
|
-
* Note: Middleware-set variables
|
|
126
|
+
* Note: Middleware-set variables read via `ctx.get()` may not be populated yet.
|
|
127
127
|
*
|
|
128
128
|
* @example
|
|
129
129
|
* ```typescript
|
|
@@ -19,6 +19,7 @@ import type {
|
|
|
19
19
|
ResolvedRouteMap,
|
|
20
20
|
} from "./route-config.js";
|
|
21
21
|
import type { LoaderDefinition } from "./loader-types.js";
|
|
22
|
+
import type { UseItems, HandlerUseItem } from "../route-types.js";
|
|
22
23
|
|
|
23
24
|
// Re-export MiddlewareFn for internal/advanced use
|
|
24
25
|
export type { MiddlewareFn } from "../router/middleware.js";
|
|
@@ -135,7 +136,7 @@ export type Handler<
|
|
|
135
136
|
| Record<string, any> = {},
|
|
136
137
|
TRouteMap extends {} = DefaultHandlerRouteMap,
|
|
137
138
|
TEnv = DefaultEnv,
|
|
138
|
-
> = (
|
|
139
|
+
> = ((
|
|
139
140
|
ctx: HandlerContext<
|
|
140
141
|
T extends `.${infer Local}`
|
|
141
142
|
? Local extends keyof TRouteMap
|
|
@@ -160,7 +161,10 @@ export type Handler<
|
|
|
160
161
|
: ExtractSearchFromEntry<DefaultHandlerRouteMap, T>,
|
|
161
162
|
TRouteMap extends DefaultHandlerRouteMap ? never : TRouteMap
|
|
162
163
|
>,
|
|
163
|
-
) => ReactNode | Promise<ReactNode> | Response | Promise<Response
|
|
164
|
+
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>) & {
|
|
165
|
+
/** Composable default DSL items merged when the handler is mounted. */
|
|
166
|
+
use?: () => UseItems<HandlerUseItem>;
|
|
167
|
+
};
|
|
164
168
|
|
|
165
169
|
/**
|
|
166
170
|
* Context passed to handlers (Hono-inspired type-safe context)
|
|
@@ -170,7 +174,7 @@ export type Handler<
|
|
|
170
174
|
* - Cleaned route URL (`url`, `searchParams`, `pathname` — no `_rsc*` params)
|
|
171
175
|
* - Original request (`request` — raw transport URL, headers, method, body)
|
|
172
176
|
* - Platform bindings (env.DB, env.KV, env.SECRETS)
|
|
173
|
-
* - Middleware variables (
|
|
177
|
+
* - Middleware variables (`get("user")`, `get("permissions")`)
|
|
174
178
|
* - Getter/setter for variables (get('user'), set('user', ...))
|
|
175
179
|
*
|
|
176
180
|
* @example
|
|
@@ -178,8 +182,7 @@ export type Handler<
|
|
|
178
182
|
* const handler = (ctx: HandlerContext<{ slug: string }, AppEnv>) => {
|
|
179
183
|
* ctx.params.slug // Route param (string)
|
|
180
184
|
* ctx.env.DB // Binding (D1Database)
|
|
181
|
-
* ctx.
|
|
182
|
-
* ctx.get('user') // Alternative getter
|
|
185
|
+
* ctx.get('user') // Variable (User | undefined)
|
|
183
186
|
* ctx.set('user', {...}) // Setter
|
|
184
187
|
* ctx.url // Clean URL (no _rsc* params)
|
|
185
188
|
* ctx.searchParams // Clean params (no _rsc* params)
|
|
@@ -206,6 +209,12 @@ export type HandlerContext<
|
|
|
206
209
|
* Live request rendering, including passthrough fallback, uses `false`.
|
|
207
210
|
*/
|
|
208
211
|
build: boolean;
|
|
212
|
+
/**
|
|
213
|
+
* True when running in Vite dev mode, false during production build or
|
|
214
|
+
* live request rendering. Use this to branch on runtime mode without
|
|
215
|
+
* changing build semantics (e.g., skip expensive operations in dev).
|
|
216
|
+
*/
|
|
217
|
+
dev: boolean;
|
|
209
218
|
/**
|
|
210
219
|
* The original incoming Request object (transport URL intact).
|
|
211
220
|
* Use `ctx.url` / `ctx.searchParams` for application logic — those have
|
|
@@ -228,22 +237,25 @@ export type HandlerContext<
|
|
|
228
237
|
*/
|
|
229
238
|
pathname: string;
|
|
230
239
|
/**
|
|
231
|
-
* The full URL object (with
|
|
240
|
+
* The full URL object (with internal `_rsc*` params stripped).
|
|
241
|
+
* Use this for application logic — routing, link generation, display.
|
|
232
242
|
*/
|
|
233
243
|
url: URL;
|
|
244
|
+
/**
|
|
245
|
+
* The original request URL with all parameters intact, including
|
|
246
|
+
* internal `_rsc*` transport params. Use `ctx.url` for application
|
|
247
|
+
* logic — this is only needed for advanced cases like debugging
|
|
248
|
+
* or custom cache keying.
|
|
249
|
+
*/
|
|
250
|
+
originalUrl: URL;
|
|
234
251
|
/**
|
|
235
252
|
* Platform bindings (DB, KV, secrets, etc.).
|
|
236
253
|
* Access resources like `ctx.env.DB`, `ctx.env.KV`.
|
|
237
254
|
*/
|
|
238
255
|
env: TEnv;
|
|
239
|
-
/**
|
|
240
|
-
* Middleware-injected variables.
|
|
241
|
-
* Access values like `ctx.var.user`, `ctx.var.permissions`.
|
|
242
|
-
*/
|
|
243
|
-
var: DefaultVars;
|
|
244
256
|
/**
|
|
245
257
|
* Type-safe getter for middleware variables.
|
|
246
|
-
*
|
|
258
|
+
* Preferred way to read middleware-injected variables.
|
|
247
259
|
*
|
|
248
260
|
* @example
|
|
249
261
|
* ```typescript
|
|
@@ -264,24 +276,18 @@ export type HandlerContext<
|
|
|
264
276
|
* ```
|
|
265
277
|
*/
|
|
266
278
|
set: {
|
|
267
|
-
<T>(
|
|
268
|
-
|
|
279
|
+
<T>(
|
|
280
|
+
contextVar: ContextVar<T>,
|
|
281
|
+
value: T,
|
|
282
|
+
options?: { cache?: boolean },
|
|
283
|
+
): void;
|
|
284
|
+
} & (<K extends keyof DefaultVars>(
|
|
285
|
+
key: K,
|
|
286
|
+
value: DefaultVars[K],
|
|
287
|
+
options?: { cache?: boolean },
|
|
288
|
+
) => void);
|
|
269
289
|
/**
|
|
270
|
-
*
|
|
271
|
-
* Headers set here are merged into the final response.
|
|
272
|
-
*
|
|
273
|
-
* @example
|
|
274
|
-
* ```typescript
|
|
275
|
-
* route("product", (ctx) => {
|
|
276
|
-
* ctx.res.headers.set("Cache-Control", "s-maxage=60");
|
|
277
|
-
* return <ProductPage />;
|
|
278
|
-
* });
|
|
279
|
-
* ```
|
|
280
|
-
*/
|
|
281
|
-
res: Response;
|
|
282
|
-
/**
|
|
283
|
-
* Shorthand for ctx.res.headers - response headers.
|
|
284
|
-
* Headers set here are merged into the final response.
|
|
290
|
+
* Response headers. Headers set here are merged into the final response.
|
|
285
291
|
*
|
|
286
292
|
* @example
|
|
287
293
|
* ```typescript
|
|
@@ -295,8 +301,15 @@ export type HandlerContext<
|
|
|
295
301
|
/**
|
|
296
302
|
* Access loader data or push handle data.
|
|
297
303
|
*
|
|
304
|
+
* Available in route handlers, layout handlers, middleware, server actions,
|
|
305
|
+
* and server components rendered within the request context.
|
|
306
|
+
*
|
|
298
307
|
* For loaders: Returns a promise that resolves to the loader data.
|
|
299
308
|
* Loaders are executed in parallel and memoized per request.
|
|
309
|
+
* Prefer DSL `loader()` + client `useLoader()` over `ctx.use(Loader)` —
|
|
310
|
+
* DSL loaders are always fresh and cache-safe. Use `ctx.use(Loader)` only
|
|
311
|
+
* when you need loader data in the handler itself (e.g., to set context
|
|
312
|
+
* variables or make routing decisions).
|
|
300
313
|
*
|
|
301
314
|
* For handles: Returns a push function to add data for this segment.
|
|
302
315
|
* Handle data accumulates across all matched route segments.
|
|
@@ -304,10 +317,11 @@ export type HandlerContext<
|
|
|
304
317
|
*
|
|
305
318
|
* @example
|
|
306
319
|
* ```typescript
|
|
307
|
-
* // Loader
|
|
308
|
-
* route("
|
|
309
|
-
* const
|
|
310
|
-
*
|
|
320
|
+
* // Loader escape hatch — use when handler needs the data directly
|
|
321
|
+
* route("product", async (ctx) => {
|
|
322
|
+
* const { product } = await ctx.use(ProductLoader);
|
|
323
|
+
* ctx.set(Product, product); // make available to children
|
|
324
|
+
* return <ProductPage />;
|
|
311
325
|
* });
|
|
312
326
|
*
|
|
313
327
|
* // Handle usage - direct value
|
|
@@ -436,6 +450,10 @@ export type InternalHandlerContext<
|
|
|
436
450
|
TEnv = DefaultEnv,
|
|
437
451
|
TSearch extends SearchSchema = {},
|
|
438
452
|
> = HandlerContext<TParams, TEnv, TSearch> & {
|
|
453
|
+
/** @internal Stub response for collecting headers/cookies. */
|
|
454
|
+
res: Response;
|
|
455
|
+
/** @internal Shared variable backing store for ctx.get()/ctx.set(). */
|
|
456
|
+
_variables: Record<string, any>;
|
|
439
457
|
/** Prerender-only control flow helper, attached when the runtime context supports it. */
|
|
440
458
|
passthrough?: () => unknown;
|
|
441
459
|
/** Current segment ID for handle data attribution. */
|
|
@@ -523,30 +541,112 @@ export type RevalidateParams<TParams = GenericParams, TEnv = any> = Parameters<
|
|
|
523
541
|
* })
|
|
524
542
|
* ```
|
|
525
543
|
*/
|
|
544
|
+
/**
|
|
545
|
+
* Revalidation function called during client-side navigation to decide whether
|
|
546
|
+
* a segment (layout, route, parallel slot, or loader) should be re-rendered.
|
|
547
|
+
*
|
|
548
|
+
* Return `true` to re-render, `false` to skip (keep client's current version),
|
|
549
|
+
* or `{ defaultShouldRevalidate: boolean }` to override the default for
|
|
550
|
+
* downstream segments.
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* ```ts
|
|
554
|
+
* // Re-render only when a cart action happened or browser signals staleness
|
|
555
|
+
* revalidate(({ actionId, stale }) =>
|
|
556
|
+
* actionId?.includes("cart") || stale || false
|
|
557
|
+
* )
|
|
558
|
+
*
|
|
559
|
+
* // Always re-render when params change (default behavior made explicit)
|
|
560
|
+
* revalidate(({ defaultShouldRevalidate }) => defaultShouldRevalidate)
|
|
561
|
+
* ```
|
|
562
|
+
*/
|
|
526
563
|
export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
|
|
564
|
+
/** Route params from the page being navigated away from. */
|
|
527
565
|
currentParams: TParams;
|
|
566
|
+
/** Full URL of the page being navigated away from. */
|
|
528
567
|
currentUrl: URL;
|
|
568
|
+
/** Route params for the navigation target. */
|
|
529
569
|
nextParams: TParams;
|
|
570
|
+
/** Full URL of the navigation target. */
|
|
530
571
|
nextUrl: URL;
|
|
572
|
+
/**
|
|
573
|
+
* The router's default revalidation decision for this segment.
|
|
574
|
+
* `true` when params changed or the segment is new to the client.
|
|
575
|
+
* Return this when you want default behavior plus your own conditions.
|
|
576
|
+
*/
|
|
531
577
|
defaultShouldRevalidate: boolean;
|
|
578
|
+
/** Full handler context — access to `ctx.use()`, `ctx.env`, `ctx.params`, etc. */
|
|
532
579
|
context: HandlerContext<TParams, TEnv>;
|
|
533
|
-
|
|
580
|
+
|
|
581
|
+
// ── Segment metadata (which segment is being evaluated) ──────────────
|
|
582
|
+
|
|
583
|
+
/** The type of segment being revalidated. */
|
|
534
584
|
segmentType: "layout" | "route" | "parallel";
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
585
|
+
/** Layout name (e.g., `"root"`, `"shop"`, `"auth"`). Only set for layout segments. */
|
|
586
|
+
layoutName?: string;
|
|
587
|
+
/** Slot name (e.g., `"@sidebar"`, `"@modal"`). Only set for parallel segments. */
|
|
588
|
+
slotName?: string;
|
|
589
|
+
|
|
590
|
+
// ── Action context (populated when revalidation is triggered by a server action) ──
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* Identifier of the server action that triggered revalidation.
|
|
594
|
+
* `undefined` during normal navigation (no action involved).
|
|
595
|
+
*
|
|
596
|
+
* Format: `"src/<path>#<exportName>"` — the file path is the source path
|
|
597
|
+
* relative to the project root, followed by `#` and the exported function name.
|
|
598
|
+
*
|
|
599
|
+
* This is stable and can be used for path-based matching to revalidate
|
|
600
|
+
* when any action in a module or directory fires:
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* ```ts
|
|
604
|
+
* // Match a specific action
|
|
605
|
+
* revalidate(({ actionId }) => actionId === "src/actions/cart.ts#addToCart")
|
|
606
|
+
*
|
|
607
|
+
* // Match any action in the cart module
|
|
608
|
+
* revalidate(({ actionId }) => actionId?.includes("cart") ?? false)
|
|
609
|
+
*
|
|
610
|
+
* // Match any action under src/apps/store/actions/
|
|
611
|
+
* revalidate(({ actionId }) => actionId?.startsWith("src/apps/store/actions/") ?? false)
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
actionId?: string;
|
|
615
|
+
/** URL where the action was executed (the page the user was on when they triggered the action). */
|
|
616
|
+
actionUrl?: URL;
|
|
617
|
+
/** Return value from the action execution. Can be used to conditionally revalidate based on the action's outcome. */
|
|
618
|
+
actionResult?: any;
|
|
619
|
+
/** FormData from the action request body. Only set for form-based actions (not inline `"use server"` actions). */
|
|
620
|
+
formData?: FormData;
|
|
621
|
+
/** HTTP method: `"GET"` for navigation, `"POST"` for server actions. */
|
|
622
|
+
method?: string;
|
|
623
|
+
|
|
624
|
+
// ── Route identity ───────────────────────────────────────────────────
|
|
625
|
+
|
|
626
|
+
/** Route name of the navigation target. Alias for `toRouteName`. */
|
|
627
|
+
routeName?: DefaultRouteName;
|
|
628
|
+
/**
|
|
629
|
+
* Route name being navigated away from.
|
|
630
|
+
* `undefined` for unnamed internal routes (those without a `name` option).
|
|
631
|
+
*/
|
|
632
|
+
fromRouteName?: DefaultRouteName;
|
|
633
|
+
/**
|
|
634
|
+
* Route name being navigated to.
|
|
635
|
+
* `undefined` for unnamed internal routes (those without a `name` option).
|
|
636
|
+
*/
|
|
637
|
+
toRouteName?: DefaultRouteName;
|
|
638
|
+
|
|
639
|
+
// ── Staleness signal ─────────────────────────────────────────────────
|
|
640
|
+
|
|
641
|
+
/**
|
|
642
|
+
* `true` when the browser signals that data may be stale — typically because
|
|
643
|
+
* a server action was executed in this or another tab (`_rsc_stale` header).
|
|
644
|
+
*
|
|
645
|
+
* This is NOT segment cache staleness (loaders are never segment-cached).
|
|
646
|
+
* Use this to decide whether loader data should be re-fetched after an
|
|
647
|
+
* action that may have mutated backend state.
|
|
648
|
+
*/
|
|
649
|
+
stale?: boolean;
|
|
550
650
|
}) => boolean | { defaultShouldRevalidate: boolean };
|
|
551
651
|
|
|
552
652
|
// MiddlewareFn is imported from "../router/middleware.js" and re-exported
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ContextVar } from "../context-var.js";
|
|
2
|
+
import type { Handle } from "../handle.js";
|
|
2
3
|
import type { MiddlewareFn } from "../router/middleware.js";
|
|
3
4
|
import type { ScopedReverseFunction } from "../reverse.js";
|
|
4
5
|
import type { SearchSchema, ResolveSearchSchema } from "../search-params.js";
|
|
@@ -53,16 +54,42 @@ export type LoaderContext<
|
|
|
53
54
|
pathname: string;
|
|
54
55
|
url: URL;
|
|
55
56
|
env: TEnv;
|
|
56
|
-
var: DefaultVars;
|
|
57
57
|
get: {
|
|
58
58
|
<T>(contextVar: ContextVar<T>): T | undefined;
|
|
59
59
|
} & (<K extends keyof DefaultVars>(key: K) => DefaultVars[K]);
|
|
60
60
|
/**
|
|
61
|
-
* Access another loader's data
|
|
61
|
+
* Access another loader's data, or read handle data after rendered().
|
|
62
|
+
*
|
|
63
|
+
* For loaders: returns a promise (loaders run in parallel).
|
|
64
|
+
* For handles: returns collected data (only after `await ctx.rendered()`).
|
|
62
65
|
*/
|
|
63
|
-
use:
|
|
64
|
-
|
|
65
|
-
|
|
66
|
+
use: {
|
|
67
|
+
<T, TLoaderParams = any>(
|
|
68
|
+
loader: LoaderDefinition<T, TLoaderParams>,
|
|
69
|
+
): Promise<T>;
|
|
70
|
+
<TData, TAccumulated = TData[]>(
|
|
71
|
+
handle: Handle<TData, TAccumulated>,
|
|
72
|
+
): TAccumulated;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* **Experimental.** Wait for all non-loader segments to settle.
|
|
76
|
+
*
|
|
77
|
+
* After the returned promise resolves, handle data is available via
|
|
78
|
+
* `ctx.use(handle)`. Only supported in DSL loaders on non-streaming
|
|
79
|
+
* trees (no `loading()`). Throws if called from a handler-invoked
|
|
80
|
+
* loader or when the tree uses streaming.
|
|
81
|
+
*
|
|
82
|
+
* @example
|
|
83
|
+
* ```typescript
|
|
84
|
+
* const PricesLoader = createLoader(async (ctx) => {
|
|
85
|
+
* "use server";
|
|
86
|
+
* await ctx.rendered();
|
|
87
|
+
* const products = ctx.use(Products); // reads handle data
|
|
88
|
+
* return pricing.getLive(products.map(p => p.id));
|
|
89
|
+
* });
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
rendered: () => Promise<void>;
|
|
66
93
|
/**
|
|
67
94
|
* HTTP method (GET, POST, PUT, PATCH, DELETE)
|
|
68
95
|
* Available when loader is called via load({ method: "POST", ... })
|
|
@@ -166,11 +193,11 @@ export type LoadOptions =
|
|
|
166
193
|
* return await db.products.findBySlug(slug);
|
|
167
194
|
* });
|
|
168
195
|
*
|
|
169
|
-
* //
|
|
170
|
-
* const
|
|
196
|
+
* // Client usage (preferred — cache-safe, always fresh)
|
|
197
|
+
* const { data } = useLoader(CartLoader);
|
|
171
198
|
*
|
|
172
|
-
* //
|
|
173
|
-
* const cart =
|
|
199
|
+
* // Server escape hatch (handler needs data directly)
|
|
200
|
+
* const cart = await ctx.use(CartLoader);
|
|
174
201
|
* ```
|
|
175
202
|
*/
|
|
176
203
|
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.
|
|
@@ -62,7 +69,7 @@ export interface RouteEntry<TEnv = any> {
|
|
|
62
69
|
prerenderRouteKeys?: Set<string>;
|
|
63
70
|
|
|
64
71
|
/**
|
|
65
|
-
* Route keys in this entry that
|
|
72
|
+
* Route keys in this entry that are wrapped with `Passthrough()`.
|
|
66
73
|
* Used by the non-trie match path to set the `pt` flag.
|
|
67
74
|
*/
|
|
68
75
|
passthroughRouteKeys?: Set<string>;
|
package/src/types/segments.ts
CHANGED
|
@@ -50,10 +50,16 @@ export interface ResolvedSegment {
|
|
|
50
50
|
parallelName?: string; // For parallels: the parallel group name (used to match with revalidations)
|
|
51
51
|
// Loader-specific fields
|
|
52
52
|
loaderId?: string; // For loaders: the loader $$id identifier
|
|
53
|
+
_inherited?: boolean; // For inherited loaders: dedup marker for buildMatchResult
|
|
53
54
|
loaderData?: any; // For loaders: the resolved data from loader execution
|
|
55
|
+
parallelLoading?: ReactNode; // For parallel-owned loaders: the parallel's loading fallback
|
|
54
56
|
// Intercept loader fields (for streaming loader data in parallel segments)
|
|
55
57
|
loaderDataPromise?: Promise<any[]> | any[]; // Loader data promise or resolved array
|
|
56
58
|
loaderIds?: string[]; // IDs ($$id) of loaders for this segment
|
|
59
|
+
parallelLoaderSources?: any[]; // Internal: preserves stable aggregate promise across renders
|
|
60
|
+
layoutLoaderSources?: any[]; // Internal: preserves stable Promise.all reference for layout/route loaders across renders
|
|
61
|
+
contentPromise?: Promise<ReactNode>; // Internal: preserves stable Promise wrapper for component across renders
|
|
62
|
+
contentSource?: ReactNode; // Internal: the component value contentPromise wraps (for reference equality check)
|
|
57
63
|
// Error-specific fields
|
|
58
64
|
error?: ErrorInfo; // For error segments: the error information
|
|
59
65
|
// NotFound-specific fields
|
|
@@ -37,7 +37,10 @@ import type {
|
|
|
37
37
|
UseItems,
|
|
38
38
|
} from "../route-types.js";
|
|
39
39
|
import type { SearchSchema } from "../search-params.js";
|
|
40
|
-
import type {
|
|
40
|
+
import type {
|
|
41
|
+
PrerenderHandlerDefinition,
|
|
42
|
+
PassthroughHandlerDefinition,
|
|
43
|
+
} from "../prerender.js";
|
|
41
44
|
import type { StaticHandlerDefinition } from "../static-handler.js";
|
|
42
45
|
import type { InterceptWhenFn } from "../server/context";
|
|
43
46
|
import type {
|
|
@@ -70,6 +73,7 @@ export type PathFn<TEnv> = <
|
|
|
70
73
|
ctx: HandlerContext<TParams, TEnv, TSearch>,
|
|
71
74
|
) => ReactNode | Promise<ReactNode> | Response | Promise<Response>)
|
|
72
75
|
| PrerenderHandlerDefinition<TParams>
|
|
76
|
+
| PassthroughHandlerDefinition<TParams, TEnv>
|
|
73
77
|
| StaticHandlerDefinition<TParams>,
|
|
74
78
|
optionsOrUse?: PathOptions<TName, TSearch> | (() => UseItems<RouteUseItem>),
|
|
75
79
|
use?: () => UseItems<RouteUseItem>,
|
|
@@ -229,12 +233,27 @@ export type PathHelpers<TEnv> = {
|
|
|
229
233
|
include: IncludeFn<TEnv>;
|
|
230
234
|
|
|
231
235
|
/**
|
|
232
|
-
* Define parallel routes that render simultaneously in named slots
|
|
236
|
+
* Define parallel routes that render simultaneously in named slots.
|
|
237
|
+
*
|
|
238
|
+
* A slot value can be a Handler / ReactNode / StaticHandlerDefinition
|
|
239
|
+
* (legacy form, broadcast use applies to every slot) or a slot descriptor
|
|
240
|
+
* `{ handler, use? }` whose `use` is scoped to that slot only. Per-slot
|
|
241
|
+
* merge order is `handler.use` → shared `use` → slot-local `use`, with
|
|
242
|
+
* narrowest scope winning for last-write-wins items like `loading()`.
|
|
233
243
|
*/
|
|
234
244
|
parallel: <
|
|
235
245
|
TSlots extends Record<
|
|
236
246
|
`@${string}`,
|
|
237
|
-
Handler<any, any, TEnv>
|
|
247
|
+
| Handler<any, any, TEnv>
|
|
248
|
+
| ReactNode
|
|
249
|
+
| StaticHandlerDefinition
|
|
250
|
+
| {
|
|
251
|
+
handler:
|
|
252
|
+
| Handler<any, any, TEnv>
|
|
253
|
+
| ReactNode
|
|
254
|
+
| StaticHandlerDefinition;
|
|
255
|
+
use?: () => ParallelUseItem[];
|
|
256
|
+
}
|
|
238
257
|
>,
|
|
239
258
|
>(
|
|
240
259
|
slots: TSlots,
|
|
@@ -260,9 +279,20 @@ export type PathHelpers<TEnv> = {
|
|
|
260
279
|
) => InterceptItem;
|
|
261
280
|
|
|
262
281
|
/**
|
|
263
|
-
* Attach middleware to the current route/layout
|
|
282
|
+
* Attach middleware to the current route/layout, or wrap child segments
|
|
264
283
|
*/
|
|
265
|
-
middleware:
|
|
284
|
+
middleware: {
|
|
285
|
+
(fn: MiddlewareFn<TEnv>): MiddlewareItem;
|
|
286
|
+
(
|
|
287
|
+
fn: MiddlewareFn<TEnv>,
|
|
288
|
+
children: () => UseItems<LayoutUseItem>,
|
|
289
|
+
): MiddlewareItem;
|
|
290
|
+
(fns: MiddlewareFn<TEnv>[]): MiddlewareItem;
|
|
291
|
+
(
|
|
292
|
+
fns: MiddlewareFn<TEnv>[],
|
|
293
|
+
children: () => UseItems<LayoutUseItem>,
|
|
294
|
+
): MiddlewareItem;
|
|
295
|
+
};
|
|
266
296
|
|
|
267
297
|
/**
|
|
268
298
|
* Control when a segment should revalidate during navigation
|
|
@@ -280,7 +310,10 @@ export type PathHelpers<TEnv> = {
|
|
|
280
310
|
/**
|
|
281
311
|
* Attach a loading component to the current route/layout
|
|
282
312
|
*/
|
|
283
|
-
loading: (
|
|
313
|
+
loading: (
|
|
314
|
+
component: ReactNode | (() => ReactNode),
|
|
315
|
+
options?: { ssr?: boolean },
|
|
316
|
+
) => LoadingItem;
|
|
284
317
|
|
|
285
318
|
/**
|
|
286
319
|
* Attach an error boundary to catch errors in this segment
|