@rangojs/router 0.17.0 → 0.18.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 (152) hide show
  1. package/README.md +3 -2
  2. package/dist/types/browser/cookie-name.d.ts +6 -0
  3. package/dist/types/browser/history-state.d.ts +16 -0
  4. package/dist/types/cache/cache-key-utils.d.ts +17 -2
  5. package/dist/types/cache/cache-policy.d.ts +17 -0
  6. package/dist/types/cache/cache-scope.d.ts +98 -14
  7. package/dist/types/cache/cache-tag.d.ts +92 -7
  8. package/dist/types/cache/cf/cf-base64.d.ts +6 -0
  9. package/dist/types/cache/cf/cf-cache-constants.d.ts +11 -0
  10. package/dist/types/cache/cf/cf-cache-store.d.ts +185 -44
  11. package/dist/types/cache/cf/cf-cache-types.d.ts +31 -4
  12. package/dist/types/cache/cf/cf-shell-frame.d.ts +95 -0
  13. package/dist/types/cache/cf/cf-tag-marker-memo.d.ts +34 -0
  14. package/dist/types/cache/handle-snapshot.d.ts +4 -3
  15. package/dist/types/cache/index.d.ts +1 -0
  16. package/dist/types/cache/isolate-tag-memo.d.ts +149 -0
  17. package/dist/types/cache/shell-memo.d.ts +178 -0
  18. package/dist/types/cache/shell-snapshot.d.ts +110 -21
  19. package/dist/types/cache/tag-invalidation.d.ts +30 -3
  20. package/dist/types/cache/types.d.ts +207 -43
  21. package/dist/types/cache/vercel/vercel-cache-store.d.ts +111 -13
  22. package/dist/types/prerender/build-shell-capture.d.ts +11 -4
  23. package/dist/types/router/instrument.d.ts +8 -0
  24. package/dist/types/router/middleware-types.d.ts +6 -1
  25. package/dist/types/router/segment-resolution/loader-cache.d.ts +40 -11
  26. package/dist/types/router/segment-resolution/loader-snapshot.d.ts +21 -10
  27. package/dist/types/router/segment-resolution/mask-nested.d.ts +65 -24
  28. package/dist/types/rsc/full-payload.d.ts +14 -0
  29. package/dist/types/rsc/handler.d.ts +1 -1
  30. package/dist/types/rsc/shell-build-manifest.d.ts +8 -0
  31. package/dist/types/rsc/shell-capture-constants.d.ts +53 -6
  32. package/dist/types/rsc/shell-capture.d.ts +146 -15
  33. package/dist/types/rsc/shell-serve.d.ts +140 -17
  34. package/dist/types/rsc/types.d.ts +7 -0
  35. package/dist/types/server/context.d.ts +109 -15
  36. package/dist/types/server/cookie-store.d.ts +21 -0
  37. package/dist/types/server/request-context.d.ts +159 -33
  38. package/dist/types/ssr/index.d.ts +9 -0
  39. package/dist/types/ssr/ssr-root.d.ts +9 -8
  40. package/dist/types/testing/flight.entry.d.ts +7 -0
  41. package/dist/types/testing/run-loader.d.ts +18 -3
  42. package/dist/types/testing/serve-shell-request.d.ts +132 -0
  43. package/dist/types/testing/shell-status.d.ts +23 -10
  44. package/dist/types/types/handler-context.d.ts +14 -1
  45. package/dist/types/urls/pattern-types.d.ts +22 -18
  46. package/dist/types/vite/router-discovery.d.ts +29 -2
  47. package/dist/vite/index.js +46 -9
  48. package/package.json +1 -1
  49. package/skills/breadcrumbs/SKILL.md +17 -7
  50. package/skills/cache-guide/SKILL.md +123 -51
  51. package/skills/caching/SKILL.md +253 -23
  52. package/skills/catalog.json +1 -1
  53. package/skills/client-urls/SKILL.md +11 -6
  54. package/skills/cloudflare/SKILL.md +15 -0
  55. package/skills/comparison/references/framework-comparison.md +44 -32
  56. package/skills/defer-hydration/SKILL.md +50 -30
  57. package/skills/deployment-caching/SKILL.md +5 -2
  58. package/skills/document-cache/SKILL.md +8 -2
  59. package/skills/handler-use/SKILL.md +2 -0
  60. package/skills/hooks/handle-and-actions.md +3 -1
  61. package/skills/hooks/state.md +3 -1
  62. package/skills/loader/SKILL.md +161 -39
  63. package/skills/middleware/SKILL.md +16 -9
  64. package/skills/migrate-nextjs/SKILL.md +67 -36
  65. package/skills/migrate-react-router/SKILL.md +2 -1
  66. package/skills/migrate-react-router/component-migration.md +3 -1
  67. package/skills/migrate-react-router/data-and-actions.md +3 -1
  68. package/skills/migrate-react-router/route-mapping.md +7 -4
  69. package/skills/observability/SKILL.md +64 -0
  70. package/skills/parallel/SKILL.md +13 -8
  71. package/skills/ppr/SKILL.md +655 -363
  72. package/skills/prerender/SKILL.md +27 -14
  73. package/skills/rango/SKILL.md +52 -33
  74. package/skills/route/SKILL.md +12 -7
  75. package/skills/router-setup/SKILL.md +4 -2
  76. package/skills/scripts/SKILL.md +8 -1
  77. package/skills/server-actions/SKILL.md +11 -0
  78. package/skills/shell-manifest/SKILL.md +21 -12
  79. package/skills/testing/SKILL.md +25 -21
  80. package/skills/testing/cache-prerender.md +15 -14
  81. package/skills/testing/loader.md +18 -1
  82. package/skills/testing/serve-shell-request.md +188 -0
  83. package/skills/theme/SKILL.md +52 -4
  84. package/skills/typesafety/env-and-bindings.md +10 -2
  85. package/skills/use-cache/SKILL.md +39 -16
  86. package/skills/vercel/SKILL.md +14 -2
  87. package/skills/view-transitions/SKILL.md +1 -1
  88. package/src/browser/cookie-name.ts +23 -0
  89. package/src/browser/history-state.ts +23 -0
  90. package/src/browser/rsc-router.tsx +5 -0
  91. package/src/cache/cache-key-utils.ts +22 -2
  92. package/src/cache/cache-policy.ts +22 -0
  93. package/src/cache/cache-runtime.ts +34 -7
  94. package/src/cache/cache-scope.ts +345 -33
  95. package/src/cache/cache-tag.ts +375 -11
  96. package/src/cache/cf/cf-base64.ts +36 -4
  97. package/src/cache/cf/cf-cache-constants.ts +12 -0
  98. package/src/cache/cf/cf-cache-store.ts +990 -348
  99. package/src/cache/cf/cf-cache-types.ts +32 -4
  100. package/src/cache/cf/cf-shell-frame.ts +258 -0
  101. package/src/cache/cf/cf-tag-marker-memo.ts +143 -39
  102. package/src/cache/handle-snapshot.ts +4 -3
  103. package/src/cache/index.ts +2 -0
  104. package/src/cache/isolate-tag-memo.ts +308 -0
  105. package/src/cache/shell-memo.ts +341 -0
  106. package/src/cache/shell-snapshot.ts +217 -27
  107. package/src/cache/tag-invalidation.ts +104 -19
  108. package/src/cache/types.ts +211 -43
  109. package/src/cache/vercel/vercel-cache-store.ts +545 -61
  110. package/src/prerender/build-shell-capture.ts +65 -11
  111. package/src/route-definition/dsl-helpers.ts +4 -3
  112. package/src/router/handler-context.ts +38 -4
  113. package/src/router/instrument.ts +54 -2
  114. package/src/router/intercept-resolution.ts +19 -7
  115. package/src/router/loader-resolution.ts +63 -32
  116. package/src/router/match-api.ts +28 -9
  117. package/src/router/match-handlers.ts +5 -0
  118. package/src/router/match-middleware/cache-lookup.ts +17 -0
  119. package/src/router/match-middleware/cache-store.ts +31 -11
  120. package/src/router/middleware-types.ts +6 -1
  121. package/src/router/middleware.ts +23 -4
  122. package/src/router/prerender-match.ts +6 -0
  123. package/src/router/segment-resolution/fresh.ts +18 -9
  124. package/src/router/segment-resolution/helpers.ts +4 -2
  125. package/src/router/segment-resolution/loader-cache.ts +227 -56
  126. package/src/router/segment-resolution/loader-snapshot.ts +203 -51
  127. package/src/router/segment-resolution/mask-nested.ts +136 -42
  128. package/src/router/segment-resolution/revalidation.ts +27 -10
  129. package/src/rsc/full-payload.ts +25 -1
  130. package/src/rsc/handler.ts +14 -2
  131. package/src/rsc/progressive-enhancement.ts +3 -2
  132. package/src/rsc/response-cache-serve.ts +9 -6
  133. package/src/rsc/rsc-rendering.ts +601 -181
  134. package/src/rsc/shell-build-manifest.ts +27 -9
  135. package/src/rsc/shell-capture-constants.ts +60 -6
  136. package/src/rsc/shell-capture.ts +1001 -250
  137. package/src/rsc/shell-serve.ts +316 -34
  138. package/src/rsc/types.ts +7 -0
  139. package/src/server/context.ts +219 -28
  140. package/src/server/cookie-store.ts +87 -62
  141. package/src/server/request-context.ts +263 -38
  142. package/src/ssr/index.tsx +96 -37
  143. package/src/ssr/ssr-root.tsx +9 -8
  144. package/src/testing/flight.entry.ts +14 -0
  145. package/src/testing/run-loader.ts +63 -5
  146. package/src/testing/serve-shell-request.ts +378 -0
  147. package/src/testing/shell-status.ts +34 -12
  148. package/src/theme/ThemeProvider.tsx +7 -6
  149. package/src/types/handler-context.ts +14 -1
  150. package/src/urls/pattern-types.ts +22 -18
  151. package/src/vite/discovery/shell-prerender-phase.ts +9 -0
  152. package/src/vite/router-discovery.ts +14 -4
