@rangojs/router 0.0.0-experimental.13 → 0.0.0-experimental.13221847

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 (298) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +884 -4
  3. package/dist/bin/rango.js +1531 -212
  4. package/dist/vite/index.js +3995 -2489
  5. package/package.json +57 -52
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +85 -23
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +12 -8
  11. package/skills/document-cache/SKILL.md +18 -16
  12. package/skills/fonts/SKILL.md +6 -4
  13. package/skills/hooks/SKILL.md +328 -70
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +131 -8
  16. package/skills/layout/SKILL.md +100 -3
  17. package/skills/links/SKILL.md +62 -15
  18. package/skills/loader/SKILL.md +368 -42
  19. package/skills/middleware/SKILL.md +171 -34
  20. package/skills/mime-routes/SKILL.md +14 -10
  21. package/skills/parallel/SKILL.md +137 -1
  22. package/skills/prerender/SKILL.md +366 -28
  23. package/skills/rango/SKILL.md +85 -21
  24. package/skills/response-routes/SKILL.md +136 -83
  25. package/skills/route/SKILL.md +195 -21
  26. package/skills/router-setup/SKILL.md +123 -30
  27. package/skills/theme/SKILL.md +9 -8
  28. package/skills/typesafety/SKILL.md +240 -102
  29. package/skills/use-cache/SKILL.md +324 -0
  30. package/src/__internal.ts +102 -4
  31. package/src/bin/rango.ts +312 -15
  32. package/src/browser/action-coordinator.ts +97 -0
  33. package/src/browser/action-response-classifier.ts +99 -0
  34. package/src/browser/event-controller.ts +92 -64
  35. package/src/browser/history-state.ts +80 -0
  36. package/src/browser/intercept-utils.ts +52 -0
  37. package/src/browser/link-interceptor.ts +24 -4
  38. package/src/browser/logging.ts +11 -0
  39. package/src/browser/merge-segment-loaders.ts +20 -12
  40. package/src/browser/navigation-bridge.ts +266 -558
  41. package/src/browser/navigation-client.ts +132 -75
  42. package/src/browser/navigation-store.ts +33 -50
  43. package/src/browser/navigation-transaction.ts +297 -0
  44. package/src/browser/network-error-handler.ts +61 -0
  45. package/src/browser/partial-update.ts +303 -309
  46. package/src/browser/prefetch/cache.ts +206 -0
  47. package/src/browser/prefetch/fetch.ts +144 -0
  48. package/src/browser/prefetch/observer.ts +65 -0
  49. package/src/browser/prefetch/policy.ts +48 -0
  50. package/src/browser/prefetch/queue.ts +128 -0
  51. package/src/browser/rango-state.ts +112 -0
  52. package/src/browser/react/Link.tsx +190 -70
  53. package/src/browser/react/NavigationProvider.tsx +78 -11
  54. package/src/browser/react/context.ts +6 -0
  55. package/src/browser/react/filter-segment-order.ts +11 -0
  56. package/src/browser/react/index.ts +12 -12
  57. package/src/browser/react/location-state-shared.ts +95 -53
  58. package/src/browser/react/location-state.ts +60 -15
  59. package/src/browser/react/mount-context.ts +6 -1
  60. package/src/browser/react/nonce-context.ts +23 -0
  61. package/src/browser/react/shallow-equal.ts +27 -0
  62. package/src/browser/react/use-action.ts +29 -51
  63. package/src/browser/react/use-client-cache.ts +5 -3
  64. package/src/browser/react/use-handle.ts +29 -70
  65. package/src/browser/react/use-link-status.ts +6 -5
  66. package/src/browser/react/use-navigation.ts +22 -63
  67. package/src/browser/react/use-params.ts +65 -0
  68. package/src/browser/react/use-pathname.ts +47 -0
  69. package/src/browser/react/use-router.ts +63 -0
  70. package/src/browser/react/use-search-params.ts +56 -0
  71. package/src/browser/react/use-segments.ts +80 -97
  72. package/src/browser/response-adapter.ts +73 -0
  73. package/src/browser/rsc-router.tsx +188 -57
  74. package/src/browser/scroll-restoration.ts +117 -44
  75. package/src/browser/segment-reconciler.ts +221 -0
  76. package/src/browser/segment-structure-assert.ts +16 -0
  77. package/src/browser/server-action-bridge.ts +488 -606
  78. package/src/browser/shallow.ts +6 -1
  79. package/src/browser/types.ts +116 -47
  80. package/src/browser/validate-redirect-origin.ts +29 -0
  81. package/src/build/generate-manifest.ts +63 -21
  82. package/src/build/generate-route-types.ts +36 -1038
  83. package/src/build/index.ts +2 -5
  84. package/src/build/route-trie.ts +38 -12
  85. package/src/build/route-types/ast-helpers.ts +25 -0
  86. package/src/build/route-types/ast-route-extraction.ts +98 -0
  87. package/src/build/route-types/codegen.ts +102 -0
  88. package/src/build/route-types/include-resolution.ts +411 -0
  89. package/src/build/route-types/param-extraction.ts +48 -0
  90. package/src/build/route-types/per-module-writer.ts +128 -0
  91. package/src/build/route-types/router-processing.ts +479 -0
  92. package/src/build/route-types/scan-filter.ts +78 -0
  93. package/src/build/runtime-discovery.ts +231 -0
  94. package/src/cache/background-task.ts +34 -0
  95. package/src/cache/cache-key-utils.ts +44 -0
  96. package/src/cache/cache-policy.ts +125 -0
  97. package/src/cache/cache-runtime.ts +342 -0
  98. package/src/cache/cache-scope.ts +122 -303
  99. package/src/cache/cf/cf-cache-store.ts +571 -17
  100. package/src/cache/cf/index.ts +13 -3
  101. package/src/cache/document-cache.ts +116 -77
  102. package/src/cache/handle-capture.ts +81 -0
  103. package/src/cache/handle-snapshot.ts +41 -0
  104. package/src/cache/index.ts +1 -15
  105. package/src/cache/memory-segment-store.ts +191 -13
  106. package/src/cache/profile-registry.ts +73 -0
  107. package/src/cache/read-through-swr.ts +134 -0
  108. package/src/cache/segment-codec.ts +256 -0
  109. package/src/cache/taint.ts +98 -0
  110. package/src/cache/types.ts +72 -122
  111. package/src/client.rsc.tsx +3 -1
  112. package/src/client.tsx +84 -126
  113. package/src/component-utils.ts +4 -4
  114. package/src/components/DefaultDocument.tsx +5 -1
  115. package/src/context-var.ts +86 -0
  116. package/src/debug.ts +19 -9
  117. package/src/errors.ts +77 -7
  118. package/src/handle.ts +12 -7
  119. package/src/handles/MetaTags.tsx +73 -20
  120. package/src/handles/breadcrumbs.ts +66 -0
  121. package/src/handles/index.ts +1 -0
  122. package/src/handles/meta.ts +30 -13
  123. package/src/host/cookie-handler.ts +21 -15
  124. package/src/host/errors.ts +8 -8
  125. package/src/host/index.ts +4 -7
  126. package/src/host/pattern-matcher.ts +27 -27
  127. package/src/host/router.ts +61 -39
  128. package/src/host/testing.ts +8 -8
  129. package/src/host/types.ts +15 -7
  130. package/src/host/utils.ts +1 -1
  131. package/src/href-client.ts +65 -45
  132. package/src/index.rsc.ts +104 -40
  133. package/src/index.ts +122 -67
  134. package/src/internal-debug.ts +9 -3
  135. package/src/loader.rsc.ts +18 -93
  136. package/src/loader.ts +26 -9
  137. package/src/network-error-thrower.tsx +3 -1
  138. package/src/outlet-provider.tsx +45 -0
  139. package/src/prerender/param-hash.ts +4 -2
  140. package/src/prerender/store.ts +121 -17
  141. package/src/prerender.ts +325 -20
  142. package/src/reverse.ts +144 -124
  143. package/src/root-error-boundary.tsx +41 -29
  144. package/src/route-content-wrapper.tsx +7 -4
  145. package/src/route-definition/dsl-helpers.ts +959 -0
  146. package/src/route-definition/helper-factories.ts +200 -0
  147. package/src/route-definition/helpers-types.ts +430 -0
  148. package/src/route-definition/index.ts +52 -0
  149. package/src/route-definition/redirect.ts +93 -0
  150. package/src/route-definition.ts +1 -1450
  151. package/src/route-map-builder.ts +87 -133
  152. package/src/route-name.ts +53 -0
  153. package/src/route-types.ts +41 -6
  154. package/src/router/content-negotiation.ts +116 -0
  155. package/src/router/debug-manifest.ts +72 -0
  156. package/src/router/error-handling.ts +9 -9
  157. package/src/router/find-match.ts +160 -0
  158. package/src/router/handler-context.ts +324 -116
  159. package/src/router/intercept-resolution.ts +11 -4
  160. package/src/router/lazy-includes.ts +237 -0
  161. package/src/router/loader-resolution.ts +179 -133
  162. package/src/router/logging.ts +112 -6
  163. package/src/router/manifest.ts +58 -19
  164. package/src/router/match-api.ts +89 -88
  165. package/src/router/match-context.ts +4 -2
  166. package/src/router/match-handlers.ts +440 -0
  167. package/src/router/match-middleware/background-revalidation.ts +86 -89
  168. package/src/router/match-middleware/cache-lookup.ts +295 -49
  169. package/src/router/match-middleware/cache-store.ts +56 -13
  170. package/src/router/match-middleware/intercept-resolution.ts +45 -22
  171. package/src/router/match-middleware/segment-resolution.ts +20 -9
  172. package/src/router/match-pipelines.ts +10 -45
  173. package/src/router/match-result.ts +44 -21
  174. package/src/router/metrics.ts +240 -15
  175. package/src/router/middleware-cookies.ts +55 -0
  176. package/src/router/middleware-types.ts +222 -0
  177. package/src/router/middleware.ts +327 -369
  178. package/src/router/pattern-matching.ts +169 -31
  179. package/src/router/prerender-match.ts +402 -0
  180. package/src/router/preview-match.ts +170 -0
  181. package/src/router/revalidation.ts +105 -14
  182. package/src/router/router-context.ts +40 -21
  183. package/src/router/router-interfaces.ts +452 -0
  184. package/src/router/router-options.ts +592 -0
  185. package/src/router/router-registry.ts +24 -0
  186. package/src/router/segment-resolution/fresh.ts +677 -0
  187. package/src/router/segment-resolution/helpers.ts +263 -0
  188. package/src/router/segment-resolution/loader-cache.ts +199 -0
  189. package/src/router/segment-resolution/revalidation.ts +1296 -0
  190. package/src/router/segment-resolution/static-store.ts +67 -0
  191. package/src/router/segment-resolution.ts +21 -1354
  192. package/src/router/segment-wrappers.ts +291 -0
  193. package/src/router/telemetry-otel.ts +299 -0
  194. package/src/router/telemetry.ts +300 -0
  195. package/src/router/timeout.ts +148 -0
  196. package/src/router/trie-matching.ts +96 -29
  197. package/src/router/types.ts +15 -9
  198. package/src/router.ts +642 -2366
  199. package/src/rsc/handler-context.ts +45 -0
  200. package/src/rsc/handler.ts +639 -1027
  201. package/src/rsc/helpers.ts +140 -6
  202. package/src/rsc/index.ts +0 -20
  203. package/src/rsc/loader-fetch.ts +209 -0
  204. package/src/rsc/manifest-init.ts +86 -0
  205. package/src/rsc/nonce.ts +14 -0
  206. package/src/rsc/origin-guard.ts +141 -0
  207. package/src/rsc/progressive-enhancement.ts +379 -0
  208. package/src/rsc/response-error.ts +37 -0
  209. package/src/rsc/response-route-handler.ts +347 -0
  210. package/src/rsc/rsc-rendering.ts +237 -0
  211. package/src/rsc/runtime-warnings.ts +42 -0
  212. package/src/rsc/server-action.ts +348 -0
  213. package/src/rsc/ssr-setup.ts +128 -0
  214. package/src/rsc/types.ts +38 -11
  215. package/src/search-params.ts +66 -54
  216. package/src/segment-system.tsx +165 -17
  217. package/src/server/context.ts +237 -54
  218. package/src/server/cookie-store.ts +190 -0
  219. package/src/server/fetchable-loader-store.ts +11 -6
  220. package/src/server/handle-store.ts +94 -15
  221. package/src/server/loader-registry.ts +15 -56
  222. package/src/server/request-context.ts +438 -71
  223. package/src/server.ts +26 -164
  224. package/src/ssr/index.tsx +101 -31
  225. package/src/static-handler.ts +22 -4
  226. package/src/theme/ThemeProvider.tsx +21 -15
  227. package/src/theme/ThemeScript.tsx +5 -5
  228. package/src/theme/constants.ts +5 -2
  229. package/src/theme/index.ts +4 -14
  230. package/src/theme/theme-context.ts +4 -30
  231. package/src/theme/theme-script.ts +21 -18
  232. package/src/types/boundaries.ts +158 -0
  233. package/src/types/cache-types.ts +198 -0
  234. package/src/types/error-types.ts +192 -0
  235. package/src/types/global-namespace.ts +100 -0
  236. package/src/types/handler-context.ts +773 -0
  237. package/src/types/index.ts +88 -0
  238. package/src/types/loader-types.ts +183 -0
  239. package/src/types/route-config.ts +170 -0
  240. package/src/types/route-entry.ts +109 -0
  241. package/src/types/segments.ts +150 -0
  242. package/src/types.ts +1 -1795
  243. package/src/urls/include-helper.ts +197 -0
  244. package/src/urls/index.ts +53 -0
  245. package/src/urls/path-helper-types.ts +339 -0
  246. package/src/urls/path-helper.ts +329 -0
  247. package/src/urls/pattern-types.ts +95 -0
  248. package/src/urls/response-types.ts +106 -0
  249. package/src/urls/type-extraction.ts +372 -0
  250. package/src/urls/urls-function.ts +98 -0
  251. package/src/urls.ts +1 -1323
  252. package/src/use-loader.tsx +85 -77
  253. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  254. package/src/vite/discovery/discover-routers.ts +344 -0
  255. package/src/vite/discovery/prerender-collection.ts +385 -0
  256. package/src/vite/discovery/route-types-writer.ts +258 -0
  257. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  258. package/src/vite/discovery/state.ts +108 -0
  259. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  260. package/src/vite/index.ts +11 -2259
  261. package/src/vite/plugin-types.ts +48 -0
  262. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  263. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  264. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  265. package/src/vite/{expose-action-id.ts → plugins/expose-action-id.ts} +72 -47
  266. package/src/vite/{expose-id-utils.ts → plugins/expose-id-utils.ts} +8 -43
  267. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  268. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  269. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  270. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  271. package/src/vite/plugins/expose-ids/types.ts +45 -0
  272. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  273. package/src/vite/plugins/refresh-cmd.ts +65 -0
  274. package/src/vite/plugins/use-cache-transform.ts +323 -0
  275. package/src/vite/plugins/version-injector.ts +83 -0
  276. package/src/vite/plugins/version-plugin.ts +266 -0
  277. package/src/vite/{virtual-entries.ts → plugins/virtual-entries.ts} +23 -14
  278. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  279. package/src/vite/rango.ts +445 -0
  280. package/src/vite/router-discovery.ts +777 -0
  281. package/src/vite/{ast-handler-extract.ts → utils/ast-handler-extract.ts} +181 -9
  282. package/src/vite/utils/banner.ts +36 -0
  283. package/src/vite/utils/bundle-analysis.ts +137 -0
  284. package/src/vite/utils/manifest-utils.ts +70 -0
  285. package/src/vite/{package-resolution.ts → utils/package-resolution.ts} +25 -29
  286. package/src/vite/utils/prerender-utils.ts +189 -0
  287. package/src/vite/utils/shared-utils.ts +169 -0
  288. package/CLAUDE.md +0 -43
  289. package/dist/vite/index.named-routes.gen.ts +0 -103
  290. package/src/browser/lru-cache.ts +0 -69
  291. package/src/browser/request-controller.ts +0 -164
  292. package/src/cache/memory-store.ts +0 -253
  293. package/src/href-context.ts +0 -33
  294. package/src/router.gen.ts +0 -6
  295. package/src/static-handler.gen.ts +0 -5
  296. package/src/urls.gen.ts +0 -8
  297. package/src/vite/expose-internal-ids.ts +0 -1167
  298. /package/src/vite/{version.d.ts → plugins/version.d.ts} +0 -0
