@rangojs/router 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (245) hide show
  1. package/README.md +254 -89
  2. package/dist/testing/vitest.js +252 -0
  3. package/dist/types/browser/navigation-transaction.d.ts +6 -0
  4. package/dist/types/browser/partial-update.d.ts +11 -0
  5. package/dist/types/browser/react/ScrollRestoration.d.ts +6 -6
  6. package/dist/types/browser/rsc-router.d.ts +1 -1
  7. package/dist/types/browser/scroll-restoration.d.ts +7 -0
  8. package/dist/types/cache/cache-exec-scope.d.ts +18 -1
  9. package/dist/types/cache/cache-scope.d.ts +34 -5
  10. package/dist/types/cache/handle-capture.d.ts +41 -3
  11. package/dist/types/cache/handle-snapshot.d.ts +53 -13
  12. package/dist/types/cache/segment-codec.d.ts +10 -3
  13. package/dist/types/cache/taint.d.ts +1 -1
  14. package/dist/types/cache/types.d.ts +15 -3
  15. package/dist/types/client.d.ts +4 -4
  16. package/dist/types/client.rsc.d.ts +4 -5
  17. package/dist/types/components/DefaultDocument.d.ts +5 -2
  18. package/dist/types/context-var.d.ts +6 -5
  19. package/dist/types/escape-script.d.ts +1 -1
  20. package/dist/types/handles/MetaTags.d.ts +5 -4
  21. package/dist/types/handles/Scripts.d.ts +8 -7
  22. package/dist/types/handles/meta.d.ts +2 -2
  23. package/dist/types/handles/script.d.ts +17 -16
  24. package/dist/types/href-client.d.ts +1 -1
  25. package/dist/types/html.d.ts +12 -0
  26. package/dist/types/index.d.ts +1 -1
  27. package/dist/types/index.rsc.d.ts +3 -2
  28. package/dist/types/loader-store.d.ts +7 -4
  29. package/dist/types/reverse.d.ts +36 -4
  30. package/dist/types/route-definition/dsl-helpers.d.ts +3 -3
  31. package/dist/types/route-definition/helpers-types.d.ts +33 -9
  32. package/dist/types/route-definition/resolve-handler-use.d.ts +23 -1
  33. package/dist/types/route-types.d.ts +1 -1
  34. package/dist/types/router/debug-manifest.d.ts +3 -2
  35. package/dist/types/router/error-handling.d.ts +20 -0
  36. package/dist/types/router/handler-context.d.ts +6 -0
  37. package/dist/types/router/match-middleware/background-revalidation.d.ts +46 -5
  38. package/dist/types/router/middleware-types.d.ts +3 -2
  39. package/dist/types/router/router-context.d.ts +3 -1
  40. package/dist/types/router/router-interfaces.d.ts +24 -2
  41. package/dist/types/router/router-options.d.ts +8 -5
  42. package/dist/types/router/segment-resolution/fresh.d.ts +10 -0
  43. package/dist/types/router/segment-resolution/loader-cache.d.ts +37 -1
  44. package/dist/types/router/segment-resolution/loader-mask.d.ts +9 -19
  45. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +3 -3
  46. package/dist/types/router/segment-resolution/mask-nested.d.ts +7 -7
  47. package/dist/types/router/segment-resolution/revalidation.d.ts +6 -2
  48. package/dist/types/router/segment-wrappers.d.ts +1 -1
  49. package/dist/types/rsc/response-route-handler.d.ts +1 -1
  50. package/dist/types/rsc/rsc-rendering.d.ts +1 -2
  51. package/dist/types/rsc/shell-capture-constants.d.ts +8 -0
  52. package/dist/types/rsc/shell-capture.d.ts +5 -4
  53. package/dist/types/rsc/ssr-setup.d.ts +6 -6
  54. package/dist/types/rsc/types.d.ts +10 -0
  55. package/dist/types/server/context.d.ts +33 -3
  56. package/dist/types/server/handle-store.d.ts +26 -2
  57. package/dist/types/server/request-context.d.ts +24 -21
  58. package/dist/types/ssr/index.d.ts +15 -0
  59. package/dist/types/static-handler.d.ts +4 -1
  60. package/dist/types/testing/generated-routes.d.ts +2 -2
  61. package/dist/types/testing/internal/context.d.ts +1 -1
  62. package/dist/types/testing/render-route.d.ts +32 -6
  63. package/dist/types/testing/vitest-stubs/plugin-rsc.d.ts +28 -5
  64. package/dist/types/testing/vitest.d.ts +35 -1
  65. package/dist/types/theme/ThemeScript.d.ts +3 -3
  66. package/dist/types/types/cache-types.d.ts +14 -10
  67. package/dist/types/types/handler-context.d.ts +14 -0
  68. package/dist/types/types/loader-types.d.ts +8 -6
  69. package/dist/types/urls/response-types.d.ts +3 -12
  70. package/dist/types/use-loader.d.ts +12 -3
  71. package/dist/types/vite/plugins/use-cache-transform.d.ts +33 -2
  72. package/dist/types/vite/rango.d.ts +1 -1
  73. package/dist/vite/index.js +1381 -1346
  74. package/package.json +2 -2
  75. package/skills/api-client/SKILL.md +22 -10
  76. package/skills/breadcrumbs/SKILL.md +65 -22
  77. package/skills/bundle-analysis/SKILL.md +22 -22
  78. package/skills/cache-guide/SKILL.md +207 -97
  79. package/skills/caching/SKILL.md +295 -93
  80. package/skills/catalog.json +16 -16
  81. package/skills/client-urls/SKILL.md +53 -31
  82. package/skills/cloudflare/SKILL.md +72 -20
  83. package/skills/cloudflare/references/d1-and-local-dev.md +15 -2
  84. package/skills/cloudflare/references/streaming-and-deploy.md +10 -3
  85. package/skills/cloudflare/references/webhooks-and-crypto.md +9 -2
  86. package/skills/comparison/SKILL.md +5 -0
  87. package/skills/comparison/references/framework-comparison.md +39 -33
  88. package/skills/composability/SKILL.md +38 -29
  89. package/skills/css/SKILL.md +10 -13
  90. package/skills/debug-manifest/SKILL.md +74 -31
  91. package/skills/defer-hydration/SKILL.md +2 -1
  92. package/skills/deployment-caching/SKILL.md +6 -1
  93. package/skills/document-cache/SKILL.md +92 -28
  94. package/skills/fonts/SKILL.md +16 -8
  95. package/skills/handler-use/SKILL.md +53 -17
  96. package/skills/hooks/SKILL.md +28 -20
  97. package/skills/hooks/data.md +23 -3
  98. package/skills/hooks/handle-and-actions.md +31 -16
  99. package/skills/hooks/navigation.md +39 -10
  100. package/skills/hooks/outlets.md +13 -6
  101. package/skills/hooks/state.md +37 -15
  102. package/skills/hooks/urls.md +7 -5
  103. package/skills/host-router/SKILL.md +64 -12
  104. package/skills/i18n/SKILL.md +60 -34
  105. package/skills/intercept/SKILL.md +161 -26
  106. package/skills/layout/SKILL.md +106 -50
  107. package/skills/links/SKILL.md +51 -19
  108. package/skills/loader/SKILL.md +242 -106
  109. package/skills/middleware/SKILL.md +119 -23
  110. package/skills/migrate-nextjs/SKILL.md +100 -45
  111. package/skills/migrate-nextjs/backend-host-swap.md +5 -2
  112. package/skills/migrate-react-router/SKILL.md +49 -38
  113. package/skills/migrate-react-router/component-migration.md +25 -6
  114. package/skills/migrate-react-router/data-and-actions.md +18 -5
  115. package/skills/migrate-react-router/route-mapping.md +12 -6
  116. package/skills/mime-routes/SKILL.md +12 -7
  117. package/skills/observability/SKILL.md +28 -14
  118. package/skills/parallel/SKILL.md +75 -39
  119. package/skills/ppr/SKILL.md +345 -150
  120. package/skills/prerender/SKILL.md +189 -100
  121. package/skills/rango/SKILL.md +65 -34
  122. package/skills/react-compiler/SKILL.md +6 -5
  123. package/skills/response-routes/SKILL.md +133 -25
  124. package/skills/route/SKILL.md +165 -60
  125. package/skills/router-setup/SKILL.md +418 -240
  126. package/skills/scripts/SKILL.md +31 -20
  127. package/skills/server-actions/SKILL.md +220 -59
  128. package/skills/shell-manifest/SKILL.md +12 -7
  129. package/skills/streams-and-websockets/SKILL.md +32 -14
  130. package/skills/tailwind/SKILL.md +37 -33
  131. package/skills/testing/SKILL.md +68 -32
  132. package/skills/testing/cache-prerender.md +55 -29
  133. package/skills/testing/client-components.md +59 -6
  134. package/skills/testing/e2e-parity.md +12 -2
  135. package/skills/testing/flight.md +19 -11
  136. package/skills/testing/handles.md +8 -5
  137. package/skills/testing/loader.md +8 -5
  138. package/skills/testing/middleware.md +8 -3
  139. package/skills/testing/predicates.md +120 -0
  140. package/skills/testing/render-handler.md +70 -22
  141. package/skills/testing/response-routes.md +11 -4
  142. package/skills/testing/reverse-and-types.md +20 -14
  143. package/skills/testing/server-actions.md +12 -3
  144. package/skills/testing/server-tree.md +9 -0
  145. package/skills/testing/setup.md +9 -5
  146. package/skills/theme/SKILL.md +106 -29
  147. package/skills/typesafety/SKILL.md +20 -9
  148. package/skills/typesafety/env-and-bindings.md +126 -47
  149. package/skills/typesafety/generated-files-and-cli.md +26 -14
  150. package/skills/typesafety/params-and-search.md +44 -19
  151. package/skills/typesafety/route-types.md +50 -14
  152. package/skills/use-cache/SKILL.md +200 -82
  153. package/skills/vercel/SKILL.md +21 -11
  154. package/skills/view-transitions/SKILL.md +41 -20
  155. package/src/__augment-tests__/augmented.check.ts +26 -1
  156. package/src/browser/index.ts +2 -2
  157. package/src/browser/navigation-bridge.ts +3 -4
  158. package/src/browser/navigation-transaction.ts +22 -2
  159. package/src/browser/partial-update.ts +35 -25
  160. package/src/browser/react/ScrollRestoration.tsx +6 -6
  161. package/src/browser/rsc-router.tsx +1 -1
  162. package/src/browser/scroll-restoration.ts +53 -6
  163. package/src/cache/cache-exec-scope.ts +37 -4
  164. package/src/cache/cache-key-utils.ts +1 -6
  165. package/src/cache/cache-runtime.ts +230 -61
  166. package/src/cache/cache-scope.ts +76 -12
  167. package/src/cache/document-cache.ts +15 -0
  168. package/src/cache/handle-capture.ts +90 -10
  169. package/src/cache/handle-snapshot.ts +132 -28
  170. package/src/cache/segment-codec.ts +26 -7
  171. package/src/cache/tag-invalidation.ts +4 -3
  172. package/src/cache/taint.ts +1 -1
  173. package/src/cache/types.ts +16 -3
  174. package/src/client.rsc.tsx +10 -9
  175. package/src/client.tsx +5 -4
  176. package/src/components/DefaultDocument.tsx +7 -2
  177. package/src/context-var.ts +6 -5
  178. package/src/escape-script.ts +1 -1
  179. package/src/handles/MetaTags.tsx +13 -11
  180. package/src/handles/Scripts.tsx +11 -10
  181. package/src/handles/meta.ts +2 -2
  182. package/src/handles/script.ts +17 -16
  183. package/src/href-client.ts +1 -1
  184. package/src/html.ts +12 -0
  185. package/src/index.rsc.ts +5 -0
  186. package/src/index.ts +1 -0
  187. package/src/loader-store.ts +7 -4
  188. package/src/prerender/build-shell-capture.ts +8 -11
  189. package/src/reverse.ts +44 -4
  190. package/src/route-definition/dsl-helpers.ts +117 -30
  191. package/src/route-definition/helpers-types.ts +33 -9
  192. package/src/route-definition/resolve-handler-use.ts +121 -6
  193. package/src/route-types.ts +0 -3
  194. package/src/router/debug-manifest.ts +10 -2
  195. package/src/router/error-handling.ts +51 -15
  196. package/src/router/handler-context.ts +34 -25
  197. package/src/router/intercept-resolution.ts +125 -39
  198. package/src/router/loader-resolution.ts +56 -11
  199. package/src/router/match-api.ts +12 -17
  200. package/src/router/match-middleware/background-revalidation.ts +161 -96
  201. package/src/router/match-middleware/cache-lookup.ts +93 -10
  202. package/src/router/match-middleware/cache-store.ts +17 -90
  203. package/src/router/middleware-types.ts +3 -5
  204. package/src/router/middleware.ts +52 -9
  205. package/src/router/prerender-match.ts +53 -19
  206. package/src/router/route-snapshot.ts +11 -1
  207. package/src/router/router-context.ts +2 -0
  208. package/src/router/router-interfaces.ts +25 -2
  209. package/src/router/router-options.ts +8 -5
  210. package/src/router/segment-resolution/fresh.ts +44 -12
  211. package/src/router/segment-resolution/loader-cache.ts +205 -45
  212. package/src/router/segment-resolution/loader-mask.ts +12 -22
  213. package/src/router/segment-resolution/loader-snapshot.ts +6 -6
  214. package/src/router/segment-resolution/mask-nested.ts +7 -7
  215. package/src/router/segment-resolution/revalidation.ts +34 -6
  216. package/src/router/segment-wrappers.ts +4 -0
  217. package/src/rsc/render-pipeline.ts +3 -0
  218. package/src/rsc/response-route-handler.ts +11 -3
  219. package/src/rsc/rsc-rendering.ts +8 -45
  220. package/src/rsc/shell-capture-constants.ts +13 -0
  221. package/src/rsc/shell-capture.ts +111 -90
  222. package/src/rsc/ssr-setup.ts +14 -8
  223. package/src/rsc/types.ts +11 -0
  224. package/src/server/context.ts +88 -11
  225. package/src/server/cookie-store.ts +5 -4
  226. package/src/server/handle-store.ts +232 -7
  227. package/src/server/request-context.ts +35 -30
  228. package/src/ssr/index.tsx +40 -7
  229. package/src/static-handler.ts +4 -1
  230. package/src/testing/flight-runtime.d.ts +21 -0
  231. package/src/testing/generated-routes.ts +2 -2
  232. package/src/testing/internal/context.ts +1 -1
  233. package/src/testing/internal/flight-client-globals.ts +7 -5
  234. package/src/testing/render-route.tsx +85 -24
  235. package/src/testing/run-loader.ts +5 -1
  236. package/src/testing/vitest-stubs/plugin-rsc.ts +123 -20
  237. package/src/testing/vitest.ts +62 -1
  238. package/src/theme/ThemeScript.tsx +3 -3
  239. package/src/types/cache-types.ts +14 -10
  240. package/src/types/handler-context.ts +14 -0
  241. package/src/types/loader-types.ts +8 -6
  242. package/src/urls/response-types.ts +3 -23
  243. package/src/use-loader.tsx +12 -3
  244. package/src/vite/plugins/use-cache-transform.ts +118 -74
  245. package/src/vite/rango.ts +7 -3
