@rangojs/router 0.0.0-experimental.140 → 0.0.0-experimental.142

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 (914) hide show
  1. package/AGENTS.md +6 -10
  2. package/README.md +17 -0
  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.map +1 -0
  82. package/dist/browser/event-controller.d.ts +191 -0
  83. package/dist/browser/event-controller.d.ts.map +1 -0
  84. package/dist/browser/event-controller.js +559 -0
  85. package/dist/browser/event-controller.js.map +1 -0
  86. package/dist/browser/index.d.ts +2 -0
  87. package/dist/browser/index.d.ts.map +1 -0
  88. package/dist/browser/index.js +14 -0
  89. package/dist/browser/index.js.map +1 -0
  90. package/dist/browser/link-interceptor.d.ts +38 -0
  91. package/dist/browser/link-interceptor.d.ts.map +1 -0
  92. package/dist/browser/link-interceptor.js +99 -0
  93. package/dist/browser/link-interceptor.js.map +1 -0
  94. package/dist/browser/logging.d.ts +10 -0
  95. package/dist/browser/logging.d.ts.map +1 -0
  96. package/dist/browser/logging.js +29 -0
  97. package/dist/browser/logging.js.map +1 -0
  98. package/dist/browser/lru-cache.d.ts +17 -0
  99. package/dist/browser/lru-cache.d.ts.map +1 -0
  100. package/dist/browser/lru-cache.js +50 -0
  101. package/dist/browser/lru-cache.js.map +1 -0
  102. package/dist/browser/merge-segment-loaders.d.ts +39 -0
  103. package/dist/browser/merge-segment-loaders.d.ts.map +1 -0
  104. package/dist/browser/merge-segment-loaders.js +102 -0
  105. package/dist/browser/merge-segment-loaders.js.map +1 -0
  106. package/dist/browser/navigation-bridge.d.ts +102 -0
  107. package/dist/browser/navigation-bridge.d.ts.map +1 -0
  108. package/dist/browser/navigation-bridge.js +708 -0
  109. package/dist/browser/navigation-bridge.js.map +1 -0
  110. package/dist/browser/navigation-client.d.ts +25 -0
  111. package/dist/browser/navigation-client.d.ts.map +1 -0
  112. package/dist/browser/navigation-client.js +157 -0
  113. package/dist/browser/navigation-client.js.map +1 -0
  114. package/dist/browser/navigation-store.d.ts +101 -0
  115. package/dist/browser/navigation-store.d.ts.map +1 -0
  116. package/dist/browser/navigation-store.js +625 -0
  117. package/dist/browser/navigation-store.js.map +1 -0
  118. package/dist/browser/partial-update.d.ts +75 -0
  119. package/dist/browser/partial-update.d.ts.map +1 -0
  120. package/dist/browser/partial-update.js +426 -0
  121. package/dist/browser/partial-update.js.map +1 -0
  122. package/dist/browser/react/Link.d.ts +86 -0
  123. package/dist/browser/react/Link.d.ts.map +1 -0
  124. package/dist/browser/react/Link.js +128 -0
  125. package/dist/browser/react/Link.js.map +1 -0
  126. package/dist/browser/react/NavigationProvider.d.ts +63 -0
  127. package/dist/browser/react/NavigationProvider.d.ts.map +1 -0
  128. package/dist/browser/react/NavigationProvider.js +216 -0
  129. package/dist/browser/react/NavigationProvider.js.map +1 -0
  130. package/dist/browser/react/ScrollRestoration.d.ts +75 -0
  131. package/dist/browser/react/ScrollRestoration.d.ts.map +1 -0
  132. package/dist/browser/react/ScrollRestoration.js +57 -0
  133. package/dist/browser/react/ScrollRestoration.js.map +1 -0
  134. package/dist/browser/react/context.d.ts +46 -0
  135. package/dist/browser/react/context.d.ts.map +1 -0
  136. package/dist/browser/react/context.js +10 -0
  137. package/dist/browser/react/context.js.map +1 -0
  138. package/dist/browser/react/index.d.ts +11 -0
  139. package/dist/browser/react/index.d.ts.map +1 -0
  140. package/dist/browser/react/index.js +22 -0
  141. package/dist/browser/react/index.js.map +1 -0
  142. package/dist/browser/react/location-state-shared.d.ts +63 -0
  143. package/dist/browser/react/location-state-shared.d.ts.map +1 -0
  144. package/dist/browser/react/location-state-shared.js +81 -0
  145. package/dist/browser/react/location-state-shared.js.map +1 -0
  146. package/dist/browser/react/location-state.d.ts +23 -0
  147. package/dist/browser/react/location-state.d.ts.map +1 -0
  148. package/dist/browser/react/location-state.js +29 -0
  149. package/dist/browser/react/location-state.js.map +1 -0
  150. package/dist/browser/react/mount-context.d.ts +24 -0
  151. package/dist/browser/react/mount-context.d.ts.map +1 -0
  152. package/dist/browser/react/mount-context.js +24 -0
  153. package/dist/browser/react/mount-context.js.map +1 -0
  154. package/dist/browser/react/use-action.d.ts +64 -0
  155. package/dist/browser/react/use-action.d.ts.map +1 -0
  156. package/dist/browser/react/use-action.js +134 -0
  157. package/dist/browser/react/use-action.js.map +1 -0
  158. package/dist/browser/react/use-client-cache.d.ts +41 -0
  159. package/dist/browser/react/use-client-cache.d.ts.map +1 -0
  160. package/dist/browser/react/use-client-cache.js +39 -0
  161. package/dist/browser/react/use-client-cache.js.map +1 -0
  162. package/dist/browser/react/use-handle.d.ts +31 -0
  163. package/dist/browser/react/use-handle.d.ts.map +1 -0
  164. package/dist/browser/react/use-handle.js +144 -0
  165. package/dist/browser/react/use-handle.js.map +1 -0
  166. package/dist/browser/react/use-href.d.ts +33 -0
  167. package/dist/browser/react/use-href.d.ts.map +1 -0
  168. package/dist/browser/react/use-href.js +39 -0
  169. package/dist/browser/react/use-href.js.map +1 -0
  170. package/dist/browser/react/use-link-status.d.ts +37 -0
  171. package/dist/browser/react/use-link-status.d.ts.map +1 -0
  172. package/dist/browser/react/use-link-status.js +99 -0
  173. package/dist/browser/react/use-link-status.js.map +1 -0
  174. package/dist/browser/react/use-mount.d.ts +25 -0
  175. package/dist/browser/react/use-mount.d.ts.map +1 -0
  176. package/dist/browser/react/use-mount.js +30 -0
  177. package/dist/browser/react/use-mount.js.map +1 -0
  178. package/dist/browser/react/use-navigation.d.ts +27 -0
  179. package/dist/browser/react/use-navigation.d.ts.map +1 -0
  180. package/dist/browser/react/use-navigation.js +87 -0
  181. package/dist/browser/react/use-navigation.js.map +1 -0
  182. package/dist/browser/react/use-segments.d.ts +38 -0
  183. package/dist/browser/react/use-segments.d.ts.map +1 -0
  184. package/dist/browser/react/use-segments.js +130 -0
  185. package/dist/browser/react/use-segments.js.map +1 -0
  186. package/dist/browser/request-controller.d.ts +26 -0
  187. package/dist/browser/request-controller.d.ts.map +1 -0
  188. package/dist/browser/request-controller.js +147 -0
  189. package/dist/browser/request-controller.js.map +1 -0
  190. package/dist/browser/rsc-router.d.ts +129 -0
  191. package/dist/browser/rsc-router.d.ts.map +1 -0
  192. package/dist/browser/rsc-router.js +195 -0
  193. package/dist/browser/rsc-router.js.map +1 -0
  194. package/dist/browser/scroll-restoration.d.ts +93 -0
  195. package/dist/browser/scroll-restoration.d.ts.map +1 -0
  196. package/dist/browser/scroll-restoration.js +321 -0
  197. package/dist/browser/scroll-restoration.js.map +1 -0
  198. package/dist/browser/segment-structure-assert.d.ts +17 -0
  199. package/dist/browser/segment-structure-assert.d.ts.map +1 -0
  200. package/dist/browser/segment-structure-assert.js +59 -0
  201. package/dist/browser/segment-structure-assert.js.map +1 -0
  202. package/dist/browser/server-action-bridge.d.ts +26 -0
  203. package/dist/browser/server-action-bridge.d.ts.map +1 -0
  204. package/dist/browser/server-action-bridge.js +668 -0
  205. package/dist/browser/server-action-bridge.js.map +1 -0
  206. package/dist/browser/shallow.d.ts +12 -0
  207. package/dist/browser/shallow.d.ts.map +1 -0
  208. package/dist/browser/shallow.js +34 -0
  209. package/dist/browser/shallow.js.map +1 -0
  210. package/dist/browser/types.d.ts +369 -0
  211. package/dist/browser/types.d.ts.map +1 -0
  212. package/dist/browser/types.js +2 -0
  213. package/dist/browser/types.js.map +1 -0
  214. package/dist/build/__tests__/generate-cli.test.d.ts +2 -0
  215. package/dist/build/__tests__/generate-cli.test.d.ts.map +1 -0
  216. package/dist/build/__tests__/generate-cli.test.js +237 -0
  217. package/dist/build/__tests__/generate-cli.test.js.map +1 -0
  218. package/dist/build/__tests__/generate-manifest.test.d.ts +2 -0
  219. package/dist/build/__tests__/generate-manifest.test.d.ts.map +1 -0
  220. package/dist/build/__tests__/generate-manifest.test.js +119 -0
  221. package/dist/build/__tests__/generate-manifest.test.js.map +1 -0
  222. package/dist/build/__tests__/generate-route-types.test.d.ts +2 -0
  223. package/dist/build/__tests__/generate-route-types.test.d.ts.map +1 -0
  224. package/dist/build/__tests__/generate-route-types.test.js +620 -0
  225. package/dist/build/__tests__/generate-route-types.test.js.map +1 -0
  226. package/dist/build/__tests__/per-router-manifest.test.d.ts +2 -0
  227. package/dist/build/__tests__/per-router-manifest.test.d.ts.map +1 -0
  228. package/dist/build/__tests__/per-router-manifest.test.js +308 -0
  229. package/dist/build/__tests__/per-router-manifest.test.js.map +1 -0
  230. package/dist/build/generate-manifest.d.ts +81 -0
  231. package/dist/build/generate-manifest.d.ts.map +1 -0
  232. package/dist/build/generate-manifest.js +276 -0
  233. package/dist/build/generate-manifest.js.map +1 -0
  234. package/dist/build/generate-route-types.d.ts +115 -0
  235. package/dist/build/generate-route-types.d.ts.map +1 -0
  236. package/dist/build/generate-route-types.js +740 -0
  237. package/dist/build/generate-route-types.js.map +1 -0
  238. package/dist/build/index.d.ts +21 -0
  239. package/dist/build/index.d.ts.map +1 -0
  240. package/dist/build/index.js +21 -0
  241. package/dist/build/index.js.map +1 -0
  242. package/dist/build/route-trie.d.ts +71 -0
  243. package/dist/build/route-trie.d.ts.map +1 -0
  244. package/dist/build/route-trie.js +175 -0
  245. package/dist/build/route-trie.js.map +1 -0
  246. package/dist/cache/__tests__/cache-scope.test.d.ts +2 -0
  247. package/dist/cache/__tests__/cache-scope.test.d.ts.map +1 -0
  248. package/dist/cache/__tests__/cache-scope.test.js +208 -0
  249. package/dist/cache/__tests__/cache-scope.test.js.map +1 -0
  250. package/dist/cache/__tests__/document-cache.test.d.ts +2 -0
  251. package/dist/cache/__tests__/document-cache.test.d.ts.map +1 -0
  252. package/dist/cache/__tests__/document-cache.test.js +345 -0
  253. package/dist/cache/__tests__/document-cache.test.js.map +1 -0
  254. package/dist/cache/__tests__/memory-segment-store.test.d.ts +2 -0
  255. package/dist/cache/__tests__/memory-segment-store.test.d.ts.map +1 -0
  256. package/dist/cache/__tests__/memory-segment-store.test.js +425 -0
  257. package/dist/cache/__tests__/memory-segment-store.test.js.map +1 -0
  258. package/dist/cache/__tests__/memory-store.test.d.ts +2 -0
  259. package/dist/cache/__tests__/memory-store.test.d.ts.map +1 -0
  260. package/dist/cache/__tests__/memory-store.test.js +367 -0
  261. package/dist/cache/__tests__/memory-store.test.js.map +1 -0
  262. package/dist/cache/cache-scope.d.ts +102 -0
  263. package/dist/cache/cache-scope.d.ts.map +1 -0
  264. package/dist/cache/cache-scope.js +440 -0
  265. package/dist/cache/cache-scope.js.map +1 -0
  266. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts +2 -0
  267. package/dist/cache/cf/__tests__/cf-cache-store.test.d.ts.map +1 -0
  268. package/dist/cache/cf/__tests__/cf-cache-store.test.js +330 -0
  269. package/dist/cache/cf/__tests__/cf-cache-store.test.js.map +1 -0
  270. package/dist/cache/cf/cf-cache-store.d.ts +165 -0
  271. package/dist/cache/cf/cf-cache-store.d.ts.map +1 -0
  272. package/dist/cache/cf/cf-cache-store.js +242 -0
  273. package/dist/cache/cf/cf-cache-store.js.map +1 -0
  274. package/dist/cache/cf/index.d.ts +14 -0
  275. package/dist/cache/cf/index.d.ts.map +1 -0
  276. package/dist/cache/cf/index.js +17 -0
  277. package/dist/cache/cf/index.js.map +1 -0
  278. package/dist/cache/document-cache.d.ts +64 -0
  279. package/dist/cache/document-cache.d.ts.map +1 -0
  280. package/dist/cache/document-cache.js +228 -0
  281. package/dist/cache/document-cache.js.map +1 -0
  282. package/dist/cache/index.d.ts +19 -0
  283. package/dist/cache/index.d.ts.map +1 -0
  284. package/dist/cache/index.js +21 -0
  285. package/dist/cache/index.js.map +1 -0
  286. package/dist/cache/memory-segment-store.d.ts +110 -0
  287. package/dist/cache/memory-segment-store.d.ts.map +1 -0
  288. package/dist/cache/memory-segment-store.js +117 -0
  289. package/dist/cache/memory-segment-store.js.map +1 -0
  290. package/dist/cache/memory-store.d.ts +41 -0
  291. package/dist/cache/memory-store.d.ts.map +1 -0
  292. package/dist/cache/memory-store.js +191 -0
  293. package/dist/cache/memory-store.js.map +1 -0
  294. package/dist/cache/types.d.ts +317 -0
  295. package/dist/cache/types.d.ts.map +1 -0
  296. package/dist/cache/types.js +12 -0
  297. package/dist/cache/types.js.map +1 -0
  298. package/dist/client.d.ts +248 -0
  299. package/dist/client.d.ts.map +1 -0
  300. package/dist/client.js +367 -0
  301. package/dist/client.js.map +1 -0
  302. package/dist/client.rsc.d.ts +26 -0
  303. package/dist/client.rsc.d.ts.map +1 -0
  304. package/dist/client.rsc.js +46 -0
  305. package/dist/client.rsc.js.map +1 -0
  306. package/dist/component-utils.d.ts +36 -0
  307. package/dist/component-utils.d.ts.map +1 -0
  308. package/dist/component-utils.js +61 -0
  309. package/dist/component-utils.js.map +1 -0
  310. package/dist/components/DefaultDocument.d.ts +13 -0
  311. package/dist/components/DefaultDocument.d.ts.map +1 -0
  312. package/dist/components/DefaultDocument.js +15 -0
  313. package/dist/components/DefaultDocument.js.map +1 -0
  314. package/dist/debug.d.ts +58 -0
  315. package/dist/debug.d.ts.map +1 -0
  316. package/dist/debug.js +157 -0
  317. package/dist/debug.js.map +1 -0
  318. package/dist/default-error-boundary.d.ts +11 -0
  319. package/dist/default-error-boundary.d.ts.map +1 -0
  320. package/dist/default-error-boundary.js +45 -0
  321. package/dist/default-error-boundary.js.map +1 -0
  322. package/dist/deps/browser.d.ts +2 -0
  323. package/dist/deps/browser.d.ts.map +1 -0
  324. package/dist/deps/browser.js +3 -0
  325. package/dist/deps/browser.js.map +1 -0
  326. package/dist/deps/html-stream-client.d.ts +2 -0
  327. package/dist/deps/html-stream-client.d.ts.map +1 -0
  328. package/dist/deps/html-stream-client.js +3 -0
  329. package/dist/deps/html-stream-client.js.map +1 -0
  330. package/dist/deps/html-stream-server.d.ts +2 -0
  331. package/dist/deps/html-stream-server.d.ts.map +1 -0
  332. package/dist/deps/html-stream-server.js +3 -0
  333. package/dist/deps/html-stream-server.js.map +1 -0
  334. package/dist/deps/rsc.d.ts +2 -0
  335. package/dist/deps/rsc.d.ts.map +1 -0
  336. package/dist/deps/rsc.js +4 -0
  337. package/dist/deps/rsc.js.map +1 -0
  338. package/dist/deps/ssr.d.ts +2 -0
  339. package/dist/deps/ssr.d.ts.map +1 -0
  340. package/dist/deps/ssr.js +3 -0
  341. package/dist/deps/ssr.js.map +1 -0
  342. package/dist/errors.d.ts +174 -0
  343. package/dist/errors.d.ts.map +1 -0
  344. package/dist/errors.js +241 -0
  345. package/dist/errors.js.map +1 -0
  346. package/dist/handle.d.ts +78 -0
  347. package/dist/handle.d.ts.map +1 -0
  348. package/dist/handle.js +82 -0
  349. package/dist/handle.js.map +1 -0
  350. package/dist/handles/MetaTags.d.ts +14 -0
  351. package/dist/handles/MetaTags.d.ts.map +1 -0
  352. package/dist/handles/MetaTags.js +136 -0
  353. package/dist/handles/MetaTags.js.map +1 -0
  354. package/dist/handles/index.d.ts +6 -0
  355. package/dist/handles/index.d.ts.map +1 -0
  356. package/dist/handles/index.js +6 -0
  357. package/dist/handles/index.js.map +1 -0
  358. package/dist/handles/meta.d.ts +39 -0
  359. package/dist/handles/meta.d.ts.map +1 -0
  360. package/dist/handles/meta.js +202 -0
  361. package/dist/handles/meta.js.map +1 -0
  362. package/dist/host/__tests__/errors.test.d.ts +2 -0
  363. package/dist/host/__tests__/errors.test.d.ts.map +1 -0
  364. package/dist/host/__tests__/errors.test.js +76 -0
  365. package/dist/host/__tests__/errors.test.js.map +1 -0
  366. package/dist/host/__tests__/pattern-comprehensive.test.d.ts +2 -0
  367. package/dist/host/__tests__/pattern-comprehensive.test.d.ts.map +1 -0
  368. package/dist/host/__tests__/pattern-comprehensive.test.js +732 -0
  369. package/dist/host/__tests__/pattern-comprehensive.test.js.map +1 -0
  370. package/dist/host/__tests__/pattern-matcher.test.d.ts +2 -0
  371. package/dist/host/__tests__/pattern-matcher.test.d.ts.map +1 -0
  372. package/dist/host/__tests__/pattern-matcher.test.js +251 -0
  373. package/dist/host/__tests__/pattern-matcher.test.js.map +1 -0
  374. package/dist/host/__tests__/router.test.d.ts +2 -0
  375. package/dist/host/__tests__/router.test.d.ts.map +1 -0
  376. package/dist/host/__tests__/router.test.js +241 -0
  377. package/dist/host/__tests__/router.test.js.map +1 -0
  378. package/dist/host/__tests__/testing.test.d.ts +2 -0
  379. package/dist/host/__tests__/testing.test.d.ts.map +1 -0
  380. package/dist/host/__tests__/testing.test.js +64 -0
  381. package/dist/host/__tests__/testing.test.js.map +1 -0
  382. package/dist/host/__tests__/utils.test.d.ts +2 -0
  383. package/dist/host/__tests__/utils.test.d.ts.map +1 -0
  384. package/dist/host/__tests__/utils.test.js +29 -0
  385. package/dist/host/__tests__/utils.test.js.map +1 -0
  386. package/dist/host/cookie-handler.d.ts +34 -0
  387. package/dist/host/cookie-handler.d.ts.map +1 -0
  388. package/dist/host/cookie-handler.js +124 -0
  389. package/dist/host/cookie-handler.js.map +1 -0
  390. package/dist/host/errors.d.ts +56 -0
  391. package/dist/host/errors.d.ts.map +1 -0
  392. package/dist/host/errors.js +79 -0
  393. package/dist/host/errors.js.map +1 -0
  394. package/dist/host/index.d.ts +29 -0
  395. package/dist/host/index.d.ts.map +1 -0
  396. package/dist/host/index.js +32 -0
  397. package/dist/host/index.js.map +1 -0
  398. package/dist/host/pattern-matcher.d.ts +36 -0
  399. package/dist/host/pattern-matcher.d.ts.map +1 -0
  400. package/dist/host/pattern-matcher.js +172 -0
  401. package/dist/host/pattern-matcher.js.map +1 -0
  402. package/dist/host/router.d.ts +26 -0
  403. package/dist/host/router.d.ts.map +1 -0
  404. package/dist/host/router.js +218 -0
  405. package/dist/host/router.js.map +1 -0
  406. package/dist/host/testing.d.ts +36 -0
  407. package/dist/host/testing.d.ts.map +1 -0
  408. package/dist/host/testing.js +55 -0
  409. package/dist/host/testing.js.map +1 -0
  410. package/dist/host/types.d.ts +115 -0
  411. package/dist/host/types.d.ts.map +1 -0
  412. package/dist/host/types.js +7 -0
  413. package/dist/host/types.js.map +1 -0
  414. package/dist/host/utils.d.ts +21 -0
  415. package/dist/host/utils.d.ts.map +1 -0
  416. package/dist/host/utils.js +23 -0
  417. package/dist/host/utils.js.map +1 -0
  418. package/dist/href-client.d.ts +131 -0
  419. package/dist/href-client.d.ts.map +1 -0
  420. package/dist/href-client.js +64 -0
  421. package/dist/href-client.js.map +1 -0
  422. package/dist/href-context.d.ts +29 -0
  423. package/dist/href-context.d.ts.map +1 -0
  424. package/dist/href-context.js +21 -0
  425. package/dist/href-context.js.map +1 -0
  426. package/dist/index.d.ts +73 -0
  427. package/dist/index.d.ts.map +1 -0
  428. package/dist/index.js +91 -0
  429. package/dist/index.js.map +1 -0
  430. package/dist/index.rsc.d.ts +32 -0
  431. package/dist/index.rsc.d.ts.map +1 -0
  432. package/dist/index.rsc.js +40 -0
  433. package/dist/index.rsc.js.map +1 -0
  434. package/dist/internal-debug.d.ts +2 -0
  435. package/dist/internal-debug.d.ts.map +1 -0
  436. package/dist/internal-debug.js +5 -0
  437. package/dist/internal-debug.js.map +1 -0
  438. package/dist/loader.d.ts +14 -0
  439. package/dist/loader.d.ts.map +1 -0
  440. package/dist/loader.js +20 -0
  441. package/dist/loader.js.map +1 -0
  442. package/dist/loader.rsc.d.ts +19 -0
  443. package/dist/loader.rsc.d.ts.map +1 -0
  444. package/dist/loader.rsc.js +99 -0
  445. package/dist/loader.rsc.js.map +1 -0
  446. package/dist/network-error-thrower.d.ts +17 -0
  447. package/dist/network-error-thrower.d.ts.map +1 -0
  448. package/dist/network-error-thrower.js +14 -0
  449. package/dist/network-error-thrower.js.map +1 -0
  450. package/dist/outlet-context.d.ts +13 -0
  451. package/dist/outlet-context.d.ts.map +1 -0
  452. package/dist/outlet-context.js +3 -0
  453. package/dist/outlet-context.js.map +1 -0
  454. package/dist/prerender/__tests__/param-hash.test.d.ts +2 -0
  455. package/dist/prerender/__tests__/param-hash.test.d.ts.map +1 -0
  456. package/dist/prerender/__tests__/param-hash.test.js +148 -0
  457. package/dist/prerender/__tests__/param-hash.test.js.map +1 -0
  458. package/dist/prerender/param-hash.d.ts +16 -0
  459. package/dist/prerender/param-hash.d.ts.map +1 -0
  460. package/dist/prerender/param-hash.js +36 -0
  461. package/dist/prerender/param-hash.js.map +1 -0
  462. package/dist/prerender/store.d.ts +38 -0
  463. package/dist/prerender/store.d.ts.map +1 -0
  464. package/dist/prerender/store.js +61 -0
  465. package/dist/prerender/store.js.map +1 -0
  466. package/dist/prerender.d.ts +66 -0
  467. package/dist/prerender.d.ts.map +1 -0
  468. package/dist/prerender.js +57 -0
  469. package/dist/prerender.js.map +1 -0
  470. package/dist/reverse.d.ts +196 -0
  471. package/dist/reverse.d.ts.map +1 -0
  472. package/dist/reverse.js +78 -0
  473. package/dist/reverse.js.map +1 -0
  474. package/dist/root-error-boundary.d.ts +33 -0
  475. package/dist/root-error-boundary.d.ts.map +1 -0
  476. package/dist/root-error-boundary.js +165 -0
  477. package/dist/root-error-boundary.js.map +1 -0
  478. package/dist/route-content-wrapper.d.ts +46 -0
  479. package/dist/route-content-wrapper.d.ts.map +1 -0
  480. package/dist/route-content-wrapper.js +77 -0
  481. package/dist/route-content-wrapper.js.map +1 -0
  482. package/dist/route-definition.d.ts +421 -0
  483. package/dist/route-definition.d.ts.map +1 -0
  484. package/dist/route-definition.js +868 -0
  485. package/dist/route-definition.js.map +1 -0
  486. package/dist/route-map-builder.d.ts +155 -0
  487. package/dist/route-map-builder.d.ts.map +1 -0
  488. package/dist/route-map-builder.js +237 -0
  489. package/dist/route-map-builder.js.map +1 -0
  490. package/dist/route-types.d.ts +165 -0
  491. package/dist/route-types.d.ts.map +1 -0
  492. package/dist/route-types.js +7 -0
  493. package/dist/route-types.js.map +1 -0
  494. package/dist/router/__tests__/handler-context.test.d.ts +2 -0
  495. package/dist/router/__tests__/handler-context.test.d.ts.map +1 -0
  496. package/dist/router/__tests__/handler-context.test.js +65 -0
  497. package/dist/router/__tests__/handler-context.test.js.map +1 -0
  498. package/dist/router/__tests__/loader-cycle-detection.test.d.ts +2 -0
  499. package/dist/router/__tests__/loader-cycle-detection.test.d.ts.map +1 -0
  500. package/dist/router/__tests__/loader-cycle-detection.test.js +221 -0
  501. package/dist/router/__tests__/loader-cycle-detection.test.js.map +1 -0
  502. package/dist/router/__tests__/match-context.test.d.ts +2 -0
  503. package/dist/router/__tests__/match-context.test.d.ts.map +1 -0
  504. package/dist/router/__tests__/match-context.test.js +92 -0
  505. package/dist/router/__tests__/match-context.test.js.map +1 -0
  506. package/dist/router/__tests__/match-pipelines.test.d.ts +2 -0
  507. package/dist/router/__tests__/match-pipelines.test.d.ts.map +1 -0
  508. package/dist/router/__tests__/match-pipelines.test.js +417 -0
  509. package/dist/router/__tests__/match-pipelines.test.js.map +1 -0
  510. package/dist/router/__tests__/match-result.test.d.ts +2 -0
  511. package/dist/router/__tests__/match-result.test.d.ts.map +1 -0
  512. package/dist/router/__tests__/match-result.test.js +457 -0
  513. package/dist/router/__tests__/match-result.test.js.map +1 -0
  514. package/dist/router/__tests__/on-error.test.d.ts +2 -0
  515. package/dist/router/__tests__/on-error.test.d.ts.map +1 -0
  516. package/dist/router/__tests__/on-error.test.js +678 -0
  517. package/dist/router/__tests__/on-error.test.js.map +1 -0
  518. package/dist/router/__tests__/pattern-matching.test.d.ts +2 -0
  519. package/dist/router/__tests__/pattern-matching.test.d.ts.map +1 -0
  520. package/dist/router/__tests__/pattern-matching.test.js +629 -0
  521. package/dist/router/__tests__/pattern-matching.test.js.map +1 -0
  522. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts +2 -0
  523. package/dist/router/__tests__/segment-resolution-parallel-loading.test.d.ts.map +1 -0
  524. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js +155 -0
  525. package/dist/router/__tests__/segment-resolution-parallel-loading.test.js.map +1 -0
  526. package/dist/router/error-handling.d.ts +77 -0
  527. package/dist/router/error-handling.d.ts.map +1 -0
  528. package/dist/router/error-handling.js +202 -0
  529. package/dist/router/error-handling.js.map +1 -0
  530. package/dist/router/handler-context.d.ts +20 -0
  531. package/dist/router/handler-context.d.ts.map +1 -0
  532. package/dist/router/handler-context.js +198 -0
  533. package/dist/router/handler-context.js.map +1 -0
  534. package/dist/router/intercept-resolution.d.ts +66 -0
  535. package/dist/router/intercept-resolution.d.ts.map +1 -0
  536. package/dist/router/intercept-resolution.js +246 -0
  537. package/dist/router/intercept-resolution.js.map +1 -0
  538. package/dist/router/loader-resolution.d.ts +64 -0
  539. package/dist/router/loader-resolution.d.ts.map +1 -0
  540. package/dist/router/loader-resolution.js +284 -0
  541. package/dist/router/loader-resolution.js.map +1 -0
  542. package/dist/router/logging.d.ts +15 -0
  543. package/dist/router/logging.d.ts.map +1 -0
  544. package/dist/router/logging.js +99 -0
  545. package/dist/router/logging.js.map +1 -0
  546. package/dist/router/manifest.d.ts +22 -0
  547. package/dist/router/manifest.d.ts.map +1 -0
  548. package/dist/router/manifest.js +181 -0
  549. package/dist/router/manifest.js.map +1 -0
  550. package/dist/router/match-api.d.ts +35 -0
  551. package/dist/router/match-api.d.ts.map +1 -0
  552. package/dist/router/match-api.js +406 -0
  553. package/dist/router/match-api.js.map +1 -0
  554. package/dist/router/match-context.d.ts +206 -0
  555. package/dist/router/match-context.d.ts.map +1 -0
  556. package/dist/router/match-context.js +17 -0
  557. package/dist/router/match-context.js.map +1 -0
  558. package/dist/router/match-middleware/background-revalidation.d.ts +127 -0
  559. package/dist/router/match-middleware/background-revalidation.d.ts.map +1 -0
  560. package/dist/router/match-middleware/background-revalidation.js +75 -0
  561. package/dist/router/match-middleware/background-revalidation.js.map +1 -0
  562. package/dist/router/match-middleware/cache-lookup.d.ts +112 -0
  563. package/dist/router/match-middleware/cache-lookup.d.ts.map +1 -0
  564. package/dist/router/match-middleware/cache-lookup.js +257 -0
  565. package/dist/router/match-middleware/cache-lookup.js.map +1 -0
  566. package/dist/router/match-middleware/cache-store.d.ts +113 -0
  567. package/dist/router/match-middleware/cache-store.d.ts.map +1 -0
  568. package/dist/router/match-middleware/cache-store.js +108 -0
  569. package/dist/router/match-middleware/cache-store.js.map +1 -0
  570. package/dist/router/match-middleware/index.d.ts +81 -0
  571. package/dist/router/match-middleware/index.d.ts.map +1 -0
  572. package/dist/router/match-middleware/index.js +80 -0
  573. package/dist/router/match-middleware/index.js.map +1 -0
  574. package/dist/router/match-middleware/intercept-resolution.d.ts +117 -0
  575. package/dist/router/match-middleware/intercept-resolution.d.ts.map +1 -0
  576. package/dist/router/match-middleware/intercept-resolution.js +134 -0
  577. package/dist/router/match-middleware/intercept-resolution.js.map +1 -0
  578. package/dist/router/match-middleware/segment-resolution.d.ts +99 -0
  579. package/dist/router/match-middleware/segment-resolution.d.ts.map +1 -0
  580. package/dist/router/match-middleware/segment-resolution.js +53 -0
  581. package/dist/router/match-middleware/segment-resolution.js.map +1 -0
  582. package/dist/router/match-pipelines.d.ts +147 -0
  583. package/dist/router/match-pipelines.d.ts.map +1 -0
  584. package/dist/router/match-pipelines.js +82 -0
  585. package/dist/router/match-pipelines.js.map +1 -0
  586. package/dist/router/match-result.d.ts +126 -0
  587. package/dist/router/match-result.d.ts.map +1 -0
  588. package/dist/router/match-result.js +93 -0
  589. package/dist/router/match-result.js.map +1 -0
  590. package/dist/router/metrics.d.ts +20 -0
  591. package/dist/router/metrics.d.ts.map +1 -0
  592. package/dist/router/metrics.js +47 -0
  593. package/dist/router/metrics.js.map +1 -0
  594. package/dist/router/middleware.d.ts +249 -0
  595. package/dist/router/middleware.d.ts.map +1 -0
  596. package/dist/router/middleware.js +434 -0
  597. package/dist/router/middleware.js.map +1 -0
  598. package/dist/router/middleware.test.d.ts +2 -0
  599. package/dist/router/middleware.test.d.ts.map +1 -0
  600. package/dist/router/middleware.test.js +816 -0
  601. package/dist/router/middleware.test.js.map +1 -0
  602. package/dist/router/pattern-matching.d.ts +149 -0
  603. package/dist/router/pattern-matching.d.ts.map +1 -0
  604. package/dist/router/pattern-matching.js +349 -0
  605. package/dist/router/pattern-matching.js.map +1 -0
  606. package/dist/router/revalidation.d.ts +44 -0
  607. package/dist/router/revalidation.d.ts.map +1 -0
  608. package/dist/router/revalidation.js +147 -0
  609. package/dist/router/revalidation.js.map +1 -0
  610. package/dist/router/router-context.d.ts +135 -0
  611. package/dist/router/router-context.d.ts.map +1 -0
  612. package/dist/router/router-context.js +36 -0
  613. package/dist/router/router-context.js.map +1 -0
  614. package/dist/router/segment-resolution.d.ts +127 -0
  615. package/dist/router/segment-resolution.d.ts.map +1 -0
  616. package/dist/router/segment-resolution.js +919 -0
  617. package/dist/router/segment-resolution.js.map +1 -0
  618. package/dist/router/trie-matching.d.ts +40 -0
  619. package/dist/router/trie-matching.d.ts.map +1 -0
  620. package/dist/router/trie-matching.js +127 -0
  621. package/dist/router/trie-matching.js.map +1 -0
  622. package/dist/router/types.d.ts +136 -0
  623. package/dist/router/types.d.ts.map +1 -0
  624. package/dist/router/types.js +7 -0
  625. package/dist/router/types.js.map +1 -0
  626. package/dist/router.d.ts +753 -0
  627. package/dist/router.d.ts.map +1 -0
  628. package/dist/router.gen.d.ts +6 -0
  629. package/dist/router.gen.d.ts.map +1 -0
  630. package/dist/router.gen.js +6 -0
  631. package/dist/router.gen.js.map +1 -0
  632. package/dist/router.js +1304 -0
  633. package/dist/router.js.map +1 -0
  634. package/dist/rsc/__tests__/helpers.test.d.ts +2 -0
  635. package/dist/rsc/__tests__/helpers.test.d.ts.map +1 -0
  636. package/dist/rsc/__tests__/helpers.test.js +140 -0
  637. package/dist/rsc/__tests__/helpers.test.js.map +1 -0
  638. package/dist/rsc/handler.d.ts +45 -0
  639. package/dist/rsc/handler.d.ts.map +1 -0
  640. package/dist/rsc/handler.js +1172 -0
  641. package/dist/rsc/handler.js.map +1 -0
  642. package/dist/rsc/helpers.d.ts +16 -0
  643. package/dist/rsc/helpers.d.ts.map +1 -0
  644. package/dist/rsc/helpers.js +55 -0
  645. package/dist/rsc/helpers.js.map +1 -0
  646. package/dist/rsc/index.d.ts +22 -0
  647. package/dist/rsc/index.d.ts.map +1 -0
  648. package/dist/rsc/index.js +23 -0
  649. package/dist/rsc/index.js.map +1 -0
  650. package/dist/rsc/nonce.d.ts +9 -0
  651. package/dist/rsc/nonce.d.ts.map +1 -0
  652. package/dist/rsc/nonce.js +18 -0
  653. package/dist/rsc/nonce.js.map +1 -0
  654. package/dist/rsc/types.d.ts +206 -0
  655. package/dist/rsc/types.d.ts.map +1 -0
  656. package/dist/rsc/types.js +8 -0
  657. package/dist/rsc/types.js.map +1 -0
  658. package/dist/search-params.d.ts +103 -0
  659. package/dist/search-params.d.ts.map +1 -0
  660. package/dist/search-params.js +74 -0
  661. package/dist/search-params.js.map +1 -0
  662. package/dist/segment-system.d.ts +75 -0
  663. package/dist/segment-system.d.ts.map +1 -0
  664. package/dist/segment-system.js +336 -0
  665. package/dist/segment-system.js.map +1 -0
  666. package/dist/server/context.d.ts +245 -0
  667. package/dist/server/context.d.ts.map +1 -0
  668. package/dist/server/context.js +197 -0
  669. package/dist/server/context.js.map +1 -0
  670. package/dist/server/fetchable-loader-store.d.ts +18 -0
  671. package/dist/server/fetchable-loader-store.d.ts.map +1 -0
  672. package/dist/server/fetchable-loader-store.js +18 -0
  673. package/dist/server/fetchable-loader-store.js.map +1 -0
  674. package/dist/server/handle-store.d.ts +85 -0
  675. package/dist/server/handle-store.d.ts.map +1 -0
  676. package/dist/server/handle-store.js +142 -0
  677. package/dist/server/handle-store.js.map +1 -0
  678. package/dist/server/loader-registry.d.ts +55 -0
  679. package/dist/server/loader-registry.d.ts.map +1 -0
  680. package/dist/server/loader-registry.js +132 -0
  681. package/dist/server/loader-registry.js.map +1 -0
  682. package/dist/server/request-context.d.ts +226 -0
  683. package/dist/server/request-context.d.ts.map +1 -0
  684. package/dist/server/request-context.js +290 -0
  685. package/dist/server/request-context.js.map +1 -0
  686. package/dist/server/root-layout.d.ts +4 -0
  687. package/dist/server/root-layout.d.ts.map +1 -0
  688. package/dist/server/root-layout.js +5 -0
  689. package/dist/server/root-layout.js.map +1 -0
  690. package/dist/server.d.ts +15 -0
  691. package/dist/server.d.ts.map +1 -0
  692. package/dist/server.js +20 -0
  693. package/dist/server.js.map +1 -0
  694. package/dist/ssr/__tests__/ssr-handler.test.d.ts +2 -0
  695. package/dist/ssr/__tests__/ssr-handler.test.d.ts.map +1 -0
  696. package/dist/ssr/__tests__/ssr-handler.test.js +132 -0
  697. package/dist/ssr/__tests__/ssr-handler.test.js.map +1 -0
  698. package/dist/ssr/index.d.ts +98 -0
  699. package/dist/ssr/index.d.ts.map +1 -0
  700. package/dist/ssr/index.js +158 -0
  701. package/dist/ssr/index.js.map +1 -0
  702. package/dist/static-handler.d.ts +50 -0
  703. package/dist/static-handler.d.ts.map +1 -0
  704. package/dist/static-handler.gen.d.ts +5 -0
  705. package/dist/static-handler.gen.d.ts.map +1 -0
  706. package/dist/static-handler.gen.js +5 -0
  707. package/dist/static-handler.gen.js.map +1 -0
  708. package/dist/static-handler.js +29 -0
  709. package/dist/static-handler.js.map +1 -0
  710. package/dist/theme/ThemeProvider.d.ts +20 -0
  711. package/dist/theme/ThemeProvider.d.ts.map +1 -0
  712. package/dist/theme/ThemeProvider.js +240 -0
  713. package/dist/theme/ThemeProvider.js.map +1 -0
  714. package/dist/theme/ThemeScript.d.ts +48 -0
  715. package/dist/theme/ThemeScript.d.ts.map +1 -0
  716. package/dist/theme/ThemeScript.js +13 -0
  717. package/dist/theme/ThemeScript.js.map +1 -0
  718. package/dist/theme/__tests__/theme.test.d.ts +2 -0
  719. package/dist/theme/__tests__/theme.test.d.ts.map +1 -0
  720. package/dist/theme/__tests__/theme.test.js +103 -0
  721. package/dist/theme/__tests__/theme.test.js.map +1 -0
  722. package/dist/theme/constants.d.ts +29 -0
  723. package/dist/theme/constants.d.ts.map +1 -0
  724. package/dist/theme/constants.js +48 -0
  725. package/dist/theme/constants.js.map +1 -0
  726. package/dist/theme/index.d.ts +31 -0
  727. package/dist/theme/index.d.ts.map +1 -0
  728. package/dist/theme/index.js +36 -0
  729. package/dist/theme/index.js.map +1 -0
  730. package/dist/theme/theme-context.d.ts +40 -0
  731. package/dist/theme/theme-context.d.ts.map +1 -0
  732. package/dist/theme/theme-context.js +60 -0
  733. package/dist/theme/theme-context.js.map +1 -0
  734. package/dist/theme/theme-script.d.ts +27 -0
  735. package/dist/theme/theme-script.d.ts.map +1 -0
  736. package/dist/theme/theme-script.js +147 -0
  737. package/dist/theme/theme-script.js.map +1 -0
  738. package/dist/theme/types.d.ts +163 -0
  739. package/dist/theme/types.d.ts.map +1 -0
  740. package/dist/theme/types.js +11 -0
  741. package/dist/theme/types.js.map +1 -0
  742. package/dist/theme/use-theme.d.ts +12 -0
  743. package/dist/theme/use-theme.d.ts.map +1 -0
  744. package/dist/theme/use-theme.js +40 -0
  745. package/dist/theme/use-theme.js.map +1 -0
  746. package/dist/types.d.ts +1479 -0
  747. package/dist/types.d.ts.map +1 -0
  748. package/dist/types.js +10 -0
  749. package/dist/types.js.map +1 -0
  750. package/dist/urls.d.ts +441 -0
  751. package/dist/urls.d.ts.map +1 -0
  752. package/dist/urls.gen.d.ts +8 -0
  753. package/dist/urls.gen.d.ts.map +1 -0
  754. package/dist/urls.gen.js +8 -0
  755. package/dist/urls.gen.js.map +1 -0
  756. package/dist/urls.js +443 -0
  757. package/dist/urls.js.map +1 -0
  758. package/dist/use-loader.d.ts +127 -0
  759. package/dist/use-loader.d.ts.map +1 -0
  760. package/dist/use-loader.js +237 -0
  761. package/dist/use-loader.js.map +1 -0
  762. package/dist/vite/__tests__/ast-handler-extract.test.d.ts +2 -0
  763. package/dist/vite/__tests__/ast-handler-extract.test.d.ts.map +1 -0
  764. package/dist/vite/__tests__/ast-handler-extract.test.js +294 -0
  765. package/dist/vite/__tests__/ast-handler-extract.test.js.map +1 -0
  766. package/dist/vite/__tests__/expose-id-utils.test.d.ts +2 -0
  767. package/dist/vite/__tests__/expose-id-utils.test.d.ts.map +1 -0
  768. package/dist/vite/__tests__/expose-id-utils.test.js +224 -0
  769. package/dist/vite/__tests__/expose-id-utils.test.js.map +1 -0
  770. package/dist/vite/__tests__/expose-internal-ids.test.d.ts +2 -0
  771. package/dist/vite/__tests__/expose-internal-ids.test.d.ts.map +1 -0
  772. package/dist/vite/__tests__/expose-internal-ids.test.js +647 -0
  773. package/dist/vite/__tests__/expose-internal-ids.test.js.map +1 -0
  774. package/dist/vite/__tests__/expose-router-id.test.d.ts +2 -0
  775. package/dist/vite/__tests__/expose-router-id.test.d.ts.map +1 -0
  776. package/dist/vite/__tests__/expose-router-id.test.js +39 -0
  777. package/dist/vite/__tests__/expose-router-id.test.js.map +1 -0
  778. package/dist/vite/ast-handler-extract.d.ts +49 -0
  779. package/dist/vite/ast-handler-extract.d.ts.map +1 -0
  780. package/dist/vite/ast-handler-extract.js +249 -0
  781. package/dist/vite/ast-handler-extract.js.map +1 -0
  782. package/dist/vite/expose-action-id.d.ts +19 -0
  783. package/dist/vite/expose-action-id.d.ts.map +1 -0
  784. package/dist/vite/expose-action-id.js +250 -0
  785. package/dist/vite/expose-action-id.js.map +1 -0
  786. package/dist/vite/expose-id-utils.d.ts +69 -0
  787. package/dist/vite/expose-id-utils.d.ts.map +1 -0
  788. package/dist/vite/expose-id-utils.js +289 -0
  789. package/dist/vite/expose-id-utils.js.map +1 -0
  790. package/dist/vite/expose-internal-ids.d.ts +22 -0
  791. package/dist/vite/expose-internal-ids.d.ts.map +1 -0
  792. package/dist/vite/expose-internal-ids.js +886 -0
  793. package/dist/vite/expose-internal-ids.js.map +1 -0
  794. package/dist/vite/index.d.ts +149 -0
  795. package/dist/vite/index.d.ts.map +1 -0
  796. package/dist/vite/index.js +82 -15
  797. package/dist/vite/index.js.bak +5448 -0
  798. package/dist/vite/index.js.map +1 -0
  799. package/dist/vite/index.named-routes.gen.ts +103 -0
  800. package/dist/vite/package-resolution.d.ts +43 -0
  801. package/dist/vite/package-resolution.d.ts.map +1 -0
  802. package/dist/vite/package-resolution.js +112 -0
  803. package/dist/vite/package-resolution.js.map +1 -0
  804. package/dist/vite/virtual-entries.d.ts +25 -0
  805. package/dist/vite/virtual-entries.d.ts.map +1 -0
  806. package/dist/vite/virtual-entries.js +110 -0
  807. package/dist/vite/virtual-entries.js.map +1 -0
  808. package/package.json +19 -18
  809. package/skills/api-client/SKILL.md +1 -1
  810. package/skills/breadcrumbs/SKILL.md +1 -1
  811. package/skills/cache-guide/SKILL.md +1 -1
  812. package/skills/caching/SKILL.md +17 -1
  813. package/skills/catalog.json +265 -0
  814. package/skills/comparison/references/framework-comparison.md +1 -1
  815. package/skills/composability/SKILL.md +1 -1
  816. package/skills/debug-manifest/SKILL.md +1 -1
  817. package/skills/document-cache/SKILL.md +9 -1
  818. package/skills/fonts/SKILL.md +1 -1
  819. package/skills/handler-use/SKILL.md +1 -1
  820. package/skills/hooks/SKILL.md +54 -892
  821. package/skills/hooks/data.md +273 -0
  822. package/skills/hooks/handle-and-actions.md +103 -0
  823. package/skills/hooks/navigation.md +110 -0
  824. package/skills/hooks/outlets.md +41 -0
  825. package/skills/hooks/state.md +228 -0
  826. package/skills/hooks/urls.md +135 -0
  827. package/skills/host-router/SKILL.md +1 -1
  828. package/skills/i18n/SKILL.md +1 -1
  829. package/skills/intercept/SKILL.md +8 -1
  830. package/skills/layout/SKILL.md +1 -1
  831. package/skills/links/SKILL.md +1 -1
  832. package/skills/loader/SKILL.md +8 -1
  833. package/skills/middleware/SKILL.md +1 -1
  834. package/skills/migrate-nextjs/SKILL.md +133 -1
  835. package/skills/migrate-react-router/SKILL.md +42 -816
  836. package/skills/migrate-react-router/cloudflare-workers.md +129 -0
  837. package/skills/migrate-react-router/component-migration.md +196 -0
  838. package/skills/migrate-react-router/data-and-actions.md +225 -0
  839. package/skills/migrate-react-router/route-mapping.md +271 -0
  840. package/skills/mime-routes/SKILL.md +1 -1
  841. package/skills/observability/SKILL.md +1 -1
  842. package/skills/parallel/SKILL.md +8 -1
  843. package/skills/ppr/SKILL.md +448 -348
  844. package/skills/prerender/SKILL.md +8 -1
  845. package/skills/rango/SKILL.md +14 -14
  846. package/skills/response-routes/SKILL.md +1 -1
  847. package/skills/route/SKILL.md +1 -1
  848. package/skills/router-setup/SKILL.md +1 -1
  849. package/skills/scripts/SKILL.md +1 -1
  850. package/skills/server-actions/SKILL.md +3 -2
  851. package/skills/shell-manifest/SKILL.md +1 -1
  852. package/skills/streams-and-websockets/SKILL.md +1 -1
  853. package/skills/tailwind/SKILL.md +1 -1
  854. package/skills/testing/SKILL.md +1 -1
  855. package/skills/theme/SKILL.md +1 -1
  856. package/skills/typesafety/SKILL.md +44 -919
  857. package/skills/typesafety/env-and-bindings.md +254 -0
  858. package/skills/typesafety/generated-files-and-cli.md +305 -0
  859. package/skills/typesafety/params-and-search.md +153 -0
  860. package/skills/typesafety/route-types.md +209 -0
  861. package/skills/use-cache/SKILL.md +7 -1
  862. package/skills/vercel/SKILL.md +1 -1
  863. package/skills/view-transitions/SKILL.md +1 -1
  864. package/src/browser/event-controller.ts +41 -10
  865. package/src/browser/logging.ts +10 -0
  866. package/src/browser/merge-segment-loaders.ts +6 -4
  867. package/src/browser/navigation-client.ts +5 -1
  868. package/src/browser/navigation-store.ts +46 -6
  869. package/src/browser/partial-update.ts +27 -15
  870. package/src/browser/prefetch/cache.ts +26 -4
  871. package/src/browser/prefetch/fetch.ts +27 -17
  872. package/src/browser/react/Link.tsx +13 -3
  873. package/src/browser/react/NavigationProvider.tsx +21 -19
  874. package/src/browser/scroll-restoration.ts +7 -5
  875. package/src/browser/segment-reconciler.ts +31 -21
  876. package/src/browser/types.ts +12 -0
  877. package/src/cache/cache-runtime.ts +203 -23
  878. package/src/cache/cf/cf-cache-store.ts +8 -0
  879. package/src/cache/document-cache.ts +20 -9
  880. package/src/cache/index.ts +0 -5
  881. package/src/cache/memory-segment-store.ts +53 -2
  882. package/src/cache/segment-codec.ts +4 -4
  883. package/src/cache/shell-snapshot.ts +417 -0
  884. package/src/cache/types.ts +86 -0
  885. package/src/cache/vercel/vercel-cache-store.ts +143 -108
  886. package/src/index.rsc.ts +1 -5
  887. package/src/index.ts +1 -17
  888. package/src/router/manifest.ts +13 -5
  889. package/src/router/match-api.ts +70 -30
  890. package/src/router/match-handlers.ts +6 -2
  891. package/src/router/match-result.ts +35 -15
  892. package/src/router/middleware.ts +10 -3
  893. package/src/router/request-classification.ts +20 -7
  894. package/src/router/route-snapshot.ts +10 -0
  895. package/src/router/segment-resolution/fresh.ts +25 -4
  896. package/src/router/segment-resolution/loader-cache.ts +57 -8
  897. package/src/router/segment-resolution/loader-mask.ts +26 -3
  898. package/src/router/segment-resolution/loader-snapshot.ts +170 -0
  899. package/src/rsc/nonce.ts +10 -1
  900. package/src/rsc/rsc-rendering.ts +310 -89
  901. package/src/rsc/shell-capture.ts +707 -66
  902. package/src/rsc/shell-serve.ts +150 -0
  903. package/src/server/context.ts +7 -0
  904. package/src/server/cookie-store.ts +6 -0
  905. package/src/server/request-context.ts +85 -46
  906. package/src/ssr/index.tsx +45 -6
  907. package/src/theme/ThemeProvider.tsx +36 -26
  908. package/src/urls/index.ts +1 -0
  909. package/src/urls/path-helper.ts +5 -0
  910. package/src/urls/pattern-types.ts +36 -0
  911. package/src/vite/discovery/dev-prerender-cache.ts +117 -0
  912. package/src/vite/router-discovery.ts +78 -15
  913. package/src/cache/shell-cache.ts +0 -386
  914. package/src/server/live.ts +0 -130
