@rangojs/router 0.15.1 → 0.17.0

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 (249) hide show
  1. package/README.md +254 -89
  2. package/dist/testing/vitest.js +252 -0
  3. package/dist/types/browser/navigation-transaction.d.ts +6 -0
  4. package/dist/types/browser/partial-update.d.ts +11 -0
  5. package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
  6. package/dist/types/browser/rsc-router.d.ts +1 -1
  7. package/dist/types/browser/scroll-restoration.d.ts +7 -0
  8. package/dist/types/cache/cache-exec-scope.d.ts +18 -1
  9. package/dist/types/cache/cache-scope.d.ts +34 -5
  10. package/dist/types/cache/handle-capture.d.ts +41 -3
  11. package/dist/types/cache/handle-snapshot.d.ts +53 -13
  12. package/dist/types/cache/segment-codec.d.ts +10 -3
  13. package/dist/types/cache/taint.d.ts +1 -1
  14. package/dist/types/cache/types.d.ts +15 -3
  15. package/dist/types/client.d.ts +4 -4
  16. package/dist/types/client.rsc.d.ts +4 -5
  17. package/dist/types/components/DefaultDocument.d.ts +5 -2
  18. package/dist/types/context-var.d.ts +6 -5
  19. package/dist/types/escape-script.d.ts +1 -1
  20. package/dist/types/handles/MetaTags.d.ts +5 -4
  21. package/dist/types/handles/Scripts.d.ts +8 -7
  22. package/dist/types/handles/meta.d.ts +2 -2
  23. package/dist/types/handles/script.d.ts +17 -16
  24. package/dist/types/href-client.d.ts +1 -1
  25. package/dist/types/html.d.ts +12 -0
  26. package/dist/types/index.d.ts +1 -1
  27. package/dist/types/index.rsc.d.ts +3 -2
  28. package/dist/types/loader-store.d.ts +52 -0
  29. package/dist/types/reverse.d.ts +36 -4
  30. package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
  31. package/dist/types/route-definition/helpers-types.d.ts +36 -10
  32. package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
  33. package/dist/types/route-types.d.ts +3 -1
  34. package/dist/types/router/debug-manifest.d.ts +3 -2
  35. package/dist/types/router/error-handling.d.ts +20 -0
  36. package/dist/types/router/handler-context.d.ts +6 -0
  37. package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
  38. package/dist/types/router/middleware-types.d.ts +3 -2
  39. package/dist/types/router/router-context.d.ts +3 -1
  40. package/dist/types/router/router-interfaces.d.ts +24 -2
  41. package/dist/types/router/router-options.d.ts +8 -5
  42. package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
  43. package/dist/types/router/segment-resolution/loader-cache.d.ts +37 -1
  44. package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
  45. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +3 -3
  46. package/dist/types/router/segment-resolution/mask-nested.d.ts +7 -7
  47. package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
  48. package/dist/types/router/segment-wrappers.d.ts +1 -1
  49. package/dist/types/rsc/response-route-handler.d.ts +1 -1
  50. package/dist/types/rsc/rsc-rendering.d.ts +1 -2
  51. package/dist/types/rsc/shell-capture-constants.d.ts +8 -0
  52. package/dist/types/rsc/shell-capture.d.ts +5 -4
  53. package/dist/types/rsc/ssr-setup.d.ts +6 -6
  54. package/dist/types/rsc/types.d.ts +10 -0
  55. package/dist/types/server/context.d.ts +33 -3
  56. package/dist/types/server/handle-store.d.ts +26 -2
  57. package/dist/types/server/request-context.d.ts +24 -21
  58. package/dist/types/ssr/index.d.ts +15 -0
  59. package/dist/types/static-handler.d.ts +4 -1
  60. package/dist/types/testing/generated-routes.d.ts +2 -2
  61. package/dist/types/testing/internal/context.d.ts +1 -1
  62. package/dist/types/testing/render-route.d.ts +32 -6
  63. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
  64. package/dist/types/testing/vitest.d.ts +35 -1
  65. package/dist/types/theme/ThemeScript.d.ts +3 -3
  66. package/dist/types/thenable-status.d.ts +9 -0
  67. package/dist/types/types/cache-types.d.ts +14 -10
  68. package/dist/types/types/handler-context.d.ts +14 -0
  69. package/dist/types/types/loader-types.d.ts +8 -6
  70. package/dist/types/urls/response-types.d.ts +3 -12
  71. package/dist/types/use-loader.d.ts +12 -3
  72. package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
  73. package/dist/types/vite/rango.d.ts +1 -1
  74. package/dist/vite/index.js +884 -768
  75. package/package.json +4 -4
  76. package/skills/api-client/SKILL.md +22 -10
  77. package/skills/breadcrumbs/SKILL.md +65 -22
  78. package/skills/bundle-analysis/SKILL.md +22 -22
  79. package/skills/cache-guide/SKILL.md +207 -97
  80. package/skills/caching/SKILL.md +295 -93
  81. package/skills/catalog.json +16 -16
  82. package/skills/client-urls/SKILL.md +53 -31
  83. package/skills/cloudflare/SKILL.md +72 -20
  84. package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
  85. package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
  86. package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
  87. package/skills/comparison/SKILL.md +5 -0
  88. package/skills/comparison/references/framework-comparison.md +39 -33
  89. package/skills/composability/SKILL.md +38 -29
  90. package/skills/css/SKILL.md +10 -13
  91. package/skills/debug-manifest/SKILL.md +74 -31
  92. package/skills/defer-hydration/SKILL.md +2 -1
  93. package/skills/deployment-caching/SKILL.md +6 -1
  94. package/skills/document-cache/SKILL.md +92 -28
  95. package/skills/fonts/SKILL.md +16 -8
  96. package/skills/handler-use/SKILL.md +53 -17
  97. package/skills/hooks/SKILL.md +28 -20
  98. package/skills/hooks/data.md +43 -1
  99. package/skills/hooks/handle-and-actions.md +31 -16
  100. package/skills/hooks/navigation.md +39 -10
  101. package/skills/hooks/outlets.md +13 -6
  102. package/skills/hooks/state.md +37 -15
  103. package/skills/hooks/urls.md +7 -5
  104. package/skills/host-router/SKILL.md +64 -12
  105. package/skills/i18n/SKILL.md +60 -34
  106. package/skills/intercept/SKILL.md +161 -26
  107. package/skills/layout/SKILL.md +106 -50
  108. package/skills/links/SKILL.md +51 -19
  109. package/skills/loader/SKILL.md +242 -106
  110. package/skills/middleware/SKILL.md +119 -23
  111. package/skills/migrate-nextjs/SKILL.md +100 -45
  112. package/skills/migrate-nextjs/backend-host-swap.md +5 -2
  113. package/skills/migrate-react-router/SKILL.md +49 -38
  114. package/skills/migrate-react-router/component-migration.md +25 -6
  115. package/skills/migrate-react-router/data-and-actions.md +18 -5
  116. package/skills/migrate-react-router/route-mapping.md +12 -6
  117. package/skills/mime-routes/SKILL.md +12 -7
  118. package/skills/observability/SKILL.md +28 -14
  119. package/skills/parallel/SKILL.md +75 -39
  120. package/skills/ppr/SKILL.md +345 -150
  121. package/skills/prerender/SKILL.md +189 -100
  122. package/skills/rango/SKILL.md +65 -34
  123. package/skills/react-compiler/SKILL.md +6 -5
  124. package/skills/response-routes/SKILL.md +133 -25
  125. package/skills/route/SKILL.md +165 -60
  126. package/skills/router-setup/SKILL.md +418 -240
  127. package/skills/scripts/SKILL.md +31 -20
  128. package/skills/server-actions/SKILL.md +220 -59
  129. package/skills/shell-manifest/SKILL.md +12 -7
  130. package/skills/streams-and-websockets/SKILL.md +32 -14
  131. package/skills/tailwind/SKILL.md +37 -33
  132. package/skills/testing/SKILL.md +68 -32
  133. package/skills/testing/cache-prerender.md +55 -29
  134. package/skills/testing/client-components.md +59 -6
  135. package/skills/testing/e2e-parity.md +12 -2
  136. package/skills/testing/flight.md +19 -11
  137. package/skills/testing/handles.md +8 -5
  138. package/skills/testing/loader.md +8 -5
  139. package/skills/testing/middleware.md +8 -3
  140. package/skills/testing/predicates.md +120 -0
  141. package/skills/testing/render-handler.md +70 -22
  142. package/skills/testing/response-routes.md +11 -4
  143. package/skills/testing/reverse-and-types.md +20 -14
  144. package/skills/testing/server-actions.md +12 -3
  145. package/skills/testing/server-tree.md +9 -0
  146. package/skills/testing/setup.md +9 -5
  147. package/skills/theme/SKILL.md +106 -29
  148. package/skills/typesafety/SKILL.md +20 -9
  149. package/skills/typesafety/env-and-bindings.md +126 -47
  150. package/skills/typesafety/generated-files-and-cli.md +26 -14
  151. package/skills/typesafety/params-and-search.md +44 -19
  152. package/skills/typesafety/route-types.md +50 -14
  153. package/skills/use-cache/SKILL.md +200 -82
  154. package/skills/vercel/SKILL.md +21 -11
  155. package/skills/view-transitions/SKILL.md +41 -20
  156. package/src/__augment-tests__/augmented.check.ts +26 -1
  157. package/src/browser/index.ts +2 -2
  158. package/src/browser/navigation-bridge.ts +3 -4
  159. package/src/browser/navigation-transaction.ts +22 -2
  160. package/src/browser/partial-update.ts +75 -80
  161. package/src/browser/react/ScrollRestoration.tsx +6 -6
  162. package/src/browser/rsc-router.tsx +1 -1
  163. package/src/browser/scroll-restoration.ts +53 -6
  164. package/src/cache/cache-exec-scope.ts +37 -4
  165. package/src/cache/cache-key-utils.ts +1 -6
  166. package/src/cache/cache-runtime.ts +230 -61
  167. package/src/cache/cache-scope.ts +76 -12
  168. package/src/cache/document-cache.ts +15 -0
  169. package/src/cache/handle-capture.ts +90 -10
  170. package/src/cache/handle-snapshot.ts +132 -28
  171. package/src/cache/segment-codec.ts +26 -7
  172. package/src/cache/tag-invalidation.ts +4 -3
  173. package/src/cache/taint.ts +1 -1
  174. package/src/cache/types.ts +16 -3
  175. package/src/client.rsc.tsx +10 -9
  176. package/src/client.tsx +5 -4
  177. package/src/components/DefaultDocument.tsx +7 -2
  178. package/src/context-var.ts +6 -5
  179. package/src/escape-script.ts +1 -1
  180. package/src/handles/MetaTags.tsx +13 -11
  181. package/src/handles/Scripts.tsx +11 -10
  182. package/src/handles/meta.ts +2 -2
  183. package/src/handles/script.ts +17 -16
  184. package/src/href-client.ts +1 -1
  185. package/src/html.ts +12 -0
  186. package/src/index.rsc.ts +5 -0
  187. package/src/index.ts +1 -0
  188. package/src/loader-store.ts +106 -0
  189. package/src/prerender/build-shell-capture.ts +8 -11
  190. package/src/reverse.ts +44 -4
  191. package/src/route-definition/dsl-helpers.ts +134 -42
  192. package/src/route-definition/helpers-types.ts +36 -10
  193. package/src/route-definition/resolve-handler-use.ts +121 -6
  194. package/src/route-types.ts +2 -3
  195. package/src/router/debug-manifest.ts +10 -2
  196. package/src/router/error-handling.ts +51 -15
  197. package/src/router/handler-context.ts +34 -25
  198. package/src/router/intercept-resolution.ts +125 -39
  199. package/src/router/loader-resolution.ts +56 -11
  200. package/src/router/match-api.ts +12 -17
  201. package/src/router/match-middleware/background-revalidation.ts +161 -96
  202. package/src/router/match-middleware/cache-lookup.ts +93 -10
  203. package/src/router/match-middleware/cache-store.ts +17 -90
  204. package/src/router/middleware-types.ts +3 -5
  205. package/src/router/middleware.ts +52 -9
  206. package/src/router/prerender-match.ts +53 -19
  207. package/src/router/route-snapshot.ts +11 -1
  208. package/src/router/router-context.ts +2 -0
  209. package/src/router/router-interfaces.ts +25 -2
  210. package/src/router/router-options.ts +8 -5
  211. package/src/router/segment-resolution/fresh.ts +44 -12
  212. package/src/router/segment-resolution/loader-cache.ts +205 -45
  213. package/src/router/segment-resolution/loader-mask.ts +12 -22
  214. package/src/router/segment-resolution/loader-snapshot.ts +6 -6
  215. package/src/router/segment-resolution/mask-nested.ts +7 -7
  216. package/src/router/segment-resolution/revalidation.ts +34 -6
  217. package/src/router/segment-wrappers.ts +4 -0
  218. package/src/rsc/render-pipeline.ts +3 -0
  219. package/src/rsc/response-route-handler.ts +11 -3
  220. package/src/rsc/rsc-rendering.ts +8 -45
  221. package/src/rsc/shell-capture-constants.ts +13 -0
  222. package/src/rsc/shell-capture.ts +111 -90
  223. package/src/rsc/ssr-setup.ts +14 -8
  224. package/src/rsc/types.ts +11 -0
  225. package/src/server/context.ts +88 -11
  226. package/src/server/cookie-store.ts +5 -4
  227. package/src/server/handle-store.ts +232 -7
  228. package/src/server/request-context.ts +35 -30
  229. package/src/ssr/index.tsx +40 -7
  230. package/src/ssr-suspension-warning.ts +2 -13
  231. package/src/static-handler.ts +4 -1
  232. package/src/testing/flight-runtime.d.ts +21 -0
  233. package/src/testing/generated-routes.ts +2 -2
  234. package/src/testing/internal/context.ts +1 -1
  235. package/src/testing/internal/flight-client-globals.ts +7 -5
  236. package/src/testing/render-route.tsx +85 -24
  237. package/src/testing/run-loader.ts +5 -1
  238. package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
  239. package/src/testing/vitest.ts +62 -1
  240. package/src/theme/ThemeProvider.tsx +14 -12
  241. package/src/theme/ThemeScript.tsx +3 -3
  242. package/src/thenable-status.ts +16 -0
  243. package/src/types/cache-types.ts +14 -10
  244. package/src/types/handler-context.ts +14 -0
  245. package/src/types/loader-types.ts +8 -6
  246. package/src/urls/response-types.ts +3 -23
  247. package/src/use-loader.tsx +24 -4
  248. package/src/vite/plugins/use-cache-transform.ts +118 -74
  249. package/src/vite/rango.ts +7 -3
