@rangojs/router 0.0.0-experimental.10

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.
Files changed (172) hide show
  1. package/CLAUDE.md +43 -0
  2. package/README.md +19 -0
  3. package/dist/bin/rango.js +227 -0
  4. package/dist/vite/index.js +3039 -0
  5. package/package.json +171 -0
  6. package/skills/caching/SKILL.md +191 -0
  7. package/skills/debug-manifest/SKILL.md +108 -0
  8. package/skills/document-cache/SKILL.md +180 -0
  9. package/skills/fonts/SKILL.md +165 -0
  10. package/skills/hooks/SKILL.md +442 -0
  11. package/skills/intercept/SKILL.md +190 -0
  12. package/skills/layout/SKILL.md +213 -0
  13. package/skills/links/SKILL.md +180 -0
  14. package/skills/loader/SKILL.md +246 -0
  15. package/skills/middleware/SKILL.md +202 -0
  16. package/skills/mime-routes/SKILL.md +124 -0
  17. package/skills/parallel/SKILL.md +228 -0
  18. package/skills/prerender/SKILL.md +283 -0
  19. package/skills/rango/SKILL.md +54 -0
  20. package/skills/response-routes/SKILL.md +358 -0
  21. package/skills/route/SKILL.md +173 -0
  22. package/skills/router-setup/SKILL.md +346 -0
  23. package/skills/tailwind/SKILL.md +129 -0
  24. package/skills/theme/SKILL.md +78 -0
  25. package/skills/typesafety/SKILL.md +394 -0
  26. package/src/__internal.ts +175 -0
  27. package/src/bin/rango.ts +24 -0
  28. package/src/browser/event-controller.ts +876 -0
  29. package/src/browser/index.ts +18 -0
  30. package/src/browser/link-interceptor.ts +121 -0
  31. package/src/browser/lru-cache.ts +69 -0
  32. package/src/browser/merge-segment-loaders.ts +126 -0
  33. package/src/browser/navigation-bridge.ts +913 -0
  34. package/src/browser/navigation-client.ts +165 -0
  35. package/src/browser/navigation-store.ts +823 -0
  36. package/src/browser/partial-update.ts +600 -0
  37. package/src/browser/react/Link.tsx +248 -0
  38. package/src/browser/react/NavigationProvider.tsx +346 -0
  39. package/src/browser/react/ScrollRestoration.tsx +94 -0
  40. package/src/browser/react/context.ts +53 -0
  41. package/src/browser/react/index.ts +52 -0
  42. package/src/browser/react/location-state-shared.ts +120 -0
  43. package/src/browser/react/location-state.ts +62 -0
  44. package/src/browser/react/mount-context.ts +32 -0
  45. package/src/browser/react/use-action.ts +240 -0
  46. package/src/browser/react/use-client-cache.ts +56 -0
  47. package/src/browser/react/use-handle.ts +203 -0
  48. package/src/browser/react/use-href.tsx +40 -0
  49. package/src/browser/react/use-link-status.ts +134 -0
  50. package/src/browser/react/use-mount.ts +31 -0
  51. package/src/browser/react/use-navigation.ts +140 -0
  52. package/src/browser/react/use-segments.ts +188 -0
  53. package/src/browser/request-controller.ts +164 -0
  54. package/src/browser/rsc-router.tsx +352 -0
  55. package/src/browser/scroll-restoration.ts +324 -0
  56. package/src/browser/segment-structure-assert.ts +67 -0
  57. package/src/browser/server-action-bridge.ts +762 -0
  58. package/src/browser/shallow.ts +35 -0
  59. package/src/browser/types.ts +478 -0
  60. package/src/build/generate-manifest.ts +377 -0
  61. package/src/build/generate-route-types.ts +828 -0
  62. package/src/build/index.ts +36 -0
  63. package/src/build/route-trie.ts +239 -0
  64. package/src/cache/cache-scope.ts +563 -0
  65. package/src/cache/cf/cf-cache-store.ts +428 -0
  66. package/src/cache/cf/index.ts +19 -0
  67. package/src/cache/document-cache.ts +340 -0
  68. package/src/cache/index.ts +58 -0
  69. package/src/cache/memory-segment-store.ts +150 -0
  70. package/src/cache/memory-store.ts +253 -0
  71. package/src/cache/types.ts +392 -0
  72. package/src/client.rsc.tsx +83 -0
  73. package/src/client.tsx +643 -0
  74. package/src/component-utils.ts +76 -0
  75. package/src/components/DefaultDocument.tsx +23 -0
  76. package/src/debug.ts +233 -0
  77. package/src/default-error-boundary.tsx +88 -0
  78. package/src/deps/browser.ts +8 -0
  79. package/src/deps/html-stream-client.ts +2 -0
  80. package/src/deps/html-stream-server.ts +2 -0
  81. package/src/deps/rsc.ts +10 -0
  82. package/src/deps/ssr.ts +2 -0
  83. package/src/errors.ts +295 -0
  84. package/src/handle.ts +130 -0
  85. package/src/handles/MetaTags.tsx +193 -0
  86. package/src/handles/index.ts +6 -0
  87. package/src/handles/meta.ts +247 -0
  88. package/src/host/cookie-handler.ts +159 -0
  89. package/src/host/errors.ts +97 -0
  90. package/src/host/index.ts +56 -0
  91. package/src/host/pattern-matcher.ts +214 -0
  92. package/src/host/router.ts +330 -0
  93. package/src/host/testing.ts +79 -0
  94. package/src/host/types.ts +138 -0
  95. package/src/host/utils.ts +25 -0
  96. package/src/href-client.ts +202 -0
  97. package/src/href-context.ts +33 -0
  98. package/src/index.rsc.ts +121 -0
  99. package/src/index.ts +165 -0
  100. package/src/loader.rsc.ts +207 -0
  101. package/src/loader.ts +47 -0
  102. package/src/network-error-thrower.tsx +21 -0
  103. package/src/outlet-context.ts +15 -0
  104. package/src/prerender/param-hash.ts +35 -0
  105. package/src/prerender/store.ts +40 -0
  106. package/src/prerender.ts +156 -0
  107. package/src/reverse.ts +267 -0
  108. package/src/root-error-boundary.tsx +277 -0
  109. package/src/route-content-wrapper.tsx +193 -0
  110. package/src/route-definition.ts +1431 -0
  111. package/src/route-map-builder.ts +242 -0
  112. package/src/route-types.ts +220 -0
  113. package/src/router/error-handling.ts +287 -0
  114. package/src/router/handler-context.ts +158 -0
  115. package/src/router/intercept-resolution.ts +387 -0
  116. package/src/router/loader-resolution.ts +327 -0
  117. package/src/router/manifest.ts +216 -0
  118. package/src/router/match-api.ts +621 -0
  119. package/src/router/match-context.ts +264 -0
  120. package/src/router/match-middleware/background-revalidation.ts +236 -0
  121. package/src/router/match-middleware/cache-lookup.ts +382 -0
  122. package/src/router/match-middleware/cache-store.ts +276 -0
  123. package/src/router/match-middleware/index.ts +81 -0
  124. package/src/router/match-middleware/intercept-resolution.ts +281 -0
  125. package/src/router/match-middleware/segment-resolution.ts +184 -0
  126. package/src/router/match-pipelines.ts +214 -0
  127. package/src/router/match-result.ts +213 -0
  128. package/src/router/metrics.ts +62 -0
  129. package/src/router/middleware.ts +791 -0
  130. package/src/router/pattern-matching.ts +407 -0
  131. package/src/router/revalidation.ts +190 -0
  132. package/src/router/router-context.ts +301 -0
  133. package/src/router/segment-resolution.ts +1315 -0
  134. package/src/router/trie-matching.ts +172 -0
  135. package/src/router/types.ts +163 -0
  136. package/src/router.gen.ts +6 -0
  137. package/src/router.ts +2423 -0
  138. package/src/rsc/handler.ts +1443 -0
  139. package/src/rsc/helpers.ts +64 -0
  140. package/src/rsc/index.ts +56 -0
  141. package/src/rsc/nonce.ts +18 -0
  142. package/src/rsc/types.ts +236 -0
  143. package/src/segment-system.tsx +442 -0
  144. package/src/server/context.ts +466 -0
  145. package/src/server/handle-store.ts +229 -0
  146. package/src/server/loader-registry.ts +174 -0
  147. package/src/server/request-context.ts +554 -0
  148. package/src/server/root-layout.tsx +10 -0
  149. package/src/server/tsconfig.json +14 -0
  150. package/src/server.ts +171 -0
  151. package/src/ssr/index.tsx +296 -0
  152. package/src/theme/ThemeProvider.tsx +291 -0
  153. package/src/theme/ThemeScript.tsx +61 -0
  154. package/src/theme/constants.ts +59 -0
  155. package/src/theme/index.ts +58 -0
  156. package/src/theme/theme-context.ts +70 -0
  157. package/src/theme/theme-script.ts +152 -0
  158. package/src/theme/types.ts +182 -0
  159. package/src/theme/use-theme.ts +44 -0
  160. package/src/types.ts +1757 -0
  161. package/src/urls.gen.ts +8 -0
  162. package/src/urls.ts +1282 -0
  163. package/src/use-loader.tsx +346 -0
  164. package/src/vite/expose-action-id.ts +344 -0
  165. package/src/vite/expose-handle-id.ts +209 -0
  166. package/src/vite/expose-loader-id.ts +426 -0
  167. package/src/vite/expose-location-state-id.ts +177 -0
  168. package/src/vite/expose-prerender-handler-id.ts +429 -0
  169. package/src/vite/index.ts +2068 -0
  170. package/src/vite/package-resolution.ts +125 -0
  171. package/src/vite/version.d.ts +12 -0
  172. package/src/vite/virtual-entries.ts +114 -0
