@rangojs/router 0.31.0 → 0.33.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 (192) hide show
  1. package/README.md +3 -2
  2. package/dist/bin/browser-7KIJ5HSV.js +15 -0
  3. package/dist/bin/chunk-3B7CKBRO.js +463 -0
  4. package/dist/bin/chunk-524VACCG.js +71 -0
  5. package/dist/bin/chunk-LD7UBHT4.js +196 -0
  6. package/dist/bin/chunk-TO6CRWLN.js +1429 -0
  7. package/dist/bin/chunk-TTXC2U3G.js +2477 -0
  8. package/dist/bin/client-urls-projection-DQLWPDAT.js +28 -0
  9. package/dist/bin/forward-user-plugins-URBZKJP4.js +62 -0
  10. package/dist/bin/rango.js +42 -10143
  11. package/dist/bin/runtime-EPUDEHO4.js +596 -0
  12. package/dist/bin/runtime-discovery-CZ2QHT4Q.js +352 -0
  13. package/dist/bin/state-ACD2MGKJ.js +16 -0
  14. package/dist/bin/temp-rsc-server-QRR6HD6T.js +3190 -0
  15. package/dist/types/bin/rango.d.ts +1 -0
  16. package/dist/types/browser/action-fence.d.ts +20 -0
  17. package/dist/types/browser/cookie-name.d.ts +51 -10
  18. package/dist/types/browser/history-state.d.ts +13 -2
  19. package/dist/types/browser/invalidate-client-cache.d.ts +8 -6
  20. package/dist/types/browser/navigation-bridge.d.ts +36 -1
  21. package/dist/types/browser/navigation-client.d.ts +19 -0
  22. package/dist/types/browser/navigation-store-handle.d.ts +37 -4
  23. package/dist/types/browser/navigation-store.d.ts +2 -2
  24. package/dist/types/browser/navigation-transaction.d.ts +0 -1
  25. package/dist/types/browser/partial-update.d.ts +13 -0
  26. package/dist/types/browser/prefetch/cache.d.ts +96 -8
  27. package/dist/types/browser/prefetch/fetch.d.ts +26 -2
  28. package/dist/types/browser/prefetch/queue.d.ts +3 -2
  29. package/dist/types/browser/rango-state.d.ts +34 -5
  30. package/dist/types/browser/react/Link.d.ts +19 -13
  31. package/dist/types/browser/react/location-state-shared.d.ts +2 -1
  32. package/dist/types/browser/types.d.ts +25 -0
  33. package/dist/types/build/generate-route-types.d.ts +1 -1
  34. package/dist/types/build/route-types/include-resolution.d.ts +9 -1
  35. package/dist/types/build/route-types/router-processing.d.ts +3 -1
  36. package/dist/types/cache/cache-key-utils.d.ts +12 -8
  37. package/dist/types/cache/cache-scope.d.ts +4 -2
  38. package/dist/types/cache/cf/cf-cache-types.d.ts +9 -5
  39. package/dist/types/cache/document-cache-policy.d.ts +9 -1
  40. package/dist/types/cache/memory-segment-store.d.ts +3 -2
  41. package/dist/types/cache/segment-codec.d.ts +2 -2
  42. package/dist/types/cache/shell-snapshot.d.ts +1 -1
  43. package/dist/types/cache/types.d.ts +12 -7
  44. package/dist/types/cache/vercel/vercel-cache-store.d.ts +2 -1
  45. package/dist/types/client-urls/types.d.ts +7 -5
  46. package/dist/types/prefetch-markers.d.ts +9 -0
  47. package/dist/types/prefetch-source-scope.d.ts +66 -0
  48. package/dist/types/route-definition/helpers-types.d.ts +8 -5
  49. package/dist/types/route-definition/redirect.d.ts +5 -0
  50. package/dist/types/router/intercept-resolution.d.ts +25 -11
  51. package/dist/types/router/match-context.d.ts +1 -0
  52. package/dist/types/router/match-handlers.d.ts +1 -1
  53. package/dist/types/router/middleware-types.d.ts +6 -0
  54. package/dist/types/router/prefetch-cache-ttl.d.ts +11 -3
  55. package/dist/types/router/router-context.d.ts +1 -1
  56. package/dist/types/router/router-options.d.ts +6 -3
  57. package/dist/types/router/segment-resolution/loader-cache.d.ts +8 -0
  58. package/dist/types/router/segment-resolution/prefetch-deferral.d.ts +23 -7
  59. package/dist/types/router/segment-resolution/revalidation.d.ts +19 -0
  60. package/dist/types/router/source-reads.d.ts +53 -0
  61. package/dist/types/router/types.d.ts +1 -1
  62. package/dist/types/rsc/helpers.d.ts +8 -3
  63. package/dist/types/rsc/prefetch-render-error.d.ts +7 -2
  64. package/dist/types/rsc/request-stages.d.ts +4 -0
  65. package/dist/types/rsc/shell-serve.d.ts +6 -1
  66. package/dist/types/server/context.d.ts +23 -6
  67. package/dist/types/server/cookie-store.d.ts +15 -1
  68. package/dist/types/server/document-location-state.d.ts +59 -0
  69. package/dist/types/server/handle-store.d.ts +2 -0
  70. package/dist/types/server/request-context.d.ts +65 -12
  71. package/dist/types/testing/e2e/server.d.ts +16 -1
  72. package/dist/types/testing/render-route.d.ts +83 -2
  73. package/dist/types/testing/serve-shell-request.d.ts +15 -3
  74. package/dist/types/types/handler-context.d.ts +21 -4
  75. package/dist/types/types/loader-types.d.ts +7 -4
  76. package/dist/types/types/segments.d.ts +18 -1
  77. package/dist/types/urls/path-helper-types.d.ts +7 -5
  78. package/dist/types/vite/temp-rsc-server.d.ts +2 -2
  79. package/dist/types/vite/utils/forward-user-plugins.d.ts +32 -2
  80. package/dist/vite/index.js +451 -191
  81. package/package.json +2 -2
  82. package/skills/app-structure/SKILL.md +74 -0
  83. package/skills/app-structure/e-commerce.md +10 -9
  84. package/skills/app-structure/large-app.md +15 -2
  85. package/skills/caching/SKILL.md +10 -5
  86. package/skills/client-urls/SKILL.md +18 -11
  87. package/skills/comparison/references/framework-comparison.md +16 -10
  88. package/skills/document-cache/SKILL.md +13 -0
  89. package/skills/hooks/navigation.md +8 -8
  90. package/skills/hooks/state.md +132 -12
  91. package/skills/intercept/SKILL.md +33 -11
  92. package/skills/layout/SKILL.md +11 -0
  93. package/skills/links/SKILL.md +101 -4
  94. package/skills/loader/SKILL.md +49 -22
  95. package/skills/migrate-nextjs/SKILL.md +8 -2
  96. package/skills/migrate-react-router/data-and-actions.md +5 -0
  97. package/skills/ppr/SKILL.md +38 -5
  98. package/skills/prerender/SKILL.md +4 -1
  99. package/skills/route/SKILL.md +6 -6
  100. package/skills/router-setup/SKILL.md +2 -1
  101. package/skills/server-actions/SKILL.md +49 -8
  102. package/skills/testing/client-components.md +73 -32
  103. package/skills/testing/serve-shell-request.md +45 -20
  104. package/skills/testing/server-actions.md +8 -0
  105. package/skills/ux-architecture/SKILL.md +67 -15
  106. package/src/bin/rango.ts +39 -13
  107. package/src/browser/action-fence.ts +72 -0
  108. package/src/browser/cookie-name.ts +82 -14
  109. package/src/browser/history-state.ts +24 -2
  110. package/src/browser/invalidate-client-cache.ts +9 -7
  111. package/src/browser/navigation-bridge.ts +164 -32
  112. package/src/browser/navigation-client.ts +95 -20
  113. package/src/browser/navigation-store-handle.ts +67 -5
  114. package/src/browser/navigation-store.ts +86 -17
  115. package/src/browser/navigation-transaction.ts +0 -6
  116. package/src/browser/partial-update.ts +31 -12
  117. package/src/browser/prefetch/cache.ts +188 -11
  118. package/src/browser/prefetch/fetch.ts +211 -51
  119. package/src/browser/prefetch/queue.ts +3 -2
  120. package/src/browser/rango-state.ts +50 -8
  121. package/src/browser/react/Link.tsx +24 -16
  122. package/src/browser/react/location-state-shared.ts +8 -1
  123. package/src/browser/react/use-navigation.ts +1 -1
  124. package/src/browser/rsc-router.tsx +13 -3
  125. package/src/browser/server-action-bridge.ts +3 -3
  126. package/src/browser/types.ts +25 -0
  127. package/src/build/generate-route-types.ts +1 -0
  128. package/src/build/route-types/include-resolution.ts +40 -3
  129. package/src/build/route-types/router-processing.ts +170 -25
  130. package/src/build/route-types/scan-filter.ts +1 -0
  131. package/src/build/runtime-discovery.ts +46 -8
  132. package/src/cache/cache-key-utils.ts +12 -8
  133. package/src/cache/cache-runtime.ts +5 -1
  134. package/src/cache/cache-scope.ts +28 -7
  135. package/src/cache/cf/cf-cache-types.ts +9 -5
  136. package/src/cache/document-cache-policy.ts +37 -1
  137. package/src/cache/document-cache.ts +12 -0
  138. package/src/cache/memory-segment-store.ts +3 -2
  139. package/src/cache/segment-codec.ts +2 -2
  140. package/src/cache/shell-snapshot.ts +1 -1
  141. package/src/cache/types.ts +12 -7
  142. package/src/cache/vercel/vercel-cache-store.ts +2 -1
  143. package/src/client-urls/types.ts +7 -5
  144. package/src/prefetch-markers.ts +10 -0
  145. package/src/prefetch-source-scope.ts +137 -0
  146. package/src/route-definition/helpers-types.ts +8 -5
  147. package/src/route-definition/redirect.ts +5 -0
  148. package/src/router/intercept-resolution.ts +94 -50
  149. package/src/router/loader-resolution.ts +8 -6
  150. package/src/router/match-api.ts +33 -29
  151. package/src/router/match-context.ts +5 -0
  152. package/src/router/match-handlers.ts +2 -1
  153. package/src/router/match-middleware/cache-lookup.ts +39 -14
  154. package/src/router/match-result.ts +45 -85
  155. package/src/router/middleware-types.ts +6 -0
  156. package/src/router/navigation-snapshot.ts +18 -3
  157. package/src/router/prefetch-cache-ttl.ts +15 -3
  158. package/src/router/revalidation.ts +63 -13
  159. package/src/router/router-context.ts +2 -1
  160. package/src/router/router-options.ts +6 -3
  161. package/src/router/segment-resolution/loader-cache.ts +21 -3
  162. package/src/router/segment-resolution/prefetch-deferral.ts +38 -31
  163. package/src/router/segment-resolution/revalidation.ts +40 -12
  164. package/src/router/source-reads.ts +122 -0
  165. package/src/router/types.ts +2 -1
  166. package/src/router.ts +11 -12
  167. package/src/rsc/helpers.ts +20 -6
  168. package/src/rsc/prefetch-render-error.ts +7 -2
  169. package/src/rsc/progressive-enhancement.ts +8 -2
  170. package/src/rsc/request-stages.ts +87 -1
  171. package/src/rsc/rsc-rendering.ts +78 -32
  172. package/src/rsc/server-action.ts +2 -1
  173. package/src/rsc/shell-capture.ts +4 -5
  174. package/src/rsc/shell-serve.ts +12 -1
  175. package/src/segment-system.tsx +1 -1
  176. package/src/server/context.ts +37 -6
  177. package/src/server/cookie-store.ts +22 -1
  178. package/src/server/document-location-state.ts +334 -0
  179. package/src/server/handle-store.ts +2 -0
  180. package/src/server/request-context.ts +137 -33
  181. package/src/testing/dispatch.ts +6 -0
  182. package/src/testing/e2e/fixture.ts +4 -12
  183. package/src/testing/e2e/server.ts +91 -4
  184. package/src/testing/render-route.tsx +309 -10
  185. package/src/testing/run-revalidate.ts +11 -1
  186. package/src/testing/serve-shell-request.ts +22 -3
  187. package/src/types/handler-context.ts +21 -4
  188. package/src/types/loader-types.ts +7 -4
  189. package/src/types/segments.ts +18 -1
  190. package/src/urls/path-helper-types.ts +7 -5
  191. package/src/vite/temp-rsc-server.ts +2 -2
  192. package/src/vite/utils/forward-user-plugins.ts +13 -5
