@vertz/ui-server 0.2.30 → 0.2.32
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.js +47 -2
- package/dist/dom-shim/index.js +1 -1
- package/dist/index.d.ts +818 -747
- package/dist/index.js +86 -150
- package/dist/node-handler.d.ts +223 -0
- package/dist/node-handler.js +8 -0
- package/dist/shared/{chunk-bd0sgykf.js → chunk-34fexgex.js} +247 -431
- package/dist/shared/chunk-es0406qq.js +227 -0
- package/dist/shared/chunk-gbcsa7h1.js +471 -0
- package/dist/shared/{chunk-gcwqkynf.js → chunk-ybftdw1r.js} +1 -1
- package/dist/ssr/index.d.ts +119 -57
- package/dist/ssr/index.js +6 -3
- package/package.json +9 -5
package/dist/index.d.ts
CHANGED
|
@@ -1,751 +1,925 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
}
|
|
6
|
-
interface AotBuildManifest {
|
|
7
|
-
components: Record<string, AotBuildComponentEntry>;
|
|
8
|
-
classificationLog: string[];
|
|
9
|
-
}
|
|
10
|
-
/**
|
|
11
|
-
* Scan all TSX files in srcDir and generate an AOT build manifest.
|
|
12
|
-
*/
|
|
13
|
-
declare function generateAotBuildManifest(srcDir: string): AotBuildManifest;
|
|
14
|
-
/** A raw HTML string that bypasses escaping during serialization. */
|
|
15
|
-
interface RawHtml {
|
|
16
|
-
__raw: true;
|
|
17
|
-
html: string;
|
|
18
|
-
}
|
|
1
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
|
+
import { FontFallbackMetrics as FontFallbackMetrics4 } from "@vertz/ui";
|
|
3
|
+
import { FontFallbackMetrics as FontFallbackMetrics3 } from "@vertz/ui";
|
|
4
|
+
import { SSRAuth as SSRAuth2 } from "@vertz/ui/internals";
|
|
19
5
|
/**
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* **WARNING: XSS RISK** — This function bypasses all HTML escaping. Never pass
|
|
23
|
-
* user-controlled input directly. Always sanitize with a trusted library (e.g.
|
|
24
|
-
* DOMPurify) before wrapping in `rawHtml()`.
|
|
6
|
+
* SSR prefetch access rule evaluator.
|
|
25
7
|
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* rawHtml('<svg>...</svg>') // static markup — OK
|
|
29
|
-
* rawHtml(DOMPurify.sanitize(userInput)) // sanitized — OK
|
|
30
|
-
* ```
|
|
8
|
+
* Evaluates serialized entity access rules against the current session
|
|
9
|
+
* to determine whether a query should be prefetched during SSR.
|
|
31
10
|
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* rawHtml(userInput) // XSS vulnerability
|
|
35
|
-
* rawHtml(`<div>${userInput}</div>`) // XSS via interpolation
|
|
36
|
-
* ```
|
|
11
|
+
* The serialized rules come from the prefetch manifest (generated at build time).
|
|
12
|
+
* The session comes from the JWT decoded at request time.
|
|
37
13
|
*/
|
|
38
|
-
declare function rawHtml(html: string): RawHtml;
|
|
39
|
-
/** Virtual node representing an HTML element for SSR serialization. */
|
|
40
|
-
interface VNode {
|
|
41
|
-
tag: string;
|
|
42
|
-
attrs: Record<string, string>;
|
|
43
|
-
children: (VNode | string | RawHtml)[];
|
|
44
|
-
}
|
|
45
|
-
/** Options for hydration marker generation. */
|
|
46
|
-
interface HydrationOptions {
|
|
47
|
-
/** Component name for `data-v-id`. */
|
|
48
|
-
componentName: string;
|
|
49
|
-
/** Unique key for `data-v-key`. */
|
|
50
|
-
key: string;
|
|
51
|
-
/** Serialized props to embed as JSON. */
|
|
52
|
-
props?: Record<string, unknown>;
|
|
53
|
-
}
|
|
54
|
-
/** Metadata collected by the Head component during rendering. */
|
|
55
|
-
interface HeadEntry {
|
|
56
|
-
tag: "title" | "meta" | "link";
|
|
57
|
-
attrs?: Record<string, string>;
|
|
58
|
-
textContent?: string;
|
|
59
|
-
}
|
|
60
|
-
/** Options for {@link renderToStream}. */
|
|
61
|
-
interface RenderToStreamOptions {
|
|
62
|
-
/**
|
|
63
|
-
* CSP nonce to inject on all inline `<script>` tags emitted during SSR.
|
|
64
|
-
*
|
|
65
|
-
* When set, every inline script (e.g. Suspense replacement scripts) will
|
|
66
|
-
* include `nonce="<value>"` so that strict Content-Security-Policy headers
|
|
67
|
-
* do not block them.
|
|
68
|
-
*/
|
|
69
|
-
nonce?: string;
|
|
70
|
-
}
|
|
71
|
-
/** Asset descriptor for script/stylesheet injection. */
|
|
72
|
-
interface AssetDescriptor {
|
|
73
|
-
type: "script" | "stylesheet";
|
|
74
|
-
src: string;
|
|
75
|
-
/** Whether to add `async` attribute (scripts only). */
|
|
76
|
-
async?: boolean;
|
|
77
|
-
/** Whether to add `defer` attribute (scripts only). */
|
|
78
|
-
defer?: boolean;
|
|
79
|
-
}
|
|
80
14
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
* - Stylesheets: `<link rel="stylesheet" href="...">`
|
|
15
|
+
* Serialized access rule — the JSON-friendly format stored in the manifest.
|
|
16
|
+
* Mirrors SerializedRule from @vertz/server/auth/rules but defined here
|
|
17
|
+
* to avoid importing the server package into the SSR pipeline.
|
|
85
18
|
*/
|
|
86
|
-
|
|
19
|
+
type SerializedAccessRule = {
|
|
20
|
+
type: "public";
|
|
21
|
+
} | {
|
|
22
|
+
type: "authenticated";
|
|
23
|
+
} | {
|
|
24
|
+
type: "role";
|
|
25
|
+
roles: string[];
|
|
26
|
+
} | {
|
|
27
|
+
type: "entitlement";
|
|
28
|
+
value: string;
|
|
29
|
+
} | {
|
|
30
|
+
type: "where";
|
|
31
|
+
conditions: Record<string, unknown>;
|
|
32
|
+
} | {
|
|
33
|
+
type: "all";
|
|
34
|
+
rules: SerializedAccessRule[];
|
|
35
|
+
} | {
|
|
36
|
+
type: "any";
|
|
37
|
+
rules: SerializedAccessRule[];
|
|
38
|
+
} | {
|
|
39
|
+
type: "fva";
|
|
40
|
+
maxAge: number;
|
|
41
|
+
} | {
|
|
42
|
+
type: "deny";
|
|
43
|
+
};
|
|
87
44
|
/**
|
|
88
|
-
*
|
|
89
|
-
*
|
|
90
|
-
* Wraps the provided CSS in `<style>...</style>` for embedding in the HTML head.
|
|
91
|
-
* Escapes any `</style>` sequences in the CSS content to prevent injection.
|
|
92
|
-
*
|
|
93
|
-
* Returns an empty string if the CSS is empty.
|
|
45
|
+
* Minimal session shape needed for prefetch access evaluation.
|
|
46
|
+
* Extracted from the JWT at SSR request time.
|
|
94
47
|
*/
|
|
95
|
-
|
|
96
|
-
|
|
48
|
+
type PrefetchSession = {
|
|
49
|
+
status: "authenticated";
|
|
50
|
+
roles?: string[];
|
|
51
|
+
entitlements?: Record<string, boolean>;
|
|
52
|
+
tenantId?: string;
|
|
53
|
+
} | {
|
|
54
|
+
status: "unauthenticated";
|
|
55
|
+
};
|
|
97
56
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* Scans the `fallback` array for generic CSS font family keywords:
|
|
101
|
-
* - 'sans-serif' or 'system-ui' → Arial
|
|
102
|
-
* - 'serif' → Times New Roman
|
|
103
|
-
* - 'monospace' → Courier New
|
|
104
|
-
*
|
|
105
|
-
* Skips non-generic entries (e.g., 'Georgia', 'Helvetica').
|
|
106
|
-
* If no generic keyword found, defaults to Arial.
|
|
57
|
+
* Minimal shape of an AccessSet for entitlement extraction.
|
|
58
|
+
* Avoids importing @vertz/server types into the SSR pipeline.
|
|
107
59
|
*/
|
|
108
|
-
|
|
60
|
+
interface AccessSetLike {
|
|
61
|
+
entitlements: Record<string, {
|
|
62
|
+
allowed: boolean;
|
|
63
|
+
}>;
|
|
64
|
+
}
|
|
109
65
|
/**
|
|
110
|
-
*
|
|
66
|
+
* Convert SSRAuth (from the JWT/session resolver) to PrefetchSession
|
|
67
|
+
* for entity access evaluation during SSR prefetching.
|
|
111
68
|
*
|
|
112
|
-
* @param
|
|
113
|
-
* @param
|
|
114
|
-
* @returns Map of font key → computed fallback metrics.
|
|
69
|
+
* @param ssrAuth - Auth state from session resolver
|
|
70
|
+
* @param accessSet - Access set from JWT acl claim (null = overflow, undefined = not configured)
|
|
115
71
|
*/
|
|
116
|
-
declare function
|
|
72
|
+
declare function toPrefetchSession(ssrAuth: {
|
|
73
|
+
status: string;
|
|
74
|
+
user?: {
|
|
75
|
+
role?: string;
|
|
76
|
+
[key: string]: unknown;
|
|
77
|
+
};
|
|
78
|
+
} | undefined, accessSet?: AccessSetLike | null): PrefetchSession;
|
|
117
79
|
/**
|
|
118
|
-
*
|
|
80
|
+
* Evaluate a serialized access rule against the current session.
|
|
119
81
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*/
|
|
123
|
-
declare class HeadCollector {
|
|
124
|
-
private entries;
|
|
125
|
-
/** Add a `<title>` entry. */
|
|
126
|
-
addTitle(text: string): void;
|
|
127
|
-
/** Add a `<meta>` entry. */
|
|
128
|
-
addMeta(attrs: Record<string, string>): void;
|
|
129
|
-
/** Add a `<link>` entry. */
|
|
130
|
-
addLink(attrs: Record<string, string>): void;
|
|
131
|
-
/** Get all collected entries. */
|
|
132
|
-
getEntries(): HeadEntry[];
|
|
133
|
-
/** Clear all collected entries. */
|
|
134
|
-
clear(): void;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Render head entries to an HTML string.
|
|
82
|
+
* Returns true if the query is eligible for prefetch (the user
|
|
83
|
+
* likely has access), false if it should be skipped.
|
|
138
84
|
*
|
|
139
|
-
*
|
|
140
|
-
* -
|
|
85
|
+
* Design rationale for specific rule types:
|
|
86
|
+
* - `where` → true: row-level filter, not an access gate. The query
|
|
87
|
+
* always executes; it just returns fewer rows for non-owners.
|
|
88
|
+
* - `fva` → optimistic for authenticated users: MFA freshness is
|
|
89
|
+
* enforced on the actual API call. If the check fails server-side,
|
|
90
|
+
* the query returns an error result.
|
|
91
|
+
* - `deny` → false: explicitly denied operations are never prefetched.
|
|
92
|
+
* - Unknown types → false: fail-secure. Don't prefetch if we can't
|
|
93
|
+
* evaluate the rule.
|
|
141
94
|
*/
|
|
142
|
-
declare function
|
|
95
|
+
declare function evaluateAccessRule(rule: SerializedAccessRule, session: PrefetchSession): boolean;
|
|
143
96
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
* This is the core serialization function for SSR. It walks the virtual tree
|
|
147
|
-
* recursively and produces an HTML string without requiring a real DOM.
|
|
97
|
+
* AOT SSR Diagnostics
|
|
148
98
|
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
99
|
+
* Tracks AOT compilation results and provides a JSON snapshot
|
|
100
|
+
* for the `/__vertz_ssr_aot` dev endpoint.
|
|
151
101
|
*/
|
|
152
|
-
|
|
102
|
+
/** AOT tier classification (mirrored from @vertz/ui-compiler to avoid cross-package dependency). */
|
|
103
|
+
type AotTier = "static" | "data-driven" | "conditional" | "runtime-fallback";
|
|
104
|
+
/** Per-component diagnostic entry in the snapshot. */
|
|
105
|
+
interface AotComponentDiagnostic {
|
|
106
|
+
tier: AotTier;
|
|
107
|
+
holes: string[];
|
|
108
|
+
}
|
|
109
|
+
/** A recorded divergence between AOT and DOM shim output. */
|
|
110
|
+
interface AotDivergenceEntry {
|
|
111
|
+
component: string;
|
|
112
|
+
aotHtml: string;
|
|
113
|
+
domHtml: string;
|
|
114
|
+
timestamp: string;
|
|
115
|
+
}
|
|
116
|
+
/** JSON snapshot returned by the `/__vertz_ssr_aot` endpoint. */
|
|
117
|
+
interface AotDiagnosticsSnapshot {
|
|
118
|
+
components: Record<string, AotComponentDiagnostic>;
|
|
119
|
+
coverage: {
|
|
120
|
+
total: number;
|
|
121
|
+
aot: number;
|
|
122
|
+
runtime: number;
|
|
123
|
+
percentage: number;
|
|
124
|
+
};
|
|
125
|
+
divergences: AotDivergenceEntry[];
|
|
126
|
+
}
|
|
127
|
+
/** Input shape matching AotComponentInfo from @vertz/ui-compiler. */
|
|
128
|
+
interface ComponentInput {
|
|
129
|
+
name: string;
|
|
130
|
+
tier: AotTier;
|
|
131
|
+
holes: string[];
|
|
132
|
+
}
|
|
153
133
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* Adds `data-v-id` and `data-v-key` attributes to the root element,
|
|
157
|
-
* and optionally embeds serialized props as a `<script type="application/json">` child.
|
|
134
|
+
* Collects AOT compilation diagnostics and produces JSON snapshots.
|
|
158
135
|
*
|
|
159
|
-
*
|
|
136
|
+
* Used by the dev server to power the `/__vertz_ssr_aot` endpoint
|
|
137
|
+
* and by the build pipeline for classification logging.
|
|
160
138
|
*/
|
|
161
|
-
declare
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
/** Array of stylesheet URLs to include in head */
|
|
139
|
+
declare class AotDiagnostics {
|
|
140
|
+
private _components;
|
|
141
|
+
private _divergences;
|
|
142
|
+
/**
|
|
143
|
+
* Record components from an AOT compilation result.
|
|
144
|
+
* Called once per file during compilation or hot rebuild.
|
|
145
|
+
*/
|
|
146
|
+
recordCompilation(components: ComponentInput[]): void;
|
|
147
|
+
/** Record a divergence between AOT and DOM shim HTML output. */
|
|
148
|
+
recordDivergence(component: string, aotHtml: string, domHtml: string): void;
|
|
149
|
+
/** Clear all recorded data (used during full rebuild). */
|
|
150
|
+
clear(): void;
|
|
151
|
+
/** Clear only component classifications (preserves divergences). */
|
|
152
|
+
clearComponents(): void;
|
|
153
|
+
/**
|
|
154
|
+
* Generate per-component classification log lines.
|
|
155
|
+
* Used by the build pipeline and VERTZ_DEBUG=aot logging.
|
|
156
|
+
*
|
|
157
|
+
* Returns lines like:
|
|
158
|
+
* - "Header: static"
|
|
159
|
+
* - "Dashboard: conditional, 1 hole (SidePanel)"
|
|
160
|
+
* - "Coverage: 3/4 components (75%)"
|
|
161
|
+
*/
|
|
162
|
+
getClassificationLog(): string[];
|
|
163
|
+
/** Produce a JSON-serializable snapshot for the diagnostic endpoint. */
|
|
164
|
+
getSnapshot(): AotDiagnosticsSnapshot;
|
|
165
|
+
}
|
|
166
|
+
import { CompiledRoute, FontFallbackMetrics, Theme } from "@vertz/ui";
|
|
167
|
+
import { SSRAuth as SSRAuth_jq1nwm } from "@vertz/ui/internals";
|
|
168
|
+
interface SSRModule {
|
|
169
|
+
default?: () => unknown;
|
|
170
|
+
App?: () => unknown;
|
|
171
|
+
theme?: Theme;
|
|
172
|
+
/** Global CSS strings to include in every SSR response (e.g. resets, body styles). */
|
|
196
173
|
styles?: string[];
|
|
197
|
-
/**
|
|
198
|
-
|
|
174
|
+
/**
|
|
175
|
+
* Return all CSS tracked by the bundled @vertz/ui instance.
|
|
176
|
+
* The Vite SSR build inlines @vertz/ui into the server bundle, creating
|
|
177
|
+
* a separate module instance from @vertz/ui-server's dependency. Without
|
|
178
|
+
* this, component CSS from module-level css() calls is invisible to the
|
|
179
|
+
* SSR renderer. Export `getInjectedCSS` from @vertz/ui in the app entry.
|
|
180
|
+
*/
|
|
181
|
+
getInjectedCSS?: () => string[];
|
|
182
|
+
/** Compiled routes exported from the app for build-time SSG with generateParams. */
|
|
183
|
+
routes?: CompiledRoute[];
|
|
184
|
+
/** Code-generated API client for manifest-driven zero-discovery prefetching. */
|
|
185
|
+
api?: Record<string, Record<string, (...args: unknown[]) => unknown>>;
|
|
186
|
+
}
|
|
187
|
+
interface SSRRenderResult {
|
|
188
|
+
html: string;
|
|
189
|
+
css: string;
|
|
190
|
+
ssrData: Array<{
|
|
191
|
+
key: string;
|
|
192
|
+
data: unknown;
|
|
193
|
+
}>;
|
|
194
|
+
/** Font preload link tags for injection into <head>. */
|
|
195
|
+
headTags: string;
|
|
196
|
+
/** Route patterns discovered by createRouter() during SSR (for build-time pre-rendering). */
|
|
197
|
+
discoveredRoutes?: string[];
|
|
198
|
+
/** Route patterns that matched the current URL (for per-route modulepreload). */
|
|
199
|
+
matchedRoutePatterns?: string[];
|
|
200
|
+
/** Set when ProtectedRoute writes a redirect during SSR. Server should return 302. */
|
|
201
|
+
redirect?: {
|
|
202
|
+
to: string;
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
interface SSRDiscoverResult {
|
|
206
|
+
resolved: Array<{
|
|
207
|
+
key: string;
|
|
208
|
+
data: unknown;
|
|
209
|
+
}>;
|
|
210
|
+
pending: string[];
|
|
199
211
|
}
|
|
200
212
|
/**
|
|
201
|
-
* Render
|
|
202
|
-
*
|
|
203
|
-
* This is the main entry point for zero-boilerplate SSR. It wraps the component
|
|
204
|
-
* in a complete HTML document with doctype, head (meta, OG, Twitter, favicon,
|
|
205
|
-
* styles), and body (component content + scripts).
|
|
206
|
-
*
|
|
207
|
-
* @param vnode - The root VNode to render
|
|
208
|
-
* @param options - Optional page configuration
|
|
209
|
-
* @returns A Response with text/html content-type
|
|
210
|
-
*
|
|
211
|
-
* @example
|
|
212
|
-
* ```ts
|
|
213
|
-
* // Minimal usage
|
|
214
|
-
* return renderPage(App)
|
|
213
|
+
* Render an SSR module to an HTML string with CSS and pre-fetched query data.
|
|
215
214
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
219
|
-
* description: 'Built with vertz',
|
|
220
|
-
* og: { image: '/og.png' },
|
|
221
|
-
* scripts: ['/app.js'],
|
|
222
|
-
* styles: ['/app.css'],
|
|
223
|
-
* })
|
|
224
|
-
* ```
|
|
215
|
+
* Performs a two-pass render:
|
|
216
|
+
* - Pass 1: Discovery — calls the app to trigger query() registrations, awaits them
|
|
217
|
+
* - Pass 2: Render — calls the app again with data populated, renders to HTML
|
|
225
218
|
*/
|
|
226
|
-
declare function
|
|
227
|
-
|
|
228
|
-
interface RenderToHTMLOptions<AppFn extends () => VNode> {
|
|
229
|
-
/** The app component function */
|
|
230
|
-
app: AppFn;
|
|
231
|
-
/** Request URL for SSR */
|
|
232
|
-
url: string;
|
|
233
|
-
/** Theme definition for CSS vars */
|
|
234
|
-
theme?: Theme;
|
|
235
|
-
/** Global CSS strings to inject */
|
|
236
|
-
styles?: string[];
|
|
237
|
-
/** HTML head configuration */
|
|
238
|
-
head?: {
|
|
239
|
-
title?: string;
|
|
240
|
-
meta?: Array<{
|
|
241
|
-
name?: string;
|
|
242
|
-
property?: string;
|
|
243
|
-
content: string;
|
|
244
|
-
}>;
|
|
245
|
-
links?: Array<{
|
|
246
|
-
rel: string;
|
|
247
|
-
href: string;
|
|
248
|
-
}>;
|
|
249
|
-
};
|
|
250
|
-
/** Container selector (default '#app') */
|
|
251
|
-
container?: string;
|
|
219
|
+
declare function ssrRenderToString(module: SSRModule, url: string, options?: {
|
|
220
|
+
ssrTimeout?: number;
|
|
252
221
|
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
253
|
-
fallbackMetrics?: Record<string,
|
|
222
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics>;
|
|
223
|
+
/** Auth state resolved from session cookie. Passed to SSRRenderContext for AuthProvider. */
|
|
224
|
+
ssrAuth?: SSRAuth_jq1nwm;
|
|
225
|
+
}): Promise<SSRRenderResult>;
|
|
226
|
+
/**
|
|
227
|
+
* Discover queries for a given URL without rendering.
|
|
228
|
+
* Runs only Pass 1 (query registration + resolution), no Pass 2 render.
|
|
229
|
+
* Used by the production handler to pre-fetch query data for client-side navigations.
|
|
230
|
+
*/
|
|
231
|
+
declare function ssrDiscoverQueries(module: SSRModule, url: string, options?: {
|
|
232
|
+
ssrTimeout?: number;
|
|
233
|
+
}): Promise<SSRDiscoverResult>;
|
|
234
|
+
import { FontFallbackMetrics as FontFallbackMetrics2 } from "@vertz/ui";
|
|
235
|
+
import { SSRAuth } from "@vertz/ui/internals";
|
|
236
|
+
import { ExtractedQuery } from "@vertz/ui-compiler";
|
|
237
|
+
/** Serialized entity access rules from the prefetch manifest. */
|
|
238
|
+
type EntityAccessMap = Record<string, Partial<Record<string, SerializedAccessRule>>>;
|
|
239
|
+
interface SSRPrefetchManifest {
|
|
240
|
+
/** Route patterns present in the manifest. */
|
|
241
|
+
routePatterns: string[];
|
|
242
|
+
/** Entity access rules keyed by entity name → operation → serialized rule. */
|
|
243
|
+
entityAccess?: EntityAccessMap;
|
|
244
|
+
/** Route entries with query binding metadata for zero-discovery prefetch. */
|
|
245
|
+
routeEntries?: Record<string, {
|
|
246
|
+
queries: ExtractedQuery[];
|
|
247
|
+
}>;
|
|
254
248
|
}
|
|
255
|
-
interface
|
|
256
|
-
/** CSP nonce for inline scripts */
|
|
257
|
-
nonce?: string;
|
|
258
|
-
/** Global default for per-query ssrTimeout (ms) */
|
|
249
|
+
interface SSRSinglePassOptions {
|
|
259
250
|
ssrTimeout?: number;
|
|
260
|
-
/**
|
|
261
|
-
|
|
251
|
+
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
252
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics2>;
|
|
253
|
+
/** Auth state resolved from session cookie. */
|
|
254
|
+
ssrAuth?: SSRAuth;
|
|
255
|
+
/** Set to false to fall back to two-pass rendering. Default: true. */
|
|
256
|
+
prefetch?: boolean;
|
|
257
|
+
/** Prefetch manifest for entity access filtering. */
|
|
258
|
+
manifest?: SSRPrefetchManifest;
|
|
259
|
+
/** Session data for access rule evaluation. */
|
|
260
|
+
prefetchSession?: PrefetchSession;
|
|
262
261
|
}
|
|
263
262
|
/**
|
|
264
|
-
* Render
|
|
263
|
+
* Render an SSR module in a single pass via discovery-only execution.
|
|
265
264
|
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
268
|
-
*
|
|
265
|
+
* 1. Discovery: Run the app factory to capture query registrations (no stream render)
|
|
266
|
+
* 2. Prefetch: Await all discovered queries with timeout
|
|
267
|
+
* 3. Render: Create a fresh context with pre-populated cache, render once
|
|
269
268
|
*
|
|
270
|
-
*
|
|
269
|
+
* Falls back to two-pass (`ssrRenderToString`) when:
|
|
270
|
+
* - `prefetch: false` is set
|
|
271
|
+
* - A redirect is detected during discovery
|
|
271
272
|
*/
|
|
272
|
-
declare function
|
|
273
|
+
declare function ssrRenderSinglePass(module: SSRModule, url: string, options?: SSRSinglePassOptions): Promise<SSRRenderResult>;
|
|
274
|
+
/** Context passed to AOT render functions for accessing data and runtime holes. */
|
|
275
|
+
interface SSRAotContext {
|
|
276
|
+
/** Pre-generated closures for runtime-rendered components. */
|
|
277
|
+
holes: Record<string, () => string>;
|
|
278
|
+
/** Access query data by cache key. */
|
|
279
|
+
getData(key: string): unknown;
|
|
280
|
+
/** Auth session for conditional rendering. */
|
|
281
|
+
session: PrefetchSession | undefined;
|
|
282
|
+
/** Route params for the current request. */
|
|
283
|
+
params: Record<string, string>;
|
|
284
|
+
}
|
|
285
|
+
/** An AOT render function: takes props/data and context, returns HTML string. */
|
|
286
|
+
type AotRenderFn = (data: Record<string, unknown>, ctx: SSRAotContext) => string;
|
|
287
|
+
/** Per-route AOT entry in the manifest. */
|
|
288
|
+
interface AotRouteEntry {
|
|
289
|
+
/** The pre-compiled render function. */
|
|
290
|
+
render: AotRenderFn;
|
|
291
|
+
/** Component names that need runtime fallback (holes). */
|
|
292
|
+
holes: string[];
|
|
293
|
+
/** Query cache keys this route reads via ctx.getData(). */
|
|
294
|
+
queryKeys?: string[];
|
|
295
|
+
}
|
|
273
296
|
/**
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* This is a wrapper around renderPage() that provides a simpler API for
|
|
277
|
-
* theme and style injection.
|
|
278
|
-
*
|
|
279
|
-
* @param options - Render options including app, url, theme, styles, head
|
|
280
|
-
* @returns Promise<string> - The rendered HTML string
|
|
281
|
-
*
|
|
282
|
-
* @example
|
|
283
|
-
* ```ts
|
|
284
|
-
* import { renderToHTML, defineTheme } from '@vertz/ui-server';
|
|
285
|
-
*
|
|
286
|
-
* const theme = defineTheme({
|
|
287
|
-
* colors: { primary: { DEFAULT: '#3b82f6' } }
|
|
288
|
-
* });
|
|
297
|
+
* AOT manifest — maps route patterns to pre-compiled render functions.
|
|
289
298
|
*
|
|
290
|
-
*
|
|
291
|
-
* app: App,
|
|
292
|
-
* url: '/',
|
|
293
|
-
* theme,
|
|
294
|
-
* styles: ['body { margin: 0; }'],
|
|
295
|
-
* head: { title: 'My App' }
|
|
296
|
-
* });
|
|
297
|
-
* ```
|
|
299
|
+
* Generated at build time by the AOT compiler pipeline.
|
|
298
300
|
*/
|
|
299
|
-
|
|
300
|
-
/**
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
301
|
+
interface AotManifest {
|
|
302
|
+
/** Route pattern → AOT entry. */
|
|
303
|
+
routes: Record<string, AotRouteEntry>;
|
|
304
|
+
}
|
|
305
|
+
/** Options for `ssrRenderAot()`. */
|
|
306
|
+
interface SSRRenderAotOptions {
|
|
307
|
+
/** AOT manifest with pre-compiled render functions. */
|
|
308
|
+
aotManifest: AotManifest;
|
|
309
|
+
/** Prefetch manifest for route matching and data fetching. */
|
|
310
|
+
manifest?: SSRPrefetchManifest;
|
|
311
|
+
/** SSR timeout in ms. */
|
|
312
|
+
ssrTimeout?: number;
|
|
313
|
+
/** Pre-computed font fallback metrics. */
|
|
314
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics3>;
|
|
315
|
+
/** Auth state resolved from session cookie. */
|
|
316
|
+
ssrAuth?: SSRAuth2;
|
|
317
|
+
/** Session data for access rule evaluation. */
|
|
318
|
+
prefetchSession?: PrefetchSession;
|
|
319
|
+
/** AOT diagnostics collector (dev mode). When provided with VERTZ_DEBUG=aot, enables dual rendering and divergence detection. */
|
|
320
|
+
diagnostics?: AotDiagnostics;
|
|
321
|
+
}
|
|
304
322
|
/**
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
* This is the main SSR entry point. It walks the virtual tree, serializing
|
|
308
|
-
* synchronous content immediately and deferring Suspense boundaries.
|
|
323
|
+
* Create closure-based runtime fallback renderers for components
|
|
324
|
+
* that cannot be AOT-compiled.
|
|
309
325
|
*
|
|
310
|
-
*
|
|
311
|
-
* 1.
|
|
312
|
-
* 2.
|
|
313
|
-
*
|
|
326
|
+
* Each hole closure:
|
|
327
|
+
* 1. Runs inside `ssrStorage.run()` to provide SSRRenderContext
|
|
328
|
+
* 2. Calls the component factory via the SSR module
|
|
329
|
+
* 3. Converts the result to VNode and serializes to HTML string
|
|
330
|
+
* 4. Shares the query cache with the AOT function
|
|
314
331
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
332
|
+
* @param holeNames - Component names that need runtime rendering
|
|
333
|
+
* @param module - SSR module with component factories
|
|
334
|
+
* @param url - Request URL for context
|
|
335
|
+
* @param queryCache - Pre-populated query cache (shared with AOT)
|
|
336
|
+
* @param ssrAuth - Auth state for the request
|
|
317
337
|
*/
|
|
318
|
-
declare function
|
|
319
|
-
/** Reset the slot counter (for testing). */
|
|
320
|
-
declare function resetSlotCounter(): void;
|
|
338
|
+
declare function createHoles(holeNames: string[], module: SSRModule, url: string, queryCache: Map<string, unknown>, ssrAuth?: SSRAuth2): Record<string, () => string>;
|
|
321
339
|
/**
|
|
322
|
-
*
|
|
340
|
+
* Render a page using pre-compiled AOT string-builder functions.
|
|
323
341
|
*
|
|
324
|
-
*
|
|
325
|
-
*
|
|
342
|
+
* Falls back to `ssrRenderSinglePass()` when:
|
|
343
|
+
* - No route match in the AOT manifest
|
|
344
|
+
* - No prefetch manifest for route matching
|
|
326
345
|
*
|
|
327
|
-
*
|
|
346
|
+
* Pipeline:
|
|
347
|
+
* 1. Match URL to route pattern
|
|
348
|
+
* 2. Look up AOT entry in manifest
|
|
349
|
+
* 3. Prefetch query data (reuses single-pass prefetch logic)
|
|
350
|
+
* 4. Create runtime holes (closures for non-AOT components)
|
|
351
|
+
* 5. Call AOT render function with data + context
|
|
352
|
+
* 6. Collect CSS, ssrData, headTags
|
|
353
|
+
* 7. Return SSRRenderResult
|
|
328
354
|
*/
|
|
329
|
-
declare function
|
|
330
|
-
|
|
331
|
-
|
|
355
|
+
declare function ssrRenderAot(module: SSRModule, url: string, options: SSRRenderAotOptions): Promise<SSRRenderResult>;
|
|
356
|
+
/** Check if VERTZ_DEBUG includes the 'aot' category. */
|
|
357
|
+
declare function isAotDebugEnabled(): boolean;
|
|
358
|
+
import { AccessSet } from "@vertz/ui/auth";
|
|
359
|
+
interface SessionData {
|
|
360
|
+
user: {
|
|
361
|
+
id: string;
|
|
362
|
+
email: string;
|
|
363
|
+
role: string;
|
|
364
|
+
[key: string]: unknown;
|
|
365
|
+
};
|
|
366
|
+
/** Unix timestamp in milliseconds (JWT exp * 1000). */
|
|
367
|
+
expiresAt: number;
|
|
368
|
+
}
|
|
369
|
+
/** Resolved session data for SSR injection. */
|
|
370
|
+
interface SSRSessionInfo {
|
|
371
|
+
session: SessionData;
|
|
372
|
+
/**
|
|
373
|
+
* Access set from JWT acl claim.
|
|
374
|
+
* - Present (object): inline access set (no overflow)
|
|
375
|
+
* - null: access control is configured but the set overflowed the JWT
|
|
376
|
+
* - undefined: access control is not configured
|
|
377
|
+
*/
|
|
378
|
+
accessSet?: AccessSet | null;
|
|
379
|
+
}
|
|
332
380
|
/**
|
|
333
|
-
*
|
|
334
|
-
*
|
|
335
|
-
* Evaluates serialized entity access rules against the current session
|
|
336
|
-
* to determine whether a query should be prefetched during SSR.
|
|
337
|
-
*
|
|
338
|
-
* The serialized rules come from the prefetch manifest (generated at build time).
|
|
339
|
-
* The session comes from the JWT decoded at request time.
|
|
381
|
+
* Callback that extracts session data from a request.
|
|
382
|
+
* Returns null when no valid session exists (expired, missing, or invalid cookie).
|
|
340
383
|
*/
|
|
384
|
+
type SessionResolver = (request: Request) => Promise<SSRSessionInfo | null>;
|
|
341
385
|
/**
|
|
342
|
-
*
|
|
343
|
-
*
|
|
344
|
-
*
|
|
386
|
+
* Serialize a session into a `<script>` tag that sets
|
|
387
|
+
* `window.__VERTZ_SESSION__`.
|
|
388
|
+
*
|
|
389
|
+
* @param session - The session data to serialize
|
|
390
|
+
* @param nonce - Optional CSP nonce for the script tag
|
|
345
391
|
*/
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
392
|
+
declare function createSessionScript(session: SessionData, nonce?: string): string;
|
|
393
|
+
interface SSRHandlerOptions {
|
|
394
|
+
/** The loaded SSR module (import('./dist/server/index.js')) */
|
|
395
|
+
module: SSRModule;
|
|
396
|
+
/** HTML template string (contents of dist/client/index.html) */
|
|
397
|
+
template: string;
|
|
398
|
+
/** SSR timeout for queries (default: 300ms) */
|
|
399
|
+
ssrTimeout?: number;
|
|
400
|
+
/**
|
|
401
|
+
* Map of CSS asset URLs to their content for inlining.
|
|
402
|
+
* Replaces `<link rel="stylesheet" href="...">` tags with inline `<style>` tags.
|
|
403
|
+
* Eliminates extra network requests, preventing FOUC on slow connections.
|
|
404
|
+
*
|
|
405
|
+
* @example
|
|
406
|
+
* ```ts
|
|
407
|
+
* inlineCSS: { '/assets/vertz.css': await Bun.file('./dist/client/assets/vertz.css').text() }
|
|
408
|
+
* ```
|
|
409
|
+
*/
|
|
410
|
+
inlineCSS?: Record<string, string>;
|
|
411
|
+
/**
|
|
412
|
+
* CSP nonce to inject on all inline `<script>` tags emitted during SSR.
|
|
413
|
+
*
|
|
414
|
+
* When set, the SSR data hydration script will include `nonce="<value>"`
|
|
415
|
+
* so that strict Content-Security-Policy headers do not block it.
|
|
416
|
+
*/
|
|
417
|
+
nonce?: string;
|
|
418
|
+
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
419
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics4>;
|
|
420
|
+
/** Paths to inject as `<link rel="modulepreload">` in `<head>`. */
|
|
421
|
+
modulepreload?: string[];
|
|
422
|
+
/**
|
|
423
|
+
* Route chunk manifest for per-route modulepreload injection.
|
|
424
|
+
* When provided, only chunks for the matched route are preloaded instead of all chunks.
|
|
425
|
+
*/
|
|
426
|
+
routeChunkManifest?: {
|
|
427
|
+
routes: Record<string, string[]>;
|
|
428
|
+
};
|
|
429
|
+
/** Cache-Control header for HTML responses. Omit or undefined = no header (safe default). */
|
|
430
|
+
cacheControl?: string;
|
|
431
|
+
/**
|
|
432
|
+
* Resolves session data from request cookies for SSR injection.
|
|
433
|
+
* When provided, SSR HTML includes `window.__VERTZ_SESSION__` and
|
|
434
|
+
* optionally `window.__VERTZ_ACCESS_SET__` for instant auth hydration.
|
|
435
|
+
*/
|
|
436
|
+
sessionResolver?: SessionResolver;
|
|
437
|
+
/**
|
|
438
|
+
* Prefetch manifest for single-pass SSR optimization.
|
|
439
|
+
*
|
|
440
|
+
* When provided with route entries and an API client export, enables
|
|
441
|
+
* zero-discovery rendering — queries are prefetched from the manifest
|
|
442
|
+
* without executing the component tree, then a single render pass
|
|
443
|
+
* produces the HTML. Without a manifest, SSR still uses the single-pass
|
|
444
|
+
* discovery-then-render approach (cheaper than two-pass).
|
|
445
|
+
*/
|
|
446
|
+
manifest?: SSRPrefetchManifest;
|
|
447
|
+
/**
|
|
448
|
+
* Enable progressive HTML streaming. Default: false.
|
|
449
|
+
*
|
|
450
|
+
* When true, the Response body is a ReadableStream that sends `<head>`
|
|
451
|
+
* content (CSS, preloads, fonts) before `<body>` rendering is complete.
|
|
452
|
+
* This improves TTFB and FCP.
|
|
453
|
+
*
|
|
454
|
+
* Has no effect on zero-discovery routes (manifest with routeEntries),
|
|
455
|
+
* which always use buffered rendering.
|
|
456
|
+
*/
|
|
457
|
+
progressiveHTML?: boolean;
|
|
458
|
+
/**
|
|
459
|
+
* AOT manifest with pre-compiled render functions.
|
|
460
|
+
*
|
|
461
|
+
* When provided, routes matching AOT entries are rendered via string-builder
|
|
462
|
+
* functions (no DOM shim), bypassing the reactive runtime for 4-6x speedup.
|
|
463
|
+
* Routes not in the manifest fall back to `ssrRenderSinglePass()`.
|
|
464
|
+
*
|
|
465
|
+
* Load via `loadAotManifest(serverDir)` at startup.
|
|
466
|
+
*/
|
|
467
|
+
aotManifest?: AotManifest;
|
|
468
|
+
}
|
|
469
|
+
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
470
|
+
type NodeHandlerOptions = SSRHandlerOptions;
|
|
471
|
+
declare function createNodeHandler(options: NodeHandlerOptions): (req: IncomingMessage, res: ServerResponse) => void;
|
|
472
|
+
import { FallbackFontName as FallbackFontName2, FontFallbackMetrics as FontFallbackMetrics7 } from "@vertz/ui";
|
|
473
|
+
import { ExtractedRoute } from "@vertz/ui-compiler";
|
|
474
|
+
import { extractRoutes } from "@vertz/ui-compiler";
|
|
475
|
+
import { AotComponentInfo } from "@vertz/ui-compiler";
|
|
476
|
+
interface AotBuildComponentEntry {
|
|
477
|
+
tier: "static" | "data-driven" | "conditional" | "runtime-fallback";
|
|
478
|
+
holes: string[];
|
|
479
|
+
queryKeys: string[];
|
|
480
|
+
}
|
|
481
|
+
/** Compiled file output from AOT compilation. */
|
|
482
|
+
interface AotCompiledFile {
|
|
483
|
+
/** Transformed source code containing __ssr_* functions. */
|
|
484
|
+
code: string;
|
|
485
|
+
/** Per-component AOT info from the compilation. */
|
|
486
|
+
components: AotComponentInfo[];
|
|
487
|
+
}
|
|
488
|
+
/** Route map entry for the AOT manifest JSON. */
|
|
489
|
+
interface AotRouteMapEntry {
|
|
490
|
+
/** Name of the __ssr_* render function (e.g., '__ssr_HomePage'). */
|
|
491
|
+
renderFn: string;
|
|
492
|
+
/** Component names that need runtime fallback rendering. */
|
|
493
|
+
holes: string[];
|
|
494
|
+
/** Query cache keys this route reads via ctx.getData(). */
|
|
495
|
+
queryKeys: string[];
|
|
496
|
+
}
|
|
497
|
+
interface AotBuildManifest {
|
|
498
|
+
components: Record<string, AotBuildComponentEntry>;
|
|
499
|
+
/** Compiled files keyed by source file path. */
|
|
500
|
+
compiledFiles: Record<string, AotCompiledFile>;
|
|
501
|
+
classificationLog: string[];
|
|
502
|
+
}
|
|
371
503
|
/**
|
|
372
|
-
*
|
|
373
|
-
* Extracted from the JWT at SSR request time.
|
|
504
|
+
* Scan all TSX files in srcDir and generate an AOT build manifest.
|
|
374
505
|
*/
|
|
375
|
-
|
|
376
|
-
status: "authenticated";
|
|
377
|
-
roles?: string[];
|
|
378
|
-
entitlements?: Record<string, boolean>;
|
|
379
|
-
tenantId?: string;
|
|
380
|
-
} | {
|
|
381
|
-
status: "unauthenticated";
|
|
382
|
-
};
|
|
506
|
+
declare function generateAotBuildManifest(srcDir: string): AotBuildManifest;
|
|
383
507
|
/**
|
|
384
|
-
*
|
|
385
|
-
*
|
|
508
|
+
* Build AOT route map — maps route patterns to render function names.
|
|
509
|
+
*
|
|
510
|
+
* Skips routes for runtime-fallback components (they can't be AOT-rendered)
|
|
511
|
+
* and routes for unknown components (not found in AOT manifest).
|
|
386
512
|
*/
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
513
|
+
declare function buildAotRouteMap(components: Record<string, AotBuildComponentEntry>, routes: Array<{
|
|
514
|
+
pattern: string;
|
|
515
|
+
componentName: string;
|
|
516
|
+
}>): Record<string, AotRouteMapEntry>;
|
|
517
|
+
/** Result of barrel generation — barrel source + file mapping for temp dir. */
|
|
518
|
+
interface AotBarrelResult {
|
|
519
|
+
/** Barrel module source code (import/re-statements). */
|
|
520
|
+
barrelSource: string;
|
|
521
|
+
/**
|
|
522
|
+
* Mapping of temp file names → compiled source code.
|
|
523
|
+
* Write each entry as `<tempDir>/<filename>.ts` alongside the barrel.
|
|
524
|
+
*/
|
|
525
|
+
files: Record<string, string>;
|
|
391
526
|
}
|
|
392
527
|
/**
|
|
393
|
-
*
|
|
394
|
-
* for entity access evaluation during SSR prefetching.
|
|
528
|
+
* Generate a barrel module that re-exports __ssr_* functions from compiled files.
|
|
395
529
|
*
|
|
396
|
-
*
|
|
397
|
-
*
|
|
530
|
+
* Returns the barrel source code and a mapping of temp filenames → compiled code
|
|
531
|
+
* that must be written alongside the barrel for bundling.
|
|
398
532
|
*/
|
|
399
|
-
declare function
|
|
400
|
-
status: string;
|
|
401
|
-
user?: {
|
|
402
|
-
role?: string;
|
|
403
|
-
[key: string]: unknown;
|
|
404
|
-
};
|
|
405
|
-
} | undefined, accessSet?: AccessSetLike | null): PrefetchSession;
|
|
533
|
+
declare function generateAotBarrel(compiledFiles: Record<string, AotCompiledFile>, routeMap: Record<string, AotRouteMapEntry>): AotBarrelResult;
|
|
406
534
|
/**
|
|
407
|
-
*
|
|
535
|
+
* Load AOT manifest and routes module from a server build directory.
|
|
408
536
|
*
|
|
409
|
-
* Returns
|
|
410
|
-
*
|
|
537
|
+
* Returns `null` if either `aot-manifest.json` or `aot-routes.js` is missing,
|
|
538
|
+
* or if no routes can be wired to render functions.
|
|
411
539
|
*
|
|
412
|
-
*
|
|
413
|
-
* - `where` → true: row-level filter, not an access gate. The query
|
|
414
|
-
* always executes; it just returns fewer rows for non-owners.
|
|
415
|
-
* - `fva` → optimistic for authenticated users: MFA freshness is
|
|
416
|
-
* enforced on the actual API call. If the check fails server-side,
|
|
417
|
-
* the query returns an error result.
|
|
418
|
-
* - `deny` → false: explicitly denied operations are never prefetched.
|
|
419
|
-
* - Unknown types → false: fail-secure. Don't prefetch if we can't
|
|
420
|
-
* evaluate the rule.
|
|
540
|
+
* @param serverDir - Path to `dist/server/` directory
|
|
421
541
|
*/
|
|
422
|
-
declare function
|
|
423
|
-
|
|
542
|
+
declare function loadAotManifest(serverDir: string): Promise<AotManifest | null>;
|
|
543
|
+
/** A raw HTML string that bypasses escaping during serialization. */
|
|
544
|
+
interface RawHtml {
|
|
545
|
+
__raw: true;
|
|
546
|
+
html: string;
|
|
547
|
+
}
|
|
424
548
|
/**
|
|
425
|
-
*
|
|
549
|
+
* Create a raw HTML string that will NOT be escaped during SSR serialization.
|
|
426
550
|
*
|
|
427
|
-
*
|
|
428
|
-
* -
|
|
429
|
-
*
|
|
551
|
+
* **WARNING: XSS RISK** — This function bypasses all HTML escaping. Never pass
|
|
552
|
+
* user-controlled input directly. Always sanitize with a trusted library (e.g.
|
|
553
|
+
* DOMPurify) before wrapping in `rawHtml()`.
|
|
430
554
|
*
|
|
431
|
-
* @
|
|
432
|
-
*
|
|
555
|
+
* @example Safe usage
|
|
556
|
+
* ```ts
|
|
557
|
+
* rawHtml('<svg>...</svg>') // static markup — OK
|
|
558
|
+
* rawHtml(DOMPurify.sanitize(userInput)) // sanitized — OK
|
|
559
|
+
* ```
|
|
560
|
+
*
|
|
561
|
+
* @example Unsafe usage — NEVER do this
|
|
562
|
+
* ```ts
|
|
563
|
+
* rawHtml(userInput) // XSS vulnerability
|
|
564
|
+
* rawHtml(`<div>${userInput}</div>`) // XSS via interpolation
|
|
565
|
+
* ```
|
|
433
566
|
*/
|
|
434
|
-
declare function
|
|
567
|
+
declare function rawHtml(html: string): RawHtml;
|
|
568
|
+
/** Virtual node representing an HTML element for SSR serialization. */
|
|
569
|
+
interface VNode {
|
|
570
|
+
tag: string;
|
|
571
|
+
attrs: Record<string, string>;
|
|
572
|
+
children: (VNode | string | RawHtml)[];
|
|
573
|
+
}
|
|
574
|
+
/** Options for hydration marker generation. */
|
|
575
|
+
interface HydrationOptions {
|
|
576
|
+
/** Component name for `data-v-id`. */
|
|
577
|
+
componentName: string;
|
|
578
|
+
/** Unique key for `data-v-key`. */
|
|
579
|
+
key: string;
|
|
580
|
+
/** Serialized props to embed as JSON. */
|
|
581
|
+
props?: Record<string, unknown>;
|
|
582
|
+
}
|
|
583
|
+
/** Metadata collected by the Head component during rendering. */
|
|
584
|
+
interface HeadEntry {
|
|
585
|
+
tag: "title" | "meta" | "link";
|
|
586
|
+
attrs?: Record<string, string>;
|
|
587
|
+
textContent?: string;
|
|
588
|
+
}
|
|
589
|
+
/** Options for {@link renderToStream}. */
|
|
590
|
+
interface RenderToStreamOptions {
|
|
591
|
+
/**
|
|
592
|
+
* CSP nonce to inject on all inline `<script>` tags emitted during SSR.
|
|
593
|
+
*
|
|
594
|
+
* When set, every inline script (e.g. Suspense replacement scripts) will
|
|
595
|
+
* include `nonce="<value>"` so that strict Content-Security-Policy headers
|
|
596
|
+
* do not block them.
|
|
597
|
+
*/
|
|
598
|
+
nonce?: string;
|
|
599
|
+
}
|
|
600
|
+
/** Asset descriptor for script/stylesheet injection. */
|
|
601
|
+
interface AssetDescriptor {
|
|
602
|
+
type: "script" | "stylesheet";
|
|
603
|
+
src: string;
|
|
604
|
+
/** Whether to add `async` attribute (scripts only). */
|
|
605
|
+
async?: boolean;
|
|
606
|
+
/** Whether to add `defer` attribute (scripts only). */
|
|
607
|
+
defer?: boolean;
|
|
608
|
+
}
|
|
435
609
|
/**
|
|
436
|
-
*
|
|
437
|
-
* `window.__VERTZ_ACCESS_SET__`.
|
|
610
|
+
* Render asset descriptors to HTML tags for script/stylesheet injection.
|
|
438
611
|
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
612
|
+
* - Scripts: `<script src="..." [async] [defer]><\/script>`
|
|
613
|
+
* - Stylesheets: `<link rel="stylesheet" href="...">`
|
|
441
614
|
*/
|
|
442
|
-
declare function
|
|
443
|
-
import { RenderAdapter } from "@vertz/ui/internals";
|
|
615
|
+
declare function renderAssetTags(assets: AssetDescriptor[]): string;
|
|
444
616
|
/**
|
|
445
|
-
*
|
|
446
|
-
*
|
|
617
|
+
* Inline critical CSS as a `<style>` tag.
|
|
618
|
+
*
|
|
619
|
+
* Wraps the provided CSS in `<style>...</style>` for embedding in the HTML head.
|
|
620
|
+
* Escapes any `</style>` sequences in the CSS content to prevent injection.
|
|
621
|
+
*
|
|
622
|
+
* Returns an empty string if the CSS is empty.
|
|
447
623
|
*/
|
|
448
|
-
declare function
|
|
624
|
+
declare function inlineCriticalCss(css: string): string;
|
|
625
|
+
import { FallbackFontName, FontDescriptor, FontFallbackMetrics as FontFallbackMetrics5 } from "@vertz/ui";
|
|
449
626
|
/**
|
|
450
|
-
*
|
|
627
|
+
* Auto-detect which system font to use as fallback base.
|
|
451
628
|
*
|
|
452
|
-
*
|
|
453
|
-
*
|
|
629
|
+
* Scans the `fallback` array for generic CSS font family keywords:
|
|
630
|
+
* - 'sans-serif' or 'system-ui' → Arial
|
|
631
|
+
* - 'serif' → Times New Roman
|
|
632
|
+
* - 'monospace' → Courier New
|
|
633
|
+
*
|
|
634
|
+
* Skips non-generic entries (e.g., 'Georgia', 'Helvetica').
|
|
635
|
+
* If no generic keyword found, defaults to Arial.
|
|
454
636
|
*/
|
|
455
|
-
|
|
456
|
-
type AotTier = "static" | "data-driven" | "conditional" | "runtime-fallback";
|
|
457
|
-
/** Per-component diagnostic entry in the snapshot. */
|
|
458
|
-
interface AotComponentDiagnostic {
|
|
459
|
-
tier: AotTier;
|
|
460
|
-
holes: string[];
|
|
461
|
-
}
|
|
462
|
-
/** A recorded divergence between AOT and DOM shim output. */
|
|
463
|
-
interface AotDivergenceEntry {
|
|
464
|
-
component: string;
|
|
465
|
-
aotHtml: string;
|
|
466
|
-
domHtml: string;
|
|
467
|
-
timestamp: string;
|
|
468
|
-
}
|
|
469
|
-
/** JSON snapshot returned by the `/__vertz_ssr_aot` endpoint. */
|
|
470
|
-
interface AotDiagnosticsSnapshot {
|
|
471
|
-
components: Record<string, AotComponentDiagnostic>;
|
|
472
|
-
coverage: {
|
|
473
|
-
total: number;
|
|
474
|
-
aot: number;
|
|
475
|
-
runtime: number;
|
|
476
|
-
percentage: number;
|
|
477
|
-
};
|
|
478
|
-
divergences: AotDivergenceEntry[];
|
|
479
|
-
}
|
|
480
|
-
/** Input shape matching AotComponentInfo from @vertz/ui-compiler. */
|
|
481
|
-
interface ComponentInput {
|
|
482
|
-
name: string;
|
|
483
|
-
tier: AotTier;
|
|
484
|
-
holes: string[];
|
|
485
|
-
}
|
|
637
|
+
declare function detectFallbackFont(fallback: readonly string[]): FallbackFontName;
|
|
486
638
|
/**
|
|
487
|
-
*
|
|
639
|
+
* Extract font metrics from .woff2 files and compute CSS fallback overrides.
|
|
488
640
|
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
641
|
+
* @param fonts - Font descriptors from theme definition.
|
|
642
|
+
* @param rootDir - Project root directory for resolving font file paths.
|
|
643
|
+
* @returns Map of font key → computed fallback metrics.
|
|
491
644
|
*/
|
|
492
|
-
declare
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
645
|
+
declare function extractFontMetrics(fonts: Record<string, FontDescriptor>, rootDir: string): Promise<Record<string, FontFallbackMetrics5>>;
|
|
646
|
+
/**
|
|
647
|
+
* Collector for `<head>` metadata during SSR.
|
|
648
|
+
*
|
|
649
|
+
* Components call `addTitle`, `addMeta`, or `addLink` during render,
|
|
650
|
+
* and the collected entries are serialized into the HTML `<head>` section.
|
|
651
|
+
*/
|
|
652
|
+
declare class HeadCollector {
|
|
653
|
+
private entries;
|
|
654
|
+
/** Add a `<title>` entry. */
|
|
655
|
+
addTitle(text: string): void;
|
|
656
|
+
/** Add a `<meta>` entry. */
|
|
657
|
+
addMeta(attrs: Record<string, string>): void;
|
|
658
|
+
/** Add a `<link>` entry. */
|
|
659
|
+
addLink(attrs: Record<string, string>): void;
|
|
660
|
+
/** Get all collected entries. */
|
|
661
|
+
getEntries(): HeadEntry[];
|
|
662
|
+
/** Clear all collected entries. */
|
|
503
663
|
clear(): void;
|
|
504
|
-
/** Clear only component classifications (preserves divergences). */
|
|
505
|
-
clearComponents(): void;
|
|
506
|
-
/**
|
|
507
|
-
* Generate per-component classification log lines.
|
|
508
|
-
* Used by the build pipeline and VERTZ_DEBUG=aot logging.
|
|
509
|
-
*
|
|
510
|
-
* Returns lines like:
|
|
511
|
-
* - "Header: static"
|
|
512
|
-
* - "Dashboard: conditional, 1 hole (SidePanel)"
|
|
513
|
-
* - "Coverage: 3/4 components (75%)"
|
|
514
|
-
*/
|
|
515
|
-
getClassificationLog(): string[];
|
|
516
|
-
/** Produce a JSON-serializable snapshot for the diagnostic endpoint. */
|
|
517
|
-
getSnapshot(): AotDiagnosticsSnapshot;
|
|
518
664
|
}
|
|
519
|
-
/**
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
/**
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
665
|
+
/**
|
|
666
|
+
* Render head entries to an HTML string.
|
|
667
|
+
*
|
|
668
|
+
* - `<title>` renders as `<title>text</title>`
|
|
669
|
+
* - `<meta>` and `<link>` render as void elements
|
|
670
|
+
*/
|
|
671
|
+
declare function renderHeadToHtml(entries: HeadEntry[]): string;
|
|
672
|
+
/**
|
|
673
|
+
* Serialize a VNode tree (or plain string) to an HTML string.
|
|
674
|
+
*
|
|
675
|
+
* This is the core serialization function for SSR. It walks the virtual tree
|
|
676
|
+
* recursively and produces an HTML string without requiring a real DOM.
|
|
677
|
+
*
|
|
678
|
+
* - Text content inside `<script>` and `<style>` tags is not escaped.
|
|
679
|
+
* - `RawHtml` values bypass escaping entirely.
|
|
680
|
+
*/
|
|
681
|
+
declare function serializeToHtml(node: VNode | string | RawHtml): string;
|
|
682
|
+
/**
|
|
683
|
+
* Wrap a VNode with hydration markers for interactive components.
|
|
684
|
+
*
|
|
685
|
+
* Adds `data-v-id` and `data-v-key` attributes to the root element,
|
|
686
|
+
* and optionally embeds serialized props as a `<script type="application/json">` child.
|
|
687
|
+
*
|
|
688
|
+
* Returns a new VNode; the original is not mutated.
|
|
689
|
+
*/
|
|
690
|
+
declare function wrapWithHydrationMarkers(node: VNode, options: HydrationOptions): VNode;
|
|
691
|
+
interface PageOptions {
|
|
692
|
+
/** HTTP status code (default: 200) */
|
|
693
|
+
status?: number;
|
|
694
|
+
/** Page title */
|
|
695
|
+
title?: string;
|
|
696
|
+
/** Meta description */
|
|
697
|
+
description?: string;
|
|
698
|
+
/** Language attribute (default: 'en') */
|
|
699
|
+
lang?: string;
|
|
700
|
+
/** Favicon URL */
|
|
701
|
+
favicon?: string;
|
|
702
|
+
/** Open Graph meta tags */
|
|
703
|
+
og?: {
|
|
704
|
+
/** OG title (falls back to title) */
|
|
705
|
+
title?: string;
|
|
706
|
+
/** OG description (falls back to description) */
|
|
707
|
+
description?: string;
|
|
708
|
+
/** OG image URL */
|
|
709
|
+
image?: string;
|
|
710
|
+
/** OG canonical URL */
|
|
711
|
+
url?: string;
|
|
712
|
+
/** OG type (default: 'website') */
|
|
713
|
+
type?: string;
|
|
714
|
+
};
|
|
715
|
+
/** Twitter card meta tags */
|
|
716
|
+
twitter?: {
|
|
717
|
+
/** Twitter card type */
|
|
718
|
+
card?: string;
|
|
719
|
+
/** Twitter @username */
|
|
720
|
+
site?: string;
|
|
721
|
+
};
|
|
722
|
+
/** Array of script URLs to include at end of body */
|
|
723
|
+
scripts?: string[];
|
|
724
|
+
/** Array of stylesheet URLs to include in head */
|
|
725
|
+
styles?: string[];
|
|
726
|
+
/** Raw HTML escape hatch for head */
|
|
727
|
+
head?: string;
|
|
553
728
|
}
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
729
|
+
/**
|
|
730
|
+
* Render a VNode to a full HTML Response with common page structure.
|
|
731
|
+
*
|
|
732
|
+
* This is the main entry point for zero-boilerplate SSR. It wraps the component
|
|
733
|
+
* in a complete HTML document with doctype, head (meta, OG, Twitter, favicon,
|
|
734
|
+
* styles), and body (component content + scripts).
|
|
735
|
+
*
|
|
736
|
+
* @param vnode - The root VNode to render
|
|
737
|
+
* @param options - Optional page configuration
|
|
738
|
+
* @returns A Response with text/html content-type
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* ```ts
|
|
742
|
+
* // Minimal usage
|
|
743
|
+
* return renderPage(App)
|
|
744
|
+
*
|
|
745
|
+
* // Full options
|
|
746
|
+
* return renderPage(App, {
|
|
747
|
+
* title: 'My App',
|
|
748
|
+
* description: 'Built with vertz',
|
|
749
|
+
* og: { image: '/og.png' },
|
|
750
|
+
* scripts: ['/app.js'],
|
|
751
|
+
* styles: ['/app.css'],
|
|
752
|
+
* })
|
|
753
|
+
* ```
|
|
754
|
+
*/
|
|
755
|
+
declare function renderPage(vnode: VNode, options?: PageOptions): Response;
|
|
756
|
+
import { FontFallbackMetrics as FontFallbackMetrics6, Theme as Theme2 } from "@vertz/ui";
|
|
757
|
+
interface RenderToHTMLOptions<AppFn extends () => VNode> {
|
|
758
|
+
/** The app component function */
|
|
759
|
+
app: AppFn;
|
|
760
|
+
/** Request URL for SSR */
|
|
761
|
+
url: string;
|
|
762
|
+
/** Theme definition for CSS vars */
|
|
562
763
|
theme?: Theme2;
|
|
563
|
-
/** Global CSS strings to
|
|
764
|
+
/** Global CSS strings to inject */
|
|
564
765
|
styles?: string[];
|
|
565
|
-
/**
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
interface SSRRenderResult {
|
|
579
|
-
html: string;
|
|
580
|
-
css: string;
|
|
581
|
-
ssrData: Array<{
|
|
582
|
-
key: string;
|
|
583
|
-
data: unknown;
|
|
584
|
-
}>;
|
|
585
|
-
/** Font preload link tags for injection into <head>. */
|
|
586
|
-
headTags: string;
|
|
587
|
-
/** Route patterns discovered by createRouter() during SSR (for build-time pre-rendering). */
|
|
588
|
-
discoveredRoutes?: string[];
|
|
589
|
-
/** Route patterns that matched the current URL (for per-route modulepreload). */
|
|
590
|
-
matchedRoutePatterns?: string[];
|
|
591
|
-
/** Set when ProtectedRoute writes a redirect during SSR. Server should return 302. */
|
|
592
|
-
redirect?: {
|
|
593
|
-
to: string;
|
|
766
|
+
/** HTML head configuration */
|
|
767
|
+
head?: {
|
|
768
|
+
title?: string;
|
|
769
|
+
meta?: Array<{
|
|
770
|
+
name?: string;
|
|
771
|
+
property?: string;
|
|
772
|
+
content: string;
|
|
773
|
+
}>;
|
|
774
|
+
links?: Array<{
|
|
775
|
+
rel: string;
|
|
776
|
+
href: string;
|
|
777
|
+
}>;
|
|
594
778
|
};
|
|
779
|
+
/** Container selector (default '#app') */
|
|
780
|
+
container?: string;
|
|
781
|
+
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
782
|
+
fallbackMetrics?: Record<string, FontFallbackMetrics6>;
|
|
595
783
|
}
|
|
596
|
-
interface
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
784
|
+
interface RenderToHTMLStreamOptions<AppFn extends () => VNode> extends RenderToHTMLOptions<AppFn> {
|
|
785
|
+
/** CSP nonce for inline scripts */
|
|
786
|
+
nonce?: string;
|
|
787
|
+
/** Global default for per-query ssrTimeout (ms) */
|
|
788
|
+
ssrTimeout?: number;
|
|
789
|
+
/** Hard timeout for entire stream (ms, default 30000) */
|
|
790
|
+
streamTimeout?: number;
|
|
602
791
|
}
|
|
603
792
|
/**
|
|
604
|
-
* Render
|
|
793
|
+
* Render a component to a streaming HTML Response.
|
|
605
794
|
*
|
|
606
|
-
*
|
|
607
|
-
*
|
|
608
|
-
*
|
|
795
|
+
* The initial HTML is sent immediately. For slow queries that timed out
|
|
796
|
+
* during SSR, their resolved data is streamed as inline `<script>` chunks
|
|
797
|
+
* that push data to the client's reactive system.
|
|
798
|
+
*
|
|
799
|
+
* @returns Promise<Response> - A streaming Response with text/html content
|
|
609
800
|
*/
|
|
610
|
-
declare function
|
|
611
|
-
ssrTimeout?: number;
|
|
612
|
-
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
613
|
-
fallbackMetrics?: Record<string, FontFallbackMetrics3>;
|
|
614
|
-
/** Auth state resolved from session cookie. Passed to SSRRenderContext for AuthProvider. */
|
|
615
|
-
ssrAuth?: SSRAuth_jq1nwm;
|
|
616
|
-
}): Promise<SSRRenderResult>;
|
|
801
|
+
declare function renderToHTMLStream<AppFn extends () => VNode>(options: RenderToHTMLStreamOptions<AppFn>): Promise<Response>;
|
|
617
802
|
/**
|
|
618
|
-
*
|
|
619
|
-
*
|
|
620
|
-
*
|
|
803
|
+
* Render a VNode to a full HTML string.
|
|
804
|
+
*
|
|
805
|
+
* This is a wrapper around renderPage() that provides a simpler API for
|
|
806
|
+
* theme and style injection.
|
|
807
|
+
*
|
|
808
|
+
* @param options - Render options including app, url, theme, styles, head
|
|
809
|
+
* @returns Promise<string> - The rendered HTML string
|
|
810
|
+
*
|
|
811
|
+
* @example
|
|
812
|
+
* ```ts
|
|
813
|
+
* import { renderToHTML, defineTheme } from '@vertz/ui-server';
|
|
814
|
+
*
|
|
815
|
+
* const theme = defineTheme({
|
|
816
|
+
* colors: { primary: { DEFAULT: '#3b82f6' } }
|
|
817
|
+
* });
|
|
818
|
+
*
|
|
819
|
+
* const html = await renderToHTML({
|
|
820
|
+
* app: App,
|
|
821
|
+
* url: '/',
|
|
822
|
+
* theme,
|
|
823
|
+
* styles: ['body { margin: 0; }'],
|
|
824
|
+
* head: { title: 'My App' }
|
|
825
|
+
* });
|
|
826
|
+
* ```
|
|
621
827
|
*/
|
|
622
|
-
declare function
|
|
623
|
-
|
|
624
|
-
})
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
import { ExtractedQuery } from "@vertz/ui-compiler";
|
|
628
|
-
/** Serialized entity access rules from the prefetch manifest. */
|
|
629
|
-
type EntityAccessMap = Record<string, Partial<Record<string, SerializedAccessRule>>>;
|
|
630
|
-
interface SSRPrefetchManifest {
|
|
631
|
-
/** Route patterns present in the manifest. */
|
|
632
|
-
routePatterns: string[];
|
|
633
|
-
/** Entity access rules keyed by entity name → operation → serialized rule. */
|
|
634
|
-
entityAccess?: EntityAccessMap;
|
|
635
|
-
/** Route entries with query binding metadata for zero-discovery prefetch. */
|
|
636
|
-
routeEntries?: Record<string, {
|
|
637
|
-
queries: ExtractedQuery[];
|
|
638
|
-
}>;
|
|
639
|
-
}
|
|
640
|
-
interface SSRSinglePassOptions {
|
|
641
|
-
ssrTimeout?: number;
|
|
642
|
-
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
643
|
-
fallbackMetrics?: Record<string, FontFallbackMetrics4>;
|
|
644
|
-
/** Auth state resolved from session cookie. */
|
|
645
|
-
ssrAuth?: SSRAuth;
|
|
646
|
-
/** Set to false to fall back to two-pass rendering. Default: true. */
|
|
647
|
-
prefetch?: boolean;
|
|
648
|
-
/** Prefetch manifest for entity access filtering. */
|
|
649
|
-
manifest?: SSRPrefetchManifest;
|
|
650
|
-
/** Session data for access rule evaluation. */
|
|
651
|
-
prefetchSession?: PrefetchSession;
|
|
652
|
-
}
|
|
828
|
+
declare function renderToHTML<AppFn extends () => VNode>(options: RenderToHTMLOptions<AppFn>): Promise<string>;
|
|
829
|
+
/**
|
|
830
|
+
* @deprecated Use the options-object overload: `renderToHTML({ app, url, ... })`
|
|
831
|
+
*/
|
|
832
|
+
declare function renderToHTML<AppFn extends () => VNode>(app: AppFn, options: RenderToHTMLOptions<AppFn>): Promise<string>;
|
|
653
833
|
/**
|
|
654
|
-
* Render
|
|
834
|
+
* Render a VNode tree to a `ReadableStream` of HTML chunks.
|
|
655
835
|
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
* 3. Render: Create a fresh context with pre-populated cache, render once
|
|
836
|
+
* This is the main SSR entry point. It walks the virtual tree, serializing
|
|
837
|
+
* synchronous content immediately and deferring Suspense boundaries.
|
|
659
838
|
*
|
|
660
|
-
*
|
|
661
|
-
*
|
|
662
|
-
*
|
|
839
|
+
* Suspense boundaries emit:
|
|
840
|
+
* 1. A `<div id="v-slot-N">fallback</div>` placeholder inline
|
|
841
|
+
* 2. A `<template id="v-tmpl-N">resolved</template><script>...<\/script>` chunk
|
|
842
|
+
* appended after the main content once the async content resolves
|
|
843
|
+
*
|
|
844
|
+
* This enables out-of-order streaming: the browser can paint the fallback
|
|
845
|
+
* immediately and swap in the resolved content when it arrives.
|
|
663
846
|
*/
|
|
664
|
-
declare function
|
|
665
|
-
/**
|
|
666
|
-
|
|
667
|
-
/** Pre-generated closures for runtime-rendered components. */
|
|
668
|
-
holes: Record<string, () => string>;
|
|
669
|
-
/** Access query data by cache key. */
|
|
670
|
-
getData(key: string): unknown;
|
|
671
|
-
/** Auth session for conditional rendering. */
|
|
672
|
-
session: PrefetchSession | undefined;
|
|
673
|
-
/** Route params for the current request. */
|
|
674
|
-
params: Record<string, string>;
|
|
675
|
-
}
|
|
676
|
-
/** An AOT render function: takes props/data and context, returns HTML string. */
|
|
677
|
-
type AotRenderFn = (data: Record<string, unknown>, ctx: SSRAotContext) => string;
|
|
678
|
-
/** Per-route AOT entry in the manifest. */
|
|
679
|
-
interface AotRouteEntry {
|
|
680
|
-
/** The pre-compiled render function. */
|
|
681
|
-
render: AotRenderFn;
|
|
682
|
-
/** Component names that need runtime fallback (holes). */
|
|
683
|
-
holes: string[];
|
|
684
|
-
/** Query cache keys this route reads via ctx.getData(). */
|
|
685
|
-
queryKeys?: string[];
|
|
686
|
-
}
|
|
847
|
+
declare function renderToStream(tree: VNode | string | RawHtml, options?: RenderToStreamOptions): ReadableStream<Uint8Array>;
|
|
848
|
+
/** Reset the slot counter (for testing). */
|
|
849
|
+
declare function resetSlotCounter(): void;
|
|
687
850
|
/**
|
|
688
|
-
*
|
|
851
|
+
* Create a Suspense slot placeholder.
|
|
689
852
|
*
|
|
690
|
-
*
|
|
853
|
+
* Wraps fallback content in a `<div id="v-slot-N">` so it can later
|
|
854
|
+
* be replaced by the resolved async content via a template chunk.
|
|
855
|
+
*
|
|
856
|
+
* Returns the placeholder VNode and the assigned slot ID.
|
|
691
857
|
*/
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
}
|
|
696
|
-
/** Options for `ssrRenderAot()`. */
|
|
697
|
-
interface SSRRenderAotOptions {
|
|
698
|
-
/** AOT manifest with pre-compiled render functions. */
|
|
699
|
-
aotManifest: AotManifest;
|
|
700
|
-
/** Prefetch manifest for route matching and data fetching. */
|
|
701
|
-
manifest?: SSRPrefetchManifest;
|
|
702
|
-
/** SSR timeout in ms. */
|
|
703
|
-
ssrTimeout?: number;
|
|
704
|
-
/** Pre-computed font fallback metrics. */
|
|
705
|
-
fallbackMetrics?: Record<string, FontFallbackMetrics5>;
|
|
706
|
-
/** Auth state resolved from session cookie. */
|
|
707
|
-
ssrAuth?: SSRAuth2;
|
|
708
|
-
/** Session data for access rule evaluation. */
|
|
709
|
-
prefetchSession?: PrefetchSession;
|
|
710
|
-
/** AOT diagnostics collector (dev mode). When provided with VERTZ_DEBUG=aot, enables dual rendering and divergence detection. */
|
|
711
|
-
diagnostics?: AotDiagnostics;
|
|
712
|
-
}
|
|
858
|
+
declare function createSlotPlaceholder(fallback: VNode | string): VNode & {
|
|
859
|
+
_slotId: number;
|
|
860
|
+
};
|
|
861
|
+
import { AccessSet as AccessSet2 } from "@vertz/ui/auth";
|
|
713
862
|
/**
|
|
714
|
-
*
|
|
715
|
-
* that cannot be AOT-compiled.
|
|
863
|
+
* Extract an AccessSet from a decoded JWT payload for SSR injection.
|
|
716
864
|
*
|
|
717
|
-
*
|
|
718
|
-
*
|
|
719
|
-
*
|
|
720
|
-
* 3. Converts the result to VNode and serializes to HTML string
|
|
721
|
-
* 4. Shares the query cache with the AOT function
|
|
865
|
+
* - If the JWT has an inline `acl.set` (no overflow), returns the decoded AccessSet.
|
|
866
|
+
* - If `acl.overflow` is true, returns `null` — caller should re-compute from live stores.
|
|
867
|
+
* - If no `acl` claim exists, returns `null`.
|
|
722
868
|
*
|
|
723
|
-
* @param
|
|
724
|
-
* @
|
|
725
|
-
* @param url - Request URL for context
|
|
726
|
-
* @param queryCache - Pre-populated query cache (shared with AOT)
|
|
727
|
-
* @param ssrAuth - Auth state for the request
|
|
869
|
+
* @param jwtPayload - The decoded JWT payload (from verifyJWT)
|
|
870
|
+
* @returns The AccessSet for SSR injection, or null
|
|
728
871
|
*/
|
|
729
|
-
declare function
|
|
872
|
+
declare function getAccessSetForSSR(jwtPayload: Record<string, unknown> | null): AccessSet2 | null;
|
|
730
873
|
/**
|
|
731
|
-
*
|
|
732
|
-
*
|
|
733
|
-
* Falls back to `ssrRenderSinglePass()` when:
|
|
734
|
-
* - No route match in the AOT manifest
|
|
735
|
-
* - No prefetch manifest for route matching
|
|
874
|
+
* Serialize an AccessSet into a `<script>` tag that sets
|
|
875
|
+
* `window.__VERTZ_ACCESS_SET__`.
|
|
736
876
|
*
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
* 2. Look up AOT entry in manifest
|
|
740
|
-
* 3. Prefetch query data (reuses single-pass prefetch logic)
|
|
741
|
-
* 4. Create runtime holes (closures for non-AOT components)
|
|
742
|
-
* 5. Call AOT render function with data + context
|
|
743
|
-
* 6. Collect CSS, ssrData, headTags
|
|
744
|
-
* 7. Return SSRRenderResult
|
|
877
|
+
* @param accessSet - The access set to serialize
|
|
878
|
+
* @param nonce - Optional CSP nonce for the script tag
|
|
745
879
|
*/
|
|
746
|
-
declare function
|
|
747
|
-
|
|
748
|
-
|
|
880
|
+
declare function createAccessSetScript(accessSet: AccessSet2, nonce?: string): string;
|
|
881
|
+
import { RenderAdapter } from "@vertz/ui/internals";
|
|
882
|
+
/**
|
|
883
|
+
* Create an SSR adapter that uses in-memory SSR node classes.
|
|
884
|
+
* Replaces `installDomShim()` — no global mutation needed.
|
|
885
|
+
*/
|
|
886
|
+
declare function createSSRAdapter(): RenderAdapter;
|
|
887
|
+
/** Per-component entry in the dev AOT manifest. */
|
|
888
|
+
interface AotDevComponentEntry {
|
|
889
|
+
tier: "static" | "data-driven" | "conditional" | "runtime-fallback";
|
|
890
|
+
holes: string[];
|
|
891
|
+
/** The file this component was compiled from. */
|
|
892
|
+
file: string;
|
|
893
|
+
}
|
|
894
|
+
/** Dev-mode AOT manifest — tracks all components across all files. */
|
|
895
|
+
interface AotDevManifest {
|
|
896
|
+
components: Record<string, AotDevComponentEntry>;
|
|
897
|
+
}
|
|
898
|
+
interface AotManifestSnapshot {
|
|
899
|
+
manifest: AotDevManifest | null;
|
|
900
|
+
rebuildCount: number;
|
|
901
|
+
lastRebuildMs: number | null;
|
|
902
|
+
lastRebuildAt: string | null;
|
|
903
|
+
}
|
|
904
|
+
interface AotManifestManagerOptions {
|
|
905
|
+
/** Read a file's contents. Returns undefined if file doesn't exist. */
|
|
906
|
+
readFile: (path: string) => string | undefined;
|
|
907
|
+
/** List all files in the source directory (absolute paths). */
|
|
908
|
+
listFiles: () => string[];
|
|
909
|
+
}
|
|
910
|
+
interface AotManifestManager {
|
|
911
|
+
/** Initial full build — compile all TSX files. */
|
|
912
|
+
build(): void;
|
|
913
|
+
/** Handle a file change — recompile a single file. */
|
|
914
|
+
onFileChange(filePath: string, sourceText: string): void;
|
|
915
|
+
/** Get the current manifest (atomic read). */
|
|
916
|
+
getManifest(): AotDevManifest | null;
|
|
917
|
+
/** Get diagnostic snapshot for endpoints. */
|
|
918
|
+
getSnapshot(): AotManifestSnapshot;
|
|
919
|
+
/** Get AotDiagnostics instance for the diagnostics endpoint. */
|
|
920
|
+
getDiagnostics(): AotDiagnostics;
|
|
921
|
+
}
|
|
922
|
+
declare function createAotManifestManager(options: AotManifestManagerOptions): AotManifestManager;
|
|
749
923
|
/**
|
|
750
924
|
* SSR AOT Runtime Helpers
|
|
751
925
|
*
|
|
@@ -821,109 +995,6 @@ declare function clearGlobalSSRTimeout(): void;
|
|
|
821
995
|
* Returns undefined if not set or outside SSR context.
|
|
822
996
|
*/
|
|
823
997
|
declare function getGlobalSSRTimeout(): number | undefined;
|
|
824
|
-
import { FontFallbackMetrics as FontFallbackMetrics6 } from "@vertz/ui";
|
|
825
|
-
import { AccessSet as AccessSet2 } from "@vertz/ui/auth";
|
|
826
|
-
interface SessionData {
|
|
827
|
-
user: {
|
|
828
|
-
id: string;
|
|
829
|
-
email: string;
|
|
830
|
-
role: string;
|
|
831
|
-
[key: string]: unknown;
|
|
832
|
-
};
|
|
833
|
-
/** Unix timestamp in milliseconds (JWT exp * 1000). */
|
|
834
|
-
expiresAt: number;
|
|
835
|
-
}
|
|
836
|
-
/** Resolved session data for SSR injection. */
|
|
837
|
-
interface SSRSessionInfo {
|
|
838
|
-
session: SessionData;
|
|
839
|
-
/**
|
|
840
|
-
* Access set from JWT acl claim.
|
|
841
|
-
* - Present (object): inline access set (no overflow)
|
|
842
|
-
* - null: access control is configured but the set overflowed the JWT
|
|
843
|
-
* - undefined: access control is not configured
|
|
844
|
-
*/
|
|
845
|
-
accessSet?: AccessSet2 | null;
|
|
846
|
-
}
|
|
847
|
-
/**
|
|
848
|
-
* Callback that extracts session data from a request.
|
|
849
|
-
* Returns null when no valid session exists (expired, missing, or invalid cookie).
|
|
850
|
-
*/
|
|
851
|
-
type SessionResolver = (request: Request) => Promise<SSRSessionInfo | null>;
|
|
852
|
-
/**
|
|
853
|
-
* Serialize a session into a `<script>` tag that sets
|
|
854
|
-
* `window.__VERTZ_SESSION__`.
|
|
855
|
-
*
|
|
856
|
-
* @param session - The session data to serialize
|
|
857
|
-
* @param nonce - Optional CSP nonce for the script tag
|
|
858
|
-
*/
|
|
859
|
-
declare function createSessionScript(session: SessionData, nonce?: string): string;
|
|
860
|
-
interface SSRHandlerOptions {
|
|
861
|
-
/** The loaded SSR module (import('./dist/server/index.js')) */
|
|
862
|
-
module: SSRModule;
|
|
863
|
-
/** HTML template string (contents of dist/client/index.html) */
|
|
864
|
-
template: string;
|
|
865
|
-
/** SSR timeout for queries (default: 300ms) */
|
|
866
|
-
ssrTimeout?: number;
|
|
867
|
-
/**
|
|
868
|
-
* Map of CSS asset URLs to their content for inlining.
|
|
869
|
-
* Replaces `<link rel="stylesheet" href="...">` tags with inline `<style>` tags.
|
|
870
|
-
* Eliminates extra network requests, preventing FOUC on slow connections.
|
|
871
|
-
*
|
|
872
|
-
* @example
|
|
873
|
-
* ```ts
|
|
874
|
-
* inlineCSS: { '/assets/vertz.css': await Bun.file('./dist/client/assets/vertz.css').text() }
|
|
875
|
-
* ```
|
|
876
|
-
*/
|
|
877
|
-
inlineCSS?: Record<string, string>;
|
|
878
|
-
/**
|
|
879
|
-
* CSP nonce to inject on all inline `<script>` tags emitted during SSR.
|
|
880
|
-
*
|
|
881
|
-
* When set, the SSR data hydration script will include `nonce="<value>"`
|
|
882
|
-
* so that strict Content-Security-Policy headers do not block it.
|
|
883
|
-
*/
|
|
884
|
-
nonce?: string;
|
|
885
|
-
/** Pre-computed font fallback metrics (computed at server startup). */
|
|
886
|
-
fallbackMetrics?: Record<string, FontFallbackMetrics6>;
|
|
887
|
-
/** Paths to inject as `<link rel="modulepreload">` in `<head>`. */
|
|
888
|
-
modulepreload?: string[];
|
|
889
|
-
/**
|
|
890
|
-
* Route chunk manifest for per-route modulepreload injection.
|
|
891
|
-
* When provided, only chunks for the matched route are preloaded instead of all chunks.
|
|
892
|
-
*/
|
|
893
|
-
routeChunkManifest?: {
|
|
894
|
-
routes: Record<string, string[]>;
|
|
895
|
-
};
|
|
896
|
-
/** Cache-Control header for HTML responses. Omit or undefined = no header (safe default). */
|
|
897
|
-
cacheControl?: string;
|
|
898
|
-
/**
|
|
899
|
-
* Resolves session data from request cookies for SSR injection.
|
|
900
|
-
* When provided, SSR HTML includes `window.__VERTZ_SESSION__` and
|
|
901
|
-
* optionally `window.__VERTZ_ACCESS_SET__` for instant auth hydration.
|
|
902
|
-
*/
|
|
903
|
-
sessionResolver?: SessionResolver;
|
|
904
|
-
/**
|
|
905
|
-
* Prefetch manifest for single-pass SSR optimization.
|
|
906
|
-
*
|
|
907
|
-
* When provided with route entries and an API client export, enables
|
|
908
|
-
* zero-discovery rendering — queries are prefetched from the manifest
|
|
909
|
-
* without executing the component tree, then a single render pass
|
|
910
|
-
* produces the HTML. Without a manifest, SSR still uses the single-pass
|
|
911
|
-
* discovery-then-render approach (cheaper than two-pass).
|
|
912
|
-
*/
|
|
913
|
-
manifest?: SSRPrefetchManifest;
|
|
914
|
-
/**
|
|
915
|
-
* Enable progressive HTML streaming. Default: false.
|
|
916
|
-
*
|
|
917
|
-
* When true, the Response body is a ReadableStream that sends `<head>`
|
|
918
|
-
* content (CSS, preloads, fonts) before `<body>` rendering is complete.
|
|
919
|
-
* This improves TTFB and FCP.
|
|
920
|
-
*
|
|
921
|
-
* Has no effect on zero-discovery routes (manifest with routeEntries),
|
|
922
|
-
* which always use buffered rendering.
|
|
923
|
-
*/
|
|
924
|
-
progressiveHTML?: boolean;
|
|
925
|
-
}
|
|
926
|
-
declare function createSSRHandler(options: SSRHandlerOptions): (request: Request) => Promise<Response>;
|
|
927
998
|
interface GenerateSSRHtmlOptions {
|
|
928
999
|
appHtml: string;
|
|
929
1000
|
css: string;
|
|
@@ -1055,4 +1126,4 @@ declare function collectStreamChunks(stream: ReadableStream<Uint8Array>): Promis
|
|
|
1055
1126
|
* @param nonce - Optional CSP nonce to add to the inline script tag.
|
|
1056
1127
|
*/
|
|
1057
1128
|
declare function createTemplateChunk(slotId: number, resolvedHtml: string, nonce?: string): string;
|
|
1058
|
-
export { wrapWithHydrationMarkers, toPrefetchSession, streamToString, ssrStorage, ssrRenderToString, ssrRenderSinglePass, ssrRenderAot, ssrDiscoverQueries, setGlobalSSRTimeout, serializeToHtml, safeSerialize, resetSlotCounter, renderToStream, renderToHTMLStream, renderToHTML, renderPage, renderHeadToHtml, renderAssetTags, registerSSRQuery, reconstructDescriptors, rawHtml, matchUrlToPatterns, isInSSR, isAotDebugEnabled, inlineCriticalCss, getStreamingRuntimeScript, getSSRUrl, getSSRQueries, getGlobalSSRTimeout, getAccessSetForSSR, generateSSRHtml, generateAotBuildManifest, extractFontMetrics, evaluateAccessRule, encodeChunk, detectFallbackFont, createTemplateChunk, createSlotPlaceholder, createSessionScript, createSSRHandler, createSSRDataChunk, createSSRAdapter, createPrefetchManifestManager, createHoles, createAotManifestManager, createAccessSetScript, collectStreamChunks, clearGlobalSSRTimeout, __ssr_style_object, __ssr_spread, __esc_attr, __esc, VNode, SessionResolver, SessionData, SerializedAccessRule, SSRSinglePassOptions, SSRSessionInfo, SSRRenderResult, SSRRenderAotOptions, SSRQueryEntry2 as SSRQueryEntry, SSRPrefetchManifest, SSRModule, SSRHandlerOptions, SSRDiscoverResult, SSRAotContext, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, ReconstructedDescriptor, RawHtml, PrefetchSession, PrefetchManifestSnapshot, PrefetchManifestManagerOptions, PrefetchManifestManager, PageOptions, MatchedRoute, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, FontFallbackMetrics7 as FontFallbackMetrics, FallbackFontName2 as FallbackFontName, EntityAccessMap, AssetDescriptor, AotTier, AotRouteEntry, AotRenderFn, AotManifestSnapshot, AotManifestManagerOptions, AotManifestManager, AotManifest, AotDivergenceEntry, AotDiagnosticsSnapshot, AotDiagnostics, AotDevManifest, AotDevComponentEntry, AotComponentDiagnostic, AotBuildManifest, AotBuildComponentEntry };
|
|
1129
|
+
export { wrapWithHydrationMarkers, toPrefetchSession, streamToString, ssrStorage, ssrRenderToString, ssrRenderSinglePass, ssrRenderAot, ssrDiscoverQueries, setGlobalSSRTimeout, serializeToHtml, safeSerialize, resetSlotCounter, renderToStream, renderToHTMLStream, renderToHTML, renderPage, renderHeadToHtml, renderAssetTags, registerSSRQuery, reconstructDescriptors, rawHtml, matchUrlToPatterns, loadAotManifest, isInSSR, isAotDebugEnabled, inlineCriticalCss, getStreamingRuntimeScript, getSSRUrl, getSSRQueries, getGlobalSSRTimeout, getAccessSetForSSR, generateSSRHtml, generateAotBuildManifest, generateAotBarrel, extractRoutes, extractFontMetrics, evaluateAccessRule, encodeChunk, detectFallbackFont, createTemplateChunk, createSlotPlaceholder, createSessionScript, createSSRHandler, createSSRDataChunk, createSSRAdapter, createPrefetchManifestManager, createNodeHandler, createHoles, createAotManifestManager, createAccessSetScript, collectStreamChunks, clearGlobalSSRTimeout, buildAotRouteMap, __ssr_style_object, __ssr_spread, __esc_attr, __esc, VNode, SessionResolver, SessionData, SerializedAccessRule, SSRSinglePassOptions, SSRSessionInfo, SSRRenderResult, SSRRenderAotOptions, SSRQueryEntry2 as SSRQueryEntry, SSRPrefetchManifest, SSRModule, SSRHandlerOptions, SSRDiscoverResult, SSRAotContext, RenderToStreamOptions, RenderToHTMLStreamOptions, RenderToHTMLOptions, ReconstructedDescriptor, RawHtml, PrefetchSession, PrefetchManifestSnapshot, PrefetchManifestManagerOptions, PrefetchManifestManager, PageOptions, NodeHandlerOptions, MatchedRoute, HydrationOptions, HeadEntry, HeadCollector, GenerateSSRHtmlOptions, FontFallbackMetrics7 as FontFallbackMetrics, FallbackFontName2 as FallbackFontName, ExtractedRoute, EntityAccessMap, AssetDescriptor, AotTier, AotRouteMapEntry, AotRouteEntry, AotRenderFn, AotManifestSnapshot, AotManifestManagerOptions, AotManifestManager, AotManifest, AotDivergenceEntry, AotDiagnosticsSnapshot, AotDiagnostics, AotDevManifest, AotDevComponentEntry, AotComponentDiagnostic, AotCompiledFile, AotBuildManifest, AotBuildComponentEntry, AotBarrelResult };
|