@rangojs/router 0.0.0-experimental.125 → 0.0.0-experimental.126

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 (881) hide show
  1. package/README.md +6 -4
  2. package/dist/__internal.d.ts +83 -0
  3. package/dist/__internal.d.ts.map +1 -0
  4. package/dist/__internal.js +19 -0
  5. package/dist/__internal.js.map +1 -0
  6. package/dist/__mocks__/version.d.ts +7 -0
  7. package/dist/__mocks__/version.d.ts.map +1 -0
  8. package/dist/__mocks__/version.js +7 -0
  9. package/dist/__mocks__/version.js.map +1 -0
  10. package/dist/__tests__/client-href.test.d.ts +2 -0
  11. package/dist/__tests__/client-href.test.d.ts.map +1 -0
  12. package/dist/__tests__/client-href.test.js +74 -0
  13. package/dist/__tests__/client-href.test.js.map +1 -0
  14. package/dist/__tests__/component-utils.test.d.ts +2 -0
  15. package/dist/__tests__/component-utils.test.d.ts.map +1 -0
  16. package/dist/__tests__/component-utils.test.js +51 -0
  17. package/dist/__tests__/component-utils.test.js.map +1 -0
  18. package/dist/__tests__/event-controller.test.d.ts +2 -0
  19. package/dist/__tests__/event-controller.test.d.ts.map +1 -0
  20. package/dist/__tests__/event-controller.test.js +538 -0
  21. package/dist/__tests__/event-controller.test.js.map +1 -0
  22. package/dist/__tests__/helpers/route-tree.d.ts +118 -0
  23. package/dist/__tests__/helpers/route-tree.d.ts.map +1 -0
  24. package/dist/__tests__/helpers/route-tree.js +374 -0
  25. package/dist/__tests__/helpers/route-tree.js.map +1 -0
  26. package/dist/__tests__/match-result.test.d.ts +2 -0
  27. package/dist/__tests__/match-result.test.d.ts.map +1 -0
  28. package/dist/__tests__/match-result.test.js +154 -0
  29. package/dist/__tests__/match-result.test.js.map +1 -0
  30. package/dist/__tests__/navigation-store.test.d.ts +2 -0
  31. package/dist/__tests__/navigation-store.test.d.ts.map +1 -0
  32. package/dist/__tests__/navigation-store.test.js +440 -0
  33. package/dist/__tests__/navigation-store.test.js.map +1 -0
  34. package/dist/__tests__/partial-update.test.d.ts +2 -0
  35. package/dist/__tests__/partial-update.test.d.ts.map +1 -0
  36. package/dist/__tests__/partial-update.test.js +1009 -0
  37. package/dist/__tests__/partial-update.test.js.map +1 -0
  38. package/dist/__tests__/reverse-types.test.d.ts +8 -0
  39. package/dist/__tests__/reverse-types.test.d.ts.map +1 -0
  40. package/dist/__tests__/reverse-types.test.js +656 -0
  41. package/dist/__tests__/reverse-types.test.js.map +1 -0
  42. package/dist/__tests__/route-definition.test.d.ts +2 -0
  43. package/dist/__tests__/route-definition.test.d.ts.map +1 -0
  44. package/dist/__tests__/route-definition.test.js +55 -0
  45. package/dist/__tests__/route-definition.test.js.map +1 -0
  46. package/dist/__tests__/router-helpers.test.d.ts +2 -0
  47. package/dist/__tests__/router-helpers.test.d.ts.map +1 -0
  48. package/dist/__tests__/router-helpers.test.js +377 -0
  49. package/dist/__tests__/router-helpers.test.js.map +1 -0
  50. package/dist/__tests__/router-integration-2.test.d.ts +2 -0
  51. package/dist/__tests__/router-integration-2.test.d.ts.map +1 -0
  52. package/dist/__tests__/router-integration-2.test.js +426 -0
  53. package/dist/__tests__/router-integration-2.test.js.map +1 -0
  54. package/dist/__tests__/router-integration.test.d.ts +2 -0
  55. package/dist/__tests__/router-integration.test.d.ts.map +1 -0
  56. package/dist/__tests__/router-integration.test.js +1051 -0
  57. package/dist/__tests__/router-integration.test.js.map +1 -0
  58. package/dist/__tests__/search-params.test.d.ts +5 -0
  59. package/dist/__tests__/search-params.test.d.ts.map +1 -0
  60. package/dist/__tests__/search-params.test.js +306 -0
  61. package/dist/__tests__/search-params.test.js.map +1 -0
  62. package/dist/__tests__/segment-system.test.d.ts +2 -0
  63. package/dist/__tests__/segment-system.test.d.ts.map +1 -0
  64. package/dist/__tests__/segment-system.test.js +627 -0
  65. package/dist/__tests__/segment-system.test.js.map +1 -0
  66. package/dist/__tests__/static-handler-types.test.d.ts +8 -0
  67. package/dist/__tests__/static-handler-types.test.d.ts.map +1 -0
  68. package/dist/__tests__/static-handler-types.test.js +63 -0
  69. package/dist/__tests__/static-handler-types.test.js.map +1 -0
  70. package/dist/__tests__/urls.test.d.ts +2 -0
  71. package/dist/__tests__/urls.test.d.ts.map +1 -0
  72. package/dist/__tests__/urls.test.js +421 -0
  73. package/dist/__tests__/urls.test.js.map +1 -0
  74. package/dist/__tests__/use-mount.test.d.ts +2 -0
  75. package/dist/__tests__/use-mount.test.d.ts.map +1 -0
  76. package/dist/__tests__/use-mount.test.js +35 -0
  77. package/dist/__tests__/use-mount.test.js.map +1 -0
  78. package/dist/bin/rango.d.ts +2 -0
  79. package/dist/bin/rango.d.ts.map +1 -0
  80. package/dist/bin/rango.js.map +1 -0
  81. package/dist/browser/event-controller.d.ts +191 -0
  82. package/dist/browser/event-controller.d.ts.map +1 -0
  83. package/dist/browser/event-controller.js +559 -0
  84. package/dist/browser/event-controller.js.map +1 -0
  85. package/dist/browser/index.d.ts +2 -0
  86. package/dist/browser/index.d.ts.map +1 -0
  87. package/dist/browser/index.js +14 -0
  88. package/dist/browser/index.js.map +1 -0
  89. package/dist/browser/link-interceptor.d.ts +38 -0
  90. package/dist/browser/link-interceptor.d.ts.map +1 -0
  91. package/dist/browser/link-interceptor.js +99 -0
  92. package/dist/browser/link-interceptor.js.map +1 -0
  93. package/dist/browser/logging.d.ts +10 -0
  94. package/dist/browser/logging.d.ts.map +1 -0
  95. package/dist/browser/logging.js +29 -0
  96. package/dist/browser/logging.js.map +1 -0
  97. package/dist/browser/lru-cache.d.ts +17 -0
  98. package/dist/browser/lru-cache.d.ts.map +1 -0
  99. package/dist/browser/lru-cache.js +50 -0
  100. package/dist/browser/lru-cache.js.map +1 -0
  101. package/dist/browser/merge-segment-loaders.d.ts +39 -0
  102. package/dist/browser/merge-segment-loaders.d.ts.map +1 -0
  103. package/dist/browser/merge-segment-loaders.js +102 -0
  104. package/dist/browser/merge-segment-loaders.js.map +1 -0
  105. package/dist/browser/navigation-bridge.d.ts +102 -0
  106. package/dist/browser/navigation-bridge.d.ts.map +1 -0
  107. package/dist/browser/navigation-bridge.js +708 -0
  108. package/dist/browser/navigation-bridge.js.map +1 -0
  109. package/dist/browser/navigation-client.d.ts +25 -0
  110. package/dist/browser/navigation-client.d.ts.map +1 -0
  111. package/dist/browser/navigation-client.js +157 -0
  112. package/dist/browser/navigation-client.js.map +1 -0
  113. package/dist/browser/navigation-store.d.ts +101 -0
  114. package/dist/browser/navigation-store.d.ts.map +1 -0
  115. package/dist/browser/navigation-store.js +625 -0
  116. package/dist/browser/navigation-store.js.map +1 -0
  117. package/dist/browser/partial-update.d.ts +75 -0
  118. package/dist/browser/partial-update.d.ts.map +1 -0
  119. package/dist/browser/partial-update.js +426 -0
  120. package/dist/browser/partial-update.js.map +1 -0
  121. package/dist/browser/react/Link.d.ts +86 -0
  122. package/dist/browser/react/Link.d.ts.map +1 -0
  123. package/dist/browser/react/Link.js +128 -0
  124. package/dist/browser/react/Link.js.map +1 -0
  125. package/dist/browser/react/NavigationProvider.d.ts +63 -0
  126. package/dist/browser/react/NavigationProvider.d.ts.map +1 -0
  127. package/dist/browser/react/NavigationProvider.js +216 -0
  128. package/dist/browser/react/NavigationProvider.js.map +1 -0
  129. package/dist/browser/react/ScrollRestoration.d.ts +75 -0
  130. package/dist/browser/react/ScrollRestoration.d.ts.map +1 -0
  131. package/dist/browser/react/ScrollRestoration.js +57 -0
  132. package/dist/browser/react/ScrollRestoration.js.map +1 -0
  133. package/dist/browser/react/context.d.ts +46 -0
  134. package/dist/browser/react/context.d.ts.map +1 -0
  135. package/dist/browser/react/context.js +10 -0
  136. package/dist/browser/react/context.js.map +1 -0
  137. package/dist/browser/react/index.d.ts +11 -0
  138. package/dist/browser/react/index.d.ts.map +1 -0
  139. package/dist/browser/react/index.js +22 -0
  140. package/dist/browser/react/index.js.map +1 -0
  141. package/dist/browser/react/location-state-shared.d.ts +63 -0
  142. package/dist/browser/react/location-state-shared.d.ts.map +1 -0
  143. package/dist/browser/react/location-state-shared.js +81 -0
  144. package/dist/browser/react/location-state-shared.js.map +1 -0
  145. package/dist/browser/react/location-state.d.ts +23 -0
  146. package/dist/browser/react/location-state.d.ts.map +1 -0
  147. package/dist/browser/react/location-state.js +29 -0
  148. package/dist/browser/react/location-state.js.map +1 -0
  149. package/dist/browser/react/mount-context.d.ts +24 -0
  150. package/dist/browser/react/mount-context.d.ts.map +1 -0
  151. package/dist/browser/react/mount-context.js +24 -0
  152. package/dist/browser/react/mount-context.js.map +1 -0
  153. package/dist/browser/react/use-action.d.ts +64 -0
  154. package/dist/browser/react/use-action.d.ts.map +1 -0
  155. package/dist/browser/react/use-action.js +134 -0
  156. package/dist/browser/react/use-action.js.map +1 -0
  157. package/dist/browser/react/use-client-cache.d.ts +41 -0
  158. package/dist/browser/react/use-client-cache.d.ts.map +1 -0
  159. package/dist/browser/react/use-client-cache.js +39 -0
  160. package/dist/browser/react/use-client-cache.js.map +1 -0
  161. package/dist/browser/react/use-handle.d.ts +31 -0
  162. package/dist/browser/react/use-handle.d.ts.map +1 -0
  163. package/dist/browser/react/use-handle.js +144 -0
  164. package/dist/browser/react/use-handle.js.map +1 -0
  165. package/dist/browser/react/use-href.d.ts +33 -0
  166. package/dist/browser/react/use-href.d.ts.map +1 -0
  167. package/dist/browser/react/use-href.js +39 -0
  168. package/dist/browser/react/use-href.js.map +1 -0
  169. package/dist/browser/react/use-link-status.d.ts +37 -0
  170. package/dist/browser/react/use-link-status.d.ts.map +1 -0
  171. package/dist/browser/react/use-link-status.js +99 -0
  172. package/dist/browser/react/use-link-status.js.map +1 -0
  173. package/dist/browser/react/use-mount.d.ts +25 -0
  174. package/dist/browser/react/use-mount.d.ts.map +1 -0
  175. package/dist/browser/react/use-mount.js +30 -0
  176. package/dist/browser/react/use-mount.js.map +1 -0
  177. package/dist/browser/react/use-navigation.d.ts +27 -0
  178. package/dist/browser/react/use-navigation.d.ts.map +1 -0
  179. package/dist/browser/react/use-navigation.js +87 -0
  180. package/dist/browser/react/use-navigation.js.map +1 -0
  181. package/dist/browser/react/use-segments.d.ts +38 -0
  182. package/dist/browser/react/use-segments.d.ts.map +1 -0
  183. package/dist/browser/react/use-segments.js +130 -0
  184. package/dist/browser/react/use-segments.js.map +1 -0
  185. package/dist/browser/request-controller.d.ts +26 -0
  186. package/dist/browser/request-controller.d.ts.map +1 -0
  187. package/dist/browser/request-controller.js +147 -0
  188. package/dist/browser/request-controller.js.map +1 -0
  189. package/dist/browser/rsc-router.d.ts +129 -0
  190. package/dist/browser/rsc-router.d.ts.map +1 -0
  191. package/dist/browser/rsc-router.js +195 -0
  192. package/dist/browser/rsc-router.js.map +1 -0
  193. package/dist/browser/scroll-restoration.d.ts +93 -0
  194. package/dist/browser/scroll-restoration.d.ts.map +1 -0
  195. package/dist/browser/scroll-restoration.js +321 -0
  196. package/dist/browser/scroll-restoration.js.map +1 -0
  197. package/dist/browser/segment-structure-assert.d.ts +17 -0
  198. package/dist/browser/segment-structure-assert.d.ts.map +1 -0
  199. package/dist/browser/segment-structure-assert.js +59 -0
  200. package/dist/browser/segment-structure-assert.js.map +1 -0
  201. package/dist/browser/server-action-bridge.d.ts +26 -0
  202. package/dist/browser/server-action-bridge.d.ts.map +1 -0
  203. package/dist/browser/server-action-bridge.js +668 -0
  204. package/dist/browser/server-action-bridge.js.map +1 -0
  205. package/dist/browser/shallow.d.ts +12 -0
  206. package/dist/browser/shallow.d.ts.map +1 -0
  207. package/dist/browser/shallow.js +34 -0
  208. package/dist/browser/shallow.js.map +1 -0
  209. package/dist/browser/types.d.ts +369 -0
  210. package/dist/browser/types.d.ts.map +1 -0
  211. package/dist/browser/types.js +2 -0
  212. package/dist/browser/types.js.map +1 -0
  213. package/dist/build/__tests__/generate-cli.test.d.ts +2 -0
  214. package/dist/build/__tests__/generate-cli.test.d.ts.map +1 -0
  215. package/dist/build/__tests__/generate-cli.test.js +237 -0
  216. package/dist/build/__tests__/generate-cli.test.js.map +1 -0
  217. package/dist/build/__tests__/generate-manifest.test.d.ts +2 -0
  218. package/dist/build/__tests__/generate-manifest.test.d.ts.map +1 -0
  219. package/dist/build/__tests__/generate-manifest.test.js +119 -0
  220. package/dist/build/__tests__/generate-manifest.test.js.map +1 -0
  221. package/dist/build/__tests__/generate-route-types.test.d.ts +2 -0
  222. package/dist/build/__tests__/generate-route-types.test.d.ts.map +1 -0
  223. package/dist/build/__tests__/generate-route-types.test.js +620 -0
  224. package/dist/build/__tests__/generate-route-types.test.js.map +1 -0
  225. package/dist/build/__tests__/per-router-manifest.test.d.ts +2 -0
  226. package/dist/build/__tests__/per-router-manifest.test.d.ts.map +1 -0
  227. package/dist/build/__tests__/per-router-manifest.test.js +308 -0
  228. package/dist/build/__tests__/per-router-manifest.test.js.map +1 -0
  229. package/dist/build/generate-manifest.d.ts +81 -0
  230. package/dist/build/generate-manifest.d.ts.map +1 -0
  231. package/dist/build/generate-manifest.js +276 -0
  232. package/dist/build/generate-manifest.js.map +1 -0
  233. package/dist/build/generate-route-types.d.ts +115 -0
  234. package/dist/build/generate-route-types.d.ts.map +1 -0
  235. package/dist/build/generate-route-types.js +740 -0
  236. package/dist/build/generate-route-types.js.map +1 -0
  237. package/dist/build/index.d.ts +21 -0
  238. package/dist/build/index.d.ts.map +1 -0
  239. package/dist/build/index.js +21 -0
  240. package/dist/build/index.js.map +1 -0
  241. package/dist/build/route-trie.d.ts +71 -0
  242. package/dist/build/route-trie.d.ts.map +1 -0
  243. package/dist/build/route-trie.js +175 -0
  244. package/dist/build/route-trie.js.map +1 -0
  245. package/dist/cache/__tests__/cache-scope.test.d.ts +2 -0
  246. package/dist/cache/__tests__/cache-scope.test.d.ts.map +1 -0
  247. package/dist/cache/__tests__/cache-scope.test.js +208 -0
  248. package/dist/cache/__tests__/cache-scope.test.js.map +1 -0
  249. package/dist/cache/__tests__/document-cache.test.d.ts +2 -0
  250. package/dist/cache/__tests__/document-cache.test.d.ts.map +1 -0
  251. package/dist/cache/__tests__/document-cache.test.js +345 -0
  252. package/dist/cache/__tests__/document-cache.test.js.map +1 -0
  253. package/dist/cache/__tests__/memory-segment-store.test.d.ts +2 -0
  254. package/dist/cache/__tests__/memory-segment-store.test.d.ts.map +1 -0
  255. package/dist/cache/__tests__/memory-segment-store.test.js +425 -0
  256. package/dist/cache/__tests__/memory-segment-store.test.js.map +1 -0
  257. package/dist/cache/__tests__/memory-store.test.d.ts +2 -0
  258. package/dist/cache/__tests__/memory-store.test.d.ts.map +1 -0
  259. package/dist/cache/__tests__/memory-store.test.js +367 -0
  260. package/dist/cache/__tests__/memory-store.test.js.map +1 -0
  261. package/dist/cache/cache-scope.d.ts +102 -0
  262. package/dist/cache/cache-scope.d.ts.map +1 -0
  263. package/dist/cache/cache-scope.js +440 -0
  264. package/dist/cache/cache-scope.js.map +1 -0
  265. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts +2 -0
  266. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts.map +1 -0
  267. package/dist/cache/cf/__tests__/cf-cache-store.test.js +330 -0
  268. package/dist/cache/cf/__tests__/cf-cache-store.test.js.map +1 -0
  269. package/dist/cache/cf/cf-cache-store.d.ts +165 -0
  270. package/dist/cache/cf/cf-cache-store.d.ts.map +1 -0
  271. package/dist/cache/cf/cf-cache-store.js +242 -0
  272. package/dist/cache/cf/cf-cache-store.js.map +1 -0
  273. package/dist/cache/cf/index.d.ts +14 -0
  274. package/dist/cache/cf/index.d.ts.map +1 -0
  275. package/dist/cache/cf/index.js +17 -0
  276. package/dist/cache/cf/index.js.map +1 -0
  277. package/dist/cache/document-cache.d.ts +64 -0
  278. package/dist/cache/document-cache.d.ts.map +1 -0
  279. package/dist/cache/document-cache.js +228 -0
  280. package/dist/cache/document-cache.js.map +1 -0
  281. package/dist/cache/index.d.ts +19 -0
  282. package/dist/cache/index.d.ts.map +1 -0
  283. package/dist/cache/index.js +21 -0
  284. package/dist/cache/index.js.map +1 -0
  285. package/dist/cache/memory-segment-store.d.ts +110 -0
  286. package/dist/cache/memory-segment-store.d.ts.map +1 -0
  287. package/dist/cache/memory-segment-store.js +117 -0
  288. package/dist/cache/memory-segment-store.js.map +1 -0
  289. package/dist/cache/memory-store.d.ts +41 -0
  290. package/dist/cache/memory-store.d.ts.map +1 -0
  291. package/dist/cache/memory-store.js +191 -0
  292. package/dist/cache/memory-store.js.map +1 -0
  293. package/dist/cache/types.d.ts +317 -0
  294. package/dist/cache/types.d.ts.map +1 -0
  295. package/dist/cache/types.js +12 -0
  296. package/dist/cache/types.js.map +1 -0
  297. package/dist/client.d.ts +248 -0
  298. package/dist/client.d.ts.map +1 -0
  299. package/dist/client.js +367 -0
  300. package/dist/client.js.map +1 -0
  301. package/dist/client.rsc.d.ts +26 -0
  302. package/dist/client.rsc.d.ts.map +1 -0
  303. package/dist/client.rsc.js +46 -0
  304. package/dist/client.rsc.js.map +1 -0
  305. package/dist/component-utils.d.ts +36 -0
  306. package/dist/component-utils.d.ts.map +1 -0
  307. package/dist/component-utils.js +61 -0
  308. package/dist/component-utils.js.map +1 -0
  309. package/dist/components/DefaultDocument.d.ts +13 -0
  310. package/dist/components/DefaultDocument.d.ts.map +1 -0
  311. package/dist/components/DefaultDocument.js +15 -0
  312. package/dist/components/DefaultDocument.js.map +1 -0
  313. package/dist/debug.d.ts +58 -0
  314. package/dist/debug.d.ts.map +1 -0
  315. package/dist/debug.js +157 -0
  316. package/dist/debug.js.map +1 -0
  317. package/dist/default-error-boundary.d.ts +11 -0
  318. package/dist/default-error-boundary.d.ts.map +1 -0
  319. package/dist/default-error-boundary.js +45 -0
  320. package/dist/default-error-boundary.js.map +1 -0
  321. package/dist/deps/browser.d.ts +2 -0
  322. package/dist/deps/browser.d.ts.map +1 -0
  323. package/dist/deps/browser.js +3 -0
  324. package/dist/deps/browser.js.map +1 -0
  325. package/dist/deps/html-stream-client.d.ts +2 -0
  326. package/dist/deps/html-stream-client.d.ts.map +1 -0
  327. package/dist/deps/html-stream-client.js +3 -0
  328. package/dist/deps/html-stream-client.js.map +1 -0
  329. package/dist/deps/html-stream-server.d.ts +2 -0
  330. package/dist/deps/html-stream-server.d.ts.map +1 -0
  331. package/dist/deps/html-stream-server.js +3 -0
  332. package/dist/deps/html-stream-server.js.map +1 -0
  333. package/dist/deps/rsc.d.ts +2 -0
  334. package/dist/deps/rsc.d.ts.map +1 -0
  335. package/dist/deps/rsc.js +4 -0
  336. package/dist/deps/rsc.js.map +1 -0
  337. package/dist/deps/ssr.d.ts +2 -0
  338. package/dist/deps/ssr.d.ts.map +1 -0
  339. package/dist/deps/ssr.js +3 -0
  340. package/dist/deps/ssr.js.map +1 -0
  341. package/dist/errors.d.ts +174 -0
  342. package/dist/errors.d.ts.map +1 -0
  343. package/dist/errors.js +241 -0
  344. package/dist/errors.js.map +1 -0
  345. package/dist/handle.d.ts +78 -0
  346. package/dist/handle.d.ts.map +1 -0
  347. package/dist/handle.js +82 -0
  348. package/dist/handle.js.map +1 -0
  349. package/dist/handles/MetaTags.d.ts +14 -0
  350. package/dist/handles/MetaTags.d.ts.map +1 -0
  351. package/dist/handles/MetaTags.js +136 -0
  352. package/dist/handles/MetaTags.js.map +1 -0
  353. package/{src/handles/index.ts → dist/handles/index.d.ts} +1 -2
  354. package/dist/handles/index.d.ts.map +1 -0
  355. package/dist/handles/index.js +6 -0
  356. package/dist/handles/index.js.map +1 -0
  357. package/dist/handles/meta.d.ts +39 -0
  358. package/dist/handles/meta.d.ts.map +1 -0
  359. package/dist/handles/meta.js +202 -0
  360. package/dist/handles/meta.js.map +1 -0
  361. package/dist/host/__tests__/errors.test.d.ts +2 -0
  362. package/dist/host/__tests__/errors.test.d.ts.map +1 -0
  363. package/dist/host/__tests__/errors.test.js +76 -0
  364. package/dist/host/__tests__/errors.test.js.map +1 -0
  365. package/dist/host/__tests__/pattern-comprehensive.test.d.ts +2 -0
  366. package/dist/host/__tests__/pattern-comprehensive.test.d.ts.map +1 -0
  367. package/dist/host/__tests__/pattern-comprehensive.test.js +732 -0
  368. package/dist/host/__tests__/pattern-comprehensive.test.js.map +1 -0
  369. package/dist/host/__tests__/pattern-matcher.test.d.ts +2 -0
  370. package/dist/host/__tests__/pattern-matcher.test.d.ts.map +1 -0
  371. package/dist/host/__tests__/pattern-matcher.test.js +251 -0
  372. package/dist/host/__tests__/pattern-matcher.test.js.map +1 -0
  373. package/dist/host/__tests__/router.test.d.ts +2 -0
  374. package/dist/host/__tests__/router.test.d.ts.map +1 -0
  375. package/dist/host/__tests__/router.test.js +241 -0
  376. package/dist/host/__tests__/router.test.js.map +1 -0
  377. package/dist/host/__tests__/testing.test.d.ts +2 -0
  378. package/dist/host/__tests__/testing.test.d.ts.map +1 -0
  379. package/dist/host/__tests__/testing.test.js +64 -0
  380. package/dist/host/__tests__/testing.test.js.map +1 -0
  381. package/dist/host/__tests__/utils.test.d.ts +2 -0
  382. package/dist/host/__tests__/utils.test.d.ts.map +1 -0
  383. package/dist/host/__tests__/utils.test.js +29 -0
  384. package/dist/host/__tests__/utils.test.js.map +1 -0
  385. package/dist/host/cookie-handler.d.ts +34 -0
  386. package/dist/host/cookie-handler.d.ts.map +1 -0
  387. package/dist/host/cookie-handler.js +124 -0
  388. package/dist/host/cookie-handler.js.map +1 -0
  389. package/dist/host/errors.d.ts +56 -0
  390. package/dist/host/errors.d.ts.map +1 -0
  391. package/dist/host/errors.js +79 -0
  392. package/dist/host/errors.js.map +1 -0
  393. package/dist/host/index.d.ts +29 -0
  394. package/dist/host/index.d.ts.map +1 -0
  395. package/dist/host/index.js +32 -0
  396. package/dist/host/index.js.map +1 -0
  397. package/dist/host/pattern-matcher.d.ts +36 -0
  398. package/dist/host/pattern-matcher.d.ts.map +1 -0
  399. package/dist/host/pattern-matcher.js +172 -0
  400. package/dist/host/pattern-matcher.js.map +1 -0
  401. package/dist/host/router.d.ts +26 -0
  402. package/dist/host/router.d.ts.map +1 -0
  403. package/dist/host/router.js +218 -0
  404. package/dist/host/router.js.map +1 -0
  405. package/dist/host/testing.d.ts +36 -0
  406. package/dist/host/testing.d.ts.map +1 -0
  407. package/dist/host/testing.js +55 -0
  408. package/dist/host/testing.js.map +1 -0
  409. package/dist/host/types.d.ts +115 -0
  410. package/dist/host/types.d.ts.map +1 -0
  411. package/dist/host/types.js +7 -0
  412. package/dist/host/types.js.map +1 -0
  413. package/dist/host/utils.d.ts +21 -0
  414. package/dist/host/utils.d.ts.map +1 -0
  415. package/dist/host/utils.js +23 -0
  416. package/dist/host/utils.js.map +1 -0
  417. package/dist/href-client.d.ts +131 -0
  418. package/dist/href-client.d.ts.map +1 -0
  419. package/dist/href-client.js +64 -0
  420. package/dist/href-client.js.map +1 -0
  421. package/dist/href-context.d.ts +29 -0
  422. package/dist/href-context.d.ts.map +1 -0
  423. package/dist/href-context.js +21 -0
  424. package/dist/href-context.js.map +1 -0
  425. package/dist/index.d.ts +73 -0
  426. package/dist/index.d.ts.map +1 -0
  427. package/dist/index.js +91 -0
  428. package/dist/index.js.map +1 -0
  429. package/dist/index.rsc.d.ts +32 -0
  430. package/dist/index.rsc.d.ts.map +1 -0
  431. package/dist/index.rsc.js +40 -0
  432. package/dist/index.rsc.js.map +1 -0
  433. package/dist/internal-debug.d.ts +2 -0
  434. package/dist/internal-debug.d.ts.map +1 -0
  435. package/dist/internal-debug.js +5 -0
  436. package/dist/internal-debug.js.map +1 -0
  437. package/dist/loader.d.ts +14 -0
  438. package/dist/loader.d.ts.map +1 -0
  439. package/dist/loader.js +20 -0
  440. package/dist/loader.js.map +1 -0
  441. package/dist/loader.rsc.d.ts +19 -0
  442. package/dist/loader.rsc.d.ts.map +1 -0
  443. package/dist/loader.rsc.js +99 -0
  444. package/dist/loader.rsc.js.map +1 -0
  445. package/dist/network-error-thrower.d.ts +17 -0
  446. package/dist/network-error-thrower.d.ts.map +1 -0
  447. package/dist/network-error-thrower.js +14 -0
  448. package/dist/network-error-thrower.js.map +1 -0
  449. package/dist/outlet-context.d.ts +13 -0
  450. package/dist/outlet-context.d.ts.map +1 -0
  451. package/dist/outlet-context.js +3 -0
  452. package/dist/outlet-context.js.map +1 -0
  453. package/dist/prerender/__tests__/param-hash.test.d.ts +2 -0
  454. package/dist/prerender/__tests__/param-hash.test.d.ts.map +1 -0
  455. package/dist/prerender/__tests__/param-hash.test.js +148 -0
  456. package/dist/prerender/__tests__/param-hash.test.js.map +1 -0
  457. package/dist/prerender/param-hash.d.ts +16 -0
  458. package/dist/prerender/param-hash.d.ts.map +1 -0
  459. package/dist/prerender/param-hash.js +36 -0
  460. package/dist/prerender/param-hash.js.map +1 -0
  461. package/dist/prerender/store.d.ts +38 -0
  462. package/dist/prerender/store.d.ts.map +1 -0
  463. package/dist/prerender/store.js +61 -0
  464. package/dist/prerender/store.js.map +1 -0
  465. package/dist/prerender.d.ts +66 -0
  466. package/dist/prerender.d.ts.map +1 -0
  467. package/dist/prerender.js +57 -0
  468. package/dist/prerender.js.map +1 -0
  469. package/dist/reverse.d.ts +196 -0
  470. package/dist/reverse.d.ts.map +1 -0
  471. package/dist/reverse.js +78 -0
  472. package/dist/reverse.js.map +1 -0
  473. package/dist/root-error-boundary.d.ts +33 -0
  474. package/dist/root-error-boundary.d.ts.map +1 -0
  475. package/dist/root-error-boundary.js +165 -0
  476. package/dist/root-error-boundary.js.map +1 -0
  477. package/dist/route-content-wrapper.d.ts +46 -0
  478. package/dist/route-content-wrapper.d.ts.map +1 -0
  479. package/dist/route-content-wrapper.js +77 -0
  480. package/dist/route-content-wrapper.js.map +1 -0
  481. package/dist/route-definition.d.ts +421 -0
  482. package/dist/route-definition.d.ts.map +1 -0
  483. package/dist/route-definition.js +868 -0
  484. package/dist/route-definition.js.map +1 -0
  485. package/dist/route-map-builder.d.ts +155 -0
  486. package/dist/route-map-builder.d.ts.map +1 -0
  487. package/dist/route-map-builder.js +237 -0
  488. package/dist/route-map-builder.js.map +1 -0
  489. package/dist/route-types.d.ts +165 -0
  490. package/dist/route-types.d.ts.map +1 -0
  491. package/dist/route-types.js +7 -0
  492. package/dist/route-types.js.map +1 -0
  493. package/dist/router/__tests__/handler-context.test.d.ts +2 -0
  494. package/dist/router/__tests__/handler-context.test.d.ts.map +1 -0
  495. package/dist/router/__tests__/handler-context.test.js +65 -0
  496. package/dist/router/__tests__/handler-context.test.js.map +1 -0
  497. package/dist/router/__tests__/loader-cycle-detection.test.d.ts +2 -0
  498. package/dist/router/__tests__/loader-cycle-detection.test.d.ts.map +1 -0
  499. package/dist/router/__tests__/loader-cycle-detection.test.js +221 -0
  500. package/dist/router/__tests__/loader-cycle-detection.test.js.map +1 -0
  501. package/dist/router/__tests__/match-context.test.d.ts +2 -0
  502. package/dist/router/__tests__/match-context.test.d.ts.map +1 -0
  503. package/dist/router/__tests__/match-context.test.js +92 -0
  504. package/dist/router/__tests__/match-context.test.js.map +1 -0
  505. package/dist/router/__tests__/match-pipelines.test.d.ts +2 -0
  506. package/dist/router/__tests__/match-pipelines.test.d.ts.map +1 -0
  507. package/dist/router/__tests__/match-pipelines.test.js +417 -0
  508. package/dist/router/__tests__/match-pipelines.test.js.map +1 -0
  509. package/dist/router/__tests__/match-result.test.d.ts +2 -0
  510. package/dist/router/__tests__/match-result.test.d.ts.map +1 -0
  511. package/dist/router/__tests__/match-result.test.js +457 -0
  512. package/dist/router/__tests__/match-result.test.js.map +1 -0
  513. package/dist/router/__tests__/on-error.test.d.ts +2 -0
  514. package/dist/router/__tests__/on-error.test.d.ts.map +1 -0
  515. package/dist/router/__tests__/on-error.test.js +678 -0
  516. package/dist/router/__tests__/on-error.test.js.map +1 -0
  517. package/dist/router/__tests__/pattern-matching.test.d.ts +2 -0
  518. package/dist/router/__tests__/pattern-matching.test.d.ts.map +1 -0
  519. package/dist/router/__tests__/pattern-matching.test.js +629 -0
  520. package/dist/router/__tests__/pattern-matching.test.js.map +1 -0
  521. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts +2 -0
  522. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts.map +1 -0
  523. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js +155 -0
  524. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js.map +1 -0
  525. package/dist/router/error-handling.d.ts +77 -0
  526. package/dist/router/error-handling.d.ts.map +1 -0
  527. package/dist/router/error-handling.js +202 -0
  528. package/dist/router/error-handling.js.map +1 -0
  529. package/dist/router/handler-context.d.ts +20 -0
  530. package/dist/router/handler-context.d.ts.map +1 -0
  531. package/dist/router/handler-context.js +198 -0
  532. package/dist/router/handler-context.js.map +1 -0
  533. package/dist/router/intercept-resolution.d.ts +66 -0
  534. package/dist/router/intercept-resolution.d.ts.map +1 -0
  535. package/dist/router/intercept-resolution.js +246 -0
  536. package/dist/router/intercept-resolution.js.map +1 -0
  537. package/dist/router/loader-resolution.d.ts +64 -0
  538. package/dist/router/loader-resolution.d.ts.map +1 -0
  539. package/dist/router/loader-resolution.js +284 -0
  540. package/dist/router/loader-resolution.js.map +1 -0
  541. package/dist/router/logging.d.ts +15 -0
  542. package/dist/router/logging.d.ts.map +1 -0
  543. package/dist/router/logging.js +99 -0
  544. package/dist/router/logging.js.map +1 -0
  545. package/dist/router/manifest.d.ts +22 -0
  546. package/dist/router/manifest.d.ts.map +1 -0
  547. package/dist/router/manifest.js +181 -0
  548. package/dist/router/manifest.js.map +1 -0
  549. package/dist/router/match-api.d.ts +35 -0
  550. package/dist/router/match-api.d.ts.map +1 -0
  551. package/dist/router/match-api.js +406 -0
  552. package/dist/router/match-api.js.map +1 -0
  553. package/dist/router/match-context.d.ts +206 -0
  554. package/dist/router/match-context.d.ts.map +1 -0
  555. package/dist/router/match-context.js +17 -0
  556. package/dist/router/match-context.js.map +1 -0
  557. package/dist/router/match-middleware/background-revalidation.d.ts +127 -0
  558. package/dist/router/match-middleware/background-revalidation.d.ts.map +1 -0
  559. package/dist/router/match-middleware/background-revalidation.js +75 -0
  560. package/dist/router/match-middleware/background-revalidation.js.map +1 -0
  561. package/dist/router/match-middleware/cache-lookup.d.ts +112 -0
  562. package/dist/router/match-middleware/cache-lookup.d.ts.map +1 -0
  563. package/dist/router/match-middleware/cache-lookup.js +257 -0
  564. package/dist/router/match-middleware/cache-lookup.js.map +1 -0
  565. package/dist/router/match-middleware/cache-store.d.ts +113 -0
  566. package/dist/router/match-middleware/cache-store.d.ts.map +1 -0
  567. package/dist/router/match-middleware/cache-store.js +108 -0
  568. package/dist/router/match-middleware/cache-store.js.map +1 -0
  569. package/dist/router/match-middleware/index.d.ts +81 -0
  570. package/dist/router/match-middleware/index.d.ts.map +1 -0
  571. package/dist/router/match-middleware/index.js +80 -0
  572. package/dist/router/match-middleware/index.js.map +1 -0
  573. package/dist/router/match-middleware/intercept-resolution.d.ts +117 -0
  574. package/dist/router/match-middleware/intercept-resolution.d.ts.map +1 -0
  575. package/dist/router/match-middleware/intercept-resolution.js +134 -0
  576. package/dist/router/match-middleware/intercept-resolution.js.map +1 -0
  577. package/dist/router/match-middleware/segment-resolution.d.ts +99 -0
  578. package/dist/router/match-middleware/segment-resolution.d.ts.map +1 -0
  579. package/dist/router/match-middleware/segment-resolution.js +53 -0
  580. package/dist/router/match-middleware/segment-resolution.js.map +1 -0
  581. package/dist/router/match-pipelines.d.ts +147 -0
  582. package/dist/router/match-pipelines.d.ts.map +1 -0
  583. package/dist/router/match-pipelines.js +82 -0
  584. package/dist/router/match-pipelines.js.map +1 -0
  585. package/dist/router/match-result.d.ts +126 -0
  586. package/dist/router/match-result.d.ts.map +1 -0
  587. package/dist/router/match-result.js +93 -0
  588. package/dist/router/match-result.js.map +1 -0
  589. package/dist/router/metrics.d.ts +20 -0
  590. package/dist/router/metrics.d.ts.map +1 -0
  591. package/dist/router/metrics.js +47 -0
  592. package/dist/router/metrics.js.map +1 -0
  593. package/dist/router/middleware.d.ts +249 -0
  594. package/dist/router/middleware.d.ts.map +1 -0
  595. package/dist/router/middleware.js +434 -0
  596. package/dist/router/middleware.js.map +1 -0
  597. package/dist/router/middleware.test.d.ts +2 -0
  598. package/dist/router/middleware.test.d.ts.map +1 -0
  599. package/dist/router/middleware.test.js +816 -0
  600. package/dist/router/middleware.test.js.map +1 -0
  601. package/dist/router/pattern-matching.d.ts +149 -0
  602. package/dist/router/pattern-matching.d.ts.map +1 -0
  603. package/dist/router/pattern-matching.js +349 -0
  604. package/dist/router/pattern-matching.js.map +1 -0
  605. package/dist/router/revalidation.d.ts +44 -0
  606. package/dist/router/revalidation.d.ts.map +1 -0
  607. package/dist/router/revalidation.js +147 -0
  608. package/dist/router/revalidation.js.map +1 -0
  609. package/dist/router/router-context.d.ts +135 -0
  610. package/dist/router/router-context.d.ts.map +1 -0
  611. package/dist/router/router-context.js +36 -0
  612. package/dist/router/router-context.js.map +1 -0
  613. package/dist/router/segment-resolution.d.ts +127 -0
  614. package/dist/router/segment-resolution.d.ts.map +1 -0
  615. package/dist/router/segment-resolution.js +919 -0
  616. package/dist/router/segment-resolution.js.map +1 -0
  617. package/dist/router/trie-matching.d.ts +40 -0
  618. package/dist/router/trie-matching.d.ts.map +1 -0
  619. package/dist/router/trie-matching.js +127 -0
  620. package/dist/router/trie-matching.js.map +1 -0
  621. package/dist/router/types.d.ts +136 -0
  622. package/dist/router/types.d.ts.map +1 -0
  623. package/dist/router/types.js +7 -0
  624. package/dist/router/types.js.map +1 -0
  625. package/dist/router.d.ts +753 -0
  626. package/dist/router.d.ts.map +1 -0
  627. package/dist/router.gen.d.ts +6 -0
  628. package/dist/router.gen.d.ts.map +1 -0
  629. package/dist/router.gen.js +6 -0
  630. package/dist/router.gen.js.map +1 -0
  631. package/dist/router.js +1304 -0
  632. package/dist/router.js.map +1 -0
  633. package/dist/rsc/__tests__/helpers.test.d.ts +2 -0
  634. package/dist/rsc/__tests__/helpers.test.d.ts.map +1 -0
  635. package/dist/rsc/__tests__/helpers.test.js +140 -0
  636. package/dist/rsc/__tests__/helpers.test.js.map +1 -0
  637. package/dist/rsc/handler.d.ts +45 -0
  638. package/dist/rsc/handler.d.ts.map +1 -0
  639. package/dist/rsc/handler.js +1172 -0
  640. package/dist/rsc/handler.js.map +1 -0
  641. package/dist/rsc/helpers.d.ts +16 -0
  642. package/dist/rsc/helpers.d.ts.map +1 -0
  643. package/dist/rsc/helpers.js +55 -0
  644. package/dist/rsc/helpers.js.map +1 -0
  645. package/dist/rsc/index.d.ts +22 -0
  646. package/dist/rsc/index.d.ts.map +1 -0
  647. package/dist/rsc/index.js +23 -0
  648. package/dist/rsc/index.js.map +1 -0
  649. package/dist/rsc/nonce.d.ts +9 -0
  650. package/dist/rsc/nonce.d.ts.map +1 -0
  651. package/dist/rsc/nonce.js +18 -0
  652. package/dist/rsc/nonce.js.map +1 -0
  653. package/dist/rsc/types.d.ts +206 -0
  654. package/dist/rsc/types.d.ts.map +1 -0
  655. package/dist/rsc/types.js +8 -0
  656. package/dist/rsc/types.js.map +1 -0
  657. package/dist/search-params.d.ts +103 -0
  658. package/dist/search-params.d.ts.map +1 -0
  659. package/dist/search-params.js +74 -0
  660. package/dist/search-params.js.map +1 -0
  661. package/dist/segment-system.d.ts +75 -0
  662. package/dist/segment-system.d.ts.map +1 -0
  663. package/dist/segment-system.js +336 -0
  664. package/dist/segment-system.js.map +1 -0
  665. package/dist/server/context.d.ts +245 -0
  666. package/dist/server/context.d.ts.map +1 -0
  667. package/dist/server/context.js +197 -0
  668. package/dist/server/context.js.map +1 -0
  669. package/dist/server/fetchable-loader-store.d.ts +18 -0
  670. package/dist/server/fetchable-loader-store.d.ts.map +1 -0
  671. package/dist/server/fetchable-loader-store.js +18 -0
  672. package/dist/server/fetchable-loader-store.js.map +1 -0
  673. package/dist/server/handle-store.d.ts +85 -0
  674. package/dist/server/handle-store.d.ts.map +1 -0
  675. package/dist/server/handle-store.js +142 -0
  676. package/dist/server/handle-store.js.map +1 -0
  677. package/dist/server/loader-registry.d.ts +55 -0
  678. package/dist/server/loader-registry.d.ts.map +1 -0
  679. package/dist/server/loader-registry.js +132 -0
  680. package/dist/server/loader-registry.js.map +1 -0
  681. package/dist/server/request-context.d.ts +226 -0
  682. package/dist/server/request-context.d.ts.map +1 -0
  683. package/dist/server/request-context.js +290 -0
  684. package/dist/server/request-context.js.map +1 -0
  685. package/dist/server/root-layout.d.ts +4 -0
  686. package/dist/server/root-layout.d.ts.map +1 -0
  687. package/dist/server/root-layout.js +5 -0
  688. package/dist/server/root-layout.js.map +1 -0
  689. package/dist/server.d.ts +15 -0
  690. package/dist/server.d.ts.map +1 -0
  691. package/dist/server.js +20 -0
  692. package/dist/server.js.map +1 -0
  693. package/dist/ssr/__tests__/ssr-handler.test.d.ts +2 -0
  694. package/dist/ssr/__tests__/ssr-handler.test.d.ts.map +1 -0
  695. package/dist/ssr/__tests__/ssr-handler.test.js +132 -0
  696. package/dist/ssr/__tests__/ssr-handler.test.js.map +1 -0
  697. package/dist/ssr/index.d.ts +98 -0
  698. package/dist/ssr/index.d.ts.map +1 -0
  699. package/dist/ssr/index.js +158 -0
  700. package/dist/ssr/index.js.map +1 -0
  701. package/dist/static-handler.d.ts +50 -0
  702. package/dist/static-handler.d.ts.map +1 -0
  703. package/dist/static-handler.gen.d.ts +5 -0
  704. package/dist/static-handler.gen.d.ts.map +1 -0
  705. package/dist/static-handler.gen.js +5 -0
  706. package/dist/static-handler.gen.js.map +1 -0
  707. package/dist/static-handler.js +29 -0
  708. package/dist/static-handler.js.map +1 -0
  709. package/dist/theme/ThemeProvider.d.ts +20 -0
  710. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  711. package/dist/theme/ThemeProvider.js +240 -0
  712. package/dist/theme/ThemeProvider.js.map +1 -0
  713. package/dist/theme/ThemeScript.d.ts +48 -0
  714. package/dist/theme/ThemeScript.d.ts.map +1 -0
  715. package/dist/theme/ThemeScript.js +13 -0
  716. package/dist/theme/ThemeScript.js.map +1 -0
  717. package/dist/theme/__tests__/theme.test.d.ts +2 -0
  718. package/dist/theme/__tests__/theme.test.d.ts.map +1 -0
  719. package/dist/theme/__tests__/theme.test.js +103 -0
  720. package/dist/theme/__tests__/theme.test.js.map +1 -0
  721. package/dist/theme/constants.d.ts +29 -0
  722. package/dist/theme/constants.d.ts.map +1 -0
  723. package/dist/theme/constants.js +48 -0
  724. package/dist/theme/constants.js.map +1 -0
  725. package/dist/theme/index.d.ts +31 -0
  726. package/dist/theme/index.d.ts.map +1 -0
  727. package/dist/theme/index.js +36 -0
  728. package/dist/theme/index.js.map +1 -0
  729. package/dist/theme/theme-context.d.ts +40 -0
  730. package/dist/theme/theme-context.d.ts.map +1 -0
  731. package/dist/theme/theme-context.js +60 -0
  732. package/dist/theme/theme-context.js.map +1 -0
  733. package/dist/theme/theme-script.d.ts +27 -0
  734. package/dist/theme/theme-script.d.ts.map +1 -0
  735. package/dist/theme/theme-script.js +147 -0
  736. package/dist/theme/theme-script.js.map +1 -0
  737. package/dist/theme/types.d.ts +163 -0
  738. package/dist/theme/types.d.ts.map +1 -0
  739. package/dist/theme/types.js +11 -0
  740. package/dist/theme/types.js.map +1 -0
  741. package/dist/theme/use-theme.d.ts +12 -0
  742. package/dist/theme/use-theme.d.ts.map +1 -0
  743. package/dist/theme/use-theme.js +40 -0
  744. package/dist/theme/use-theme.js.map +1 -0
  745. package/dist/types.d.ts +1479 -0
  746. package/dist/types.d.ts.map +1 -0
  747. package/dist/types.js +10 -0
  748. package/dist/types.js.map +1 -0
  749. package/dist/urls.d.ts +441 -0
  750. package/dist/urls.d.ts.map +1 -0
  751. package/dist/urls.gen.d.ts +8 -0
  752. package/dist/urls.gen.d.ts.map +1 -0
  753. package/dist/urls.gen.js +8 -0
  754. package/dist/urls.gen.js.map +1 -0
  755. package/dist/urls.js +443 -0
  756. package/dist/urls.js.map +1 -0
  757. package/dist/use-loader.d.ts +127 -0
  758. package/dist/use-loader.d.ts.map +1 -0
  759. package/dist/use-loader.js +237 -0
  760. package/dist/use-loader.js.map +1 -0
  761. package/dist/vite/__tests__/ast-handler-extract.test.d.ts +2 -0
  762. package/dist/vite/__tests__/ast-handler-extract.test.d.ts.map +1 -0
  763. package/dist/vite/__tests__/ast-handler-extract.test.js +294 -0
  764. package/dist/vite/__tests__/ast-handler-extract.test.js.map +1 -0
  765. package/dist/vite/__tests__/expose-id-utils.test.d.ts +2 -0
  766. package/dist/vite/__tests__/expose-id-utils.test.d.ts.map +1 -0
  767. package/dist/vite/__tests__/expose-id-utils.test.js +224 -0
  768. package/dist/vite/__tests__/expose-id-utils.test.js.map +1 -0
  769. package/dist/vite/__tests__/expose-internal-ids.test.d.ts +2 -0
  770. package/dist/vite/__tests__/expose-internal-ids.test.d.ts.map +1 -0
  771. package/dist/vite/__tests__/expose-internal-ids.test.js +647 -0
  772. package/dist/vite/__tests__/expose-internal-ids.test.js.map +1 -0
  773. package/dist/vite/__tests__/expose-router-id.test.d.ts +2 -0
  774. package/dist/vite/__tests__/expose-router-id.test.d.ts.map +1 -0
  775. package/dist/vite/__tests__/expose-router-id.test.js +39 -0
  776. package/dist/vite/__tests__/expose-router-id.test.js.map +1 -0
  777. package/dist/vite/ast-handler-extract.d.ts +49 -0
  778. package/dist/vite/ast-handler-extract.d.ts.map +1 -0
  779. package/dist/vite/ast-handler-extract.js +249 -0
  780. package/dist/vite/ast-handler-extract.js.map +1 -0
  781. package/dist/vite/expose-action-id.d.ts +19 -0
  782. package/dist/vite/expose-action-id.d.ts.map +1 -0
  783. package/dist/vite/expose-action-id.js +250 -0
  784. package/dist/vite/expose-action-id.js.map +1 -0
  785. package/dist/vite/expose-id-utils.d.ts +69 -0
  786. package/dist/vite/expose-id-utils.d.ts.map +1 -0
  787. package/dist/vite/expose-id-utils.js +289 -0
  788. package/dist/vite/expose-id-utils.js.map +1 -0
  789. package/dist/vite/expose-internal-ids.d.ts +22 -0
  790. package/dist/vite/expose-internal-ids.d.ts.map +1 -0
  791. package/dist/vite/expose-internal-ids.js +886 -0
  792. package/dist/vite/expose-internal-ids.js.map +1 -0
  793. package/dist/vite/index.d.ts +149 -0
  794. package/dist/vite/index.d.ts.map +1 -0
  795. package/dist/vite/index.js +307 -26
  796. package/dist/vite/index.js.bak +5448 -0
  797. package/dist/vite/index.js.map +1 -0
  798. package/dist/vite/index.named-routes.gen.ts +103 -0
  799. package/dist/vite/package-resolution.d.ts +43 -0
  800. package/dist/vite/package-resolution.d.ts.map +1 -0
  801. package/dist/vite/package-resolution.js +112 -0
  802. package/dist/vite/package-resolution.js.map +1 -0
  803. package/dist/vite/virtual-entries.d.ts +25 -0
  804. package/dist/vite/virtual-entries.d.ts.map +1 -0
  805. package/dist/vite/virtual-entries.js +110 -0
  806. package/dist/vite/virtual-entries.js.map +1 -0
  807. package/package.json +19 -18
  808. package/skills/breadcrumbs/SKILL.md +60 -0
  809. package/skills/server-actions/SKILL.md +25 -1
  810. package/skills/testing/SKILL.md +17 -17
  811. package/skills/testing/cache-prerender.md +29 -3
  812. package/skills/testing/flight.md +13 -10
  813. package/skills/testing/render-handler.md +3 -0
  814. package/skills/testing/server-tree.md +1 -1
  815. package/skills/testing/setup.md +1 -1
  816. package/src/browser/partial-update.ts +22 -1
  817. package/src/browser/react/use-router.ts +2 -1
  818. package/src/browser/rsc-router.tsx +8 -1
  819. package/src/browser/server-action-bridge.ts +51 -3
  820. package/src/browser/types.ts +23 -4
  821. package/src/browser/validate-redirect-origin.ts +43 -15
  822. package/src/build/index.ts +8 -9
  823. package/src/build/route-trie.ts +43 -11
  824. package/src/cache/cache-runtime.ts +21 -0
  825. package/src/cache/document-cache.ts +10 -0
  826. package/src/cache/profile-registry.ts +6 -34
  827. package/src/client.rsc.tsx +3 -0
  828. package/src/client.tsx +5 -0
  829. package/src/context-var.ts +12 -0
  830. package/src/defer.ts +196 -0
  831. package/src/handles/breadcrumbs.ts +16 -5
  832. package/src/index.rsc.ts +7 -0
  833. package/src/index.ts +7 -0
  834. package/src/redirect-origin.ts +100 -0
  835. package/src/route-definition/redirect.ts +32 -8
  836. package/src/route-definition/resolve-handler-use.ts +6 -0
  837. package/src/router/find-match.ts +0 -1
  838. package/src/router/loader-resolution.ts +22 -21
  839. package/src/router/match-middleware/cache-lookup.ts +95 -125
  840. package/src/router/match-middleware/segment-resolution.ts +0 -1
  841. package/src/router/middleware-types.ts +0 -1
  842. package/src/router/middleware.ts +71 -31
  843. package/src/router/pattern-matching.ts +4 -23
  844. package/src/router/prerender-match.ts +27 -6
  845. package/src/router/revalidation.ts +9 -4
  846. package/src/router/route-snapshot.ts +0 -1
  847. package/src/router/router-context.ts +0 -1
  848. package/src/router/router-interfaces.ts +10 -0
  849. package/src/router/segment-resolution/loader-cache.ts +25 -10
  850. package/src/router/segment-resolution/revalidation.ts +185 -243
  851. package/src/router/segment-wrappers.ts +0 -3
  852. package/src/router/trie-matching.ts +3 -5
  853. package/src/router.ts +4 -9
  854. package/src/rsc/handler-context.ts +1 -0
  855. package/src/rsc/handler.ts +9 -2
  856. package/src/rsc/helpers.ts +56 -3
  857. package/src/rsc/json-route-result.ts +38 -0
  858. package/src/rsc/origin-guard.ts +10 -4
  859. package/src/rsc/progressive-enhancement.ts +8 -0
  860. package/src/rsc/redirect-guard.ts +99 -0
  861. package/src/rsc/response-route-handler.ts +23 -18
  862. package/src/rsc/server-action.ts +4 -1
  863. package/src/rsc/types.ts +6 -2
  864. package/src/server/request-context.ts +22 -13
  865. package/src/testing/cache-status.ts +44 -1
  866. package/src/testing/dispatch.ts +43 -6
  867. package/src/testing/e2e/index.ts +1 -0
  868. package/src/testing/flight.ts +48 -2
  869. package/src/testing/index.ts +1 -0
  870. package/src/testing/render-handler.ts +31 -12
  871. package/src/testing/render-route.tsx +75 -13
  872. package/src/types/handler-context.ts +28 -1
  873. package/src/vite/discovery/discover-routers.ts +22 -11
  874. package/src/vite/discovery/prerender-collection.ts +2 -4
  875. package/src/vite/discovery/state.ts +5 -0
  876. package/src/vite/plugin-types.ts +32 -8
  877. package/src/vite/plugins/refresh-cmd.ts +1 -1
  878. package/src/vite/plugins/use-cache-transform.ts +21 -10
  879. package/src/vite/rango.ts +1 -0
  880. package/src/vite/router-discovery.ts +7 -0
  881. package/src/router/middleware-cookies.ts +0 -42