@@ -22,26 +22,26 @@ function createRangoDebugger(namespace) {
22
22
  if (shadow.enabled) return shadow;
23
23
  return void 0;
24
24
  }
25
- async function timed(debug14, label, fn) {
26
- if (!debug14) return await fn();
25
+ async function timed(debug13, label, fn) {
26
+ if (!debug13) return await fn();
27
27
  const start = performance.now();
28
28
  try {
29
29
  return await fn();
30
30
  } finally {
31
- debug14("%s (%sms)", label, (performance.now() - start).toFixed(1));
31
+ debug13("%s (%sms)", label, (performance.now() - start).toFixed(1));
32
32
  }
33
33
  }
34
- function timedSync(debug14, label, fn) {
35
- if (!debug14) return fn();
34
+ function timedSync(debug13, label, fn) {
35
+ if (!debug13) return fn();
36
36
  const start = performance.now();
37
37
  try {
38
38
  return fn();
39
39
  } finally {
40
- debug14("%s (%sms)", label, (performance.now() - start).toFixed(1));
40
+ debug13("%s (%sms)", label, (performance.now() - start).toFixed(1));
41
41
  }
42
42
  }
43
- function createCounter(debug14, label) {
44
- if (!debug14) return void 0;
43
+ function createCounter(debug13, label) {
44
+ if (!debug13) return void 0;
45
45
  let n = 0;
46
46
  let totalMs = 0;
47
47
  let slowestMs = 0;
@@ -75,7 +75,7 @@ function createCounter(debug14, label) {
75
75
  },
76
76
  flush() {
77
77
  if (n === 0) return;
78
- debug14(
78
+ debug13(
79
79
  "%s: %d files, %sms total, slowest %sms %s",
80
80
  label,
81
81
  n,
@@ -158,7 +158,7 @@ function transformClientRefs(code, projectRoot) {
158
158
  return hasReplacement ? result : null;
159
159
  }
160
160
  function hashClientRefs(projectRoot) {
161
- const counter = createCounter(debug7, "hash-client-refs");
161
+ const counter = createCounter(debug6, "hash-client-refs");
162
162
  return {
163
163
  name: "@rangojs/router:hash-client-refs",
164
164
  enforce: "post",
@@ -180,12 +180,12 @@ function hashClientRefs(projectRoot) {
180
180
  }
181
181
  };
182
182
  }
183
- var debug7, CLIENT_PKG_PROXY_PREFIX, CLIENT_IN_SERVER_PKG_PROXY_PREFIX, FS_PREFIX, REGISTER_CLIENT_REF_RE;
183
+ var debug6, CLIENT_PKG_PROXY_PREFIX, CLIENT_IN_SERVER_PKG_PROXY_PREFIX, FS_PREFIX, REGISTER_CLIENT_REF_RE;
184
184
  var init_client_ref_hashing = __esm({
185
185
  "src/vite/plugins/client-ref-hashing.ts"() {
186
186
  "use strict";
187
187
  init_debug();
188
- debug7 = createRangoDebugger(NS.transform);
188
+ debug6 = createRangoDebugger(NS.transform);
189
189
  CLIENT_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-package-proxy/";
190
190
  CLIENT_IN_SERVER_PKG_PROXY_PREFIX = "/@id/__x00__virtual:vite-rsc/client-in-server-package-proxy/";
191
191
  FS_PREFIX = "/@fs/";
@@ -193,6 +193,30 @@ var init_client_ref_hashing = __esm({
193
193
  }
194
194
  });
195
195
 
196
+ // src/internal-debug.ts
197
+ var INTERNAL_RANGO_DEBUG;
198
+ var init_internal_debug = __esm({
199
+ "src/internal-debug.ts"() {
200
+ "use strict";
201
+ INTERNAL_RANGO_DEBUG = typeof process !== "undefined" && Boolean(process.env?.INTERNAL_RANGO_DEBUG);
202
+ }
203
+ });
204
+
205
+ // src/browser/logging.ts
206
+ function debugLog(msg, ...args) {
207
+ if (INTERNAL_RANGO_DEBUG) {
208
+ console.log(msg, ...args);
209
+ }
210
+ }
211
+ var IS_BROWSER_DEBUG;
212
+ var init_logging = __esm({
213
+ "src/browser/logging.ts"() {
214
+ "use strict";
215
+ init_internal_debug();
216
+ IS_BROWSER_DEBUG = INTERNAL_RANGO_DEBUG;
217
+ }
218
+ });
219
+
196
220
  // src/browser/notify-listeners.ts
197
221
  function notifyListeners(entries, notify, isActive, shouldContinue) {
198
222
  const errors = [];
@@ -627,30 +651,6 @@ var init_policy = __esm({
627
651
  }
628
652
  });
629
653
 
630
- // src/internal-debug.ts
631
- var INTERNAL_RANGO_DEBUG;
632
- var init_internal_debug = __esm({
633
- "src/internal-debug.ts"() {
634
- "use strict";
635
- INTERNAL_RANGO_DEBUG = typeof process !== "undefined" && Boolean(process.env?.INTERNAL_RANGO_DEBUG);
636
- }
637
- });
638
-
639
- // src/browser/logging.ts
640
- function debugLog(msg, ...args) {
641
- if (INTERNAL_RANGO_DEBUG) {
642
- console.log(msg, ...args);
643
- }
644
- }
645
- var IS_BROWSER_DEBUG;
646
- var init_logging = __esm({
647
- "src/browser/logging.ts"() {
648
- "use strict";
649
- init_internal_debug();
650
- IS_BROWSER_DEBUG = INTERNAL_RANGO_DEBUG;
651
- }
652
- });
653
-
654
654
  // src/redirect-origin.ts
655
655
  var init_redirect_origin = __esm({
656
656
  "src/redirect-origin.ts"() {
@@ -3050,17 +3050,12 @@ ${lazyImports.join(",\n")}
3050
3050
 
3051
3051
  // src/vite/plugins/use-cache-transform.ts
3052
3052
  import path5 from "node:path";
3053
- import MagicString4 from "magic-string";
3054
- init_debug();
3055
- var debug4 = createRangoDebugger(NS.transform);
3056
3053
  var CACHE_RUNTIME_IMPORT = "@rangojs/router/cache-runtime";
3057
3054
  var LAYOUT_TEMPLATE_PATTERN = /\/(layout|template)\.(tsx?|jsx?)$/;
3058
3055
  var USE_CACHE_DIRECTIVE_RE = /^use cache(:\s*[\w-]+)?$/;
3059
- function useCacheTransform() {
3056
+ function useCacheTransform(counter) {
3060
3057
  let projectRoot = "";
3061
3058
  let isBuild = false;
3062
- let rscTransforms = null;
3063
- const counter = createCounter(debug4, "use-cache");
3064
3059
  return {
3065
3060
  name: "@rangojs/router:use-cache",
3066
3061
  enforce: "post",
@@ -3073,62 +3068,73 @@ function useCacheTransform() {
3073
3068
  },
3074
3069
  async transform(code, id) {
3075
3070
  if (this.environment?.name !== "rsc") return;
3076
- if (!code.includes("use cache")) return;
3077
- if (id.includes("/node_modules/") || id.startsWith("\0")) return;
3078
- if (!/\.(tsx?|jsx?|mjs)$/.test(id)) return;
3071
+ if (!isUseCacheCandidate(code, id)) return;
3079
3072
  const start = counter ? performance.now() : 0;
3080
3073
  try {
3081
- if (!rscTransforms) {
3082
- try {
3083
- rscTransforms = await import("@vitejs/plugin-rsc/transforms");
3084
- } catch {
3085
- return;
3086
- }
3087
- }
3088
- const {
3089
- hasDirective: hasDirective2,
3090
- transformWrapExport,
3091
- transformHoistInlineDirective
3092
- } = rscTransforms;
3093
- let ast;
3094
- try {
3095
- const { parseAst: parseAst6 } = await import("vite");
3096
- ast = parseAst6(code, { lang: "tsx" });
3097
- } catch {
3098
- return;
3099
- }
3100
- stripNullDirectiveFields(ast);
3101
- const filePath = normalizePath(path5.relative(projectRoot, id));
3102
- const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
3103
- if (hasDirective2(ast.body, "use cache")) {
3104
- return transformFileLevelUseCache(
3105
- code,
3106
- ast,
3107
- filePath,
3108
- id,
3109
- isBuild,
3110
- isLayoutOrTemplate,
3111
- transformWrapExport,
3112
- hasDirective2
3113
- );
3114
- }
3115
- const functionResult = transformFunctionLevelUseCache(
3116
- code,
3117
- ast,
3118
- filePath,
3119
- id,
3074
+ return await transformUseCache(code, id, {
3075
+ root: projectRoot,
3120
3076
  isBuild,
3121
- transformHoistInlineDirective
3122
- );
3123
- warnOnNearMissDirectives(ast, id, this.warn.bind(this));
3124
- if (functionResult) return functionResult;
3077
+ warn: (message) => this.warn(message)
3078
+ });
3125
3079
  } finally {
3126
3080
  counter?.record(id, performance.now() - start);
3127
3081
  }
3128
3082
  }
3129
3083
  };
3130
3084
  }
3131
- function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLayoutOrTemplate, transformWrapExport, hasDirective2) {
3085
+ function isUseCacheCandidate(code, id) {
3086
+ return (
3087
+ // Quick bail: no "use cache" in source
3088
+ code.includes("use cache") && // Skip node_modules and virtual modules
3089
+ !id.includes("/node_modules/") && !id.startsWith("\0") && // Only JS/TS files
3090
+ /\.(tsx?|jsx?|mjs)$/.test(id)
3091
+ );
3092
+ }
3093
+ var rscTransforms = null;
3094
+ async function transformUseCache(code, id, options) {
3095
+ if (!rscTransforms) {
3096
+ try {
3097
+ rscTransforms = await import("@vitejs/plugin-rsc/transforms");
3098
+ } catch (error) {
3099
+ options.onTransformsError?.(error);
3100
+ return;
3101
+ }
3102
+ }
3103
+ const { hasDirective: hasDirective2, transformWrapExport, transformHoistInlineDirective } = rscTransforms;
3104
+ let ast;
3105
+ try {
3106
+ const { parseAst: parseAst6 } = await import("vite");
3107
+ ast = parseAst6(code, { lang: "tsx" });
3108
+ } catch {
3109
+ return;
3110
+ }
3111
+ stripNullDirectiveFields(ast);
3112
+ const filePath = normalizePath(path5.relative(options.root, id));
3113
+ const isLayoutOrTemplate = LAYOUT_TEMPLATE_PATTERN.test(id);
3114
+ if (hasDirective2(ast.body, "use cache")) {
3115
+ return transformFileLevelUseCache(
3116
+ code,
3117
+ ast,
3118
+ filePath,
3119
+ id,
3120
+ options.isBuild,
3121
+ isLayoutOrTemplate,
3122
+ transformWrapExport,
3123
+ hasDirective2
3124
+ );
3125
+ }
3126
+ const functionResult = transformFunctionLevelUseCache(
3127
+ code,
3128
+ ast,
3129
+ filePath,
3130
+ id,
3131
+ options.isBuild,
3132
+ transformHoistInlineDirective
3133
+ );
3134
+ warnOnNearMissDirectives(ast, id, options.warn);
3135
+ if (functionResult) return functionResult;
3136
+ }
3137
+ async function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLayoutOrTemplate, transformWrapExport, hasDirective2) {
3132
3138
  const unconfirmedExports = [];
3133
3139
  const { exportNames, output } = transformWrapExport(code, ast, {
3134
3140
  runtime: (value, name) => {
@@ -3157,6 +3163,7 @@ function transformFileLevelUseCache(code, ast, filePath, sourceId, isBuild, isLa
3157
3163
  );
3158
3164
  }
3159
3165
  if (exportNames.length === 0) {
3166
+ const { default: MagicString4 } = await import("magic-string");
3160
3167
  const s = new MagicString4(code);
3161
3168
  const directive2 = findFileLevelDirective(ast);
3162
3169
  if (directive2) {
@@ -3285,7 +3292,7 @@ function warnOnNearMissDirectives(ast, fileId, warn) {
3285
3292
  init_debug();
3286
3293
  import { existsSync, readFileSync } from "node:fs";
3287
3294
  import { isAbsolute, join, normalize, relative } from "node:path";
3288
- var debug5 = createRangoDebugger(NS.transform);
3295
+ var debug4 = createRangoDebugger(NS.transform);
3289
3296
  var CLIENT_IN_SERVER_PROXY_PREFIX = "virtual:vite-rsc/client-in-server-package-proxy/";
3290
3297
  var DEDUP_PREFIX = "\0rango:dedup/";
3291
3298
  function extractPackageName(absolutePath) {
@@ -3462,8 +3469,8 @@ function clientRefDedup() {
3462
3469
  resolvePackage = config.createResolver({ scan: true });
3463
3470
  },
3464
3471
  buildEnd() {
3465
- if (debug5 && dedupedPackages.size > 0) {
3466
- debug5(
3472
+ if (debug4 && dedupedPackages.size > 0) {
3473
+ debug4(
3467
3474
  "client-ref-dedup: redirected %d package(s) (%s)",
3468
3475
  dedupedPackages.size,
3469
3476
  [...dedupedPackages].join(",")
@@ -3505,7 +3512,7 @@ function clientRefDedup() {
3505
3512
  }
3506
3513
  return specifierPromise.then((specifier) => {
3507
3514
  if (!specifier) return;
3508
- if (debug5) dedupedPackages.add(packageName);
3515
+ if (debug4) dedupedPackages.add(packageName);
3509
3516
  return `${DEDUP_PREFIX}${encodeURIComponent(specifier)}`;
3510
3517
  });
3511
3518
  },
@@ -3795,7 +3802,7 @@ import { resolve } from "node:path";
3795
3802
  // package.json
3796
3803
  var package_default = {
3797
3804
  name: "@rangojs/router",
3798
- version: "0.15.1",
3805
+ version: "0.17.0",
3799
3806
  description: "Django-inspired RSC router with composable URL patterns",
3800
3807
  keywords: [
3801
3808
  "react",
@@ -3993,7 +4000,7 @@ var package_default = {
3993
4000
  },
3994
4001
  dependencies: {
3995
4002
  "@types/debug": "^4.1.12",
3996
- "@vitejs/plugin-rsc": "^0.5.34",
4003
+ "@vitejs/plugin-rsc": "^0.5.35",
3997
4004
  debug: "^4.4.1",
3998
4005
  "magic-string": "^0.30.17",
3999
4006
  picomatch: "^4.0.4",
@@ -4006,7 +4013,7 @@ var package_default = {
4006
4013
  "@opentelemetry/api": "^1.9.0",
4007
4014
  "@opentelemetry/context-async-hooks": "^2.9.0",
4008
4015
  "@opentelemetry/sdk-trace-base": "^2.9.0",
4009
- "@playwright/test": "^1.49.1",
4016
+ "@playwright/test": "^1.62.0",
4010
4017
  "@shared/e2e": "workspace:*",
4011
4018
  "@testing-library/dom": "^10.4.1",
4012
4019
  "@testing-library/react": "^16.3.2",
@@ -4026,7 +4033,7 @@ var package_default = {
4026
4033
  "@playwright/test": "^1.49.1",
4027
4034
  "@testing-library/react": ">=16",
4028
4035
  "@vercel/functions": "^3.0.0",
4029
- "@vitejs/plugin-rsc": "^0.5.34",
4036
+ "@vitejs/plugin-rsc": "^0.5.35",
4030
4037
  react: ">=19.2.8 <20",
4031
4038
  "react-dom": ">=19.2.8 <20",
4032
4039
  vite: "^8.0.16",
@@ -5482,7 +5489,7 @@ import { isAbsolute as isAbsolute2, resolve as resolve4 } from "node:path";
5482
5489
  // src/vite/plugins/performance-tracks.ts
5483
5490
  init_debug();
5484
5491
  import { readFile } from "node:fs/promises";
5485
- var debug6 = createRangoDebugger(NS.transform);
5492
+ var debug5 = createRangoDebugger(NS.transform);
5486
5493
  var RSDW_PATCH_RE = /((?:var|let|const)\s+\w+\s*=\s*root\._children\s*,\s*(\w+)\s*=\s*root\._debugInfo\s*[;,])/;
5487
5494
  function buildPatchReplacement(match, debugInfoVar) {
5488
5495
  return `${match}
@@ -5517,7 +5524,7 @@ function performanceTracksOptimizeDepsPlugin() {
5517
5524
  };
5518
5525
  }
5519
5526
  function performanceTracksPlugin() {
5520
- const counter = createCounter(debug6, "performance-tracks");
5527
+ const counter = createCounter(debug5, "performance-tracks");
5521
5528
  return {
5522
5529
  name: "@rangojs/router:performance-tracks",
5523
5530
  buildEnd() {
@@ -5529,7 +5536,7 @@ function performanceTracksPlugin() {
5529
5536
  try {
5530
5537
  const patched = patchRsdwClientDebugInfoRecovery(code);
5531
5538
  if (!patched.debugInfoVar) return;
5532
- debug6?.("patched RSDW client (var: %s)", patched.debugInfoVar);
5539
+ debug5?.("patched RSDW client (var: %s)", patched.debugInfoVar);
5533
5540
  return patched.code;
5534
5541
  } finally {
5535
5542
  counter?.record(id, performance.now() - start);
@@ -6060,7 +6067,7 @@ function createVersionInjectorPlugin(rscEntryPath) {
6060
6067
 
6061
6068
  // src/vite/plugins/cjs-to-esm.ts
6062
6069
  init_debug();
6063
- var debug8 = createRangoDebugger(NS.transform);
6070
+ var debug7 = createRangoDebugger(NS.transform);
6064
6071
  function createCjsToEsmPlugin() {
6065
6072
  let isProduction = false;
6066
6073
  return {
@@ -6074,7 +6081,7 @@ function createCjsToEsmPlugin() {
6074
6081
  if (cleanId.includes("vendor/react-server-dom/client.browser.js")) {
6075
6082
  const isProd = isProduction;
6076
6083
  const cjsFile = isProd ? "./cjs/react-server-dom-webpack-client.browser.production.js" : "./cjs/react-server-dom-webpack-client.browser.development.js";
6077
- debug8?.("cjs-to-esm entry redirect %s", id);
6084
+ debug7?.("cjs-to-esm entry redirect %s", id);
6078
6085
  return {
6079
6086
  code: `export * from "${cjsFile}";`,
6080
6087
  map: null
@@ -6110,7 +6117,7 @@ function createCjsToEsmPlugin() {
6110
6117
  "export const $1 ="
6111
6118
  );
6112
6119
  transformed = license + "\n" + transformed;
6113
- debug8?.("cjs-to-esm body rewrite %s", id);
6120
+ debug7?.("cjs-to-esm body rewrite %s", id);
6114
6121
  return {
6115
6122
  code: transformed,
6116
6123
  map: null
@@ -6138,17 +6145,35 @@ var DEV_DISCOVERY_EPOCH_HEADER = "x-rango-dev-discovery-epoch";
6138
6145
  import React from "react";
6139
6146
 
6140
6147
  // src/server/context.ts
6148
+ import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
6149
+
6150
+ // src/cache/cache-exec-scope.ts
6141
6151
  import { AsyncLocalStorage } from "node:async_hooks";
6152
+
6153
+ // src/cache/taint.ts
6154
+ var cacheExecScopeProbe = () => false;
6155
+ function _setCacheExecScopeProbe(probe) {
6156
+ cacheExecScopeProbe = probe;
6157
+ }
6158
+
6159
+ // src/cache/cache-exec-scope.ts
6160
+ var cacheExecStorage = new AsyncLocalStorage();
6161
+ function isInsideCacheExecScope() {
6162
+ return cacheExecStorage.getStore() !== void 0;
6163
+ }
6164
+ _setCacheExecScopeProbe(isInsideCacheExecScope);
6165
+
6166
+ // src/server/context.ts
6142
6167
  var RSC_CONTEXT_KEY = /* @__PURE__ */ Symbol.for("rangojs-router:rsc-context");
6143
- var RangoContext = globalThis[RSC_CONTEXT_KEY] ??= new AsyncLocalStorage();
6168
+ var RangoContext = globalThis[RSC_CONTEXT_KEY] ??= new AsyncLocalStorage2();
6144
6169
  var LOADER_SCOPE_KEY = /* @__PURE__ */ Symbol.for("rangojs-router:loader-scope");
6145
- var loaderScopeALS = globalThis[LOADER_SCOPE_KEY] ??= new AsyncLocalStorage();
6170
+ var loaderScopeALS = globalThis[LOADER_SCOPE_KEY] ??= new AsyncLocalStorage2();
6146
6171
  var LOADER_BODY_SCOPE_KEY = /* @__PURE__ */ Symbol.for("rangojs-router:loader-body-scope");
6147
- var loaderBodyScopeALS = globalThis[LOADER_BODY_SCOPE_KEY] ??= new AsyncLocalStorage();
6172
+ var loaderBodyScopeALS = globalThis[LOADER_BODY_SCOPE_KEY] ??= new AsyncLocalStorage2();
6148
6173
  var PUSH_CALLBACK_SCOPE_KEY = /* @__PURE__ */ Symbol.for(
6149
6174
  "rangojs-router:push-callback-scope"
6150
6175
  );
6151
- var pushCallbackScopeALS = globalThis[PUSH_CALLBACK_SCOPE_KEY] ??= new AsyncLocalStorage();
6176
+ var pushCallbackScopeALS = globalThis[PUSH_CALLBACK_SCOPE_KEY] ??= new AsyncLocalStorage2();
6152
6177
 
6153
6178
  // src/rsc/shell-capture-constants.ts
6154
6179
  var SHELL_CAPTURE_MAX_WAIT_MS = 15e3;
@@ -6325,7 +6350,7 @@ init_client_ref_hashing();
6325
6350
  // src/vite/plugins/server-ref-hashing.ts
6326
6351
  init_debug();
6327
6352
  init_client_ref_hashing();
6328
- var debug9 = createRangoDebugger(NS.transform);
6353
+ var debug8 = createRangoDebugger(NS.transform);
6329
6354
  function transformServerRefs(code, projectRoot) {
6330
6355
  if (!code.includes("registerServerReference")) return null;
6331
6356
  let hasReplacement = false;
@@ -6342,7 +6367,7 @@ function transformServerRefs(code, projectRoot) {
6342
6367
  return hasReplacement ? result : null;
6343
6368
  }
6344
6369
  function hashServerRefs(projectRoot) {
6345
- const counter = createCounter(debug9, "hash-server-refs");
6370
+ const counter = createCounter(debug8, "hash-server-refs");
6346
6371
  return {
6347
6372
  name: "@rangojs/router:hash-server-refs",
6348
6373
  enforce: "post",
@@ -7207,11 +7232,11 @@ function copyStagedBuildAssets(projectRoot, fileNames) {
7207
7232
 
7208
7233
  // src/vite/discovery/prerender-collection.ts
7209
7234
  init_debug();
7210
- var debug10 = createRangoDebugger(NS.prerender);
7235
+ var debug9 = createRangoDebugger(NS.prerender);
7211
7236
  async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7212
7237
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode) return;
7213
- const overallStart = debug10 ? performance.now() : 0;
7214
- debug10?.(
7238
+ const overallStart = debug9 ? performance.now() : 0;
7239
+ debug9?.(
7215
7240
  "expandPrerenderRoutes: start (%d router manifest(s))",
7216
7241
  allManifests.length
7217
7242
  );
@@ -7264,7 +7289,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7264
7289
  });
7265
7290
  } else {
7266
7291
  if (def?.getParams) {
7267
- const getParamsStart = debug10 ? performance.now() : 0;
7292
+ const getParamsStart = debug9 ? performance.now() : 0;
7268
7293
  try {
7269
7294
  const buildVars = {};
7270
7295
  const buildEnv = state.resolvedBuildEnv;
@@ -7283,7 +7308,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7283
7308
  }
7284
7309
  };
7285
7310
  const paramsList = await def.getParams(getParamsCtx);
7286
- debug10?.(
7311
+ debug9?.(
7287
7312
  "getParams %s -> %d params (%sms)",
7288
7313
  routeName,
7289
7314
  paramsList.length,
@@ -7355,7 +7380,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7355
7380
  }
7356
7381
  }
7357
7382
  if (entries.length === 0) {
7358
- debug10?.(
7383
+ debug9?.(
7359
7384
  "no prerender entries (done in %sms)",
7360
7385
  (performance.now() - overallStart).toFixed(1)
7361
7386
  );
@@ -7366,7 +7391,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7366
7391
  console.log(
7367
7392
  `[rango] Pre-rendering ${entries.length} URL(s)${concurrencyNote}...`
7368
7393
  );
7369
- debug10?.(
7394
+ debug9?.(
7370
7395
  "prerender loop: %d entries, max concurrency %d",
7371
7396
  entries.length,
7372
7397
  maxConcurrency
@@ -7474,7 +7499,7 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7474
7499
  console.log(
7475
7500
  `[rango] Pre-render complete: ${parts.join(", ")} (${totalElapsed}ms total)`
7476
7501
  );
7477
- debug10?.(
7502
+ debug9?.(
7478
7503
  "expandPrerenderRoutes done: %d done, %d skipped, %sms (overall %sms)",
7479
7504
  doneCount,
7480
7505
  skipCount,
@@ -7485,8 +7510,8 @@ async function expandPrerenderRoutes(state, rscEnv, registry, allManifests) {
7485
7510
  async function renderStaticHandlers(state, rscEnv, registry) {
7486
7511
  if (!state.opts?.enableBuildPrerender || !state.isBuildMode || !state.resolvedStaticModules?.size)
7487
7512
  return;
7488
- const overallStart = debug10 ? performance.now() : 0;
7489
- debug10?.(
7513
+ const overallStart = debug9 ? performance.now() : 0;
7514
+ debug9?.(
7490
7515
  "renderStaticHandlers: start (%d static module(s))",
7491
7516
  state.resolvedStaticModules.size
7492
7517
  );
@@ -7576,7 +7601,7 @@ async function renderStaticHandlers(state, rscEnv, registry) {
7576
7601
  console.log(
7577
7602
  `[rango] Static render complete: ${staticParts.join(", ")} (${totalStaticElapsed}ms total)`
7578
7603
  );
7579
- debug10?.(
7604
+ debug9?.(
7580
7605
  "renderStaticHandlers done: %d done, %d skipped, %sms (overall %sms)",
7581
7606
  staticDone,
7582
7607
  staticSkip,
@@ -7707,103 +7732,405 @@ import {
7707
7732
  Suspense as Suspense2
7708
7733
  } from "react";
7709
7734
 
7710
- // src/outlet-context.ts
7711
- import { createContext } from "react";
7712
- var OutletContext = createContext(null);
7713
-
7714
- // src/route-content-wrapper.tsx
7715
- import { Component, Suspense, use } from "react";
7716
-
7717
- // src/outlet-provider.tsx
7718
- import { useContext, useMemo } from "react";
7719
- import { jsx } from "react/jsx-runtime";
7720
-
7721
- // src/decode-loader-results.ts
7722
- import { createElement as createElement2 } from "react";
7723
-
7724
- // src/loader-redirect.tsx
7725
- import { useEffect } from "react";
7726
-
7727
- // src/browser/react/use-router.ts
7728
- import { useContext as useContext3, useMemo as useMemo2 } from "react";
7729
-
7730
- // src/browser/react/context.ts
7731
- import { createContext as createContext2 } from "react";
7732
- var NavigationStoreContext = createContext2(null);
7733
-
7734
- // src/browser/react/use-mount.ts
7735
- import { useContext as useContext2 } from "react";
7736
-
7737
- // src/browser/react/mount-context.ts
7735
+ // src/browser/react/use-handle.ts
7738
7736
  import {
7739
- createContext as createContext3,
7740
- createElement
7737
+ useContext,
7738
+ useState,
7739
+ useEffect,
7740
+ useRef,
7741
+ useOptimistic,
7742
+ startTransition
7741
7743
  } from "react";
7742
- var MountContext = createContext3("/");
7743
7744
 
7744
- // src/browser/react/use-router.ts
7745
- init_loader();
7746
-
7747
- // src/browser/app-version.ts
7748
- var currentVersion2;
7749
- function getAppVersion() {
7750
- return currentVersion2;
7745
+ // src/missing-id-error.ts
7746
+ var SELF_FILES = /* @__PURE__ */ new Set([
7747
+ "missing-id-error",
7748
+ "loader",
7749
+ "loader.rsc",
7750
+ "handle"
7751
+ ]);
7752
+ function findUserCallSite() {
7753
+ try {
7754
+ const stack = new Error().stack;
7755
+ if (!stack) return void 0;
7756
+ for (const frame of stack.split("\n").slice(1)) {
7757
+ const m = frame.match(
7758
+ /(?:\(|@|\s)(?:file:\/\/)?((?:\/|[A-Za-z]:[\\/])[^()\s]+?\.(?:ts|tsx|js|jsx|mts|cts)):(\d+):(\d+)\)?/
7759
+ );
7760
+ if (!m) continue;
7761
+ const path6 = m[1];
7762
+ if (path6.includes("node_modules") || path6.includes("@rangojs/router")) {
7763
+ continue;
7764
+ }
7765
+ const base = path6.split(/[\\/]/).pop().replace(/\.(?:ts|tsx|js|jsx|mts|cts)$/, "");
7766
+ if (SELF_FILES.has(base)) continue;
7767
+ return `${path6}:${m[2]}:${m[3]}`;
7768
+ }
7769
+ } catch {
7770
+ }
7771
+ return void 0;
7772
+ }
7773
+ function missingInjectedIdError(kind, fnName) {
7774
+ const site = findUserCallSite();
7775
+ const at = site ? ` (created at ${site})` : "";
7776
+ return new Error(
7777
+ `[rango] ${kind} is missing $$id${at}.
7778
+ The @rangojs/router:expose-internal-ids Vite transform injects ${fnName}()'s stable $$id from an EXPORTED const declaration only:
7779
+ export const X = ${fnName}(...)
7780
+ const X = ${fnName}(...); export { X }
7781
+ A non-exported const, an \`export let/var\`, or an inline ${fnName}(...) call gets no $$id \u2014 export it as \`export const\`. (A matching "Unsupported ${fnName} shape" warning names the exact file:line.)`
7782
+ );
7751
7783
  }
7752
7784
 
7753
- // src/loader-redirect.tsx
7754
- init_validate_redirect_origin();
7755
- init_redirect_origin();
7756
-
7757
- // src/route-content-wrapper.tsx
7758
- import { jsx as jsx2 } from "react/jsx-runtime";
7785
+ // src/handle.ts
7786
+ function defaultCollect(segments) {
7787
+ return segments;
7788
+ }
7789
+ var collectRegistry = /* @__PURE__ */ new Map();
7790
+ var runtimeHandleIdCounter = 0;
7791
+ function createHandle(collect, __injectedId) {
7792
+ let handleId = __injectedId ?? "";
7793
+ if (!handleId) {
7794
+ if (isUnderTestRunner()) {
7795
+ handleId = `__rango_runtime_handle_${runtimeHandleIdCounter++}`;
7796
+ } else if (process.env.NODE_ENV !== "production") {
7797
+ throw missingInjectedIdError("Handle", "createHandle");
7798
+ } else {
7799
+ throw new Error(
7800
+ "[rango] Handle is missing $$id \u2014 the build plugin did not inject one. Export it as `export const X = createHandle(...)`."
7801
+ );
7802
+ }
7803
+ }
7804
+ const collectFn = collect ?? defaultCollect;
7805
+ collectRegistry.set(
7806
+ handleId,
7807
+ collectFn
7808
+ );
7809
+ return {
7810
+ __brand: "handle",
7811
+ $$id: handleId
7812
+ };
7813
+ }
7759
7814
 
7760
- // src/use-loader.tsx
7761
- import {
7762
- isValidElement,
7763
- startTransition,
7764
- use as use2,
7765
- useCallback,
7766
- useContext as useContext4,
7767
- useEffect as useEffect2,
7768
- useMemo as useMemo3,
7769
- useRef,
7770
- useState
7771
- } from "react";
7815
+ // src/browser/react/context.ts
7816
+ import { createContext } from "react";
7817
+ var NavigationStoreContext = createContext(null);
7772
7818
 
7773
- // src/loader-store.ts
7774
- var EMPTY_SNAPSHOT = Object.freeze({
7775
- value: void 0,
7776
- hasValue: false,
7777
- error: null,
7778
- isLoading: false,
7779
- requestId: 0
7780
- });
7781
- function normalizeGroups(group) {
7782
- if (group === void 0) return [];
7783
- if (typeof group === "string") return [group];
7784
- return [...new Set(group)];
7819
+ // src/handles/meta.ts
7820
+ function isUnsetDescriptor(descriptor) {
7821
+ return typeof descriptor === "object" && descriptor !== null && "unset" in descriptor && typeof descriptor.unset === "string";
7785
7822
  }
7786
- var LoaderStore = class {
7787
- constructor() {
7788
- this.entries = /* @__PURE__ */ new Map();
7789
- /** loader.$$id -> set of bucket keys, so clearFamily() can reach every bucket. */
7790
- this.families = /* @__PURE__ */ new Map();
7791
- /** refresh group name -> set of bucket keys, for refreshGroups(). */
7792
- this.groups = /* @__PURE__ */ new Map();
7823
+ function isTitleDescriptor(descriptor) {
7824
+ return typeof descriptor === "object" && descriptor !== null && "title" in descriptor;
7825
+ }
7826
+ function isTitleTemplate(title) {
7827
+ return typeof title === "object" && title !== null && "template" in title && "default" in title;
7828
+ }
7829
+ function isAbsoluteTitle(title) {
7830
+ return typeof title === "object" && title !== null && "absolute" in title;
7831
+ }
7832
+ function getMetaKey(descriptor) {
7833
+ if (isUnsetDescriptor(descriptor)) {
7834
+ return void 0;
7793
7835
  }
7794
- getOrCreate(bucketKey) {
7795
- let e = this.entries.get(bucketKey);
7796
- if (!e) {
7797
- e = {
7798
- snapshot: EMPTY_SNAPSHOT,
7799
- listeners: /* @__PURE__ */ new Set(),
7800
- latestRequestId: 0,
7801
- loaderId: bucketKey,
7802
- sticky: false,
7836
+ if ("charSet" in descriptor) {
7837
+ return "charSet";
7838
+ }
7839
+ if ("title" in descriptor) {
7840
+ return "title";
7841
+ }
7842
+ if ("name" in descriptor && "content" in descriptor) {
7843
+ return `name:${descriptor.name}`;
7844
+ }
7845
+ if ("property" in descriptor && "content" in descriptor) {
7846
+ return `property:${descriptor.property}`;
7847
+ }
7848
+ if ("httpEquiv" in descriptor && "content" in descriptor) {
7849
+ return `httpEquiv:${descriptor.httpEquiv}`;
7850
+ }
7851
+ if ("script:ld+json" in descriptor) {
7852
+ return void 0;
7853
+ }
7854
+ if ("tagName" in descriptor) {
7855
+ if (descriptor.tagName === "link" && "rel" in descriptor) {
7856
+ const uniqueRels = ["canonical", "icon", "apple-touch-icon"];
7857
+ if (uniqueRels.includes(descriptor.rel)) {
7858
+ return `link:${descriptor.rel}`;
7859
+ }
7860
+ }
7861
+ return void 0;
7862
+ }
7863
+ return void 0;
7864
+ }
7865
+ var defaultMetaDescriptors = [
7866
+ { charSet: "utf-8" },
7867
+ { name: "viewport", content: "width=device-width, initial-scale=1" }
7868
+ ];
7869
+ function addOrReplace(result, keyToIndex, descriptor, key) {
7870
+ if (key !== void 0 && keyToIndex.has(key)) {
7871
+ result[keyToIndex.get(key)] = descriptor;
7872
+ } else {
7873
+ if (key !== void 0) {
7874
+ keyToIndex.set(key, result.length);
7875
+ }
7876
+ result.push(descriptor);
7877
+ }
7878
+ }
7879
+ function updateIndicesAfterRemoval(keyToIndex, removedIndex) {
7880
+ for (const [key, index] of keyToIndex) {
7881
+ if (index > removedIndex) {
7882
+ keyToIndex.set(key, index - 1);
7883
+ }
7884
+ }
7885
+ }
7886
+ function collectMeta(segments) {
7887
+ const result = [];
7888
+ const keyToIndex = /* @__PURE__ */ new Map();
7889
+ let titleTemplate;
7890
+ for (const descriptor of defaultMetaDescriptors) {
7891
+ const key = getMetaKey(descriptor);
7892
+ if (key !== void 0) {
7893
+ keyToIndex.set(key, result.length);
7894
+ }
7895
+ result.push(descriptor);
7896
+ }
7897
+ for (const descriptors of segments) {
7898
+ for (const descriptor of descriptors) {
7899
+ if (isUnsetDescriptor(descriptor)) {
7900
+ const keyToRemove = descriptor.unset;
7901
+ if (keyToIndex.has(keyToRemove)) {
7902
+ const idx = keyToIndex.get(keyToRemove);
7903
+ result.splice(idx, 1);
7904
+ keyToIndex.delete(keyToRemove);
7905
+ updateIndicesAfterRemoval(keyToIndex, idx);
7906
+ }
7907
+ continue;
7908
+ }
7909
+ if (isTitleDescriptor(descriptor)) {
7910
+ const titleValue = descriptor.title;
7911
+ if (isTitleTemplate(titleValue)) {
7912
+ titleTemplate = titleValue.template;
7913
+ addOrReplace(
7914
+ result,
7915
+ keyToIndex,
7916
+ { title: titleValue.default },
7917
+ "title"
7918
+ );
7919
+ continue;
7920
+ }
7921
+ if (isAbsoluteTitle(titleValue)) {
7922
+ addOrReplace(
7923
+ result,
7924
+ keyToIndex,
7925
+ { title: titleValue.absolute },
7926
+ "title"
7927
+ );
7928
+ continue;
7929
+ }
7930
+ const finalTitle = titleTemplate ? titleTemplate.split("%s").join(titleValue) : titleValue;
7931
+ addOrReplace(
7932
+ result,
7933
+ keyToIndex,
7934
+ { title: finalTitle },
7935
+ "title"
7936
+ );
7937
+ continue;
7938
+ }
7939
+ const key = getMetaKey(descriptor);
7940
+ addOrReplace(result, keyToIndex, descriptor, key);
7941
+ }
7942
+ }
7943
+ return result;
7944
+ }
7945
+ var Meta = createHandle(collectMeta, "__rsc_router_meta__");
7946
+
7947
+ // src/theme/theme-context.ts
7948
+ import { createContext as createContext2, useContext as useContext2 } from "react";
7949
+ var ThemeContext = createContext2(null);
7950
+
7951
+ // src/browser/react/nonce-context.ts
7952
+ import { createContext as createContext3, useContext as useContext3 } from "react";
7953
+ var NonceContext = createContext3(void 0);
7954
+
7955
+ // src/handles/MetaTags.tsx
7956
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7957
+
7958
+ // src/handles/Scripts.tsx
7959
+ import { useState as useState2 } from "react";
7960
+
7961
+ // src/handles/script.ts
7962
+ function validateConfigDev(config) {
7963
+ if (process.env.NODE_ENV === "production") return;
7964
+ const c = config;
7965
+ if (c.src != null && c.children != null) {
7966
+ console.warn(
7967
+ `[Script] A config has both "src" and "children"; they are mutually exclusive \u2014 "src" wins and the inline body is ignored.`
7968
+ );
7969
+ } else if (c.src == null && c.children == null) {
7970
+ console.warn(
7971
+ `[Script] A config has neither "src" nor "children"; it injects nothing.`
7972
+ );
7973
+ } else if (c.src == null && c.id == null) {
7974
+ console.warn(
7975
+ `[Script] An inline script was pushed without an "id" and cannot be deduplicated. Pass an "id" so a layout + child pushing the same script inject it only once.`
7976
+ );
7977
+ }
7978
+ if (c.async && c.defer) {
7979
+ console.warn(
7980
+ `[Script] A config has both "async" and "defer"; they are mutually exclusive.`
7981
+ );
7982
+ }
7983
+ }
7984
+ function collectScripts(segments) {
7985
+ const result = [];
7986
+ const keyToIndex = /* @__PURE__ */ new Map();
7987
+ for (const configs of segments) {
7988
+ for (const config of configs) {
7989
+ validateConfigDev(config);
7990
+ const isAsyncResource = config.src != null && config.async === true;
7991
+ const key = isAsyncResource ? config.src : config.id ?? config.src;
7992
+ if (key === void 0) {
7993
+ result.push(config);
7994
+ continue;
7995
+ }
7996
+ const existing = keyToIndex.get(key);
7997
+ if (existing !== void 0) {
7998
+ result[existing] = config;
7999
+ } else {
8000
+ keyToIndex.set(key, result.length);
8001
+ result.push(config);
8002
+ }
8003
+ }
8004
+ }
8005
+ return result;
8006
+ }
8007
+ var Script = createHandle(collectScripts, "__rsc_router_script__");
8008
+
8009
+ // src/handles/Scripts.tsx
8010
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
8011
+
8012
+ // src/browser/react/ScrollRestoration.tsx
8013
+ import { useEffect as useEffect2 } from "react";
8014
+
8015
+ // src/browser/scroll-restoration.ts
8016
+ init_logging();
8017
+
8018
+ // src/outlet-context.ts
8019
+ import { createContext as createContext4 } from "react";
8020
+ var OutletContext = createContext4(null);
8021
+
8022
+ // src/route-content-wrapper.tsx
8023
+ import { Component, Suspense, use } from "react";
8024
+
8025
+ // src/outlet-provider.tsx
8026
+ import { useContext as useContext4, useMemo } from "react";
8027
+ import { jsx as jsx3 } from "react/jsx-runtime";
8028
+
8029
+ // src/decode-loader-results.ts
8030
+ import { createElement as createElement2 } from "react";
8031
+
8032
+ // src/loader-redirect.tsx
8033
+ import { useEffect as useEffect3 } from "react";
8034
+
8035
+ // src/browser/react/use-router.ts
8036
+ import { useContext as useContext6, useMemo as useMemo2 } from "react";
8037
+
8038
+ // src/browser/react/use-mount.ts
8039
+ import { useContext as useContext5 } from "react";
8040
+
8041
+ // src/browser/react/mount-context.ts
8042
+ import {
8043
+ createContext as createContext5,
8044
+ createElement
8045
+ } from "react";
8046
+ var MountContext = createContext5("/");
8047
+
8048
+ // src/browser/react/use-router.ts
8049
+ init_loader();
8050
+
8051
+ // src/browser/app-version.ts
8052
+ var currentVersion2;
8053
+ function getAppVersion() {
8054
+ return currentVersion2;
8055
+ }
8056
+
8057
+ // src/loader-redirect.tsx
8058
+ init_validate_redirect_origin();
8059
+ init_redirect_origin();
8060
+
8061
+ // src/route-content-wrapper.tsx
8062
+ import { jsx as jsx4 } from "react/jsx-runtime";
8063
+
8064
+ // src/use-loader.tsx
8065
+ import {
8066
+ isValidElement,
8067
+ startTransition as startTransition2,
8068
+ use as use2,
8069
+ useCallback,
8070
+ useContext as useContext7,
8071
+ useEffect as useEffect4,
8072
+ useMemo as useMemo3,
8073
+ useOptimistic as useOptimistic2,
8074
+ useRef as useRef2,
8075
+ useState as useState3
8076
+ } from "react";
8077
+
8078
+ // src/thenable-status.ts
8079
+ function unwrapsSynchronously(stream) {
8080
+ const status = stream.status;
8081
+ return status === "fulfilled" || status === "resolved_model" || status === "resolved_module";
8082
+ }
8083
+
8084
+ // src/loader-store.ts
8085
+ var EMPTY_SNAPSHOT = Object.freeze({
8086
+ value: void 0,
8087
+ hasValue: false,
8088
+ error: null,
8089
+ isLoading: false,
8090
+ requestId: 0
8091
+ });
8092
+ function normalizeGroups(group) {
8093
+ if (group === void 0) return [];
8094
+ if (typeof group === "string") return [group];
8095
+ return [...new Set(group)];
8096
+ }
8097
+ var LoaderStore = class {
8098
+ constructor() {
8099
+ this.entries = /* @__PURE__ */ new Map();
8100
+ /** loader.$$id -> set of bucket keys, so clearFamily() can reach every bucket. */
8101
+ this.families = /* @__PURE__ */ new Map();
8102
+ /** refresh group name -> set of bucket keys, for refreshGroups(). */
8103
+ this.groups = /* @__PURE__ */ new Map();
8104
+ /**
8105
+ * loader.$$id -> per-loader streams a committed navigation is still
8106
+ * receiving. Registered and announced by announcePendingStreams(), which
8107
+ * browser/partial-update.ts calls INSIDE every transition commit, so a
8108
+ * reader whose content is held on screen by that transition pins
8109
+ * `isLoading: true` for the data it is still showing until the transition
8110
+ * lands (use-loader.tsx). Family-keyed on purpose: a persisting layout
8111
+ * reader of the same createLoader the child is re-running is pinned too
8112
+ * (the loader is in flight; not "this segment's copy is being replaced" —
8113
+ * issue #862, working as designed). The new tree's own read of the loader
8114
+ * suspends at the read site and never observes the flag. A Set, not a
8115
+ * flag: overlapping navigations (second click before the first stream
8116
+ * lands) keep the family pending until the LAST stream settles.
8117
+ */
8118
+ this.pendingStreams = /* @__PURE__ */ new Map();
8119
+ }
8120
+ getOrCreate(bucketKey) {
8121
+ let e = this.entries.get(bucketKey);
8122
+ if (!e) {
8123
+ e = {
8124
+ snapshot: EMPTY_SNAPSHOT,
8125
+ listeners: /* @__PURE__ */ new Set(),
8126
+ latestRequestId: 0,
8127
+ loaderId: bucketKey,
8128
+ sticky: false,
7803
8129
  pendingClear: false,
7804
8130
  clearWhenSettled: false,
7805
8131
  groups: /* @__PURE__ */ new Map(),
7806
- refetch: void 0
8132
+ refetch: void 0,
8133
+ streamPendingListeners: /* @__PURE__ */ new Set()
7807
8134
  };
7808
8135
  this.entries.set(bucketKey, e);
7809
8136
  }
@@ -7837,8 +8164,11 @@ var LoaderStore = class {
7837
8164
  e.pendingClear = false;
7838
8165
  e.clearWhenSettled = false;
7839
8166
  e.listeners.add(cb);
8167
+ const onStreamPending = options?.onStreamPending;
8168
+ if (onStreamPending) e.streamPendingListeners.add(onStreamPending);
7840
8169
  return () => {
7841
8170
  e.listeners.delete(cb);
8171
+ if (onStreamPending) e.streamPendingListeners.delete(onStreamPending);
7842
8172
  for (const group of groups) this.releaseGroup(group, bucketKey, e);
7843
8173
  this.maybeScheduleRefcountClear(bucketKey, e);
7844
8174
  };
@@ -8081,7 +8411,64 @@ var LoaderStore = class {
8081
8411
  for (const cb of e.listeners) cb();
8082
8412
  }
8083
8413
  /**
8084
- * Test-only escape hatch. Drops every entry. Production code should never
8414
+ * Register a still-streaming per-loader result for `loaderId`. No-op for a
8415
+ * settled Flight chunk (unwrapsSynchronously / "rejected"): the forceAwait
8416
+ * and action lanes commit settled chunks, and a { ssr: false } loader is
8417
+ * awaited before flush. Released as the stream settles, either way.
8418
+ */
8419
+ trackPendingStream(loaderId, stream) {
8420
+ if (unwrapsSynchronously(stream) || stream.status === "rejected") {
8421
+ return;
8422
+ }
8423
+ let set = this.pendingStreams.get(loaderId);
8424
+ if (set?.has(stream)) return;
8425
+ if (!set) {
8426
+ set = /* @__PURE__ */ new Set();
8427
+ this.pendingStreams.set(loaderId, set);
8428
+ }
8429
+ set.add(stream);
8430
+ const release = () => {
8431
+ const current = this.pendingStreams.get(loaderId);
8432
+ if (!current || !current.delete(stream)) return;
8433
+ if (current.size === 0) this.pendingStreams.delete(loaderId);
8434
+ };
8435
+ stream.then(release, release);
8436
+ }
8437
+ /** True while any navigation stream for `loaderId` is still pending. */
8438
+ isStreamPending(loaderId) {
8439
+ return this.pendingStreams.has(loaderId);
8440
+ }
8441
+ /**
8442
+ * Register the loader segments of a tree being committed whose data is
8443
+ * still a pending promise, then fire `onStreamPending` on every subscriber
8444
+ * of every family with a pending stream. MUST run inside the startTransition
8445
+ * that commits the tree: the hook answers with a useOptimistic update, which
8446
+ * React reverts exactly when that transition commits — so the held reader
8447
+ * reports isLoading:true for as long as its data stays on screen and not a
8448
+ * frame longer. Outside a transition React reverts an optimistic update on
8449
+ * the next render, pinning nothing useful. Registering here rather than in
8450
+ * the tree build keeps register + announce in one call; settled chunks
8451
+ * (cached/reused segments, forceAwait lanes) are skipped by
8452
+ * trackPendingStream.
8453
+ */
8454
+ announcePendingStreams(segments) {
8455
+ for (const segment of segments) {
8456
+ if (segment.type === "loader" && segment.loaderId && segment.loaderData instanceof Promise) {
8457
+ this.trackPendingStream(segment.loaderId, segment.loaderData);
8458
+ }
8459
+ }
8460
+ for (const loaderId of this.pendingStreams.keys()) {
8461
+ const fam = this.families.get(loaderId);
8462
+ if (!fam) continue;
8463
+ for (const bucketKey of fam) {
8464
+ const e = this.entries.get(bucketKey);
8465
+ if (!e) continue;
8466
+ for (const cb of e.streamPendingListeners) cb();
8467
+ }
8468
+ }
8469
+ }
8470
+ /**
8471
+ * Test-only escape hatch. Drops every entry. Production code should never
8085
8472
  * call this; the store is process-scoped and lives for the tab's lifetime.
8086
8473
  * @internal
8087
8474
  */
@@ -8089,85 +8476,78 @@ var LoaderStore = class {
8089
8476
  this.entries.clear();
8090
8477
  this.families.clear();
8091
8478
  this.groups.clear();
8479
+ this.pendingStreams.clear();
8092
8480
  }
8093
8481
  };
8094
8482
  var loaderStore = new LoaderStore();
8095
8483
 
8096
8484
  // src/browser/react/use-navigation.ts
8097
8485
  import {
8098
- useContext as useContext5,
8099
- useState as useState2,
8100
- useEffect as useEffect3,
8101
- useOptimistic,
8102
- startTransition as startTransition2,
8103
- useRef as useRef2
8486
+ useContext as useContext8,
8487
+ useState as useState4,
8488
+ useEffect as useEffect5,
8489
+ useOptimistic as useOptimistic3,
8490
+ startTransition as startTransition3,
8491
+ useRef as useRef3
8104
8492
  } from "react";
8105
8493
 
8106
8494
  // src/browser/react/use-pathname.ts
8107
- import { useContext as useContext6, useState as useState3, useEffect as useEffect4, useRef as useRef3 } from "react";
8495
+ import { useContext as useContext9, useState as useState5, useEffect as useEffect6, useRef as useRef4 } from "react";
8108
8496
 
8109
8497
  // src/client-urls/optimistic-location.ts
8110
- import { createContext as createContext4 } from "react";
8111
- var OptimisticLocationContext = createContext4(null);
8498
+ import { createContext as createContext6 } from "react";
8499
+ var OptimisticLocationContext = createContext6(null);
8112
8500
 
8113
8501
  // src/browser/react/use-search-params.ts
8114
8502
  import {
8115
8503
  useCallback as useCallback2,
8116
- useContext as useContext7,
8117
- useEffect as useEffect5,
8504
+ useContext as useContext10,
8505
+ useEffect as useEffect7,
8118
8506
  useMemo as useMemo4,
8119
- useRef as useRef4,
8120
- useState as useState4
8507
+ useRef as useRef5,
8508
+ useState as useState6
8121
8509
  } from "react";
8122
8510
 
8123
8511
  // src/browser/react/use-params.ts
8124
- import { useContext as useContext8, useState as useState5, useEffect as useEffect6, useRef as useRef5 } from "react";
8512
+ import { useContext as useContext11, useState as useState7, useEffect as useEffect8, useRef as useRef6 } from "react";
8125
8513
  var EMPTY_PARAMS = Object.freeze({});
8126
8514
 
8127
- // src/browser/react/nonce-context.ts
8128
- import { createContext as createContext5, useContext as useContext9 } from "react";
8129
- var NonceContext = createContext5(void 0);
8130
-
8131
8515
  // src/browser/react/use-action.ts
8132
8516
  import {
8133
- useContext as useContext10,
8134
- useState as useState6,
8135
- useEffect as useEffect7,
8136
- useRef as useRef6,
8137
- useOptimistic as useOptimistic2,
8138
- startTransition as startTransition3
8517
+ useContext as useContext12,
8518
+ useState as useState8,
8519
+ useEffect as useEffect9,
8520
+ useRef as useRef7,
8521
+ useOptimistic as useOptimistic4,
8522
+ startTransition as startTransition4
8139
8523
  } from "react";
8140
8524
 
8141
8525
  // src/browser/react/use-segments.ts
8142
- import { useContext as useContext11, useState as useState7, useEffect as useEffect8, useRef as useRef7 } from "react";
8526
+ import { useContext as useContext13, useState as useState9, useEffect as useEffect10, useRef as useRef8 } from "react";
8143
8527
 
8144
8528
  // src/browser/react/NavigationProvider.tsx
8145
8529
  import {
8146
- useState as useState10,
8147
- useEffect as useEffect10,
8530
+ useState as useState12,
8531
+ useEffect as useEffect12,
8148
8532
  useLayoutEffect,
8149
8533
  useCallback as useCallback4,
8150
8534
  useMemo as useMemo6,
8151
- useRef as useRef9,
8535
+ useRef as useRef10,
8152
8536
  use as use3
8153
8537
  } from "react";
8154
8538
 
8155
8539
  // src/root-error-boundary.tsx
8156
- import { Component as Component2, useState as useState8 } from "react";
8157
- import { jsx as jsx3, jsxs } from "react/jsx-runtime";
8540
+ import { Component as Component2, useState as useState10 } from "react";
8541
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
8158
8542
 
8159
8543
  // src/theme/ThemeProvider.tsx
8160
8544
  import {
8161
8545
  useCallback as useCallback3,
8162
- useEffect as useEffect9,
8546
+ useEffect as useEffect11,
8163
8547
  useMemo as useMemo5,
8164
- useState as useState9
8548
+ useState as useState11
8165
8549
  } from "react";
8166
8550
 
8167
- // src/theme/theme-context.ts
8168
- import { createContext as createContext6, useContext as useContext12 } from "react";
8169
- var ThemeContext = createContext6(null);
8170
-
8171
8551
  // src/theme/constants.ts
8172
8552
  var THEME_COOKIE = {
8173
8553
  maxAge: 60 * 60 * 24 * 365,
@@ -8177,15 +8557,10 @@ var THEME_COOKIE = {
8177
8557
  };
8178
8558
 
8179
8559
  // src/theme/ThemeProvider.tsx
8180
- import { jsx as jsx4 } from "react/jsx-runtime";
8560
+ import { jsx as jsx6 } from "react/jsx-runtime";
8181
8561
 
8182
8562
  // src/browser/react/NavigationProvider.tsx
8183
8563
  init_loader();
8184
-
8185
- // src/browser/scroll-restoration.ts
8186
- init_logging();
8187
-
8188
- // src/browser/react/NavigationProvider.tsx
8189
8564
  init_logging();
8190
8565
 
8191
8566
  // src/browser/navigation-store.ts
@@ -8193,28 +8568,28 @@ init_cache();
8193
8568
  init_rango_state();
8194
8569
 
8195
8570
  // src/browser/react/NavigationProvider.tsx
8196
- import { jsx as jsx5 } from "react/jsx-runtime";
8571
+ import { jsx as jsx7 } from "react/jsx-runtime";
8197
8572
 
8198
8573
  // src/browser/react/Link.tsx
8199
8574
  import {
8200
8575
  forwardRef,
8201
8576
  useCallback as useCallback5,
8202
- useContext as useContext14,
8203
- useEffect as useEffect13,
8577
+ useContext as useContext15,
8578
+ useEffect as useEffect15,
8204
8579
  useMemo as useMemo7,
8205
- useRef as useRef12,
8580
+ useRef as useRef13,
8206
8581
  useSyncExternalStore
8207
8582
  } from "react";
8208
8583
 
8209
8584
  // src/browser/react/use-link-status.ts
8210
8585
  import {
8211
8586
  createContext as createContext7,
8212
- useContext as useContext13,
8213
- useState as useState11,
8214
- useEffect as useEffect11,
8215
- useRef as useRef10,
8216
- useOptimistic as useOptimistic3,
8217
- startTransition as startTransition4
8587
+ useContext as useContext14,
8588
+ useState as useState13,
8589
+ useEffect as useEffect13,
8590
+ useRef as useRef11,
8591
+ useOptimistic as useOptimistic5,
8592
+ startTransition as startTransition5
8218
8593
  } from "react";
8219
8594
  var LinkContext = createContext7(
8220
8595
  null
@@ -8443,496 +8818,230 @@ function isPrefetchScopeDisabled(element) {
8443
8818
  }
8444
8819
 
8445
8820
  // src/browser/react/location-state.ts
8446
- import { useState as useState12, useEffect as useEffect12, useRef as useRef11 } from "react";
8447
-
8448
- // src/browser/react/location-state-shared.ts
8449
- function isLocationStateEntry(value) {
8450
- return value !== null && typeof value === "object" && "__rsc_ls_key" in value && "__rsc_ls_value" in value && typeof value.__rsc_ls_key === "string";
8451
- }
8452
- function resolveLocationStateEntries(entries) {
8453
- const result = {};
8454
- for (const entry of entries) {
8455
- result[entry.__rsc_ls_key] = entry.__rsc_ls_lazy ? entry.__rsc_ls_value() : entry.__rsc_ls_value;
8456
- }
8457
- return result;
8458
- }
8821
+ import { useState as useState14, useEffect as useEffect14, useRef as useRef12 } from "react";
8459
8822
 
8460
- // src/browser/react/Link.tsx
8461
- init_loader();
8462
- import { jsx as jsx6 } from "react/jsx-runtime";
8463
- var IGNORE_STRATEGY_CHANGES = (_listener) => () => {
8464
- };
8465
- function isExternalUrl(href2, origin) {
8466
- if (href2.startsWith("//")) return true;
8467
- if (href2.startsWith("http://") || href2.startsWith("https://")) {
8468
- if (!origin) return false;
8469
- try {
8470
- return new URL(href2).origin !== origin;
8471
- } catch {
8472
- return false;
8473
- }
8474
- }
8475
- if (/^[a-z][a-z0-9+.-]*:/i.test(href2)) {
8476
- return true;
8477
- }
8478
- return false;
8479
- }
8480
- var Link = forwardRef(function Link2({
8481
- to,
8482
- replace = false,
8483
- scroll = true,
8484
- reloadDocument = false,
8485
- revalidate: revalidate2,
8486
- prefetch,
8487
- prefetchKey,
8488
- state,
8489
- children,
8490
- onClick,
8491
- ...props
8492
- }, ref) {
8493
- const ctx = useContext14(NavigationStoreContext);
8494
- const storeLocation = ctx?.eventController.getState().location;
8495
- const origin = storeLocation?.origin ?? (typeof window !== "undefined" ? window.location.origin : void 0);
8496
- const isExternal = isExternalUrl(to, origin);
8497
- const resolvedTo = useMemo7(() => {
8498
- if (isExternal) return to;
8499
- const bn = ctx?.basename;
8500
- if (!bn || !to.startsWith("/") || to.startsWith(bn + "/") || to === bn)
8501
- return to;
8502
- return to === "/" ? bn : bn + to;
8503
- }, [to, isExternal, ctx?.basename]);
8504
- const configuredStrategy = prefetch ?? ctx?.defaultPrefetch ?? getDefaultPrefetchStrategy();
8505
- const resolvedStrategy = useSyncExternalStore(
8506
- configuredStrategy === "adaptive" ? subscribeToAdaptiveStrategyChange : IGNORE_STRATEGY_CHANGES,
8507
- () => resolveAdaptiveStrategy(configuredStrategy),
8508
- () => configuredStrategy === "adaptive" ? "hover" : configuredStrategy
8509
- );
8510
- const internalRef = useRef12(null);
8511
- const setRef = useCallback5(
8512
- (node) => {
8513
- internalRef.current = node;
8514
- if (typeof ref === "function") {
8515
- ref(node);
8516
- } else if (ref) {
8517
- ref.current = node;
8518
- }
8519
- },
8520
- [ref]
8521
- );
8522
- const stateRef = useRef12(state);
8523
- stateRef.current = state;
8524
- const handleClick = useCallback5(
8525
- (e) => {
8526
- onClick?.(e);
8527
- if (e.defaultPrevented) return;
8528
- if (isExternal) return;
8529
- if (reloadDocument) return;
8530
- if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
8531
- if (e.currentTarget.hasAttribute("download"))
8532
- return;
8533
- const target = e.currentTarget.target;
8534
- if (target && target !== "_self") return;
8535
- if (isHashOnlyNavigation(e.currentTarget)) {
8536
- return;
8537
- }
8538
- if (!ctx?.navigate) {
8539
- return;
8540
- }
8541
- e.preventDefault();
8542
- e.stopPropagation();
8543
- const currentState = stateRef.current;
8544
- let resolvedState;
8545
- if (Array.isArray(currentState) && currentState.length > 0 && isLocationStateEntry(currentState[0])) {
8546
- resolvedState = resolveLocationStateEntries(
8547
- currentState
8548
- );
8549
- } else if (typeof currentState === "function") {
8550
- resolvedState = currentState();
8551
- } else if (currentState != null) {
8552
- resolvedState = currentState;
8553
- }
8554
- ctx.navigate(resolvedTo, {
8555
- replace,
8556
- scroll,
8557
- state: resolvedState,
8558
- revalidate: revalidate2
8559
- });
8560
- },
8561
- [
8562
- resolvedTo,
8563
- isExternal,
8564
- reloadDocument,
8565
- replace,
8566
- scroll,
8567
- revalidate2,
8568
- ctx,
8569
- onClick
8570
- ]
8571
- );
8572
- const handleMouseEnter = useCallback5(() => {
8573
- const element = internalRef.current;
8574
- if ((resolvedStrategy === "hover" || resolvedStrategy === "viewport") && !isExternal && ctx?.store && (!element || !isHashOnlyNavigation(element) && !isPrefetchScopeDisabled(element))) {
8575
- const segmentState = ctx.store.getSegmentState();
8576
- prefetchDirect2(
8577
- resolvedTo,
8578
- segmentState.currentSegmentIds,
8579
- getAppVersion(),
8580
- ctx.store.getRouterId?.(),
8581
- prefetchKey
8582
- );
8583
- }
8584
- }, [resolvedStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
8585
- useEffect13(() => {
8586
- if (isExternal || !ctx?.store) return;
8587
- const isViewport = resolvedStrategy === "viewport";
8588
- const isRender = resolvedStrategy === "render";
8589
- if (!isViewport && !isRender) return;
8590
- const armPrefetch = () => {
8591
- const element2 = internalRef.current;
8592
- if (element2 && (isHashOnlyNavigation(element2) || isPrefetchScopeDisabled(element2))) {
8593
- return () => {
8594
- };
8595
- }
8596
- let cancelled = false;
8597
- let unsubIdle;
8598
- let stopObserving;
8599
- const triggerPrefetch = () => {
8600
- if (cancelled) return;
8601
- const currentElement = internalRef.current;
8602
- if (currentElement && isPrefetchScopeDisabled(currentElement)) return;
8603
- const segmentState = ctx.store.getSegmentState();
8604
- prefetchQueued2(
8605
- resolvedTo,
8606
- segmentState.currentSegmentIds,
8607
- getAppVersion(),
8608
- ctx.store.getRouterId?.(),
8609
- prefetchKey
8610
- );
8611
- };
8612
- if (isRender) {
8613
- unsubIdle = schedulePrefetchWhenRouterIdle(
8614
- ctx.eventController,
8615
- triggerPrefetch
8616
- );
8617
- } else {
8618
- const viewportElement = internalRef.current;
8619
- if (viewportElement) {
8620
- stopObserving = observeForPrefetch(viewportElement, () => {
8621
- unsubIdle = schedulePrefetchWhenRouterIdle(
8622
- ctx.eventController,
8623
- triggerPrefetch
8624
- );
8625
- });
8626
- }
8627
- }
8628
- return () => {
8629
- cancelled = true;
8630
- unsubIdle?.();
8631
- stopObserving?.();
8632
- };
8633
- };
8634
- let disarmPrefetch = armPrefetch();
8635
- const element = internalRef.current;
8636
- const unsubscribeScope = element ? subscribeToPrefetchScopeChange(element, () => {
8637
- disarmPrefetch();
8638
- disarmPrefetch = armPrefetch();
8639
- }) : void 0;
8640
- const unsubscribeLocation = subscribeToLocationChange(
8641
- ctx.eventController,
8642
- () => {
8643
- disarmPrefetch();
8644
- disarmPrefetch = armPrefetch();
8645
- }
8646
- );
8647
- return () => {
8648
- unsubscribeScope?.();
8649
- unsubscribeLocation();
8650
- disarmPrefetch();
8651
- };
8652
- }, [resolvedStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
8653
- return /* @__PURE__ */ jsx6(
8654
- "a",
8655
- {
8656
- ref: setRef,
8657
- href: resolvedTo,
8658
- onClick: handleClick,
8659
- onMouseEnter: handleMouseEnter,
8660
- "data-link-component": true,
8661
- "data-external": isExternal ? "" : void 0,
8662
- "data-scroll": scroll === false ? "false" : void 0,
8663
- "data-replace": replace ? "true" : void 0,
8664
- "data-revalidate": revalidate2 === false ? "false" : void 0,
8665
- ...props,
8666
- children: /* @__PURE__ */ jsx6(LinkContext.Provider, { value: resolvedTo, children })
8667
- }
8668
- );
8669
- });
8670
-
8671
- // src/browser/react/ScrollRestoration.tsx
8672
- import { useEffect as useEffect14 } from "react";
8673
-
8674
- // src/browser/react/use-handle.ts
8675
- import {
8676
- useContext as useContext15,
8677
- useState as useState13,
8678
- useEffect as useEffect15,
8679
- useRef as useRef13,
8680
- useOptimistic as useOptimistic4,
8681
- startTransition as startTransition5
8682
- } from "react";
8683
-
8684
- // src/missing-id-error.ts
8685
- var SELF_FILES = /* @__PURE__ */ new Set([
8686
- "missing-id-error",
8687
- "loader",
8688
- "loader.rsc",
8689
- "handle"
8690
- ]);
8691
- function findUserCallSite() {
8692
- try {
8693
- const stack = new Error().stack;
8694
- if (!stack) return void 0;
8695
- for (const frame of stack.split("\n").slice(1)) {
8696
- const m = frame.match(
8697
- /(?:\(|@|\s)(?:file:\/\/)?((?:\/|[A-Za-z]:[\\/])[^()\s]+?\.(?:ts|tsx|js|jsx|mts|cts)):(\d+):(\d+)\)?/
8698
- );
8699
- if (!m) continue;
8700
- const path6 = m[1];
8701
- if (path6.includes("node_modules") || path6.includes("@rangojs/router")) {
8702
- continue;
8703
- }
8704
- const base = path6.split(/[\\/]/).pop().replace(/\.(?:ts|tsx|js|jsx|mts|cts)$/, "");
8705
- if (SELF_FILES.has(base)) continue;
8706
- return `${path6}:${m[2]}:${m[3]}`;
8707
- }
8708
- } catch {
8709
- }
8710
- return void 0;
8711
- }
8712
- function missingInjectedIdError(kind, fnName) {
8713
- const site = findUserCallSite();
8714
- const at = site ? ` (created at ${site})` : "";
8715
- return new Error(
8716
- `[rango] ${kind} is missing $$id${at}.
8717
- The @rangojs/router:expose-internal-ids Vite transform injects ${fnName}()'s stable $$id from an EXPORTED const declaration only:
8718
- export const X = ${fnName}(...)
8719
- const X = ${fnName}(...); export { X }
8720
- A non-exported const, an \`export let/var\`, or an inline ${fnName}(...) call gets no $$id \u2014 export it as \`export const\`. (A matching "Unsupported ${fnName} shape" warning names the exact file:line.)`
8721
- );
8722
- }
8723
-
8724
- // src/handle.ts
8725
- function defaultCollect(segments) {
8726
- return segments;
8727
- }
8728
- var collectRegistry = /* @__PURE__ */ new Map();
8729
- var runtimeHandleIdCounter = 0;
8730
- function createHandle(collect, __injectedId) {
8731
- let handleId = __injectedId ?? "";
8732
- if (!handleId) {
8733
- if (isUnderTestRunner()) {
8734
- handleId = `__rango_runtime_handle_${runtimeHandleIdCounter++}`;
8735
- } else if (process.env.NODE_ENV !== "production") {
8736
- throw missingInjectedIdError("Handle", "createHandle");
8737
- } else {
8738
- throw new Error(
8739
- "[rango] Handle is missing $$id \u2014 the build plugin did not inject one. Export it as `export const X = createHandle(...)`."
8740
- );
8741
- }
8742
- }
8743
- const collectFn = collect ?? defaultCollect;
8744
- collectRegistry.set(
8745
- handleId,
8746
- collectFn
8747
- );
8748
- return {
8749
- __brand: "handle",
8750
- $$id: handleId
8751
- };
8752
- }
8753
-
8754
- // src/handles/meta.ts
8755
- function isUnsetDescriptor(descriptor) {
8756
- return typeof descriptor === "object" && descriptor !== null && "unset" in descriptor && typeof descriptor.unset === "string";
8757
- }
8758
- function isTitleDescriptor(descriptor) {
8759
- return typeof descriptor === "object" && descriptor !== null && "title" in descriptor;
8760
- }
8761
- function isTitleTemplate(title) {
8762
- return typeof title === "object" && title !== null && "template" in title && "default" in title;
8763
- }
8764
- function isAbsoluteTitle(title) {
8765
- return typeof title === "object" && title !== null && "absolute" in title;
8766
- }
8767
- function getMetaKey(descriptor) {
8768
- if (isUnsetDescriptor(descriptor)) {
8769
- return void 0;
8770
- }
8771
- if ("charSet" in descriptor) {
8772
- return "charSet";
8773
- }
8774
- if ("title" in descriptor) {
8775
- return "title";
8776
- }
8777
- if ("name" in descriptor && "content" in descriptor) {
8778
- return `name:${descriptor.name}`;
8779
- }
8780
- if ("property" in descriptor && "content" in descriptor) {
8781
- return `property:${descriptor.property}`;
8782
- }
8783
- if ("httpEquiv" in descriptor && "content" in descriptor) {
8784
- return `httpEquiv:${descriptor.httpEquiv}`;
8785
- }
8786
- if ("script:ld+json" in descriptor) {
8787
- return void 0;
8788
- }
8789
- if ("tagName" in descriptor) {
8790
- if (descriptor.tagName === "link" && "rel" in descriptor) {
8791
- const uniqueRels = ["canonical", "icon", "apple-touch-icon"];
8792
- if (uniqueRels.includes(descriptor.rel)) {
8793
- return `link:${descriptor.rel}`;
8794
- }
8795
- }
8796
- return void 0;
8797
- }
8798
- return void 0;
8799
- }
8800
- var defaultMetaDescriptors = [
8801
- { charSet: "utf-8" },
8802
- { name: "viewport", content: "width=device-width, initial-scale=1" }
8803
- ];
8804
- function addOrReplace(result, keyToIndex, descriptor, key) {
8805
- if (key !== void 0 && keyToIndex.has(key)) {
8806
- result[keyToIndex.get(key)] = descriptor;
8807
- } else {
8808
- if (key !== void 0) {
8809
- keyToIndex.set(key, result.length);
8810
- }
8811
- result.push(descriptor);
8812
- }
8813
- }
8814
- function updateIndicesAfterRemoval(keyToIndex, removedIndex) {
8815
- for (const [key, index] of keyToIndex) {
8816
- if (index > removedIndex) {
8817
- keyToIndex.set(key, index - 1);
8818
- }
8819
- }
8820
- }
8821
- function collectMeta(segments) {
8822
- const result = [];
8823
- const keyToIndex = /* @__PURE__ */ new Map();
8824
- let titleTemplate;
8825
- for (const descriptor of defaultMetaDescriptors) {
8826
- const key = getMetaKey(descriptor);
8827
- if (key !== void 0) {
8828
- keyToIndex.set(key, result.length);
8829
- }
8830
- result.push(descriptor);
8831
- }
8832
- for (const descriptors of segments) {
8833
- for (const descriptor of descriptors) {
8834
- if (isUnsetDescriptor(descriptor)) {
8835
- const keyToRemove = descriptor.unset;
8836
- if (keyToIndex.has(keyToRemove)) {
8837
- const idx = keyToIndex.get(keyToRemove);
8838
- result.splice(idx, 1);
8839
- keyToIndex.delete(keyToRemove);
8840
- updateIndicesAfterRemoval(keyToIndex, idx);
8841
- }
8842
- continue;
8843
- }
8844
- if (isTitleDescriptor(descriptor)) {
8845
- const titleValue = descriptor.title;
8846
- if (isTitleTemplate(titleValue)) {
8847
- titleTemplate = titleValue.template;
8848
- addOrReplace(
8849
- result,
8850
- keyToIndex,
8851
- { title: titleValue.default },
8852
- "title"
8853
- );
8854
- continue;
8855
- }
8856
- if (isAbsoluteTitle(titleValue)) {
8857
- addOrReplace(
8858
- result,
8859
- keyToIndex,
8860
- { title: titleValue.absolute },
8861
- "title"
8862
- );
8863
- continue;
8864
- }
8865
- const finalTitle = titleTemplate ? titleTemplate.split("%s").join(titleValue) : titleValue;
8866
- addOrReplace(
8867
- result,
8868
- keyToIndex,
8869
- { title: finalTitle },
8870
- "title"
8871
- );
8872
- continue;
8873
- }
8874
- const key = getMetaKey(descriptor);
8875
- addOrReplace(result, keyToIndex, descriptor, key);
8876
- }
8823
+ // src/browser/react/location-state-shared.ts
8824
+ function isLocationStateEntry(value) {
8825
+ return value !== null && typeof value === "object" && "__rsc_ls_key" in value && "__rsc_ls_value" in value && typeof value.__rsc_ls_key === "string";
8826
+ }
8827
+ function resolveLocationStateEntries(entries) {
8828
+ const result = {};
8829
+ for (const entry of entries) {
8830
+ result[entry.__rsc_ls_key] = entry.__rsc_ls_lazy ? entry.__rsc_ls_value() : entry.__rsc_ls_value;
8877
8831
  }
8878
8832
  return result;
8879
8833
  }
8880
- var Meta = createHandle(collectMeta, "__rsc_router_meta__");
8881
-
8882
- // src/handles/MetaTags.tsx
8883
- import { Fragment, jsx as jsx7, jsxs as jsxs2 } from "react/jsx-runtime";
8884
8834
 
8885
- // src/handles/script.ts
8886
- function validateConfigDev(config) {
8887
- if (process.env.NODE_ENV === "production") return;
8888
- const c = config;
8889
- if (c.src != null && c.children != null) {
8890
- console.warn(
8891
- `[Script] A config has both "src" and "children"; they are mutually exclusive \u2014 "src" wins and the inline body is ignored.`
8892
- );
8893
- } else if (c.src == null && c.children == null) {
8894
- console.warn(
8895
- `[Script] A config has neither "src" nor "children"; it injects nothing.`
8896
- );
8897
- } else if (c.src == null && c.id == null) {
8898
- console.warn(
8899
- `[Script] An inline script was pushed without an "id" and cannot be deduplicated. Pass an "id" so a layout + child pushing the same script inject it only once.`
8900
- );
8835
+ // src/browser/react/Link.tsx
8836
+ init_loader();
8837
+ import { jsx as jsx8 } from "react/jsx-runtime";
8838
+ var IGNORE_STRATEGY_CHANGES = (_listener) => () => {
8839
+ };
8840
+ function isExternalUrl(href2, origin) {
8841
+ if (href2.startsWith("//")) return true;
8842
+ if (href2.startsWith("http://") || href2.startsWith("https://")) {
8843
+ if (!origin) return false;
8844
+ try {
8845
+ return new URL(href2).origin !== origin;
8846
+ } catch {
8847
+ return false;
8848
+ }
8901
8849
  }
8902
- if (c.async && c.defer) {
8903
- console.warn(
8904
- `[Script] A config has both "async" and "defer"; they are mutually exclusive.`
8905
- );
8850
+ if (/^[a-z][a-z0-9+.-]*:/i.test(href2)) {
8851
+ return true;
8906
8852
  }
8853
+ return false;
8907
8854
  }
8908
- function collectScripts(segments) {
8909
- const result = [];
8910
- const keyToIndex = /* @__PURE__ */ new Map();
8911
- for (const configs of segments) {
8912
- for (const config of configs) {
8913
- validateConfigDev(config);
8914
- const isAsyncResource = config.src != null && config.async === true;
8915
- const key = isAsyncResource ? config.src : config.id ?? config.src;
8916
- if (key === void 0) {
8917
- result.push(config);
8918
- continue;
8855
+ var Link = forwardRef(function Link2({
8856
+ to,
8857
+ replace = false,
8858
+ scroll = true,
8859
+ reloadDocument = false,
8860
+ revalidate: revalidate2,
8861
+ prefetch,
8862
+ prefetchKey,
8863
+ state,
8864
+ children,
8865
+ onClick,
8866
+ ...props
8867
+ }, ref) {
8868
+ const ctx = useContext15(NavigationStoreContext);
8869
+ const storeLocation = ctx?.eventController.getState().location;
8870
+ const origin = storeLocation?.origin ?? (typeof window !== "undefined" ? window.location.origin : void 0);
8871
+ const isExternal = isExternalUrl(to, origin);
8872
+ const resolvedTo = useMemo7(() => {
8873
+ if (isExternal) return to;
8874
+ const bn = ctx?.basename;
8875
+ if (!bn || !to.startsWith("/") || to.startsWith(bn + "/") || to === bn)
8876
+ return to;
8877
+ return to === "/" ? bn : bn + to;
8878
+ }, [to, isExternal, ctx?.basename]);
8879
+ const configuredStrategy = prefetch ?? ctx?.defaultPrefetch ?? getDefaultPrefetchStrategy();
8880
+ const resolvedStrategy = useSyncExternalStore(
8881
+ configuredStrategy === "adaptive" ? subscribeToAdaptiveStrategyChange : IGNORE_STRATEGY_CHANGES,
8882
+ () => resolveAdaptiveStrategy(configuredStrategy),
8883
+ () => configuredStrategy === "adaptive" ? "hover" : configuredStrategy
8884
+ );
8885
+ const internalRef = useRef13(null);
8886
+ const setRef = useCallback5(
8887
+ (node) => {
8888
+ internalRef.current = node;
8889
+ if (typeof ref === "function") {
8890
+ ref(node);
8891
+ } else if (ref) {
8892
+ ref.current = node;
8919
8893
  }
8920
- const existing = keyToIndex.get(key);
8921
- if (existing !== void 0) {
8922
- result[existing] = config;
8894
+ },
8895
+ [ref]
8896
+ );
8897
+ const stateRef = useRef13(state);
8898
+ stateRef.current = state;
8899
+ const handleClick = useCallback5(
8900
+ (e) => {
8901
+ onClick?.(e);
8902
+ if (e.defaultPrevented) return;
8903
+ if (isExternal) return;
8904
+ if (reloadDocument) return;
8905
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
8906
+ if (e.currentTarget.hasAttribute("download"))
8907
+ return;
8908
+ const target = e.currentTarget.target;
8909
+ if (target && target !== "_self") return;
8910
+ if (isHashOnlyNavigation(e.currentTarget)) {
8911
+ return;
8912
+ }
8913
+ if (!ctx?.navigate) {
8914
+ return;
8915
+ }
8916
+ e.preventDefault();
8917
+ e.stopPropagation();
8918
+ const currentState = stateRef.current;
8919
+ let resolvedState;
8920
+ if (Array.isArray(currentState) && currentState.length > 0 && isLocationStateEntry(currentState[0])) {
8921
+ resolvedState = resolveLocationStateEntries(
8922
+ currentState
8923
+ );
8924
+ } else if (typeof currentState === "function") {
8925
+ resolvedState = currentState();
8926
+ } else if (currentState != null) {
8927
+ resolvedState = currentState;
8928
+ }
8929
+ ctx.navigate(resolvedTo, {
8930
+ replace,
8931
+ scroll,
8932
+ state: resolvedState,
8933
+ revalidate: revalidate2
8934
+ });
8935
+ },
8936
+ [
8937
+ resolvedTo,
8938
+ isExternal,
8939
+ reloadDocument,
8940
+ replace,
8941
+ scroll,
8942
+ revalidate2,
8943
+ ctx,
8944
+ onClick
8945
+ ]
8946
+ );
8947
+ const handleMouseEnter = useCallback5(() => {
8948
+ const element = internalRef.current;
8949
+ if ((resolvedStrategy === "hover" || resolvedStrategy === "viewport") && !isExternal && ctx?.store && (!element || !isHashOnlyNavigation(element) && !isPrefetchScopeDisabled(element))) {
8950
+ const segmentState = ctx.store.getSegmentState();
8951
+ prefetchDirect2(
8952
+ resolvedTo,
8953
+ segmentState.currentSegmentIds,
8954
+ getAppVersion(),
8955
+ ctx.store.getRouterId?.(),
8956
+ prefetchKey
8957
+ );
8958
+ }
8959
+ }, [resolvedStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
8960
+ useEffect15(() => {
8961
+ if (isExternal || !ctx?.store) return;
8962
+ const isViewport = resolvedStrategy === "viewport";
8963
+ const isRender = resolvedStrategy === "render";
8964
+ if (!isViewport && !isRender) return;
8965
+ const armPrefetch = () => {
8966
+ const element2 = internalRef.current;
8967
+ if (element2 && (isHashOnlyNavigation(element2) || isPrefetchScopeDisabled(element2))) {
8968
+ return () => {
8969
+ };
8970
+ }
8971
+ let cancelled = false;
8972
+ let unsubIdle;
8973
+ let stopObserving;
8974
+ const triggerPrefetch = () => {
8975
+ if (cancelled) return;
8976
+ const currentElement = internalRef.current;
8977
+ if (currentElement && isPrefetchScopeDisabled(currentElement)) return;
8978
+ const segmentState = ctx.store.getSegmentState();
8979
+ prefetchQueued2(
8980
+ resolvedTo,
8981
+ segmentState.currentSegmentIds,
8982
+ getAppVersion(),
8983
+ ctx.store.getRouterId?.(),
8984
+ prefetchKey
8985
+ );
8986
+ };
8987
+ if (isRender) {
8988
+ unsubIdle = schedulePrefetchWhenRouterIdle(
8989
+ ctx.eventController,
8990
+ triggerPrefetch
8991
+ );
8923
8992
  } else {
8924
- keyToIndex.set(key, result.length);
8925
- result.push(config);
8993
+ const viewportElement = internalRef.current;
8994
+ if (viewportElement) {
8995
+ stopObserving = observeForPrefetch(viewportElement, () => {
8996
+ unsubIdle = schedulePrefetchWhenRouterIdle(
8997
+ ctx.eventController,
8998
+ triggerPrefetch
8999
+ );
9000
+ });
9001
+ }
9002
+ }
9003
+ return () => {
9004
+ cancelled = true;
9005
+ unsubIdle?.();
9006
+ stopObserving?.();
9007
+ };
9008
+ };
9009
+ let disarmPrefetch = armPrefetch();
9010
+ const element = internalRef.current;
9011
+ const unsubscribeScope = element ? subscribeToPrefetchScopeChange(element, () => {
9012
+ disarmPrefetch();
9013
+ disarmPrefetch = armPrefetch();
9014
+ }) : void 0;
9015
+ const unsubscribeLocation = subscribeToLocationChange(
9016
+ ctx.eventController,
9017
+ () => {
9018
+ disarmPrefetch();
9019
+ disarmPrefetch = armPrefetch();
8926
9020
  }
9021
+ );
9022
+ return () => {
9023
+ unsubscribeScope?.();
9024
+ unsubscribeLocation();
9025
+ disarmPrefetch();
9026
+ };
9027
+ }, [resolvedStrategy, resolvedTo, isExternal, ctx, prefetchKey]);
9028
+ return /* @__PURE__ */ jsx8(
9029
+ "a",
9030
+ {
9031
+ ref: setRef,
9032
+ href: resolvedTo,
9033
+ onClick: handleClick,
9034
+ onMouseEnter: handleMouseEnter,
9035
+ "data-link-component": true,
9036
+ "data-external": isExternal ? "" : void 0,
9037
+ "data-scroll": scroll === false ? "false" : void 0,
9038
+ "data-replace": replace ? "true" : void 0,
9039
+ "data-revalidate": revalidate2 === false ? "false" : void 0,
9040
+ ...props,
9041
+ children: /* @__PURE__ */ jsx8(LinkContext.Provider, { value: resolvedTo, children })
8927
9042
  }
8928
- }
8929
- return result;
8930
- }
8931
- var Script = createHandle(collectScripts, "__rsc_router_script__");
8932
-
8933
- // src/handles/Scripts.tsx
8934
- import { useState as useState14 } from "react";
8935
- import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs3 } from "react/jsx-runtime";
9043
+ );
9044
+ });
8936
9045
 
8937
9046
  // src/handles/breadcrumbs.ts
8938
9047
  function collectBreadcrumbs(segments) {
@@ -9024,15 +9133,18 @@ var ALL_USE_ITEM_TYPES = new Set(
9024
9133
  );
9025
9134
 
9026
9135
  // src/server/request-context.ts
9027
- import { AsyncLocalStorage as AsyncLocalStorage3 } from "node:async_hooks";
9136
+ import { AsyncLocalStorage as AsyncLocalStorage4 } from "node:async_hooks";
9028
9137
  init_cookie_name();
9029
9138
 
9139
+ // src/server/handle-store.ts
9140
+ var LOADER_SLOT = Object.freeze({ loader: true });
9141
+
9030
9142
  // src/router/router-context.ts
9031
- import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
9032
- var routerContext = new AsyncLocalStorage2();
9143
+ import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
9144
+ var routerContext = new AsyncLocalStorage3();
9033
9145
 
9034
9146
  // src/server/request-context.ts
9035
- var requestContextStorage = new AsyncLocalStorage3();
9147
+ var requestContextStorage = new AsyncLocalStorage4();
9036
9148
 
9037
9149
  // src/route-definition/redirect.ts
9038
9150
  init_redirect_origin();
@@ -9226,7 +9338,7 @@ function serializeClientUrlPatterns(patterns) {
9226
9338
  // src/vite/discovery/client-urls-projection.ts
9227
9339
  init_client_ref_hashing();
9228
9340
  init_debug();
9229
- var debug11 = createRangoDebugger(NS.discovery);
9341
+ var debug10 = createRangoDebugger(NS.discovery);
9230
9342
  function hasDefaultExport(code) {
9231
9343
  let program;
9232
9344
  try {
@@ -9315,7 +9427,7 @@ async function refreshRecordedClientUrlProjections(state, ssrEnv, serverMod) {
9315
9427
  const projection = serializeClientUrlPatterns(
9316
9428
  definition
9317
9429
  );
9318
- debug11?.(
9430
+ debug10?.(
9319
9431
  "pre-entry refresh %s -> [%s]",
9320
9432
  source,
9321
9433
  projection.routes.map((route2) => route2.pattern).join(", ")
@@ -9397,21 +9509,21 @@ async function discoverClientUrlProjections(state, ssrEnv, serverMod) {
9397
9509
  }
9398
9510
 
9399
9511
  // src/vite/discovery/discover-routers.ts
9400
- var debug12 = createRangoDebugger(NS.discovery);
9512
+ var debug11 = createRangoDebugger(NS.discovery);
9401
9513
  async function discoverRouters(state, rscEnv, ssrEnv) {
9402
9514
  if (!state.resolvedEntryPath) return;
9403
9515
  const serverMod = await timed(
9404
- debug12,
9516
+ debug11,
9405
9517
  "inner: import @rangojs/router/server",
9406
9518
  () => rscEnv.runner.import("@rangojs/router/server")
9407
9519
  );
9408
9520
  await timed(
9409
- debug12,
9521
+ debug11,
9410
9522
  "inner: refresh recorded client URL projections",
9411
9523
  () => refreshRecordedClientUrlProjections(state, ssrEnv, serverMod)
9412
9524
  );
9413
9525
  await timed(
9414
- debug12,
9526
+ debug11,
9415
9527
  "inner: import entry",
9416
9528
  () => rscEnv.runner.import(state.resolvedEntryPath)
9417
9529
  );
@@ -9427,7 +9539,7 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
9427
9539
  const handlerErrors = await resolveHostRouterHandlers(hostRegistry);
9428
9540
  discoveryErrors.push(...handlerErrors);
9429
9541
  for (const { context, error } of handlerErrors) {
9430
- debug12?.("caught error while resolving %s: %O", context, error);
9542
+ debug11?.("caught error while resolving %s: %O", context, error);
9431
9543
  }
9432
9544
  const freshServerMod = await rscEnv.runner.import(
9433
9545
  "@rangojs/router/server"
@@ -9446,17 +9558,17 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
9446
9558
  }
9447
9559
  }
9448
9560
  await timed(
9449
- debug12,
9561
+ debug11,
9450
9562
  "inner: discover client URL projections",
9451
9563
  () => discoverClientUrlProjections(state, ssrEnv, serverMod)
9452
9564
  );
9453
9565
  const buildMod = await timed(
9454
- debug12,
9566
+ debug11,
9455
9567
  "inner: import @rangojs/router/build",
9456
9568
  () => rscEnv.runner.import("@rangojs/router/build")
9457
9569
  );
9458
9570
  const generateManifestFull = buildMod.generateManifestFull;
9459
- debug12?.("inner: found %d router(s) in registry", registry.size);
9571
+ debug11?.("inner: found %d router(s) in registry", registry.size);
9460
9572
  const nestedRouterConflict = findNestedRouterConflict(
9461
9573
  [...registry.values()].map((router) => router.__sourceFile).filter(
9462
9574
  (sourceFile) => typeof sourceFile === "string"
@@ -9481,7 +9593,7 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
9481
9593
  collectFallbackClientRefs(node, collectClientFallbackRef);
9482
9594
  }
9483
9595
  };
9484
- const manifestGenStart = debug12 ? performance.now() : 0;
9596
+ const manifestGenStart = debug11 ? performance.now() : 0;
9485
9597
  for (const [id, router] of registry) {
9486
9598
  if (!generateManifestFull) {
9487
9599
  continue;
@@ -9559,12 +9671,12 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
9559
9671
  );
9560
9672
  }
9561
9673
  }
9562
- debug12?.(
9674
+ debug11?.(
9563
9675
  "inner: generated manifests for %d router(s) (%sms)",
9564
9676
  allManifests.length,
9565
9677
  (performance.now() - manifestGenStart).toFixed(1)
9566
9678
  );
9567
- const trieStart = debug12 ? performance.now() : 0;
9679
+ const trieStart = debug11 ? performance.now() : 0;
9568
9680
  if (Object.keys(newMergedRouteManifest).length > 0) {
9569
9681
  for (const { id, manifest } of allManifests) {
9570
9682
  const perRouterTrie = buildPerRouterTrie(manifest);
@@ -9573,7 +9685,7 @@ async function discoverRouters(state, rscEnv, ssrEnv) {
9573
9685
  }
9574
9686
  }
9575
9687
  }
9576
- debug12?.(
9688
+ debug11?.(
9577
9689
  "inner: trie build done (%sms)",
9578
9690
  (performance.now() - trieStart).toFixed(1)
9579
9691
  );
@@ -9608,7 +9720,7 @@ function buildShellManifestKey(pathname) {
9608
9720
 
9609
9721
  // src/vite/discovery/shell-prerender-phase.ts
9610
9722
  init_debug();
9611
- var debug13 = createRangoDebugger(NS.prerender);
9723
+ var debug12 = createRangoDebugger(NS.prerender);
9612
9724
  async function runShellPrerenderPhase(s, builder) {
9613
9725
  const tempServer = s.shellPhaseTempServer;
9614
9726
  if (!s.isBuildMode || !s.shellCandidates?.length || !tempServer) return;
@@ -9664,7 +9776,7 @@ async function runShellPrerenderPhase(s, builder) {
9664
9776
  const base = builder?.config?.base ?? "/";
9665
9777
  const normalizedBase = base.endsWith("/") ? base : `${base}/`;
9666
9778
  const bootstrapContent = `import("${normalizedBase}assets/${entryFile}")`;
9667
- debug13?.("shell prerender bootstrap: %s", bootstrapContent);
9779
+ debug12?.("shell prerender bootstrap: %s", bootstrapContent);
9668
9780
  const minimalPlugin = (tempServer.config?.plugins ?? []).find(
9669
9781
  (p) => p?.name === "rsc:minimal"
9670
9782
  );
@@ -9751,7 +9863,7 @@ async function runShellPrerenderPhase(s, builder) {
9751
9863
  buildEnv: s.resolvedBuildEnv,
9752
9864
  buildVersion,
9753
9865
  captureShellHTML,
9754
- debug: !!debug13
9866
+ debug: !!debug12
9755
9867
  });
9756
9868
  if (res.outcome === "route-mismatch") {
9757
9869
  mismatches.push(res.matchedRouteName ?? "(no match)");
@@ -10333,7 +10445,7 @@ function postprocessBundle(state) {
10333
10445
  }
10334
10446
 
10335
10447
  // src/vite/discovery/gate-state.ts
10336
- function createDiscoveryGate(s, debug14) {
10448
+ function createDiscoveryGate(s, debug13) {
10337
10449
  let gatePending = false;
10338
10450
  let gateResolver = () => {
10339
10451
  };
@@ -10350,7 +10462,7 @@ function createDiscoveryGate(s, debug14) {
10350
10462
  const resolveGate = () => {
10351
10463
  if (!gatePending) return;
10352
10464
  if (inProgress || queued2 || pendingEvents) {
10353
- debug14?.(
10465
+ debug13?.(
10354
10466
  "hmr: resolveGate deferred \u2014 work in flight (inProgress=%s queued=%s pendingEvents=%s)",
10355
10467
  inProgress,
10356
10468
  queued2,
@@ -10359,7 +10471,7 @@ function createDiscoveryGate(s, debug14) {
10359
10471
  return;
10360
10472
  }
10361
10473
  gatePending = false;
10362
- debug14?.("hmr: discoveryDone resolved");
10474
+ debug13?.("hmr: discoveryDone resolved");
10363
10475
  gateResolver();
10364
10476
  };
10365
10477
  const noteRouteEvent = () => {
@@ -10369,7 +10481,7 @@ function createDiscoveryGate(s, debug14) {
10369
10481
  const runRefreshCycle = async (work) => {
10370
10482
  if (inProgress) {
10371
10483
  queued2 = true;
10372
- debug14?.("hmr: rediscovery in flight \u2014 queued for a follow-up cycle");
10484
+ debug13?.("hmr: rediscovery in flight \u2014 queued for a follow-up cycle");
10373
10485
  return;
10374
10486
  }
10375
10487
  pendingEvents = false;
@@ -10380,16 +10492,16 @@ function createDiscoveryGate(s, debug14) {
10380
10492
  inProgress = false;
10381
10493
  if (queued2) {
10382
10494
  queued2 = false;
10383
- debug14?.("hmr: consuming queued rediscovery");
10495
+ debug13?.("hmr: consuming queued rediscovery");
10384
10496
  runRefreshCycle(work).catch((err) => {
10385
- debug14?.(
10497
+ debug13?.(
10386
10498
  "hmr: queued cycle rejected \u2014 releasing gate (%s)",
10387
10499
  err instanceof Error ? err.message : String(err)
10388
10500
  );
10389
10501
  resolveGate();
10390
10502
  });
10391
10503
  } else if (pendingEvents) {
10392
- debug14?.(
10504
+ debug13?.(
10393
10505
  "hmr: holding gate for pending events (debounce not yet fired)"
10394
10506
  );
10395
10507
  } else {
@@ -12356,7 +12468,11 @@ If this is a multi-app host router, export a createHostRouter() instance and set
12356
12468
  plugins.push(createLoaderDirectiveGuardPlugin());
12357
12469
  plugins.push(createLoaderScanStubPlugin());
12358
12470
  plugins.push(exposeActionId());
12359
- plugins.push(useCacheTransform());
12471
+ plugins.push(
12472
+ useCacheTransform(
12473
+ createCounter(createRangoDebugger(NS.transform), "use-cache")
12474
+ )
12475
+ );
12360
12476
  plugins.push(exposeInternalIds());
12361
12477
  plugins.push(exposeRouterId());
12362
12478
  plugins.push(createVersionPlugin());
@@ -12400,7 +12516,7 @@ function poke() {
12400
12516
  apply: "serve",
12401
12517
  configureServer(server) {
12402
12518
  const stdin = process.stdin;
12403
- const debug14 = process.env.RANGO_POKE_DEBUG === "1";
12519
+ const debug13 = process.env.RANGO_POKE_DEBUG === "1";
12404
12520
  const triggerReload = (source) => {
12405
12521
  server.hot.send({ type: "full-reload", path: "*" });
12406
12522
  server.config.logger.info(` browser reload (${source})`, {
@@ -12433,7 +12549,7 @@ function poke() {
12433
12549
  lines.pop();
12434
12550
  return lines;
12435
12551
  };
12436
- if (debug14) {
12552
+ if (debug13) {
12437
12553
  server.config.logger.info(
12438
12554
  ` poke debug enabled (isTTY=${stdin.isTTY ? "yes" : "no"}, isRaw=${stdin.isTTY ? stdin.isRaw ? "yes" : "no" : "n/a"})`,
12439
12555
  { timestamp: true }
@@ -12446,7 +12562,7 @@ function poke() {
12446
12562
  );
12447
12563
  }
12448
12564
  const onData = (data) => {
12449
- if (debug14) {
12565
+ if (debug13) {
12450
12566
  server.config.logger.info(` poke stdin ${formatChunk(data)}`, {
12451
12567
  timestamp: true
12452
12568
  });