@@ -1,14 +1,14 @@
1
1
  ---
2
2
  name: ppr
3
- description: PPR shell caching — serve a cached HTML shell instantly and resume live loader holes (createShellCacheMiddleware)
3
+ description: PPR shell caching — opt a page route in with the `ppr` path option; the router serves the cached HTML shell instantly and resumes the live holes. Use when a page should render instantly from a cached shell while specific parts stay live, or asking about partial prerendering in Rango.
4
4
  argument-hint: "[setup]"
5
5
  ---
6
6
 
7
7
  # PPR Shell Caching
8
8
 
9
- Caches the rendered HTML **shell** of a route (React `prerender` prelude bytes
10
- plus `postponed` state) and, on a later request, flushes those bytes before any
11
- render work happens, then resumes fizz for just the live loader holes. The
9
+ Caches the rendered HTML **shell** of a page route (React `prerender` prelude
10
+ bytes plus `postponed` state) and, on a later request, flushes those bytes
11
+ before any render work happens, then resumes fizz for just the live holes. The
12
12
  browser sees one ordinary streamed document; loaders stay fresh on every
13
13
  request. This is the second render axis — the default axis-1 path is untouched,
14
14
  and every ineligible request falls open to it.
@@ -17,21 +17,43 @@ Compare `/document-cache`, which freezes the WHOLE response including loader
17
17
  output. Shell caching is for pages that mix a stable shell with live data: the
