@rangojs/router 0.19.1 → 0.21.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 (185) hide show
  1. package/dist/bin/rango.js +26 -14
  2. package/dist/types/browser/event-controller.d.ts +32 -0
  3. package/dist/types/browser/history-state.d.ts +16 -4
  4. package/dist/types/browser/navigation-transaction.d.ts +7 -0
  5. package/dist/types/browser/rango-state.d.ts +3 -1
  6. package/dist/types/browser/react/context.d.ts +11 -0
  7. package/dist/types/browser/react/location-state-shared.d.ts +84 -10
  8. package/dist/types/browser/react/location-state.d.ts +7 -2
  9. package/dist/types/browser/react/subscribe-to-nothing.d.ts +7 -0
  10. package/dist/types/cache/cache-scope.d.ts +4 -5
  11. package/dist/types/cache/cf/cf-cache-store.d.ts +27 -6
  12. package/dist/types/cache/cf/cf-cache-types.d.ts +12 -4
  13. package/dist/types/cache/handle-snapshot.d.ts +36 -50
  14. package/dist/types/cache/index.d.ts +2 -0
  15. package/dist/types/cache/shell-snapshot.d.ts +19 -3
  16. package/dist/types/cache/types.d.ts +21 -10
  17. package/dist/types/cache/vercel/vercel-cache-store.d.ts +28 -16
  18. package/dist/types/client-urls/navigation.d.ts +7 -1
  19. package/dist/types/client-urls/optimistic-location.d.ts +10 -4
  20. package/dist/types/handles/deferred-resolution.d.ts +11 -1
  21. package/dist/types/prerender/build-shell-capture.d.ts +8 -6
  22. package/dist/types/router/match-middleware/cache-lookup.d.ts +1 -1
  23. package/dist/types/router/navigation-snapshot.d.ts +8 -1
  24. package/dist/types/router/router-interfaces.d.ts +2 -1
  25. package/dist/types/router/router-options.d.ts +17 -7
  26. package/dist/types/router/segment-resolution/loader-cache.d.ts +21 -9
  27. package/dist/types/router-versions.d.ts +39 -0
  28. package/dist/types/rsc/handler-context.d.ts +5 -0
  29. package/dist/types/rsc/shell-build-manifest.d.ts +3 -2
  30. package/dist/types/rsc/types.d.ts +4 -6
  31. package/dist/types/server/build-version-table.d.ts +44 -0
  32. package/dist/types/server/handle-store.d.ts +87 -46
  33. package/dist/types/server/request-context.d.ts +22 -8
  34. package/dist/types/ssr/entry-preload-priority.d.ts +69 -0
  35. package/dist/types/ssr/index.d.ts +1 -0
  36. package/dist/types/ssr/preinit-client-references.d.ts +17 -5
  37. package/dist/types/testing/build-versions.d.ts +32 -0
  38. package/dist/types/testing/dom.entry.d.ts +1 -1
  39. package/dist/types/testing/flight.entry.d.ts +1 -1
  40. package/dist/types/testing/index.d.ts +2 -0
  41. package/dist/types/testing/internal/seed-vars.d.ts +7 -2
  42. package/dist/types/testing/location-state-key.d.ts +5 -1
  43. package/dist/types/testing/render-route.d.ts +77 -6
  44. package/dist/types/testing/serve-shell-request.d.ts +21 -0
  45. package/dist/types/testing/vitest-stubs/version.d.ts +1 -0
  46. package/dist/types/types/handler-context.d.ts +8 -5
  47. package/dist/types/vite/discovery/build-versions.d.ts +393 -0
  48. package/dist/types/vite/discovery/installed-packages.d.ts +26 -0
  49. package/dist/types/vite/discovery/router-versions-phase.d.ts +52 -0
  50. package/dist/types/vite/discovery/shell-prerender-phase.d.ts +7 -17
  51. package/dist/types/vite/discovery/state.d.ts +14 -0
  52. package/dist/types/vite/encryption-key.d.ts +18 -0
  53. package/dist/types/vite/plugin-types.d.ts +47 -6
  54. package/dist/types/vite/plugins/{version-injector.d.ts → entry-bootstrap-injector.d.ts} +4 -6
  55. package/dist/types/vite/plugins/expose-ids/export-analysis.d.ts +1 -0
  56. package/dist/types/vite/plugins/expose-ids/router-transform.d.ts +14 -0
  57. package/dist/types/vite/plugins/stable-rsc-output.d.ts +51 -0
  58. package/dist/types/vite/plugins/version-plugin.d.ts +9 -11
  59. package/dist/types/vite/plugins/virtual-entries.d.ts +21 -2
  60. package/dist/types/vite/router-discovery.d.ts +6 -0
  61. package/dist/types/vite/utils/client-chunks.d.ts +24 -2
  62. package/dist/types/vite/utils/compare-strings.d.ts +7 -0
  63. package/dist/types/vite/utils/package-resolution.d.ts +8 -0
  64. package/dist/types/vite/utils/prerender-utils.d.ts +2 -0
  65. package/dist/vite/index.js +1544 -420
  66. package/package.json +1 -2
  67. package/skills/bundle-analysis/SKILL.md +1 -1
  68. package/skills/cache-guide/SKILL.md +84 -20
  69. package/skills/caching/SKILL.md +16 -5
  70. package/skills/client-urls/SKILL.md +16 -0
  71. package/skills/cloudflare/SKILL.md +2 -1
  72. package/skills/comparison/references/framework-comparison.md +16 -12
  73. package/skills/hooks/SKILL.md +3 -0
  74. package/skills/hooks/handle-and-actions.md +6 -0
  75. package/skills/hooks/navigation.md +28 -0
  76. package/skills/hooks/state.md +209 -8
  77. package/skills/hooks/urls.md +40 -1
  78. package/skills/host-router/SKILL.md +13 -0
  79. package/skills/intercept/SKILL.md +6 -0
  80. package/skills/loader/SKILL.md +19 -1
  81. package/skills/middleware/SKILL.md +2 -0
  82. package/skills/ppr/SKILL.md +119 -40
  83. package/skills/prerender/SKILL.md +7 -3
  84. package/skills/rango/SKILL.md +8 -7
  85. package/skills/router-setup/SKILL.md +23 -3
  86. package/skills/testing/SKILL.md +30 -26
  87. package/skills/testing/client-components.md +193 -8
  88. package/skills/testing/handles.md +2 -0
  89. package/skills/testing/loader.md +1 -1
  90. package/skills/testing/middleware.md +1 -1
  91. package/skills/testing/render-handler.md +18 -18
  92. package/skills/testing/serve-shell-request.md +63 -14
  93. package/skills/testing/server-actions.md +17 -17
  94. package/skills/testing/server-tree.md +1 -1
  95. package/skills/typesafety/generated-files-and-cli.md +1 -1
  96. package/skills/use-cache/SKILL.md +16 -5
  97. package/skills/vercel/SKILL.md +3 -3
  98. package/src/bin/rango.ts +1 -1
  99. package/src/browser/event-controller.ts +75 -2
  100. package/src/browser/history-state.ts +39 -17
  101. package/src/browser/navigation-bridge.ts +41 -18
  102. package/src/browser/navigation-transaction.ts +15 -10
  103. package/src/browser/partial-update.ts +10 -2
  104. package/src/browser/rango-state.ts +3 -1
  105. package/src/browser/react/Link.tsx +2 -3
  106. package/src/browser/react/NavigationProvider.tsx +25 -1
  107. package/src/browser/react/context.ts +13 -0
  108. package/src/browser/react/location-state-shared.ts +200 -34
  109. package/src/browser/react/location-state.ts +40 -75
  110. package/src/browser/react/subscribe-to-nothing.ts +7 -0
  111. package/src/browser/react/use-handle.ts +20 -1
  112. package/src/browser/rsc-router.tsx +33 -11
  113. package/src/browser/server-action-bridge.ts +17 -1
  114. package/src/cache/cache-scope.ts +18 -8
  115. package/src/cache/cf/cf-cache-store.ts +75 -55
  116. package/src/cache/cf/cf-cache-types.ts +12 -4
  117. package/src/cache/handle-snapshot.ts +69 -87
  118. package/src/cache/index.ts +4 -0
  119. package/src/cache/shell-snapshot.ts +35 -5
  120. package/src/cache/types.ts +21 -10
  121. package/src/cache/vercel/vercel-cache-store.ts +48 -20
  122. package/src/client-urls/client-root.tsx +1 -0
  123. package/src/client-urls/navigation.ts +10 -0
  124. package/src/client-urls/optimistic-location.ts +10 -4
  125. package/src/handles/deferred-resolution.ts +31 -0
  126. package/src/prerender/build-shell-capture.ts +11 -9
  127. package/src/router/manifest.ts +2 -0
  128. package/src/router/match-middleware/cache-lookup.ts +11 -18
  129. package/src/router/navigation-snapshot.ts +25 -6
  130. package/src/router/request-classification.ts +15 -12
  131. package/src/router/router-interfaces.ts +2 -1
  132. package/src/router/router-options.ts +17 -7
  133. package/src/router/segment-resolution/loader-cache.ts +95 -60
  134. package/src/router-versions.ts +60 -0
  135. package/src/rsc/full-payload.ts +6 -2
  136. package/src/rsc/handler-context.ts +5 -0
  137. package/src/rsc/handler.ts +14 -4
  138. package/src/rsc/rsc-rendering.ts +12 -1
  139. package/src/rsc/shell-build-manifest.ts +10 -7
  140. package/src/rsc/shell-capture.ts +36 -26
  141. package/src/rsc/types.ts +4 -6
  142. package/src/server/build-version-table.ts +115 -0
  143. package/src/server/handle-store.ts +344 -127
  144. package/src/server/request-context.ts +62 -11
  145. package/src/ssr/entry-preload-priority.ts +308 -0
  146. package/src/ssr/index.tsx +46 -9
  147. package/src/ssr/preinit-client-references.ts +17 -5
  148. package/src/ssr/ssr-root.tsx +16 -4
  149. package/src/testing/build-versions.ts +64 -0
  150. package/src/testing/dispatch.ts +6 -0
  151. package/src/testing/dom.entry.ts +1 -0
  152. package/src/testing/flight.entry.ts +1 -0
  153. package/src/testing/index.ts +3 -0
  154. package/src/testing/internal/context.ts +2 -1
  155. package/src/testing/internal/seed-vars.ts +14 -2
  156. package/src/testing/location-state-key.ts +18 -8
  157. package/src/testing/render-handler.ts +2 -1
  158. package/src/testing/render-route.tsx +277 -43
  159. package/src/testing/serve-shell-request.ts +80 -11
  160. package/src/testing/vitest-stubs/version.ts +4 -1
  161. package/src/types/handler-context.ts +8 -5
  162. package/src/vite/discovery/build-versions.ts +1281 -0
  163. package/src/vite/discovery/installed-packages.ts +159 -0
  164. package/src/vite/discovery/prerender-collection.ts +38 -1
  165. package/src/vite/discovery/router-versions-phase.ts +336 -0
  166. package/src/vite/discovery/shell-prerender-phase.ts +28 -38
  167. package/src/vite/discovery/state.ts +19 -0
  168. package/src/vite/discovery/virtual-module-codegen.ts +7 -2
  169. package/src/vite/encryption-key.ts +101 -9
  170. package/src/vite/plugin-types.ts +48 -6
  171. package/src/vite/plugins/{version-injector.ts → entry-bootstrap-injector.ts} +7 -22
  172. package/src/vite/plugins/expose-ids/export-analysis.ts +1 -1
  173. package/src/vite/plugins/expose-ids/router-transform.ts +36 -15
  174. package/src/vite/plugins/expose-internal-ids.ts +12 -3
  175. package/src/vite/plugins/stable-rsc-output.ts +147 -0
  176. package/src/vite/plugins/version-plugin.ts +19 -24
  177. package/src/vite/plugins/version.d.ts +17 -2
  178. package/src/vite/plugins/virtual-entries.ts +40 -7
  179. package/src/vite/rango.ts +30 -3
  180. package/src/vite/router-discovery.ts +82 -13
  181. package/src/vite/utils/client-chunks.ts +84 -14
  182. package/src/vite/utils/compare-strings.ts +9 -0
  183. package/src/vite/utils/package-resolution.ts +9 -0
  184. package/src/vite/utils/prerender-utils.ts +4 -1
  185. package/src/vite/utils/shared-utils.ts +1 -1
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Testing primitive for the per-router cache versions.
3
+ *
4
+ * A production build gives every `createRouter()` a data version and a
5
+ * document version (docs/design/per-app-cache-version.md) and ships them as a
6
+ * table in the `@rangojs/router:version` module. A test has no build, so the
7
+ * module is a stub and every router serves unversioned. `setBuildVersions`
8
+ * installs a table in its place; the request handler, `dispatch` and the cache
9
+ * stores then resolve versions through the same code path the built app uses.
10
+ * Calling it again is a deploy: the next request is served by a handler that
11
+ * resolved the new versions.
12
+ */
13
+ import { installRouterVersionsTable } from "../server/build-version-table.js";
14
+ import {
15
+ DEFAULT_ROUTER_VERSIONS_KEY,
16
+ type RouterVersions,
17
+ type RouterVersionsTable,
18
+ } from "../router-versions.js";
19
+
20
+ export type { RouterVersions };
21
+
22
+ /**
23
+ * The versions a simulated build ships.
24
+ *
25
+ * - `data` and `document`: the versions of every router the test does not list
26
+ * under `routers` (the build's whole-build pair).
27
+ * - `routers`: versions per router id (`router.id`), as a host-router build
28
+ * computes them.
29
+ */
30
+ export interface BuildVersions extends RouterVersions {
31
+ routers?: Record<string, RouterVersions>;
32
+ }
33
+
34
+ /**
35
+ * Install the cache versions of a simulated build, or remove them with no
36
+ * argument (routers serve unversioned again, the default in tests).
37
+ *
38
+ * A router that sets `createRouter({ version })` keeps that value for both
39
+ * versions, exactly as in production.
40
+ *
41
+ * @example A deploy that changes the client only keeps cached data
42
+ * ```ts
43
+ * setBuildVersions({ data: "d1", document: "h1" });
44
+ * await serveShellRequest(router, "/product/1", { cacheStore });
45
+ * setBuildVersions({ data: "d1", document: "h2" });
46
+ * const { shellStatus } = await serveShellRequest(router, "/product/1", {
47
+ * cacheStore,
48
+ * });
49
+ * // shellStatus is "MISS": the stored HTML was keyed by document version h1.
50
+ * ```
51
+ */
52
+ export function setBuildVersions(versions?: BuildVersions): void {
53
+ if (!versions) {
54
+ installRouterVersionsTable(undefined);
55
+ return;
56
+ }
57
+ const table: Record<string, readonly [string, string]> = {
58
+ [DEFAULT_ROUTER_VERSIONS_KEY]: [versions.data, versions.document],
59
+ };
60
+ for (const [routerId, pair] of Object.entries(versions.routers ?? {})) {
61
+ table[routerId] = [pair.data, pair.document];
62
+ }
63
+ installRouterVersionsTable(table satisfies RouterVersionsTable);
64
+ }
@@ -106,6 +106,7 @@ import {
106
106
  runWithRequestContext,
107
107
  setRequestContextParams,
108
108
  } from "../server/request-context.js";