package/src/server.ts CHANGED
@@ -1,98 +1,30 @@
1
1
  /**
2
- * rsc-router/server
2
+ * @rangojs/router/server — Internal subpath
3
3
  *
4
- * Server-only exports for route definition and building
5
- * These should only be imported in server-side handler files
4
+ * This module is NOT user-facing. Import from "@rangojs/router" instead.
5
+ *
6
+ * Exports here are consumed by the Vite plugin (discovery, manifest injection,
7
+ * virtual modules) and the RSC handler internals. They are not part of the
8
+ * public API and may change without notice.
6
9
  */
7
10
 
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";
11
+ // Router registry (used by Vite plugin for build-time discovery)
12
+ export { RSC_ROUTER_BRAND, RouterRegistry } from "./router.js";
66
13
 
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
14
+ // Host router registry (used by Vite plugin for host-router lazy discovery)
71
15
  export {
72
- Prerender,
73
- isPrerenderHandler,
74
- type PrerenderHandlerDefinition,
75
- type PrerenderOptions,
76
- type BuildContext,
77
- } from "./prerender.js";
16
+ HostRouterRegistry,
17
+ type HostRouterRegistryEntry,
18
+ } from "./host/router.js";
78
19
 
79
- // Static handler API
80
- export {
81
- Static,
82
- isStaticHandler,
83
- type StaticHandlerDefinition,
84
- } from "./static-handler.js";
85
-
86
- // Built-in handles
87
- export { Meta } from "./handles/meta.js";
88
-
89
- // Loader registry (for GET-based loader fetching)
90
- export { registerLoaderById, setLoaderImports } from "./server/loader-registry.js";
91
-
92
- // Route map builder (for build-time manifest registration)
20
+ // Route map builder (Vite plugin injects these via virtual modules)
93
21
  export {
94
22
  registerRouteMap,
95
23
  setCachedManifest,
24
+ clearCachedManifest,
25
+ clearAllRouterData,
26
+ getGlobalRouteMap,
27
+ getRouterManifest,
96
28
  setPrecomputedEntries,
97
29
  setRouteTrie,
98
30
  setManifestReadyPromise,
@@ -103,87 +35,17 @@ export {
103
35
  ensureRouterManifest,
104
36
  } from "./route-map-builder.js";
105
37
 
106
- // Request context (for accessing request data in server components/actions)
38
+ // Loader registry (Vite plugin registers lazy loader imports)
39
+ export {
40
+ registerLoaderById,
41
+ setLoaderImports,
42
+ } from "./server/loader-registry.js";
43
+
44
+ // Request context creation (used by RSC handler, not user-facing)
107
45
  export {
108
- getRequestContext,
109
- requireRequestContext,
110
46
  createRequestContext,
111
- type RequestContext,
112
47
  type CreateRequestContextOptions,
113
48
  } from "./server/request-context.js";
114
49
 
115
- // Meta types
116
- export type { MetaDescriptor, MetaDescriptorBase } from "./router/types.js";
117
-
118
- // Middleware context types (Middleware type is exported from types.ts)
119
- export type {
120
- MiddlewareContext,
121
- CookieOptions,
122
- } from "./router/middleware.js";
123
-
124
- // Error classes and utilities
125
- export {
126
- RouteNotFoundError,
127
- DataNotFoundError,
128
- notFound,
129
- MiddlewareError,
130
- HandlerError,
131
- BuildError,
132
- InvalidHandlerError,
133
- sanitizeError,
134
- RouterError,
135
- } from "./errors.js";
136
-
137
- // Component utilities
138
- export {
139
- isClientComponent,
140
- assertClientComponent,
141
- } from "./component-utils.js";
142
-
143
- // Debug utilities for route matching (development only)
144
- export {
145
- enableMatchDebug,
146
- getMatchDebugStats,
147
- } from "./router/pattern-matching.js";
148
-
149
- // Types (re-exported for convenience - user-facing only)
150
- export type {
151
- // Configuration types
152
- RouterEnv,
153
- DefaultEnv,
154
- RouteDefinition,
155
- RouteConfig,
156
- RouteDefinitionOptions,
157
- TrailingSlashMode,
158
- // Handler types
159
- Handler, // Supports params object, path pattern, or route name
160
- HandlerContext,
161
- ExtractParams,
162
- GenericParams,
163
- // Middleware types (also exported from router/middleware.js above)
164
- Middleware, // Supports env type and optional route name for params
165
- // Revalidation types
166
- RevalidateParams,
167
- Revalidate,
168
- RouteKeys,
169
- // Loader types
170
- LoaderDefinition,
171
- LoaderFn,
172
- LoaderContext,
173
- // Error boundary types
174
- ErrorInfo,
175
- ErrorBoundaryFallbackProps,
176
- ErrorBoundaryHandler,
177
- ClientErrorBoundaryFallbackProps,
178
- // NotFound boundary types
179
- NotFoundInfo,
180
- NotFoundBoundaryFallbackProps,
181
- NotFoundBoundaryHandler,
182
- // Error handling callback types
183
- ErrorPhase,
184
- OnErrorContext,
185
- OnErrorCallback,
186
- } from "./types.js";
187
-
188
- // Path-based response type lookup from RegisteredRoutes
189
- export type { PathResponse } from "./href-client.js";
50
+ // Component utilities (used internally for server/client boundary checks)
51
+ export { isClientComponent, assertClientComponent } from "./component-utils.js";
package/src/ssr/index.tsx CHANGED
@@ -1,14 +1,18 @@
1
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";
2
+ import { renderSegments } from "../segment-system.js";
3
+ import { filterSegmentOrder } from "../browser/react/filter-segment-order.js";
5
4
  import { ThemeProvider } from "../theme/ThemeProvider.js";
5
+ import { NonceContext } from "../browser/react/nonce-context.js";
6
6
  import { NavigationStoreContext } from "../browser/react/context.js";
7
7
  import type { NavigationStoreContextValue } from "../browser/react/context.js";
8
8
  import type { HandleData } from "../browser/types.js";
9
9
  import type { ErrorPhase } from "../types.js";
10
+ import type { ResolvedSegment } from "../types.js";
10
11
  import type { ResolvedThemeConfig, Theme } from "../theme/types.js";
11
- import type { EventController, DerivedNavigationState } from "../browser/event-controller.js";
12
+ import type {
13
+ EventController,
14
+ DerivedNavigationState,
15
+ } from "../browser/event-controller.js";
12
16
 
13
17
  /**
14
18
  * Options for injectRSCPayload
@@ -29,6 +33,13 @@ interface RenderToReadableStreamOptions {
29
33
  formState?: unknown;
30
34
  }
31
35
 
36
+ /**
37
+ * ReadableStream with the allReady promise added by react-dom/server.edge.
38
+ */
39
+ interface ReactDOMReadableStream extends ReadableStream<Uint8Array> {
40
+ allReady: Promise<void>;
41
+ }
42
+
32
43
  /**
33
44
  * Options for the renderHTML function
34
45
  */
@@ -45,6 +56,14 @@ export interface SSRRenderOptions {
45
56
  * Nonce for Content Security Policy (CSP)
46
57
  */
47
58
  nonce?: string;
59
+
60
+ /**
61
+ * SSR stream mode.
62
+ *
63
+ * - `"stream"` (default) — start flushing HTML immediately.
64
+ * - `"allReady"` — await `stream.allReady` before returning.
65
+ */
66
+ streamMode?: import("../router/router-options.js").SSRStreamMode;
48
67
  }
49
68
 
50
69
  /**
@@ -54,22 +73,24 @@ export interface SSRDependencies<TEnv = unknown> {
54
73
  /**
55
74
  * createFromReadableStream from @vitejs/plugin-rsc/ssr
56
75
  */
57
- createFromReadableStream: <T>(stream: ReadableStream<Uint8Array>) => Promise<T>;
76
+ createFromReadableStream: <T>(
77
+ stream: ReadableStream<Uint8Array>,
78
+ ) => Promise<T>;
58
79
 
59
80
  /**
60
81
  * renderToReadableStream from react-dom/server.edge
61
82
  */
62
83
  renderToReadableStream: (
63
84
  element: React.ReactNode,
64
- options?: RenderToReadableStreamOptions
65
- ) => Promise<ReadableStream<Uint8Array>>;
85
+ options?: RenderToReadableStreamOptions,
86
+ ) => Promise<ReactDOMReadableStream>;
66
87
 