@@ -6,11 +6,25 @@
6
6
  * requests; the server responds with `Vary: X-Rango-State`, so the browser HTTP
7
7
  * cache keys responses by (URL, X-Rango-State value).
8
8
  *
9
- * Value format: `{buildVersion}:{invalidationTimestamp}`
9
+ * Value format: `{buildVersion}:{invalidationTimestamp}[:{write}]`
10
10
  * - Build version: the router's document version. It changes when a deploy
11
11
  * changes the router's server code or the client assets, busting all cached
12
12
  * prefetches at boot; a deploy that changes neither keeps them.
13
- * - Timestamp rotates on invalidation (server action, invalidateClientCache).
13
+ * - Timestamp rotates on invalidation (invalidateClientCache, either seat).
14
+ * - Write: bumped by the response of a server action without a directive (its
15
+ * `Set-Cookie`, server side, so a no-JS action bumps it too); left out while
16
+ * 0. It changes the `X-Rango-State` every tab and every reload sends, so
17
+ * responses the browser HTTP cache holds from before the write miss.
18
+ *
19
+ * The client's in-memory comparisons read only `{buildVersion}:{timestamp}`
20
+ * (`stateEpoch`): the prefetch cache keys, the action fence's rotation check,
21
+ * the external-rotation observer below and the cross-tab adopt. A write-only
22
+ * change is soft: prefetches and history entries are marked stale and nothing
23
+ * is flushed. The tab that ran the action marks its own when it lands
24
+ * (navigation-store.ts markStaleSoftAndBroadcast) and tells the tabs that
25
+ * share a segment with it. Any other tab marks its own when a read sees the
26
+ * write part change (the write observer below), so a tab that ignores the
27
+ * broadcast or has crossTabSync off does not serve a pre-write prefetch fresh.
14
28
  *
