@rangojs/router 0.34.1 → 0.35.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 (95) hide show
  1. package/README.md +1 -1
  2. package/dist/bin/{chunk-4HHFQSDC.js → chunk-3CQXDX3A.js} +5 -5
  3. package/dist/bin/{client-urls-projection-4Q5EQDAL.js → client-urls-projection-2GBDM5YK.js} +1 -1
  4. package/dist/bin/rango.js +1 -1
  5. package/dist/bin/{runtime-discovery-RY6NMQM3.js → runtime-discovery-PA3V3IFQ.js} +2 -2
  6. package/dist/bin/{temp-rsc-server-F7AHOPV4.js → temp-rsc-server-WDXEHDY7.js} +2 -2
  7. package/dist/types/cache/shell-snapshot.d.ts +1 -1
  8. package/dist/types/cache/types.d.ts +1 -1
  9. package/dist/types/client-urls/server-projection.d.ts +1 -1
  10. package/dist/types/client-urls/types.d.ts +6 -6
  11. package/dist/types/decode-loader-results.d.ts +1 -1
  12. package/dist/types/errors.d.ts +3 -3
  13. package/dist/types/handles/meta.d.ts +2 -2
  14. package/dist/types/loader-store.d.ts +1 -1
  15. package/dist/types/outlet-context.d.ts +1 -1
  16. package/dist/types/route-definition/helpers-types.d.ts +5 -5
  17. package/dist/types/router/loader-resolution.d.ts +1 -1
  18. package/dist/types/router/segment-resolution/loader-cache.d.ts +7 -7
  19. package/dist/types/router/types.d.ts +1 -1
  20. package/dist/types/rsc/shell-capture-constants.d.ts +2 -2
  21. package/dist/types/server/context.d.ts +3 -3
  22. package/dist/types/server/request-context.d.ts +1 -1
  23. package/dist/types/ssr/index.d.ts +1 -1
  24. package/dist/types/ssr/ssr-root.d.ts +1 -1
  25. package/dist/types/ssr-suspension-warning.d.ts +1 -1
  26. package/dist/types/types/loader-types.d.ts +19 -19
  27. package/dist/types/types/segments.d.ts +2 -2
  28. package/dist/types/urls/path-helper-types.d.ts +4 -4
  29. package/dist/types/urls/pattern-types.d.ts +1 -1
  30. package/dist/types/vite/plugin-types.d.ts +1 -1
  31. package/dist/vite/index.js +7 -7
  32. package/package.json +1 -1
  33. package/skills/app-structure/e-commerce.md +1 -1
  34. package/skills/breadcrumbs/SKILL.md +5 -5
  35. package/skills/cache-guide/SKILL.md +4 -4
  36. package/skills/caching/SKILL.md +1 -1
  37. package/skills/catalog.json +2 -2
  38. package/skills/client-urls/SKILL.md +20 -20
  39. package/skills/comparison/references/framework-comparison.md +6 -6
  40. package/skills/defer-hydration/SKILL.md +2 -2
  41. package/skills/handler-use/SKILL.md +5 -5
  42. package/skills/hooks/data.md +1 -1
  43. package/skills/hooks/handle-and-actions.md +9 -9
  44. package/skills/loader/SKILL.md +52 -47
  45. package/skills/middleware/SKILL.md +2 -2
  46. package/skills/migrate-nextjs/SKILL.md +10 -10
  47. package/skills/migrate-react-router/SKILL.md +1 -1
  48. package/skills/migrate-react-router/component-migration.md +2 -2
  49. package/skills/migrate-react-router/data-and-actions.md +2 -2
  50. package/skills/migrate-react-router/route-mapping.md +2 -2
  51. package/skills/parallel/SKILL.md +2 -2
  52. package/skills/ppr/SKILL.md +91 -92
  53. package/skills/prerender/SKILL.md +5 -5
  54. package/skills/rango/SKILL.md +7 -7
  55. package/skills/route/SKILL.md +1 -1
  56. package/skills/router-setup/SKILL.md +2 -2
  57. package/skills/scripts/SKILL.md +3 -3
  58. package/skills/shell-manifest/SKILL.md +3 -3
  59. package/skills/testing/serve-shell-request.md +1 -1
  60. package/skills/theme/SKILL.md +2 -2
  61. package/skills/typesafety/env-and-bindings.md +2 -2
  62. package/skills/use-cache/SKILL.md +1 -1
  63. package/skills/ux-architecture/SKILL.md +7 -7
  64. package/src/cache/shell-snapshot.ts +1 -1
  65. package/src/cache/types.ts +1 -1
  66. package/src/client-urls/client-urls.ts +16 -9
  67. package/src/client-urls/server-projection.ts +4 -4
  68. package/src/client-urls/types.ts +6 -6
  69. package/src/decode-loader-results.ts +1 -1
  70. package/src/errors.ts +9 -9
  71. package/src/handles/meta.ts +2 -2
  72. package/src/loader-store.ts +1 -1
  73. package/src/outlet-context.ts +1 -1
  74. package/src/route-definition/dsl-helpers.ts +4 -8
  75. package/src/route-definition/helpers-types.ts +5 -5
  76. package/src/router/loader-resolution.ts +4 -4
  77. package/src/router/segment-resolution/loader-cache.ts +8 -8
  78. package/src/router/segment-resolution/prefetch-deferral.ts +1 -1
  79. package/src/router/segment-resolution/revalidation.ts +3 -3
  80. package/src/router/types.ts +1 -1
  81. package/src/rsc/shell-capture-constants.ts +3 -3
  82. package/src/rsc/shell-capture.ts +12 -12
  83. package/src/segment-system.tsx +3 -3
  84. package/src/server/context.ts +5 -5
  85. package/src/server/cookie-store.ts +1 -1
  86. package/src/server/request-context.ts +1 -1
  87. package/src/ssr/index.tsx +6 -6
  88. package/src/ssr/ssr-root.tsx +1 -1
  89. package/src/ssr-suspension-warning.ts +4 -4
  90. package/src/types/loader-types.ts +19 -19
  91. package/src/types/segments.ts +2 -2
  92. package/src/urls/path-helper-types.ts +4 -4
  93. package/src/urls/pattern-types.ts +1 -1
  94. package/src/vite/discovery/shell-prerender-phase.ts +1 -1
  95. package/src/vite/plugin-types.ts +1 -1