67
88
  /**
68
89
  * injectRSCPayload from rsc-html-stream/server
69
90
  */
70
91
  injectRSCPayload: (
71
92
  rscStream: ReadableStream<Uint8Array>,
72
- options?: InjectRSCPayloadOptions
93
+ options?: InjectRSCPayloadOptions,
73
94
  ) => TransformStream<Uint8Array, Uint8Array>;
74
95
 
75
96
  /**
@@ -101,13 +122,16 @@ export interface SSRDependencies<TEnv = unknown> {
101
122
  * RSC payload type (minimal interface for SSR)
102
123
  */
103
124
  interface RscPayload {
104
- root: React.ReactNode;
105
125
  metadata?: {
126
+ segments?: ResolvedSegment[];
127
+ rootLayout?: React.ComponentType<{ children: React.ReactNode }>;
106
128
  handles?: AsyncGenerator<HandleData, void, unknown>;
107
129
  matched?: string[];
108
130
  pathname?: string;
131
+ params?: Record<string, string>;
109
132
  themeConfig?: ResolvedThemeConfig | null;
110
133
  initialTheme?: Theme;
134
+ version?: string;
111
135
  };
112
136
  }
113
137
 
@@ -116,7 +140,7 @@ interface RscPayload {
116
140
  * Used for SSR where we need to await all handle data before rendering.
117
141
  */
118
142
  async function consumeAsyncGenerator(
119
- generator: AsyncGenerator<HandleData, void, unknown>
143
+ generator: AsyncGenerator<HandleData, void, unknown>,
120
144
  ): Promise<HandleData> {
121
145
  let lastData: HandleData = {};
122
146
  for await (const data of generator) {
@@ -129,11 +153,22 @@ async function consumeAsyncGenerator(
129
153
  * Create a minimal event controller for SSR.
130
154
  * This provides the correct pathname so useNavigation returns the right value during SSR.
131
155
  */
132
- function createSsrEventController(pathname: string): EventController {
133
- const location = new URL(pathname, "http://localhost");
156
+ function createSsrEventController(opts: {
157
+ pathname: string;
158
+ params?: Record<string, string>;
159
+ handleData?: HandleData;
160
+ matched?: string[];
161
+ }): EventController {
162
+ const location = new URL(opts.pathname, "http://localhost");
163
+ let params = opts.params ?? {};
164
+ const handleState = {
165
+ data: opts.handleData ?? {},
166
+ segmentOrder: filterSegmentOrder(opts.matched ?? []),
167
+ };
134
168
  const state: DerivedNavigationState = {
135
169
  state: "idle",
136
170
  isStreaming: false,
171
+ isNavigating: false,
137
172
  location,
138
173
  pendingUrl: null,
139
174
  inflightActions: [],
@@ -141,6 +176,7 @@ function createSsrEventController(pathname: string): EventController {
141
176
 
142
177
  return {
143
178
  getState: () => state,
179
+ getLocation: () => location,
144
180
  subscribe: () => () => {},
145
181
  getActionState: () => ({
146
182
  state: "idle",
@@ -152,7 +188,11 @@ function createSsrEventController(pathname: string): EventController {
152
188
  subscribeToAction: () => () => {},
153
189
  subscribeToHandles: () => () => {},
154
190
  setHandleData: () => {},
155
- getHandleState: () => ({ data: {}, segmentOrder: [] }),
191
+ getHandleState: () => handleState,
192
+ setParams: (nextParams) => {
193
+ params = nextParams;
194
+ },
195
+ getParams: () => params,
156
196
  setLocation: () => {},
157
197
  startNavigation: () => {
158
198
  throw new Error("Navigation not supported during SSR");
@@ -164,6 +204,7 @@ function createSsrEventController(pathname: string): EventController {
164
204
  abortAllActions: () => {},
165
205
  getCurrentNavigation: () => null,
166
206
  getInflightActions: () => new Map(),
207
+ hadAnyConcurrentActions: () => false,
167
208
  };
168
209
  }
169
210
 
@@ -203,9 +244,9 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
203
244
  */
204
245
  return async function renderHTML(
205
246
  rscStream: ReadableStream<Uint8Array>,
206
- options?: SSRRenderOptions
247
+ options?: SSRRenderOptions,
207
248
  ): Promise<ReadableStream<Uint8Array>> {
208
- const { nonce, formState } = options ?? {};
249
+ const { nonce, formState, streamMode } = options ?? {};
209
250
 
210
251
  try {
211
252
  // Tee the stream:
@@ -220,47 +261,68 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
220
261
  function SsrRoot() {
221
262
  payload ??= createFromReadableStream<RscPayload>(rscStream1);
222
263
  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
264
  const themeConfig = resolved.metadata?.themeConfig ?? null;
229
- initThemeConfigSync(themeConfig);
265
+ const pathname = resolved.metadata?.pathname ?? "/";
230
266
 
231
- // Await handles and initialize state before children render
267
+ // Await handles before creating SSR event controller so hooks can
268
+ // read request-local handle data via NavigationStoreContext.
232
269
  // The handles property is an async generator that yields on each push
233
270
  // Memoize the promise since async generators can only be iterated once
271
+ let handleData: HandleData = {};
234
272
  if (resolved.metadata?.handles) {
235
273
  handlesPromise ??= consumeAsyncGenerator(resolved.metadata.handles);
236
- const handleData = React.use(handlesPromise);
237
- initHandleDataSync(handleData, resolved.metadata.matched);
274
+ handleData = React.use(handlesPromise);
238
275
  }
239
276
 
240
- // Create SSR context with correct pathname for useNavigation
277
+ // Create SSR context with request-local pathname/params/handles.
241
278
  ssrContextValue ??= {
242
279
  store: null as any,
243
- eventController: createSsrEventController(resolved.metadata?.pathname ?? "/"),
280
+ eventController: createSsrEventController({
281
+ pathname,
282
+ params: resolved.metadata?.params,
283
+ handleData,
284
+ matched: resolved.metadata?.matched,
285
+ }),
244
286
  navigate: async () => {},
245
287
  refresh: async () => {},
288
+ version: resolved.metadata?.version,
246
289
  };
247
290
 
248
- // Build content tree with all necessary providers
291
+ // Build content tree from segments.
249
292
  // Order must match NavigationProvider: NavigationStoreContext > ThemeProvider > content
250
- let content: React.ReactNode = resolved.root;
293
+ const reconstructedRoot = renderSegments(
294
+ resolved.metadata?.segments ?? [],
295
+ {
296
+ rootLayout: resolved.metadata?.rootLayout,
297
+ },
298
+ );
299
+ let content: React.ReactNode =
300
+ reconstructedRoot instanceof Promise
301
+ ? React.use(reconstructedRoot)
302
+ : reconstructedRoot;
251
303
 
252
304
  // Wrap content with ThemeProvider if theme is enabled
253
305
  if (themeConfig) {
254
306
  content = (
255
- <ThemeProvider config={themeConfig} initialTheme={resolved.metadata?.initialTheme}>
307
+ <ThemeProvider
308
+ config={themeConfig}
309
+ initialTheme={resolved.metadata?.initialTheme}
310
+ >
256
311
  {content}
257
312
  </ThemeProvider>
258
313
  );
259
314
  }
260
315
 
316
+ // Wrap with NonceContext so client components (e.g. MetaTags) can
317
+ // apply CSP nonces to inline scripts during SSR. Always present to
318
+ // match the browser-side NavigationProvider tree shape for hydration.
319
+ content = (
320
+ <NonceContext.Provider value={nonce}>{content}</NonceContext.Provider>
321
+ );
322
+
261
323
  // Wrap with NavigationStoreContext for useNavigation hook
262
324
  return (
263
- <NavigationStoreContext.Provider value={ssrContextValue}>
325
+ <NavigationStoreContext.Provider value={ssrContextValue!}>
264
326
  {content}
265
327
  </NavigationStoreContext.Provider>
266
328
  );
@@ -278,12 +340,20 @@ export function createSSRHandler<TEnv = unknown>(deps: SSRDependencies<TEnv>) {
278
340
  nonce,
279
341
  });
280
342
 
343
+ // Wait for all Suspense boundaries to resolve when streamMode is "allReady".
344
+ // This buffers the entire HTML before flushing — used for bots that
345
+ // cannot process streamed HTML.
346
+ if (streamMode === "allReady") {
347
+ await htmlStream.allReady;
348
+ }
349
+
281
350
  // Inject RSC payload into HTML as <script nonce="...">__FLIGHT_DATA__</script>
282
351
  return htmlStream.pipeThrough(injectRSCPayload(rscStream2, { nonce }));
283
352
  } catch (error) {
284
353
  // Invoke onError callback if provided
285
354
  if (onError) {
286
- const errorObj = error instanceof Error ? error : new Error(String(error));
355
+ const errorObj =
356
+ error instanceof Error ? error : new Error(String(error));
287
357
  try {
288
358
  onError(errorObj, { phase: "rendering" });
289
359
  } catch (callbackError) {
@@ -31,12 +31,15 @@
31
31
  * ```
32
32
  */
33
33
  import type { ReactNode } from "react";
34
- import type { Handler, HandlerContext } from "./types.js";
35
- import type { PrerenderOptions } from "./prerender.js";
34
+ import type { Handler } from "./types.js";
35
+ import type { PrerenderOptions, StaticBuildContext } from "./prerender.js";
36
+ import { isCachedFunction } from "./cache/taint.js";
36
37
 
37
38
  // -- Types ------------------------------------------------------------------
38
39
 
39
- export interface StaticHandlerDefinition<TParams extends Record<string, any> = any> {
40
+ export interface StaticHandlerDefinition<
41
+ TParams extends Record<string, any> = any,
42
+ > {
40
43
  readonly __brand: "staticHandler";
41
44
  /** Auto-generated unique ID (injected by Vite plugin). */
42
45
  $$id: string;
@@ -49,7 +52,7 @@ export interface StaticHandlerDefinition<TParams extends Record<string, any> = a
49
52
  // -- Function ---------------------------------------------------------------
50
53
 
51
54
  export function Static<TParams extends Record<string, any> = {}>(
52
- handler: (ctx: HandlerContext<TParams>) => ReactNode | Promise<ReactNode>,
55
+ handler: (ctx: StaticBuildContext) => ReactNode | Promise<ReactNode>,
53
56
  options?: PrerenderOptions,
54
57
  __injectedId?: string,
55
58
  ): StaticHandlerDefinition<TParams>;
@@ -61,6 +64,14 @@ export function Static<TParams extends Record<string, any>>(
61
64
  optionsOrId?: PrerenderOptions | string,
62
65
  maybeId?: string,
63
66
  ): StaticHandlerDefinition<TParams> {
67
+ if (isCachedFunction(handler)) {
68
+ throw new Error(
69
+ 'A "use cache" function cannot be used as a Static() handler. ' +
70
+ "Static handlers are rendered once at build time. Remove the " +
71
+ '"use cache" directive — Static already provides caching.',
72
+ );
73
+ }
74
+
64
75
  let options: PrerenderOptions | undefined;
65
76
  let id: string;
66
77
 
@@ -71,6 +82,13 @@ export function Static<TParams extends Record<string, any>>(
71
82
  id = maybeId ?? "";
72
83
  }
73
84
 
85
+ if (!id) {
86
+ throw new Error(
87
+ "[rsc-router] Static: missing $$id. " +
88
+ "Ensure the exposeInternalIds Vite plugin is configured.",
89
+ );
90
+ }
91
+
74
92
  return {
75
93
  __brand: "staticHandler" as const,
76
94
  $$id: id,
@@ -11,7 +11,13 @@
11
11
  * - Handles SSR hydration by deferring system theme detection
12
12
  */
13
13
 
14
- import React, { useCallback, useEffect, useMemo, useState, useRef } from "react";
14
+ import React, {
15
+ useCallback,
16
+ useEffect,
17
+ useMemo,
18
+ useState,
19
+ useRef,
20
+ } from "react";
15
21
  import { ThemeContext } from "./theme-context.js";
16
22
  import type {
17
23
  ResolvedTheme,
@@ -44,7 +50,12 @@ function readThemeFromCookie(storageKey: string): string | null {
44
50
  for (const cookie of cookies) {
45
51
  const [name, ...rest] = cookie.trim().split("=");
46
52
  if (name === storageKey) {
47
- return decodeURIComponent(rest.join("="));
53
+ const raw = rest.join("=");
54
+ try {
55
+ return decodeURIComponent(raw);
56
+ } catch {
57
+ return raw;
58
+ }
48
59
  }
49
60
  }
50
61
  return null;
@@ -90,15 +101,13 @@ function writeThemeToStorage(storageKey: string, theme: Theme): void {
90
101
  /**
91
102
  * Apply theme to HTML element
92
103
  */
93
- function applyThemeToDocument(
94
- theme: Theme,
95
- config: ResolvedThemeConfig
96
- ): void {
104
+ function applyThemeToDocument(theme: Theme, config: ResolvedThemeConfig): void {
97
105
  if (typeof document === "undefined") return;
98
106
 
99
- const resolved = theme === "system" && config.enableSystem
100
- ? getSystemTheme()
101
- : (theme as ResolvedTheme);
107
+ const resolved =
108
+ theme === "system" && config.enableSystem
109
+ ? getSystemTheme()
110
+ : (theme as ResolvedTheme);
102
111
 
103
112
  const value = config.value[resolved] || resolved;
104
113
  const el = document.documentElement;
@@ -188,7 +197,7 @@ export function ThemeProvider({
188
197
  writeThemeToStorage(config.storageKey, newTheme);
189
198
  applyThemeToDocument(newTheme, config);
190
199
  },
191
- [config]
200
+ [config],
192
201
  );
193
202
 
194
203
  // Listen for system preference changes
@@ -227,10 +236,7 @@ export function ThemeProvider({
227
236
  if (!newTheme) return;
228
237
 
229
238
  // Validate and apply
230
- if (
231
- newTheme === "system" ||
232
- config.themes.includes(newTheme)
233
- ) {
239
+ if (newTheme === "system" || config.themes.includes(newTheme)) {
234
240
  setThemeState(newTheme as Theme);
235
241
  applyThemeToDocument(newTheme as Theme, config);
236
242
  }
@@ -280,7 +286,7 @@ export function ThemeProvider({
280
286
  themes,
281
287
  config,
282
288
  }),
283
- [theme, setTheme, resolvedTheme, systemTheme, themes, config, mounted]
289
+ [theme, setTheme, resolvedTheme, systemTheme, themes, config, mounted],
284
290
  );
285
291
 
286
292
  return (
@@ -49,13 +49,13 @@ export interface ThemeScriptProps {
49
49
  * This renders a synchronous inline script that applies the theme
50
50
  * to the HTML element before React hydration, preventing FOUC.
51
51
  */
52
- export function ThemeScript({ config, nonce }: ThemeScriptProps): React.ReactNode {
52
+ export function ThemeScript({
53
+ config,
54
+ nonce,
55
+ }: ThemeScriptProps): React.ReactNode {
53
56
  const scriptContent = generateThemeScript(config);
54
57
 
55
58
  return (
56
- <script
57
- nonce={nonce}
58
- dangerouslySetInnerHTML={{ __html: scriptContent }}
59
- />
59
+ <script nonce={nonce} dangerouslySetInnerHTML={{ __html: scriptContent }} />
60
60
  );
61
61
  }
@@ -33,7 +33,9 @@ export const THEME_COOKIE: {
33
33
  * Resolve theme config by applying defaults.
34
34
  * Accepts `true` to enable with all defaults, or a config object.
35
35
  */
36
- export function resolveThemeConfig(config: ThemeConfig | true): ResolvedThemeConfig {
36
+ export function resolveThemeConfig(
37
+ config: ThemeConfig | true,
38
+ ): ResolvedThemeConfig {
37
39
  // Handle `theme: true` shorthand
38
40
  if (config === true) {
39
41
  config = {};
@@ -53,7 +55,8 @@ export function resolveThemeConfig(config: ThemeConfig | true): ResolvedThemeCon
53
55
  attribute: config.attribute ?? THEME_DEFAULTS.attribute,
54
56
  storageKey: config.storageKey ?? THEME_DEFAULTS.storageKey,
55
57
  enableSystem: config.enableSystem ?? THEME_DEFAULTS.enableSystem,
56
- enableColorScheme: config.enableColorScheme ?? THEME_DEFAULTS.enableColorScheme,
58
+ enableColorScheme:
59
+ config.enableColorScheme ?? THEME_DEFAULTS.enableColorScheme,
57
60
  value,
58
61
  };
59
62
  }