@rangojs/router 0.0.0-experimental.18 → 0.0.0-experimental.19
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 +46 -8
- package/dist/bin/rango.js +105 -18
- package/dist/vite/index.js +227 -93
- package/package.json +15 -14
- package/skills/hooks/SKILL.md +1 -1
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +94 -1
- package/skills/middleware/SKILL.md +81 -0
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +187 -17
- package/skills/route/SKILL.md +42 -1
- package/skills/router-setup/SKILL.md +77 -0
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +38 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +25 -27
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +0 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +46 -13
- package/src/browser/navigation-client.ts +32 -61
- package/src/browser/navigation-store.ts +1 -31
- package/src/browser/navigation-transaction.ts +46 -207
- package/src/browser/partial-update.ts +102 -150
- package/src/browser/{prefetch-cache.ts → prefetch/cache.ts} +23 -4
- package/src/browser/{prefetch-fetch.ts → prefetch/fetch.ts} +36 -8
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/{prefetch-queue.ts → prefetch/queue.ts} +10 -3
- package/src/browser/react/Link.tsx +28 -23
- package/src/browser/react/NavigationProvider.tsx +9 -1
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +1 -1
- package/src/browser/react/location-state.ts +2 -0
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/use-action.ts +9 -1
- package/src/browser/react/use-handle.ts +3 -25
- package/src/browser/react/use-params.ts +2 -4
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +1 -1
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +7 -60
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +29 -23
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +115 -96
- package/src/browser/types.ts +1 -31
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +5 -0
- package/src/build/generate-route-types.ts +2 -0
- package/src/build/route-types/codegen.ts +13 -4
- package/src/build/route-types/include-resolution.ts +13 -0
- package/src/build/route-types/per-module-writer.ts +15 -3
- package/src/build/route-types/router-processing.ts +45 -3
- package/src/build/runtime-discovery.ts +13 -1
- package/src/cache/background-task.ts +34 -0
- package/src/cache/cache-key-utils.ts +44 -0
- package/src/cache/cache-policy.ts +125 -0
- package/src/cache/cache-runtime.ts +132 -96
- package/src/cache/cache-scope.ts +71 -73
- package/src/cache/cf/cf-cache-store.ts +9 -4
- package/src/cache/document-cache.ts +72 -47
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/memory-segment-store.ts +18 -7
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +101 -112
- package/src/cache/taint.ts +26 -0
- package/src/client.tsx +53 -30
- package/src/errors.ts +6 -1
- package/src/handle.ts +1 -1
- package/src/handles/MetaTags.tsx +5 -2
- package/src/host/cookie-handler.ts +8 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +33 -1
- package/src/index.ts +27 -0
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +4 -3
- package/src/prerender.ts +76 -18
- package/src/reverse.ts +11 -7
- package/src/root-error-boundary.tsx +30 -26
- package/src/route-definition/dsl-helpers.ts +9 -6
- package/src/route-definition/redirect.ts +15 -3
- package/src/route-map-builder.ts +38 -2
- package/src/route-name.ts +53 -0
- package/src/route-types.ts +7 -0
- package/src/router/content-negotiation.ts +1 -1
- package/src/router/debug-manifest.ts +16 -3
- package/src/router/handler-context.ts +94 -15
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +1 -0
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +61 -7
- package/src/router/match-context.ts +3 -0
- package/src/router/match-handlers.ts +185 -11
- package/src/router/match-middleware/background-revalidation.ts +65 -85
- package/src/router/match-middleware/cache-lookup.ts +69 -4
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/middleware-types.ts +7 -0
- package/src/router/middleware.ts +93 -8
- package/src/router/pattern-matching.ts +41 -5
- package/src/router/prerender-match.ts +34 -6
- package/src/router/preview-match.ts +7 -1
- package/src/router/revalidation.ts +61 -2
- package/src/router/router-context.ts +15 -0
- package/src/router/router-interfaces.ts +34 -0
- package/src/router/router-options.ts +200 -0
- package/src/router/segment-resolution/fresh.ts +123 -30
- package/src/router/segment-resolution/helpers.ts +19 -0
- package/src/router/segment-resolution/loader-cache.ts +37 -146
- package/src/router/segment-resolution/revalidation.ts +358 -94
- package/src/router/segment-wrappers.ts +3 -0
- package/src/router/telemetry-otel.ts +299 -0
- package/src/router/telemetry.ts +300 -0
- package/src/router/timeout.ts +148 -0
- package/src/router/types.ts +7 -1
- package/src/router.ts +155 -11
- package/src/rsc/handler-context.ts +11 -0
- package/src/rsc/handler.ts +380 -88
- package/src/rsc/helpers.ts +25 -16
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +232 -19
- package/src/rsc/response-route-handler.ts +37 -26
- package/src/rsc/rsc-rendering.ts +12 -5
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +134 -58
- package/src/rsc/types.ts +8 -0
- package/src/search-params.ts +22 -10
- package/src/server/context.ts +53 -5
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +66 -9
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +90 -9
- package/src/ssr/index.tsx +63 -27
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +1 -6
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +5 -0
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +9 -0
- package/src/types/handler-context.ts +35 -13
- package/src/types/loader-types.ts +7 -0
- package/src/types/route-entry.ts +28 -0
- package/src/urls/include-helper.ts +49 -8
- package/src/urls/index.ts +1 -0
- package/src/urls/path-helper-types.ts +30 -12
- package/src/urls/path-helper.ts +17 -2
- package/src/urls/pattern-types.ts +21 -1
- package/src/urls/response-types.ts +27 -2
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +12 -4
- package/src/vite/discovery/bundle-postprocess.ts +12 -7
- package/src/vite/discovery/discover-routers.ts +30 -18
- package/src/vite/discovery/prerender-collection.ts +24 -27
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/rango.ts +3 -3
- package/src/vite/router-discovery.ts +99 -36
- package/src/vite/utils/prerender-utils.ts +21 -0
- package/src/vite/utils/shared-utils.ts +3 -1
- package/src/browser/request-controller.ts +0 -164
- package/src/href-context.ts +0 -33
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/src/browser/{prefetch-observer.ts → prefetch/observer.ts} +0 -0
|
@@ -20,12 +20,17 @@
|
|
|
20
20
|
|
|
21
21
|
import type { LoaderEntry } from "../../server/context.js";
|
|
22
22
|
import type { HandlerContext } from "../../types.js";
|
|
23
|
-
import type { SegmentCacheStore } from "../../cache/types.js";
|
|
24
23
|
import { INTERNAL_RANGO_DEBUG } from "../../internal-debug.js";
|
|
24
|
+
import { getRequestContext } from "../../server/request-context.js";
|
|
25
|
+
import { sortedRouteParams } from "../../cache/cache-key-utils.js";
|
|
25
26
|
import {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
27
|
+
resolveTtl,
|
|
28
|
+
resolveSwrWindow,
|
|
29
|
+
resolveCacheKey,
|
|
30
|
+
resolveCacheStore,
|
|
31
|
+
DEFAULT_ROUTE_TTL,
|
|
32
|
+
} from "../../cache/cache-policy.js";
|
|
33
|
+
import { readThroughItem } from "../../cache/read-through-swr.js";
|
|
29
34
|
// Lazy-loaded to avoid pulling @vitejs/plugin-rsc/rsc into modules that
|
|
30
35
|
// import segment-resolution but never use loader caching.
|
|
31
36
|
let _serializeResult: typeof import("../../cache/segment-codec.js").serializeResult;
|
|
@@ -42,8 +47,6 @@ async function getCodec() {
|
|
|
42
47
|
};
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
const DEFAULT_TTL_SECONDS = 60;
|
|
46
|
-
|
|
47
50
|
function debugLoaderCacheLog(message: string): void {
|
|
48
51
|
if (INTERNAL_RANGO_DEBUG) {
|
|
49
52
|
console.log(message);
|
|
@@ -55,69 +58,32 @@ function getDefaultLoaderCacheKey(
|
|
|
55
58
|
pathname: string,
|
|
56
59
|
params: Record<string, string>,
|
|
57
60
|
): string {
|
|
58
|
-
const paramStr =
|
|
59
|
-
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0))
|
|
60
|
-
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
61
|
-
.join("&");
|
|
62
|
-
|
|
61
|
+
const paramStr = sortedRouteParams(params);
|
|
63
62
|
const base = paramStr ? `${pathname}:${paramStr}` : pathname;
|
|
64
63
|
return `loader:${loaderId}:${base}`;
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
/**
|
|
68
|
-
* Resolve cache key using the
|
|
69
|
-
* 1. options.key (full override)
|
|
70
|
-
* 2. store.keyGenerator (modifies default)
|
|
71
|
-
* 3. Default loader key
|
|
67
|
+
* Resolve cache key using the shared 3-tier priority.
|
|
72
68
|
*/
|
|
73
69
|
async function resolveLoaderKey(
|
|
74
70
|
loaderEntry: LoaderEntry,
|
|
75
|
-
store: SegmentCacheStore,
|
|
71
|
+
store: import("../../cache/types.js").SegmentCacheStore,
|
|
76
72
|
loaderId: string,
|
|
77
73
|
pathname: string,
|
|
78
74
|
params: Record<string, string>,
|
|
79
75
|
): Promise<string> {
|
|
80
76
|
const options = loaderEntry.cache!.options;
|
|
81
|
-
if (options === false) {
|
|
82
|
-
return getDefaultLoaderCacheKey(loaderId, pathname, params);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const requestCtx = getRequestContext();
|
|
86
|
-
|
|
87
|
-
// Priority 1: Route-level key function (full override)
|
|
88
|
-
if (options.key && requestCtx) {
|
|
89
|
-
try {
|
|
90
|
-
return await options.key(requestCtx);
|
|
91
|
-
} catch (error) {
|
|
92
|
-
console.error(
|
|
93
|
-
`[LoaderCache] Custom key function failed, using default:`,
|
|
94
|
-
error,
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
77
|
const defaultKey = getDefaultLoaderCacheKey(loaderId, pathname, params);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
if (store.keyGenerator && requestCtx) {
|
|
103
|
-
try {
|
|
104
|
-
return await store.keyGenerator(requestCtx, defaultKey);
|
|
105
|
-
} catch (error) {
|
|
106
|
-
console.error(
|
|
107
|
-
`[LoaderCache] Store keyGenerator failed, using default:`,
|
|
108
|
-
error,
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
// Priority 3: Default key
|
|
114
|
-
return defaultKey;
|
|
78
|
+
if (options === false) return defaultKey;
|
|
79
|
+
return resolveCacheKey(options.key, store, defaultKey, "LoaderCache");
|
|
115
80
|
}
|
|
116
81
|
|
|
117
82
|
/**
|
|
118
83
|
* Resolve tags from cache options (static array or function).
|
|
119
84
|
* Fails open: a thrown tag callback falls back to no tags rather than
|
|
120
|
-
* aborting the request
|
|
85
|
+
* aborting the request. Tags are additive metadata (not identity), so
|
|
86
|
+
* a missing tag does not cause cache collisions.
|
|
121
87
|
*/
|
|
122
88
|
function resolveTags(loaderEntry: LoaderEntry): string[] | undefined {
|
|
123
89
|
const options = loaderEntry.cache?.options;
|
|
@@ -140,41 +106,12 @@ function resolveTags(loaderEntry: LoaderEntry): string[] | undefined {
|
|
|
140
106
|
return options.tags;
|
|
141
107
|
}
|
|
142
108
|
|
|
143
|
-
function getLoaderStore(
|
|
144
|
-
const cacheConfig = loaderEntry.cache;
|
|
145
|
-
if (!cacheConfig || cacheConfig.options === false) return null;
|
|
146
|
-
const options = cacheConfig.options;
|
|
147
|
-
|
|
148
|
-
// Explicit store from cache() options
|
|
149
|
-
if (options.store) return options.store;
|
|
150
|
-
|
|
151
|
-
// App-level store from request context
|
|
152
|
-
return _getRequestContext()?._cacheStore ?? null;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function getLoaderTtl(
|
|
156
|
-
loaderEntry: LoaderEntry,
|
|
157
|
-
store: SegmentCacheStore,
|
|
158
|
-
): number {
|
|
159
|
-
const cacheConfig = loaderEntry.cache;
|
|
160
|
-
if (!cacheConfig || cacheConfig.options === false) return DEFAULT_TTL_SECONDS;
|
|
161
|
-
const options = cacheConfig.options;
|
|
162
|
-
|
|
163
|
-
if (options.ttl !== undefined) return options.ttl;
|
|
164
|
-
if (store.defaults?.ttl !== undefined) return store.defaults.ttl;
|
|
165
|
-
return DEFAULT_TTL_SECONDS;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function getLoaderSwr(
|
|
109
|
+
function getLoaderStore(
|
|
169
110
|
loaderEntry: LoaderEntry,
|
|
170
|
-
|
|
171
|
-
): number | undefined {
|
|
111
|
+
): import("../../cache/types.js").SegmentCacheStore | null {
|
|
172
112
|
const cacheConfig = loaderEntry.cache;
|
|
173
|
-
if (!cacheConfig || cacheConfig.options === false) return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
if (options.swr !== undefined) return options.swr;
|
|
177
|
-
return store.defaults?.swr;
|
|
113
|
+
if (!cacheConfig || cacheConfig.options === false) return null;
|
|
114
|
+
return resolveCacheStore(cacheConfig.options.store);
|
|
178
115
|
}
|
|
179
116
|
|
|
180
117
|
/**
|
|
@@ -210,8 +147,9 @@ export function resolveLoaderData<TEnv>(
|
|
|
210
147
|
}
|
|
211
148
|
|
|
212
149
|
const loaderId = loaderEntry.loader.$$id;
|
|
213
|
-
const ttl =
|
|
214
|
-
const
|
|
150
|
+
const ttl = resolveTtl(options.ttl, store.defaults, DEFAULT_ROUTE_TTL);
|
|
151
|
+
const swrWindow = resolveSwrWindow(options.swr, store.defaults);
|
|
152
|
+
const swr = swrWindow || undefined;
|
|
215
153
|
const tags = resolveTags(loaderEntry);
|
|
216
154
|
|
|
217
155
|
// Wrap ctx.use() so cache HIT primes the handler's memoization map.
|
|
@@ -228,67 +166,20 @@ export function resolveLoaderData<TEnv>(
|
|
|
228
166
|
ctx.params,
|
|
229
167
|
);
|
|
230
168
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
const requestCtx = getRequestContext();
|
|
246
|
-
const revalidate = async () => {
|
|
247
|
-
try {
|
|
248
|
-
const fresh = await originalUse(loaderEntry.loader);
|
|
249
|
-
const serialized = await codec.serializeResult(fresh);
|
|
250
|
-
if (serialized !== null) {
|
|
251
|
-
await store.setItem!(key, serialized, { ttl, swr, tags });
|
|
252
|
-
}
|
|
253
|
-
} catch {
|
|
254
|
-
// Background revalidation failed silently
|
|
255
|
-
}
|
|
256
|
-
};
|
|
257
|
-
if (requestCtx?.waitUntil) {
|
|
258
|
-
requestCtx.waitUntil(revalidate);
|
|
259
|
-
} else {
|
|
260
|
-
revalidate();
|
|
261
|
-
}
|
|
262
|
-
return data;
|
|
263
|
-
}
|
|
264
|
-
} catch {
|
|
265
|
-
// Cache lookup failed, fall through to fresh execution
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
// Cache miss — execute loader via ctx.use() (which memoizes it)
|
|
269
|
-
debugLoaderCacheLog(`[LoaderCache] MISS: ${key}`);
|
|
270
|
-
const data = await originalUse(loaderEntry.loader);
|
|
271
|
-
|
|
272
|
-
// Non-blocking cache write
|
|
273
|
-
const requestCtx = getRequestContext();
|
|
274
|
-
const cacheWrite = async () => {
|
|
275
|
-
try {
|
|
276
|
-
const serialized = await codec.serializeResult(data);
|
|
277
|
-
if (serialized !== null) {
|
|
278
|
-
await store.setItem!(key, serialized, { ttl, swr, tags });
|
|
279
|
-
debugLoaderCacheLog(`[LoaderCache] Cached: ${key}`);
|
|
280
|
-
}
|
|
281
|
-
} catch {
|
|
282
|
-
// Cache write failed silently
|
|
283
|
-
}
|
|
284
|
-
};
|
|
285
|
-
if (requestCtx?.waitUntil) {
|
|
286
|
-
requestCtx.waitUntil(cacheWrite);
|
|
287
|
-
} else {
|
|
288
|
-
await cacheWrite();
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
return data;
|
|
169
|
+
return readThroughItem({
|
|
170
|
+
getItem: (k) => store.getItem!(k),
|
|
171
|
+
setItem: (k, v, o) => store.setItem!(k, v, o),
|
|
172
|
+
key,
|
|
173
|
+
execute: () => originalUse(loaderEntry.loader),
|
|
174
|
+
serialize: (d) => codec.serializeResult(d),
|
|
175
|
+
deserialize: (v) => codec.deserializeResult(v),
|
|
176
|
+
storeOptions: { ttl, swr, tags },
|
|
177
|
+
onHit: () => debugLoaderCacheLog(`[LoaderCache] HIT: ${key}`),
|
|
178
|
+
onStale: () => debugLoaderCacheLog(`[LoaderCache] STALE: ${key}`),
|
|
179
|
+
onMiss: () => debugLoaderCacheLog(`[LoaderCache] MISS: ${key}`),
|
|
180
|
+
onCached: () => debugLoaderCacheLog(`[LoaderCache] Cached: ${key}`),
|
|
181
|
+
host: getRequestContext(),
|
|
182
|
+
});
|
|
292
183
|
})();
|
|
293
184
|
|
|
294
185
|
// Temporarily replace ctx.use() so the handler's call returns cached data.
|