@rangojs/router 0.18.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 (244) hide show
  1. package/dist/types/browser/event-controller.d.ts +3 -3
  2. package/dist/types/browser/history-state.d.ts +2 -0
  3. package/dist/types/browser/navigation-transaction.d.ts +20 -2
  4. package/dist/types/browser/partial-update.d.ts +16 -1
  5. package/dist/types/browser/prefetch/cache.d.ts +6 -6
  6. package/dist/types/browser/react/Link.d.ts +19 -9
  7. package/dist/types/browser/react/context.d.ts +2 -2
  8. package/dist/types/browser/react/location-state-shared.d.ts +95 -56
  9. package/dist/types/browser/react/location-state.d.ts +1 -1
  10. package/dist/types/browser/transition-when.d.ts +80 -0
  11. package/dist/types/browser/types.d.ts +104 -9
  12. package/dist/types/build/generate-manifest.d.ts +8 -0
  13. package/dist/types/cache/background-task.d.ts +24 -2
  14. package/dist/types/cache/cache-exec-scope.d.ts +31 -0
  15. package/dist/types/cache/cache-key-utils.d.ts +44 -13
  16. package/dist/types/cache/cache-policy.d.ts +1 -1
  17. package/dist/types/cache/cache-runtime.d.ts +7 -0
  18. package/dist/types/cache/cache-scope.d.ts +117 -35
  19. package/dist/types/cache/cache-tag.d.ts +24 -4
  20. package/dist/types/cache/cf/cf-cache-store.d.ts +55 -44
  21. package/dist/types/cache/cf/cf-shell-frame.d.ts +1 -1
  22. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +0 -15
  23. package/dist/types/cache/document-cache.d.ts +10 -0
  24. package/dist/types/cache/handle-snapshot.d.ts +40 -8
  25. package/dist/types/cache/invalidation-order.d.ts +23 -0
  26. package/dist/types/cache/request-tag-mask.d.ts +54 -0
  27. package/dist/types/cache/shell-snapshot.d.ts +88 -141
  28. package/dist/types/cache/tag-invalidation.d.ts +38 -14
  29. package/dist/types/cache/types.d.ts +68 -61
  30. package/dist/types/cache/vercel/vercel-cache-store.d.ts +23 -16
  31. package/dist/types/client-urls/navigation.d.ts +28 -2
  32. package/dist/types/client-urls/server-projection.d.ts +5 -2
  33. package/dist/types/client-urls/types.d.ts +5 -5
  34. package/dist/types/client.d.ts +1 -1
  35. package/dist/types/index.d.ts +1 -1
  36. package/dist/types/index.rsc.d.ts +1 -1
  37. package/dist/types/route-definition/helpers-types.d.ts +11 -11
  38. package/dist/types/route-name.d.ts +7 -0
  39. package/dist/types/router/error-handling.d.ts +1 -1
  40. package/dist/types/router/intercept-resolution.d.ts +2 -0
  41. package/dist/types/router/match-context.d.ts +5 -0
  42. package/dist/types/router/match-middleware/background-revalidation.d.ts +0 -4
  43. package/dist/types/router/router-options.d.ts +4 -1
  44. package/dist/types/router/segment-resolution/loader-cache.d.ts +18 -1
  45. package/dist/types/router/segment-resolution/loader-mask.d.ts +3 -4
  46. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +14 -40
  47. package/dist/types/router/segment-resolution/mask-nested.d.ts +11 -3
  48. package/dist/types/router/segment-resolution/view-transition-default.d.ts +7 -5
  49. package/dist/types/router/transition-when.d.ts +24 -10
  50. package/dist/types/rsc/attach-transition-when.d.ts +18 -0
  51. package/dist/types/rsc/full-payload.d.ts +14 -7
  52. package/dist/types/rsc/response-cache-serve.d.ts +15 -12
  53. package/dist/types/rsc/shell-build-manifest.d.ts +4 -3
  54. package/dist/types/rsc/shell-capture-constants.d.ts +77 -20
  55. package/dist/types/rsc/shell-capture.d.ts +78 -81
  56. package/dist/types/rsc/shell-serve.d.ts +19 -48
  57. package/dist/types/rsc/types.d.ts +2 -0
  58. package/dist/types/segment-system.d.ts +7 -0
  59. package/dist/types/server/context.d.ts +89 -61
  60. package/dist/types/server/cookie-store.d.ts +30 -18
  61. package/dist/types/server/handle-store.d.ts +52 -1
  62. package/dist/types/server/request-context.d.ts +66 -45
  63. package/dist/types/server/request-headers.d.ts +44 -0
  64. package/dist/types/server.d.ts +1 -0
  65. package/dist/types/testing/e2e/index.d.ts +1 -1
  66. package/dist/types/testing/index.d.ts +3 -2
  67. package/dist/types/testing/internal/action-id.d.ts +7 -0
  68. package/dist/types/testing/location-state-key.d.ts +24 -0
  69. package/dist/types/testing/render-route.d.ts +17 -1
  70. package/dist/types/testing/run-transition-when.d.ts +44 -69
  71. package/dist/types/testing/serve-shell-request.d.ts +7 -6
  72. package/dist/types/testing/shell-status.d.ts +34 -22
  73. package/dist/types/transition-when-ref.d.ts +79 -0
  74. package/dist/types/types/cache-types.d.ts +15 -3
  75. package/dist/types/types/index.d.ts +1 -1
  76. package/dist/types/types/segments.d.ts +86 -71
  77. package/dist/types/urls/path-helper-types.d.ts +3 -4
  78. package/dist/types/urls/pattern-types.d.ts +3 -4
  79. package/dist/types/vite/plugins/transition-when-hoist.d.ts +45 -0
  80. package/dist/vite/index.js +960 -97
  81. package/package.json +1 -1
  82. package/skills/cache-guide/SKILL.md +76 -49
  83. package/skills/caching/SKILL.md +167 -69
  84. package/skills/client-urls/SKILL.md +1 -1
  85. package/skills/comparison/references/framework-comparison.md +8 -5
  86. package/skills/document-cache/SKILL.md +24 -0
  87. package/skills/hooks/SKILL.md +3 -1
  88. package/skills/hooks/navigation.md +23 -2
  89. package/skills/hooks/state.md +98 -6
  90. package/skills/intercept/SKILL.md +29 -12
  91. package/skills/loader/SKILL.md +52 -21
  92. package/skills/middleware/SKILL.md +18 -0
  93. package/skills/migrate-nextjs/SKILL.md +3 -3
  94. package/skills/observability/SKILL.md +6 -5
  95. package/skills/parallel/SKILL.md +1 -1
  96. package/skills/ppr/SKILL.md +129 -77
  97. package/skills/prerender/SKILL.md +3 -3
  98. package/skills/rango/SKILL.md +25 -6
  99. package/skills/route/SKILL.md +2 -0
  100. package/skills/testing/SKILL.md +27 -25
  101. package/skills/testing/cache-prerender.md +9 -9
  102. package/skills/testing/client-components.md +45 -3
  103. package/skills/testing/loader.md +3 -2
  104. package/skills/testing/middleware.md +1 -0
  105. package/skills/testing/predicates.md +49 -49
  106. package/skills/testing/render-handler.md +1 -0
  107. package/skills/testing/serve-shell-request.md +14 -0
  108. package/skills/testing/server-actions.md +9 -3
  109. package/skills/theme/SKILL.md +8 -4
  110. package/skills/typesafety/env-and-bindings.md +8 -0
  111. package/skills/use-cache/SKILL.md +64 -19
  112. package/skills/view-transitions/SKILL.md +150 -29
  113. package/src/__augment-tests__/augmented.check.ts +23 -0
  114. package/src/browser/event-controller.ts +4 -4
  115. package/src/browser/history-state.ts +52 -1
  116. package/src/browser/link-interceptor.ts +4 -0
  117. package/src/browser/navigation-bridge.ts +77 -18
  118. package/src/browser/navigation-client.ts +4 -3
  119. package/src/browser/navigation-store.ts +43 -1
  120. package/src/browser/navigation-transaction.ts +31 -11
  121. package/src/browser/partial-update.ts +146 -35
  122. package/src/browser/prefetch/cache.ts +6 -6
  123. package/src/browser/prefetch/fetch.ts +2 -2
  124. package/src/browser/react/Link.tsx +38 -33
  125. package/src/browser/react/NavigationProvider.tsx +2 -2
  126. package/src/browser/react/context.ts +2 -2
  127. package/src/browser/react/location-state-shared.ts +241 -49
  128. package/src/browser/react/location-state.ts +5 -2
  129. package/src/browser/rsc-router.tsx +4 -0
  130. package/src/browser/server-action-bridge.ts +84 -15
  131. package/src/browser/transition-when.ts +205 -0
  132. package/src/browser/types.ts +124 -10
  133. package/src/build/generate-manifest.ts +77 -1
  134. package/src/cache/background-task.ts +55 -2
  135. package/src/cache/cache-exec-scope.ts +48 -1
  136. package/src/cache/cache-key-utils.ts +56 -16
  137. package/src/cache/cache-policy.ts +9 -4
  138. package/src/cache/cache-runtime.ts +197 -144
  139. package/src/cache/cache-scope.ts +304 -112
  140. package/src/cache/cache-tag.ts +105 -13
  141. package/src/cache/cf/cf-cache-store.ts +152 -76
  142. package/src/cache/cf/cf-shell-frame.ts +2 -2
  143. package/src/cache/cf/cf-tag-marker-memo.ts +11 -100
  144. package/src/cache/document-cache.ts +53 -7
  145. package/src/cache/handle-snapshot.ts +52 -9
  146. package/src/cache/invalidation-order.ts +84 -0
  147. package/src/cache/request-tag-mask.ts +147 -0
  148. package/src/cache/shell-snapshot.ts +130 -348
  149. package/src/cache/tag-invalidation.ts +57 -51
  150. package/src/cache/types.ts +71 -67
  151. package/src/cache/vercel/vercel-cache-store.ts +61 -58
  152. package/src/client-urls/client-root.tsx +8 -4
  153. package/src/client-urls/client-urls.ts +8 -6
  154. package/src/client-urls/navigation.ts +55 -4
  155. package/src/client-urls/server-projection.ts +31 -10
  156. package/src/client-urls/types.ts +12 -5
  157. package/src/client.tsx +2 -0
  158. package/src/host/cookie-handler.ts +2 -1
  159. package/src/index.rsc.ts +3 -0
  160. package/src/index.ts +3 -0
  161. package/src/loader-redirect.tsx +7 -3
  162. package/src/prerender/build-shell-capture.ts +155 -146
  163. package/src/route-definition/dsl-helpers.ts +2 -0
  164. package/src/route-definition/helpers-types.ts +11 -11
  165. package/src/route-name.ts +12 -0
  166. package/src/router/content-negotiation.ts +4 -1
  167. package/src/router/error-handling.ts +5 -2
  168. package/src/router/handler-context.ts +10 -7
  169. package/src/router/intercept-resolution.ts +11 -1
  170. package/src/router/loader-resolution.ts +28 -3
  171. package/src/router/logging.ts +5 -3
  172. package/src/router/match-api.ts +62 -34
  173. package/src/router/match-context.ts +5 -0
  174. package/src/router/match-handlers.ts +9 -29
  175. package/src/router/match-middleware/background-revalidation.ts +3 -5
  176. package/src/router/match-middleware/cache-lookup.ts +35 -19
  177. package/src/router/match-middleware/cache-store.ts +15 -9
  178. package/src/router/match-result.ts +1 -0
  179. package/src/router/middleware.ts +2 -5
  180. package/src/router/navigation-snapshot.ts +6 -5
  181. package/src/router/prerender-match.ts +4 -0
  182. package/src/router/request-classification.ts +7 -4
  183. package/src/router/router-options.ts +4 -1
  184. package/src/router/segment-resolution/fresh.ts +0 -4
  185. package/src/router/segment-resolution/loader-cache.ts +150 -25
  186. package/src/router/segment-resolution/loader-mask.ts +3 -4
  187. package/src/router/segment-resolution/loader-snapshot.ts +15 -78
  188. package/src/router/segment-resolution/mask-nested.ts +47 -22
  189. package/src/router/segment-resolution/revalidation.ts +0 -3
  190. package/src/router/segment-resolution/view-transition-default.ts +6 -21
  191. package/src/router/telemetry.ts +6 -3
  192. package/src/router/transition-when.ts +34 -52
  193. package/src/router.ts +2 -1
  194. package/src/rsc/attach-transition-when.ts +47 -0
  195. package/src/rsc/full-payload.ts +30 -10
  196. package/src/rsc/handler.ts +12 -10
  197. package/src/rsc/loader-fetch.ts +2 -1
  198. package/src/rsc/manifest-init.ts +4 -0
  199. package/src/rsc/origin-guard.ts +8 -5
  200. package/src/rsc/progressive-enhancement.ts +7 -30
  201. package/src/rsc/response-cache-serve.ts +68 -80
  202. package/src/rsc/response-route-handler.ts +2 -2
  203. package/src/rsc/rsc-rendering.ts +206 -252
  204. package/src/rsc/server-action.ts +8 -20
  205. package/src/rsc/shell-build-manifest.ts +22 -20
  206. package/src/rsc/shell-capture-constants.ts +136 -20
  207. package/src/rsc/shell-capture.ts +352 -589
  208. package/src/rsc/shell-serve.ts +73 -109
  209. package/src/rsc/ssr-setup.ts +3 -2
  210. package/src/rsc/types.ts +2 -0
  211. package/src/segment-system.tsx +40 -7
  212. package/src/server/context.ts +189 -124
  213. package/src/server/cookie-store.ts +153 -127
  214. package/src/server/handle-store.ts +245 -27
  215. package/src/server/request-context.ts +123 -84
  216. package/src/server/request-headers.ts +134 -0
  217. package/src/server.ts +4 -0
  218. package/src/testing/dispatch.ts +4 -7
  219. package/src/testing/e2e/index.ts +1 -0
  220. package/src/testing/index.ts +5 -0
  221. package/src/testing/internal/action-id.ts +25 -0
  222. package/src/testing/internal/context.ts +1 -1
  223. package/src/testing/location-state-key.ts +45 -0
  224. package/src/testing/render-handler.ts +1 -1
  225. package/src/testing/render-route.tsx +109 -16
  226. package/src/testing/run-client-revalidate.ts +3 -19
  227. package/src/testing/run-loader.ts +5 -1
  228. package/src/testing/run-transition-when.ts +109 -172
  229. package/src/testing/serve-shell-request.ts +10 -7
  230. package/src/testing/shell-status.ts +60 -56
  231. package/src/transition-when-ref.ts +220 -0
  232. package/src/types/cache-types.ts +15 -3
  233. package/src/types/index.ts +3 -0
  234. package/src/types/segments.ts +94 -91
  235. package/src/urls/path-helper-types.ts +3 -4
  236. package/src/urls/pattern-types.ts +3 -4
  237. package/src/vite/discovery/discover-routers.ts +3 -0
  238. package/src/vite/discovery/shell-prerender-phase.ts +0 -2
  239. package/src/vite/discovery/virtual-module-codegen.ts +16 -6
  240. package/src/vite/plugins/transition-when-hoist.ts +1064 -0
  241. package/src/vite/rango.ts +2 -0
  242. package/src/vite/router-discovery.ts +75 -5
  243. package/dist/types/rsc/transition-gate.d.ts +0 -23
  244. package/src/rsc/transition-gate.ts +0 -86
