@voltro/web 0.28.0 → 0.30.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 +655 -0
- package/dist/{frameworkBoot-DHdoAwd9.js → frameworkBoot-BkBY6M2s.js} +100 -108
- package/dist/frameworkBoot.d.ts +11 -9
- package/dist/frameworkBoot.js +1 -1
- package/dist/index.d.ts +94 -10
- package/dist/index.js +72 -48
- package/dist/{mount-DfyG67bY.js → mount-fDzFaIuf.js} +26 -26
- package/dist/mount.d.ts +1 -10
- package/dist/mount.js +1 -1
- package/dist/{routerState-DDDBYFrI.js → routerState-DAT472IC.js} +3 -1
- package/dist/{serverContext-BASpdhXG.js → serverContext-CV1ieZKF.js} +236 -229
- package/dist/ssr.d.ts +43 -5
- package/dist/ssr.js +56 -46
- package/package.json +3 -3
package/dist/ssr.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ComponentType } from 'react';
|
|
2
2
|
import { PipeableStream } from 'react-dom/server';
|
|
3
|
+
import { PreloadSeed } from '@voltro/client';
|
|
3
4
|
import { ReactNode } from 'react';
|
|
5
|
+
import { seedPreloadedSubscription } from '@voltro/client';
|
|
4
6
|
import { StoreSeed } from '@voltro/client';
|
|
5
7
|
import { StoreSeedBag } from '@voltro/client';
|
|
6
8
|
|
|
@@ -45,15 +47,23 @@ export declare const assertDeferralSupported: (target: DeferralTarget) => void;
|
|
|
45
47
|
* `enterWith` is the Node API for exactly this case: it installs the store for
|
|
46
48
|
* the current async context and everything that flows from it, which for a
|
|
47
49
|
* per-request handler is precisely the intended lifetime. Call it once at the
|
|
48
|
-
* top of a render, then read `currentStoreSeeds()`
|
|
50
|
+
* top of a render, then read `currentStoreSeeds()` / `currentPreloadSeeds()`
|
|
51
|
+
* where the payload is built.
|
|
49
52
|
*
|
|
50
53
|
* Do NOT call it from a long-lived context (a module top level, a server
|
|
51
54
|
* bootstrap): there it would install one bag for the whole process, which is
|
|
52
55
|
* the leak the scoping exists to prevent.
|
|
56
|
+
*
|
|
57
|
+
* Returns the STORE bag so existing callers keep their `{ seeds }` shape; the
|
|
58
|
+
* preload bag is installed into the same scope and read via
|
|
59
|
+
* `currentPreloadSeeds()`.
|
|
53
60
|
*/
|
|
54
61
|
export declare const beginStoreSeeds: () => StoreSeedBag;
|
|
55
62
|
|
|
56
|
-
/** The current request's seeds, or null outside a render.
|
|
63
|
+
/** The current request's preloaded-subscription seeds, or null outside a render. */
|
|
64
|
+
export declare const currentPreloadSeeds: () => ReadonlyArray<PreloadSeed> | null;
|
|
65
|
+
|
|
66
|
+
/** The current request's store seeds, or null outside a render. Tests + diagnostics. */
|
|
57
67
|
export declare const currentStoreSeeds: () => ReadonlyArray<StoreSeed> | null;
|
|
58
68
|
|
|
59
69
|
/** Render modes / interactive modes a page may combine with `defer()`. */
|
|
@@ -153,6 +163,18 @@ export declare interface InlinedRouterState {
|
|
|
153
163
|
readonly key?: string;
|
|
154
164
|
readonly value: unknown;
|
|
155
165
|
}>;
|
|
166
|
+
/**
|
|
167
|
+
* Preloaded-subscription first values the server seeded for this document
|
|
168
|
+
* (see `seedPreloadedSubscription` in `@voltro/client`). Rides the same
|
|
169
|
+
* payload as `storeSeeds` for the same reason: one thing to emit, parse, and
|
|
170
|
+
* remember on all three SSR paths. `mount` applies them before the first
|
|
171
|
+
* render so `usePreloadedSubscription` shows real data with no flash.
|
|
172
|
+
*/
|
|
173
|
+
readonly preloadSeeds?: ReadonlyArray<{
|
|
174
|
+
readonly api: string;
|
|
175
|
+
readonly key: string;
|
|
176
|
+
readonly value: unknown;
|
|
177
|
+
}>;
|
|
156
178
|
/** Deferred (streamed) loader fields: field name -> client-registry id, for
|
|
157
179
|
* the page loader and per chain index. The VALUES are not here — they
|
|
158
180
|
* arrive later, published by the settle `<script>` each `<Await>` boundary
|
|
@@ -316,6 +338,11 @@ declare interface PageMeta {
|
|
|
316
338
|
* JSON-serialisable is accepted; `@context` + `@type` are
|
|
317
339
|
* authored, not synthesised. */
|
|
318
340
|
readonly jsonLd?: ReadonlyArray<Record<string, unknown>>;
|
|
341
|
+
/** Keep this page out of the index. Emits
|
|
342
|
+
* `<meta name="robots" content="noindex, nofollow">`, and the build's
|
|
343
|
+
* `sitemap.xml` generator EXCLUDES the route. Use for utility pages
|
|
344
|
+
* (checkout steps, previews, thank-you pages) that should not rank. */
|
|
345
|
+
readonly noIndex?: boolean;
|
|
319
346
|
}
|
|
320
347
|
|
|
321
348
|
export declare const parseCookieHeader: (raw: string | undefined) => Record<string, string>;
|
|
@@ -512,7 +539,8 @@ export declare const renderPageToHtml: (options: RenderPageOptions) => RenderPag
|
|
|
512
539
|
export declare const renderPageToStream: (options: RenderPageStreamOptions) => RenderPageStreamResult;
|
|
513
540
|
|
|
514
541
|
/**
|
|
515
|
-
* The hydration payload, with this request's client-store seeds
|
|
542
|
+
* The hydration payload, with this request's client-store seeds AND
|
|
543
|
+
* preloaded-subscription seeds folded in.
|
|
516
544
|
*
|
|
517
545
|
* The fold happens HERE and nowhere else on purpose. There are three
|
|
518
546
|
* independent SSR emitters, and every one of them already calls this function —
|
|
@@ -521,8 +549,8 @@ export declare const renderPageToStream: (options: RenderPageStreamOptions) => R
|
|
|
521
549
|
* passing `storeSeeds` itself) is three chances to forget and one silent
|
|
522
550
|
* difference between environments.
|
|
523
551
|
*
|
|
524
|
-
* An explicit `storeSeeds` on the input still wins, so the
|
|
525
|
-
* can pass a set it computed some other way.
|
|
552
|
+
* An explicit `storeSeeds` / `preloadSeeds` on the input still wins, so the
|
|
553
|
+
* build-time prerender can pass a set it computed some other way.
|
|
526
554
|
*/
|
|
527
555
|
export declare const renderRouterStateScript: (input: RouterStateInput) => string;
|
|
528
556
|
|
|
@@ -551,6 +579,13 @@ export declare interface RouterStateInput {
|
|
|
551
579
|
readonly key?: string;
|
|
552
580
|
readonly value: unknown;
|
|
553
581
|
}>;
|
|
582
|
+
/** Preloaded-subscription first values seeded during this render (see
|
|
583
|
+
* `InlinedRouterState`). */
|
|
584
|
+
readonly preloadSeeds?: ReadonlyArray<{
|
|
585
|
+
readonly api: string;
|
|
586
|
+
readonly key: string;
|
|
587
|
+
readonly value: unknown;
|
|
588
|
+
}>;
|
|
554
589
|
/** Set by the SSR-layout-shell path (a `renderMode:'spa'` page under an SSR
|
|
555
590
|
* layout chain): the server rendered the layouts + an empty page slot and
|
|
556
591
|
* ran only the layout loaders. Tells the client to reproduce the empty slot
|
|
@@ -596,6 +631,8 @@ declare interface RouteSegment {
|
|
|
596
631
|
readonly loader?: LoaderFn | undefined;
|
|
597
632
|
}
|
|
598
633
|
|
|
634
|
+
export { seedPreloadedSubscription }
|
|
635
|
+
|
|
599
636
|
/**
|
|
600
637
|
* Serialise arbitrary data for inlining into an HTML `<script>` body.
|
|
601
638
|
*
|
|
@@ -627,6 +664,7 @@ declare interface ServerRequestContextValue {
|
|
|
627
664
|
export declare const withStoreSeeds: <T>(render: () => Promise<T>) => Promise<{
|
|
628
665
|
readonly result: T;
|
|
629
666
|
readonly storeSeeds: ReadonlyArray<StoreSeed>;
|
|
667
|
+
readonly preloadSeeds: ReadonlyArray<PreloadSeed>;
|
|
630
668
|
}>;
|
|
631
669
|
|
|
632
670
|
export { }
|
package/dist/ssr.js
CHANGED
|
@@ -1,40 +1,50 @@
|
|
|
1
|
-
import { _ as e, a as t, g as n, m as r, n as i, p as a, s as o, t as s } from "./routerState-
|
|
2
|
-
import { c,
|
|
3
|
-
import { createElement as
|
|
4
|
-
import { makeStoreSeedBag as
|
|
5
|
-
import { renderToPipeableStream as
|
|
6
|
-
import { AsyncLocalStorage as
|
|
1
|
+
import { _ as e, a as t, g as n, m as r, n as i, p as a, s as o, t as s } from "./routerState-DAT472IC.js";
|
|
2
|
+
import { T as c, c as l, d as u, h as d, l as f, r as p, t as m } from "./serverContext-CV1ieZKF.js";
|
|
3
|
+
import { createElement as h } from "react";
|
|
4
|
+
import { makePreloadSeedBag as g, makeStoreSeedBag as _, seedPreloadedSubscription as v, setPreloadSeedResolver as y, setStoreSeedResolver as b } from "@voltro/client";
|
|
5
|
+
import { renderToPipeableStream as x, renderToString as S } from "react-dom/server";
|
|
6
|
+
import { AsyncLocalStorage as C } from "node:async_hooks";
|
|
7
7
|
//#region src/storeSeedScope.ts
|
|
8
|
-
var
|
|
9
|
-
|
|
10
|
-
var
|
|
11
|
-
let t =
|
|
8
|
+
var w = new C();
|
|
9
|
+
b(() => w.getStore()?.store ?? null), y(() => w.getStore()?.preload ?? null);
|
|
10
|
+
var T = async (e) => {
|
|
11
|
+
let t = {
|
|
12
|
+
store: _(),
|
|
13
|
+
preload: g()
|
|
14
|
+
};
|
|
12
15
|
return {
|
|
13
|
-
result: await
|
|
14
|
-
storeSeeds: t.seeds
|
|
16
|
+
result: await w.run(t, e),
|
|
17
|
+
storeSeeds: t.store.seeds,
|
|
18
|
+
preloadSeeds: t.preload.seeds
|
|
19
|
+
};
|
|
20
|
+
}, E = () => {
|
|
21
|
+
let e = {
|
|
22
|
+
store: _(),
|
|
23
|
+
preload: g()
|
|
15
24
|
};
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}, C = () => b.getStore()?.seeds ?? null, w = (e) => {
|
|
20
|
-
let n = C();
|
|
21
|
-
return t(e.storeSeeds !== void 0 || n === null || n.length === 0 ? e : {
|
|
25
|
+
return w.enterWith(e), e.store;
|
|
26
|
+
}, D = () => w.getStore()?.store.seeds ?? null, O = () => w.getStore()?.preload.seeds ?? null, k = (e) => {
|
|
27
|
+
let n = D(), r = O(), i = e.storeSeeds !== void 0 || n === null || n.length === 0 ? e : {
|
|
22
28
|
...e,
|
|
23
29
|
storeSeeds: n
|
|
30
|
+
};
|
|
31
|
+
return t(e.preloadSeeds !== void 0 || r === null || r.length === 0 ? i : {
|
|
32
|
+
...i,
|
|
33
|
+
preloadSeeds: r
|
|
24
34
|
});
|
|
25
|
-
},
|
|
26
|
-
let
|
|
27
|
-
if (!t) return
|
|
35
|
+
}, A = (e, t, n, r, i) => {
|
|
36
|
+
let a = h(l.Provider, { value: i ? n : f }, h(e));
|
|
37
|
+
if (!t) return a;
|
|
28
38
|
for (let e = t.length - 1; e >= 0; e--) {
|
|
29
39
|
let n = t[e];
|
|
30
40
|
if (n.Layout) {
|
|
31
41
|
let t = n.Layout;
|
|
32
|
-
|
|
42
|
+
a = h(l.Provider, { value: n.loader === void 0 ? f : r(e) }, h(t, { children: a }));
|
|
33
43
|
}
|
|
34
44
|
}
|
|
35
|
-
return
|
|
36
|
-
},
|
|
37
|
-
let { descriptor: t, params: n, pathname: r, loaderData: i, segmentLoaderData: a, requestContext: o, outerWrap: s, locale:
|
|
45
|
+
return a;
|
|
46
|
+
}, j = (e) => {
|
|
47
|
+
let { descriptor: t, params: n, pathname: r, loaderData: i, segmentLoaderData: a, requestContext: o, outerWrap: s, locale: l, pageSlot: f } = e, p = c(t.meta, n, i, l), g = {
|
|
38
48
|
pathname: r,
|
|
39
49
|
search: "",
|
|
40
50
|
params: n,
|
|
@@ -45,23 +55,23 @@ var x = async (e) => {
|
|
|
45
55
|
prefetch: () => {},
|
|
46
56
|
registerBlocker: () => () => {},
|
|
47
57
|
blocked: null
|
|
48
|
-
}, _ =
|
|
58
|
+
}, _ = f ? u : t.Component;
|
|
49
59
|
if (!_) throw Error(`SSR received a descriptor with no Component for "${r}". Lazy page routes are a client-only optimization and must not be used on the server.`);
|
|
50
|
-
let v =
|
|
51
|
-
return o && (v = m
|
|
60
|
+
let v = h(d.Provider, { value: g }, A(_, t.chain, f ? void 0 : i, (e) => a?.[e], !f && t.loader !== void 0));
|
|
61
|
+
return o && (v = h(m.Provider, { value: o }, v)), s && (v = s(v)), {
|
|
52
62
|
tree: v,
|
|
53
|
-
meta:
|
|
63
|
+
meta: p
|
|
54
64
|
};
|
|
55
|
-
},
|
|
56
|
-
let { tree: t, meta: n } =
|
|
65
|
+
}, M = (e) => {
|
|
66
|
+
let { tree: t, meta: n } = j(e);
|
|
57
67
|
return {
|
|
58
|
-
html:
|
|
68
|
+
html: S(t),
|
|
59
69
|
meta: n
|
|
60
70
|
};
|
|
61
|
-
},
|
|
62
|
-
let { tree: t, meta: n } =
|
|
71
|
+
}, N = (e) => {
|
|
72
|
+
let { tree: t, meta: n } = j(e);
|
|
63
73
|
return {
|
|
64
|
-
stream:
|
|
74
|
+
stream: x(t, {
|
|
65
75
|
...e.bootstrapModules ? { bootstrapModules: [...e.bootstrapModules] } : {},
|
|
66
76
|
...e.onShellReady ? { onShellReady: e.onShellReady } : {},
|
|
67
77
|
...e.onShellError ? { onShellError: e.onShellError } : {},
|
|
@@ -69,20 +79,20 @@ var x = async (e) => {
|
|
|
69
79
|
}),
|
|
70
80
|
meta: n
|
|
71
81
|
};
|
|
72
|
-
},
|
|
82
|
+
}, P = (e) => {
|
|
73
83
|
if (!e) return "";
|
|
74
84
|
let t = [];
|
|
75
|
-
if (typeof e.title == "string" && t.push(`<title>${
|
|
76
|
-
let e = n.name ? `name="${
|
|
77
|
-
e && t.push(`<meta ${e} content="${
|
|
85
|
+
if (typeof e.title == "string" && t.push(`<title>${F(e.title)}</title>`), e.description && t.push(`<meta name="description" content="${I(e.description)}" />`), e.noIndex && t.push("<meta name=\"robots\" content=\"noindex, nofollow\" />"), e.canonical && t.push(`<link rel="canonical" href="${I(e.canonical)}" />`), e.tags) for (let n of e.tags) {
|
|
86
|
+
let e = n.name ? `name="${I(n.name)}"` : n.property ? `property="${I(n.property)}"` : "";
|
|
87
|
+
e && t.push(`<meta ${e} content="${I(n.content)}" />`);
|
|
78
88
|
}
|
|
79
89
|
if (e.links) for (let n of e.links) {
|
|
80
90
|
let e = [
|
|
81
|
-
`rel="${
|
|
82
|
-
`href="${
|
|
83
|
-
n.hreflang ? `hreflang="${
|
|
84
|
-
n.type ? `type="${
|
|
85
|
-
n.title ? `title="${
|
|
91
|
+
`rel="${I(n.rel)}"`,
|
|
92
|
+
`href="${I(n.href)}"`,
|
|
93
|
+
n.hreflang ? `hreflang="${I(n.hreflang)}"` : "",
|
|
94
|
+
n.type ? `type="${I(n.type)}"` : "",
|
|
95
|
+
n.title ? `title="${I(n.title)}"` : ""
|
|
86
96
|
].filter(Boolean).join(" ");
|
|
87
97
|
t.push(`<link ${e} />`);
|
|
88
98
|
}
|
|
@@ -91,6 +101,6 @@ var x = async (e) => {
|
|
|
91
101
|
t.push(`<script type="application/ld+json" data-voltro-page-jsonld>${e}<\/script>`);
|
|
92
102
|
}
|
|
93
103
|
return t.join("\n ");
|
|
94
|
-
},
|
|
104
|
+
}, F = (e) => e.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"), I = (e) => e.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<");
|
|
95
105
|
//#endregion
|
|
96
|
-
export { s as ROUTER_STATE_SCRIPT_ID, o as assertDeferralSupported,
|
|
106
|
+
export { s as ROUTER_STATE_SCRIPT_ID, o as assertDeferralSupported, E as beginStoreSeeds, O as currentPreloadSeeds, D as currentStoreSeeds, i as encodeRouterState, a as isDeferredLoaderResult, p as parseCookieHeader, r as prepareDeferredLoaderData, n as renderDeferredRegistryScript, P as renderMetaToHtml, M as renderPageToHtml, N as renderPageToStream, k as renderRouterStateScript, v as seedPreloadedSubscription, e as serialiseStateForInlining, T as withStoreSeeds };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/web",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.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",
|
|
@@ -52,8 +52,8 @@
|
|
|
52
52
|
"node": ">=24.0.0"
|
|
53
53
|
},
|
|
54
54
|
"dependencies": {
|
|
55
|
-
"@voltro/client": "0.
|
|
56
|
-
"@voltro/ui": "0.
|
|
55
|
+
"@voltro/client": "0.30.0",
|
|
56
|
+
"@voltro/ui": "0.30.0"
|
|
57
57
|
},
|
|
58
58
|
"peerDependencies": {
|
|
59
59
|
"@effect/platform": "^0.97.0",
|