@rangojs/router 0.0.0-experimental.19 → 0.0.0-experimental.1fa245e2
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/{CLAUDE.md → AGENTS.md} +4 -0
- package/README.md +122 -30
- package/dist/bin/rango.js +245 -63
- package/dist/vite/index.js +859 -418
- package/package.json +3 -3
- package/skills/breadcrumbs/SKILL.md +250 -0
- package/skills/cache-guide/SKILL.md +32 -0
- package/skills/caching/SKILL.md +49 -8
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +33 -31
- package/skills/host-router/SKILL.md +218 -0
- package/skills/links/SKILL.md +3 -1
- package/skills/loader/SKILL.md +72 -22
- package/skills/middleware/SKILL.md +2 -0
- package/skills/parallel/SKILL.md +126 -0
- package/skills/prerender/SKILL.md +112 -70
- package/skills/rango/SKILL.md +0 -1
- package/skills/route/SKILL.md +34 -4
- package/skills/router-setup/SKILL.md +95 -5
- package/skills/typesafety/SKILL.md +35 -23
- package/src/__internal.ts +92 -0
- package/src/bin/rango.ts +18 -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 +114 -18
- package/src/browser/navigation-client.ts +126 -44
- package/src/browser/navigation-store.ts +43 -8
- package/src/browser/navigation-transaction.ts +11 -9
- package/src/browser/partial-update.ts +80 -15
- package/src/browser/prefetch/cache.ts +166 -27
- package/src/browser/prefetch/fetch.ts +52 -39
- 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 +70 -14
- 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 +143 -59
- package/src/browser/scroll-restoration.ts +41 -42
- package/src/browser/segment-reconciler.ts +6 -1
- package/src/browser/server-action-bridge.ts +454 -436
- package/src/browser/types.ts +60 -5
- package/src/build/generate-manifest.ts +6 -6
- package/src/build/generate-route-types.ts +5 -0
- package/src/build/route-trie.ts +19 -3
- package/src/build/route-types/include-resolution.ts +8 -1
- package/src/build/route-types/router-processing.ts +346 -87
- 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 -1
- package/src/client.tsx +3 -102
- 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/host/index.ts +0 -3
- package/src/index.rsc.ts +8 -37
- package/src/index.ts +40 -66
- package/src/prerender/store.ts +57 -15
- package/src/prerender.ts +138 -77
- package/src/reverse.ts +22 -1
- package/src/route-definition/dsl-helpers.ts +73 -25
- package/src/route-definition/helpers-types.ts +10 -6
- package/src/route-definition/index.ts +3 -3
- 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 +108 -25
- package/src/router/intercept-resolution.ts +11 -4
- package/src/router/lazy-includes.ts +4 -1
- package/src/router/loader-resolution.ts +123 -11
- 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 +88 -16
- 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 +22 -15
- package/src/router/metrics.ts +238 -13
- package/src/router/middleware-types.ts +53 -12
- package/src/router/middleware.ts +172 -85
- package/src/router/navigation-snapshot.ts +182 -0
- package/src/router/pattern-matching.ts +20 -5
- 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/revalidation.ts +27 -7
- package/src/router/route-snapshot.ts +245 -0
- package/src/router/router-context.ts +6 -1
- package/src/router/router-interfaces.ts +50 -5
- package/src/router/router-options.ts +50 -19
- package/src/router/segment-resolution/fresh.ts +200 -19
- 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 +429 -301
- package/src/router/segment-wrappers.ts +2 -0
- package/src/router/trie-matching.ts +20 -2
- package/src/router/types.ts +1 -0
- package/src/router.ts +88 -15
- package/src/rsc/handler.ts +546 -359
- package/src/rsc/index.ts +0 -20
- package/src/rsc/manifest-init.ts +5 -1
- package/src/rsc/progressive-enhancement.ts +25 -8
- package/src/rsc/rsc-rendering.ts +35 -43
- package/src/rsc/server-action.ts +16 -10
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +10 -1
- package/src/search-params.ts +16 -13
- package/src/segment-system.tsx +140 -4
- package/src/server/context.ts +148 -16
- package/src/server/loader-registry.ts +9 -8
- package/src/server/request-context.ts +182 -34
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +4 -0
- package/src/static-handler.ts +18 -6
- package/src/theme/index.ts +4 -13
- 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-config.ts +17 -8
- package/src/types/route-entry.ts +8 -1
- package/src/types/segments.ts +2 -5
- package/src/urls/path-helper-types.ts +9 -2
- 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 +73 -4
- package/src/vite/discovery/bundle-postprocess.ts +61 -89
- package/src/vite/discovery/discover-routers.ts +23 -5
- package/src/vite/discovery/prerender-collection.ts +48 -15
- package/src/vite/discovery/state.ts +17 -13
- package/src/vite/index.ts +8 -3
- package/src/vite/plugin-types.ts +51 -79
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/expose-action-id.ts +1 -3
- package/src/vite/plugins/performance-tracks.ts +88 -0
- package/src/vite/plugins/refresh-cmd.ts +127 -0
- package/src/vite/plugins/version-plugin.ts +13 -1
- package/src/vite/rango.ts +174 -211
- package/src/vite/router-discovery.ts +169 -42
- package/src/vite/utils/banner.ts +3 -3
- package/src/vite/utils/prerender-utils.ts +78 -0
- package/src/vite/utils/shared-utils.ts +3 -2
- package/skills/testing/SKILL.md +0 -226
- package/src/route-definition/route-function.ts +0 -119
package/src/browser/types.ts
CHANGED
|
@@ -32,6 +32,9 @@ export type HandleData = Record<string, Record<string, unknown[]>>;
|
|
|
32
32
|
export interface RscMetadata {
|
|
33
33
|
pathname: string;
|
|
34
34
|
segments: ResolvedSegment[];
|
|
35
|
+
/** Router instance ID. When this changes between navigations, the client
|
|
36
|
+
* forces a full tree replacement (app switch via host router). */
|
|
37
|
+
routerId?: string;
|
|
35
38
|
isPartial?: boolean;
|
|
36
39
|
isError?: boolean;
|
|
37
40
|
matched?: string[];
|
|
@@ -55,6 +58,11 @@ export interface RscMetadata {
|
|
|
55
58
|
* Used to detect version mismatches after HMR/deployment.
|
|
56
59
|
*/
|
|
57
60
|
version?: string;
|
|
61
|
+
/**
|
|
62
|
+
* TTL in milliseconds for the client-side in-memory prefetch cache.
|
|
63
|
+
* Sent on initial render so the browser can configure its cache duration.
|
|
64
|
+
*/
|
|
65
|
+
prefetchCacheTTL?: number;
|
|
58
66
|
/**
|
|
59
67
|
* Theme configuration from router.
|
|
60
68
|
* Included when theme is enabled in router config.
|
|
@@ -65,6 +73,8 @@ export interface RscMetadata {
|
|
|
65
73
|
* Included when theme is enabled in router config.
|
|
66
74
|
*/
|
|
67
75
|
initialTheme?: Theme;
|
|
76
|
+
/** URL prefix for all routes (from createRouter({ basename })). */
|
|
77
|
+
basename?: string;
|
|
68
78
|
/** Whether connection warmup is enabled */
|
|
69
79
|
warmupEnabled?: boolean;
|
|
70
80
|
/** Server-side redirect with optional state (for partial requests) */
|
|
@@ -210,6 +220,15 @@ export interface SegmentState {
|
|
|
210
220
|
export interface NavigationUpdate {
|
|
211
221
|
root: ReactNode | Promise<ReactNode>;
|
|
212
222
|
metadata: RscMetadata;
|
|
223
|
+
/** Scroll behavior to apply after React commits this update */
|
|
224
|
+
scroll?: {
|
|
225
|
+
/** For back/forward: restore saved position */
|
|
226
|
+
restore?: boolean;
|
|
227
|
+
/** Set to false to disable scrolling entirely */
|
|
228
|
+
enabled?: boolean;
|
|
229
|
+
/** Function to check if streaming is in progress */
|
|
230
|
+
isStreaming?: () => boolean;
|
|
231
|
+
};
|
|
213
232
|
}
|
|
214
233
|
|
|
215
234
|
/**
|
|
@@ -227,6 +246,25 @@ export type HistoryState =
|
|
|
227
246
|
export interface NavigateOptions {
|
|
228
247
|
replace?: boolean;
|
|
229
248
|
scroll?: boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Whether to revalidate server data on navigation.
|
|
251
|
+
* Set to `false` to skip the RSC server fetch and only update the URL.
|
|
252
|
+
*
|
|
253
|
+
* Only takes effect when the pathname stays the same (search param / hash changes).
|
|
254
|
+
* If the pathname changes, this option is ignored and a full navigation occurs.
|
|
255
|
+
*
|
|
256
|
+
* All location-aware hooks (`useSearchParams`, `useNavigation`, etc.) still update.
|
|
257
|
+
* Server components do not re-render.
|
|
258
|
+
*
|
|
259
|
+
* @default true
|
|
260
|
+
*
|
|
261
|
+
* @example
|
|
262
|
+
* ```tsx
|
|
263
|
+
* router.push("/products?color=blue", { revalidate: false });
|
|
264
|
+
* router.replace("/products?page=3", { revalidate: false });
|
|
265
|
+
* ```
|
|
266
|
+
*/
|
|
267
|
+
revalidate?: boolean;
|
|
230
268
|
/**
|
|
231
269
|
* State to pass to history.pushState/replaceState
|
|
232
270
|
* Accessible via useLocationState() hook.
|
|
@@ -308,7 +346,13 @@ export type ReadonlyURLSearchParams = Omit<
|
|
|
308
346
|
export interface RscBrowserDependencies {
|
|
309
347
|
createFromFetch: <T>(
|
|
310
348
|
response: Promise<Response>,
|
|
311
|
-
options?: {
|
|
349
|
+
options?: {
|
|
350
|
+
temporaryReferences?: any;
|
|
351
|
+
findSourceMapURL?: (
|
|
352
|
+
filename: string,
|
|
353
|
+
environmentName: string,
|
|
354
|
+
) => string | null;
|
|
355
|
+
},
|
|
312
356
|
) => Promise<T>;
|
|
313
357
|
createFromReadableStream: <T>(stream: ReadableStream) => Promise<T>;
|
|
314
358
|
encodeReply: (
|
|
@@ -370,10 +414,13 @@ export interface NavigationStore {
|
|
|
370
414
|
segments: ResolvedSegment[],
|
|
371
415
|
handleData?: HandleData,
|
|
372
416
|
): void;
|
|
373
|
-
getCachedSegments(
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
417
|
+
getCachedSegments(historyKey: string):
|
|
418
|
+
| {
|
|
419
|
+
segments: ResolvedSegment[];
|
|
420
|
+
stale: boolean;
|
|
421
|
+
handleData?: HandleData;
|
|
422
|
+
routerId?: string;
|
|
423
|
+
}
|
|
377
424
|
| undefined;
|
|
378
425
|
hasHistoryCache(historyKey: string): boolean;
|
|
379
426
|
updateCacheHandleData(historyKey: string, handleData: HandleData): void;
|
|
@@ -389,6 +436,10 @@ export interface NavigationStore {
|
|
|
389
436
|
getInterceptSourceUrl(): string | null;
|
|
390
437
|
setInterceptSourceUrl(url: string | null): void;
|
|
391
438
|
|
|
439
|
+
// Router identity tracking (for cross-app navigation detection)
|
|
440
|
+
getRouterId?(): string | undefined;
|
|
441
|
+
setRouterId?(id: string): void;
|
|
442
|
+
|
|
392
443
|
// UI update notifications
|
|
393
444
|
onUpdate(callback: UpdateSubscriber): () => void;
|
|
394
445
|
emitUpdate(update: NavigationUpdate): void;
|
|
@@ -419,6 +470,8 @@ export interface FetchPartialOptions {
|
|
|
419
470
|
interceptSourceUrl?: string;
|
|
420
471
|
/** RSC version for cache invalidation detection */
|
|
421
472
|
version?: string;
|
|
473
|
+
/** Current router ID — server detects app switch and returns full response */
|
|
474
|
+
routerId?: string;
|
|
422
475
|
/** If true, this is an HMR refetch - server should invalidate manifest cache */
|
|
423
476
|
hmr?: boolean;
|
|
424
477
|
}
|
|
@@ -487,6 +540,8 @@ export interface NavigationBridge {
|
|
|
487
540
|
refresh(): Promise<void>;
|
|
488
541
|
handlePopstate(): Promise<void>;
|
|
489
542
|
registerLinkInterception(): () => void;
|
|
543
|
+
/** Update the RSC version (e.g. after HMR). Clears prefetch cache. */
|
|
544
|
+
updateVersion(newVersion: string): void;
|
|
490
545
|
}
|
|
491
546
|
|
|
492
547
|
/**
|
|
@@ -45,7 +45,7 @@ export interface GeneratedManifest {
|
|
|
45
45
|
routeTrailingSlash?: Record<string, string>;
|
|
46
46
|
/** Route names using Prerender (for dev-mode Node.js delegation) */
|
|
47
47
|
prerenderRoutes?: string[];
|
|
48
|
-
/** Route names with
|
|
48
|
+
/** Route names wrapped with Passthrough() (live handler for runtime fallback) */
|
|
49
49
|
passthroughRoutes?: string[];
|
|
50
50
|
/** Route name → response type for non-RSC routes */
|
|
51
51
|
responseTypeRoutes?: Record<string, string>;
|
|
@@ -150,10 +150,7 @@ function buildPrefixTreeNode(
|
|
|
150
150
|
if (prerenderDefs && entry.prerenderDef) {
|
|
151
151
|
prerenderDefs[name] = entry.prerenderDef;
|
|
152
152
|
}
|
|
153
|
-
if (
|
|
154
|
-
passthroughRoutes &&
|
|
155
|
-
entry.prerenderDef?.options?.passthrough === true
|
|
156
|
-
) {
|
|
153
|
+
if (passthroughRoutes && entry.isPassthrough === true) {
|
|
157
154
|
passthroughRoutes.push(name);
|
|
158
155
|
}
|
|
159
156
|
}
|
|
@@ -285,6 +282,7 @@ export function generateManifest<TEnv>(
|
|
|
285
282
|
export function generateManifestFull<TEnv>(
|
|
286
283
|
urlpatterns: UrlPatterns<TEnv, any>,
|
|
287
284
|
mountIndex: number = 0,
|
|
285
|
+
options?: { urlPrefix?: string },
|
|
288
286
|
): FullManifest {
|
|
289
287
|
const routeManifest: Record<string, string> = {};
|
|
290
288
|
const routeAncestry: Record<string, string[]> = {};
|
|
@@ -310,6 +308,8 @@ export function generateManifestFull<TEnv>(
|
|
|
310
308
|
counters: {},
|
|
311
309
|
mountIndex,
|
|
312
310
|
trackedIncludes, // Enable include tracking
|
|
311
|
+
// basename sets the initial URL prefix for all path() registrations
|
|
312
|
+
...(options?.urlPrefix ? { urlPrefix: options.urlPrefix } : {}),
|
|
313
313
|
},
|
|
314
314
|
() => {
|
|
315
315
|
const helpers = createRouteHelpers();
|
|
@@ -347,7 +347,7 @@ export function generateManifestFull<TEnv>(
|
|
|
347
347
|
if (entry.prerenderDef) {
|
|
348
348
|
prerenderDefs[name] = entry.prerenderDef;
|
|
349
349
|
}
|
|
350
|
-
if (entry.
|
|
350
|
+
if (entry.isPassthrough === true) {
|
|
351
351
|
passthroughRoutes.push(name);
|
|
352
352
|
}
|
|
353
353
|
}
|
|
@@ -25,9 +25,14 @@ export {
|
|
|
25
25
|
} from "./route-types/include-resolution.js";
|
|
26
26
|
export {
|
|
27
27
|
extractUrlsVariableFromRouter,
|
|
28
|
+
extractUrlsFromRouter,
|
|
29
|
+
extractBasenameFromRouter,
|
|
30
|
+
type UrlsExtractionResult,
|
|
28
31
|
buildCombinedRouteMapForRouterFile,
|
|
29
32
|
detectUnresolvableIncludes,
|
|
30
33
|
detectUnresolvableIncludesForUrlsFile,
|
|
34
|
+
findNestedRouterConflict,
|
|
35
|
+
formatNestedRouterConflictError,
|
|
31
36
|
findRouterFiles,
|
|
32
37
|
writeCombinedRouteTypes,
|
|
33
38
|
} from "./route-types/router-processing.js";
|
package/src/build/route-trie.ts
CHANGED
|
@@ -47,6 +47,8 @@ export interface TrieNode {
|
|
|
47
47
|
s?: Record<string, TrieNode>;
|
|
48
48
|
/** Param child: { n: paramName, c: child node } */
|
|
49
49
|
p?: { n: string; c: TrieNode };
|
|
50
|
+
/** Suffix-param children keyed by suffix (e.g., ".html" → { n: "productId", c: ... }) */
|
|
51
|
+
xp?: Record<string, { n: string; c: TrieNode }>;
|
|
50
52
|
/** Wildcard terminal: leaf + paramName */
|
|
51
53
|
w?: TrieLeaf & { pn: string };
|
|
52
54
|
}
|
|
@@ -158,6 +160,11 @@ export function extractAncestryFromTrie(
|
|
|
158
160
|
visit(child);
|
|
159
161
|
}
|
|
160
162
|
}
|
|
163
|
+
if (node.xp) {
|
|
164
|
+
for (const child of Object.values(node.xp)) {
|
|
165
|
+
visit(child.c);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
161
168
|
if (node.p) {
|
|
162
169
|
visit(node.p.c);
|
|
163
170
|
}
|
|
@@ -235,10 +242,19 @@ function insertSegments(
|
|
|
235
242
|
mergeLeaf(node, leaf);
|
|
236
243
|
// AND continue with param child (param present)
|
|
237
244
|
}
|
|
238
|
-
if (
|
|
239
|
-
|
|
245
|
+
if (segment.suffix) {
|
|
246
|
+
// Suffix param: keyed by suffix string (e.g., ".html")
|
|
247
|
+
if (!node.xp) node.xp = {};
|
|
248
|
+
if (!node.xp[segment.suffix]) {
|
|
249
|
+
node.xp[segment.suffix] = { n: segment.value, c: {} };
|
|
250
|
+
}
|
|
251
|
+
insertSegments(node.xp[segment.suffix].c, segments, index + 1, leaf);
|
|
252
|
+
} else {
|
|
253
|
+
if (!node.p) {
|
|
254
|
+
node.p = { n: segment.value, c: {} };
|
|
255
|
+
}
|
|
256
|
+
insertSegments(node.p.c, segments, index + 1, leaf);
|
|
240
257
|
}
|
|
241
|
-
insertSegments(node.p.c, segments, index + 1, leaf);
|
|
242
258
|
} else if (segment.type === "wildcard") {
|
|
243
259
|
// Wildcard consumes all remaining segments
|
|
244
260
|
const wildLeaf = { ...leaf, pn: "*" };
|
|
@@ -357,12 +357,17 @@ function buildRouteMapFromBlock(
|
|
|
357
357
|
/**
|
|
358
358
|
* Build route map and search schemas together.
|
|
359
359
|
* Internal helper used by the include resolution path.
|
|
360
|
+
*
|
|
361
|
+
* @param inlineBlock - Optional pre-extracted code block (e.g. from an inline
|
|
362
|
+
* builder function). When provided, variableName is ignored and the block
|
|
363
|
+
* is parsed directly for path()/include() calls.
|
|
360
364
|
*/
|
|
361
365
|
export function buildCombinedRouteMapWithSearch(
|
|
362
366
|
filePath: string,
|
|
363
367
|
variableName?: string,
|
|
364
368
|
visited?: Set<string>,
|
|
365
369
|
diagnosticsOut?: UnresolvableInclude[],
|
|
370
|
+
inlineBlock?: string,
|
|
366
371
|
): {
|
|
367
372
|
routes: Record<string, string>;
|
|
368
373
|
searchSchemas: Record<string, Record<string, string>>;
|
|
@@ -384,7 +389,9 @@ export function buildCombinedRouteMapWithSearch(
|
|
|
384
389
|
}
|
|
385
390
|
|
|
386
391
|
let block: string;
|
|
387
|
-
if (
|
|
392
|
+
if (inlineBlock) {
|
|
393
|
+
block = inlineBlock;
|
|
394
|
+
} else if (variableName) {
|
|
388
395
|
const extracted = extractUrlsBlockForVariable(source, variableName);
|
|
389
396
|
if (!extracted) return { routes: {}, searchSchemas: {} };
|
|
390
397
|
block = extracted;
|