@rangojs/router 0.0.0-experimental.002d056c

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 (305) hide show
  1. package/AGENTS.md +9 -0
  2. package/README.md +899 -0
  3. package/dist/bin/rango.js +1606 -0
  4. package/dist/vite/index.js +5153 -0
  5. package/package.json +177 -0
  6. package/skills/breadcrumbs/SKILL.md +250 -0
  7. package/skills/cache-guide/SKILL.md +262 -0
  8. package/skills/caching/SKILL.md +253 -0
  9. package/skills/composability/SKILL.md +172 -0
  10. package/skills/debug-manifest/SKILL.md +112 -0
  11. package/skills/document-cache/SKILL.md +182 -0
  12. package/skills/fonts/SKILL.md +167 -0
  13. package/skills/hooks/SKILL.md +704 -0
  14. package/skills/host-router/SKILL.md +218 -0
  15. package/skills/intercept/SKILL.md +313 -0
  16. package/skills/layout/SKILL.md +310 -0
  17. package/skills/links/SKILL.md +239 -0
  18. package/skills/loader/SKILL.md +596 -0
  19. package/skills/middleware/SKILL.md +339 -0
  20. package/skills/mime-routes/SKILL.md +128 -0
  21. package/skills/parallel/SKILL.md +305 -0
  22. package/skills/prerender/SKILL.md +643 -0
  23. package/skills/rango/SKILL.md +118 -0
  24. package/skills/response-routes/SKILL.md +411 -0
  25. package/skills/route/SKILL.md +385 -0
  26. package/skills/router-setup/SKILL.md +439 -0
  27. package/skills/tailwind/SKILL.md +129 -0
  28. package/skills/theme/SKILL.md +79 -0
  29. package/skills/typesafety/SKILL.md +623 -0
  30. package/skills/use-cache/SKILL.md +324 -0
  31. package/src/__internal.ts +273 -0
  32. package/src/bin/rango.ts +321 -0
  33. package/src/browser/action-coordinator.ts +97 -0
  34. package/src/browser/action-response-classifier.ts +99 -0
  35. package/src/browser/event-controller.ts +899 -0
  36. package/src/browser/history-state.ts +80 -0
  37. package/src/browser/index.ts +18 -0
  38. package/src/browser/intercept-utils.ts +52 -0
  39. package/src/browser/link-interceptor.ts +141 -0
  40. package/src/browser/logging.ts +55 -0
  41. package/src/browser/merge-segment-loaders.ts +134 -0
  42. package/src/browser/navigation-bridge.ts +638 -0
  43. package/src/browser/navigation-client.ts +261 -0
  44. package/src/browser/navigation-store.ts +806 -0
  45. package/src/browser/navigation-transaction.ts +297 -0
  46. package/src/browser/network-error-handler.ts +61 -0
  47. package/src/browser/partial-update.ts +582 -0
  48. package/src/browser/prefetch/cache.ts +206 -0
  49. package/src/browser/prefetch/fetch.ts +145 -0
  50. package/src/browser/prefetch/observer.ts +65 -0
  51. package/src/browser/prefetch/policy.ts +48 -0
  52. package/src/browser/prefetch/queue.ts +128 -0
  53. package/src/browser/rango-state.ts +112 -0
  54. package/src/browser/react/Link.tsx +368 -0
  55. package/src/browser/react/NavigationProvider.tsx +413 -0
  56. package/src/browser/react/ScrollRestoration.tsx +94 -0
  57. package/src/browser/react/context.ts +59 -0
  58. package/src/browser/react/filter-segment-order.ts +11 -0
  59. package/src/browser/react/index.ts +52 -0
  60. package/src/browser/react/location-state-shared.ts +162 -0
  61. package/src/browser/react/location-state.ts +107 -0
  62. package/src/browser/react/mount-context.ts +37 -0
  63. package/src/browser/react/nonce-context.ts +23 -0
  64. package/src/browser/react/shallow-equal.ts +27 -0
  65. package/src/browser/react/use-action.ts +218 -0
  66. package/src/browser/react/use-client-cache.ts +58 -0
  67. package/src/browser/react/use-handle.ts +162 -0
  68. package/src/browser/react/use-href.tsx +40 -0
  69. package/src/browser/react/use-link-status.ts +135 -0
  70. package/src/browser/react/use-mount.ts +31 -0
  71. package/src/browser/react/use-navigation.ts +99 -0
  72. package/src/browser/react/use-params.ts +65 -0
  73. package/src/browser/react/use-pathname.ts +47 -0
  74. package/src/browser/react/use-router.ts +63 -0
  75. package/src/browser/react/use-search-params.ts +56 -0
  76. package/src/browser/react/use-segments.ts +171 -0
  77. package/src/browser/response-adapter.ts +73 -0
  78. package/src/browser/rsc-router.tsx +464 -0
  79. package/src/browser/scroll-restoration.ts +397 -0
  80. package/src/browser/segment-reconciler.ts +216 -0
  81. package/src/browser/segment-structure-assert.ts +83 -0
  82. package/src/browser/server-action-bridge.ts +667 -0
  83. package/src/browser/shallow.ts +40 -0
  84. package/src/browser/types.ts +547 -0
  85. package/src/browser/validate-redirect-origin.ts +29 -0
  86. package/src/build/generate-manifest.ts +438 -0
  87. package/src/build/generate-route-types.ts +36 -0
  88. package/src/build/index.ts +35 -0
  89. package/src/build/route-trie.ts +265 -0
  90. package/src/build/route-types/ast-helpers.ts +25 -0
  91. package/src/build/route-types/ast-route-extraction.ts +98 -0
  92. package/src/build/route-types/codegen.ts +102 -0
  93. package/src/build/route-types/include-resolution.ts +411 -0
  94. package/src/build/route-types/param-extraction.ts +48 -0
  95. package/src/build/route-types/per-module-writer.ts +128 -0
  96. package/src/build/route-types/router-processing.ts +479 -0
  97. package/src/build/route-types/scan-filter.ts +78 -0
  98. package/src/build/runtime-discovery.ts +231 -0
  99. package/src/cache/background-task.ts +34 -0
  100. package/src/cache/cache-key-utils.ts +44 -0
  101. package/src/cache/cache-policy.ts +125 -0
  102. package/src/cache/cache-runtime.ts +338 -0
  103. package/src/cache/cache-scope.ts +382 -0
  104. package/src/cache/cf/cf-cache-store.ts +982 -0
  105. package/src/cache/cf/index.ts +29 -0
  106. package/src/cache/document-cache.ts +369 -0
  107. package/src/cache/handle-capture.ts +81 -0
  108. package/src/cache/handle-snapshot.ts +41 -0
  109. package/src/cache/index.ts +44 -0
  110. package/src/cache/memory-segment-store.ts +328 -0
  111. package/src/cache/profile-registry.ts +73 -0
  112. package/src/cache/read-through-swr.ts +134 -0
  113. package/src/cache/segment-codec.ts +256 -0
  114. package/src/cache/taint.ts +98 -0
  115. package/src/cache/types.ts +342 -0
  116. package/src/client.rsc.tsx +85 -0
  117. package/src/client.tsx +601 -0
  118. package/src/component-utils.ts +76 -0
  119. package/src/components/DefaultDocument.tsx +27 -0
  120. package/src/context-var.ts +86 -0
  121. package/src/debug.ts +243 -0
  122. package/src/default-error-boundary.tsx +88 -0
  123. package/src/deps/browser.ts +8 -0
  124. package/src/deps/html-stream-client.ts +2 -0
  125. package/src/deps/html-stream-server.ts +2 -0
  126. package/src/deps/rsc.ts +10 -0
  127. package/src/deps/ssr.ts +2 -0
  128. package/src/errors.ts +365 -0
  129. package/src/handle.ts +135 -0
  130. package/src/handles/MetaTags.tsx +246 -0
  131. package/src/handles/breadcrumbs.ts +66 -0
  132. package/src/handles/index.ts +7 -0
  133. package/src/handles/meta.ts +264 -0
  134. package/src/host/cookie-handler.ts +165 -0
  135. package/src/host/errors.ts +97 -0
  136. package/src/host/index.ts +53 -0
  137. package/src/host/pattern-matcher.ts +214 -0
  138. package/src/host/router.ts +352 -0
  139. package/src/host/testing.ts +79 -0
  140. package/src/host/types.ts +146 -0
  141. package/src/host/utils.ts +25 -0
  142. package/src/href-client.ts +222 -0
  143. package/src/index.rsc.ts +233 -0
  144. package/src/index.ts +277 -0
  145. package/src/internal-debug.ts +11 -0
  146. package/src/loader.rsc.ts +89 -0
  147. package/src/loader.ts +64 -0
  148. package/src/network-error-thrower.tsx +23 -0
  149. package/src/outlet-context.ts +15 -0
  150. package/src/outlet-provider.tsx +45 -0
  151. package/src/prerender/param-hash.ts +37 -0
  152. package/src/prerender/store.ts +185 -0
  153. package/src/prerender.ts +463 -0
  154. package/src/reverse.ts +330 -0
  155. package/src/root-error-boundary.tsx +289 -0
  156. package/src/route-content-wrapper.tsx +196 -0
  157. package/src/route-definition/dsl-helpers.ts +934 -0
  158. package/src/route-definition/helper-factories.ts +200 -0
  159. package/src/route-definition/helpers-types.ts +430 -0
  160. package/src/route-definition/index.ts +52 -0
  161. package/src/route-definition/redirect.ts +93 -0
  162. package/src/route-definition.ts +1 -0
  163. package/src/route-map-builder.ts +281 -0
  164. package/src/route-name.ts +53 -0
  165. package/src/route-types.ts +259 -0
  166. package/src/router/content-negotiation.ts +116 -0
  167. package/src/router/debug-manifest.ts +72 -0
  168. package/src/router/error-handling.ts +287 -0
  169. package/src/router/find-match.ts +160 -0
  170. package/src/router/handler-context.ts +451 -0
  171. package/src/router/intercept-resolution.ts +397 -0
  172. package/src/router/lazy-includes.ts +236 -0
  173. package/src/router/loader-resolution.ts +420 -0
  174. package/src/router/logging.ts +251 -0
  175. package/src/router/manifest.ts +269 -0
  176. package/src/router/match-api.ts +620 -0
  177. package/src/router/match-context.ts +266 -0
  178. package/src/router/match-handlers.ts +440 -0
  179. package/src/router/match-middleware/background-revalidation.ts +223 -0
  180. package/src/router/match-middleware/cache-lookup.ts +634 -0
  181. package/src/router/match-middleware/cache-store.ts +295 -0
  182. package/src/router/match-middleware/index.ts +81 -0
  183. package/src/router/match-middleware/intercept-resolution.ts +306 -0
  184. package/src/router/match-middleware/segment-resolution.ts +193 -0
  185. package/src/router/match-pipelines.ts +179 -0
  186. package/src/router/match-result.ts +219 -0
  187. package/src/router/metrics.ts +282 -0
  188. package/src/router/middleware-cookies.ts +55 -0
  189. package/src/router/middleware-types.ts +222 -0
  190. package/src/router/middleware.ts +749 -0
  191. package/src/router/pattern-matching.ts +563 -0
  192. package/src/router/prerender-match.ts +402 -0
  193. package/src/router/preview-match.ts +170 -0
  194. package/src/router/revalidation.ts +289 -0
  195. package/src/router/router-context.ts +320 -0
  196. package/src/router/router-interfaces.ts +452 -0
  197. package/src/router/router-options.ts +592 -0
  198. package/src/router/router-registry.ts +24 -0
  199. package/src/router/segment-resolution/fresh.ts +570 -0
  200. package/src/router/segment-resolution/helpers.ts +263 -0
  201. package/src/router/segment-resolution/loader-cache.ts +198 -0
  202. package/src/router/segment-resolution/revalidation.ts +1242 -0
  203. package/src/router/segment-resolution/static-store.ts +67 -0
  204. package/src/router/segment-resolution.ts +21 -0
  205. package/src/router/segment-wrappers.ts +291 -0
  206. package/src/router/telemetry-otel.ts +299 -0
  207. package/src/router/telemetry.ts +300 -0
  208. package/src/router/timeout.ts +148 -0
  209. package/src/router/trie-matching.ts +239 -0
  210. package/src/router/types.ts +170 -0
  211. package/src/router.ts +1006 -0
  212. package/src/rsc/handler-context.ts +45 -0
  213. package/src/rsc/handler.ts +1089 -0
  214. package/src/rsc/helpers.ts +198 -0
  215. package/src/rsc/index.ts +36 -0
  216. package/src/rsc/loader-fetch.ts +209 -0
  217. package/src/rsc/manifest-init.ts +86 -0
  218. package/src/rsc/nonce.ts +32 -0
  219. package/src/rsc/origin-guard.ts +141 -0
  220. package/src/rsc/progressive-enhancement.ts +379 -0
  221. package/src/rsc/response-error.ts +37 -0
  222. package/src/rsc/response-route-handler.ts +347 -0
  223. package/src/rsc/rsc-rendering.ts +237 -0
  224. package/src/rsc/runtime-warnings.ts +42 -0
  225. package/src/rsc/server-action.ts +348 -0
  226. package/src/rsc/ssr-setup.ts +128 -0
  227. package/src/rsc/types.ts +263 -0
  228. package/src/search-params.ts +230 -0
  229. package/src/segment-system.tsx +454 -0
  230. package/src/server/context.ts +591 -0
  231. package/src/server/cookie-store.ts +190 -0
  232. package/src/server/fetchable-loader-store.ts +37 -0
  233. package/src/server/handle-store.ts +308 -0
  234. package/src/server/loader-registry.ts +133 -0
  235. package/src/server/request-context.ts +920 -0
  236. package/src/server/root-layout.tsx +10 -0
  237. package/src/server/tsconfig.json +14 -0
  238. package/src/server.ts +51 -0
  239. package/src/ssr/index.tsx +365 -0
  240. package/src/static-handler.ts +114 -0
  241. package/src/theme/ThemeProvider.tsx +297 -0
  242. package/src/theme/ThemeScript.tsx +61 -0
  243. package/src/theme/constants.ts +62 -0
  244. package/src/theme/index.ts +48 -0
  245. package/src/theme/theme-context.ts +44 -0
  246. package/src/theme/theme-script.ts +155 -0
  247. package/src/theme/types.ts +182 -0
  248. package/src/theme/use-theme.ts +44 -0
  249. package/src/types/boundaries.ts +158 -0
  250. package/src/types/cache-types.ts +198 -0
  251. package/src/types/error-types.ts +192 -0
  252. package/src/types/global-namespace.ts +100 -0
  253. package/src/types/handler-context.ts +687 -0
  254. package/src/types/index.ts +88 -0
  255. package/src/types/loader-types.ts +183 -0
  256. package/src/types/route-config.ts +170 -0
  257. package/src/types/route-entry.ts +109 -0
  258. package/src/types/segments.ts +148 -0
  259. package/src/types.ts +1 -0
  260. package/src/urls/include-helper.ts +197 -0
  261. package/src/urls/index.ts +53 -0
  262. package/src/urls/path-helper-types.ts +339 -0
  263. package/src/urls/path-helper.ts +329 -0
  264. package/src/urls/pattern-types.ts +95 -0
  265. package/src/urls/response-types.ts +106 -0
  266. package/src/urls/type-extraction.ts +372 -0
  267. package/src/urls/urls-function.ts +98 -0
  268. package/src/urls.ts +1 -0
  269. package/src/use-loader.tsx +354 -0
  270. package/src/vite/discovery/bundle-postprocess.ts +184 -0
  271. package/src/vite/discovery/discover-routers.ts +344 -0
  272. package/src/vite/discovery/prerender-collection.ts +385 -0
  273. package/src/vite/discovery/route-types-writer.ts +258 -0
  274. package/src/vite/discovery/self-gen-tracking.ts +47 -0
  275. package/src/vite/discovery/state.ts +108 -0
  276. package/src/vite/discovery/virtual-module-codegen.ts +203 -0
  277. package/src/vite/index.ts +16 -0
  278. package/src/vite/plugin-types.ts +48 -0
  279. package/src/vite/plugins/cjs-to-esm.ts +93 -0
  280. package/src/vite/plugins/client-ref-dedup.ts +115 -0
  281. package/src/vite/plugins/client-ref-hashing.ts +105 -0
  282. package/src/vite/plugins/expose-action-id.ts +363 -0
  283. package/src/vite/plugins/expose-id-utils.ts +287 -0
  284. package/src/vite/plugins/expose-ids/export-analysis.ts +296 -0
  285. package/src/vite/plugins/expose-ids/handler-transform.ts +179 -0
  286. package/src/vite/plugins/expose-ids/loader-transform.ts +74 -0
  287. package/src/vite/plugins/expose-ids/router-transform.ts +110 -0
  288. package/src/vite/plugins/expose-ids/types.ts +45 -0
  289. package/src/vite/plugins/expose-internal-ids.ts +569 -0
  290. package/src/vite/plugins/refresh-cmd.ts +65 -0
  291. package/src/vite/plugins/use-cache-transform.ts +323 -0
  292. package/src/vite/plugins/version-injector.ts +83 -0
  293. package/src/vite/plugins/version-plugin.ts +266 -0
  294. package/src/vite/plugins/version.d.ts +12 -0
  295. package/src/vite/plugins/virtual-entries.ts +123 -0
  296. package/src/vite/plugins/virtual-stub-plugin.ts +29 -0
  297. package/src/vite/rango.ts +445 -0
  298. package/src/vite/router-discovery.ts +777 -0
  299. package/src/vite/utils/ast-handler-extract.ts +517 -0
  300. package/src/vite/utils/banner.ts +36 -0
  301. package/src/vite/utils/bundle-analysis.ts +137 -0
  302. package/src/vite/utils/manifest-utils.ts +70 -0
  303. package/src/vite/utils/package-resolution.ts +121 -0
  304. package/src/vite/utils/prerender-utils.ts +189 -0
  305. package/src/vite/utils/shared-utils.ts +169 -0
