@voltro/web 0.52.0 → 0.53.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.
package/dist/ssr.d.ts CHANGED
@@ -94,12 +94,25 @@ export declare interface DeferralTarget {
94
94
  * Absent for every full-page render, where `renderMode` alone decides.
95
95
  */
96
96
  readonly layoutShell?: 'stream' | 'artefact' | undefined;
97
+ /**
98
+ * Partial prerendering: the page EXPORTED `ppr = true`. Makes `defer()` on
99
+ * `renderMode: 'isr'` legal — the cached SHELL renders the eager fields plus
100
+ * each `<Await>` fallback (credential-free, fail-closed), and the deferred
101
+ * HOLES run per request with the full request context, appended to the same
102
+ * response as registry settle scripts. The client reveals them through
103
+ * hydration — no React resume, no cached reveal scripts.
104
+ */
105
+ readonly ppr?: boolean;
97
106
  }
98
107
 
99
108
  /** Brand key on the object `defer()` returns. A single explicit container
100
109
  * check — NOT a scan of arbitrary loader results for markers by shape. */
101
110
  declare const DEFERRED_RESULT_TAG: "__voltroDeferredLoaderResult";
102
111
 
112
+ /** Stable registry id for one deferred field. `scope` is `'page'` or the
113
+ * layout's chain index — the same index the router-state payload keys by. */
114
+ export declare const deferredId: (scope: "page" | number, key: string) => string;
115
+
103
116
  /** What a loader returns when it defers part of its data. Opaque to user
104
117
  * code: build it with {@link defer}, read it with `useLoaderData()`. */
105
118
  declare interface DeferredLoaderResult<TEager extends object = object, TDeferred extends Record<string, Promise<unknown>> = Record<string, Promise<unknown>>> {
@@ -112,6 +125,9 @@ declare interface DeferredLoaderResult<TEager extends object = object, TDeferred
112
125
  readonly deferred: TDeferred;
113
126
  }
114
127
 
128
+ /** The body of the `<script>` `<Await>` emits when its promise resolves. */
129
+ export declare const deferredSettleScript: (id: string, value: unknown) => string;
130
+
115
131
  /** Build the wire payload from what the server render was given. */
116
132
  export declare const encodeRouterState: (input: RouterStateInput) => InlinedRouterState;
117
133
 
@@ -373,6 +389,19 @@ declare interface PageDescriptor<TLoaderData = unknown> {
373
389
  * set this to `'islands'` so only marked interactive zones
374
390
  * hydrate; the rest stays pure static HTML. */
375
391
  readonly interactive?: InteractiveMode | undefined;
392
+ /**
393
+ * Intercepting route (modal-with-URL): when a SOFT navigation reaches this
394
+ * page FROM one of the `from` route patterns, the page renders as an
395
+ * OVERLAY above the still-mounted origin page instead of replacing it —
396
+ * URL changes, Back closes the overlay, a hard load of the same URL
397
+ * renders the page standalone. `from` is one or more route PATTERNS
398
+ * (`'/photos'`, `'/albums/[id]'`); a soft navigation from any other route
399
+ * renders standalone. Full-hydration pages only (`interactive: 'full'`) —
400
+ * islands/none pages have no client router to intercept with.
401
+ */
402
+ readonly intercept?: {
403
+ readonly from: string | ReadonlyArray<string>;
404
+ } | undefined;
376
405
  }
377
406
 
