@rangojs/router 0.0.0-experimental.84 → 0.0.0-experimental.86
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 +50 -20
- package/dist/vite/index.js +19 -9
- package/package.json +14 -15
- package/skills/breadcrumbs/SKILL.md +3 -1
- package/skills/hooks/SKILL.md +4 -2
- package/skills/links/SKILL.md +88 -16
- package/skills/loader/SKILL.md +35 -2
- package/skills/typesafety/SKILL.md +3 -1
- package/src/browser/app-shell.ts +52 -0
- package/src/browser/navigation-bridge.ts +51 -2
- package/src/browser/navigation-store.ts +25 -1
- package/src/browser/partial-update.ts +20 -1
- package/src/browser/prefetch/cache.ts +16 -0
- package/src/browser/rango-state.ts +53 -13
- package/src/browser/react/NavigationProvider.tsx +44 -9
- package/src/browser/react/use-router.ts +8 -1
- package/src/browser/rsc-router.tsx +34 -6
- package/src/browser/types.ts +13 -0
- package/src/cache/cf/cf-cache-store.ts +5 -7
- package/src/index.rsc.ts +3 -0
- package/src/index.ts +3 -0
- package/src/outlet-context.ts +1 -1
- package/src/reverse.ts +3 -2
- package/src/router/handler-context.ts +20 -3
- package/src/router/lazy-includes.ts +1 -1
- package/src/router/loader-resolution.ts +3 -0
- package/src/router/match-api.ts +3 -3
- package/src/router/middleware-types.ts +2 -22
- package/src/router/middleware.ts +18 -3
- package/src/router/pattern-matching.ts +60 -9
- package/src/router/trie-matching.ts +10 -4
- package/src/router/url-params.ts +49 -0
- package/src/router.ts +1 -2
- package/src/rsc/handler.ts +2 -1
- package/src/rsc/response-route-handler.ts +3 -0
- package/src/server/request-context.ts +10 -42
- package/src/types/handler-context.ts +2 -34
- package/src/types/loader-types.ts +2 -6
- package/src/types/request-scope.ts +126 -0
- package/src/urls/response-types.ts +2 -10
- package/src/vite/rango.ts +23 -7
- package/src/vite/utils/banner.ts +1 -1
- package/src/vite/utils/package-resolution.ts +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RequestScope: the fields every user-facing context shares.
|
|
3
|
+
*
|
|
4
|
+
* A handler, middleware, loader, response handler, and the ALS-bound
|
|
5
|
+
* RequestContext are all different phases of the same request, and they
|
|
6
|
+
* all carry the same set of request-scoped capabilities: the raw Request,
|
|
7
|
+
* the parsed URL pair (`url` is cleaned of internal `_rsc*` params,
|
|
8
|
+
* `originalUrl` retains them), pathname/searchParams, platform bindings
|
|
9
|
+
* (`env`), and two escape hatches for work that outlives the response
|
|
10
|
+
* (`waitUntil`) or needs the raw Cloudflare runtime object
|
|
11
|
+
* (`executionContext`).
|
|
12
|
+
*
|
|
13
|
+
* Each public context type intersects `RequestScope<TEnv>` with its own
|
|
14
|
+
* phase-specific fields (e.g. `params`/`reverse` on HandlerContext,
|
|
15
|
+
* `headers`/`header()` on MiddlewareContext). That keeps platform surface
|
|
16
|
+
* in one place and lets the next runtime escape hatch we need land in
|
|
17
|
+
* one file instead of four.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import type { DefaultEnv } from "./global-namespace.js";
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Minimal subset of Cloudflare Workers' ExecutionContext that the router
|
|
24
|
+
* uses. Defined locally so the package does not depend on
|
|
25
|
+
* `@cloudflare/workers-types`. Consumers that want the full type can cast.
|
|
26
|
+
*
|
|
27
|
+
* On non-Cloudflare runtimes (Node, dev server, tests), this is undefined
|
|
28
|
+
* — portable apps should prefer `ctx.waitUntil(...)`, which degrades
|
|
29
|
+
* gracefully. `ctx.executionContext` is the escape hatch for libraries
|
|
30
|
+
* (MCP, Durable Object routing, etc.) that type their arguments as the
|
|
31
|
+
* raw ExecutionContext.
|
|
32
|
+
*/
|
|
33
|
+
export interface ExecutionContext {
|
|
34
|
+
waitUntil(promise: Promise<any>): void;
|
|
35
|
+
passThroughOnException(): void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Fallback `waitUntil` body used when no Cloudflare `ExecutionContext`
|
|
40
|
+
* is available (Node, dev, tests). Runs the work fire-and-forget and
|
|
41
|
+
* logs errors so they don't silently swallow.
|
|
42
|
+
*
|
|
43
|
+
* Exported so every `waitUntil` call site degrades identically instead
|
|
44
|
+
* of inventing its own fallback policy.
|
|
45
|
+
*/
|
|
46
|
+
export function fireAndForgetWaitUntil(fn: () => Promise<void>): void {
|
|
47
|
+
fn().catch((err) =>
|
|
48
|
+
console.error("[waitUntil] Background task failed:", err),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Fields present on every user-facing request context.
|
|
54
|
+
*
|
|
55
|
+
* @template TEnv - Platform bindings type (Cloudflare env, etc.).
|
|
56
|
+
*/
|
|
57
|
+
export interface RequestScope<TEnv = DefaultEnv> {
|
|
58
|
+
/**
|
|
59
|
+
* The original incoming Request object (transport URL intact).
|
|
60
|
+
* Use `url` / `searchParams` for application logic — those have
|
|
61
|
+
* internal `_rsc*` params stripped. `request` preserves the raw URL
|
|
62
|
+
* when you need original headers, method, or body.
|
|
63
|
+
*/
|
|
64
|
+
request: Request;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The request URL with internal `_rsc*` transport params stripped.
|
|
68
|
+
* Use this for routing, link generation, and display.
|
|
69
|
+
*/
|
|
70
|
+
url: URL;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The original request URL with all parameters intact, including
|
|
74
|
+
* internal `_rsc*` transport params. Use `url` for application logic
|
|
75
|
+
* — this is only needed for advanced cases like debugging or custom
|
|
76
|
+
* cache keying.
|
|
77
|
+
*/
|
|
78
|
+
originalUrl: URL;
|
|
79
|
+
|
|
80
|
+
/** URL pathname (same as `url.pathname`). */
|
|
81
|
+
pathname: string;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Query parameters from the URL (system params like `_rsc*` are
|
|
85
|
+
* filtered). Always a standard `URLSearchParams` instance.
|
|
86
|
+
*/
|
|
87
|
+
searchParams: URLSearchParams;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Platform bindings (DB, KV, secrets, etc.). On Cloudflare Workers
|
|
91
|
+
* these are the `env` object passed to the Worker's `fetch()` handler.
|
|
92
|
+
*/
|
|
93
|
+
env: TEnv;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Schedule work to run after the response is sent.
|
|
97
|
+
* On Cloudflare Workers, delegates to `executionContext.waitUntil()`.
|
|
98
|
+
* On Node / dev / tests, runs as fire-and-forget with error logging.
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* ctx.waitUntil(async () => {
|
|
103
|
+
* await cacheStore.set(key, data, ttl);
|
|
104
|
+
* });
|
|
105
|
+
* ```
|
|
106
|
+
*/
|
|
107
|
+
waitUntil(fn: () => Promise<void>): void;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Raw Cloudflare Workers `ExecutionContext`, when running on a
|
|
111
|
+
* Cloudflare-compatible runtime. Undefined elsewhere.
|
|
112
|
+
*
|
|
113
|
+
* Escape hatch for libraries that type their arguments as
|
|
114
|
+
* `ExecutionContext` (MCP `fetch`, `routeAgentRequest`, etc.).
|
|
115
|
+
* For the common "do work after the response" case, prefer
|
|
116
|
+
* `ctx.waitUntil(...)` — it is platform-neutral.
|
|
117
|
+
*
|
|
118
|
+
* @example
|
|
119
|
+
* ```typescript
|
|
120
|
+
* path.any("/mcp", (ctx) =>
|
|
121
|
+
* emailMcp.fetch(ctx.request, ctx.env, ctx.executionContext!),
|
|
122
|
+
* );
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
executionContext?: ExecutionContext;
|
|
126
|
+
}
|
|
@@ -5,6 +5,7 @@ import type {
|
|
|
5
5
|
DefaultVars,
|
|
6
6
|
} from "../types/global-namespace.js";
|
|
7
7
|
import type { UseItems, ResponseRouteUseItem } from "../route-types.js";
|
|
8
|
+
import type { RequestScope } from "../types/request-scope.js";
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Reverse function for response handler contexts.
|
|
@@ -93,19 +94,10 @@ export type TextResponseHandler<
|
|
|
93
94
|
export interface ResponseHandlerContext<
|
|
94
95
|
TParams = Record<string, string>,
|
|
95
96
|
TEnv = any,
|
|
96
|
-
> {
|
|
97
|
-
request: Request;
|
|
97
|
+
> extends RequestScope<TEnv> {
|
|
98
98
|
params: TParams;
|
|
99
99
|
/** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
|
|
100
100
|
readonly _paramCheck?: (params: TParams) => TParams;
|
|
101
|
-
/** Platform bindings (DB, KV, secrets, etc.). */
|
|
102
|
-
env: TEnv;
|
|
103
|
-
/** Query parameters from the URL (system params like `_rsc*` are filtered). */
|
|
104
|
-
searchParams: URLSearchParams;
|
|
105
|
-
/** The full URL object (with system params filtered). */
|
|
106
|
-
url: URL;
|
|
107
|
-
/** The pathname portion of the request URL. */
|
|
108
|
-
pathname: string;
|
|
109
101
|
reverse: ResponseReverseFunction;
|
|
110
102
|
/** Read a variable set by middleware via ctx.set(key, value) or ctx.set(ContextVar, value). */
|
|
111
103
|
get: {
|
package/src/vite/rango.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { VIRTUAL_IDS } from "./plugins/virtual-entries.js";
|
|
|
12
12
|
import {
|
|
13
13
|
getExcludeDeps,
|
|
14
14
|
getPackageAliases,
|
|
15
|
+
getPublishedPackageName,
|
|
15
16
|
} from "./utils/package-resolution.js";
|
|
16
17
|
import { findRouterFiles } from "../build/generate-route-types.js";
|
|
17
18
|
import { createVersionPlugin } from "./plugins/version-plugin.js";
|
|
@@ -72,6 +73,13 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
72
73
|
"@vitejs/plugin-rsc/vendor/react-server-dom/client.browser",
|
|
73
74
|
];
|
|
74
75
|
|
|
76
|
+
// Vite supports a nested `A > B` syntax in optimizeDeps.include that resolves
|
|
77
|
+
// B from A's location. We anchor transitive deps (rsc-html-stream,
|
|
78
|
+
// @vitejs/plugin-rsc/vendor/*) to @rangojs/router so pnpm consumers — where
|
|
79
|
+
// these aren't visible at the app root — can still pre-bundle them.
|
|
80
|
+
const pkg = getPublishedPackageName();
|
|
81
|
+
const nested = (spec: string) => `${pkg} > ${spec}`;
|
|
82
|
+
|
|
75
83
|
// Mutable ref for router path (node preset only).
|
|
76
84
|
// Set immediately when user-specified, or populated by the auto-discover
|
|
77
85
|
// config() hook using Vite's resolved root.
|
|
@@ -126,7 +134,7 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
126
134
|
// Pre-bundle rsc-html-stream to prevent discovery during first request
|
|
127
135
|
// Exclude rsc-router modules to ensure same Context instance
|
|
128
136
|
optimizeDeps: {
|
|
129
|
-
include: ["rsc-html-stream/client"],
|
|
137
|
+
include: [nested("rsc-html-stream/client")],
|
|
130
138
|
exclude: excludeDeps,
|
|
131
139
|
esbuildOptions: sharedEsbuildOptions,
|
|
132
140
|
},
|
|
@@ -151,8 +159,10 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
151
159
|
"react-dom/static.edge",
|
|
152
160
|
"react/jsx-runtime",
|
|
153
161
|
"react/jsx-dev-runtime",
|
|
154
|
-
"rsc-html-stream/server",
|
|
155
|
-
|
|
162
|
+
nested("rsc-html-stream/server"),
|
|
163
|
+
nested(
|
|
164
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
|
|
165
|
+
),
|
|
156
166
|
],
|
|
157
167
|
exclude: excludeDeps,
|
|
158
168
|
esbuildOptions: sharedEsbuildOptions,
|
|
@@ -167,7 +177,9 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
167
177
|
"react",
|
|
168
178
|
"react/jsx-runtime",
|
|
169
179
|
"react/jsx-dev-runtime",
|
|
170
|
-
|
|
180
|
+
nested(
|
|
181
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
|
|
182
|
+
),
|
|
171
183
|
],
|
|
172
184
|
exclude: excludeDeps,
|
|
173
185
|
esbuildOptions: sharedEsbuildOptions,
|
|
@@ -280,7 +292,7 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
280
292
|
"react-dom",
|
|
281
293
|
"react/jsx-runtime",
|
|
282
294
|
"react/jsx-dev-runtime",
|
|
283
|
-
"rsc-html-stream/client",
|
|
295
|
+
nested("rsc-html-stream/client"),
|
|
284
296
|
],
|
|
285
297
|
exclude: excludeDeps,
|
|
286
298
|
esbuildOptions: sharedEsbuildOptions,
|
|
@@ -297,7 +309,9 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
297
309
|
"react-dom/static.edge",
|
|
298
310
|
"react/jsx-runtime",
|
|
299
311
|
"react/jsx-dev-runtime",
|
|
300
|
-
|
|
312
|
+
nested(
|
|
313
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/client.edge",
|
|
314
|
+
),
|
|
301
315
|
],
|
|
302
316
|
exclude: excludeDeps,
|
|
303
317
|
esbuildOptions: sharedEsbuildOptions,
|
|
@@ -310,7 +324,9 @@ export async function rango(options?: RangoOptions): Promise<PluginOption[]> {
|
|
|
310
324
|
"react",
|
|
311
325
|
"react/jsx-runtime",
|
|
312
326
|
"react/jsx-dev-runtime",
|
|
313
|
-
|
|
327
|
+
nested(
|
|
328
|
+
"@vitejs/plugin-rsc/vendor/react-server-dom/server.edge",
|
|
329
|
+
),
|
|
314
330
|
],
|
|
315
331
|
esbuildOptions: sharedEsbuildOptions,
|
|
316
332
|
},
|
package/src/vite/utils/banner.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
9
|
import { resolve } from "node:path";
|
|
10
|
-
import packageJson from "../../../package.json"
|
|
10
|
+
import packageJson from "../../../package.json";
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* The canonical name used in virtual entries (without scope)
|