package/src/server.ts ADDED
@@ -0,0 +1,171 @@
1
+ /**
2
+ * rsc-router/server
3
+ *
4
+ * Server-only exports for route definition and building
5
+ * These should only be imported in server-side handler files
6
+ */
7
+
8
+ // Route definition helpers (server-only)
9
+ export {
10
+ createLoader,
11
+ redirect,
12
+ type RouteHelpers,
13
+ type RouteHandlers,
14
+ } from "./route-definition.js";
15
+
16
+ // Django-style URL patterns (server-only)
17
+ export {
18
+ urls,
19
+ RESPONSE_TYPE,
20
+ type PathHelpers,
21
+ type PathOptions,
22
+ type UrlPatterns,
23
+ type IncludeOptions,
24
+ type ResponseHandler,
25
+ type ResponseHandlerContext,
26
+ type JsonResponseHandler,
27
+ type TextResponseHandler,
28
+ type JsonValue,
29
+ type ResponsePathFn,
30
+ type JsonResponsePathFn,
31
+ type TextResponsePathFn,
32
+ type RouteResponse,
33
+ type ResponseError,
34
+ type ResponseEnvelope,
35
+ } from "./urls.js";
36
+
37
+ // Re-export IncludeItem from route-types
38
+ export type { IncludeItem } from "./route-types.js";
39
+
40
+ // Core router (server-only)
41
+ export {
42
+ createRouter,
43
+ RSC_ROUTER_BRAND,
44
+ RouterRegistry,
45
+ type RSCRouter,
46
+ type RSCRouterOptions,
47
+ type RootLayoutProps,
48
+ } from "./router.js";
49
+
50
+ // Type-safe reverse utilities (Django-style URL reversal)
51
+ export {
52
+ createReverse,
53
+ type ReverseFunction,
54
+ type PrefixedRoutes,
55
+ type PrefixRoutePatterns,
56
+ type ParamsFor,
57
+ type SanitizePrefix,
58
+ type MergeRoutes,
59
+ } from "./reverse.js";
60
+
61
+ // Segment system (server-only)
62
+ export { renderSegments } from "./segment-system.js";
63
+
64
+ // Performance tracking (server-only)
65
+ export { track } from "./server/context.js";
66
+
67
+ // Handle API (works in both server and client contexts)
68
+ export { createHandle, isHandle, type Handle } from "./handle.js";
69
+
70
+ // Pre-render handler API
71
+ export {
72
+ createPrerenderHandler,
73
+ isPrerenderHandler,
74
+ type PrerenderHandlerDefinition,
75
+ type PrerenderOptions,
76
+ type BuildContext,
77
+ } from "./prerender.js";
78
+
79
+ // Built-in handles
80
+ export { Meta } from "./handles/meta.js";
81
+
82
+ // Loader registry (for GET-based loader fetching)
83
+ export { registerLoaderById, setLoaderImports } from "./server/loader-registry.js";
84
+
85
+ // Route map builder (for build-time manifest registration)
86
+ export { registerRouteMap, setCachedManifest, setPrecomputedEntries, setRouteTrie, setManifestReadyPromise } from "./route-map-builder.js";
87
+
88
+ // Request context (for accessing request data in server components/actions)
89
+ export {
90
+ getRequestContext,
91
+ requireRequestContext,
92
+ createRequestContext,
93
+ type RequestContext,
94
+ type CreateRequestContextOptions,
95
+ } from "./server/request-context.js";
96
+
97
+ // Meta types
98
+ export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
99
+
100
+ // Middleware context types (Middleware type is exported from types.ts)
101
+ export type {
102
+ MiddlewareContext,
103
+ CookieOptions,
104
+ } from "./router/middleware.js";
105
+
106
+ // Error classes and utilities
107
+ export {
108
+ RouteNotFoundError,
109
+ DataNotFoundError,
110
+ notFound,
111
+ MiddlewareError,
112
+ HandlerError,
113
+ BuildError,
114
+ InvalidHandlerError,
115
+ sanitizeError,
116
+ RouterError,
117
+ } from "./errors.js";
118
+
119
+ // Component utilities
120
+ export {
121
+ isClientComponent,
122
+ assertClientComponent,
123
+ } from "./component-utils.js";
124
+
125
+ // Debug utilities for route matching (development only)
126
+ export {
127
+ enableMatchDebug,
128
+ getMatchDebugStats,
129
+ } from "./router/pattern-matching.js";
130
+
131
+ // Types (re-exported for convenience - user-facing only)
132
+ export type {
133
+ // Configuration types
134
+ RouterEnv,
135
+ DefaultEnv,
136
+ RouteDefinition,
137
+ RouteConfig,
138
+ RouteDefinitionOptions,
139
+ TrailingSlashMode,
140
+ // Handler types
141
+ Handler, // Supports params object, path pattern, or route name
142
+ HandlerContext,
143
+ ExtractParams,
144
+ GenericParams,
145
+ // Middleware types (also exported from router/middleware.js above)
146
+ Middleware, // Supports env type and optional route name for params
147
+ // Revalidation types
148
+ RevalidateParams,
149
+ Revalidate,
150
+ RouteKeys,
151
+ // Loader types
152
+ LoaderDefinition,
153
+ LoaderFn,
154
+ LoaderContext,
155
+ // Error boundary types
156
+ ErrorInfo,
157
+ ErrorBoundaryFallbackProps,
158
+ ErrorBoundaryHandler,
159
+ ClientErrorBoundaryFallbackProps,
160
+ // NotFound boundary types
161
+ NotFoundInfo,
162
+ NotFoundBoundaryFallbackProps,
163
+ NotFoundBoundaryHandler,
164
+ // Error handling callback types
165
+ ErrorPhase,
166
+ OnErrorContext,
167
+ OnErrorCallback,
168
+ } from "./types.js";
169
+
170
+ // Path-based response type lookup from RegisteredRoutes
171
+ export type { PathResponse } from "./href-client.js";
@@ -0,0 +1,296 @@
1
+ import React from "react";
2
+ import { initHandleDataSync } from "../browser/react/use-handle.js";
3
+ import { initSegmentsSync } from "../browser/react/use-segments.js";
4
+ import { initThemeConfigSync } from "../theme/theme-context.js";
5
+ import { ThemeProvider } from "../theme/ThemeProvider.js";
6
+ import { NavigationStoreContext } from "../browser/react/context.js";
7
+ import type { NavigationStoreContextValue } from "../browser/react/context.js";
8
+ import type { HandleData } from "../browser/types.js";
9
+ import type { ErrorPhase } from "../types.js";
10
+ import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
11
+ import type { EventController, DerivedNavigationState } from "../browser/event-controller.js";
12
+
13
+ /**
14
+ * Options for injectRSCPayload
15
+ */
16
+ export interface InjectRSCPayloadOptions {
17
+ /**
18
+ * Nonce for Content Security Policy (CSP)
19
+ */
20
+ nonce?: string;
21
+ }
22
+
23
+ /**
24
+ * Options for renderToReadableStream from react-dom/server
25
+ */
26
+ interface RenderToReadableStreamOptions {
27
+ bootstrapScriptContent?: string;
28
+ nonce?: string;
29
+ formState?: unknown;
30
+ }
31
+
32
+ /**
33
+ * Options for the renderHTML function
34
+ */
35
+ export interface SSRRenderOptions {
36
+ /**
37
+ * Form state for useActionState progressive enhancement.
38
+ * This is the result of decodeFormState() and should be passed to
39
+ * react-dom's renderToReadableStream to enable useActionState to
40
+ * receive the action result during SSR.
41
+ */
42
+ formState?: unknown;
43
+
44
+ /**
45
+ * Nonce for Content Security Policy (CSP)
46
+ */
47
+ nonce?: string;
48
+ }
49
+
50
+ /**
51
+ * SSR dependencies from external packages
52
+ */
53
+ export interface SSRDependencies<TEnv = unknown> {
54
+ /**
55
+ * createFromReadableStream from @vitejs/plugin-rsc/ssr
56
+ */
57
+ createFromReadableStream: <T>(stream: ReadableStream<Uint8Array>) => Promise<T>;
58
+
59
+ /**
60
+ * renderToReadableStream from react-dom/server.edge
61
+ */
62
+ renderToReadableStream: (
63
+ element: React.ReactNode,
64
+ options?: RenderToReadableStreamOptions
65
+ ) => Promise<ReadableStream<Uint8Array>>;
66
+
67
+ /**
68
+ * injectRSCPayload from rsc-html-stream/server
69
+ */
70
+ injectRSCPayload: (
71
+ rscStream: ReadableStream<Uint8Array>,
72
+ options?: InjectRSCPayloadOptions
73
+ ) => TransformStream<Uint8Array, Uint8Array>;
74
+
75
+ /**
76
+ * Function to load bootstrap script content
77
+ * Typically: () => import.meta.viteRsc.loadBootstrapScriptContent("index")
78
+ */
79
+ loadBootstrapScriptContent: () => Promise<string>;
80
+
81
+ /**
82
+ * Optional callback invoked when an error occurs during SSR rendering.
83
+ *
84
+ * This callback is for notification/logging purposes.
85
+ *
86
+ * @example
87
+ * ```typescript
88
+ * export const renderHTML = createSSRHandler({
89
+ * // ... other deps
90
+ * onError: (error, context) => {
91
+ * console.error('[SSR] Rendering error:', error);
92
+ * Sentry.captureException(error);
93
+ * },
94
+ * });
95
+ * ```
96
+ */
97
+ onError?: (error: Error, context: { phase: ErrorPhase }) => void;
98
+ }
99
+
100
+ /**
101
+ * RSC payload type (minimal interface for SSR)
102
+ */
103
+ interface RscPayload {
104
+ root: React.ReactNode;
105
+ metadata?: {
106
+ handles?: AsyncGenerator<HandleData, void, unknown>;
107
+ matched?: string[];
108
+ pathname?: string;
109
+ themeConfig?: ResolvedThemeConfig | null;
110
+ initialTheme?: Theme;
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Consume an async generator and return a Promise that resolves with the final value.
116
+ * Used for SSR where we need to await all handle data before rendering.
117
+ */
118
+ async function consumeAsyncGenerator(
119
+ generator: AsyncGenerator<HandleData, void, unknown>
120
+ ): Promise<HandleData> {
121
+ let lastData: HandleData = {};
122
+ for await (const data of generator) {
123
+ lastData = data;
124
+ }
125
+ return lastData;
126
+ }
127
+
128
+ /**
129
+ * Create a minimal event controller for SSR.
130
+ * This provides the correct pathname so useNavigation returns the right value during SSR.
131
+ */
132
+ function createSsrEventController(pathname: string): EventController {
133
+ const location = new URL(pathname, "http://localhost");
134
+ const state: DerivedNavigationState = {
135
+ state: "idle",
136
+ isStreaming: false,
137
+ location,
138
+ pendingUrl: null,
139
+ inflightActions: [],
140
+ };
141
+
142
+ return {
143
+ getState: () => state,
144
+ subscribe: () => () => {},
145
+ getActionState: () => ({
146
+ state: "idle",
147
+ actionId: null,
148
+ payload: null,
149
+ error: null,
150
+ result: null,
151
+ }),
152
+ subscribeToAction: () => () => {},
153
+ subscribeToHandles: () => () => {},
154
+ setHandleData: () => {},
155
+ getHandleState: () => ({ data: {}, segmentOrder: [] }),
156
+ setLocation: () => {},
157
+ startNavigation: () => {
158
+ throw new Error("Navigation not supported during SSR");
159
+ },
160
+ abortNavigation: () => {},
161
+ startAction: () => {
162
+ throw new Error("Actions not supported during SSR");
163
+ },
164
+ abortAllActions: () => {},
165
+ getCurrentNavigation: () => null,
166
+ getInflightActions: () => new Map(),
167
+ };
168
+ }
169
+
170
+ /**
171
+ * Create an SSR handler that converts RSC streams to HTML.
172
+ *
173
+ * @example
174
+ * ```tsx
175
+ * import { createSSRHandler } from "rsc-router/ssr";
176
+ * import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
177
+ * import { renderToReadableStream } from "react-dom/server.edge";
178
+ * import { injectRSCPayload } from "rsc-html-stream/server";
179
+ *
180
+ * export const renderHTML = createSSRHandler({
181
+ * createFromReadableStream,
182
+ * renderToReadableStream,
183
+ * injectRSCPayload,
184
+ * loadBootstrapScriptContent: () =>
185
+ * import.meta.viteRsc.loadBootstrapScriptContent("index"),
186
+ * });
187
+ * ```
188
+ */
189
+ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
190
+ const {
191
+ createFromReadableStream,
192
+ renderToReadableStream,
193
+ injectRSCPayload,
194
+ loadBootstrapScriptContent,
195
+ onError,
196
+ } = deps;
197
+
198
+ /**
199
+ * Render RSC stream to HTML stream
200
+ *
201
+ * @param rscStream - The RSC stream to render
202
+ * @param options - Optional render options including formState for useActionState and nonce for CSP
203
+ */
204
+ return async function renderHTML(
205
+ rscStream: ReadableStream<Uint8Array>,
206
+ options?: SSRRenderOptions
207
+ ): Promise<ReadableStream<Uint8Array>> {
208
+ const { nonce, formState } = options ?? {};
209
+
210
+ try {
211
+ // Tee the stream:
212
+ // - rscStream1: For SSR rendering (deserialize to React VDOM)
213
+ // - rscStream2: For browser hydration (inject as __FLIGHT_DATA__)
214
+ const [rscStream1, rscStream2] = rscStream.tee();
215
+
216
+ // Deserialize RSC stream to React tree
217
+ let payload: Promise<RscPayload> | undefined;
218
+ let handlesPromise: Promise<HandleData> | undefined;
219
+ let ssrContextValue: NavigationStoreContextValue | undefined;
220
+ function SsrRoot() {
221
+ payload ??= createFromReadableStream<RscPayload>(rscStream1);
222
+ const resolved = React.use(payload);
223
+
224
+ // Initialize segments state before children render (for useSegments hook)
225
+ initSegmentsSync(resolved.metadata?.matched, resolved.metadata?.pathname);
226
+
227
+ // Initialize theme config for MetaTags to render theme script
228
+ const themeConfig = resolved.metadata?.themeConfig ?? null;
229
+ initThemeConfigSync(themeConfig);
230
+
231
+ // Await handles and initialize state before children render
232
+ // The handles property is an async generator that yields on each push
233
+ // Memoize the promise since async generators can only be iterated once
234
+ if (resolved.metadata?.handles) {
235
+ handlesPromise ??= consumeAsyncGenerator(resolved.metadata.handles);
236
+ const handleData = React.use(handlesPromise);
237
+ initHandleDataSync(handleData, resolved.metadata.matched);
238
+ }
239
+
240
+ // Create SSR context with correct pathname for useNavigation
241
+ ssrContextValue ??= {
242
+ store: null as any,
243
+ eventController: createSsrEventController(resolved.metadata?.pathname ?? "/"),
244
+ navigate: async () => {},
245
+ refresh: async () => {},
246
+ };
247
+
248
+ // Build content tree with all necessary providers
249
+ // Order must match NavigationProvider: NavigationStoreContext > ThemeProvider > content
250
+ let content: React.ReactNode = resolved.root;
251
+
252
+ // Wrap content with ThemeProvider if theme is enabled
253
+ if (themeConfig) {
254
+ content = (
255
+ <ThemeProvider config={themeConfig} initialTheme={resolved.metadata?.initialTheme}>
256
+ {content}
257
+ </ThemeProvider>
258
+ );
259
+ }
260
+
261
+ // Wrap with NavigationStoreContext for useNavigation hook
262
+ return (
263
+ <NavigationStoreContext.Provider value={ssrContextValue}>
264
+ {content}
265
+ </NavigationStoreContext.Provider>
266
+ );
267
+ }
268
+
269
+ // Get bootstrap script content
270
+ const bootstrapScriptContent = await loadBootstrapScriptContent();
271
+
272
+ // Render React tree to HTML stream
273
+ // Pass formState for useActionState progressive enhancement if provided
274
+ // Pass nonce for CSP if provided
275
+ const htmlStream = await renderToReadableStream(<SsrRoot />, {
276
+ bootstrapScriptContent,
277
+ formState,
278
+ nonce,
279
+ });
280
+
281
+ // Inject RSC payload into HTML as <script nonce="...">__FLIGHT_DATA__</script>
282
+ return htmlStream.pipeThrough(injectRSCPayload(rscStream2, { nonce }));
283
+ } catch (error) {
284
+ // Invoke onError callback if provided
285
+ if (onError) {
286
+ const errorObj = error instanceof Error ? error : new Error(String(error));
287
+ try {
288
+ onError(errorObj, { phase: "rendering" });
289
+ } catch (callbackError) {
290
+ console.error("[SSRHandler.onError] Callback error:", callbackError);
291
+ }
292
+ }
293
+ throw error;
294
+ }
295
+ };
296
+ }