378
407
  declare interface PageMeta {
package/dist/ssr.js CHANGED
@@ -1,31 +1,31 @@
1
1
  import { s as e } from "./defaultFallbacks-SQ45vAhz.js";
2
- import { _ as t, a as n, g as r, m as i, n as a, p as o, s, t as c } from "./routerState-DAT472IC.js";
3
- import { C as l, a as u, c as d, o as f, t as p, x as m } from "./serverContext-CzmZZwuQ.js";
4
- import { createElement as h } from "react";
5
- import { ServerRequestContext as g, makePreloadSeedBag as _, makeStoreSeedBag as v, seedPreloadedSubscription as y, seedPreloadedSubscriptionFailure as b, setPreloadSeedResolver as x, setStoreSeedResolver as S } from "@voltro/client";
6
- import { renderToPipeableStream as C, renderToString as w } from "react-dom/server";
7
- import { AsyncLocalStorage as T } from "node:async_hooks";
2
+ import { _ as t, a as n, f as r, h as i, m as a, n as o, s, t as c, u as l, v as u } from "./routerState-Cr6fTbka.js";
3
+ import { C as d, a as f, c as p, o as m, t as h, x as g } from "./serverContext-BwzZ6Q7d.js";
4
+ import { createElement as _ } from "react";
5
+ import { ServerRequestContext as v, makePreloadSeedBag as y, makeStoreSeedBag as b, seedPreloadedSubscription as x, seedPreloadedSubscriptionFailure as S, setPreloadSeedResolver as C, setStoreSeedResolver as w } from "@voltro/client";
6
+ import { renderToPipeableStream as T, renderToString as E } from "react-dom/server";
7
+ import { AsyncLocalStorage as D } from "node:async_hooks";
8
8
  //#region src/storeSeedScope.ts
9
- var E = new T();
10
- S(() => E.getStore()?.store ?? null), x(() => E.getStore()?.preload ?? null);
11
- var D = async (e) => {
9
+ var O = new D();
10
+ w(() => O.getStore()?.store ?? null), C(() => O.getStore()?.preload ?? null);
11
+ var k = async (e) => {
12
12
  let t = {
13
- store: v(),
14
- preload: _()
13
+ store: b(),
14
+ preload: y()
15
15
  };
16
16
  return {
17
- result: await E.run(t, e),
17
+ result: await O.run(t, e),
18
18
  storeSeeds: t.store.seeds,
19
19
  preloadSeeds: t.preload.seeds
20
20
  };
21
- }, O = () => {
21
+ }, A = () => {
22
22
  let e = {
23
- store: v(),
24
- preload: _()
23
+ store: b(),
24
+ preload: y()
25
25
  };
26
- return E.enterWith(e), e.store;
27
- }, k = () => E.getStore()?.store.seeds ?? null, A = () => E.getStore()?.preload.seeds ?? null, j = (e) => {
28
- let t = k(), r = A(), i = e.storeSeeds !== void 0 || t === null || t.length === 0 ? e : {
26
+ return O.enterWith(e), e.store;
27
+ }, j = () => O.getStore()?.store.seeds ?? null, M = () => O.getStore()?.preload.seeds ?? null, N = (e) => {
28
+ let t = j(), r = M(), i = e.storeSeeds !== void 0 || t === null || t.length === 0 ? e : {
29
29
  ...e,
30
30
  storeSeeds: t
31
31
  };
@@ -33,19 +33,19 @@ var D = async (e) => {
33
33
  ...i,
34
34
  preloadSeeds: r
35
35
  });
36
- }, M = (e, t, n, r, i) => {
37
- let a = h(u.Provider, { value: i ? n : f }, h(e));
36
+ }, P = (e, t, n, r, i) => {
37
+ let a = _(f.Provider, { value: i ? n : m }, _(e));
38
38
  if (!t) return a;
39
39
  for (let e = t.length - 1; e >= 0; e--) {
40
40
  let n = t[e];
41
41
  if (n.Layout) {
42
42
  let t = n.Layout;
43
- a = h(u.Provider, { value: n.loader === void 0 ? f : r(e) }, h(t, { children: a }));
43
+ a = _(f.Provider, { value: n.loader === void 0 ? m : r(e) }, _(t, { children: a }));
44
44
  }
45
45
  }
46
46
  return a;
47
- }, N = (t) => {
48
- let { descriptor: n, params: r, pathname: i, search: a, loaderData: o, segmentLoaderData: s, requestContext: c, outerWrap: u, locale: f, pageSlot: p } = t, _ = l(n.meta, r, o, f), v = {
47
+ }, F = (t) => {
48
+ let { descriptor: n, params: r, pathname: i, search: a, loaderData: o, segmentLoaderData: s, requestContext: c, outerWrap: l, locale: u, pageSlot: f } = t, m = d(n.meta, r, o, u), h = {
49
49
  pathname: i,
50
50
  search: a ?? "",
51
51
  params: r,
@@ -56,26 +56,26 @@ var D = async (e) => {
56
56
  prefetch: () => {},
57
57
  registerBlocker: () => () => {},
58
58
  blocked: null
59
- }, y = p ? d : n.Component;
59
+ }, y = f ? p : n.Component;
60
60
  if (!y) throw Error(`SSR received a descriptor with no Component for "${i}". Lazy page routes are a client-only optimization and must not be used on the server.`);
61
- let b = m(v, M(y, n.chain, p ? void 0 : o, (e) => s?.[e], !p && n.loader !== void 0));
62
- return b = h(e, {
61
+ let b = g(h, P(y, n.chain, f ? void 0 : o, (e) => s?.[e], !f && n.loader !== void 0));
62
+ return b = _(e, {
63
63
  chrome: null,
64
64
  children: b
65
- }), c && (b = h(g.Provider, { value: c }, b)), u && (b = u(b)), {
65
+ }), c && (b = _(v.Provider, { value: c }, b)), l && (b = l(b)), {
66
66
  tree: b,
67
- meta: _
67
+ meta: m
68
68
  };
69
- }, P = (e) => {
70
- let { tree: t, meta: n } = N(e);
69
+ }, I = (e) => {
70
+ let { tree: t, meta: n } = F(e);
71
71
  return {
72
- html: w(t),
72
+ html: E(t),
73
73
  meta: n
74
74
  };
75
- }, F = (e) => {
76
- let { tree: t, meta: n } = N(e);
75
+ }, L = (e) => {
76
+ let { tree: t, meta: n } = F(e);
77
77
  return {
78
- stream: C(t, {
78
+ stream: T(t, {
79
79
  ...e.bootstrapModules ? { bootstrapModules: [...e.bootstrapModules] } : {},
80
80
  ...e.nonce === void 0 ? {} : { nonce: e.nonce },
81
81
  ...e.onShellReady ? { onShellReady: e.onShellReady } : {},
@@ -84,20 +84,20 @@ var D = async (e) => {
84
84
  }),
85
85
  meta: n
86
86
  };
87
- }, I = (e) => {
87
+ }, R = (e) => {
88
88
  if (!e) return "";
89
89
  let t = [];
90
- if (typeof e.title == "string" && t.push(`<title>${L(e.title)}</title>`), e.description && t.push(`<meta name="description" content="${R(e.description)}" />`), e.noIndex && t.push("<meta name=\"robots\" content=\"noindex, nofollow\" />"), e.canonical && t.push(`<link rel="canonical" href="${R(e.canonical)}" />`), e.tags) for (let n of e.tags) {
91
- let e = n.name ? `name="${R(n.name)}"` : n.property ? `property="${R(n.property)}"` : "";
92
- e && t.push(`<meta ${e} content="${R(n.content)}" />`);
90
+ if (typeof e.title == "string" && t.push(`<title>${z(e.title)}</title>`), e.description && t.push(`<meta name="description" content="${B(e.description)}" />`), e.noIndex && t.push("<meta name=\"robots\" content=\"noindex, nofollow\" />"), e.canonical && t.push(`<link rel="canonical" href="${B(e.canonical)}" />`), e.tags) for (let n of e.tags) {
91
+ let e = n.name ? `name="${B(n.name)}"` : n.property ? `property="${B(n.property)}"` : "";
92
+ e && t.push(`<meta ${e} content="${B(n.content)}" />`);
93
93
  }
94
94
  if (e.links) for (let n of e.links) {
95
95
  let e = [
96
- `rel="${R(n.rel)}"`,
97
- `href="${R(n.href)}"`,
98
- n.hreflang ? `hreflang="${R(n.hreflang)}"` : "",
99
- n.type ? `type="${R(n.type)}"` : "",
100
- n.title ? `title="${R(n.title)}"` : ""
96
+ `rel="${B(n.rel)}"`,
97
+ `href="${B(n.href)}"`,
98
+ n.hreflang ? `hreflang="${B(n.hreflang)}"` : "",
99
+ n.type ? `type="${B(n.type)}"` : "",
100
+ n.title ? `title="${B(n.title)}"` : ""
101
101
  ].filter(Boolean).join(" ");
102
102
  t.push(`<link ${e} />`);
103
103
  }
@@ -106,6 +106,6 @@ var D = async (e) => {
106
106
  t.push(`<script type="application/ld+json" data-voltro-page-jsonld>${e}<\/script>`);
107
107
  }
108
108
  return t.join("\n ");
109
- }, L = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"), R = (e) => e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
109
+ }, z = (e) => e.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"), B = (e) => e.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;");
110
110
  //#endregion
111
- export { c as ROUTER_STATE_SCRIPT_ID, s as assertDeferralSupported, O as beginStoreSeeds, A as currentPreloadSeeds, k as currentStoreSeeds, a as encodeRouterState, o as isDeferredLoaderResult, p as parseCookieHeader, i as prepareDeferredLoaderData, r as renderDeferredRegistryScript, I as renderMetaToHtml, P as renderPageToHtml, F as renderPageToStream, j as renderRouterStateScript, y as seedPreloadedSubscription, b as seedPreloadedSubscriptionFailure, t as serialiseStateForInlining, D as withStoreSeeds };
111
+ export { c as ROUTER_STATE_SCRIPT_ID, s as assertDeferralSupported, A as beginStoreSeeds, M as currentPreloadSeeds, j as currentStoreSeeds, l as deferredId, r as deferredSettleScript, o as encodeRouterState, a as isDeferredLoaderResult, h as parseCookieHeader, i as prepareDeferredLoaderData, t as renderDeferredRegistryScript, R as renderMetaToHtml, I as renderPageToHtml, L as renderPageToStream, N as renderRouterStateScript, x as seedPreloadedSubscription, S as seedPreloadedSubscriptionFailure, u as serialiseStateForInlining, k as withStoreSeeds };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/web",
3
- "version": "0.52.0",
3
+ "version": "0.53.0",
4
4
  "description": "The Voltro web framework — file-based routing, render modes (SSR / SSG / islands), the page-export contract, data hooks, and the browser mount.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -63,8 +63,8 @@
63
63
  "node": ">=24.0.0"
64
64
  },
65
65
  "dependencies": {
66
- "@voltro/client": "0.52.0",
67
- "@voltro/ui": "0.52.0"
66
+ "@voltro/client": "0.53.0",
67
+ "@voltro/ui": "0.53.0"
68
68
  },
69
69
  "peerDependencies": {
70
70
  "@effect/platform": "^0.97.0",