109
+ import { resolveRouterVersions } from "../server/build-version-table.js";
109
110
  import { executeMiddleware, matchMiddleware } from "../router/middleware.js";
110
111
  import type {
111
112
  MiddlewareEntry,
@@ -166,6 +167,8 @@ import { applyStreamIdleTimeout } from "../rsc/stream-idle.js";
166
167
  interface DispatchableRouter<TEnv> {
167
168
  id?: string;
168
169
  routerId?: string;
170
+ /** createRouter({ version }), the consumer-set version. */
171
+ version?: string;
169
172
  routeMap: Record<string, unknown>;
170
173
  middleware: MiddlewareEntry<TEnv>[];
171
174
  onError?: OnErrorCallback<TEnv>;
@@ -411,6 +414,9 @@ export async function dispatch<TEnv = any>(
411
414
  cacheStore,
412
415
  searchParamsFilter,
413
416
  cacheProfiles: router.cacheProfiles,
417
+ // The store keys with the router's versions, resolved as the production
418
+ // handler resolves them (rsc/handler.ts).
419
+ versions: resolveRouterVersions(routerId, router.version),
414
420
  });
415
421
  // Wire background error reporting so cache degradation (reportCacheError ->
416
422
  // _reportBackgroundError) reaches the router's onError, mirroring the production
@@ -18,5 +18,6 @@ export type {
18
18
  RenderRouteOptions,
19
19
  TestRouterHandle,
20
20
  RenderRouteResult,
21
+ RenderRouteHydrateResult,
21
22
  HandleDataSeed,
22
23
  } from "./render-route.js";
@@ -63,4 +63,5 @@ export {
63
63
  export type {
64
64
  ServeShellRequestOptions,
65
65
  ServeShellRequestResult,
66
+ ShellRequestHandles,
66
67
  } from "./serve-shell-request.js";
@@ -98,6 +98,9 @@ export type {
98
98
  ShellStatusTarget,
99
99
  } from "./shell-status.js";
100
100
 
101
+ export { setBuildVersions } from "./build-versions.js";
102
+ export type { BuildVersions, RouterVersions } from "./build-versions.js";
103
+
101
104
  export { collectHandle } from "./collect-handle.js";
102
105
 
103
106
  export { withLocationStateKey } from "./location-state-key.js";
@@ -19,6 +19,7 @@ import { normalizeBasename } from "../../router/basename.js";
19
19
  import {
20
20
  seedVariables,
21
21
  resolveSeededStateCookieName,
22
+ seededVersions,
22
23
  type VarsInit,
23
24
  type StateCookieSeed,
24
25
  } from "./seed-vars.js";
@@ -164,7 +165,7 @@ export function createTestRequestContext<TEnv>(
164
165
  cacheStore: opts.cacheStore,
165
166
  cacheProfiles: opts.cacheProfiles,
166
167
  stateCookieName,
167
- version: opts.stateCookie?.version,
168
+ versions: seededVersions(opts.stateCookie),
168
169
  });
169
170
  if (opts.basename !== undefined)
170
171
  ctx._basename = normalizeBasename(opts.basename);
@@ -8,6 +8,7 @@
8
8
  */
9
9
  import { contextSet, type ContextVar } from "../../context-var.js";
10
10
  import { resolveStateCookieName } from "../../router/state-cookie-name.js";
11
+ import { uniformVersions, type RouterVersions } from "../../router-versions.js";
11
12
 
12
13
  export interface StateCookieSeed {
13
14
  /**
@@ -22,8 +23,10 @@ export interface StateCookieSeed {
22
23
  */
23
24
  routerId?: string;
24
25
  /**
25
- * Build version used as the rotated value's prefix (`{version}:{timestamp}`).
26
- * Defaults to `"0"` (resolved inside createRequestContext).
26
+ * The app's version, as `createRouter({ version })` would set it: the
27
+ * rotated value's prefix (`{version}:{timestamp}`) and the version a cache
28
+ * store keys with. Defaults to `"0"` for the prefix (resolved inside
29
+ * createRequestContext) and to the build versions for the store.
27
30
  */
28
31
  version?: string;
29
32
  }
@@ -32,6 +35,15 @@ export function resolveSeededStateCookieName(seed?: StateCookieSeed): string {
32
35
  return resolveStateCookieName(seed?.prefix, seed?.routerId ?? "router_0");
33
36
  }
34
37
 
38
+ /** The request context's `versions` for a seeded `stateCookie.version`. */
39
+ export function seededVersions(
40
+ seed?: StateCookieSeed,
41
+ ): RouterVersions | undefined {
42
+ return seed?.version === undefined
43
+ ? undefined
44
+ : uniformVersions(seed.version);
45
+ }
46
+
35
47
  export type VarsInit =
36
48
  | Record<string, unknown>
37
49
  | ReadonlyArray<readonly [ContextVar<unknown> | string, unknown]>;
@@ -1,12 +1,11 @@
1
1
  import {
2
+ isLocationStateKey,
3
+ LOCATION_STATE_KEY_PREFIX,
4
+ LOCATION_STATE_KEY_SUFFIX_SEPARATOR,
2
5
  peekLocationStateKey,
3
6
  type LocationStateDefinition,
4
7
  } from "../browser/react/location-state-shared.js";
5
8
 
6
- // Typed history.state slots must carry this prefix: buildHistoryState spreads
7
- // only `__rsc_ls_*` keys onto history.state (history-state.ts).
8
- const KEY_PREFIX = "__rsc_ls_";
9
-
10
9
  let syntheticKeyCounter = 0;
11
10
 
12
11
  /**
@@ -16,11 +15,15 @@ let syntheticKeyCounter = 0;
16
15
  * use (`MyState(value)`, `useLocationState(MyState)`, `.read()`).
17
16
  *
18
17
  * - `withLocationStateKey(GridState, "GridState")` sets `__rsc_ls_GridState`
19
- * (a name that already starts with `__rsc_ls_` is used as-is).
18
+ * (a name that already starts with `__rsc_ls_` is used as-is). A definition
19
+ * with `clearOnReload` appends its suffix, so read the stored key back from
20
+ * `GridState.__rsc_ls_key` (`__rsc_ls_GridState~r`).
20
21
  * - Without a name, a key already set (by the plugin or an earlier call) is
21
22
  * kept; otherwise the definition gets a synthetic `__rsc_ls_test_<n>` key,
22
23
  * which then stays on it.
23
24
  *
25
+ * A name may not contain "~": that separator is reserved for the suffix.
26
+ *
24
27
  * Returns the definition.
25
28
  *
26
29
  * @example
@@ -35,11 +38,18 @@ export function withLocationStateKey<
35
38
  TDefinition extends LocationStateDefinition<any, any>,
36
39
  >(definition: TDefinition, name?: string): TDefinition {
37
40
  if (name !== undefined) {
38
- definition.__rsc_ls_key = name.startsWith(KEY_PREFIX)
41
+ if (name.includes(LOCATION_STATE_KEY_SUFFIX_SEPARATOR)) {
42
+ throw new Error(
43
+ `withLocationStateKey: "${name}" contains "${LOCATION_STATE_KEY_SUFFIX_SEPARATOR}", ` +
44
+ "which is reserved for the key suffix of the `clearOnReload` option. " +
45
+ "Pass the name without a suffix; the definition appends its own.",
46
+ );
47
+ }
48
+ definition.__rsc_ls_key = isLocationStateKey(name)
39
49
  ? name
40
- : KEY_PREFIX + name;
50
+ : LOCATION_STATE_KEY_PREFIX + name;
41
51
  } else if (!peekLocationStateKey(definition)) {
42
- definition.__rsc_ls_key = `${KEY_PREFIX}test_${syntheticKeyCounter++}`;
52
+ definition.__rsc_ls_key = `${LOCATION_STATE_KEY_PREFIX}test_${syntheticKeyCounter++}`;
43
53
  }
44
54
  return definition;
45
55
  }
@@ -37,6 +37,7 @@ import type { LoaderDefinition } from "../types.js";
37
37
  import {
38
38
  seedVariables,
39
39
  resolveSeededStateCookieName,
40
+ seededVersions,
40
41
  type VarsInit,
41
42
  type StateCookieSeed,
42
43
  } from "./internal/seed-vars.js";
@@ -253,7 +254,7 @@ export async function renderHandler<TEnv = any>(
253
254
  variables: seedVariables({}, opts.vars),
254
255
  build: opts.build,
255
256
  stateCookieName,
256
- version: opts.stateCookie?.version,
257
+ versions: seededVersions(opts.stateCookie),
257
258
  cacheStore: opts.cacheStore,
258
259
  cacheProfiles: opts.cacheProfiles,
259
260
  themeConfig: