@voltro/web 0.52.0 → 0.54.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/CHANGELOG.md +424 -0
- package/THIRD-PARTY-NOTICES.md +281 -1
- package/dist/index.d.ts +103 -4
- package/dist/index.js +180 -70
- package/dist/middleware.d.ts +19 -0
- package/dist/mount.js +1 -1
- package/dist/{routerState-DAT472IC.js → routerState-Cr6fTbka.js} +4 -2
- package/dist/serverContext-BwzZ6Q7d.js +1148 -0
- package/dist/ssr.d.ts +47 -4
- package/dist/ssr.js +45 -45
- package/package.json +3 -3
- package/dist/serverContext-CzmZZwuQ.js +0 -910
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 {
|
|
@@ -572,10 +601,24 @@ export declare interface RenderPageStreamOptions extends RenderPageOptions {
|
|
|
572
601
|
/** Bootstrap `<script>` module URLs injected by React at the end of the
|
|
573
602
|
* shell — the client bundle that hydrates the streamed HTML. */
|
|
574
603
|
readonly bootstrapModules?: ReadonlyArray<string>;
|
|
575
|
-
/**
|
|
576
|
-
*
|
|
577
|
-
*
|
|
578
|
-
*
|
|
604
|
+
/**
|
|
605
|
+
* Per-request CSP nonce (plan 08 §7). The POLICY header is the middleware's.
|
|
606
|
+
*
|
|
607
|
+
* React stamps this onto the scripts REACT ITSELF INJECTS — the bootstrap
|
|
608
|
+
* modules and its own Suspense completion scripts. Read that boundary
|
|
609
|
+
* strictly: it does NOT cover a `<script>` element in the rendered TREE, so
|
|
610
|
+
* the settle script `<Await>` emits inside a resolved boundary goes out bare.
|
|
611
|
+
* The CLI's `stampScriptNonce` covers the document the CLI assembles — the
|
|
612
|
+
* shell plus the `<head>` injection — and cannot see into React's stream
|
|
613
|
+
* either. Nothing currently stamps the settle script, which is why `defer()`
|
|
614
|
+
* and a nonce'd CSP do not compose; it is stated in the middleware docs rather
|
|
615
|
+
* than left for a reader to discover as a boundary that silently never
|
|
616
|
+
* resolves.
|
|
617
|
+
*
|
|
618
|
+
* The previous version of this comment said "bootstrap modules, Suspense
|
|
619
|
+
* settle scripts" and read as covering both, which is exactly the belief that
|
|
620
|
+
* let both boot paths pass the nonce HERE and nowhere else.
|
|
621
|
+
*/
|
|
579
622
|
readonly nonce?: string;
|
|
580
623
|
}
|
|
581
624
|
|
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,
|
|
3
|
-
import { C as
|
|
4
|
-
import { createElement as
|
|
5
|
-
import { ServerRequestContext as
|
|
6
|
-
import { renderToPipeableStream as
|
|
7
|
-
import { AsyncLocalStorage as
|
|
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
|
|
10
|
-
|
|
11
|
-
var
|
|
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:
|
|
14
|
-
preload:
|
|
13
|
+
store: b(),
|
|
14
|
+
preload: y()
|
|
15
15
|
};
|
|
16
16
|
return {
|
|
17
|
-
result: await
|
|
17
|
+
result: await O.run(t, e),
|
|
18
18
|
storeSeeds: t.store.seeds,
|
|
19
19
|
preloadSeeds: t.preload.seeds
|
|
20
20
|
};
|
|
21
|
-
},
|
|
21
|
+
}, A = () => {
|
|
22
22
|
let e = {
|
|
23
|
-
store:
|
|
24
|
-
preload:
|
|
23
|
+
store: b(),
|
|
24
|
+
preload: y()
|
|
25
25
|
};
|
|
26
|
-
return
|
|
27
|
-
},
|
|
28
|
-
let t =
|
|
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
|
-
},
|
|
37
|
-
let a =
|
|
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 =
|
|
43
|
+
a = _(f.Provider, { value: n.loader === void 0 ? m : r(e) }, _(t, { children: a }));
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
return a;
|
|
47
|
-
},
|
|
48
|
-
let { descriptor: n, params: r, pathname: i, search: a, loaderData: o, segmentLoaderData: s, requestContext: c, outerWrap:
|
|
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 =
|
|
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 =
|
|
62
|
-
return b =
|
|
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 =
|
|
65
|
+
}), c && (b = _(v.Provider, { value: c }, b)), l && (b = l(b)), {
|
|
66
66
|
tree: b,
|
|
67
|
-
meta:
|
|
67
|
+
meta: m
|
|
68
68
|
};
|
|
69
|
-
},
|
|
70
|
-
let { tree: t, meta: n } =
|
|
69
|
+
}, I = (e) => {
|
|
70
|
+
let { tree: t, meta: n } = F(e);
|
|
71
71
|
return {
|
|
72
|
-
html:
|
|
72
|
+
html: E(t),
|
|
73
73
|
meta: n
|
|
74
74
|
};
|
|
75
|
-
},
|
|
76
|
-
let { tree: t, meta: n } =
|
|
75
|
+
}, L = (e) => {
|
|
76
|
+
let { tree: t, meta: n } = F(e);
|
|
77
77
|
return {
|
|
78
|
-
stream:
|
|
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
|
-
},
|
|
87
|
+
}, R = (e) => {
|
|
88
88
|
if (!e) return "";
|
|
89
89
|
let t = [];
|
|
90
|
-
if (typeof e.title == "string" && t.push(`<title>${
|
|
91
|
-
let e = n.name ? `name="${
|
|
92
|
-
e && t.push(`<meta ${e} 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="${
|
|
97
|
-
`href="${
|
|
98
|
-
n.hreflang ? `hreflang="${
|
|
99
|
-
n.type ? `type="${
|
|
100
|
-
n.title ? `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
|
-
},
|
|
109
|
+
}, z = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"), B = (e) => e.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
110
110
|
//#endregion
|
|
111
|
-
export { c as ROUTER_STATE_SCRIPT_ID, s as assertDeferralSupported,
|
|
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.
|
|
3
|
+
"version": "0.54.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.
|
|
67
|
-
"@voltro/ui": "0.
|
|
66
|
+
"@voltro/client": "0.54.0",
|
|
67
|
+
"@voltro/ui": "0.54.0"
|
|
68
68
|
},
|
|
69
69
|
"peerDependencies": {
|
|
70
70
|
"@effect/platform": "^0.97.0",
|