@rangojs/router 0.0.0-experimental.ea6d5eec → 0.0.0-experimental.ea9f40f2

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 (1249) hide show
  1. package/AGENTS.md +8 -4
  2. package/README.md +303 -741
  3. package/dist/__internal.d.ts +83 -0
  4. package/dist/__internal.d.ts.map +1 -0
  5. package/dist/__internal.js +19 -0
  6. package/dist/__internal.js.map +1 -0
  7. package/dist/__mocks__/version.d.ts +7 -0
  8. package/dist/__mocks__/version.d.ts.map +1 -0
  9. package/dist/__mocks__/version.js +7 -0
  10. package/dist/__mocks__/version.js.map +1 -0
  11. package/dist/__tests__/client-href.test.d.ts +2 -0
  12. package/dist/__tests__/client-href.test.d.ts.map +1 -0
  13. package/dist/__tests__/client-href.test.js +74 -0
  14. package/dist/__tests__/client-href.test.js.map +1 -0
  15. package/dist/__tests__/component-utils.test.d.ts +2 -0
  16. package/dist/__tests__/component-utils.test.d.ts.map +1 -0
  17. package/dist/__tests__/component-utils.test.js +51 -0
  18. package/dist/__tests__/component-utils.test.js.map +1 -0
  19. package/dist/__tests__/event-controller.test.d.ts +2 -0
  20. package/dist/__tests__/event-controller.test.d.ts.map +1 -0
  21. package/dist/__tests__/event-controller.test.js +538 -0
  22. package/dist/__tests__/event-controller.test.js.map +1 -0
  23. package/dist/__tests__/helpers/route-tree.d.ts +118 -0
  24. package/dist/__tests__/helpers/route-tree.d.ts.map +1 -0
  25. package/dist/__tests__/helpers/route-tree.js +374 -0
  26. package/dist/__tests__/helpers/route-tree.js.map +1 -0
  27. package/dist/__tests__/match-result.test.d.ts +2 -0
  28. package/dist/__tests__/match-result.test.d.ts.map +1 -0
  29. package/dist/__tests__/match-result.test.js +154 -0
  30. package/dist/__tests__/match-result.test.js.map +1 -0
  31. package/dist/__tests__/navigation-store.test.d.ts +2 -0
  32. package/dist/__tests__/navigation-store.test.d.ts.map +1 -0
  33. package/dist/__tests__/navigation-store.test.js +440 -0
  34. package/dist/__tests__/navigation-store.test.js.map +1 -0
  35. package/dist/__tests__/partial-update.test.d.ts +2 -0
  36. package/dist/__tests__/partial-update.test.d.ts.map +1 -0
  37. package/dist/__tests__/partial-update.test.js +1009 -0
  38. package/dist/__tests__/partial-update.test.js.map +1 -0
  39. package/dist/__tests__/reverse-types.test.d.ts +8 -0
  40. package/dist/__tests__/reverse-types.test.d.ts.map +1 -0
  41. package/dist/__tests__/reverse-types.test.js +656 -0
  42. package/dist/__tests__/reverse-types.test.js.map +1 -0
  43. package/dist/__tests__/route-definition.test.d.ts +2 -0
  44. package/dist/__tests__/route-definition.test.d.ts.map +1 -0
  45. package/dist/__tests__/route-definition.test.js +55 -0
  46. package/dist/__tests__/route-definition.test.js.map +1 -0
  47. package/dist/__tests__/router-helpers.test.d.ts +2 -0
  48. package/dist/__tests__/router-helpers.test.d.ts.map +1 -0
  49. package/dist/__tests__/router-helpers.test.js +377 -0
  50. package/dist/__tests__/router-helpers.test.js.map +1 -0
  51. package/dist/__tests__/router-integration-2.test.d.ts +2 -0
  52. package/dist/__tests__/router-integration-2.test.d.ts.map +1 -0
  53. package/dist/__tests__/router-integration-2.test.js +426 -0
  54. package/dist/__tests__/router-integration-2.test.js.map +1 -0
  55. package/dist/__tests__/router-integration.test.d.ts +2 -0
  56. package/dist/__tests__/router-integration.test.d.ts.map +1 -0
  57. package/dist/__tests__/router-integration.test.js +1051 -0
  58. package/dist/__tests__/router-integration.test.js.map +1 -0
  59. package/dist/__tests__/search-params.test.d.ts +5 -0
  60. package/dist/__tests__/search-params.test.d.ts.map +1 -0
  61. package/dist/__tests__/search-params.test.js +306 -0
  62. package/dist/__tests__/search-params.test.js.map +1 -0
  63. package/dist/__tests__/segment-system.test.d.ts +2 -0
  64. package/dist/__tests__/segment-system.test.d.ts.map +1 -0
  65. package/dist/__tests__/segment-system.test.js +627 -0
  66. package/dist/__tests__/segment-system.test.js.map +1 -0
  67. package/dist/__tests__/static-handler-types.test.d.ts +8 -0
  68. package/dist/__tests__/static-handler-types.test.d.ts.map +1 -0
  69. package/dist/__tests__/static-handler-types.test.js +63 -0
  70. package/dist/__tests__/static-handler-types.test.js.map +1 -0
  71. package/dist/__tests__/urls.test.d.ts +2 -0
  72. package/dist/__tests__/urls.test.d.ts.map +1 -0
  73. package/dist/__tests__/urls.test.js +421 -0
  74. package/dist/__tests__/urls.test.js.map +1 -0
  75. package/dist/__tests__/use-mount.test.d.ts +2 -0
  76. package/dist/__tests__/use-mount.test.d.ts.map +1 -0
  77. package/dist/__tests__/use-mount.test.js +35 -0
  78. package/dist/__tests__/use-mount.test.js.map +1 -0
  79. package/dist/bin/rango.d.ts +2 -0
  80. package/dist/bin/rango.d.ts.map +1 -0
  81. package/dist/bin/rango.js +724 -183
  82. package/dist/bin/rango.js.map +1 -0
  83. package/dist/browser/event-controller.d.ts +191 -0
  84. package/dist/browser/event-controller.d.ts.map +1 -0
  85. package/dist/browser/event-controller.js +559 -0
  86. package/dist/browser/event-controller.js.map +1 -0
  87. package/dist/browser/index.d.ts +2 -0
  88. package/dist/browser/index.d.ts.map +1 -0
  89. package/dist/browser/index.js +14 -0
  90. package/dist/browser/index.js.map +1 -0
  91. package/dist/browser/link-interceptor.d.ts +38 -0
  92. package/dist/browser/link-interceptor.d.ts.map +1 -0
  93. package/dist/browser/link-interceptor.js +99 -0
  94. package/dist/browser/link-interceptor.js.map +1 -0
  95. package/dist/browser/logging.d.ts +10 -0
  96. package/dist/browser/logging.d.ts.map +1 -0
  97. package/dist/browser/logging.js +29 -0
  98. package/dist/browser/logging.js.map +1 -0
  99. package/dist/browser/lru-cache.d.ts +17 -0
  100. package/dist/browser/lru-cache.d.ts.map +1 -0
  101. package/dist/browser/lru-cache.js +50 -0
  102. package/dist/browser/lru-cache.js.map +1 -0
  103. package/dist/browser/merge-segment-loaders.d.ts +39 -0
  104. package/dist/browser/merge-segment-loaders.d.ts.map +1 -0
  105. package/dist/browser/merge-segment-loaders.js +102 -0
  106. package/dist/browser/merge-segment-loaders.js.map +1 -0
  107. package/dist/browser/navigation-bridge.d.ts +102 -0
  108. package/dist/browser/navigation-bridge.d.ts.map +1 -0
  109. package/dist/browser/navigation-bridge.js +708 -0
  110. package/dist/browser/navigation-bridge.js.map +1 -0
  111. package/dist/browser/navigation-client.d.ts +25 -0
  112. package/dist/browser/navigation-client.d.ts.map +1 -0
  113. package/dist/browser/navigation-client.js +157 -0
  114. package/dist/browser/navigation-client.js.map +1 -0
  115. package/dist/browser/navigation-store.d.ts +101 -0
  116. package/dist/browser/navigation-store.d.ts.map +1 -0
  117. package/dist/browser/navigation-store.js +625 -0
  118. package/dist/browser/navigation-store.js.map +1 -0
  119. package/dist/browser/partial-update.d.ts +75 -0
  120. package/dist/browser/partial-update.d.ts.map +1 -0
  121. package/dist/browser/partial-update.js +426 -0
  122. package/dist/browser/partial-update.js.map +1 -0
  123. package/dist/browser/react/Link.d.ts +86 -0
  124. package/dist/browser/react/Link.d.ts.map +1 -0
  125. package/dist/browser/react/Link.js +128 -0
  126. package/dist/browser/react/Link.js.map +1 -0
  127. package/dist/browser/react/NavigationProvider.d.ts +63 -0
  128. package/dist/browser/react/NavigationProvider.d.ts.map +1 -0
  129. package/dist/browser/react/NavigationProvider.js +216 -0
  130. package/dist/browser/react/NavigationProvider.js.map +1 -0
  131. package/dist/browser/react/ScrollRestoration.d.ts +75 -0
  132. package/dist/browser/react/ScrollRestoration.d.ts.map +1 -0
  133. package/dist/browser/react/ScrollRestoration.js +57 -0
  134. package/dist/browser/react/ScrollRestoration.js.map +1 -0
  135. package/dist/browser/react/context.d.ts +46 -0
  136. package/dist/browser/react/context.d.ts.map +1 -0
  137. package/dist/browser/react/context.js +10 -0
  138. package/dist/browser/react/context.js.map +1 -0
  139. package/dist/browser/react/index.d.ts +11 -0
  140. package/dist/browser/react/index.d.ts.map +1 -0
  141. package/dist/browser/react/index.js +22 -0
  142. package/dist/browser/react/index.js.map +1 -0
  143. package/dist/browser/react/location-state-shared.d.ts +63 -0
  144. package/dist/browser/react/location-state-shared.d.ts.map +1 -0
  145. package/dist/browser/react/location-state-shared.js +81 -0
  146. package/dist/browser/react/location-state-shared.js.map +1 -0
  147. package/dist/browser/react/location-state.d.ts +23 -0
  148. package/dist/browser/react/location-state.d.ts.map +1 -0
  149. package/dist/browser/react/location-state.js +29 -0
  150. package/dist/browser/react/location-state.js.map +1 -0
  151. package/dist/browser/react/mount-context.d.ts +24 -0
  152. package/dist/browser/react/mount-context.d.ts.map +1 -0
  153. package/dist/browser/react/mount-context.js +24 -0
  154. package/dist/browser/react/mount-context.js.map +1 -0
  155. package/dist/browser/react/use-action.d.ts +64 -0
  156. package/dist/browser/react/use-action.d.ts.map +1 -0
  157. package/dist/browser/react/use-action.js +134 -0
  158. package/dist/browser/react/use-action.js.map +1 -0
  159. package/dist/browser/react/use-client-cache.d.ts +41 -0
  160. package/dist/browser/react/use-client-cache.d.ts.map +1 -0
  161. package/{src/browser/react/use-client-cache.ts → dist/browser/react/use-client-cache.js} +9 -28
  162. package/dist/browser/react/use-client-cache.js.map +1 -0
  163. package/dist/browser/react/use-handle.d.ts +31 -0
  164. package/dist/browser/react/use-handle.d.ts.map +1 -0
  165. package/dist/browser/react/use-handle.js +144 -0
  166. package/dist/browser/react/use-handle.js.map +1 -0
  167. package/dist/browser/react/use-href.d.ts +33 -0
  168. package/dist/browser/react/use-href.d.ts.map +1 -0
  169. package/dist/browser/react/use-href.js +39 -0
  170. package/dist/browser/react/use-href.js.map +1 -0
  171. package/dist/browser/react/use-link-status.d.ts +37 -0
  172. package/dist/browser/react/use-link-status.d.ts.map +1 -0
  173. package/dist/browser/react/use-link-status.js +99 -0
  174. package/dist/browser/react/use-link-status.js.map +1 -0
  175. package/dist/browser/react/use-mount.d.ts +25 -0
  176. package/dist/browser/react/use-mount.d.ts.map +1 -0
  177. package/dist/browser/react/use-mount.js +30 -0
  178. package/dist/browser/react/use-mount.js.map +1 -0
  179. package/dist/browser/react/use-navigation.d.ts +27 -0
  180. package/dist/browser/react/use-navigation.d.ts.map +1 -0
  181. package/dist/browser/react/use-navigation.js +87 -0
  182. package/dist/browser/react/use-navigation.js.map +1 -0
  183. package/dist/browser/react/use-segments.d.ts +38 -0
  184. package/dist/browser/react/use-segments.d.ts.map +1 -0
  185. package/dist/browser/react/use-segments.js +130 -0
  186. package/dist/browser/react/use-segments.js.map +1 -0
  187. package/dist/browser/request-controller.d.ts +26 -0
  188. package/dist/browser/request-controller.d.ts.map +1 -0
  189. package/dist/browser/request-controller.js +147 -0
  190. package/dist/browser/request-controller.js.map +1 -0
  191. package/dist/browser/rsc-router.d.ts +129 -0
  192. package/dist/browser/rsc-router.d.ts.map +1 -0
  193. package/dist/browser/rsc-router.js +195 -0
  194. package/dist/browser/rsc-router.js.map +1 -0
  195. package/dist/browser/scroll-restoration.d.ts +93 -0
  196. package/dist/browser/scroll-restoration.d.ts.map +1 -0
  197. package/dist/browser/scroll-restoration.js +321 -0
  198. package/dist/browser/scroll-restoration.js.map +1 -0
  199. package/dist/browser/segment-structure-assert.d.ts +17 -0
  200. package/dist/browser/segment-structure-assert.d.ts.map +1 -0
  201. package/dist/browser/segment-structure-assert.js +59 -0
  202. package/dist/browser/segment-structure-assert.js.map +1 -0
  203. package/dist/browser/server-action-bridge.d.ts +26 -0
  204. package/dist/browser/server-action-bridge.d.ts.map +1 -0
  205. package/dist/browser/server-action-bridge.js +668 -0
  206. package/dist/browser/server-action-bridge.js.map +1 -0
  207. package/dist/browser/shallow.d.ts +12 -0
  208. package/dist/browser/shallow.d.ts.map +1 -0
  209. package/dist/browser/shallow.js +34 -0
  210. package/dist/browser/shallow.js.map +1 -0
  211. package/dist/browser/types.d.ts +369 -0
  212. package/dist/browser/types.d.ts.map +1 -0
  213. package/dist/browser/types.js +2 -0
  214. package/dist/browser/types.js.map +1 -0
  215. package/dist/build/__tests__/generate-cli.test.d.ts +2 -0
  216. package/dist/build/__tests__/generate-cli.test.d.ts.map +1 -0
  217. package/dist/build/__tests__/generate-cli.test.js +237 -0
  218. package/dist/build/__tests__/generate-cli.test.js.map +1 -0
  219. package/dist/build/__tests__/generate-manifest.test.d.ts +2 -0
  220. package/dist/build/__tests__/generate-manifest.test.d.ts.map +1 -0
  221. package/dist/build/__tests__/generate-manifest.test.js +119 -0
  222. package/dist/build/__tests__/generate-manifest.test.js.map +1 -0
  223. package/dist/build/__tests__/generate-route-types.test.d.ts +2 -0
  224. package/dist/build/__tests__/generate-route-types.test.d.ts.map +1 -0
  225. package/dist/build/__tests__/generate-route-types.test.js +620 -0
  226. package/dist/build/__tests__/generate-route-types.test.js.map +1 -0
  227. package/dist/build/__tests__/per-router-manifest.test.d.ts +2 -0
  228. package/dist/build/__tests__/per-router-manifest.test.d.ts.map +1 -0
  229. package/dist/build/__tests__/per-router-manifest.test.js +308 -0
  230. package/dist/build/__tests__/per-router-manifest.test.js.map +1 -0
  231. package/dist/build/generate-manifest.d.ts +81 -0
  232. package/dist/build/generate-manifest.d.ts.map +1 -0
  233. package/dist/build/generate-manifest.js +276 -0
  234. package/dist/build/generate-manifest.js.map +1 -0
  235. package/dist/build/generate-route-types.d.ts +115 -0
  236. package/dist/build/generate-route-types.d.ts.map +1 -0
  237. package/dist/build/generate-route-types.js +740 -0
  238. package/dist/build/generate-route-types.js.map +1 -0
  239. package/dist/build/index.d.ts +21 -0
  240. package/dist/build/index.d.ts.map +1 -0
  241. package/dist/build/index.js +21 -0
  242. package/dist/build/index.js.map +1 -0
  243. package/dist/build/route-trie.d.ts +71 -0
  244. package/dist/build/route-trie.d.ts.map +1 -0
  245. package/dist/build/route-trie.js +175 -0
  246. package/dist/build/route-trie.js.map +1 -0
  247. package/dist/cache/__tests__/cache-scope.test.d.ts +2 -0
  248. package/dist/cache/__tests__/cache-scope.test.d.ts.map +1 -0
  249. package/dist/cache/__tests__/cache-scope.test.js +208 -0
  250. package/dist/cache/__tests__/cache-scope.test.js.map +1 -0
  251. package/dist/cache/__tests__/document-cache.test.d.ts +2 -0
  252. package/dist/cache/__tests__/document-cache.test.d.ts.map +1 -0
  253. package/dist/cache/__tests__/document-cache.test.js +345 -0
  254. package/dist/cache/__tests__/document-cache.test.js.map +1 -0
  255. package/dist/cache/__tests__/memory-segment-store.test.d.ts +2 -0
  256. package/dist/cache/__tests__/memory-segment-store.test.d.ts.map +1 -0
  257. package/dist/cache/__tests__/memory-segment-store.test.js +425 -0
  258. package/dist/cache/__tests__/memory-segment-store.test.js.map +1 -0
  259. package/dist/cache/__tests__/memory-store.test.d.ts +2 -0
  260. package/dist/cache/__tests__/memory-store.test.d.ts.map +1 -0
  261. package/dist/cache/__tests__/memory-store.test.js +367 -0
  262. package/dist/cache/__tests__/memory-store.test.js.map +1 -0
  263. package/dist/cache/cache-scope.d.ts +102 -0
  264. package/dist/cache/cache-scope.d.ts.map +1 -0
  265. package/dist/cache/cache-scope.js +440 -0
  266. package/dist/cache/cache-scope.js.map +1 -0
  267. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts +2 -0
  268. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts.map +1 -0
  269. package/dist/cache/cf/__tests__/cf-cache-store.test.js +330 -0
  270. package/dist/cache/cf/__tests__/cf-cache-store.test.js.map +1 -0
  271. package/dist/cache/cf/cf-cache-store.d.ts +165 -0
  272. package/dist/cache/cf/cf-cache-store.d.ts.map +1 -0
  273. package/dist/cache/cf/cf-cache-store.js +242 -0
  274. package/dist/cache/cf/cf-cache-store.js.map +1 -0
  275. package/dist/cache/cf/index.d.ts +14 -0
  276. package/dist/cache/cf/index.d.ts.map +1 -0
  277. package/dist/cache/cf/index.js +17 -0
  278. package/dist/cache/cf/index.js.map +1 -0
  279. package/dist/cache/document-cache.d.ts +64 -0
  280. package/dist/cache/document-cache.d.ts.map +1 -0
  281. package/dist/cache/document-cache.js +228 -0
  282. package/dist/cache/document-cache.js.map +1 -0
  283. package/dist/cache/index.d.ts +19 -0
  284. package/dist/cache/index.d.ts.map +1 -0
  285. package/dist/cache/index.js +21 -0
  286. package/dist/cache/index.js.map +1 -0
  287. package/dist/cache/memory-segment-store.d.ts +110 -0
  288. package/dist/cache/memory-segment-store.d.ts.map +1 -0
  289. package/dist/cache/memory-segment-store.js +117 -0
  290. package/dist/cache/memory-segment-store.js.map +1 -0
  291. package/dist/cache/memory-store.d.ts +41 -0
  292. package/dist/cache/memory-store.d.ts.map +1 -0
  293. package/dist/cache/memory-store.js +191 -0
  294. package/dist/cache/memory-store.js.map +1 -0
  295. package/dist/cache/types.d.ts +317 -0
  296. package/dist/cache/types.d.ts.map +1 -0
  297. package/dist/cache/types.js +12 -0
  298. package/dist/cache/types.js.map +1 -0
  299. package/dist/client.d.ts +248 -0
  300. package/dist/client.d.ts.map +1 -0
  301. package/dist/client.js +367 -0
  302. package/dist/client.js.map +1 -0
  303. package/dist/client.rsc.d.ts +26 -0
  304. package/dist/client.rsc.d.ts.map +1 -0
  305. package/dist/client.rsc.js +46 -0
  306. package/dist/client.rsc.js.map +1 -0
  307. package/dist/component-utils.d.ts +36 -0
  308. package/dist/component-utils.d.ts.map +1 -0
  309. package/dist/component-utils.js +61 -0
  310. package/dist/component-utils.js.map +1 -0
  311. package/dist/components/DefaultDocument.d.ts +13 -0
  312. package/dist/components/DefaultDocument.d.ts.map +1 -0
  313. package/dist/components/DefaultDocument.js +15 -0
  314. package/dist/components/DefaultDocument.js.map +1 -0
  315. package/dist/debug.d.ts +58 -0
  316. package/dist/debug.d.ts.map +1 -0
  317. package/dist/debug.js +157 -0
  318. package/dist/debug.js.map +1 -0
  319. package/dist/default-error-boundary.d.ts +11 -0
  320. package/dist/default-error-boundary.d.ts.map +1 -0
  321. package/dist/default-error-boundary.js +45 -0
  322. package/dist/default-error-boundary.js.map +1 -0
  323. package/dist/deps/browser.d.ts +2 -0
  324. package/dist/deps/browser.d.ts.map +1 -0
  325. package/dist/deps/browser.js +3 -0
  326. package/dist/deps/browser.js.map +1 -0
  327. package/dist/deps/html-stream-client.d.ts +2 -0
  328. package/dist/deps/html-stream-client.d.ts.map +1 -0
  329. package/dist/deps/html-stream-client.js +3 -0
  330. package/dist/deps/html-stream-client.js.map +1 -0
  331. package/dist/deps/html-stream-server.d.ts +2 -0
  332. package/dist/deps/html-stream-server.d.ts.map +1 -0
  333. package/dist/deps/html-stream-server.js +3 -0
  334. package/dist/deps/html-stream-server.js.map +1 -0
  335. package/dist/deps/rsc.d.ts +2 -0
  336. package/dist/deps/rsc.d.ts.map +1 -0
  337. package/dist/deps/rsc.js +4 -0
  338. package/dist/deps/rsc.js.map +1 -0
  339. package/dist/deps/ssr.d.ts +2 -0
  340. package/dist/deps/ssr.d.ts.map +1 -0
  341. package/dist/deps/ssr.js +3 -0
  342. package/dist/deps/ssr.js.map +1 -0
  343. package/dist/errors.d.ts +174 -0
  344. package/dist/errors.d.ts.map +1 -0
  345. package/dist/errors.js +241 -0
  346. package/dist/errors.js.map +1 -0
  347. package/dist/handle.d.ts +78 -0
  348. package/dist/handle.d.ts.map +1 -0
  349. package/dist/handle.js +82 -0
  350. package/dist/handle.js.map +1 -0
  351. package/dist/handles/MetaTags.d.ts +14 -0
  352. package/dist/handles/MetaTags.d.ts.map +1 -0
  353. package/dist/handles/MetaTags.js +136 -0
  354. package/dist/handles/MetaTags.js.map +1 -0
  355. package/{src/handles/index.ts → dist/handles/index.d.ts} +1 -2
  356. package/dist/handles/index.d.ts.map +1 -0
  357. package/dist/handles/index.js +6 -0
  358. package/dist/handles/index.js.map +1 -0
  359. package/dist/handles/meta.d.ts +39 -0
  360. package/dist/handles/meta.d.ts.map +1 -0
  361. package/dist/handles/meta.js +202 -0
  362. package/dist/handles/meta.js.map +1 -0
  363. package/dist/host/__tests__/errors.test.d.ts +2 -0
  364. package/dist/host/__tests__/errors.test.d.ts.map +1 -0
  365. package/dist/host/__tests__/errors.test.js +76 -0
  366. package/dist/host/__tests__/errors.test.js.map +1 -0
  367. package/dist/host/__tests__/pattern-comprehensive.test.d.ts +2 -0
  368. package/dist/host/__tests__/pattern-comprehensive.test.d.ts.map +1 -0
  369. package/dist/host/__tests__/pattern-comprehensive.test.js +732 -0
  370. package/dist/host/__tests__/pattern-comprehensive.test.js.map +1 -0
  371. package/dist/host/__tests__/pattern-matcher.test.d.ts +2 -0
  372. package/dist/host/__tests__/pattern-matcher.test.d.ts.map +1 -0
  373. package/dist/host/__tests__/pattern-matcher.test.js +251 -0
  374. package/dist/host/__tests__/pattern-matcher.test.js.map +1 -0
  375. package/dist/host/__tests__/router.test.d.ts +2 -0
  376. package/dist/host/__tests__/router.test.d.ts.map +1 -0
  377. package/dist/host/__tests__/router.test.js +241 -0
  378. package/dist/host/__tests__/router.test.js.map +1 -0
  379. package/dist/host/__tests__/testing.test.d.ts +2 -0
  380. package/dist/host/__tests__/testing.test.d.ts.map +1 -0
  381. package/dist/host/__tests__/testing.test.js +64 -0
  382. package/dist/host/__tests__/testing.test.js.map +1 -0
  383. package/dist/host/__tests__/utils.test.d.ts +2 -0
  384. package/dist/host/__tests__/utils.test.d.ts.map +1 -0
  385. package/dist/host/__tests__/utils.test.js +29 -0
  386. package/dist/host/__tests__/utils.test.js.map +1 -0
  387. package/dist/host/cookie-handler.d.ts +34 -0
  388. package/dist/host/cookie-handler.d.ts.map +1 -0
  389. package/dist/host/cookie-handler.js +124 -0
  390. package/dist/host/cookie-handler.js.map +1 -0
  391. package/dist/host/errors.d.ts +56 -0
  392. package/dist/host/errors.d.ts.map +1 -0
  393. package/dist/host/errors.js +79 -0
  394. package/dist/host/errors.js.map +1 -0
  395. package/dist/host/index.d.ts +29 -0
  396. package/dist/host/index.d.ts.map +1 -0
  397. package/dist/host/index.js +32 -0
  398. package/dist/host/index.js.map +1 -0
  399. package/dist/host/pattern-matcher.d.ts +36 -0
  400. package/dist/host/pattern-matcher.d.ts.map +1 -0
  401. package/dist/host/pattern-matcher.js +172 -0
  402. package/dist/host/pattern-matcher.js.map +1 -0
  403. package/dist/host/router.d.ts +26 -0
  404. package/dist/host/router.d.ts.map +1 -0
  405. package/dist/host/router.js +218 -0
  406. package/dist/host/router.js.map +1 -0
  407. package/dist/host/testing.d.ts +36 -0
  408. package/dist/host/testing.d.ts.map +1 -0
  409. package/dist/host/testing.js +55 -0
  410. package/dist/host/testing.js.map +1 -0
  411. package/dist/host/types.d.ts +115 -0
  412. package/dist/host/types.d.ts.map +1 -0
  413. package/dist/host/types.js +7 -0
  414. package/dist/host/types.js.map +1 -0
  415. package/dist/host/utils.d.ts +21 -0
  416. package/dist/host/utils.d.ts.map +1 -0
  417. package/dist/host/utils.js +23 -0
  418. package/dist/host/utils.js.map +1 -0
  419. package/dist/href-client.d.ts +131 -0
  420. package/dist/href-client.d.ts.map +1 -0
  421. package/dist/href-client.js +64 -0
  422. package/dist/href-client.js.map +1 -0
  423. package/dist/href-context.d.ts +29 -0
  424. package/dist/href-context.d.ts.map +1 -0
  425. package/dist/href-context.js +21 -0
  426. package/dist/href-context.js.map +1 -0
  427. package/dist/index.d.ts +73 -0
  428. package/dist/index.d.ts.map +1 -0
  429. package/dist/index.js +91 -0
  430. package/dist/index.js.map +1 -0
  431. package/dist/index.rsc.d.ts +32 -0
  432. package/dist/index.rsc.d.ts.map +1 -0
  433. package/dist/index.rsc.js +40 -0
  434. package/dist/index.rsc.js.map +1 -0
  435. package/dist/internal-debug.d.ts +2 -0
  436. package/dist/internal-debug.d.ts.map +1 -0
  437. package/dist/internal-debug.js +5 -0
  438. package/dist/internal-debug.js.map +1 -0
  439. package/dist/loader.d.ts +14 -0
  440. package/dist/loader.d.ts.map +1 -0
  441. package/dist/loader.js +20 -0
  442. package/dist/loader.js.map +1 -0
  443. package/dist/loader.rsc.d.ts +19 -0
  444. package/dist/loader.rsc.d.ts.map +1 -0
  445. package/dist/loader.rsc.js +99 -0
  446. package/dist/loader.rsc.js.map +1 -0
  447. package/{src/network-error-thrower.tsx → dist/network-error-thrower.d.ts} +4 -10
  448. package/dist/network-error-thrower.d.ts.map +1 -0
  449. package/dist/network-error-thrower.js +14 -0
  450. package/dist/network-error-thrower.js.map +1 -0
  451. package/dist/outlet-context.d.ts +13 -0
  452. package/dist/outlet-context.d.ts.map +1 -0
  453. package/dist/outlet-context.js +3 -0
  454. package/dist/outlet-context.js.map +1 -0
  455. package/dist/prerender/__tests__/param-hash.test.d.ts +2 -0
  456. package/dist/prerender/__tests__/param-hash.test.d.ts.map +1 -0
  457. package/dist/prerender/__tests__/param-hash.test.js +148 -0
  458. package/dist/prerender/__tests__/param-hash.test.js.map +1 -0
  459. package/dist/prerender/param-hash.d.ts +16 -0
  460. package/dist/prerender/param-hash.d.ts.map +1 -0
  461. package/dist/prerender/param-hash.js +36 -0
  462. package/dist/prerender/param-hash.js.map +1 -0
  463. package/dist/prerender/store.d.ts +38 -0
  464. package/dist/prerender/store.d.ts.map +1 -0
  465. package/dist/prerender/store.js +61 -0
  466. package/dist/prerender/store.js.map +1 -0
  467. package/dist/prerender.d.ts +66 -0
  468. package/dist/prerender.d.ts.map +1 -0
  469. package/dist/prerender.js +57 -0
  470. package/dist/prerender.js.map +1 -0
  471. package/dist/reverse.d.ts +196 -0
  472. package/dist/reverse.d.ts.map +1 -0
  473. package/dist/reverse.js +78 -0
  474. package/dist/reverse.js.map +1 -0
  475. package/dist/root-error-boundary.d.ts +33 -0
  476. package/dist/root-error-boundary.d.ts.map +1 -0
  477. package/dist/root-error-boundary.js +165 -0
  478. package/dist/root-error-boundary.js.map +1 -0
  479. package/dist/route-content-wrapper.d.ts +46 -0
  480. package/dist/route-content-wrapper.d.ts.map +1 -0
  481. package/dist/route-content-wrapper.js +77 -0
  482. package/dist/route-content-wrapper.js.map +1 -0
  483. package/dist/route-definition.d.ts +421 -0
  484. package/dist/route-definition.d.ts.map +1 -0
  485. package/dist/route-definition.js +868 -0
  486. package/dist/route-definition.js.map +1 -0
  487. package/dist/route-map-builder.d.ts +155 -0
  488. package/dist/route-map-builder.d.ts.map +1 -0
  489. package/dist/route-map-builder.js +237 -0
  490. package/dist/route-map-builder.js.map +1 -0
  491. package/dist/route-types.d.ts +165 -0
  492. package/dist/route-types.d.ts.map +1 -0
  493. package/dist/route-types.js +7 -0
  494. package/dist/route-types.js.map +1 -0
  495. package/dist/router/__tests__/handler-context.test.d.ts +2 -0
  496. package/dist/router/__tests__/handler-context.test.d.ts.map +1 -0
  497. package/dist/router/__tests__/handler-context.test.js +65 -0
  498. package/dist/router/__tests__/handler-context.test.js.map +1 -0
  499. package/dist/router/__tests__/loader-cycle-detection.test.d.ts +2 -0
  500. package/dist/router/__tests__/loader-cycle-detection.test.d.ts.map +1 -0
  501. package/dist/router/__tests__/loader-cycle-detection.test.js +221 -0
  502. package/dist/router/__tests__/loader-cycle-detection.test.js.map +1 -0
  503. package/dist/router/__tests__/match-context.test.d.ts +2 -0
  504. package/dist/router/__tests__/match-context.test.d.ts.map +1 -0
  505. package/dist/router/__tests__/match-context.test.js +92 -0
  506. package/dist/router/__tests__/match-context.test.js.map +1 -0
  507. package/dist/router/__tests__/match-pipelines.test.d.ts +2 -0
  508. package/dist/router/__tests__/match-pipelines.test.d.ts.map +1 -0
  509. package/dist/router/__tests__/match-pipelines.test.js +417 -0
  510. package/dist/router/__tests__/match-pipelines.test.js.map +1 -0
  511. package/dist/router/__tests__/match-result.test.d.ts +2 -0
  512. package/dist/router/__tests__/match-result.test.d.ts.map +1 -0
  513. package/dist/router/__tests__/match-result.test.js +457 -0
  514. package/dist/router/__tests__/match-result.test.js.map +1 -0
  515. package/dist/router/__tests__/on-error.test.d.ts +2 -0
  516. package/dist/router/__tests__/on-error.test.d.ts.map +1 -0
  517. package/dist/router/__tests__/on-error.test.js +678 -0
  518. package/dist/router/__tests__/on-error.test.js.map +1 -0
  519. package/dist/router/__tests__/pattern-matching.test.d.ts +2 -0
  520. package/dist/router/__tests__/pattern-matching.test.d.ts.map +1 -0
  521. package/dist/router/__tests__/pattern-matching.test.js +629 -0
  522. package/dist/router/__tests__/pattern-matching.test.js.map +1 -0
  523. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts +2 -0
  524. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts.map +1 -0
  525. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js +155 -0
  526. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js.map +1 -0
  527. package/dist/router/error-handling.d.ts +77 -0
  528. package/dist/router/error-handling.d.ts.map +1 -0
  529. package/dist/router/error-handling.js +202 -0
  530. package/dist/router/error-handling.js.map +1 -0
  531. package/dist/router/handler-context.d.ts +20 -0
  532. package/dist/router/handler-context.d.ts.map +1 -0
  533. package/dist/router/handler-context.js +198 -0
  534. package/dist/router/handler-context.js.map +1 -0
  535. package/dist/router/intercept-resolution.d.ts +66 -0
  536. package/dist/router/intercept-resolution.d.ts.map +1 -0
  537. package/dist/router/intercept-resolution.js +246 -0
  538. package/dist/router/intercept-resolution.js.map +1 -0
  539. package/dist/router/loader-resolution.d.ts +64 -0
  540. package/dist/router/loader-resolution.d.ts.map +1 -0
  541. package/dist/router/loader-resolution.js +284 -0
  542. package/dist/router/loader-resolution.js.map +1 -0
  543. package/dist/router/logging.d.ts +15 -0
  544. package/dist/router/logging.d.ts.map +1 -0
  545. package/dist/router/logging.js +99 -0
  546. package/dist/router/logging.js.map +1 -0
  547. package/dist/router/manifest.d.ts +22 -0
  548. package/dist/router/manifest.d.ts.map +1 -0
  549. package/dist/router/manifest.js +181 -0
  550. package/dist/router/manifest.js.map +1 -0
  551. package/dist/router/match-api.d.ts +35 -0
  552. package/dist/router/match-api.d.ts.map +1 -0
  553. package/dist/router/match-api.js +406 -0
  554. package/dist/router/match-api.js.map +1 -0
  555. package/dist/router/match-context.d.ts +206 -0
  556. package/dist/router/match-context.d.ts.map +1 -0
  557. package/dist/router/match-context.js +17 -0
  558. package/dist/router/match-context.js.map +1 -0
  559. package/dist/router/match-middleware/background-revalidation.d.ts +127 -0
  560. package/dist/router/match-middleware/background-revalidation.d.ts.map +1 -0
  561. package/dist/router/match-middleware/background-revalidation.js +75 -0
  562. package/dist/router/match-middleware/background-revalidation.js.map +1 -0
  563. package/dist/router/match-middleware/cache-lookup.d.ts +112 -0
  564. package/dist/router/match-middleware/cache-lookup.d.ts.map +1 -0
  565. package/dist/router/match-middleware/cache-lookup.js +257 -0
  566. package/dist/router/match-middleware/cache-lookup.js.map +1 -0
  567. package/dist/router/match-middleware/cache-store.d.ts +113 -0
  568. package/dist/router/match-middleware/cache-store.d.ts.map +1 -0
  569. package/dist/router/match-middleware/cache-store.js +108 -0
  570. package/dist/router/match-middleware/cache-store.js.map +1 -0
  571. package/dist/router/match-middleware/index.d.ts +81 -0
  572. package/dist/router/match-middleware/index.d.ts.map +1 -0
  573. package/dist/router/match-middleware/index.js +80 -0
  574. package/dist/router/match-middleware/index.js.map +1 -0
  575. package/dist/router/match-middleware/intercept-resolution.d.ts +117 -0
  576. package/dist/router/match-middleware/intercept-resolution.d.ts.map +1 -0
  577. package/dist/router/match-middleware/intercept-resolution.js +134 -0
  578. package/dist/router/match-middleware/intercept-resolution.js.map +1 -0
  579. package/dist/router/match-middleware/segment-resolution.d.ts +99 -0
  580. package/dist/router/match-middleware/segment-resolution.d.ts.map +1 -0
  581. package/dist/router/match-middleware/segment-resolution.js +53 -0
  582. package/dist/router/match-middleware/segment-resolution.js.map +1 -0
  583. package/dist/router/match-pipelines.d.ts +147 -0
  584. package/dist/router/match-pipelines.d.ts.map +1 -0
  585. package/dist/router/match-pipelines.js +82 -0
  586. package/dist/router/match-pipelines.js.map +1 -0
  587. package/dist/router/match-result.d.ts +126 -0
  588. package/dist/router/match-result.d.ts.map +1 -0
  589. package/dist/router/match-result.js +93 -0
  590. package/dist/router/match-result.js.map +1 -0
  591. package/dist/router/metrics.d.ts +20 -0
  592. package/dist/router/metrics.d.ts.map +1 -0
  593. package/dist/router/metrics.js +47 -0
  594. package/dist/router/metrics.js.map +1 -0
  595. package/dist/router/middleware.d.ts +249 -0
  596. package/dist/router/middleware.d.ts.map +1 -0
  597. package/dist/router/middleware.js +434 -0
  598. package/dist/router/middleware.js.map +1 -0
  599. package/dist/router/middleware.test.d.ts +2 -0
  600. package/dist/router/middleware.test.d.ts.map +1 -0
  601. package/dist/router/middleware.test.js +816 -0
  602. package/dist/router/middleware.test.js.map +1 -0
  603. package/dist/router/pattern-matching.d.ts +149 -0
  604. package/dist/router/pattern-matching.d.ts.map +1 -0
  605. package/dist/router/pattern-matching.js +349 -0
  606. package/dist/router/pattern-matching.js.map +1 -0
  607. package/dist/router/revalidation.d.ts +44 -0
  608. package/dist/router/revalidation.d.ts.map +1 -0
  609. package/dist/router/revalidation.js +147 -0
  610. package/dist/router/revalidation.js.map +1 -0
  611. package/dist/router/router-context.d.ts +135 -0
  612. package/dist/router/router-context.d.ts.map +1 -0
  613. package/dist/router/router-context.js +36 -0
  614. package/dist/router/router-context.js.map +1 -0
  615. package/dist/router/segment-resolution.d.ts +127 -0
  616. package/dist/router/segment-resolution.d.ts.map +1 -0
  617. package/dist/router/segment-resolution.js +919 -0
  618. package/dist/router/segment-resolution.js.map +1 -0
  619. package/dist/router/trie-matching.d.ts +40 -0
  620. package/dist/router/trie-matching.d.ts.map +1 -0
  621. package/dist/router/trie-matching.js +127 -0
  622. package/dist/router/trie-matching.js.map +1 -0
  623. package/dist/router/types.d.ts +136 -0
  624. package/dist/router/types.d.ts.map +1 -0
  625. package/dist/router/types.js +7 -0
  626. package/dist/router/types.js.map +1 -0
  627. package/dist/router.d.ts +753 -0
  628. package/dist/router.d.ts.map +1 -0
  629. package/dist/router.gen.d.ts +6 -0
  630. package/dist/router.gen.d.ts.map +1 -0
  631. package/dist/router.gen.js +6 -0
  632. package/dist/router.gen.js.map +1 -0
  633. package/dist/router.js +1304 -0
  634. package/dist/router.js.map +1 -0
  635. package/dist/rsc/__tests__/helpers.test.d.ts +2 -0
  636. package/dist/rsc/__tests__/helpers.test.d.ts.map +1 -0
  637. package/dist/rsc/__tests__/helpers.test.js +140 -0
  638. package/dist/rsc/__tests__/helpers.test.js.map +1 -0
  639. package/dist/rsc/handler.d.ts +45 -0
  640. package/dist/rsc/handler.d.ts.map +1 -0
  641. package/dist/rsc/handler.js +1172 -0
  642. package/dist/rsc/handler.js.map +1 -0
  643. package/dist/rsc/helpers.d.ts +16 -0
  644. package/dist/rsc/helpers.d.ts.map +1 -0
  645. package/dist/rsc/helpers.js +55 -0
  646. package/dist/rsc/helpers.js.map +1 -0
  647. package/dist/rsc/index.d.ts +22 -0
  648. package/dist/rsc/index.d.ts.map +1 -0
  649. package/dist/rsc/index.js +23 -0
  650. package/dist/rsc/index.js.map +1 -0
  651. package/dist/rsc/nonce.d.ts +9 -0
  652. package/dist/rsc/nonce.d.ts.map +1 -0
  653. package/dist/rsc/nonce.js +18 -0
  654. package/dist/rsc/nonce.js.map +1 -0
  655. package/dist/rsc/types.d.ts +206 -0
  656. package/dist/rsc/types.d.ts.map +1 -0
  657. package/dist/rsc/types.js +8 -0
  658. package/dist/rsc/types.js.map +1 -0
  659. package/dist/search-params.d.ts +103 -0
  660. package/dist/search-params.d.ts.map +1 -0
  661. package/dist/search-params.js +74 -0
  662. package/dist/search-params.js.map +1 -0
  663. package/dist/segment-system.d.ts +75 -0
  664. package/dist/segment-system.d.ts.map +1 -0
  665. package/dist/segment-system.js +336 -0
  666. package/dist/segment-system.js.map +1 -0
  667. package/dist/server/context.d.ts +245 -0
  668. package/dist/server/context.d.ts.map +1 -0
  669. package/dist/server/context.js +197 -0
  670. package/dist/server/context.js.map +1 -0
  671. package/dist/server/fetchable-loader-store.d.ts +18 -0
  672. package/dist/server/fetchable-loader-store.d.ts.map +1 -0
  673. package/dist/server/fetchable-loader-store.js +18 -0
  674. package/dist/server/fetchable-loader-store.js.map +1 -0
  675. package/dist/server/handle-store.d.ts +85 -0
  676. package/dist/server/handle-store.d.ts.map +1 -0
  677. package/dist/server/handle-store.js +142 -0
  678. package/dist/server/handle-store.js.map +1 -0
  679. package/dist/server/loader-registry.d.ts +55 -0
  680. package/dist/server/loader-registry.d.ts.map +1 -0
  681. package/dist/server/loader-registry.js +132 -0
  682. package/dist/server/loader-registry.js.map +1 -0
  683. package/dist/server/request-context.d.ts +226 -0
  684. package/dist/server/request-context.d.ts.map +1 -0
  685. package/dist/server/request-context.js +290 -0
  686. package/dist/server/request-context.js.map +1 -0
  687. package/dist/server/root-layout.d.ts +4 -0
  688. package/dist/server/root-layout.d.ts.map +1 -0
  689. package/dist/server/root-layout.js +5 -0
  690. package/dist/server/root-layout.js.map +1 -0
  691. package/dist/server.d.ts +15 -0
  692. package/dist/server.d.ts.map +1 -0
  693. package/dist/server.js +20 -0
  694. package/dist/server.js.map +1 -0
  695. package/dist/ssr/__tests__/ssr-handler.test.d.ts +2 -0
  696. package/dist/ssr/__tests__/ssr-handler.test.d.ts.map +1 -0
  697. package/dist/ssr/__tests__/ssr-handler.test.js +132 -0
  698. package/dist/ssr/__tests__/ssr-handler.test.js.map +1 -0
  699. package/dist/ssr/index.d.ts +98 -0
  700. package/dist/ssr/index.d.ts.map +1 -0
  701. package/dist/ssr/index.js +158 -0
  702. package/dist/ssr/index.js.map +1 -0
  703. package/dist/static-handler.d.ts +50 -0
  704. package/dist/static-handler.d.ts.map +1 -0
  705. package/dist/static-handler.gen.d.ts +5 -0
  706. package/dist/static-handler.gen.d.ts.map +1 -0
  707. package/dist/static-handler.gen.js +5 -0
  708. package/dist/static-handler.gen.js.map +1 -0
  709. package/dist/static-handler.js +29 -0
  710. package/dist/static-handler.js.map +1 -0
  711. package/dist/testing/vitest.js +82 -0
  712. package/dist/theme/ThemeProvider.d.ts +20 -0
  713. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  714. package/dist/theme/ThemeProvider.js +240 -0
  715. package/dist/theme/ThemeProvider.js.map +1 -0
  716. package/dist/theme/ThemeScript.d.ts +48 -0
  717. package/dist/theme/ThemeScript.d.ts.map +1 -0
  718. package/dist/theme/ThemeScript.js +13 -0
  719. package/dist/theme/ThemeScript.js.map +1 -0
  720. package/dist/theme/__tests__/theme.test.d.ts +2 -0
  721. package/dist/theme/__tests__/theme.test.d.ts.map +1 -0
  722. package/dist/theme/__tests__/theme.test.js +103 -0
  723. package/dist/theme/__tests__/theme.test.js.map +1 -0
  724. package/dist/theme/constants.d.ts +29 -0
  725. package/dist/theme/constants.d.ts.map +1 -0
  726. package/dist/theme/constants.js +48 -0
  727. package/dist/theme/constants.js.map +1 -0
  728. package/dist/theme/index.d.ts +31 -0
  729. package/dist/theme/index.d.ts.map +1 -0
  730. package/dist/theme/index.js +36 -0
  731. package/dist/theme/index.js.map +1 -0
  732. package/dist/theme/theme-context.d.ts +40 -0
  733. package/dist/theme/theme-context.d.ts.map +1 -0
  734. package/dist/theme/theme-context.js +60 -0
  735. package/dist/theme/theme-context.js.map +1 -0
  736. package/dist/theme/theme-script.d.ts +27 -0
  737. package/dist/theme/theme-script.d.ts.map +1 -0
  738. package/dist/theme/theme-script.js +147 -0
  739. package/dist/theme/theme-script.js.map +1 -0
  740. package/dist/theme/types.d.ts +163 -0
  741. package/dist/theme/types.d.ts.map +1 -0
  742. package/dist/theme/types.js +11 -0
  743. package/dist/theme/types.js.map +1 -0
  744. package/dist/theme/use-theme.d.ts +12 -0
  745. package/dist/theme/use-theme.d.ts.map +1 -0
  746. package/dist/theme/use-theme.js +40 -0
  747. package/dist/theme/use-theme.js.map +1 -0
  748. package/dist/types.d.ts +1479 -0
  749. package/dist/types.d.ts.map +1 -0
  750. package/dist/types.js +10 -0
  751. package/dist/types.js.map +1 -0
  752. package/dist/urls.d.ts +441 -0
  753. package/dist/urls.d.ts.map +1 -0
  754. package/dist/urls.gen.d.ts +8 -0
  755. package/dist/urls.gen.d.ts.map +1 -0
  756. package/dist/urls.gen.js +8 -0
  757. package/dist/urls.gen.js.map +1 -0
  758. package/dist/urls.js +443 -0
  759. package/dist/urls.js.map +1 -0
  760. package/dist/use-loader.d.ts +127 -0
  761. package/dist/use-loader.d.ts.map +1 -0
  762. package/dist/use-loader.js +237 -0
  763. package/dist/use-loader.js.map +1 -0
  764. package/dist/vite/__tests__/ast-handler-extract.test.d.ts +2 -0
  765. package/dist/vite/__tests__/ast-handler-extract.test.d.ts.map +1 -0
  766. package/dist/vite/__tests__/ast-handler-extract.test.js +294 -0
  767. package/dist/vite/__tests__/ast-handler-extract.test.js.map +1 -0
  768. package/dist/vite/__tests__/expose-id-utils.test.d.ts +2 -0
  769. package/dist/vite/__tests__/expose-id-utils.test.d.ts.map +1 -0
  770. package/dist/vite/__tests__/expose-id-utils.test.js +224 -0
  771. package/dist/vite/__tests__/expose-id-utils.test.js.map +1 -0
  772. package/dist/vite/__tests__/expose-internal-ids.test.d.ts +2 -0
  773. package/dist/vite/__tests__/expose-internal-ids.test.d.ts.map +1 -0
  774. package/dist/vite/__tests__/expose-internal-ids.test.js +647 -0
  775. package/dist/vite/__tests__/expose-internal-ids.test.js.map +1 -0
  776. package/dist/vite/__tests__/expose-router-id.test.d.ts +2 -0
  777. package/dist/vite/__tests__/expose-router-id.test.d.ts.map +1 -0
  778. package/dist/vite/__tests__/expose-router-id.test.js +39 -0
  779. package/dist/vite/__tests__/expose-router-id.test.js.map +1 -0
  780. package/dist/vite/ast-handler-extract.d.ts +49 -0
  781. package/dist/vite/ast-handler-extract.d.ts.map +1 -0
  782. package/dist/vite/ast-handler-extract.js +249 -0
  783. package/dist/vite/ast-handler-extract.js.map +1 -0
  784. package/dist/vite/expose-action-id.d.ts +19 -0
  785. package/dist/vite/expose-action-id.d.ts.map +1 -0
  786. package/dist/vite/expose-action-id.js +250 -0
  787. package/dist/vite/expose-action-id.js.map +1 -0
  788. package/dist/vite/expose-id-utils.d.ts +69 -0
  789. package/dist/vite/expose-id-utils.d.ts.map +1 -0
  790. package/dist/vite/expose-id-utils.js +289 -0
  791. package/dist/vite/expose-id-utils.js.map +1 -0
  792. package/dist/vite/expose-internal-ids.d.ts +22 -0
  793. package/dist/vite/expose-internal-ids.d.ts.map +1 -0
  794. package/dist/vite/expose-internal-ids.js +886 -0
  795. package/dist/vite/expose-internal-ids.js.map +1 -0
  796. package/dist/vite/index.d.ts +149 -0
  797. package/dist/vite/index.d.ts.map +1 -0
  798. package/dist/vite/index.js +4279 -1209
  799. package/dist/vite/index.js.bak +5448 -0
  800. package/dist/vite/index.js.map +1 -0
  801. package/dist/vite/index.named-routes.gen.ts +103 -0
  802. package/dist/vite/package-resolution.d.ts +43 -0
  803. package/dist/vite/package-resolution.d.ts.map +1 -0
  804. package/dist/vite/package-resolution.js +112 -0
  805. package/dist/vite/package-resolution.js.map +1 -0
  806. package/dist/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  807. package/dist/vite/virtual-entries.d.ts +25 -0
  808. package/dist/vite/virtual-entries.d.ts.map +1 -0
  809. package/dist/vite/virtual-entries.js +110 -0
  810. package/dist/vite/virtual-entries.js.map +1 -0
  811. package/package.json +86 -15
  812. package/skills/api-client/SKILL.md +211 -0
  813. package/skills/breadcrumbs/SKILL.md +85 -6
  814. package/skills/bundle-analysis/SKILL.md +159 -0
  815. package/skills/cache-guide/SKILL.md +251 -24
  816. package/skills/caching/SKILL.md +338 -13
  817. package/skills/catalog.json +271 -0
  818. package/skills/comparison/SKILL.md +50 -0
  819. package/skills/comparison/agents/openai.yaml +4 -0
  820. package/skills/comparison/references/framework-comparison.md +837 -0
  821. package/skills/composability/SKILL.md +110 -4
  822. package/skills/css/SKILL.md +76 -0
  823. package/skills/debug-manifest/SKILL.md +5 -3
  824. package/skills/defer-hydration/SKILL.md +235 -0
  825. package/skills/document-cache/SKILL.md +87 -56
  826. package/skills/fonts/SKILL.md +1 -1
  827. package/skills/handler-use/SKILL.md +364 -0
  828. package/skills/hooks/SKILL.md +73 -691
  829. package/skills/hooks/data.md +273 -0
  830. package/skills/hooks/handle-and-actions.md +103 -0
  831. package/skills/hooks/navigation.md +110 -0
  832. package/skills/hooks/outlets.md +41 -0
  833. package/skills/hooks/state.md +228 -0
  834. package/skills/hooks/urls.md +135 -0
  835. package/skills/host-router/SKILL.md +129 -27
  836. package/skills/i18n/SKILL.md +276 -0
  837. package/skills/intercept/SKILL.md +94 -18
  838. package/skills/layout/SKILL.md +62 -19
  839. package/skills/links/SKILL.md +249 -17
  840. package/skills/loader/SKILL.md +302 -54
  841. package/skills/middleware/SKILL.md +59 -16
  842. package/skills/migrate-nextjs/SKILL.md +745 -0
  843. package/skills/migrate-react-router/SKILL.md +153 -0
  844. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  845. package/skills/migrate-react-router/component-migration.md +196 -0
  846. package/skills/migrate-react-router/data-and-actions.md +225 -0
  847. package/skills/migrate-react-router/route-mapping.md +271 -0
  848. package/skills/mime-routes/SKILL.md +29 -2
  849. package/skills/observability/SKILL.md +202 -0
  850. package/skills/parallel/SKILL.md +225 -10
  851. package/skills/ppr/SKILL.md +622 -0
  852. package/skills/prerender/SKILL.md +178 -124
  853. package/skills/rango/SKILL.md +318 -24
  854. package/skills/react-compiler/SKILL.md +168 -0
  855. package/skills/response-routes/SKILL.md +138 -49
  856. package/skills/route/SKILL.md +172 -9
  857. package/skills/router-setup/SKILL.md +131 -11
  858. package/skills/scripts/SKILL.md +179 -0
  859. package/skills/server-actions/SKILL.md +776 -0
  860. package/skills/shell-manifest/SKILL.md +185 -0
  861. package/skills/streams-and-websockets/SKILL.md +283 -0
  862. package/skills/tailwind/SKILL.md +28 -4
  863. package/skills/testing/SKILL.md +130 -0
  864. package/skills/testing/bindings.md +103 -0
  865. package/skills/testing/cache-prerender.md +127 -0
  866. package/skills/testing/client-components.md +124 -0
  867. package/skills/testing/e2e-parity.md +125 -0
  868. package/skills/testing/flight.md +91 -0
  869. package/skills/testing/handles.md +131 -0
  870. package/skills/testing/loader.md +128 -0
  871. package/skills/testing/middleware.md +99 -0
  872. package/skills/testing/render-handler.md +122 -0
  873. package/skills/testing/response-routes.md +95 -0
  874. package/skills/testing/reverse-and-types.md +85 -0
  875. package/skills/testing/server-actions.md +107 -0
  876. package/skills/testing/server-tree.md +128 -0
  877. package/skills/testing/setup.md +123 -0
  878. package/skills/theme/SKILL.md +1 -1
  879. package/skills/typesafety/SKILL.md +45 -616
  880. package/skills/typesafety/env-and-bindings.md +254 -0
  881. package/skills/typesafety/generated-files-and-cli.md +335 -0
  882. package/skills/typesafety/params-and-search.md +153 -0
  883. package/skills/typesafety/route-types.md +209 -0
  884. package/skills/use-cache/SKILL.md +74 -15
  885. package/skills/vercel/SKILL.md +128 -0
  886. package/skills/view-transitions/SKILL.md +337 -0
  887. package/src/__augment-tests__/augment.ts +81 -0
  888. package/src/__augment-tests__/augmented.check.ts +116 -0
  889. package/src/__internal.ts +1 -66
  890. package/src/browser/action-coordinator.ts +53 -36
  891. package/src/browser/action-fence.ts +47 -0
  892. package/src/browser/app-shell.ts +39 -0
  893. package/src/browser/app-version.ts +14 -0
  894. package/src/browser/connection-warmup.ts +134 -0
  895. package/src/browser/cookie-name.ts +140 -0
  896. package/src/browser/event-controller.ts +257 -158
  897. package/src/browser/history-state.ts +21 -0
  898. package/src/browser/index.ts +3 -3
  899. package/src/browser/invalidate-client-cache.ts +52 -0
  900. package/src/browser/logging.ts +28 -0
  901. package/src/browser/merge-segment-loaders.ts +6 -4
  902. package/src/browser/navigation-bridge.ts +131 -30
  903. package/src/browser/navigation-client.ts +202 -105
  904. package/src/browser/navigation-store-handle.ts +38 -0
  905. package/src/browser/navigation-store.ts +203 -80
  906. package/src/browser/navigation-transaction.ts +9 -59
  907. package/src/browser/network-error-handler.ts +34 -7
  908. package/src/browser/partial-update.ts +205 -121
  909. package/src/browser/prefetch/cache.ts +247 -72
  910. package/src/browser/prefetch/fetch.ts +359 -42
  911. package/src/browser/prefetch/queue.ts +113 -32
  912. package/src/browser/prefetch/resource-ready.ts +77 -0
  913. package/src/browser/rango-state.ts +158 -76
  914. package/src/browser/react/Link.tsx +112 -15
  915. package/src/browser/react/NavigationProvider.tsx +213 -122
  916. package/src/browser/react/ScrollRestoration.tsx +10 -6
  917. package/src/browser/react/context.ts +7 -2
  918. package/src/browser/react/filter-segment-order.ts +66 -7
  919. package/src/browser/react/index.ts +0 -48
  920. package/src/browser/react/location-state-shared.ts +178 -8
  921. package/src/browser/react/location-state.ts +39 -14
  922. package/src/browser/react/use-action.ts +6 -15
  923. package/src/browser/react/use-handle.ts +23 -69
  924. package/src/browser/react/use-href.tsx +8 -1
  925. package/src/browser/react/use-link-status.ts +33 -8
  926. package/src/browser/react/use-navigation.ts +32 -7
  927. package/src/browser/react/use-params.ts +20 -10
  928. package/src/browser/react/use-reverse.ts +106 -0
  929. package/src/browser/react/use-router.ts +46 -11
  930. package/src/browser/react/use-search-params.ts +0 -5
  931. package/src/browser/react/use-segments.ts +11 -21
  932. package/src/browser/response-adapter.ts +99 -8
  933. package/src/browser/rsc-router.tsx +186 -27
  934. package/src/browser/scroll-restoration.ts +44 -27
  935. package/src/browser/segment-reconciler.ts +44 -7
  936. package/src/browser/segment-structure-assert.ts +2 -2
  937. package/src/browser/server-action-bridge.ts +244 -71
  938. package/src/browser/types.ts +127 -12
  939. package/src/browser/validate-redirect-origin.ts +43 -16
  940. package/src/build/collect-fallback-refs.ts +107 -0
  941. package/src/build/generate-manifest.ts +207 -158
  942. package/src/build/generate-route-types.ts +6 -1
  943. package/src/build/index.ts +11 -3
  944. package/src/build/prefix-tree-utils.ts +123 -0
  945. package/src/build/route-trie.ts +198 -41
  946. package/src/build/route-types/ast-route-extraction.ts +15 -8
  947. package/src/build/route-types/codegen.ts +16 -5
  948. package/src/build/route-types/include-resolution.ts +464 -63
  949. package/src/build/route-types/param-extraction.ts +6 -3
  950. package/src/build/route-types/per-module-writer.ts +22 -6
  951. package/src/build/route-types/router-processing.ts +333 -117
  952. package/src/build/route-types/scan-filter.ts +9 -2
  953. package/src/build/route-types/source-scan.ts +216 -0
  954. package/src/build/runtime-discovery.ts +13 -21
  955. package/src/cache/cache-error.ts +104 -0
  956. package/src/cache/cache-key-utils.ts +58 -13
  957. package/src/cache/cache-policy.ts +108 -34
  958. package/src/cache/cache-runtime.ts +454 -97
  959. package/src/cache/cache-scope.ts +233 -101
  960. package/src/cache/cache-tag.ts +149 -0
  961. package/src/cache/cf/cf-base64.ts +33 -0
  962. package/src/cache/cf/cf-cache-constants.ts +127 -0
  963. package/src/cache/cf/cf-cache-store.ts +2206 -372
  964. package/src/cache/cf/cf-cache-types.ts +349 -0
  965. package/src/cache/cf/cf-kv-utils.ts +46 -0
  966. package/src/cache/cf/cf-tag-marker-memo.ts +105 -0
  967. package/src/cache/cf/index.ts +6 -16
  968. package/src/cache/document-cache.ts +144 -49
  969. package/src/cache/handle-snapshot.ts +70 -0
  970. package/src/cache/index.ts +23 -20
  971. package/src/cache/memory-segment-store.ts +243 -37
  972. package/src/cache/profile-registry.ts +46 -31
  973. package/src/cache/read-through-swr.ts +56 -12
  974. package/src/cache/segment-codec.ts +13 -21
  975. package/src/cache/shell-snapshot.ts +417 -0
  976. package/src/cache/tag-invalidation.ts +230 -0
  977. package/src/cache/taint.ts +55 -0
  978. package/src/cache/types.ts +194 -99
  979. package/src/cache/vercel/index.ts +11 -0
  980. package/src/cache/vercel/vercel-cache-store.ts +1132 -0
  981. package/src/client.rsc.tsx +41 -21
  982. package/src/client.tsx +116 -290
  983. package/src/cloudflare/index.ts +11 -0
  984. package/src/cloudflare/tracing.ts +108 -0
  985. package/src/component-utils.ts +19 -0
  986. package/src/components/DefaultDocument.tsx +8 -2
  987. package/src/context-var.ts +84 -2
  988. package/src/debug.ts +2 -2
  989. package/src/decode-loader-results.ts +52 -0
  990. package/src/defer.ts +185 -0
  991. package/src/deps/ssr.ts +0 -1
  992. package/src/encode-kv.ts +49 -0
  993. package/src/errors.ts +30 -4
  994. package/src/escape-script.ts +52 -0
  995. package/src/handle.ts +104 -34
  996. package/src/handles/MetaTags.tsx +24 -53
  997. package/src/handles/Scripts.tsx +183 -0
  998. package/src/handles/breadcrumbs.ts +35 -8
  999. package/src/handles/deferred-resolution.ts +127 -0
  1000. package/src/handles/is-thenable.ts +18 -0
  1001. package/src/handles/meta.ts +14 -40
  1002. package/src/handles/script.ts +244 -0
  1003. package/src/host/cookie-handler.ts +9 -60
  1004. package/src/host/errors.ts +13 -22
  1005. package/src/host/index.ts +9 -2
  1006. package/src/host/pattern-matcher.ts +23 -52
  1007. package/src/host/router.ts +107 -99
  1008. package/src/host/testing.ts +40 -27
  1009. package/src/host/types.ts +37 -4
  1010. package/src/host/utils.ts +1 -1
  1011. package/src/href-client.ts +137 -22
  1012. package/src/index.rsc.ts +100 -13
  1013. package/src/index.ts +143 -19
  1014. package/src/internal-debug.ts +11 -10
  1015. package/src/loader-store.ts +500 -0
  1016. package/src/loader.rsc.ts +20 -13
  1017. package/src/loader.ts +12 -11
  1018. package/src/missing-id-error.ts +68 -0
  1019. package/src/outlet-context.ts +1 -1
  1020. package/src/outlet-provider.tsx +1 -5
  1021. package/src/prerender/param-hash.ts +16 -16
  1022. package/src/prerender/store.ts +37 -41
  1023. package/src/prerender.ts +215 -86
  1024. package/src/redirect-origin.ts +114 -0
  1025. package/src/regex-escape.ts +8 -0
  1026. package/src/render-error-thrower.tsx +20 -0
  1027. package/src/response-utils.ts +62 -0
  1028. package/src/reverse.ts +65 -15
  1029. package/src/root-error-boundary.tsx +1 -19
  1030. package/src/route-content-wrapper.tsx +19 -77
  1031. package/src/route-definition/dsl-helpers.ts +485 -303
  1032. package/src/route-definition/helper-factories.ts +28 -140
  1033. package/src/route-definition/helpers-types.ts +153 -77
  1034. package/src/route-definition/index.ts +4 -2
  1035. package/src/route-definition/redirect.ts +51 -10
  1036. package/src/route-definition/resolve-handler-use.ts +160 -0
  1037. package/src/route-definition/use-item-types.ts +29 -0
  1038. package/src/route-map-builder.ts +41 -20
  1039. package/src/route-types.ts +37 -46
  1040. package/src/router/basename.ts +14 -0
  1041. package/src/router/content-negotiation.ts +164 -17
  1042. package/src/router/error-handling.ts +45 -18
  1043. package/src/router/find-match.ts +129 -30
  1044. package/src/router/handler-context.ts +83 -39
  1045. package/src/router/instrument.ts +355 -0
  1046. package/src/router/intercept-resolution.ts +48 -24
  1047. package/src/router/lazy-includes.ts +87 -63
  1048. package/src/router/loader-resolution.ts +286 -56
  1049. package/src/router/logging.ts +1 -7
  1050. package/src/router/manifest.ts +102 -55
  1051. package/src/router/match-api.ts +178 -218
  1052. package/src/router/match-context.ts +0 -22
  1053. package/src/router/match-handlers.ts +211 -165
  1054. package/src/router/match-middleware/background-revalidation.ts +66 -22
  1055. package/src/router/match-middleware/cache-lookup.ts +214 -263
  1056. package/src/router/match-middleware/cache-store.ts +105 -50
  1057. package/src/router/match-middleware/intercept-resolution.ts +8 -28
  1058. package/src/router/match-middleware/segment-resolution.ts +51 -18
  1059. package/src/router/match-pipelines.ts +1 -42
  1060. package/src/router/match-result.ts +128 -44
  1061. package/src/router/metrics.ts +5 -34
  1062. package/src/router/middleware-types.ts +13 -142
  1063. package/src/router/middleware.ts +300 -177
  1064. package/src/router/navigation-snapshot.ts +133 -0
  1065. package/src/router/params-util.ts +23 -0
  1066. package/src/router/parse-pattern.ts +115 -0
  1067. package/src/router/pattern-matching.ts +181 -150
  1068. package/src/router/prefetch-cache-ttl.ts +51 -0
  1069. package/src/router/prefetch-limits.ts +37 -0
  1070. package/src/router/prerender-match.ts +203 -58
  1071. package/src/router/preview-match.ts +35 -103
  1072. package/src/router/request-classification.ts +291 -0
  1073. package/src/router/revalidation.ts +123 -73
  1074. package/src/router/route-snapshot.ts +256 -0
  1075. package/src/router/router-context.ts +6 -28
  1076. package/src/router/router-interfaces.ts +146 -35
  1077. package/src/router/router-options.ts +202 -15
  1078. package/src/router/router-registry.ts +2 -5
  1079. package/src/router/segment-resolution/fresh.ts +301 -78
  1080. package/src/router/segment-resolution/helpers.ts +115 -30
  1081. package/src/router/segment-resolution/loader-cache.ts +156 -39
  1082. package/src/router/segment-resolution/loader-mask.ts +60 -0
  1083. package/src/router/segment-resolution/loader-snapshot.ts +259 -0
  1084. package/src/router/segment-resolution/mask-nested.ts +83 -0
  1085. package/src/router/segment-resolution/revalidation.ts +473 -384
  1086. package/src/router/segment-resolution/static-store.ts +19 -5
  1087. package/src/router/segment-resolution/streamed-handler-telemetry.ts +52 -0
  1088. package/src/router/segment-resolution/view-transition-default.ts +56 -0
  1089. package/src/router/segment-resolution.ts +5 -1
  1090. package/src/router/segment-wrappers.ts +6 -5
  1091. package/src/router/state-cookie-name.ts +33 -0
  1092. package/src/router/substitute-pattern-params.ts +75 -0
  1093. package/src/router/telemetry-otel.ts +160 -200
  1094. package/src/router/telemetry.ts +105 -20
  1095. package/src/router/timeout.ts +0 -20
  1096. package/src/router/tracing.ts +215 -0
  1097. package/src/router/trie-matching.ts +171 -59
  1098. package/src/router/types.ts +10 -63
  1099. package/src/router/url-params.ts +57 -0
  1100. package/src/router.ts +206 -71
  1101. package/src/rsc/full-payload.ts +70 -0
  1102. package/src/rsc/handler-context.ts +3 -2
  1103. package/src/rsc/handler.ts +682 -508
  1104. package/src/rsc/helpers.ts +168 -46
  1105. package/src/rsc/index.ts +2 -5
  1106. package/src/rsc/json-route-result.ts +38 -0
  1107. package/src/rsc/loader-fetch.ts +127 -31
  1108. package/src/rsc/manifest-init.ts +33 -42
  1109. package/src/rsc/nonce.ts +10 -1
  1110. package/src/rsc/origin-guard.ts +39 -25
  1111. package/src/rsc/progressive-enhancement.ts +138 -15
  1112. package/src/rsc/redirect-guard.ts +100 -0
  1113. package/src/rsc/response-cache-serve.ts +238 -0
  1114. package/src/rsc/response-error.ts +79 -12
  1115. package/src/rsc/response-route-handler.ts +99 -189
  1116. package/src/rsc/rsc-rendering.ts +498 -73
  1117. package/src/rsc/runtime-warnings.ts +23 -10
  1118. package/src/rsc/server-action.ts +287 -113
  1119. package/src/rsc/shell-capture.ts +1190 -0
  1120. package/src/rsc/shell-serve.ts +181 -0
  1121. package/src/rsc/ssr-setup.ts +18 -2
  1122. package/src/rsc/transition-gate.ts +89 -0
  1123. package/src/rsc/types.ts +62 -6
  1124. package/src/runtime-env.ts +18 -0
  1125. package/src/search-params.ts +35 -30
  1126. package/src/segment-content-promise.ts +67 -0
  1127. package/src/segment-loader-promise.ts +167 -0
  1128. package/src/segment-system.tsx +418 -131
  1129. package/src/serialize.ts +243 -0
  1130. package/src/server/context.ts +367 -61
  1131. package/src/server/cookie-parse.ts +32 -0
  1132. package/src/server/cookie-store.ts +152 -5
  1133. package/src/server/handle-store.ts +40 -38
  1134. package/src/server/loader-registry.ts +38 -46
  1135. package/src/server/request-context.ts +550 -171
  1136. package/src/ssr/index.tsx +467 -174
  1137. package/src/ssr/inject-rsc-eager.ts +167 -0
  1138. package/src/ssr/ssr-root.tsx +228 -0
  1139. package/src/static-handler.ts +27 -18
  1140. package/src/testing/cache-status.ts +162 -0
  1141. package/src/testing/collect-handle.ts +46 -0
  1142. package/src/testing/dispatch.ts +813 -0
  1143. package/src/testing/dom.entry.ts +22 -0
  1144. package/src/testing/e2e/fixture.ts +188 -0
  1145. package/src/testing/e2e/index.ts +128 -0
  1146. package/src/testing/e2e/matchers.ts +35 -0
  1147. package/src/testing/e2e/page-helpers.ts +272 -0
  1148. package/src/testing/e2e/parity.ts +387 -0
  1149. package/src/testing/e2e/server.ts +195 -0
  1150. package/src/testing/flight-matchers.ts +97 -0
  1151. package/src/testing/flight-normalize.ts +11 -0
  1152. package/src/testing/flight-runtime.d.ts +57 -0
  1153. package/src/testing/flight-tree.ts +682 -0
  1154. package/src/testing/flight.entry.ts +52 -0
  1155. package/src/testing/flight.ts +257 -0
  1156. package/src/testing/generated-routes.ts +199 -0
  1157. package/src/testing/index.ts +105 -0
  1158. package/src/testing/internal/context.ts +371 -0
  1159. package/src/testing/internal/flight-client-globals.ts +30 -0
  1160. package/src/testing/internal/seed-vars.ts +54 -0
  1161. package/src/testing/render-handler.ts +357 -0
  1162. package/src/testing/render-route.tsx +584 -0
  1163. package/src/testing/run-loader.ts +385 -0
  1164. package/src/testing/run-middleware.ts +205 -0
  1165. package/src/testing/run-transition-when.ts +164 -0
  1166. package/src/testing/vitest-stubs/cloudflare-email.ts +9 -0
  1167. package/src/testing/vitest-stubs/cloudflare-workers.ts +21 -0
  1168. package/src/testing/vitest-stubs/plugin-rsc.ts +16 -0
  1169. package/src/testing/vitest-stubs/version.ts +5 -0
  1170. package/src/testing/vitest.ts +305 -0
  1171. package/src/theme/ThemeProvider.tsx +56 -84
  1172. package/src/theme/ThemeScript.tsx +7 -9
  1173. package/src/theme/constants.ts +52 -13
  1174. package/src/theme/index.ts +0 -7
  1175. package/src/theme/theme-context.ts +1 -5
  1176. package/src/theme/theme-script.ts +22 -21
  1177. package/src/theme/use-theme.ts +0 -3
  1178. package/src/types/boundaries.ts +0 -35
  1179. package/src/types/cache-types.ts +17 -8
  1180. package/src/types/error-types.ts +30 -90
  1181. package/src/types/global-namespace.ts +54 -41
  1182. package/src/types/handler-context.ts +234 -82
  1183. package/src/types/index.ts +3 -10
  1184. package/src/types/loader-types.ts +44 -15
  1185. package/src/types/request-scope.ts +112 -0
  1186. package/src/types/route-config.ts +20 -52
  1187. package/src/types/route-entry.ts +12 -7
  1188. package/src/types/segments.ts +137 -14
  1189. package/src/urls/include-helper.ts +40 -75
  1190. package/src/urls/include-provider.ts +71 -0
  1191. package/src/urls/index.ts +2 -11
  1192. package/src/urls/path-helper-types.ts +102 -23
  1193. package/src/urls/path-helper.ts +62 -111
  1194. package/src/urls/pattern-types.ts +84 -19
  1195. package/src/urls/response-types.ts +25 -22
  1196. package/src/urls/type-extraction.ts +98 -154
  1197. package/src/urls/urls-function.ts +1 -19
  1198. package/src/use-loader.tsx +346 -89
  1199. package/src/vercel/index.ts +11 -0
  1200. package/src/vercel/tracing.ts +88 -0
  1201. package/src/vite/debug.ts +185 -0
  1202. package/src/vite/discovery/bundle-postprocess.ts +36 -38
  1203. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  1204. package/src/vite/discovery/discover-routers.ts +130 -85
  1205. package/src/vite/discovery/discovery-errors.ts +255 -0
  1206. package/src/vite/discovery/gate-state.ts +171 -0
  1207. package/src/vite/discovery/prerender-collection.ts +214 -132
  1208. package/src/vite/discovery/route-types-writer.ts +40 -84
  1209. package/src/vite/discovery/self-gen-tracking.ts +27 -1
  1210. package/src/vite/discovery/state.ts +57 -4
  1211. package/src/vite/discovery/virtual-module-codegen.ts +14 -34
  1212. package/src/vite/index.ts +15 -0
  1213. package/src/vite/inject-client-debug.ts +88 -0
  1214. package/src/vite/plugin-types.ts +261 -6
  1215. package/src/vite/plugins/cjs-to-esm.ts +16 -19
  1216. package/src/vite/plugins/client-ref-dedup.ts +16 -11
  1217. package/src/vite/plugins/client-ref-hashing.ts +28 -15
  1218. package/src/vite/plugins/cloudflare-protocol-loader-hook.d.mts +23 -0
  1219. package/src/vite/plugins/cloudflare-protocol-loader-hook.mjs +76 -0
  1220. package/src/vite/plugins/cloudflare-protocol-stub.ts +194 -0
  1221. package/src/vite/plugins/expose-action-id.ts +48 -95
  1222. package/src/vite/plugins/expose-id-utils.ts +96 -51
  1223. package/src/vite/plugins/expose-ids/export-analysis.ts +101 -34
  1224. package/src/vite/plugins/expose-ids/handler-transform.ts +15 -64
  1225. package/src/vite/plugins/expose-ids/loader-transform.ts +14 -24
  1226. package/src/vite/plugins/expose-ids/router-transform.ts +118 -29
  1227. package/src/vite/plugins/expose-internal-ids.ts +553 -317
  1228. package/src/vite/plugins/performance-tracks.ts +89 -0
  1229. package/src/vite/plugins/refresh-cmd.ts +89 -27
  1230. package/src/vite/plugins/use-cache-transform.ts +73 -83
  1231. package/src/vite/plugins/vercel-output.ts +384 -0
  1232. package/src/vite/plugins/version-injector.ts +40 -29
  1233. package/src/vite/plugins/version-plugin.ts +37 -40
  1234. package/src/vite/plugins/virtual-entries.ts +138 -27
  1235. package/src/vite/rango.ts +247 -132
  1236. package/src/vite/router-discovery.ts +1091 -159
  1237. package/src/vite/utils/ast-handler-extract.ts +26 -35
  1238. package/src/vite/utils/banner.ts +4 -4
  1239. package/src/vite/utils/bundle-analysis.ts +10 -15
  1240. package/src/vite/utils/client-chunks.ts +184 -0
  1241. package/src/vite/utils/directive-prologue.ts +40 -0
  1242. package/src/vite/utils/forward-user-plugins.ts +171 -0
  1243. package/src/vite/utils/manifest-utils.ts +4 -59
  1244. package/src/vite/utils/package-resolution.ts +20 -52
  1245. package/src/vite/utils/prerender-utils.ts +98 -38
  1246. package/src/vite/utils/shared-utils.ts +144 -44
  1247. package/src/browser/action-response-classifier.ts +0 -99
  1248. package/src/browser/shallow.ts +0 -40
  1249. package/src/router/middleware-cookies.ts +0 -55
