sinwan 0.1.0 → 1.0.0

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.
Files changed (87) hide show
  1. package/README.md +66 -39
  2. package/dist/cjs/index.development.js +1624 -961
  3. package/dist/cjs/index.development.js.map +21 -18
  4. package/dist/cjs/index.production.min.js +2 -2
  5. package/dist/cjs/index.production.min.js.map +21 -18
  6. package/dist/cjs/jsx/jsx-dev-runtime.development.js +6 -16
  7. package/dist/cjs/jsx/jsx-dev-runtime.development.js.map +3 -3
  8. package/dist/cjs/jsx/jsx-dev-runtime.production.min.js +2 -2
  9. package/dist/cjs/jsx/jsx-dev-runtime.production.min.js.map +3 -3
  10. package/dist/cjs/jsx/jsx-runtime.development.js +6 -16
  11. package/dist/cjs/jsx/jsx-runtime.development.js.map +3 -3
  12. package/dist/cjs/jsx/jsx-runtime.production.min.js +2 -2
  13. package/dist/cjs/jsx/jsx-runtime.production.min.js.map +3 -3
  14. package/dist/cjs/renderer/index.development.js +1175 -0
  15. package/dist/cjs/renderer/index.development.js.map +24 -0
  16. package/dist/cjs/renderer/index.production.min.js +3 -0
  17. package/dist/cjs/renderer/index.production.min.js.map +24 -0
  18. package/dist/cjs/server/index.development.js +665 -329
  19. package/dist/cjs/server/index.development.js.map +11 -10
  20. package/dist/cjs/server/index.production.min.js +2 -2
  21. package/dist/cjs/server/index.production.min.js.map +11 -10
  22. package/dist/component/control-flow.d.ts +18 -0
  23. package/dist/component/control-flow.d.ts.map +1 -0
  24. package/dist/component/index.d.ts +3 -1
  25. package/dist/component/index.d.ts.map +1 -1
  26. package/dist/component/instance.d.ts +7 -1
  27. package/dist/component/instance.d.ts.map +1 -1
  28. package/dist/component/lifecycle.d.ts +2 -1
  29. package/dist/component/lifecycle.d.ts.map +1 -1
  30. package/dist/component/provide-inject.d.ts +11 -5
  31. package/dist/component/provide-inject.d.ts.map +1 -1
  32. package/dist/esm/index.development.js +1301 -660
  33. package/dist/esm/index.development.js.map +21 -18
  34. package/dist/esm/index.production.min.js +2 -2
  35. package/dist/esm/index.production.min.js.map +21 -18
  36. package/dist/esm/jsx/jsx-dev-runtime.development.js +6 -16
  37. package/dist/esm/jsx/jsx-dev-runtime.development.js.map +3 -3
  38. package/dist/esm/jsx/jsx-dev-runtime.production.min.js +2 -2
  39. package/dist/esm/jsx/jsx-dev-runtime.production.min.js.map +3 -3
  40. package/dist/esm/jsx/jsx-runtime.development.js +6 -16
  41. package/dist/esm/jsx/jsx-runtime.development.js.map +3 -3
  42. package/dist/esm/jsx/jsx-runtime.production.min.js +2 -2
  43. package/dist/esm/jsx/jsx-runtime.production.min.js.map +3 -3
  44. package/dist/esm/renderer/index.development.js +1124 -0
  45. package/dist/esm/renderer/index.development.js.map +24 -0
  46. package/dist/esm/renderer/index.production.min.js +4 -0
  47. package/dist/esm/renderer/index.production.min.js.map +24 -0
  48. package/dist/esm/server/index.development.js +665 -329
  49. package/dist/esm/server/index.development.js.map +11 -10
  50. package/dist/esm/server/index.production.min.js +2 -2
  51. package/dist/esm/server/index.production.min.js.map +11 -10
  52. package/dist/hydration/walk.d.ts.map +1 -1
  53. package/dist/index.d.ts +6 -6
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/jsx/jsx-runtime.d.ts +13 -0
  56. package/dist/jsx/jsx-runtime.d.ts.map +1 -1
  57. package/dist/jsx/jsx-types.d.ts +122 -57
  58. package/dist/jsx/jsx-types.d.ts.map +1 -1
  59. package/dist/renderer/attributes.d.ts.map +1 -1
  60. package/dist/renderer/dom-ops.d.ts +4 -1
  61. package/dist/renderer/dom-ops.d.ts.map +1 -1
  62. package/dist/renderer/index.d.ts +1 -1
  63. package/dist/renderer/index.d.ts.map +1 -1
  64. package/dist/renderer/mount.d.ts +2 -5
  65. package/dist/renderer/mount.d.ts.map +1 -1
  66. package/dist/renderer/render-children.d.ts +2 -2
  67. package/dist/renderer/render-children.d.ts.map +1 -1
  68. package/dist/renderer/render-control-flow.d.ts +13 -0
  69. package/dist/renderer/render-control-flow.d.ts.map +1 -0
  70. package/dist/renderer/render-element.d.ts +1 -1
  71. package/dist/renderer/render-element.d.ts.map +1 -1
  72. package/dist/renderer/types.d.ts +2 -0
  73. package/dist/renderer/types.d.ts.map +1 -1
  74. package/dist/renderer/unmount.d.ts +20 -0
  75. package/dist/renderer/unmount.d.ts.map +1 -0
  76. package/dist/renderer.d.ts +1 -0
  77. package/dist/renderer.js +7 -0
  78. package/dist/renderer.mjs +4 -0
  79. package/dist/server/hydration-markers.d.ts.map +1 -1
  80. package/dist/server/index.d.ts +1 -1
  81. package/dist/server/index.d.ts.map +1 -1
  82. package/dist/server/renderer.d.ts.map +1 -1
  83. package/dist/server/stream.d.ts +9 -1
  84. package/dist/server/stream.d.ts.map +1 -1
  85. package/dist/types.d.ts +8 -2
  86. package/dist/types.d.ts.map +1 -1
  87. package/package.json +15 -1