18
18
  shell is shared per host+URL, the holes are per request.
19
19
 
20
- ## Setup
20
+ ## Not this skill if…
21
21
 
22
- The middleware needs a store that implements the shell family
23
- (`getShell`/`putShell`): `MemorySegmentCacheStore` (dev/tests), `CFCacheStore`
24
- (Cloudflare KV), or `VercelCacheStore` (runtime cache). It defaults to the
25
- app-level store from `createRouter({ cache })`; a store without the family
26
- disables the middleware (fail-open to axis 1).
22
+ - You want the WHOLE response frozen, loader output included — see
23
+ `/document-cache`.
24
+ - You want routes rendered at build time with `Static()`/`Prerender()` — the
25
+ `ppr` path option captures at runtime; see `/prerender`.
26
+ - You are unsure which cache layer you need — start at `/cache-guide`.
27
+
28
+ ## Setup: one path option, no middleware
29
+
30
+ PPR is a DOCUMENT-level property declared on the page route via the `ppr` path
31
+ option. Serving is **integral to the router** — there is nothing to mount. The
32
+ only prerequisite is an app-level `createRouter({ cache })` store that
33
+ implements the shell family (`getShell`/`putShell`): `MemorySegmentCacheStore`
34
+ (dev/tests), `CFCacheStore` (Cloudflare KV), or `VercelCacheStore` (runtime
35
+ cache). A ppr route on a store without the family stays on axis 1 with a
36
+ once-per-key warning.
27
37
 