15
29
  * Storage is a session cookie named by the server-resolved name passed to
16
30
  * initRangoState (`{prefix}_{routerId}`, default prefix `rango-state`). The
@@ -27,10 +41,20 @@
27
41
  */
28
42
  /**
29
43
  * Register the observer invoked when a read detects an EXTERNAL rotation (a
30
- * sibling tab, a server `Set-Cookie`, or a cookie clear). Self-rotations
44
+ * sibling tab, a server `Set-Cookie`, or a cookie clear) of the
45
+ * `{version}:{timestamp}` part. A change to the write part alone (a server
46
+ * action's write) goes to the write observer instead. Self-rotations
31
47
  * (invalidateRangoState) update the mirror synchronously and never fire it.
32
48
  */
33
49
  export declare function setRangoStateObserver(observer: ((value: string) => void) | null): void;
50
+ /**
51
+ * Register the observer invoked when a read sees the write part change while
52
+ * the `{version}:{timestamp}` part stays: a server action's response, in this
53
+ * tab or another, bumped it in the shared jar since the last read. Every read
54
+ * updates the mirror, and a prefetch reads the state before it starts, so a
55
+ * prefetch sent with the new value is never one this observer sees as old.
56
+ */
57
+ export declare function setRangoStateWriteObserver(observer: (() => void) | null): void;
34
58
  /** Return the resolved cookie name used to namespace cross-tab messages. */
35
59
  export declare function getRangoStateCookieName(): string;
36
60
  /**
@@ -49,10 +73,15 @@ export declare function initRangoState(version: string, stateCookieName?: string
49
73
  export declare function getRangoState(): string;
50
74
  /**
51
75
  * Invalidate the Rango state (self-rotation). Called when the client clears its
52
- * prefetch caches (e.g. via the server-action bridge). Rotates the timestamp,
76
+ * prefetch caches (e.g. invalidateClientCache()). Rotates the timestamp,
53
77
  * keeps the version, writes the cookie, and updates the mirror synchronously so
54
78
  * the external-rotation observer is NOT triggered by our own write.
55
79
  */
56
80
  export declare function invalidateRangoState(): void;
57
- /** Adopt the state carried by a same-origin cache-invalidation broadcast. */
81
+ /**
82
+ * Adopt the state carried by a same-origin cache-invalidation broadcast when
83
+ * its timestamp is newer (a rotation). The write part is not compared: a soft
84
+ * broadcast's newer write is already in the shared jar, and a value that
85
+ * differs only there is no rotation to adopt.
86
+ */
58
87
  export declare function adoptRangoState(value: string): boolean;
@@ -66,23 +66,29 @@ export interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorEle
66
66
  * keyed on Rango state + target URL. This is correct for routes whose
67
67
  * response shape doesn't depend on where the user navigates from.
68
68
  *
69
- * Set `":source"` when this Link's response would legitimately differ
70
- * based on the source page — typically when the target route (or one
71
- * of its layouts) uses a custom `revalidate()` handler that reads
72
- * `currentUrl` / `currentParams`, and the wildcard entry would
73
- * therefore serve the wrong diff to a navigation from a different
74
- * source.
69
+ * Set `":source"` to scope every prefetch of this Link to the page that
70
+ * sent it, when its response would differ by source page in a way the
71
+ * server cannot see.
75
72
  *
