@vertz/ui-server 0.2.12 → 0.2.13
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/bun-dev-server.d.ts +4 -1
- package/dist/bun-dev-server.js +110 -119
- package/dist/bun-plugin/index.d.ts +18 -1
- package/dist/bun-plugin/index.js +51 -3
- package/dist/dom-shim/index.js +1 -1
- package/dist/fetch-scope.d.ts +17 -0
- package/dist/fetch-scope.js +32 -0
- package/dist/index.d.ts +25 -17
- package/dist/index.js +52 -42
- package/dist/shared/{chunk-32688jav.js → chunk-98972e43.js} +43 -86
- package/dist/shared/{chunk-4t0ekdyv.js → chunk-n1arq9xq.js} +34 -7
- package/dist/ssr/index.js +2 -2
- package/package.json +8 -4
package/dist/bun-dev-server.d.ts
CHANGED
|
@@ -29,6 +29,8 @@ interface BunDevServerOptions {
|
|
|
29
29
|
* @default 'vscode'
|
|
30
30
|
*/
|
|
31
31
|
editor?: string;
|
|
32
|
+
/** Extra HTML tags to inject into the <head> (e.g., font preloads, meta tags). */
|
|
33
|
+
headTags?: string;
|
|
32
34
|
}
|
|
33
35
|
interface ErrorDetail {
|
|
34
36
|
message: string;
|
|
@@ -93,11 +95,12 @@ interface SSRPageHtmlOptions {
|
|
|
93
95
|
ssrData: unknown[];
|
|
94
96
|
scriptTag: string;
|
|
95
97
|
editor?: string;
|
|
98
|
+
headTags?: string;
|
|
96
99
|
}
|
|
97
100
|
/**
|
|
98
101
|
* Generate a full SSR HTML page with the given content, CSS, SSR data, and script tag.
|
|
99
102
|
*/
|
|
100
|
-
declare function generateSSRPageHtml({ title, css, bodyHtml, ssrData, scriptTag, editor }: SSRPageHtmlOptions): string;
|
|
103
|
+
declare function generateSSRPageHtml({ title, css, bodyHtml, ssrData, scriptTag, editor, headTags }: SSRPageHtmlOptions): string;
|
|
101
104
|
interface FetchInterceptorOptions {
|
|
102
105
|
apiHandler: (req: Request) => Promise<Response>;
|
|
103
106
|
origin: string;
|
package/dist/bun-dev-server.js
CHANGED
|
@@ -56,6 +56,9 @@ class DiagnosticsCollector {
|
|
|
56
56
|
ssrFailedReloadCount = 0;
|
|
57
57
|
hmrBundledScriptUrl = null;
|
|
58
58
|
hmrBootstrapDiscovered = false;
|
|
59
|
+
manifestFileCount = 0;
|
|
60
|
+
manifestDurationMs = 0;
|
|
61
|
+
manifestWarnings = [];
|
|
59
62
|
errorCurrent = null;
|
|
60
63
|
errorLastCategory = null;
|
|
61
64
|
errorLastMessage = null;
|
|
@@ -86,6 +89,11 @@ class DiagnosticsCollector {
|
|
|
86
89
|
this.ssrFailedReloadCount++;
|
|
87
90
|
}
|
|
88
91
|
}
|
|
92
|
+
recordManifestPrepass(fileCount, durationMs, warnings) {
|
|
93
|
+
this.manifestFileCount = fileCount;
|
|
94
|
+
this.manifestDurationMs = durationMs;
|
|
95
|
+
this.manifestWarnings = warnings;
|
|
96
|
+
}
|
|
89
97
|
recordHMRAssets(bundledScriptUrl, bootstrapDiscovered) {
|
|
90
98
|
this.hmrBundledScriptUrl = bundledScriptUrl;
|
|
91
99
|
this.hmrBootstrapDiscovered = bootstrapDiscovered;
|
|
@@ -141,6 +149,11 @@ class DiagnosticsCollector {
|
|
|
141
149
|
bundledScriptUrl: this.hmrBundledScriptUrl,
|
|
142
150
|
bootstrapDiscovered: this.hmrBootstrapDiscovered
|
|
143
151
|
},
|
|
152
|
+
manifest: {
|
|
153
|
+
fileCount: this.manifestFileCount,
|
|
154
|
+
durationMs: this.manifestDurationMs,
|
|
155
|
+
warnings: [...this.manifestWarnings]
|
|
156
|
+
},
|
|
144
157
|
errors: {
|
|
145
158
|
current: this.errorCurrent,
|
|
146
159
|
lastCategory: this.errorLastCategory,
|
|
@@ -158,6 +171,28 @@ class DiagnosticsCollector {
|
|
|
158
171
|
}
|
|
159
172
|
}
|
|
160
173
|
|
|
174
|
+
// src/fetch-scope.ts
|
|
175
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
176
|
+
var fetchScope = new AsyncLocalStorage;
|
|
177
|
+
var _originalFetch = null;
|
|
178
|
+
function installFetchProxy() {
|
|
179
|
+
if (_originalFetch !== null)
|
|
180
|
+
return;
|
|
181
|
+
const original = globalThis.fetch;
|
|
182
|
+
_originalFetch = original;
|
|
183
|
+
const proxy = (input, init) => {
|
|
184
|
+
const scoped = fetchScope.getStore();
|
|
185
|
+
if (scoped)
|
|
186
|
+
return scoped(input, init);
|
|
187
|
+
return original(input, init);
|
|
188
|
+
};
|
|
189
|
+
proxy.preconnect = original.preconnect;
|
|
190
|
+
globalThis.fetch = proxy;
|
|
191
|
+
}
|
|
192
|
+
function runWithScopedFetch(interceptor, fn) {
|
|
193
|
+
return fetchScope.run(interceptor, fn);
|
|
194
|
+
}
|
|
195
|
+
|
|
161
196
|
// src/source-map-resolver.ts
|
|
162
197
|
import { readFileSync } from "fs";
|
|
163
198
|
import { resolve as resolvePath } from "path";
|
|
@@ -305,6 +340,7 @@ function createSourceMapResolver(projectRoot) {
|
|
|
305
340
|
|
|
306
341
|
// src/ssr-render.ts
|
|
307
342
|
import { compileTheme } from "@vertz/ui";
|
|
343
|
+
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
308
344
|
|
|
309
345
|
// src/dom-shim/index.ts
|
|
310
346
|
import { setAdapter } from "@vertz/ui/internals";
|
|
@@ -628,6 +664,25 @@ function createSSRAdapter() {
|
|
|
628
664
|
};
|
|
629
665
|
}
|
|
630
666
|
|
|
667
|
+
// src/ssr-context.ts
|
|
668
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
669
|
+
import { registerSSRResolver } from "@vertz/ui/internals";
|
|
670
|
+
var ssrStorage = new AsyncLocalStorage2;
|
|
671
|
+
registerSSRResolver(() => ssrStorage.getStore());
|
|
672
|
+
function getSSRQueries() {
|
|
673
|
+
return ssrStorage.getStore()?.queries ?? [];
|
|
674
|
+
}
|
|
675
|
+
function setGlobalSSRTimeout(timeout) {
|
|
676
|
+
const store = ssrStorage.getStore();
|
|
677
|
+
if (store)
|
|
678
|
+
store.globalSSRTimeout = timeout;
|
|
679
|
+
}
|
|
680
|
+
function clearGlobalSSRTimeout() {
|
|
681
|
+
const store = ssrStorage.getStore();
|
|
682
|
+
if (store)
|
|
683
|
+
store.globalSSRTimeout = undefined;
|
|
684
|
+
}
|
|
685
|
+
|
|
631
686
|
// src/dom-shim/index.ts
|
|
632
687
|
var SHIM_GLOBALS = [
|
|
633
688
|
"document",
|
|
@@ -663,10 +718,6 @@ function installDomShim() {
|
|
|
663
718
|
}
|
|
664
719
|
installedGlobals = [];
|
|
665
720
|
setAdapter(createSSRAdapter());
|
|
666
|
-
const isSSRContext = typeof globalThis.__SSR_URL__ !== "undefined";
|
|
667
|
-
if (typeof document !== "undefined" && !isSSRContext && !shimInstalled) {
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
721
|
if (!shimInstalled) {
|
|
671
722
|
savedGlobals = new Map;
|
|
672
723
|
for (const g of SHIM_GLOBALS) {
|
|
@@ -699,7 +750,7 @@ function installDomShim() {
|
|
|
699
750
|
globalThis.document = fakeDocument;
|
|
700
751
|
if (typeof window === "undefined") {
|
|
701
752
|
globalThis.window = {
|
|
702
|
-
location: { pathname:
|
|
753
|
+
location: { pathname: ssrStorage.getStore()?.url || "/", search: "", hash: "" },
|
|
703
754
|
addEventListener: () => {},
|
|
704
755
|
removeEventListener: () => {},
|
|
705
756
|
history: {
|
|
@@ -710,7 +761,7 @@ function installDomShim() {
|
|
|
710
761
|
} else {
|
|
711
762
|
globalThis.window.location = {
|
|
712
763
|
...globalThis.window.location || {},
|
|
713
|
-
pathname:
|
|
764
|
+
pathname: ssrStorage.getStore()?.url || "/"
|
|
714
765
|
};
|
|
715
766
|
}
|
|
716
767
|
globalThis.Node = SSRNode;
|
|
@@ -772,31 +823,6 @@ function installDomShim() {
|
|
|
772
823
|
}
|
|
773
824
|
});
|
|
774
825
|
}
|
|
775
|
-
function removeDomShim() {
|
|
776
|
-
setAdapter(null);
|
|
777
|
-
if (!shimInstalled) {
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
shimInstalled = false;
|
|
781
|
-
if (savedGlobals) {
|
|
782
|
-
for (const g of SHIM_GLOBALS) {
|
|
783
|
-
if (savedGlobals.has(g)) {
|
|
784
|
-
globalThis[g] = savedGlobals.get(g);
|
|
785
|
-
} else {
|
|
786
|
-
delete globalThis[g];
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
savedGlobals = null;
|
|
790
|
-
} else {
|
|
791
|
-
for (const g of SHIM_GLOBALS) {
|
|
792
|
-
delete globalThis[g];
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
for (const g of installedGlobals) {
|
|
796
|
-
delete globalThis[g];
|
|
797
|
-
}
|
|
798
|
-
installedGlobals = [];
|
|
799
|
-
}
|
|
800
826
|
function toVNode(element) {
|
|
801
827
|
if (element instanceof SSRElement) {
|
|
802
828
|
return element.toVNode();
|
|
@@ -976,49 +1002,36 @@ function renderToStream(tree, options) {
|
|
|
976
1002
|
});
|
|
977
1003
|
}
|
|
978
1004
|
|
|
979
|
-
// src/ssr-context.ts
|
|
980
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
981
|
-
var ssrStorage = new AsyncLocalStorage;
|
|
982
|
-
function isInSSR() {
|
|
983
|
-
return ssrStorage.getStore() !== undefined;
|
|
984
|
-
}
|
|
985
|
-
function registerSSRQuery(entry) {
|
|
986
|
-
ssrStorage.getStore()?.queries.push(entry);
|
|
987
|
-
}
|
|
988
|
-
function getSSRQueries() {
|
|
989
|
-
return ssrStorage.getStore()?.queries ?? [];
|
|
990
|
-
}
|
|
991
|
-
function setGlobalSSRTimeout(timeout) {
|
|
992
|
-
const store = ssrStorage.getStore();
|
|
993
|
-
if (store)
|
|
994
|
-
store.globalSSRTimeout = timeout;
|
|
995
|
-
}
|
|
996
|
-
function clearGlobalSSRTimeout() {
|
|
997
|
-
const store = ssrStorage.getStore();
|
|
998
|
-
if (store)
|
|
999
|
-
store.globalSSRTimeout = undefined;
|
|
1000
|
-
}
|
|
1001
|
-
function getGlobalSSRTimeout() {
|
|
1002
|
-
return ssrStorage.getStore()?.globalSSRTimeout;
|
|
1003
|
-
}
|
|
1004
|
-
globalThis.__VERTZ_IS_SSR__ = isInSSR;
|
|
1005
|
-
globalThis.__VERTZ_SSR_REGISTER_QUERY__ = registerSSRQuery;
|
|
1006
|
-
globalThis.__VERTZ_GET_GLOBAL_SSR_TIMEOUT__ = getGlobalSSRTimeout;
|
|
1007
|
-
|
|
1008
1005
|
// src/ssr-streaming-runtime.ts
|
|
1009
1006
|
function safeSerialize(data) {
|
|
1010
1007
|
return JSON.stringify(data).replace(/</g, "\\u003c");
|
|
1011
1008
|
}
|
|
1012
1009
|
|
|
1013
1010
|
// src/ssr-render.ts
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1011
|
+
function createRequestContext(url) {
|
|
1012
|
+
return {
|
|
1013
|
+
url,
|
|
1014
|
+
adapter: createSSRAdapter(),
|
|
1015
|
+
subscriber: null,
|
|
1016
|
+
readValueCb: null,
|
|
1017
|
+
cleanupStack: [],
|
|
1018
|
+
batchDepth: 0,
|
|
1019
|
+
pendingEffects: new Map,
|
|
1020
|
+
contextScope: null,
|
|
1021
|
+
entityStore: new EntityStore,
|
|
1022
|
+
envelopeStore: new QueryEnvelopeStore,
|
|
1023
|
+
queryCache: new MemoryCache,
|
|
1024
|
+
inflight: new Map,
|
|
1025
|
+
queries: [],
|
|
1026
|
+
errors: []
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
var domShimInstalled = false;
|
|
1030
|
+
function ensureDomShim() {
|
|
1031
|
+
if (domShimInstalled && typeof document !== "undefined")
|
|
1032
|
+
return;
|
|
1033
|
+
domShimInstalled = true;
|
|
1034
|
+
installDomShim();
|
|
1022
1035
|
}
|
|
1023
1036
|
function resolveAppFactory(module) {
|
|
1024
1037
|
const createApp = module.default || module.App;
|
|
@@ -1038,35 +1051,18 @@ function collectCSS(themeCss, module) {
|
|
|
1038
1051
|
for (const s of module.styles)
|
|
1039
1052
|
alreadyIncluded.add(s);
|
|
1040
1053
|
}
|
|
1041
|
-
|
|
1042
|
-
if (module.getInjectedCSS) {
|
|
1043
|
-
componentCss = module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s));
|
|
1044
|
-
} else {
|
|
1045
|
-
componentCss = [];
|
|
1046
|
-
const head = globalThis.document?.head;
|
|
1047
|
-
if (head instanceof SSRElement) {
|
|
1048
|
-
for (const child of head.children) {
|
|
1049
|
-
if (child instanceof SSRElement && child.tag === "style" && "data-vertz-css" in child.attrs && child.textContent && !alreadyIncluded.has(child.textContent)) {
|
|
1050
|
-
componentCss.push(child.textContent);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
}
|
|
1054
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
1055
1055
|
const componentStyles = componentCss.map((s) => `<style data-vertz-css>${s}</style>`).join(`
|
|
1056
1056
|
`);
|
|
1057
1057
|
return [themeTag, globalTags, componentStyles].filter(Boolean).join(`
|
|
1058
1058
|
`);
|
|
1059
1059
|
}
|
|
1060
1060
|
async function ssrRenderToString(module, url, options) {
|
|
1061
|
-
return withRenderLock(() => ssrRenderToStringUnsafe(module, url, options));
|
|
1062
|
-
}
|
|
1063
|
-
async function ssrRenderToStringUnsafe(module, url, options) {
|
|
1064
1061
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
1065
1062
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
|
|
1063
|
+
ensureDomShim();
|
|
1064
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
1065
|
+
return ssrStorage.run(ctx, async () => {
|
|
1070
1066
|
try {
|
|
1071
1067
|
setGlobalSSRTimeout(ssrTimeout);
|
|
1072
1068
|
const createApp = resolveAppFactory(module);
|
|
@@ -1078,7 +1074,6 @@ async function ssrRenderToStringUnsafe(module, url, options) {
|
|
|
1078
1074
|
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
1079
1075
|
}
|
|
1080
1076
|
}
|
|
1081
|
-
globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
|
|
1082
1077
|
createApp();
|
|
1083
1078
|
const queries = getSSRQueries();
|
|
1084
1079
|
const resolvedQueries = [];
|
|
@@ -1107,8 +1102,6 @@ async function ssrRenderToStringUnsafe(module, url, options) {
|
|
|
1107
1102
|
return { html, css, ssrData };
|
|
1108
1103
|
} finally {
|
|
1109
1104
|
clearGlobalSSRTimeout();
|
|
1110
|
-
removeDomShim();
|
|
1111
|
-
delete globalThis.__SSR_URL__;
|
|
1112
1105
|
}
|
|
1113
1106
|
});
|
|
1114
1107
|
}
|
|
@@ -1116,14 +1109,12 @@ async function ssrStreamNavQueries(module, url, options) {
|
|
|
1116
1109
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
1117
1110
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
1118
1111
|
const navTimeout = options?.navSsrTimeout ?? 5000;
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
|
|
1112
|
+
ensureDomShim();
|
|
1113
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
1114
|
+
const queries = await ssrStorage.run(ctx, async () => {
|
|
1123
1115
|
try {
|
|
1124
1116
|
setGlobalSSRTimeout(ssrTimeout);
|
|
1125
1117
|
const createApp = resolveAppFactory(module);
|
|
1126
|
-
globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
|
|
1127
1118
|
createApp();
|
|
1128
1119
|
const discovered = getSSRQueries();
|
|
1129
1120
|
return discovered.map((q) => ({
|
|
@@ -1134,10 +1125,8 @@ async function ssrStreamNavQueries(module, url, options) {
|
|
|
1134
1125
|
}));
|
|
1135
1126
|
} finally {
|
|
1136
1127
|
clearGlobalSSRTimeout();
|
|
1137
|
-
removeDomShim();
|
|
1138
|
-
delete globalThis.__SSR_URL__;
|
|
1139
1128
|
}
|
|
1140
|
-
})
|
|
1129
|
+
});
|
|
1141
1130
|
if (queries.length === 0) {
|
|
1142
1131
|
const encoder3 = new TextEncoder;
|
|
1143
1132
|
return new ReadableStream({
|
|
@@ -1441,7 +1430,8 @@ function generateSSRPageHtml({
|
|
|
1441
1430
|
bodyHtml,
|
|
1442
1431
|
ssrData,
|
|
1443
1432
|
scriptTag,
|
|
1444
|
-
editor = "vscode"
|
|
1433
|
+
editor = "vscode",
|
|
1434
|
+
headTags = ""
|
|
1445
1435
|
}) {
|
|
1446
1436
|
const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__=${safeSerialize(ssrData)};</script>` : "";
|
|
1447
1437
|
return `<!doctype html>
|
|
@@ -1450,6 +1440,7 @@ function generateSSRPageHtml({
|
|
|
1450
1440
|
<meta charset="UTF-8" />
|
|
1451
1441
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
1452
1442
|
<title>${title}</title>
|
|
1443
|
+
${headTags}
|
|
1453
1444
|
${css}
|
|
1454
1445
|
${buildErrorChannelScript(editor)}
|
|
1455
1446
|
${RELOAD_GUARD_SCRIPT}
|
|
@@ -1530,9 +1521,13 @@ function createBunDevServer(options) {
|
|
|
1530
1521
|
title = "Vertz App",
|
|
1531
1522
|
projectRoot = process.cwd(),
|
|
1532
1523
|
logRequests = true,
|
|
1533
|
-
editor: editorOption
|
|
1524
|
+
editor: editorOption,
|
|
1525
|
+
headTags = ""
|
|
1534
1526
|
} = options;
|
|
1535
1527
|
const editor = detectEditor(editorOption);
|
|
1528
|
+
if (apiHandler) {
|
|
1529
|
+
installFetchProxy();
|
|
1530
|
+
}
|
|
1536
1531
|
const devDir = resolve(projectRoot, ".vertz", "dev");
|
|
1537
1532
|
mkdirSync(devDir, { recursive: true });
|
|
1538
1533
|
const logger = createDebugLogger(devDir);
|
|
@@ -1936,16 +1931,13 @@ data: {}
|
|
|
1936
1931
|
console.log(`[Server] SSR: ${pathname}`);
|
|
1937
1932
|
}
|
|
1938
1933
|
try {
|
|
1939
|
-
const
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
});
|
|
1947
|
-
}
|
|
1948
|
-
try {
|
|
1934
|
+
const interceptor = apiHandler ? createFetchInterceptor({
|
|
1935
|
+
apiHandler,
|
|
1936
|
+
origin: `http://${host}:${server?.port}`,
|
|
1937
|
+
skipSSRPaths,
|
|
1938
|
+
originalFetch: globalThis.fetch
|
|
1939
|
+
}) : null;
|
|
1940
|
+
const doRender = async () => {
|
|
1949
1941
|
logger.log("ssr", "render-start", { url: pathname });
|
|
1950
1942
|
const ssrStart = performance.now();
|
|
1951
1943
|
const result = await ssrRenderToString(ssrMod, pathname, { ssrTimeout: 300 });
|
|
@@ -1961,7 +1953,8 @@ data: {}
|
|
|
1961
1953
|
bodyHtml: result.html,
|
|
1962
1954
|
ssrData: result.ssrData,
|
|
1963
1955
|
scriptTag,
|
|
1964
|
-
editor
|
|
1956
|
+
editor,
|
|
1957
|
+
headTags
|
|
1965
1958
|
});
|
|
1966
1959
|
return new Response(html, {
|
|
1967
1960
|
status: 200,
|
|
@@ -1970,11 +1963,8 @@ data: {}
|
|
|
1970
1963
|
"Cache-Control": "no-store"
|
|
1971
1964
|
}
|
|
1972
1965
|
});
|
|
1973
|
-
}
|
|
1974
|
-
|
|
1975
|
-
globalThis.fetch = originalFetch;
|
|
1976
|
-
}
|
|
1977
|
-
}
|
|
1966
|
+
};
|
|
1967
|
+
return interceptor ? await runWithScopedFetch(interceptor, doRender) : await doRender();
|
|
1978
1968
|
} catch (err) {
|
|
1979
1969
|
console.error("[Server] SSR error:", err);
|
|
1980
1970
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
@@ -1988,7 +1978,8 @@ data: {}
|
|
|
1988
1978
|
bodyHtml: "",
|
|
1989
1979
|
ssrData: [],
|
|
1990
1980
|
scriptTag,
|
|
1991
|
-
editor
|
|
1981
|
+
editor,
|
|
1982
|
+
headTags
|
|
1992
1983
|
});
|
|
1993
1984
|
return new Response(fallbackHtml, {
|
|
1994
1985
|
status: 200,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
type ErrorCategory = "build" | "resolve" | "runtime" | "ssr";
|
|
2
2
|
import { CSSExtractionResult } from "@vertz/ui-compiler";
|
|
3
|
-
type DebugCategory = "plugin" | "ssr" | "watcher" | "ws";
|
|
3
|
+
type DebugCategory = "manifest" | "plugin" | "ssr" | "watcher" | "ws";
|
|
4
4
|
interface DebugLogger {
|
|
5
5
|
log(category: DebugCategory, message: string, data?: Record<string, unknown>): void;
|
|
6
6
|
isEnabled(category: DebugCategory): boolean;
|
|
@@ -32,6 +32,14 @@ interface DiagnosticsSnapshot {
|
|
|
32
32
|
bundledScriptUrl: string | null;
|
|
33
33
|
bootstrapDiscovered: boolean;
|
|
34
34
|
};
|
|
35
|
+
manifest: {
|
|
36
|
+
fileCount: number;
|
|
37
|
+
durationMs: number;
|
|
38
|
+
warnings: {
|
|
39
|
+
type: string;
|
|
40
|
+
message: string;
|
|
41
|
+
}[];
|
|
42
|
+
};
|
|
35
43
|
errors: {
|
|
36
44
|
current: ErrorCategory | null;
|
|
37
45
|
lastCategory: ErrorCategory | null;
|
|
@@ -61,6 +69,9 @@ declare class DiagnosticsCollector {
|
|
|
61
69
|
private ssrFailedReloadCount;
|
|
62
70
|
private hmrBundledScriptUrl;
|
|
63
71
|
private hmrBootstrapDiscovered;
|
|
72
|
+
private manifestFileCount;
|
|
73
|
+
private manifestDurationMs;
|
|
74
|
+
private manifestWarnings;
|
|
64
75
|
private errorCurrent;
|
|
65
76
|
private errorLastCategory;
|
|
66
77
|
private errorLastMessage;
|
|
@@ -72,6 +83,10 @@ declare class DiagnosticsCollector {
|
|
|
72
83
|
recordPluginConfig(filter: string, hmr: boolean, fastRefresh: boolean): void;
|
|
73
84
|
recordPluginProcess(file: string): void;
|
|
74
85
|
recordSSRReload(success: boolean, durationMs: number, error?: string): void;
|
|
86
|
+
recordManifestPrepass(fileCount: number, durationMs: number, warnings: {
|
|
87
|
+
type: string;
|
|
88
|
+
message: string;
|
|
89
|
+
}[]): void;
|
|
75
90
|
recordHMRAssets(bundledScriptUrl: string | null, bootstrapDiscovered: boolean): void;
|
|
76
91
|
recordError(category: ErrorCategory, message: string): void;
|
|
77
92
|
recordErrorClear(): void;
|
|
@@ -103,6 +118,8 @@ interface VertzBunPluginOptions {
|
|
|
103
118
|
fastRefresh?: boolean;
|
|
104
119
|
/** Project root for computing relative paths. */
|
|
105
120
|
projectRoot?: string;
|
|
121
|
+
/** Source directory for manifest generation pre-pass. Defaults to `src/` relative to projectRoot. */
|
|
122
|
+
srcDir?: string;
|
|
106
123
|
/** Debug logger for opt-in diagnostic logging. */
|
|
107
124
|
logger?: DebugLogger;
|
|
108
125
|
/** Diagnostics collector for the health check endpoint. */
|
package/dist/bun-plugin/index.js
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
__require
|
|
4
|
+
} from "../shared/chunk-eb80r8e8.js";
|
|
3
5
|
|
|
4
6
|
// src/bun-plugin/plugin.ts
|
|
5
7
|
import { mkdirSync, writeFileSync } from "fs";
|
|
6
8
|
import { dirname, relative, resolve } from "path";
|
|
7
9
|
import remapping from "@ampproject/remapping";
|
|
8
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
ComponentAnalyzer,
|
|
12
|
+
CSSExtractor,
|
|
13
|
+
compile,
|
|
14
|
+
generateAllManifests,
|
|
15
|
+
HydrationTransformer
|
|
16
|
+
} from "@vertz/ui-compiler";
|
|
9
17
|
import MagicString from "magic-string";
|
|
10
18
|
import { Project, ts as ts2 } from "ts-morph";
|
|
11
19
|
|
|
@@ -109,6 +117,45 @@ function createVertzBunPlugin(options) {
|
|
|
109
117
|
const diagnostics = options?.diagnostics;
|
|
110
118
|
const fileExtractions = new Map;
|
|
111
119
|
const cssSidecarMap = new Map;
|
|
120
|
+
const srcDir = options?.srcDir ?? resolve(projectRoot, "src");
|
|
121
|
+
const frameworkManifestJson = __require(__require.resolve("@vertz/ui/reactivity.json"));
|
|
122
|
+
const manifestResult = generateAllManifests({
|
|
123
|
+
srcDir,
|
|
124
|
+
packageManifests: { "@vertz/ui": frameworkManifestJson }
|
|
125
|
+
});
|
|
126
|
+
const manifests = manifestResult.manifests;
|
|
127
|
+
let manifestsRecord = null;
|
|
128
|
+
const getManifestsRecord = () => {
|
|
129
|
+
if (manifestsRecord)
|
|
130
|
+
return manifestsRecord;
|
|
131
|
+
const record = {};
|
|
132
|
+
for (const [key, value] of manifests) {
|
|
133
|
+
record[key] = value;
|
|
134
|
+
}
|
|
135
|
+
manifestsRecord = record;
|
|
136
|
+
return record;
|
|
137
|
+
};
|
|
138
|
+
if (logger?.isEnabled("manifest")) {
|
|
139
|
+
logger.log("manifest", "pre-pass", {
|
|
140
|
+
files: manifests.size,
|
|
141
|
+
durationMs: Math.round(manifestResult.durationMs),
|
|
142
|
+
warnings: manifestResult.warnings.length
|
|
143
|
+
});
|
|
144
|
+
for (const [filePath, manifest] of manifests) {
|
|
145
|
+
const exportShapes = {};
|
|
146
|
+
for (const [name, info] of Object.entries(manifest.exports)) {
|
|
147
|
+
exportShapes[name] = info.reactivity.type;
|
|
148
|
+
}
|
|
149
|
+
logger.log("manifest", "file", {
|
|
150
|
+
file: relative(projectRoot, filePath),
|
|
151
|
+
exports: exportShapes
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
for (const warning of manifestResult.warnings) {
|
|
155
|
+
logger.log("manifest", "warning", { type: warning.type, message: warning.message });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
diagnostics?.recordManifestPrepass(manifests.size, Math.round(manifestResult.durationMs), manifestResult.warnings.map((w) => ({ type: w.type, message: w.message })));
|
|
112
159
|
mkdirSync(cssOutDir, { recursive: true });
|
|
113
160
|
const plugin = {
|
|
114
161
|
name: "vertz-bun-plugin",
|
|
@@ -141,7 +188,8 @@ function createVertzBunPlugin(options) {
|
|
|
141
188
|
});
|
|
142
189
|
const compileResult = compile(hydratedCode, {
|
|
143
190
|
filename: args.path,
|
|
144
|
-
target: options?.target
|
|
191
|
+
target: options?.target,
|
|
192
|
+
manifests: getManifestsRecord()
|
|
145
193
|
});
|
|
146
194
|
const remapped = remapping([compileResult.map, hydrationMap], () => null);
|
|
147
195
|
const extraction = cssExtractor.extract(source, args.path);
|
package/dist/dom-shim/index.js
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Install a global fetch proxy that delegates to per-request interceptors.
|
|
3
|
+
*
|
|
4
|
+
* Call once at server startup. Subsequent calls are no-ops.
|
|
5
|
+
* Outside of runWithScopedFetch(), fetch() behaves normally.
|
|
6
|
+
*/
|
|
7
|
+
declare function installFetchProxy(): void;
|
|
8
|
+
/**
|
|
9
|
+
* Run a function with a scoped fetch interceptor.
|
|
10
|
+
*
|
|
11
|
+
* Any fetch() call inside fn() routes through the interceptor.
|
|
12
|
+
* Calls outside fn() (or without installFetchProxy()) use the original fetch.
|
|
13
|
+
*/
|
|
14
|
+
declare function runWithScopedFetch<T>(interceptor: typeof fetch, fn: () => T): T;
|
|
15
|
+
/** Reset fetch proxy state. Used in tests only. */
|
|
16
|
+
declare function _resetFetchProxy(): void;
|
|
17
|
+
export { runWithScopedFetch, installFetchProxy, _resetFetchProxy };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// src/fetch-scope.ts
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
var fetchScope = new AsyncLocalStorage;
|
|
4
|
+
var _originalFetch = null;
|
|
5
|
+
function installFetchProxy() {
|
|
6
|
+
if (_originalFetch !== null)
|
|
7
|
+
return;
|
|
8
|
+
const original = globalThis.fetch;
|
|
9
|
+
_originalFetch = original;
|
|
10
|
+
const proxy = (input, init) => {
|
|
11
|
+
const scoped = fetchScope.getStore();
|
|
12
|
+
if (scoped)
|
|
13
|
+
return scoped(input, init);
|
|
14
|
+
return original(input, init);
|
|
15
|
+
};
|
|
16
|
+
proxy.preconnect = original.preconnect;
|
|
17
|
+
globalThis.fetch = proxy;
|
|
18
|
+
}
|
|
19
|
+
function runWithScopedFetch(interceptor, fn) {
|
|
20
|
+
return fetchScope.run(interceptor, fn);
|
|
21
|
+
}
|
|
22
|
+
function _resetFetchProxy() {
|
|
23
|
+
if (_originalFetch !== null) {
|
|
24
|
+
globalThis.fetch = _originalFetch;
|
|
25
|
+
_originalFetch = null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export {
|
|
29
|
+
runWithScopedFetch,
|
|
30
|
+
installFetchProxy,
|
|
31
|
+
_resetFetchProxy
|
|
32
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -293,6 +293,26 @@ declare function resetSlotCounter(): void;
|
|
|
293
293
|
declare function createSlotPlaceholder(fallback: VNode | string): VNode & {
|
|
294
294
|
_slotId: number;
|
|
295
295
|
};
|
|
296
|
+
import { AccessSet } from "@vertz/ui/auth";
|
|
297
|
+
/**
|
|
298
|
+
* Extract an AccessSet from a decoded JWT payload for SSR injection.
|
|
299
|
+
*
|
|
300
|
+
* - If the JWT has an inline `acl.set` (no overflow), returns the decoded AccessSet.
|
|
301
|
+
* - If `acl.overflow` is true, returns `null` — caller should re-compute from live stores.
|
|
302
|
+
* - If no `acl` claim exists, returns `null`.
|
|
303
|
+
*
|
|
304
|
+
* @param jwtPayload - The decoded JWT payload (from verifyJWT)
|
|
305
|
+
* @returns The AccessSet for SSR injection, or null
|
|
306
|
+
*/
|
|
307
|
+
declare function getAccessSetForSSR(jwtPayload: Record<string, unknown> | null): AccessSet | null;
|
|
308
|
+
/**
|
|
309
|
+
* Serialize an AccessSet into a `<script>` tag that sets
|
|
310
|
+
* `window.__VERTZ_ACCESS_SET__`.
|
|
311
|
+
*
|
|
312
|
+
* @param accessSet - The access set to serialize
|
|
313
|
+
* @param nonce - Optional CSP nonce for the script tag
|
|
314
|
+
*/
|
|
315
|
+
declare function createAccessSetScript(accessSet: AccessSet, nonce?: string): string;
|
|
296
316
|
import { RenderAdapter } from "@vertz/ui/internals";
|
|
297
317
|
/**
|
|
298
318
|
* Create an SSR adapter that uses in-memory SSR node classes.
|
|
@@ -300,21 +320,9 @@ import { RenderAdapter } from "@vertz/ui/internals";
|
|
|
300
320
|
*/
|
|
301
321
|
declare function createSSRAdapter(): RenderAdapter;
|
|
302
322
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
resolve: (data: unknown) => void;
|
|
307
|
-
key: string;
|
|
308
|
-
resolved?: boolean;
|
|
309
|
-
}
|
|
310
|
-
interface SSRContext {
|
|
311
|
-
url: string;
|
|
312
|
-
errors: unknown[];
|
|
313
|
-
queries: SSRQueryEntry[];
|
|
314
|
-
/** Global per-query timeout override (ms). Set by the dev server entry. */
|
|
315
|
-
globalSSRTimeout?: number;
|
|
316
|
-
}
|
|
317
|
-
declare const ssrStorage: AsyncLocalStorage<SSRContext>;
|
|
323
|
+
import { SSRQueryEntry, SSRRenderContext } from "@vertz/ui/internals";
|
|
324
|
+
import { SSRQueryEntry as SSRQueryEntry2 } from "@vertz/ui/internals";
|
|
325
|
+
declare const ssrStorage: AsyncLocalStorage<SSRRenderContext>;
|
|
318
326
|
declare function isInSSR(): boolean;
|
|
319
327
|
declare function getSSRUrl(): string | undefined;
|
|
320
328
|
/**
|
|
@@ -326,7 +334,7 @@ declare function registerSSRQuery(entry: SSRQueryEntry): void;
|
|
|
326
334
|
* Get all registered SSR queries for the current render.
|
|
327
335
|
* Returns an empty array when called outside an SSR context.
|
|
328
336
|
*/
|
|
329
|
-
declare function getSSRQueries():
|
|
337
|
+
declare function getSSRQueries(): SSRRenderContext["queries"];
|
|
330
338
|
/**
|
|
331
339
|
* Set the global SSR timeout for queries that don't specify their own.
|
|
332
340
|
* Called by the virtual SSR entry to propagate the plugin-level ssrTimeout.
|
|
@@ -491,4 +499,4 @@ declare function collectStreamChunks(stream: ReadableStream<Uint8Array>): Promis
|
|
|
491
499
|
* @param nonce - Optional CSP nonce to add to the inline script tag.
|
|
492
500
|
*/
|
|
493
501
|
declare function createTemplateChunk(slotId: number, resolvedHtml: string, nonce?: string): string;
|
|
494
|
-
export { wrapWithHydrationMarkers, streamToString, ssrStorage, ssrRenderToString, ssrDiscoverQueries, setGlobalSSRTimeout, serializeToHtml, safeSerialize, resetSlotCounter, renderToStream, renderToHTMLStream, renderToHTML, renderPage, renderHeadToHtml, renderAssetTags, registerSSRQuery, rawHtml, isInSSR, inlineCriticalCss, getStreamingRuntimeScript, getSSRUrl, getSSRQueries, getGlobalSSRTimeout, generateSSRHtml, encodeChunk, createTemplateChunk, createSlotPlaceholder, createSSRHandler, createSSRDataChunk, createSSRAdapter, collectStreamChunks, clearGlobalSSRTimeout, VNode, SSRRenderResult, SSRQueryEntry, SSRModule, SSRHandlerOptions, SSRDiscoverResult, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, RawHtml, PageOptions, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, AssetDescriptor };
|
|
502
|
+
export { wrapWithHydrationMarkers, streamToString, ssrStorage, ssrRenderToString, ssrDiscoverQueries, setGlobalSSRTimeout, serializeToHtml, safeSerialize, resetSlotCounter, renderToStream, renderToHTMLStream, renderToHTML, renderPage, renderHeadToHtml, renderAssetTags, registerSSRQuery, rawHtml, isInSSR, inlineCriticalCss, getStreamingRuntimeScript, getSSRUrl, getSSRQueries, getGlobalSSRTimeout, getAccessSetForSSR, generateSSRHtml, encodeChunk, createTemplateChunk, createSlotPlaceholder, createSSRHandler, createSSRDataChunk, createSSRAdapter, createAccessSetScript, collectStreamChunks, clearGlobalSSRTimeout, VNode, SSRRenderResult, SSRQueryEntry2 as SSRQueryEntry, SSRModule, SSRHandlerOptions, SSRDiscoverResult, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, RawHtml, PageOptions, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, AssetDescriptor };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
clearGlobalSSRTimeout,
|
|
3
2
|
collectStreamChunks,
|
|
3
|
+
createRequestContext,
|
|
4
4
|
createSSRDataChunk,
|
|
5
5
|
createSSRHandler,
|
|
6
6
|
createSlotPlaceholder,
|
|
@@ -8,29 +8,27 @@ import {
|
|
|
8
8
|
encodeChunk,
|
|
9
9
|
escapeAttr,
|
|
10
10
|
escapeHtml,
|
|
11
|
-
getGlobalSSRTimeout,
|
|
12
|
-
getSSRQueries,
|
|
13
|
-
getSSRUrl,
|
|
14
11
|
getStreamingRuntimeScript,
|
|
15
|
-
isInSSR,
|
|
16
|
-
registerSSRQuery,
|
|
17
12
|
renderToStream,
|
|
18
13
|
resetSlotCounter,
|
|
19
14
|
safeSerialize,
|
|
20
15
|
serializeToHtml,
|
|
21
|
-
setGlobalSSRTimeout,
|
|
22
16
|
ssrDiscoverQueries,
|
|
23
17
|
ssrRenderToString,
|
|
24
|
-
ssrStorage,
|
|
25
18
|
streamToString
|
|
26
|
-
} from "./shared/chunk-
|
|
19
|
+
} from "./shared/chunk-98972e43.js";
|
|
27
20
|
import {
|
|
28
|
-
|
|
21
|
+
clearGlobalSSRTimeout,
|
|
29
22
|
createSSRAdapter,
|
|
30
|
-
|
|
23
|
+
getGlobalSSRTimeout,
|
|
24
|
+
getSSRQueries,
|
|
25
|
+
getSSRUrl,
|
|
26
|
+
isInSSR,
|
|
31
27
|
rawHtml,
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
registerSSRQuery,
|
|
29
|
+
setGlobalSSRTimeout,
|
|
30
|
+
ssrStorage
|
|
31
|
+
} from "./shared/chunk-n1arq9xq.js";
|
|
34
32
|
|
|
35
33
|
// src/asset-pipeline.ts
|
|
36
34
|
function renderAssetTags(assets) {
|
|
@@ -216,16 +214,7 @@ ${options.head}` : headHtml;
|
|
|
216
214
|
});
|
|
217
215
|
}
|
|
218
216
|
// src/render-to-html.ts
|
|
219
|
-
import { compileTheme } from "@vertz/ui";
|
|
220
|
-
import { setAdapter } from "@vertz/ui/internals";
|
|
221
|
-
function installSSR() {
|
|
222
|
-
setAdapter(createSSRAdapter());
|
|
223
|
-
installDomShim();
|
|
224
|
-
}
|
|
225
|
-
function removeSSR() {
|
|
226
|
-
setAdapter(null);
|
|
227
|
-
removeDomShim();
|
|
228
|
-
}
|
|
217
|
+
import { compileTheme, getInjectedCSS } from "@vertz/ui";
|
|
229
218
|
async function twoPassRender(options) {
|
|
230
219
|
options.app();
|
|
231
220
|
const queries = getSSRQueries();
|
|
@@ -243,17 +232,7 @@ async function twoPassRender(options) {
|
|
|
243
232
|
}
|
|
244
233
|
const pendingQueries = queries.filter((q) => !q.resolved);
|
|
245
234
|
const vnode = options.app();
|
|
246
|
-
const
|
|
247
|
-
const collectedCSS = [];
|
|
248
|
-
if (fakeDoc?.head?.children) {
|
|
249
|
-
for (const child of fakeDoc.head.children) {
|
|
250
|
-
if (child instanceof SSRElement && child.tag === "style") {
|
|
251
|
-
const cssText = child.children?.join("") ?? "";
|
|
252
|
-
if (cssText)
|
|
253
|
-
collectedCSS.push(cssText);
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
}
|
|
235
|
+
const collectedCSS = getInjectedCSS();
|
|
257
236
|
const themeCss = options.theme ? compileTheme(options.theme).css : "";
|
|
258
237
|
const allStyles = [themeCss, ...options.styles ?? [], ...collectedCSS].filter(Boolean);
|
|
259
238
|
const styleTags = allStyles.map((css) => `<style>${css}</style>`).join(`
|
|
@@ -273,9 +252,8 @@ async function twoPassRender(options) {
|
|
|
273
252
|
return { html, pendingQueries };
|
|
274
253
|
}
|
|
275
254
|
async function renderToHTMLStream(options) {
|
|
276
|
-
installSSR();
|
|
277
255
|
const streamTimeout = options.streamTimeout ?? 30000;
|
|
278
|
-
return ssrStorage.run(
|
|
256
|
+
return ssrStorage.run(createRequestContext(options.url), async () => {
|
|
279
257
|
try {
|
|
280
258
|
if (options.ssrTimeout !== undefined) {
|
|
281
259
|
setGlobalSSRTimeout(options.ssrTimeout);
|
|
@@ -283,14 +261,12 @@ async function renderToHTMLStream(options) {
|
|
|
283
261
|
const { html, pendingQueries } = await twoPassRender(options);
|
|
284
262
|
if (pendingQueries.length === 0) {
|
|
285
263
|
clearGlobalSSRTimeout();
|
|
286
|
-
removeSSR();
|
|
287
264
|
return new Response(html, {
|
|
288
265
|
status: 200,
|
|
289
266
|
headers: { "content-type": "text/html; charset=utf-8" }
|
|
290
267
|
});
|
|
291
268
|
}
|
|
292
269
|
clearGlobalSSRTimeout();
|
|
293
|
-
removeSSR();
|
|
294
270
|
const TIMEOUT_SENTINEL = Symbol("stream-timeout");
|
|
295
271
|
let closed = false;
|
|
296
272
|
let hardTimeoutId;
|
|
@@ -322,24 +298,56 @@ async function renderToHTMLStream(options) {
|
|
|
322
298
|
});
|
|
323
299
|
} catch (err) {
|
|
324
300
|
clearGlobalSSRTimeout();
|
|
325
|
-
removeSSR();
|
|
326
301
|
throw err;
|
|
327
302
|
}
|
|
328
303
|
});
|
|
329
304
|
}
|
|
330
305
|
async function renderToHTML(appOrOptions, maybeOptions) {
|
|
331
306
|
const options = typeof appOrOptions === "function" ? { ...maybeOptions, app: appOrOptions } : appOrOptions;
|
|
332
|
-
|
|
333
|
-
return ssrStorage.run({ url: options.url, errors: [], queries: [] }, async () => {
|
|
307
|
+
return ssrStorage.run(createRequestContext(options.url), async () => {
|
|
334
308
|
try {
|
|
335
309
|
const { html } = await twoPassRender(options);
|
|
336
310
|
return html;
|
|
337
311
|
} finally {
|
|
338
312
|
clearGlobalSSRTimeout();
|
|
339
|
-
removeSSR();
|
|
340
313
|
}
|
|
341
314
|
});
|
|
342
315
|
}
|
|
316
|
+
// src/ssr-access-set.ts
|
|
317
|
+
function getAccessSetForSSR(jwtPayload) {
|
|
318
|
+
if (!jwtPayload)
|
|
319
|
+
return null;
|
|
320
|
+
const acl = jwtPayload.acl;
|
|
321
|
+
if (!acl)
|
|
322
|
+
return null;
|
|
323
|
+
if (acl.overflow)
|
|
324
|
+
return null;
|
|
325
|
+
if (!acl.set)
|
|
326
|
+
return null;
|
|
327
|
+
return {
|
|
328
|
+
entitlements: Object.fromEntries(Object.entries(acl.set.entitlements).map(([name, check]) => [
|
|
329
|
+
name,
|
|
330
|
+
{
|
|
331
|
+
allowed: check.allowed,
|
|
332
|
+
reasons: check.reasons ?? [],
|
|
333
|
+
...check.reason ? { reason: check.reason } : {},
|
|
334
|
+
...check.meta ? { meta: check.meta } : {}
|
|
335
|
+
}
|
|
336
|
+
])),
|
|
337
|
+
flags: acl.set.flags,
|
|
338
|
+
plan: acl.set.plan,
|
|
339
|
+
computedAt: acl.set.computedAt
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function createAccessSetScript(accessSet, nonce) {
|
|
343
|
+
const json = JSON.stringify(accessSet);
|
|
344
|
+
const escaped = json.replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
345
|
+
const nonceAttr = nonce ? ` nonce="${escapeAttr2(nonce)}"` : "";
|
|
346
|
+
return `<script${nonceAttr}>window.__VERTZ_ACCESS_SET__=${escaped}</script>`;
|
|
347
|
+
}
|
|
348
|
+
function escapeAttr2(s) {
|
|
349
|
+
return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
|
|
350
|
+
}
|
|
343
351
|
// src/ssr-html.ts
|
|
344
352
|
function generateSSRHtml(options) {
|
|
345
353
|
const { appHtml, css, ssrData, clientEntry, title = "Vertz App" } = options;
|
|
@@ -383,6 +391,7 @@ export {
|
|
|
383
391
|
getSSRUrl,
|
|
384
392
|
getSSRQueries,
|
|
385
393
|
getGlobalSSRTimeout,
|
|
394
|
+
getAccessSetForSSR,
|
|
386
395
|
generateSSRHtml,
|
|
387
396
|
encodeChunk,
|
|
388
397
|
createTemplateChunk,
|
|
@@ -390,6 +399,7 @@ export {
|
|
|
390
399
|
createSSRHandler,
|
|
391
400
|
createSSRDataChunk,
|
|
392
401
|
createSSRAdapter,
|
|
402
|
+
createAccessSetScript,
|
|
393
403
|
collectStreamChunks,
|
|
394
404
|
clearGlobalSSRTimeout,
|
|
395
405
|
HeadCollector
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
clearGlobalSSRTimeout,
|
|
3
|
+
createSSRAdapter,
|
|
4
|
+
getSSRQueries,
|
|
3
5
|
installDomShim,
|
|
4
|
-
|
|
6
|
+
setGlobalSSRTimeout,
|
|
7
|
+
ssrStorage,
|
|
5
8
|
toVNode
|
|
6
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-n1arq9xq.js";
|
|
7
10
|
|
|
8
11
|
// src/html-serializer.ts
|
|
9
12
|
var VOID_ELEMENTS = new Set([
|
|
@@ -177,38 +180,6 @@ function renderToStream(tree, options) {
|
|
|
177
180
|
});
|
|
178
181
|
}
|
|
179
182
|
|
|
180
|
-
// src/ssr-context.ts
|
|
181
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
182
|
-
var ssrStorage = new AsyncLocalStorage;
|
|
183
|
-
function isInSSR() {
|
|
184
|
-
return ssrStorage.getStore() !== undefined;
|
|
185
|
-
}
|
|
186
|
-
function getSSRUrl() {
|
|
187
|
-
return ssrStorage.getStore()?.url;
|
|
188
|
-
}
|
|
189
|
-
function registerSSRQuery(entry) {
|
|
190
|
-
ssrStorage.getStore()?.queries.push(entry);
|
|
191
|
-
}
|
|
192
|
-
function getSSRQueries() {
|
|
193
|
-
return ssrStorage.getStore()?.queries ?? [];
|
|
194
|
-
}
|
|
195
|
-
function setGlobalSSRTimeout(timeout) {
|
|
196
|
-
const store = ssrStorage.getStore();
|
|
197
|
-
if (store)
|
|
198
|
-
store.globalSSRTimeout = timeout;
|
|
199
|
-
}
|
|
200
|
-
function clearGlobalSSRTimeout() {
|
|
201
|
-
const store = ssrStorage.getStore();
|
|
202
|
-
if (store)
|
|
203
|
-
store.globalSSRTimeout = undefined;
|
|
204
|
-
}
|
|
205
|
-
function getGlobalSSRTimeout() {
|
|
206
|
-
return ssrStorage.getStore()?.globalSSRTimeout;
|
|
207
|
-
}
|
|
208
|
-
globalThis.__VERTZ_IS_SSR__ = isInSSR;
|
|
209
|
-
globalThis.__VERTZ_SSR_REGISTER_QUERY__ = registerSSRQuery;
|
|
210
|
-
globalThis.__VERTZ_GET_GLOBAL_SSR_TIMEOUT__ = getGlobalSSRTimeout;
|
|
211
|
-
|
|
212
183
|
// src/ssr-streaming-runtime.ts
|
|
213
184
|
function safeSerialize(data) {
|
|
214
185
|
return JSON.stringify(data).replace(/</g, "\\u003c");
|
|
@@ -225,14 +196,31 @@ function createSSRDataChunk(key, data, nonce) {
|
|
|
225
196
|
|
|
226
197
|
// src/ssr-render.ts
|
|
227
198
|
import { compileTheme } from "@vertz/ui";
|
|
228
|
-
|
|
229
|
-
function
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
199
|
+
import { EntityStore, MemoryCache, QueryEnvelopeStore } from "@vertz/ui/internals";
|
|
200
|
+
function createRequestContext(url) {
|
|
201
|
+
return {
|
|
202
|
+
url,
|
|
203
|
+
adapter: createSSRAdapter(),
|
|
204
|
+
subscriber: null,
|
|
205
|
+
readValueCb: null,
|
|
206
|
+
cleanupStack: [],
|
|
207
|
+
batchDepth: 0,
|
|
208
|
+
pendingEffects: new Map,
|
|
209
|
+
contextScope: null,
|
|
210
|
+
entityStore: new EntityStore,
|
|
211
|
+
envelopeStore: new QueryEnvelopeStore,
|
|
212
|
+
queryCache: new MemoryCache,
|
|
213
|
+
inflight: new Map,
|
|
214
|
+
queries: [],
|
|
215
|
+
errors: []
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
var domShimInstalled = false;
|
|
219
|
+
function ensureDomShim() {
|
|
220
|
+
if (domShimInstalled && typeof document !== "undefined")
|
|
221
|
+
return;
|
|
222
|
+
domShimInstalled = true;
|
|
223
|
+
installDomShim();
|
|
236
224
|
}
|
|
237
225
|
function resolveAppFactory(module) {
|
|
238
226
|
const createApp = module.default || module.App;
|
|
@@ -252,35 +240,18 @@ function collectCSS(themeCss, module) {
|
|
|
252
240
|
for (const s of module.styles)
|
|
253
241
|
alreadyIncluded.add(s);
|
|
254
242
|
}
|
|
255
|
-
|
|
256
|
-
if (module.getInjectedCSS) {
|
|
257
|
-
componentCss = module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s));
|
|
258
|
-
} else {
|
|
259
|
-
componentCss = [];
|
|
260
|
-
const head = globalThis.document?.head;
|
|
261
|
-
if (head instanceof SSRElement) {
|
|
262
|
-
for (const child of head.children) {
|
|
263
|
-
if (child instanceof SSRElement && child.tag === "style" && "data-vertz-css" in child.attrs && child.textContent && !alreadyIncluded.has(child.textContent)) {
|
|
264
|
-
componentCss.push(child.textContent);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
243
|
+
const componentCss = module.getInjectedCSS ? module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s)) : [];
|
|
269
244
|
const componentStyles = componentCss.map((s) => `<style data-vertz-css>${s}</style>`).join(`
|
|
270
245
|
`);
|
|
271
246
|
return [themeTag, globalTags, componentStyles].filter(Boolean).join(`
|
|
272
247
|
`);
|
|
273
248
|
}
|
|
274
249
|
async function ssrRenderToString(module, url, options) {
|
|
275
|
-
return withRenderLock(() => ssrRenderToStringUnsafe(module, url, options));
|
|
276
|
-
}
|
|
277
|
-
async function ssrRenderToStringUnsafe(module, url, options) {
|
|
278
250
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
279
251
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
|
|
252
|
+
ensureDomShim();
|
|
253
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
254
|
+
return ssrStorage.run(ctx, async () => {
|
|
284
255
|
try {
|
|
285
256
|
setGlobalSSRTimeout(ssrTimeout);
|
|
286
257
|
const createApp = resolveAppFactory(module);
|
|
@@ -292,7 +263,6 @@ async function ssrRenderToStringUnsafe(module, url, options) {
|
|
|
292
263
|
console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
|
|
293
264
|
}
|
|
294
265
|
}
|
|
295
|
-
globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
|
|
296
266
|
createApp();
|
|
297
267
|
const queries = getSSRQueries();
|
|
298
268
|
const resolvedQueries = [];
|
|
@@ -321,25 +291,18 @@ async function ssrRenderToStringUnsafe(module, url, options) {
|
|
|
321
291
|
return { html, css, ssrData };
|
|
322
292
|
} finally {
|
|
323
293
|
clearGlobalSSRTimeout();
|
|
324
|
-
removeDomShim();
|
|
325
|
-
delete globalThis.__SSR_URL__;
|
|
326
294
|
}
|
|
327
295
|
});
|
|
328
296
|
}
|
|
329
297
|
async function ssrDiscoverQueries(module, url, options) {
|
|
330
|
-
return withRenderLock(() => ssrDiscoverQueriesUnsafe(module, url, options));
|
|
331
|
-
}
|
|
332
|
-
async function ssrDiscoverQueriesUnsafe(module, url, options) {
|
|
333
298
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
334
299
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
|
|
300
|
+
ensureDomShim();
|
|
301
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
302
|
+
return ssrStorage.run(ctx, async () => {
|
|
339
303
|
try {
|
|
340
304
|
setGlobalSSRTimeout(ssrTimeout);
|
|
341
305
|
const createApp = resolveAppFactory(module);
|
|
342
|
-
globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
|
|
343
306
|
createApp();
|
|
344
307
|
const queries = getSSRQueries();
|
|
345
308
|
const resolvedQueries = [];
|
|
@@ -375,8 +338,6 @@ async function ssrDiscoverQueriesUnsafe(module, url, options) {
|
|
|
375
338
|
};
|
|
376
339
|
} finally {
|
|
377
340
|
clearGlobalSSRTimeout();
|
|
378
|
-
removeDomShim();
|
|
379
|
-
delete globalThis.__SSR_URL__;
|
|
380
341
|
}
|
|
381
342
|
});
|
|
382
343
|
}
|
|
@@ -384,14 +345,12 @@ async function ssrStreamNavQueries(module, url, options) {
|
|
|
384
345
|
const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
|
|
385
346
|
const ssrTimeout = options?.ssrTimeout ?? 300;
|
|
386
347
|
const navTimeout = options?.navSsrTimeout ?? 5000;
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
|
|
348
|
+
ensureDomShim();
|
|
349
|
+
const ctx = createRequestContext(normalizedUrl);
|
|
350
|
+
const queries = await ssrStorage.run(ctx, async () => {
|
|
391
351
|
try {
|
|
392
352
|
setGlobalSSRTimeout(ssrTimeout);
|
|
393
353
|
const createApp = resolveAppFactory(module);
|
|
394
|
-
globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
|
|
395
354
|
createApp();
|
|
396
355
|
const discovered = getSSRQueries();
|
|
397
356
|
return discovered.map((q) => ({
|
|
@@ -402,10 +361,8 @@ async function ssrStreamNavQueries(module, url, options) {
|
|
|
402
361
|
}));
|
|
403
362
|
} finally {
|
|
404
363
|
clearGlobalSSRTimeout();
|
|
405
|
-
removeDomShim();
|
|
406
|
-
delete globalThis.__SSR_URL__;
|
|
407
364
|
}
|
|
408
|
-
})
|
|
365
|
+
});
|
|
409
366
|
if (queries.length === 0) {
|
|
410
367
|
const encoder3 = new TextEncoder;
|
|
411
368
|
return new ReadableStream({
|
|
@@ -561,4 +518,4 @@ async function handleHTMLRequest(module, template, url, ssrTimeout, nonce) {
|
|
|
561
518
|
}
|
|
562
519
|
}
|
|
563
520
|
|
|
564
|
-
export { escapeHtml, escapeAttr, serializeToHtml, resetSlotCounter, createSlotPlaceholder, encodeChunk, streamToString, collectStreamChunks, createTemplateChunk, renderToStream,
|
|
521
|
+
export { escapeHtml, escapeAttr, serializeToHtml, resetSlotCounter, createSlotPlaceholder, encodeChunk, streamToString, collectStreamChunks, createTemplateChunk, renderToStream, safeSerialize, getStreamingRuntimeScript, createSSRDataChunk, createRequestContext, ssrRenderToString, ssrDiscoverQueries, createSSRHandler };
|
|
@@ -320,6 +320,37 @@ function createSSRAdapter() {
|
|
|
320
320
|
};
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
// src/ssr-context.ts
|
|
324
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
325
|
+
import { registerSSRResolver } from "@vertz/ui/internals";
|
|
326
|
+
var ssrStorage = new AsyncLocalStorage;
|
|
327
|
+
registerSSRResolver(() => ssrStorage.getStore());
|
|
328
|
+
function isInSSR() {
|
|
329
|
+
return ssrStorage.getStore() !== undefined;
|
|
330
|
+
}
|
|
331
|
+
function getSSRUrl() {
|
|
332
|
+
return ssrStorage.getStore()?.url;
|
|
333
|
+
}
|
|
334
|
+
function registerSSRQuery(entry) {
|
|
335
|
+
ssrStorage.getStore()?.queries.push(entry);
|
|
336
|
+
}
|
|
337
|
+
function getSSRQueries() {
|
|
338
|
+
return ssrStorage.getStore()?.queries ?? [];
|
|
339
|
+
}
|
|
340
|
+
function setGlobalSSRTimeout(timeout) {
|
|
341
|
+
const store = ssrStorage.getStore();
|
|
342
|
+
if (store)
|
|
343
|
+
store.globalSSRTimeout = timeout;
|
|
344
|
+
}
|
|
345
|
+
function clearGlobalSSRTimeout() {
|
|
346
|
+
const store = ssrStorage.getStore();
|
|
347
|
+
if (store)
|
|
348
|
+
store.globalSSRTimeout = undefined;
|
|
349
|
+
}
|
|
350
|
+
function getGlobalSSRTimeout() {
|
|
351
|
+
return ssrStorage.getStore()?.globalSSRTimeout;
|
|
352
|
+
}
|
|
353
|
+
|
|
323
354
|
// src/dom-shim/index.ts
|
|
324
355
|
var SHIM_GLOBALS = [
|
|
325
356
|
"document",
|
|
@@ -355,10 +386,6 @@ function installDomShim() {
|
|
|
355
386
|
}
|
|
356
387
|
installedGlobals = [];
|
|
357
388
|
setAdapter(createSSRAdapter());
|
|
358
|
-
const isSSRContext = typeof globalThis.__SSR_URL__ !== "undefined";
|
|
359
|
-
if (typeof document !== "undefined" && !isSSRContext && !shimInstalled) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
389
|
if (!shimInstalled) {
|
|
363
390
|
savedGlobals = new Map;
|
|
364
391
|
for (const g of SHIM_GLOBALS) {
|
|
@@ -391,7 +418,7 @@ function installDomShim() {
|
|
|
391
418
|
globalThis.document = fakeDocument;
|
|
392
419
|
if (typeof window === "undefined") {
|
|
393
420
|
globalThis.window = {
|
|
394
|
-
location: { pathname:
|
|
421
|
+
location: { pathname: ssrStorage.getStore()?.url || "/", search: "", hash: "" },
|
|
395
422
|
addEventListener: () => {},
|
|
396
423
|
removeEventListener: () => {},
|
|
397
424
|
history: {
|
|
@@ -402,7 +429,7 @@ function installDomShim() {
|
|
|
402
429
|
} else {
|
|
403
430
|
globalThis.window.location = {
|
|
404
431
|
...globalThis.window.location || {},
|
|
405
|
-
pathname:
|
|
432
|
+
pathname: ssrStorage.getStore()?.url || "/"
|
|
406
433
|
};
|
|
407
434
|
}
|
|
408
435
|
globalThis.Node = SSRNode;
|
|
@@ -510,4 +537,4 @@ function toVNode(element) {
|
|
|
510
537
|
return { tag: "span", attrs: {}, children: [String(element)] };
|
|
511
538
|
}
|
|
512
539
|
|
|
513
|
-
export { SSRNode, SSRComment, rawHtml, SSRTextNode, SSRDocumentFragment, SSRElement, createSSRAdapter, installDomShim, removeDomShim, toVNode };
|
|
540
|
+
export { ssrStorage, isInSSR, getSSRUrl, registerSSRQuery, getSSRQueries, setGlobalSSRTimeout, clearGlobalSSRTimeout, getGlobalSSRTimeout, SSRNode, SSRComment, rawHtml, SSRTextNode, SSRDocumentFragment, SSRElement, createSSRAdapter, installDomShim, removeDomShim, toVNode };
|
package/dist/ssr/index.js
CHANGED
|
@@ -2,8 +2,8 @@ import {
|
|
|
2
2
|
createSSRHandler,
|
|
3
3
|
ssrDiscoverQueries,
|
|
4
4
|
ssrRenderToString
|
|
5
|
-
} from "../shared/chunk-
|
|
6
|
-
import"../shared/chunk-
|
|
5
|
+
} from "../shared/chunk-98972e43.js";
|
|
6
|
+
import"../shared/chunk-n1arq9xq.js";
|
|
7
7
|
export {
|
|
8
8
|
ssrRenderToString,
|
|
9
9
|
ssrDiscoverQueries,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vertz/ui-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Vertz UI server-side rendering runtime",
|
|
@@ -39,6 +39,10 @@
|
|
|
39
39
|
"./bun-dev-server": {
|
|
40
40
|
"import": "./dist/bun-dev-server.js",
|
|
41
41
|
"types": "./dist/bun-dev-server.d.ts"
|
|
42
|
+
},
|
|
43
|
+
"./fetch-scope": {
|
|
44
|
+
"import": "./dist/fetch-scope.js",
|
|
45
|
+
"types": "./dist/fetch-scope.d.ts"
|
|
42
46
|
}
|
|
43
47
|
},
|
|
44
48
|
"files": [
|
|
@@ -53,9 +57,9 @@
|
|
|
53
57
|
"dependencies": {
|
|
54
58
|
"@ampproject/remapping": "^2.3.0",
|
|
55
59
|
"@jridgewell/trace-mapping": "^0.3.31",
|
|
56
|
-
"@vertz/core": "^0.2.
|
|
57
|
-
"@vertz/ui": "^0.2.
|
|
58
|
-
"@vertz/ui-compiler": "^0.2.
|
|
60
|
+
"@vertz/core": "^0.2.12",
|
|
61
|
+
"@vertz/ui": "^0.2.12",
|
|
62
|
+
"@vertz/ui-compiler": "^0.2.12",
|
|
59
63
|
"magic-string": "^0.30.0",
|
|
60
64
|
"ts-morph": "^27.0.2"
|
|
61
65
|
},
|