@rangojs/router 0.1.0 → 0.2.0
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 +2 -1
- package/dist/bin/rango.js +11 -1
- package/dist/types/browser/dev-discovery.d.ts +11 -0
- package/dist/types/browser/navigation-store.d.ts +3 -13
- package/dist/types/browser/prefetch/loader.d.ts +10 -0
- package/dist/types/browser/prefetch/runtime.d.ts +3 -0
- package/dist/types/browser/types.d.ts +4 -16
- package/dist/types/build/generate-manifest.d.ts +6 -0
- package/dist/types/cache/cache-scope.d.ts +7 -5
- package/dist/types/cache/cf/cf-cache-store.d.ts +10 -9
- package/dist/types/cache/document-cache.d.ts +3 -1
- package/dist/types/cache/memory-segment-store.d.ts +6 -6
- package/dist/types/cache/shell-snapshot.d.ts +21 -8
- package/dist/types/cache/types.d.ts +25 -7
- package/dist/types/cache/vercel/vercel-cache-store.d.ts +9 -8
- package/dist/types/dev-discovery-protocol.d.ts +5 -0
- package/dist/types/prerender/store.d.ts +1 -0
- package/dist/types/route-definition/helpers-types.d.ts +6 -6
- package/dist/types/route-map-builder.d.ts +4 -29
- package/dist/types/router/match-handlers.d.ts +2 -3
- package/dist/types/router/prerender-match.d.ts +4 -1
- package/dist/types/router/router-interfaces.d.ts +3 -1
- package/dist/types/router/segment-resolution/view-transition-default.d.ts +3 -4
- package/dist/types/router/transition-when.d.ts +13 -0
- package/dist/types/rsc/handler-context.d.ts +1 -0
- package/dist/types/rsc/render-pipeline.d.ts +3 -2
- package/dist/types/rsc/rsc-rendering.d.ts +5 -10
- package/dist/types/rsc/shell-capture.d.ts +4 -8
- package/dist/types/rsc/shell-serve.d.ts +2 -0
- package/dist/types/rsc/transition-gate.d.ts +10 -14
- package/dist/types/rsc/types.d.ts +2 -0
- package/dist/types/server/context.d.ts +16 -0
- package/dist/types/server/request-context.d.ts +22 -8
- package/dist/types/server.d.ts +1 -1
- package/dist/types/testing/e2e/index.d.ts +1 -1
- package/dist/types/testing/index.d.ts +2 -2
- package/dist/types/testing/run-transition-when.d.ts +6 -5
- package/dist/types/testing/shell-status.d.ts +23 -3
- package/dist/types/types/route-entry.d.ts +3 -0
- package/dist/types/types/segments.d.ts +27 -22
- package/dist/types/urls/path-helper-types.d.ts +4 -3
- package/dist/types/vite/discovery/state.d.ts +3 -2
- package/dist/types/vite/utils/manifest-utils.d.ts +1 -1
- package/dist/vite/index.js +194 -107
- package/package.json +24 -20
- package/skills/cache-guide/SKILL.md +6 -3
- package/skills/caching/SKILL.md +1 -1
- package/skills/catalog.json +7 -1
- package/skills/deployment-caching/SKILL.md +176 -0
- package/skills/document-cache/SKILL.md +30 -3
- package/skills/ppr/SKILL.md +90 -14
- package/skills/prerender/SKILL.md +15 -8
- package/skills/rango/SKILL.md +20 -17
- package/skills/testing/SKILL.md +1 -1
- package/skills/testing/cache-prerender.md +5 -1
- package/skills/vercel/SKILL.md +22 -1
- package/skills/view-transitions/SKILL.md +12 -8
- package/src/browser/dev-discovery.ts +66 -0
- package/src/browser/navigation-client.ts +6 -0
- package/src/browser/navigation-store.ts +4 -271
- package/src/browser/partial-update.ts +7 -14
- package/src/browser/prefetch/cache.ts +1 -1
- package/src/browser/prefetch/loader.ts +110 -0
- package/src/browser/prefetch/runtime.ts +7 -0
- package/src/browser/react/Link.tsx +7 -10
- package/src/browser/react/NavigationProvider.tsx +1 -1
- package/src/browser/react/use-router.ts +1 -1
- package/src/browser/rsc-router.tsx +4 -2
- package/src/browser/types.ts +4 -27
- package/src/build/generate-manifest.ts +11 -0
- package/src/cache/cache-scope.ts +23 -7
- package/src/cache/cf/cf-cache-store.ts +63 -39
- package/src/cache/document-cache.ts +4 -2
- package/src/cache/memory-segment-store.ts +17 -6
- package/src/cache/shell-snapshot.ts +45 -15
- package/src/cache/types.ts +24 -6
- package/src/cache/vercel/vercel-cache-store.ts +52 -22
- package/src/dev-discovery-protocol.ts +11 -0
- package/src/prerender/build-shell-capture.ts +7 -1
- package/src/prerender/store.ts +5 -0
- package/src/route-definition/dsl-helpers.ts +7 -2
- package/src/route-definition/helpers-types.ts +6 -6
- package/src/route-map-builder.ts +56 -49
- package/src/router/handler-context.ts +13 -5
- package/src/router/lazy-includes.ts +1 -0
- package/src/router/match-api.ts +8 -4
- package/src/router/match-handlers.ts +44 -6
- package/src/router/match-middleware/cache-lookup.ts +10 -3
- package/src/router/prerender-match.ts +19 -2
- package/src/router/router-interfaces.ts +4 -0
- package/src/router/segment-resolution/static-store.ts +36 -10
- package/src/router/segment-resolution/view-transition-default.ts +9 -5
- package/src/router/transition-when.ts +76 -0
- package/src/router.ts +34 -3
- package/src/rsc/handler-context.ts +1 -0
- package/src/rsc/handler.ts +3 -1
- package/src/rsc/loader-fetch.ts +2 -2
- package/src/rsc/manifest-init.ts +4 -11
- package/src/rsc/progressive-enhancement.ts +9 -14
- package/src/rsc/render-pipeline.ts +10 -2
- package/src/rsc/rsc-rendering.ts +319 -147
- package/src/rsc/shell-capture.ts +34 -11
- package/src/rsc/shell-serve.ts +3 -0
- package/src/rsc/transition-gate.ts +37 -40
- package/src/rsc/types.ts +2 -0
- package/src/server/context.ts +28 -0
- package/src/server/request-context.ts +38 -10
- package/src/server.ts +0 -2
- package/src/testing/dispatch.ts +1 -0
- package/src/testing/e2e/index.ts +5 -0
- package/src/testing/index.ts +9 -1
- package/src/testing/run-transition-when.ts +42 -9
- package/src/testing/shell-status.ts +92 -3
- package/src/types/route-entry.ts +3 -0
- package/src/types/segments.ts +27 -22
- package/src/urls/include-helper.ts +1 -0
- package/src/urls/path-helper-types.ts +4 -3
- package/src/urls/path-helper.ts +8 -4
- package/src/vite/discovery/bundle-postprocess.ts +10 -7
- package/src/vite/discovery/discover-routers.ts +7 -66
- package/src/vite/discovery/prerender-collection.ts +13 -2
- package/src/vite/discovery/state.ts +4 -4
- package/src/vite/discovery/virtual-module-codegen.ts +75 -42
- package/src/vite/plugins/version-injector.ts +0 -1
- package/src/vite/plugins/virtual-entries.ts +11 -1
- package/src/vite/router-discovery.ts +132 -22
- package/src/vite/utils/manifest-utils.ts +1 -4
- package/src/vite/utils/shared-utils.ts +7 -0
package/src/router.ts
CHANGED
|
@@ -5,10 +5,14 @@ import { isCachedFunction } from "./cache/taint.js";
|
|
|
5
5
|
import { assertClientComponent } from "./component-utils.js";
|
|
6
6
|
import { DefaultDocument } from "./components/DefaultDocument.js";
|
|
7
7
|
import type { SerializedManifest } from "./debug.js";
|
|
8
|
+
import {
|
|
9
|
+
DEV_DISCOVERY_EPOCH_HEADER,
|
|
10
|
+
DEV_DISCOVERY_PROBE_HEADER,
|
|
11
|
+
isValidDevDiscoveryEpoch,
|
|
12
|
+
} from "./dev-discovery-protocol.js";
|
|
8
13
|
import { createReverse, type ReverseFunction } from "./reverse.js";
|
|
9
14
|
import {
|
|
10
15
|
registerRouteMap,
|
|
11
|
-
getPrecomputedEntries,
|
|
12
16
|
getRouterManifest,
|
|
13
17
|
getRouterPrecomputedEntries,
|
|
14
18
|
ensureRouterManifest,
|
|
@@ -229,6 +233,14 @@ export function createRouter<TEnv = any>(
|
|
|
229
233
|
// order (unlike the counter which depends on import order).
|
|
230
234
|
const routerId =
|
|
231
235
|
userProvidedId ?? injectedId ?? `router_${nextRouterAutoId()}`;
|
|
236
|
+
const rawDevDiscoveryEpoch = (
|
|
237
|
+
globalThis as typeof globalThis & {
|
|
238
|
+
__RANGO_DEV_DISCOVERY_EPOCH?: unknown;
|
|
239
|
+
}
|
|
240
|
+
).__RANGO_DEV_DISCOVERY_EPOCH;
|
|
241
|
+
const devDiscoveryEpoch = isValidDevDiscoveryEpoch(rawDevDiscoveryEpoch)
|
|
242
|
+
? rawDevDiscoveryEpoch
|
|
243
|
+
: undefined;
|
|
232
244
|
|
|
233
245
|
// Resolve the rango state cookie name once, here, so the two cookie writers
|
|
234
246
|
// (the client document.cookie writer and the server Set-Cookie writer)
|
|
@@ -405,8 +417,7 @@ export function createRouter<TEnv = any>(
|
|
|
405
417
|
string,
|
|
406
418
|
Record<string, string>
|
|
407
419
|
> | null {
|
|
408
|
-
const current =
|
|
409
|
-
getRouterPrecomputedEntries(routerId) ?? getPrecomputedEntries();
|
|
420
|
+
const current = getRouterPrecomputedEntries(routerId);
|
|
410
421
|
if (current !== precomputedSource) {
|
|
411
422
|
precomputedSource = current;
|
|
412
423
|
// buildPrecomputedByPrefix drops any staticPrefix owned by more than one
|
|
@@ -669,6 +680,7 @@ export function createRouter<TEnv = any>(
|
|
|
669
680
|
|
|
670
681
|
// Prerender/static match deps (bind closure state for extracted functions)
|
|
671
682
|
const prerenderDeps = {
|
|
683
|
+
routerId,
|
|
672
684
|
findMatch,
|
|
673
685
|
buildRouterContext,
|
|
674
686
|
mergedRouteMap,
|
|
@@ -700,6 +712,7 @@ export function createRouter<TEnv = any>(
|
|
|
700
712
|
routeName?: string,
|
|
701
713
|
buildEnv?: TEnv,
|
|
702
714
|
devMode?: boolean,
|
|
715
|
+
rootScoped?: boolean,
|
|
703
716
|
) {
|
|
704
717
|
return _renderStaticSegment<TEnv>(
|
|
705
718
|
handler,
|
|
@@ -708,6 +721,8 @@ export function createRouter<TEnv = any>(
|
|
|
708
721
|
routeName,
|
|
709
722
|
buildEnv,
|
|
710
723
|
devMode,
|
|
724
|
+
routerId,
|
|
725
|
+
rootScoped,
|
|
711
726
|
);
|
|
712
727
|
}
|
|
713
728
|
|
|
@@ -788,6 +803,7 @@ export function createRouter<TEnv = any>(
|
|
|
788
803
|
parent: syntheticMapRoot,
|
|
789
804
|
counters: {},
|
|
790
805
|
mountIndex: currentMountIndex,
|
|
806
|
+
routerId,
|
|
791
807
|
cacheProfiles: resolvedCacheProfiles,
|
|
792
808
|
// basename sets the initial URL prefix so all path() patterns
|
|
793
809
|
// are registered with the prefix (e.g. "/admin" + "/users" = "/admin/users").
|
|
@@ -1078,6 +1094,9 @@ export function createRouter<TEnv = any>(
|
|
|
1078
1094
|
// Expose basename for runtime manifest generation
|
|
1079
1095
|
__basename: basename,
|
|
1080
1096
|
|
|
1097
|
+
// Pin payload metadata to the worker generation that created this router.
|
|
1098
|
+
__devDiscoveryEpoch: devDiscoveryEpoch,
|
|
1099
|
+
|
|
1081
1100
|
// Expose router-level boundary defaults for build-time clientChunks
|
|
1082
1101
|
// discovery (so a "use client" default boundary lands in app-fallback).
|
|
1083
1102
|
// These are createRouter options, never pushed onto EntryData.
|
|
@@ -1096,6 +1115,18 @@ export function createRouter<TEnv = any>(
|
|
|
1096
1115
|
| null = null;
|
|
1097
1116
|
|
|
1098
1117
|
return async (request: Request, input: RouterRequestInput<TEnv> = {}) => {
|
|
1118
|
+
if (
|
|
1119
|
+
devDiscoveryEpoch !== undefined &&
|
|
1120
|
+
request.headers.get(DEV_DISCOVERY_PROBE_HEADER) ===
|
|
1121
|
+
String(devDiscoveryEpoch)
|
|
1122
|
+
) {
|
|
1123
|
+
return new Response(null, {
|
|
1124
|
+
headers: {
|
|
1125
|
+
[DEV_DISCOVERY_EPOCH_HEADER]: String(devDiscoveryEpoch),
|
|
1126
|
+
},
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
|
|
1099
1130
|
// Trigger lazy import of per-router manifest data before route matching.
|
|
1100
1131
|
// No-op if data is already loaded or no loader is registered.
|
|
1101
1132
|
await ensureRouterManifest(routerId);
|
|
@@ -15,6 +15,7 @@ import type { SSRStreamMode } from "../router/router-options.js";
|
|
|
15
15
|
export interface HandlerContext<TEnv = unknown> {
|
|
16
16
|
router: RangoInternal<TEnv, any>;
|
|
17
17
|
version: string;
|
|
18
|
+
devDiscoveryEpoch?: number;
|
|
18
19
|
renderToReadableStream: RSCDependencies["renderToReadableStream"];
|
|
19
20
|
decodeReply: RSCDependencies["decodeReply"];
|
|
20
21
|
createTemporaryReferenceSet: RSCDependencies["createTemporaryReferenceSet"];
|
package/src/rsc/handler.ts
CHANGED
|
@@ -389,6 +389,7 @@ export function createRSCHandler<
|
|
|
389
389
|
const handlerCtx: HandlerContext<TEnv> = {
|
|
390
390
|
router,
|
|
391
391
|
version,
|
|
392
|
+
devDiscoveryEpoch: router.__devDiscoveryEpoch,
|
|
392
393
|
renderToReadableStream,
|
|
393
394
|
decodeReply,
|
|
394
395
|
createTemporaryReferenceSet,
|
|
@@ -583,6 +584,7 @@ export function createRSCHandler<
|
|
|
583
584
|
// - Streaming
|
|
584
585
|
// Store basename on request context (scoped per-request via existing ALS)
|
|
585
586
|
requestContext._basename = router.basename;
|
|
587
|
+
requestContext._routerId = router.id;
|
|
586
588
|
|
|
587
589
|
// Resolved span tracing for this request (read at each traced phase).
|
|
588
590
|
requestContext._tracing = router.tracing;
|
|
@@ -1236,7 +1238,7 @@ export function createRSCHandler<
|
|
|
1236
1238
|
};
|
|
1237
1239
|
return renderRscResponse(
|
|
1238
1240
|
{
|
|
1239
|
-
ctx:
|
|
1241
|
+
ctx: handlerCtx,
|
|
1240
1242
|
request,
|
|
1241
1243
|
url,
|
|
1242
1244
|
env,
|
package/src/rsc/loader-fetch.ts
CHANGED
|
@@ -209,7 +209,7 @@ export async function handleLoaderFetch<TEnv>(
|
|
|
209
209
|
const cleanUrl = stripInternalParams(url);
|
|
210
210
|
const cleanSearchParams = cleanUrl.searchParams;
|
|
211
211
|
const searchSchema = reqCtx._routeName
|
|
212
|
-
? getSearchSchema(reqCtx._routeName)
|
|
212
|
+
? getSearchSchema(reqCtx._routeName, reqCtx._routerId)
|
|
213
213
|
: undefined;
|
|
214
214
|
const loaderCtx: any = {
|
|
215
215
|
...reqCtx,
|
|
@@ -228,7 +228,7 @@ export async function handleLoaderFetch<TEnv>(
|
|
|
228
228
|
reqCtx._routeName,
|
|
229
229
|
mergedParams,
|
|
230
230
|
reqCtx._routeName
|
|
231
|
-
? isRouteRootScoped(reqCtx._routeName)
|
|
231
|
+
? isRouteRootScoped(reqCtx._routeName, reqCtx._routerId)
|
|
232
232
|
: undefined,
|
|
233
233
|
),
|
|
234
234
|
...(loaderFormData ? { formData: loaderFormData } : {}),
|
package/src/rsc/manifest-init.ts
CHANGED
|
@@ -9,8 +9,6 @@ import {
|
|
|
9
9
|
getGlobalRouteMap,
|
|
10
10
|
hasCachedManifest,
|
|
11
11
|
setCachedManifest,
|
|
12
|
-
getRouteTrie,
|
|
13
|
-
setRouteTrie,
|
|
14
12
|
setRouterManifest,
|
|
15
13
|
setRouterTrie,
|
|
16
14
|
setRouterPrecomputedEntries,
|
|
@@ -32,11 +30,10 @@ export async function buildRouterTrieFromUrlpatterns(
|
|
|
32
30
|
): Promise<void> {
|
|
33
31
|
const { generateManifestFull } =
|
|
34
32
|
await import("../build/generate-manifest.js");
|
|
35
|
-
const generated = await generateManifestFull(
|
|
36
|
-
router.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
);
|
|
33
|
+
const generated = await generateManifestFull(router.urlpatterns, undefined, {
|
|
34
|
+
routerId: router.id,
|
|
35
|
+
...(router.basename ? { urlPrefix: router.basename } : {}),
|
|
36
|
+
});
|
|
40
37
|
// Build the trie through the SAME shared helper the production discovery uses
|
|
41
38
|
// (discover-routers.ts), so the dev runtime-rebuilt trie and the prod
|
|
42
39
|
// serialized trie cannot drift. buildPerRouterTrie returns null when there
|
|
@@ -45,10 +42,6 @@ export async function buildRouterTrieFromUrlpatterns(
|
|
|
45
42
|
const trie = buildPerRouterTrie(generated);
|
|
46
43
|
if (trie) {
|
|
47
44
|
setRouterTrie(router.id, trie);
|
|
48
|
-
// Set global trie only if not already set by another router
|
|
49
|
-
if (!getRouteTrie()) {
|
|
50
|
-
setRouteTrie(trie);
|
|
51
|
-
}
|
|
52
45
|
}
|
|
53
46
|
setRouterManifest(router.id, generated.routeManifest);
|
|
54
47
|
|
|
@@ -277,10 +277,15 @@ export async function handleProgressiveEnhancement<TEnv>(
|
|
|
277
277
|
// JS/PE parity: this is an action's revalidation render, so mark it BEFORE
|
|
278
278
|
// matching — a stale `foregroundOnAction` cache entry must re-execute in the
|
|
279
279
|
// foreground during the re-render, exactly as the JS path's
|
|
280
|
-
// revalidateAfterAction does.
|
|
281
|
-
//
|
|
282
|
-
//
|
|
283
|
-
getRequestContext()
|
|
280
|
+
// revalidateAfterAction does. PPR transition({ when }) runs during match,
|
|
281
|
+
// while foregroundOnAction reads _inActionRevalidation there too, so all
|
|
282
|
+
// action metadata must be available before matching.
|
|
283
|
+
const peReqCtx = getRequestContext();
|
|
284
|
+
peReqCtx._inActionRevalidation = true;
|
|
285
|
+
peReqCtx._gateActionId = directActionId ?? undefined;
|
|
286
|
+
peReqCtx._gateActionUrl = new URL(url);
|
|
287
|
+
peReqCtx._gateActionResult = actionResult;
|
|
288
|
+
peReqCtx._gateFormData = formData;
|
|
284
289
|
|
|
285
290
|
const match = await ctx.router.match(renderRequest, { env });
|
|
286
291
|
|
|
@@ -291,16 +296,6 @@ export async function handleProgressiveEnhancement<TEnv>(
|
|
|
291
296
|
});
|
|
292
297
|
}
|
|
293
298
|
|
|
294
|
-
// Expose the no-JS action to the transition({ when }) gate. currentUrl/Params
|
|
295
|
-
// are absent on this full-render path (no navigation snapshot); useActionState
|
|
296
|
-
// ids are block-scoped, so only a direct action id is available here.
|
|
297
|
-
// actionUrl is the page the action was submitted from (this request's url).
|
|
298
|
-
const peReqCtx = getRequestContext();
|
|
299
|
-
peReqCtx._gateActionId = directActionId ?? undefined;
|
|
300
|
-
peReqCtx._gateActionUrl = new URL(url);
|
|
301
|
-
peReqCtx._gateActionResult = actionResult;
|
|
302
|
-
peReqCtx._gateFormData = formData;
|
|
303
|
-
|
|
304
299
|
const payload: RscPayload = {
|
|
305
300
|
metadata: {
|
|
306
301
|
pathname: url.pathname,
|
|
@@ -53,8 +53,13 @@ export interface RscRenderStageTracking {
|
|
|
53
53
|
span?: TraceSpan;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
type RscRenderContext<TEnv> = Pick<
|
|
57
|
+
HandlerContext<TEnv>,
|
|
58
|
+
"renderToReadableStream" | "callOnError" | "devDiscoveryEpoch"
|
|
59
|
+
>;
|
|
60
|
+
|
|
56
61
|
export interface RscRenderInput<TEnv> {
|
|
57
|
-
ctx:
|
|
62
|
+
ctx: RscRenderContext<TEnv>;
|
|
58
63
|
request: Request;
|
|
59
64
|
url: URL;
|
|
60
65
|
env: TEnv;
|
|
@@ -66,7 +71,7 @@ export interface RscRenderInput<TEnv> {
|
|
|
66
71
|
}
|
|
67
72
|
|
|
68
73
|
export interface RscFlightStageInput<TEnv> {
|
|
69
|
-
ctx:
|
|
74
|
+
ctx: RscRenderContext<TEnv>;
|
|
70
75
|
request: Request;
|
|
71
76
|
url: URL;
|
|
72
77
|
env: TEnv;
|
|
@@ -146,6 +151,9 @@ function createFlightStage<TEnv>(
|
|
|
146
151
|
input: RscFlightStageInput<TEnv>,
|
|
147
152
|
init: ResponseInit,
|
|
148
153
|
): RscFlightStage {
|
|
154
|
+
if (input.ctx.devDiscoveryEpoch !== undefined && input.payload.metadata) {
|
|
155
|
+
input.payload.metadata.devDiscoveryEpoch = input.ctx.devDiscoveryEpoch;
|
|
156
|
+
}
|
|
149
157
|
const stream = input.ctx.renderToReadableStream<RscPayload>(input.payload, {
|
|
150
158
|
temporaryReferences: input.temporaryReferences,
|
|
151
159
|
onError: (error: unknown) => {
|