28
38
  ```typescript
29
- import { createRouter } from "@rangojs/router";
30
- import {
31
- createShellCacheMiddleware,
32
- CFCacheStore,
33
- } from "@rangojs/router/cache";
34
- import { urlpatterns } from "./urls";
39
+ import { createRouter, urls } from "@rangojs/router";
40
+ import { CFCacheStore } from "@rangojs/router/cache";
41
+
42
+ export const urlpatterns = urls(({ path, layout, loader, loading }) => [
43
+ layout(ProductShell, () => [
44
+ path(
45
+ "/products/:id",
46
+ PricePage,
47
+ // `ppr` is the whole opt-in AND the policy. `ppr: true` uses the default
48
+ // ttl (300s); an object sets ttl/swr/tags (PartialPrerenderProps).
49
+ { name: "product", ppr: { ttl: 600, swr: 120 } },
50
+ () => [
51
+ loader(LivePriceLoader),
52
+ loading(<PriceSkeleton />), // structural hole: the loader subtree stays live
53
+ ],
54
+ ),
55
+ ]),
56
+ ]);
35
57
 
36
58
  const router = createRouter<AppBindings>({
37
59
  document: Document,
@@ -40,383 +62,461 @@ const router = createRouter<AppBindings>({
40
62
  store: new CFCacheStore({ kv: env.CACHE_KV, ctx: ctx! }),
41
63
  }),
42
64
  });
65
+ export default router;
66
+ ```
43
67
 