@@ -71,8 +71,23 @@ export interface HandleStore {
71
71
  * Push handle data for a specific handle and segment.
72
72
  * Multiple pushes to the same handle/segment accumulate in an array.
73
73
  * Each push triggers an emission on the stream.
74
+ *
75
+ * `loaderPush` overrides the push-time DSL-loader tag
76
+ * (isInsideLoaderScope()) that getDataForSegment(id, true) excludes. The
77
+ * PPR shell capture passes false for the bake-lane pushes its record keeps
78
+ * (shell-capture.ts deriveShellCaptureContext), with `owner` set to the
79
+ * pushing loader's id so the record can restore them through pushReplayed
80
+ * (getRecordOwners).
74
81
  */
75
- push(handleName: string, segmentId: string, data: unknown): void;
82
+ push(handleName: string, segmentId: string, data: unknown, loaderPush?: boolean, owner?: string): void;
83
+ /**
84
+ * push() a loader-cache HIT's replayed value, recorded from `loaderId`'s
85
+ * body (loader-cache.ts replayLoaderHandles). If that loader then runs live
86
+ * in this request, its first push removes every value replayed for it and
87
+ * takes the first one's position (later live pushes follow it), so the
88
+ * handle output stays live without a duplicate.
89
+ */
90
+ pushReplayed(handleName: string, segmentId: string, data: unknown, loaderId: string): void;
76
91
  /**
77
92
  * Get all collected handle data after all handlers have settled.
78
93
  * Waits for `settled` (handler lane), then returns the data at that point.
@@ -100,8 +115,17 @@ export interface HandleStore {
100
115
  /**
101
116
  * Get handle data for a specific segment (for caching).
102
117
  * Returns data in format: { handleName: [values...] }
118
+ *
119
+ * `excludeLoaderPushes` drops values tagged as DSL-loader pushes at push
120
+ * time (see push); see captureHandles.
121
+ */
122
+ getDataForSegment(segmentId: string, excludeLoaderPushes?: boolean): Record<string, unknown[]>;
123
+ /**
124
+ * The push `owner` of each value in getDataForSegment(segmentId, true),
125
+ * index-aligned per handle (null: no owner). Handles without an owned
126
+ * value are omitted; undefined when none has one.
103
127
  */
104
- getDataForSegment(segmentId: string): Record<string, unknown[]>;
128
+ getRecordOwners(segmentId: string): Record<string, (string | null)[]> | undefined;
105
129
  /**
106
130
  * Replay cached handle data back into the store (for cache hits).
107
131
  * Used to restore handle data when serving cached segments.
@@ -129,8 +129,13 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
129
129
  };
130
130
  /** HTTP method (GET, POST, PUT, PATCH, DELETE, etc.) */
131
131
  method: string;
132
- /** @internal Handle store for tracking handle data across segments */
133
- _handleStore: HandleStore;
132
+ /**
133
+ * @internal Handle store for tracking handle data across segments.
134
+ * Readonly: a background render that needs its own store derives a context
135
+ * (rerenderAndCacheRoute, shell-capture.ts) instead of swapping this field,
136
+ * which the foreground still reads while it produces the page.
137
+ */
138
+ readonly _handleStore: HandleStore;
134
139
  /**
135
140
  * @internal transition({ when }) predicates for segments matched this request,
136
141
  * keyed by segment id. Collected during resolution (the function is stripped
@@ -178,13 +183,12 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
178
183
  /**
179
184
  * @internal Bake-lane loader containers collected DURING a shell capture:
180
185
  * segment-key -> the loader's (pre-wrap) result promise. Populated by
181
- * resolveLoaderData for loaders on entries with no renderable loading() (the
182
- * bake lane — they execute at capture instead of being masked; see
183
- * docs/design/loader-container-bake.md). Drained by captureAndStoreShell
184
- * after the shell quiesces: settled containers are promise-elided,
185
- * Flight-serialized, and pinned into the snapshot's loader family; a
186
- * REJECTED container refuses the capture (error UI must never bake into the
187
- * shared shell). Own property of the capture's derived context only.
186
+ * resolveLoaderData for bake-lane loaders (lane rule: see its JSDoc in
187
+ * loader-cache.ts). Drained by captureAndStoreShell after the shell
188
+ * quiesces: settled containers are promise-elided, Flight-serialized, and
189
+ * pinned into the snapshot's loader family; a REJECTED container refuses
190
+ * the capture (error UI must never bake into the shared shell). Own
191
+ * property of the capture's derived context only.
188
192
  */
189
193
  _shellCaptureLoaderRecords?: Map<string, Promise<unknown>>;
190
194
  /**
@@ -297,17 +301,6 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
297
301
  pendingPushes: number;
298
302
  handlerInvokedLoader: boolean;
299
303
  };
300
- /**
301
- * @internal Handle values pushed from a DSL loader scope DURING a shell
302
- * capture (identity set; populated by the capture push wrapper in
303
- * shell-capture.ts). cacheRoute threads it into captureHandles so those
304
- * values stay out of cache-write handle records — loaders re-run fresh on
305
- * every HIT, so replaying their captured (masked) values would duplicate
306
- * the fresh push and stall the Flight handle encode. Own property of the
307
- * capture's derived context only; render-time handle consumers are
308
- * unaffected (the exclusion applies only at the captureHandles call site).
309
- */
310
- _shellCaptureLoaderHandleValues?: WeakSet<object>;
311
304
  /**
312
305
  * @internal Set (to the offending fn name) by the cookies()/headers()
313
306
  * capture guard when it throws DURING a capture render. Load-bearing for the
@@ -535,6 +528,16 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
535
528
  _renderBarrierGuardClosed?: boolean;
536
529
  /** @internal Per-request error dedup set for onError reporting */
537
530
  _reportedErrors: WeakSet<object>;
531
+ /**
532
+ * @internal Errors this response's Flight or Fizz render reported through
533
+ * onError: a component that threw does not fail the stream, which completes
534
+ * with an error row or an errored Suspense boundary. Whole-response captures
535
+ * (document-cache.ts, captureAndStoreShell) store nothing while it is
536
+ * non-empty (issue #915). An array so a render under a derived context (the
537
+ * shell HIT tail) pushes into the request's own list through the prototype;
538
+ * deriveShellCaptureContext gives the capture its own.
539
+ */
540
+ _renderErrors?: unknown[];
538
541
  /**
539
542
  * @internal Report a non-fatal background error through the router's
540
543
  * onError callback. Wired by the RSC handler / router during request
@@ -598,7 +601,7 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
598
601
  * This is the type exported to library consumers. Internal code should
599
602
  * use the full RequestContext interface directly.
600
603
  */
601
- export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhen" | "_pprTransitionDecisions" | "_pprReplayPostMatchReason" | "_cacheStore" | "_searchParamsFilter" | "_shellCaptureRun" | "_shellImplicitCache" | "_shellLoaderSeed" | "_shellCaptureLoaderRecords" | "_shellCaptureHandleLiveness" | "_shellCaptureLoaderHandleValues" | "_shellFragmentPayload" | "_shellCaptureGuardTripped" | "_shellCaptureGuardTrippedLoaderId" | "_explicitTaggedStores" | "_requestTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_locationState" | "_routeName" | "_prevRouteKey" | "_gateCurrentUrl" | "_gateCurrentParams" | "_gateActionId" | "_gateActionUrl" | "_gateActionResult" | "_gateFormData" | "_inActionRevalidation" | "_reportedErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_tracing" | "_basename" | "_routerId" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
604
+ export type PublicRequestContext<TEnv = DefaultEnv, TParams = Record<string, string>> = Omit<RequestContext<TEnv, TParams>, "cookie" | "cookies" | "setCookie" | "deleteCookie" | "_handleStore" | "_transitionWhen" | "_pprTransitionDecisions" | "_pprReplayPostMatchReason" | "_cacheStore" | "_searchParamsFilter" | "_shellCaptureRun" | "_shellImplicitCache" | "_shellLoaderSeed" | "_shellCaptureLoaderRecords" | "_shellCaptureHandleLiveness" | "_shellFragmentPayload" | "_shellCaptureGuardTripped" | "_shellCaptureGuardTrippedLoaderId" | "_explicitTaggedStores" | "_requestTags" | "_cacheProfiles" | "_onResponseCallbacks" | "_pendingBackgroundTasks" | "_themeConfig" | "_locationState" | "_routeName" | "_prevRouteKey" | "_gateCurrentUrl" | "_gateCurrentParams" | "_gateActionId" | "_gateActionUrl" | "_gateActionResult" | "_gateFormData" | "_inActionRevalidation" | "_reportedErrors" | "_renderErrors" | "_renderBarrier" | "_resolveRenderBarrier" | "_renderBarrierSegmentOrder" | "_treeHasStreaming" | "_renderBarrierWaiters" | "_handlerLoaderDeps" | "_awaitBeforeFlushLoaderIds" | "_renderBarrierHandleSnapshot" | "_renderBarrierGuardClosed" | "_reportBackgroundError" | "_debugPerformance" | "_metricsStore" | "_renderForeground" | "_activeRoutine" | "_renderDiagnosticsEnabled" | "_handlerStart" | "_tracing" | "_basename" | "_routerId" | "_setStatus" | "_rotateStateCookie" | "_setKeepCacheDirective" | "_variables" | "_classifiedRoute" | "_requestMode" | "_cacheSignal" | "_dynamic" | "res">;
602
605
  /**
603
606
  * Marker for a waitUntil-scheduled fn whose task promise must NOT enter
604
607
  * _pendingBackgroundTasks. Used by the PPR shell capture for its own task:
@@ -20,6 +20,7 @@ interface RenderToReadableStreamOptions {
20
20
  nonce?: string;
21
21
  formState?: unknown;
22
22
  progressiveChunkSize?: number;
23
+ onError?: (error: unknown) => void;
23
24
  }
24
25
  /**
25
26
  * ReadableStream with the allReady promise added by react-dom/server.edge.
@@ -99,6 +100,12 @@ export interface SSRRenderOptions {
99
100
  * prerender pass (host-agnostic bare captures).
100
101
  */
101
102
  origin?: string;
103
+ /**
104
+ * Called for each error Fizz reports through its onError: a component that
105
+ * threw inside a Suspense boundary, which leaves the boundary errored in an
106
+ * otherwise completed document. React's default console.error is kept.
107
+ */
108
+ onError?: (error: unknown) => void;
102
109
  }