@@ -81,6 +81,66 @@ path("/product/:id", async (ctx) => {
81
81
  Async content is a `Promise<ReactNode>`. Resolve it in your component
82
82
  with React's `use()` hook wrapped in `<Suspense>`.
83
83
 
84
+ ### Deferred content (decide now, resolve from a deep component)
85
+
86
+ When the handler should DECIDE to push a crumb (it holds `ctx`, so the decision
87
+ must land before the handles stream seals) but the value is produced far away — by
88
+ a deep async component, not the handler — call `.defer()` on the push function.
89
+ `ctx.use(Handle)` returns the push function; `.defer(options)` reserves the crumb's
90
+ slot synchronously and returns a **resolver that is push-equal** — you call it
91
+ later, anywhere in the render, with the same argument you'd have passed to the
92
+ push (a value, a `Promise`, or a thunk). The only added behavior is a timeout, so a
93
+ forgotten resolve can't hold the Flight stream (and the HTTP response) open forever.
94
+
95
+ Reserve the slot in the handler, then resolve it from a nested async component
96
+ that closes over the resolver — no extra wiring (the resolver is a plain closure,
97
+ not outlet context):
98
+
99
+ ```tsx
100
+ import { Breadcrumbs } from "@rangojs/router";
101
+ import { Outlet } from "@rangojs/router/client";
102
+ import { Suspense } from "react";
103
+
104
+ function DocsLayout(ctx) {
105
+ const breadcrumb = ctx.use(Breadcrumbs);
106
+ // Decide now (the slot is reserved before the stream seals); resolve later.
107
+ const resolveCrumb = breadcrumb.defer({ timeoutMs: 5000, else: null });
108
+
109
+ // Deep, async, far from the handler — closes over the resolver, never touches ctx.
110
+ // Same call shape as breadcrumb({ ... }), just deferred:
111
+ async function LiveCrumb() {
112
+ const n = await countOpenIssues();
113
+ resolveCrumb({ label: "Docs", href: "/docs", content: <span>{n}</span> });
114
+ return null;
115
+ }
116
+
117
+ return (
118
+ <>
119
+ <Suspense>
120
+ <LiveCrumb />
121
+ </Suspense>
122
+ <Outlet />
123
+ </>
124
+ );
125
+ }
126
+ ```
127
+
128
+ If the resolver is never called, the slot auto-resolves to `else` after
129
+ `timeoutMs` (default 10s) and warns in dev — graceful degradation instead of a
130
+ hung request. `timeoutMs: 0` or `Infinity` disable the timeout intentionally; any
131
+ other non-finite or negative value falls back to the default rather than silently
132
+ disabling the safety net.
133
+
134
+ **Consumer note:** because `.defer()` reserves the slot for the WHOLE item, a
135
+ client reading the handle (`useHandle(Breadcrumbs)`) sees that entry as a
136
+ `Promise` until it resolves. Type such reads with the exported
137
+ `DeferredHandleEntry<BreadcrumbItem>` (from `@rangojs/router/client`); a
138
+ deferred-aware consumer should `use()` thenable entries inside `<Suspense>`, while
139
+ a simple one can skip them (`typeof entry.then === "function"`). Use `.defer()`
140
+ only when even `label`/`href` are unknown at handler time — if you know them and
141
+ only the `content` is async, push a concrete item with a `Promise` `content` field
142
+ instead (no `.defer()` needed).
143
+
84
144
  ## Consuming Breadcrumbs (Client)
85
145
 
86
146
  Use `useHandle(Breadcrumbs)` in a client component to read the accumulated items:
@@ -465,7 +465,10 @@ See `/middleware` for the full cross-segment revalidation contract.
465
465
 
466
466
  `redirect()` works inside actions. Both `return redirect(...)` and
467
467
  `throw redirect(...)` are supported and behave the same way for the
468
- client. Throwing is clearer when the redirect is conditional.
468
+ client. Throwing is clearer when the redirect is conditional, and it keeps
469
+ the action's return type narrow (e.g. `Promise<void>`) — `redirect()` returns
470
+ a `Response`, so the `return` form needs `Promise<Response>` in the signature.
471
+ Prefer `throw redirect(...)`; never cast with `as any`.
469
472
 
470
473
  ```typescript
471
474
  "use server";
@@ -490,6 +493,27 @@ Redirects from actions render the **target** route tree's matched segments
490
493
  source page's — the target is what the user sees next. See `/hooks
491
494
  useLocationState` for reading flash state on the target page.
492
495
 
496
+ ### Same-origin by default (open-redirect protection)
497
+
498
+ `redirect()` is same-origin by default on every path — JS, no-JS PE, and
499
+ full-page. A cross-origin target (e.g. from unvalidated user input) is blocked
500
+ and the user is sent to the app root instead, so `redirect(userInput)` can never
501
+ become an open redirect. To intentionally redirect off-host (an OAuth provider,
502
+ say), opt in explicitly:
503
+
504
+ ```typescript
505
+ throw redirect("https://accounts.google.com/o/oauth2/v2/auth?...", {
506
+ external: true,
507
+ });
508
+ ```
509
+
510
+ `{ external: true }` is the audit point: passing user input with it re-opens the
511
+ cross-origin risk and is your responsibility (the Rails `allow_other_host: true`
512
+ model). Omit it and off-host targets stay blocked. `external` only waives the
513
+ **same-origin** rule, not scheme safety: the target must be `http(s)` — a
514
+ `javascript:` or `data:` URL is still neutralized, so a forged or mistaken
515
+ `external` target can never become a scriptable navigation.
516
+
493
517
  ## Error Handling
494
518
 
495
519
  ### Validation errors — return them as state
@@ -81,23 +81,23 @@ The single rule that drives everything:
81
81
 
82
82
  Each primitive links to its sub-file (API + recipe + caveats).
83
83
 
84
- | The behavior is… | Layer | Primitive | Import root |
85
- | --------------------------------------------------------------------------------------------------------- | ------------ | ------------------------------------------------------------------------------ | -------------------------------- |
86
- | a pure function / `reverse` / `href` / a predicate (`revalidate`, `isAction`) | unit + types | [`reverse`/`@ts-expect-error`](./reverse-and-types.md) | `@rangojs/router/testing` |
87
- | one loader's data logic | unit (node) | [`runLoader`](./loader.md) | `@rangojs/router/testing` |
88
- | a loader's cookie / header / redirect output (auth-loader pattern) | unit (node) | [`runLoaderResult`](./loader.md) | `@rangojs/router/testing` |
89
- | one middleware's ordering / short-circuit / cookie+header merge | unit (node) | [`runMiddleware`](./middleware.md) | `@rangojs/router/testing` |
90
- | a `"use server"` action's cookie / header / flash output (even on `throw redirect()`) | unit (node) | [`runInRequestContext`](./server-actions.md) | `@rangojs/router/testing` |
91
- | a handle's `collect`/accumulator, or a seeded handle read | unit | [`collectHandle` / seeded `handles`](./handles.md) | `@rangojs/router/testing[/dom]` |
92
- | a CLIENT component reading router context (`useParams`/`useReverse`/`Outlet`/`useNavigation`/`useLoader`) | unit (DOM) | [`renderRoute`](./client-components.md) | `@rangojs/router/testing/dom` |
93
- | a redirect / status / headers / cookies / **response route** (json/text/html/xml/md), no Flight | integration | [`dispatch`](./response-routes.md) | `@rangojs/router/testing` |
94
- | a real async **Server Component** / Flight serialization shape | RSC unit | [`renderToFlightString` + `toMatchFlight`](./flight.md) | `@rangojs/router/testing/flight` |
95
- | a client island's **typed props** / the **server-rendered** host content | RSC unit | [`renderServerTree` + `findClientBoundaries`/`findElements`](./server-tree.md) | `@rangojs/router/testing/flight` |
96
- | a real route **handler** `(ctx) => rsc` (params/loaders/vars -> rendered RSC + effects) | RSC unit | [`renderHandler`](./render-handler.md) | `@rangojs/router/testing/flight` |
97
- | navigation, hydration, PE parity, view transitions, real SSR | e2e | [`createRangoE2E` -> `parityDescribe`/`expectParity`](./e2e-parity.md) | `@rangojs/router/testing/e2e` |
98
- | cache hit/miss/stale, prerender (= a cache hit by design) | e2e + signal | [`assertCacheStatus` / telemetry sink](./cache-prerender.md) | `@rangojs/router/testing[/e2e]` |
99
- | generated route map drift vs runtime | unit (node) | [`assertGeneratedRoutesMatch`](./reverse-and-types.md) | `@rangojs/router/testing` |
100
- | a platform binding (`env.DB` / Durable Object / `env.R2`) | unit/integr. | [your own double via `env`](./bindings.md) | (any primitive's `env` option) |
84
+ | The behavior is… | Layer | Primitive | Import root |
85
+ | ---------------------------------------------------------------------------------------------------------------------------------- | ------------ | --------------------------------------------------------------------------------------------- | -------------------------------- |
86
+ | a pure function / `reverse` / `href` / a predicate (`revalidate`, `isAction`) | unit + types | [`reverse`/`@ts-expect-error`](./reverse-and-types.md) | `@rangojs/router/testing` |
87
+ | one loader's data logic | unit (node) | [`runLoader`](./loader.md) | `@rangojs/router/testing` |
88
+ | a loader's cookie / header / redirect output (auth-loader pattern) | unit (node) | [`runLoaderResult`](./loader.md) | `@rangojs/router/testing` |
89
+ | one middleware's ordering / short-circuit / cookie+header merge | unit (node) | [`runMiddleware`](./middleware.md) | `@rangojs/router/testing` |
90
+ | a `"use server"` action's cookie / header / flash output (even on `throw redirect()`) | unit (node) | [`runInRequestContext`](./server-actions.md) | `@rangojs/router/testing` |
91
+ | a handle's `collect`/accumulator, or a seeded handle read | unit | [`collectHandle` / seeded `handles`](./handles.md) | `@rangojs/router/testing[/dom]` |
92
+ | a CLIENT component reading router context (`useParams`/`useReverse`/`Outlet`/`useNavigation`/`useLoader`) | unit (DOM) | [`renderRoute`](./client-components.md) | `@rangojs/router/testing/dom` |
93
+ | a redirect / status / headers / cookies / **response route** (json/text/html/xml/md), no Flight | integration | [`dispatch`](./response-routes.md) | `@rangojs/router/testing` |
94
+ | a real async **Server Component** (assert what it rendered: typed boundary props, server-rendered host content, inlined-vs-island) | RSC unit | [`renderServerTree` + `findClientBoundaries`/`findElements`](./server-tree.md) | `@rangojs/router/testing/flight` |
95
+ | the exact Flight **wire payload** shape (a drift snapshot) | RSC unit | [`renderToFlightString` + `toMatchFlightSnapshot`](./flight.md) | `@rangojs/router/testing/flight` |
96
+ | a real route **handler** `(ctx) => rsc` (params/loaders/vars -> rendered RSC + effects) | RSC unit | [`renderHandler`](./render-handler.md) | `@rangojs/router/testing/flight` |
97
+ | navigation, hydration, PE parity, view transitions, real SSR | e2e | [`createRangoE2E` -> `parityDescribe`/`expectParity`](./e2e-parity.md) | `@rangojs/router/testing/e2e` |
98
+ | cache hit/miss/stale, prerender (= a cache hit by design) | e2e + signal | [`assertCacheStatus` (header) / `assertCacheDecision` (telemetry sink)](./cache-prerender.md) | `@rangojs/router/testing[/e2e]` |
99
+ | generated route map drift vs runtime | unit (node) | [`assertGeneratedRoutesMatch`](./reverse-and-types.md) | `@rangojs/router/testing` |
100
+ | a platform binding (`env.DB` / Durable Object / `env.R2`) | unit/integr. | [your own double via `env`](./bindings.md) | (any primitive's `env` option) |
101
101
 
102
102
  Cross-references to the DSL skills: `/loader`, `/middleware`, `/server-actions`,
103
103
  `/handler-use`, `/hooks`, `/response-routes`, `/route`, `/caching`, `/prerender`,
@@ -1,9 +1,20 @@
1
1
  # Testing cache / SWR / prerender — assertCacheStatus
2
2
 
3
- **Layer:** e2e + signal · **Import:** the cache-status helpers (`assertCacheStatus`/`parseCacheHeader`/`createCacheSink`/`filterCacheDecisions`) are re-exported from BOTH entries — use `@rangojs/router/testing` from a Vitest unit/integration test, and `@rangojs/router/testing/e2e` from a plain Playwright runner (the e2e barrel avoids the Vite-only virtuals the main barrel pulls in). · **DSL it tests:** `cache()` / `"use cache"` / loader cache / `Prerender(...)` (see `/caching`, `/prerender`, `/use-cache`)
3
+ **Layer:** e2e + signal · **Import:** the cache-status helpers (`assertCacheStatus`/`parseCacheHeader`/`createCacheSink`/`assertCacheDecision`/`filterCacheDecisions`) are re-exported from BOTH entries — use `@rangojs/router/testing` from a Vitest unit/integration test, and `@rangojs/router/testing/e2e` from a plain Playwright runner (the e2e barrel avoids the Vite-only virtuals the main barrel pulls in). · **DSL it tests:** `cache()` / `"use cache"` / loader cache / `Prerender(...)` (see `/caching`, `/prerender`, `/use-cache`)
4
4
 
5
5
  The router's REAL cache pipeline runs (runtime cache, SWR revalidation, prerender lookup); you SEED nothing — you drive a request through the real fetch path and read the resulting cache decision. The decision surfaces two ways: the `X-Rango-Cache` response header (a debug gate) or a captured `cache.decision` telemetry event.
6
6
 
7
+ ## Which path to use
8
+
9
+ Both report the SAME coarse route-level signal (keyed by the route NAME). Pick by **transport**, not by meaning:
10
+
11
+ | Path | Helper | Transport | Needs the debug gate? | Production surface | Per-segment `shouldRevalidate`? |
12
+ | ------------- | -------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | ------------------------------------------------- | --------------------------------- | ------------------------------- |
13
+ | **Header** | `assertCacheStatus(res, routeKey, expected)` / `parseCacheHeader` | the `X-Rango-Cache` response header — the ONLY signal a black-box Playwright `Response` carries | Yes (`debugCacheSignal` / `RANGO_TEST_SIGNALS=1`) | the header (gated off by default) | no |
14
+ | **Telemetry** | `assertCacheDecision(events, routeKey, expected)` / `filterCacheDecisions` | a captured `cache.decision` event off a `createCacheSink()` sink | No | zero | yes (the only path exposing it) |
15
+
16
+ Use the header path when all you have is a black-box `Response` (a Playwright `APIResponse`); use the telemetry path when you can wire `createRouter({ telemetry: sink })` and want zero production surface or per-segment `shouldRevalidate`. `assertCacheDecision` is the one-call counterpart of `assertCacheStatus` (parallel `(…, routeKey, expected)` shape — captured `events` in place of a `Response`); reach for raw `filterCacheDecisions` only when you need the per-segment event fields directly.
17
+
7
18
  ## API
8
19
 
9
20
  ### Options — `assertCacheStatus(target, segment, expected)`
@@ -38,7 +49,11 @@ parseCacheHeader(headerValue: string | null | undefined): Record<string, string>
38
49
  // createCacheSink -> a sink to wire via createRouter({ telemetry: sink }), plus the array it records into.
39
50
  createCacheSink(): { sink: TelemetrySink; events: TelemetryEvent[] }
40
51
 
41
- // filterCacheDecisions -> narrow captured events to cache.decision events.
52
+ // assertCacheDecision -> the one-call telemetry assert (counterpart of assertCacheStatus).
53
+ // Throws on mismatch / no matching segment / unknown routeKey; returns void.
54
+ assertCacheDecision(events: readonly TelemetryEvent[], routeKey: string, expected: ExpectedCacheStatus): void
55
+
56
+ // filterCacheDecisions -> narrow captured events to cache.decision events (raw form).
42
57
  filterCacheDecisions(events: readonly TelemetryEvent[]): CacheDecisionEvent[]
43
58
  ```
44
59
 
@@ -74,16 +89,27 @@ parityDescribe("product page caches", (f) => {
74
89
  Zero-prod-surface alternative — the telemetry sink. No header at all; you inspect captured `cache.decision` events:
75
90
 
76
91
  ```ts
77
- import { createCacheSink, filterCacheDecisions } from "@rangojs/router/testing";
92
+ import {
93
+ createCacheSink,
94
+ assertCacheDecision,
95
+ filterCacheDecisions,
96
+ } from "@rangojs/router/testing";
78
97
 
79
98
  const { sink, events } = createCacheSink();
80
99
  const router = createRouter({ telemetry: sink }).routes(urlpatterns);
81
100
  // ...drive a request through the router's RSC fetch path...
101
+
102
+ // One-call assert (counterpart of assertCacheStatus), keyed by the route NAME:
103
+ assertCacheDecision(events, "product.detail", "stale");
104
+
105
+ // Or read the raw event when you need per-segment fields (shouldRevalidate):
82
106
  const decision = filterCacheDecisions(events)[0];
83
107
  expect(decision.segments?.[0].cacheStatus).toBe("stale");
84
108
  expect(decision.segments?.[0].shouldRevalidate).toBe(true);
85
109
  ```
86
110
 
111
+ `events` accumulates across requests, so the FIRST matching segment for a `routeKey` wins — slice or recreate the sink between requests for the same route.
112
+
87
113
  ## Caveats
88
114
 
89
115
  - The `X-Rango-Cache` header is emitted ONLY when the gate is on: `createRouter({ debugCacheSignal: true })` or `process.env.RANGO_TEST_SIGNALS === "1"`. Off by default — zero production surface. With the gate off, `assertCacheStatus` throws a clear "header missing" error.
@@ -1,21 +1,24 @@
1
- # Testing an async Server Component — renderToFlightString
1
+ # Pinning the Flight wire payload — renderToFlightString
2
2
 
3
3
  **Layer:** RSC unit (react-server project) · **Import:** `@rangojs/router/testing/flight` + `@rangojs/router/testing/flight-matchers` · **DSL it tests:** an async Server Component / Flight output (see `/route`)
4
4
 
5
+ > **Prefer `renderServerTree` (see [`./server-tree.md`](./server-tree.md)) for assertions on a Flight render** — it deserializes to a traversable tree with TYPED boundary props (a `Date` is a `Date`, not the opaque `$D...` encoding). Reach for `renderToFlightString` + the wire matchers (`toMatchFlight`/`toMatchFlightSnapshot`) only to pin the raw wire payload SHAPE — a `toMatchFlightSnapshot` drift snapshot. That is the niche/escape-hatch case; for "testing an async Server Component (assert what it rendered)" start at `./server-tree.md`.
6
+
5
7
  `renderToFlightString` runs the REAL react-server-dom serializer the router uses at runtime — your async Server Component genuinely renders to its Flight wire string in plain node, with a request context active for the render. What you SEED is the request, headers, env, params, routeName, and vars that context exposes.
6
8
 
7
9
  ## API
8
10
 
9
11
  ### Options — `RenderToFlightStringOptions`
10
12
 
11
- | Field | Type | Meaning |
12
- | ----------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
13
- | `request` | `Request \| string` | The request the render runs under: a `Request`, or a URL string (absolute or path). Defaults to `http://localhost/`. A component reading `getRequestContext()` sees this request's url/cookies. When a `Request` is passed, its headers are used and `headers` is ignored. |
14
- | `headers` | `HeadersInit` | Request headers (e.g. Cookie) visible to the server tree, used only when `request` is a string. |
15
- | `env` | `unknown` | Env / bindings exposed as `ctx.env`. Defaults to `{}`. |
16
- | `params` | `Record<string, string>` | Route params exposed via `ctx.params` and loader contexts. |
17
- | `routeName` | `string` | Matched route name (drives `ctx.routeName` and scoped reverse). |
18
- | `vars` | `VarsInit` | Variables a prior middleware would have set, visible via `ctx.get(...)`. Object form (`{ user }`) or `[key, value]` tuples (`[[userVar, u]]`). |
13
+ | Field | Type | Meaning |
14
+ | ----------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
15
+ | `request` | `Request \| string` | The request the render runs under: a `Request`, or a URL string (absolute or path). Defaults to `http://localhost/`. A component reading `getRequestContext()` sees this request's url/cookies. When a `Request` is passed, its headers are used and `headers` is ignored. |
16
+ | `headers` | `HeadersInit` | Request headers (e.g. Cookie) visible to the server tree, used only when `request` is a string. |
17
+ | `env` | `unknown` | Env / bindings exposed as `ctx.env`. Defaults to `{}`. |
18
+ | `params` | `Record<string, string>` | Route params exposed via `ctx.params` and loader contexts. |
19
+ | `routeName` | `string` | Matched route name (drives `ctx.routeName` and scoped reverse). |
20
+ | `routeMap` | `Record<string, string>` | Route name -> pattern map scoping `ctx.reverse()` (like `renderHandler`). Without it, a component that reverses resolves against the GLOBAL route map and is order-dependent on whatever router registered last. Pass the router-under-test's map for deterministic reverse. |
21
+ | `vars` | `VarsInit` | Variables a prior middleware would have set, visible via `ctx.get(...)`. Object form (`{ user }`) or `[key, value]` tuples (`[[userVar, u]]`). |
19
22
 
20
23
  ### Context — `RequestContext` (what your component receives)
21
24
 
@@ -78,7 +81,7 @@ it("snapshots the normalized payload", async () => {
78
81
 
79
82
  - Leaf / server-only: a client island in the tree emits an un-hydratable `I[...]` import row against the empty client manifest. Keep Flight tests to leaf server components; test full pages at e2e.
80
83
  - Requires the react-server vitest project (see `./setup.md`): `resolve.conditions` includes `react-server`, the `@rangojs/router -> index.rsc.ts` alias, `NODE_ENV=production`, and the worker `execArgv`. Name files `*.rsc-test.{ts,tsx}` and run `pnpm test:unit:rsc`. The main vitest project must NOT set `react-server` (it would flip React to the no-hooks server build).
81
- - A component that imports a server API (`getRequestContext`, `cookies`) from the bare `@rangojs/router` barrel works ONLY with the `index.rsc.ts` alias wired (see `./setup.md`); without it the bare import resolves to the throwing out-of-react-server stub. Pure-leaf components that take all data as props need no barrel import and are the simplest case.
84
+ - A component that imports a server API (`getRequestContext`, `cookies`) from the bare `@rangojs/router` barrel works ONLY with the `index.rsc.ts` alias wired (see `./setup.md`); without it the bare import resolves to the throwing out-of-react-server stub. `renderToFlightString` / `renderServerTree` self-diagnose this exact misconfiguration — they reject with an actionable message naming `rangoTestAliases`, rather than surfacing the opaque stub error. Pure-leaf components that take all data as props need no barrel import and are the simplest case.
82
85
  - `toMatchFlight` is containment (substring), not equality — the row framing (prefixes/quoting) is an internal serializer detail, so pin the rendered text/shape, not the framing. `toMatchFlightSnapshot()` snapshots the normalized payload; run under `NODE_ENV=production` for the cleanest, most stable bytes.
83
86
  - No hydration / no interaction here — that is the e2e tier. For typed assertions on a client boundary's props (a `Date` back as a `Date`), or to confirm an island actually crossed the boundary, use `renderServerTree` (see `./server-tree.md`).
84
87
 
@@ -20,6 +20,8 @@ A Rango route handler is a pure function `(ctx) => rsc` — the function you pas
20
20
  | `loaders` | `ReadonlyArray<readonly [LoaderDefinition, unknown]>` | Seed the data `ctx.use(SomeLoader)` returns. Matched by loader reference; NO real loader runs. |
21
21
  | `clientComponents` | `Record<string, unknown>` | `"use client"` components in the handler's RSC, so they serialize as real boundaries when `rangoUseClientTransform()` is not wired. Keyed by name. |
22
22
  | `stateCookie` | `StateCookieSeed` (`{ prefix?, routerId?, version? }`) | Customize the rango state cookie a handler calling `invalidateClientCache()` rotates. The name is ALWAYS seeded (default `rango-state_router_0`) so the rotation `Set-Cookie` fires like production rather than no-opping; override `prefix`/`routerId` to match your `createRouter({ stateCookiePrefix, id })`, or `version` (the value is `{version}:{timestamp}`, default `"0"`). |
23
+ | `cacheStore` | `SegmentCacheStore` | Segment cache store backing a `"use cache"` function the handler invokes (e.g. `new MemorySegmentCacheStore()`). WITHOUT it, `registerCachedFunction` takes the uncached bypass and the cached path is NOT exercised (the runtime emits a one-time warning under the test runner). Pair with `cacheProfiles`. |
24
+ | `cacheProfiles` | `Record<string, CacheProfile>` | Cache profiles in the `createRouter({ cacheProfiles })` shape, required for `"use cache: profileName"` resolution once a `cacheStore` is wired. |
23
25
 
24
26
  ### Context — `HandlerContext` (what your handler receives)
25
27
 
@@ -110,6 +112,7 @@ it("asserts the client-cache directives", async () => {
110
112
  - A `throw redirect()` is captured on `thrown` (with `tree` undefined, since it produced a `Response`) — assert on `thrown`/`response`, no try/catch needed.
111
113
  - No hydration and no interaction — for clicks, forms, and navigation use e2e.
112
114
  - `renderHandler` runs a handler FUNCTION `(ctx) => rsc`; for a plain ELEMENT `<Page/>` use `renderServerTree` (see [`./server-tree.md`](./server-tree.md)).
115
+ - A handler that calls a `"use cache"` function runs UNCACHED unless you seed `cacheStore` (and `cacheProfiles` for a named profile). With nothing seeded the runtime bypasses to the live body and warns once under the test runner — assert real cache behavior by passing `{ cacheStore: new MemorySegmentCacheStore(), cacheProfiles: { default: { ttl: 60 } } }`.
113
116
 
114
117
  ## See also
115
118
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Layer:** RSC unit (react-server project) · **Import:** `@rangojs/router/testing/flight` · **DSL it tests:** client islands across the boundary + server-rendered host content (see `/route`)
4
4
 
5
- `renderServerTree` serializes the real Flight (identical bytes to `renderToFlightString`) and then deserializes it back to an inspectable React element tree you traverse — that serialize/deserialize round-trip is REAL; what you SEED is the element you render plus the request context (`request`/`headers`/`params`/`vars`/`env`). The win over the wire string: a client boundary's props come back as real JS values (a `Date` is a `Date`, not the opaque `$D...` encoding) and you can confirm a `"use client"` component actually crossed the boundary (an `I` row) instead of being inlined. There is NO hydration and NO interaction — boundaries are inert placeholders carrying props.
5
+ `renderServerTree` is the DEFAULT way to assert on a Flight render; `renderToFlightString` (see [`./flight.md`](./flight.md)) is the escape hatch for pinning the raw wire bytes. It serializes the real Flight (identical bytes to `renderToFlightString`) and then deserializes it back to an inspectable React element tree you traverse — that serialize/deserialize round-trip is REAL; what you SEED is the element you render plus the request context (`request`/`headers`/`params`/`vars`/`env`). The win over the wire string: a client boundary's props come back as real JS values (a `Date` is a `Date`, not the opaque `$D...` encoding) and you can confirm a `"use client"` component actually crossed the boundary (an `I` row) instead of being inlined. There is NO hydration and NO interaction — boundaries are inert placeholders carrying props.
6
6
 
7
7
  ## API
8
8
 
@@ -106,7 +106,7 @@ Scripts:
106
106
 
107
107
  - Node >= 23 requires `rangoTestConfig()`, not bare `rangoTestAliases()`. `@rangojs/router` is consumed as SOURCE (exports -> `./src/*.ts`), and Node >= 23 refuses to type-strip `.ts` under `node_modules` (`ERR_UNSUPPORTED_NODE_MODULES_TYPE_STRIPPING`). `rangoTestConfig` ships as compiled JS AND adds `server.deps.inline: [/@rangojs[/\\]router/]` so Vite (not Node) transpiles rango source. With bare `rangoTestAliases` you must wire `deps.inline` yourself.
108
108
  - Two separate projects. The node/DOM project keeps React on its CLIENT build; the Flight project uses the `react-server` condition in a separate `vitest.rsc.config.ts`. The main project must NOT set `react-server` — it flips React to the no-hooks server build and breaks every `renderRoute` / client test.
109
- - The rsc project needs BOTH `resolve.conditions: ["react-server"]` AND the bare `@rangojs/router` -> `index.rsc.ts` alias from `rangoTestAliases({ preset })`. `resolve.conditions` alone is not reliably applied to bare-package export resolution; without the alias a handler/component reading `getRequestContext()` / `cookies()` resolves the throwing out-of-react-server stub (symptom: `renderHandler` returns `tree: undefined`).
109
+ - The rsc project needs BOTH `resolve.conditions: ["react-server"]` AND the bare `@rangojs/router` -> `index.rsc.ts` alias from `rangoTestAliases({ preset })`. `resolve.conditions` alone is not reliably applied to bare-package export resolution; without the alias a handler/component reading `getRequestContext()` / `cookies()` resolves the throwing out-of-react-server stub (symptom: `renderHandler` returns `tree: undefined`). `renderToFlightString` / `renderServerTree` now self-diagnose this exact misconfiguration — they reject with an actionable message naming `rangoTestAliases`, rather than surfacing the opaque stub error.
110
110
  - `NODE_ENV` must be `"production"` in the rsc project. Dev `NODE_ENV` crashes the bare worker (jsxDEV owner-stack machinery uninitialized) and emits volatile debug rows that defeat stable Flight snapshots.
111
111
  - The forked rsc worker (`pool: "forks"`) must force the condition via `execArgv: ["--conditions=react-server"]`, or React throws "the react-server condition must be enabled".
112
112
  - The `@rangojs/router:version` and `@vitejs/plugin-rsc/rsc` virtuals must be stubbed; the preset does it. A bare router import without stubbing throws.
@@ -21,7 +21,10 @@ import {
21
21
  import type { BoundTransaction } from "./navigation-transaction.js";
22
22
  import { ServerRedirect } from "../errors.js";
23
23
  import { debugLog } from "./logging.js";
24
- import { validateRedirectOrigin } from "./validate-redirect-origin.js";
24
+ import {
25
+ validateRedirectOrigin,
26
+ validateExternalRedirect,
27
+ } from "./validate-redirect-origin.js";
25
28
  import type { NavigationUpdate } from "./types.js";
26
29
 
27
30
  function toScrollPayload(
@@ -211,6 +214,24 @@ export function createPartialUpdater(
211
214
  debugLog("[Browser] Ignoring stale redirect (aborted)");
212
215
  return;
213
216
  }
217
+ // Explicit off-host redirect (redirect(url, { external: true })):
218
+ // hard-navigate, but still scheme-validate (http/https only). external
219
+ // waives the same-origin check the app opted out of, NOT scheme safety, so
220
+ // a forged payload carrying a javascript:/data: URL cannot script via
221
+ // location.assign.
222
+ if (payload.metadata.redirect.external) {
223
+ const externalUrl = validateExternalRedirect(
224
+ payload.metadata.redirect.url,
225
+ window.location.origin,
226
+ );
227
+ if (!externalUrl) {
228
+ debugLog("[Browser] Ignoring blocked external redirect payload");
229
+ return;
230
+ }
231
+ debugLog("[Browser] External redirect (hard navigation)");
232
+ window.location.assign(externalUrl);
233
+ return;
234
+ }
214
235
  const redirectUrl = validateRedirectOrigin(
215
236
  payload.metadata.redirect.url,
216
237
  window.location.origin,
@@ -60,7 +60,7 @@ export function useRouter(): RouterInstance {
60
60
  return ctx.refresh();
61
61
  },
62
62
 
63
- prefetch(url: string): void {
63
+ prefetch(url: string, options?: { key?: ":source" }): void {
64
64
  const segmentState = ctx.store?.getSegmentState();
65
65
  if (segmentState) {
66
66
  prefetchDirect(
@@ -68,6 +68,7 @@ export function useRouter(): RouterInstance {
68
68
  segmentState.currentSegmentIds,
69
69
  getAppVersion(),
70
70
  ctx.store?.getRouterId?.(),
71
+ options?.key,
71
72
  );
72
73
  }
73
74
  },
@@ -8,6 +8,7 @@ import {
8
8
  generateHistoryKey,
9
9
  } from "./navigation-store.js";
10
10
  import { createEventController } from "./event-controller.js";
11
+ import { validateRedirectOrigin } from "./validate-redirect-origin.js";
11
12
  import { createNavigationClient } from "./navigation-client.js";
12
13
  import { createServerActionBridge } from "./server-action-bridge.js";
13
14
  import { createNavigationBridge } from "./navigation-bridge.js";
@@ -280,7 +281,13 @@ export async function initBrowserApp(
280
281
  renderSegments,
281
282
  onNavigate: (url, options) => {
282
283
  if (!navigateFn) {
283
- window.location.href = url;
284
+ // Navigation bridge not wired yet: hard-navigate, but re-validate
285
+ // same-origin defensively so this init-window fallback cannot become an
286
+ // open redirect (the normal path validates inside the navigation bridge).
287
+ const safe = validateRedirectOrigin(url, window.location.origin);
288
+ if (safe) {
289
+ window.location.href = safe;
290
+ }
284
291
  return Promise.resolve();
285
292
  }
286
293
  return navigateFn(url, options);
@@ -23,7 +23,10 @@ import {
23
23
  isBrowserDebugEnabled,
24
24
  startBrowserTransaction,
25
25
  } from "./logging.js";
26
- import { validateRedirectOrigin } from "./validate-redirect-origin.js";
26
+ import {
27
+ validateRedirectOrigin,
28
+ validateExternalRedirect,
29
+ } from "./validate-redirect-origin.js";
27
30
  import {
28
31
  extractRscHeaderUrl,
29
32
  emptyResponse,
@@ -83,6 +86,9 @@ export function createServerActionBridge(
83
86
 
84
87
  // SPA-navigate when onNavigate is set, else hard-reload. state is omitted (not
85
88
  // passed as undefined) to match the header path's prior call shape.
89
+ // Callers pass an already same-origin-validated url; the hard-reload fallback
90
+ // re-validates defensively so this leaf cannot become an open redirect if a
91
+ // future caller forgets (the SPA path validates inside the navigation bridge).
86
92
  async function dispatchRedirect(url: string, state?: unknown): Promise<void> {
87
93
  if (onNavigate) {
88
94
  await onNavigate(url, {
@@ -91,7 +97,10 @@ export function createServerActionBridge(
91
97
  _skipCache: true,
92
98
  });
93
99
  } else {
94
- window.location.href = url;
100
+ const safe = validateRedirectOrigin(url, window.location.origin);
101
+ if (safe) {
102
+ window.location.href = safe;
103
+ }
95
104
  }
96
105
  }
97
106
 
@@ -161,6 +170,15 @@ export function createServerActionBridge(
161
170
  // Whether the action's response carried the keepClientCache() directive.
162
171
  // Set when the response arrives; gates the deferred invalidation below.
163
172
  let keepCache = false;
173
+ // Whether a Response actually settled from the network (the server saw the
174
+ // request). Set true as the first statement in the fetch .then() below.
175
+ // Gates the automatic invalidation: a pre-dispatch failure (encodeReply
176
+ // throw or a fetch rejection — server unreachable/DNS/connection refused)
177
+ // leaves this false, so finalizeAction() must NOT invalidate or broadcast —
178
+ // nothing reached the server, so nothing could have mutated. A failed Flight
179
+ // DECODE after the response arrived keeps it true (the mutation may have
180
+ // committed, so invalidating the now-possibly-stale client cache is correct).
181
+ let responseReceived = false;
164
182
  // Single deferred invalidation + fence release, run exactly ONCE however the
165
183
  // action terminates (normal, redirect, error, abort, intercept, concurrent).
166
184
  // This replaces main's eager clear at action start: every directive-free
@@ -176,7 +194,10 @@ export function createServerActionBridge(
176
194
  actionFinalized = true;
177
195
  // finally so a throw in invalidation cannot leak the fence (latch is set).
178
196
  try {
179
- if (!keepCache && !skipInvalidation) {
197
+ // responseReceived gates the automatic invalidation: a pre-dispatch
198
+ // failure (serialize throw / fetch reject) never reached the server, so
199
+ // marking the cache stale + broadcasting cross-tab would be spurious.
200
+ if (responseReceived && !keepCache && !skipInvalidation) {
180
201
  store.markCacheAsStaleAndBroadcast();
181
202
  }
182
203
  } finally {
@@ -263,6 +284,12 @@ export function createServerActionBridge(
263
284
  body: encodedBody,
264
285
  signal: fetchAbort.signal,
265
286
  }).then(async (response) => {
287
+ // A settled fetch promise means the request reached the server and a
288
+ // Response came back (true for 2xx, 4xx, AND 5xx — fetch only rejects
289
+ // on network-layer failure, never on HTTP status). Record it as the
290
+ // first statement so every downstream terminal can invalidate; a
291
+ // pre-dispatch failure never gets here and stays gated out.
292
+ responseReceived = true;
266
293
  // Response arrived — disconnect fetch abort from handle abort so
267
294
  // abortAllActions() doesn't disrupt the in-progress Flight stream.
268
295
  handle.signal.removeEventListener("abort", onHandleAbort);
@@ -399,6 +426,27 @@ export function createServerActionBridge(
399
426
  // Check handle.signal.aborted to avoid redirecting from a stale action
400
427
  // when the user has already navigated away.
401
428
  if (metadata?.redirect && !handle.signal.aborted) {
429
+ // Explicit off-host redirect (redirect(url, { external: true })):
430
+ // hard-navigate, but still scheme-validate (http/https only). external
431
+ // waives the same-origin check, NOT scheme safety, so a forged payload
432
+ // carrying a javascript:/data: URL cannot script via location.assign.
433
+ if (metadata.redirect.external) {
434
+ const externalUrl = validateExternalRedirect(
435
+ metadata.redirect.url,
436
+ window.location.origin,
437
+ );
438
+ if (!externalUrl) {
439
+ log("blocked external action redirect payload", {
440
+ url: metadata.redirect.url,
441
+ });
442
+ handle.complete(returnValue?.data);
443
+ return returnValue?.data;
444
+ }
445
+ log("external action redirect", { url: externalUrl });
446
+ handle.complete(returnValue?.data);
447
+ window.location.assign(externalUrl);
448
+ return returnValue?.data;
449
+ }
402
450
  const redirectUrl = validateRedirectOrigin(
403
451
  metadata.redirect.url,
404
452
  window.location.origin,
@@ -90,8 +90,12 @@ export interface RscMetadata {
90
90
  basename?: string;
91
91
  /** Whether connection warmup is enabled */
92
92
  warmupEnabled?: boolean;
93
- /** Server-side redirect with optional state (for partial requests) */
94
- redirect?: { url: string };
93
+ /**
94
+ * Server-side redirect with optional state (for partial requests).
95
+ * `external: true` (from redirect(url, { external: true })) tells the client
96
+ * to hard-navigate to an off-host target instead of validating same-origin.
97
+ */
98
+ redirect?: { url: string; external?: boolean };
95
99
  /** Server-set location state to include in history.pushState */
96
100
  locationState?: Record<string, unknown>;
97
101
  }
@@ -192,6 +196,15 @@ export interface TrackedActionState {
192
196
  result: unknown | null;
193
197
  }
194
198
 
199
+ /**
200
+ * The value returned by {@link useAction} when called without a selector.
201
+ *
202
+ * This is the stable, public name for the action-state shape; consumers can
203
+ * name it in their own signatures (e.g. a wrapper hook). It aliases the
204
+ * internal {@link TrackedActionState}.
205
+ */
206
+ export type ActionState = TrackedActionState;
207
+
195
208
  /**
196
209
  * Listener for action state changes
197
210
  *
@@ -329,8 +342,14 @@ export interface RouterInstance {
329
342
  replace(url: string, options?: RouterNavigateOptions): Promise<void>;
330
343
  /** Refresh the current route (re-fetch server data, preserve client state) */
331
344
  refresh(): Promise<void>;
332
- /** Prefetch a URL for faster client-side transition */
333
- prefetch(url: string): void;
345
+ /**
346
+ * Prefetch a URL for faster client-side transition.
347
+ *
348
+ * Pass `{ key: ":source" }` to source-scope the prefetch cache entry (parity
349
+ * with `<Link prefetchKey=":source">`) when the target's response can differ
350
+ * by source page.
351
+ */
352
+ prefetch(url: string, options?: { key?: ":source" }): void;
334
353
  /** Go back in browser history */
335
354
  back(): void;
336
355
  /** Go forward in browser history */
@@ -1,28 +1,56 @@
1
+ import {
2
+ resolveSameOriginRedirect,
3
+ resolveExternalRedirect,
4
+ } from "../redirect-origin.js";
5
+
1
6
  /**
2
7
  * Validate that a client-consumed redirect URL (from headers or Flight payload)
3
8
  * targets the same origin as the current page. Prevents open-redirect attacks
4
9
  * via crafted responses.
5
10
  *
11
+ * Thin wrapper over the shared {@link resolveSameOriginRedirect} rule (also used
12
+ * by the server guard in `rsc/redirect-guard.ts`) so client and server enforce
13
+ * the identical same-origin contract. Adds the client-side `console.error` on a
14
+ * block; the resolver itself stays pure.
15
+ *
6
16
  * @returns The canonical (normalized) URL string on success, or null if blocked.
7
17
  */
8
18
  export function validateRedirectOrigin(
9
19
  url: string,
10
20
  currentOrigin: string,
11
21
  ): string | null {
12
- try {
13
- const target = new URL(url, currentOrigin);
14
- if (target.origin !== currentOrigin) {
15
- console.error(
16
- `[rango] Redirect blocked: origin mismatch (${target.origin})`,
17
- );
18
- return null;
19
- }
20
- // Origin matched above, so target.href is same-origin: return the
21
- // canonical full href. This normalizes protocol-relative and other
22
- // ambiguous forms.
23
- return target.href;
24
- } catch {
25
- console.error(`[rango] Redirect blocked: invalid URL "${url}"`);
26
- return null;
22
+ const resolved = resolveSameOriginRedirect(url, currentOrigin);
23
+ if (resolved === null) {
24
+ console.error(
25
+ `[rango] Redirect blocked: cross-origin or invalid target "${url}"`,
26
+ );
27
+ }
28
+ return resolved;
29
+ }
30
+
31
+ /**
32
+ * Validate an explicit off-origin redirect (`redirect(url, { external: true })`)
33
+ * the client is about to hard-navigate to via `window.location.assign()`.
34
+ *
35
+ * Thin wrapper over the shared {@link resolveExternalRedirect} rule (also used by
36
+ * the server guard in `rsc/redirect-guard.ts`) so client and server enforce the
37
+ * identical contract: `external` allows an off-origin target but only an
38
+ * http(s) scheme. This stops a forged or mistaken external payload carrying a
39
+ * `javascript:`/`data:` URL from turning `location.assign` into a scriptable
40
+ * navigation. Adds the client-side `console.error` on a block; the resolver
41
+ * itself stays pure.
42
+ *
43
+ * @returns The normalized URL string on success, or null if blocked.
44
+ */
45
+ export function validateExternalRedirect(
46
+ url: string,
47
+ currentOrigin: string,
48
+ ): string | null {
49
+ const resolved = resolveExternalRedirect(url, currentOrigin);
50
+ if (resolved === null) {
51
+ console.error(
52
+ `[rango] External redirect blocked: non-http(s) target "${url}"`,
53
+ );
27
54
  }
55
+ return resolved;
28
56
  }