@vertz/ui-server 0.2.0 → 0.2.3

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.
@@ -0,0 +1,2032 @@
1
+ // @bun
2
+ import {
3
+ __require
4
+ } from "./shared/chunk-eb80r8e8.js";
5
+
6
+ // src/bun-dev-server.ts
7
+ import { execSync } from "child_process";
8
+ import { existsSync, mkdirSync, readFileSync as readFileSync2, renameSync, watch, writeFileSync } from "fs";
9
+ import { dirname, normalize, resolve } from "path";
10
+
11
+ // src/source-map-resolver.ts
12
+ import { readFileSync } from "fs";
13
+ import { resolve as resolvePath } from "path";
14
+ import { originalPositionFor, TraceMap } from "@jridgewell/trace-mapping";
15
+ function extractInlineSourceMap(jsContent) {
16
+ const match = jsContent.match(/\/\/# sourceMappingURL=data:application\/json;base64,([A-Za-z0-9+/=]+)/);
17
+ if (!match?.[1])
18
+ return null;
19
+ try {
20
+ return JSON.parse(atob(match[1]));
21
+ } catch {
22
+ return null;
23
+ }
24
+ }
25
+ function resolvePosition(sourceMapJSON, line, column) {
26
+ const tracer = new TraceMap(sourceMapJSON);
27
+ const pos = originalPositionFor(tracer, { line, column });
28
+ if (pos.source == null)
29
+ return null;
30
+ return {
31
+ source: pos.source,
32
+ line: pos.line ?? 1,
33
+ column: pos.column ?? 0,
34
+ name: pos.name
35
+ };
36
+ }
37
+ function readLineText(filePath, line) {
38
+ try {
39
+ const content = readFileSync(filePath, "utf-8");
40
+ const lines = content.split(`
41
+ `);
42
+ const idx = line - 1;
43
+ if (idx < 0 || idx >= lines.length)
44
+ return;
45
+ return lines[idx];
46
+ } catch {
47
+ return;
48
+ }
49
+ }
50
+ function parseStackFrames(stack) {
51
+ const frames = [];
52
+ const lines = stack.split(`
53
+ `);
54
+ for (const line of lines) {
55
+ const namedMatch = line.match(/^\s+at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/);
56
+ if (namedMatch?.[1] && namedMatch[2]) {
57
+ frames.push({
58
+ functionName: namedMatch[1],
59
+ file: namedMatch[2],
60
+ line: Number(namedMatch[3]),
61
+ column: Number(namedMatch[4])
62
+ });
63
+ continue;
64
+ }
65
+ const anonMatch = line.match(/^\s+at\s+((?:https?:\/\/).+?):(\d+):(\d+)/);
66
+ if (anonMatch?.[1]) {
67
+ frames.push({
68
+ functionName: null,
69
+ file: anonMatch[1],
70
+ line: Number(anonMatch[2]),
71
+ column: Number(anonMatch[3])
72
+ });
73
+ }
74
+ }
75
+ return frames;
76
+ }
77
+ function createSourceMapResolver(projectRoot) {
78
+ const cache = new Map;
79
+ async function fetchAndCache(url, fetchFn) {
80
+ if (cache.has(url))
81
+ return cache.get(url) ?? null;
82
+ try {
83
+ const res = await fetchFn(url);
84
+ const jsContent = await res.text();
85
+ const sourceMap = extractInlineSourceMap(jsContent);
86
+ cache.set(url, sourceMap);
87
+ return sourceMap;
88
+ } catch {
89
+ cache.set(url, null);
90
+ return null;
91
+ }
92
+ }
93
+ return {
94
+ async resolve(bundledUrl, line, column, fetchFn) {
95
+ const sourceMap = await fetchAndCache(bundledUrl, fetchFn);
96
+ if (!sourceMap)
97
+ return null;
98
+ const pos = resolvePosition(sourceMap, line, column);
99
+ if (!pos)
100
+ return null;
101
+ return {
102
+ ...pos,
103
+ absSource: resolvePath(projectRoot, pos.source)
104
+ };
105
+ },
106
+ async resolveStack(stack, message, fetchFn) {
107
+ const frames = parseStackFrames(stack);
108
+ const resolvedFrames = [];
109
+ let topResolved = null;
110
+ for (const frame of frames) {
111
+ if (frame.file.includes("/_bun/")) {
112
+ const resolved = await this.resolve(frame.file, frame.line, frame.column, fetchFn);
113
+ if (resolved) {
114
+ if (!topResolved)
115
+ topResolved = resolved;
116
+ resolvedFrames.push({
117
+ functionName: frame.functionName,
118
+ file: resolved.source,
119
+ absFile: resolved.absSource,
120
+ line: resolved.line,
121
+ column: resolved.column
122
+ });
123
+ continue;
124
+ }
125
+ }
126
+ resolvedFrames.push({
127
+ functionName: frame.functionName,
128
+ file: frame.file,
129
+ absFile: frame.file,
130
+ line: frame.line,
131
+ column: frame.column
132
+ });
133
+ }
134
+ const errors = [];
135
+ if (topResolved) {
136
+ const lineText = readLineText(topResolved.absSource, topResolved.line);
137
+ errors.push({
138
+ message,
139
+ file: topResolved.source,
140
+ absFile: topResolved.absSource,
141
+ line: topResolved.line,
142
+ column: topResolved.column,
143
+ lineText
144
+ });
145
+ } else {
146
+ errors.push({ message });
147
+ }
148
+ return { errors, parsedStack: resolvedFrames };
149
+ },
150
+ invalidate() {
151
+ cache.clear();
152
+ }
153
+ };
154
+ }
155
+
156
+ // src/ssr-render.ts
157
+ import { compileTheme } from "@vertz/ui";
158
+
159
+ // src/dom-shim/index.ts
160
+ import { setAdapter } from "@vertz/ui/internals";
161
+
162
+ // src/dom-shim/ssr-node.ts
163
+ class SSRNode {
164
+ childNodes = [];
165
+ parentNode = null;
166
+ get firstChild() {
167
+ return this.childNodes[0] ?? null;
168
+ }
169
+ get nextSibling() {
170
+ if (!this.parentNode)
171
+ return null;
172
+ const index = this.parentNode.childNodes.indexOf(this);
173
+ return this.parentNode.childNodes[index + 1] ?? null;
174
+ }
175
+ removeChild(child) {
176
+ const index = this.childNodes.indexOf(child);
177
+ if (index !== -1) {
178
+ this.childNodes.splice(index, 1);
179
+ child.parentNode = null;
180
+ }
181
+ return child;
182
+ }
183
+ insertBefore(newNode, referenceNode) {
184
+ if (!referenceNode) {
185
+ this.childNodes.push(newNode);
186
+ newNode.parentNode = this;
187
+ } else {
188
+ const index = this.childNodes.indexOf(referenceNode);
189
+ if (index !== -1) {
190
+ this.childNodes.splice(index, 0, newNode);
191
+ newNode.parentNode = this;
192
+ }
193
+ }
194
+ return newNode;
195
+ }
196
+ replaceChild(newNode, oldNode) {
197
+ const index = this.childNodes.indexOf(oldNode);
198
+ if (index !== -1) {
199
+ this.childNodes[index] = newNode;
200
+ newNode.parentNode = this;
201
+ oldNode.parentNode = null;
202
+ }
203
+ return oldNode;
204
+ }
205
+ }
206
+
207
+ // src/dom-shim/ssr-comment.ts
208
+ class SSRComment extends SSRNode {
209
+ text;
210
+ constructor(text) {
211
+ super();
212
+ this.text = text;
213
+ }
214
+ get data() {
215
+ return this.text;
216
+ }
217
+ set data(value) {
218
+ this.text = value;
219
+ }
220
+ }
221
+
222
+ // src/types.ts
223
+ function rawHtml(html) {
224
+ return { __raw: true, html };
225
+ }
226
+
227
+ // src/dom-shim/ssr-text-node.ts
228
+ class SSRTextNode extends SSRNode {
229
+ text;
230
+ constructor(text) {
231
+ super();
232
+ this.text = text;
233
+ }
234
+ get data() {
235
+ return this.text;
236
+ }
237
+ set data(value) {
238
+ this.text = value;
239
+ }
240
+ }
241
+
242
+ // src/dom-shim/ssr-fragment.ts
243
+ class SSRDocumentFragment extends SSRNode {
244
+ children = [];
245
+ appendChild(child) {
246
+ if (child instanceof SSRTextNode) {
247
+ this.children.push(child.text);
248
+ this.childNodes.push(child);
249
+ child.parentNode = this;
250
+ } else if (child instanceof SSRDocumentFragment) {
251
+ this.children.push(...child.children);
252
+ this.childNodes.push(...child.childNodes);
253
+ for (const fragmentChild of child.childNodes) {
254
+ fragmentChild.parentNode = this;
255
+ }
256
+ } else {
257
+ this.children.push(child);
258
+ this.childNodes.push(child);
259
+ child.parentNode = this;
260
+ }
261
+ }
262
+ }
263
+
264
+ // src/dom-shim/ssr-element.ts
265
+ function createStyleProxy(element) {
266
+ const styles = {};
267
+ return new Proxy(styles, {
268
+ set(_target, prop, value) {
269
+ if (typeof prop === "string") {
270
+ styles[prop] = value;
271
+ const pairs = Object.entries(styles).map(([k, v]) => {
272
+ const key = k.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
273
+ return `${key}: ${v}`;
274
+ });
275
+ element.attrs.style = pairs.join("; ");
276
+ }
277
+ return true;
278
+ },
279
+ get(_target, prop) {
280
+ if (typeof prop === "string") {
281
+ return styles[prop] ?? "";
282
+ }
283
+ return;
284
+ }
285
+ });
286
+ }
287
+
288
+ class SSRElement extends SSRNode {
289
+ tag;
290
+ attrs = {};
291
+ children = [];
292
+ _classList = new Set;
293
+ _textContent = null;
294
+ _innerHTML = null;
295
+ style;
296
+ constructor(tag) {
297
+ super();
298
+ this.tag = tag;
299
+ this.style = createStyleProxy(this);
300
+ }
301
+ setAttribute(name, value) {
302
+ if (name === "class") {
303
+ this._classList = new Set(value.split(/\s+/).filter(Boolean));
304
+ }
305
+ this.attrs[name] = value;
306
+ }
307
+ getAttribute(name) {
308
+ return this.attrs[name] ?? null;
309
+ }
310
+ removeAttribute(name) {
311
+ delete this.attrs[name];
312
+ if (name === "class") {
313
+ this._classList.clear();
314
+ }
315
+ }
316
+ appendChild(child) {
317
+ if (child instanceof SSRComment) {
318
+ this.children.push(child);
319
+ this.childNodes.push(child);
320
+ child.parentNode = this;
321
+ } else if (child instanceof SSRTextNode) {
322
+ this.children.push(child.text);
323
+ this.childNodes.push(child);
324
+ child.parentNode = this;
325
+ } else if (child instanceof SSRDocumentFragment) {
326
+ for (const fragmentChild of child.childNodes) {
327
+ if (fragmentChild instanceof SSRComment) {
328
+ this.children.push(fragmentChild);
329
+ } else if (fragmentChild instanceof SSRTextNode) {
330
+ this.children.push(fragmentChild.text);
331
+ } else if (fragmentChild instanceof SSRElement) {
332
+ this.children.push(fragmentChild);
333
+ }
334
+ this.childNodes.push(fragmentChild);
335
+ fragmentChild.parentNode = this;
336
+ }
337
+ } else {
338
+ this.children.push(child);
339
+ this.childNodes.push(child);
340
+ child.parentNode = this;
341
+ }
342
+ }
343
+ insertBefore(newNode, referenceNode) {
344
+ const refIdx = referenceNode ? this._findChildIndex(referenceNode) : -1;
345
+ const result = super.insertBefore(newNode, referenceNode);
346
+ if (newNode instanceof SSRDocumentFragment) {
347
+ const fragmentChildren = [];
348
+ for (const fc of newNode.childNodes) {
349
+ if (fc instanceof SSRComment)
350
+ fragmentChildren.push(fc);
351
+ else if (fc instanceof SSRTextNode)
352
+ fragmentChildren.push(fc.text);
353
+ else if (fc instanceof SSRElement)
354
+ fragmentChildren.push(fc);
355
+ }
356
+ if (!referenceNode || refIdx === -1) {
357
+ this.children.push(...fragmentChildren);
358
+ } else {
359
+ this.children.splice(refIdx, 0, ...fragmentChildren);
360
+ }
361
+ } else {
362
+ const child = newNode instanceof SSRComment ? newNode : newNode instanceof SSRTextNode ? newNode.text : newNode instanceof SSRElement ? newNode : null;
363
+ if (child != null) {
364
+ if (!referenceNode || refIdx === -1) {
365
+ this.children.push(child);
366
+ } else {
367
+ this.children.splice(refIdx, 0, child);
368
+ }
369
+ }
370
+ }
371
+ return result;
372
+ }
373
+ replaceChild(newNode, oldNode) {
374
+ const oldIdx = this._findChildIndex(oldNode);
375
+ const result = super.replaceChild(newNode, oldNode);
376
+ if (oldIdx !== -1) {
377
+ const newChild = newNode instanceof SSRComment ? newNode : newNode instanceof SSRTextNode ? newNode.text : newNode instanceof SSRElement ? newNode : null;
378
+ if (newChild != null) {
379
+ this.children[oldIdx] = newChild;
380
+ } else {
381
+ this.children.splice(oldIdx, 1);
382
+ }
383
+ }
384
+ return result;
385
+ }
386
+ _findChildIndex(node) {
387
+ return this.childNodes.indexOf(node);
388
+ }
389
+ removeChild(child) {
390
+ const idx = this._findChildIndex(child);
391
+ const result = super.removeChild(child);
392
+ if (idx !== -1) {
393
+ this.children.splice(idx, 1);
394
+ }
395
+ return result;
396
+ }
397
+ get classList() {
398
+ const self = this;
399
+ return {
400
+ add(cls) {
401
+ self._classList.add(cls);
402
+ self.attrs.class = [...self._classList].join(" ");
403
+ },
404
+ remove(cls) {
405
+ self._classList.delete(cls);
406
+ const val = [...self._classList].join(" ");
407
+ if (val) {
408
+ self.attrs.class = val;
409
+ } else {
410
+ delete self.attrs.class;
411
+ }
412
+ }
413
+ };
414
+ }
415
+ set className(value) {
416
+ this._classList = new Set(value.split(/\s+/).filter(Boolean));
417
+ if (value) {
418
+ this.attrs.class = value;
419
+ } else {
420
+ delete this.attrs.class;
421
+ }
422
+ }
423
+ get className() {
424
+ return this.attrs.class ?? "";
425
+ }
426
+ set textContent(value) {
427
+ this._textContent = value;
428
+ this.children = value ? [value] : [];
429
+ this.childNodes = [];
430
+ }
431
+ get textContent() {
432
+ return this._textContent;
433
+ }
434
+ set innerHTML(value) {
435
+ this._innerHTML = value;
436
+ this.children = value ? [value] : [];
437
+ this.childNodes = [];
438
+ }
439
+ get innerHTML() {
440
+ return this._innerHTML ?? "";
441
+ }
442
+ addEventListener(_event, _handler) {}
443
+ removeEventListener(_event, _handler) {}
444
+ toVNode() {
445
+ return {
446
+ tag: this.tag,
447
+ attrs: { ...this.attrs },
448
+ children: this.children.map((child) => {
449
+ if (typeof child === "string") {
450
+ return this._innerHTML != null ? rawHtml(child) : child;
451
+ }
452
+ if (child instanceof SSRComment)
453
+ return rawHtml(`<!--${child.text}-->`);
454
+ if (typeof child.toVNode === "function")
455
+ return child.toVNode();
456
+ return String(child);
457
+ })
458
+ };
459
+ }
460
+ }
461
+
462
+ // src/ssr-adapter.ts
463
+ var BRAND = Symbol.for("vertz:render-node");
464
+ Object.defineProperty(SSRNode.prototype, BRAND, {
465
+ value: true,
466
+ enumerable: false,
467
+ configurable: false,
468
+ writable: false
469
+ });
470
+ function createSSRAdapter() {
471
+ return {
472
+ createElement: (tag) => new SSRElement(tag),
473
+ createElementNS: (_ns, tag) => new SSRElement(tag),
474
+ createTextNode: (text) => new SSRTextNode(text),
475
+ createComment: (text) => new SSRComment(text),
476
+ createDocumentFragment: () => new SSRDocumentFragment,
477
+ isNode: (value) => value != null && typeof value === "object" && (BRAND in value)
478
+ };
479
+ }
480
+
481
+ // src/dom-shim/index.ts
482
+ var SHIM_GLOBALS = [
483
+ "document",
484
+ "window",
485
+ "Node",
486
+ "HTMLElement",
487
+ "HTMLAnchorElement",
488
+ "HTMLDivElement",
489
+ "HTMLInputElement",
490
+ "HTMLButtonElement",
491
+ "HTMLSelectElement",
492
+ "HTMLTextAreaElement",
493
+ "DocumentFragment",
494
+ "MouseEvent",
495
+ "Event"
496
+ ];
497
+ var savedGlobals = null;
498
+ var shimInstalled = false;
499
+ var installedGlobals = [];
500
+ function installGlobal(name, value) {
501
+ if (globalThis[name] === undefined) {
502
+ Object.defineProperty(globalThis, name, {
503
+ value,
504
+ writable: true,
505
+ configurable: true
506
+ });
507
+ installedGlobals.push(name);
508
+ }
509
+ }
510
+ function installDomShim() {
511
+ for (const g of installedGlobals) {
512
+ delete globalThis[g];
513
+ }
514
+ installedGlobals = [];
515
+ setAdapter(createSSRAdapter());
516
+ const isSSRContext = typeof globalThis.__SSR_URL__ !== "undefined";
517
+ if (typeof document !== "undefined" && !isSSRContext && !shimInstalled) {
518
+ return;
519
+ }
520
+ if (!shimInstalled) {
521
+ savedGlobals = new Map;
522
+ for (const g of SHIM_GLOBALS) {
523
+ if (g in globalThis) {
524
+ savedGlobals.set(g, globalThis[g]);
525
+ }
526
+ }
527
+ }
528
+ shimInstalled = true;
529
+ const fakeDocument = {
530
+ createElement(tag) {
531
+ return new SSRElement(tag);
532
+ },
533
+ createTextNode(text) {
534
+ return new SSRTextNode(text);
535
+ },
536
+ createComment(text) {
537
+ return new SSRComment(text);
538
+ },
539
+ createDocumentFragment() {
540
+ return new SSRDocumentFragment;
541
+ },
542
+ head: new SSRElement("head"),
543
+ body: new SSRElement("body"),
544
+ querySelector: () => null,
545
+ querySelectorAll: () => [],
546
+ getElementById: () => null,
547
+ cookie: ""
548
+ };
549
+ globalThis.document = fakeDocument;
550
+ if (typeof window === "undefined") {
551
+ globalThis.window = {
552
+ location: { pathname: globalThis.__SSR_URL__ || "/", search: "", hash: "" },
553
+ addEventListener: () => {},
554
+ removeEventListener: () => {},
555
+ history: {
556
+ pushState: () => {},
557
+ replaceState: () => {}
558
+ }
559
+ };
560
+ } else {
561
+ globalThis.window.location = {
562
+ ...globalThis.window.location || {},
563
+ pathname: globalThis.__SSR_URL__ || "/"
564
+ };
565
+ }
566
+ globalThis.Node = SSRNode;
567
+ globalThis.HTMLElement = SSRElement;
568
+ globalThis.HTMLAnchorElement = SSRElement;
569
+ globalThis.HTMLDivElement = SSRElement;
570
+ globalThis.HTMLInputElement = SSRElement;
571
+ globalThis.HTMLButtonElement = SSRElement;
572
+ globalThis.HTMLSelectElement = SSRElement;
573
+ globalThis.HTMLTextAreaElement = SSRElement;
574
+ globalThis.DocumentFragment = SSRDocumentFragment;
575
+ globalThis.MouseEvent = class MockMouseEvent {
576
+ };
577
+ globalThis.Event = class MockEvent {
578
+ };
579
+ const storageStub = {
580
+ getItem: () => null,
581
+ setItem: () => {},
582
+ removeItem: () => {},
583
+ clear: () => {},
584
+ key: () => null,
585
+ length: 0
586
+ };
587
+ installGlobal("localStorage", storageStub);
588
+ installGlobal("sessionStorage", { ...storageStub });
589
+ installGlobal("navigator", {
590
+ userAgent: "",
591
+ language: "en",
592
+ languages: ["en"],
593
+ onLine: true,
594
+ cookieEnabled: false,
595
+ hardwareConcurrency: 1,
596
+ maxTouchPoints: 0,
597
+ platform: "",
598
+ vendor: ""
599
+ });
600
+ const NoopObserver = class {
601
+ observe() {}
602
+ unobserve() {}
603
+ disconnect() {}
604
+ takeRecords() {
605
+ return [];
606
+ }
607
+ };
608
+ installGlobal("IntersectionObserver", NoopObserver);
609
+ installGlobal("ResizeObserver", NoopObserver);
610
+ installGlobal("MutationObserver", NoopObserver);
611
+ let nextFrameId = 1;
612
+ installGlobal("requestAnimationFrame", () => nextFrameId++);
613
+ installGlobal("cancelAnimationFrame", () => {});
614
+ installGlobal("requestIdleCallback", () => nextFrameId++);
615
+ installGlobal("cancelIdleCallback", () => {});
616
+ installGlobal("CustomEvent", class MockCustomEvent {
617
+ type;
618
+ detail;
619
+ constructor(type, init) {
620
+ this.type = type;
621
+ this.detail = init?.detail ?? null;
622
+ }
623
+ });
624
+ }
625
+ function removeDomShim() {
626
+ setAdapter(null);
627
+ if (!shimInstalled) {
628
+ return;
629
+ }
630
+ shimInstalled = false;
631
+ if (savedGlobals) {
632
+ for (const g of SHIM_GLOBALS) {
633
+ if (savedGlobals.has(g)) {
634
+ globalThis[g] = savedGlobals.get(g);
635
+ } else {
636
+ delete globalThis[g];
637
+ }
638
+ }
639
+ savedGlobals = null;
640
+ } else {
641
+ for (const g of SHIM_GLOBALS) {
642
+ delete globalThis[g];
643
+ }
644
+ }
645
+ for (const g of installedGlobals) {
646
+ delete globalThis[g];
647
+ }
648
+ installedGlobals = [];
649
+ }
650
+ function toVNode(element) {
651
+ if (element instanceof SSRElement) {
652
+ return element.toVNode();
653
+ }
654
+ if (element instanceof SSRDocumentFragment) {
655
+ return {
656
+ tag: "fragment",
657
+ attrs: {},
658
+ children: element.children.map((child) => {
659
+ if (typeof child === "string")
660
+ return child;
661
+ return child.toVNode();
662
+ })
663
+ };
664
+ }
665
+ if (typeof element === "object" && "tag" in element) {
666
+ return element;
667
+ }
668
+ return { tag: "span", attrs: {}, children: [String(element)] };
669
+ }
670
+
671
+ // src/html-serializer.ts
672
+ var VOID_ELEMENTS = new Set([
673
+ "area",
674
+ "base",
675
+ "br",
676
+ "col",
677
+ "embed",
678
+ "hr",
679
+ "img",
680
+ "input",
681
+ "link",
682
+ "meta",
683
+ "param",
684
+ "source",
685
+ "track",
686
+ "wbr"
687
+ ]);
688
+ var RAW_TEXT_ELEMENTS = new Set(["script", "style"]);
689
+ function escapeHtml(text) {
690
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
691
+ }
692
+ function escapeAttr(value) {
693
+ const str = typeof value === "string" ? value : String(value);
694
+ return str.replace(/&/g, "&amp;").replace(/"/g, "&quot;");
695
+ }
696
+ function serializeAttrs(attrs) {
697
+ const parts = [];
698
+ for (const [key, value] of Object.entries(attrs)) {
699
+ parts.push(` ${key}="${escapeAttr(value)}"`);
700
+ }
701
+ return parts.join("");
702
+ }
703
+ function isRawHtml(value) {
704
+ return typeof value === "object" && "__raw" in value && value.__raw === true;
705
+ }
706
+ function serializeToHtml(node) {
707
+ if (typeof node === "string") {
708
+ return escapeHtml(node);
709
+ }
710
+ if (isRawHtml(node)) {
711
+ return node.html;
712
+ }
713
+ const { tag, attrs, children } = node;
714
+ const attrStr = serializeAttrs(attrs);
715
+ if (VOID_ELEMENTS.has(tag)) {
716
+ return `<${tag}${attrStr}>`;
717
+ }
718
+ const isRawText = RAW_TEXT_ELEMENTS.has(tag);
719
+ const childrenHtml = children.map((child) => {
720
+ if (typeof child === "string" && isRawText) {
721
+ return child;
722
+ }
723
+ return serializeToHtml(child);
724
+ }).join("");
725
+ return `<${tag}${attrStr}>${childrenHtml}</${tag}>`;
726
+ }
727
+
728
+ // src/slot-placeholder.ts
729
+ var slotCounter = 0;
730
+ function createSlotPlaceholder(fallback) {
731
+ const id = slotCounter++;
732
+ const placeholder = {
733
+ tag: "div",
734
+ attrs: { id: `v-slot-${id}` },
735
+ children: typeof fallback === "string" ? [fallback] : [fallback],
736
+ _slotId: id
737
+ };
738
+ return placeholder;
739
+ }
740
+
741
+ // src/streaming.ts
742
+ var encoder = new TextEncoder;
743
+ var decoder = new TextDecoder;
744
+ function encodeChunk(html) {
745
+ return encoder.encode(html);
746
+ }
747
+ async function streamToString(stream) {
748
+ const reader = stream.getReader();
749
+ const parts = [];
750
+ for (;; ) {
751
+ const { done, value } = await reader.read();
752
+ if (done)
753
+ break;
754
+ parts.push(decoder.decode(value, { stream: true }));
755
+ }
756
+ parts.push(decoder.decode());
757
+ return parts.join("");
758
+ }
759
+
760
+ // src/template-chunk.ts
761
+ function createTemplateChunk(slotId, resolvedHtml, nonce) {
762
+ const tmplId = `v-tmpl-${slotId}`;
763
+ const slotRef = `v-slot-${slotId}`;
764
+ const nonceAttr = nonce != null ? ` nonce="${escapeAttr(nonce)}"` : "";
765
+ return `<template id="${tmplId}">${resolvedHtml}</template>` + `<script${nonceAttr}>` + `(function(){` + `var s=document.getElementById("${slotRef}");` + `var t=document.getElementById("${tmplId}");` + `if(s&&t){s.replaceWith(t.content.cloneNode(true));t.remove()}` + `})()` + "</script>";
766
+ }
767
+
768
+ // src/render-to-stream.ts
769
+ function isSuspenseNode(node) {
770
+ return typeof node === "object" && "tag" in node && node.tag === "__suspense" && "_resolve" in node;
771
+ }
772
+ function renderToStream(tree, options) {
773
+ const pendingBoundaries = [];
774
+ function walkAndSerialize(node) {
775
+ if (typeof node === "string") {
776
+ return escapeHtml(node);
777
+ }
778
+ if (isRawHtml(node)) {
779
+ return node.html;
780
+ }
781
+ if (isSuspenseNode(node)) {
782
+ const placeholder = createSlotPlaceholder(node._fallback);
783
+ pendingBoundaries.push({
784
+ slotId: placeholder._slotId,
785
+ resolve: node._resolve
786
+ });
787
+ return serializeToHtml(placeholder);
788
+ }
789
+ const { tag, attrs, children } = node;
790
+ const isRawText = RAW_TEXT_ELEMENTS.has(tag);
791
+ const attrStr = Object.entries(attrs).map(([k, v]) => ` ${k}="${escapeAttr(v)}"`).join("");
792
+ if (VOID_ELEMENTS.has(tag)) {
793
+ return `<${tag}${attrStr}>`;
794
+ }
795
+ const childrenHtml = children.map((child) => {
796
+ if (typeof child === "string" && isRawText) {
797
+ return child;
798
+ }
799
+ return walkAndSerialize(child);
800
+ }).join("");
801
+ return `<${tag}${attrStr}>${childrenHtml}</${tag}>`;
802
+ }
803
+ return new ReadableStream({
804
+ async start(controller) {
805
+ const mainHtml = walkAndSerialize(tree);
806
+ controller.enqueue(encodeChunk(mainHtml));
807
+ if (pendingBoundaries.length > 0) {
808
+ const nonce = options?.nonce;
809
+ const resolutions = pendingBoundaries.map(async (boundary) => {
810
+ try {
811
+ const resolved = await boundary.resolve;
812
+ const resolvedHtml = serializeToHtml(resolved);
813
+ return createTemplateChunk(boundary.slotId, resolvedHtml, nonce);
814
+ } catch (_err) {
815
+ const errorHtml = `<div data-v-ssr-error="true" id="v-ssr-error-${boundary.slotId}">` + "<!--SSR error--></div>";
816
+ return createTemplateChunk(boundary.slotId, errorHtml, nonce);
817
+ }
818
+ });
819
+ const chunks = await Promise.all(resolutions);
820
+ for (const chunk of chunks) {
821
+ controller.enqueue(encodeChunk(chunk));
822
+ }
823
+ }
824
+ controller.close();
825
+ }
826
+ });
827
+ }
828
+
829
+ // src/ssr-context.ts
830
+ import { AsyncLocalStorage } from "async_hooks";
831
+ var ssrStorage = new AsyncLocalStorage;
832
+ function isInSSR() {
833
+ return ssrStorage.getStore() !== undefined;
834
+ }
835
+ function registerSSRQuery(entry) {
836
+ ssrStorage.getStore()?.queries.push(entry);
837
+ }
838
+ function getSSRQueries() {
839
+ return ssrStorage.getStore()?.queries ?? [];
840
+ }
841
+ function setGlobalSSRTimeout(timeout) {
842
+ const store = ssrStorage.getStore();
843
+ if (store)
844
+ store.globalSSRTimeout = timeout;
845
+ }
846
+ function clearGlobalSSRTimeout() {
847
+ const store = ssrStorage.getStore();
848
+ if (store)
849
+ store.globalSSRTimeout = undefined;
850
+ }
851
+ function getGlobalSSRTimeout() {
852
+ return ssrStorage.getStore()?.globalSSRTimeout;
853
+ }
854
+ globalThis.__VERTZ_IS_SSR__ = isInSSR;
855
+ globalThis.__VERTZ_SSR_REGISTER_QUERY__ = registerSSRQuery;
856
+ globalThis.__VERTZ_GET_GLOBAL_SSR_TIMEOUT__ = getGlobalSSRTimeout;
857
+
858
+ // src/ssr-streaming-runtime.ts
859
+ function safeSerialize(data) {
860
+ return JSON.stringify(data).replace(/</g, "\\u003c");
861
+ }
862
+
863
+ // src/ssr-render.ts
864
+ var renderLock = Promise.resolve();
865
+ function withRenderLock(fn) {
866
+ const prev = renderLock;
867
+ let release;
868
+ renderLock = new Promise((r) => {
869
+ release = r;
870
+ });
871
+ return prev.then(fn).finally(() => release());
872
+ }
873
+ function resolveAppFactory(module) {
874
+ const createApp = module.default || module.App;
875
+ if (typeof createApp !== "function") {
876
+ throw new Error("App entry must export a default function or named App function");
877
+ }
878
+ return createApp;
879
+ }
880
+ function collectCSS(themeCss, module) {
881
+ const themeTag = themeCss ? `<style data-vertz-css>${themeCss}</style>` : "";
882
+ const globalTags = module.styles ? module.styles.map((s) => `<style data-vertz-css>${s}</style>`).join(`
883
+ `) : "";
884
+ const alreadyIncluded = new Set;
885
+ if (themeCss)
886
+ alreadyIncluded.add(themeCss);
887
+ if (module.styles) {
888
+ for (const s of module.styles)
889
+ alreadyIncluded.add(s);
890
+ }
891
+ let componentCss;
892
+ if (module.getInjectedCSS) {
893
+ componentCss = module.getInjectedCSS().filter((s) => !alreadyIncluded.has(s));
894
+ } else {
895
+ componentCss = [];
896
+ const head = globalThis.document?.head;
897
+ if (head instanceof SSRElement) {
898
+ for (const child of head.children) {
899
+ if (child instanceof SSRElement && child.tag === "style" && "data-vertz-css" in child.attrs && child.textContent && !alreadyIncluded.has(child.textContent)) {
900
+ componentCss.push(child.textContent);
901
+ }
902
+ }
903
+ }
904
+ }
905
+ const componentStyles = componentCss.map((s) => `<style data-vertz-css>${s}</style>`).join(`
906
+ `);
907
+ return [themeTag, globalTags, componentStyles].filter(Boolean).join(`
908
+ `);
909
+ }
910
+ async function ssrRenderToString(module, url, options) {
911
+ return withRenderLock(() => ssrRenderToStringUnsafe(module, url, options));
912
+ }
913
+ async function ssrRenderToStringUnsafe(module, url, options) {
914
+ const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
915
+ const ssrTimeout = options?.ssrTimeout ?? 300;
916
+ return ssrStorage.run({ url: normalizedUrl, errors: [], queries: [] }, async () => {
917
+ globalThis.__SSR_URL__ = normalizedUrl;
918
+ installDomShim();
919
+ globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
920
+ try {
921
+ setGlobalSSRTimeout(ssrTimeout);
922
+ const createApp = resolveAppFactory(module);
923
+ let themeCss = "";
924
+ if (module.theme) {
925
+ try {
926
+ themeCss = compileTheme(module.theme).css;
927
+ } catch (e) {
928
+ console.error("[vertz] Failed to compile theme export. Ensure your theme is created with defineTheme().", e);
929
+ }
930
+ }
931
+ globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
932
+ createApp();
933
+ const queries = getSSRQueries();
934
+ const resolvedQueries = [];
935
+ if (queries.length > 0) {
936
+ await Promise.allSettled(queries.map(({ promise, timeout, resolve, key }) => Promise.race([
937
+ promise.then((data) => {
938
+ resolve(data);
939
+ resolvedQueries.push({ key, data });
940
+ return "resolved";
941
+ }),
942
+ new Promise((r) => setTimeout(r, timeout || ssrTimeout)).then(() => "timeout")
943
+ ])));
944
+ const store = ssrStorage.getStore();
945
+ if (store)
946
+ store.queries = [];
947
+ }
948
+ const app = createApp();
949
+ const vnode = toVNode(app);
950
+ const stream = renderToStream(vnode);
951
+ const html = await streamToString(stream);
952
+ const css = collectCSS(themeCss, module);
953
+ const ssrData = resolvedQueries.length > 0 ? resolvedQueries.map(({ key, data }) => ({
954
+ key,
955
+ data: JSON.parse(JSON.stringify(data))
956
+ })) : [];
957
+ return { html, css, ssrData };
958
+ } finally {
959
+ clearGlobalSSRTimeout();
960
+ removeDomShim();
961
+ delete globalThis.__SSR_URL__;
962
+ }
963
+ });
964
+ }
965
+ async function ssrStreamNavQueries(module, url, options) {
966
+ const normalizedUrl = url.endsWith("/index.html") ? url.slice(0, -"/index.html".length) || "/" : url;
967
+ const ssrTimeout = options?.ssrTimeout ?? 300;
968
+ const navTimeout = options?.navSsrTimeout ?? 5000;
969
+ const queries = await withRenderLock(() => ssrStorage.run({ url: normalizedUrl, errors: [], queries: [] }, async () => {
970
+ globalThis.__SSR_URL__ = normalizedUrl;
971
+ installDomShim();
972
+ globalThis.__VERTZ_CLEAR_QUERY_CACHE__?.();
973
+ try {
974
+ setGlobalSSRTimeout(ssrTimeout);
975
+ const createApp = resolveAppFactory(module);
976
+ globalThis.__VERTZ_SSR_SYNC_ROUTER__?.(normalizedUrl);
977
+ createApp();
978
+ const discovered = getSSRQueries();
979
+ return discovered.map((q) => ({
980
+ promise: q.promise,
981
+ timeout: q.timeout || ssrTimeout,
982
+ resolve: q.resolve,
983
+ key: q.key
984
+ }));
985
+ } finally {
986
+ clearGlobalSSRTimeout();
987
+ removeDomShim();
988
+ delete globalThis.__SSR_URL__;
989
+ }
990
+ }));
991
+ if (queries.length === 0) {
992
+ const encoder3 = new TextEncoder;
993
+ return new ReadableStream({
994
+ start(controller) {
995
+ controller.enqueue(encoder3.encode(`event: done
996
+ data: {}
997
+
998
+ `));
999
+ controller.close();
1000
+ }
1001
+ });
1002
+ }
1003
+ const encoder2 = new TextEncoder;
1004
+ let remaining = queries.length;
1005
+ return new ReadableStream({
1006
+ start(controller) {
1007
+ let closed = false;
1008
+ function safeEnqueue(chunk) {
1009
+ if (closed)
1010
+ return;
1011
+ try {
1012
+ controller.enqueue(chunk);
1013
+ } catch {
1014
+ closed = true;
1015
+ }
1016
+ }
1017
+ function safeClose() {
1018
+ if (closed)
1019
+ return;
1020
+ closed = true;
1021
+ try {
1022
+ controller.close();
1023
+ } catch {}
1024
+ }
1025
+ function checkDone() {
1026
+ if (remaining === 0) {
1027
+ safeEnqueue(encoder2.encode(`event: done
1028
+ data: {}
1029
+
1030
+ `));
1031
+ safeClose();
1032
+ }
1033
+ }
1034
+ for (const { promise, resolve, key } of queries) {
1035
+ let settled = false;
1036
+ promise.then((data) => {
1037
+ if (settled)
1038
+ return;
1039
+ settled = true;
1040
+ resolve(data);
1041
+ const entry = { key, data: JSON.parse(JSON.stringify(data)) };
1042
+ safeEnqueue(encoder2.encode(`event: data
1043
+ data: ${safeSerialize(entry)}
1044
+
1045
+ `));
1046
+ remaining--;
1047
+ checkDone();
1048
+ }, () => {
1049
+ if (settled)
1050
+ return;
1051
+ settled = true;
1052
+ remaining--;
1053
+ checkDone();
1054
+ });
1055
+ setTimeout(() => {
1056
+ if (settled)
1057
+ return;
1058
+ settled = true;
1059
+ remaining--;
1060
+ checkDone();
1061
+ }, navTimeout);
1062
+ }
1063
+ }
1064
+ });
1065
+ }
1066
+
1067
+ // src/bun-dev-server.ts
1068
+ function killStaleProcess(targetPort) {
1069
+ try {
1070
+ const output = execSync(`lsof -ti :${targetPort}`, { encoding: "utf8" }).trim();
1071
+ if (!output)
1072
+ return;
1073
+ const pids = output.split(`
1074
+ `).filter(Boolean);
1075
+ const myPid = String(process.pid);
1076
+ for (const pid of pids) {
1077
+ if (pid === myPid)
1078
+ continue;
1079
+ try {
1080
+ process.kill(Number(pid), "SIGTERM");
1081
+ console.log(`[Server] Killed stale process on port ${targetPort} (PID ${pid})`);
1082
+ } catch {}
1083
+ }
1084
+ } catch {}
1085
+ }
1086
+ function createIndexHtmlStasher(projectRoot) {
1087
+ const indexHtmlPath = resolve(projectRoot, "index.html");
1088
+ const indexHtmlBackupPath = resolve(projectRoot, ".vertz", "dev", "index.html.bak");
1089
+ let stashed = false;
1090
+ return {
1091
+ stash() {
1092
+ if (!existsSync(indexHtmlPath) && existsSync(indexHtmlBackupPath)) {
1093
+ renameSync(indexHtmlBackupPath, indexHtmlPath);
1094
+ }
1095
+ if (existsSync(indexHtmlPath)) {
1096
+ mkdirSync(resolve(projectRoot, ".vertz", "dev"), { recursive: true });
1097
+ renameSync(indexHtmlPath, indexHtmlBackupPath);
1098
+ stashed = true;
1099
+ }
1100
+ },
1101
+ restore() {
1102
+ if (stashed && existsSync(indexHtmlBackupPath)) {
1103
+ renameSync(indexHtmlBackupPath, indexHtmlPath);
1104
+ stashed = false;
1105
+ }
1106
+ }
1107
+ };
1108
+ }
1109
+ function parseHMRAssets(html) {
1110
+ const srcMatch = html.match(/src="(\/_bun\/client\/[^"]+\.js)"/);
1111
+ const bootstrapMatch = html.match(/<script>(\(\(a\)=>\{document\.addEventListener.*?)<\/script>/);
1112
+ return {
1113
+ scriptUrl: srcMatch?.[1] ?? null,
1114
+ bootstrapScript: bootstrapMatch?.[1] ? `<script>${bootstrapMatch[1]}</script>` : null
1115
+ };
1116
+ }
1117
+ var ERROR_CHANNEL_SCRIPT = [
1118
+ "<script>(function(){",
1119
+ "var V=window.__vertz_overlay={};",
1120
+ "V._ws=null;",
1121
+ "V._src=null;",
1122
+ "V._hadClientError=false;",
1123
+ "V._needsReload=false;",
1124
+ 'var rts=sessionStorage.getItem("__vertz_recovering");',
1125
+ "V._recovering=rts&&(Date.now()-Number(rts)<10000);",
1126
+ 'if(V._recovering)sessionStorage.removeItem("__vertz_recovering");',
1127
+ "var _reload=location.reload.bind(location);",
1128
+ "try{location.reload=function(){if(V._src){V._needsReload=true;return}_reload()}}catch(e){}",
1129
+ "V.esc=function(s){return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')};",
1130
+ "V.formatErrors=function(errs){",
1131
+ `if(!errs||!errs.length)return'<p style="margin:0;color:var(--ve-muted);font-size:12px">Check your terminal for details.</p>';`,
1132
+ "var groups=[],seen={};",
1133
+ "errs.forEach(function(e){",
1134
+ "var k=(e.file||'')+'|'+(e.line||0);",
1135
+ "if(!seen[k]){seen[k]={file:e.file,absFile:e.absFile,line:e.line,lineText:e.lineText,msgs:[]};groups.push(seen[k])}",
1136
+ "seen[k].msgs.push({message:e.message,column:e.column})});",
1137
+ "return groups.map(function(g){var h='';",
1138
+ "if(g.file){",
1139
+ "var loc=V.esc(g.file)+(g.line?':'+g.line:'');",
1140
+ "var href=g.absFile?'vscode://file/'+encodeURI(g.absFile)+(g.line?':'+g.line:''):'';",
1141
+ `h+=href?'<a href="'+href+'" style="color:var(--ve-link);font-size:12px;text-decoration:underline;text-underline-offset:2px">'+loc+'</a>'` + `:'<span style="color:var(--ve-link);font-size:12px">'+loc+'</span>';`,
1142
+ "h+='<br>'}",
1143
+ `g.msgs.forEach(function(m){h+='<div style="color:var(--ve-error);font-size:12px;margin:2px 0">'+V.esc(m.message)+'</div>'});`,
1144
+ `if(g.lineText){h+='<pre style="margin:4px 0 0;color:var(--ve-code);font-size:11px;background:var(--ve-code-bg);border-radius:4px;padding:6px 8px;overflow-x:auto;border:1px solid var(--ve-border)">'+V.esc(g.lineText)+'</pre>'}`,
1145
+ `return'<div style="margin-bottom:10px">'+h+'</div>'}).join('')};`,
1146
+ "V.formatStack=function(frames){",
1147
+ "if(!frames||!frames.length)return'';",
1148
+ `var h='<div style="margin-top:8px;border-top:1px solid var(--ve-border);padding-top:8px">';`,
1149
+ "var visible=frames.slice(0,3);var hidden=frames.slice(3);",
1150
+ "visible.forEach(function(f){h+=V._renderFrame(f)});",
1151
+ `if(hidden.length){h+='<details style="margin-top:2px"><summary style="color:var(--ve-muted);font-size:11px;cursor:pointer;list-style:none">'`,
1152
+ "+hidden.length+' more frame'+(hidden.length>1?'s':'')+'</summary>';",
1153
+ "hidden.forEach(function(f){h+=V._renderFrame(f)});h+='</details>'}",
1154
+ "return h+'</div>'};",
1155
+ "V._renderFrame=function(f){",
1156
+ "var name=f.functionName||'(anonymous)';",
1157
+ "var loc=V.esc(f.file)+(f.line?':'+f.line:'');",
1158
+ "var isSrc=f.file&&f.file.indexOf('src/')!==-1&&f.file.indexOf('node_modules')===-1;",
1159
+ "var color=isSrc?'var(--ve-fg)':'var(--ve-muted)';",
1160
+ "var href=f.absFile?'vscode://file/'+encodeURI(f.absFile)+(f.line?':'+f.line:''):'';",
1161
+ `var link=href?'<a href="'+href+'" style="color:var(--ve-link);text-decoration:underline;text-underline-offset:2px">'+loc+'</a>':'<span>'+loc+'</span>';`,
1162
+ `return'<div style="font-size:11px;color:'+color+';margin:1px 0;font-family:ui-monospace,monospace">'+V.esc(name)+' '+link+'</div>'};`,
1163
+ "V.removeOverlay=function(){V._src=null;var e=document.getElementById('__vertz_error');if(e)e.remove();" + "var d=document.getElementById('__vertz_error_data');if(d)d.remove()};",
1164
+ "V.showOverlay=function(t,body,payload,src){",
1165
+ "V.removeOverlay();",
1166
+ "V._src=src||'ws';",
1167
+ "var d=document,c=d.createElement('div');",
1168
+ "c.id='__vertz_error';",
1169
+ "c.style.cssText='",
1170
+ "--ve-bg:hsl(0 0% 100%);--ve-fg:hsl(0 0% 9%);--ve-muted:hsl(0 0% 45%);",
1171
+ "--ve-error:hsl(0 72% 51%);--ve-link:hsl(221 83% 53%);--ve-border:hsl(0 0% 90%);",
1172
+ "--ve-code:hsl(24 70% 45%);--ve-code-bg:hsl(0 0% 97%);--ve-btn:hsl(0 0% 9%);--ve-btn-fg:hsl(0 0% 100%);",
1173
+ "position:fixed;bottom:16px;left:50%;transform:translateX(-50%);z-index:2147483647;",
1174
+ "background:var(--ve-bg);color:var(--ve-fg);border-radius:8px;padding:14px 16px;",
1175
+ "max-width:480px;width:calc(100% - 32px);font-family:ui-sans-serif,system-ui,sans-serif;",
1176
+ "box-shadow:0 4px 24px rgba(0,0,0,0.12),0 1px 3px rgba(0,0,0,0.08);border:1px solid var(--ve-border)';",
1177
+ "var st=d.createElement('style');",
1178
+ "st.textContent='@media(prefers-color-scheme:dark){#__vertz_error{",
1179
+ "--ve-bg:hsl(0 0% 7%);--ve-fg:hsl(0 0% 93%);--ve-muted:hsl(0 0% 55%);",
1180
+ "--ve-error:hsl(0 72% 65%);--ve-link:hsl(217 91% 70%);--ve-border:hsl(0 0% 18%);",
1181
+ "--ve-code:hsl(36 80% 65%);--ve-code-bg:hsl(0 0% 11%);--ve-btn:hsl(0 0% 93%);--ve-btn-fg:hsl(0 0% 7%)}}';",
1182
+ "d.head.appendChild(st);",
1183
+ `c.innerHTML='<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:10px">'`,
1184
+ `+'<span style="font-size:13px;font-weight:600;color:var(--ve-error)">'+V.esc(t)+'</span>'`,
1185
+ `+'<button id="__vertz_retry" style="background:var(--ve-btn);color:var(--ve-btn-fg);border:none;border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer;font-weight:500">Retry</button>'`,
1186
+ "+'</div>'+body;",
1187
+ "(d.body||d.documentElement).appendChild(c);",
1188
+ "d.getElementById('__vertz_retry').onclick=function(){location.reload()};",
1189
+ "if(payload){var s=d.createElement('script');s.type='application/json';s.id='__vertz_error_data';s.textContent=JSON.stringify(payload);(d.body||d.documentElement).appendChild(s)}};",
1190
+ "var delay=1000,maxDelay=30000;",
1191
+ "function connect(){",
1192
+ "var p=location.protocol==='https:'?'wss:':'ws:';",
1193
+ "var ws=new WebSocket(p+'//'+location.host+'/__vertz_errors');",
1194
+ "V._ws=ws;",
1195
+ "ws.onmessage=function(e){",
1196
+ "try{var m=JSON.parse(e.data);",
1197
+ "if(m.type==='error'){",
1198
+ "if(V._recovering)return;",
1199
+ "V.showOverlay(m.category==='build'?'Build failed':m.category==='ssr'?'SSR error':m.category==='resolve'?'Module not found':'Runtime error',V.formatErrors(m.errors)+V.formatStack(m.parsedStack),m,'ws')}",
1200
+ "else if(m.type==='clear'){",
1201
+ "if(V._needsReload){V._needsReload=false;V.removeOverlay();sessionStorage.setItem('__vertz_recovering',String(Date.now()));_reload();return}",
1202
+ "var a=document.getElementById('app');",
1203
+ "if(!a||a.innerHTML.length<50){V.removeOverlay();sessionStorage.setItem('__vertz_recovering',String(Date.now()));_reload();return}",
1204
+ "if(V._hadClientError)return;",
1205
+ "V.removeOverlay()}",
1206
+ "else if(m.type==='connected'){delay=1000}",
1207
+ "}catch(ex){}};",
1208
+ "ws.onclose=function(){V._ws=null;setTimeout(function(){delay=Math.min(delay*2,maxDelay);connect()},delay)};",
1209
+ "ws.onerror=function(){ws.close()}}",
1210
+ "connect();",
1211
+ "V._sendResolveStack=function(stack,msg){",
1212
+ 'if(V._ws&&V._ws.readyState===1){try{V._ws.send(JSON.stringify({type:"resolve-stack",stack:stack,message:msg}))}catch(e){}}};',
1213
+ "function showRuntimeError(title,errors,payload){",
1214
+ "var a=document.getElementById('app');",
1215
+ "if(V._recovering&&a&&a.innerHTML.length>50)return;",
1216
+ "if(V._recovering)V._recovering=false;",
1217
+ "V._hadClientError=true;",
1218
+ "V.showOverlay(title,V.formatErrors(errors),payload,'client')}",
1219
+ "if(V._recovering){setTimeout(function(){V._recovering=false},5000)}",
1220
+ "window.addEventListener('error',function(e){",
1221
+ "var msg=e.message||String(e.error);",
1222
+ "var stk=e.error&&e.error.stack;",
1223
+ "if(stk){V._sendResolveStack(stk,msg)}",
1224
+ "var f=e.filename,isBundled=f&&(f.indexOf('/_bun/')!==-1||f.indexOf('blob:')!==-1);",
1225
+ "var errInfo=isBundled?{message:msg}:{message:msg,file:f,line:e.lineno,column:e.colno};",
1226
+ "showRuntimeError('Runtime error',[errInfo],{type:'error',category:'runtime',errors:[errInfo]})});",
1227
+ "window.addEventListener('unhandledrejection',function(e){",
1228
+ "var m=e.reason instanceof Error?e.reason.message:String(e.reason);",
1229
+ "var stk=e.reason&&e.reason.stack;",
1230
+ "if(stk){V._sendResolveStack(stk,m)}",
1231
+ "showRuntimeError('Runtime error',[{message:m}],{type:'error',category:'runtime',errors:[{message:m}]})});",
1232
+ "var hmrErr=false,origCE=console.error,origCL=console.log;",
1233
+ "console.error=function(){",
1234
+ "var t=Array.prototype.join.call(arguments,' ');",
1235
+ "var hmr=t.match(/\\[vertz-hmr\\] Error re-mounting (\\w+): ([\\s\\S]*?)(?:\\n\\s+at |$)/);",
1236
+ "if(hmr){hmrErr=true;V._hadClientError=true;",
1237
+ "V.showOverlay('Runtime error',V.formatErrors([{message:hmr[2].split('\\n')[0]}]),{type:'error',category:'runtime',errors:[{message:hmr[2].split('\\n')[0]}]},'client')}",
1238
+ "origCE.apply(console,arguments)};",
1239
+ "console.log=function(){",
1240
+ "var t=Array.prototype.join.call(arguments,' ');",
1241
+ "if(t.indexOf('[vertz-hmr] Hot updated:')!==-1){",
1242
+ "if(!hmrErr&&V._src==='client'){",
1243
+ "V._hadClientError=false;V.removeOverlay();setTimeout(function(){var a=document.getElementById('app');if(!a||a.innerHTML.length<50){V._needsReload=true}},500)}",
1244
+ "hmrErr=false}",
1245
+ "origCL.apply(console,arguments)};",
1246
+ "})()</script>"
1247
+ ].join("");
1248
+ var RELOAD_GUARD_SCRIPT = `<script>(function(){var K="__vertz_reload_count",T="__vertz_reload_ts",s=sessionStorage,n=parseInt(s.getItem(K)||"0",10),t=parseInt(s.getItem(T)||"0",10),now=Date.now();if(now-t<100){n++}else{n=1}s.setItem(K,String(n));s.setItem(T,String(now));if(n>10){window.stop();s.removeItem(K);s.removeItem(T);var d=document,o=d.createElement("div");o.style.cssText="position:fixed;inset:0;z-index:2147483647;display:flex;align-items:center;justify-content:center;background:rgba(0,0,0,0.6)";var c=d.createElement("div");c.style.cssText="background:#fff;color:#1a1a1a;border-radius:12px;padding:32px;max-width:480px;width:90%;font-family:system-ui,sans-serif;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.3)";c.innerHTML='<div style="font-size:40px;margin-bottom:16px">&#9888;&#65039;</div><h2 style="margin:0 0 8px;font-size:20px">Dev server connection lost</h2><p style="margin:0 0 20px;color:#666;font-size:14px;line-height:1.5">The page reloaded 10+ times in rapid succession. This usually means the dev server stopped or a build failed.</p><button id="__vertz_retry" style="background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 24px;font-size:14px;cursor:pointer">Retry</button>';o.appendChild(c);(d.body||d.documentElement).appendChild(o);d.getElementById("__vertz_retry").onclick=function(){location.href=location.href}}else{setTimeout(function(){s.removeItem(K);s.removeItem(T)},5e3)}})()</script>`;
1249
+ function generateSSRPageHtml({
1250
+ title,
1251
+ css,
1252
+ bodyHtml,
1253
+ ssrData,
1254
+ scriptTag
1255
+ }) {
1256
+ const ssrDataScript = ssrData.length > 0 ? `<script>window.__VERTZ_SSR_DATA__=${safeSerialize(ssrData)};</script>` : "";
1257
+ return `<!doctype html>
1258
+ <html lang="en">
1259
+ <head>
1260
+ <meta charset="UTF-8" />
1261
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
1262
+ <title>${title}</title>
1263
+ ${css}
1264
+ ${ERROR_CHANNEL_SCRIPT}
1265
+ ${RELOAD_GUARD_SCRIPT}
1266
+ </head>
1267
+ <body>
1268
+ <div id="app">${bodyHtml}</div>
1269
+ ${ssrDataScript}
1270
+ ${scriptTag}
1271
+ </body>
1272
+ </html>`;
1273
+ }
1274
+ function createFetchInterceptor({
1275
+ apiHandler,
1276
+ origin,
1277
+ skipSSRPaths,
1278
+ originalFetch
1279
+ }) {
1280
+ const intercepted = (input, init) => {
1281
+ const rawUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
1282
+ const isRelative = rawUrl.startsWith("/");
1283
+ const fetchPath = isRelative ? rawUrl.split("?")[0] ?? "/" : new URL(rawUrl).pathname;
1284
+ const isLocal = isRelative || new URL(rawUrl).origin === origin;
1285
+ if (isLocal && skipSSRPaths.some((p) => fetchPath.startsWith(p))) {
1286
+ const absoluteUrl = isRelative ? `${origin}${rawUrl}` : rawUrl;
1287
+ const req = new Request(absoluteUrl, init);
1288
+ return apiHandler(req);
1289
+ }
1290
+ return originalFetch(input, init);
1291
+ };
1292
+ intercepted.preconnect = originalFetch.preconnect;
1293
+ return intercepted;
1294
+ }
1295
+ var BUILD_ERROR_LOADER = [
1296
+ "(function(){",
1297
+ "var el=document.querySelector('[data-bun-dev-server-script]');if(!el)return;var src=el.src;",
1298
+ "var V=window.__vertz_overlay||{};",
1299
+ `var formatErrors=V.formatErrors||function(){return'<p style="margin:0;color:#666;font-size:12px">Check your terminal for details.</p>'};`,
1300
+ "var showOverlay=V.showOverlay||function(t,body){",
1301
+ "var e=document.getElementById('__vertz_error');if(e)e.remove();",
1302
+ "var d=document,c=d.createElement('div');c.id='__vertz_error';",
1303
+ "c.style.cssText='position:fixed;bottom:16px;left:50%;transform:translateX(-50%);z-index:2147483647;background:#fff;color:#1a1a1a;border-radius:8px;padding:14px 16px;max-width:480px;width:calc(100% - 32px);font-family:system-ui,sans-serif;box-shadow:0 4px 24px rgba(0,0,0,0.12);border:1px solid #e5e5e5';",
1304
+ `c.innerHTML='<div style="margin-bottom:10px;font-size:13px;font-weight:600;color:#dc2626">'+t+'</div>'+body+'<button onclick="location.reload()" style="margin-top:8px;background:#1a1a1a;color:#fff;border:none;border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer">Retry</button>';`,
1305
+ "(d.body||d.documentElement).appendChild(c)};",
1306
+ "el.remove();",
1307
+ "fetch(src).then(function(r){return r.text()}).then(function(t){",
1308
+ "if(t.trimStart().startsWith('try{location.reload()}')){",
1309
+ "fetch('/__vertz_build_check').then(function(r){return r.json()}).then(function(j){",
1310
+ "if(j.errors&&j.errors.length>0){showOverlay('Build failed',formatErrors(j.errors),j)}",
1311
+ "else{var rk='__vertz_stub_retry',rc=+(sessionStorage.getItem(rk)||0);",
1312
+ "if(rc<3){sessionStorage.setItem(rk,String(rc+1));setTimeout(function(){location.reload()},2000)}",
1313
+ `else{sessionStorage.removeItem(rk);showOverlay('Build failed','<p style="margin:0;color:#666;font-size:12px">Could not load client bundle. Try reloading manually.</p>')}}`,
1314
+ "}).catch(function(){",
1315
+ `showOverlay('Build failed','<p style="margin:0;color:#666;font-size:12px">Check your terminal for details.</p>')})}`,
1316
+ "else{sessionStorage.removeItem('__vertz_stub_retry');var s=document.createElement('script');s.type='module';s.crossOrigin='';s.src=src;document.body.appendChild(s)}",
1317
+ `}).catch(function(){showOverlay('Dev server unreachable','<p style="margin:0;color:#666;font-size:12px">Could not connect. Is it still running?</p>')})`,
1318
+ "})()"
1319
+ ].join("");
1320
+ function buildScriptTag(bundledScriptUrl, hmrBootstrapScript, clientSrc) {
1321
+ if (bundledScriptUrl) {
1322
+ const placeholder = `<script type="text/plain" crossorigin src="${bundledScriptUrl}" data-bun-dev-server-script></script>`;
1323
+ const bootstrap = hmrBootstrapScript ? `
1324
+ ${hmrBootstrapScript}` : "";
1325
+ const loader = `<script>${BUILD_ERROR_LOADER}</script>`;
1326
+ return `${placeholder}${bootstrap}
1327
+ ${loader}`;
1328
+ }
1329
+ return `<script type="module" src="${clientSrc}"></script>`;
1330
+ }
1331
+ function createBunDevServer(options) {
1332
+ const {
1333
+ entry,
1334
+ port = 3000,
1335
+ host = "localhost",
1336
+ apiHandler,
1337
+ skipSSRPaths = ["/api/"],
1338
+ openapi,
1339
+ clientEntry: clientEntryOption,
1340
+ title = "Vertz App",
1341
+ projectRoot = process.cwd(),
1342
+ logRequests = true
1343
+ } = options;
1344
+ let server = null;
1345
+ let srcWatcherRef = null;
1346
+ let refreshTimeout = null;
1347
+ let stopped = false;
1348
+ const wsClients = new Set;
1349
+ let currentError = null;
1350
+ const sourceMapResolver = createSourceMapResolver(projectRoot);
1351
+ let clearGraceUntil = 0;
1352
+ let runtimeDebounceTimer = null;
1353
+ let pendingRuntimeError = null;
1354
+ function broadcastError(category, errors) {
1355
+ if (currentError?.category === "build" && category !== "build") {
1356
+ return;
1357
+ }
1358
+ if (category === "runtime" && Date.now() < clearGraceUntil) {
1359
+ return;
1360
+ }
1361
+ if (category === "runtime") {
1362
+ if (!pendingRuntimeError || errors.some((e) => e.file)) {
1363
+ pendingRuntimeError = errors;
1364
+ }
1365
+ if (!runtimeDebounceTimer) {
1366
+ runtimeDebounceTimer = setTimeout(() => {
1367
+ runtimeDebounceTimer = null;
1368
+ const errs = pendingRuntimeError;
1369
+ pendingRuntimeError = null;
1370
+ if (errs) {
1371
+ currentError = { category: "runtime", errors: errs };
1372
+ const msg2 = JSON.stringify({ type: "error", category: "runtime", errors: errs });
1373
+ for (const ws of wsClients) {
1374
+ ws.sendText(msg2);
1375
+ }
1376
+ }
1377
+ }, 100);
1378
+ }
1379
+ return;
1380
+ }
1381
+ currentError = { category, errors };
1382
+ const msg = JSON.stringify({ type: "error", category, errors });
1383
+ for (const ws of wsClients) {
1384
+ ws.sendText(msg);
1385
+ }
1386
+ }
1387
+ function clearError() {
1388
+ if (currentError === null && !pendingRuntimeError)
1389
+ return;
1390
+ currentError = null;
1391
+ if (runtimeDebounceTimer) {
1392
+ clearTimeout(runtimeDebounceTimer);
1393
+ runtimeDebounceTimer = null;
1394
+ pendingRuntimeError = null;
1395
+ }
1396
+ clearGraceUntil = Date.now() + 5000;
1397
+ const msg = JSON.stringify({ type: "clear" });
1398
+ for (const ws of wsClients) {
1399
+ ws.sendText(msg);
1400
+ }
1401
+ }
1402
+ function clearErrorForFileChange() {
1403
+ if (currentError === null && !pendingRuntimeError)
1404
+ return;
1405
+ currentError = null;
1406
+ if (runtimeDebounceTimer) {
1407
+ clearTimeout(runtimeDebounceTimer);
1408
+ runtimeDebounceTimer = null;
1409
+ pendingRuntimeError = null;
1410
+ }
1411
+ clearGraceUntil = 0;
1412
+ const msg = JSON.stringify({ type: "clear" });
1413
+ for (const ws of wsClients) {
1414
+ ws.sendText(msg);
1415
+ }
1416
+ }
1417
+ let lastBuildError = "";
1418
+ let lastBroadcastedError = "";
1419
+ let lastChangedFile = "";
1420
+ const resolvePatterns = ["Could not resolve", "Module not found", "Cannot find module"];
1421
+ const hmrErrorPattern = /\[vertz-hmr\] Error re-mounting (\w+): ([\s\S]*?)(?:\n\s+at |$)/;
1422
+ const frontendErrorPattern = /\x1b\[31mfrontend\x1b\[0m ([\s\S]*?)(?:\n\s+from browser|$)/;
1423
+ function parseSourceFromStack(text) {
1424
+ const stackLines = text.split(`
1425
+ `);
1426
+ const srcLine = stackLines.find((l) => l.includes("/src/") && !l.includes("node_modules"));
1427
+ if (srcLine) {
1428
+ const locMatch = srcLine.match(/(?:at .+? \(|at )(.+?):(\d+):(\d+)/);
1429
+ if (locMatch) {
1430
+ const absFile = locMatch[1];
1431
+ const line = Number(locMatch[2]);
1432
+ const lineText = absFile ? readLineText(absFile, line) : undefined;
1433
+ return {
1434
+ message: "",
1435
+ file: absFile?.replace(projectRoot, "").replace(/^\//, ""),
1436
+ absFile,
1437
+ line,
1438
+ column: Number(locMatch[3]),
1439
+ lineText
1440
+ };
1441
+ }
1442
+ }
1443
+ return { message: "" };
1444
+ }
1445
+ const origConsoleError = console.error;
1446
+ console.error = (...args) => {
1447
+ const text = args.map((a) => typeof a === "string" ? a : String(a)).join(" ");
1448
+ if (!text.startsWith("[Server]")) {
1449
+ lastBuildError = text;
1450
+ if (resolvePatterns.some((p) => text.includes(p)) && text !== lastBroadcastedError) {
1451
+ lastBroadcastedError = text;
1452
+ broadcastError("resolve", [{ message: text }]);
1453
+ } else {
1454
+ const hmrMatch = text.match(hmrErrorPattern);
1455
+ if (hmrMatch && text !== lastBroadcastedError) {
1456
+ lastBroadcastedError = text;
1457
+ const component = hmrMatch[1];
1458
+ const errorMsg = hmrMatch[2]?.trim() ?? "Unknown error";
1459
+ const loc = parseSourceFromStack(text);
1460
+ if (!loc.file && lastChangedFile) {
1461
+ loc.file = lastChangedFile;
1462
+ loc.absFile = resolve(projectRoot, lastChangedFile);
1463
+ }
1464
+ broadcastError("runtime", [
1465
+ {
1466
+ message: `${errorMsg} (in ${component})`,
1467
+ file: loc.file,
1468
+ absFile: loc.absFile,
1469
+ line: loc.line,
1470
+ column: loc.column,
1471
+ lineText: loc.lineText
1472
+ }
1473
+ ]);
1474
+ } else {
1475
+ const feMatch = text.match(frontendErrorPattern);
1476
+ if (feMatch && text !== lastBroadcastedError) {
1477
+ lastBroadcastedError = text;
1478
+ const errorMsg = feMatch[1]?.split(`
1479
+ `)[0]?.trim() ?? "Unknown error";
1480
+ const loc = parseSourceFromStack(text);
1481
+ if (!loc.file && lastChangedFile) {
1482
+ loc.file = lastChangedFile;
1483
+ loc.absFile = resolve(projectRoot, lastChangedFile);
1484
+ }
1485
+ broadcastError("runtime", [
1486
+ {
1487
+ message: errorMsg,
1488
+ file: loc.file,
1489
+ absFile: loc.absFile,
1490
+ line: loc.line,
1491
+ column: loc.column,
1492
+ lineText: loc.lineText
1493
+ }
1494
+ ]);
1495
+ }
1496
+ }
1497
+ }
1498
+ }
1499
+ origConsoleError.apply(console, args);
1500
+ };
1501
+ const indexHtmlStasher = createIndexHtmlStasher(projectRoot);
1502
+ let cachedSpec = null;
1503
+ let specWatcher = null;
1504
+ const loadOpenAPISpec = () => {
1505
+ if (!openapi)
1506
+ return null;
1507
+ try {
1508
+ const specContent = readFileSync2(openapi.specPath, "utf-8");
1509
+ return JSON.parse(specContent);
1510
+ } catch (err) {
1511
+ console.error("[Server] Error reading OpenAPI spec:", err);
1512
+ return null;
1513
+ }
1514
+ };
1515
+ const setupOpenAPIWatcher = () => {
1516
+ if (!openapi || !existsSync(openapi.specPath))
1517
+ return;
1518
+ cachedSpec = loadOpenAPISpec();
1519
+ if (cachedSpec === null)
1520
+ return;
1521
+ try {
1522
+ const specDir = dirname(openapi.specPath);
1523
+ const specFile = openapi.specPath.split("/").pop() || "openapi.json";
1524
+ specWatcher = watch(specDir, { persistent: false }, (eventType, filename) => {
1525
+ if (filename === specFile && (eventType === "change" || eventType === "rename")) {
1526
+ if (logRequests) {
1527
+ console.log("[Server] OpenAPI spec file changed, reloading...");
1528
+ }
1529
+ cachedSpec = loadOpenAPISpec();
1530
+ }
1531
+ });
1532
+ } catch (err) {
1533
+ console.warn("[Server] Could not set up file watcher for OpenAPI spec:", err);
1534
+ }
1535
+ };
1536
+ const serveOpenAPISpec = () => {
1537
+ if (cachedSpec) {
1538
+ return new Response(JSON.stringify(cachedSpec), {
1539
+ headers: { "Content-Type": "application/json" }
1540
+ });
1541
+ }
1542
+ if (openapi && existsSync(openapi.specPath)) {
1543
+ cachedSpec = loadOpenAPISpec();
1544
+ if (cachedSpec) {
1545
+ return new Response(JSON.stringify(cachedSpec), {
1546
+ headers: { "Content-Type": "application/json" }
1547
+ });
1548
+ }
1549
+ }
1550
+ return new Response("OpenAPI spec not found", { status: 404 });
1551
+ };
1552
+ async function start() {
1553
+ indexHtmlStasher.stash();
1554
+ const { plugin } = await Promise.resolve(globalThis.Bun);
1555
+ const { createVertzBunPlugin } = await import("./bun-plugin/index.js");
1556
+ const entryPath = resolve(projectRoot, entry);
1557
+ const rawClientSrc = clientEntryOption ?? entry;
1558
+ const clientSrc = rawClientSrc.replace(/^\.\//, "/");
1559
+ plugin({
1560
+ name: "vertz-ssr-jsx-swap",
1561
+ setup(build) {
1562
+ build.onResolve({ filter: /^@vertz\/ui\/jsx-runtime$/ }, () => ({
1563
+ path: "@vertz/ui-server/jsx-runtime",
1564
+ external: false
1565
+ }));
1566
+ build.onResolve({ filter: /^@vertz\/ui\/jsx-dev-runtime$/ }, () => ({
1567
+ path: "@vertz/ui-server/jsx-runtime",
1568
+ external: false
1569
+ }));
1570
+ }
1571
+ });
1572
+ const { plugin: serverPlugin } = createVertzBunPlugin({
1573
+ hmr: false,
1574
+ fastRefresh: false
1575
+ });
1576
+ plugin(serverPlugin);
1577
+ let ssrMod;
1578
+ try {
1579
+ ssrMod = await import(entryPath);
1580
+ if (logRequests) {
1581
+ console.log("[Server] SSR module loaded");
1582
+ }
1583
+ } catch (e) {
1584
+ console.error("[Server] Failed to load SSR module:", e);
1585
+ process.exit(1);
1586
+ }
1587
+ const devDir = resolve(projectRoot, ".vertz", "dev");
1588
+ mkdirSync(devDir, { recursive: true });
1589
+ const frRuntimePath = "../../node_modules/@vertz/ui-server/dist/bun-plugin/fast-refresh-runtime.js";
1590
+ const hmrShellHtml = `<!doctype html>
1591
+ <html lang="en"><head>
1592
+ <meta charset="UTF-8" />
1593
+ <title>HMR Shell</title>
1594
+ </head><body>
1595
+ <script type="module" src="${frRuntimePath}"></script>
1596
+ <script type="module" src="${clientSrc}"></script>
1597
+ </body></html>`;
1598
+ const hmrShellPath = resolve(devDir, "hmr-shell.html");
1599
+ writeFileSync(hmrShellPath, hmrShellHtml);
1600
+ const hmrShellModule = __require(hmrShellPath);
1601
+ setupOpenAPIWatcher();
1602
+ let bundledScriptUrl = null;
1603
+ let hmrBootstrapScript = null;
1604
+ const routes = {
1605
+ "/__vertz_hmr": hmrShellModule
1606
+ };
1607
+ if (openapi) {
1608
+ routes["/api/openapi.json"] = () => serveOpenAPISpec();
1609
+ }
1610
+ if (apiHandler) {
1611
+ routes["/api/*"] = (req) => apiHandler(req);
1612
+ }
1613
+ killStaleProcess(port);
1614
+ server = Bun.serve({
1615
+ port,
1616
+ hostname: host,
1617
+ routes,
1618
+ async fetch(request) {
1619
+ const url = new URL(request.url);
1620
+ const pathname = url.pathname;
1621
+ if (pathname === "/__vertz_errors") {
1622
+ if (server?.upgrade(request, { data: {} })) {
1623
+ return;
1624
+ }
1625
+ return new Response("WebSocket upgrade failed", { status: 400 });
1626
+ }
1627
+ if (pathname.startsWith("/_bun/")) {
1628
+ return;
1629
+ }
1630
+ if (pathname === "/__vertz_build_check") {
1631
+ if (currentError) {
1632
+ return Response.json({ errors: currentError.errors });
1633
+ }
1634
+ try {
1635
+ const clientRelative = rawClientSrc.replace(/^\//, "");
1636
+ const result = await Bun.build({
1637
+ entrypoints: [resolve(projectRoot, clientRelative)],
1638
+ root: projectRoot,
1639
+ target: "browser",
1640
+ throw: false
1641
+ });
1642
+ if (!result.success && result.logs.length > 0) {
1643
+ const errors = result.logs.filter((l) => l.level === "error").map((l) => {
1644
+ const pos = l.position;
1645
+ const file = pos?.file ? pos.file.replace(projectRoot, "").replace(/^\//, "") : undefined;
1646
+ return {
1647
+ message: l.message,
1648
+ file,
1649
+ absFile: pos?.file,
1650
+ line: pos?.line,
1651
+ column: pos?.column,
1652
+ lineText: pos?.lineText
1653
+ };
1654
+ });
1655
+ return Response.json({ errors });
1656
+ }
1657
+ if (lastBuildError) {
1658
+ return Response.json({ errors: [{ message: lastBuildError }] });
1659
+ }
1660
+ return Response.json({ errors: [] });
1661
+ } catch (e) {
1662
+ const msg = e instanceof Error ? e.message : String(e);
1663
+ return Response.json({ errors: [{ message: msg }] });
1664
+ }
1665
+ }
1666
+ if (openapi && request.method === "GET" && pathname === "/api/openapi.json") {
1667
+ return serveOpenAPISpec();
1668
+ }
1669
+ if (apiHandler && skipSSRPaths.some((p) => pathname.startsWith(p))) {
1670
+ return apiHandler(request);
1671
+ }
1672
+ if (request.headers.get("x-vertz-nav") === "1") {
1673
+ try {
1674
+ const stream = await ssrStreamNavQueries(ssrMod, pathname, { navSsrTimeout: 5000 });
1675
+ return new Response(stream, {
1676
+ status: 200,
1677
+ headers: {
1678
+ "Content-Type": "text/event-stream",
1679
+ "Cache-Control": "no-cache"
1680
+ }
1681
+ });
1682
+ } catch {
1683
+ return new Response(`event: done
1684
+ data: {}
1685
+
1686
+ `, {
1687
+ status: 200,
1688
+ headers: {
1689
+ "Content-Type": "text/event-stream",
1690
+ "Cache-Control": "no-cache"
1691
+ }
1692
+ });
1693
+ }
1694
+ }
1695
+ if (pathname !== "/" && !pathname.endsWith(".html")) {
1696
+ const safePath = normalize(pathname).replace(/^(\.\.(\/|\\|$))+/, "");
1697
+ const publicDir = resolve(projectRoot, "public");
1698
+ const resolvedPublic = resolve(publicDir, safePath.slice(1));
1699
+ if (resolvedPublic.startsWith(publicDir)) {
1700
+ const publicFile = Bun.file(resolvedPublic);
1701
+ if (await publicFile.exists()) {
1702
+ return new Response(publicFile);
1703
+ }
1704
+ }
1705
+ const resolvedRoot = resolve(projectRoot, safePath.slice(1));
1706
+ if (resolvedRoot.startsWith(projectRoot)) {
1707
+ const rootFile = Bun.file(resolvedRoot);
1708
+ if (await rootFile.exists()) {
1709
+ return new Response(rootFile);
1710
+ }
1711
+ }
1712
+ }
1713
+ if (!request.headers.get("accept")?.includes("text/html") && !pathname.endsWith(".html") && pathname !== "/") {
1714
+ return new Response("Not Found", { status: 404 });
1715
+ }
1716
+ if (logRequests) {
1717
+ console.log(`[Server] SSR: ${pathname}`);
1718
+ }
1719
+ try {
1720
+ const originalFetch = globalThis.fetch;
1721
+ if (apiHandler) {
1722
+ globalThis.fetch = createFetchInterceptor({
1723
+ apiHandler,
1724
+ origin: `http://${host}:${server?.port}`,
1725
+ skipSSRPaths,
1726
+ originalFetch
1727
+ });
1728
+ }
1729
+ try {
1730
+ const result = await ssrRenderToString(ssrMod, pathname, { ssrTimeout: 300 });
1731
+ const scriptTag = buildScriptTag(bundledScriptUrl, hmrBootstrapScript, clientSrc);
1732
+ const html = generateSSRPageHtml({
1733
+ title,
1734
+ css: result.css,
1735
+ bodyHtml: result.html,
1736
+ ssrData: result.ssrData,
1737
+ scriptTag
1738
+ });
1739
+ return new Response(html, {
1740
+ status: 200,
1741
+ headers: {
1742
+ "Content-Type": "text/html; charset=utf-8",
1743
+ "Cache-Control": "no-store"
1744
+ }
1745
+ });
1746
+ } finally {
1747
+ if (apiHandler) {
1748
+ globalThis.fetch = originalFetch;
1749
+ }
1750
+ }
1751
+ } catch (err) {
1752
+ console.error("[Server] SSR error:", err);
1753
+ const errMsg = err instanceof Error ? err.message : String(err);
1754
+ const errStack = err instanceof Error ? err.stack : undefined;
1755
+ broadcastError("ssr", [{ message: errMsg, stack: errStack }]);
1756
+ const scriptTag = buildScriptTag(bundledScriptUrl, hmrBootstrapScript, clientSrc);
1757
+ const fallbackHtml = generateSSRPageHtml({
1758
+ title,
1759
+ css: "",
1760
+ bodyHtml: "",
1761
+ ssrData: [],
1762
+ scriptTag
1763
+ });
1764
+ return new Response(fallbackHtml, {
1765
+ status: 200,
1766
+ headers: {
1767
+ "Content-Type": "text/html; charset=utf-8",
1768
+ "Cache-Control": "no-store"
1769
+ }
1770
+ });
1771
+ }
1772
+ },
1773
+ websocket: {
1774
+ open(ws) {
1775
+ wsClients.add(ws);
1776
+ ws.sendText(JSON.stringify({ type: "connected" }));
1777
+ if (currentError) {
1778
+ ws.sendText(JSON.stringify({
1779
+ type: "error",
1780
+ category: currentError.category,
1781
+ errors: currentError.errors
1782
+ }));
1783
+ }
1784
+ },
1785
+ message(ws, msg) {
1786
+ try {
1787
+ const data = JSON.parse(typeof msg === "string" ? msg : new TextDecoder().decode(msg));
1788
+ if (data.type === "ping") {
1789
+ ws.sendText(JSON.stringify({ type: "pong" }));
1790
+ } else if (data.type === "resolve-stack" && data.stack) {
1791
+ const selfFetch = async (url) => {
1792
+ const absUrl = url.startsWith("http") ? url : `http://${host}:${server?.port}${url}`;
1793
+ return fetch(absUrl);
1794
+ };
1795
+ sourceMapResolver.resolveStack(data.stack, data.message ?? "", selfFetch).then((result) => {
1796
+ const hasFileInfo = result.errors.some((e) => e.file);
1797
+ if (!hasFileInfo) {
1798
+ const msg2 = result.errors[0]?.message ?? "";
1799
+ let enrichedErrors = null;
1800
+ if (currentError?.errors.some((e) => e.file)) {
1801
+ enrichedErrors = currentError.errors.map((e) => ({
1802
+ ...e,
1803
+ message: msg2 || e.message
1804
+ }));
1805
+ } else if (lastChangedFile) {
1806
+ const absFile = resolve(projectRoot, lastChangedFile);
1807
+ enrichedErrors = [{ message: msg2, file: lastChangedFile, absFile }];
1808
+ }
1809
+ if (enrichedErrors) {
1810
+ const payload2 = {
1811
+ type: "error",
1812
+ category: "runtime",
1813
+ errors: enrichedErrors,
1814
+ parsedStack: result.parsedStack
1815
+ };
1816
+ currentError = { category: "runtime", errors: enrichedErrors };
1817
+ const text2 = JSON.stringify(payload2);
1818
+ for (const client of wsClients) {
1819
+ client.sendText(text2);
1820
+ }
1821
+ return;
1822
+ }
1823
+ }
1824
+ const payload = {
1825
+ type: "error",
1826
+ category: "runtime",
1827
+ errors: result.errors,
1828
+ parsedStack: result.parsedStack
1829
+ };
1830
+ currentError = { category: "runtime", errors: result.errors };
1831
+ const text = JSON.stringify(payload);
1832
+ for (const client of wsClients) {
1833
+ client.sendText(text);
1834
+ }
1835
+ }).catch(() => {
1836
+ let errors;
1837
+ if (currentError?.errors.some((e) => e.file)) {
1838
+ errors = currentError.errors;
1839
+ } else if (lastChangedFile) {
1840
+ const absFile = resolve(projectRoot, lastChangedFile);
1841
+ errors = [
1842
+ { message: data.message ?? "Unknown error", file: lastChangedFile, absFile }
1843
+ ];
1844
+ }
1845
+ if (errors) {
1846
+ const payload = {
1847
+ type: "error",
1848
+ category: "runtime",
1849
+ errors
1850
+ };
1851
+ currentError = { category: "runtime", errors };
1852
+ const text = JSON.stringify(payload);
1853
+ for (const client of wsClients) {
1854
+ client.sendText(text);
1855
+ }
1856
+ } else {
1857
+ broadcastError("runtime", [{ message: data.message ?? "Unknown error" }]);
1858
+ }
1859
+ });
1860
+ }
1861
+ } catch {}
1862
+ },
1863
+ close(ws) {
1864
+ wsClients.delete(ws);
1865
+ }
1866
+ },
1867
+ development: {
1868
+ hmr: true,
1869
+ console: true
1870
+ }
1871
+ });
1872
+ if (logRequests) {
1873
+ console.log(`[Server] SSR+HMR dev server running at http://${host}:${server.port}`);
1874
+ }
1875
+ await discoverHMRAssets();
1876
+ async function discoverHMRAssets() {
1877
+ try {
1878
+ const res = await fetch(`http://${host}:${server?.port}/__vertz_hmr`);
1879
+ const html = await res.text();
1880
+ const assets = parseHMRAssets(html);
1881
+ if (assets.scriptUrl) {
1882
+ bundledScriptUrl = assets.scriptUrl;
1883
+ if (logRequests) {
1884
+ console.log("[Server] Discovered bundled script URL:", bundledScriptUrl);
1885
+ }
1886
+ }
1887
+ if (assets.bootstrapScript) {
1888
+ hmrBootstrapScript = assets.bootstrapScript;
1889
+ if (logRequests) {
1890
+ console.log("[Server] Extracted HMR bootstrap script");
1891
+ }
1892
+ }
1893
+ } catch (e) {
1894
+ console.warn("[Server] Could not discover HMR bundled URL:", e);
1895
+ }
1896
+ }
1897
+ const srcDir = resolve(projectRoot, "src");
1898
+ stopped = false;
1899
+ if (existsSync(srcDir)) {
1900
+ srcWatcherRef = watch(srcDir, { recursive: true }, (_event, filename) => {
1901
+ if (!filename)
1902
+ return;
1903
+ if (refreshTimeout)
1904
+ clearTimeout(refreshTimeout);
1905
+ refreshTimeout = setTimeout(async () => {
1906
+ lastChangedFile = `src/${filename}`;
1907
+ lastBroadcastedError = "";
1908
+ sourceMapResolver.invalidate();
1909
+ if (logRequests) {
1910
+ console.log(`[Server] File changed: ${filename}`);
1911
+ }
1912
+ if (stopped)
1913
+ return;
1914
+ await discoverHMRAssets();
1915
+ if (stopped)
1916
+ return;
1917
+ try {
1918
+ const clientRelative = rawClientSrc.replace(/^\//, "");
1919
+ const result = await Bun.build({
1920
+ entrypoints: [resolve(projectRoot, clientRelative)],
1921
+ root: projectRoot,
1922
+ target: "browser",
1923
+ throw: false
1924
+ });
1925
+ if (!result.success && result.logs.length > 0) {
1926
+ const errors = result.logs.filter((l) => l.level === "error").map((l) => {
1927
+ const pos = l.position;
1928
+ const file = pos?.file ? pos.file.replace(projectRoot, "").replace(/^\//, "") : undefined;
1929
+ return {
1930
+ message: l.message,
1931
+ file,
1932
+ absFile: pos?.file,
1933
+ line: pos?.line,
1934
+ column: pos?.column,
1935
+ lineText: pos?.lineText
1936
+ };
1937
+ });
1938
+ broadcastError("build", errors);
1939
+ } else {
1940
+ const prevUrl = bundledScriptUrl;
1941
+ for (let attempt = 0;attempt < 5; attempt++) {
1942
+ if (stopped)
1943
+ return;
1944
+ await new Promise((r) => setTimeout(r, 200));
1945
+ if (stopped)
1946
+ return;
1947
+ await discoverHMRAssets();
1948
+ if (bundledScriptUrl !== prevUrl)
1949
+ break;
1950
+ }
1951
+ clearErrorForFileChange();
1952
+ }
1953
+ } catch {}
1954
+ if (stopped)
1955
+ return;
1956
+ for (const key of Object.keys(__require.cache)) {
1957
+ if (key.startsWith(srcDir) || key.startsWith(entryPath)) {
1958
+ delete __require.cache[key];
1959
+ }
1960
+ }
1961
+ try {
1962
+ const freshMod = await import(`${entryPath}?t=${Date.now()}`);
1963
+ ssrMod = freshMod;
1964
+ if (logRequests) {
1965
+ console.log("[Server] SSR module refreshed");
1966
+ }
1967
+ } catch (e) {
1968
+ if (stopped)
1969
+ return;
1970
+ await new Promise((r) => setTimeout(r, 500));
1971
+ if (stopped)
1972
+ return;
1973
+ for (const key of Object.keys(__require.cache)) {
1974
+ if (key.startsWith(srcDir) || key.startsWith(entryPath)) {
1975
+ delete __require.cache[key];
1976
+ }
1977
+ }
1978
+ try {
1979
+ const freshMod = await import(`${entryPath}?t=${Date.now()}`);
1980
+ ssrMod = freshMod;
1981
+ if (logRequests) {
1982
+ console.log("[Server] SSR module refreshed (retry)");
1983
+ }
1984
+ } catch (e2) {
1985
+ console.error("[Server] Failed to refresh SSR module:", e2);
1986
+ const errMsg = e2 instanceof Error ? e2.message : String(e2);
1987
+ const errStack = e2 instanceof Error ? e2.stack : undefined;
1988
+ broadcastError("ssr", [{ message: errMsg, stack: errStack }]);
1989
+ }
1990
+ }
1991
+ }, 100);
1992
+ });
1993
+ }
1994
+ }
1995
+ return {
1996
+ start,
1997
+ broadcastError,
1998
+ clearError,
1999
+ clearErrorForFileChange,
2000
+ setLastChangedFile(file) {
2001
+ lastChangedFile = file;
2002
+ },
2003
+ async stop() {
2004
+ stopped = true;
2005
+ if (refreshTimeout) {
2006
+ clearTimeout(refreshTimeout);
2007
+ refreshTimeout = null;
2008
+ }
2009
+ if (specWatcher) {
2010
+ specWatcher.close();
2011
+ specWatcher = null;
2012
+ }
2013
+ if (srcWatcherRef) {
2014
+ srcWatcherRef.close();
2015
+ srcWatcherRef = null;
2016
+ }
2017
+ if (server) {
2018
+ server.stop(true);
2019
+ server = null;
2020
+ }
2021
+ indexHtmlStasher.restore();
2022
+ }
2023
+ };
2024
+ }
2025
+ export {
2026
+ parseHMRAssets,
2027
+ generateSSRPageHtml,
2028
+ createIndexHtmlStasher,
2029
+ createFetchInterceptor,
2030
+ createBunDevServer,
2031
+ buildScriptTag
2032
+ };