@vertz/ui-server 0.2.15 → 0.2.16
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 +36 -1
- package/dist/bun-dev-server.js +352 -23
- package/dist/bun-plugin/index.d.ts +73 -1
- package/dist/bun-plugin/index.js +964 -17
- package/dist/dom-shim/index.js +1 -1
- package/dist/index.d.ts +84 -12
- package/dist/index.js +127 -41
- package/dist/shared/{chunk-98972e43.js → chunk-969qgkdf.js} +186 -23
- package/dist/shared/{chunk-n1arq9xq.js → chunk-9jjdzz8c.js} +2 -2
- package/dist/shared/chunk-gggnhyqj.js +57 -0
- package/dist/ssr/index.d.ts +116 -9
- package/dist/ssr/index.js +79 -2
- package/package.json +8 -5
package/dist/dom-shim/index.js
CHANGED
package/dist/index.d.ts
CHANGED
|
@@ -80,6 +80,27 @@ declare function renderAssetTags(assets: AssetDescriptor[]): string;
|
|
|
80
80
|
* Returns an empty string if the CSS is empty.
|
|
81
81
|
*/
|
|
82
82
|
declare function inlineCriticalCss(css: string): string;
|
|
83
|
+
import { FallbackFontName, FontDescriptor, FontFallbackMetrics } from "@vertz/ui";
|
|
84
|
+
/**
|
|
85
|
+
* Auto-detect which system font to use as fallback base.
|
|
86
|
+
*
|
|
87
|
+
* Scans the `fallback` array for generic CSS font family keywords:
|
|
88
|
+
* - 'sans-serif' or 'system-ui' → Arial
|
|
89
|
+
* - 'serif' → Times New Roman
|
|
90
|
+
* - 'monospace' → Courier New
|
|
91
|
+
*
|
|
92
|
+
* Skips non-generic entries (e.g., 'Georgia', 'Helvetica').
|
|
93
|
+
* If no generic keyword found, defaults to Arial.
|
|
94
|
+
*/
|
|
95
|
+
declare function detectFallbackFont(fallback: readonly string[]): FallbackFontName;
|
|
96
|
+
/**
|
|
97
|
+
* Extract font metrics from .woff2 files and compute CSS fallback overrides.
|
|
98
|
+
*
|
|
99
|
+
* @param fonts - Font descriptors from theme definition.
|
|
100
|
+
* @param rootDir - Project root directory for resolving font file paths.
|
|
101
|
+
* @returns Map of font key → computed fallback metrics.
|
|
102
|
+
*/
|
|
103
|
+
declare function extractFontMetrics(fonts: Record<string, FontDescriptor>, rootDir: string): Promise<Record<string, FontFallbackMetrics>>;
|
|
83
104
|
/**
|
|
84
105
|
* Collector for `<head>` metadata during SSR.
|
|
85
106
|
*
|
|
@@ -190,7 +211,7 @@ interface PageOptions {
|
|
|
190
211
|
* ```
|
|
191
212
|
*/
|
|
192
213
|
declare function renderPage(vnode: VNode, options?: PageOptions): Response;
|
|
193
|
-
import { Theme } from "@vertz/ui";
|
|
214
|
+
import { FontFallbackMetrics as FontFallbackMetrics2, Theme } from "@vertz/ui";
|
|
194
215
|
interface RenderToHTMLOptions<AppFn extends () => VNode> {
|
|
195
216
|
/** The app component function */
|
|
196
217
|
app: AppFn;
|
|
@@ -215,6 +236,8 @@ interface RenderToHTMLOptions<AppFn extends () => VNode> {
|
|
|
215
236
|
};
|
|
216
237
|
/** Container selector (default '#app') */
|
|
217
238
|
container?: string;
|
|
239
|
+
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
240
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics2>;
|
|
218
241
|
}
|
|
219
242
|
interface RenderToHTMLStreamOptions<AppFn extends () => VNode> extends RenderToHTMLOptions<AppFn> {
|
|
220
243
|
/** CSP nonce for inline scripts */
|
|
@@ -350,7 +373,8 @@ declare function clearGlobalSSRTimeout(): void;
|
|
|
350
373
|
* Returns undefined if not set or outside SSR context.
|
|
351
374
|
*/
|
|
352
375
|
declare function getGlobalSSRTimeout(): number | undefined;
|
|
353
|
-
import {
|
|
376
|
+
import { FontFallbackMetrics as FontFallbackMetrics4 } from "@vertz/ui";
|
|
377
|
+
import { FontFallbackMetrics as FontFallbackMetrics3, Theme as Theme2 } from "@vertz/ui";
|
|
354
378
|
interface SSRModule {
|
|
355
379
|
default?: () => unknown;
|
|
356
380
|
App?: () => unknown;
|
|
@@ -373,6 +397,10 @@ interface SSRRenderResult {
|
|
|
373
397
|
key: string;
|
|
374
398
|
data: unknown;
|
|
375
399
|
}>;
|
|
400
|
+
/** Font preload link tags for injection into <head>. */
|
|
401
|
+
headTags: string;
|
|
402
|
+
/** Route patterns discovered by createRouter() during SSR (for build-time pre-rendering). */
|
|
403
|
+
discoveredRoutes?: string[];
|
|
376
404
|
}
|
|
377
405
|
interface SSRDiscoverResult {
|
|
378
406
|
resolved: Array<{
|
|
@@ -390,6 +418,8 @@ interface SSRDiscoverResult {
|
|
|
390
418
|
*/
|
|
391
419
|
declare function ssrRenderToString(module: SSRModule, url: string, options?: {
|
|
392
420
|
ssrTimeout?: number;
|
|
421
|
+
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
422
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics3>;
|
|
393
423
|
}): Promise<SSRRenderResult>;
|
|
394
424
|
/**
|
|
395
425
|
* Discover queries for a given URL without rendering.
|
|
@@ -399,6 +429,41 @@ declare function ssrRenderToString(module: SSRModule, url: string, options?: {
|
|
|
399
429
|
declare function ssrDiscoverQueries(module: SSRModule, url: string, options?: {
|
|
400
430
|
ssrTimeout?: number;
|
|
401
431
|
}): Promise<SSRDiscoverResult>;
|
|
432
|
+
import { AccessSet as AccessSet2 } from "@vertz/ui/auth";
|
|
433
|
+
interface SessionData {
|
|
434
|
+
user: {
|
|
435
|
+
id: string;
|
|
436
|
+
email: string;
|
|
437
|
+
role: string;
|
|
438
|
+
[key: string]: unknown;
|
|
439
|
+
};
|
|
440
|
+
/** Unix timestamp in milliseconds (JWT exp * 1000). */
|
|
441
|
+
expiresAt: number;
|
|
442
|
+
}
|
|
443
|
+
/** Resolved session data for SSR injection. */
|
|
444
|
+
interface SSRSessionInfo {
|
|
445
|
+
session: SessionData;
|
|
446
|
+
/**
|
|
447
|
+
* Access set from JWT acl claim.
|
|
448
|
+
* - Present (object): inline access set (no overflow)
|
|
449
|
+
* - null: access control is configured but the set overflowed the JWT
|
|
450
|
+
* - undefined: access control is not configured
|
|
451
|
+
*/
|
|
452
|
+
accessSet?: AccessSet2 | null;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Callback that extracts session data from a request.
|
|
456
|
+
* Returns null when no valid session exists (expired, missing, or invalid cookie).
|
|
457
|
+
*/
|
|
458
|
+
type SessionResolver = (request: Request) => Promise<SSRSessionInfo | null>;
|
|
459
|
+
/**
|
|
460
|
+
* Serialize a session into a `<script>` tag that sets
|
|
461
|
+
* `window.__VERTZ_SESSION__`.
|
|
462
|
+
*
|
|
463
|
+
* @param session - The session data to serialize
|
|
464
|
+
* @param nonce - Optional CSP nonce for the script tag
|
|
465
|
+
*/
|
|
466
|
+
declare function createSessionScript(session: SessionData, nonce?: string): string;
|
|
402
467
|
interface SSRHandlerOptions {
|
|
403
468
|
/** The loaded SSR module (import('./dist/server/index.js')) */
|
|
404
469
|
module: SSRModule;
|
|
@@ -424,16 +489,19 @@ interface SSRHandlerOptions {
|
|
|
424
489
|
* so that strict Content-Security-Policy headers do not block it.
|
|
425
490
|
*/
|
|
426
491
|
nonce?: string;
|
|
492
|
+
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
493
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics4>;
|
|
494
|
+
/** Paths to inject as `<link rel="modulepreload">` in `<head>`. */
|
|
495
|
+
modulepreload?: string[];
|
|
496
|
+
/** Cache-Control header for HTML responses. Omit or undefined = no header (safe default). */
|
|
497
|
+
cacheControl?: string;
|
|
498
|
+
/**
|
|
499
|
+
* Resolves session data from request cookies for SSR injection.
|
|
500
|
+
* When provided, SSR HTML includes `window.__VERTZ_SESSION__` and
|
|
501
|
+
* optionally `window.__VERTZ_ACCESS_SET__` for instant auth hydration.
|
|
502
|
+
*/
|
|
503
|
+
sessionResolver?: SessionResolver;
|
|
427
504
|
}
|
|
428
|
-
/**
|
|
429
|
-
* Create a web-standard SSR request handler.
|
|
430
|
-
*
|
|
431
|
-
* Handles two types of requests:
|
|
432
|
-
* - X-Vertz-Nav: 1 -> SSE Response with pre-fetched query data
|
|
433
|
-
* - Normal HTML request -> SSR-rendered HTML Response
|
|
434
|
-
*
|
|
435
|
-
* Does NOT serve static files — that's the adapter/platform's job.
|
|
436
|
-
*/
|
|
437
505
|
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
438
506
|
interface GenerateSSRHtmlOptions {
|
|
439
507
|
appHtml: string;
|
|
@@ -444,6 +512,10 @@ interface GenerateSSRHtmlOptions {
|
|
|
444
512
|
}>;
|
|
445
513
|
clientEntry: string;
|
|
446
514
|
title?: string;
|
|
515
|
+
/** Extra HTML tags to inject into <head> before CSS (e.g., font preloads). */
|
|
516
|
+
headTags?: string;
|
|
517
|
+
/** Paths to inject as `<link rel="modulepreload">` in `<head>`. */
|
|
518
|
+
modulepreload?: string[];
|
|
447
519
|
}
|
|
448
520
|
/**
|
|
449
521
|
* Generate a complete HTML document from SSR render results.
|
|
@@ -499,4 +571,4 @@ declare function collectStreamChunks(stream: ReadableStream<Uint8Array>): Promis
|
|
|
499
571
|
* @param nonce - Optional CSP nonce to add to the inline script tag.
|
|
500
572
|
*/
|
|
501
573
|
declare function createTemplateChunk(slotId: number, resolvedHtml: string, nonce?: string): string;
|
|
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 };
|
|
574
|
+
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, extractFontMetrics, encodeChunk, detectFallbackFont, createTemplateChunk, createSlotPlaceholder, createSessionScript, createSSRHandler, createSSRDataChunk, createSSRAdapter, createAccessSetScript, collectStreamChunks, clearGlobalSSRTimeout, VNode, SessionResolver, SessionData, SSRSessionInfo, SSRRenderResult, SSRQueryEntry2 as SSRQueryEntry, SSRModule, SSRHandlerOptions, SSRDiscoverResult, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, RawHtml, PageOptions, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, AssetDescriptor };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
collectStreamChunks,
|
|
3
|
+
createAccessSetScript,
|
|
3
4
|
createRequestContext,
|
|
4
5
|
createSSRDataChunk,
|
|
5
6
|
createSSRHandler,
|
|
7
|
+
createSessionScript,
|
|
6
8
|
createSlotPlaceholder,
|
|
7
9
|
createTemplateChunk,
|
|
8
10
|
encodeChunk,
|
|
9
11
|
escapeAttr,
|
|
10
12
|
escapeHtml,
|
|
13
|
+
getAccessSetForSSR,
|
|
11
14
|
getStreamingRuntimeScript,
|
|
12
15
|
renderToStream,
|
|
13
16
|
resetSlotCounter,
|
|
@@ -16,7 +19,7 @@ import {
|
|
|
16
19
|
ssrDiscoverQueries,
|
|
17
20
|
ssrRenderToString,
|
|
18
21
|
streamToString
|
|
19
|
-
} from "./shared/chunk-
|
|
22
|
+
} from "./shared/chunk-969qgkdf.js";
|
|
20
23
|
import {
|
|
21
24
|
clearGlobalSSRTimeout,
|
|
22
25
|
createSSRAdapter,
|
|
@@ -28,7 +31,7 @@ import {
|
|
|
28
31
|
registerSSRQuery,
|
|
29
32
|
setGlobalSSRTimeout,
|
|
30
33
|
ssrStorage
|
|
31
|
-
} from "./shared/chunk-
|
|
34
|
+
} from "./shared/chunk-9jjdzz8c.js";
|
|
32
35
|
|
|
33
36
|
// src/asset-pipeline.ts
|
|
34
37
|
function renderAssetTags(assets) {
|
|
@@ -55,6 +58,108 @@ function inlineCriticalCss(css) {
|
|
|
55
58
|
const safeCss = css.replace(/<\/style>/gi, "<\\/style>");
|
|
56
59
|
return `<style>${safeCss}</style>`;
|
|
57
60
|
}
|
|
61
|
+
// src/font-metrics.ts
|
|
62
|
+
import { readFile } from "node:fs/promises";
|
|
63
|
+
import { join } from "node:path";
|
|
64
|
+
import { fromBuffer } from "@capsizecss/unpack";
|
|
65
|
+
var SYSTEM_FONT_METRICS = {
|
|
66
|
+
Arial: {
|
|
67
|
+
ascent: 1854,
|
|
68
|
+
descent: -434,
|
|
69
|
+
lineGap: 67,
|
|
70
|
+
unitsPerEm: 2048,
|
|
71
|
+
xWidthAvg: 904
|
|
72
|
+
},
|
|
73
|
+
"Times New Roman": {
|
|
74
|
+
ascent: 1825,
|
|
75
|
+
descent: -443,
|
|
76
|
+
lineGap: 87,
|
|
77
|
+
unitsPerEm: 2048,
|
|
78
|
+
xWidthAvg: 819
|
|
79
|
+
},
|
|
80
|
+
"Courier New": {
|
|
81
|
+
ascent: 1705,
|
|
82
|
+
descent: -615,
|
|
83
|
+
lineGap: 0,
|
|
84
|
+
unitsPerEm: 2048,
|
|
85
|
+
xWidthAvg: 1229
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function detectFallbackFont(fallback) {
|
|
89
|
+
for (const f of fallback) {
|
|
90
|
+
const lower = f.toLowerCase();
|
|
91
|
+
if (lower === "sans-serif" || lower === "system-ui")
|
|
92
|
+
return "Arial";
|
|
93
|
+
if (lower === "serif")
|
|
94
|
+
return "Times New Roman";
|
|
95
|
+
if (lower === "monospace")
|
|
96
|
+
return "Courier New";
|
|
97
|
+
}
|
|
98
|
+
return "Arial";
|
|
99
|
+
}
|
|
100
|
+
function formatPercent(value) {
|
|
101
|
+
return `${(value * 100).toFixed(2)}%`;
|
|
102
|
+
}
|
|
103
|
+
function computeFallbackMetrics(fontMetrics, fallbackFont) {
|
|
104
|
+
const systemMetrics = SYSTEM_FONT_METRICS[fallbackFont];
|
|
105
|
+
const fontNormalizedWidth = fontMetrics.xWidthAvg / fontMetrics.unitsPerEm;
|
|
106
|
+
const systemNormalizedWidth = systemMetrics.xWidthAvg / systemMetrics.unitsPerEm;
|
|
107
|
+
const sizeAdjust = fontNormalizedWidth / systemNormalizedWidth;
|
|
108
|
+
const ascentOverride = fontMetrics.ascent / (fontMetrics.unitsPerEm * sizeAdjust);
|
|
109
|
+
const descentOverride = Math.abs(fontMetrics.descent) / (fontMetrics.unitsPerEm * sizeAdjust);
|
|
110
|
+
const lineGapOverride = fontMetrics.lineGap / (fontMetrics.unitsPerEm * sizeAdjust);
|
|
111
|
+
return {
|
|
112
|
+
ascentOverride: formatPercent(ascentOverride),
|
|
113
|
+
descentOverride: formatPercent(descentOverride),
|
|
114
|
+
lineGapOverride: formatPercent(lineGapOverride),
|
|
115
|
+
sizeAdjust: formatPercent(sizeAdjust),
|
|
116
|
+
fallbackFont
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function getPrimarySrcPath(descriptor) {
|
|
120
|
+
const { src } = descriptor;
|
|
121
|
+
if (!src)
|
|
122
|
+
return null;
|
|
123
|
+
if (typeof src === "string")
|
|
124
|
+
return src;
|
|
125
|
+
const first = src[0];
|
|
126
|
+
if (first)
|
|
127
|
+
return first.path;
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
function resolveFilePath(urlPath, rootDir) {
|
|
131
|
+
const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
|
|
132
|
+
return join(rootDir, cleaned);
|
|
133
|
+
}
|
|
134
|
+
async function extractFontMetrics(fonts, rootDir) {
|
|
135
|
+
const result = {};
|
|
136
|
+
for (const [key, descriptor] of Object.entries(fonts)) {
|
|
137
|
+
const adjustFontFallback = descriptor.adjustFontFallback ?? true;
|
|
138
|
+
if (adjustFontFallback === false)
|
|
139
|
+
continue;
|
|
140
|
+
const srcPath = getPrimarySrcPath(descriptor);
|
|
141
|
+
if (!srcPath)
|
|
142
|
+
continue;
|
|
143
|
+
if (!srcPath.toLowerCase().endsWith(".woff2"))
|
|
144
|
+
continue;
|
|
145
|
+
try {
|
|
146
|
+
const filePath = resolveFilePath(srcPath, rootDir);
|
|
147
|
+
const buffer = await readFile(filePath);
|
|
148
|
+
const metrics = await fromBuffer(buffer);
|
|
149
|
+
const fallbackFont = typeof adjustFontFallback === "string" ? adjustFontFallback : detectFallbackFont(descriptor.fallback);
|
|
150
|
+
result[key] = computeFallbackMetrics({
|
|
151
|
+
ascent: metrics.ascent,
|
|
152
|
+
descent: metrics.descent,
|
|
153
|
+
lineGap: metrics.lineGap,
|
|
154
|
+
unitsPerEm: metrics.unitsPerEm,
|
|
155
|
+
xWidthAvg: metrics.xWidthAvg
|
|
156
|
+
}, fallbackFont);
|
|
157
|
+
} catch (error) {
|
|
158
|
+
console.warn(`[vertz] Failed to extract font metrics for "${key}" from "${srcPath}":`, error instanceof Error ? error.message : error);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
58
163
|
// src/head.ts
|
|
59
164
|
class HeadCollector {
|
|
60
165
|
entries = [];
|
|
@@ -233,10 +338,10 @@ async function twoPassRender(options) {
|
|
|
233
338
|
const pendingQueries = queries.filter((q) => !q.resolved);
|
|
234
339
|
const vnode = options.app();
|
|
235
340
|
const collectedCSS = getInjectedCSS();
|
|
236
|
-
const themeCss = options.theme ? compileTheme(options.theme).css : "";
|
|
341
|
+
const themeCss = options.theme ? compileTheme(options.theme, { fallbackMetrics: options.fallbackMetrics }).css : "";
|
|
237
342
|
const allStyles = [themeCss, ...options.styles ?? [], ...collectedCSS].filter(Boolean);
|
|
238
|
-
const styleTags = allStyles.
|
|
239
|
-
`);
|
|
343
|
+
const styleTags = allStyles.length > 0 ? `<style>${allStyles.join(`
|
|
344
|
+
`)}</style>` : "";
|
|
240
345
|
const metaHtml = options.head?.meta?.map((m) => `<meta ${m.name ? `name="${m.name}"` : `property="${m.property}"`} content="${m.content}">`).join(`
|
|
241
346
|
`) ?? "";
|
|
242
347
|
const linkHtml = options.head?.links?.map((link) => `<link rel="${link.rel}" href="${link.href}">`).join(`
|
|
@@ -313,44 +418,21 @@ async function renderToHTML(appOrOptions, maybeOptions) {
|
|
|
313
418
|
}
|
|
314
419
|
});
|
|
315
420
|
}
|
|
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
|
-
}
|
|
351
421
|
// src/ssr-html.ts
|
|
352
422
|
function generateSSRHtml(options) {
|
|
353
|
-
const {
|
|
423
|
+
const {
|
|
424
|
+
appHtml,
|
|
425
|
+
css,
|
|
426
|
+
ssrData,
|
|
427
|
+
clientEntry,
|
|
428
|
+
title = "Vertz App",
|
|
429
|
+
headTags: rawHeadTags = "",
|
|
430
|
+
modulepreload
|
|
431
|
+
} = options;
|
|
432
|
+
const modulepreloadTags = modulepreload?.length ? modulepreload.map((p) => `<link rel="modulepreload" href="${escapeAttr(p)}">`).join(`
|
|
433
|
+
`) : "";
|
|
434
|
+
const headTags = [rawHeadTags, modulepreloadTags].filter(Boolean).join(`
|
|
435
|
+
`);
|
|
354
436
|
const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__ = ${JSON.stringify(ssrData)};</script>` : "";
|
|
355
437
|
return `<!doctype html>
|
|
356
438
|
<html lang="en">
|
|
@@ -358,6 +440,7 @@ function generateSSRHtml(options) {
|
|
|
358
440
|
<meta charset="UTF-8" />
|
|
359
441
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
360
442
|
<title>${escapeHtml(title)}</title>
|
|
443
|
+
${headTags}
|
|
361
444
|
${css}
|
|
362
445
|
</head>
|
|
363
446
|
<body>
|
|
@@ -393,9 +476,12 @@ export {
|
|
|
393
476
|
getGlobalSSRTimeout,
|
|
394
477
|
getAccessSetForSSR,
|
|
395
478
|
generateSSRHtml,
|
|
479
|
+
extractFontMetrics,
|
|
396
480
|
encodeChunk,
|
|
481
|
+
detectFallbackFont,
|
|
397
482
|
createTemplateChunk,
|
|
398
483
|
createSlotPlaceholder,
|
|
484
|
+
createSessionScript,
|
|
399
485
|
createSSRHandler,
|
|
400
486
|
createSSRDataChunk,
|
|
401
487
|
createSSRAdapter,
|