@@ -40,22 +40,12 @@ function buildElement(type, props, children) {
40
40
  if (type === Fragment) {
41
41
  return { tag: "", props: {}, children };
42
42
  }
43
- if (typeof type === "function") {
44
- if (type._SinwanComponent || type._SinwanPage) {
45
- const result2 = type(props);
46
- if (result2 && typeof result2 === "object" && "tag" in result2) {
47
- return result2;
48
- }
49
- return { tag: "", props: {}, children: normalizeChildren(result2) };
50
- }
51
- const result = type(props);
52
- if (result && typeof result === "object" && "tag" in result) {
53
- return result;
43
+ if (typeof type === "function" || typeof type === "string") {
44
+ const finalProps = props ?? {};
45
+ if (children.length > 0 && finalProps.children === undefined) {
46
+ finalProps.children = children.length === 1 ? children[0] : children;
54
47
  }
55
- return { tag: "", props: {}, children: normalizeChildren(result) };
56
- }
57
- if (typeof type === "string") {
58
- return { tag: type, props: props || {}, children };
48
+ return { tag: type, props: finalProps, children };
59
49
  }
60
50
  return { tag: "", props: {}, children };
61
51
  }
@@ -75,255 +65,68 @@ function jsxDEV(type, props, key, isStaticChildren, source, self) {
75
65
  return element;
76
66
  }
77
67
 
78
- // src/escaper.ts
79
- var _bun = globalThis.Bun;
80
- var _nativeEscape = typeof _bun?.escapeHTML === "function" ? _bun.escapeHTML.bind(_bun) : undefined;
81
- var HTML_ESCAPE_RE = /[&<>"']/g;
82
- var HTML_ESCAPE_MAP = {
83
- "&": "&amp;",
84
- "<": "&lt;",
85
- ">": "&gt;",
86
- '"': "&quot;",
87
- "'": "&#39;"
88
- };
89
- function portableEscape(str) {
90
- HTML_ESCAPE_RE.lastIndex = 0;
91
- if (!HTML_ESCAPE_RE.test(str))
92
- return str;
93
- return str.replace(HTML_ESCAPE_RE, (c) => HTML_ESCAPE_MAP[c]);
94
- }
95
- function escapeHtml(value) {
96
- if (value == null || typeof value === "boolean")
97
- return "";
98
- if (typeof value === "number")
99
- return String(value);
100
- if (value instanceof HtmlEscapedString)
101
- return value.value;
102
- const s = String(value);
103
- return _nativeEscape ? _nativeEscape(s) : portableEscape(s);
104
- }
105
- function safeHtml(html) {
106
- return raw(html);
107
- }
108
- function isSafeHtml(value) {
109
- return value instanceof HtmlEscapedString;
110
- }
111
-
112
- // src/server/renderer.ts
113
- var componentCache = new WeakMap;
114
- var pageRegistry = new Map;
115
- function registerPage(name, page) {
116
- pageRegistry.set(name, page);
117
- }
118
- function getPage(name) {
119
- return pageRegistry.get(name);
120
- }
121
- function hasPage(name) {
122
- return pageRegistry.has(name);
123
- }
124
- async function renderPage(name, data) {
125
- const page = getPage(name);
126
- if (!page) {
127
- throw new Error(`Page "${name}" not found in registry`);
128
- }
129
- const element = await page(data);
130
- return renderToString(element);
131
- }
132
- async function renderToString(node) {
133
- if (node == null || typeof node === "boolean") {
134
- return "";
135
- }
136
- if (typeof node === "string") {
137
- return escapeHtml(node);
138
- }
139
- if (typeof node === "number") {
140
- return String(node);
141
- }
142
- if (node instanceof HtmlEscapedString) {
143
- return node.value;
144
- }
145
- if (Array.isArray(node)) {
146
- const results = await Promise.all(node.map((child) => renderToString(child)));
147
- return results.join("");
148
- }
149
- if (node instanceof Promise) {
150
- return renderElement(await node);
151
- }
152
- return renderElement(node);
153
- }
154
- async function renderElement(element) {
155
- const { tag, props, children } = element;
156
- if (typeof tag === "function") {
157
- const result = await tag(props);
158
- return renderToString(result);
159
- }
160
- if (typeof tag === "string") {
161
- return renderIntrinsicElement(tag, props, children);
162
- }
163
- return renderToString(children);
164
- }
165
- var VOID_ELEMENTS2 = new Set([
166
- "area",
167
- "base",
168
- "br",
169
- "col",
170
- "embed",
171
- "hr",
172
- "img",
173
- "input",
174
- "link",
175
- "meta",
176
- "param",
177
- "source",
178
- "track",
179
- "wbr"
180
- ]);
181
- async function renderIntrinsicElement(tag, props, children) {
182
- const attrs = renderAttributes(props);
183
- if (VOID_ELEMENTS2.has(tag)) {
184
- return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
185
- }
186
- const childrenHtml = await renderChildren(children, props);
187
- return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
188
- }
189
- function renderAttributes(props) {
190
- let attrs = "";
191
- for (const [key, value] of Object.entries(props)) {
192
- if (key === "children")
193
- continue;
194
- if (value == null || value === false)
195
- continue;
196
- if (value === true) {
197
- attrs += ` ${key}`;
198
- continue;
199
- }
200
- if (key === "dangerouslySetInnerHTML") {
201
- continue;
202
- }
203
- const attrName = key === "className" ? "class" : key;
204
- const finalName = attrName === "htmlFor" ? "for" : attrName;
205
- const attrValue = escapeHtml(String(value));
206
- attrs += ` ${finalName}="${attrValue}"`;
207
- }
208
- return attrs;
209
- }
210
- async function renderChildren(children, props) {
211
- const dangerous = props.dangerouslySetInnerHTML;
212
- if (dangerous && typeof dangerous.__html === "string") {
213
- return dangerous.__html;
214
- }
215
- return renderToString(children);
216
- }
217
- function isSlots(children) {
218
- return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
219
- }
220
- // src/server/stream.ts
221
- var VOID_ELEMENTS3 = new Set([
222
- "area",
223
- "base",
224
- "br",
225
- "col",
226
- "embed",
227
- "hr",
228
- "img",
229
- "input",
230
- "link",
231
- "meta",
232
- "param",
233
- "source",
234
- "track",
235
- "wbr"
236
- ]);
237
- function streamPage(page, data) {
238
- const encoder = new TextEncoder;
239
- return new ReadableStream({
240
- async start(controller) {
241
- try {
242
- const element = await page(data);
243
- await streamNode(element, controller, encoder);
244
- controller.close();
245
- } catch (error) {
246
- controller.error(error);
247
- }
248
- }
249
- });
250
- }
251
- async function streamNode(node, controller, encoder) {
252
- if (node == null || typeof node === "boolean") {
253
- return;
254
- }
255
- if (typeof node === "string") {
256
- controller.enqueue(encoder.encode(escapeHtml(node)));
257
- return;
258
- }
259
- if (typeof node === "number") {
260
- controller.enqueue(encoder.encode(String(node)));
261
- return;
262
- }
263
- if (node instanceof HtmlEscapedString) {
264
- controller.enqueue(encoder.encode(node.value));
265
- return;
266
- }
267
- if (Array.isArray(node)) {
268
- for (const child of node) {
269
- await streamNode(child, controller, encoder);
68
+ // src/renderer/dom-ops.ts
69
+ function createDefaultDOMOps() {
70
+ return {
71
+ createElement(tag) {
72
+ return document.createElement(tag);
73
+ },
74
+ createElementNS(namespace, tag) {
75
+ return document.createElementNS(namespace, tag);
76
+ },
77
+ createTextNode(text) {
78
+ return document.createTextNode(text);
79
+ },
80
+ createComment(text) {
81
+ return document.createComment(text);
82
+ },
83
+ setAttribute(el, key, value) {
84
+ el.setAttribute(key, value);
85
+ },
86
+ removeAttribute(el, key) {
87
+ el.removeAttribute(key);
88
+ },
89
+ setProperty(el, key, value) {
90
+ el[key] = value;
91
+ },
92
+ insertBefore(parent, child, anchor) {
93
+ parent.insertBefore(child, anchor);
94
+ },
95
+ appendChild(parent, child) {
96
+ parent.appendChild(child);
97
+ },
98
+ remove(node) {
99
+ node.parentNode?.removeChild(node);
100
+ },
101
+ setTextContent(node, text) {
102
+ node.data = text;
103
+ },
104
+ addEventListener(el, event, handler) {
105
+ el.addEventListener(event, handler);
106
+ },
107
+ removeEventListener(el, event, handler) {
108
+ el.removeEventListener(event, handler);
109
+ },
110
+ parentNode(node) {
111
+ return node.parentNode;
112
+ },
113
+ nextSibling(node) {
114
+ return node.nextSibling;
270
115
  }
271
- return;
272
- }
273
- if (node instanceof Promise) {
274
- const resolved = await node;
275
- await streamElement(resolved, controller, encoder);
276
- return;
277
- }
278
- await streamElement(node, controller, encoder);
279
- }
280
- async function streamElement(element, controller, encoder) {
281
- const { tag, props, children } = element;
282
- if (typeof tag === "function") {
283
- const result = await tag(props);
284
- await streamNode(result, controller, encoder);
285
- return;
286
- }
287
- if (typeof tag === "string") {
288
- await streamIntrinsicElement(tag, props, children, controller, encoder);
289
- return;
290
- }
291
- await streamNode(children, controller, encoder);
116
+ };
292
117
  }
293
- async function streamIntrinsicElement(tag, props, children, controller, encoder) {
294
- const attrs = renderAttributes2(props);
295
- const dangerous = props.dangerouslySetInnerHTML;
296
- if (VOID_ELEMENTS3.has(tag)) {
297
- const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
298
- controller.enqueue(encoder.encode(html));
299
- return;
300
- }
301
- const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
302
- controller.enqueue(encoder.encode(openTag));
303
- if (dangerous && typeof dangerous.__html === "string") {
304
- controller.enqueue(encoder.encode(dangerous.__html));
305
- } else {
306
- await streamNode(children, controller, encoder);
307
- }
308
- controller.enqueue(encoder.encode(`</${tag}>`));
118
+ var defaultDOMOps = createDefaultDOMOps();
119
+ var domOps = { ...defaultDOMOps };
120
+ function setDOMOps(overrides) {
121
+ Object.assign(domOps, overrides);
309
122
  }
310
- function renderAttributes2(props) {
311
- let attrs = "";
312
- for (const [key, value] of Object.entries(props)) {
313
- if (key === "children" || key === "dangerouslySetInnerHTML")
314
- continue;
315
- if (value == null || value === false)
316
- continue;
317
- if (value === true) {
318
- attrs += ` ${key}`;
319
- continue;
320
- }
321
- const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
322
- const attrValue = escapeHtml(String(value));
323
- attrs += ` ${attrName}="${attrValue}"`;
123
+ function resetDOMOps() {
124
+ for (const key of Object.keys(domOps)) {
125
+ delete domOps[key];
324
126
  }
325
- return attrs;
127
+ Object.assign(domOps, defaultDOMOps);
326
128
  }
129
+
327
130
  // src/reactivity/scheduler.ts
328
131
  var pendingEffects = new Set;
329
132
  var flushScheduled = false;
@@ -566,82 +369,9 @@ function isComputed(value) {
566
369
  return value != null && typeof value === "object" && COMPUTED_BRAND in value;
567
370
  }
568
371
 
569
- // src/hydration/markers.ts
570
- var COMP_ID_ATTR = "data-sinwan-id";
571
- var COMP_ID_PREFIX = "c";
572
- var TEXT_MARKER_OPEN = "sinwan-t:";
573
- var TEXT_MARKER_CLOSE = "/sinwan-t";
574
- var EVENT_ATTR = "data-sinwan-ev";
575
- function compId(index) {
576
- return `${COMP_ID_PREFIX}${index}`;
577
- }
578
- function textMarkerOpen(index) {
579
- return `<!--${TEXT_MARKER_OPEN}${index}-->`;
580
- }
581
- function textMarkerCloseStr() {
582
- return `<!--${TEXT_MARKER_CLOSE}-->`;
583
- }
584
- function parseTextOpenMarker(node) {
585
- const data = node.data;
586
- if (data.startsWith(TEXT_MARKER_OPEN)) {
587
- const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
588
- return Number.isNaN(idx) ? -1 : idx;
589
- }
590
- return -1;
591
- }
592
- function isTextCloseMarker(node) {
593
- return node.data === TEXT_MARKER_CLOSE;
594
- }
595
-
596
- // src/renderer/dom-ops.ts
597
- var domOps = {
598
- createElement(tag) {
599
- return document.createElement(tag);
600
- },
601
- createTextNode(text) {
602
- return document.createTextNode(text);
603
- },
604
- createComment(text) {
605
- return document.createComment(text);
606
- },
607
- setAttribute(el, key, value) {
608
- el.setAttribute(key, value);
609
- },
610
- removeAttribute(el, key) {
611
- el.removeAttribute(key);
612
- },
613
- setProperty(el, key, value) {
614
- el[key] = value;
615
- },
616
- insertBefore(parent, child, anchor) {
617
- parent.insertBefore(child, anchor);
618
- },
619
- appendChild(parent, child) {
620
- parent.appendChild(child);
621
- },
622
- remove(node) {
623
- node.parentNode?.removeChild(node);
624
- },
625
- setTextContent(node, text) {
626
- node.data = text;
627
- },
628
- addEventListener(el, event, handler) {
629
- el.addEventListener(event, handler);
630
- },
631
- removeEventListener(el, event, handler) {
632
- el.removeEventListener(event, handler);
633
- },
634
- parentNode(node) {
635
- return node.parentNode;
636
- },
637
- nextSibling(node) {
638
- return node.nextSibling;
639
- }
640
- };
641
-
642
- // src/renderer/events.ts
643
- function isEventProp(key) {
644
- return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
372
+ // src/renderer/events.ts
373
+ function isEventProp(key) {
374
+ return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
645
375
  }
646
376
  function toEventName(key) {
647
377
  return key.slice(2).toLowerCase();
@@ -695,7 +425,18 @@ function setCurrentInstance(instance) {
695
425
  currentInstance = instance;
696
426
  return prev;
697
427
  }
428
+ function withInstance(instance, fn) {
429
+ const prev = setCurrentInstance(instance);
430
+ try {
431
+ return fn();
432
+ } finally {
433
+ setCurrentInstance(prev);
434
+ }
435
+ }
698
436
  function fireMountedHooks(instance) {
437
+ if (instance.isUnmounted) {
438
+ return;
439
+ }
699
440
  for (const child of instance.children) {
700
441
  fireMountedHooks(child);
701
442
  }
@@ -722,6 +463,24 @@ function fireUnmountedHooks(instance) {
722
463
  instance.effects.length = 0;
723
464
  }
724
465
  }
466
+ function fireUpdatedHooks(instance) {
467
+ for (const hook of instance._updatedHooks) {
468
+ hook();
469
+ }
470
+ }
471
+ var queuedUpdatedHooks = new Set;
472
+ function queueUpdatedHooks(instance) {
473
+ if (!instance || !instance.isMounted || instance.isUnmounted || instance._updatedHooks.length === 0 || queuedUpdatedHooks.has(instance)) {
474
+ return;
475
+ }
476
+ queuedUpdatedHooks.add(instance);
477
+ nextTick(() => {
478
+ queuedUpdatedHooks.delete(instance);
479
+ if (instance.isMounted && !instance.isUnmounted) {
480
+ fireUpdatedHooks(instance);
481
+ }
482
+ });
483
+ }
725
484
  function handleComponentError(instance, err) {
726
485
  let current = instance;
727
486
  while (current) {
@@ -736,212 +495,6 @@ function handleComponentError(instance, err) {
736
495
  console.error("[Sinwan] Unhandled component error:", err);
737
496
  }
738
497
 
739
- // src/server/hydration-markers.ts
740
- function createHydrationContext() {
741
- return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
742
- }
743
- async function renderToHydratableString(component, props) {
744
- const ctx = createHydrationContext();
745
- const mergedProps = props ?? {};
746
- const instance = createComponentInstance(component, mergedProps, null);
747
- setCurrentInstance(instance);
748
- const result = await component(mergedProps);
749
- setCurrentInstance(null);
750
- if (result && typeof result === "object" && "tag" in result) {
751
- return renderElementH(result, ctx, true);
752
- }
753
- return renderNodeH(result, ctx);
754
- }
755
- async function renderNodeToHydratableString(node) {
756
- const ctx = createHydrationContext();
757
- return renderNodeH(node, ctx);
758
- }
759
- var VOID_ELEMENTS4 = new Set([
760
- "area",
761
- "base",
762
- "br",
763
- "col",
764
- "embed",
765
- "hr",
766
- "img",
767
- "input",
768
- "link",
769
- "meta",
770
- "param",
771
- "source",
772
- "track",
773
- "wbr"
774
- ]);
775
- function renderNodeH(node, ctx) {
776
- if (node == null || typeof node === "boolean")
777
- return "";
778
- if (typeof node === "string")
779
- return escapeHtml(node);
780
- if (typeof node === "number")
781
- return String(node);
782
- if (node instanceof HtmlEscapedString)
783
- return node.value;
784
- if (isSignal(node) || isComputed(node)) {
785
- const value = node.value;
786
- const idx = ctx.textIndex++;
787
- return `${textMarkerOpen(idx)}${escapeHtml(String(value))}${textMarkerCloseStr()}`;
788
- }
789
- if (Array.isArray(node)) {
790
- return node.map((child) => renderNodeH(child, ctx)).join("");
791
- }
792
- if (node instanceof Promise) {
793
- return "";
794
- }
795
- if (typeof node === "object" && "tag" in node) {
796
- return renderElementH(node, ctx, false);
797
- }
798
- return escapeHtml(String(node));
799
- }
800
- function renderElementH(element, ctx, isComponentRoot) {
801
- const { tag, props, children } = element;
802
- if (tag === "") {
803
- return children.map((child) => renderNodeH(child, ctx)).join("");
804
- }
805
- if (typeof tag === "function") {
806
- return renderComponentH(tag, props, ctx);
807
- }
808
- if (typeof tag === "string") {
809
- return renderIntrinsicH(tag, props, children, ctx, isComponentRoot);
810
- }
811
- return children.map((child) => renderNodeH(child, ctx)).join("");
812
- }
813
- function renderComponentH(component, props, ctx) {
814
- const parentInstance = globalThis.__SinwanCurrentInstance;
815
- const instance = createComponentInstance(component, props, null);
816
- const prev = setCurrentInstance(instance);
817
- const result = component(props);
818
- setCurrentInstance(prev);
819
- if (result && typeof result === "object" && "tag" in result) {
820
- return renderElementH(result, ctx, true);
821
- }
822
- return renderNodeH(result, ctx);
823
- }
824
- function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
825
- let attrs = "";
826
- if (isComponentRoot) {
827
- attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
828
- }
829
- const eventParts = [];
830
- for (const [key, value] of Object.entries(props)) {
831
- if (key === "children" || key === "dangerouslySetInnerHTML")
832
- continue;
833
- if (isEventProp(key)) {
834
- const eventName = toEventName(key);
835
- eventParts.push(`${eventName}:${ctx.eventIndex++}`);
836
- continue;
837
- }
838
- if (value == null || value === false)
839
- continue;
840
- let resolvedValue = value;
841
- if (isSignal(value) || isComputed(value)) {
842
- resolvedValue = value.value;
843
- }
844
- if (resolvedValue === true) {
845
- const attrName2 = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
846
- attrs += ` ${attrName2}`;
847
- continue;
848
- }
849
- const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
850
- attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
851
- }
852
- if (eventParts.length > 0) {
853
- attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
854
- }
855
- if (VOID_ELEMENTS4.has(tag)) {
856
- return `<${tag}${attrs}>`;
857
- }
858
- const dangerous = props.dangerouslySetInnerHTML;
859
- if (dangerous && typeof dangerous.__html === "string") {
860
- return `<${tag}${attrs}>${dangerous.__html}</${tag}>`;
861
- }
862
- const childrenHtml = children.map((child) => renderNodeH(child, ctx)).join("");
863
- return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
864
- }
865
- // src/reactivity/batch.ts
866
- var batchDepth = 0;
867
- function batch(fn) {
868
- batchDepth++;
869
- try {
870
- fn();
871
- } finally {
872
- batchDepth--;
873
- if (batchDepth === 0) {
874
- flushSync();
875
- }
876
- }
877
- }
878
- // src/component/lifecycle.ts
879
- function onMounted(fn) {
880
- const instance = getCurrentInstance();
881
- if (!instance) {
882
- throw new Error("onMounted() called outside of component setup.");
883
- }
884
- instance._mountedHooks.push(fn);
885
- }
886
- function onUnmounted(fn) {
887
- const instance = getCurrentInstance();
888
- if (!instance) {
889
- throw new Error("onUnmounted() called outside of component setup.");
890
- }
891
- instance._unmountedHooks.push(fn);
892
- }
893
- function onUpdated(fn) {
894
- const instance = getCurrentInstance();
895
- if (!instance) {
896
- throw new Error("onUpdated() called outside of component setup.");
897
- }
898
- instance._updatedHooks.push(fn);
899
- }
900
- function onError(fn) {
901
- const instance = getCurrentInstance();
902
- if (!instance) {
903
- throw new Error("onError() called outside of component setup.");
904
- }
905
- instance._errorHooks.push(fn);
906
- }
907
- // src/component/create.ts
908
- function createComponent(fn) {
909
- const component = (props) => fn(props);
910
- component._SinwanComponent = true;
911
- component._displayName = fn.name || "AnonymousComponent";
912
- return component;
913
- }
914
- function createPage(fn) {
915
- const page = (data) => fn(data);
916
- page._SinwanPage = true;
917
- page._displayName = fn.name || "AnonymousPage";
918
- return page;
919
- }
920
- function createLayout(fn) {
921
- return createComponent(fn);
922
- }
923
- // src/component/provide-inject.ts
924
- function provide(key, value) {
925
- const instance = getCurrentInstance();
926
- if (!instance) {
927
- throw new Error("provide() called outside of component setup.");
928
- }
929
- instance.provides[key] = value;
930
- }
931
- function inject(key, defaultValue) {
932
- const instance = getCurrentInstance();
933
- if (!instance) {
934
- throw new Error("inject() called outside of component setup.");
935
- }
936
- if (key in instance.provides) {
937
- return instance.provides[key];
938
- }
939
- if (arguments.length >= 2) {
940
- return defaultValue;
941
- }
942
- console.warn(`[Sinwan] inject() key "${String(key)}" not found and no default provided.`);
943
- return;
944
- }
945
498
  // src/renderer/attributes.ts
946
499
  var SKIP_PROPS = new Set(["children", "key", "ref", "dangerouslySetInnerHTML"]);
947
500
  var DOM_PROPERTIES = new Set(["value", "checked", "selected", "disabled", "readOnly", "multiple", "indeterminate"]);
@@ -953,12 +506,18 @@ var PROP_ALIASES = {
953
506
  };
954
507
  function applyAttributes(el, props) {
955
508
  const disposers = [];
509
+ const owner = getCurrentInstance();
956
510
  for (const [key, value] of Object.entries(props)) {
957
511
  if (SKIP_PROPS.has(key) || isEventProp(key))
958
512
  continue;
959
513
  if (isSignal(value) || isComputed(value)) {
514
+ let initialized = false;
960
515
  const dispose = effect(() => {
961
516
  setSingleAttribute(el, key, value.value);
517
+ if (initialized) {
518
+ queueUpdatedHooks(owner);
519
+ }
520
+ initialized = true;
962
521
  });
963
522
  disposers.push(dispose);
964
523
  } else {
@@ -1018,47 +577,293 @@ function applyClass(el, value) {
1018
577
  domOps.setAttribute(el, "class", classStr);
1019
578
  }
1020
579
 
1021
- // src/renderer/render-element.ts
1022
- var VOID_ELEMENTS5 = new Set([
1023
- "area",
1024
- "base",
1025
- "br",
1026
- "col",
1027
- "embed",
1028
- "hr",
1029
- "img",
1030
- "input",
1031
- "link",
1032
- "meta",
1033
- "param",
1034
- "source",
1035
- "track",
1036
- "wbr"
1037
- ]);
1038
- function renderElementToDOM(element, parent, anchor = null) {
1039
- const { tag, props, children } = element;
1040
- if (tag === "" || tag === Fragment) {
1041
- return renderFragmentToDOM(children, parent, anchor);
1042
- }
1043
- if (typeof tag === "function") {
1044
- return renderComponentToDOM(tag, props, parent, anchor);
1045
- }
1046
- if (typeof tag === "string") {
1047
- return renderIntrinsicToDOM(tag, props, children, parent, anchor);
1048
- }
1049
- return renderFragmentToDOM(children, parent, anchor);
580
+ // src/component/control-flow.ts
581
+ var SHOW_TYPE = Symbol.for("Sinwan.Show");
582
+ var FOR_TYPE = Symbol.for("Sinwan.For");
583
+ function Show(props) {
584
+ return {
585
+ tag: SHOW_TYPE,
586
+ props,
587
+ children: []
588
+ };
1050
589
  }
1051
- function renderIntrinsicToDOM(tag, props, children, parent, anchor) {
1052
- const el = domOps.createElement(tag);
1053
- const attrDisposers = applyAttributes(el, props);
590
+ function For(props) {
591
+ return {
592
+ tag: FOR_TYPE,
593
+ props,
594
+ children: []
595
+ };
596
+ }
597
+ function isShowElement(element) {
598
+ return element.tag === SHOW_TYPE;
599
+ }
600
+ function isForElement(element) {
601
+ return element.tag === FOR_TYPE;
602
+ }
603
+
604
+ // src/renderer/unmount.ts
605
+ function getMountedDomNodes(node) {
606
+ switch (node.type) {
607
+ case "text":
608
+ case "reactive-text":
609
+ return [node.node];
610
+ case "element":
611
+ return [node.node];
612
+ case "fragment":
613
+ return [
614
+ node.anchor,
615
+ ...node.children.flatMap((child) => getMountedDomNodes(child))
616
+ ];
617
+ case "reactive-block":
618
+ return [
619
+ node.startAnchor,
620
+ ...node.children.flatMap((child) => getMountedDomNodes(child)),
621
+ node.endAnchor
622
+ ];
623
+ case "component":
624
+ return node.children.flatMap((child) => getMountedDomNodes(child));
625
+ }
626
+ }
627
+ function unmountNode(node) {
628
+ switch (node.type) {
629
+ case "text":
630
+ break;
631
+ case "reactive-text":
632
+ node.dispose();
633
+ break;
634
+ case "element":
635
+ for (const dispose of node.attrDisposers) {
636
+ dispose();
637
+ }
638
+ for (const cleanup of node.eventCleanups) {
639
+ cleanup();
640
+ }
641
+ node.refCleanup?.();
642
+ for (const child of node.children) {
643
+ unmountNode(child);
644
+ }
645
+ break;
646
+ case "fragment":
647
+ for (const child of node.children) {
648
+ unmountNode(child);
649
+ }
650
+ break;
651
+ case "reactive-block":
652
+ node.dispose();
653
+ for (const child of node.children) {
654
+ unmountNode(child);
655
+ }
656
+ break;
657
+ case "component":
658
+ if (node.instance) {
659
+ fireUnmountedHooks(node.instance);
660
+ } else {
661
+ for (const dispose of node.disposers) {
662
+ dispose();
663
+ }
664
+ }
665
+ for (const child of node.children) {
666
+ unmountNode(child);
667
+ }
668
+ break;
669
+ }
670
+ }
671
+ function removeMountedNode(node) {
672
+ const domNodes = getMountedDomNodes(node);
673
+ unmountNode(node);
674
+ for (const domNode of domNodes) {
675
+ if (domNode.parentNode) {
676
+ domOps.remove(domNode);
677
+ }
678
+ }
679
+ }
680
+
681
+ // src/renderer/render-control-flow.ts
682
+ function renderControlFlowToDOM(element, parent, anchor, namespace) {
683
+ const startAnchor = domOps.createComment("Sinwan-b");
684
+ const endAnchor = domOps.createComment("/Sinwan-b");
685
+ insertNode(parent, startAnchor, anchor);
686
+ insertNode(parent, endAnchor, anchor);
687
+ const owner = getCurrentInstance();
688
+ let disposeEffect = () => {};
689
+ const block = {
690
+ type: "reactive-block",
691
+ dispose: () => disposeEffect(),
692
+ children: [],
693
+ startAnchor,
694
+ endAnchor
695
+ };
696
+ if (isShowElement(element)) {
697
+ disposeEffect = renderShowBlock(element, block, parent, namespace, owner);
698
+ } else if (isForElement(element)) {
699
+ disposeEffect = renderForBlock(element, block, parent, namespace, owner);
700
+ }
701
+ return block;
702
+ }
703
+ function renderShowBlock(element, block, parent, namespace, owner) {
704
+ let initialized = false;
705
+ return effect(() => {
706
+ clearChildren(block);
707
+ const when = readReactive(element.props.when);
708
+ const content = withOptionalInstance(owner, () => when ? resolveShowChildren(element, when) : element.props.fallback);
709
+ block.children = renderBlockContent(content, parent, block.endAnchor, namespace, owner);
710
+ if (initialized) {
711
+ fireMountedAndQueueUpdated(owner);
712
+ }
713
+ initialized = true;
714
+ });
715
+ }
716
+ function renderForBlock(element, block, parent, namespace, owner) {
717
+ let initialized = false;
718
+ let records = [];
719
+ return effect(() => {
720
+ const props = element.props;
721
+ const items = readReactive(props.each);
722
+ const list = Array.isArray(items) ? items : [];
723
+ const renderChild = props.children;
724
+ if (typeof renderChild !== "function") {
725
+ clearChildren(block);
726
+ records = [];
727
+ if (initialized) {
728
+ queueUpdatedHooks(owner);
729
+ }
730
+ initialized = true;
731
+ return;
732
+ }
733
+ const oldByKey = new Map;
734
+ for (const record of records) {
735
+ oldByKey.set(record.key, record);
736
+ }
737
+ const nextRecords = [];
738
+ list.forEach((item, index) => {
739
+ const key = props.key ? props.key(item, index) : item;
740
+ const old = oldByKey.get(key);
741
+ if (old && old.item === item) {
742
+ old.index = index;
743
+ moveBeforeEnd(parent, old.mounted, block.endAnchor);
744
+ nextRecords.push(old);
745
+ oldByKey.delete(key);
746
+ return;
747
+ }
748
+ if (old) {
749
+ removeMountedNode(old.mounted);
750
+ oldByKey.delete(key);
751
+ }
752
+ const record = {
753
+ key,
754
+ item,
755
+ index,
756
+ mounted: { type: "text", node: domOps.createTextNode("") }
757
+ };
758
+ record.mounted = withOptionalInstance(owner, () => renderNodeToDOM(renderChild(item, () => record.index), parent, block.endAnchor, namespace));
759
+ nextRecords.push(record);
760
+ });
761
+ for (const record of oldByKey.values()) {
762
+ removeMountedNode(record.mounted);
763
+ }
764
+ records = nextRecords;
765
+ block.children = nextRecords.map((record) => record.mounted);
766
+ if (initialized) {
767
+ fireMountedAndQueueUpdated(owner);
768
+ }
769
+ initialized = true;
770
+ });
771
+ }
772
+ function resolveShowChildren(element, value) {
773
+ const children = element.props.children ?? element.children;
774
+ if (typeof children === "function") {
775
+ return children(value);
776
+ }
777
+ return children;
778
+ }
779
+ function renderBlockContent(content, parent, anchor, namespace, owner) {
780
+ if (content == null || typeof content === "boolean") {
781
+ return [];
782
+ }
783
+ const nodes = Array.isArray(content) ? content : [content];
784
+ return nodes.map((node) => withOptionalInstance(owner, () => renderNodeToDOM(node, parent, anchor, namespace)));
785
+ }
786
+ function clearChildren(block) {
787
+ for (const child of block.children) {
788
+ removeMountedNode(child);
789
+ }
790
+ block.children = [];
791
+ }
792
+ function moveBeforeEnd(parent, mounted, endAnchor) {
793
+ for (const node of getMountedDomNodes(mounted)) {
794
+ domOps.insertBefore(parent, node, endAnchor);
795
+ }
796
+ }
797
+ function fireMountedAndQueueUpdated(owner) {
798
+ if (owner) {
799
+ fireMountedHooks(owner);
800
+ }
801
+ queueUpdatedHooks(owner);
802
+ }
803
+ function withOptionalInstance(owner, fn) {
804
+ return owner ? withInstance(owner, fn) : fn();
805
+ }
806
+ function readReactive(value) {
807
+ return isSignal(value) || isComputed(value) ? value.value : value;
808
+ }
809
+ function insertNode(parent, child, anchor) {
810
+ if (anchor) {
811
+ domOps.insertBefore(parent, child, anchor);
812
+ } else {
813
+ domOps.appendChild(parent, child);
814
+ }
815
+ }
816
+
817
+ // src/renderer/render-element.ts
818
+ var VOID_ELEMENTS2 = new Set([
819
+ "area",
820
+ "base",
821
+ "br",
822
+ "col",
823
+ "embed",
824
+ "hr",
825
+ "img",
826
+ "input",
827
+ "link",
828
+ "meta",
829
+ "param",
830
+ "source",
831
+ "track",
832
+ "wbr"
833
+ ]);
834
+ var SVG_NS = "http://www.w3.org/2000/svg";
835
+ var MATH_NS = "http://www.w3.org/1998/Math/MathML";
836
+ function renderElementToDOM(element, parent, anchor = null, namespace = null) {
837
+ const { tag, props, children } = element;
838
+ if (tag === "" || tag === Fragment) {
839
+ return renderFragmentToDOM(children, parent, anchor, namespace);
840
+ }
841
+ if (tag === Show || tag === For) {
842
+ return renderElementToDOM(tag(props), parent, anchor, namespace);
843
+ }
844
+ if (isShowElement(element) || isForElement(element)) {
845
+ return renderControlFlowToDOM(element, parent, anchor, namespace);
846
+ }
847
+ if (typeof tag === "function") {
848
+ return renderComponentToDOM(tag, props, parent, anchor, namespace);
849
+ }
850
+ if (typeof tag === "string") {
851
+ return renderIntrinsicToDOM(tag, props, children, parent, anchor, namespace);
852
+ }
853
+ return renderFragmentToDOM(children, parent, anchor, namespace);
854
+ }
855
+ function renderIntrinsicToDOM(tag, props, children, parent, anchor, parentNamespace) {
856
+ const namespace = getElementNamespace(tag, parentNamespace);
857
+ const el = namespace ? domOps.createElementNS(namespace, tag) : domOps.createElement(tag);
858
+ const attrDisposers = applyAttributes(el, props);
1054
859
  const eventCleanups = bindEvents(el, props);
1055
860
  let mountedChildren = [];
1056
- if (!VOID_ELEMENTS5.has(tag)) {
861
+ if (!VOID_ELEMENTS2.has(tag)) {
1057
862
  const dangerous = props.dangerouslySetInnerHTML;
1058
863
  if (dangerous && typeof dangerous.__html === "string") {
1059
864
  el.innerHTML = dangerous.__html;
1060
865
  } else {
1061
- mountedChildren = renderChildrenToDOM(children, el);
866
+ mountedChildren = renderChildrenToDOM(children, el, getChildNamespace(tag, namespace));
1062
867
  }
1063
868
  }
1064
869
  if (anchor) {
@@ -1066,15 +871,17 @@ function renderIntrinsicToDOM(tag, props, children, parent, anchor) {
1066
871
  } else {
1067
872
  domOps.appendChild(parent, el);
1068
873
  }
874
+ const refCleanup = applyRef(el, props.ref);
1069
875
  return {
1070
876
  type: "element",
1071
877
  node: el,
1072
878
  children: mountedChildren,
1073
879
  eventCleanups,
1074
- attrDisposers
880
+ attrDisposers,
881
+ refCleanup
1075
882
  };
1076
883
  }
1077
- function renderComponentToDOM(component, props, parent, anchor) {
884
+ function renderComponentToDOM(component, props, parent, anchor, namespace) {
1078
885
  const parentInstance = getCurrentInstance();
1079
886
  const instance = createComponentInstance(component, props, parentInstance);
1080
887
  if (parentInstance) {
@@ -1086,9 +893,9 @@ function renderComponentToDOM(component, props, parent, anchor) {
1086
893
  try {
1087
894
  result = component(props);
1088
895
  if (result && typeof result === "object" && "tag" in result) {
1089
- child = renderElementToDOM(result, parent, anchor);
896
+ child = renderElementToDOM(result, parent, anchor, namespace);
1090
897
  } else {
1091
- child = renderNodeToDOM(result, parent, anchor);
898
+ child = renderNodeToDOM(result, parent, anchor, namespace);
1092
899
  }
1093
900
  } catch (err) {
1094
901
  setCurrentInstance(prevInstance);
@@ -1115,7 +922,7 @@ function renderComponentToDOM(component, props, parent, anchor) {
1115
922
  instance
1116
923
  };
1117
924
  }
1118
- function renderFragmentToDOM(children, parent, anchor) {
925
+ function renderFragmentToDOM(children, parent, anchor, namespace) {
1119
926
  const anchorComment = domOps.createComment("Sinwan-f");
1120
927
  if (anchor) {
1121
928
  domOps.insertBefore(parent, anchorComment, anchor);
@@ -1124,77 +931,113 @@ function renderFragmentToDOM(children, parent, anchor) {
1124
931
  }
1125
932
  const mounted = [];
1126
933
  for (const child of children) {
1127
- mounted.push(renderNodeToDOM(child, parent, anchor));
934
+ mounted.push(renderNodeToDOM(child, parent, anchor, namespace));
1128
935
  }
1129
936
  return { type: "fragment", children: mounted, anchor: anchorComment };
1130
937
  }
938
+ function getElementNamespace(tag, parentNamespace) {
939
+ if (tag === "svg")
940
+ return SVG_NS;
941
+ if (tag === "math")
942
+ return MATH_NS;
943
+ return parentNamespace;
944
+ }
945
+ function getChildNamespace(tag, namespace) {
946
+ if (namespace === SVG_NS && tag === "foreignObject") {
947
+ return null;
948
+ }
949
+ return namespace;
950
+ }
951
+ function applyRef(el, ref) {
952
+ const value = ref;
953
+ if (!value) {
954
+ return null;
955
+ }
956
+ if (typeof value === "function") {
957
+ value(el);
958
+ return () => value(null);
959
+ }
960
+ if (typeof value === "object" && "current" in value) {
961
+ value.current = el;
962
+ return () => {
963
+ value.current = null;
964
+ };
965
+ }
966
+ return null;
967
+ }
1131
968
 
1132
969
  // src/renderer/render-children.ts
1133
- function renderNodeToDOM(node, parent, anchor = null) {
970
+ function renderNodeToDOM(node, parent, anchor = null, namespace = null) {
1134
971
  if (node == null || typeof node === "boolean") {
1135
972
  const text2 = domOps.createTextNode("");
1136
- insertNode(parent, text2, anchor);
973
+ insertNode2(parent, text2, anchor);
1137
974
  return { type: "text", node: text2 };
1138
975
  }
1139
976
  if (typeof node === "string") {
1140
977
  const text2 = domOps.createTextNode(node);
1141
- insertNode(parent, text2, anchor);
978
+ insertNode2(parent, text2, anchor);
1142
979
  return { type: "text", node: text2 };
1143
980
  }
1144
981
  if (typeof node === "number") {
1145
982
  const text2 = domOps.createTextNode(String(node));
1146
- insertNode(parent, text2, anchor);
983
+ insertNode2(parent, text2, anchor);
1147
984
  return { type: "text", node: text2 };
1148
985
  }
1149
986
  if (node instanceof HtmlEscapedString) {
1150
987
  const text2 = domOps.createTextNode(node.value);
1151
- insertNode(parent, text2, anchor);
988
+ insertNode2(parent, text2, anchor);
1152
989
  return { type: "text", node: text2 };
1153
990
  }
1154
991
  if (isSignal(node) || isComputed(node)) {
1155
992
  const text2 = domOps.createTextNode(String(node.value));
1156
- insertNode(parent, text2, anchor);
993
+ insertNode2(parent, text2, anchor);
994
+ const owner = getCurrentInstance();
995
+ let initialized = false;
1157
996
  const dispose = effect(() => {
1158
997
  domOps.setTextContent(text2, String(node.value));
998
+ if (initialized) {
999
+ queueUpdatedHooks(owner);
1000
+ }
1001
+ initialized = true;
1159
1002
  });
1160
1003
  return { type: "reactive-text", node: text2, dispose };
1161
1004
  }
1162
1005
  if (Array.isArray(node)) {
1163
- return renderArrayToDOM(node, parent, anchor);
1006
+ return renderArrayToDOM(node, parent, anchor, namespace);
1164
1007
  }
1165
1008
  if (node instanceof Promise) {
1166
1009
  const placeholder = domOps.createTextNode("");
1167
- insertNode(parent, placeholder, anchor);
1010
+ insertNode2(parent, placeholder, anchor);
1168
1011
  node.then((resolved) => {
1169
- const mounted = renderNodeToDOM(resolved, parent, placeholder);
1012
+ const mounted = renderNodeToDOM(resolved, parent, placeholder, namespace);
1170
1013
  domOps.remove(placeholder);
1171
1014
  });
1172
1015
  return { type: "text", node: placeholder };
1173
1016
  }
1174
1017
  if (typeof node === "object" && "tag" in node) {
1175
- return renderElementToDOM(node, parent, anchor);
1018
+ return renderElementToDOM(node, parent, anchor, namespace);
1176
1019
  }
1177
1020
  const text = domOps.createTextNode(String(node));
1178
- insertNode(parent, text, anchor);
1021
+ insertNode2(parent, text, anchor);
1179
1022
  return { type: "text", node: text };
1180
1023
  }
1181
- function renderArrayToDOM(nodes, parent, anchor) {
1024
+ function renderArrayToDOM(nodes, parent, anchor, namespace) {
1182
1025
  const anchorComment = domOps.createComment("Sinwan-f");
1183
- insertNode(parent, anchorComment, anchor);
1026
+ insertNode2(parent, anchorComment, anchor);
1184
1027
  const children = [];
1185
1028
  for (const child of nodes) {
1186
- children.push(renderNodeToDOM(child, parent, anchor));
1029
+ children.push(renderNodeToDOM(child, parent, anchor, namespace));
1187
1030
  }
1188
1031
  return { type: "fragment", children, anchor: anchorComment };
1189
1032
  }
1190
- function renderChildrenToDOM(children, parent) {
1033
+ function renderChildrenToDOM(children, parent, namespace = null) {
1191
1034
  const mounted = [];
1192
1035
  for (const child of children) {
1193
- mounted.push(renderNodeToDOM(child, parent));
1036
+ mounted.push(renderNodeToDOM(child, parent, null, namespace));
1194
1037
  }
1195
1038
  return mounted;
1196
1039
  }
1197
- function insertNode(parent, child, anchor) {
1040
+ function insertNode2(parent, child, anchor) {
1198
1041
  if (anchor) {
1199
1042
  domOps.insertBefore(parent, child, anchor);
1200
1043
  } else {
@@ -1260,79 +1103,793 @@ function render(node, container) {
1260
1103
  }
1261
1104
  };
1262
1105
  }
1263
- function unmountNode(node) {
1264
- switch (node.type) {
1265
- case "text":
1266
- break;
1267
- case "reactive-text":
1268
- node.dispose();
1269
- break;
1270
- case "element":
1271
- for (const dispose of node.attrDisposers) {
1272
- dispose();
1273
- }
1274
- for (const cleanup of node.eventCleanups) {
1275
- cleanup();
1276
- }
1277
- for (const child of node.children) {
1278
- unmountNode(child);
1279
- }
1280
- break;
1281
- case "fragment":
1282
- for (const child of node.children) {
1283
- unmountNode(child);
1284
- }
1285
- break;
1286
- case "reactive-block":
1287
- node.dispose();
1288
- for (const child of node.children) {
1289
- unmountNode(child);
1290
- }
1291
- break;
1292
- case "component":
1293
- if (node.instance) {
1294
- fireUnmountedHooks(node.instance);
1295
- } else {
1296
- for (const dispose of node.disposers) {
1297
- dispose();
1298
- }
1299
- }
1300
- for (const child of node.children) {
1301
- unmountNode(child);
1302
- }
1303
- break;
1304
- }
1106
+ // src/escaper.ts
1107
+ var _bun = globalThis.Bun;
1108
+ var _nativeEscape = typeof _bun?.escapeHTML === "function" ? _bun.escapeHTML.bind(_bun) : undefined;
1109
+ var HTML_ESCAPE_RE = /[&<>"']/g;
1110
+ var HTML_ESCAPE_MAP = {
1111
+ "&": "&amp;",
1112
+ "<": "&lt;",
1113
+ ">": "&gt;",
1114
+ '"': "&quot;",
1115
+ "'": "&#39;"
1116
+ };
1117
+ function portableEscape(str) {
1118
+ HTML_ESCAPE_RE.lastIndex = 0;
1119
+ if (!HTML_ESCAPE_RE.test(str))
1120
+ return str;
1121
+ return str.replace(HTML_ESCAPE_RE, (c) => HTML_ESCAPE_MAP[c]);
1305
1122
  }
1306
- // src/hydration/walk.ts
1307
- function advance(cursor) {
1308
- const node = cursor.current;
1309
- if (node) {
1310
- cursor.current = node.nextSibling;
1123
+ function escapeHtml(value) {
1124
+ if (value == null || typeof value === "boolean")
1125
+ return "";
1126
+ if (typeof value === "number")
1127
+ return String(value);
1128
+ if (value instanceof HtmlEscapedString)
1129
+ return value.value;
1130
+ const s = String(value);
1131
+ return _nativeEscape ? _nativeEscape(s) : portableEscape(s);
1132
+ }
1133
+ function safeHtml(html) {
1134
+ return raw(html);
1135
+ }
1136
+ function isSafeHtml(value) {
1137
+ return value instanceof HtmlEscapedString;
1138
+ }
1139
+
1140
+ // src/server/renderer.ts
1141
+ var componentCache = new WeakMap;
1142
+ var pageRegistry = new Map;
1143
+ function registerPage(name, page) {
1144
+ pageRegistry.set(name, page);
1145
+ }
1146
+ function getPage(name) {
1147
+ return pageRegistry.get(name);
1148
+ }
1149
+ function hasPage(name) {
1150
+ return pageRegistry.has(name);
1151
+ }
1152
+ async function renderPage(name, data) {
1153
+ const page = getPage(name);
1154
+ if (!page) {
1155
+ throw new Error(`Page "${name}" not found in registry`);
1311
1156
  }
1312
- return node;
1157
+ const element = await page(data);
1158
+ return renderToString(element);
1313
1159
  }
1314
- function hydrateNode(node, cursor) {
1160
+ async function renderToString(node) {
1315
1161
  if (node == null || typeof node === "boolean") {
1316
- const textNode2 = advance(cursor);
1317
- return { type: "text", node: textNode2 ?? document.createTextNode("") };
1162
+ return "";
1318
1163
  }
1319
1164
  if (typeof node === "string") {
1320
- const textNode2 = advance(cursor);
1321
- return { type: "text", node: textNode2 };
1165
+ return escapeHtml(node);
1322
1166
  }
1323
1167
  if (typeof node === "number") {
1324
- const textNode2 = advance(cursor);
1325
- return { type: "text", node: textNode2 };
1168
+ return String(node);
1326
1169
  }
1327
1170
  if (node instanceof HtmlEscapedString) {
1328
- const textNode2 = advance(cursor);
1329
- return { type: "text", node: textNode2 };
1171
+ return node.value;
1330
1172
  }
1331
1173
  if (isSignal(node) || isComputed(node)) {
1332
- return hydrateReactiveText(node, cursor);
1174
+ return escapeHtml(String(node.value));
1333
1175
  }
1334
1176
  if (Array.isArray(node)) {
1335
- return hydrateArray(node, cursor);
1177
+ const results = await Promise.all(node.map((child) => renderToString(child)));
1178
+ return results.join("");
1179
+ }
1180
+ if (node instanceof Promise) {
1181
+ return renderElement(await node);
1182
+ }
1183
+ return renderElement(node);
1184
+ }
1185
+ async function renderElement(element) {
1186
+ const { tag, props, children } = element;
1187
+ if (isShowElement(element)) {
1188
+ const when = readReactive2(props.when);
1189
+ return renderToString(when ? resolveShowChildren2(element, when) : props.fallback);
1190
+ }
1191
+ if (isForElement(element)) {
1192
+ return renderForElement(element);
1193
+ }
1194
+ if (typeof tag === "function") {
1195
+ const result = await tag(props);
1196
+ return renderToString(result);
1197
+ }
1198
+ if (typeof tag === "string") {
1199
+ return renderIntrinsicElement(tag, props, children);
1200
+ }
1201
+ return renderToString(children);
1202
+ }
1203
+ var VOID_ELEMENTS3 = new Set([
1204
+ "area",
1205
+ "base",
1206
+ "br",
1207
+ "col",
1208
+ "embed",
1209
+ "hr",
1210
+ "img",
1211
+ "input",
1212
+ "link",
1213
+ "meta",
1214
+ "param",
1215
+ "source",
1216
+ "track",
1217
+ "wbr"
1218
+ ]);
1219
+ async function renderIntrinsicElement(tag, props, children) {
1220
+ const attrs = renderAttributes(props);
1221
+ if (VOID_ELEMENTS3.has(tag)) {
1222
+ return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1223
+ }
1224
+ const childrenHtml = await renderChildren(children, props);
1225
+ return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
1226
+ }
1227
+ function renderAttributes(props) {
1228
+ let attrs = "";
1229
+ for (const [key, value] of Object.entries(props)) {
1230
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
1231
+ continue;
1232
+ }
1233
+ const resolvedValue = readReactive2(value);
1234
+ if (resolvedValue == null || resolvedValue === false)
1235
+ continue;
1236
+ const attrName = key === "className" ? "class" : key;
1237
+ const finalName = attrName === "htmlFor" ? "for" : attrName;
1238
+ if (resolvedValue === true) {
1239
+ attrs += ` ${finalName}`;
1240
+ continue;
1241
+ }
1242
+ const attrValue = escapeHtml(String(resolvedValue));
1243
+ attrs += ` ${finalName}="${attrValue}"`;
1244
+ }
1245
+ return attrs;
1246
+ }
1247
+ async function renderChildren(children, props) {
1248
+ const dangerous = props.dangerouslySetInnerHTML;
1249
+ if (dangerous && typeof dangerous.__html === "string") {
1250
+ return dangerous.__html;
1251
+ }
1252
+ return renderToString(children);
1253
+ }
1254
+ function isSlots(children) {
1255
+ return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
1256
+ }
1257
+ async function renderForElement(element) {
1258
+ const props = element.props;
1259
+ const each = readReactive2(props.each);
1260
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1261
+ return "";
1262
+ }
1263
+ const rendered = await Promise.all(each.map((item, index) => renderToString(props.children(item, () => index))));
1264
+ return rendered.join("");
1265
+ }
1266
+ function resolveShowChildren2(element, value) {
1267
+ const children = element.props.children ?? element.children;
1268
+ if (typeof children === "function") {
1269
+ return children(value);
1270
+ }
1271
+ return children;
1272
+ }
1273
+ function readReactive2(value) {
1274
+ return isSignal(value) || isComputed(value) ? value.value : value;
1275
+ }
1276
+ // src/hydration/markers.ts
1277
+ var COMP_ID_ATTR = "data-sinwan-id";
1278
+ var COMP_ID_PREFIX = "c";
1279
+ var TEXT_MARKER_OPEN = "sinwan-t:";
1280
+ var TEXT_MARKER_CLOSE = "/sinwan-t";
1281
+ var EVENT_ATTR = "data-sinwan-ev";
1282
+ function compId(index) {
1283
+ return `${COMP_ID_PREFIX}${index}`;
1284
+ }
1285
+ function textMarkerOpen(index) {
1286
+ return `<!--${TEXT_MARKER_OPEN}${index}-->`;
1287
+ }
1288
+ function textMarkerCloseStr() {
1289
+ return `<!--${TEXT_MARKER_CLOSE}-->`;
1290
+ }
1291
+ function parseTextOpenMarker(node) {
1292
+ const data = node.data;
1293
+ if (data.startsWith(TEXT_MARKER_OPEN)) {
1294
+ const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
1295
+ return Number.isNaN(idx) ? -1 : idx;
1296
+ }
1297
+ return -1;
1298
+ }
1299
+ function isTextCloseMarker(node) {
1300
+ return node.data === TEXT_MARKER_CLOSE;
1301
+ }
1302
+
1303
+ // src/server/stream.ts
1304
+ function createHydratableStreamContext() {
1305
+ return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
1306
+ }
1307
+ var VOID_ELEMENTS4 = new Set([
1308
+ "area",
1309
+ "base",
1310
+ "br",
1311
+ "col",
1312
+ "embed",
1313
+ "hr",
1314
+ "img",
1315
+ "input",
1316
+ "link",
1317
+ "meta",
1318
+ "param",
1319
+ "source",
1320
+ "track",
1321
+ "wbr"
1322
+ ]);
1323
+ function streamPage(page, data) {
1324
+ const encoder = new TextEncoder;
1325
+ return new ReadableStream({
1326
+ async start(controller) {
1327
+ try {
1328
+ const element = await page(data);
1329
+ await streamNode(element, controller, encoder);
1330
+ controller.close();
1331
+ } catch (error) {
1332
+ controller.error(error);
1333
+ }
1334
+ }
1335
+ });
1336
+ }
1337
+ function streamHydratablePage(component, props) {
1338
+ const encoder = new TextEncoder;
1339
+ const ctx = createHydratableStreamContext();
1340
+ return new ReadableStream({
1341
+ async start(controller) {
1342
+ try {
1343
+ await streamHydratableComponent(component, props ?? {}, controller, encoder, ctx, true);
1344
+ controller.close();
1345
+ } catch (error) {
1346
+ controller.error(error);
1347
+ }
1348
+ }
1349
+ });
1350
+ }
1351
+ function streamHydratableNode(node) {
1352
+ const encoder = new TextEncoder;
1353
+ const ctx = createHydratableStreamContext();
1354
+ return new ReadableStream({
1355
+ async start(controller) {
1356
+ try {
1357
+ await streamHydratableNodeToController(node, controller, encoder, ctx);
1358
+ controller.close();
1359
+ } catch (error) {
1360
+ controller.error(error);
1361
+ }
1362
+ }
1363
+ });
1364
+ }
1365
+ async function streamNode(node, controller, encoder) {
1366
+ if (node == null || typeof node === "boolean") {
1367
+ return;
1368
+ }
1369
+ if (typeof node === "string") {
1370
+ controller.enqueue(encoder.encode(escapeHtml(node)));
1371
+ return;
1372
+ }
1373
+ if (typeof node === "number") {
1374
+ controller.enqueue(encoder.encode(String(node)));
1375
+ return;
1376
+ }
1377
+ if (node instanceof HtmlEscapedString) {
1378
+ controller.enqueue(encoder.encode(node.value));
1379
+ return;
1380
+ }
1381
+ if (isSignal(node) || isComputed(node)) {
1382
+ controller.enqueue(encoder.encode(escapeHtml(String(node.value))));
1383
+ return;
1384
+ }
1385
+ if (Array.isArray(node)) {
1386
+ for (const child of node) {
1387
+ await streamNode(child, controller, encoder);
1388
+ }
1389
+ return;
1390
+ }
1391
+ if (node instanceof Promise) {
1392
+ const resolved = await node;
1393
+ await streamElement(resolved, controller, encoder);
1394
+ return;
1395
+ }
1396
+ await streamElement(node, controller, encoder);
1397
+ }
1398
+ async function streamElement(element, controller, encoder) {
1399
+ const { tag, props, children } = element;
1400
+ if (isShowElement(element)) {
1401
+ const when = readReactive3(props.when);
1402
+ await streamNode(when ? resolveShowChildren3(element, when) : props.fallback, controller, encoder);
1403
+ return;
1404
+ }
1405
+ if (isForElement(element)) {
1406
+ await streamForElement(element, controller, encoder);
1407
+ return;
1408
+ }
1409
+ if (typeof tag === "function") {
1410
+ const result = await tag(props);
1411
+ await streamNode(result, controller, encoder);
1412
+ return;
1413
+ }
1414
+ if (typeof tag === "string") {
1415
+ await streamIntrinsicElement(tag, props, children, controller, encoder);
1416
+ return;
1417
+ }
1418
+ await streamNode(children, controller, encoder);
1419
+ }
1420
+ async function streamIntrinsicElement(tag, props, children, controller, encoder) {
1421
+ const attrs = renderAttributes2(props);
1422
+ const dangerous = props.dangerouslySetInnerHTML;
1423
+ if (VOID_ELEMENTS4.has(tag)) {
1424
+ const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1425
+ controller.enqueue(encoder.encode(html));
1426
+ return;
1427
+ }
1428
+ const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1429
+ controller.enqueue(encoder.encode(openTag));
1430
+ if (dangerous && typeof dangerous.__html === "string") {
1431
+ controller.enqueue(encoder.encode(dangerous.__html));
1432
+ } else {
1433
+ await streamNode(children, controller, encoder);
1434
+ }
1435
+ controller.enqueue(encoder.encode(`</${tag}>`));
1436
+ }
1437
+ function renderAttributes2(props) {
1438
+ let attrs = "";
1439
+ for (const [key, value] of Object.entries(props)) {
1440
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
1441
+ continue;
1442
+ }
1443
+ const resolvedValue = readReactive3(value);
1444
+ if (resolvedValue == null || resolvedValue === false)
1445
+ continue;
1446
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1447
+ if (resolvedValue === true) {
1448
+ attrs += ` ${attrName}`;
1449
+ continue;
1450
+ }
1451
+ const attrValue = escapeHtml(String(resolvedValue));
1452
+ attrs += ` ${attrName}="${attrValue}"`;
1453
+ }
1454
+ return attrs;
1455
+ }
1456
+ async function streamHydratableNodeToController(node, controller, encoder, ctx, isComponentRoot = false) {
1457
+ if (node == null || typeof node === "boolean") {
1458
+ return;
1459
+ }
1460
+ if (typeof node === "string") {
1461
+ enqueue(controller, encoder, escapeHtml(node));
1462
+ return;
1463
+ }
1464
+ if (typeof node === "number") {
1465
+ enqueue(controller, encoder, String(node));
1466
+ return;
1467
+ }
1468
+ if (node instanceof HtmlEscapedString) {
1469
+ enqueue(controller, encoder, node.value);
1470
+ return;
1471
+ }
1472
+ if (isSignal(node) || isComputed(node)) {
1473
+ const idx = ctx.textIndex++;
1474
+ enqueue(controller, encoder, `${textMarkerOpen(idx)}${escapeHtml(String(node.value))}${textMarkerCloseStr()}`);
1475
+ return;
1476
+ }
1477
+ if (Array.isArray(node)) {
1478
+ for (const child of node) {
1479
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
1480
+ }
1481
+ return;
1482
+ }
1483
+ if (node instanceof Promise) {
1484
+ await streamHydratableElement(await node, controller, encoder, ctx, isComponentRoot);
1485
+ return;
1486
+ }
1487
+ await streamHydratableElement(node, controller, encoder, ctx, isComponentRoot);
1488
+ }
1489
+ async function streamHydratableElement(element, controller, encoder, ctx, isComponentRoot) {
1490
+ const { tag, props, children } = element;
1491
+ if (tag === "") {
1492
+ for (const child of children) {
1493
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
1494
+ }
1495
+ return;
1496
+ }
1497
+ if (tag === Show || tag === For) {
1498
+ await streamHydratableElement(tag(props), controller, encoder, ctx, isComponentRoot);
1499
+ return;
1500
+ }
1501
+ if (isShowElement(element)) {
1502
+ const when = readReactive3(props.when);
1503
+ await streamHydratableNodeToController(when ? resolveShowChildren3(element, when) : props.fallback, controller, encoder, ctx, isComponentRoot);
1504
+ return;
1505
+ }
1506
+ if (isForElement(element)) {
1507
+ await streamHydratableForElement(element, controller, encoder, ctx);
1508
+ return;
1509
+ }
1510
+ if (typeof tag === "function") {
1511
+ await streamHydratableComponent(tag, props, controller, encoder, ctx, true);
1512
+ return;
1513
+ }
1514
+ if (typeof tag === "string") {
1515
+ await streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot);
1516
+ return;
1517
+ }
1518
+ await streamHydratableNodeToController(children, controller, encoder, ctx);
1519
+ }
1520
+ async function streamHydratableComponent(component, props, controller, encoder, ctx, isComponentRoot) {
1521
+ const parentInstance = getCurrentInstance();
1522
+ const instance = createComponentInstance(component, props, parentInstance);
1523
+ if (parentInstance) {
1524
+ parentInstance.children.push(instance);
1525
+ }
1526
+ const prev = setCurrentInstance(instance);
1527
+ try {
1528
+ const result = await component(props);
1529
+ await streamHydratableNodeToController(result, controller, encoder, ctx, isComponentRoot);
1530
+ } finally {
1531
+ setCurrentInstance(prev);
1532
+ }
1533
+ }
1534
+ async function streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot) {
1535
+ const attrs = renderHydratableAttributes(props, ctx, isComponentRoot);
1536
+ const dangerous = props.dangerouslySetInnerHTML;
1537
+ enqueue(controller, encoder, attrs ? `<${tag}${attrs}>` : `<${tag}>`);
1538
+ if (VOID_ELEMENTS4.has(tag)) {
1539
+ return;
1540
+ }
1541
+ if (dangerous && typeof dangerous.__html === "string") {
1542
+ enqueue(controller, encoder, dangerous.__html);
1543
+ } else {
1544
+ for (const child of children) {
1545
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
1546
+ }
1547
+ }
1548
+ enqueue(controller, encoder, `</${tag}>`);
1549
+ }
1550
+ async function streamHydratableForElement(element, controller, encoder, ctx) {
1551
+ const props = element.props;
1552
+ const each = readReactive3(props.each);
1553
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1554
+ return;
1555
+ }
1556
+ for (let index = 0;index < each.length; index++) {
1557
+ await streamHydratableNodeToController(props.children(each[index], () => index), controller, encoder, ctx);
1558
+ }
1559
+ }
1560
+ function renderHydratableAttributes(props, ctx, isComponentRoot) {
1561
+ let attrs = "";
1562
+ if (isComponentRoot) {
1563
+ attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
1564
+ }
1565
+ const eventParts = [];
1566
+ for (const [key, value] of Object.entries(props)) {
1567
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
1568
+ continue;
1569
+ }
1570
+ if (isEventProp(key)) {
1571
+ eventParts.push(`${toEventName(key)}:${ctx.eventIndex++}`);
1572
+ continue;
1573
+ }
1574
+ const resolvedValue = readReactive3(value);
1575
+ if (resolvedValue == null || resolvedValue === false)
1576
+ continue;
1577
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1578
+ if (resolvedValue === true) {
1579
+ attrs += ` ${attrName}`;
1580
+ continue;
1581
+ }
1582
+ attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
1583
+ }
1584
+ if (eventParts.length > 0) {
1585
+ attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1586
+ }
1587
+ return attrs;
1588
+ }
1589
+ function enqueue(controller, encoder, html) {
1590
+ controller.enqueue(encoder.encode(html));
1591
+ }
1592
+ async function streamForElement(element, controller, encoder) {
1593
+ const props = element.props;
1594
+ const each = readReactive3(props.each);
1595
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1596
+ return;
1597
+ }
1598
+ for (let index = 0;index < each.length; index++) {
1599
+ await streamNode(props.children(each[index], () => index), controller, encoder);
1600
+ }
1601
+ }
1602
+ function resolveShowChildren3(element, value) {
1603
+ const children = element.props.children ?? element.children;
1604
+ if (typeof children === "function") {
1605
+ return children(value);
1606
+ }
1607
+ return children;
1608
+ }
1609
+ function readReactive3(value) {
1610
+ return isSignal(value) || isComputed(value) ? value.value : value;
1611
+ }
1612
+ // src/server/hydration-markers.ts
1613
+ function createHydrationContext() {
1614
+ return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
1615
+ }
1616
+ async function renderToHydratableString(component, props) {
1617
+ const ctx = createHydrationContext();
1618
+ const mergedProps = props ?? {};
1619
+ const instance = createComponentInstance(component, mergedProps, null);
1620
+ const prev = setCurrentInstance(instance);
1621
+ try {
1622
+ const result = await component(mergedProps);
1623
+ if (result && typeof result === "object" && "tag" in result) {
1624
+ return renderElementH(result, ctx, true);
1625
+ }
1626
+ return renderNodeH(result, ctx);
1627
+ } finally {
1628
+ setCurrentInstance(prev);
1629
+ }
1630
+ }
1631
+ async function renderNodeToHydratableString(node) {
1632
+ const ctx = createHydrationContext();
1633
+ return renderNodeH(node, ctx);
1634
+ }
1635
+ var VOID_ELEMENTS5 = new Set([
1636
+ "area",
1637
+ "base",
1638
+ "br",
1639
+ "col",
1640
+ "embed",
1641
+ "hr",
1642
+ "img",
1643
+ "input",
1644
+ "link",
1645
+ "meta",
1646
+ "param",
1647
+ "source",
1648
+ "track",
1649
+ "wbr"
1650
+ ]);
1651
+ function renderNodeH(node, ctx) {
1652
+ if (node == null || typeof node === "boolean")
1653
+ return "";
1654
+ if (typeof node === "string")
1655
+ return escapeHtml(node);
1656
+ if (typeof node === "number")
1657
+ return String(node);
1658
+ if (node instanceof HtmlEscapedString)
1659
+ return node.value;
1660
+ if (isSignal(node) || isComputed(node)) {
1661
+ const value = node.value;
1662
+ const idx = ctx.textIndex++;
1663
+ return `${textMarkerOpen(idx)}${escapeHtml(String(value))}${textMarkerCloseStr()}`;
1664
+ }
1665
+ if (Array.isArray(node)) {
1666
+ return node.map((child) => renderNodeH(child, ctx)).join("");
1667
+ }
1668
+ if (node instanceof Promise) {
1669
+ return "";
1670
+ }
1671
+ if (typeof node === "object" && "tag" in node) {
1672
+ return renderElementH(node, ctx, false);
1673
+ }
1674
+ return escapeHtml(String(node));
1675
+ }
1676
+ function renderElementH(element, ctx, isComponentRoot) {
1677
+ const { tag, props, children } = element;
1678
+ if (tag === "") {
1679
+ return children.map((child) => renderNodeH(child, ctx)).join("");
1680
+ }
1681
+ if (tag === Show || tag === For) {
1682
+ return renderElementH(tag(props), ctx, isComponentRoot);
1683
+ }
1684
+ if (isShowElement(element)) {
1685
+ const when = readReactive4(props.when);
1686
+ const content = when ? resolveShowChildren4(element, when) : props.fallback;
1687
+ return renderNodeMaybeRoot(content, ctx, isComponentRoot);
1688
+ }
1689
+ if (isForElement(element)) {
1690
+ return renderForElementH(element, ctx);
1691
+ }
1692
+ if (typeof tag === "function") {
1693
+ return renderComponentH(tag, props, ctx);
1694
+ }
1695
+ if (typeof tag === "string") {
1696
+ return renderIntrinsicH(tag, props, children, ctx, isComponentRoot);
1697
+ }
1698
+ return children.map((child) => renderNodeH(child, ctx)).join("");
1699
+ }
1700
+ function renderComponentH(component, props, ctx) {
1701
+ const parentInstance = getCurrentInstance();
1702
+ const instance = createComponentInstance(component, props, parentInstance);
1703
+ if (parentInstance) {
1704
+ parentInstance.children.push(instance);
1705
+ }
1706
+ const prev = setCurrentInstance(instance);
1707
+ try {
1708
+ const result = component(props);
1709
+ if (result && typeof result === "object" && "tag" in result) {
1710
+ return renderElementH(result, ctx, true);
1711
+ }
1712
+ return renderNodeH(result, ctx);
1713
+ } finally {
1714
+ setCurrentInstance(prev);
1715
+ }
1716
+ }
1717
+ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
1718
+ let attrs = "";
1719
+ if (isComponentRoot) {
1720
+ attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
1721
+ }
1722
+ const eventParts = [];
1723
+ for (const [key, value] of Object.entries(props)) {
1724
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
1725
+ continue;
1726
+ }
1727
+ if (isEventProp(key)) {
1728
+ const eventName = toEventName(key);
1729
+ eventParts.push(`${eventName}:${ctx.eventIndex++}`);
1730
+ continue;
1731
+ }
1732
+ if (value == null || value === false)
1733
+ continue;
1734
+ let resolvedValue = value;
1735
+ if (isSignal(value) || isComputed(value)) {
1736
+ resolvedValue = value.value;
1737
+ }
1738
+ if (resolvedValue === true) {
1739
+ const attrName2 = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1740
+ attrs += ` ${attrName2}`;
1741
+ continue;
1742
+ }
1743
+ const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
1744
+ attrs += ` ${attrName}="${escapeHtml(String(resolvedValue))}"`;
1745
+ }
1746
+ if (eventParts.length > 0) {
1747
+ attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1748
+ }
1749
+ if (VOID_ELEMENTS5.has(tag)) {
1750
+ return `<${tag}${attrs}>`;
1751
+ }
1752
+ const dangerous = props.dangerouslySetInnerHTML;
1753
+ if (dangerous && typeof dangerous.__html === "string") {
1754
+ return `<${tag}${attrs}>${dangerous.__html}</${tag}>`;
1755
+ }
1756
+ const childrenHtml = children.map((child) => renderNodeH(child, ctx)).join("");
1757
+ return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
1758
+ }
1759
+ function renderNodeMaybeRoot(node, ctx, isComponentRoot) {
1760
+ if (isComponentRoot && node && typeof node === "object" && !Array.isArray(node) && "tag" in node) {
1761
+ return renderElementH(node, ctx, true);
1762
+ }
1763
+ return renderNodeH(node, ctx);
1764
+ }
1765
+ function renderForElementH(element, ctx) {
1766
+ const props = element.props;
1767
+ const each = readReactive4(props.each);
1768
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1769
+ return "";
1770
+ }
1771
+ return each.map((item, index) => renderNodeH(props.children(item, () => index), ctx)).join("");
1772
+ }
1773
+ function resolveShowChildren4(element, value) {
1774
+ const children = element.props.children ?? element.children;
1775
+ if (typeof children === "function") {
1776
+ return children(value);
1777
+ }
1778
+ return children;
1779
+ }
1780
+ function readReactive4(value) {
1781
+ return isSignal(value) || isComputed(value) ? value.value : value;
1782
+ }
1783
+ // src/reactivity/batch.ts
1784
+ var batchDepth = 0;
1785
+ function batch(fn) {
1786
+ batchDepth++;
1787
+ try {
1788
+ fn();
1789
+ } finally {
1790
+ batchDepth--;
1791
+ if (batchDepth === 0) {
1792
+ flushSync();
1793
+ }
1794
+ }
1795
+ }
1796
+ // src/component/lifecycle.ts
1797
+ function onMounted(fn) {
1798
+ const instance = getCurrentInstance();
1799
+ if (!instance) {
1800
+ throw new Error("onMounted() called outside of component setup.");
1801
+ }
1802
+ instance._mountedHooks.push(() => withInstance(instance, fn));
1803
+ }
1804
+ function onUnmounted(fn) {
1805
+ const instance = getCurrentInstance();
1806
+ if (!instance) {
1807
+ throw new Error("onUnmounted() called outside of component setup.");
1808
+ }
1809
+ instance._unmountedHooks.push(() => withInstance(instance, fn));
1810
+ }
1811
+ function onUpdated(fn) {
1812
+ const instance = getCurrentInstance();
1813
+ if (!instance) {
1814
+ throw new Error("onUpdated() called outside of component setup.");
1815
+ }
1816
+ instance._updatedHooks.push(() => withInstance(instance, fn));
1817
+ }
1818
+ function onError(fn) {
1819
+ const instance = getCurrentInstance();
1820
+ if (!instance) {
1821
+ throw new Error("onError() called outside of component setup.");
1822
+ }
1823
+ instance._errorHooks.push((err) => withInstance(instance, () => fn(err)));
1824
+ }
1825
+ // src/component/create.ts
1826
+ function createComponent(fn) {
1827
+ const component = (props) => fn(props);
1828
+ component._SinwanComponent = true;
1829
+ component._displayName = fn.name || "AnonymousComponent";
1830
+ return component;
1831
+ }
1832
+ function createPage(fn) {
1833
+ const page = (data) => fn(data);
1834
+ page._SinwanPage = true;
1835
+ page._displayName = fn.name || "AnonymousPage";
1836
+ return page;
1837
+ }
1838
+ function createLayout(fn) {
1839
+ return createComponent(fn);
1840
+ }
1841
+ // src/component/provide-inject.ts
1842
+ function provide(key, value) {
1843
+ const instance = getCurrentInstance();
1844
+ if (!instance) {
1845
+ throw new Error("provide() called outside of component setup.");
1846
+ }
1847
+ instance.provides[key] = value;
1848
+ }
1849
+ function inject(key, defaultValue) {
1850
+ const instance = getCurrentInstance();
1851
+ if (!instance) {
1852
+ throw new Error("inject() called outside of component setup.");
1853
+ }
1854
+ if (key in instance.provides) {
1855
+ return instance.provides[key];
1856
+ }
1857
+ if (arguments.length >= 2) {
1858
+ return defaultValue;
1859
+ }
1860
+ console.warn(`[Sinwan] inject() key "${String(key)}" not found and no default provided.`);
1861
+ return;
1862
+ }
1863
+ // src/hydration/walk.ts
1864
+ function advance(cursor) {
1865
+ const node = cursor.current;
1866
+ if (node) {
1867
+ cursor.current = node.nextSibling;
1868
+ }
1869
+ return node;
1870
+ }
1871
+ function hydrateNode(node, cursor) {
1872
+ if (node == null || typeof node === "boolean") {
1873
+ const textNode2 = advance(cursor);
1874
+ return { type: "text", node: textNode2 ?? document.createTextNode("") };
1875
+ }
1876
+ if (typeof node === "string") {
1877
+ const textNode2 = advance(cursor);
1878
+ return { type: "text", node: textNode2 };
1879
+ }
1880
+ if (typeof node === "number") {
1881
+ const textNode2 = advance(cursor);
1882
+ return { type: "text", node: textNode2 };
1883
+ }
1884
+ if (node instanceof HtmlEscapedString) {
1885
+ const textNode2 = advance(cursor);
1886
+ return { type: "text", node: textNode2 };
1887
+ }
1888
+ if (isSignal(node) || isComputed(node)) {
1889
+ return hydrateReactiveText(node, cursor);
1890
+ }
1891
+ if (Array.isArray(node)) {
1892
+ return hydrateArray(node, cursor);
1336
1893
  }
1337
1894
  if (typeof node === "object" && node !== null && "tag" in node) {
1338
1895
  return hydrateElement(node, cursor);
@@ -1342,6 +1899,7 @@ function hydrateNode(node, cursor) {
1342
1899
  }
1343
1900
  function hydrateReactiveText(reactive, cursor) {
1344
1901
  const openComment = cursor.current;
1902
+ const owner = getCurrentInstance();
1345
1903
  if (openComment && openComment.nodeType === 8 && parseTextOpenMarker(openComment) >= 0) {
1346
1904
  advance(cursor);
1347
1905
  const textNode2 = advance(cursor);
@@ -1349,21 +1907,36 @@ function hydrateReactiveText(reactive, cursor) {
1349
1907
  if (closeComment && closeComment.nodeType === 8 && isTextCloseMarker(closeComment)) {
1350
1908
  advance(cursor);
1351
1909
  }
1910
+ let initialized2 = false;
1352
1911
  const dispose2 = effect(() => {
1353
1912
  textNode2.data = String(reactive.value);
1913
+ if (initialized2) {
1914
+ queueUpdatedHooks(owner);
1915
+ }
1916
+ initialized2 = true;
1354
1917
  });
1355
1918
  return { type: "reactive-text", node: textNode2, dispose: dispose2 };
1356
1919
  }
1357
1920
  const textNode = advance(cursor);
1358
1921
  if (textNode) {
1922
+ let initialized2 = false;
1359
1923
  const dispose2 = effect(() => {
1360
1924
  textNode.data = String(reactive.value);
1925
+ if (initialized2) {
1926
+ queueUpdatedHooks(owner);
1927
+ }
1928
+ initialized2 = true;
1361
1929
  });
1362
1930
  return { type: "reactive-text", node: textNode, dispose: dispose2 };
1363
1931
  }
1364
1932
  const newText = document.createTextNode(String(reactive.value));
1933
+ let initialized = false;
1365
1934
  const dispose = effect(() => {
1366
1935
  newText.data = String(reactive.value);
1936
+ if (initialized) {
1937
+ queueUpdatedHooks(owner);
1938
+ }
1939
+ initialized = true;
1367
1940
  });
1368
1941
  return { type: "reactive-text", node: newText, dispose };
1369
1942
  }
@@ -1372,6 +1945,12 @@ function hydrateElement(element, cursor) {
1372
1945
  if (tag === "") {
1373
1946
  return hydrateArray(children, cursor);
1374
1947
  }
1948
+ if (tag === Show || tag === For) {
1949
+ return hydrateElement(tag(props), cursor);
1950
+ }
1951
+ if (isShowElement(element) || isForElement(element)) {
1952
+ return hydrateControlFlow(element, cursor);
1953
+ }
1375
1954
  if (typeof tag === "function") {
1376
1955
  return hydrateComponent(tag, props, cursor);
1377
1956
  }
@@ -1390,6 +1969,7 @@ function hydrateIntrinsic(tag, props, children, cursor) {
1390
1969
  el.removeAttribute("data-sinwan-ev");
1391
1970
  const attrDisposers = hydrateAttributes(el, props);
1392
1971
  const eventCleanups = bindEvents(el, props);
1972
+ const refCleanup = applyRef2(el, props.ref);
1393
1973
  const childCursor = {
1394
1974
  parent: el,
1395
1975
  current: el.firstChild
@@ -1403,15 +1983,18 @@ function hydrateIntrinsic(tag, props, children, cursor) {
1403
1983
  node: el,
1404
1984
  children: mountedChildren,
1405
1985
  eventCleanups,
1406
- attrDisposers
1986
+ attrDisposers,
1987
+ refCleanup
1407
1988
  };
1408
1989
  }
1409
1990
  function hydrateAttributes(el, props) {
1410
1991
  const disposers = [];
1992
+ const owner = getCurrentInstance();
1411
1993
  for (const [key, value] of Object.entries(props)) {
1412
1994
  if (key === "children" || key === "key" || key === "ref" || isEventProp(key))
1413
1995
  continue;
1414
1996
  if (isSignal(value) || isComputed(value)) {
1997
+ let initialized = false;
1415
1998
  const dispose = effect(() => {
1416
1999
  const v = value.value;
1417
2000
  const attrName = key === "className" ? "class" : key === "htmlFor" ? "for" : key;
@@ -1422,12 +2005,63 @@ function hydrateAttributes(el, props) {
1422
2005
  } else {
1423
2006
  el.setAttribute(attrName, String(v));
1424
2007
  }
2008
+ if (initialized) {
2009
+ queueUpdatedHooks(owner);
2010
+ }
2011
+ initialized = true;
1425
2012
  });
1426
2013
  disposers.push(dispose);
1427
2014
  }
1428
2015
  }
1429
2016
  return disposers;
1430
2017
  }
2018
+ function hydrateControlFlow(element, cursor) {
2019
+ if (isShowElement(element)) {
2020
+ const when = readReactive5(element.props.when);
2021
+ const content = when ? resolveShowChildren5(element, when) : element.props.fallback;
2022
+ return hydrateContent(content, cursor);
2023
+ }
2024
+ if (isForElement(element)) {
2025
+ const props = element.props;
2026
+ const items = readReactive5(props.each);
2027
+ const children = Array.isArray(items) && typeof props.children === "function" ? items.map((item, index) => props.children(item, () => index)) : [];
2028
+ return hydrateArray(children, cursor);
2029
+ }
2030
+ return hydrateArray(element.children, cursor);
2031
+ }
2032
+ function hydrateContent(content, cursor) {
2033
+ if (content == null || typeof content === "boolean") {
2034
+ return hydrateArray([], cursor);
2035
+ }
2036
+ return Array.isArray(content) ? hydrateArray(content, cursor) : hydrateNode(content, cursor);
2037
+ }
2038
+ function resolveShowChildren5(element, value) {
2039
+ const children = element.props.children ?? element.children;
2040
+ if (typeof children === "function") {
2041
+ return children(value);
2042
+ }
2043
+ return children;
2044
+ }
2045
+ function readReactive5(value) {
2046
+ return isSignal(value) || isComputed(value) ? value.value : value;
2047
+ }
2048
+ function applyRef2(el, ref) {
2049
+ const value = ref;
2050
+ if (!value) {
2051
+ return null;
2052
+ }
2053
+ if (typeof value === "function") {
2054
+ value(el);
2055
+ return () => value(null);
2056
+ }
2057
+ if (typeof value === "object" && "current" in value) {
2058
+ value.current = el;
2059
+ return () => {
2060
+ value.current = null;
2061
+ };
2062
+ }
2063
+ return null;
2064
+ }
1431
2065
  function hydrateComponent(component, props, cursor) {
1432
2066
  const parentInstance = getCurrentInstance();
1433
2067
  const instance = createComponentInstance(component, props, parentInstance);
@@ -1516,8 +2150,12 @@ function hydrate(component, container, props) {
1516
2150
  export {
1517
2151
  unmountNode,
1518
2152
  streamPage,
2153
+ streamHydratablePage,
2154
+ streamHydratableNode,
1519
2155
  signal,
2156
+ setDOMOps,
1520
2157
  safeHtml,
2158
+ resetDOMOps,
1521
2159
  renderToString,
1522
2160
  renderToHydratableString,
1523
2161
  renderPage,
@@ -1547,14 +2185,17 @@ export {
1547
2185
  getCurrentInstance,
1548
2186
  escapeHtml,
1549
2187
  effect,
2188
+ domOps,
1550
2189
  createPage,
1551
2190
  createLayout,
1552
2191
  createComponent,
1553
2192
  computed,
1554
2193
  batch,
2194
+ Show,
1555
2195
  HtmlEscapedString,
1556
- Fragment
2196
+ Fragment,
2197
+ For
1557
2198
  };
1558
2199
 
1559
- //# debugId=37C387A0B7C368DE64756E2164756E21
2200
+ //# debugId=14DE79AF90B411D764756E2164756E21
1560
2201
  //# sourceMappingURL=index.development.js.map