@rangojs/router 0.0.0-experimental.259 → 0.0.0-experimental.26
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 +294 -28
- package/dist/bin/rango.js +355 -47
- package/dist/vite/index.js +1658 -1239
- package/package.json +3 -3
- package/skills/cache-guide/SKILL.md +9 -5
- package/skills/caching/SKILL.md +4 -4
- package/skills/document-cache/SKILL.md +2 -2
- package/skills/hooks/SKILL.md +40 -29
- package/skills/host-router/SKILL.md +218 -0
- package/skills/intercept/SKILL.md +79 -0
- package/skills/layout/SKILL.md +62 -2
- package/skills/loader/SKILL.md +229 -15
- package/skills/middleware/SKILL.md +109 -30
- package/skills/parallel/SKILL.md +57 -2
- package/skills/prerender/SKILL.md +189 -19
- package/skills/rango/SKILL.md +1 -2
- package/skills/response-routes/SKILL.md +3 -3
- package/skills/route/SKILL.md +44 -3
- package/skills/router-setup/SKILL.md +80 -3
- package/skills/theme/SKILL.md +5 -4
- package/skills/typesafety/SKILL.md +59 -16
- package/skills/use-cache/SKILL.md +16 -2
- package/src/__internal.ts +1 -1
- package/src/bin/rango.ts +56 -19
- package/src/browser/action-coordinator.ts +97 -0
- package/src/browser/event-controller.ts +29 -48
- package/src/browser/history-state.ts +80 -0
- package/src/browser/intercept-utils.ts +1 -1
- package/src/browser/link-interceptor.ts +19 -3
- package/src/browser/merge-segment-loaders.ts +9 -2
- package/src/browser/navigation-bridge.ts +66 -443
- package/src/browser/navigation-client.ts +34 -62
- package/src/browser/navigation-store.ts +4 -33
- package/src/browser/navigation-transaction.ts +295 -0
- package/src/browser/partial-update.ts +103 -151
- package/src/browser/prefetch/cache.ts +67 -0
- package/src/browser/prefetch/fetch.ts +137 -0
- package/src/browser/prefetch/observer.ts +65 -0
- package/src/browser/prefetch/policy.ts +42 -0
- package/src/browser/prefetch/queue.ts +88 -0
- package/src/browser/rango-state.ts +112 -0
- package/src/browser/react/Link.tsx +154 -44
- package/src/browser/react/NavigationProvider.tsx +32 -0
- package/src/browser/react/context.ts +6 -0
- package/src/browser/react/filter-segment-order.ts +11 -0
- package/src/browser/react/index.ts +2 -6
- package/src/browser/react/location-state-shared.ts +29 -11
- package/src/browser/react/location-state.ts +6 -4
- package/src/browser/react/nonce-context.ts +23 -0
- package/src/browser/react/shallow-equal.ts +27 -0
- package/src/browser/react/use-action.ts +23 -45
- package/src/browser/react/use-client-cache.ts +5 -3
- package/src/browser/react/use-handle.ts +21 -64
- package/src/browser/react/use-navigation.ts +7 -32
- package/src/browser/react/use-params.ts +5 -34
- package/src/browser/react/use-pathname.ts +2 -3
- package/src/browser/react/use-router.ts +3 -6
- package/src/browser/react/use-search-params.ts +2 -1
- package/src/browser/react/use-segments.ts +75 -114
- package/src/browser/response-adapter.ts +73 -0
- package/src/browser/rsc-router.tsx +46 -22
- package/src/browser/scroll-restoration.ts +10 -7
- package/src/browser/server-action-bridge.ts +458 -405
- package/src/browser/types.ts +21 -35
- package/src/browser/validate-redirect-origin.ts +29 -0
- package/src/build/generate-manifest.ts +38 -13
- package/src/build/generate-route-types.ts +4 -0
- package/src/build/index.ts +1 -0
- package/src/build/route-trie.ts +19 -3
- 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 +170 -18
- 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 +136 -123
- package/src/cache/cache-scope.ts +76 -83
- package/src/cache/cf/cf-cache-store.ts +12 -7
- package/src/cache/document-cache.ts +93 -69
- package/src/cache/handle-capture.ts +81 -0
- package/src/cache/index.ts +0 -15
- package/src/cache/memory-segment-store.ts +43 -69
- package/src/cache/profile-registry.ts +43 -8
- package/src/cache/read-through-swr.ts +134 -0
- package/src/cache/segment-codec.ts +140 -117
- package/src/cache/taint.ts +30 -3
- package/src/cache/types.ts +1 -115
- package/src/client.rsc.tsx +0 -1
- package/src/client.tsx +53 -76
- 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/index.ts +0 -3
- package/src/host/router.ts +14 -1
- package/src/href-client.ts +3 -1
- package/src/index.rsc.ts +53 -10
- package/src/index.ts +73 -43
- package/src/loader.rsc.ts +12 -4
- package/src/loader.ts +8 -0
- package/src/prerender/store.ts +60 -18
- 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/index.ts +0 -3
- 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 +96 -17
- package/src/router/intercept-resolution.ts +6 -4
- package/src/router/lazy-includes.ts +4 -0
- package/src/router/loader-resolution.ts +6 -11
- package/src/router/logging.ts +100 -3
- package/src/router/manifest.ts +32 -3
- package/src/router/match-api.ts +62 -54
- 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 +78 -10
- package/src/router/match-middleware/cache-store.ts +2 -0
- package/src/router/match-pipelines.ts +8 -43
- package/src/router/match-result.ts +0 -9
- package/src/router/metrics.ts +233 -13
- package/src/router/middleware-types.ts +34 -39
- package/src/router/middleware.ts +290 -130
- package/src/router/pattern-matching.ts +61 -10
- package/src/router/prerender-match.ts +36 -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 +158 -40
- package/src/router/router-options.ts +223 -1
- package/src/router/router-registry.ts +5 -2
- package/src/router/segment-resolution/fresh.ts +165 -242
- package/src/router/segment-resolution/helpers.ts +263 -0
- package/src/router/segment-resolution/loader-cache.ts +102 -98
- package/src/router/segment-resolution/revalidation.ts +394 -272
- package/src/router/segment-resolution/static-store.ts +2 -2
- package/src/router/segment-resolution.ts +1 -3
- 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/trie-matching.ts +20 -2
- package/src/router/types.ts +7 -1
- package/src/router.ts +203 -18
- package/src/rsc/handler-context.ts +13 -2
- package/src/rsc/handler.ts +489 -438
- package/src/rsc/helpers.ts +125 -5
- package/src/rsc/index.ts +0 -20
- package/src/rsc/loader-fetch.ts +84 -42
- package/src/rsc/manifest-init.ts +3 -2
- package/src/rsc/origin-guard.ts +141 -0
- package/src/rsc/progressive-enhancement.ts +245 -19
- package/src/rsc/response-route-handler.ts +347 -0
- package/src/rsc/rsc-rendering.ts +47 -43
- package/src/rsc/runtime-warnings.ts +42 -0
- package/src/rsc/server-action.ts +166 -66
- package/src/rsc/ssr-setup.ts +128 -0
- package/src/rsc/types.ts +20 -2
- package/src/search-params.ts +38 -23
- package/src/server/context.ts +61 -7
- package/src/server/cookie-store.ts +190 -0
- package/src/server/fetchable-loader-store.ts +11 -6
- package/src/server/handle-store.ts +84 -12
- package/src/server/loader-registry.ts +11 -46
- package/src/server/request-context.ts +275 -49
- package/src/server.ts +6 -0
- package/src/ssr/index.tsx +67 -28
- package/src/static-handler.ts +7 -0
- package/src/theme/ThemeProvider.tsx +6 -1
- package/src/theme/index.ts +4 -18
- package/src/theme/theme-context.ts +1 -28
- package/src/theme/theme-script.ts +2 -1
- package/src/types/cache-types.ts +6 -1
- package/src/types/error-types.ts +3 -0
- package/src/types/global-namespace.ts +22 -0
- package/src/types/handler-context.ts +103 -16
- package/src/types/index.ts +1 -1
- package/src/types/loader-types.ts +9 -6
- package/src/types/route-config.ts +17 -26
- package/src/types/route-entry.ts +28 -0
- package/src/types/segments.ts +0 -5
- 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 +29 -7
- package/src/urls/type-extraction.ts +23 -15
- package/src/use-loader.tsx +27 -9
- package/src/vite/discovery/bundle-postprocess.ts +32 -52
- package/src/vite/discovery/discover-routers.ts +52 -26
- package/src/vite/discovery/prerender-collection.ts +58 -41
- package/src/vite/discovery/route-types-writer.ts +7 -7
- package/src/vite/discovery/state.ts +7 -7
- package/src/vite/discovery/virtual-module-codegen.ts +5 -2
- package/src/vite/index.ts +10 -51
- package/src/vite/plugins/client-ref-dedup.ts +115 -0
- package/src/vite/plugins/client-ref-hashing.ts +3 -3
- package/src/vite/plugins/expose-internal-ids.ts +4 -3
- package/src/vite/plugins/refresh-cmd.ts +65 -0
- package/src/vite/plugins/use-cache-transform.ts +91 -3
- package/src/vite/plugins/version-plugin.ts +188 -18
- package/src/vite/rango.ts +61 -36
- package/src/vite/router-discovery.ts +173 -100
- package/src/vite/utils/prerender-utils.ts +81 -0
- package/src/vite/utils/shared-utils.ts +19 -9
- package/skills/testing/SKILL.md +0 -226
- package/src/browser/lru-cache.ts +0 -61
- package/src/browser/react/prefetch.ts +0 -27
- package/src/browser/request-controller.ts +0 -164
- package/src/cache/memory-store.ts +0 -253
- package/src/href-context.ts +0 -33
- package/src/route-definition/route-function.ts +0 -119
- package/src/router.gen.ts +0 -6
- package/src/static-handler.gen.ts +0 -5
- package/src/urls.gen.ts +0 -8
- /package/{CLAUDE.md → AGENTS.md} +0 -0
|
@@ -14,6 +14,7 @@ import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js";
|
|
|
14
14
|
import { RSC_ROUTER_BRAND } from "./router-registry.js";
|
|
15
15
|
import type { RSCRouterOptions, RootLayoutProps } from "./router-options.js";
|
|
16
16
|
import type { DefaultVars } from "../types/global-namespace.js";
|
|
17
|
+
import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js";
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Options passed to router.fetch(), router.match(), and other request entrypoints.
|
|
@@ -47,19 +48,19 @@ type MergeRoutesWithResponses<
|
|
|
47
48
|
};
|
|
48
49
|
|
|
49
50
|
/**
|
|
50
|
-
* RSC Router interface
|
|
51
|
-
*
|
|
51
|
+
* Public RSC Router interface — the user-facing API surface.
|
|
52
|
+
*
|
|
53
|
+
* Users interact with this type when building and using routers.
|
|
54
|
+
* Internal framework code uses RSCRouterInternal (via toInternal()) to access
|
|
55
|
+
* matching, build-time, and configuration members that are not part of the
|
|
56
|
+
* public contract.
|
|
57
|
+
*
|
|
58
|
+
* TRoutes accumulates all registered route types through the builder chain.
|
|
52
59
|
*/
|
|
53
60
|
export interface RSCRouter<
|
|
54
61
|
TEnv = any,
|
|
55
62
|
TRoutes extends Record<string, unknown> = Record<string, string>,
|
|
56
63
|
> {
|
|
57
|
-
/**
|
|
58
|
-
* Brand marker for build-time discovery.
|
|
59
|
-
* The Vite plugin uses this to identify router instances in module exports.
|
|
60
|
-
*/
|
|
61
|
-
readonly __brand: typeof RSC_ROUTER_BRAND;
|
|
62
|
-
|
|
63
64
|
/**
|
|
64
65
|
* Unique identifier for this router instance.
|
|
65
66
|
* Used to namespace static output and isolate route maps between routers.
|
|
@@ -134,6 +135,90 @@ export interface RSCRouter<
|
|
|
134
135
|
*/
|
|
135
136
|
readonly routeMap: TRoutes;
|
|
136
137
|
|
|
138
|
+
/**
|
|
139
|
+
* Handle an RSC request.
|
|
140
|
+
*
|
|
141
|
+
* Uses the router's configuration (nonce, version, cache) automatically.
|
|
142
|
+
* The handler is lazily created on first call.
|
|
143
|
+
*
|
|
144
|
+
* @example Cloudflare Workers
|
|
145
|
+
* ```tsx
|
|
146
|
+
* import { router } from "./router";
|
|
147
|
+
*
|
|
148
|
+
* export default { fetch: router.fetch };
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @example Direct export
|
|
152
|
+
* ```tsx
|
|
153
|
+
* const router = createRouter({
|
|
154
|
+
* document: Document,
|
|
155
|
+
* urls: urlpatterns,
|
|
156
|
+
* nonce: () => true,
|
|
157
|
+
* });
|
|
158
|
+
*
|
|
159
|
+
* export const fetch = router.fetch;
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
fetch(request: Request, input?: RouterRequestInput<TEnv>): Promise<Response>;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Internal RSC Router interface — the full framework-facing API.
|
|
167
|
+
*
|
|
168
|
+
* This type includes all members used by the Vite plugin, RSC handler,
|
|
169
|
+
* pre-rendering pipeline, and other framework internals. It is NOT exported
|
|
170
|
+
* from the public package API.
|
|
171
|
+
*
|
|
172
|
+
* Use toInternal(router) to assert a public RSCRouter into this type
|
|
173
|
+
* at the boundary where framework code receives a user-provided router.
|
|
174
|
+
*/
|
|
175
|
+
export interface RSCRouterInternal<
|
|
176
|
+
TEnv = any,
|
|
177
|
+
TRoutes extends Record<string, unknown> = Record<string, string>,
|
|
178
|
+
> {
|
|
179
|
+
/**
|
|
180
|
+
* Brand marker for build-time discovery.
|
|
181
|
+
* The Vite plugin uses this to identify router instances in module exports.
|
|
182
|
+
*/
|
|
183
|
+
readonly __brand: typeof RSC_ROUTER_BRAND;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Unique identifier for this router instance.
|
|
187
|
+
* Used to namespace static output and isolate route maps between routers.
|
|
188
|
+
*/
|
|
189
|
+
readonly id: string;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Register routes using URL patterns from urls()
|
|
193
|
+
*/
|
|
194
|
+
routes<T extends UrlPatterns<TEnv, any>>(
|
|
195
|
+
patterns: T,
|
|
196
|
+
): RSCRouter<
|
|
197
|
+
TEnv,
|
|
198
|
+
TRoutes &
|
|
199
|
+
(NonNullable<T["_routes"]> extends Record<string, unknown>
|
|
200
|
+
? MergeRoutesWithResponses<NonNullable<T["_routes"]>, T["_responses"]>
|
|
201
|
+
: Record<string, string>)
|
|
202
|
+
>;
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Add global middleware that runs on all routes
|
|
206
|
+
*/
|
|
207
|
+
use(
|
|
208
|
+
patternOrMiddleware: string | MiddlewareFn<TEnv>,
|
|
209
|
+
middleware?: MiddlewareFn<TEnv>,
|
|
210
|
+
): RSCRouter<TEnv, TRoutes>;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Type-safe URL builder for registered routes
|
|
214
|
+
*/
|
|
215
|
+
reverse: ReverseFunction<TRoutes>;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Accumulated route map for typeof extraction
|
|
219
|
+
*/
|
|
220
|
+
readonly routeMap: TRoutes;
|
|
221
|
+
|
|
137
222
|
/**
|
|
138
223
|
* Root layout component that wraps the entire application
|
|
139
224
|
* Access this to pass to renderSegments
|
|
@@ -147,12 +232,12 @@ export interface RSCRouter<
|
|
|
147
232
|
readonly onError?: RSCRouterOptions<TEnv>["onError"];
|
|
148
233
|
|
|
149
234
|
/**
|
|
150
|
-
* Cache configuration
|
|
235
|
+
* Cache configuration
|
|
151
236
|
*/
|
|
152
237
|
readonly cache?: RSCRouterOptions<TEnv>["cache"];
|
|
153
238
|
|
|
154
239
|
/**
|
|
155
|
-
* Not found component to render when no route matches
|
|
240
|
+
* Not found component to render when no route matches
|
|
156
241
|
*/
|
|
157
242
|
readonly notFound?: RSCRouterOptions<TEnv>["notFound"];
|
|
158
243
|
|
|
@@ -162,6 +247,21 @@ export interface RSCRouter<
|
|
|
162
247
|
*/
|
|
163
248
|
readonly themeConfig: import("../theme/types.js").ResolvedThemeConfig | null;
|
|
164
249
|
|
|
250
|
+
/**
|
|
251
|
+
* Cache profiles for "use cache" per-request resolution.
|
|
252
|
+
* Always includes at least the "default" profile.
|
|
253
|
+
*/
|
|
254
|
+
readonly cacheProfiles: Record<
|
|
255
|
+
string,
|
|
256
|
+
import("../cache/profile-registry.js").CacheProfile
|
|
257
|
+
>;
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Cache-Control header value for prefetch responses.
|
|
261
|
+
* False means no browser caching of prefetch responses.
|
|
262
|
+
*/
|
|
263
|
+
readonly prefetchCacheControl: string | false;
|
|
264
|
+
|
|
165
265
|
/**
|
|
166
266
|
* Whether connection warmup is enabled.
|
|
167
267
|
* When true, the client sends HEAD /?_rsc_warmup after idle periods
|
|
@@ -169,40 +269,65 @@ export interface RSCRouter<
|
|
|
169
269
|
*/
|
|
170
270
|
readonly warmupEnabled: boolean;
|
|
171
271
|
|
|
272
|
+
/**
|
|
273
|
+
* Whether router-wide performance debugging is enabled.
|
|
274
|
+
* Used by the request handler to create metrics before middleware runs.
|
|
275
|
+
*/
|
|
276
|
+
readonly debugPerformance?: boolean;
|
|
277
|
+
|
|
172
278
|
/**
|
|
173
279
|
* Whether ?__debug_manifest is allowed in production.
|
|
174
280
|
* Always enabled in development.
|
|
175
|
-
* @internal
|
|
176
281
|
*/
|
|
177
282
|
readonly allowDebugManifest: boolean;
|
|
178
283
|
|
|
179
284
|
/**
|
|
180
|
-
*
|
|
285
|
+
* Resolved timeout configuration (merged from shorthand + structured).
|
|
286
|
+
*/
|
|
287
|
+
readonly timeouts: ResolvedTimeouts;
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Custom timeout response handler.
|
|
291
|
+
*/
|
|
292
|
+
readonly onTimeout?: OnTimeoutCallback<TEnv>;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* App-level middleware entries
|
|
181
296
|
* These wrap the entire request/response cycle
|
|
182
297
|
*/
|
|
183
298
|
readonly middleware: MiddlewareEntry<TEnv>[];
|
|
184
299
|
|
|
185
300
|
/**
|
|
186
|
-
* Nonce provider for CSP
|
|
301
|
+
* Nonce provider for CSP
|
|
187
302
|
*/
|
|
188
303
|
readonly nonce?: NonceProvider<TEnv>;
|
|
189
304
|
|
|
190
305
|
/**
|
|
191
|
-
* RSC version string
|
|
306
|
+
* RSC version string
|
|
192
307
|
*/
|
|
193
308
|
readonly version?: string;
|
|
194
309
|
|
|
195
310
|
/**
|
|
196
311
|
* URL patterns reference for build-time manifest generation
|
|
197
|
-
* @internal
|
|
198
312
|
*/
|
|
199
313
|
readonly urlpatterns?: UrlPatterns<TEnv, any>;
|
|
200
314
|
|
|
315
|
+
/**
|
|
316
|
+
* SSR configuration. resolveStreaming determines stream vs allReady
|
|
317
|
+
* per HTML request (undefined = always stream).
|
|
318
|
+
*/
|
|
319
|
+
readonly ssr?: import("./router-options.js").SSROptions<TEnv>;
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Cross-origin request protection configuration.
|
|
323
|
+
* Default: true (enabled).
|
|
324
|
+
*/
|
|
325
|
+
readonly originCheck: import("../rsc/origin-guard.js").OriginCheckConfig<TEnv>;
|
|
326
|
+
|
|
201
327
|
/**
|
|
202
328
|
* Source file path where createRouter() was called.
|
|
203
329
|
* Set via Error.stack parsing at construction time.
|
|
204
330
|
* Used by the Vite plugin to write per-router named-routes.gen.ts files.
|
|
205
|
-
* @internal
|
|
206
331
|
*/
|
|
207
332
|
readonly __sourceFile?: string;
|
|
208
333
|
|
|
@@ -215,12 +340,12 @@ export interface RSCRouter<
|
|
|
215
340
|
* Build-time pre-render match. Resolves segments with a BuildContext
|
|
216
341
|
* (no request/env/headers/cookies), skipping middleware and loaders.
|
|
217
342
|
* Used by the Vite plugin to collect pre-render data at build time.
|
|
218
|
-
* @internal
|
|
219
343
|
*/
|
|
220
344
|
matchForPrerender(
|
|
221
345
|
pathname: string,
|
|
222
346
|
params: Record<string, string>,
|
|
223
347
|
buildVars?: Record<string, any>,
|
|
348
|
+
isPassthroughRoute?: boolean,
|
|
224
349
|
): Promise<{
|
|
225
350
|
segments: SerializedSegmentData[];
|
|
226
351
|
handles: Record<string, SegmentHandleData>;
|
|
@@ -228,12 +353,12 @@ export interface RSCRouter<
|
|
|
228
353
|
params: Record<string, string>;
|
|
229
354
|
interceptSegments?: SerializedSegmentData[];
|
|
230
355
|
interceptHandles?: Record<string, SegmentHandleData>;
|
|
356
|
+
passthrough?: true;
|
|
231
357
|
} | null>;
|
|
232
358
|
|
|
233
359
|
/**
|
|
234
360
|
* Render a single Static handler at build time.
|
|
235
361
|
* Returns the RSC-serialized component string and handle data, or null on failure.
|
|
236
|
-
* @internal
|
|
237
362
|
*/
|
|
238
363
|
renderStaticSegment(
|
|
239
364
|
handler: Function,
|
|
@@ -293,7 +418,6 @@ export interface RSCRouter<
|
|
|
293
418
|
): Promise<MatchResult | null>;
|
|
294
419
|
|
|
295
420
|
/**
|
|
296
|
-
* @internal
|
|
297
421
|
* Debug utility to serialize the manifest for inspection
|
|
298
422
|
* Returns a JSON-friendly representation of all routes and layouts
|
|
299
423
|
*/
|
|
@@ -301,27 +425,21 @@ export interface RSCRouter<
|
|
|
301
425
|
|
|
302
426
|
/**
|
|
303
427
|
* Handle an RSC request.
|
|
304
|
-
*
|
|
305
|
-
* Uses the router's configuration (nonce, version, cache) automatically.
|
|
306
|
-
* The handler is lazily created on first call.
|
|
307
|
-
*
|
|
308
|
-
* @example Cloudflare Workers
|
|
309
|
-
* ```tsx
|
|
310
|
-
* import { router } from "./router";
|
|
311
|
-
*
|
|
312
|
-
* export default { fetch: router.fetch };
|
|
313
|
-
* ```
|
|
314
|
-
*
|
|
315
|
-
* @example Direct export
|
|
316
|
-
* ```tsx
|
|
317
|
-
* const router = createRouter({
|
|
318
|
-
* document: Document,
|
|
319
|
-
* urls: urlpatterns,
|
|
320
|
-
* nonce: () => true,
|
|
321
|
-
* });
|
|
322
|
-
*
|
|
323
|
-
* export const fetch = router.fetch;
|
|
324
|
-
* ```
|
|
325
428
|
*/
|
|
326
429
|
fetch(request: Request, input?: RouterRequestInput<TEnv>): Promise<Response>;
|
|
327
430
|
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Assert a public RSCRouter into the internal type.
|
|
434
|
+
*
|
|
435
|
+
* Use this at the boundary where framework code receives a user-provided
|
|
436
|
+
* router and needs access to internal members (match, config, build-time).
|
|
437
|
+
* The cast is safe because createRouter() always produces an object that
|
|
438
|
+
* satisfies RSCRouterInternal; the public type is just a narrower view.
|
|
439
|
+
*/
|
|
440
|
+
export function toInternal<
|
|
441
|
+
TEnv = any,
|
|
442
|
+
TRoutes extends Record<string, unknown> = Record<string, string>,
|
|
443
|
+
>(router: RSCRouter<TEnv, TRoutes>): RSCRouterInternal<TEnv, TRoutes> {
|
|
444
|
+
return router as RSCRouterInternal<TEnv, TRoutes>;
|
|
445
|
+
}
|
|
@@ -9,6 +9,58 @@ import type { NonceProvider } from "../rsc/types.js";
|
|
|
9
9
|
import type { ExecutionContext } from "../server/request-context.js";
|
|
10
10
|
import type { UrlPatterns } from "../urls.js";
|
|
11
11
|
import type { NamedRouteEntry } from "./content-negotiation.js";
|
|
12
|
+
import type { TelemetrySink } from "./telemetry.js";
|
|
13
|
+
import type { RouterTimeouts, OnTimeoutCallback } from "./timeout.js";
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* SSR stream mode returned by resolveStreaming.
|
|
17
|
+
*
|
|
18
|
+
* - `"stream"` — start flushing HTML as soon as the shell is ready
|
|
19
|
+
* (default React SSR behavior via `renderToReadableStream`).
|
|
20
|
+
* - `"allReady"` — wait for every Suspense boundary to resolve before
|
|
21
|
+
* sending any bytes (equivalent to awaiting `stream.allReady`).
|
|
22
|
+
*/
|
|
23
|
+
export type SSRStreamMode = "stream" | "allReady";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Context passed to the resolveStreaming callback.
|
|
27
|
+
*/
|
|
28
|
+
export interface ResolveStreamingContext<TEnv = unknown> {
|
|
29
|
+
request: Request;
|
|
30
|
+
env: TEnv;
|
|
31
|
+
url: URL;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* SSR configuration options.
|
|
36
|
+
*/
|
|
37
|
+
export interface SSROptions<TEnv = unknown> {
|
|
38
|
+
/**
|
|
39
|
+
* Determine whether an HTML response should stream progressively or
|
|
40
|
+
* wait for full readiness before flushing.
|
|
41
|
+
*
|
|
42
|
+
* Called once per HTML request, before the HTML response is produced.
|
|
43
|
+
* Does NOT apply to RSC responses (`__rsc`, partial navigation, prefetch).
|
|
44
|
+
*
|
|
45
|
+
* Return `"stream"` (default) for progressive streaming or `"allReady"`
|
|
46
|
+
* to buffer the complete HTML before sending.
|
|
47
|
+
*
|
|
48
|
+
* @example Bot detection
|
|
49
|
+
* ```ts
|
|
50
|
+
* createRouter({
|
|
51
|
+
* ssr: {
|
|
52
|
+
* resolveStreaming: async ({ request, env }) => {
|
|
53
|
+
* const bot = await detectBot(request, env);
|
|
54
|
+
* return bot.isBot && !bot.supportsStreaming ? "allReady" : "stream";
|
|
55
|
+
* },
|
|
56
|
+
* },
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*/
|
|
60
|
+
resolveStreaming?: (
|
|
61
|
+
context: ResolveStreamingContext<TEnv>,
|
|
62
|
+
) => SSRStreamMode | Promise<SSRStreamMode>;
|
|
63
|
+
}
|
|
12
64
|
|
|
13
65
|
/**
|
|
14
66
|
* Props passed to the root layout component
|
|
@@ -187,7 +239,7 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
187
239
|
*
|
|
188
240
|
* @example Static config
|
|
189
241
|
* ```typescript
|
|
190
|
-
* import { MemorySegmentCacheStore } from "
|
|
242
|
+
* import { MemorySegmentCacheStore } from "@rangojs/router/cache";
|
|
191
243
|
*
|
|
192
244
|
* const router = createRouter({
|
|
193
245
|
* cache: {
|
|
@@ -338,6 +390,16 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
338
390
|
* nonce: (request, env) => env.nonce,
|
|
339
391
|
* });
|
|
340
392
|
* ```
|
|
393
|
+
*
|
|
394
|
+
* @example Access nonce in middleware
|
|
395
|
+
* ```tsx
|
|
396
|
+
* import { nonce } from "@rangojs/router";
|
|
397
|
+
*
|
|
398
|
+
* const cspMiddleware: Middleware = async (ctx, next) => {
|
|
399
|
+
* const value = ctx.get(nonce); // string | undefined
|
|
400
|
+
* await next();
|
|
401
|
+
* };
|
|
402
|
+
* ```
|
|
341
403
|
*/
|
|
342
404
|
nonce?: NonceProvider<TEnv>;
|
|
343
405
|
|
|
@@ -352,6 +414,18 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
352
414
|
*/
|
|
353
415
|
version?: string;
|
|
354
416
|
|
|
417
|
+
/**
|
|
418
|
+
* Cache-Control header value for prefetch responses.
|
|
419
|
+
* Only applied to non-intercept partial responses that include the
|
|
420
|
+
* `X-Rango-Prefetch` header (sent by the Link component's prefetch fetch).
|
|
421
|
+
* Navigation responses are never cached by the browser.
|
|
422
|
+
*
|
|
423
|
+
* Set to `false` to disable browser caching of prefetch responses entirely.
|
|
424
|
+
*
|
|
425
|
+
* @default "private, max-age=300"
|
|
426
|
+
*/
|
|
427
|
+
prefetchCacheControl?: string | false;
|
|
428
|
+
|
|
355
429
|
/**
|
|
356
430
|
* Enable connection warmup to keep TCP+TLS alive after idle periods.
|
|
357
431
|
*
|
|
@@ -362,4 +436,152 @@ export interface RSCRouterOptions<TEnv = any> {
|
|
|
362
436
|
* @default true
|
|
363
437
|
*/
|
|
364
438
|
warmup?: boolean;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Shorthand timeout (ms) applied to both action execution and render start.
|
|
442
|
+
* Does NOT apply to streamIdleMs.
|
|
443
|
+
* Overridden by individual values in `timeouts`.
|
|
444
|
+
*
|
|
445
|
+
* @example
|
|
446
|
+
* ```typescript
|
|
447
|
+
* createRouter({ timeout: 10_000 });
|
|
448
|
+
* ```
|
|
449
|
+
*/
|
|
450
|
+
timeout?: number;
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Structured timeout configuration per phase.
|
|
454
|
+
* Values here override the `timeout` shorthand.
|
|
455
|
+
*
|
|
456
|
+
* @example
|
|
457
|
+
* ```typescript
|
|
458
|
+
* createRouter({
|
|
459
|
+
* timeouts: {
|
|
460
|
+
* actionMs: 10_000,
|
|
461
|
+
* renderStartMs: 8_000,
|
|
462
|
+
* },
|
|
463
|
+
* });
|
|
464
|
+
* ```
|
|
465
|
+
*/
|
|
466
|
+
timeouts?: RouterTimeouts;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Custom handler invoked when a timeout occurs.
|
|
470
|
+
* Receives context about which phase timed out and must return a Response.
|
|
471
|
+
* If not provided, returns a plain 504 with "Request timed out" body
|
|
472
|
+
* and X-Rango-Timeout-Phase header.
|
|
473
|
+
*
|
|
474
|
+
* If the callback throws, the default 504 response is used as fallback.
|
|
475
|
+
*
|
|
476
|
+
* @example
|
|
477
|
+
* ```typescript
|
|
478
|
+
* createRouter({
|
|
479
|
+
* timeout: 10_000,
|
|
480
|
+
* onTimeout: (ctx) => {
|
|
481
|
+
* return new Response(
|
|
482
|
+
* JSON.stringify({ error: "timeout", phase: ctx.phase }),
|
|
483
|
+
* { status: 504, headers: { "Content-Type": "application/json" } },
|
|
484
|
+
* );
|
|
485
|
+
* },
|
|
486
|
+
* });
|
|
487
|
+
* ```
|
|
488
|
+
*/
|
|
489
|
+
onTimeout?: OnTimeoutCallback<TEnv>;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Telemetry sink for structured lifecycle events.
|
|
493
|
+
*
|
|
494
|
+
* When provided, the router emits events for request start/end,
|
|
495
|
+
* loader start/end/error, handler errors, cache decisions, and
|
|
496
|
+
* revalidation decisions.
|
|
497
|
+
*
|
|
498
|
+
* No-op when not configured (zero overhead).
|
|
499
|
+
*
|
|
500
|
+
* @example Console logging
|
|
501
|
+
* ```typescript
|
|
502
|
+
* import { createConsoleSink } from "@rangojs/router";
|
|
503
|
+
*
|
|
504
|
+
* const router = createRouter({
|
|
505
|
+
* telemetry: createConsoleSink(),
|
|
506
|
+
* });
|
|
507
|
+
* ```
|
|
508
|
+
*
|
|
509
|
+
* @example Custom sink
|
|
510
|
+
* ```typescript
|
|
511
|
+
* const router = createRouter({
|
|
512
|
+
* telemetry: {
|
|
513
|
+
* emit(event) {
|
|
514
|
+
* myTracer.record(event);
|
|
515
|
+
* },
|
|
516
|
+
* },
|
|
517
|
+
* });
|
|
518
|
+
* ```
|
|
519
|
+
*/
|
|
520
|
+
telemetry?: TelemetrySink;
|
|
521
|
+
|
|
522
|
+
/**
|
|
523
|
+
* SSR configuration options.
|
|
524
|
+
*
|
|
525
|
+
* @example
|
|
526
|
+
* ```typescript
|
|
527
|
+
* createRouter({
|
|
528
|
+
* ssr: {
|
|
529
|
+
* resolveStreaming: async ({ request, env }) => {
|
|
530
|
+
* const bot = await detectBot(request, env);
|
|
531
|
+
* return bot.isBot ? "allReady" : "stream";
|
|
532
|
+
* },
|
|
533
|
+
* },
|
|
534
|
+
* });
|
|
535
|
+
* ```
|
|
536
|
+
*/
|
|
537
|
+
ssr?: SSROptions<TEnv>;
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Cross-origin request protection for server actions, loader fetches,
|
|
541
|
+
* and progressive enhancement form submissions.
|
|
542
|
+
*
|
|
543
|
+
* When enabled, the router validates that the request's Origin header
|
|
544
|
+
* (or Referer fallback) matches the Host before executing actions,
|
|
545
|
+
* loaders, or PE submissions. Requests without Origin/Referer are
|
|
546
|
+
* allowed (same-origin navigations, non-browser clients).
|
|
547
|
+
*
|
|
548
|
+
* The built-in check compares Origin against the Host header and
|
|
549
|
+
* url.protocol. It does NOT trust X-Forwarded-Host/Proto headers
|
|
550
|
+
* (they are client-controllable without a trusted proxy). On standard
|
|
551
|
+
* deployments (Cloudflare Workers, Node behind nginx/caddy) the Host
|
|
552
|
+
* header is already set to the public-facing host by the platform or
|
|
553
|
+
* proxy. For non-standard proxy setups where Host differs from the
|
|
554
|
+
* public origin, use a custom function that reads the appropriate
|
|
555
|
+
* forwarded headers from your trusted proxy.
|
|
556
|
+
*
|
|
557
|
+
* - `true` (default) -- enable built-in origin validation
|
|
558
|
+
* - `false` -- disable
|
|
559
|
+
* - function -- full custom control with access to env, phase,
|
|
560
|
+
* and the built-in check via `ctx.defaultCheck()`
|
|
561
|
+
*
|
|
562
|
+
* The callback receives `OriginCheckContext` with `request`, `url`,
|
|
563
|
+
* `env`, `routerId`, `phase` ("action" | "loader" | "pe-form"),
|
|
564
|
+
* and `defaultCheck()`. Return `true` to allow, `false` for default
|
|
565
|
+
* 403 rejection, or a `Response` for custom rejection.
|
|
566
|
+
*
|
|
567
|
+
* @default true
|
|
568
|
+
*
|
|
569
|
+
* @example Trusted proxy with X-Forwarded-Host
|
|
570
|
+
* ```ts
|
|
571
|
+
* createRouter({
|
|
572
|
+
* originCheck({ request, url, env, defaultCheck }) {
|
|
573
|
+
* if (env.TRUST_PROXY) {
|
|
574
|
+
* const origin = request.headers.get("origin");
|
|
575
|
+
* if (!origin) return true;
|
|
576
|
+
* if (origin === "null") return false;
|
|
577
|
+
* const host = request.headers.get("x-forwarded-host")
|
|
578
|
+
* ?? request.headers.get("host") ?? url.host;
|
|
579
|
+
* return origin.toLowerCase() === `${url.protocol}//${host}`.toLowerCase();
|
|
580
|
+
* }
|
|
581
|
+
* return defaultCheck();
|
|
582
|
+
* },
|
|
583
|
+
* });
|
|
584
|
+
* ```
|
|
585
|
+
*/
|
|
586
|
+
originCheck?: import("../rsc/origin-guard.js").OriginCheckConfig<TEnv>;
|
|
365
587
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RSCRouterInternal } from "./router-interfaces.js";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Brand marker for identifying router instances at build time.
|
|
@@ -12,7 +12,10 @@ export const RSC_ROUTER_BRAND = "__rsc_router__" as const;
|
|
|
12
12
|
* Used by the Vite plugin at build time to discover routers and extract
|
|
13
13
|
* manifests, prefix trees, and pre-render candidates.
|
|
14
14
|
*/
|
|
15
|
-
export const RouterRegistry: Map<
|
|
15
|
+
export const RouterRegistry: Map<
|
|
16
|
+
string,
|
|
17
|
+
RSCRouterInternal<any, any>
|
|
18
|
+
> = new Map();
|
|
16
19
|
|
|
17
20
|
export let routerAutoId = 0;
|
|
18
21
|
|