@@ -342,6 +342,12 @@ export type HandlerContext<
342
342
  * Current theme (from cookie or default).
343
343
  * Only available when theme is enabled in router config.
344
344
  *
345
+ * The visitor's cookie, so it is guarded like `cookies()`: reading it on a
346
+ * `ppr` route refuses the shell capture, and inside a `cache()` boundary or
347
+ * a `"use cache"` function it throws (#971). Read it with `useTheme()` in a
348
+ * client component, or in a live loader, on those routes. A read-only,
349
+ * non-enumerable getter: `{ ...ctx }` does not carry it.
350
+ *
345
351
  * @example
346
352
  * ```typescript
347
353
  * route("settings", (ctx) => {
@@ -350,7 +356,7 @@ export type HandlerContext<
350
356
  * });
351
357
  * ```
352
358
  */
353
- theme?: Theme;
359
+ readonly theme?: Theme;
354
360
  /**
355
361
  * Set the theme (only available when theme is enabled in router config).
356
362
  * Sets a cookie with the new theme value.
@@ -469,6 +475,13 @@ export type InternalHandlerContext<
469
475
  * from the page. See setupLoaderAccess.
470
476
  */
471
477
  _runLoaderIsolated?: (loader: any) => Promise<any>;
478
+ /**
479
+ * @internal A loader's cache() binding started with these cache() tags: an
480
+ * execution of that loader outside the binding (a reader started it first,
481
+ * or a stale refresh runs it) answers for them too (#964). See
482
+ * setupLoaderAccess.
483
+ */
484
+ _bindLoaderCacheTags?: (loaderId: string, tags: Set<string>) => void;
472
485
  };