44
- // Path-scoped: only the routes that fit the shell/hole shape below.
45
- router.use(
46
- "/products",
47
- createShellCacheMiddleware({ ttlSeconds: 600, swrSeconds: 120 }),
48
- );
68
+ That is ONE of two hole mechanisms — the loader one. Do not conclude PPR
69
+ requires a loader:
49
70
 
50
- export default router;
71
+ ### The same opt-in with NO loader and NO loading() — promise holes
72
+
73
+ A route (or its layouts) whose live regions are pending promises under
74
+ `<Suspense>` PPRs with no `loader()` and no `loading()` anywhere. Hand the
75
+ un-awaited promise over as a prop; the consumer suspends under its OWN
76
+ boundary; the boundary postpones at capture and becomes the hole:
77
+
78
+ ```typescript
79
+ // Handler: kick off the fetch, do NOT await it.
80
+ function ProductPage(ctx: HandlerContext) {
81
+ const reviews = fetchReviews(ctx.params.id); // Promise<Review[]> — pending
82
+ return (
83
+ <main>
84
+ <h1>Product {ctx.params.id}</h1> {/* shell — baked into the prelude */}
85
+ <ReviewsSection promise={reviews} /> {/* hole — resumes per request */}
86
+ </main>
87
+ );
88
+ }
51
89
  ```
52
90
 
91
+ ```tsx
92
+ // ReviewsSection.tsx — the consumer owns its Suspense boundary.
93
+ "use client";
94
+
95
+ import { Suspense, use } from "react";
96
+
97
+ function Inner({ promise }: { promise: Promise<Review[]> }) {
98
+ return <ReviewList reviews={use(promise)} />;
99
+ }
100
+ export function ReviewsSection({ promise }: { promise: Promise<Review[]> }) {
101
+ return (
102
+ <Suspense fallback={<ReviewsSkeleton />}>
103
+ <Inner promise={promise} />
104
+ </Suspense>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ```typescript
110
+ path("/products/:id", ProductPage, { name: "product", ppr: true });
111
+ // No use() list at all: no loader, no loading, still a shell + live hole.
112
+ ```
113
+
114
+ At capture the pending fetch cannot win the task-quantized quiet window, so
115
+ the boundary postpones — fallback in the frozen prelude, value resumed fresh
116
+ on every HIT. This is the PHYSICS class from the hole doctrine below, and it
117
+ is exactly how an existing Suspense-shaped tree (e.g. migrated from Next.js
118
+ PPR) works with zero restructuring. The e2e proof is
119
+ `e2e/test-app/src/components/ShellPhysicsValue.tsx` — a promise hole living in
120
+ a LAYOUT with no loader registration at all.
121
+
122
+ A route WITHOUT the `ppr` option is pure axis 1: no store read, no capture, no
123
+ logs, zero cost. `ppr` is per page route — declaring it on a layout is not
124
+ supported (subtree inheritance is a possible follow-up).
125
+
53
126
  ## Where PPR sits: the cache onion
54
127
 
55
128
  Rango's caches layer like an onion — each ring stores a progressively more
56
- "cooked" representation of the same page, and PPR is a new ring, not a
57
- replacement for any existing one. From innermost (raw values) to outermost
58
- (final bytes):
129
+ "cooked" representation of the same page. From innermost (raw values) to
130
+ outermost (final bytes):
59
131
 
60
132
  | Ring | Primitive | What is stored | What stays live on a hit |
61
133
  | ----------------------- | ---------------------------------------- | -------------------------------------------------- | -------------------------------------- |
62
134
  | 1. Function values | `"use cache"` | a function's return value | everything around the call |
63
135
  | 2. Loader values | `loader(Fn, () => [cache({...})])` | one loader's result (opt-in; loaders default live) | all other loaders, handlers, rendering |
64
136
  | 3. Segments (Flight) | `cache()` route / build-time `prerender` | serialized rendered segments + replayed handles | loaders, HTML render |
65
- | 4. **HTML shell (PPR)** | `createShellCacheMiddleware` | rendered prelude bytes + React postponed state | loaders (the holes), hydration payload |
137
+ | 4. **HTML shell (PPR)** | `ppr` path option | rendered prelude bytes + React postponed state | the holes, hydration payload |
66
138
  | 5. Whole response | `/document-cache` | final response bytes, headers included | nothing — all-or-nothing |
67
139
 
68
- Each ring is derived from the ones inside it, and PPR makes that literal:
69
- the captured shell is the fizz render of ring 3's replayed segments, which is
70
- why shell/payload consistency holds by construction on `cache()` routes. The
71
- rings compose in one request: a HIT serves ring 4's bytes instantly, the
72
- resume pass replays ring 3's segments for the hydration payload, a hole's
73
- loader may consult ring 2, and a component inside it may consult ring 1.
74
-
75
- The onion also explains the boundary with ring 5: the document cache freezes
76
- loaders too (no holes, coarser but simpler), which is why stacking both on
77
- one route is pointless — pick the outermost ring whose "stays live" column
78
- matches the route (see Pitfalls).
79
-
80
- Invalidation crosses rings: `invalidateTags` reaches segment, shell, loader,
81
- and item entries in the same store, and shell entries additionally
82
- self-invalidate on `React.version` change. TTL/SWR are per-ring — an inner
83
- ring's shorter TTL shows through a hole immediately (loaders are live), but
84
- shell-embedded content refreshes only on the shell's own recapture.
85
-
86
- ## Creating holes: I want X → do Y
87
-
88
- A shell-cached route is a stable shell with live **holes** punched through it.
89
- Everything hinges on where the hole is, and the hole is always a route-level
90
- `loading()` boundary. Start here:
91
-
92
- | I want… | Do this |
93
- | ------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
94
- | Live per-request data in the page | a route `loader()` **+ route-level `loading()`** — the loader is the hole, `loading()` is the boundary the capture postpones at and the resume stitches into |
95
- | A slow nested value streamed **inside** the hole | return `{ outer, pendingData: Promise }` from the loader; `use(pendingData)` under the consumer's OWN inner `<Suspense>` (see "Loader-carried promises") |
96
- | A hole for data that is **already resolved** (in-memory, `Promise.resolve`, a cached read) | wrap it in **`live(() => …)`** under your own `<Suspense>` — masked at capture exactly like a loader, so it postpones instead of settling into the shared shell (see "live()") |
97
- | Shell-safe, deterministic data | `await` it in a **handler** — it is shell material, frozen into the prelude |
98
- | **Per-user** data | a `loader` (masked at capture, always fresh), or **`live()`** for a boundary that is not a route loader. NEVER a handler or middleware-derived `ctx` state — those run during capture and bake into the **shared** shell (see Security) |
99
- | A slow nested value on a route with **no** `loading()` | still fine on axis 1: the tree-build await is SHALLOW, so `{ outer, pendingData }` streams the inner under the consumer's `<Suspense>` — but the route is not shell-cacheable |
100
-
101
- The last row is the common trap: "no `loading()` blocks" does NOT mean "nested
102
- promises can't stream". They stream today, unchanged by PPR — the route just
103
- has no hole, so shell caching stays off for it (eternal MISS, below).
104
-
105
- ## The hole contract (read this before wiring a route)
106
-
107
- A hole exists ONLY where the route-level `loading()` boundary separates loader
108
- consumption from the shell. `loading()` becomes `LoaderBoundary`
109
- (`src/route-content-wrapper.tsx`) — a `<Suspense>` whose resolver `use()`es the
110
- loader promise INSIDE it — so the masked loader postpones exactly there and the
111
- prelude freezes the layouts plus the fallback. Two consequences:
112
-
113
- 1. Shell material (static content, handle reads, interactive client islands)
114
- lives in a **layout** above the loader route.
115
- 2. The loader-consuming route below carries **`loading()`**.
140
+ PPR is ORTHOGONAL to `cache()` (ring 3): a ppr route may be uncached (its
141
+ handlers run fresh on every serve and during capture), fully `cache()`d (its
142
+ segments replay), or mixed. One useful cache() property to know: the segment
143
+ codec **deep-settles promises at the ring-3 write**, so nothing inside a
144
+ `cache()` boundary can stay live — that is a cache() fact, not a ppr one.
145
+
146
+ Invalidation crosses rings: `updateTag()`/`revalidateTag()` reach segment,
147
+ shell, loader, and item entries in the same store, and shell entries
148
+ additionally self-invalidate on `React.version` change.
149
+
150
+ ## The serve pipeline: commit after ALL middleware
151
+
152
+ On a document GET to a ppr route the router runs:
153
+
154
+ 1. **match** — route identified, `ppr` config read from the matched route;
155
+ 2. **the WHOLE middleware chain** — the global `router.use()` chain AND route
156
+ DSL `middleware()`; both are guards, and the COMMIT POINT is after all of
157
+ them: any rejection/redirect/401 wins before a single shell byte, on MISS
158
+ and on a warmed HIT alike;
159
+ 3. **shell lookup** — `getShell(key)` on the app store (key =
160
+ host+pathname+sorted search);
161
+ 4. **HIT** — the composed response is committed immediately: the stored prelude
162
+ bytes flush first, while segment resolution, the fresh Flight render (the
163
+ full hydration payload — there is no Flight-side resume), and the fizz
164
+ `resume` of just the holes run BEHIND them inside the response stream;
165
+ 5. **MISS** — plain axis-1 serve, tagged `x-rango-shell: MISS`, plus a
166
+ background capture (stampede-guarded, retry-in-place, exponential backoff).
167
+
168
+ `x-rango-shell: HIT | MISS` is the observability header. Because the commit
169
+ point is after the chain, an unauthorized request NEVER sees shell bytes — put
170
+ auth middleware anywhere (global or route DSL) and it guards PPR for free.
171
+
172
+ ## The hole doctrine (encode this in your head)
173
+
174
+ Holes are **render-defined**, decided by the shape of the tree, on three rules:
175
+
176
+ | Class | What makes the hole | At capture | At serve |
177
+ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------- |
178
+ | **STRUCTURAL** | the ENTIRE segment subtree under a `loading()` registration — the loader LIVE lane | loaders masked; the LoaderBoundary postpones; the fallback bakes in as route structure | loaders run fresh; resume fills it |
179
+ | **PHYSICS** | any promise NESTED in handed-over data still pending at capture, under the consumer's own `<Suspense>` — handler props, handle containers (`push({ x: promise })`), loader-carried | real I/O cannot win the task-quantized quiet window; the boundary postpones | the promise settles and streams in |
180
+ | **SHELL** | awaited handler data, TOP-LEVEL `push(promise)` (awaited before SSR), resolved promises, replayed `cache()` segments, BAKE-lane loader containers | baked into the prelude | served from the frozen prelude |
181
+
182
+ ONE rule for promises, every lane — handlers, handles, AND loaders: **a promise
183
+ nested inside your data is never baked; the container settles.** A loader
184
+ without `loading()` is the BAKE lane (see below): its settled container is
185
+ shell material, exactly like awaited handler data and top-level handle pushes.
186
+
187
+ **`loading()` is NOT the gate for holes — it is the LANE SELECTOR for loader
188
+ data.** A pending promise under any plain `<Suspense>` postpones and is a hole
189
+ at whatever level it suspends — the PHYSICS row needs no `loading()` anywhere
190
+ (the e2e fixture's physics and nested-handle holes sit in a layout with none).
191
+ A route without a loader PPRs on pure promise/Suspense holes. For loaders,
192
+ `loading()` picks the lane: present = the GUARANTEED live lane (masked at
193
+ capture, fresh every serve, immune to fast resolution); absent = the bake lane
194
+ (the container is shell material, nested promises stay live).
195
+
196
+ The three promise positions, side by side:
116
197
 
117
198
  ```typescript