@@ -0,0 +1,1190 @@
1
+ /**
2
+ * PPR shell capture orchestration (Axis 2, see docs/design/ppr-shell-resume.md).
3
+ *
4
+ * Capture does NOT flow through the HTTP middleware pipeline. The integrated PPR
5
+ * serve path (rsc-rendering.ts + shell-serve.ts) builds a ShellCaptureDescriptor
6
+ * from the route's `ppr` path option after the served response is built and calls
7
+ * scheduleShellCapture. The capture then runs as a background task that re-derives
8
+ * the page via `ctx.router.match()` under its OWN derived request context — fresh
9
+ * handle store, `_shellCaptureRun: true` so loaders mask (loader-mask.ts) and every
10
+ * loading() subtree postpones. The render is MIXED-CHAIN: cache()'d segments replay
11
+ * from ring 3, uncached segments execute their handlers fresh. It drives the static
12
+ * prerender to a quiescent shell, aborts to freeze the prelude + postponed state,
13
+ * and stores the pair via putShell. Because it uses match() rather than the HTTP
14
+ * pipeline, the middleware chain (auth, logging) never re-runs — it already ran for
15
+ * the triggering request, and the derived context inherits its post-middleware
16
+ * state (variables, cache store). Guarding is serve-time.
17
+ */
18
+
19
+ import React from "react";
20
+ import { bufferToBase64 } from "../cache/cf/cf-base64.js";
21
+ import { reportCacheError } from "../cache/cache-error.js";
22
+ import { runBackground } from "../cache/background-task.js";
23
+ import { observePhase, PHASES } from "../router/instrument.js";
24
+ import {
25
+ runWithRequestContext,
26
+ setRequestContextParams,
27
+ UNTRACKED_BACKGROUND_TASK,
28
+ type RequestContext,
29
+ } from "../server/request-context.js";
30
+ import { createHandleStore, type HandleStore } from "../server/handle-store.js";
31
+ import { maskNestedContainerThenables } from "../router/segment-resolution/mask-nested.js";
32
+ import { isThenable } from "../handles/is-thenable.js";
33
+ import type {
34
+ ShellCacheEntry,
35
+ SegmentCacheStore,
36
+ ShellSnapshotRecord,
37
+ } from "../cache/types.js";
38
+ import {
39
+ elideLoaderContainer,
40
+ isLoaderHoleMarker,
41
+ } from "../router/segment-resolution/loader-snapshot.js";
42
+ import {
43
+ RecordingShellStore,
44
+ getRecordingStore,
45
+ } from "../cache/shell-snapshot.js";
46
+ import type { HandlerContext } from "./handler-context.js";
47
+ import type { RscPayload, SSRModule } from "./types.js";
48
+ import { buildFullPayload } from "./full-payload.js";
49
+ import { resolveDeferredHandleValues } from "../handles/deferred-resolution.js";
50
+
51
+ /**
52
+ * Task-quantized quiesce: the number of consecutive macrotask hops with zero new
53
+ * Flight bytes that marks the shell "quiet". This replaces the old 50ms
54
+ * wall-clock debounce.
55
+ *
56
+ * The capture Flight render is a REGULAR renderToReadableStream (not a static
57
+ * prerender), so React schedules both its retries and its byte-flush on
58
+ * setTimeout(0) MACROTASKS (verified against the vendored edge production
59
+ * react-server-dom build: pingTask uses scheduleMicrotask only when
60
+ * request.type === PRERENDER, otherwise setTimeout; enqueueFlush is always
61
+ * setTimeout). Masked loaders are the live lane — their rows never emit — so once
62
+ * the shell rows finish flushing the stream goes permanently byte-silent, and K
63
+ * consecutive quiet macrotask hops after the last observed byte declare quiesce.
64
+ *
65
+ * K=2 gives a race window of ~two event-loop turns: shell work still producing
66
+ * bytes keeps resetting the counter; anything not producing bytes within the
67
+ * window (the masked loaders, and any genuinely pending I/O) becomes a hole. The
68
+ * only residual is raw per-request I/O rendered directly in shell (not via a
69
+ * loader) that resolves inside the window — a documented shell anti-pattern; put
70
+ * per-request data in loaders. See docs/design/ppr-shell-resume.md.
71
+ */
72
+ const FLIGHT_QUIET_HOPS = 2;
73
+
74
+ /** Default upper bound on the capture prerender wait before forcing the abort. */
75
+ const SHELL_CAPTURE_MAX_WAIT_MS = 5000;
76
+
77
+ /**
78
+ * Upper bound on waiting for the capture's DEFERRED cache writes to settle before
79
+ * draining the snapshot. Cache writes run under waitUntil (fire-and-forget on
80
+ * Node, executionContext on workerd), so a MISS-at-capture value's setItem/set —
81
+ * hence its snapshot record — can land after the shell has quiesced. We collect
82
+ * those write promises and await them here so the written value is pinned. Kept
83
+ * short: a pathological slow write must never stall the background capture; a key
84
+ * that does not settle in time is simply left unpinned (it drifts, the
85
+ * pre-snapshot behavior) rather than hanging. Reads that HIT are recorded
86
+ * synchronously during the render and do not depend on this.
87
+ */
88
+ const SHELL_SNAPSHOT_WRITE_SETTLE_MS = 1000;
89
+
90
+ /**
91
+ * Upper bound on the pre-render WRITE BARRIER: before the capture's match/render,
92
+ * settle the background tasks the FOREGROUND request already scheduled — its
93
+ * deferred ring-3 cacheRoute and ring-1 setItem writes all go through
94
+ * reqCtx.waitUntil, and every one of them is scheduled BEFORE scheduleShellCapture
95
+ * runs (the response, and its onResponse callbacks, are committed first). Draining
96
+ * them turns the capture's cache reads from a RACE into an ORDERING EDGE: the
97
+ * capture deterministically observes the foreground's cache generation, replays it
98
+ * (handler skipped, module-level side effects untouched), and records THAT
99
+ * generation into the snapshot — so prelude, snapshot, and ring-3 all agree on the
100
+ * foreground's generation and the capture can never clobber a foreground-produced
101
+ * entry with a re-render of its own. Scar tissue: without this, the capture's
102
+ * ring-3 lookup could land between the foreground write chain's serialization and
103
+ * its store.set, MISS, re-execute the route handler (bumping module-level
104
+ * counters), and — via the synthetic onResponse fire below — overwrite the
105
+ * foreground's entry (the mini shell-manifest regression). Bounded: a slow
106
+ * consumer waitUntil task must never stall the background capture; on timeout the
107
+ * capture proceeds with the pre-barrier (racy) behavior.
108
+ */
109
+ const SHELL_CAPTURE_WRITE_BARRIER_MS = 1500;
110
+
111
+ /**
112
+ * Settle the tracked background tasks on `reqCtx._pendingBackgroundTasks`,
113
+ * ITERATIVELY: a settled task can have scheduled a nested one (cache-store's
114
+ * cacheRoute outer task schedules the actual store.set in a second waitUntil), so
115
+ * each awaited batch may append more. Loop until no new tasks appear or the
116
+ * deadline passes. The capture's own task never enters the list
117
+ * (UNTRACKED_BACKGROUND_TASK), so the loop terminates.
118
+ */
119
+ async function settleTrackedBackgroundTasks(
120
+ reqCtx: RequestContext<any>,
121
+ timeoutMs: number,
122
+ ): Promise<void> {
123
+ const tasks = reqCtx._pendingBackgroundTasks;
124
+ if (!tasks) return;
125
+ const deadline = Date.now() + timeoutMs;
126
+ let seen = 0;
127
+ while (tasks.length > seen) {
128
+ const remaining = deadline - Date.now();
129
+ if (remaining <= 0) return;
130
+ const batch = tasks.slice(seen);
131
+ seen = tasks.length;
132
+ let timer: ReturnType<typeof setTimeout> | undefined;
133
+ const guard = new Promise<void>((resolve) => {
134
+ timer = setTimeout(resolve, remaining);
135
+ (timer as { unref?: () => void }).unref?.();
136
+ });
137
+ await Promise.race([Promise.allSettled(batch).then(() => {}), guard]);
138
+ if (timer) clearTimeout(timer);
139
+ }
140
+ }
141
+
142
+ /**
143
+ * Delay before the in-place retry of a capture that produced no usable shell.
144
+ *
145
+ * The dominant reason a first capture comes back with a trivial prelude is a
146
+ * COLD render: in dev the module transform graph (route modules, the SSR/Flight
147
+ * transforms) is being built lazily and outlasts the task-quantized quiesce, so
148
+ * the shell has not finished rendering when we freeze it; on a cold worker the
149
+ * first invocation pays the same one-time cost. The first attempt WARMS that
150
+ * graph, so a second attempt a short beat later usually completes the shell in
151
+ * the SAME background task — no extra HTTP request needed. Short enough to feel
152
+ * instant, long enough for the module graph to settle. See
153
+ * docs/design/ppr-shell-resume.md ("Capture retry-in-place").
154
+ */
155
+ const SHELL_CAPTURE_RETRY_DELAY_MS = 400;
156
+
157
+ /** Sleep `ms`, unref'd so a Node dev process is never kept alive by the timer. */
158
+ function delay(ms: number): Promise<void> {
159
+ return new Promise((resolve) => {
160
+ const t = setTimeout(resolve, ms);
161
+ (t as { unref?: () => void }).unref?.();
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Module-level in-flight key set: the stampede guard for background captures, and
167
+ * its single owner. One capture runs per key per isolate; concurrent MISS/stale
168
+ * requests for the same key coalesce onto the first (the rest see the key present
169
+ * in scheduleShellCapture and skip). Added when a capture is scheduled and cleared
170
+ * in the task's finally once it settles, so a later request can recapture when TTL
171
+ * rolls. Living here (not split across the middleware) keeps the add/clear
172
+ * lifecycle in one layer.
173
+ */
174
+ const inFlightCaptures = new Set<string>();
175
+
176
+ /**
177
+ * Refused-capture backoff bounds. The window is EXPONENTIAL in the consecutive
178
+ * failure count: `min(BASE * 2^(failures-1), ceiling)` — 1s, 2s, 4s, … up to the
179
+ * mode's ceiling (60s in production, {@link REFUSED_CAPTURE_DEV_MAX_MS} in dev).
180
+ *
181
+ * Why exponential and not a flat 60s: a flat long window conflates two very
182
+ * different failures. A STRUCTURALLY ineligible route (no loading(), a cookie
183
+ * reader) fails forever and wants the long 60s cap. But a cold-but-ELIGIBLE route
184
+ * can also fail the in-place retry under a truly cold graph (dev module transform,
185
+ * or a cold worker under parallel load) — and it must recover FAST, on the next
186
+ * request or two, not be frozen for 60s (that would re-break the very cold-start DX
187
+ * the retry fixes; it bit the cloudflare dev e2e). Escalating from 1s means the
188
+ * eligible route re-probes almost immediately (warm now → HIT and clear), while the
189
+ * doomed route ramps to the ceiling within a handful of failures. Either way an
190
+ * app-wide mount never re-renders a doomed route on EVERY request.
191
+ */
192
+ const REFUSED_CAPTURE_BASE_MS = 1_000;
193
+ const REFUSED_CAPTURE_MAX_MS = 60_000;
194
+
195
+ /**
196
+ * DEV-only backoff ceiling. In dev the 60s production cap is pure harm: the
197
+ * dominant no-shell cause is a COLD module graph (route modules, SSR/Flight
198
+ * transforms built lazily), and the very attempt that failed WARMS that graph, so
199
+ * the next attempt a beat later usually completes the shell. Capping the dev window
200
+ * low keeps a cold-but-eligible route re-probing every ~2s instead of freezing for
201
+ * up to 60s once the exponential climbs (1s→2s→4s→…→60s). A 60s freeze outlasts the
202
+ * e2e warm windows on cold CI runners: the capture races an unfinished shell,
203
+ * escalates the backoff past the poll window, and every subsequent request inside
204
+ * that window is skipped as backed-off — an eternal MISS for the test even though
205
+ * the modules are warm by then. Production keeps the full 60s cap: there the
206
+ * no-shell cause is far more likely to be a genuinely ineligible route (no
207
+ * loading()), which SHOULD be re-probed rarely. See #652 (item 3) and
208
+ * docs/design/ppr-shell-resume.md ("Refused-capture backoff").
209
+ */
210
+ const REFUSED_CAPTURE_DEV_MAX_MS = 2_000;
211
+
212
+ /**
213
+ * Dev signal, matching the rest of the RSC runtime (handler.ts, server-action.ts,
214
+ * progressive-enhancement.ts): treat anything but an explicit production build as
215
+ * dev. The build folds `process.env.NODE_ENV` to a literal, so this is a compile-
216
+ * time constant in the shipped worker — no runtime probe.
217
+ */
218
+ function isDevMode(): boolean {
219
+ return process.env.NODE_ENV !== "production";
220
+ }
221
+
222
+ /** The active backoff ceiling for the current mode (dev capped low, prod at 60s). */
223
+ function refusedCaptureCeilingMs(): number {
224
+ return isDevMode() ? REFUSED_CAPTURE_DEV_MAX_MS : REFUSED_CAPTURE_MAX_MS;
225
+ }
226
+
227
+ /**
228
+ * Refused-capture backoff: key -> { consecutive failure count, epoch ms until which
229
+ * the key is not re-probed }. A key enters backoff only after runShellCapture's
230
+ * in-place retry ALSO failed (or a genuine error). A successful capture clears the
231
+ * entry outright (failure count resets). Module-level (same lifetime as
232
+ * inFlightCaptures) so the whole lifecycle lives in one layer.
233
+ */
234
+ const refusedCaptures = new Map<string, { failures: number; until: number }>();
235
+
236
+ /** True iff `key` is still inside its (exponential) backoff window. */
237
+ function isCaptureBackedOff(key: string): boolean {
238
+ const entry = refusedCaptures.get(key);
239
+ if (entry === undefined) return false;
240
+ // Window elapsed: allow a re-probe. Keep the entry (its failure count drives the
241
+ // NEXT window's escalation if the re-probe also fails); a success clears it.
242
+ return Date.now() < entry.until;
243
+ }
244
+
245
+ /**
246
+ * Record a refused/failed capture, escalating the backoff window exponentially up
247
+ * to the current mode's ceiling. The failure count keeps climbing across attempts
248
+ * (so a genuinely doomed route still ramps toward its cap), but the WINDOW is
249
+ * clamped: 60s in production, {@link REFUSED_CAPTURE_DEV_MAX_MS} in dev so a
250
+ * cold-but-eligible route re-probes fast instead of freezing out the e2e warm
251
+ * window on a cold CI runner (#652 item 3).
252
+ */
253
+ function markCaptureBackoff(key: string): void {
254
+ const failures = (refusedCaptures.get(key)?.failures ?? 0) + 1;
255
+ const window = Math.min(
256
+ REFUSED_CAPTURE_BASE_MS * 2 ** (failures - 1),
257
+ refusedCaptureCeilingMs(),
258
+ );
259
+ refusedCaptures.set(key, { failures, until: Date.now() + window });
260
+ }
261
+
262
+ /** Clear any backoff for a key that just captured successfully. */
263
+ function clearCaptureBackoff(key: string): void {
264
+ refusedCaptures.delete(key);
265
+ }
266
+
267
+ /**
268
+ * Keys already warned about a refused (null) capture, so the eternal-MISS shape
269
+ * logs once per key per isolate instead of on every request.
270
+ */
271
+ const warnedNullCaptures = new Set<string>();
272
+
273
+ /**
274
+ * Warn once per key that a capture produced no usable shell EVEN AFTER the
275
+ * in-place retry (runShellCapture attempt 2). Naming both causes with the
276
+ * distinguishing signal — does the route ever flip to HIT — is the whole point:
277
+ * the pre-retry version blamed "a loader route without loading()" unconditionally
278
+ * and misled users whose route DID have loading() and was merely cold. Because the
279
+ * retry already absorbs the cold-start case, by the time this fires cold-start has
280
+ * usually healed, so a firing warning leans toward the structural cause — but we
281
+ * still name both so a cold-start straggler is not misdiagnosed.
282
+ *
283
+ * The pointer is shipped-path-safe (a05c8251 convention): the /ppr skill ships in
284
+ * the npm tarball, but docs/design/ is repo-only, so link it by absolute GitHub URL
285
+ * rather than a relative path that dead-ends for consumers.
286
+ */
287
+ function warnNullCaptureOnce(key: string): void {
288
+ if (warnedNullCaptures.has(key)) return;
289
+ warnedNullCaptures.add(key);
290
+ console.warn(
291
+ `[rango] Shell capture for "${key}" produced no usable shell after an in-place ` +
292
+ "retry; nothing was stored, so this request stays on MISS. Causes, told apart " +
293
+ "by whether the route ever flips to HIT:\n" +
294
+ " 1. Cold-start warmup (dev module transform, or a cold worker): the capture raced " +
295
+ "an unfinished shell render. This SELF-HEALS — the route flips to HIT once a later " +
296
+ "request warms the modules. Usually nothing to do.\n" +
297
+ " 2. Something suspends above <body> with no Suspense boundary and never settles " +
298
+ "within the capture window: a slower-than-the-capture-guard bake-lane loader " +
299
+ "(loaders on entries WITHOUT loading() execute at capture and their containers " +
300
+ "bake — the boundary-less await must settle for a shell to exist), or a pending " +
301
+ "promise consumed without a <Suspense> above it. The boundary belongs on the " +
302
+ "entry/component that OWNS the data: loading() on the entry that registers the " +
303
+ "loader (a child route's loading() does not unpin a parent layout's loaders), or " +
304
+ "a <Suspense> above the consuming component.\n" +
305
+ 'See the /ppr skill (node_modules/@rangojs/router/skills/ppr/SKILL.md), "The hole ' +
306
+ 'doctrine" and "The layout-with-loaders playbook", or the design docs: ' +
307
+ "https://github.com/ivogt/vite-rsc/blob/main/packages/rangojs-router/docs/design/ppr-shell-resume.md",
308
+ );
309
+ }
310
+
311
+ /** Keys already warned about a deterministic capture refusal (once per key). */
312
+ const warnedRefusedCaptures = new Set<string>();
313
+
314
+ /**
315
+ * Warn once per key that the capture was REFUSED for a deterministic reason
316
+ * (identity-guard trip or a rejected bake-lane loader). Distinct from
317
+ * warnNullCaptureOnce: these are not cold-start shapes, the retry is skipped,
318
+ * and the message carries the concrete cause instead of a differential.
319
+ */
320
+ function warnCaptureRefusedOnce(key: string, reason: string): void {
321
+ if (warnedRefusedCaptures.has(key)) return;
322
+ warnedRefusedCaptures.add(key);
323
+ console.warn(
324
+ `[rango] Shell capture for "${key}" was refused: ${reason}\n` +
325
+ "The route stays on MISS (axis 1) — the page keeps working, only the shell " +
326
+ "cache is off. See the /ppr skill " +
327
+ "(node_modules/@rangojs/router/skills/ppr/SKILL.md) and " +
328
+ "docs/design/loader-container-bake.md.",
329
+ );
330
+ }
331
+
332
+ /** Keys already warned about untagged bake-lane data baked into the shell. */
333
+ const warnedUntaggedShellBakes = new Set<string>();
334
+
335
+ /**
336
+ * Warn once per shell key that a bake-lane loader baked material into the shell
337
+ * but the capture recorded ZERO tags (no render-collected _requestTags, no
338
+ * static ppr.tags). Such data is frozen in the shared shell until TTL and is
339
+ * un-evictable by tag: a server action refreshes the CLIENT only (rotates Rango
340
+ * state, busts the browser HTTP cache) and never touches the server shell store,
341
+ * and updateTag()/revalidateTag() cannot drop data that was baked WITHOUT a tag.
342
+ *
343
+ * Coarse per-shell-key signal, not per-loader attribution — the tag set is
344
+ * unioned globally at the write barrier, not tracked per loader, so we cannot
345
+ * name the offending loader without adding attribution plumbing (deliberately
346
+ * not done). Dev-only + once-per-key so it never spams production or fires on
347
+ * every capture.
348
+ */
349
+ function warnUntaggedShellBakeOnce(key: string): void {
350
+ if (warnedUntaggedShellBakes.has(key)) return;
351
+ warnedUntaggedShellBakes.add(key);
352
+ console.warn(
353
+ `[rango] Shell capture for "${key}" baked bake-lane loader data into the ` +
354
+ "shell with NO cache tag. That data is frozen in the shared shell until " +
355
+ "TTL and cannot be tag-invalidated: a server action refresh touches the " +
356
+ "client only (not the server shell store), and updateTag() cannot evict " +
357
+ "data that was baked without a tag.\n" +
358
+ 'Fix: tag the data (cacheTag() / "use cache" / cache({ tags })) so ' +
359
+ "updateTag() drops the shell, or move the volatile read under a loading() " +
360
+ "hole so it stays on the live lane and is never baked. See the /ppr skill " +
361
+ "(node_modules/@rangojs/router/skills/ppr/SKILL.md).",
362
+ );
363
+ }
364
+
365
+ export interface FlightCaptureGate {
366
+ /** Identity passthrough of the source stream; feed this to captureShellHTML. */
367
+ stream: ReadableStream<Uint8Array>;
368
+ /**
369
+ * Resolves once the source has been byte-quiet for FLIGHT_QUIET_HOPS macrotask
370
+ * hops (or has closed — the DATA variant). At that instant the gate FREEZES:
371
+ * no further source byte reaches the fizz side, and the readable is left open
372
+ * (never closed / errored) so fizz postpones the still-pending references
373
+ * instead of seeing "Connection closed".
374
+ */
375
+ quiesce: Promise<void>;
376
+ /**
377
+ * Stop the internal macrotask-hop loop. captureShellHTML's maxWaitMs bounds the
378
+ * overall wait; dispose() is the clean shutdown for the pathological case where
379
+ * the source never goes byte-quiet (quiesce never fires), so the hop loop would
380
+ * otherwise keep rescheduling after captureShellHTML has already aborted and
381
+ * returned.
382
+ */
383
+ dispose(): void;
384
+ }
385
+
386
+ /**
387
+ * Wrap the capture Flight stream so the fizz shell prerender reads a stream that
388
+ * (a) forwards the shell rows unchanged, (b) resolves `quiesce` after the rows go
389
+ * byte-silent for FLIGHT_QUIET_HOPS macrotask hops, and (c) FREEZES at that
390
+ * instant — dropping any later byte without closing or erroring the readable, so
391
+ * the pending masked-loader references stay pending and fizz postpones them (the
392
+ * "unclosing stream" property, here for free because the masked rows never emit).
393
+ * Freezing also guarantees no post-quiesce byte — including an error row from any
394
+ * later abort/cancel of the underlying render — can corrupt the frozen prelude.
395
+ *
396
+ * Quiet is measured in TASKS, not wall-clock: after the first byte a macrotask
397
+ * hop loop compares a byte counter each turn and fires after K quiet turns. The
398
+ * hop timers are unref'd so they never keep a Node process alive, and the source
399
+ * closing (no holes) fires quiesce immediately for the DATA variant — the
400
+ * TransformStream then closes the readable, so fizz completes with postponed null.
401
+ *
402
+ * `holdUntil` keeps the gate from FREEZING before shell material with real latency
403
+ * has emitted. The hole doctrine bakes TOP-LEVEL pushed handle promises into the
404
+ * shell (resolvedHandleStream awaits them before the handles row emits), but a
405
+ * pushed promise that takes longer than the quiet window would otherwise be frozen
406
+ * out — the handles row would never reach fizz and the prelude would come back
407
+ * trivial. While `holdUntil` is pending, byte-quiet detection keeps running but the
408
+ * gate neither fires nor freezes; once it resolves, the quiet counter restarts so a
409
+ * burst of rows unblocked by it (the resolved handles row) is still captured. It
410
+ * never delays a HOLE from postponing: holes are pending promises that emit no
411
+ * bytes, so holding the gate open longer only ever admits shell rows. Bounded by
412
+ * captureShellHTML's maxWaitMs like every other quiesce input.
413
+ */
414
+ export function gateFlightForCapture(
415
+ source: ReadableStream<Uint8Array>,
416
+ quietHops: number = FLIGHT_QUIET_HOPS,
417
+ holdUntil?: Promise<unknown>,
418
+ ): FlightCaptureGate {
419
+ let resolveQuiet!: () => void;
420
+ const quiesce = new Promise<void>((resolve) => {
421
+ resolveQuiet = resolve;
422
+ });
423
+
424
+ let bytesSeen = 0;
425
+ let armed = false;
426
+ let settled = false;
427
+ let disposed = false;
428
+ let frozen = false;
429
+ let held = holdUntil !== undefined;
430
+ let heldFirePending = false;
431
+
432
+ if (holdUntil !== undefined) {
433
+ const release = (): void => {
434
+ held = false;
435
+ if (heldFirePending && !settled && !disposed) {
436
+ // Quiet elapsed while held: restart the quiet count instead of firing
437
+ // immediately, so rows unblocked by the hold (the baked handles row)
438
+ // still flow before the freeze.
439
+ heldFirePending = false;
440
+ armed = false;
441
+ arm();
442
+ }
443
+ };
444
+ // Resolve OR reject releases the hold (a rejected handle value is dropped by
445
+ // resolveDeferredHandleValues; the capture must not hang on it).
446
+ holdUntil.then(release, release);
447
+ }
448
+
449
+ const fire = (): void => {
450
+ if (settled) return;
451
+ if (held) {
452
+ heldFirePending = true;
453
+ return;
454
+ }
455
+ settled = true;
456
+ frozen = true;
457
+ resolveQuiet();
458
+ };
459
+
460
+ const scheduleHop = (fn: () => void): void => {
461
+ const t = setTimeout(fn, 0);
462
+ // Never let the quiet-detection hop alone keep a Node process alive
463
+ // (no-op on workerd).
464
+ (t as { unref?: () => void }).unref?.();
465
+ };
466
+
467
+ // The hop loop starts only after the first byte, so it can never declare
468
+ // quiesce before fizz has begun pulling rows through the transform.
469
+ const arm = (): void => {
470
+ if (armed || settled || disposed) return;
471
+ armed = true;
472
+ let lastSeen = bytesSeen;
473
+ let quiet = 0;
474
+ const hop = (): void => {
475
+ if (settled || disposed) return;
476
+ if (bytesSeen === lastSeen) {
477
+ quiet += 1;
478
+ if (quiet >= quietHops) {
479
+ fire();
480
+ return;
481
+ }
482
+ } else {
483
+ lastSeen = bytesSeen;
484
+ quiet = 0;
485
+ }
486
+ scheduleHop(hop);
487
+ };
488
+ scheduleHop(hop);
489
+ };
490
+
491
+ const monitor = new TransformStream<Uint8Array, Uint8Array>({
492
+ transform(chunk, controller) {
493
+ // Post-quiesce: drop the byte. Do NOT enqueue and do NOT close/error — the
494
+ // frozen fizz input must stay a fixed byte set behind an open (unclosing)
495
+ // readable so still-pending references postpone.
496
+ if (frozen) return;
497
+ bytesSeen += chunk.length;
498
+ arm();
499
+ controller.enqueue(chunk);
500
+ },
501
+ flush() {
502
+ // Source closed with no freeze => DATA variant (no holes): quiet
503
+ // immediately. The TransformStream then closes the readable, so fizz
504
+ // completes and postponed comes back null.
505
+ fire();
506
+ },
507
+ });
508
+
509
+ return {
510
+ stream: source.pipeThrough(monitor),
511
+ quiesce,
512
+ dispose(): void {
513
+ disposed = true;
514
+ },
515
+ };
516
+ }
517
+
518
+ /**
519
+ * The background shell-capture descriptor: everything the capture task needs to
520
+ * store the shell. Built by the integrated PPR serve path (rsc-rendering.ts) from
521
+ * the route's `ppr` path option (`PartialPrerenderProps`) and the app-level cache
522
+ * store, and passed to scheduleShellCapture directly — it is NOT threaded through
523
+ * the request context. `tags` carries the route's OPERATIONAL `ppr.tags`; the
524
+ * capture UNIONS them with the shell's own auto-collected (non-loader) request
525
+ * tags from its derived render (the collected set stays authoritative). That
526
+ * union happens at the putShell WRITE BARRIER in captureAndStoreShell — after the
527
+ * capture quiesces — not at stream construction, so a tag recorded after an await
528
+ * in async shell content is still collected (issue #676). `store` is the same
529
+ * store the serve path resolved for its getShell read (requestCtx._cacheStore),
530
+ * so the capture writes where the serve reads.
531
+ */
532
+ export interface ShellCaptureDescriptor {
533
+ key: string;
534
+ /**
535
+ * The RSC handler's build version (HandlerContext.version), stamped into the
536
+ * stored entry as ShellCacheEntry.buildVersion — the serve-side
537
+ * isValidShellHit gate compares it against the running build so a persistent
538
+ * store can never resume a stale build's postponed blob.
539
+ */
540
+ buildVersion: string;
541
+ ttl?: number;
542
+ swr?: number;
543
+ tags?: string[];
544
+ store?: SegmentCacheStore<any>;
545
+ /** Gates the concise per-attempt capture breadcrumbs (INTERNAL_RANGO_DEBUG). */
546
+ debug?: boolean;
547
+ }
548
+
549
+ /**
550
+ * Schedule the background shell capture for a served document. Stampede-guarded:
551
+ * one capture per key per isolate. Runs via runBackground (waitUntil on workerd,
552
+ * fire-and-forget in Node dev), so the served response is never blocked on it. Any
553
+ * error is routed through reportCacheError — capture is best-effort; a failure just
554
+ * means the next request recaptures.
555
+ *
556
+ * Eligibility (nonce/allReady/partial/status/strategy) is decided by the caller
557
+ * (rsc-rendering.ts maybeScheduleShellCapture); this function only owns the
558
+ * stampede guard and the background dispatch.
559
+ */
560
+ export function scheduleShellCapture(
561
+ ctx: HandlerContext<any>,
562
+ request: Request,
563
+ env: any,
564
+ url: URL,
565
+ reqCtx: RequestContext<any>,
566
+ ssrModule: SSRModule,
567
+ descriptor: ShellCaptureDescriptor,
568
+ ): void {
569
+ const key = descriptor.key;
570
+ if (inFlightCaptures.has(key)) return;
571
+ // Refused/failed within the window → skip the doomed re-render (one probe per
572
+ // key per window per isolate). Expired entries self-evict inside the check.
573
+ if (isCaptureBackedOff(key)) return;
574
+ inFlightCaptures.add(key);
575
+ const captureTask = async () => {
576
+ try {
577
+ const outcome = await runShellCapture(
578
+ ctx,
579
+ request,
580
+ env,
581
+ url,
582
+ reqCtx,
583
+ ssrModule,
584
+ descriptor,
585
+ );
586
+ // Update the negative cache off the terminal outcome. A stored shell clears
587
+ // any prior backoff; a `no-shell` (after the in-place retry) backs the key
588
+ // off so the next requests don't re-probe it. A `redirect` has no shell but
589
+ // is not a doomed render — leave the backoff untouched.
590
+ if (outcome === "stored") clearCaptureBackoff(key);
591
+ else if (outcome === "no-shell") markCaptureBackoff(key);
592
+ } catch (error) {
593
+ // Detached background task — pass reqCtx so onError still fires when the ALS
594
+ // context is gone. A genuine failure recurs, so back it off too (re-probe
595
+ // once per window, not every request) and report it once.
596
+ markCaptureBackoff(key);
597
+ reportCacheError(error, "cache-write", "[ShellCache] capture", reqCtx);
598
+ } finally {
599
+ inFlightCaptures.delete(key);
600
+ }
601
+ };
602
+ // The capture's own task must NOT enter reqCtx._pendingBackgroundTasks: the
603
+ // capture drains that list before rendering (the write-barrier ordering edge),
604
+ // and awaiting its own still-running promise would burn the whole barrier
605
+ // deadline on every capture.
606
+ (captureTask as { [UNTRACKED_BACKGROUND_TASK]?: boolean })[
607
+ UNTRACKED_BACKGROUND_TASK
608
+ ] = true;
609
+ runBackground(reqCtx, captureTask);
610
+ }
611
+
612
+ /**
613
+ * The outcome of one capture attempt.
614
+ * - `stored`: a usable shell was captured (and a putShell was attempted; a store
615
+ * I/O failure is reported separately and does NOT make the attempt retryable —
616
+ * the capture itself worked).
617
+ * - `redirect`: the matched route redirects, so there is no shell to capture.
618
+ * - `no-shell`: the prelude came back trivial (no <body>) OR captureShellHTML
619
+ * rejected with our own abort. This is the only RETRYABLE outcome.
620
+ */
621
+ type CaptureAttemptOutcome = "stored" | "redirect" | "no-shell" | "refused";
622
+
623
+ /**
624
+ * Run the shell capture with a single in-place retry, then store the result.
625
+ *
626
+ * Each attempt re-derives EVERYTHING (fresh context, fresh router.match, fresh
627
+ * Flight render) via {@link attemptCapture} — a capture consumes its handle store,
628
+ * its request-tag set, and its one-shot Flight stream, so none of them are
629
+ * reusable across attempts. A first attempt that comes back `no-shell` is almost
630
+ * always a cold render (dev module transform / cold worker) that had not finished
631
+ * when we froze the shell; the attempt itself warmed the module graph, so a second
632
+ * attempt a short beat later usually completes the shell in the SAME background
633
+ * task. That kills the old multi-request warmup where the caller had to re-issue
634
+ * several HTTP requests before a capture stuck. We retry ONLY on `no-shell` (and a
635
+ * defensively-caught abort); a genuine render error is NOT retried — it propagates
636
+ * to scheduleShellCapture's reportCacheError. See docs/design/ppr-shell-resume.md.
637
+ *
638
+ * `retryDelayMs` is a parameter (defaulting to the module const) so unit tests can
639
+ * drive the retry without a real 400ms wall-clock wait.
640
+ */
641
+ async function runShellCapture(
642
+ ctx: HandlerContext<any>,
643
+ request: Request,
644
+ env: any,
645
+ url: URL,
646
+ reqCtx: RequestContext<any>,
647
+ ssrModule: SSRModule,
648
+ descriptor: ShellCaptureDescriptor,
649
+ retryDelayMs: number = SHELL_CAPTURE_RETRY_DELAY_MS,
650
+ ): Promise<CaptureAttemptOutcome> {
651
+ const log = descriptor.debug
652
+ ? (message: string) => console.log(message)
653
+ : () => {};
654
+
655
+ const first = await attemptCapture(
656
+ ctx,
657
+ request,
658
+ env,
659
+ url,
660
+ reqCtx,
661
+ ssrModule,
662
+ descriptor,
663
+ );
664
+ // "refused" is deterministic (identity guard / rejected bake-lane loader —
665
+ // its own warning already fired): no retry, and the caller backs the key off
666
+ // exactly like a structural no-shell.
667
+ if (first === "refused") return "no-shell";
668
+ // "stored" (success) or "redirect" (no shell exists): nothing to retry.
669
+ if (first !== "no-shell") return first;
670
+
671
+ // Attempt 1 produced no usable shell. Retry ONCE in place — the first attempt
672
+ // warmed the dev transform graph / cold worker, so attempt 2 typically completes
673
+ // the shell without another HTTP request. The concise line is gated on the
674
+ // middleware's debug flag (threaded via the descriptor) so it replaces the old
675
+ // full DOMException dump with one readable breadcrumb.
676
+ log(
677
+ `[ShellCache] capture attempt 1/2 for ${descriptor.key} aborted before shell completed (cold modules?) — retrying`,
678
+ );
679
+ await delay(retryDelayMs);
680
+ const second = await attemptCapture(
681
+ ctx,
682
+ request,
683
+ env,
684
+ url,
685
+ reqCtx,
686
+ ssrModule,
687
+ descriptor,
688
+ );
689
+ if (second === "refused") return "no-shell";
690
+ if (second !== "no-shell") return second;
691
+
692
+ // Both attempts came back with no usable shell. Cold-start would have healed by
693
+ // now, so the eternal-MISS structural shape (a loader route without loading()) is
694
+ // the likely cause — warn once per key. Ordering matters: because the retry
695
+ // absorbs cold-start, cold-start routes almost never reach this warning. The
696
+ // caller (scheduleShellCapture) reads this `no-shell` return to back the key off.
697
+ log(
698
+ `[ShellCache] capture attempt 2/2 for ${descriptor.key} aborted — giving up until next request`,
699
+ );
700
+ warnNullCaptureOnce(descriptor.key);
701
+ return "no-shell";
702
+ }
703
+
704
+ /**
705
+ * One capture attempt in a DERIVED request context.
706
+ *
707
+ * The derived context is `Object.create(reqCtx)` so it inherits the foreground's
708
+ * post-middleware state (variables, cache store, env/request/url, waitUntil) while
709
+ * overriding the render-scoped accumulators as own properties:
710
+ * - _handleStore: a fresh store. The foreground store is already drained to
711
+ * completion (its stream() flipped `completed` on settle) and would throw
712
+ * LateHandlePushError on any re-push. Every downstream reader resolves the
713
+ * store off the ambient context (setupLoaderAccess captures
714
+ * _getRequestContext()._handleStore; trackHandler reads it), so the fresh
715
+ * store on the derived context is what the capture match() writes handles to.
716
+ * - _requestTags: a fresh Set. The capture collects its OWN shell tags here —
717
+ * non-loader tags only, since loaders are masked — which is exactly the tag
718
+ * set a shell entry should be invalidatable by (loader tags belong to holes).
719
+ * - _transitionWhen: a fresh [] so the capture's transition gating is its own.
720
+ * - _shellCaptureRun: true — the switch loaders/cookies/headers guards read.
721
+ * - _metricsStore: undefined so the capture never appends to the foreground's
722
+ * (already-finalized) metrics.
723
+ *
724
+ * The capture is MIXED-CHAIN: its match() behaves like a normal render with
725
+ * respect to the segment cache — cache()'d segments replay from ring 3, UNCACHED
726
+ * segments execute their handlers fresh (which is why the cookies()/headers()
727
+ * capture guard is load-bearing). Middleware is NOT re-run: it already ran for the
728
+ * triggering request, and the derived context inherits its post-middleware state
729
+ * (guarding is serve-time; the shell is never served without the full chain).
730
+ *
731
+ * A FRESH context (and match/render) per attempt is what makes the retry sound:
732
+ * the second attempt is a clean capture, not a resumption of the first.
733
+ */
734
+ async function attemptCapture(
735
+ ctx: HandlerContext<any>,
736
+ request: Request,
737
+ env: any,
738
+ url: URL,
739
+ reqCtx: RequestContext<any>,
740
+ ssrModule: SSRModule,
741
+ descriptor: ShellCaptureDescriptor,
742
+ ): Promise<CaptureAttemptOutcome> {
743
+ // WRITE BARRIER (ordering edge, not a narrower race): settle the foreground's
744
+ // already-scheduled background tasks — its deferred ring-3/ring-1 cache writes —
745
+ // BEFORE this attempt's match/render, so the capture's cache reads observe the
746
+ // foreground's generation deterministically. Contract: a capture must never
747
+ // clobber a ring-3 entry the foreground produced; with the barrier, the
748
+ // capture's ring-3 lookup HITs the foreground's entry and REPLAYS it (handler
749
+ // skipped, cache-store middleware's write path gated off by state.cacheHit), so
750
+ // prelude, snapshot, and ring-3 agree on the foreground's generation. Runs per
751
+ // attempt (the retry re-checks; already-settled promises are free).
752
+ await settleTrackedBackgroundTasks(reqCtx, SHELL_CAPTURE_WRITE_BARRIER_MS);
753
+
754
+ const freshHandleStore = createHandleStore();
755
+ freshHandleStore.onError = reqCtx._handleStore.onError;
756
+ // Shape = liveness for handles, exactly as for bake-lane loader containers
757
+ // (mask-nested.ts): nested thenables in a pushed handle container are
758
+ // per-request by declaration, so the CAPTURE's copy masks them — the
759
+ // consuming boundary postpones as a hole regardless of settle timing,
760
+ // instead of a fast-settling nested value baking into the shared shell. A
761
+ // TOP-LEVEL promise push keeps its documented bake contract (awaited
762
+ // pre-SSR, gate held open for it), but the container it RESOLVES to gets
763
+ // the same nested masking. Wrapping THIS store's push is the single funnel:
764
+ // the store exists only for this capture attempt, so every push wrapper
765
+ // (setupLoaderAccess, createUseFunction, prerender) inherits the policy and
766
+ // the foreground store is untouched.
767
+ const rawCapturePush = freshHandleStore.push.bind(freshHandleStore);
768
+ freshHandleStore.push = (
769
+ handleName: string,
770
+ segmentId: string,
771
+ value: unknown,
772
+ ) => {
773
+ const masked = isThenable(value)
774
+ ? value.then((v: unknown) => maskNestedContainerThenables(v))
775
+ : maskNestedContainerThenables(value);
776
+ rawCapturePush(handleName, segmentId, masked);
777
+ };
778
+
779
+ const derivedCtx: RequestContext = Object.create(reqCtx);
780
+ derivedCtx._handleStore = freshHandleStore;
781
+ derivedCtx._requestTags = new Set<string>();
782
+ derivedCtx._transitionWhen = [];
783
+ derivedCtx._shellCaptureRun = true;
784
+ derivedCtx._metricsStore = undefined;
785
+ // Spans, like perf metrics above, are a FOREGROUND surface: the capture
786
+ // re-render must not emit a second rango.render/loader/ssr set after the
787
+ // foreground rango.request span ended (orphan spans in the trace).
788
+ // _tracing is otherwise inherited through Object.create(reqCtx).
789
+ derivedCtx._tracing = undefined;
790
+ // Bake-lane loader containers (loaders on entries with no renderable
791
+ // loading() execute during capture — docs/design/loader-container-bake.md).
792
+ // resolveLoaderData registers each container promise here; the drain in
793
+ // captureAndStoreShell elides + pins them into the snapshot's loader family.
794
+ derivedCtx._shellCaptureLoaderRecords = new Map();
795
+ // Own onResponse list so the capture's match-middleware callbacks (the ring-3
796
+ // segment cache write registers here) are ISOLATED from the foreground's shared
797
+ // array AND can be fired by captureAndStoreShell. The segment write is gated
798
+ // behind onResponse, which the capture never triggers (it builds no Response) —
799
+ // without firing it, a ring-3 cache() MISS at capture renders fresh into the
800
+ // prelude but is never written, so it is never recorded and drifts on a HIT.
801
+ derivedCtx._onResponseCallbacks = [];
802
+
803
+ // Capture data snapshot: read every cache-store hit/write through a recording
804
+ // wrapper on the DERIVED context's store (own property, so the shared
805
+ // reqCtx._cacheStore is untouched — the snapshot is per-capture). Its records
806
+ // ride inside the ShellCacheEntry so a HIT can reproduce the shell's cached
807
+ // content byte-identically. See cache/shell-snapshot.ts and the design doc.
808
+ //
809
+ // Cache writes are deferred (waitUntil): a MISS-at-capture value's setItem/set
810
+ // — hence its record — would otherwise land after the shell quiesces. Override
811
+ // the derived context's waitUntil to COLLECT those write promises (still
812
+ // forwarding to the parent so the write persists and the worker stays alive),
813
+ // then captureAndStoreShell awaits them before draining. Reads that HIT are
814
+ // recorded synchronously during the render and need none of this.
815
+ if (reqCtx._cacheStore) {
816
+ const recordingStore = new RecordingShellStore(reqCtx._cacheStore);
817
+ derivedCtx._cacheStore = recordingStore;
818
+ derivedCtx.waitUntil = (fn: () => Promise<void>): void => {
819
+ const p = Promise.resolve().then(fn);
820
+ recordingStore.trackWrite(p);
821
+ reqCtx.waitUntil(() => p);
822
+ };
823
+ }
824
+
825
+ return runWithRequestContext(derivedCtx, async () => {
826
+ const match = await ctx.router.match(request, { env });
827
+ // A route that redirects has no shell to capture — bail (no store write, no
828
+ // retry: a redirect is deterministic).
829
+ if (match.redirect) return "redirect";
830
+
831
+ setRequestContextParams(match.params, match.routeName);
832
+
833
+ const payload = buildFullPayload(
834
+ match,
835
+ ctx,
836
+ url,
837
+ derivedCtx,
838
+ freshHandleStore,
839
+ );
840
+ const rscStream = ctx.renderToReadableStream<RscPayload>(payload, {
841
+ onError: (error: unknown) => {
842
+ ctx.callOnError(error, "rendering", { request, url, env });
843
+ },
844
+ });
845
+
846
+ // Pass the descriptor with its STATIC ppr.tags unchanged. The shell's own
847
+ // render-recorded tags are snapshotted at the putShell WRITE BARRIER inside
848
+ // captureAndStoreShell, not here: a tag recorded AFTER an await in async shell
849
+ // content (and tags propagated by async cache()/"use cache" reads) lands after
850
+ // this synchronous construction point, so snapshotting here dropped it — the
851
+ // shell-tag snapshot must sit behind the quiesce gate (issue #676).
852
+ return captureAndStoreShell(
853
+ ssrModule,
854
+ rscStream,
855
+ freshHandleStore,
856
+ derivedCtx,
857
+ descriptor,
858
+ );
859
+ });
860
+ }
861
+
862
+ /**
863
+ * Seal handles, derive the quiesce signal, prerender + abort via the SSR module's
864
+ * captureShellHTML, and store the result. Returns the attempt outcome (the caller
865
+ * owns retry/warn decisions — this function no longer warns). Never throws out of
866
+ * the store write: a failed putShell is routed through reportCacheError so the
867
+ * background task stays best-effort, and the attempt still counts as `stored` (the
868
+ * capture worked; only the store I/O failed). `ssrModule.captureShellHTML` MUST be
869
+ * present (eligibility is checked before scheduling).
870
+ *
871
+ * A `no-shell` result (trivial prelude, or a defensively-caught abort) is the only
872
+ * retryable outcome; a genuine (non-abort) captureShellHTML error propagates so it
873
+ * reaches reportCacheError and is NOT retried.
874
+ */
875
+ async function captureAndStoreShell(
876
+ ssrModule: SSRModule,
877
+ rscStream: ReadableStream<Uint8Array>,
878
+ handleStore: HandleStore,
879
+ reqCtx: RequestContext<any>,
880
+ capture: ShellCaptureDescriptor,
881
+ ): Promise<Exclude<CaptureAttemptOutcome, "redirect">> {
882
+ const captureShellHTML = ssrModule.captureShellHTML!;
883
+
884
+ // Seal the handle store so the payload's handles generator (resolvedHandleStream
885
+ // -> handleStore.stream()) converges and completes even though masked loaders
886
+ // never resolve. handleStore.settled gates ONLY on tracked HANDLER promises
887
+ // (handleStore.track, via trackHandler) — NOT on deferred handle VALUES pushed
888
+ // through ctx.use(Handle).defer(), which are plain pushed promises. So seal()
889
+ // does not reject or hang on outstanding defers: settled resolves once the
890
+ // handlers settle, and each deferred slot resolves on its own createDeferred
891
+ // timeout (defer.ts, default 10s) or when its resolver fires. A defer whose
892
+ // resolver depends on a masked loader can never fire, so it stays pending until
893
+ // that 10s timeout — longer than maxWaitMs (5s). At the abort the handles
894
+ // generator has not yielded, SsrRoot suspends at the root (consumeAsyncGenerator
895
+ // sits above every boundary), the prelude comes back trivial, and
896
+ // captureShellHTML's sanity gate returns null: the designed fail-safe no-op, not
897
+ // an error. This mirrors the __prerender_collect seal+settled regime, which also
898
+ // excludes loaders. See docs/design/ppr-shell-resume.md ("Loaders and handles").
899
+ handleStore.seal();
900
+
901
+ // Handles contract, shell half ("nesting = liveness"): TOP-LEVEL pushed handle
902
+ // promises are BAKED into the shell — resolvedHandleStream awaits them before
903
+ // the payload's handles row emits. A pushed promise with real latency would lose
904
+ // the byte-quiet race (the pending handles row emits no bytes, the gate freezes,
905
+ // the row is dropped, SsrRoot suspends at the root), so the gate is HELD open
906
+ // until the same await completes: handlesBaked mirrors resolvedHandleStream's
907
+ // resolution (getData waits the tracked-handler barrier; resolveDeferredHandleValues
908
+ // awaits the top-level thenables). NESTED promises inside pushed containers are
909
+ // shallow-skipped by isThenable and never hold the gate — they stay holes.
910
+ // Bounded by maxWaitMs like every quiesce input (a defer hanging on a masked
911
+ // loader still ends in the sanity-gate refusal).
912
+ const handlesBaked = handleStore.getData().then(resolveDeferredHandleValues);
913
+ // Bake-lane loader containers hold the gate the same way (loader-container-
914
+ // bake): a boundary-less container with real latency (a 100ms layout loader)
915
+ // would otherwise lose the 2-hop byte-quiet race — the pending loaderData row
916
+ // emits no bytes, the gate freezes, and the awaiting tree pins above <body>.
917
+ // The records map is fully populated before this point (loader promises are
918
+ // created during the capture's match()), so the hold covers every bake-lane
919
+ // container. allSettled: a REJECTED container releases the hold (the drain
920
+ // below refuses the capture); nested promises INSIDE a container never hold
921
+ // the gate — they stay holes. Bounded by maxWaitMs like every quiesce input.
922
+ const loaderRecordsForHold = reqCtx._shellCaptureLoaderRecords;
923
+ const holdUntil =
924
+ loaderRecordsForHold && loaderRecordsForHold.size > 0
925
+ ? Promise.allSettled([handlesBaked, ...loaderRecordsForHold.values()])
926
+ : handlesBaked;
927
+ const gate = gateFlightForCapture(rscStream, undefined, holdUntil);
928
+ // Quiesce = handles settled AND the Flight shell rows went task-quiet. Either
929
+ // half stalling is bounded by captureShellHTML's maxWaitMs.
930
+ const quiesce = Promise.all([handleStore.settled, gate.quiesce]).then(
931
+ () => {},
932
+ );
933
+
934
+ // Deterministic identity-guard refusal, checked at BOTH exits below: the
935
+ // guard error either rejects the prerender itself (boundary-less segment —
936
+ // lands in the catch) or is swallowed into per-loader error UI (the render
937
+ // completes — caught after the try). One helper so the message and the
938
+ // "refused" mapping cannot drift between the two sites.
939
+ const refuseOnGuardTrip = (): "refused" | undefined => {
940
+ const fnName = reqCtx._shellCaptureGuardTripped;
941
+ if (!fnName) return undefined;
942
+ // Name the recorded source instead of hardcoding a lane. Under the
943
+ // consumption-lane rule, handler-INVOKED loader bodies are exempt from
944
+ // the guard (their value is a baked shared copy, mirroring cache()), so a
945
+ // trip can only come from a bake-lane SEGMENT loader or from non-loader
946
+ // handler/render code — the old blanket "bake-lane loader" attribution
947
+ // sent a live-lane debugging session down the wrong lane (issue #672).
948
+ const loaderId = reqCtx._shellCaptureGuardTrippedLoaderId;
949
+ const origin = loaderId
950
+ ? `segment loader "${loaderId}"`
951
+ : "handler/render code (no loader body was executing)";
952
+ warnCaptureRefusedOnce(
953
+ capture.key,
954
+ `${origin} called ${fnName}() during capture. Identity must not bake into a shared shell. ` +
955
+ "For a segment loader (bake lane, no loading()): give its entry a loading() boundary " +
956
+ "(the live lane, masked at capture) or move the identity-dependent part into a nested " +
957
+ "promise. For handler/render code: keep the value live by consuming a loader " +
958
+ 'client-side (useLoader in a "use client" component). Note: `await ctx.use(loader)` ' +
959
+ "inside a HANDLER is exempt from this guard — its value bakes into the shared shell " +
960
+ "as a capture-time copy, mirroring cache() semantics (the consumption-lane rule).",
961
+ );
962
+ return "refused";
963
+ };
964
+
965
+ try {
966
+ // captureShellHTML CONSUMES the (gated) stream — it is not also SSR'd.
967
+ let result: Awaited<ReturnType<typeof captureShellHTML>>;
968
+ try {
969
+ result = await observePhase(PHASES.ssr, () =>
970
+ captureShellHTML(gate.stream, {
971
+ quiesce,
972
+ maxWaitMs: SHELL_CAPTURE_MAX_WAIT_MS,
973
+ }),
974
+ );
975
+ } catch (error) {
976
+ // Guard-tripped rejection arrives here (not at the drain) — refuse
977
+ // BEFORE the AbortError-vs-rethrow decision below.
978
+ const refused = refuseOnGuardTrip();
979
+ if (refused) return refused;
980
+ // captureShellHTML normally converts its OWN deliberate abort to a null
981
+ // return (index.tsx). This catch is defensive: if an AbortError still escapes
982
+ // (a runtime where the abort surfaces as a stream rejection outside its
983
+ // guard), treat it as the same retryable "no usable shell" degradation rather
984
+ // than a failure — do NOT report it as an error. A genuine (non-abort) render
985
+ // error is a real failure: rethrow so it reaches reportCacheError (no retry).
986
+ if ((error as { name?: string } | null)?.name === "AbortError") {
987
+ return "no-shell";
988
+ }
989
+ throw error;
990
+ }
991
+
992
+ // null = sanity gate refused (trivial/empty prelude, no <body>). Store nothing
993
+ // and report `no-shell` so the caller (runShellCapture) can retry once and, if
994
+ // that also fails, warn once per key. On a cold render this is the shell not
995
+ // yet finished; on a loader route WITHOUT a route-level loading() boundary it is
996
+ // the structural eternal-MISS shape (the masked loader pins the tree above
997
+ // <body> at tree-build). The caller's warning names both.
998
+ // Guard check first — BEFORE the trivial-prelude retry path. A guard trip
999
+ // is deterministic (retrying re-trips it), and when the tripping loader's
1000
+ // error UI still completed a shell, storing it would bake the failure into
1001
+ // a shared page.
1002
+ const refused = refuseOnGuardTrip();
1003
+ if (refused) return refused;
1004
+
1005
+ if (result === null) {
1006
+ return "no-shell";
1007
+ }
1008
+
1009
+ // Store per the flag's key/ttl/swr/tags, into the flag's store: the middleware
1010
+ // threads the SAME store it resolved for its getShell read (options.store ??
1011
+ // _cacheStore), so a store-attached middleware writes captures where it reads
1012
+ // them. The _cacheStore fallback covers a flag armed without a store (tests).
1013
+ // reactVersion is read from the same React.version import the middleware
1014
+ // validates reads against, so capture and serve always agree.
1015
+ // Fire the capture's isolated onResponse callbacks with a synthetic 200 so
1016
+ // the ring-3 segment cache write (cacheScope.cacheRoute, registered via
1017
+ // onResponse by the cache-store match-middleware and gated on a 200) runs
1018
+ // DURING capture, routed through the recording store. The foreground path
1019
+ // never fires for the capture — it builds no Response — so without this a
1020
+ // cache() SEGMENT that MISSED at capture would be rendered fresh into the
1021
+ // prelude yet never written, hence never recorded, and would drift on a HIT
1022
+ // (an item-family "use cache" write already runs inline during the render, so
1023
+ // it needs none of this; only segment writes are onResponse-gated). The
1024
+ // derived context's own _onResponseCallbacks holds only capture match-
1025
+ // middleware callbacks (HTTP middleware never runs for a capture), so firing
1026
+ // them is safe. Best-effort: a throwing callback must not fail the capture.
1027
+ const responseCallbacks = reqCtx._onResponseCallbacks;
1028
+ if (responseCallbacks && responseCallbacks.length > 0) {
1029
+ const synthetic = new Response(null, { status: 200 });
1030
+ for (const cb of responseCallbacks) {
1031
+ try {
1032
+ cb(synthetic);
1033
+ } catch {
1034
+ // A capture-time cache write that throws is degradation, not failure.
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ // Drain the capture data snapshot from the recording store on the derived
1040
+ // context. Await the deferred cache writes first so a MISS-at-capture value
1041
+ // (setItem/set scheduled under waitUntil, including the segment write just
1042
+ // fired) is pinned, not just read-hits. When no recording store is installed
1043
+ // (unit tests that call this directly), there is simply no snapshot.
1044
+ const recording = getRecordingStore(reqCtx._cacheStore);
1045
+ let snapshot: ShellSnapshotRecord[] | undefined;
1046
+ if (recording) {
1047
+ await recording.settleWrites(SHELL_SNAPSHOT_WRITE_SETTLE_MS);
1048
+ snapshot = recording.drainSnapshot();
1049
+ }
1050
+
1051
+ // Pin the bake-lane loader containers (loader family). Settled containers
1052
+ // are promise-elided (a still-pending nested promise is a hole marker, not
1053
+ // shell material) and Flight-serialized; a REJECTED container refuses the
1054
+ // capture — per-loader error UI must never bake into the shared shell. A
1055
+ // container still pending here either pinned the tree (the trivial-prelude
1056
+ // gate above already returned no-shell) or postponed under an ANCESTOR
1057
+ // boundary (it is a hole; omitting the record keeps it live).
1058
+ const loaderRecords = reqCtx._shellCaptureLoaderRecords;
1059
+ // Set once a bake-lane loader settles with real (non-hole) material: its data
1060
+ // is frozen into the shell prelude regardless of whether snapshot
1061
+ // serialization succeeds. Drives the untagged-bake dev warning below.
1062
+ let bakedLoaderMaterial = false;
1063
+ if (loaderRecords && loaderRecords.size > 0) {
1064
+ // The codec import is deferred past the elide probes: a rejected record
1065
+ // refuses and a never-settled record is omitted WITHOUT touching Flight
1066
+ // (also keeps the virtual @vitejs/plugin-rsc import out of unit configs).
1067
+ let serializeContainer:
1068
+ | typeof import("../cache/segment-codec.js").serializeResult
1069
+ | undefined;
1070
+ for (const [segmentKey, containerPromise] of loaderRecords) {
1071
+ const elided = await elideLoaderContainer(containerPromise);
1072
+ if (elided.state === "rejected") {
1073
+ warnCaptureRefusedOnce(
1074
+ capture.key,
1075
+ `the loader for segment "${segmentKey}" rejected during capture; its error UI must not bake into the shared shell. ` +
1076
+ "Fix the loader, or give its entry a loading() boundary so it stays on the live lane.",
1077
+ );
1078
+ return "refused";
1079
+ }
1080
+ // The container itself never settled: it is a hole (under an ancestor
1081
+ // boundary) or the trivial-prelude gate already fired. Omit — no pin.
1082
+ if (isLoaderHoleMarker(elided.value)) continue;
1083
+ // Past the hole check: this container settled with real material that
1084
+ // bakes into the shell prelude (independent of the snapshot pin below).
1085
+ bakedLoaderMaterial = true;
1086
+ try {
1087
+ // serializeResult (not rscSerialize): null is a valid container and
1088
+ // must round-trip; serializeResult preserves it through Flight.
1089
+ serializeContainer ??= (await import("../cache/segment-codec.js"))
1090
+ .serializeResult;
1091
+ const serialized = await serializeContainer(elided.value);
1092
+ if (serialized !== null) {
1093
+ (snapshot ??= []).push({
1094
+ family: "loader",
1095
+ key: segmentKey,
1096
+ value: { value: serialized },
1097
+ });
1098
+ }
1099
+ } catch {
1100
+ // Non-serializable container: leave it unpinned (it drifts on a HIT,
1101
+ // the pre-snapshot behavior) rather than failing the capture.
1102
+ }
1103
+ }
1104
+ }
1105
+
1106
+ // Shell tags snapshot at the WRITE BARRIER, not at stream construction: by
1107
+ // here the capture has quiesced and the deferred cache writes were awaited, so
1108
+ // tags recorded AFTER an await in async shell content (and by async
1109
+ // cache()/"use cache" reads propagating through recordRequestTags) are
1110
+ // included — issue #676. Loaders are masked, so loader cache tags — which
1111
+ // belong to the holes, not the shell — never execute during capture and cannot
1112
+ // contribute. Union with the route's static ppr.tags (capture.tags); the
1113
+ // collected set is authoritative, the option only adds what the render cannot
1114
+ // know.
1115
+ const collected = [...reqCtx._requestTags];
1116
+ const union = new Set<string>([...(capture.tags ?? []), ...collected]);
1117
+ const shellTags = union.size > 0 ? [...union] : undefined;
1118
+
1119
+ // Untagged-bake diagnostic: a bake-lane loader froze mutable data into the
1120
+ // shell but nothing tags the entry, so it is un-invalidatable except by TTL —
1121
+ // a read-your-own-writes gap on the document channel (an action refresh skips
1122
+ // the server shell; updateTag cannot drop untagged data). Coarse per-shell-key
1123
+ // signal; dev-only and once-per-key so it never spams production.
1124
+ if (isDevMode() && bakedLoaderMaterial && shellTags === undefined) {
1125
+ warnUntaggedShellBakeOnce(capture.key);
1126
+ }
1127
+
1128
+ const store = capture.store ?? reqCtx._cacheStore;
1129
+ if (store?.putShell) {
1130
+ try {
1131
+ const entry: ShellCacheEntry = {
1132
+ // slice() copies just this view's bytes into a fresh ArrayBuffer, so a
1133
+ // prelude that is a subarray of a larger backing buffer encodes only its
1134
+ // own region — bufferToBase64 reads the whole ArrayBuffer it is handed.
1135
+ prelude: bufferToBase64(result.prelude.slice().buffer as ArrayBuffer),
1136
+ postponed: result.postponed,
1137
+ reactVersion: React.version,
1138
+ buildVersion: capture.buildVersion,
1139
+ // The theme this capture's payload was built with (buildFullPayload
1140
+ // reads reqCtx.theme off the derived context). The serve tail replays
1141
+ // it so the resume tree matches the frozen prelude — see
1142
+ // ShellCacheEntry.initialTheme.
1143
+ initialTheme: reqCtx.theme,
1144
+ snapshot,
1145
+ createdAt: Date.now(),
1146
+ };
1147
+ await store.putShell(
1148
+ capture.key,
1149
+ entry,
1150
+ capture.ttl,
1151
+ capture.swr,
1152
+ shellTags,
1153
+ );
1154
+ } catch (error) {
1155
+ // Best-effort: a failed put must never throw out of the background task.
1156
+ reportCacheError(
1157
+ error,
1158
+ "cache-write",
1159
+ "[ShellCache] capture put",
1160
+ reqCtx,
1161
+ );
1162
+ }
1163
+ }
1164
+ // A shell was captured (the store I/O may have failed, but that is reported,
1165
+ // not retried) — so this attempt is `stored` and the caller does not retry.
1166
+ return "stored";
1167
+ } finally {
1168
+ // Stop the hop loop for the pathological never-quiets path (quiesce never
1169
+ // fired, capture returned via maxWaitMs). On the normal path the loop already
1170
+ // stopped when it fired quiesce; dispose() is then a no-op.
1171
+ gate.dispose();
1172
+ }
1173
+ }
1174
+
1175
+ // Exported for unit tests that drive the capture core directly.
1176
+ export { runShellCapture, captureAndStoreShell };
1177
+
1178
+ // Exported for unit tests that pin the refused-capture backoff policy directly
1179
+ // (dev cap vs production exponential growth, stored-clears, cold-start re-probe).
1180
+ // These are the same module-level functions the schedule path uses; a test that
1181
+ // drove them through a real capture round-trip could not assert the exact window
1182
+ // arithmetic without a full cold render.
1183
+ export {
1184
+ isCaptureBackedOff,
1185
+ markCaptureBackoff,
1186
+ clearCaptureBackoff,
1187
+ REFUSED_CAPTURE_BASE_MS,
1188
+ REFUSED_CAPTURE_MAX_MS,
1189
+ REFUSED_CAPTURE_DEV_MAX_MS,
1190
+ };