@@ -0,0 +1,261 @@
1
+ import type {
2
+ NavigationClient,
3
+ FetchPartialOptions,
4
+ FetchPartialResult,
5
+ RscPayload,
6
+ RscBrowserDependencies,
7
+ } from "./types.js";
8
+ import { NetworkError, ServerRedirect, isNetworkError } from "../errors.js";
9
+ import {
10
+ browserDebugLog,
11
+ isBrowserDebugEnabled,
12
+ startBrowserTransaction,
13
+ } from "./logging.js";
14
+ import { getRangoState } from "./rango-state.js";
15
+ import {
16
+ extractRscHeaderUrl,
17
+ emptyResponse,
18
+ teeWithCompletion,
19
+ } from "./response-adapter.js";
20
+ import {
21
+ buildPrefetchKey,
22
+ consumePrefetch,
23
+ consumeInflightPrefetch,
24
+ } from "./prefetch/cache.js";
25
+
26
+ /**
27
+ * Create a navigation client for fetching RSC payloads
28
+ *
29
+ * The client handles building URLs with RSC parameters and
30
+ * deserializing the response using the RSC runtime.
31
+ *
32
+ * Checks the in-memory prefetch cache before making a network request.
33
+ * The cache key is source-dependent (includes the previous URL) so
34
+ * prefetch responses match the exact diff the server would produce.
35
+ *
36
+ * @param deps - RSC browser dependencies (createFromFetch)
37
+ * @returns NavigationClient instance
38
+ */
39
+ export function createNavigationClient(
40
+ deps: Pick<RscBrowserDependencies, "createFromFetch">,
41
+ ): NavigationClient {
42
+ return {
43
+ /**
44
+ * Fetch a partial RSC payload for navigation
45
+ *
46
+ * First checks the in-memory prefetch cache for a matching entry.
47
+ * If found, uses the cached response instantly. Otherwise sends
48
+ * current segment IDs to the server for diff-based rendering.
49
+ *
50
+ * @param options - Fetch options
51
+ * @returns RSC payload with segments and metadata, plus stream completion promise
52
+ */
53
+ async fetchPartial(
54
+ options: FetchPartialOptions,
55
+ ): Promise<FetchPartialResult> {
56
+ const {
57
+ targetUrl,
58
+ segmentIds,
59
+ previousUrl,
60
+ signal,
61
+ staleRevalidation,
62
+ interceptSourceUrl,
63
+ version,
64
+ hmr,
65
+ } = options;
66
+
67
+ const debugEnabled = isBrowserDebugEnabled();
68
+ const tx = debugEnabled
69
+ ? startBrowserTransaction(staleRevalidation ? "revalidate" : "navigate")
70
+ : null;
71
+ if (tx) {
72
+ browserDebugLog(tx, "request start", {
73
+ from: previousUrl,
74
+ to: targetUrl,
75
+ segments: segmentIds,
76
+ staleRevalidation: !!staleRevalidation,
77
+ });
78
+ }
79
+
80
+ // Build fetch URL with partial rendering params (used for both
81
+ // cache key lookup and actual fetch if cache misses)
82
+ const fetchUrl = new URL(targetUrl, window.location.origin);
83
+ fetchUrl.searchParams.set("_rsc_partial", "true");
84
+ fetchUrl.searchParams.set("_rsc_segments", segmentIds.join(","));
85
+ if (staleRevalidation) {
86
+ fetchUrl.searchParams.set("_rsc_stale", "true");
87
+ }
88
+ if (version) {
89
+ fetchUrl.searchParams.set("_rsc_v", version);
90
+ }
91
+
92
+ // Check in-memory prefetch cache before making a network request.
93
+ // The cache key includes the source URL (previousUrl) because the
94
+ // server's diff response depends on the source page context.
95
+ // Skip cache for stale revalidation (needs fresh data), HMR (needs
96
+ // fresh modules), and intercept contexts (source-dependent responses).
97
+ const canUsePrefetch = !staleRevalidation && !hmr && !interceptSourceUrl;
98
+ const cacheKey = buildPrefetchKey(previousUrl, fetchUrl);
99
+ const cachedResponse = canUsePrefetch ? consumePrefetch(cacheKey) : null;
100
+ // If no completed cache entry, check for in-flight prefetch.
101
+ // This reuses a prefetch that is still downloading rather than
102
+ // starting a duplicate request from scratch.
103
+ const inflightPrefetch =
104
+ !cachedResponse && canUsePrefetch
105
+ ? consumeInflightPrefetch(cacheKey)
106
+ : null;
107
+
108
+ // Track when the stream completes
109
+ let resolveStreamComplete: () => void;
110
+ const streamComplete = new Promise<void>((resolve) => {
111
+ resolveStreamComplete = resolve;
112
+ });
113
+
114
+ /** Start a fresh navigation fetch (no cache / inflight hit). */
115
+ const doFreshFetch = (): Promise<Response> => {
116
+ if (tx) {
117
+ browserDebugLog(tx, "fetching", {
118
+ path: `${fetchUrl.pathname}${fetchUrl.search}`,
119
+ });
120
+ }
121
+
122
+ return fetch(fetchUrl, {
123
+ headers: {
124
+ "X-RSC-Router-Client-Path": previousUrl,
125
+ "X-Rango-State": getRangoState(),
126
+ ...(tx && { "X-RSC-Router-Request-Id": tx.requestId }),
127
+ ...(interceptSourceUrl && {
128
+ "X-RSC-Router-Intercept-Source": interceptSourceUrl,
129
+ }),
130
+ ...(hmr && { "X-RSC-HMR": "1" }),
131
+ },
132
+ signal,
133
+ }).then((response) => {
134
+ // Check for version mismatch - server wants us to reload
135
+ const reload = extractRscHeaderUrl(response, "X-RSC-Reload");
136
+ if (reload === "blocked") {
137
+ resolveStreamComplete();
138
+ return emptyResponse();
139
+ }
140
+ if (reload) {
141
+ if (tx) {
142
+ browserDebugLog(tx, "version mismatch, reloading", {
143
+ reloadUrl: reload.url,
144
+ });
145
+ }
146
+ window.location.href = reload.url;
147
+ return new Promise<Response>(() => {});
148
+ }
149
+
150
+ // Server-side redirect without state: the server returned 204 with
151
+ // X-RSC-Redirect instead of a 3xx (which fetch would auto-follow
152
+ // to a URL rendering full HTML). Throw ServerRedirect so the
153
+ // navigation bridge catches it and re-navigates with _skipCache.
154
+ const redirect = extractRscHeaderUrl(response, "X-RSC-Redirect");
155
+ if (redirect === "blocked") {
156
+ resolveStreamComplete();
157
+ return emptyResponse();
158
+ }
159
+ if (redirect) {
160
+ if (tx) {
161
+ browserDebugLog(tx, "server redirect", {
162
+ redirectUrl: redirect.url,
163
+ });
164
+ }
165
+ resolveStreamComplete();
166
+ throw new ServerRedirect(redirect.url, undefined);
167
+ }
168
+
169
+ return teeWithCompletion(
170
+ response,
171
+ () => {
172
+ if (tx) browserDebugLog(tx, "stream complete");
173
+ resolveStreamComplete();
174
+ },
175
+ signal,
176
+ );
177
+ });
178
+ };
179
+
180
+ let responsePromise: Promise<Response>;
181
+
182
+ if (cachedResponse) {
183
+ if (tx) {
184
+ browserDebugLog(tx, "prefetch cache hit", { key: cacheKey });
185
+ }
186
+ // Cached response body is already fully buffered (arrayBuffer),
187
+ // so stream completion is immediate.
188
+ responsePromise = Promise.resolve(cachedResponse).then((response) => {
189
+ return teeWithCompletion(
190
+ response,
191
+ () => {
192
+ if (tx) browserDebugLog(tx, "stream complete (from cache)");
193
+ resolveStreamComplete();
194
+ },
195
+ signal,
196
+ );
197
+ });
198
+ } else if (inflightPrefetch) {
199
+ if (tx) {
200
+ browserDebugLog(tx, "reusing inflight prefetch", { key: cacheKey });
201
+ }
202
+ // Await the in-flight prefetch. If it resolves with a Response,
203
+ // use it like a cache hit. If it fails (null), fall back to
204
+ // a fresh navigation fetch.
205
+ responsePromise = inflightPrefetch.then((prefetchResponse) => {
206
+ if (!prefetchResponse) {
207
+ if (tx) {
208
+ browserDebugLog(
209
+ tx,
210
+ "inflight prefetch failed, falling back to fetch",
211
+ );
212
+ }
213
+ return doFreshFetch();
214
+ }
215
+ if (tx) {
216
+ browserDebugLog(tx, "inflight prefetch resolved", {
217
+ key: cacheKey,
218
+ });
219
+ }
220
+ return teeWithCompletion(
221
+ prefetchResponse,
222
+ () => {
223
+ if (tx)
224
+ browserDebugLog(tx, "stream complete (from inflight prefetch)");
225
+ resolveStreamComplete();
226
+ },
227
+ signal,
228
+ );
229
+ });
230
+ } else {
231
+ responsePromise = doFreshFetch();
232
+ }
233
+
234
+ try {
235
+ // Deserialize RSC payload
236
+ const payload = await deps.createFromFetch<RscPayload>(responsePromise);
237
+ if (tx) {
238
+ browserDebugLog(tx, "response received", {
239
+ isPartial: payload.metadata?.isPartial,
240
+ matchedCount: payload.metadata?.matched?.length ?? 0,
241
+ diffCount: payload.metadata?.diff?.length ?? 0,
242
+ });
243
+ }
244
+ return { payload, streamComplete };
245
+ } catch (error) {
246
+ // Convert network-level errors to NetworkError for proper handling
247
+ if (isNetworkError(error)) {
248
+ throw new NetworkError(
249
+ "Unable to connect to server. Please check your connection.",
250
+ {
251
+ cause: error,
252
+ url: fetchUrl.toString(),
253
+ operation: staleRevalidation ? "revalidation" : "navigation",
254
+ },
255
+ );
256
+ }
257
+ throw error;
258
+ }
259
+ },
260
+ };
261
+ }