118
- export const urlpatterns = urls(({ path, layout, loader, loading }) => [
119
- // Shell: header, nav, islands, handle pushes. Frozen into the prelude.
120
- layout(ProductShellLayout, () => [
121
- // Hole: the live price. Masked at capture, fresh on every serve.
122
- path("/products/:id", PricePage, { name: "product" }, () => [
123
- loader(LivePriceLoader),
124
- loading(<PriceSkeleton />), // the boundary capture postpones at
125
- ]),
126
- ]),
127
- ]);
199
+ async function Handler(ctx: HandlerContext) {
200
+ const push = ctx.use(MyHandle);
201
+
202
+ push(fetchBadge()); // TOP-LEVEL push: awaited pre-SSR → BAKED
203
+ push({ label: "x", stat: fetchStat() }); // NESTED in container → container baked,
204
+ // stat streams → HOLE (consumer Suspends it)
205
+
206
+ const data = await fetchHeader(ctx); // awaited by the handler → BAKED
207
+
208
+ return (
209
+ <section>
210
+ <Header data={data} /> {/* shell */}
211
+ <StatsPanel promise={fetchStats(ctx)} /> {/* un-awaited prop + own
212
+ <Suspense> + use() → HOLE */}
213
+ </section>
214
+ );
215
+ }
128
216
  ```
129
217
 
130
- **Why a route without `loading()` can never be a hole — even with a fast
131
- loader.** The loading-less branch in `renderSegments` (`src/segment-system.tsx`)
132
- awaits loader data at TREE-BUILD (`await buildLoaderPromise(...)`), above every
133
- Suspense boundary. That await is SHALLOW — it settles only the loader's OUTER
134
- value — but during capture the loader is masked WHOLE: `createMaskedLoaderPromise`
135
- (`src/router/segment-resolution/loader-mask.ts`) hands back a never-resolving
136
- promise for the entire value, so even the outer never settles. The tree-build
137
- await pins the whole tree above `<body>`, the prelude comes back trivial, and
138
- the sanity gate refuses to store. Observable symptom: `x-rango-shell: MISS` on
139
- every request forever, plus a **once-per-key** worker warning you can grep for —
140
- `produced no usable shell … without a route-level loading() boundary`
141
- (`src/rsc/shell-capture.ts`).
142
-
143
- Whole-loader masking is deliberate scar tissue, not a limitation to route
144
- around. Loaders are the ONE lane exempt from the `cookies()`/`headers()` capture
145
- guard — they always run fresh on serve — so running a loader even _partially_
146
- during capture could bake a per-user outer field into the shared shell, breaking
147
- freshness and the security model at once. Finer-grained masking is intentionally
148
- not offered. A hand-rolled `<Suspense>` around a `useLoader()` reader does not
149
- help either: the pin is at the tree-build await, which is _above_ it.
150
-
151
- ## Loader-carried promises: streaming inside a hole
152
-
153
- A hole is not limited to one value. A loader can return its outer value fast and
154
- carry a **nested promise** that settles later; `FlightSerialize` preserves the
155
- `Promise` across the RSC boundary (`src/serialize.ts`), so the client `use()`es
156
- it under its OWN inner `<Suspense>` — a second streaming layer _inside_ the hole.
218
+ ### Choosing the hole mechanism
219
+
220
+ | Your live region is… | Use | Why |
221
+ | ----------------------------------------------- | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
222
+ | loader data, must be fresh EVERY serve | `loader()` + `loading()` (the live lane) | the guaranteed structural hole — masked at capture, fresh every serve, immune to fast resolution |
223
+ | loader data, shell container + live parts | bake-lane loader (no `loading()`): `{ static, dynamic: promise }` | container bakes (snapshot-pinned per shell); nested promises hole at the consumer's `<Suspense>` |
224
+ | handler-fetched real I/O (db, fetch) | un-awaited promise prop + consumer `<Suspense>` + `use()` | no loader needed; real latency postpones by physics |
225
+ | per-segment metadata consumed elsewhere | handle container with a NESTED promise + consumer `<Suspense>` | container is shell, nested value streams — "nesting = liveness" |
226
+ | already-resolved / instant / synchronous values | `loader(() => Promise.resolve(x))` + `loading()` | a raw promise that settles inside the quiet window BAKES; only the live lane guarantees live |
227
+ | none of the above | nothing | it bakes — that is what the shell is for |
228
+
229
+ The physics caveat in one line: promise holes are holes because the I/O is
230
+ genuinely pending at capture. If the value can resolve near-instantly (memory
231
+ read, warmed cache), it may bake into the shell — when liveness must be
232
+ guaranteed rather than probable, use the live lane (`loading()`). The same
233
+ physics governs bake-lane nested promises.
234
+
235
+ ### Handles: "nesting = liveness"
236
+
237
+ - `ctx.use(H)(promise)` — a TOP-LEVEL pushed promise is awaited server-side
238
+ before SSR (`resolvedHandleStream`) and BAKED into the shell. The capture
239
+ gate is held open for the same await, so real latency here is safe (bounded
240
+ by the capture's 5s guard).
241
+ - `ctx.use(H)({ x: promise })` — the container passes through verbatim
242
+ (resolution is shallow); the nested promise streams to the consumer, who must
243
+ `<Suspense>` it. Under capture that boundary postpones — a hole.
244
+
245
+ ### Want a hole for already-resolved data?
246
+
247
+ Put it in a loader: `loader(() => Promise.resolve(x))` + `loading()`. Loaders
248
+ are always the live lane — masked at capture, fresh on every serve — no matter
249
+ how fast the value settles.
250
+
251
+ ### The bake lane: loaders without loading() on THEIR entry
252
+
253
+ A loader on an entry with no renderable `loading()` EXECUTES during capture
254
+ (the capture gate holds open for its real latency, bounded by the 5s guard).
255
+ Its settled container bakes into the prelude; every promise still nested in it
256
+ postpones at the consumer's own `<Suspense>` — a hole. On every HIT the
257
+ capture snapshot's loader family overlays the recorded container onto the
258
+ fresh run, so the payload matches the frozen prelude byte-for-byte while the
259
+ nested promises run fresh. The return shape is the declaration:
157
260
 
158
261
  ```typescript
