@rangojs/router 0.0.0-experimental.77 → 0.0.0-experimental.77ed8945
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/README.md +120 -25
- package/dist/bin/rango.js +147 -57
- package/dist/vite/index.js +2103 -861
- package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/package.json +13 -8
- package/skills/api-client/SKILL.md +211 -0
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/bundle-analysis/SKILL.md +159 -0
- package/skills/cache-guide/SKILL.md +220 -30
- package/skills/caching/SKILL.md +116 -8
- package/skills/composability/SKILL.md +27 -2
- package/skills/css/SKILL.md +76 -0
- package/skills/document-cache/SKILL.md +78 -55
- package/skills/handler-use/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +229 -20
- package/skills/host-router/SKILL.md +66 -20
- package/skills/i18n/SKILL.md +276 -0
- package/skills/intercept/SKILL.md +26 -4
- package/skills/layout/SKILL.md +6 -7
- package/skills/links/SKILL.md +247 -17
- package/skills/loader/SKILL.md +219 -9
- package/skills/middleware/SKILL.md +47 -12
- package/skills/migrate-nextjs/SKILL.md +562 -0
- package/skills/migrate-react-router/SKILL.md +769 -0
- package/skills/mime-routes/SKILL.md +27 -0
- package/skills/observability/SKILL.md +137 -0
- package/skills/parallel/SKILL.md +12 -6
- package/skills/prerender/SKILL.md +14 -33
- package/skills/rango/SKILL.md +238 -22
- package/skills/react-compiler/SKILL.md +168 -0
- package/skills/response-routes/SKILL.md +122 -47
- package/skills/route/SKILL.md +33 -4
- package/skills/router-setup/SKILL.md +3 -3
- package/skills/server-actions/SKILL.md +751 -0
- package/skills/streams-and-websockets/SKILL.md +283 -0
- package/skills/tailwind/SKILL.md +27 -3
- package/skills/typesafety/SKILL.md +319 -27
- package/skills/use-cache/SKILL.md +34 -5
- package/skills/view-transitions/SKILL.md +294 -0
- package/src/__augment-tests__/augment.ts +81 -0
- package/src/__augment-tests__/augmented.check.ts +116 -0
- package/src/browser/action-coordinator.ts +53 -36
- package/src/browser/app-shell.ts +39 -0
- package/src/browser/event-controller.ts +86 -70
- package/src/browser/history-state.ts +21 -0
- package/src/browser/index.ts +3 -3
- package/src/browser/navigation-bridge.ts +29 -9
- package/src/browser/navigation-client.ts +99 -77
- package/src/browser/navigation-store.ts +7 -8
- package/src/browser/navigation-transaction.ts +10 -28
- package/src/browser/partial-update.ts +60 -40
- package/src/browser/prefetch/cache.ts +196 -49
- package/src/browser/prefetch/fetch.ts +203 -59
- package/src/browser/prefetch/queue.ts +36 -5
- package/src/browser/rango-state.ts +37 -13
- package/src/browser/react/Link.tsx +18 -13
- package/src/browser/react/NavigationProvider.tsx +75 -31
- package/src/browser/react/filter-segment-order.ts +51 -7
- package/src/browser/react/index.ts +3 -0
- package/src/browser/react/location-state-shared.ts +175 -4
- package/src/browser/react/location-state.ts +39 -13
- package/src/browser/react/use-handle.ts +17 -9
- package/src/browser/react/use-navigation.ts +22 -2
- package/src/browser/react/use-params.ts +20 -8
- package/src/browser/react/use-reverse.ts +106 -0
- package/src/browser/react/use-router.ts +23 -2
- package/src/browser/react/use-segments.ts +11 -8
- package/src/browser/response-adapter.ts +52 -1
- package/src/browser/rsc-router.tsx +71 -22
- package/src/browser/scroll-restoration.ts +22 -14
- package/src/browser/segment-reconciler.ts +10 -14
- package/src/browser/segment-structure-assert.ts +2 -2
- package/src/browser/server-action-bridge.ts +44 -30
- package/src/browser/types.ts +12 -2
- package/src/build/collect-fallback-refs.ts +107 -0
- package/src/build/generate-manifest.ts +60 -35
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/index.ts +8 -1
- package/src/build/prefix-tree-utils.ts +123 -0
- package/src/build/route-trie.ts +45 -1
- package/src/build/route-types/codegen.ts +4 -4
- package/src/build/route-types/include-resolution.ts +1 -1
- package/src/build/route-types/per-module-writer.ts +7 -4
- package/src/build/route-types/router-processing.ts +55 -14
- package/src/build/route-types/scan-filter.ts +1 -1
- package/src/build/route-types/source-scan.ts +118 -0
- package/src/build/runtime-discovery.ts +9 -20
- package/src/cache/cache-runtime.ts +17 -5
- package/src/cache/cache-scope.ts +51 -49
- package/src/cache/cf/cf-cache-store.ts +502 -32
- package/src/cache/cf/index.ts +3 -0
- package/src/cache/handle-snapshot.ts +103 -0
- package/src/cache/index.ts +3 -0
- package/src/cache/memory-segment-store.ts +3 -2
- package/src/cache/types.ts +10 -6
- package/src/client.rsc.tsx +3 -0
- package/src/client.tsx +96 -205
- package/src/context-var.ts +5 -5
- package/src/decode-loader-results.ts +36 -0
- package/src/errors.ts +30 -4
- package/src/handle.ts +4 -6
- package/src/host/index.ts +2 -2
- package/src/host/router.ts +129 -57
- package/src/host/types.ts +31 -2
- package/src/host/utils.ts +1 -1
- package/src/href-client.ts +140 -21
- package/src/index.rsc.ts +10 -6
- package/src/index.ts +17 -8
- package/src/loader-store.ts +500 -0
- package/src/loader.rsc.ts +2 -5
- package/src/loader.ts +3 -10
- package/src/missing-id-error.ts +68 -0
- package/src/outlet-context.ts +1 -1
- package/src/prerender/store.ts +9 -7
- package/src/prerender.ts +4 -4
- package/src/response-utils.ts +37 -0
- package/src/reverse.ts +65 -39
- package/src/route-content-wrapper.tsx +6 -28
- package/src/route-definition/dsl-helpers.ts +253 -265
- package/src/route-definition/helper-factories.ts +29 -139
- package/src/route-definition/helpers-types.ts +43 -15
- package/src/route-definition/resolve-handler-use.ts +6 -0
- package/src/route-definition/use-item-types.ts +32 -0
- package/src/route-types.ts +26 -41
- package/src/router/content-negotiation.ts +15 -2
- package/src/router/error-handling.ts +1 -1
- package/src/router/find-match.ts +54 -6
- package/src/router/handler-context.ts +21 -41
- package/src/router/intercept-resolution.ts +4 -18
- package/src/router/lazy-includes.ts +41 -22
- package/src/router/loader-resolution.ts +82 -36
- package/src/router/manifest.ts +41 -19
- package/src/router/match-api.ts +4 -3
- package/src/router/match-handlers.ts +1 -0
- package/src/router/match-middleware/cache-lookup.ts +57 -95
- package/src/router/match-middleware/cache-store.ts +3 -2
- package/src/router/match-result.ts +53 -32
- package/src/router/metrics.ts +1 -1
- package/src/router/middleware-types.ts +15 -26
- package/src/router/middleware.ts +99 -84
- package/src/router/pattern-matching.ts +116 -19
- package/src/router/prerender-match.ts +40 -15
- package/src/router/preview-match.ts +3 -1
- package/src/router/request-classification.ts +40 -37
- package/src/router/revalidation.ts +58 -2
- package/src/router/router-interfaces.ts +51 -35
- package/src/router/router-options.ts +25 -1
- package/src/router/router-registry.ts +2 -5
- package/src/router/segment-resolution/fresh.ts +27 -6
- package/src/router/segment-resolution/revalidation.ts +147 -106
- package/src/router/segment-resolution/static-store.ts +19 -5
- package/src/router/segment-resolution/view-transition-default.ts +36 -0
- package/src/router/substitute-pattern-params.ts +56 -0
- package/src/router/trie-matching.ts +40 -16
- package/src/router/types.ts +8 -0
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +37 -25
- package/src/rsc/handler-context.ts +2 -2
- package/src/rsc/handler.ts +58 -77
- package/src/rsc/helpers.ts +72 -43
- package/src/rsc/index.ts +1 -1
- package/src/rsc/manifest-init.ts +28 -41
- package/src/rsc/origin-guard.ts +30 -10
- package/src/rsc/progressive-enhancement.ts +4 -0
- package/src/rsc/response-error.ts +79 -12
- package/src/rsc/response-route-handler.ts +76 -61
- package/src/rsc/rsc-rendering.ts +45 -51
- package/src/rsc/runtime-warnings.ts +9 -10
- package/src/rsc/server-action.ts +33 -39
- package/src/rsc/ssr-setup.ts +16 -0
- package/src/rsc/types.ts +8 -2
- package/src/search-params.ts +4 -4
- package/src/segment-content-promise.ts +67 -0
- package/src/segment-loader-promise.ts +122 -0
- package/src/segment-system.tsx +132 -116
- package/src/serialize.ts +243 -0
- package/src/server/context.ts +175 -53
- package/src/server/cookie-store.ts +28 -4
- package/src/server/request-context.ts +57 -51
- package/src/ssr/index.tsx +5 -1
- package/src/static-handler.ts +1 -1
- package/src/types/global-namespace.ts +39 -26
- package/src/types/handler-context.ts +68 -50
- package/src/types/index.ts +1 -0
- package/src/types/loader-types.ts +11 -9
- package/src/types/request-scope.ts +126 -0
- package/src/types/route-entry.ts +11 -0
- package/src/types/segments.ts +35 -2
- package/src/urls/include-helper.ts +34 -67
- package/src/urls/index.ts +1 -5
- package/src/urls/path-helper-types.ts +17 -3
- package/src/urls/path-helper.ts +17 -52
- package/src/urls/pattern-types.ts +36 -19
- package/src/urls/response-types.ts +22 -29
- package/src/urls/type-extraction.ts +58 -139
- package/src/urls/urls-function.ts +1 -5
- package/src/use-loader.tsx +413 -42
- package/src/vite/debug.ts +185 -0
- package/src/vite/discovery/bundle-postprocess.ts +6 -6
- package/src/vite/discovery/discover-routers.ts +106 -75
- package/src/vite/discovery/discovery-errors.ts +194 -0
- package/src/vite/discovery/gate-state.ts +171 -0
- package/src/vite/discovery/prerender-collection.ts +72 -31
- package/src/vite/discovery/route-types-writer.ts +40 -84
- package/src/vite/discovery/self-gen-tracking.ts +27 -1
- package/src/vite/discovery/state.ts +33 -0
- package/src/vite/discovery/virtual-module-codegen.ts +13 -23
- package/src/vite/index.ts +2 -0
- package/src/vite/plugin-types.ts +67 -0
- package/src/vite/plugins/cjs-to-esm.ts +8 -7
- package/src/vite/plugins/client-ref-dedup.ts +16 -0
- package/src/vite/plugins/client-ref-hashing.ts +28 -5
- package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
- package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
- package/src/vite/plugins/cloudflare-protocol-stub.ts +214 -0
- package/src/vite/plugins/expose-action-id.ts +54 -30
- package/src/vite/plugins/expose-id-utils.ts +12 -8
- package/src/vite/plugins/expose-ids/export-analysis.ts +100 -20
- package/src/vite/plugins/expose-ids/handler-transform.ts +8 -61
- package/src/vite/plugins/expose-ids/loader-transform.ts +3 -5
- package/src/vite/plugins/expose-ids/router-transform.ts +20 -3
- package/src/vite/plugins/expose-internal-ids.ts +496 -486
- package/src/vite/plugins/performance-tracks.ts +29 -25
- package/src/vite/plugins/use-cache-transform.ts +65 -50
- package/src/vite/plugins/version-injector.ts +39 -23
- package/src/vite/plugins/version-plugin.ts +59 -2
- package/src/vite/plugins/virtual-entries.ts +2 -2
- package/src/vite/rango.ts +116 -29
- package/src/vite/router-discovery.ts +753 -104
- package/src/vite/utils/ast-handler-extract.ts +15 -15
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/bundle-analysis.ts +4 -2
- package/src/vite/utils/client-chunks.ts +190 -0
- package/src/vite/utils/forward-user-plugins.ts +193 -0
- package/src/vite/utils/manifest-utils.ts +8 -59
- package/src/vite/utils/package-resolution.ts +41 -1
- package/src/vite/utils/prerender-utils.ts +5 -4
- package/src/vite/utils/shared-utils.ts +107 -26
- package/src/browser/action-response-classifier.ts +0 -99
|
@@ -40,6 +40,12 @@ interface VersionMismatchPlan<TEnv = any> {
|
|
|
40
40
|
reloadUrl: string;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
interface AppSwitchReloadPlan {
|
|
44
|
+
mode: "app-switch";
|
|
45
|
+
/** Clean target URL (internal _rsc_* params stripped) to navigate to. */
|
|
46
|
+
reloadUrl: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
43
49
|
interface ResponseRoutePlan<TEnv = any> {
|
|
44
50
|
mode: "response";
|
|
45
51
|
route: RouteSnapshot<TEnv>;
|
|
@@ -86,6 +92,7 @@ interface PartialRenderPlan<TEnv = any> {
|
|
|
86
92
|
export type RequestPlan<TEnv = any> =
|
|
87
93
|
| RedirectPlan<TEnv>
|
|
88
94
|
| VersionMismatchPlan<TEnv>
|
|
95
|
+
| AppSwitchReloadPlan
|
|
89
96
|
| ResponseRoutePlan<TEnv>
|
|
90
97
|
| LoaderFetchPlan<TEnv>
|
|
91
98
|
| PeRenderPlan<TEnv>
|
|
@@ -94,12 +101,13 @@ export type RequestPlan<TEnv = any> =
|
|
|
94
101
|
| PartialRenderPlan<TEnv>;
|
|
95
102
|
|
|
96
103
|
/**
|
|
97
|
-
* Plans that have passed the terminal-check gate (version-mismatch
|
|
98
|
-
* and are ready for execution. Always have a
|
|
104
|
+
* Plans that have passed the terminal-check gate (version-mismatch and
|
|
105
|
+
* app-switch reloads handled) and are ready for execution. Always have a
|
|
106
|
+
* `route` field.
|
|
99
107
|
*/
|
|
100
108
|
export type ExecutableRequestPlan<TEnv = any> = Exclude<
|
|
101
109
|
RequestPlan<TEnv>,
|
|
102
|
-
VersionMismatchPlan<TEnv>
|
|
110
|
+
VersionMismatchPlan<TEnv> | AppSwitchReloadPlan
|
|
103
111
|
>;
|
|
104
112
|
|
|
105
113
|
/**
|
|
@@ -179,6 +187,30 @@ export async function classifyRequest<TEnv = any>(
|
|
|
179
187
|
};
|
|
180
188
|
}
|
|
181
189
|
|
|
190
|
+
// App switch — also runs BEFORE route resolution (like version-mismatch
|
|
191
|
+
// above), and for the same reason: a cross-app SPA navigation must reload
|
|
192
|
+
// even when the target route does NOT exist in the target app. If we resolved
|
|
193
|
+
// first, a missing route would throw RouteNotFoundError and the 404 would
|
|
194
|
+
// render in-place under the SOURCE app's document — violating the invariant
|
|
195
|
+
// that crossing a host-router app boundary is always a full document load.
|
|
196
|
+
// A mismatched routerId (_rsc_rid) means the navigation crossed an app
|
|
197
|
+
// boundary; force a real document navigation. A soft swap can't faithfully
|
|
198
|
+
// re-establish the target app's document (stylesheets shared across apps are
|
|
199
|
+
// dropped by React 19's by-href dedup; theme/warmup/prefetch-TTL are
|
|
200
|
+
// document-lifetime — see browser/app-shell.ts). Only SPA (`_rsc_partial`)
|
|
201
|
+
// requests need this; a direct full load already IS the document navigation.
|
|
202
|
+
const clientRouterId = url.searchParams.get("_rsc_rid");
|
|
203
|
+
if (
|
|
204
|
+
clientRouterId &&
|
|
205
|
+
clientRouterId !== deps.routerId &&
|
|
206
|
+
url.searchParams.has("_rsc_partial")
|
|
207
|
+
) {
|
|
208
|
+
return {
|
|
209
|
+
mode: "app-switch",
|
|
210
|
+
reloadUrl: stripInternalParams(url).toString(),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
182
214
|
// No metricsStore — classification is a lightweight gating step.
|
|
183
215
|
// Route-matching and manifest-loading metrics belong in the match path
|
|
184
216
|
// (createMatchContextForFull/Partial) which runs the authoritative resolution.
|
|
@@ -252,12 +284,7 @@ export async function classifyRequest<TEnv = any>(
|
|
|
252
284
|
return { mode: "pe-render", route: snapshot };
|
|
253
285
|
}
|
|
254
286
|
|
|
255
|
-
|
|
256
|
-
const clientRouterId = url.searchParams.get("_rsc_rid");
|
|
257
|
-
const isAppSwitch = !!(clientRouterId && clientRouterId !== deps.routerId);
|
|
258
|
-
const isPartial = url.searchParams.has("_rsc_partial") && !isAppSwitch;
|
|
259
|
-
|
|
260
|
-
if (isPartial) {
|
|
287
|
+
if (url.searchParams.has("_rsc_partial")) {
|
|
261
288
|
return { mode: "partial-render", route: snapshot, negotiated };
|
|
262
289
|
}
|
|
263
290
|
|
|
@@ -278,33 +305,9 @@ async function classifyResponseRoute<TEnv>(
|
|
|
278
305
|
pathname: string,
|
|
279
306
|
snapshot: RouteSnapshot<TEnv>,
|
|
280
307
|
): Promise<ResponseRoutePlan<TEnv> | null> {
|
|
281
|
-
|
|
282
|
-
|
|
308
|
+
// negotiateRoute returns the response plan (variant or plain) or null for RSC.
|
|
283
309
|
const negotiation = await negotiateRoute(request, pathname, snapshot);
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
route: snapshot,
|
|
288
|
-
...negotiation,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
// Non-negotiated response route (no variants, or RSC won negotiation)
|
|
293
|
-
if (responseType) {
|
|
294
|
-
const handler =
|
|
295
|
-
manifestEntry.type === "route" ? manifestEntry.handler : undefined;
|
|
296
|
-
if (handler) {
|
|
297
|
-
return {
|
|
298
|
-
mode: "response",
|
|
299
|
-
route: snapshot,
|
|
300
|
-
handler,
|
|
301
|
-
responseType,
|
|
302
|
-
negotiated: false,
|
|
303
|
-
manifestEntry,
|
|
304
|
-
routeMiddleware: snapshot.routeMiddleware,
|
|
305
|
-
};
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
return null;
|
|
310
|
+
return negotiation
|
|
311
|
+
? { mode: "response", route: snapshot, ...negotiation }
|
|
312
|
+
: null;
|
|
310
313
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Evaluates whether segments should revalidate based on params, actions, and custom functions.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ResolvedSegment, HandlerContext } from "../types";
|
|
7
|
+
import type { ResolvedSegment, HandlerContext, ActionRef } from "../types";
|
|
8
8
|
import type { ActionContext } from "./types";
|
|
9
9
|
import {
|
|
10
10
|
debugLog,
|
|
@@ -15,6 +15,47 @@ import type { RevalidationTraceEntry } from "./logging.js";
|
|
|
15
15
|
import { _getRequestContext } from "../server/request-context.js";
|
|
16
16
|
import { isAutoGeneratedRouteName } from "../route-name.js";
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Resolve a server-action reference's stable id, mirroring how the action
|
|
20
|
+
* boundary derives `actionContext.actionId` in `rsc/server-action.ts`
|
|
21
|
+
* (`$id ?? $$id`): the file-path `$id` set by the expose-action-id plugin in a
|
|
22
|
+
* production RSC build when present, otherwise React's `$$id`. Resolving both
|
|
23
|
+
* the incoming `actionId` and the reference with the same precedence makes
|
|
24
|
+
* `isAction()` form-agnostic across dev and production.
|
|
25
|
+
*/
|
|
26
|
+
function resolveActionRefId(ref: unknown): string | undefined {
|
|
27
|
+
if (ref == null) return undefined;
|
|
28
|
+
const r = ref as { $id?: unknown; $$id?: unknown };
|
|
29
|
+
if (typeof r.$id === "string") return r.$id;
|
|
30
|
+
if (typeof r.$$id === "string") return r.$$id;
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Build the `isAction()` helper bound to the current action's id. Matches a
|
|
36
|
+
* single imported action reference, several (variadic), or any export of a
|
|
37
|
+
* namespace import (`import * as Mod`). Returns `false` when there is no action
|
|
38
|
+
* (plain navigation) or nothing matches.
|
|
39
|
+
*/
|
|
40
|
+
function makeIsAction(
|
|
41
|
+
currentActionId: string | undefined,
|
|
42
|
+
): (...actions: ActionRef[]) => boolean {
|
|
43
|
+
return (...actions: ActionRef[]): boolean => {
|
|
44
|
+
if (!currentActionId) return false;
|
|
45
|
+
for (const action of actions) {
|
|
46
|
+
if (typeof action === "function") {
|
|
47
|
+
if (resolveActionRefId(action) === currentActionId) return true;
|
|
48
|
+
} else if (action && typeof action === "object") {
|
|
49
|
+
// Namespace import: match any export of the module.
|
|
50
|
+
for (const value of Object.values(action)) {
|
|
51
|
+
if (resolveActionRefId(value) === currentActionId) return true;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return false;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
18
59
|
function paramsEqual(
|
|
19
60
|
a: Record<string, string>,
|
|
20
61
|
b: Record<string, string>,
|
|
@@ -59,6 +100,14 @@ interface EvaluateRevalidationOptions<TEnv> {
|
|
|
59
100
|
stale?: boolean;
|
|
60
101
|
/** Trace source hint for the revalidation trace */
|
|
61
102
|
traceSource?: RevalidationTraceEntry["source"];
|
|
103
|
+
/**
|
|
104
|
+
* Override the segment-type-derived default. When set, the value is used as
|
|
105
|
+
* the seed `defaultShouldRevalidate` passed to user revalidate fns and the
|
|
106
|
+
* reason flows into the trace. Callers use this when client-knowledge
|
|
107
|
+
* (e.g. parallel slot not in clientSegmentIds) should dictate the seed
|
|
108
|
+
* instead of the params/method-based heuristic.
|
|
109
|
+
*/
|
|
110
|
+
defaultOverride?: { value: boolean; reason: string };
|
|
62
111
|
}
|
|
63
112
|
|
|
64
113
|
/**
|
|
@@ -81,6 +130,7 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
81
130
|
actionContext,
|
|
82
131
|
stale,
|
|
83
132
|
traceSource,
|
|
133
|
+
defaultOverride,
|
|
84
134
|
} = options;
|
|
85
135
|
const nextParams = segment.params || {};
|
|
86
136
|
const paramsChanged = !paramsEqual(nextParams, prevParams);
|
|
@@ -110,7 +160,12 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
110
160
|
let defaultShouldRevalidate: boolean;
|
|
111
161
|
let defaultReason: string;
|
|
112
162
|
|
|
113
|
-
if (
|
|
163
|
+
if (defaultOverride) {
|
|
164
|
+
// Caller injected the seed (e.g. parallel slot not in clientSegmentIds).
|
|
165
|
+
// Skip the type-derived heuristic — caller knows better in this context.
|
|
166
|
+
defaultShouldRevalidate = defaultOverride.value;
|
|
167
|
+
defaultReason = defaultOverride.reason;
|
|
168
|
+
} else if (request.method === "POST") {
|
|
114
169
|
// Actions: revalidate segments that belong to the route, skip parent chain
|
|
115
170
|
if (segment.type === "route") {
|
|
116
171
|
// Route segment always revalidates on actions
|
|
@@ -226,6 +281,7 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
226
281
|
slotName: segment.slot,
|
|
227
282
|
// Action context (only populated when triggered by server action)
|
|
228
283
|
actionId: actionContext?.actionId,
|
|
284
|
+
isAction: makeIsAction(actionContext?.actionId),
|
|
229
285
|
actionUrl: actionContext?.actionUrl,
|
|
230
286
|
actionResult: actionContext?.actionResult,
|
|
231
287
|
formData: actionContext?.formData,
|
|
@@ -2,18 +2,15 @@ import type { ComponentType, ReactNode } from "react";
|
|
|
2
2
|
import type { SerializedManifest } from "../debug.js";
|
|
3
3
|
import type { ReverseFunction } from "../reverse.js";
|
|
4
4
|
import type { UrlPatterns } from "../urls.js";
|
|
5
|
-
import type { UrlBuilder } from "../urls/pattern-types.js";
|
|
5
|
+
import type { UrlBuilder, EnvCompatible } from "../urls/pattern-types.js";
|
|
6
6
|
import type { EntryData } from "../server/context";
|
|
7
7
|
import type { ErrorInfo, MatchResult } from "../types";
|
|
8
8
|
import type { NonceProvider } from "../rsc/types.js";
|
|
9
9
|
import type { ExecutionContext } from "../server/request-context.js";
|
|
10
|
-
import type {
|
|
11
|
-
SerializedSegmentData,
|
|
12
|
-
SegmentHandleData,
|
|
13
|
-
} from "../cache/types.js";
|
|
10
|
+
import type { SerializedSegmentData } from "../cache/types.js";
|
|
14
11
|
import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js";
|
|
15
12
|
import { RSC_ROUTER_BRAND } from "./router-registry.js";
|
|
16
|
-
import type {
|
|
13
|
+
import type { RangoOptions, RootLayoutProps } from "./router-options.js";
|
|
17
14
|
import type { DefaultVars } from "../types/global-namespace.js";
|
|
18
15
|
import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js";
|
|
19
16
|
|
|
@@ -49,16 +46,16 @@ type MergeRoutesWithResponses<
|
|
|
49
46
|
};
|
|
50
47
|
|
|
51
48
|
/**
|
|
52
|
-
* Public
|
|
49
|
+
* Public Rango router interface — the user-facing API surface.
|
|
53
50
|
*
|
|
54
51
|
* Users interact with this type when building and using routers.
|
|
55
|
-
* Internal framework code uses
|
|
52
|
+
* Internal framework code uses RangoInternal (via toInternal()) to access
|
|
56
53
|
* matching, build-time, and configuration members that are not part of the
|
|
57
54
|
* public contract.
|
|
58
55
|
*
|
|
59
56
|
* TRoutes accumulates all registered route types through the builder chain.
|
|
60
57
|
*/
|
|
61
|
-
export interface
|
|
58
|
+
export interface Rango<
|
|
62
59
|
TEnv = any,
|
|
63
60
|
TRoutes extends Record<string, unknown> = Record<string, string>,
|
|
64
61
|
> {
|
|
@@ -89,16 +86,16 @@ export interface RSCRouter<
|
|
|
89
86
|
* ])
|
|
90
87
|
* ```
|
|
91
88
|
*/
|
|
92
|
-
routes<T extends UrlPatterns<
|
|
93
|
-
patterns: T,
|
|
94
|
-
):
|
|
89
|
+
routes<T extends UrlPatterns<any, any, any>>(
|
|
90
|
+
patterns: T & EnvCompatible<T, TEnv>,
|
|
91
|
+
): Rango<
|
|
95
92
|
TEnv,
|
|
96
93
|
TRoutes &
|
|
97
94
|
(NonNullable<T["_routes"]> extends Record<string, unknown>
|
|
98
95
|
? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
|
|
99
96
|
: Record<string, string>)
|
|
100
97
|
>;
|
|
101
|
-
routes(builder: UrlBuilder<TEnv>):
|
|
98
|
+
routes(builder: UrlBuilder<TEnv>): Rango<TEnv, TRoutes>;
|
|
102
99
|
|
|
103
100
|
/**
|
|
104
101
|
* Add global middleware that runs on all routes
|
|
@@ -114,7 +111,7 @@ export interface RSCRouter<
|
|
|
114
111
|
use(
|
|
115
112
|
patternOrMiddleware: string | MiddlewareFn<TEnv>,
|
|
116
113
|
middleware?: MiddlewareFn<TEnv>,
|
|
117
|
-
):
|
|
114
|
+
): Rango<TEnv, TRoutes>;
|
|
118
115
|
|
|
119
116
|
/**
|
|
120
117
|
* Type-safe URL builder for registered routes
|
|
@@ -141,7 +138,7 @@ export interface RSCRouter<
|
|
|
141
138
|
* type AppRoutes = typeof _router.routeMap;
|
|
142
139
|
*
|
|
143
140
|
* declare global {
|
|
144
|
-
* namespace
|
|
141
|
+
* namespace Rango {
|
|
145
142
|
* interface RegisteredRoutes extends AppRoutes {}
|
|
146
143
|
* }
|
|
147
144
|
* }
|
|
@@ -177,16 +174,16 @@ export interface RSCRouter<
|
|
|
177
174
|
}
|
|
178
175
|
|
|
179
176
|
/**
|
|
180
|
-
* Internal
|
|
177
|
+
* Internal Rango router interface — the full framework-facing API.
|
|
181
178
|
*
|
|
182
179
|
* This type includes all members used by the Vite plugin, RSC handler,
|
|
183
180
|
* pre-rendering pipeline, and other framework internals. It is NOT exported
|
|
184
181
|
* from the public package API.
|
|
185
182
|
*
|
|
186
|
-
* Use toInternal(router) to assert a public
|
|
183
|
+
* Use toInternal(router) to assert a public Rango into this type
|
|
187
184
|
* at the boundary where framework code receives a user-provided router.
|
|
188
185
|
*/
|
|
189
|
-
export interface
|
|
186
|
+
export interface RangoInternal<
|
|
190
187
|
TEnv = any,
|
|
191
188
|
TRoutes extends Record<string, unknown> = Record<string, string>,
|
|
192
189
|
> {
|
|
@@ -206,18 +203,24 @@ export interface RSCRouterInternal<
|
|
|
206
203
|
readonly basename: string | undefined;
|
|
207
204
|
|
|
208
205
|
/**
|
|
209
|
-
* Register routes using URL patterns from urls() or a builder function
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
206
|
+
* Register routes using URL patterns from urls() or a builder function.
|
|
207
|
+
*
|
|
208
|
+
* Env compatibility is checked by EnvCompatible: an env-agnostic urls() block
|
|
209
|
+
* (its env is `unknown` — e.g. a shared module, or an app that does not augment
|
|
210
|
+
* `Rango.Env`) attaches to any router, while a urls<TEnv>() block carrying a
|
|
211
|
+
* concrete env is accepted only when this router's `TEnv` satisfies it. So a
|
|
212
|
+
* `urls<{ DB }>()` cannot be mounted on a `createRouter<{}>()`.
|
|
213
|
+
*/
|
|
214
|
+
routes<T extends UrlPatterns<any, any, any>>(
|
|
215
|
+
patterns: T & EnvCompatible<T, TEnv>,
|
|
216
|
+
): Rango<
|
|
214
217
|
TEnv,
|
|
215
218
|
TRoutes &
|
|
216
219
|
(NonNullable<T["_routes"]> extends Record<string, unknown>
|
|
217
220
|
? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
|
|
218
221
|
: Record<string, string>)
|
|
219
222
|
>;
|
|
220
|
-
routes(builder: UrlBuilder<TEnv>):
|
|
223
|
+
routes(builder: UrlBuilder<TEnv>): Rango<TEnv, TRoutes>;
|
|
221
224
|
|
|
222
225
|
/**
|
|
223
226
|
* Add global middleware that runs on all routes
|
|
@@ -225,7 +228,7 @@ export interface RSCRouterInternal<
|
|
|
225
228
|
use(
|
|
226
229
|
patternOrMiddleware: string | MiddlewareFn<TEnv>,
|
|
227
230
|
middleware?: MiddlewareFn<TEnv>,
|
|
228
|
-
):
|
|
231
|
+
): Rango<TEnv, TRoutes>;
|
|
229
232
|
|
|
230
233
|
/**
|
|
231
234
|
* Type-safe URL builder for registered routes
|
|
@@ -247,17 +250,17 @@ export interface RSCRouterInternal<
|
|
|
247
250
|
* Error callback for monitoring/alerting
|
|
248
251
|
* Called when errors occur in loaders, actions, or routes
|
|
249
252
|
*/
|
|
250
|
-
readonly onError?:
|
|
253
|
+
readonly onError?: RangoOptions<TEnv>["onError"];
|
|
251
254
|
|
|
252
255
|
/**
|
|
253
256
|
* Cache configuration
|
|
254
257
|
*/
|
|
255
|
-
readonly cache?:
|
|
258
|
+
readonly cache?: RangoOptions<TEnv>["cache"];
|
|
256
259
|
|
|
257
260
|
/**
|
|
258
261
|
* Not found component to render when no route matches
|
|
259
262
|
*/
|
|
260
|
-
readonly notFound?:
|
|
263
|
+
readonly notFound?: RangoOptions<TEnv>["notFound"];
|
|
261
264
|
|
|
262
265
|
/**
|
|
263
266
|
* Resolved theme configuration (null if theme not enabled)
|
|
@@ -359,6 +362,17 @@ export interface RSCRouterInternal<
|
|
|
359
362
|
/** @internal basename for runtime manifest generation */
|
|
360
363
|
readonly __basename?: string;
|
|
361
364
|
|
|
365
|
+
/**
|
|
366
|
+
* @internal Router-level error/notFound fallbacks (`createRouter` options),
|
|
367
|
+
* exposed for the build-time clientChunks discovery so a `"use client"`
|
|
368
|
+
* default boundary is routed into the dedicated `app-fallback` chunk. Unlike
|
|
369
|
+
* the route-tree `errorBoundary()`/`notFoundBoundary()` helpers these never
|
|
370
|
+
* land in `EntryData`, so they are read directly off the router instance.
|
|
371
|
+
*/
|
|
372
|
+
readonly __defaultErrorBoundary?: RangoOptions<TEnv>["defaultErrorBoundary"];
|
|
373
|
+
readonly __defaultNotFoundBoundary?: RangoOptions<TEnv>["defaultNotFoundBoundary"];
|
|
374
|
+
readonly __notFound?: RangoOptions<TEnv>["notFound"];
|
|
375
|
+
|
|
362
376
|
match(
|
|
363
377
|
request: Request,
|
|
364
378
|
input?: RouterRequestInput<TEnv>,
|
|
@@ -378,11 +392,13 @@ export interface RSCRouterInternal<
|
|
|
378
392
|
devMode?: boolean,
|
|
379
393
|
): Promise<{
|
|
380
394
|
segments: SerializedSegmentData[];
|
|
381
|
-
|
|
395
|
+
/** RSC-encoded handle map ("" when none) — see handle-snapshot.ts. */
|
|
396
|
+
handles: string;
|
|
382
397
|
routeName: string;
|
|
383
398
|
params: Record<string, string>;
|
|
384
399
|
interceptSegments?: SerializedSegmentData[];
|
|
385
|
-
|
|
400
|
+
/** RSC-encoded MERGED (main + intercept) handle map for the intercept artifact. */
|
|
401
|
+
interceptHandles?: string;
|
|
386
402
|
passthrough?: true;
|
|
387
403
|
} | null>;
|
|
388
404
|
|
|
@@ -396,7 +412,7 @@ export interface RSCRouterInternal<
|
|
|
396
412
|
routeName?: string,
|
|
397
413
|
buildEnv?: any,
|
|
398
414
|
devMode?: boolean,
|
|
399
|
-
): Promise<{ encoded: string; handles:
|
|
415
|
+
): Promise<{ encoded: string; handles: string } | null>;
|
|
400
416
|
|
|
401
417
|
/**
|
|
402
418
|
* Preview match - returns route middleware without segment resolution.
|
|
@@ -469,16 +485,16 @@ export interface RSCRouterInternal<
|
|
|
469
485
|
}
|
|
470
486
|
|
|
471
487
|
/**
|
|
472
|
-
* Assert a public
|
|
488
|
+
* Assert a public Rango into the internal type.
|
|
473
489
|
*
|
|
474
490
|
* Use this at the boundary where framework code receives a user-provided
|
|
475
491
|
* router and needs access to internal members (match, config, build-time).
|
|
476
492
|
* The cast is safe because createRouter() always produces an object that
|
|
477
|
-
* satisfies
|
|
493
|
+
* satisfies RangoInternal; the public type is just a narrower view.
|
|
478
494
|
*/
|
|
479
495
|
export function toInternal<
|
|
480
496
|
TEnv = any,
|
|
481
497
|
TRoutes extends Record<string, unknown> = Record<string, string>,
|
|
482
|
-
>(router:
|
|
483
|
-
return router as
|
|
498
|
+
>(router: Rango<TEnv, TRoutes>): RangoInternal<TEnv, TRoutes> {
|
|
499
|
+
return router as RangoInternal<TEnv, TRoutes>;
|
|
484
500
|
}
|
|
@@ -73,7 +73,7 @@ export interface RootLayoutProps {
|
|
|
73
73
|
/**
|
|
74
74
|
* Router configuration options
|
|
75
75
|
*/
|
|
76
|
-
export interface
|
|
76
|
+
export interface RangoOptions<TEnv = any> {
|
|
77
77
|
/**
|
|
78
78
|
* Unique identifier for this router instance.
|
|
79
79
|
* Used to namespace static output files and route maps.
|
|
@@ -357,6 +357,30 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
357
357
|
*/
|
|
358
358
|
theme?: import("../theme/types.js").ThemeConfig | true;
|
|
359
359
|
|
|
360
|
+
/**
|
|
361
|
+
* Default for whether the router wraps `transition()` segments in its own
|
|
362
|
+
* React `<ViewTransition>` boundary (experimental React only).
|
|
363
|
+
*
|
|
364
|
+
* - "auto" (default): every route/layout that opts in via `transition()`
|
|
365
|
+
* gets a router-owned cross-fade.
|
|
366
|
+
* - false: the router never places its own boundary. Routes that use
|
|
367
|
+
* `transition()` still drive navigation through startTransition (so loaders
|
|
368
|
+
* hold instead of flashing a skeleton) and still let consumer-placed
|
|
369
|
+
* `<ViewTransition>` elements animate — the router just contributes no
|
|
370
|
+
* cross-fade of its own. This is the "router triggers, you place the
|
|
371
|
+
* transitions" model.
|
|
372
|
+
*
|
|
373
|
+
* A per-segment `transition({ viewTransition })` overrides this default.
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* ```typescript
|
|
377
|
+
* // App-wide: drive + hold, but never auto-wrap. Place <ViewTransition>
|
|
378
|
+
* // yourself in components where you want a morph.
|
|
379
|
+
* const router = createRouter<AppEnv>({ viewTransition: false });
|
|
380
|
+
* ```
|
|
381
|
+
*/
|
|
382
|
+
viewTransition?: "auto" | false;
|
|
383
|
+
|
|
360
384
|
/**
|
|
361
385
|
* URL patterns to register with the router.
|
|
362
386
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RangoInternal } from "./router-interfaces.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Brand marker for identifying router instances at build time.
|
|
@@ -12,10 +12,7 @@ export const RSC_ROUTER_BRAND = "__rsc_router__" as const;
|
|
|
12
12
|
* Used by the Vite plugin at build time to discover routers and extract
|
|
13
13
|
* manifests, prefix trees, and pre-render candidates.
|
|
14
14
|
*/
|
|
15
|
-
export const RouterRegistry: Map<
|
|
16
|
-
string,
|
|
17
|
-
RSCRouterInternal<any, any>
|
|
18
|
-
> = new Map();
|
|
15
|
+
export const RouterRegistry: Map<string, RangoInternal<any, any>> = new Map();
|
|
19
16
|
|
|
20
17
|
export let routerAutoId = 0;
|
|
21
18
|
|
|
@@ -28,11 +28,12 @@ import {
|
|
|
28
28
|
resolveLayoutComponent,
|
|
29
29
|
resolveWithErrorBoundary,
|
|
30
30
|
} from "./helpers.js";
|
|
31
|
+
import { applyViewTransitionDefault } from "./view-transition-default.js";
|
|
31
32
|
import { getRouterContext } from "../router-context.js";
|
|
32
33
|
import { resolveSink, safeEmit } from "../telemetry.js";
|
|
33
34
|
import {
|
|
34
35
|
track,
|
|
35
|
-
|
|
36
|
+
RangoContext,
|
|
36
37
|
runInsideLoaderScope,
|
|
37
38
|
} from "../../server/context.js";
|
|
38
39
|
|
|
@@ -224,7 +225,10 @@ export async function resolveSegment<TEnv>(
|
|
|
224
225
|
index: 0,
|
|
225
226
|
component,
|
|
226
227
|
loading: entry.loading === false ? null : entry.loading,
|
|
227
|
-
transition:
|
|
228
|
+
transition: applyViewTransitionDefault(
|
|
229
|
+
entry.transition,
|
|
230
|
+
deps.viewTransitionDefault,
|
|
231
|
+
),
|
|
228
232
|
params,
|
|
229
233
|
belongsToRoute: false,
|
|
230
234
|
layoutName: entry.id,
|
|
@@ -359,7 +363,10 @@ export async function resolveSegment<TEnv>(
|
|
|
359
363
|
index: 0,
|
|
360
364
|
component: component ?? null,
|
|
361
365
|
loading: entry.loading === false ? null : entry.loading,
|
|
362
|
-
transition:
|
|
366
|
+
transition: applyViewTransitionDefault(
|
|
367
|
+
entry.transition,
|
|
368
|
+
deps.viewTransitionDefault,
|
|
369
|
+
),
|
|
363
370
|
params,
|
|
364
371
|
belongsToRoute: true,
|
|
365
372
|
...(entry.mountPath ? { mountPath: entry.mountPath } : {}),
|
|
@@ -443,7 +450,10 @@ export async function resolveOrphanLayout<TEnv>(
|
|
|
443
450
|
belongsToRoute,
|
|
444
451
|
layoutName: orphan.id,
|
|
445
452
|
loading: orphan.loading === false ? null : orphan.loading,
|
|
446
|
-
transition:
|
|
453
|
+
transition: applyViewTransitionDefault(
|
|
454
|
+
orphan.transition,
|
|
455
|
+
deps.viewTransitionDefault,
|
|
456
|
+
),
|
|
447
457
|
...(orphan.mountPath ? { mountPath: orphan.mountPath } : {}),
|
|
448
458
|
});
|
|
449
459
|
|
|
@@ -515,6 +525,14 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
515
525
|
if (handler === undefined) {
|
|
516
526
|
continue;
|
|
517
527
|
}
|
|
528
|
+
// Pin `_currentSegmentId` to the slot's own id so handle pushes from
|
|
529
|
+
// inside the slot handler get their own bucket in the HandleStore.
|
|
530
|
+
// Parent-keying would collapse them into the parent layout's bucket;
|
|
531
|
+
// the partial-update merge then replaces the parent's bucket on a
|
|
532
|
+
// slot-only revalidation and drops layout-pushed Meta/Breadcrumbs.
|
|
533
|
+
// filterSegmentOrder() retains slot ids so the client preserves them.
|
|
534
|
+
(context as InternalHandlerContext<any, TEnv>)._currentSegmentId =
|
|
535
|
+
`${parentShortCode}.${slot}`;
|
|
518
536
|
const doneParallelHandler = track(
|
|
519
537
|
`handler:${parallelEntry.id}.${slot}`,
|
|
520
538
|
2,
|
|
@@ -557,7 +575,10 @@ export async function resolveParallelEntry<TEnv>(
|
|
|
557
575
|
index: 0,
|
|
558
576
|
component,
|
|
559
577
|
loading: parallelEntry.loading === false ? null : parallelEntry.loading,
|
|
560
|
-
transition:
|
|
578
|
+
transition: applyViewTransitionDefault(
|
|
579
|
+
parallelEntry.transition,
|
|
580
|
+
deps.viewTransitionDefault,
|
|
581
|
+
),
|
|
561
582
|
params,
|
|
562
583
|
slot,
|
|
563
584
|
belongsToRoute,
|
|
@@ -624,7 +645,7 @@ export async function resolveAllSegments<TEnv>(
|
|
|
624
645
|
// can guard non-cacheable variable reads. Also guards response-level
|
|
625
646
|
// side effects (headers.set). Persists for all descendant entries.
|
|
626
647
|
if (entry.type === "cache") {
|
|
627
|
-
const store =
|
|
648
|
+
const store = RangoContext.getStore();
|
|
628
649
|
if (store) store.insideCacheScope = true;
|
|
629
650
|
}
|
|
630
651
|
const doneEntry = track(`segment:${entry.id}`, 1);
|