473
486
 
474
487
  /**
@@ -59,30 +59,34 @@ export interface PartialPrerenderProps {
59
59
  /**
60
60
  * Operational tags attached to the captured shell entry for
61
61
  * `updateTag()`/`revalidateTag()`-driven eviction. UNIONED with the tags the
62
- * capture render auto-collects (the shell's own non-loader request tags).
62
+ * capture collects: its doc record's tags (what the handler layer and the
63
+ * server components it rendered recorded) and its bake-lane loaders'.
63
64
  */
64
65
  tags?: string[];
65
66
  /**
66
- * Upper bound (serialized UTF-8 bytes) on the capture data snapshot riding
67
- * inside the shell entry. The snapshot duplicates every cache-store value
68
- * the capture pinned, so a page over a large cache() segment can push the
69
- * entry toward store value limits (Cloudflare KV caps a value at 25 MiB).
70
- * Over the cap the snapshot is skipped: the shell is still stored and
71
- * served, but pinned reads fall back to the live store, so drifted cached
72
- * content can hydration-mismatch and be repaired client-side (the
73
- * pre-snapshot behavior). Reported once per key. Defaults to 8 MiB.
67
+ * Upper bound (serialized UTF-8 bytes) on the loader pins in the capture
68
+ * data snapshot: the loader records and the cache values loaders read,
69
+ * duplicated into the shell entry. The doc record (the handler layer every
70
+ * HIT replays) is exempt. Over the cap the pins are dropped: the shell is
71
+ * still stored and every HIT still replays the handler layer, but the
72
+ * pinned loaders read the live store, so drifted cached content can
73
+ * hydration-mismatch and be repaired client-side. Reported once per key.
74
+ * Defaults to 8 MiB. The whole entry is bounded separately by the store's
75
+ * value limit (Cloudflare KV: 25 MiB); an entry over it is refused.
74
76
  */
75
77
  maxSnapshotBytes?: number;