159
- // loader — outer resolves fast; the nested promise settles later
160
- export const StreamLoader = createLoader(async () => {
161
- const pendingData = new Promise<string>((r) =>
162
- setTimeout(() => r("slow inner value"), 300),
163
- );
164
- return { label: "fast outer value", pendingData };
262
+ export const StorefrontContextLoader = createLoader(async (ctx) => {
263
+ const config = await loadSiteConfig(ctx.params.locale); // bakes (pinned per shell)
264
+ return {
265
+ config, // shell material
266
+ basket: fetchBasket(ctx), // hole — consumer <Suspense>s it, fresh per request
267
+ };
165
268
  });
166
269
  ```
167
270
 
168
- ```tsx
169
- // consumer (client): use() the nested promise under an INNER Suspense
170
- "use client";
171
- import { Suspense, use } from "react";
172
- import { useLoader } from "@rangojs/router/client";
271
+ The lane is decided PER TREE NODE, at the entry that REGISTERS the loaders —
272
+ `loading()` on a CHILD route does not change a parent layout's lane, and
273
+ `loading()` IS valid on layout and parallel entries, not just routes.
274
+
275
+ Three hard edges (each e2e/unit-pinned):
276
+
277
+ - **Identity refuses.** `cookies()`/`headers()` inside a bake-lane loader
278
+ throws during capture and the capture REFUSES (deterministic, once-per-key
279
+ warned) — identity can never bake into the shared shell. Give that loader's
280
+ entry `loading()` (the live lane is exempt) or move the identity-dependent
281
+ part into a nested promise.
282
+ - **A rejecting bake-lane loader refuses.** Error UI never bakes.
283
+ - **Baked containers show CAPTURE-time data** for the shell's lifetime on
284
+ document GETs (client navigations stay fresh — axis 1). That IS the bake
285
+ lane's meaning; if a value must be fresh on every serve, it belongs on the
286
+ live lane (`loading()`) or in a nested promise.
287
+
288
+ ### The layout-with-loaders playbook (the storefront case)
289
+
290
+ The most common real-app shape: an app-wide layout registers per-user loaders
291
+ (session context, basket, wishlist) and the page under it declares `ppr`.
292
+ Those loaders are on the BAKE lane (no `loading()` on the layout), so the page
293
+ captures and HITs — the question is which parts of their data should bake vs
294
+ stay live. Your levers, in order of preference:
295
+
296
+ 1. **Shape the return value.** Shared/config data returns as plain values
297
+ (bakes, pinned per shell); per-request data returns as NESTED promises
298
+ consumed under the widget's own `<Suspense>` (live holes). No `loading()`,
299
+ no restructuring. One wall: a bake-lane loader that reads
300
+ `cookies()`/`headers()` refuses the capture — identity belongs in a nested
301
+ promise or on the live lane.
302
+ 2. **Do NOT put `loading()` on the layout itself** — that flips the WHOLE
303
+ layout to the live lane and the LoaderBoundary fallback wraps the layout's
304
+ ENTIRE subtree: chrome (header, nav, footer) falls out of the shell into
305
+ the skeleton. Technically PPR, practically pointless.
306
+ 3. **Guaranteed-fresh widgets: a parallel slot with its OWN `loading()`.**
307
+ Parallel-owned loaders get their own per-slot boundary (`fresh.ts` tags
308
+ them with the slot's loading; `segment-system.tsx` builds a per-slot
309
+ LoaderBoundary), so the chrome bakes into the shell and each widget is an
310
+ independent, widget-sized hole:
311
+
312
+ ```typescript
313
+ layout(StoreChrome, () => [
314
+ // chrome renders NO loader data itself — it bakes into the shell
315
+ parallel({ "@basket": BasketBadge }, () => [
316
+ loader(BasketLoader),
317
+ loading(<BadgeSkeleton />), // hole the size of a badge, not a page
318
+ ]),
319
+ parallel({ "@wishlist": WishlistBadge }, () => [
320
+ loader(WishlistLoader),
321
+ loading(<BadgeSkeleton />),
322
+ ]),
323
+ path("/", HomePage, { name: "home", ppr: true }),
324
+ ]),
325
+ ```
326
+
327
+ Slot-owned loaders are masked at capture and GUARANTEED fresh per serve —
328
+ use this where the bake lane's physics (a fast resolve bakes) or pinning
329
+ (capture-time data for the shell's lifetime) is not acceptable, at the cost
330
+ of a widget-sized fallback in the shell.
331
+
332
+ 4. **Shared layout data can also leave the loader lane entirely**: an
333
+ un-awaited handler promise under the consumer's `<Suspense>` (a physics
334
+ hole) or `cache()`/`"use cache"` to bake it with tag-invalidation.
335
+
336
+ The identity rule, stated once: per-user data on a PPR page lives in a NESTED
337
+ promise (a hole, fresh per request) or behind `loading()` (the live lane).
338
+ Reading `cookies()`/`headers()` where the value would bake — handler shell
339
+ material or a bake-lane container — refuses the capture by construction.
173
340
 
174
- function Inner({ promise }: { promise: Promise<string> }) {
175
- return <span>{use(promise)}</span>;
176
- }
341
+ ## Execution matrix
177
342
 
178
- export function StreamView({ loader }: { loader: LoaderDefinition<Data> }) {
179
- const { data } = useLoader(loader); // resolves the OUTER value
180
- return (
181
- <>
182
- <div>{data.label}</div>
183
- <Suspense fallback={<div>loading inner…</div>}>
184
- <Inner promise={data.pendingData} /> {/* streams the nested value */}
185
- </Suspense>
186
- </>
187
- );
188
- }
189
- ```
343
+ | Phase | MISS (foreground) | Background capture | HIT (foreground) |
344
+ | ---------------- | ---------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------- |
345
+ | Middleware chain | runs (full) | **NOT re-run** — inherits the request's post-middleware context | runs (full) — commit point is after it |
346
+ | `router.match` | runs | re-runs under a derived context | runs (behind the flushed prelude) |
347
+ | Handlers | run | run on UNCACHED segments; `cache()`d segments replay (mixed-chain) | run (same mixed-chain rules as any render) |
348
+ | Loaders | run **fresh** | LIVE lane (`loading()`): MASKED; BAKE lane: execute + snapshot-pin | run **fresh** (bake containers overlaid from the snapshot) |
349
+ | Flight render | full | full | full (hydration needs the whole payload — no Flight resume) |
350
+ | HTML production | full fizz | `prerender` + abort → prelude + postponed | `resume` only the holes — O(paths to holes) |
351
+ | Shell store | schedules a bg capture | `putShell(key, …)` | `getShell(key)`; a stale/SWR hit also schedules a recapture |
352
+ | Prelude bytes | — | — | flushed FIRST, before segment resolution starts |
353
+
354
+ Middleware is not re-run during capture because it already ran for the
355
+ triggering request — the capture's derived context inherits the
356
+ post-middleware state (`ctx` variables included, which is what makes
357
+ middleware-derived shell content photograph correctly). Guarding is
358
+ serve-time: the commit point runs the full chain on EVERY serve.
359
+
360
+ Because handlers on uncached segments EXECUTE during capture — and BAKE-lane
361
+ loaders now do too — the `cookies()`/`headers()` capture guard is load-bearing:
362
+ those reads THROW during a capture render (`assertNotInsideShellCapture`), so
363
+ identity can never leak into a shared shell through them. Live-lane loaders
364
+ (behind `loading()`) are exempt: masked at capture, they never run there.
365
+
366
+ ## allReady: the SEO/bot story
367
+
368
+ `ssr: { resolveStreaming: ... }` returning `"allReady"` (e.g. for bot user
369
+ agents) bypasses PPR entirely — the request gets one complete, fully-buffered
370
+ axis-1 document. Crawlers that dislike streamed shells get a finished page;
371
+ regular users get the streamed shell. No configuration interaction: allReady
372
+ wins.
190
373
 
191
- Route shape is unchanged: `loader(StreamLoader)` + `loading(<Skeleton />)`. On a
192
- **HIT** the resume streams three progressive layers in one response body:
374
+ ## Security
193
375
 
194
- 1. the cached shell prelude (layout + the `loading()` fallback) — flushed
195
- instantly, before any render work;
196
- 2. the outer loader value fills the hole, carrying the inner `<Suspense>`
197
- fallback;
198
- 3. the nested-promise inner value + React's `$RC` boundary stitch.
376
+ Shell caching shares one shell per host+URL across all users:
199
377
 
200
- Capture never sees any of this: the loader is masked, so the whole subtree
201
- postpones at `loading()` and the nested promise costs nothing at capture time.
202
- That is what makes loader-carried promises DETERMINISTIC — contrast the
203
- handler-passed promise below, which races the capture's quiet window. The
204
- three-layer timeline is pinned in dev + production e2e
205
- (`tests/cloudflare-basic/e2e/ppr-shell.test.ts`, `e2e/shell-cache.test.ts`).
378
+ **(a) Access control is sound by construction.** The commit point is after ALL
379
+ middleware on every serve. A 401/redirect short-circuit returns before any
380
+ shell byte.
206
381
 
207
- One nuance: a loader with a `cache(...)` config deep-settles on write, so a
208
- loader-cache HIT delivers the inner promise already resolved.
382
+ **(b) Identity can't leak via cookies/headers.** `cookies()` and `headers()`
383
+ THROW during the background capture render — in handlers AND in bake-lane
384
+ loaders (whose containers would bake). A shell that reads them is
385
+ PPR-ineligible by construction; the live lane (`loading()`) stays exempt.
209
386
 
210
- ## live(): a deterministic hole for any boundary
387
+ **(c) Residual hazard — middleware-derived per-user state.** A `ctx` variable
388
+ set by an upstream auth middleware and rendered by shell material is
389
+ photographed into the SHARED shell (the capture inherits post-middleware
390
+ state). That is scope fidelity working as designed — for shared values. If the
391
+ value is per-user: shell-cache only public/shared pages, put per-user content
392
+ in loaders, or key per variant at the CDN tier.
211
393
 
212
- `loading()` makes a route LOADER a hole. `live()` makes ANY boundary a hole —
213
- including one whose data is already resolved. During the background capture
214
- `live()` behaves exactly like the loader mask: it returns a never-settling
215
- promise, so the consuming `<Suspense>` postpones and the prelude freezes only the
216
- fallback. On the serve pass (and on the client) it is a passthrough — the thunk
217
- runs, or the promise passes through unchanged.
394
+ ## What always stays on axis 1
218
395
 
219
- ```tsx
220
- import { Suspense } from "react";
221
- import { live } from "@rangojs/router";
222
-
223
- async function Greeting() {
224
- // Promise.resolve(...) would normally SETTLE during capture and bake into the
225
- // shared shell. live() holds it out, so this boundary postpones instead.
226
- const name = await live(() => Promise.resolve(currentUserName()));
227
- return <span>Hi {name}</span>;
228
- }
396
+ Non-GET, RSC/partial/action/loader fetches, per-request CSP nonce,
397
+ `streamMode: "allReady"`, redirects, 404s, error renders, routes without `ppr`,
398
+ and any store without the shell family. A stored shell is invalidated when
399
+ `React.version` changes (postponed state is build-coupled), so deploys
400
+ self-heal via recapture.
401
+
402
+ The per-request CSP nonce guarantee covers BOTH ways a nonce arrives — the
403
+ `createRouter({ nonce })` provider AND a direct `ctx.set(nonce, value)` token
404
+ write in middleware (the `nonce` token from `@rangojs/router`). Either way the
405
+ nonce ends up rendered into the document (`useNonce()` puts the provider nonce
406
+ on every nonced script/style/meta; a token nonce is rendered by whatever app
407
+ code reads `ctx.get(nonce)`), so a shell shared per host+URL cannot bake it
408
+ without freezing one request's nonce for every visitor (the browser's CSP
409
+ would then reject the frozen nonce for all but the capture request). The serve
410
+ gate reads the token off the post-middleware request variables at the commit
411
+ point (which runs after the whole middleware chain), so a middleware-set nonce
412
+ blocks capture the same as a provider one. Because the route DECLARED `ppr`
413
+ but cannot be honored, it logs a once-per-key worker warning (same
414
+ declared-intent-cannot-be-honored doctrine as the missing-store warning) and
415
+ serves pure axis 1 with no `x-rango-shell` header. An undeclared route stays
416
+ silent.
417
+
418
+ ### The proper way to supply a nonce
419
+
420
+ `createRouter({ nonce })` is the canonical path — supply the nonce THERE, not
421
+ via a token write. The provider value is threaded into the router's own SSR
422
+ machinery: `NonceContext`/`useNonce()`, automatic nonce attributes on
423
+ `<Scripts />` and `<MetaTags />` output, and the inlined Flight payload
424
+ scripts. It ALSO sets the `nonce` token, so `ctx.get(nonce)` works in
425
+ middleware and handlers for the CSP response header. A direct
426
+ `ctx.set(nonce, value)` write in middleware is app-managed only: the router
427
+ resolves its SSR nonce BEFORE middleware runs, so a token-set value is
428
+ readable via `ctx.get(nonce)` and gates PPR (this section), but the router
429
+ will NOT apply it to its own scripts — `useNonce()` stays undefined and the
430
+ Flight payload scripts carry no nonce, which a nonce-only `script-src` policy
431
+ would then block. If you need a per-request nonce, use the provider; reserve
432
+ the token for READING the value.
433
+
434
+ ## Options: PartialPrerenderProps
229
435
 
230
- // under the frozen shell:
231
- // <Suspense fallback={<span>…</span>}>
232
- // <Greeting />
233
- // </Suspense>
436
+ ```typescript
437
+ path("/products/:id", Page, { name: "product", ppr: true }, use);
438
+ path(
439
+ "/products/:id",
440
+ Page,
441
+ { name: "product", ppr: { ttl: 600, swr: 120, tags: ["catalog"] } },
442
+ use,
443
+ );
234
444
  ```
235
445
 
236
- Two forms:
237
-
238
- - **Thunk (preferred): `live(() => value)`** — during capture the thunk NEVER
239
- runs (no fetch, no cost); the boundary is a pure hole.
240
- - **Value: `live(promise)`** — the work already fired before `live()` saw it, so
241
- during capture the real promise is DISCARDED and a hole returned in its place.
242
- Use it only when you already hold the promise; prefer the thunk otherwise.
243
-
244
- `live()` is what makes a resolved value a hole at all: a bare `Promise.resolve(x)`
245
- under `<Suspense>` settles inside the capture's quiet window and freezes into the
246
- shell. It is also the escape hatch for the passed-promise trap below. The
247
- capture/serve split is pinned in dev + production e2e (the "live() makes a
248
- resolved promise a HOLE" case in `tests/cloudflare-basic/e2e/ppr-shell.test.ts`
249
- and `e2e/shell-cache.test.ts`).
250
-
251
- ## Passed promises are not holes
252
-
253
- The pattern that looks like a hole but is not: a **handler** creates a promise
254
- and passes it as a prop to a client component that `use()`s it inside its own
255
- `<Suspense>`. Only route **loaders** (and `live()`) are masked at capture — a
256
- handler and any promise it creates EXECUTE during the background capture render.
257
- What happens next is decided by the promise's LATENCY CLASS against the
258
- capture's quiet window (task-quantized: it closes a couple of macrotask hops
259
- after the last Flight byte, not on a wall clock). Both sides are reliable —
260
- just in opposite directions:
261
-
262
- - Resolved or microtask-resolvable (`Promise.resolve`, a warm in-memory read):
263
- reliably SHELL, every capture — it settles in the same window as plain JSX.
264
- If the value is per-request, that is a deterministic bug: frozen into the
265
- shared shell until TTL (hydration repairs it from the fresh payload —
266
- degraded, not corrupt, but a drift you shipped).
267
- - Genuinely pending real I/O: reliably a HOLE — it cannot win a task-quantized
268
- window. Resume fills it at serve. The capture still paid the promise's
269
- execution cost and side effects, though. The only nondeterministic sliver
270
- left is I/O completing within ~2 event-loop turns of the shell going quiet
271
- — freakishly fast, self-healing via TTL/recapture, and only reachable by
272
- code that declared no intent.
273
-
274
- An async HANDLER (a streamed `loading()` handler returning a promise) is the
275
- deliberate opposite: it is tracked in the handle store, and capture WAITS for
276
- handlers to settle before aborting — handler output is shell material by
277
- design, never a hole.
278
-
279
- Verdict: a promise's latency class picks its side — you can safely assume a
280
- genuinely pending, unresolved promise becomes a hole. But that decision was
281
- made by latency, not by you. Wherever intent and latency could disagree —
282
- per-request data that might get cache-fast, a value that must never appear in
283
- the shared shell — say it in code: **`live()`** for a guaranteed hole (masked
284
- at capture like a loader; prefer the thunk form so nothing runs during
285
- capture), a loader behind `loading()` for route-level live data (zero capture
286
- cost, and its nested promises stream too, per above), a plain `await` for
287
- shell-safe deterministic data. Unwrapped promises are for the cases where
288
- either outcome is acceptable.
289
-
290
- Note on `useLoader()`: it never observes pending data. Inside a `loading()`
291
- route, `LoaderBoundary` resolves the loader promise INSIDE its own Suspense
292
- before children render, so `useLoader().data` (the OUTER value) is always
293
- resolved; `isLoading` is client-side refetch state, not a server pending signal.
294
- A nested promise on that data is separate — it streams under the consumer's own
295
- inner `<Suspense>` (above). Multiple holes per page (several `loading()`
296
- routes/parallels) are fine: resume fills every postponed boundary.
446
+ | Field | Default | Notes |
447
+ | ------ | ------- | -------------------------------------------------------------------------------------------------- |
448
+ | `ttl` | `300` | shell freshness window in seconds (`ppr: true` uses the default) |
449
+ | `swr` | — | stale window: serve the stale shell + background recapture |
450
+ | `tags` | — | operational tags UNIONED with the tags the capture render auto-collects — see "Invalidation" below |
297
451
 