103
110
  /**
104
111
  * SSR dependencies from external packages
@@ -225,6 +232,12 @@ interface ShellCaptureOptions {
225
232
  * capture, resume, and browser hydration.
226
233
  */
227
234
  origin?: string;
235
+ /**
236
+ * Called for each component error the prerender reports (an errored
237
+ * boundary in the prelude), never for the capture's own abort. The caller
238
+ * refuses to store the shell (issue #915).
239
+ */
240
+ onError?: (error: unknown) => void;
228
241
  }
229
242
  /**
230
243
  * Result of a successful shell capture. `prelude` is the raw prelude bytes;
@@ -253,6 +266,8 @@ interface ShellResumeOptions {
253
266
  search?: string;
254
267
  /** The HIT request's origin — same host as the capture's (key-scoped). */
255
268
  origin?: string;
269
+ /** Called for each component error the resumed holes report. */
270
+ onError?: (error: unknown) => void;
256
271
  }
257
272
  /**
258
273
  * Create an SSR handler that converts RSC streams to HTML.
@@ -21,11 +21,14 @@
21
21
  * ```ts
22
22
  * export const DocsNav = Static((ctx) => <Nav docs={readDocsSync()} />);
23
23
  * export const DocShell = Static((ctx) => <Shell />);
24
+ * // Prerender/Static must be exported consts; an inline call inside path()
25
+ * // gets no build-injected $$id and throws.
26
+ * export const DocPage = Prerender(getParams, DocPageHandler);
24
27
  *
25
28
  * urls(({ path, layout }) => [
26
29
  * layout(DocsNav, () => [
27
30
  * path("/getting-started", DocShell, { name: "doc.gs" }),
28
- * path("/:slug", Prerender(getParams, DocPageHandler)),
31
+ * path("/:slug", DocPage),
29
32
  * ]),
30
33
  * ]);
31
34
  * ```
@@ -56,10 +56,10 @@ export declare function diffGeneratedRoutes(router: RouterWithRouteMap, generate
56
56
  *
57
57
  * @example
58
58
  * ```ts
59
- * import generated from "./router.named-routes.gen";
59
+ * import { NamedRoutes } from "./router.named-routes.gen";
60
60
  * import { router } from "./router";
61
61
  *
62
- * await assertGeneratedRoutesMatch(router, generated);
62
+ * await assertGeneratedRoutesMatch(router, NamedRoutes);
63
63
  * ```
64
64
  */