76
78
  /**
77
- * Capture settle budget in MILLISECONDS (default 15000). Bounds the whole
78
- * background capture: the wait for deferred shell material — top-level
79
- * pushed handle promises (`ctx.use(Meta)(promise.then(...))` and friends)
80
- * are AWAITED and their settled values baked into the stored shell — AND
81
- * the fizz prerender deadline. Declare it to tighten the budget below the
82
- * default or when a route's shell material takes longer than 15s to
83
- * settle. A budget that expires with pushes still pending REFUSES the
84
- * capture (the route stays MISS with the once-per-key warning) — a shell
85
- * with missing head material is never stored. Capture is background work
79
+ * Capture settle budget in MILLISECONDS (default 15000). One deadline for
80
+ * the whole background capture: the match (the handlers, and the loaders
81
+ * they await), the wait for handler output (handle pushes and the promises
82
+ * nested in them, bake-lane loaders, and the doc record's encode, which
83
+ * awaits handler promises and async server components), AND the fizz
84
+ * prerender. Everything a handler produces is
85
+ * awaited and baked into the stored shell. Declare it to tighten the
86
+ * budget below the default or when a route's shell material takes longer
87
+ * than 15s to settle. A budget that expires before the doc record is
88
+ * written REFUSES the capture (the route stays MISS with the once-per-key
89
+ * warning): a shell with missing handler output is never stored. Capture is background work
86
90
  * (waitUntil), so a longer budget costs latency-to-HIT only, never a served
87
91
  * response; the platform waitUntil lifetime (workerd: ~30s past response
88
92
  * completion) is the physical ceiling — the default's derivation lives in
@@ -64,6 +64,14 @@ export async function runShellPrerenderPhase(
64
64
  const startTotal = performance.now();
65
65
  console.log(`[rango] Shell-prerendering ${candidates.length} URL(s)...`);
66
66
 
67
+ // Hold the process open for the phase. The capture's waits are unref'd
68
+ // timers (retry delay, Flight quiesce hops) so a runtime capture never keeps
69
+ // Node alive; every bundle is already written and the build temp server has
70
+ // no file watcher (#947). Its only remaining handle is the HMR WebSocket on
71
+ // 24678, which fails to bind silently when another Vite server holds the
72
+ // port; `vite build` then exited 0 mid-phase without writing
73
+ // __shell-manifest.js.
74
+ const keepAlive = setInterval(() => {}, 60_000);
67
75
  let restoreClientRequire: (() => void) | undefined;
68
76
  try {
69
77
  // Runner access needs the RunnableDevEnvironment surface; the environments
@@ -343,6 +351,7 @@ export async function runShellPrerenderPhase(
343
351
  // buildApp post hook's finally on success, buildEnd on an aborted build.
344
352
  delete (globalThis as any).__loadPrerenderManifestModule;
345
353
  restoreClientRequire?.();
354
+ clearInterval(keepAlive);
346
355
  }
347
356
  }
348
357
 
@@ -6,7 +6,7 @@
6
6
  * generation, virtual module codegen, and bundle post-processing.
7
7
  */
8
8
 
9
- import type { Plugin } from "vite";
9
+ import type { Plugin, ViteDevServer } from "vite";
10
10
  import { createServer as createViteServer } from "vite";
11
11
  import { resolve } from "node:path";
12
12
  import { readFileSync } from "node:fs";
@@ -177,8 +177,9 @@ const injectedShellNotReadyPaths = new Set<string>();
177
177
  * - Build: hashed IDs (forceBuild), hashClientRefs for production bundles
178
178
  *
179
179
  * Returns the ViteDevServer instance. Callers access .environments.rsc as needed.
180
+ * @internal Exported for tests.
180
181
  */
181
- async function createTempRscServer(
182
+ export async function createTempRscServer(
182
183
  state: DiscoveryState,
183
184
  options: {
184
185
  forceBuild?: boolean;
@@ -195,7 +196,7 @@ async function createTempRscServer(
195
196
  */
196
197
  realSsrEntry?: boolean;
197
198
  } = {},
198
- ) {
199
+ ): Promise<ViteDevServer> {
199
200
  // Install the Node ESM loader hook before any module evaluation so
200
201
  // `cloudflare:*` specifiers in externalized/loader-delegated modules
201
202
  // (e.g. packages plugin-rsc marks as external) resolve to stubs
@@ -216,7 +217,16 @@ async function createTempRscServer(
216
217
  return createViteServer({
217
218
  root: state.projectRoot,
218
219
  configFile: false,
219
- server: { middlewareMode: true },
220
+ server: {
221
+ middlewareMode: true,
222
+ // Build mode: no file watching. buildStart installs the per-router tries
223
+ // into this realm and runShellPrerenderPhase reuses it after the bundles
224
+ // are written. A watched write in between (writeRouteTypesFiles' own
225
+ // <router>.named-routes.gen.ts, which every router file imports) can
226
+ // make Vite full-reload the RSC runner; the shell phase then matches in
227
+ // a fresh realm with no tries and a root path("/*") wins (#947).
228
+ ...(options.forceBuild ? { watch: null } : {}),
229
+ },
220
230
  appType: "custom",
221
231
  logLevel: "silent",
222
232
  resolve: resolveConfig,