298
- ## Execution matrix
452
+ The shell store is always the app-level `createRouter({ cache })` store; the
453
+ default key is `${host}${pathname}${sortedSearch}:shell` (host-scoped so
454
+ multi-tenant shells never collide).
299
455
 
300
- Three passes, three different cost profiles. The foreground request is never
301
- blocked on the background capture.
302
-
303
- | Phase | MISS (foreground) | Background capture | HIT (foreground) |
304
- | ---------------- | ---------------------- | --------------------------------------------------------------- | ----------------------------------------------------------- |
305
- | Middleware chain | runs (full) | **NOT re-run** — inherits the request's post-middleware context | runs (full) |
306
- | `router.match` | runs | re-runs under a derived context | runs |
307
- | Handlers | run | run | run |
308
- | Loaders | run **fresh** | **MASKED** (never execute) | run **fresh** |
309
- | Flight render | full | full | full (hydration needs the whole payload — no Flight resume) |
310
- | HTML production | full fizz | `prerender` + abort → prelude + postponed | `resume` only the holes — O(paths to holes) |
311
- | Shell store | schedules a bg capture | `putShell(key, …)` | `getShell(key)`; a stale/SWR hit also schedules a recapture |
312
- | Prelude bytes | — | — | prepended by the middleware before the resumed body |
313
-
314
- Loader freshness under PPR is **identical to axis 1**: loaders — the outer value
315
- AND any nested promise — run fresh on every request, including HITs. Only the
316
- HTML _around_ the hole came from cache. Background capture is scheduled via
317
- `runBackground` (`waitUntil` on workerd, fire-and-forget in Node dev), so it
318
- never delays the served response. Re-deriving through `router.match()` rather
319
- than a second `next()` is what keeps middleware from running twice
320
- (`src/rsc/shell-capture.ts`).
456
+ ## Invalidation: tags vs revalidate()
321
457
 
322
- ## Security
458
+ `updateTag()`/`revalidateTag()` is the ONLY lever that changes the frozen shell
459
+ HTML; `revalidate()` is a DATA lever that never touches it.
323
460
 
324
- Shell caching shares one shell per host+URL across all users, so its safety
325
- rests on three things — the first two are enforced, the third is on you.
326
-
327
- **(a) Access control is sound.** The middleware runs on every request, including
328
- HITs, and composition is **marker-gated**: the middleware prepends the cached
329
- prelude ONLY when the live response carries the internal `x-rango-shell-resumed`
330
- marker (`src/cache/shell-cache.ts`). Any middleware short-circuit — a 401, a
331
- redirect, a 404 — never resumes, so it never carries the marker and passes
332
- through **untouched**, never composed with a cached shell. Put auth middleware
333
- upstream of the shell middleware and unauthorized users get their 401/redirect,
334
- not someone else's cached page.
335
-
336
- **(b) Identity can't leak into a shared shell.** `cookies()` and `headers()`
337
- THROW during the background capture render (`assertNotInsideShellCapture`,
338
- `src/server/cookie-store.ts`), the same guard family as `"use cache"` and
339
- `cache()`. A shell that reads cookies is PPR-ineligible by construction.
340
-
341
- **(c) Residual hazard — state it plainly.** Middleware-derived per-user state is
342
- NOT guarded: a `ctx` variable set by an upstream auth middleware and read by a
343
- handler WITHOUT `cookies()`/`headers()` is invisible to guard (b). The background
344
- capture inherits the triggering request's post-middleware context and bakes that
345
- state into the shared shell. Mitigations, in order of preference:
346
-
347
- - shell-cache only **public/shared** pages;
348
- - put all per-user content in **loaders** (the enforced, masked lane);
349
- - `isEnabled` to disable the middleware for authenticated sessions;
350
- - `keyGenerator` to add a per-variant dimension (it owns the FULL key identity,
351
- including host — see Options).
352
-
353
- Shell content that still varies per request degrades to a hydration repair
354
- (bounded by TTL/SWR), not corruption — but it is a smell. `cache()` the route so
355
- the same replayed segments feed the captured shell and every resumed render.
461
+ A captured shell auto-carries the UNION of the non-loader tags recorded during
462
+ the capture render — every `cacheTag(...)` from a `"use cache"` function or
463
+ `cache()` segment that ran as shell material. Loader tags never attach (the
464
+ holes are already live). `ppr.tags` adds operational tags the render cannot
465
+ know (a tenant id, a deploy marker).
356
466
 
357
- ## What always stays on axis 1
358
-
359
- Non-GET, RSC/partial/action/loader fetches, per-request CSP nonce,
360
- `streamMode: "allReady"`, redirects, 404s, error renders, and any store without
361
- the shell family. A stored shell is also invalidated when `React.version`
362
- changes (postponed state is build-coupled), so deploys self-heal via recapture.
363
-
364
- First byte on a HIT does not wait on the shell render or the loader. Hydration
365
- uses the fresh per-request Flight payload, so interactivity is unaffected.
366
-
367
- ## Partial navigations
368
-
369
- Soft navigations (`_rsc_partial`) bypass this middleware, and that is by
370
- design, not a gap: a partial response has no HTML tier — no fizz render to
371
- skip, which is the entire cost document-PPR eliminates. On a `cache()` route a
372
- partial navigation already delivers the PPR contract at the data tier:
373
- replayed cached segments flush immediately (in-memory after the store read),
374
- loaders run fresh and stream their rows into the same response, and the
375
- client shows `loading()` fallbacks until they arrive. Shell instantly, live
376
- holes revived — same semantics, different wire format.
377
-
378
- For warm-navigation latency, combine `cache()` with prefetching: a prefetched
379
- partial payload is the client-side analogue of the shell cache, and loaders
380
- still stream fresh on arrival. Document-PPR covers the cold full-document
381
- load; prefetch + `cache()` covers navigation.
382
-
383
- What partial navigations do NOT get is the byte-level shortcut: the worker
384
- still deserializes stored segments and re-encodes the Flight payload per
385
- request. Serving a stored Flight byte-prefix and appending fresh loader rows
386
- would require hand-managed row-ID alignment — React has no Flight-side
387
- resume (no postponed-state equivalent exists for Flight) — and is a deferred
388
- optimization, tracked in the design doc's out-of-scope list.
389
-
390
- ## Options
391
-
392
- | Option | Default | Notes |
393
- | -------------- | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
394
- | `store` | app-level `_cacheStore` | must implement `getShell`/`putShell`; the capture writes to the SAME store the middleware reads |
395
- | `ttlSeconds` | `300` | shell freshness window |
396
- | `swrSeconds` | — | stale window: serve stale + background recapture |
397
- | `keyGenerator` | `${host}${pathname}${sortedSearch}` | custom keys own the FULL identity — include the host unless the store is provably single-host (multi-tenant shells must never collide) |
398
- | `isEnabled` | — | per-request opt-out predicate (e.g. disable for authed sessions) |
399
- | `skipPaths` | `[]` | path-prefix opt-out |
400
- | `debug` | `false` | HIT/MISS/CAPTURED logging |
467
+ | Lever | Reaches the frozen shell? | Reaches the holes? |
468
+ | --------------------------------------------- | ------------------------------------------------------------- | --------------------------------------------------- |
469
+ | `updateTag` / `revalidateTag` on a SHELL tag | YES — drops the shell → MISS → recapture | n/a (holes are already live) |
470
+ | `updateTag` / `revalidateTag` on a LOADER tag | no — loader tags never attach to a shell | drops that loader's cached value (if it `cache()`s) |
471
+ | `revalidate()` (named revalidation contract) | **no** — re-runs segments/loaders for the PAYLOAD, never HTML | yes — the hole re-renders with fresh data |
401
472
 
402
473
  ## Pitfalls
403
474
 
404
- - **Loader route without `loading()`**: eternal MISS plus a once-per-key
405
- console warning. Move shell material to a layout and add `loading()` (see "The
406
- hole contract").
407
- - **Handler-passed promise for live data**: nondeterministic race, drift into
408
- the shared shell. Use a loader behind `loading()`, or wrap it in `live()`.
409
- - **`live()` value form (`live(promise)`)**: the work already fired before
410
- `live()` saw it, so during capture the promise still runs and its side effects
411
- still happen — only its result is held out of the shell. Prefer the thunk form
412
- `live(() => …)` so nothing executes during capture.
413
- - **Per-user state via `ctx` variables**: not guarded — see Security (c).
414
- - **Stacking with `/document-cache`**: pick one per route. The document cache
415
- would cache the composite — correct output, but it makes shell caching
416
- redundant there.
475
+ - **A bake-lane loader that reads `cookies()`/`headers()`**: the capture is
476
+ REFUSED (deterministic, once-per-key warned) — the route stays on axis 1.
477
+ Move identity into a nested promise or behind `loading()`.
478
+ - **A bake-lane container that must be fresh per document GET**: it is
479
+ snapshot-pinned for the shell's lifetime by design. Use the live lane
480
+ (`loading()`) or a nested promise instead.
481
+ - **A bake-lane loader slower than the capture guard (~5s)**: the capture
482
+ cannot hold for it — eternal MISS with the once-per-key warning.
483
+ - **Per-user value in shell material**: baked into the shared shell —
484
+ deterministically, not by race (handler promises deep-settle at the ring-3
485
+ write on cached chains; awaited/resolved values bake everywhere). Put
486
+ per-user data in a loader.
487
+ - **Theme on a HIT is capture-then-corrected**: the resume tree replays the
488
+ CAPTURE's `initialTheme` (resume requires it to match the frozen prelude);
489
+ the visitor's cookie theme is applied pre-paint by the FOUC script and
490
+ re-synced post-mount by ThemeProvider. Nothing to configure — but a themed
491
+ component in the shell may briefly render the captured theme's markup before
492
+ the post-mount re-sync.
493
+ - **Shell shows CAPTURE-time data for the shell's lifetime**: a `cache()`/`"use
494
+ cache"` value baked into the shell is PINNED at capture (the capture data
495
+ snapshot) and replayed on every HIT, so the shell stays byte-identical to the
496
+ frozen prelude even after that cache entry expires, gets recomputed, or is
497
+ tag-invalidated. This is deliberate — parity beats freshness inside the shell.
498
+ If a shell region needs to be fresh, put it under a hole — `loading()` for
499
+ loader data, or an un-awaited promise under the consumer's `<Suspense>`
500
+ (holes are never pinned) — or make the SHELL itself invalidatable by adding
501
+ the tag to `ppr.tags`. Ring-1/ring-3 tag invalidation does NOT drop the shell.
502
+ - **Uncached nondeterminism in the shell is a hydration hazard**: a raw
503
+ `Date.now()` / `Math.random()` / uncached `fetch` rendered directly in shell
504
+ material (outside any cache ring) drifts between capture and hit and the
505
+ snapshot CANNOT pin it — it was never a cache read. It will mismatch the frozen
506
+ prelude and detonate hydration. Wrap it in `cache()`/`"use cache"` (then it is
507
+ pinned) or move it under a hole (`loading()`, or a pending-promise
508
+ `<Suspense>` region).
509
+ - **Stacking with `/document-cache`**: pick one per route — the document cache
510
+ would cache the composite.
417
511
  - **Dev + HMR**: works, but edits produce stale shells until TTL/recapture.
418
- - A cold-worker capture can occasionally abort mid-render; it is logged as
419
- retryable and the next request recaptures — self-healing, not an error.
512
+ - **Dev cold-start cadence**: expect `MISS -> (in-place retry) -> HIT`. A
513
+ refused capture is negatively cached with an exponential window (1s doubling
514
+ to a 60s cap), so declaring `ppr` on an ineligible route never re-renders it
515
+ on every request.
516
+ - **HIT status is committed at the flush**: a failing hole cannot become a
517
+ 500/redirect after the first shell byte — error UI renders inline via
518
+ Suspense/error boundaries (the same property any streamed SSR page has after
519
+ its shell flushes).
420
520
 
421
521
  ## Related
422
522