@rangojs/router 0.17.0 → 0.19.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 (293) hide show
  1. package/README.md +3 -2
  2. package/dist/types/browser/cookie-name.d.ts +6 -0
  3. package/dist/types/browser/event-controller.d.ts +3 -3
  4. package/dist/types/browser/history-state.d.ts +18 -0
  5. package/dist/types/browser/navigation-transaction.d.ts +20 -2
  6. package/dist/types/browser/partial-update.d.ts +16 -1
  7. package/dist/types/browser/prefetch/cache.d.ts +6 -6
  8. package/dist/types/browser/react/Link.d.ts +19 -9
  9. package/dist/types/browser/react/context.d.ts +2 -2
  10. package/dist/types/browser/react/location-state-shared.d.ts +95 -56
  11. package/dist/types/browser/react/location-state.d.ts +1 -1
  12. package/dist/types/browser/transition-when.d.ts +80 -0
  13. package/dist/types/browser/types.d.ts +104 -9
  14. package/dist/types/build/generate-manifest.d.ts +8 -0
  15. package/dist/types/cache/background-task.d.ts +24 -2
  16. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  17. package/dist/types/cache/cache-key-utils.d.ts +48 -2
  18. package/dist/types/cache/cache-policy.d.ts +18 -1
  19. package/dist/types/cache/cache-runtime.d.ts +7 -0
  20. package/dist/types/cache/cache-scope.d.ts +192 -26
  21. package/dist/types/cache/cache-tag.d.ts +116 -11
  22. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  23. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  24. package/dist/types/cache/cf/cf-cache-store.d.ts +224 -72
  25. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  26. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  27. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +19 -0
  28. package/dist/types/cache/document-cache.d.ts +10 -0
  29. package/dist/types/cache/handle-snapshot.d.ts +41 -8
  30. package/dist/types/cache/index.d.ts +1 -0
  31. package/dist/types/cache/invalidation-order.d.ts +23 -0
  32. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  33. package/dist/types/cache/request-tag-mask.d.ts +54 -0
  34. package/dist/types/cache/shell-memo.d.ts +178 -0
  35. package/dist/types/cache/shell-snapshot.d.ts +141 -105
  36. package/dist/types/cache/tag-invalidation.d.ts +54 -3
  37. package/dist/types/cache/types.d.ts +267 -96
  38. package/dist/types/cache/vercel/vercel-cache-store.d.ts +123 -18
  39. package/dist/types/client-urls/navigation.d.ts +28 -2
  40. package/dist/types/client-urls/server-projection.d.ts +5 -2
  41. package/dist/types/client-urls/types.d.ts +5 -5
  42. package/dist/types/client.d.ts +1 -1
  43. package/dist/types/index.d.ts +1 -1
  44. package/dist/types/index.rsc.d.ts +1 -1
  45. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  46. package/dist/types/route-definition/helpers-types.d.ts +11 -11
  47. package/dist/types/route-name.d.ts +7 -0
  48. package/dist/types/router/error-handling.d.ts +1 -1
  49. package/dist/types/router/instrument.d.ts +8 -0
  50. package/dist/types/router/intercept-resolution.d.ts +2 -0
  51. package/dist/types/router/match-context.d.ts +5 -0
  52. package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
  53. package/dist/types/router/middleware-types.d.ts +6 -1
  54. package/dist/types/router/router-options.d.ts +4 -1
  55. package/dist/types/router/segment-resolution/loader-cache.d.ts +58 -12
  56. package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
  57. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +33 -48
  58. package/dist/types/router/segment-resolution/mask-nested.d.ts +73 -24
  59. package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
  60. package/dist/types/router/transition-when.d.ts +24 -10
  61. package/dist/types/rsc/attach-transition-when.d.ts +18 -0
  62. package/dist/types/rsc/full-payload.d.ts +21 -0
  63. package/dist/types/rsc/handler.d.ts +1 -1
  64. package/dist/types/rsc/response-cache-serve.d.ts +15 -12
  65. package/dist/types/rsc/shell-build-manifest.d.ts +12 -3
  66. package/dist/types/rsc/shell-capture-constants.d.ts +116 -12
  67. package/dist/types/rsc/shell-capture.d.ts +202 -74
  68. package/dist/types/rsc/shell-serve.d.ts +144 -50
  69. package/dist/types/rsc/types.d.ts +9 -0
  70. package/dist/types/segment-system.d.ts +7 -0
  71. package/dist/types/server/context.d.ts +155 -33
  72. package/dist/types/server/cookie-store.d.ts +33 -0
  73. package/dist/types/server/handle-store.d.ts +52 -1
  74. package/dist/types/server/request-context.d.ts +210 -63
  75. package/dist/types/server/request-headers.d.ts +44 -0
  76. package/dist/types/server.d.ts +1 -0
  77. package/dist/types/ssr/index.d.ts +9 -0
  78. package/dist/types/ssr/ssr-root.d.ts +9 -8
  79. package/dist/types/testing/e2e/index.d.ts +1 -1
  80. package/dist/types/testing/flight.entry.d.ts +7 -0
  81. package/dist/types/testing/index.d.ts +3 -2
  82. package/dist/types/testing/internal/action-id.d.ts +7 -0
  83. package/dist/types/testing/location-state-key.d.ts +24 -0
  84. package/dist/types/testing/render-route.d.ts +17 -1
  85. package/dist/types/testing/run-loader.d.ts +18 -3
  86. package/dist/types/testing/run-transition-when.d.ts +44 -69
  87. package/dist/types/testing/serve-shell-request.d.ts +133 -0
  88. package/dist/types/testing/shell-status.d.ts +44 -19
  89. package/dist/types/transition-when-ref.d.ts +79 -0
  90. package/dist/types/types/cache-types.d.ts +15 -3
  91. package/dist/types/types/handler-context.d.ts +14 -1
  92. package/dist/types/types/index.d.ts +1 -1
  93. package/dist/types/types/segments.d.ts +86 -71
  94. package/dist/types/urls/path-helper-types.d.ts +3 -4
  95. package/dist/types/urls/pattern-types.d.ts +21 -18
  96. package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
  97. package/dist/types/vite/router-discovery.d.ts +29 -2
  98. package/dist/vite/index.js +1005 -105
  99. package/package.json +1 -1
  100. package/skills/breadcrumbs/SKILL.md +17 -7
  101. package/skills/cache-guide/SKILL.md +175 -76
  102. package/skills/caching/SKILL.md +361 -33
  103. package/skills/catalog.json +1 -1
  104. package/skills/client-urls/SKILL.md +12 -7
  105. package/skills/cloudflare/SKILL.md +15 -0
  106. package/skills/comparison/references/framework-comparison.md +52 -37
  107. package/skills/defer-hydration/SKILL.md +50 -30
  108. package/skills/deployment-caching/SKILL.md +5 -2
  109. package/skills/document-cache/SKILL.md +32 -2
  110. package/skills/handler-use/SKILL.md +2 -0
  111. package/skills/hooks/SKILL.md +3 -1
  112. package/skills/hooks/handle-and-actions.md +3 -1
  113. package/skills/hooks/navigation.md +23 -2
  114. package/skills/hooks/state.md +101 -7
  115. package/skills/intercept/SKILL.md +29 -12
  116. package/skills/loader/SKILL.md +197 -44
  117. package/skills/middleware/SKILL.md +34 -9
  118. package/skills/migrate-nextjs/SKILL.md +68 -37
  119. package/skills/migrate-react-router/SKILL.md +2 -1
  120. package/skills/migrate-react-router/component-migration.md +3 -1
  121. package/skills/migrate-react-router/data-and-actions.md +3 -1
  122. package/skills/migrate-react-router/route-mapping.md +7 -4
  123. package/skills/observability/SKILL.md +65 -0
  124. package/skills/parallel/SKILL.md +13 -8
  125. package/skills/ppr/SKILL.md +715 -371
  126. package/skills/prerender/SKILL.md +30 -17
  127. package/skills/rango/SKILL.md +72 -34
  128. package/skills/route/SKILL.md +14 -7
  129. package/skills/router-setup/SKILL.md +4 -2
  130. package/skills/scripts/SKILL.md +8 -1
  131. package/skills/server-actions/SKILL.md +11 -0
  132. package/skills/shell-manifest/SKILL.md +21 -12
  133. package/skills/testing/SKILL.md +49 -43
  134. package/skills/testing/cache-prerender.md +15 -14
  135. package/skills/testing/client-components.md +45 -3
  136. package/skills/testing/loader.md +19 -1
  137. package/skills/testing/middleware.md +1 -0
  138. package/skills/testing/predicates.md +49 -49
  139. package/skills/testing/render-handler.md +1 -0
  140. package/skills/testing/serve-shell-request.md +202 -0
  141. package/skills/testing/server-actions.md +9 -3
  142. package/skills/theme/SKILL.md +56 -4
  143. package/skills/typesafety/env-and-bindings.md +18 -2
  144. package/skills/use-cache/SKILL.md +91 -23
  145. package/skills/vercel/SKILL.md +14 -2
  146. package/skills/view-transitions/SKILL.md +150 -29
  147. package/src/__augment-tests__/augmented.check.ts +23 -0
  148. package/src/browser/cookie-name.ts +23 -0
  149. package/src/browser/event-controller.ts +4 -4
  150. package/src/browser/history-state.ts +75 -1
  151. package/src/browser/link-interceptor.ts +4 -0
  152. package/src/browser/navigation-bridge.ts +77 -18
  153. package/src/browser/navigation-client.ts +4 -3
  154. package/src/browser/navigation-store.ts +43 -1
  155. package/src/browser/navigation-transaction.ts +31 -11
  156. package/src/browser/partial-update.ts +146 -35
  157. package/src/browser/prefetch/cache.ts +6 -6
  158. package/src/browser/prefetch/fetch.ts +2 -2
  159. package/src/browser/react/Link.tsx +38 -33
  160. package/src/browser/react/NavigationProvider.tsx +2 -2
  161. package/src/browser/react/context.ts +2 -2
  162. package/src/browser/react/location-state-shared.ts +241 -49
  163. package/src/browser/react/location-state.ts +5 -2
  164. package/src/browser/rsc-router.tsx +9 -0
  165. package/src/browser/server-action-bridge.ts +84 -15
  166. package/src/browser/transition-when.ts +205 -0
  167. package/src/browser/types.ts +124 -10
  168. package/src/build/generate-manifest.ts +77 -1
  169. package/src/cache/background-task.ts +55 -2
  170. package/src/cache/cache-exec-scope.ts +48 -1
  171. package/src/cache/cache-key-utils.ts +62 -2
  172. package/src/cache/cache-policy.ts +31 -4
  173. package/src/cache/cache-runtime.ts +209 -129
  174. package/src/cache/cache-scope.ts +573 -69
  175. package/src/cache/cache-tag.ts +475 -19
  176. package/src/cache/cf/cf-base64.ts +36 -4
  177. package/src/cache/cf/cf-cache-constants.ts +12 -0
  178. package/src/cache/cf/cf-cache-store.ts +1094 -376
  179. package/src/cache/cf/cf-cache-types.ts +32 -4
  180. package/src/cache/cf/cf-shell-frame.ts +258 -0
  181. package/src/cache/cf/cf-tag-marker-memo.ts +55 -40
  182. package/src/cache/document-cache.ts +53 -7
  183. package/src/cache/handle-snapshot.ts +53 -9
  184. package/src/cache/index.ts +2 -0
  185. package/src/cache/invalidation-order.ts +84 -0
  186. package/src/cache/isolate-tag-memo.ts +308 -0
  187. package/src/cache/request-tag-mask.ts +147 -0
  188. package/src/cache/shell-memo.ts +341 -0
  189. package/src/cache/shell-snapshot.ts +249 -277
  190. package/src/cache/tag-invalidation.ts +109 -18
  191. package/src/cache/types.ts +274 -102
  192. package/src/cache/vercel/vercel-cache-store.ts +561 -74
  193. package/src/client-urls/client-root.tsx +8 -4
  194. package/src/client-urls/client-urls.ts +8 -6
  195. package/src/client-urls/navigation.ts +55 -4
  196. package/src/client-urls/server-projection.ts +31 -10
  197. package/src/client-urls/types.ts +12 -5
  198. package/src/client.tsx +2 -0
  199. package/src/host/cookie-handler.ts +2 -1
  200. package/src/index.rsc.ts +3 -0
  201. package/src/index.ts +3 -0
  202. package/src/loader-redirect.tsx +7 -3
  203. package/src/prerender/build-shell-capture.ts +173 -110
  204. package/src/route-definition/dsl-helpers.ts +6 -3
  205. package/src/route-definition/helpers-types.ts +11 -11
  206. package/src/route-name.ts +12 -0
  207. package/src/router/content-negotiation.ts +4 -1
  208. package/src/router/error-handling.ts +5 -2
  209. package/src/router/handler-context.ts +42 -5
  210. package/src/router/instrument.ts +54 -2
  211. package/src/router/intercept-resolution.ts +30 -8
  212. package/src/router/loader-resolution.ts +89 -33
  213. package/src/router/logging.ts +5 -3
  214. package/src/router/match-api.ts +89 -42
  215. package/src/router/match-context.ts +5 -0
  216. package/src/router/match-handlers.ts +14 -29
  217. package/src/router/match-middleware/background-revalidation.ts +3 -5
  218. package/src/router/match-middleware/cache-lookup.ts +52 -19
  219. package/src/router/match-middleware/cache-store.ts +36 -10
  220. package/src/router/match-result.ts +1 -0
  221. package/src/router/middleware-types.ts +6 -1
  222. package/src/router/middleware.ts +20 -4
  223. package/src/router/navigation-snapshot.ts +6 -5
  224. package/src/router/prerender-match.ts +10 -0
  225. package/src/router/request-classification.ts +7 -4
  226. package/src/router/router-options.ts +4 -1
  227. package/src/router/segment-resolution/fresh.ts +18 -13
  228. package/src/router/segment-resolution/helpers.ts +4 -2
  229. package/src/router/segment-resolution/loader-cache.ts +366 -70
  230. package/src/router/segment-resolution/loader-mask.ts +3 -4
  231. package/src/router/segment-resolution/loader-snapshot.ts +215 -126
  232. package/src/router/segment-resolution/mask-nested.ts +161 -42
  233. package/src/router/segment-resolution/revalidation.ts +27 -13
  234. package/src/router/segment-resolution/view-transition-default.ts +6 -21
  235. package/src/router/telemetry.ts +6 -3
  236. package/src/router/transition-when.ts +34 -52
  237. package/src/router.ts +2 -1
  238. package/src/rsc/attach-transition-when.ts +47 -0
  239. package/src/rsc/full-payload.ts +47 -3
  240. package/src/rsc/handler.ts +24 -10
  241. package/src/rsc/loader-fetch.ts +2 -1
  242. package/src/rsc/manifest-init.ts +4 -0
  243. package/src/rsc/origin-guard.ts +8 -5
  244. package/src/rsc/progressive-enhancement.ts +10 -32
  245. package/src/rsc/response-cache-serve.ts +69 -78
  246. package/src/rsc/response-route-handler.ts +2 -2
  247. package/src/rsc/rsc-rendering.ts +712 -338
  248. package/src/rsc/server-action.ts +8 -20
  249. package/src/rsc/shell-build-manifest.ts +49 -29
  250. package/src/rsc/shell-capture-constants.ts +182 -12
  251. package/src/rsc/shell-capture.ts +1126 -612
  252. package/src/rsc/shell-serve.ts +331 -85
  253. package/src/rsc/ssr-setup.ts +3 -2
  254. package/src/rsc/types.ts +9 -0
  255. package/src/segment-system.tsx +40 -7
  256. package/src/server/context.ts +328 -72
  257. package/src/server/cookie-store.ts +184 -133
  258. package/src/server/handle-store.ts +245 -27
  259. package/src/server/request-context.ts +364 -100
  260. package/src/server/request-headers.ts +134 -0
  261. package/src/server.ts +4 -0
  262. package/src/ssr/index.tsx +96 -37
  263. package/src/ssr/ssr-root.tsx +9 -8
  264. package/src/testing/dispatch.ts +4 -7
  265. package/src/testing/e2e/index.ts +1 -0
  266. package/src/testing/flight.entry.ts +14 -0
  267. package/src/testing/index.ts +5 -0
  268. package/src/testing/internal/action-id.ts +25 -0
  269. package/src/testing/internal/context.ts +1 -1
  270. package/src/testing/location-state-key.ts +45 -0
  271. package/src/testing/render-handler.ts +1 -1
  272. package/src/testing/render-route.tsx +109 -16
  273. package/src/testing/run-client-revalidate.ts +3 -19
  274. package/src/testing/run-loader.ts +67 -5
  275. package/src/testing/run-transition-when.ts +109 -172
  276. package/src/testing/serve-shell-request.ts +381 -0
  277. package/src/testing/shell-status.ts +73 -47
  278. package/src/theme/ThemeProvider.tsx +7 -6
  279. package/src/transition-when-ref.ts +220 -0
  280. package/src/types/cache-types.ts +15 -3
  281. package/src/types/handler-context.ts +14 -1
  282. package/src/types/index.ts +3 -0
  283. package/src/types/segments.ts +94 -91
  284. package/src/urls/path-helper-types.ts +3 -4
  285. package/src/urls/pattern-types.ts +21 -18
  286. package/src/vite/discovery/discover-routers.ts +3 -0
  287. package/src/vite/discovery/shell-prerender-phase.ts +9 -2
  288. package/src/vite/discovery/virtual-module-codegen.ts +16 -6
  289. package/src/vite/plugins/transition-when-hoist.ts +1064 -0
  290. package/src/vite/rango.ts +2 -0
  291. package/src/vite/router-discovery.ts +89 -9
  292. package/dist/types/rsc/transition-gate.d.ts +0 -23
  293. package/src/rsc/transition-gate.ts +0 -86