package/README.md CHANGED
@@ -304,7 +304,7 @@ Loaders run in parallel with the handler and stream their results.
304
304
  into it. Without `loading()`, the segment has no skeleton, and the HTML
305
305
  normally arrives with the data already in place. To guarantee that a
306
306
  loader's data, head tags, or a real 404 are in the first HTML flush, register
307
- it with `loader(StockLoader, { ssr: false })`.
307
+ it with `loader(StockLoader, { ssr: "await" })`.
308
308
 
309
309
  When a loader's data changes, every component that reads it updates too.
310
310
  Calling `load()` from `useLoader()` refetches it on the client, and
@@ -23,7 +23,7 @@ import * as React4 from "react";
23
23
  import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
24
24
 
25
25
  // src/rsc/shell-capture-constants.ts
26
- var PPR_LANE_HINT = 'Lane rule: only loader(Def, { ssr: false }) executes at capture and bakes its non-promise data; every other loader is live (masked at capture, fresh per request) and needs loading() or an inline <Suspense> above its reader. See the /ppr skill, "The loader lane rule" (node_modules/@rangojs/router/skills/ppr/SKILL.md).';
26
+ var PPR_LANE_HINT = 'Lane rule: only loader(Def, { ssr: "await" }) executes at capture and bakes its non-promise data; every other loader is live (masked at capture, fresh per request) and needs loading() or an inline <Suspense> above its reader. See the /ppr skill, "The loader lane rule" (node_modules/@rangojs/router/skills/ppr/SKILL.md).';
27
27
 
28
28
  // src/cache/cache-exec-scope.ts
