@rangojs/router 0.0.0-experimental.90 → 0.0.0-experimental.91
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
CHANGED
|
@@ -2040,7 +2040,7 @@ import { resolve } from "node:path";
|
|
|
2040
2040
|
// package.json
|
|
2041
2041
|
var package_default = {
|
|
2042
2042
|
name: "@rangojs/router",
|
|
2043
|
-
version: "0.0.0-experimental.
|
|
2043
|
+
version: "0.0.0-experimental.91",
|
|
2044
2044
|
description: "Django-inspired RSC router with composable URL patterns",
|
|
2045
2045
|
keywords: [
|
|
2046
2046
|
"react",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rangojs/router",
|
|
3
|
-
"version": "0.0.0-experimental.
|
|
3
|
+
"version": "0.0.0-experimental.91",
|
|
4
4
|
"description": "Django-inspired RSC router with composable URL patterns",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -132,6 +132,15 @@
|
|
|
132
132
|
"access": "public",
|
|
133
133
|
"tag": "experimental"
|
|
134
134
|
},
|
|
135
|
+
"scripts": {
|
|
136
|
+
"build": "pnpm dlx esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && mkdir -p dist/vite/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/vite/plugins/cloudflare-protocol-loader-hook.mjs && pnpm dlx esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
|
|
137
|
+
"prepublishOnly": "pnpm build",
|
|
138
|
+
"typecheck": "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit",
|
|
139
|
+
"test": "playwright test",
|
|
140
|
+
"test:ui": "playwright test --ui",
|
|
141
|
+
"test:unit": "vitest run",
|
|
142
|
+
"test:unit:watch": "vitest"
|
|
143
|
+
},
|
|
135
144
|
"dependencies": {
|
|
136
145
|
"@vitejs/plugin-rsc": "^0.5.23",
|
|
137
146
|
"debug": "^4.4.1",
|
|
@@ -143,12 +152,12 @@
|
|
|
143
152
|
"@playwright/test": "^1.49.1",
|
|
144
153
|
"@types/debug": "^4.1.12",
|
|
145
154
|
"@types/node": "^24.10.1",
|
|
146
|
-
"@types/react": "
|
|
147
|
-
"@types/react-dom": "
|
|
155
|
+
"@types/react": "catalog:",
|
|
156
|
+
"@types/react-dom": "catalog:",
|
|
148
157
|
"esbuild": "^0.27.0",
|
|
149
158
|
"jiti": "^2.6.1",
|
|
150
|
-
"react": "
|
|
151
|
-
"react-dom": "
|
|
159
|
+
"react": "catalog:",
|
|
160
|
+
"react-dom": "catalog:",
|
|
152
161
|
"tinyexec": "^0.3.2",
|
|
153
162
|
"typescript": "^5.3.0",
|
|
154
163
|
"vitest": "^4.0.0"
|
|
@@ -166,13 +175,5 @@
|
|
|
166
175
|
"vite": {
|
|
167
176
|
"optional": true
|
|
168
177
|
}
|
|
169
|
-
},
|
|
170
|
-
"scripts": {
|
|
171
|
-
"build": "pnpm dlx esbuild src/vite/index.ts --bundle --format=esm --outfile=dist/vite/index.js --platform=node --packages=external && mkdir -p dist/vite/plugins && cp src/vite/plugins/cloudflare-protocol-loader-hook.mjs dist/vite/plugins/cloudflare-protocol-loader-hook.mjs && pnpm dlx esbuild src/bin/rango.ts --bundle --format=esm --outfile=dist/bin/rango.js --platform=node --packages=external --banner:js='#!/usr/bin/env node' && chmod +x dist/bin/rango.js",
|
|
172
|
-
"typecheck": "tsc --noEmit && tsc -p tsconfig.strict-check.json --noEmit",
|
|
173
|
-
"test": "playwright test",
|
|
174
|
-
"test:ui": "playwright test --ui",
|
|
175
|
-
"test:unit": "vitest run",
|
|
176
|
-
"test:unit:watch": "vitest"
|
|
177
178
|
}
|
|
178
|
-
}
|
|
179
|
+
}
|
|
@@ -59,6 +59,14 @@ interface EvaluateRevalidationOptions<TEnv> {
|
|
|
59
59
|
stale?: boolean;
|
|
60
60
|
/** Trace source hint for the revalidation trace */
|
|
61
61
|
traceSource?: RevalidationTraceEntry["source"];
|
|
62
|
+
/**
|
|
63
|
+
* Override the segment-type-derived default. When set, the value is used as
|
|
64
|
+
* the seed `defaultShouldRevalidate` passed to user revalidate fns and the
|
|
65
|
+
* reason flows into the trace. Callers use this when client-knowledge
|
|
66
|
+
* (e.g. parallel slot not in clientSegmentIds) should dictate the seed
|
|
67
|
+
* instead of the params/method-based heuristic.
|
|
68
|
+
*/
|
|
69
|
+
defaultOverride?: { value: boolean; reason: string };
|
|
62
70
|
}
|
|
63
71
|
|
|
64
72
|
/**
|
|
@@ -81,6 +89,7 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
81
89
|
actionContext,
|
|
82
90
|
stale,
|
|
83
91
|
traceSource,
|
|
92
|
+
defaultOverride,
|
|
84
93
|
} = options;
|
|
85
94
|
const nextParams = segment.params || {};
|
|
86
95
|
const paramsChanged = !paramsEqual(nextParams, prevParams);
|
|
@@ -110,7 +119,12 @@ export async function evaluateRevalidation<TEnv>(
|
|
|
110
119
|
let defaultShouldRevalidate: boolean;
|
|
111
120
|
let defaultReason: string;
|
|
112
121
|
|
|
113
|
-
if (
|
|
122
|
+
if (defaultOverride) {
|
|
123
|
+
// Caller injected the seed (e.g. parallel slot not in clientSegmentIds).
|
|
124
|
+
// Skip the type-derived heuristic — caller knows better in this context.
|
|
125
|
+
defaultShouldRevalidate = defaultOverride.value;
|
|
126
|
+
defaultReason = defaultOverride.reason;
|
|
127
|
+
} else if (request.method === "POST") {
|
|
114
128
|
// Actions: revalidate segments that belong to the route, skip parent chain
|
|
115
129
|
if (segment.type === "route") {
|
|
116
130
|
// Route segment always revalidates on actions
|
|
@@ -89,6 +89,27 @@ function observeStreamedHandler(
|
|
|
89
89
|
});
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* Trace a parallel slot that's being force-rendered on a full refetch (client
|
|
94
|
+
* has no cached state). User revalidate fns are bypassed in this case — see
|
|
95
|
+
* the call sites for the load-bearing rationale.
|
|
96
|
+
*/
|
|
97
|
+
function traceFullRefetchedParallelSlot(
|
|
98
|
+
parallelId: string,
|
|
99
|
+
belongsToRoute: boolean,
|
|
100
|
+
): void {
|
|
101
|
+
if (!isTraceActive()) return;
|
|
102
|
+
pushRevalidationTraceEntry({
|
|
103
|
+
segmentId: parallelId,
|
|
104
|
+
segmentType: "parallel",
|
|
105
|
+
belongsToRoute,
|
|
106
|
+
source: "parallel",
|
|
107
|
+
defaultShouldRevalidate: true,
|
|
108
|
+
finalShouldRevalidate: true,
|
|
109
|
+
reason: "full-refetch",
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
92
113
|
// ---------------------------------------------------------------------------
|
|
93
114
|
// Revalidation telemetry helper
|
|
94
115
|
// ---------------------------------------------------------------------------
|
|
@@ -448,44 +469,30 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
448
469
|
|
|
449
470
|
const isFullRefetch = clientSegmentIds.size === 0;
|
|
450
471
|
const isNewParent = !clientSegmentIds.has(entry.shortCode);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
) {
|
|
457
|
-
matchedIds.push(parallelId);
|
|
458
|
-
}
|
|
472
|
+
// Always announce the slot in matchedIds — it's unconditionally appended
|
|
473
|
+
// to `segments` below, and a segment present in segments but missing from
|
|
474
|
+
// matched lets the client prune it (then it's missing from clientSegmentIds
|
|
475
|
+
// on the next request, perpetuating the staleness).
|
|
476
|
+
matchedIds.push(parallelId);
|
|
459
477
|
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
}
|
|
473
|
-
return true;
|
|
474
|
-
}
|
|
478
|
+
let shouldResolve: boolean;
|
|
479
|
+
if (isFullRefetch) {
|
|
480
|
+
// Client has nothing cached — slot MUST render. User revalidate fns are
|
|
481
|
+
// bypassed here because returning false would leave the segment blank
|
|
482
|
+
// with no client-side fallback.
|
|
483
|
+
traceFullRefetchedParallelSlot(parallelId, belongsToRoute);
|
|
484
|
+
shouldResolve = true;
|
|
485
|
+
} else {
|
|
486
|
+
// For non-empty client sets, consult user revalidate fns. When the slot
|
|
487
|
+
// is unknown to the client, override the type-derived default so the
|
|
488
|
+
// soft chain seeds with the right "new segment" / "parent-chain" value.
|
|
489
|
+
let defaultOverride: { value: boolean; reason: string } | undefined;
|
|
475
490
|
if (!clientSegmentIds.has(parallelId)) {
|
|
476
|
-
const
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
belongsToRoute,
|
|
482
|
-
source: "parallel",
|
|
483
|
-
defaultShouldRevalidate: result,
|
|
484
|
-
finalShouldRevalidate: result,
|
|
485
|
-
reason: result ? "new-segment" : "skip-parent-chain",
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
return result;
|
|
491
|
+
const value = belongsToRoute || isNewParent;
|
|
492
|
+
defaultOverride = {
|
|
493
|
+
value,
|
|
494
|
+
reason: value ? "new-segment" : "skip-parent-chain",
|
|
495
|
+
};
|
|
489
496
|
}
|
|
490
497
|
|
|
491
498
|
const dummySegment: ResolvedSegment = {
|
|
@@ -503,7 +510,7 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
503
510
|
: {}),
|
|
504
511
|
};
|
|
505
512
|
|
|
506
|
-
|
|
513
|
+
shouldResolve = await evaluateRevalidation({
|
|
507
514
|
segment: dummySegment,
|
|
508
515
|
prevParams,
|
|
509
516
|
getPrevSegment: null,
|
|
@@ -519,8 +526,9 @@ export async function resolveParallelSegmentsWithRevalidation<TEnv>(
|
|
|
519
526
|
actionContext,
|
|
520
527
|
stale,
|
|
521
528
|
traceSource: "parallel",
|
|
529
|
+
defaultOverride,
|
|
522
530
|
});
|
|
523
|
-
}
|
|
531
|
+
}
|
|
524
532
|
emitRevalidationDecision(
|
|
525
533
|
parallelId,
|
|
526
534
|
context.pathname,
|
|
@@ -868,7 +876,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
868
876
|
prevUrl,
|
|
869
877
|
nextUrl,
|
|
870
878
|
routeKey,
|
|
871
|
-
loaderPromises,
|
|
872
879
|
true,
|
|
873
880
|
deps,
|
|
874
881
|
actionContext,
|
|
@@ -953,7 +960,6 @@ export async function resolveSegmentWithRevalidation<TEnv>(
|
|
|
953
960
|
prevUrl,
|
|
954
961
|
nextUrl,
|
|
955
962
|
routeKey,
|
|
956
|
-
loaderPromises,
|
|
957
963
|
false,
|
|
958
964
|
deps,
|
|
959
965
|
actionContext,
|
|
@@ -980,7 +986,6 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
980
986
|
prevUrl: URL,
|
|
981
987
|
nextUrl: URL,
|
|
982
988
|
routeKey: string,
|
|
983
|
-
loaderPromises: Map<string, Promise<any>>,
|
|
984
989
|
belongsToRoute: boolean,
|
|
985
990
|
deps: SegmentResolutionDeps<TEnv>,
|
|
986
991
|
actionContext?: ActionContext,
|
|
@@ -1166,21 +1171,20 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1166
1171
|
const parallelId = `${orphan.shortCode}.${slot}`;
|
|
1167
1172
|
matchedIds.push(parallelId);
|
|
1168
1173
|
|
|
1169
|
-
const
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
}
|
|
1174
|
+
const isFullRefetch = clientSegmentIds.size === 0;
|
|
1175
|
+
let shouldResolve: boolean;
|
|
1176
|
+
if (isFullRefetch) {
|
|
1177
|
+
// Same load-bearing rationale as the main parallel path: full refetch
|
|
1178
|
+
// means the client has nothing to fall back to, so the slot must render.
|
|
1179
|
+
traceFullRefetchedParallelSlot(parallelId, belongsToRoute);
|
|
1180
|
+
shouldResolve = true;
|
|
1181
|
+
} else {
|
|
1182
|
+
// When slot is unknown to the client, seed the soft chain with `true`
|
|
1183
|
+
// (orphan parallels always belong to the route — we want them rendered
|
|
1184
|
+
// unless the user explicitly opts out via revalidate()).
|
|
1185
|
+
const defaultOverride = clientSegmentIds.has(parallelId)
|
|
1186
|
+
? undefined
|
|
1187
|
+
: { value: true, reason: "new-segment" };
|
|
1184
1188
|
|
|
1185
1189
|
const dummySegment: ResolvedSegment = {
|
|
1186
1190
|
id: parallelId,
|
|
@@ -1197,7 +1201,7 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1197
1201
|
: {}),
|
|
1198
1202
|
};
|
|
1199
1203
|
|
|
1200
|
-
|
|
1204
|
+
shouldResolve = await evaluateRevalidation({
|
|
1201
1205
|
segment: dummySegment,
|
|
1202
1206
|
prevParams,
|
|
1203
1207
|
getPrevSegment: null,
|
|
@@ -1213,8 +1217,9 @@ export async function resolveOrphanLayoutWithRevalidation<TEnv>(
|
|
|
1213
1217
|
actionContext,
|
|
1214
1218
|
stale,
|
|
1215
1219
|
traceSource: "parallel",
|
|
1220
|
+
defaultOverride,
|
|
1216
1221
|
});
|
|
1217
|
-
}
|
|
1222
|
+
}
|
|
1218
1223
|
emitRevalidationDecision(
|
|
1219
1224
|
parallelId,
|
|
1220
1225
|
context.pathname,
|