@rangojs/router 0.0.0-experimental.40 → 0.0.0-experimental.42
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/vite/index.js +1 -1
- package/package.json +1 -1
- package/src/browser/navigation-bridge.ts +10 -2
- package/src/browser/scroll-restoration.ts +5 -1
- package/src/route-map-builder.ts +7 -1
- package/src/router/find-match.ts +4 -2
- package/src/router/intercept-resolution.ts +2 -0
- package/src/router/lazy-includes.ts +2 -0
- package/src/router/logging.ts +4 -1
- package/src/router/manifest.ts +3 -1
- package/src/router/match-middleware/segment-resolution.ts +1 -0
- package/src/router/router-context.ts +5 -1
- package/src/router/segment-resolution/revalidation.ts +4 -1
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router.ts +4 -0
- package/src/types/route-entry.ts +7 -0
package/dist/vite/index.js
CHANGED
|
@@ -1745,7 +1745,7 @@ import { resolve } from "node:path";
|
|
|
1745
1745
|
// package.json
|
|
1746
1746
|
var package_default = {
|
|
1747
1747
|
name: "@rangojs/router",
|
|
1748
|
-
version: "0.0.0-experimental.
|
|
1748
|
+
version: "0.0.0-experimental.42",
|
|
1749
1749
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
1750
1750
|
keywords: [
|
|
1751
1751
|
"react",
|
package/package.json
CHANGED
|
@@ -485,8 +485,16 @@ export function createNavigationBridge(
|
|
|
485
485
|
onUpdate(popstateUpdate);
|
|
486
486
|
}
|
|
487
487
|
|
|
488
|
-
// Restore scroll position for back/forward navigation
|
|
489
|
-
|
|
488
|
+
// Restore scroll position for back/forward navigation.
|
|
489
|
+
// Defer to next frame so React has committed the cached content to
|
|
490
|
+
// the DOM before we measure scrollHeight and restore scroll position.
|
|
491
|
+
const defer =
|
|
492
|
+
typeof requestAnimationFrame === "function"
|
|
493
|
+
? requestAnimationFrame
|
|
494
|
+
: (fn: () => void) => setTimeout(fn, 0);
|
|
495
|
+
defer(() => {
|
|
496
|
+
handleNavigationEnd({ restore: true, isStreaming });
|
|
497
|
+
});
|
|
490
498
|
|
|
491
499
|
// SWR: If stale, trigger background revalidation
|
|
492
500
|
if (isStale) {
|
|
@@ -288,7 +288,11 @@ export function restoreScrollPosition(options?: {
|
|
|
288
288
|
// Not streaming — scroll after React commits and browser paints.
|
|
289
289
|
// startTransition defers the DOM commit, so scrolling synchronously
|
|
290
290
|
// would be overwritten when React replaces the content.
|
|
291
|
-
|
|
291
|
+
const defer =
|
|
292
|
+
typeof requestAnimationFrame === "function"
|
|
293
|
+
? requestAnimationFrame
|
|
294
|
+
: (fn: () => void) => setTimeout(fn, 0);
|
|
295
|
+
defer(() => {
|
|
292
296
|
window.scrollTo(0, savedY);
|
|
293
297
|
debugLog("[Scroll] Restored position:", savedY, "for key:", key);
|
|
294
298
|
});
|
package/src/route-map-builder.ts
CHANGED
|
@@ -199,7 +199,13 @@ export function registerRouterManifestLoader(
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
export async function ensureRouterManifest(routerId: string): Promise<void> {
|
|
202
|
-
|
|
202
|
+
// Check both manifest AND trie. The virtual module's setRouterManifest()
|
|
203
|
+
// pre-sets the manifest at startup, but the per-router trie is only
|
|
204
|
+
// available from the lazy loader. Without this, the lazy loader never
|
|
205
|
+
// runs and findMatch falls back to the global merged trie — which
|
|
206
|
+
// contains routes from ALL routers and breaks multi-router setups.
|
|
207
|
+
if (perRouterManifestMap.has(routerId) && perRouterTrieMap.has(routerId))
|
|
208
|
+
return;
|
|
203
209
|
const loader = routerManifestLoaders.get(routerId);
|
|
204
210
|
if (loader) {
|
|
205
211
|
const mod = await loader();
|
package/src/router/find-match.ts
CHANGED
|
@@ -52,8 +52,10 @@ export function createFindMatch<TEnv = any>(
|
|
|
52
52
|
: undefined;
|
|
53
53
|
|
|
54
54
|
// Phase 1: Try trie match (O(path_length))
|
|
55
|
-
//
|
|
56
|
-
|
|
55
|
+
// Only use the per-router trie. The global trie merges routes from ALL
|
|
56
|
+
// routers and must not be used — in multi-router setups (host routing)
|
|
57
|
+
// overlapping paths like "/" would match the wrong app's route.
|
|
58
|
+
const routeTrie = getRouterTrie(deps.routerId);
|
|
57
59
|
if (routeTrie) {
|
|
58
60
|
const trieStart = performance.now();
|
|
59
61
|
const trieResult = tryTrieMatch(routeTrie, pathname);
|
|
@@ -188,6 +188,7 @@ export async function resolveInterceptEntry<TEnv>(
|
|
|
188
188
|
context,
|
|
189
189
|
actionContext,
|
|
190
190
|
stale,
|
|
191
|
+
traceSource: "intercept-loader",
|
|
191
192
|
});
|
|
192
193
|
|
|
193
194
|
if (!shouldRevalidate) {
|
|
@@ -355,6 +356,7 @@ export async function resolveInterceptLoadersOnly<TEnv>(
|
|
|
355
356
|
context,
|
|
356
357
|
actionContext,
|
|
357
358
|
stale,
|
|
359
|
+
traceSource: "intercept-loader",
|
|
358
360
|
});
|
|
359
361
|
|
|
360
362
|
if (!shouldRevalidate) {
|
|
@@ -14,6 +14,7 @@ export interface LazyEvalDeps<TEnv = any> {
|
|
|
14
14
|
mergedRouteMap: Record<string, string>;
|
|
15
15
|
nextMountIndex: () => number;
|
|
16
16
|
getPrecomputedByPrefix: () => Map<string, Record<string, string>> | null;
|
|
17
|
+
routerId?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
// Detect lazy includes in handler result and create placeholder entries
|
|
@@ -200,6 +201,7 @@ export function evaluateLazyEntry<TEnv = any>(
|
|
|
200
201
|
trailingSlash: entry.trailingSlash,
|
|
201
202
|
handler: (lazyInclude.patterns as UrlPatterns<TEnv>).handler,
|
|
202
203
|
mountIndex: deps.nextMountIndex(),
|
|
204
|
+
routerId: deps.routerId,
|
|
203
205
|
// Lazy evaluation fields
|
|
204
206
|
lazy: true,
|
|
205
207
|
lazyPatterns: lazyInclude.patterns,
|
package/src/router/logging.ts
CHANGED
|
@@ -12,7 +12,10 @@ export interface RevalidationTraceEntry {
|
|
|
12
12
|
| "cache-hit"
|
|
13
13
|
| "loader"
|
|
14
14
|
| "parallel"
|
|
15
|
-
| "orphan-layout"
|
|
15
|
+
| "orphan-layout"
|
|
16
|
+
| "route-handler"
|
|
17
|
+
| "layout-handler"
|
|
18
|
+
| "intercept-loader";
|
|
16
19
|
defaultShouldRevalidate: boolean;
|
|
17
20
|
finalShouldRevalidate: boolean;
|
|
18
21
|
reason: string;
|
package/src/router/manifest.ts
CHANGED
|
@@ -65,7 +65,9 @@ export async function loadManifest(
|
|
|
65
65
|
const mountIndex = entry.mountIndex;
|
|
66
66
|
|
|
67
67
|
// Check module-level cache (persists across requests within same isolate)
|
|
68
|
-
|
|
68
|
+
// Include routerId so multi-router setups (host routing) don't share cached
|
|
69
|
+
// EntryData across routers with overlapping mountIndex + routeKey combinations.
|
|
70
|
+
const cacheKey = `${VERSION}:${entry.routerId ?? ""}:${mountIndex ?? ""}:${routeKey}:${isSSR ? 1 : 0}`;
|
|
69
71
|
const cached = manifestModuleCache.get(cacheKey);
|
|
70
72
|
if (cached) {
|
|
71
73
|
const cacheStart = performance.now();
|
|
@@ -138,6 +138,7 @@ export interface RouterContext<TEnv = any> {
|
|
|
138
138
|
interceptResult: InterceptResult | null,
|
|
139
139
|
localRouteName: string,
|
|
140
140
|
pathname: string,
|
|
141
|
+
stale?: boolean,
|
|
141
142
|
) => Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }>;
|
|
142
143
|
|
|
143
144
|
// Generator-based segment resolution (for pipeline)
|
|
@@ -188,7 +189,10 @@ export interface RouterContext<TEnv = any> {
|
|
|
188
189
|
| "cache-hit"
|
|
189
190
|
| "loader"
|
|
190
191
|
| "parallel"
|
|
191
|
-
| "orphan-layout"
|
|
192
|
+
| "orphan-layout"
|
|
193
|
+
| "route-handler"
|
|
194
|
+
| "layout-handler"
|
|
195
|
+
| "intercept-loader";
|
|
192
196
|
}) => Promise<boolean>;
|
|
193
197
|
|
|
194
198
|
// Request context
|
|
@@ -608,6 +608,8 @@ export async function resolveEntryHandlerWithRevalidation<TEnv>(
|
|
|
608
608
|
context,
|
|
609
609
|
actionContext,
|
|
610
610
|
stale,
|
|
611
|
+
traceSource:
|
|
612
|
+
entry.type === "route" ? "route-handler" : "layout-handler",
|
|
611
613
|
});
|
|
612
614
|
emitRevalidationDecision(
|
|
613
615
|
entry.shortCode,
|
|
@@ -1165,6 +1167,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1165
1167
|
localRouteName: string,
|
|
1166
1168
|
pathname: string,
|
|
1167
1169
|
deps: SegmentResolutionDeps<TEnv>,
|
|
1170
|
+
stale?: boolean,
|
|
1168
1171
|
): Promise<{ segments: ResolvedSegment[]; matchedIds: string[] }> {
|
|
1169
1172
|
const allSegments: ResolvedSegment[] = [];
|
|
1170
1173
|
const matchedIds: string[] = [];
|
|
@@ -1209,7 +1212,7 @@ export async function resolveAllSegmentsWithRevalidation<TEnv>(
|
|
|
1209
1212
|
loaderPromises,
|
|
1210
1213
|
deps,
|
|
1211
1214
|
actionContext,
|
|
1212
|
-
|
|
1215
|
+
stale,
|
|
1213
1216
|
),
|
|
1214
1217
|
(seg) => ({ segments: [seg], matchedIds: [seg.id] }),
|
|
1215
1218
|
deps,
|
|
@@ -204,6 +204,7 @@ export function createSegmentWrappers<TEnv = any>(
|
|
|
204
204
|
interceptResult: { intercept: InterceptEntry; entry: EntryData } | null,
|
|
205
205
|
localRouteName: string,
|
|
206
206
|
pathname: string,
|
|
207
|
+
stale?: boolean,
|
|
207
208
|
): ReturnType<typeof _resolveAllSegmentsWithRevalidation> {
|
|
208
209
|
return _resolveAllSegmentsWithRevalidation(
|
|
209
210
|
entries,
|
|
@@ -221,6 +222,7 @@ export function createSegmentWrappers<TEnv = any>(
|
|
|
221
222
|
localRouteName,
|
|
222
223
|
pathname,
|
|
223
224
|
segmentDeps,
|
|
225
|
+
stale,
|
|
224
226
|
);
|
|
225
227
|
}
|
|
226
228
|
|
package/src/router.ts
CHANGED
|
@@ -560,6 +560,7 @@ export function createRouter<TEnv = any>(
|
|
|
560
560
|
mergedRouteMap,
|
|
561
561
|
nextMountIndex: () => mountIndex++,
|
|
562
562
|
getPrecomputedByPrefix,
|
|
563
|
+
routerId,
|
|
563
564
|
};
|
|
564
565
|
|
|
565
566
|
function evaluateLazyEntry(entry: RouteEntry<TEnv>): void {
|
|
@@ -751,6 +752,7 @@ export function createRouter<TEnv = any>(
|
|
|
751
752
|
trailingSlash: trailingSlashConfig,
|
|
752
753
|
handler: urlPatterns.handler,
|
|
753
754
|
mountIndex: currentMountIndex,
|
|
755
|
+
routerId,
|
|
754
756
|
cacheProfiles: resolvedCacheProfiles,
|
|
755
757
|
...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
|
|
756
758
|
...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
|
|
@@ -770,6 +772,7 @@ export function createRouter<TEnv = any>(
|
|
|
770
772
|
trailingSlash: trailingSlashConfig,
|
|
771
773
|
handler: urlPatterns.handler,
|
|
772
774
|
mountIndex: currentMountIndex,
|
|
775
|
+
routerId,
|
|
773
776
|
cacheProfiles: resolvedCacheProfiles,
|
|
774
777
|
...(prerenderRouteKeys ? { prerenderRouteKeys } : {}),
|
|
775
778
|
...(passthroughRouteKeys ? { passthroughRouteKeys } : {}),
|
|
@@ -813,6 +816,7 @@ export function createRouter<TEnv = any>(
|
|
|
813
816
|
trailingSlash: trailingSlashConfig,
|
|
814
817
|
handler: urlPatterns.handler,
|
|
815
818
|
mountIndex: mountIndex++,
|
|
819
|
+
routerId,
|
|
816
820
|
// Lazy evaluation fields
|
|
817
821
|
lazy: true,
|
|
818
822
|
lazyPatterns: lazyInclude.patterns,
|
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.
|