@@ -0,0 +1,220 @@
1
+ /**
2
+ * transition({ when }) references: what a route definition may carry as a
3
+ * `when`, how definitions are validated, and how the browser turns the carried
4
+ * value back into a callable predicate.
5
+ *
6
+ * `when` is a BROWSER predicate. The server never calls it; it only carries it
7
+ * to the browser inside the segment's transition config, as one of:
8
+ *
9
+ * - a React client reference: a function exported from a `"use client"`
10
+ * module and imported into a server `urls()` file, or an inline literal the
11
+ * build hoisted into one (vite/plugins/transition-when-hoist.ts). Flight
12
+ * serializes it as a module reference and the browser resolves it to the
13
+ * real function;
14
+ * - a clientUrls() when reference, synthesized by the projection materializer
15
+ * (client-urls/server-projection.ts): the group's definition client
16
+ * reference plus the route id. The browser resolves it to the `when` the
17
+ * route declared in its `"use client"` module.
18
+ *
19
+ * A plain function (a server function) cannot cross Flight, and a
20
+ * non-function is never callable. Both are LOUD errors: route discovery
21
+ * (generateManifestFull `validateTransitionWhen`) fails dev startup, the
22
+ * build and HMR re-discovery, and attachTransitionWhen (rsc/) throws the same
23
+ * error at render time for a route discovery did not see.
24
+ *
25
+ * Telling a client reference apart from a server function needs the
26
+ * react-server module graph, where a `"use client"` import IS a client
27
+ * reference. Outside it (a node/DOM unit-test project that aliases the router
28
+ * entry) the import is the plain function, so strict validation is enabled
29
+ * only by the Vite plugin: discovery passes the option, and the generated
30
+ * routes-manifest module calls enableTransitionWhenValidation() in the app's
31
+ * RSC realm (vite/discovery/virtual-module-codegen.ts).
32
+ */
33
+
34
+ import { isAutoGeneratedRouteName } from "./route-name.js";
35
+ import type { TransitionWhenFn } from "./types/segments.js";
36
+
37
+ const CLIENT_REFERENCE = Symbol.for("react.client.reference");
38
+
39
+ /** String key, so the marker survives Flight (symbol keys are not serialized). */
40
+ const CLIENT_URLS_WHEN_KEY = "__rangoClientUrlsWhen";
41
+
42
+ interface ClientUrlsWhenRef {
43
+ readonly __rangoClientUrlsWhen: 1;
44
+ /** The clientUrls() definition: a client reference on the server, the definition object in the browser. */
45
+ readonly definition: unknown;
46
+ readonly routeId: string;
47
+ }
48
+
49
+ export function isClientReference(value: unknown): boolean {
50
+ if (typeof value !== "function" && (typeof value !== "object" || !value)) {
51
+ return false;
52
+ }
53
+ return (value as { $$typeof?: unknown }).$$typeof === CLIENT_REFERENCE;
54
+ }
55
+
56
+ function isClientUrlsWhenRef(value: unknown): value is ClientUrlsWhenRef {
57
+ return (
58
+ typeof value === "object" &&
59
+ value !== null &&
60
+ (value as Record<string, unknown>)[CLIENT_URLS_WHEN_KEY] === 1
61
+ );
62
+ }
63
+
64
+ export function createClientUrlsWhenRef(
65
+ definition: unknown,
66
+ routeId: string,
67
+ ): TransitionWhenFn {
68
+ const ref: ClientUrlsWhenRef = {
69
+ __rangoClientUrlsWhen: 1,
70
+ definition,
71
+ routeId,
72
+ };
73
+ return ref as unknown as TransitionWhenFn;
74
+ }
75
+
76
+ let strictValidation = false;
77
+
78
+ /**
79
+ * Turn on server-function rejection for this realm. Called by the Vite
80
+ * plugin's generated routes-manifest module, never by tests: see the module
81
+ * comment for why it cannot be on by default.
82
+ */
83
+ export function enableTransitionWhenValidation(): void {
84
+ strictValidation = true;
85
+ }
86
+
87
+ export function isTransitionWhenValidationEnabled(): boolean {
88
+ return strictValidation;
89
+ }
90
+
91
+ /** @internal Reset for unit tests. */
92
+ export function resetTransitionWhenValidation(): void {
93
+ strictValidation = false;
94
+ }
95
+
96
+ /** Thrown for an invalid transition({ when }); recognized by the dev HMR overlay. */
97
+ export class TransitionWhenError extends Error {
98
+ override readonly name: string = "TransitionWhenError";
99
+ }
100
+
101
+ /**
102
+ * The TransitionWhenError in an error's cause chain, matched by name: discovery
103
+ * throws it from the module runner's copy of this module (another class
104
+ * object), and include resolution wraps it (build/generate-manifest.ts).
105
+ */
106
+ export function findTransitionWhenError(error: unknown): Error | undefined {
107
+ const seen = new Set<unknown>();
108
+ let current: unknown = error;
109
+ while (
110
+ typeof current === "object" &&
111
+ current !== null &&
112
+ !seen.has(current)
113
+ ) {
114
+ seen.add(current);
115
+ if ((current as Error).name === "TransitionWhenError") {
116
+ return current as Error;
117
+ }
118
+ current = (current as { cause?: unknown }).cause;
119
+ }
120
+ return undefined;
121
+ }
122
+
123
+ /** Where an invalid `when` was declared, for the error message. */
124
+ export interface TransitionWhenSite {
125
+ /** The route the entry belongs to (the matched or enumerated route). */
126
+ readonly routeName?: string;
127
+ readonly pattern?: string;
128
+ /** The declaring entry's type when it is not the route itself. */
129
+ readonly entryType?: string;
130
+ }
131
+
132
+ function describeSite(site: TransitionWhenSite): string {
133
+ const named =
134
+ site.routeName && !isAutoGeneratedRouteName(site.routeName)
135
+ ? `route "${site.routeName}"${site.pattern ? ` (${site.pattern})` : ""}`
136
+ : site.pattern
137
+ ? `route ${site.pattern}`
138
+ : "a route";
139
+ if (!site.entryType || site.entryType === "route") return named;
140
+ return `a ${site.entryType} of ${named}`;
141
+ }
142
+
143
+ function describeValue(value: unknown): string {
144
+ if (value === null) return "null";
145
+ if (Array.isArray(value)) return "an array";
146
+ return typeof value;
147
+ }
148
+
149
+ /**
150
+ * The problem with a `when` value, or null when it is valid. Non-functions
151
+ * are always invalid; a plain function is invalid only under strict
152
+ * validation (see the module comment).
153
+ */
154
+ export function transitionWhenProblem(
155
+ when: unknown,
156
+ strict: boolean,
157
+ ): "not-a-function" | "server-function" | null {
158
+ if (when === undefined || isClientUrlsWhenRef(when)) return null;
159
+ if (typeof when !== "function") return "not-a-function";
160
+ if (strict && !isClientReference(when)) return "server-function";
161
+ return null;
162
+ }
163
+
164
+ export function createTransitionWhenError(
165
+ when: unknown,
166
+ problem: "not-a-function" | "server-function",
167
+ site: TransitionWhenSite,
168
+ ): TransitionWhenError {
169
+ const where = `transition({ when }) on ${describeSite(site)}`;
170
+ const head =
171
+ problem === "not-a-function"
172
+ ? `${where} must be a function, got ${describeValue(when)}.`
173
+ : `${where} is not a client function.`;
174
+ return new TransitionWhenError(
175
+ `[rango] ${head} ` +
176
+ "`when` runs in the browser: write it inline in the transition() call " +
177
+ "(transition({ when: (ctx) => ... }), where transition is the urls() helper " +
178
+ 'or imported from "@rangojs/router"; the build hoists that literal into a ' +
179
+ 'client module) or export it from a "use client" module and import it ' +
180
+ "into your urls() file. It receives { kind, from, to, isAction, action }: " +
181
+ "URLs, params, route names, history state and action data; get(), env " +
182
+ 'and handler state do not exist there. See skills/view-transitions "Conditional transitions".',
183
+ );
184
+ }
185
+
186
+ /**
187
+ * Definition-time shape check (the DSL's transition()). Runs in every
188
+ * environment, so it rejects only what is invalid everywhere: a non-function.
189
+ */
190
+ export function assertTransitionWhenShape(when: unknown): void {
191
+ if (transitionWhenProblem(when, false) === "not-a-function") {
192
+ throw new TypeError(
193
+ createTransitionWhenError(when, "not-a-function", {}).message,
194
+ );
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Browser: turn a transition config's `when` into the predicate to call.
200
+ * Undefined when there is none (or a clientUrls route no longer declares it).
201
+ */
202
+ export function resolveTransitionWhen(
203
+ when: unknown,
204
+ ): TransitionWhenFn | undefined {
205
+ if (typeof when === "function") return when as TransitionWhenFn;
206
+ if (!isClientUrlsWhenRef(when)) return undefined;
207
+ const definition = when.definition as
208
+ | {
209
+ routes?: ReadonlyArray<{
210
+ id: string;
211
+ transition?: { when?: unknown };
212
+ }>;
213
+ }
214
+ | undefined;
215
+ const record = definition?.routes?.find(
216
+ (candidate) => candidate.id === when.routeId,
217
+ );
218
+ const fn = record?.transition?.when;
219
+ return typeof fn === "function" ? (fn as TransitionWhenFn) : undefined;
220
+ }
@@ -72,7 +72,13 @@ export interface CacheOptions<TEnv = unknown> {
72
72
  /**
73
73
  * Override the cache store for this boundary.
74
74
  * When specified, this boundary and its children use this store
75
- * instead of the app-level store from handler config.
75
+ * instead of the app-level store from handler config. The records of a
76
+ * boundary with a store here are partitioned by the `keyGenerator` of each
77
+ * enclosing cache() without a `key` whose store differs, the app-level
78
+ * store included: under `cache(() => [...])` on an app store with a
79
+ * `keyGenerator`, `cache({ store })` records are split by its result too.
80
+ * Likewise, without a `key` of its own, this store's `keyGenerator`
81
+ * partitions a nested cache() on another store.
76
82
  *
77
83
  * Useful for:
78
84
  * - Different backends per route section (memory vs KV vs Redis)
@@ -108,6 +114,8 @@ export interface CacheOptions<TEnv = unknown> {
108
114
  /**
109
115
  * Conditional cache read function.
110
116
  * Return false to skip cache for this request (always fetch fresh).
117
+ * It gates every cache() nested under this one too: a nested boundary
118
+ * caches only when every enclosing condition allows it.
111
119
  *
112
120
  * Has access to full RequestContext including env, request, params, cookies, etc.
113
121
  * Runs after global and route middleware on RSC routes; on response routes,
@@ -130,7 +138,10 @@ export interface CacheOptions<TEnv = unknown> {
130
138
 
131
139
  /**
132
140
  * Custom cache key function - FULL OVERRIDE.
133
- * Bypasses default key generation AND store's keyGenerator.
141
+ * Bypasses default key generation AND store's keyGenerator. The result is
142
+ * stored namespaced (`key:` plus its URI encoding), so no value it returns
143
+ * names another route's record; a nested cache() keys its records within
144
+ * this partition.
134
145
  *
135
146
  * Has access to full RequestContext including env, request, params, cookies, etc.
136
147
  * Runs after global and route middleware on RSC routes; on response routes,
@@ -154,7 +165,8 @@ export interface CacheOptions<TEnv = unknown> {
154
165
 
155
166
  /**
156
167
  * Tags for cache invalidation.
157
- * Can be a static array or a function that returns tags.
168
+ * Can be a static array or a function that returns tags. They tag the
169
+ * records of every cache() nested under this one too.
158
170
  *
159
171
  * The built-in `MemorySegmentCacheStore`, `CFCacheStore`, and
160
172
  * `VercelCacheStore` index by tag.
@@ -342,6 +342,12 @@ export type HandlerContext<
342
342
  * Current theme (from cookie or default).
343
343
  * Only available when theme is enabled in router config.
344
344
  *
345
+ * The visitor's cookie, so it is guarded like `cookies()`: reading it on a
346
+ * `ppr` route refuses the shell capture, and inside a `cache()` boundary or
347
+ * a `"use cache"` function it throws (#971). Read it with `useTheme()` in a
348
+ * client component, or in a live loader, on those routes. A read-only,
349
+ * non-enumerable getter: `{ ...ctx }` does not carry it.
350
+ *
345
351
  * @example
346
352
  * ```typescript
347
353
  * route("settings", (ctx) => {
@@ -350,7 +356,7 @@ export type HandlerContext<
350
356
  * });
351
357
  * ```
352
358
  */
353
- theme?: Theme;
359
+ readonly theme?: Theme;
354
360
  /**
355
361
  * Set the theme (only available when theme is enabled in router config).
356
362
  * Sets a cookie with the new theme value.
@@ -469,6 +475,13 @@ export type InternalHandlerContext<
469
475
  * from the page. See setupLoaderAccess.
470
476
  */
471
477
  _runLoaderIsolated?: (loader: any) => Promise<any>;
478
+ /**
479
+ * @internal A loader's cache() binding started with these cache() tags: an
480
+ * execution of that loader outside the binding (a reader started it first,
481
+ * or a stale refresh runs it) answers for them too (#964). See
482
+ * setupLoaderAccess.
483
+ */
484
+ _bindLoaderCacheTags?: (loaderId: string, tags: Set<string>) => void;
472
485
  };
473
486
 
474
487
  /**
@@ -51,6 +51,9 @@ export type {
51
51
  TransitionConfig,
52
52
  TransitionWhenFn,
53
53
  TransitionWhenContext,
54
+ TransitionWhenKind,
55
+ RouteLocation,
56
+ ServerRouteLocation,
54
57
  ResolvedSegment,
55
58
  SegmentMetadata,
56
59
  SlotState,
@@ -1,6 +1,7 @@
1
1
  import type { ReactNode } from "react";
2
2
  import type { ErrorInfo, NotFoundInfo } from "./boundaries.js";
3
- import type { RevalidateParams, HandlerContext } from "./handler-context.js";
3
+ import type { IsActionFn } from "./handler-context.js";
4
+ import type { DefaultRouteName } from "./global-namespace.js";
4
5
 
5
6
  /**
6
7
  * CSS class(es) for a ViewTransition phase.
@@ -10,96 +11,97 @@ import type { RevalidateParams, HandlerContext } from "./handler-context.js";
10
11
  export type ViewTransitionClass = Record<string, string> | string;
11
12
 
12
13
  /**
13
- * The context a transition({ when }) predicate receives.
14
- *
15
- * It mirrors the {@link ShouldRevalidateFn} args a `revalidate()` predicate
16
- * gets — the same navigation/action metadata — so the two read the same shape,
17
- * plus `get`/`env` for request-context reads. There is no full `HandlerContext`
18
- * here: the gate runs at the RSC-payload layer with the request context, not a
19
- * handler context, so handler-only sugar (`search`/`build`/`dev`/`headers`) is
20
- * absent by design. On ordinary routes, `get` can read what handlers or
21
- * middleware set via `ctx.set(...)`. On `ppr` routes the gate runs before route
22
- * handlers, so only middleware-established values are available.
23
- *
24
- * Field availability (all source fields are optional — never fabricated):
25
- * - `currentUrl` / `currentParams` / `fromRouteName` (the navigation SOURCE) are
26
- * populated on soft navigations and action-success revalidations. They are
27
- * undefined on an initial full document load and on action-error / no-JS error
28
- * paths that skip the navigation snapshot — there is no prior page to name.
29
- * - `nextUrl` / `nextParams` / `get` / `env` / `method` are always present;
30
- * `toRouteName` is present only when the target route is named (undefined for
31
- * unnamed/auto-generated routes, like `fromRouteName`).
32
- * - `actionId` / `actionUrl` / `actionResult` / `formData` are populated only
33
- * when a server action triggered the render; `method` is "POST" then, "GET"
34
- * otherwise. On no-JS (progressive-enhancement) action paths `actionId` may be
35
- * undefined when React cannot surface the action's stable id: the success
36
- * re-render still sets `actionUrl`/`formData` for a recognized action, but the
37
- * error-boundary re-render exposes `actionUrl` only when `actionId` resolved.
38
- * Malformed form bodies that fail before action detection expose no action
39
- * fields. Treat `actionId` as "the action, if known", not as "was this an
40
- * action".
41
- *
42
- * PREFETCH / CACHE CAVEAT (read this before gating on the source): the gate runs
43
- * server-side during resolution. A PREFETCHED navigation renders at prefetch
44
- * time, so `currentUrl`/`currentParams`/`fromRouteName` reflect the page the
45
- * prefetch fired from, NOT necessarily the page the user actually navigates from
46
- * — the decision is baked into the stored Flight payload and replayed verbatim.
47
- * A non-PPR `cache()`/prerender hit replays the stored transition with the
48
- * predicate NOT re-run. A `ppr` route instead evaluates the predicate before
49
- * handlers on every match, including cache, prerender, and PPR replay. Prefetch
50
- * still freezes the server decision into its Flight payload. If the gate must
51
- * reflect the exact click-time source, source-scope the prefetch
52
- * (`<Link prefetchKey=":source">`).
14
+ * One side of a navigation as the browser knows it when a
15
+ * `transition({ when })` predicate runs.
16
+ */
17
+ export interface RouteLocation {
18
+ readonly url: URL;
19
+ /** Merged route params of the location. */
20
+ readonly params: Readonly<Record<string, string>>;
21
+ /**
22
+ * The matched route's name (include prefixes applied), typed from the
23
+ * generated route map; undefined for an unnamed route or when unknown.
24
+ * Internal names never appear here.
25
+ */
26
+ readonly routeName: DefaultRouteName | undefined;
27
+ /**
28
+ * `history.state` of this entry at decision time. Read a typed slot with
29
+ * `Def.read(location)` (a `createLocationState()` definition).
30
+ */
31
+ readonly state: unknown;
32
+ }
33
+
34
+ /**
35
+ * A navigation side as the server sees it, for intercept's `when` selector:
36
+ * a {@link RouteLocation} without `state` (history state never reaches the
37
+ * server).
53
38
  */
54
- export type TransitionWhenContext<
55
- TParams = Record<string, string>,
56
- TEnv = unknown,
57
- > = Partial<
58
- Pick<
59
- RevalidateParams<TParams, TEnv>,
60
- "currentUrl" | "currentParams" | "fromRouteName"
61
- >
62
- > &
63
- Pick<
64
- RevalidateParams<TParams, TEnv>,
65
- | "nextUrl"
66
- | "nextParams"
67
- | "toRouteName"
68
- | "actionId"
69
- | "actionUrl"
70
- | "actionResult"
71
- | "formData"
72
- | "method"
73
- > &
74
- Pick<HandlerContext<any, TEnv>, "get" | "env">;
39
+ export type ServerRouteLocation = Omit<RouteLocation, "state">;
75
40
 
76
41
  /**
77
- * Predicate that gates whether a transition() applies for the current request.
42
+ * How the navigation being decided was started.
78
43
  *
79
- * Evaluated server-side outside any cache scope. On ordinary routes it runs
80
- * AFTER the route's handler, so `get(...)` can read handler/middleware state. A
81
- * route with `ppr` automatically hoists it before route handlers and reevaluates
82
- * it on every cache/prerender/PPR replay; there `get(...)` can read middleware
83
- * state, but not values set by route handlers. Return false to drop this
84
- * segment's transition for the request; return true to apply it. The
85
- * context ({@link TransitionWhenContext}) carries the same navigation/action
86
- * metadata a `revalidate()` predicate sees plus `get`/`env`. If it throws, the
87
- * error is reported to the router's onError (phase "rendering") and the
88
- * transition is dropped (the navigation does not hold).
44
+ * - `"push"` / `"replace"`: a Link click, `router.push()` / `router.replace()`
45
+ * or `navigate()`.
46
+ * - `"pop"`: browser back/forward (restored from the history cache or
47
+ * refetched).
48
+ * - `"action"`: the commit that applies a server action's revalidation,
49
+ * including the error-boundary commit of a failed action.
50
+ * - `"revalidate"`: a re-render of the current URL: `router.refresh()`,
51
+ * cross-tab invalidation, or the background revalidation of a stale entry.
52
+ */
53
+ export type TransitionWhenKind =
54
+ | "push"
55
+ | "replace"
56
+ | "pop"
57
+ | "action"
58
+ | "revalidate";
59
+
60
+ /**
61
+ * The context a `transition({ when })` predicate receives. The predicate runs
62
+ * in the BROWSER, once per navigation, at the first commit that presents the
63
+ * destination. The server never calls it.
89
64
  *
90
- * Distinct from intercept()'s `when` config selector, which runs at MATCH time
91
- * over `{ from, to, params, segments, … }`.
65
+ * - `from`: the committed location being left.
66
+ * - `to`: the destination. The same object as `from` for `"action"` and
67
+ * `"revalidate"`.
68
+ * - `isAction`: the matcher `revalidate()` predicates get; `false` off an
69
+ * action.
70
+ * - `action`: present only for `kind: "action"`.
71
+ */
72
+ export interface TransitionWhenContext {
73
+ readonly kind: TransitionWhenKind;
74
+ readonly from: RouteLocation;
75
+ readonly to: RouteLocation;
76
+ readonly isAction: IsActionFn;
77
+ /** Present only for `kind: "action"`. */
78
+ readonly action?: {
79
+ /** The action's hashed `$$id` in the browser; match with `isAction(fn)` instead. */
80
+ readonly id: string | undefined;
81
+ /** The FormData argument the action received (the 2nd argument under `useActionState`). */
82
+ readonly formData: FormData | undefined;
83
+ /** The action's return value on success. */
84
+ readonly result: unknown;
85
+ /** What a failed action threw (its error-boundary commit); `result` is undefined then. */
86
+ readonly error: unknown;
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Browser-run predicate that gates whether a navigation holds and animates.
92
92
  *
93
- * Scope: dropping a transition removes only THIS segment's contribution to the
94
- * navigation's hold. The startTransition hold is navigation-wide — it engages if
95
- * any matched segment still has a transition — so `when: false` makes the
96
- * navigation stream its loading fallback only when no other matched segment
97
- * keeps a transition (the common case: a single transition on the route).
93
+ * In `urls()` write it inline (`transition({ when: (ctx) => ... })`, which
94
+ * the build hoists into a client module; it may reference only its own
95
+ * bindings, globals and client-safe imports) or export it from a
96
+ * `"use client"` module and import it; in `clientUrls()` it is an inline
97
+ * function. Anything else (a server function, a non-function) fails route
98
+ * discovery.
98
99
  *
99
- * On non-PPR routes it runs only during fresh resolution. On PPR routes it runs
100
- * before handlers for every match, including runtime cache, build-time
101
- * prerender, and PPR segment replay. A prefetched navigation still freezes the
102
- * result to prefetch time; see {@link TransitionWhenContext}.
100
+ * `false` makes the navigation an urgent commit: no hold, no view transition,
101
+ * and a same-route navigation streams its `loading()` fallback. The segment
102
+ * keeps its key and its `<ViewTransition>` element (every class "none"), so
103
+ * nothing remounts. One predicate returning false decides for the whole
104
+ * navigation. A throw is logged with `console.error` and counts as `false`.
103
105
  */
104
106
  export type TransitionWhenFn = (ctx: TransitionWhenContext) => boolean;
105
107
 
@@ -132,13 +134,8 @@ export interface TransitionConfig {
132
134
  */
133
135
  viewTransition?: "auto" | false;
134
136
  /**
135
- * Optional server-side predicate that gates this transition per request. When
136
- * present and it returns false, the router drops this segment's transition for
137
- * the request, so the navigation streams its loading fallback instead of
138
- * holding. PPR routes evaluate it before route handlers and on every replay;
139
- * other routes evaluate it after handlers on fresh resolution. The predicate
140
- * is server-only and never serialized to the client; only its resolved effect
141
- * crosses. See {@link TransitionWhenFn}.
137
+ * Optional browser-run predicate that gates this transition per
138
+ * navigation. See {@link TransitionWhenFn}.
142
139
  */
143
140
  when?: TransitionWhenFn;
144
141
  }
@@ -285,6 +282,12 @@ export interface MatchResult {
285
282
  * decline its optimistic presentation for targets an intercept would claim.
286
283
  */
287
284
  interceptTargets?: string[];
285
+ /**
286
+ * Set on a partial match when an intercept targets the matched route,
287
+ * whether or not it applied from this source. The response depends on the
288
+ * source, so the RSC handler scopes its prefetch entry to it (#1007).
289
+ */
290
+ interceptTargeted?: true;
288
291
  /**
289
292
  * Redirect URL for trailing slash normalization.
290
293
  * When set, the RSC handler should return a 308 redirect to this URL
@@ -417,10 +417,9 @@ export type PathHelpers<TEnv> = {
417
417
  * transition cannot fire without a startTransition. See
418
418
  * skills/view-transitions for the startTransition x ViewTransition matrix.
419
419
  *
420
- * Pass `when: (ctx) => boolean` to gate the transition per request. It normally
421
- * runs after the route handler; `ppr` routes automatically run it before route
422
- * handlers and on every replay, where `ctx.get(...)` sees middleware state but
423
- * not handler-set values.
420
+ * Pass `when` (inline, hoisted into a client module by the build, or a
421
+ * "use client" export) to gate the navigation's hold and view transition in
422
+ * the browser (never on the server); see TransitionWhenFn.
424
423
  */
425
424
  transition: {
426
425
  (): TransitionItem;
@@ -59,30 +59,33 @@ export interface PartialPrerenderProps {
59
59
  /**
60
60
  * Operational tags attached to the captured shell entry for
61
61
  * `updateTag()`/`revalidateTag()`-driven eviction. UNIONED with the tags the
62
- * capture render auto-collects (the shell's own non-loader request tags).
62
+ * capture collects: its doc record's tags (what the handler layer and the
63
+ * server components it rendered recorded) and its bake-lane loaders'.
63
64
  */
64
65
  tags?: string[];
65
66
  /**
66
- * Upper bound (serialized UTF-8 bytes) on the capture data snapshot riding
67
- * inside the shell entry. The snapshot duplicates every cache-store value
68
- * the capture pinned, so a page over a large cache() segment can push the
69
- * entry toward store value limits (Cloudflare KV caps a value at 25 MiB).
70
- * Over the cap the snapshot is skipped: the shell is still stored and
71
- * served, but pinned reads fall back to the live store, so drifted cached
72
- * content can hydration-mismatch and be repaired client-side (the
73
- * pre-snapshot behavior). Reported once per key. Defaults to 8 MiB.
67
+ * Upper bound (serialized UTF-8 bytes) on the loader pins in the capture
68
+ * data snapshot: the bake-lane loader records. The doc record (the handler
69
+ * layer every HIT replays) is exempt. Over the cap the pins are dropped: the
70
+ * shell is still stored and every HIT still replays the handler layer, but the
71
+ * pinned loaders read the live store, so drifted cached content can
72
+ * hydration-mismatch and be repaired client-side. Reported once per key.
73
+ * Defaults to 8 MiB. The whole entry is bounded separately by the store's
74
+ * value limit (Cloudflare KV: 25 MiB); an entry over it is refused.
74
75
  */
75
76
  maxSnapshotBytes?: number;
76
77
  /**
77
- * Capture settle budget in MILLISECONDS (default 15000). Bounds the whole
78
- * background capture: the wait for deferred shell material — top-level
79
- * pushed handle promises (`ctx.use(Meta)(promise.then(...))` and friends)
80
- * are AWAITED and their settled values baked into the stored shell — AND
81
- * the fizz prerender deadline. Declare it to tighten the budget below the
82
- * default or when a route's shell material takes longer than 15s to
83
- * settle. A budget that expires with pushes still pending REFUSES the
84
- * capture (the route stays MISS with the once-per-key warning) — a shell
85
- * with missing head material is never stored. Capture is background work
78
+ * Capture settle budget in MILLISECONDS (default 15000). One deadline for
79
+ * the whole background capture: the match (the handlers, and the loaders
80
+ * they await), the wait for handler output (handle pushes and the promises
81
+ * nested in them, bake-lane loaders, and the doc record's encode, which
82
+ * awaits handler promises and async server components), AND the fizz
83
+ * prerender. Everything a handler produces is
84
+ * awaited and baked into the stored shell. Declare it to tighten the
85
+ * budget below the default or when a route's shell material takes longer
86
+ * than 15s to settle. A budget that expires before the doc record is
87
+ * written REFUSES the capture (the route stays MISS with the once-per-key
88
+ * warning): a shell with missing handler output is never stored. Capture is background work
86
89
  * (waitUntil), so a longer budget costs latency-to-HIT only, never a served
87
90
  * response; the platform waitUntil lifetime (workerd: ~30s past response
88
91
  * completion) is the physical ceiling — the default's derivation lives in
@@ -211,6 +211,9 @@ export async function discoverRouters(
211
211
  routerId: id,
212
212
  ...(router.__basename ? { urlPrefix: router.__basename } : {}),
213
213
  ...(collectClientFallbackRef ? { collectClientFallbackRef } : {}),
214
+ // Discovery evaluates the tree in the react-server graph, where a
215
+ // "use client" import is a client reference: reject server `when`s.
216
+ validateTransitionWhen: true,
214
217
  }),
215
218
  );
216
219
  routerMountIndex++;
@@ -64,6 +64,14 @@ export async function runShellPrerenderPhase(
64
64
  const startTotal = performance.now();
65
65
  console.log(`[rango] Shell-prerendering ${candidates.length} URL(s)...`);
66
66
 
67
+ // Hold the process open for the phase. The capture's waits are unref'd
68
+ // timers (retry delay, Flight quiesce hops) so a runtime capture never keeps
69
+ // Node alive; every bundle is already written and the build temp server has
70
+ // no file watcher (#947). Its only remaining handle is the HMR WebSocket on
71
+ // 24678, which fails to bind silently when another Vite server holds the
72
+ // port; `vite build` then exited 0 mid-phase without writing
73
+ // __shell-manifest.js.
74
+ const keepAlive = setInterval(() => {}, 60_000);
67
75
  let restoreClientRequire: (() => void) | undefined;
68
76
  try {
69
77
  // Runner access needs the RunnableDevEnvironment surface; the environments
@@ -282,9 +290,7 @@ export async function runShellPrerenderPhase(
282
290
  const value: BuildShellEntry = {
283
291
  entry: res.entry,
284
292
  ttl: policy.ttl,
285
- swr: policy.swr,
286
293
  tags: res.tags,
287
- routeName: cand.routeName,
288
294
  };
289
295
  staged.push({
290
296
  key: buildShellManifestKey(cand.urlPath),
@@ -343,6 +349,7 @@ export async function runShellPrerenderPhase(
343
349
  // buildApp post hook's finally on success, buildEnd on an aborted build.
344
350
  delete (globalThis as any).__loadPrerenderManifestModule;
345
351
  restoreClientRequire?.();
352
+ clearInterval(keepAlive);
346
353
  }
347
354
  }
348
355