@@ -8,6 +8,7 @@ import {
8
8
  } from "./revalidate-chain.js";
9
9
  import { encodeClientRevalidationDecisions } from "./revalidation-protocol.js";
10
10
  import type { ClientUrlPatterns } from "./types.js";
11
+ import type { TransitionWhenFn } from "../types/segments.js";
11
12
 
12
13
  export interface ClientUrlNavigationIntent {
13
14
  readonly routeId: string;
@@ -16,6 +17,26 @@ export interface ClientUrlNavigationIntent {
16
17
  /** Absolute destination pathname (mount included) and search ("?..." or ""). */
17
18
  readonly pathname: string;
18
19
  readonly search: string;
20
+ /**
21
+ * transition({ when }) gated this navigation off at the swap: the
22
+ * destination presents urgently instead of in a transition lane.
23
+ */
24
+ readonly transitionGatedOff?: boolean;
25
+ }
26
+
27
+ /** What the bridge needs to decide transition({ when }) at the swap. */
28
+ export interface ClientUrlDestination {
29
+ /** Definition-local params of the destination match. */
30
+ readonly params: Readonly<Record<string, string>>;
31
+ /** Canonical route name (include prefix applied); undefined when unnamed. */
32
+ readonly routeName: string | undefined;
33
+ /** The destination route's own `when`, from its "use client" definition. */
34
+ readonly when: TransitionWhenFn | undefined;
35
+ /**
36
+ * Param names the ORIGIN route matched inside the group: the committed
37
+ * params minus these are the mount's (include prefix) params.
38
+ */
39
+ readonly originLocalParamNames: readonly string[];
19
40
  }
20
41
 
21
42
  interface ActiveClientUrlGroup {
@@ -24,6 +45,8 @@ interface ActiveClientUrlGroup {
24
45
  readonly mount: string;
25
46
  /** include() route-name prefix ("" at root) for canonical name composition. */
26
47
  readonly namePrefix: string;
48
+ /** The group route on screen (ClientUrlsRoot's committed routeId). */
49
+ readonly routeId: string | undefined;
27
50
  readonly setIntent: (intent: ClientUrlNavigationIntent | null) => void;
28
51
  intent: ClientUrlNavigationIntent | null;
29
52
  }
@@ -48,6 +71,12 @@ export function setActiveInterceptTargets(
48
71
 
49
72
  export interface ClientUrlNavigationPresentation {
50
73
  readonly routeId: string;
74
+ /**
75
+ * The transition({ when }) decision made at the optimistic swap (true =
76
+ * gated off), or undefined when nothing was swapped (a same-route
77
+ * navigation decides at the canonical commit).
78
+ */
79
+ readonly transitionGatedOff: boolean | undefined;
51
80
  clear(): void;
52
81
  }
53
82
 
@@ -75,11 +104,13 @@ export function registerClientUrlGroup(
75
104
  mount: string,
76
105
  namePrefix: string,
77
106
  setIntent: (intent: ClientUrlNavigationIntent | null) => void,
107
+ routeId?: string,
78
108
  ): () => void {
79
109
  const group: ActiveClientUrlGroup = {
80
110
  definition,
81
111
  mount,
82
112
  namePrefix,
113
+ routeId,
83
114
  setIntent,
84
115
  intent: null,
85
116
  };
@@ -93,6 +124,7 @@ export function registerClientUrlGroup(
93
124
  export function beginClientUrlNavigation(
94
125
  targetUrl: URL,
95
126
  signal: AbortSignal,
127
+ decideTransition?: (destination: ClientUrlDestination) => boolean,
96
128
  ): ClientUrlNavigationPresentation | null {
97
129
  const group = activeGroup;
98
130
  if (!group) return null;
@@ -110,11 +142,28 @@ export function beginClientUrlNavigation(
110
142
  const record = group.definition.routes.find(
111
143
  (candidate) => candidate.id === match.routeKey,
112
144
  );
113
- if (record?.name) {
114
- const canonicalName = group.namePrefix
145
+ const canonicalName = record?.name
146
+ ? group.namePrefix
115
147
  ? `${group.namePrefix}.${record.name}`
116
- : record.name;
117
- if (activeInterceptTargets.has(canonicalName)) return null;
148
+ : record.name
149
+ : undefined;
150
+ if (canonicalName && activeInterceptTargets.has(canonicalName)) return null;
151
+
152
+ // A same-route intent never swaps (ClientUrlsRoot), so transition({ when })
153
+ // decides at the canonical commit; a cross-route one decides here, at the
154
+ // navigation's first presentation.
155
+ let transitionGatedOff: boolean | undefined;
156
+ if (decideTransition && match.routeKey !== group.routeId) {
157
+ const originLocal = stripMountPrefix(window.location.pathname, group.mount);
158
+ const originMatch =
159
+ originLocal === null ? null : group.definition.match(originLocal);
160
+ const when = record?.transition?.when;
161
+ transitionGatedOff = decideTransition({
162
+ params: match.params,
163
+ routeName: canonicalName,
164
+ when: typeof when === "function" ? when : undefined,
165
+ originLocalParamNames: Object.keys(originMatch?.params ?? {}),
166
+ });
118
167
  }
119
168
 
120
169
  const intent: ClientUrlNavigationIntent = {
@@ -122,6 +171,7 @@ export function beginClientUrlNavigation(
122
171
  params: match.params,
123
172
  pathname: targetUrl.pathname,
124
173
  search: targetUrl.search,
174
+ transitionGatedOff,
125
175
  };
126
176
  group.intent = intent;
127
177
  group.setIntent(intent);
@@ -142,6 +192,7 @@ export function beginClientUrlNavigation(
142
192
 
143
193
  return {
144
194
  routeId: match.routeKey,
195
+ transitionGatedOff,
145
196
  clear() {
146
197
  signal.removeEventListener("abort", clear);
147
198
  clear();
@@ -1,3 +1,4 @@
1
+ import { requestHeaders } from "../server/request-headers.js";
1
2
  import * as React from "react";
2
3
  import type { SearchSchemaValue } from "../search-params.js";
3
4
  import { getLoaderLazy } from "../server/loader-registry.js";
@@ -27,6 +28,7 @@ import {
27
28
  ClientUrlsLoading,
28
29
  ClientUrlsRoot,
29
30
  } from "./client-root.js";
31
+ import { createClientUrlsWhenRef } from "../transition-when-ref.js";
30
32
  import type {
31
33
  ClientTransitionConfig,
32
34
  ClientUrlInterceptRecord,
@@ -94,9 +96,12 @@ export interface ClientUrlProjectionRoute {
94
96
  * document renders await them before first flush. Absent (= none) in
95
97
  * projections serialized before the option existed. */
96
98
  readonly awaitedLoaderIndices?: readonly number[];
97
- /** Data-only transition config (no `when` — server-tree only); absent in
98
- * projections serialized before transition support. */
99
+ /** Data-only transition config (never `when`); absent in projections
100
+ * serialized before transition support. */
99
101
  readonly transition?: Readonly<ClientTransitionConfig>;
102
+ /** The route's transition() declares a browser-run `when`. Materialization
103
+ * attaches a clientUrls when reference (definition + route id). */
104
+ readonly transitionWhen?: true;
100
105
  }
101
106
 
102
107
  export interface ClientUrlProjectionIntercept {
@@ -249,11 +254,12 @@ function serializeTransition(
249
254
  ): Readonly<ClientTransitionConfig> | undefined {
250
255
  const source = route.transition;
251
256
  if (!source) return undefined;
252
- // The DSL already validated shape and rejected `when`; re-copy defensively
253
- // so the projection is a plain JSON value (class maps included).
257
+ // The DSL already validated shape; re-copy defensively so the projection is
258
+ // a plain JSON value (class maps included). `when` is a function: the
259
+ // projection records only that it exists (transitionWhen).
254
260
  const config: Record<string, unknown> = {};
255
261
  for (const [key, value] of Object.entries(source)) {
256
- if (value === undefined) continue;
262
+ if (value === undefined || key === "when") continue;
257
263
  config[key] =
258
264
  typeof value === "object" && value !== null
259
265
  ? Object.freeze({ ...(value as Record<string, string>) })
@@ -289,6 +295,9 @@ function serializeRoute(route: ClientUrlRouteRecord): ClientUrlProjectionRoute {
289
295
  ? { awaitedLoaderIndices: Object.freeze(awaitedLoaderIndices) }
290
296
  : {}),
291
297
  ...(transition ? { transition } : {}),
298
+ ...(typeof route.transition?.when === "function"
299
+ ? { transitionWhen: true as const }
300
+ : {}),
292
301
  });
293
302
  }
294
303
 
@@ -433,7 +442,7 @@ function clientDecisionsFor(
433
442
  return decisionsByRequest.get(request) ?? null;
434
443
  }
435
444
  const decoded = decodeClientRevalidationDecisions(
436
- request.headers.get(CLIENT_REVALIDATION_HEADER),
445
+ requestHeaders(request).get(CLIENT_REVALIDATION_HEADER),
437
446
  );
438
447
  decisionsByRequest.set(request, decoded);
439
448
  return decoded;
@@ -561,9 +570,20 @@ function materializeRouteItems(
561
570
  ),
562
571
  ]
563
572
  : []),
564
- // Data-only per-route transition config: same child position as a
565
- // hand-written server transition(config) — no `when`.
566
- ...(route.transition ? [transition({ ...route.transition })] : []),
573
+ // Per-route transition config: same child position as a
574
+ // hand-written server transition(config). A declared `when` rides
575
+ // as a reference to the definition + route id; the browser calls
576
+ // the function the "use client" module declares.
577
+ ...(route.transition
578
+ ? [
579
+ transition({
580
+ ...route.transition,
581
+ ...(route.transitionWhen
582
+ ? { when: createClientUrlsWhenRef(reference, route.id) }
583
+ : {}),
584
+ }),
585
+ ]
586
+ : []),
567
587
  ],
568
588
  ) as AllUseItems,
569
589
  );
@@ -596,7 +616,8 @@ function materializeRouteItems(
596
616
  const isInGroupOrigin = ({ from }: InterceptSelectorContext): boolean =>
597
617
  mount === "/"
598
618
  ? true
599
- : from.pathname === mount || from.pathname.startsWith(`${mount}/`);
619
+ : from.url.pathname === mount ||
620
+ from.url.pathname.startsWith(`${mount}/`);
600
621
  const slotNames = [...new Set(intercepts.map((record) => record.slotName))];
601
622
  const buildInterceptItems = (): AllUseItems[] =>
602
623
  intercepts.map(
@@ -123,14 +123,21 @@ export interface ClientUrlLoaderRecord {
123
123
  }
124
124
 
125
125
  /**
126
- * The data-only subset of TransitionConfig a clientUrls() route may declare:
127
- * ViewTransition classes/name plus the boundary opt-out. The `when` gate is a
128
- * server-executed predicate and stays in the server tree — it cannot cross the
129
- * "use client" projection boundary.
126
+ * The TransitionConfig subset a clientUrls() route may declare: ViewTransition
127
+ * classes/name, the boundary opt-out, and the browser-run `when` gate. The
128
+ * projection carries only a "has when" marker; the browser calls the function
129
+ * this module declares (transition-when-ref.ts resolveTransitionWhen).
130
130
  */
131
131
  export type ClientTransitionConfig = Pick<
132
132
  TransitionConfig,
133
- "enter" | "exit" | "update" | "share" | "default" | "name" | "viewTransition"
133
+ | "enter"
134
+ | "exit"
135
+ | "update"
136
+ | "share"
137
+ | "default"
138
+ | "name"
139
+ | "viewTransition"
140
+ | "when"
134
141
  >;
135
142
 
136
143
  export interface ClientUrlRouteRecord {
package/src/client.tsx CHANGED
@@ -396,6 +396,8 @@ export { useNonce } from "./browser/react/nonce-context.js";
396
396
  export type {
397
397
  RouterInstance,
398
398
  RouterNavigateOptions,
399
+ HistoryState,
400
+ PlainHistoryState,
399
401
  ReadonlyURLSearchParams,
400
402
  ActionState,
401
403
  ActionLifecycleState,
@@ -1,3 +1,4 @@
1
+ import { requestHeaders } from "../server/request-headers.js";
1
2
  import type { HostOverrideConfig } from "./types.js";
2
3
  import type { RouterRequestInput } from "../router/router-interfaces.js";
3
4
  import { matchPattern, parseRequest } from "./pattern-matcher.js";
@@ -9,7 +10,7 @@ import {
9
10
  import { parseCookiesFromHeader } from "../server/cookie-parse.js";
10
11
 
11
12
  export function parseCookies(request: Request): Record<string, string> {
12
- return parseCookiesFromHeader(request.headers.get("cookie"));
13
+ return parseCookiesFromHeader(requestHeaders(request).get("cookie"));
13
14
  }
14
15
 
15
16
  export function getCookie(request: Request, name: string): string | undefined {
package/src/index.rsc.ts CHANGED
@@ -72,6 +72,9 @@ export type {
72
72
  TransitionConfig,
73
73
  TransitionWhenFn,
74
74
  TransitionWhenContext,
75
+ TransitionWhenKind,
76
+ RouteLocation,
77
+ ServerRouteLocation,
75
78
  ViewTransitionClass,
76
79
  } from "./types.js";
77
80
 
package/src/index.ts CHANGED
@@ -94,6 +94,9 @@ export type {
94
94
  TransitionConfig,
95
95
  TransitionWhenFn,
96
96
  TransitionWhenContext,
97
+ TransitionWhenKind,
98
+ RouteLocation,
99
+ ServerRouteLocation,
97
100
  ViewTransitionClass,
98
101
  } from "./types.js";
99
102
 
@@ -1,7 +1,10 @@
1
1
  "use client";
2
2
  import type { ReactNode } from "react";
3
3
  import { useEffect } from "react";
4
- import type { NavigateOptionsInternal } from "./browser/types.js";
4
+ import type {
5
+ NavigateOptionsInternal,
6
+ RouterNavigateOptions,
7
+ } from "./browser/types.js";
5
8
  import { useRouter } from "./browser/react/use-router.js";
6
9
  import { validateExternalRedirect } from "./browser/validate-redirect-origin.js";
7
10
  import { resolveSameOriginRedirect } from "./redirect-origin.js";
@@ -48,8 +51,9 @@ export function LoaderRedirect({
48
51
  // so it must not log a "blocked" error on its way to the branch below.
49
52
  const sameOrigin = resolveSameOriginRedirect(to, origin);
50
53
  if (sameOrigin) {
51
- const options: Omit<NavigateOptionsInternal, "replace"> | undefined =
52
- state ? { state, _skipCache: true } : undefined;
54
+ const options:
55
+ | (RouterNavigateOptions & Pick<NavigateOptionsInternal, "_skipCache">)
56
+ | undefined = state ? { state, _skipCache: true } : undefined;
53
57
  void router.replace(sameOrigin, options);
54
58
  return;
55
59
  }