@vertz/ui-server 0.2.21 → 0.2.23

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.
@@ -72,6 +72,16 @@ interface BunDevServerOptions {
72
72
  * optionally `window.__VERTZ_ACCESS_SET__` for instant auth hydration.
73
73
  */
74
74
  sessionResolver?: SessionResolver;
75
+ /**
76
+ * Watch workspace-linked package dist directories for changes.
77
+ * When a dist directory changes, automatically restart the server.
78
+ *
79
+ * Accepts an array of package names (e.g., ['@vertz/theme-shadcn', '@vertz/ui'])
80
+ * or `true` to auto-detect all `@vertz/*` packages linked via workspace symlinks.
81
+ *
82
+ * @default false
83
+ */
84
+ watchDeps?: boolean | string[];
75
85
  }
76
86
  interface ErrorDetail {
77
87
  message: string;
@@ -9,7 +9,7 @@ import {
9
9
 
10
10
  // src/bun-dev-server.ts
11
11
  import { execSync } from "child_process";
12
- import { existsSync, mkdirSync, readFileSync as readFileSync2, watch, writeFileSync as writeFileSync2 } from "fs";
12
+ import { existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, watch as watch2, writeFileSync as writeFileSync2 } from "fs";
13
13
  import { dirname, normalize, resolve } from "path";
14
14
 
15
15
  // src/debug-logger.ts
@@ -286,6 +286,7 @@ function runWithScopedFetch(interceptor, fn) {
286
286
  }
287
287
 
288
288
  // src/font-metrics.ts
289
+ import { access as fsAccess } from "fs/promises";
289
290
  import { readFile } from "fs/promises";
290
291
  import { join as join2 } from "path";
291
292
  import { fromBuffer } from "@capsizecss/unpack";
@@ -354,9 +355,15 @@ function getPrimarySrcPath(descriptor) {
354
355
  return first.path;
355
356
  return null;
356
357
  }
357
- function resolveFilePath(urlPath, rootDir) {
358
+ async function resolveFilePath(urlPath, rootDir) {
358
359
  const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
359
- return join2(rootDir, cleaned);
360
+ const direct = join2(rootDir, cleaned);
361
+ try {
362
+ await fsAccess(direct);
363
+ return direct;
364
+ } catch {
365
+ return join2(rootDir, "public", cleaned);
366
+ }
360
367
  }
361
368
  async function extractFontMetrics(fonts, rootDir) {
362
369
  const result = {};
@@ -370,7 +377,7 @@ async function extractFontMetrics(fonts, rootDir) {
370
377
  if (!srcPath.toLowerCase().endsWith(".woff2"))
371
378
  continue;
372
379
  try {
373
- const filePath = resolveFilePath(srcPath, rootDir);
380
+ const filePath = await resolveFilePath(srcPath, rootDir);
374
381
  const buffer = await readFile(filePath);
375
382
  const metrics = await fromBuffer(buffer);
376
383
  const fallbackFont = typeof adjustFontFallback === "string" ? adjustFontFallback : detectFallbackFont(descriptor.fallback);
@@ -891,6 +898,96 @@ class SSRElement extends SSRNode {
891
898
  get innerHTML() {
892
899
  return this._innerHTML ?? "";
893
900
  }
901
+ get placeholder() {
902
+ return this.attrs.placeholder ?? "";
903
+ }
904
+ set placeholder(value) {
905
+ this.attrs.placeholder = value;
906
+ }
907
+ get type() {
908
+ return this.attrs.type ?? "";
909
+ }
910
+ set type(value) {
911
+ this.attrs.type = value;
912
+ }
913
+ get name() {
914
+ return this.attrs.name ?? "";
915
+ }
916
+ set name(value) {
917
+ this.attrs.name = value;
918
+ }
919
+ get value() {
920
+ return this.attrs.value ?? "";
921
+ }
922
+ set value(value) {
923
+ this.attrs.value = value;
924
+ }
925
+ get src() {
926
+ return this.attrs.src ?? "";
927
+ }
928
+ set src(value) {
929
+ this.attrs.src = value;
930
+ }
931
+ get alt() {
932
+ return this.attrs.alt ?? "";
933
+ }
934
+ set alt(value) {
935
+ this.attrs.alt = value;
936
+ }
937
+ get htmlFor() {
938
+ return this.attrs.for ?? "";
939
+ }
940
+ set htmlFor(value) {
941
+ this.attrs.for = value;
942
+ }
943
+ get disabled() {
944
+ return "disabled" in this.attrs;
945
+ }
946
+ set disabled(value) {
947
+ if (value) {
948
+ this.attrs.disabled = "";
949
+ } else {
950
+ delete this.attrs.disabled;
951
+ }
952
+ }
953
+ get checked() {
954
+ return "checked" in this.attrs;
955
+ }
956
+ set checked(value) {
957
+ if (value) {
958
+ this.attrs.checked = "";
959
+ } else {
960
+ delete this.attrs.checked;
961
+ }
962
+ }
963
+ get selected() {
964
+ return "selected" in this.attrs;
965
+ }
966
+ set selected(value) {
967
+ if (value) {
968
+ this.attrs.selected = "";
969
+ } else {
970
+ delete this.attrs.selected;
971
+ }
972
+ }
973
+ get rows() {
974
+ return Number(this.attrs.rows) || 0;
975
+ }
976
+ set rows(value) {
977
+ this.attrs.rows = String(value);
978
+ }
979
+ get scope() {
980
+ return this.attrs.scope ?? "";
981
+ }
982
+ set scope(value) {
983
+ this.attrs.scope = value;
984
+ }
985
+ get href() {
986
+ return this.attrs.href ?? "";
987
+ }
988
+ set href(value) {
989
+ this.attrs.href = value;
990
+ }
894
991
  addEventListener(_event, _handler) {}
895
992
  removeEventListener(_event, _handler) {}
896
993
  toVNode() {
@@ -1524,15 +1621,95 @@ function escapeAttr3(s) {
1524
1621
  return s.replace(/[&"'<>]/g, (c) => `&#${c.charCodeAt(0)};`);
1525
1622
  }
1526
1623
 
1624
+ // src/upstream-watcher.ts
1625
+ import { existsSync, lstatSync, readdirSync, realpathSync, watch } from "fs";
1626
+ import { join as join3 } from "path";
1627
+ function resolveWorkspacePackages(projectRoot, filter) {
1628
+ const results = [];
1629
+ const packageNames = filter === true ? discoverVertzPackages(projectRoot) : parsePackageNames(filter);
1630
+ for (const { scope, name } of packageNames) {
1631
+ const nmPath = join3(projectRoot, "node_modules", scope, name);
1632
+ if (!existsSync(nmPath))
1633
+ continue;
1634
+ const stat = lstatSync(nmPath);
1635
+ if (!stat.isSymbolicLink())
1636
+ continue;
1637
+ const realPath = realpathSync(nmPath);
1638
+ const distPath = join3(realPath, "dist");
1639
+ if (!existsSync(distPath))
1640
+ continue;
1641
+ const fullName = scope ? `${scope}/${name}` : name;
1642
+ results.push({ name: fullName, distPath });
1643
+ }
1644
+ return results;
1645
+ }
1646
+ function discoverVertzPackages(projectRoot) {
1647
+ const scopeDir = join3(projectRoot, "node_modules", "@vertz");
1648
+ if (!existsSync(scopeDir))
1649
+ return [];
1650
+ return readdirSync(scopeDir).filter((entry) => !entry.startsWith(".")).map((entry) => ({ scope: "@vertz", name: entry }));
1651
+ }
1652
+ function parsePackageNames(names) {
1653
+ return names.map((pkg) => {
1654
+ const match = pkg.match(/^(@[^/]+)\/(.+)$/);
1655
+ if (match?.[1] && match[2])
1656
+ return { scope: match[1], name: match[2] };
1657
+ return { scope: "", name: pkg };
1658
+ });
1659
+ }
1660
+ function createUpstreamWatcher(options) {
1661
+ const { projectRoot, watchDeps, onDistChanged, debounceMs = 1000, persistent = false } = options;
1662
+ const packages = resolveWorkspacePackages(projectRoot, watchDeps);
1663
+ const watchers = [];
1664
+ const debounceTimers = new Map;
1665
+ let closed = false;
1666
+ for (const pkg of packages) {
1667
+ try {
1668
+ const watcher = watch(pkg.distPath, { recursive: true, persistent }, () => {
1669
+ if (closed)
1670
+ return;
1671
+ const existing = debounceTimers.get(pkg.name);
1672
+ if (existing)
1673
+ clearTimeout(existing);
1674
+ debounceTimers.set(pkg.name, setTimeout(() => {
1675
+ debounceTimers.delete(pkg.name);
1676
+ if (!closed) {
1677
+ onDistChanged(pkg.name);
1678
+ }
1679
+ }, debounceMs));
1680
+ });
1681
+ watcher.on("error", () => {});
1682
+ watchers.push(watcher);
1683
+ } catch {}
1684
+ }
1685
+ return {
1686
+ packages,
1687
+ close() {
1688
+ closed = true;
1689
+ for (const w of watchers) {
1690
+ try {
1691
+ w.close();
1692
+ } catch {}
1693
+ }
1694
+ watchers.length = 0;
1695
+ for (const timer of debounceTimers.values()) {
1696
+ clearTimeout(timer);
1697
+ }
1698
+ debounceTimers.clear();
1699
+ }
1700
+ };
1701
+ }
1702
+
1527
1703
  // src/bun-dev-server.ts
1528
1704
  function detectFaviconTag(projectRoot) {
1529
1705
  const faviconPath = resolve(projectRoot, "public", "favicon.svg");
1530
- return existsSync(faviconPath) ? '<link rel="icon" type="image/svg+xml" href="/favicon.svg">' : "";
1706
+ return existsSync2(faviconPath) ? '<link rel="icon" type="image/svg+xml" href="/favicon.svg">' : "";
1531
1707
  }
1532
1708
  var STALE_GRAPH_PATTERNS = [
1533
1709
  /Export named ['"].*['"] not found in module/i,
1534
1710
  /No matching export in ['"].*['"] for import/i,
1535
- /does not provide an export named/i
1711
+ /does not provide an export named/i,
1712
+ /Failed to resolve module specifier/i
1536
1713
  ];
1537
1714
  function isStaleGraphError(message) {
1538
1715
  return STALE_GRAPH_PATTERNS.some((pattern) => pattern.test(message));
@@ -1632,7 +1809,7 @@ function buildErrorChannelScript(editor) {
1632
1809
  "V._hadClientError=false;",
1633
1810
  "V._needsReload=false;",
1634
1811
  "V._restarting=false;",
1635
- `V.isStaleGraph=function(m){return/Export named ['"].*['"] not found in module/i.test(m)||/No matching export in ['"].*['"] for import/i.test(m)||/does not provide an export named/i.test(m)};`,
1812
+ `V.isStaleGraph=function(m){return/Export named ['"].*['"] not found in module/i.test(m)||/No matching export in ['"].*['"] for import/i.test(m)||/does not provide an export named/i.test(m)||/Failed to resolve module specifier/i.test(m)};`,
1636
1813
  "V._canAutoRestart=function(){",
1637
1814
  "var raw=sessionStorage.getItem('__vertz_auto_restart');",
1638
1815
  "var ts;try{ts=raw?JSON.parse(raw):[]}catch(e){ts=[]}",
@@ -1904,7 +2081,8 @@ function createBunDevServer(options) {
1904
2081
  logRequests = true,
1905
2082
  editor: editorOption,
1906
2083
  headTags: headTagsOption = "",
1907
- sessionResolver
2084
+ sessionResolver,
2085
+ watchDeps
1908
2086
  } = options;
1909
2087
  const faviconTag = detectFaviconTag(projectRoot);
1910
2088
  const headTags = [faviconTag, headTagsOption].filter(Boolean).join(`
@@ -2136,7 +2314,7 @@ function createBunDevServer(options) {
2136
2314
  }
2137
2315
  };
2138
2316
  const setupOpenAPIWatcher = () => {
2139
- if (!openapi || !existsSync(openapi.specPath))
2317
+ if (!openapi || !existsSync2(openapi.specPath))
2140
2318
  return;
2141
2319
  cachedSpec = loadOpenAPISpec();
2142
2320
  if (cachedSpec === null)
@@ -2144,7 +2322,7 @@ function createBunDevServer(options) {
2144
2322
  try {
2145
2323
  const specDir = dirname(openapi.specPath);
2146
2324
  const specFile = openapi.specPath.split("/").pop() || "openapi.json";
2147
- specWatcher = watch(specDir, { persistent: false }, (eventType, filename) => {
2325
+ specWatcher = watch2(specDir, { persistent: false }, (eventType, filename) => {
2148
2326
  if (filename === specFile && (eventType === "change" || eventType === "rename")) {
2149
2327
  if (logRequests) {
2150
2328
  console.log("[Server] OpenAPI spec file changed, reloading...");
@@ -2162,7 +2340,7 @@ function createBunDevServer(options) {
2162
2340
  headers: { "Content-Type": "application/json" }
2163
2341
  });
2164
2342
  }
2165
- if (openapi && existsSync(openapi.specPath)) {
2343
+ if (openapi && existsSync2(openapi.specPath)) {
2166
2344
  cachedSpec = loadOpenAPISpec();
2167
2345
  if (cachedSpec) {
2168
2346
  return new Response(JSON.stringify(cachedSpec), {
@@ -2175,6 +2353,42 @@ function createBunDevServer(options) {
2175
2353
  let isRestarting = false;
2176
2354
  let pluginsRegistered = false;
2177
2355
  let stableUpdateManifest = null;
2356
+ let upstreamWatcherRef = null;
2357
+ let pendingDistRestart = false;
2358
+ let restartFn = null;
2359
+ if (watchDeps) {
2360
+ upstreamWatcherRef = createUpstreamWatcher({
2361
+ projectRoot,
2362
+ watchDeps,
2363
+ onDistChanged: (pkgName) => {
2364
+ if (!restartFn || stopped)
2365
+ return;
2366
+ if (logRequests) {
2367
+ console.log(`[Server] Upstream package rebuilt: ${pkgName} \u2014 restarting...`);
2368
+ }
2369
+ if (isRestarting) {
2370
+ pendingDistRestart = true;
2371
+ return;
2372
+ }
2373
+ restartFn().then(() => {
2374
+ if (pendingDistRestart && restartFn && !stopped) {
2375
+ pendingDistRestart = false;
2376
+ restartFn().catch((err) => {
2377
+ const msg = err instanceof Error ? err.message : String(err);
2378
+ console.error(`[Server] Pending upstream restart failed: ${msg}`);
2379
+ });
2380
+ }
2381
+ }).catch((err) => {
2382
+ const msg = err instanceof Error ? err.message : String(err);
2383
+ console.error(`[Server] Upstream restart failed: ${msg}`);
2384
+ });
2385
+ }
2386
+ });
2387
+ if (logRequests && upstreamWatcherRef.packages.length > 0) {
2388
+ const names = upstreamWatcherRef.packages.map((p) => p.name).join(", ");
2389
+ console.log(`[Server] Watching upstream packages: ${names}`);
2390
+ }
2391
+ }
2178
2392
  async function start() {
2179
2393
  const { plugin } = await Promise.resolve(globalThis.Bun);
2180
2394
  const { createVertzBunPlugin } = await import("./bun-plugin/index.js");
@@ -2640,8 +2854,8 @@ data: {}
2640
2854
  }
2641
2855
  const srcDir = resolve(projectRoot, "src");
2642
2856
  stopped = false;
2643
- if (existsSync(srcDir)) {
2644
- srcWatcherRef = watch(srcDir, { recursive: true }, (_event, filename) => {
2857
+ if (existsSync2(srcDir)) {
2858
+ srcWatcherRef = watch2(srcDir, { recursive: true }, (_event, filename) => {
2645
2859
  if (!filename)
2646
2860
  return;
2647
2861
  if (refreshTimeout)
@@ -2800,6 +3014,10 @@ data: {}
2800
3014
  server.stop(true);
2801
3015
  server = null;
2802
3016
  }
3017
+ if (upstreamWatcherRef) {
3018
+ upstreamWatcherRef.close();
3019
+ upstreamWatcherRef = null;
3020
+ }
2803
3021
  },
2804
3022
  async restart() {
2805
3023
  if (isRestarting) {
@@ -2859,6 +3077,7 @@ data: {}
2859
3077
  isRestarting = false;
2860
3078
  }
2861
3079
  };
3080
+ restartFn = () => devServer.restart();
2862
3081
  return devServer;
2863
3082
  }
2864
3083
  export {
@@ -84,6 +84,32 @@ declare class SSRElement extends SSRNode {
84
84
  get textContent(): string | null;
85
85
  set innerHTML(value: string);
86
86
  get innerHTML(): string;
87
+ get placeholder(): string;
88
+ set placeholder(value: string);
89
+ get type(): string;
90
+ set type(value: string);
91
+ get name(): string;
92
+ set name(value: string);
93
+ get value(): string;
94
+ set value(value: string);
95
+ get src(): string;
96
+ set src(value: string);
97
+ get alt(): string;
98
+ set alt(value: string);
99
+ get htmlFor(): string;
100
+ set htmlFor(value: string);
101
+ get disabled(): boolean;
102
+ set disabled(value: boolean);
103
+ get checked(): boolean;
104
+ set checked(value: boolean);
105
+ get selected(): boolean;
106
+ set selected(value: boolean);
107
+ get rows(): number;
108
+ set rows(value: number);
109
+ get scope(): string;
110
+ set scope(value: string);
111
+ get href(): string;
112
+ set href(value: string);
87
113
  addEventListener(_event: string, _handler: any): void;
88
114
  removeEventListener(_event: string, _handler: any): void;
89
115
  /** Convert to a VNode tree for @vertz/ui-server */
@@ -7,7 +7,7 @@ import {
7
7
  installDomShim,
8
8
  removeDomShim,
9
9
  toVNode
10
- } from "../shared/chunk-8matma4a.js";
10
+ } from "../shared/chunk-bm16zy8d.js";
11
11
  export {
12
12
  toVNode,
13
13
  removeDomShim,
package/dist/index.d.ts CHANGED
@@ -80,6 +80,7 @@ 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 as FallbackFontName2, FontFallbackMetrics as FontFallbackMetrics5 } from "@vertz/ui";
83
84
  import { FallbackFontName, FontDescriptor, FontFallbackMetrics } from "@vertz/ui";
84
85
  /**
85
86
  * Auto-detect which system font to use as fallback base.
@@ -589,4 +590,4 @@ declare function collectStreamChunks(stream: ReadableStream<Uint8Array>): Promis
589
590
  * @param nonce - Optional CSP nonce to add to the inline script tag.
590
591
  */
591
592
  declare function createTemplateChunk(slotId: number, resolvedHtml: string, nonce?: string): string;
592
- 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 };
593
+ 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, FontFallbackMetrics5 as FontFallbackMetrics, FallbackFontName2 as FallbackFontName, AssetDescriptor };
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ import {
19
19
  ssrDiscoverQueries,
20
20
  ssrRenderToString,
21
21
  streamToString
22
- } from "./shared/chunk-vyjj02de.js";
22
+ } from "./shared/chunk-5cny4vzm.js";
23
23
  import {
24
24
  clearGlobalSSRTimeout,
25
25
  createSSRAdapter,
@@ -31,7 +31,7 @@ import {
31
31
  registerSSRQuery,
32
32
  setGlobalSSRTimeout,
33
33
  ssrStorage
34
- } from "./shared/chunk-8matma4a.js";
34
+ } from "./shared/chunk-bm16zy8d.js";
35
35
 
36
36
  // src/asset-pipeline.ts
37
37
  function renderAssetTags(assets) {
@@ -59,6 +59,7 @@ function inlineCriticalCss(css) {
59
59
  return `<style>${safeCss}</style>`;
60
60
  }
61
61
  // src/font-metrics.ts
62
+ import { access as fsAccess } from "node:fs/promises";
62
63
  import { readFile } from "node:fs/promises";
63
64
  import { join } from "node:path";
64
65
  import { fromBuffer } from "@capsizecss/unpack";
@@ -127,9 +128,15 @@ function getPrimarySrcPath(descriptor) {
127
128
  return first.path;
128
129
  return null;
129
130
  }
130
- function resolveFilePath(urlPath, rootDir) {
131
+ async function resolveFilePath(urlPath, rootDir) {
131
132
  const cleaned = urlPath.startsWith("/") ? urlPath.slice(1) : urlPath;
132
- return join(rootDir, cleaned);
133
+ const direct = join(rootDir, cleaned);
134
+ try {
135
+ await fsAccess(direct);
136
+ return direct;
137
+ } catch {
138
+ return join(rootDir, "public", cleaned);
139
+ }
133
140
  }
134
141
  async function extractFontMetrics(fonts, rootDir) {
135
142
  const result = {};
@@ -143,7 +150,7 @@ async function extractFontMetrics(fonts, rootDir) {
143
150
  if (!srcPath.toLowerCase().endsWith(".woff2"))
144
151
  continue;
145
152
  try {
146
- const filePath = resolveFilePath(srcPath, rootDir);
153
+ const filePath = await resolveFilePath(srcPath, rootDir);
147
154
  const buffer = await readFile(filePath);
148
155
  const metrics = await fromBuffer(buffer);
149
156
  const fallbackFont = typeof adjustFontFallback === "string" ? adjustFontFallback : detectFallbackFont(descriptor.fallback);
@@ -6,7 +6,7 @@ import {
6
6
  setGlobalSSRTimeout,
7
7
  ssrStorage,
8
8
  toVNode
9
- } from "./chunk-8matma4a.js";
9
+ } from "./chunk-bm16zy8d.js";
10
10
 
11
11
  // src/html-serializer.ts
12
12
  var VOID_ELEMENTS = new Set([
@@ -341,6 +341,96 @@ class SSRElement extends SSRNode {
341
341
  get innerHTML() {
342
342
  return this._innerHTML ?? "";
343
343
  }
344
+ get placeholder() {
345
+ return this.attrs.placeholder ?? "";
346
+ }
347
+ set placeholder(value) {
348
+ this.attrs.placeholder = value;
349
+ }
350
+ get type() {
351
+ return this.attrs.type ?? "";
352
+ }
353
+ set type(value) {
354
+ this.attrs.type = value;
355
+ }
356
+ get name() {
357
+ return this.attrs.name ?? "";
358
+ }
359
+ set name(value) {
360
+ this.attrs.name = value;
361
+ }
362
+ get value() {
363
+ return this.attrs.value ?? "";
364
+ }
365
+ set value(value) {
366
+ this.attrs.value = value;
367
+ }
368
+ get src() {
369
+ return this.attrs.src ?? "";
370
+ }
371
+ set src(value) {
372
+ this.attrs.src = value;
373
+ }
374
+ get alt() {
375
+ return this.attrs.alt ?? "";
376
+ }
377
+ set alt(value) {
378
+ this.attrs.alt = value;
379
+ }
380
+ get htmlFor() {
381
+ return this.attrs.for ?? "";
382
+ }
383
+ set htmlFor(value) {
384
+ this.attrs.for = value;
385
+ }
386
+ get disabled() {
387
+ return "disabled" in this.attrs;
388
+ }
389
+ set disabled(value) {
390
+ if (value) {
391
+ this.attrs.disabled = "";
392
+ } else {
393
+ delete this.attrs.disabled;
394
+ }
395
+ }
396
+ get checked() {
397
+ return "checked" in this.attrs;
398
+ }
399
+ set checked(value) {
400
+ if (value) {
401
+ this.attrs.checked = "";
402
+ } else {
403
+ delete this.attrs.checked;
404
+ }
405
+ }
406
+ get selected() {
407
+ return "selected" in this.attrs;
408
+ }
409
+ set selected(value) {
410
+ if (value) {
411
+ this.attrs.selected = "";
412
+ } else {
413
+ delete this.attrs.selected;
414
+ }
415
+ }
416
+ get rows() {
417
+ return Number(this.attrs.rows) || 0;
418
+ }
419
+ set rows(value) {
420
+ this.attrs.rows = String(value);
421
+ }
422
+ get scope() {
423
+ return this.attrs.scope ?? "";
424
+ }
425
+ set scope(value) {
426
+ this.attrs.scope = value;
427
+ }
428
+ get href() {
429
+ return this.attrs.href ?? "";
430
+ }
431
+ set href(value) {
432
+ this.attrs.href = value;
433
+ }
344
434
  addEventListener(_event, _handler) {}
345
435
  removeEventListener(_event, _handler) {}
346
436
  toVNode() {
@@ -1,4 +1,4 @@
1
- import { CompiledRoute as CompiledRoute2 } from "@vertz/ui";
1
+ import { CompiledRoute as CompiledRoute2, FontFallbackMetrics as FontFallbackMetrics2 } from "@vertz/ui";
2
2
  import { CompiledRoute, FontFallbackMetrics, Theme } from "@vertz/ui";
3
3
  import { SSRAuth as SSRAuth_jq1nwm } from "@vertz/ui/internals";
4
4
  interface SSRModule {
@@ -76,6 +76,8 @@ interface PrerenderOptions {
76
76
  routes: string[];
77
77
  /** CSP nonce for inline scripts. */
78
78
  nonce?: string;
79
+ /** Pre-computed font fallback metrics for zero-CLS font loading. */
80
+ fallbackMetrics?: Record<string, FontFallbackMetrics2>;
79
81
  }
80
82
  /**
81
83
  * Discover all route patterns from an SSR module.
@@ -124,7 +126,7 @@ declare function stripScriptsFromStaticHTML(html: string): string;
124
126
  * - Routes without `prerender` or `generateParams` are not collected
125
127
  */
126
128
  declare function collectPrerenderPaths(routes: CompiledRoute2[], prefix?: string): Promise<string[]>;
127
- import { FontFallbackMetrics as FontFallbackMetrics2 } from "@vertz/ui";
129
+ import { FontFallbackMetrics as FontFallbackMetrics3 } from "@vertz/ui";
128
130
  import { AccessSet } from "@vertz/ui/auth";
129
131
  interface SessionData {
130
132
  user: {
@@ -178,7 +180,7 @@ interface SSRHandlerOptions {
178
180
  */
179
181
  nonce?: string;
180
182
  /** Pre-computed font fallback metrics (computed at server startup). */
181
- fallbackMetrics?: Record<string, FontFallbackMetrics2>;
183
+ fallbackMetrics?: Record<string, FontFallbackMetrics3>;
182
184
  /** Paths to inject as `<link rel="modulepreload">` in `<head>`. */
183
185
  modulepreload?: string[];
184
186
  /**
package/dist/ssr/index.js CHANGED
@@ -3,8 +3,8 @@ import {
3
3
  injectIntoTemplate,
4
4
  ssrDiscoverQueries,
5
5
  ssrRenderToString
6
- } from "../shared/chunk-vyjj02de.js";
7
- import"../shared/chunk-8matma4a.js";
6
+ } from "../shared/chunk-5cny4vzm.js";
7
+ import"../shared/chunk-bm16zy8d.js";
8
8
 
9
9
  // src/prerender.ts
10
10
  async function discoverRoutes(module) {
@@ -28,7 +28,9 @@ async function prerenderRoutes(module, template, options) {
28
28
  for (const routePath of options.routes) {
29
29
  let renderResult;
30
30
  try {
31
- renderResult = await ssrRenderToString(module, routePath);
31
+ renderResult = await ssrRenderToString(module, routePath, {
32
+ fallbackMetrics: options.fallbackMetrics
33
+ });
32
34
  } catch (error) {
33
35
  const message = error instanceof Error ? error.message : String(error);
34
36
  throw new Error(`Pre-render failed for ${routePath}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/ui-server",
3
- "version": "0.2.21",
3
+ "version": "0.2.23",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Vertz UI server-side rendering runtime",
@@ -58,16 +58,16 @@
58
58
  "@ampproject/remapping": "^2.3.0",
59
59
  "@capsizecss/unpack": "^4.0.0",
60
60
  "@jridgewell/trace-mapping": "^0.3.31",
61
- "@vertz/core": "^0.2.20",
62
- "@vertz/ui": "^0.2.20",
63
- "@vertz/ui-compiler": "^0.2.20",
61
+ "@vertz/core": "^0.2.22",
62
+ "@vertz/ui": "^0.2.22",
63
+ "@vertz/ui-compiler": "^0.2.22",
64
64
  "magic-string": "^0.30.0",
65
65
  "sharp": "^0.34.5",
66
66
  "ts-morph": "^27.0.2"
67
67
  },
68
68
  "devDependencies": {
69
- "@vertz/codegen": "^0.2.20",
70
- "@vertz/ui-auth": "^0.2.18",
69
+ "@vertz/codegen": "^0.2.22",
70
+ "@vertz/ui-auth": "^0.2.19",
71
71
  "bun-types": "^1.3.10",
72
72
  "bunup": "^0.16.31",
73
73
  "@happy-dom/global-registrator": "^20.8.3",