@ivogt/rsc-router 0.0.0-experimental.1
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 +19 -0
- package/package.json +131 -0
- package/src/__mocks__/version.ts +6 -0
- package/src/__tests__/route-definition.test.ts +63 -0
- package/src/browser/event-controller.ts +876 -0
- package/src/browser/index.ts +18 -0
- package/src/browser/link-interceptor.ts +121 -0
- package/src/browser/lru-cache.ts +69 -0
- package/src/browser/merge-segment-loaders.ts +126 -0
- package/src/browser/navigation-bridge.ts +891 -0
- package/src/browser/navigation-client.ts +155 -0
- package/src/browser/navigation-store.ts +823 -0
- package/src/browser/partial-update.ts +545 -0
- package/src/browser/react/Link.tsx +248 -0
- package/src/browser/react/NavigationProvider.tsx +228 -0
- package/src/browser/react/ScrollRestoration.tsx +94 -0
- package/src/browser/react/context.ts +53 -0
- package/src/browser/react/index.ts +52 -0
- package/src/browser/react/location-state-shared.ts +120 -0
- package/src/browser/react/location-state.ts +62 -0
- package/src/browser/react/use-action.ts +240 -0
- package/src/browser/react/use-client-cache.ts +56 -0
- package/src/browser/react/use-handle.ts +178 -0
- package/src/browser/react/use-link-status.ts +134 -0
- package/src/browser/react/use-navigation.ts +150 -0
- package/src/browser/react/use-segments.ts +188 -0
- package/src/browser/request-controller.ts +149 -0
- package/src/browser/rsc-router.tsx +310 -0
- package/src/browser/scroll-restoration.ts +324 -0
- package/src/browser/server-action-bridge.ts +747 -0
- package/src/browser/shallow.ts +35 -0
- package/src/browser/types.ts +443 -0
- package/src/cache/__tests__/memory-segment-store.test.ts +487 -0
- package/src/cache/__tests__/memory-store.test.ts +484 -0
- package/src/cache/cache-scope.ts +565 -0
- package/src/cache/cf/__tests__/cf-cache-store.test.ts +361 -0
- package/src/cache/cf/cf-cache-store.ts +274 -0
- package/src/cache/cf/index.ts +19 -0
- package/src/cache/index.ts +52 -0
- package/src/cache/memory-segment-store.ts +150 -0
- package/src/cache/memory-store.ts +253 -0
- package/src/cache/types.ts +366 -0
- package/src/client.rsc.tsx +88 -0
- package/src/client.tsx +609 -0
- package/src/components/DefaultDocument.tsx +20 -0
- package/src/default-error-boundary.tsx +88 -0
- package/src/deps/browser.ts +8 -0
- package/src/deps/html-stream-client.ts +2 -0
- package/src/deps/html-stream-server.ts +2 -0
- package/src/deps/rsc.ts +10 -0
- package/src/deps/ssr.ts +2 -0
- package/src/errors.ts +259 -0
- package/src/handle.ts +120 -0
- package/src/handles/MetaTags.tsx +178 -0
- package/src/handles/index.ts +6 -0
- package/src/handles/meta.ts +247 -0
- package/src/href-client.ts +128 -0
- package/src/href.ts +139 -0
- package/src/index.rsc.ts +69 -0
- package/src/index.ts +84 -0
- package/src/loader.rsc.ts +204 -0
- package/src/loader.ts +47 -0
- package/src/network-error-thrower.tsx +21 -0
- package/src/outlet-context.ts +15 -0
- package/src/root-error-boundary.tsx +277 -0
- package/src/route-content-wrapper.tsx +198 -0
- package/src/route-definition.ts +1333 -0
- package/src/route-map-builder.ts +140 -0
- package/src/route-types.ts +148 -0
- package/src/route-utils.ts +89 -0
- package/src/router/__tests__/match-context.test.ts +104 -0
- package/src/router/__tests__/match-pipelines.test.ts +537 -0
- package/src/router/__tests__/match-result.test.ts +566 -0
- package/src/router/__tests__/on-error.test.ts +935 -0
- package/src/router/__tests__/pattern-matching.test.ts +577 -0
- package/src/router/error-handling.ts +287 -0
- package/src/router/handler-context.ts +60 -0
- package/src/router/loader-resolution.ts +326 -0
- package/src/router/manifest.ts +116 -0
- package/src/router/match-context.ts +261 -0
- package/src/router/match-middleware/background-revalidation.ts +236 -0
- package/src/router/match-middleware/cache-lookup.ts +261 -0
- package/src/router/match-middleware/cache-store.ts +250 -0
- package/src/router/match-middleware/index.ts +81 -0
- package/src/router/match-middleware/intercept-resolution.ts +268 -0
- package/src/router/match-middleware/segment-resolution.ts +174 -0
- package/src/router/match-pipelines.ts +214 -0
- package/src/router/match-result.ts +212 -0
- package/src/router/metrics.ts +62 -0
- package/src/router/middleware.test.ts +1355 -0
- package/src/router/middleware.ts +748 -0
- package/src/router/pattern-matching.ts +271 -0
- package/src/router/revalidation.ts +190 -0
- package/src/router/router-context.ts +299 -0
- package/src/router/types.ts +96 -0
- package/src/router.ts +3484 -0
- package/src/rsc/__tests__/helpers.test.ts +175 -0
- package/src/rsc/handler.ts +942 -0
- package/src/rsc/helpers.ts +64 -0
- package/src/rsc/index.ts +56 -0
- package/src/rsc/nonce.ts +18 -0
- package/src/rsc/types.ts +225 -0
- package/src/segment-system.tsx +405 -0
- package/src/server/__tests__/request-context.test.ts +171 -0
- package/src/server/context.ts +340 -0
- package/src/server/handle-store.ts +230 -0
- package/src/server/loader-registry.ts +174 -0
- package/src/server/request-context.ts +470 -0
- package/src/server/root-layout.tsx +10 -0
- package/src/server/tsconfig.json +14 -0
- package/src/server.ts +126 -0
- package/src/ssr/__tests__/ssr-handler.test.tsx +188 -0
- package/src/ssr/index.tsx +215 -0
- package/src/types.ts +1473 -0
- package/src/use-loader.tsx +346 -0
- package/src/vite/__tests__/expose-loader-id.test.ts +117 -0
- package/src/vite/expose-action-id.ts +344 -0
- package/src/vite/expose-handle-id.ts +209 -0
- package/src/vite/expose-loader-id.ts +357 -0
- package/src/vite/expose-location-state-id.ts +177 -0
- package/src/vite/index.ts +608 -0
- package/src/vite/version.d.ts +12 -0
- package/src/vite/virtual-entries.ts +109 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
NavigationClient,
|
|
3
|
+
FetchPartialOptions,
|
|
4
|
+
FetchPartialResult,
|
|
5
|
+
RscPayload,
|
|
6
|
+
RscBrowserDependencies,
|
|
7
|
+
} from "./types.js";
|
|
8
|
+
import { NetworkError, isNetworkError } from "../errors.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a navigation client for fetching RSC payloads
|
|
12
|
+
*
|
|
13
|
+
* The client handles building URLs with RSC parameters and
|
|
14
|
+
* deserializing the response using the RSC runtime.
|
|
15
|
+
*
|
|
16
|
+
* @param deps - RSC browser dependencies (createFromFetch)
|
|
17
|
+
* @returns NavigationClient instance
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { createFromFetch } from "@vitejs/plugin-rsc/browser";
|
|
22
|
+
*
|
|
23
|
+
* const client = createNavigationClient({ createFromFetch });
|
|
24
|
+
*
|
|
25
|
+
* const payload = await client.fetchPartial({
|
|
26
|
+
* targetUrl: "/shop/products",
|
|
27
|
+
* segmentIds: ["root", "shop"],
|
|
28
|
+
* previousUrl: "/",
|
|
29
|
+
* });
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
32
|
+
export function createNavigationClient(
|
|
33
|
+
deps: Pick<RscBrowserDependencies, "createFromFetch">
|
|
34
|
+
): NavigationClient {
|
|
35
|
+
return {
|
|
36
|
+
/**
|
|
37
|
+
* Fetch a partial RSC payload for navigation
|
|
38
|
+
*
|
|
39
|
+
* Sends current segment IDs to the server so it can determine
|
|
40
|
+
* which segments need to be re-rendered (diff).
|
|
41
|
+
*
|
|
42
|
+
* @param options - Fetch options
|
|
43
|
+
* @returns RSC payload with segments and metadata, plus stream completion promise
|
|
44
|
+
*/
|
|
45
|
+
async fetchPartial(
|
|
46
|
+
options: FetchPartialOptions
|
|
47
|
+
): Promise<FetchPartialResult> {
|
|
48
|
+
const {
|
|
49
|
+
targetUrl,
|
|
50
|
+
segmentIds,
|
|
51
|
+
previousUrl,
|
|
52
|
+
signal,
|
|
53
|
+
staleRevalidation,
|
|
54
|
+
interceptSourceUrl,
|
|
55
|
+
version,
|
|
56
|
+
} = options;
|
|
57
|
+
|
|
58
|
+
console.log(`\n[Browser] >>> NAVIGATION`);
|
|
59
|
+
console.log(`[Browser] From: ${previousUrl}`);
|
|
60
|
+
console.log(`[Browser] To: ${targetUrl}`);
|
|
61
|
+
console.log(`[Browser] Segments to send: ${segmentIds.join(", ")}`);
|
|
62
|
+
if (staleRevalidation) {
|
|
63
|
+
console.log(`[Browser] Stale revalidation request`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Build fetch URL with partial rendering params
|
|
67
|
+
const fetchUrl = new URL(targetUrl, window.location.origin);
|
|
68
|
+
fetchUrl.searchParams.set("_rsc_partial", "true");
|
|
69
|
+
fetchUrl.searchParams.set("_rsc_segments", segmentIds.join(","));
|
|
70
|
+
if (staleRevalidation) {
|
|
71
|
+
fetchUrl.searchParams.set("_rsc_stale", "true");
|
|
72
|
+
}
|
|
73
|
+
if (version) {
|
|
74
|
+
fetchUrl.searchParams.set("_rsc_v", version);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(`[Browser] Fetching: ${fetchUrl.pathname}${fetchUrl.search}`);
|
|
78
|
+
|
|
79
|
+
// Track when the stream completes
|
|
80
|
+
let resolveStreamComplete: () => void;
|
|
81
|
+
const streamComplete = new Promise<void>((resolve) => {
|
|
82
|
+
resolveStreamComplete = resolve;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Create a response promise that tracks stream completion
|
|
86
|
+
const responsePromise = fetch(fetchUrl, {
|
|
87
|
+
headers: {
|
|
88
|
+
"X-RSC-Router-Client-Path": previousUrl,
|
|
89
|
+
...(interceptSourceUrl && {
|
|
90
|
+
"X-RSC-Router-Intercept-Source": interceptSourceUrl,
|
|
91
|
+
}),
|
|
92
|
+
},
|
|
93
|
+
signal,
|
|
94
|
+
}).then((response) => {
|
|
95
|
+
// Check for version mismatch - server wants us to reload
|
|
96
|
+
const reloadUrl = response.headers.get("X-RSC-Reload");
|
|
97
|
+
if (reloadUrl) {
|
|
98
|
+
console.log(`[Browser] Version mismatch - reloading: ${reloadUrl}`);
|
|
99
|
+
window.location.href = reloadUrl;
|
|
100
|
+
// Return a never-resolving promise to prevent further processing
|
|
101
|
+
return new Promise<Response>(() => {});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!response.body) {
|
|
105
|
+
// No body means stream is already complete
|
|
106
|
+
resolveStreamComplete();
|
|
107
|
+
return response;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Tee the stream: one for RSC runtime, one for tracking completion
|
|
111
|
+
const [rscStream, trackingStream] = response.body.tee();
|
|
112
|
+
|
|
113
|
+
// Consume the tracking stream to detect when it closes
|
|
114
|
+
(async () => {
|
|
115
|
+
const reader = trackingStream.getReader();
|
|
116
|
+
try {
|
|
117
|
+
while (true) {
|
|
118
|
+
const { done } = await reader.read();
|
|
119
|
+
if (done) break;
|
|
120
|
+
}
|
|
121
|
+
} finally {
|
|
122
|
+
console.log("[STREAMING] RSC stream complete");
|
|
123
|
+
resolveStreamComplete();
|
|
124
|
+
}
|
|
125
|
+
})();
|
|
126
|
+
|
|
127
|
+
// Return response with the RSC stream
|
|
128
|
+
return new Response(rscStream, {
|
|
129
|
+
headers: response.headers,
|
|
130
|
+
status: response.status,
|
|
131
|
+
statusText: response.statusText,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
// Deserialize RSC payload
|
|
137
|
+
const payload = await deps.createFromFetch<RscPayload>(responsePromise);
|
|
138
|
+
return { payload, streamComplete };
|
|
139
|
+
} catch (error) {
|
|
140
|
+
// Convert network-level errors to NetworkError for proper handling
|
|
141
|
+
if (isNetworkError(error)) {
|
|
142
|
+
throw new NetworkError(
|
|
143
|
+
"Unable to connect to server. Please check your connection.",
|
|
144
|
+
{
|
|
145
|
+
cause: error,
|
|
146
|
+
url: fetchUrl.toString(),
|
|
147
|
+
operation: staleRevalidation ? "revalidation" : "navigation",
|
|
148
|
+
}
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|