76
- * A route an intercept targets is scoped to the source automatically
77
- * (server-side `x-rsc-prefetch-scope: source`), whether or not the
78
- * intercept applies from this page: the modal and the full page are
79
- * both source-specific. Every other route stays shared, so `":source"`
80
- * is only needed for custom revalidation logic.
73
+ * The server scopes a prefetch to its source automatically
74
+ * (`x-rsc-prefetch-scope: source`) when an intercept targets the route or
75
+ * the source page, whether or not it applies (the modal and the full page
76
+ * are both source-specific), when the answer defers a `prefetch: false`
77
+ * unit the source does not hold, when a `revalidate()` of the target read
78
+ * `currentUrl` and kept the segment, and when a kept answer sends no
79
+ * segment. A `revalidate()` that read `currentParams` or `fromRouteName`
80
+ * and kept the segment, or a prerendered target that keeps segments on
81
+ * equal params (`storedEntrySends`), shares the answer between the pages
82
+ * with equal values of what was read (`x-rsc-prefetch-scope: params`,
83
+ * #1331). Every other response stays shared. Use the key
84
+ * for what the server does not track: `context` in a `revalidate()`, or
85
+ * a `Referer` read.
86
+ *
87
+ * A source-scoped entry is keyed by the source page's full URL, query
88
+ * and hash included, so every page prefetches the link again.
81
89
  *
82
90
  * @example
83
91
  * ```tsx
84
- * // Route uses a `revalidate()` that branches on currentUrl — opt in
85
- * // so prefetches don't bleed across source pages.
86
92
  * <Link to="/dashboard" prefetch="hover" prefetchKey=":source" />
87
93
  * ```
88
94
  */
@@ -269,7 +269,8 @@ export declare function isLocationStateDefinition(value: unknown): value is Loca
269
269
  */
270
270
  export declare function isLocationStateEntry(value: unknown): value is LocationStateEntry;
271
271
  /**
272
- * Resolve state entries into a flat object for history.state
272
+ * Resolve state entries into a flat object for history.state. One slot per
273
+ * definition: a later entry of the same definition replaces an earlier one.
273
274
  */
274
275
  export declare function resolveLocationStateEntries(entries: LocationStateEntry[]): Record<string, unknown>;
275
276
  export {};
@@ -600,7 +600,15 @@ export interface NavigationStore {
600
600
  */
601
601
  getPageHistoryKeys(historyKey: string): string[];
602
602
  markHistoryCacheStale(): void;
603
+ /**
604
+ * Mark history entries and prefetches stale without a broadcast: a cookie
605
+ * read saw another write (rango-state.ts setRangoStateWriteObserver).
606
+ */
607
+ markCachesStaleAfterWrite(): void;
608
+ /** A server action without a directive: mark stale, soft broadcast. */
603
609
  markCacheAsStaleAndBroadcast(): void;
610
+ /** The client seat of invalidateClientCache(): flush, rotate, broadcast. */
611
+ invalidateCacheAndBroadcast(): void;
604
612
  clearHistoryCache(): void;
605
613
  setCrossTabRefreshCallback(callback: () => void): void;
606
614
  getInterceptSourceUrl(): string | null;
@@ -637,6 +645,16 @@ export interface FetchPartialOptions {
637
645
  * to a prefetch cache or the HTTP cache, and it cancels no prefetch.
638
646
  */
639
647
  fill?: boolean;
648
+ /**
649
+ * Whether a prefetch a server action marked stale (prefetch/cache.ts
650
+ * isPrefetchStale) may answer this navigation. Only a click sets it: it
651
+ * revalidates its page once it settled (navigation-bridge.ts navigate,
652
+ * partial-update.ts onStalePrefetch). For any other caller (Back/Forward on
653
+ * a history-cache miss, leaving an intercept) a stale stored entry is a
654
+ * miss, and a stale in-flight one is dropped and fetched again, so no path
655
+ * that never revalidates commits pre-write data.
656
+ */
657
+ acceptStalePrefetch?: boolean;
640
658
  }
641
659
  /**
642
660
  * Result of a partial fetch including stream completion tracking
@@ -654,6 +672,13 @@ export interface FetchPartialResult {
654
672
  * fallbacks stream as usual.
655
673
  */
656
674
  fullyPrefetched?: boolean;
675
+ /**
676
+ * True when the payload is a prefetch a server action marked stale after it
677
+ * was fetched (prefetch/cache.ts isPrefetchStale). The click commits it as
678
+ * any prefetch; the navigation then revalidates its page once it settled,
679
+ * as Back onto a stale history entry does (navigation-bridge.ts navigate).
680
+ */
681
+ stalePrefetch?: boolean;
657
682
  }
658
683
  /**
659
684
  * Navigation client for fetching RSC payloads
@@ -4,5 +4,5 @@ export { generatePerModuleTypesSource, generateRouteTypesSource, } from "./route
4
4
  export { DEFAULT_EXCLUDE_PATTERNS, type ScanFilter, createScanFilter, findTsFiles, isInSkippedScanDir, } from "./route-types/scan-filter.js";
5
5
  export { writePerModuleRouteTypes, writePerModuleRouteTypesForFile, } from "./route-types/per-module-writer.js";
6
6
  export { type UnresolvableReason, type UnresolvableInclude, extractIncludesWithDiagnostics, } from "./route-types/include-resolution.js";
7
- export { extractUrlsFromRouter, type UrlsExtractionResult, buildCombinedRouteMapForRouterFile, detectUnresolvableIncludes, detectUnresolvableIncludesForUrlsFile, findNestedRouterConflict, formatNestedRouterConflictError, findRouterFiles, findHostRouterFiles, writeCombinedRouteTypes, genFileTsPath, resolveSearchSchemas, } from "./route-types/router-processing.js";
7
+ export { extractUrlsFromRouter, type UrlsExtractionResult, buildCombinedRouteMapForRouterFile, detectUnresolvableIncludes, detectUnresolvableIncludesForUrlsFile, findNestedRouterConflict, formatNestedRouterConflictError, findRouterFiles, findHostRouterFiles, isRoutableSourceFile, writeCombinedRouteTypes, genFileTsPath, resolveSearchSchemas, } from "./route-types/router-processing.js";
8
8
  export { findUrlsVariableNames } from "./route-types/per-module-writer.js";
@@ -1,5 +1,5 @@
1
1
  import ts from "typescript";
2
- export type UnresolvableReason = "factory-call" | "dynamic-expression" | "unresolvable-import" | "file-not-found" | "unresolvable-basename";
2
+ export type UnresolvableReason = "factory-call" | "dynamic-expression" | "unresolvable-import" | "file-not-found" | "unresolvable-basename" | "unresolvable-routes";
3
3
  export interface UnresolvableInclude {
4
4
  pathPrefix: string;
5
5
  namePrefix: string | null;
@@ -25,6 +25,14 @@ export interface ScanMemo {
25
25
  blockSourceFiles: Map<string, ts.SourceFile>;
26
26
  }
27
27
  export declare function createScanMemo(): ScanMemo;
28
+ export declare function isUrlsDefinitionCall(node: ts.CallExpression, aliases?: ReadonlySet<string>): boolean;
29
+ /**
30
+ * Local names that `import { urls as u, clientUrls as cu } from
31
+ * "@rangojs/router"` (or `/client`) binds to urls()/clientUrls(), so a renamed
32
+ * import is read like the plain name. Other modules' exports are not guessed
33
+ * at: a `urls` from elsewhere is read only under its own name.
34
+ */
35
+ export declare function collectUrlsImportAliases(sourceFile: ts.SourceFile): ReadonlySet<string>;
28
36
  /**
29
37
  * Extract include() calls with diagnostics for unresolvable ones.
30
38
  * Returns both resolved includes (identifier second args) and unresolvable
@@ -1,5 +1,6 @@
1
1
  import { type ScanFilter } from "./scan-filter.js";
2
2
  import { type UnresolvableInclude } from "./include-resolution.js";
3
+ export declare function isRoutableSourceFile(name: string): boolean;
3
4
  export declare function findNestedRouterConflict(routerFiles: string[]): {
4
5
  ancestor: string;
5
6
  nested: string;
@@ -22,11 +23,12 @@ export type UrlsExtractionResult = {
22
23
  };
23
24
  /**
24
25
  * Extract the url patterns from a router file using AST.
25
- * Detects four patterns:
26
+ * Detects these patterns, in either mount (`.routes(x)` or `urls: x`):
26
27
  * 1. createRouter(...).routes(variableName)
27
28
  * 2. createRouter({ urls: variableName, ... })
28
29
  * 3. createRouter(...).routes(({ path, ... }) => [...])
29
30
  * 4. createRouter({ urls: ({ path, ... }) => [...], ... })
31
+ * 5. createRouter(...).routes(urls(({ path, ... }) => [...])), and clientUrls()
30
32
  * Returns either a variable name or an inline code block.
31
33
  */
32
34
  export declare function extractUrlsFromRouter(code: string): UrlsExtractionResult | null;
@@ -97,9 +97,10 @@ export declare const KEY_PART_PREFIX = "key:";
97
97
  * (CacheScope.resolveKeyFrom, CacheScope.resolvePartition): the `key()`
98
98
  * results of the scope's cache() chain, outermost first, then its default
99
99
  * parts: the keyGenerator results of enclosing scopes on another store
100
- * (#974) and, for a route record key, the scope's own part: without a
100
+ * (#974) and, for a route record key, the scope's own parts: without a
101
101
  * `key()` of its own, its default key (or its store's keyGenerator result,
102
- * #970); with one, the default key's request kind (`doc`, `partial` or
102
+ * #970, then the request kind when that result does not start with it,
103
+ * #1298); with one, the default key's request kind (`doc`, `partial` or
103
104
  * `intercept:<intercept>`, routeRecordKind in cache-scope.ts).
104
105
  *
105
106
  * Key scheme invariant (#975). A `key()` result is often request input,
@@ -119,7 +120,9 @@ export declare const KEY_PART_PREFIX = "key:";
119
120
  * - one `key()` part is the key; two or more parts are joined by `|`,
120
121
  * `key()` parts first;
121
122
  * - a lone default part stays raw: with no `key()` on the chain the record
122
- * key is the default key or the store's keyGenerator result, unchanged.
123
+ * key is the default key or the store's keyGenerator result, unchanged
124
+ * when that result starts with its request kind, else that result and
125
+ * the kind composed (`items|doc`).
123
126
  *
124
127
  * Why nothing collides: the router's default keys (`doc:`, `partial:`,
125
128
  * `intercept:`, `response:<type>:`) never start with `key:` and hold a `:`.
@@ -128,11 +131,12 @@ export declare const KEY_PART_PREFIX = "key:";
128
131
  * Among composed keys, splitting on `|` recovers each part, a part's kind
129
132
  * (a `key:` prefix, or no `:` at all) and its value. A request kind part is
130
133
  * a default key's prefix without the URL after it, so it never equals a
131
- * default key part; a keyGenerator part can equal it (`doc`) only when the
132
- * keyGenerator returns a constant, which already gives every URL one record.
133
- * Pinned by the collision probe in
134
- * src/cache/__tests__/cache-scope-chain.test.ts. A keyGenerator result is
135
- * the store's own namespace, as before.
134
+ * default key part. A raw keyGenerator record key starts with its kind and
135
+ * holds a `:`, so it never equals a composed one, and two kinds' prefixes
136
+ * differ; a result without its kind is composed with it, never prefixed,
137
+ * since `doc:` + `x` would be the raw result `doc:x`. Pinned by the
138
+ * collision probes in src/cache/__tests__/cache-scope-chain.test.ts. A
139
+ * keyGenerator result is the store's own namespace, as before.
136
140
  *
137
141
  * Shared with the testing helper `shellCacheKey`, so a test builds the
138
142
  * production partition.
@@ -200,8 +200,10 @@ export declare class CacheScope {
200
200
  * replaces the URL part, and its store's keyGenerator does not run. It
201
201
  * appends `kind` instead (routeRecordKind): the `key()` decides which
202
202
  * requests of one kind share a record, never that a document, a
203
- * navigation and an intercept navigation do. With no partition part it is
204
- * resolveDefaultKey alone. With a `key()` on the chain but no request
203
+ * navigation and an intercept navigation do. A keyGenerator result that
204
+ * does not start with its kind gets `kind` after it too (ownParts). With
205
+ * no partition part it is resolveDefaultKey alone, or that result and
206
+ * `kind` composed. With a `key()` on the chain but no request
205
207
  * context it rejects (keyWithoutContextError). `partitionedPrefix`
206
208
  * prefixes a key built from partition parts only (the response family's
207
209
  * `response:`, which passes no `kind`: a response route answers every
@@ -369,19 +369,23 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
369
369
  /**
370
370
  * Custom key generator applied to all cache operations.
371
371
  * Receives the full RequestContext (including env) and the default-generated key.
372
- * Return value becomes the final cache key (unless route overrides with `key` option).
372
+ * The return value is the cache key (unless route overrides with `key`
373
+ * option), except that a route record keeps its request kind: a result that
374
+ * does not start with `defaultKey`'s prefix (`doc:`, `partial:`,
375
+ * `intercept:<intercept>:`) gets the kind appended (see
376
+ * SegmentCacheStore.keyGenerator).
373
377
  *
374
378
  * Reserved prefixes: tag-invalidation markers live in the SAME KV namespace as
375
379
  * data, keyed `__tag__/<tag>` (and `__tagmarker__/<tag>` for the L1 cache). A
376
380
  * returned key must NOT begin with `__tag__/` or `__tagmarker__/`, or it can
377
381
  * collide with a tag marker and corrupt invalidation. The documented
378
- * prepend-style generators below are safe.
382
+ * generators below start with `defaultKey` and are safe.
379
383
  *
380
384
  * @example Using headers for user segmentation
381
385
  * ```typescript
382
386
  * keyGenerator: (ctx, defaultKey) => {
383
387
  * const segment = ctx.request.headers.get('x-user-segment') || 'default';
384
- * return `${segment}:${defaultKey}`;
388
+ * return `${defaultKey}|${segment}`;
385
389
  * }
386
390
  * ```
387
391
  *
@@ -389,7 +393,7 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
389
393
  * ```typescript
390
394
  * keyGenerator: (ctx, defaultKey) => {
391
395
  * const region = ctx.env.REGION || 'us';
392
- * return `${region}:${defaultKey}`;
396
+ * return `${defaultKey}|${region}`;
393
397
  * }
394
398
  * ```
395
399
  *
@@ -397,7 +401,7 @@ export interface CFCacheStoreOptions<TEnv = unknown> {
397
401
  * ```typescript
398
402
  * keyGenerator: (ctx, defaultKey) => {
399
403
  * const locale = cookies().get('locale')?.value || 'en';
400
- * return `${locale}:${defaultKey}`;
404
+ * return `${defaultKey}|${locale}`;
401
405
  * }
402
406
  * ```
403
407
  */
@@ -12,7 +12,15 @@ export interface CacheDirectives {
12
12
  /**
13
13
  * Check if response should be cached based on Cache-Control headers
14
14
  */
15
- export declare function shouldCacheResponse(response: Response, requestCtx?: Pick<RequestContext, "_payloadVisitorTheme" | "_payloadDeferred" | "_payloadVisitorLocation">): CacheDirectives | null;
15
+ export declare function shouldCacheResponse(response: Response, requestCtx?: Pick<RequestContext, "_payloadVisitorTheme" | "_payloadDeferred" | "_payloadSourceScoped" | "_payloadVisitorLocation" | "_documentLocationState">): CacheDirectives | null;
16
+ /**
17
+ * Whether a response carries one client's state, which a stored copy would
18
+ * replay to that client's later requests: a per-client signal header (a state
19
+ * cookie rotation, `x-rango-keep-cache`) or location state. The stored
20
+ * prefetch Cache-Control (rsc/request-stages.ts) is not sent on
21
+ * one.
22
+ */
23
+ export declare function carriesClientState(response: Response, requestCtx: Pick<RequestContext, "_locationState" | "_documentLocationState">): boolean;
16
24
  /**
17
25
  * Whether this cache will store the response the current render produces, as
18
26
  * far as the render can tell: the middleware runs it (a MISS or a stale
@@ -46,13 +46,14 @@ export interface MemorySegmentCacheStoreOptions<TEnv = unknown> {
46
46
  defaults?: CacheDefaults;
47
47
  /**
48
48
  * Custom key generator applied to all cache operations.
49
- * Receives the full RequestContext and the default-generated key.
49
+ * Receives the full RequestContext and the default-generated key. A route
50
+ * record keeps its request kind (see SegmentCacheStore.keyGenerator).
50
51
  *
51
52
  * @example
52
53
  * ```typescript
53
54
  * keyGenerator: (ctx, defaultKey) => {
54
55
  * const locale = cookies().get('locale')?.value || 'en';
55
- * return `${locale}:${defaultKey}`;
56
+ * return `${defaultKey}|${locale}`;
56
57
  * }
57
58
  * ```
58
59
  */
@@ -88,8 +88,8 @@ export declare function serializeSegments(segments: ResolvedSegment[], onError?:
88
88
  * cached with the route record).
89
89
  *
90
90
  * The loading "null" sentinel decodes here (not on the consumer): loading:null
91
- * must survive as null, and shipping the raw sentinel would change the
92
- * deduplicateLoaderSegments loading-presence check.
91
+ * must survive as null, and shipping the raw sentinel would hand renderSegments
92
+ * a truthy `loading`, the fallback tree shape (docs/tree-structure.md).
93
93
  */
94
94
  export declare function fragmentSegments(data: SerializedSegmentData[]): Promise<ResolvedSegment[]>;
95
95
  /**
@@ -201,7 +201,7 @@ export interface ShellLoaderSeedEntry {
201
201
  * Deserializations run in parallel; a record that fails to decode is skipped
202
202
  * and reported to `onUndecodable`: its loader runs instead, which a document
203
203
  * HIT has already committed a prelude against, so serveShellHit retires the
204
- * entry (dc-t89). Returns undefined when the snapshot carries no loader
204
+ * entry. Returns undefined when the snapshot carries no loader
205
205
  * records, without touching the Flight codec (kept lazy for cold paths and
206
206
  * non-RSC configs).
207
207
  */
@@ -83,16 +83,21 @@ export interface SegmentCacheStore<TEnv = unknown> {
83
83
  /**
84
84
  * Custom key generator applied to all cache operations using this store.
85
85
  * Receives the full RequestContext and the default-generated key.
86
- * Return value becomes the final cache key (unless route overrides with `key` option).
86
+ * The return value is the cache key (unless route overrides with `key`
87
+ * option), with a route record's request kind appended when it does not
88
+ * start with that kind (below).
87
89
  *
88
90
  * Resolution priority:
89
91
  * 1. Route-level `key` function (replaces the URL part; the request kind stays)
90
92
  * 2. Store-level `keyGenerator` (modifies default key)
91
93
  * 3. Default key generation (prefix:pathname:params)
92
94
  *
93
- * Keep `defaultKey` in the result: its prefix names the request kind (a
94
- * document, a navigation or an intercept navigation), whose records hold
95
- * different segments. A result without it gives every kind one record.
95
+ * A route record keeps its request kind (a document, a navigation or an
96
+ * intercept navigation), whose records hold different segments: the kind
97
+ * is `defaultKey`'s prefix (`doc:`, `partial:`, `intercept:<intercept>:`),
98
+ * and a result that does not start with it gets the kind appended as a
99
+ * key part of its own. Build on `defaultKey` (`${defaultKey}|${segment}`)
100
+ * and the result is the record key byte for byte.
96
101
  *
97
102
  * Return `defaultKey` unchanged to leave a request unpartitioned. An empty
98
103
  * string is not a key: where the result partitions a nested cache() on
@@ -104,7 +109,7 @@ export interface SegmentCacheStore<TEnv = unknown> {
104
109
  * ```typescript
105
110
  * keyGenerator: (ctx, defaultKey) => {
106
111
  * const segment = ctx.request.headers.get('x-user-segment') || 'default';
107
- * return `${segment}:${defaultKey}`;
112
+ * return `${defaultKey}|${segment}`;
108
113
  * }
109
114
  * ```
110
115
  *
@@ -112,7 +117,7 @@ export interface SegmentCacheStore<TEnv = unknown> {
112
117
  * ```typescript
113
118
  * keyGenerator: (ctx, defaultKey) => {
114
119
  * const region = ctx.env.REGION || 'us';
115
- * return `${region}:${defaultKey}`;
120
+ * return `${defaultKey}|${region}`;
116
121
  * }
117
122
  * ```
118
123
  *
@@ -120,7 +125,7 @@ export interface SegmentCacheStore<TEnv = unknown> {
120
125
  * ```typescript
121
126
  * keyGenerator: (ctx, defaultKey) => {
122
127
  * const locale = cookies().get('locale')?.value || 'en';
123
- * return `${locale}:${defaultKey}`;
128
+ * return `${defaultKey}|${locale}`;
124
129
  * }
125
130
  * ```
126
131
  */
@@ -125,7 +125,8 @@ export interface VercelCacheStoreOptions<TEnv = unknown> {
125
125
  defaults?: CacheDefaults;
126
126
  /**
127
127
  * Custom key generator applied to all cache operations (region/locale/user
128
- * segmentation). Receives the request context and the default key.
128
+ * segmentation). Receives the request context and the default key. A route
129
+ * record keeps its request kind (see SegmentCacheStore.keyGenerator).
129
130
  */
130
131
  keyGenerator?: (ctx: RequestContext<TEnv>, defaultKey: string) => string | Promise<string>;
131
132
  /**
@@ -48,8 +48,9 @@ export interface ClientUrlLoaderRecord {
48
48
  */
49
49
  readonly ssr?: false;
50
50
  /**
51
- * loader(Def, { prefetch: false }): a prefetch of the route, or of an
52
- * intercept's target from its source, defers this loader and the click
51
+ * loader(Def, { prefetch: false }): a viewport or render prefetch of the
52
+ * route, or of an intercept's target from its source, defers this loader
53
+ * (a hover prefetch runs it) and the click
53
54
  * that adopts it runs it in its fill request (see {@link LoaderOptions}).
54
55
  * Projected onto the materialized stub.
55
56
  */
@@ -118,12 +119,13 @@ export interface ClientUrlHelpers {
118
119
  * holes).
119
120
  *
120
121
  * Pass `{ prefetch: false }` to keep this loader out of the route's
121
- * prefetches (see {@link LoaderOptions}): an in-group click still shows
122
+ * viewport and render prefetches; a hover prefetch runs it (see
123
+ * {@link LoaderOptions}). An in-group click still shows
122
124
  * the destination at once, commits the adopted prefetch with the other
123
125
  * loaders' data, and runs only the flagged loaders in its one fill
124
126
  * request. A `layout()` loader with the flag is flagged on every route
125
- * below it. Inside intercept() it defers the loader in a prefetch of the
126
- * intercept from its source, as in urls().
127
+ * below it. Inside intercept() it defers the loader in a viewport or
128
+ * render prefetch of the intercept from its source, as in urls().
127
129
  */
128
130
  readonly loader: <TData>(definition: LoaderDefinition<TData>, optionsOrUse?: Pick<LoaderOptions, "ssr" | "prefetch"> | ClientUrlUse, use?: ClientUrlUse) => ClientUrlItem;
129
131
  readonly loading: (component: ReactNode) => ClientUrlItem;
@@ -7,6 +7,15 @@
7
7
  */
8
8
  /** Request header of a `<Link>` prefetch (browser/prefetch/fetch.ts). */
9
9
  export declare const PREFETCH_HEADER = "X-Rango-Prefetch";
10
+ /**
11
+ * PREFETCH_HEADER's value on an intent prefetch: a hover or an explicit
12
+ * `prefetch()` call (browser/prefetch/fetch.ts prefetchDirect). The server
13
+ * defers nothing in it: `prefetch: false` loaders and the work behind a
14
+ * flagged `loading()` run, as in the navigation it starts early. A
15
+ * speculative prefetch (viewport, render) sends `1`, and any value other than
16
+ * this one reads as speculative.
17
+ */
18
+ export declare const PREFETCH_INTENT = "intent";
10
19
  /**
11
20
  * The last row of a prefetch whose render reported an error
12
21
  * (rsc/prefetch-render-error.ts): a Flight hint with a code React does not
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The source group of a prefetch answer a revalidate() rule kept from the
3
+ * page being left (#1331). Shared by the server, which reports
4
+ * the group in `x-rsc-prefetch-scope: params; <label>` (rsc/rsc-rendering.ts),
5
+ * and the browser, which stores the answer under that label and rebuilds it
6
+ * from the page on screen to look it up (browser/prefetch/cache.ts).
7
+ * Dependency free, like prefetch-markers.ts: the browser must not import the
8
+ * server modules that record the reads (router/source-reads.ts).
9
+ *
10
+ * Why a label is enough: a rule's decision depends on the source page only
11
+ * through what it read. Two pages with equal values of everything the kept
12
+ * rules read run every rule the same way, so one answer serves both.
13
+ */
14
+ /** What the revalidate() rules that kept a segment read of the source page. */
15
+ export interface SourceReads {
16
+ /**
17
+ * `currentParams` was read. Its key set is then part of the group: a rule
18
+ * can test a key's presence (`in`, `Object.keys`) without reading a value.
19
+ */
20
+ keys: boolean;
21
+ /**
22
+ * Each `currentParams` value read; a spread or JSON.stringify reads all.
23
+ * Non-empty only with `keys`: a value is read through `currentParams`.
24
+ */
25
+ params: Set<string>;
26
+ /** `fromRouteName` was read. */
27
+ route: boolean;
28
+ /** `currentUrl` was read: the answer is the exact page's. */
29
+ url: boolean;
30
+ }
31
+ /** The page a label describes: the source on the server, the screen in the browser. */
32
+ export interface SourcePage {
33
+ params: Readonly<Record<string, string | undefined>>;
34
+ /** The public route name (route-name.ts publicRouteName). */
35
+ routeName: string | undefined;
36
+ }
37
+ /** What a label reads, without the values: one per kind of rule set. */
38
+ export interface SourceShape {
39
+ keys: boolean;
40
+ route: boolean;
41
+ /** Sorted. */
42
+ names: readonly string[];
43
+ }
44
+ /** `x-rsc-prefetch-scope` value prefix of a params-grouped answer. */
45
+ export declare const PARAMS_SCOPE_PREFIX = "params; ";
46
+ export declare function createSourceReads(): SourceReads;
47
+ export declare function hasSourceReads(reads: SourceReads): boolean;
48
+ /** Union `from` into `into`. */
49
+ export declare function mergeSourceReads(into: SourceReads, from: SourceReads): void;
50
+ export declare function shapeOfReads(reads: SourceReads): SourceShape;
51
+ /**
52
+ * The label of `page` under `shape`: `&`-joined, `$k=<keys>` first, then
53
+ * `$r[=<route>]`, then `<name>[=<value>]` per read param, sorted. A missing
54
+ * value has no `=`. Names and values are URI-encoded, so a `$` entry never
55
+ * collides with a param (encodeURIComponent encodes `$`).
56
+ */
57
+ export declare function formatSourceLabel(shape: SourceShape, page: SourcePage): string;
58
+ /**
59
+ * The label of an `x-rsc-prefetch-scope` value, or undefined when the value
60
+ * is not a params scope.
61
+ */
62
+ export declare function paramsScopeLabel(header: string | null | undefined): string | undefined;
63
+ /** The shape of a label formatSourceLabel wrote. */
64
+ export declare function parseSourceShape(label: string): SourceShape;
65
+ /** A stable id of a shape, for indexing. */
66
+ export declare function sourceShapeId(shape: SourceShape): string;
@@ -231,9 +231,10 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
231
231
  * loader(RecommendationsLoader), // still streams behind loading()
232
232
  * ```
233
233
  *
234
- * Pass `{ prefetch: false }` to keep the loader out of prefetches: a
235
- * prefetch skips it and the click that adopts the prefetch fetches it in
236
- * one follow-up request (see {@link LoaderOptions}). Only on a segment the
234
+ * Pass `{ prefetch: false }` to keep the loader out of speculative
235
+ * prefetches (viewport, render): such a prefetch skips it and the click
236
+ * that adopts it fetches it in one follow-up request; a hover prefetch runs
237
+ * it (see {@link LoaderOptions}). Only on a segment the
237
238
  * client does not have yet: a loader the client already holds runs in the
238
239
  * prefetch when it revalidates.
239
240
  *
@@ -255,14 +256,16 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
255
256
  * // Skip loading on SSR, only show on client navigation
256
257
  * loading(<Skeleton />, { ssr: false })
257
258
  *
258
- * // Keep everything behind this fallback out of prefetches
259
+ * // Keep everything behind this fallback out of viewport and render
260
+ * // prefetches (a hover prefetch runs it)
259
261
  * loading(<Skeleton />, { prefetch: false })
260
262
  * ```
261
263
  * @param component - The loading UI to show during navigation
262
264
  * @param options - Configuration options
263
265
  * @param options.ssr - If false, skip showing loading on document requests (SSR)
264
266
  * @param options.prefetch - If false, the flag applies when this entry's
265
- * segment is new to the client. A prefetch then skips the whole unit: the
267
+ * segment is new to the client. A viewport or render prefetch then skips
268
+ * the whole unit (a hover prefetch runs it): the
266
269
  * handler, its loaders, its orphan layouts and parallel slots and, for a
267
270
  * layout, every deeper entry; the click that adopts the prefetch shows
268
271
  * this fallback and fetches them in one follow-up request, which reads
@@ -9,6 +9,11 @@ import type { LocationStateEntry } from "../browser/react/location-state-shared.
9
9
  * Supports an optional state parameter to carry location state through the
10
10
  * redirect. On the target page, state can be read via useLocationState()
11
11
  * (use { flash: true } in createLocationState for read-once flash messages).
12
+ * On a client navigation or action the state travels in the redirect's
13
+ * payload. On a document request (a page load, a no-JS form post) it travels
14
+ * in a one-shot cookie the target's document request reads and clears, as
15
+ * JSON: give it plain JSON data there. `{ external: true }` carries none.
16
+ * Two entries of the same definition keep only the last.
12
17
  *
13
18
  * @param url - The URL to redirect to
14
19
  * @param statusOrOptions - HTTP status code (default: 302) or options object