29
29
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -52,8 +52,8 @@ var LOADER_BODY_SCOPE_KEY = /* @__PURE__ */ Symbol.for("rangojs-router:loader-bo
52
52
  var loaderBodyScopeALS = globalThis[LOADER_BODY_SCOPE_KEY] ??= new AsyncLocalStorage2();
53
53
  function requestReadCaptureFix(what) {
54
54
  return {
55
- capture: `The captured shell is served to every user of this URL, so request-scoped data read here would leak one user's ${what} to others. Read it inside a loader without ssr: false and consume it with useLoader, e.g. createLoader(async () => getUser(cookies().get("session")?.value)). ${PPR_LANE_HINT}`,
56
- warning: "Read it in a loader without ssr: false and consume it with useLoader under loading() or an inline <Suspense> (a live hole). A promise the handler passes or pushes does not help: the capture waits for it."
55
+ capture: `The captured shell is served to every user of this URL, so request-scoped data read here would leak one user's ${what} to others. Read it inside a loader without ssr: "await" and consume it with useLoader, e.g. createLoader(async () => getUser(cookies().get("session")?.value)). ${PPR_LANE_HINT}`,
56
+ warning: 'Read it in a loader without ssr: "await" and consume it with useLoader under loading() or an inline <Suspense> (a live hole). A promise the handler passes or pushes does not help: the capture waits for it.'
57
57
  };
58
58
  }
59
59
  var NON_CACHEABLE_READ = {
@@ -1149,7 +1149,7 @@ var RAW_COOKIES_READ = {
1149
1149
  verb: "called",
1150
1150
  fix: COOKIES_READ.fix
1151
1151
  };
1152
- var THEME_READ_FIX = "On ppr and cache() routes, read the theme with useTheme() in a client component, or in a live loader (no ssr: false) with cookies().get(<storageKey>). The <html> theme class needs neither: the theme script sets it before paint. See the /theme skill (node_modules/@rangojs/router/skills/theme/SKILL.md).";
1152
+ var THEME_READ_FIX = 'On ppr and cache() routes, read the theme with useTheme() in a client component, or in a live loader (no ssr: "await") with cookies().get(<storageKey>). The <html> theme class needs neither: the theme script sets it before paint. See the /theme skill (node_modules/@rangojs/router/skills/theme/SKILL.md).';
1153
1153
  var THEME_READ = {
1154
1154
  verb: "read",
1155
1155
  fix: {
@@ -1348,7 +1348,7 @@ function serializeRoute(route2, options, ordinals) {
1348
1348
  });
1349
1349
  const awaitedLoaderIndices = indicesWhere(
1350
1350
  route2.loaders,
1351
- ({ ssr }) => ssr === false
1351
+ ({ ssr }) => ssr === "await"
1352
1352
  );
1353
1353
  const transition3 = serializeTransition(route2);
1354
1354
  return Object.freeze({
@@ -10,7 +10,7 @@ import {
10
10
  refreshRecordedClientUrlProjections,
11
11
  resolveClientUrlsSource,
12
12
  scanForClientUrlModules
13
- } from "./chunk-4HHFQSDC.js";
13
+ } from "./chunk-3CQXDX3A.js";
14
14
  import "./chunk-MWOHBDPM.js";
15
15
  import "./chunk-LD7UBHT4.js";
16
16
  export {
package/dist/bin/rango.js CHANGED
@@ -229,7 +229,7 @@ ${formatNestedRouterConflictError(nestedRouterConflict, "[rango]")}
229
229
  }
230
230
  let discoverAndWriteRouteTypes;
231
231
  try {
232
- const mod = await import("./runtime-discovery-RY6NMQM3.js");
232
+ const mod = await import("./runtime-discovery-PA3V3IFQ.js");
233
233
  discoverAndWriteRouteTypes = mod.discoverAndWriteRouteTypes;
234
234
  } catch (err) {
235
235
  if (err.code === "ERR_MODULE_NOT_FOUND" || err.code === "MODULE_NOT_FOUND") {
@@ -163,9 +163,9 @@ async function discoverAndWriteRouteTypes(opts) {
163
163
  "Runtime discovery requires '@vitejs/plugin-rsc'. Install it with: pnpm add -D @vitejs/plugin-rsc"
164
164
  );
165
165
  }
166
- const { createTempRscServer } = await import("./temp-rsc-server-F7AHOPV4.js");
166
+ const { createTempRscServer } = await import("./temp-rsc-server-WDXEHDY7.js");
167
167
  const { createDiscoveryState } = await import("./state-ACD2MGKJ.js");
168
- const { discoverClientUrlProjections } = await import("./client-urls-projection-4Q5EQDAL.js");
168
+ const { discoverClientUrlProjections } = await import("./client-urls-projection-2GBDM5YK.js");
169
169
  const {
170
170
  isFrameworkPlugin,
171
171
  pickForwardedRunnerConfig,
@@ -27,7 +27,7 @@ import {
27
27
  propertyKeyIs,
28
28
  recordClientUrlsModule,
29
29
  unwrapExpression
30
- } from "./chunk-4HHFQSDC.js";
30
+ } from "./chunk-3CQXDX3A.js";
31
31
  import "./chunk-MWOHBDPM.js";
32
32
  import {
33
33
  codeMatchIndices,
@@ -47,7 +47,7 @@ var DEFAULT_ROUTER_VERSIONS_KEY = "*";
47
47
  // package.json
48
48
  var package_default = {
49
49
  name: "@rangojs/router",
50
- version: "0.34.1",
50
+ version: "0.35.0",
51
51
  description: "Django-inspired RSC router with composable URL patterns",
52
52
  keywords: [
53
53
  "react",
@@ -5,7 +5,7 @@
5
5
  * that must agree with them byte for byte. The handler layer's part of that
6
6
  * payload is the capture's own doc segment record: the capture writes it
7
7
  * first, renders its prelude from it, and every HIT replays it (no handler
8
- * runs on a HIT). The loader layer's part is the bake-lane (`ssr: false`)
8
+ * runs on a HIT). The loader layer's part is the bake-lane (`ssr: "await"`)
9
9
  * loader pins: a promise-free bake-lane loader is served from its pin and
10
10
  * does not run; one whose return carries promises (`holes`), or whose pushes
11
11
  * the record could not keep (`runs`), runs on the HIT. See
@@ -770,7 +770,7 @@ export type SegmentHandleData = Record<string, unknown[]>;
770
770
  /**
771
771
  * segmentId -> handleName -> the owning loader id of each recorded value, by
772
772
  * index (null: not owned). Only a PPR shell capture writes owners: the
773
- * settled pushes of the loader bodies it ran (an `ssr: false` loader's own,
773
+ * settled pushes of the loader bodies it ran (an `ssr: "await"` loader's own,
774
774
  * the loaders it awaits, and its own cache() replays), which the record keeps
775
775
  * because the prelude rendered them. A restore of the record
776
776
  * (handle-snapshot.ts restoreHandles) keeps an owner's value where the
@@ -28,7 +28,7 @@ export interface ClientUrlProjectionRoute {
28
28
  readonly options: ClientUrlProjectionOptions;
29
29
  readonly loaderIds: readonly string[];
30
30
  readonly hasLoading: boolean;
31
- /** Indices into loaderIds of loaders declared loader(Def, { ssr: false });
31
+ /** Indices into loaderIds of loaders declared loader(Def, { ssr: "await" });
32
32
  * materialization passes the option through to the server loader() so
33
33
  * document renders await them before first flush. Absent (= none) in
34
34
  * projections serialized before the option existed. */
@@ -42,12 +42,12 @@ export interface ClientUrlLoaderRecord {
42
42
  */
43
43
  readonly revalidate: readonly (ShouldRevalidateFn<any, DefaultEnv> | HoistedRevalidateRef | boolean)[];
44
44
  /**
45
- * loader(Def, { ssr: false }): document renders await this loader before
45
+ * loader(Def, { ssr: "await" }): document renders await this loader before
46
46
  * first flush (see {@link LoaderOptions}). Projected into the server tree,
47
47
  * where the per-isSSR entry stamping applies — client navigations stream
48
48
  * regardless.
49
49
  */
50
- readonly ssr?: false;
50
+ readonly ssr?: "await";
51
51
  /**
52
52
  * loader(Def, { prefetch: false | "intent" }): a prefetch of the route, or
53
53
  * of an intercept's target from its source, defers this loader and the
@@ -109,10 +109,10 @@ export interface ClientUrlHelpers {
109
109
  * Attach a projected loader. The optional use callback may contain
110
110
  * revalidate() only: a per-loader predicate the server runs, as in urls().
111
111
  *
112
- * Pass `{ ssr: false }` — the same knob as loading(fallback, { ssr:
113
- * false }) — to await this loader before first flush on DOCUMENT requests
114
- * (see {@link LoaderOptions}): the opt-in for loaders whose data, handle
115
- * pushes, or thrown notFound()/redirect() must be in the SSR'd HTML.
112
+ * Pass `{ ssr: "await" }` to await this loader before first flush on
113
+ * DOCUMENT requests (see {@link LoaderOptions}): the opt-in for loaders
114
+ * whose data, handle pushes, or thrown notFound()/redirect() must be in the
115
+ * SSR'd HTML.
116
116
  * Per-loader: a streaming sibling keeps streaming. Under a `ppr` group
117
117
  * route the flag BAKES: the loader executes at shell capture and its
118
118
  * settled return freezes into the shell (nested promises stay live
@@ -18,7 +18,7 @@ import { type ErrorInfo, type ResolvedSegment } from "./types.js";
18
18
  * decodeLoaderResults runs during the tree build on settled results, where a
19
19
  * throw would collapse the whole payload — it routes signals through the
20
20
  * errorFallback slot instead (same visual: the slot replaces the children
21
- * under OutletProvider). The document lane's `{ ssr: false }` results take
21
+ * under OutletProvider). The document lane's `{ ssr: "await" }` results take
22
22
  * this route (segment-system buildLoaderStreams): they are settled before the
23
23
  * tree is built, and the read-site throw would land in the Fizz shell with no
24
24
  * boundary to absorb it.
@@ -243,10 +243,10 @@ export declare class InvalidHandlerError extends Error {
243
243
  */
244
244
  export declare function invariant(condition: any, message: string): asserts condition;
245
245
  /**
246
- * Rejects a boolean-only DSL option that holds anything else (undefined
247
- * passes), as an invariant.
246
+ * Rejects a loader `ssr` option other than `"stream"` or `"await"`
247
+ * (undefined passes). clientUrls() inlines the same checks.
248
248
  */
249
- export declare function assertBooleanOption(value: unknown, option: string, effect: string): void;
249
+ export declare function assertLoaderSsrOption(value: unknown, helper: string): void;
250
250
  /**
251
251
  * Rejects a `prefetch` DSL option that is neither a boolean nor `"intent"`
252
252
  * (undefined passes), as an invariant.
@@ -40,9 +40,9 @@ import type { MetaDescriptor } from "../router/types.js";
40
40
  * title-templating identically to synchronous descriptors. A descriptor that
41
41
  * resolves to `null`/`undefined` (or rejects) is dropped.
42
42
  *
43
- * On a ppr route, a descriptor a handler or an `ssr: false` loader pushes,
43
+ * On a ppr route, a descriptor a handler or an `ssr: "await"` loader pushes,
44
44
  * promise or not, is in the shell's `<head>` on every HIT (#1182). One a
45
- * loader without `ssr: false` pushes is live: on a HIT it arrives after
45
+ * loader without `ssr: "await"` pushes is live: on a HIT it arrives after
46
46
  * hydration.
47
47
  */
48
48
  export declare const Meta: Handle<MetaDescriptor, MetaDescriptor[]>;
@@ -233,7 +233,7 @@ export declare class LoaderStore {
233
233
  /**
234
234
  * Register a still-streaming per-loader result for `loaderId`. No-op for a
235
235
  * settled Flight chunk (unwrapsSynchronously / "rejected"): the forceAwait
236
- * and action lanes commit settled chunks, and a { ssr: false } loader is
236
+ * and action lanes commit settled chunks, and a { ssr: "await" } loader is
237
237
  * awaited before flush. Released as the stream settles, either way.
238
238
  */
239
239
  trackPendingStream(loaderId: string, stream: Promise<unknown>): void;
@@ -19,7 +19,7 @@ export interface OutletContextValue {
19
19
  loaderStreams?: Record<string, unknown>;
20
20
  /**
21
21
  * Loader $$ids on this segment that the render awaited before first flush
22
- * (loader(Def, { ssr: false })). Dev-diagnostic input only: useLoader uses
22
+ * (loader(Def, { ssr: "await" })). Dev-diagnostic input only: useLoader uses
23
23
  * it to warn when a read still suspends during SSR on a render that awaited
24
24
  * flagged loaders (ssr-suspension-warning.ts). Present only where a
25
25
  * segment carries flagged loaders (document and shell-capture renders, and
@@ -221,14 +221,14 @@ export type RouteHelpers<T extends RouteDefinition, TEnv> = {
221
221
  * return <div>{data.name}</div>;
222
222
  * }
223
223
  * ```
224
- * Pass `{ ssr: false }` — the same knob as loading(fallback, { ssr:
225
- * false }) — to await this loader before first flush on DOCUMENT requests
226
- * (see {@link LoaderOptions}): the opt-in for loaders whose data, handle
227
- * pushes, or thrown notFound()/redirect() must be in the SSR'd HTML.
224
+ * Pass `{ ssr: "await" }` to await this loader before first flush on
225
+ * DOCUMENT requests (see {@link LoaderOptions}): the opt-in for loaders
226
+ * whose data, handle pushes, or thrown notFound()/redirect() must be in the
227
+ * SSR'd HTML.
228
228
  * Per-loader: a streaming sibling in the same segment keeps streaming.
229
229
  *
230
230
  * ```typescript
231
- * loader(ProductLoader, { ssr: false }, () => [cache()]),
231
+ * loader(ProductLoader, { ssr: "await" }, () => [cache()]),
232
232
  * loader(RecommendationsLoader), // still streams behind loading()
233
233
  * ```
234
234
  *
@@ -44,7 +44,7 @@ export type LoaderErrorCallback = (error: unknown, context: {
44
44
  * This has a simplified signature for internal use - the caller (typically
45
45
  * wrapLoaderPromise in router.ts) is responsible for bridging to the full
46
46
  * OnErrorCallback with complete request context (request, url, env, etc.).
47
- * @param useDefaultErrorBoundary - An `ssr: false` loader on a document or
47
+ * @param useDefaultErrorBoundary - An `ssr: "await"` loader on a document or
48
48
  * capture render: an error with no declared boundary gets
49
49
  * `DefaultErrorFallback` (it would otherwise reject the tree), and every
50
50
  * error answers 500.
@@ -56,14 +56,14 @@ import { type RequestContext } from "../../server/request-context.js";
56
56
  export declare function bindsLoaderCache(entries: readonly EntryData[]): boolean;
57
57
  /**
58
58
  * The lane of each loader the matched chain `entries` registers: "live" (no
59
- * `ssr: false`) or "bake". A loader registered on both is "live". A PPR
59
+ * `ssr: "await"`) or "bake". A loader registered on both is "live". A PPR
60
60
  * capture credits a push to the first registered loader around it when that
61
61
  * one is live (shell-capture.ts deriveShellCaptureContext).
62
62
  */
63
63
  export declare function routeLoaderLanes(entries: readonly EntryData[]): ReadonlyMap<string, "live" | "bake">;
64
64
  /**
65
65
  * This request serves `loaderEntry` from a pin (servedPins), so its body does
66
- * not decide the value: resolveLoaderData serves a pin to an `ssr: false`
66
+ * not decide the value: resolveLoaderData serves a pin to an `ssr: "await"`
67
67
  * registration only. A prefetch does not defer such a loader
68
68
  * (prefetch-deferral.ts defersLoader): nothing would run, and the pin is the
69
69
  * value the document shows.
@@ -77,14 +77,14 @@ export declare function servesLoaderPin(loaderEntry: LoaderEntry, reqCtx?: Reque
77
77
  * only and the store asks while loader bodies run.
78
78
  *
79
79
  * Each side applies its own lane to the pin. The value is per registration
80
- * (resolveLoaderData: an `ssr: false` registration is served its pin). The
81
- * pushes are per loader: one the route also registers without `ssr: false`
80
+ * (resolveLoaderData: an `ssr: "await"` registration is served its pin). The
81
+ * pushes are per loader: one the route also registers without `ssr: "await"`
82
82
  * is a hole whatever its other registration's pin says, because the capture
83
83
  * credited its pushes to the live registration's run (routeLoaderLanes).
84
84
  *
85
85
  * A dependency the route does not register has no pin of its own, and the
86
86
  * record does not name the loader that ran it at capture: its copies stand
87
- * only while every `ssr: false` loader is pinned (exact when the pins are
87
+ * only while every `ssr: "await"` loader is pinned (exact when the pins are
88
88
  * all there or all gone).
89
89
  */
90
90
  export declare function loaderPins(entries: readonly EntryData[], reqCtx: RequestContext<any> | undefined): OwnedPushDelivery;
@@ -108,7 +108,7 @@ export declare function loaderPins(entries: readonly EntryData[], reqCtx: Reques
108
108
  * never run under a shell capture), so the lane is decided here, per LOADER,
109
109
  * never by the entry's loading():
110
110
  *
111
- * - BAKE lane: `loader(Def, { ssr: false })` (LoaderEntry `bake`: the
111
+ * - BAKE lane: `loader(Def, { ssr: "await" })` (LoaderEntry `bake`: the
112
112
  * caller passes a `bakeSegmentKey` for it on document and navigation
113
113
  * evaluations alike; a capture, a document evaluation, also has
114
114
  * awaitBeforeFlush). The loader EXECUTES at capture (the
@@ -130,7 +130,7 @@ export declare function loaderPins(entries: readonly EntryData[], reqCtx: Reques
130
130
  * (loader-mask.ts) and postpones at the reader's boundary — loading() or an
131
131
  * inline Suspense — then streams fresh per request. A masked read with NO
132
132
  * boundary above it root-postpones and the <body> sanity gate refuses the
133
- * shell (eternal-MISS warning). A route whose loaders are ALL `ssr: false`
133
+ * shell (eternal-MISS warning). A route whose loaders are ALL `ssr: "await"`
134
134
  * needs no loading(): nothing masks.
135
135
  */
136
136
  export declare function resolveLoaderData<TEnv>(loaderEntry: LoaderEntry, ctx: HandlerContext<any, TEnv>, pathname: string, bakeSegmentKey?: string | null): Promise<any>;
@@ -35,7 +35,7 @@ export interface SegmentResolutionDeps<TEnv = any> {
35
35
  env?: TEnv;
36
36
  isPartial?: boolean;
37
37
  },
38
- /** An `ssr: false` loader on a document or capture render. */
38
+ /** An `ssr: "await"` loader on a document or capture render. */
39
39
  useDefaultErrorBoundary?: boolean) => Promise<LoaderDataResult<T>>;
40
40
  trackHandler: <T>(promise: Promise<T>, errorContext?: {
41
41
  segmentId?: string;
@@ -148,12 +148,12 @@ export declare const SHELL_CAPTURE_TASK_HARD_CAP_MS = 25000;
148
148
  * (ShellCaptureDebugEvent.refusal, `caches.refusal` on a `router.prerender()`
149
149
  * result): `identity` (a request-scoped read: cookies(), headers(), the
150
150
  * theme, a `{ cache: false }` variable), `dynamic` (`ctx.dynamic()`),
151
- * `loader` (a `{ ssr: false }` loader rejected, settled with
151
+ * `loader` (a `{ ssr: "await" }` loader rejected, settled with
152
152
  * redirect()/notFound(), or a promise it pushed rejected),
153
153
  * `no-record` (no doc record: the route's cache() refused the write, or the
154
154
  * handler output failed), `handles` (a pushed
155
155
  * handle value did not finish encoding), `size` (over the store's value
156
- * limit, or `ssr: false` loader pins over `ppr.maxSnapshotBytes`: a
156
+ * limit, or `ssr: "await"` loader pins over `ppr.maxSnapshotBytes`: a
157
157
  * navigation-only entry was stored in place of the document shell),
158
158
  * `record-expired` (the route cache() record
159
159
  * the capture wrote ran out first), `invalidated` (a tag of the shell was
@@ -119,7 +119,7 @@ export type LoaderEntry = {
119
119
  cache?: EntryCacheConfig;
120
120
  /**
121
121
  * Document renders await this loader before segment resolution returns
122
- * (loader(Def, { ssr: false })), so its data, handle pushes, and
122
+ * (loader(Def, { ssr: "await" })), so its data, handle pushes, and
123
123
  * thrown notFound()/redirect() deterministically precede first flush.
124
124
  * Resolved at DSL-evaluation time from ctx.isSSR — entries are cached
125
125
  * per-isSSR (router/manifest.ts cache key), so the flag is already
@@ -129,7 +129,7 @@ export type LoaderEntry = {
129
129
  */
130
130
  awaitBeforeFlush?: true;
131
131
  /**
132
- * loader(Def, { ssr: false }) on every DSL evaluation, document and
132
+ * loader(Def, { ssr: "await" }) on every DSL evaluation, document and
133
133
  * navigation: the loader's PPR bake lane (loader-cache.ts
134
134
  * resolveLoaderData). The bake/seed key rides on it, so a client
135
135
  * navigation's shell replay pins exactly the loaders a document HIT pins,
@@ -491,7 +491,7 @@ export type { HelperContext };
491
491
  * parent pointer, handler) stays shared, which is correct and cheap.
492
492
  */
493
493
  export declare function getIsolatedLazyParent(captured: EntryData | null | undefined): EntryData | null;
494
- /** loader(Def, { ssr: false }): a document awaits it before the first flush. */
494
+ /** loader(Def, { ssr: "await" }): a document awaits it before the first flush. */
495
495
  export declare function documentModeLoader(entry: LoaderEntry): LoaderEntry;
496
496
  /** loading(fallback, { ssr: false }): a document renders no fallback. */
497
497
  export declare function applyDocumentModeLoading(entry: EntryPropRender): void;
@@ -710,7 +710,7 @@ export interface RequestContext<TEnv = DefaultEnv, TParams = Record<string, stri
710
710
  _handlerLoaderDeps?: Set<string>;
711
711
  /**
712
712
  * @internal Loader IDs ($$id) whose entries carry `awaitBeforeFlush`
713
- * (loader(Def, { ssr: false })): segment resolution awaits these
713
+ * (loader(Def, { ssr: "await" })): segment resolution awaits these
714
714
  * before returning, so the render barrier cannot resolve until they settle.
715
715
  * rendered() checks this set to fail fast — a flagged loader awaiting the
716
716
  * barrier is a guaranteed cycle, not a race. Registered by resolveOwnerLoaders
@@ -171,7 +171,7 @@ export interface SSRDependencies<TEnv = unknown> {
171
171
  *
172
172
  * When UNSET, createSSRHandler and createShellCaptureHandler auto-raise to
173
173
  * Number.MAX_SAFE_INTEGER for document renders and shell captures whose
174
- * matched chain has a `loader(Def, { ssr: false })` entry (payload segments
174
+ * matched chain has a `loader(Def, { ssr: "await" })` entry (payload segments
175
175
  * carry `awaitBeforeFlush`): the loader was awaited before first flush
176
176
  * precisely so its content ships in-place, and React 19.2's Fizz would
177
177
  * otherwise move any completed boundary over ~500 bytes to an end-of-stream
@@ -30,7 +30,7 @@ export interface RscPayload {
30
30
  * Deserialize the Flight stream branch into the payload SsrRoot renders,
31
31
  * fragment expansion included. Factored out of SsrRoot so createSSRHandler and
32
32
  * createShellCaptureHandler can create the promise BEFORE fizz starts and read
33
- * metadata that must influence the fizz call itself (the ssr:false
33
+ * metadata that must influence the fizz call itself (the ssr: "await"
34
34
  * progressiveChunkSize auto-raise) — awaiting it there is latency-neutral
35
35
  * because fizz cannot emit even <html> until this same promise settles.
36
36
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Dev-only diagnostic for the { ssr: false } SSR-completeness opt-in.
2
+ * Dev-only diagnostic for the { ssr: "await" } SSR-completeness opt-in.
3
3
  *
4
4
  * A document render that awaits flagged loaders before first flush is usually
5
5
  * expected to produce an SSR-complete boundary — but the await is scoped per
@@ -63,7 +63,7 @@ export type LoaderContext<TParams = Record<string, string | undefined>, TEnv = D
63
63
  * later ones stream to the client and apply post-hydration (document lane)
64
64
  * or progressively (navigation/action lanes). To guarantee a loader's
65
65
  * handles are in the SSR'd document, register it as
66
- * `loader(Def, { ssr: false })` so the document render awaits it
66
+ * `loader(Def, { ssr: "await" })` so the document render awaits it
67
67
  * (see {@link LoaderOptions}).
68
68
  *
69
69
  * @example
@@ -150,29 +150,29 @@ export type LoaderFn<T, TParams = Record<string, string | undefined>, TEnv = Def
150
150
  /**
151
151
  * SSR delivery for a DSL-registered loader: `loader(Def, { ssr })`.
152
152
  *
153
- * Default (omitted / `true`): the loader streams on every render. Its data,
154
- * its `ctx.use(Handle)` pushes, and any `notFound()`/`redirect()` it throws
155
- * may land AFTER the document Response is constructed, so none of them are
156
- * guaranteed to be in the SSR'd HTML.
153
+ * Default (omitted / `"stream"`): the loader streams on every render. Its
154
+ * data, its `ctx.use(Handle)` pushes, and any `notFound()`/`redirect()` it
155
+ * throws may land AFTER the document Response is constructed, so none of
156
+ * them are guaranteed to be in the SSR'd HTML.
157
157
  *
158
- * `ssr: false` turns SSR streaming off for this loader — the same knob
159
- * `loading(fallback, { ssr: false })` is for the fallback: on a DOCUMENT
160
- * request the loader is awaited before first flush, so no fallback paints for
161
- * it (`useLoader` still suspends, but on an already-settled promise). Client
162
- * navigations keep streaming it. This is the SSR-completeness opt-in. Choose
163
- * it when the loader feeds something that must exist in the document:
164
- * `<head>` meta via a handle, or a real 404 status (an awaited `notFound()`
165
- * deterministically precedes Response construction, where the streamed
166
- * default only wins that race opportunistically). Under ppr it is also the
167
- * BAKE lane: the loader executes at shell capture and its settled return is
158
+ * `ssr: "await"` makes the server renders that produce HTML await this
159
+ * loader: on a DOCUMENT request the loader is awaited before first flush, so
160
+ * no fallback paints for it (`useLoader` still suspends, but on an
161
+ * already-settled promise); a ppr shell capture, at runtime or at build time
162
+ * for a Prerender + ppr route, awaits it too. Client navigations keep
163
+ * streaming it. This is the SSR-completeness opt-in. Choose it when the
164
+ * loader feeds something that must exist in the document: `<head>` meta via
165
+ * a handle, or a real 404 status (an awaited `notFound()` deterministically
166
+ * precedes Response construction, where the streamed default only wins
167
+ * that race opportunistically). Under ppr it is also the BAKE lane: the loader executes at shell capture and its settled return is
168
168
  * shell material (nested promises stay live holes), and so are its handle
169
169
  * pushes, a pushed promise or `.defer()` included: the capture awaits it and
170
170
  * every HIT serves the value it settled to (#1182). A pushed promise that
171
171
  * rejects, or misses `ppr.captureTimeout`, keeps the route on MISS. A value
172
- * that must stay per request belongs to a loader without `ssr: false` — see
172
+ * that must stay per request belongs to a loader without `ssr: "await"` — see
173
173
  * the ppr docs.
174
174
  *
175
- * Scoped per LOADER, not per segment: an `ssr: false` loader alongside a
175
+ * Scoped per LOADER, not per segment: an `ssr: "await"` loader alongside a
176
176
  * deliberately streaming sibling awaits only itself, and the sibling keeps
177
177
  * streaming behind its `loading()`/Suspense boundary. Watch the scoping when
178
178
  * the boundary must be SSR-complete: ONE unflagged read under it still
@@ -181,7 +181,7 @@ export type LoaderFn<T, TParams = Record<string, string | undefined>, TEnv = Def
181
181
  * site naming the unflagged loader (ssr-suspension-warning.ts).
182
182
  */
183
183
  export type LoaderOptions = {
184
- ssr?: boolean;
184
+ ssr?: "stream" | "await";
185
185
  /**
186
186
  * `prefetch: false` keeps this loader out of every `<Link>` prefetch: a
187
187
  * prefetch (a partial request with `X-Rango-Prefetch`) does not execute
@@ -211,7 +211,7 @@ export type LoaderOptions = {
211
211
  * Limits: a handler that awaits the loader with `ctx.use()` makes it run in
212
212
  * the prefetch anyway; the loader cannot call `ctx.rendered()` (it throws
213
213
  * on every request); on a `ppr` route the flag is ignored for an
214
- * `ssr: false` loader (the bake lane), with a dev warning. In the fill
214
+ * `ssr: "await"` loader (the bake lane), with a dev warning. In the fill
215
215
  * request only deferred work and middleware run, so values a held handler
216
216
  * would have set with `ctx.set()` are not visible.
217
217
  */
@@ -150,7 +150,7 @@ export interface ResolvedSegment {
150
150
  loaderData?: any;
151
151
  /**
152
152
  * True when this loader was awaited before first flush
153
- * (loader(Def, { ssr: false })). Stamped by resolveOwnerLoaders on
153
+ * (loader(Def, { ssr: "await" })). Stamped by resolveOwnerLoaders on
154
154
  * document AND shell-capture renders — capture bakes flagged loaders and
155
155
  * awaits the same lane. Feeds segment-system's settled-value delivery and
156
156
  * the dev SSR suspension warning (ssr-suspension-warning.ts).
@@ -170,7 +170,7 @@ export interface ResolvedSegment {
170
170
  loaderIds?: string[];
171
171
  /**
172
172
  * Per-loader UNDECODED results for a layout/route stream map.
173
- * Flagged (ssr:false) entries are settled values; unflagged siblings
173
+ * Flagged (ssr: "await") entries are settled values; unflagged siblings
174
174
  * stay promises. Parallel slots do not use this channel — they pin
175
175
  * loading() / live-lane holes via use(loaderDataPromise).
176
176
  */
@@ -153,10 +153,10 @@ export type PathHelpers<TEnv> = {
153
153
  /**
154
154
  * Attach a data loader to the current route/layout.
155
155
  *
156
- * Pass `{ ssr: false }` — the same knob as loading(fallback, { ssr:
157
- * false }) — to await this loader before first flush on DOCUMENT requests
158
- * (see {@link LoaderOptions}): the opt-in for loaders whose data, handle
159
- * pushes, or thrown notFound()/redirect() must be in the SSR'd HTML.
156
+ * Pass `{ ssr: "await" }` to await this loader before first flush on
157
+ * DOCUMENT requests (see {@link LoaderOptions}): the opt-in for loaders
158
+ * whose data, handle pushes, or thrown notFound()/redirect() must be in the
159
+ * SSR'd HTML.
160
160
  * Per-loader: a streaming sibling keeps streaming. Under a `ppr` route the
161
161
  * flag BAKES: the loader executes at shell capture and its settled return
162
162
  * freezes into the shell (nested promises stay live holes) — the pre-flush
@@ -61,7 +61,7 @@ export interface PartialPrerenderProps {
61
61
  * Upper bound (serialized UTF-8 bytes) on the loader pins in the capture
62
62
  * data snapshot: the bake-lane loader records. The doc record (the handler
63
63
  * layer every HIT replays) is exempt. A document HIT is served only from an
64
- * entry that holds a pin for every `ssr: false` loader its HTML baked, so
64
+ * entry that holds a pin for every `ssr: "await"` loader its HTML baked, so
65
65
  * over the cap no document shell is stored: documents render in full
66
66
  * (MISS), and client navigations still replay the handler layer and run
67
67
  * those loaders fresh. Reported once per key. Defaults to 8 MiB. The whole
@@ -166,7 +166,7 @@ interface RangoBaseOptions {
166
166
  * which is why React outlines by default.
167
167
  *
168
168
  * When UNSET, document renders and PPR shell captures whose matched chain
169
- * has a `loader(Def, { ssr: false })` entry auto-raise to MAX_SAFE_INTEGER —
169
+ * has a `loader(Def, { ssr: "await" })` entry auto-raise to MAX_SAFE_INTEGER —
170
170
  * the loader was awaited before first flush precisely so its content ships
171
171
  * in-place, and outlining the boundary it feeds would defeat that. A shell
172
172
  * HIT inherits the captured value, so it matches the live render. Setting
@@ -4604,7 +4604,7 @@ function routerKeyPrefix(routerId2) {
4604
4604
 
4605
4605
  // src/rsc/shell-capture-constants.ts
4606
4606
  var SHELL_CAPTURE_MAX_WAIT_MS = 15e3;
4607
- var PPR_LANE_HINT = 'Lane rule: only loader(Def, { ssr: false }) executes at capture and bakes its non-promise data; every other loader is live (masked at capture, fresh per request) and needs loading() or an inline <Suspense> above its reader. See the /ppr skill, "The loader lane rule" (node_modules/@rangojs/router/skills/ppr/SKILL.md).';
4607
+ var PPR_LANE_HINT = 'Lane rule: only loader(Def, { ssr: "await" }) executes at capture and bakes its non-promise data; every other loader is live (masked at capture, fresh per request) and needs loading() or an inline <Suspense> above its reader. See the /ppr skill, "The loader lane rule" (node_modules/@rangojs/router/skills/ppr/SKILL.md).';
4608
4608
 
4609
4609
  // src/cache/cache-exec-scope.ts
4610
4610
  import { AsyncLocalStorage } from "node:async_hooks";
@@ -4633,8 +4633,8 @@ var LOADER_BODY_SCOPE_KEY = /* @__PURE__ */ Symbol.for("rangojs-router:loader-bo
4633
4633
  var loaderBodyScopeALS = globalThis[LOADER_BODY_SCOPE_KEY] ??= new AsyncLocalStorage2();
4634
4634
  function requestReadCaptureFix(what) {
4635
4635
  return {
4636
- capture: `The captured shell is served to every user of this URL, so request-scoped data read here would leak one user's ${what} to others. Read it inside a loader without ssr: false and consume it with useLoader, e.g. createLoader(async () => getUser(cookies().get("session")?.value)). ${PPR_LANE_HINT}`,
4637
- warning: "Read it in a loader without ssr: false and consume it with useLoader under loading() or an inline <Suspense> (a live hole). A promise the handler passes or pushes does not help: the capture waits for it."
4636
+ capture: `The captured shell is served to every user of this URL, so request-scoped data read here would leak one user's ${what} to others. Read it inside a loader without ssr: "await" and consume it with useLoader, e.g. createLoader(async () => getUser(cookies().get("session")?.value)). ${PPR_LANE_HINT}`,
4637
+ warning: 'Read it in a loader without ssr: "await" and consume it with useLoader under loading() or an inline <Suspense> (a live hole). A promise the handler passes or pushes does not help: the capture waits for it.'
4638
4638
  };
4639
4639
  }
4640
4640
  var NON_CACHEABLE_READ = {
@@ -6074,7 +6074,7 @@ var RAW_COOKIES_READ = {
6074
6074
  verb: "called",
6075
6075
  fix: COOKIES_READ.fix
6076
6076
  };
6077
- var THEME_READ_FIX = "On ppr and cache() routes, read the theme with useTheme() in a client component, or in a live loader (no ssr: false) with cookies().get(<storageKey>). The <html> theme class needs neither: the theme script sets it before paint. See the /theme skill (node_modules/@rangojs/router/skills/theme/SKILL.md).";
6077
+ var THEME_READ_FIX = 'On ppr and cache() routes, read the theme with useTheme() in a client component, or in a live loader (no ssr: "await") with cookies().get(<storageKey>). The <html> theme class needs neither: the theme script sets it before paint. See the /theme skill (node_modules/@rangojs/router/skills/theme/SKILL.md).';
6078
6078
  var THEME_READ = {
6079
6079
  verb: "read",
6080
6080
  fix: {
@@ -6289,7 +6289,7 @@ function serializeRoute(route2, options, ordinals) {
6289
6289
  });
6290
6290
  const awaitedLoaderIndices = indicesWhere(
6291
6291
  route2.loaders,
6292
- ({ ssr }) => ssr === false
6292
+ ({ ssr }) => ssr === "await"
6293
6293
  );
6294
6294
  const transition3 = serializeTransition(route2);
6295
6295
  return Object.freeze({
@@ -7344,7 +7344,7 @@ function resolveVersionsFrom(table, routerId2) {
7344
7344
  // package.json
7345
7345
  var package_default = {
7346
7346
  name: "@rangojs/router",
7347
- version: "0.34.1",
7347
+ version: "0.35.0",
7348
7348
  description: "Django-inspired RSC router with composable URL patterns",
7349
7349
  keywords: [
7350
7350
  "react",
@@ -14567,7 +14567,7 @@ async function runShellPrerenderPhase(s, builder, versionsTable) {
14567
14567
  injectRSCPayload: htmlStream.injectRSCPayload,
14568
14568
  headScripts: "preinit",
14569
14569
  // Build-captured shells honor the explicit outlining budget, or the
14570
- // ssr:false auto-raise without one (createShellCaptureHandler): the
14570
+ // ssr: "await" auto-raise without one (createShellCaptureHandler): the
14571
14571
  // capture value rides the postponed state into every runtime resume().
14572
14572
  ...s.opts?.progressiveChunkSize !== void 0 && {
14573
14573
  progressiveChunkSize: s.opts.progressiveChunkSize
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rangojs/router",
3
- "version": "0.34.1",
3
+ "version": "0.35.0",
4
4
  "description": "Django-inspired RSC router with composable URL patterns",
5
5
  "keywords": [
6
6
  "react",
@@ -379,7 +379,7 @@ product) and the pages render entirely from loaders, mount the shop as a
379
379
  `clientUrls()` group, `include("/shop", shopUrls, { name: "shop" })`:
380
380
  navigations inside it render at the click, and each loader's `revalidate()`
381
381
  holds its data across them. A group has no handlers, so a `ppr` group route
382
- holds its client components' HTML and its `ssr: false` loaders' data, and
382
+ holds its client components' HTML and its `ssr: "await"` loaders' data, and
383
383
  every other loader is a hole; handler output baked from a tagged
384
384
  `"use cache"` read, as in the catalog above, needs `urls()` routes. The router
385
385
  repository's `tests/vite-rsc-demo/src/urls/client-shop.client.tsx` is a full