65
65
  export declare function assertGeneratedRoutesMatch(router: RouterWithRouteMap, generatedMap?: Record<string, unknown>): Promise<void>;
@@ -205,7 +205,7 @@ export declare function buildRunSnapshot<TEnv>(ctx: RequestContext<TEnv>, thrown
205
205
  *
206
206
  * @example
207
207
  * ```ts
208
- * const { result, cookies, response, thrown } = await runInRequestContext(
208
+ * const { cookies, headers, response, thrown } = await runInRequestContext(
209
209
  * () => loginAction(input), // sets a session cookie, then `throw redirect("/app")`
210
210
  * {
211
211
  * env,
@@ -23,10 +23,13 @@
23
23
  * SEEDED directly into client context (see the `loaders` / `locationState` /
24
24
  * `handles` / `outletPending` options) — nothing is executed on the server.
25
25
  * This exercises the context read path, not the run path.
26
- * - navigate() commits synchronously, so it does NOT drive the navigation
27
- * lifecycle: useNavigation().state, useLinkStatus().pending, and
28
- * useAction().state stay "idle". Assert pending/loading/submitting transition
29
- * states with renderServerTree / e2e instead (navigate() warns once if used).
26
+ * - navigate() does NOT drive the navigation lifecycle: useNavigation().state,
27
+ * useLinkStatus().pending, and useAction().state stay "idle". Assert
28
+ * pending/loading/submitting transition states with renderServerTree / e2e
29
+ * instead (navigate() warns once if used). The one pending state it does
30
+ * model is the held-navigation useLoader().isLoading: a `transition` spec
31
+ * plus a pending Promise seeded via navigate(url, { loaders }) commits
32
+ * through production's commitInTransition (browser/partial-update.ts).
30
33
  * What it DOES cover: client hooks that read NavigationProvider /
31
34
  * OutletContext — useParams, useReverse, useHref, useMount, useNavigation,
32
35
  * useRouter, usePathname, useSearchParams, Outlet/useOutlet nesting and seeded
@@ -42,7 +45,7 @@ import { type ComponentType } from "react";
42
45
  import type { RenderResult } from "@testing-library/react";
43
46
  import type { NavigationStore } from "../browser/types.js";
44
47
  import type { EventController } from "../browser/event-controller.js";
45
- import type { LoaderDefinition } from "../types.js";
48
+ import type { LoaderDefinition, TransitionConfig } from "../types.js";
46
49
  import type { LocationStateDefinition } from "../browser/react/location-state-shared.js";
47
50
  import type { Handle } from "../handle.js";
48
51
  import type { ThemeConfig } from "../theme/types.js";
@@ -78,6 +81,14 @@ export interface RenderRouteSpec {
78
81
  loaderIds?: string[];
79
82
  /** Optional route name (informational; not used for matching). */
80
83
  name?: string;
84
+ /**
85
+ * The `transition()` config this node declares (`{}` for a bare
86
+ * `transition()`), attached to its segment as the DSL does. Puts the chain in
87
+ * a transition scope: param-agnostic keys (segment-system.tsx) and a held
88
+ * navigate() commit, so a pending loader seed passed to
89
+ * `router.navigate(url, { loaders })` pins `useLoader().isLoading`.
90
+ */
91
+ transition?: TransitionConfig;
81
92
  }
82
93
  /**
83
94
  * Options for renderRoute.
@@ -247,8 +258,23 @@ export interface TestRouterHandle {
247
258
  * updates params + location, and re-renders the segment tree. This is a
248
259
  * client-only navigation: no server fetch occurs, so only the components in
249
260
  * `routes` can be reached.
261
+ *
262
+ * `options.loaders` seeds loader data for THIS navigation, merged over the
263
+ * render-time seeds (the data a real navigation's response would carry). A
264
+ * pending Promise suspends the read like a streaming Flight chunk. When a
265
+ * spec in the chain has `transition`, the commit goes through production's
266
+ * commitInTransition: the reader stays on screen with
267
+ * `useLoader().isLoading === true` until the promise settles. Settle it
268
+ * inside RTL's `act()` to flush the commit. Without `transition` the commit
269
+ * is urgent and the read suspends to the nearest Suspense boundary.
270
+ *
271
+ * @example
272
+ * await router.navigate("/products/2", { loaders: [[ProductLoader, next]] });
273
+ * // reader: isLoading true, old data
274
+ * await act(async () => resolve({ name: "Product 2" }));
275
+ * // reader: isLoading false, new data
250
276
  */
251
- navigate(url: string): Promise<void>;
277
+ navigate(url: string, options?: Pick<RenderRouteOptions, "loaders">): Promise<void>;
252
278
  /** The current committed pathname. */
253
279
  pathname(): string;
254
280
  /** The current committed params. */
@@ -1,9 +1,32 @@
1
- export declare const createFromReadableStream: () => never;
2
- export declare const renderToReadableStream: () => never;
1
+ /**
2
+ * Stub for `@vitejs/plugin-rsc/rsc` and the split `/rsc/server`, `/rsc/client`
3
+ * entries, shipped so consumers do not have to write a per-file `vi.mock(...)`.
4
+ * The real entries top-level import Vite virtuals that do not resolve in plain
5
+ * node.
6
+ *
7
+ * Under the `react-server` condition (the Flight project) the "use cache"
8
+ * codec runs on real Flight: the vendored react-server-dom `server.edge` /
9
+ * `client.edge` builds that plugin-rsc's `react/rsc/server` and
10
+ * `react/rsc/client` wrap, with the same manifests and the same server-side
11
+ * reference loader (`@vitejs/plugin-rsc/core/rsc` `setRequireModule`), so
12
+ * keys, writes, hits, value round trips and error rows match a build. The
13
+ * wrappers are not reused: their manifests read `import.meta.env.DEV`, which
14
+ * is undefined in a module node loads directly (vitest externalizes
15
+ * node_modules).
16
+ *
17
+ * Without the condition React's server build refuses to load, so the codec
18
+ * functions throw and a "use cache" call runs uncached. Any other load failure
19
+ * (a moved vendored path, a missing peer) throws. The action entries stay
20
+ * inert either way: no primitive decodes an action request.
21
+ */
22
+ export declare const renderToReadableStream: (value: unknown, options?: object) => ReadableStream<Uint8Array>;
23
+ export declare const createTemporaryReferenceSet: () => unknown;
24
+ export declare const createFromReadableStream: (stream: ReadableStream<Uint8Array>, options?: object, extraOptions?: {
25
+ preserveServerReferences?: boolean;
26
+ }) => Promise<unknown>;
27
+ export declare const encodeReply: (value: unknown, options?: object) => Promise<string | FormData>;
28
+ export declare const createClientTemporaryReferenceSet: () => unknown;
3
29
  export declare const loadServerAction: () => undefined;
4
30
  export declare const decodeReply: () => undefined;
5
31
  export declare const decodeAction: () => undefined;
6
32
  export declare const decodeFormState: () => undefined;
7
- export declare const createTemporaryReferenceSet: () => Record<string, never>;
8
- export declare const encodeReply: () => never;
9
- export declare const createClientTemporaryReferenceSet: () => Record<string, never>;
@@ -170,6 +170,7 @@ interface FlightTransformPlugin {
170
170
  * import { defineConfig } from "vitest/config";
171
171
  * import {
172
172
  * rangoUseClientTransform,
173
+ * rangoUseCacheTransform,
173
174
  * rangoTestAliases,
174
175
  * rangoInlineDeps,
175
176
  * } from "@rangojs/router/testing/vitest";
@@ -179,7 +180,7 @@ interface FlightTransformPlugin {
179
180
  * process.env.NODE_ENV = "production";
180
181
  *
181
182
  * export default defineConfig({
182
- * plugins: [rangoUseClientTransform()],
183
+ * plugins: [rangoUseClientTransform(), rangoUseCacheTransform()],
183
184
  * resolve: {
184
185
  * conditions: ["react-server"],
185
186
  * // Bare `@rangojs/router` -> its react-server build, so a handler/component
@@ -203,4 +204,37 @@ interface FlightTransformPlugin {
203
204
  * so `renderToFlightString` of pure leaf trees is unaffected.
204
205
  */
205
206
  export declare function rangoUseClientTransform(): FlightTransformPlugin;
207
+ /** The Vite plugin shape {@link rangoUseCacheTransform} returns. */
208
+ interface UseCacheTransformPlugin {
209
+ name: string;
210
+ enforce: "post";
211
+ configResolved(config: {
212
+ root: string;
213
+ }): void;
214
+ transform(this: {
215
+ warn(message: string): void;
216
+ }, code: string, id: string): Promise<{
217
+ code: string;
218
+ map: unknown;
219
+ } | undefined>;
220
+ }
221
+ /**
222
+ * A Vite plugin that applies the rango plugin's `"use cache"` transform in a
223
+ * Vitest project, so a function written with the directive is wrapped with
224
+ * `registerCachedFunction` as in dev and a build. `rango()` runs that transform
225
+ * only in its `rsc` environment and Vitest transforms in `ssr`, so without this
226
+ * plugin the directive is an inert string and the function runs on every call.
227
+ *
228
+ * Add it to the react-server project next to {@link rangoUseClientTransform}
229
+ * (see that function's config). There, with a seeded `cacheStore`,
230
+ * `renderHandler` / `runLoader` observe real hits. In the node project the
231
+ * wrap applies too, but the stubbed Flight serializer cannot write an entry.
232
+ *
233
+ * Ids are the ones `vite dev` emits: `<path relative to the Vitest root>#<name>`
234
+ * (a function-level directive gives `src/data.ts#$$hoist_0_getData`), not the
235
+ * build's hashed path, so store keys stay readable. `configResolved` sets that
236
+ * root. If `@vitejs/plugin-rsc/transforms` fails to load, the transform throws
237
+ * instead of leaving the module unwrapped (which the rango plugin does).
238
+ */
239
+ export declare function rangoUseCacheTransform(): UseCacheTransformPlugin;
206
240
  export {};
@@ -8,14 +8,14 @@
8
8
  *
9
9
  * Must be placed in the <head> element of your document, before any stylesheets.
10
10
  *
11
- * Note: when theme is enabled in the router config, `<MetaTags />` ALREADY
11
+ * Note: when theme is enabled in the router config, `<Html.Meta />` ALREADY
12
12
  * renders this FOUC script. Use `<ThemeScript />` only if you do NOT render
13
- * `<MetaTags />`. Rendering both is safe — the inline script guards the
13
+ * `<Html.Meta />`. Rendering both is safe — the inline script guards the
14
14
  * matchMedia listener registration against double-running — but it is redundant.
15
15
  *
16
16
  * @example
17
17
  * ```tsx
18
- * // In your document component. Use ThemeScript only when you do not render MetaTags.
18
+ * // In your document component. Use ThemeScript only when you do not render Html.Meta.
19
19
  * import { ThemeScript } from "@rangojs/router/theme";
20
20
  *
21
21
  * export function Document({ children }) {
@@ -1,12 +1,13 @@
1
1
  /**
2
- * Context passed to cache condition/key/tags functions.
2
+ * Context passed to cache condition/key/tags functions (the full
3
+ * RequestContext).
3
4
  *
4
- * This is a subset of RequestContext that's guaranteed to be available
5
- * during cache key generation (before middleware runs).
6
- *
7
- * Note: While the full RequestContext is passed, middleware-set variables
8
- * read via `ctx.get()` may not be populated yet since cache lookup happens
9
- * before middleware execution.
5
+ * Ordering: global middleware wraps the whole request, and on RSC routes route
6
+ * middleware wraps the match/render pipeline (rsc/handler.ts executeRender), so
7
+ * variables they set before `next()` are readable via `ctx.get()`. On response
8
+ * routes the cache lookup runs BEFORE route middleware, which wraps only the
9
+ * handler run on a miss (rsc/response-route-handler.ts); only global-middleware
10
+ * variables are populated there.
10
11
  */
11
12
  export type { RequestContext as CacheContext } from "../server/request-context.js";
12
13
  /**
@@ -105,7 +106,8 @@ export interface CacheOptions<TEnv = unknown> {
105
106
  * Return false to skip cache for this request (always fetch fresh).
106
107
  *
107
108
  * Has access to full RequestContext including env, request, params, cookies, etc.
108
- * Note: Middleware-set variables read via `ctx.get()` may not be populated yet.
109
+ * Runs after global and route middleware on RSC routes; on response routes,
110
+ * before route middleware (see CacheContext).
109
111
  *
110
112
  * @example
111
113
  * ```typescript
@@ -124,7 +126,8 @@ export interface CacheOptions<TEnv = unknown> {
124
126
  * Bypasses default key generation AND store's keyGenerator.
125
127
  *
126
128
  * Has access to full RequestContext including env, request, params, cookies, etc.
127
- * Note: Middleware-set variables read via `ctx.get()` may not be populated yet.
129
+ * Runs after global and route middleware on RSC routes; on response routes,
130
+ * before route middleware (see CacheContext).
128
131
  *
129
132
  * @example
130
133
  * ```typescript
@@ -143,7 +146,8 @@ export interface CacheOptions<TEnv = unknown> {
143
146
  * Tags for cache invalidation.
144
147
  * Can be a static array or a function that returns tags.
145
148
  *
146
- * The built-in `MemorySegmentCacheStore` and `CFCacheStore` index by tag.
149
+ * The built-in `MemorySegmentCacheStore`, `CFCacheStore`, and
150
+ * `VercelCacheStore` index by tag.
147
151
  * Invalidate on demand with `updateTag(...tags)` (awaitable, read-your-own-writes;
148
152
  * for server actions) or `revalidateTag(...tags)` (background hard-purge, not
149
153
  * awaited; for route handlers / webhooks). For `CFCacheStore`, distributed
@@ -335,6 +335,20 @@ export type InternalHandlerContext<TParams = {}, TEnv = DefaultEnv, TSearch exte
335
335
  * table (so a loader cannot await its own in-flight memoized promise).
336
336
  */
337
337
  _loaderCacheOriginalUse?: (item: any) => any;
338
+ /**
339
+ * @internal A loader-cache HIT asks before replaying a loader's recorded
340
+ * pushes: true when that loader has not run in this request and no other
341
+ * replay delivered it; its later run then replaces the replayed values
342
+ * (HandleStore.pushReplayed). One copy per loader per request. See
343
+ * setupLoaderAccess.
344
+ */
345
+ _claimLoaderPushes?: (loaderId: string) => boolean;
346
+ /**
347
+ * @internal Run a loader on a fresh executor (own memo map): a loader-cache
348
+ * stale refresh, whose diverted pushes must not take a dependency's run
349
+ * from the page. See setupLoaderAccess.
350
+ */
351
+ _runLoaderIsolated?: (loader: any) => Promise<any>;
338
352
  };
339
353
  /**
340
354
  * Generic params type - flexible object with string keys
@@ -23,9 +23,10 @@ import type { RequestScope } from "./request-scope.js";
23
23
  * return await db.cart.get(user.id);
24
24
  * });
25
25
  *
26
- * // With typed params:
27
- * const ProductLoader = createLoader<Product, { slug: string }>(async (ctx) => {
28
- * const { slug } = ctx.params; // slug is typed as string
26
+ * // createLoader's one type parameter is the data type; params are
27
+ * // Record<string, string | undefined>:
28
+ * const ProductLoader = createLoader<Product>(async (ctx) => {
29
+ * const { slug } = ctx.params; // string | undefined
29
30
  * return await db.products.findBySlug(slug);
30
31
  * });
31
32
  * ```
@@ -213,9 +214,10 @@ export type LoadOptions = {
213
214
  * return await db.cart.get(ctx.get("user").id);
214
215
  * });
215
216
  *
216
- * // With typed params:
217
- * export const ProductLoader = createLoader<Product, { slug: string }>(async (ctx) => {
218
- * const { slug } = ctx.params; // slug is typed as string
217
+ * // createLoader's one type parameter is the data type; params are
218
+ * // Record<string, string | undefined>:
219
+ * export const ProductLoader = createLoader<Product>(async (ctx) => {
220
+ * const { slug } = ctx.params; // string | undefined
219
221
  * return await db.products.findBySlug(slug);
220
222
  * });
221
223
  *
@@ -1,18 +1,8 @@
1
1
  import type { ContextVar } from "../context-var.js";
2
- import type { ReverseFunction } from "../reverse.js";
2
+ import type { GlobalReverseFunction } from "../reverse.js";
3
3
  import type { DefaultReverseRouteMap, DefaultVars } from "../types/global-namespace.js";
4
4
  import type { UseItems, ResponseRouteUseItem } from "../route-types.js";
5
5
  import type { RequestScope } from "../types/request-scope.js";
6
- /**
7
- * Reverse function for response handler contexts.
8
- * Global names get autocomplete and param validation from the generated route map.
9
- * Local `.name` calls are accepted but not validated (scope unknown at type level).
10
- */
11
- type ResponseReverseFunction = [DefaultReverseRouteMap] extends [
12
- Record<string, string>
13
- ] ? (name: string, params?: Record<string, string>, search?: Record<string, unknown>) => string : ReverseFunction<DefaultReverseRouteMap> & {
14
- (name: `.${string}`, params?: Record<string, string>, search?: Record<string, unknown>): string;
15
- };
16
6
  /**
17
7
  * Symbol marking a route as a response route (non-RSC).
18
8
  * Stored on PathOptions and UrlPatterns to signal the trie to short-circuit.
@@ -56,7 +46,8 @@ export interface ResponseHandlerContext<TParams = Record<string, string>, TEnv =
56
46
  params: TParams;
57
47
  /** @internal Phantom property for params type invariance. Prevents mounting handlers on wrong routes. */
58
48
  readonly _paramCheck?: (params: TParams) => TParams;
59
- reverse: ResponseReverseFunction;
49
+ /** Global route names only; see {@link GlobalReverseFunction}. */
50
+ reverse: GlobalReverseFunction<DefaultReverseRouteMap>;
60
51
  /** Read a variable set by middleware via ctx.set(key, value) or ctx.set(ContextVar, value). */
61
52
  get: {
62
53
  <T>(contextVar: ContextVar<T>): T | undefined;
@@ -2,6 +2,14 @@ import type { LoaderDefinition, LoadOptions } from "./types.js";
2
2
  export type LoadFunction<T> = (options?: LoadOptions) => Promise<T>;
3
3
  export interface UseLoaderResult<T> {
4
4
  data: T;
5
+ /**
6
+ * True while this loader family (`loader.$$id`) is in flight: a `load()` /
7
+ * group refresh, or a held navigation still streaming a re-run of this
8
+ * loader. Family-keyed, not segment-keyed: a layout `useLoader` of the same
9
+ * `createLoader` the child route is re-running also reports true, even if
10
+ * the layout copy is not replaced. Not used for the initial streamed read
11
+ * (that suspends).
12
+ */
5
13
  isLoading: boolean;
6
14
  error: Error | null;
7
15
  load: LoadFunction<T>;
@@ -9,6 +17,7 @@ export interface UseLoaderResult<T> {
9
17
  }
10
18
  export interface UseFetchLoaderResult<T> {
11
19
  data: T | undefined;
20
+ /** Same family-keyed contract as {@link UseLoaderResult.isLoading}. */
12
21
  isLoading: boolean;
13
22
  error: Error | null;
14
23
  load: LoadFunction<T>;
@@ -36,7 +45,7 @@ export interface UseLoaderOptions {
36
45
  * @example Basic usage - accessing route loader data
37
46
  * ```tsx
38
47
  * "use client";
39
- * import { useLoader } from "rsc-router/client";
48
+ * import { useLoader } from "@rangojs/router/client";
40
49
  * import { CartLoader } from "../loaders/cart";
41
50
  *
42
51
  * // In route definition: loader(CartLoader)
@@ -67,7 +76,7 @@ export declare function useLoader<T>(loader: LoaderDefinition<T>, options?: UseL
67
76
  * @example On-demand fetching
68
77
  * ```tsx
69
78
  * "use client";
70
- * import { useFetchLoader } from "rsc-router/client";
79
+ * import { useFetchLoader } from "@rangojs/router/client";
71
80
  * import { SearchLoader } from "../loaders/search";
72
81
  *
73
82
  * export function SearchResults() {
@@ -121,7 +130,7 @@ export declare function useFetchLoader<T>(loader: LoaderDefinition<T>, options?:
121
130
  * @example
122
131
  * ```tsx
123
132
  * "use client";
124
- * import { useLoader, useRefreshLoaders } from "rsc-router/client";
133
+ * import { useLoader, useRefreshLoaders } from "@rangojs/router/client";
125
134
  *
126
135
  * function Profile() {
127
136
  * const { data } = useLoader(ProfileLoader, { key: userId, refreshGroup: "account" });
@@ -15,6 +15,37 @@
15
15
  * - transformWrapExport() for file-level wrapping
16
16
  * - transformHoistInlineDirective() for function-level hoisting
17
17
  */
18
- import type { Plugin } from "vite";
18
+ import type { Plugin, Rollup } from "vite";
19
+ import type { Counter } from "../debug.js";
19
20
  export declare const USE_CACHE_DIRECTIVE_RE: RegExp;
20
- export declare function useCacheTransform(): Plugin;
21
+ /**
22
+ * @param counter `rango:transform` timing counter, created by `rango()`
23
+ * (`createCounter` in ../debug.js); omitted when debugging is off.
24
+ */
25
+ export declare function useCacheTransform(counter?: Counter): Plugin;
26
+ /** Options for {@link transformUseCache}. */
27
+ export interface UseCacheTransformOptions {
28
+ /** Root the ids are relative to (Vite's `config.root`). */
29
+ root: string;
30
+ /** Build ids hash the path (`hashId`); dev ids are `<path>#<export>`. */
31
+ isBuild: boolean;
32
+ /** Sink for the near-miss directive warning. */
33
+ warn: (message: string) => void;
34
+ /**
35
+ * Called when `@vitejs/plugin-rsc/transforms` fails to load; the module is
36
+ * then left unwrapped. The rango plugin omits it (skip silently).
37
+ */
38
+ onTransformsError?: (error: unknown) => void;
39
+ }
40
+ /** Whether a module may carry a `"use cache"` directive the transform wraps. */
41
+ export declare function isUseCacheCandidate(code: string, id: string): boolean;
42
+ /**
43
+ * Wrap a module's `"use cache"` functions with `registerCachedFunction`. The
44
+ * rango plugin runs it in the rsc environment; `rangoUseCacheTransform()`
45
+ * (testing/vitest.ts) runs it in a Vitest project, whose environment is `ssr`.
46
+ * Call it only for an {@link isUseCacheCandidate} module.
47
+ */
48
+ export declare function transformUseCache(code: string, id: string, options: UseCacheTransformOptions): Promise<{
49
+ code: string;
50
+ map: Rollup.ExistingRawSourceMap;
51
+ } | undefined>;
@@ -21,7 +21,7 @@ export { hasUseClientDirective };
21
21
  * plugins: [
22
22
  * react(),
23
23
  * rango({ preset: 'cloudflare' }),
24
- * cloudflare({ viteEnvironment: { name: 'rsc' } }),
24
+ * cloudflare({ viteEnvironment: { name: 'rsc', childEnvironments: ['ssr'] } }),
25
25
  * ],
26
26
  * });
27
27
  * ```