sinwan 0.1.0 → 1.1.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 (90) hide show
  1. package/README.md +66 -39
  2. package/dist/cjs/index.development.js +2373 -842
  3. package/dist/cjs/index.development.js.map +22 -18
  4. package/dist/cjs/index.production.min.js +2 -2
  5. package/dist/cjs/index.production.min.js.map +22 -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 +1581 -0
  15. package/dist/cjs/renderer/index.development.js.map +25 -0
  16. package/dist/cjs/renderer/index.production.min.js +3 -0
  17. package/dist/cjs/renderer/index.production.min.js.map +25 -0
  18. package/dist/cjs/server/index.development.js +1210 -362
  19. package/dist/cjs/server/index.development.js.map +13 -10
  20. package/dist/cjs/server/index.production.min.js +2 -2
  21. package/dist/cjs/server/index.production.min.js.map +13 -10
  22. package/dist/component/control-flow.d.ts +71 -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 +2253 -744
  33. package/dist/esm/index.development.js.map +22 -18
  34. package/dist/esm/index.production.min.js +2 -2
  35. package/dist/esm/index.production.min.js.map +22 -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 +1530 -0
  45. package/dist/esm/renderer/index.development.js.map +25 -0
  46. package/dist/esm/renderer/index.production.min.js +4 -0
  47. package/dist/esm/renderer/index.production.min.js.map +25 -0
  48. package/dist/esm/server/index.development.js +1210 -362
  49. package/dist/esm/server/index.development.js.map +13 -10
  50. package/dist/esm/server/index.production.min.js +2 -2
  51. package/dist/esm/server/index.production.min.js.map +13 -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 +11 -0
  60. package/dist/renderer/attributes.d.ts.map +1 -1
  61. package/dist/renderer/dom-ops.d.ts +4 -1
  62. package/dist/renderer/dom-ops.d.ts.map +1 -1
  63. package/dist/renderer/index.d.ts +2 -2
  64. package/dist/renderer/index.d.ts.map +1 -1
  65. package/dist/renderer/mount.d.ts +2 -5
  66. package/dist/renderer/mount.d.ts.map +1 -1
  67. package/dist/renderer/render-children.d.ts +2 -2
  68. package/dist/renderer/render-children.d.ts.map +1 -1
  69. package/dist/renderer/render-control-flow.d.ts +13 -0
  70. package/dist/renderer/render-control-flow.d.ts.map +1 -0
  71. package/dist/renderer/render-element.d.ts +1 -1
  72. package/dist/renderer/render-element.d.ts.map +1 -1
  73. package/dist/renderer/types.d.ts +10 -1
  74. package/dist/renderer/types.d.ts.map +1 -1
  75. package/dist/renderer/unmount.d.ts +20 -0
  76. package/dist/renderer/unmount.d.ts.map +1 -0
  77. package/dist/renderer.d.ts +1 -0
  78. package/dist/renderer.js +7 -0
  79. package/dist/renderer.mjs +4 -0
  80. package/dist/server/attribute-utils.d.ts +2 -0
  81. package/dist/server/attribute-utils.d.ts.map +1 -0
  82. package/dist/server/hydration-markers.d.ts.map +1 -1
  83. package/dist/server/index.d.ts +1 -1
  84. package/dist/server/index.d.ts.map +1 -1
  85. package/dist/server/renderer.d.ts.map +1 -1
  86. package/dist/server/stream.d.ts +9 -1
  87. package/dist/server/stream.d.ts.map +1 -1
  88. package/dist/types.d.ts +8 -2
  89. package/dist/types.d.ts.map +1 -1
  90. package/package.json +19 -5
@@ -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
  }
@@ -109,221 +99,37 @@ function isSafeHtml(value) {
109
99
  return value instanceof HtmlEscapedString;
110
100
  }
111
101
 
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") {
102
+ // src/server/attribute-utils.ts
103
+ var PROP_ALIASES = {
104
+ className: "class",
105
+ htmlFor: "for",
106
+ tabIndex: "tabindex",
107
+ crossOrigin: "crossorigin"
108
+ };
109
+ function renderServerAttribute(key, value) {
110
+ const attrName = PROP_ALIASES[key] ?? key;
111
+ if (value == null || value === false) {
134
112
  return "";
135
113
  }
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);
270
- }
271
- return;
272
- }
273
- if (node instanceof Promise) {
274
- const resolved = await node;
275
- await streamElement(resolved, controller, encoder);
276
- return;
114
+ if (value === true) {
115
+ return ` ${attrName}`;
277
116
  }
278
- await streamElement(node, controller, encoder);
117
+ const attrValue = attrName === "class" && typeof value === "object" ? stringifyClass(value) : attrName === "style" && typeof value === "object" ? stringifyStyle(value) : String(value);
118
+ return ` ${attrName}="${escapeHtml(attrValue)}"`;
279
119
  }
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;
120
+ function stringifyClass(value) {
121
+ if (Array.isArray(value)) {
122
+ return value.filter(Boolean).join(" ");
290
123
  }
291
- await streamNode(children, controller, encoder);
124
+ return Object.entries(value).filter(([, enabled]) => Boolean(enabled)).map(([name]) => name).join(" ");
292
125
  }
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}>`));
126
+ function stringifyStyle(value) {
127
+ return Object.entries(value).filter(([, val]) => val != null && val !== false).map(([prop, val]) => `${toKebabCase(prop)}:${String(val)}`).join(";");
309
128
  }
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}"`;
324
- }
325
- return attrs;
129
+ function toKebabCase(value) {
130
+ return value.includes("-") ? value : value.replace(/[A-Z]/g, (char) => `-${char.toLowerCase()}`);
326
131
  }
132
+
327
133
  // src/reactivity/scheduler.ts
328
134
  var pendingEffects = new Set;
329
135
  var flushScheduled = false;
@@ -566,79 +372,68 @@ function isComputed(value) {
566
372
  return value != null && typeof value === "object" && COMPUTED_BRAND in value;
567
373
  }
568
374
 
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}-->`;
375
+ // src/renderer/dom-ops.ts
376
+ function createDefaultDOMOps() {
377
+ return {
378
+ createElement(tag) {
379
+ return document.createElement(tag);
380
+ },
381
+ createElementNS(namespace, tag) {
382
+ return document.createElementNS(namespace, tag);
383
+ },
384
+ createTextNode(text) {
385
+ return document.createTextNode(text);
386
+ },
387
+ createComment(text) {
388
+ return document.createComment(text);
389
+ },
390
+ setAttribute(el, key, value) {
391
+ el.setAttribute(key, value);
392
+ },
393
+ removeAttribute(el, key) {
394
+ el.removeAttribute(key);
395
+ },
396
+ setProperty(el, key, value) {
397
+ el[key] = value;
398
+ },
399
+ insertBefore(parent, child, anchor) {
400
+ parent.insertBefore(child, anchor);
401
+ },
402
+ appendChild(parent, child) {
403
+ parent.appendChild(child);
404
+ },
405
+ remove(node) {
406
+ node.parentNode?.removeChild(node);
407
+ },
408
+ setTextContent(node, text) {
409
+ node.data = text;
410
+ },
411
+ addEventListener(el, event, handler) {
412
+ el.addEventListener(event, handler);
413
+ },
414
+ removeEventListener(el, event, handler) {
415
+ el.removeEventListener(event, handler);
416
+ },
417
+ parentNode(node) {
418
+ return node.parentNode;
419
+ },
420
+ nextSibling(node) {
421
+ return node.nextSibling;
422
+ }
423
+ };
580
424
  }
581
- function textMarkerCloseStr() {
582
- return `<!--${TEXT_MARKER_CLOSE}-->`;
425
+ var defaultDOMOps = createDefaultDOMOps();
426
+ var domOps = { ...defaultDOMOps };
427
+ function setDOMOps(overrides) {
428
+ Object.assign(domOps, overrides);
583
429
  }
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;
430
+ function resetDOMOps() {
431
+ for (const key of Object.keys(domOps)) {
432
+ delete domOps[key];
589
433
  }
590
- return -1;
591
- }
592
- function isTextCloseMarker(node) {
593
- return node.data === TEXT_MARKER_CLOSE;
434
+ Object.assign(domOps, defaultDOMOps);
594
435
  }
595
436
 
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
437
  // src/renderer/events.ts
643
438
  function isEventProp(key) {
644
439
  return key.length > 2 && key[0] === "o" && key[1] === "n" && key[2] >= "A" && key[2] <= "Z";
@@ -665,77 +460,991 @@ function bindEvents(el, props) {
665
460
  }
666
461
  return cleanups;
667
462
  }
668
-
669
- // src/component/instance.ts
670
- var uidCounter = 0;
671
- function createComponentInstance(component, props, parent) {
463
+ // src/reactivity/batch.ts
464
+ var batchDepth = 0;
465
+ function batch(fn) {
466
+ batchDepth++;
467
+ try {
468
+ fn();
469
+ } finally {
470
+ batchDepth--;
471
+ if (batchDepth === 0) {
472
+ flushSync();
473
+ }
474
+ }
475
+ }
476
+ // src/component/control-flow.ts
477
+ var SHOW_TYPE = Symbol.for("Sinwan.Show");
478
+ var FOR_TYPE = Symbol.for("Sinwan.For");
479
+ var SWITCH_TYPE = Symbol.for("Sinwan.Switch");
480
+ var MATCH_TYPE = Symbol.for("Sinwan.Match");
481
+ var INDEX_TYPE = Symbol.for("Sinwan.Index");
482
+ var KEY_TYPE = Symbol.for("Sinwan.Key");
483
+ var DYNAMIC_TYPE = Symbol.for("Sinwan.Dynamic");
484
+ var PORTAL_TYPE = Symbol.for("Sinwan.Portal");
485
+ function Show(props) {
672
486
  return {
673
- uid: uidCounter++,
674
- component,
487
+ tag: SHOW_TYPE,
675
488
  props,
676
- element: null,
677
- parent,
678
- children: [],
679
- effects: [],
680
- _mountedHooks: [],
681
- _unmountedHooks: [],
682
- _updatedHooks: [],
683
- _errorHooks: [],
684
- provides: parent ? Object.create(parent.provides) : Object.create(null),
685
- isMounted: false,
686
- isUnmounted: false
489
+ children: []
687
490
  };
688
491
  }
689
- var currentInstance = null;
690
- function getCurrentInstance() {
691
- return currentInstance;
492
+ function For(props) {
493
+ return {
494
+ tag: FOR_TYPE,
495
+ props,
496
+ children: []
497
+ };
692
498
  }
693
- function setCurrentInstance(instance) {
499
+ function Switch(props) {
500
+ return {
501
+ tag: SWITCH_TYPE,
502
+ props,
503
+ children: []
504
+ };
505
+ }
506
+ function Match(props) {
507
+ return {
508
+ tag: MATCH_TYPE,
509
+ props,
510
+ children: []
511
+ };
512
+ }
513
+ function Index(props) {
514
+ return {
515
+ tag: INDEX_TYPE,
516
+ props,
517
+ children: []
518
+ };
519
+ }
520
+ function Key(props) {
521
+ return {
522
+ tag: KEY_TYPE,
523
+ props,
524
+ children: []
525
+ };
526
+ }
527
+ function Dynamic(props) {
528
+ return {
529
+ tag: DYNAMIC_TYPE,
530
+ props,
531
+ children: []
532
+ };
533
+ }
534
+ function Visible(props) {
535
+ const {
536
+ when,
537
+ as = "span",
538
+ style,
539
+ children,
540
+ ...rest
541
+ } = props;
542
+ const visibleStyle = computed(() => {
543
+ const base = readReactive(style);
544
+ const visible = Boolean(readReactive(when));
545
+ if (typeof base === "string") {
546
+ return visible ? base : appendHiddenDisplay(base);
547
+ }
548
+ const styleObject = base && typeof base === "object" ? { ...base } : {};
549
+ styleObject.display = visible ? styleObject.display : "none";
550
+ return styleObject;
551
+ });
552
+ return {
553
+ tag: as,
554
+ props: {
555
+ ...rest,
556
+ style: visibleStyle,
557
+ children
558
+ },
559
+ children: normalizeChildren2(children)
560
+ };
561
+ }
562
+ function Portal(props) {
563
+ return {
564
+ tag: PORTAL_TYPE,
565
+ props,
566
+ children: []
567
+ };
568
+ }
569
+ function isShowElement(element) {
570
+ return element.tag === SHOW_TYPE;
571
+ }
572
+ function isForElement(element) {
573
+ return element.tag === FOR_TYPE;
574
+ }
575
+ function isSwitchElement(element) {
576
+ return element.tag === SWITCH_TYPE;
577
+ }
578
+ function isMatchElement(element) {
579
+ return element.tag === MATCH_TYPE;
580
+ }
581
+ function isIndexElement(element) {
582
+ return element.tag === INDEX_TYPE;
583
+ }
584
+ function isKeyElement(element) {
585
+ return element.tag === KEY_TYPE;
586
+ }
587
+ function isDynamicElement(element) {
588
+ return element.tag === DYNAMIC_TYPE;
589
+ }
590
+ function isPortalElement(element) {
591
+ return element.tag === PORTAL_TYPE;
592
+ }
593
+ function normalizeChildren2(children) {
594
+ if (children == null || typeof children === "boolean") {
595
+ return [];
596
+ }
597
+ return Array.isArray(children) ? children : [children];
598
+ }
599
+ function readReactive(value) {
600
+ return isSignal(value) || isComputed(value) ? value.value : value;
601
+ }
602
+ function appendHiddenDisplay(style) {
603
+ const trimmed = style.trim();
604
+ const separator = trimmed.length > 0 && !trimmed.endsWith(";") ? ";" : "";
605
+ return `${trimmed}${separator}display:none`;
606
+ }
607
+
608
+ // src/server/renderer.ts
609
+ var componentCache = new WeakMap;
610
+ var pageRegistry = new Map;
611
+ function registerPage(name, page) {
612
+ pageRegistry.set(name, page);
613
+ }
614
+ function getPage(name) {
615
+ return pageRegistry.get(name);
616
+ }
617
+ function hasPage(name) {
618
+ return pageRegistry.has(name);
619
+ }
620
+ async function renderPage(name, data) {
621
+ const page = getPage(name);
622
+ if (!page) {
623
+ throw new Error(`Page "${name}" not found in registry`);
624
+ }
625
+ const element = await page(data);
626
+ return renderToString(element);
627
+ }
628
+ async function renderToString(node) {
629
+ if (node == null || typeof node === "boolean") {
630
+ return "";
631
+ }
632
+ if (typeof node === "string") {
633
+ return escapeHtml(node);
634
+ }
635
+ if (typeof node === "number") {
636
+ return String(node);
637
+ }
638
+ if (node instanceof HtmlEscapedString) {
639
+ return node.value;
640
+ }
641
+ if (isSignal(node) || isComputed(node)) {
642
+ return escapeHtml(String(node.value));
643
+ }
644
+ if (Array.isArray(node)) {
645
+ const results = await Promise.all(node.map((child) => renderToString(child)));
646
+ return results.join("");
647
+ }
648
+ if (node instanceof Promise) {
649
+ return renderElement(await node);
650
+ }
651
+ return renderElement(node);
652
+ }
653
+ async function renderElement(element) {
654
+ const { tag, props, children } = element;
655
+ if (tag === Show || tag === For || tag === Switch || tag === Index || tag === Key || tag === Dynamic || tag === Portal) {
656
+ return renderElement(tag(props));
657
+ }
658
+ if (tag === Visible) {
659
+ return renderElement(tag(props));
660
+ }
661
+ if (isShowElement(element)) {
662
+ const when = readReactive2(props.when);
663
+ return renderToString(when ? resolveShowChildren(element, when) : props.fallback);
664
+ }
665
+ if (isForElement(element)) {
666
+ return renderForElement(element);
667
+ }
668
+ if (isSwitchElement(element)) {
669
+ return renderToString(resolveSwitchContent(element));
670
+ }
671
+ if (isMatchElement(element)) {
672
+ const when = readReactive2(props.when);
673
+ return renderToString(when ? resolveMatchChildren(element, when) : null);
674
+ }
675
+ if (isIndexElement(element)) {
676
+ return renderIndexElement(element);
677
+ }
678
+ if (isKeyElement(element)) {
679
+ const key = readReactive2(props.when);
680
+ return renderToString(resolveKeyChildren(element, key));
681
+ }
682
+ if (isDynamicElement(element)) {
683
+ const tag2 = readReactive2(props.component);
684
+ const dynamic = createDynamicElement(element, tag2);
685
+ return dynamic ? renderElement(dynamic) : "";
686
+ }
687
+ if (isPortalElement(element)) {
688
+ return "";
689
+ }
690
+ if (typeof tag === "function") {
691
+ const result = await tag(props);
692
+ return renderToString(result);
693
+ }
694
+ if (typeof tag === "string") {
695
+ return renderIntrinsicElement(tag, props, children);
696
+ }
697
+ return renderToString(children);
698
+ }
699
+ var VOID_ELEMENTS2 = new Set([
700
+ "area",
701
+ "base",
702
+ "br",
703
+ "col",
704
+ "embed",
705
+ "hr",
706
+ "img",
707
+ "input",
708
+ "link",
709
+ "meta",
710
+ "param",
711
+ "source",
712
+ "track",
713
+ "wbr"
714
+ ]);
715
+ async function renderIntrinsicElement(tag, props, children) {
716
+ const attrs = renderAttributes(props);
717
+ if (VOID_ELEMENTS2.has(tag)) {
718
+ return attrs ? `<${tag}${attrs}>` : `<${tag}>`;
719
+ }
720
+ const childrenHtml = await renderChildren(children, props);
721
+ return attrs ? `<${tag}${attrs}>${childrenHtml}</${tag}>` : `<${tag}>${childrenHtml}</${tag}>`;
722
+ }
723
+ function renderAttributes(props) {
724
+ let attrs = "";
725
+ for (const [key, value] of Object.entries(props)) {
726
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
727
+ continue;
728
+ }
729
+ const resolvedValue = readReactive2(value);
730
+ if (resolvedValue == null || resolvedValue === false)
731
+ continue;
732
+ attrs += renderServerAttribute(key, resolvedValue);
733
+ }
734
+ return attrs;
735
+ }
736
+ async function renderChildren(children, props) {
737
+ const dangerous = props.dangerouslySetInnerHTML;
738
+ if (dangerous && typeof dangerous.__html === "string") {
739
+ return dangerous.__html;
740
+ }
741
+ return renderToString(children);
742
+ }
743
+ function isSlots(children) {
744
+ return children != null && typeof children === "object" && !Array.isArray(children) && !(children instanceof HtmlEscapedString);
745
+ }
746
+ async function renderForElement(element) {
747
+ const props = element.props;
748
+ const each = readReactive2(props.each);
749
+ if (!Array.isArray(each) || typeof props.children !== "function") {
750
+ return props.fallback ? renderToString(props.fallback) : "";
751
+ }
752
+ if (each.length === 0) {
753
+ return props.fallback ? renderToString(props.fallback) : "";
754
+ }
755
+ const rendered = await Promise.all(each.map((item, index) => renderToString(props.children(item, () => index))));
756
+ return rendered.join("");
757
+ }
758
+ async function renderIndexElement(element) {
759
+ const props = element.props;
760
+ const each = readReactive2(props.each);
761
+ if (!Array.isArray(each) || typeof props.children !== "function") {
762
+ return props.fallback ? renderToString(props.fallback) : "";
763
+ }
764
+ if (each.length === 0) {
765
+ return props.fallback ? renderToString(props.fallback) : "";
766
+ }
767
+ const rendered = await Promise.all(each.map((item, index) => renderToString(props.children(() => item, index))));
768
+ return rendered.join("");
769
+ }
770
+ function resolveSwitchContent(element) {
771
+ const props = element.props;
772
+ const children = normalizeContent(props.children ?? element.children);
773
+ for (const child of children) {
774
+ const match = getMatchElement(child);
775
+ if (!match) {
776
+ continue;
777
+ }
778
+ const when = readReactive2(match.props.when);
779
+ if (when) {
780
+ return resolveMatchChildren(match, when);
781
+ }
782
+ }
783
+ return props.fallback;
784
+ }
785
+ function resolveMatchChildren(element, value) {
786
+ const children = element.props.children ?? element.children;
787
+ if (typeof children === "function") {
788
+ return children(value);
789
+ }
790
+ return children;
791
+ }
792
+ function resolveKeyChildren(element, value) {
793
+ const children = element.props.children ?? element.children;
794
+ if (typeof children === "function") {
795
+ return children(value);
796
+ }
797
+ return children;
798
+ }
799
+ function createDynamicElement(element, tag) {
800
+ if (typeof tag !== "string" && typeof tag !== "function") {
801
+ return null;
802
+ }
803
+ const { component, ...props } = element.props;
804
+ const children = normalizeContent(props.children ?? element.children);
805
+ return {
806
+ tag,
807
+ props,
808
+ children
809
+ };
810
+ }
811
+ function resolveShowChildren(element, value) {
812
+ const children = element.props.children ?? element.children;
813
+ if (typeof children === "function") {
814
+ return children(value);
815
+ }
816
+ return children;
817
+ }
818
+ function readReactive2(value) {
819
+ return isSignal(value) || isComputed(value) ? value.value : value;
820
+ }
821
+ function normalizeContent(content) {
822
+ if (content == null || typeof content === "boolean") {
823
+ return [];
824
+ }
825
+ return Array.isArray(content) ? content : [content];
826
+ }
827
+ function isElementLike(value) {
828
+ return value != null && typeof value === "object" && "tag" in value;
829
+ }
830
+ function getMatchElement(value) {
831
+ if (!isElementLike(value)) {
832
+ return null;
833
+ }
834
+ if (isMatchElement(value)) {
835
+ return value;
836
+ }
837
+ return value.tag === Match ? Match(value.props) : null;
838
+ }
839
+ // src/component/instance.ts
840
+ var uidCounter = 0;
841
+ function createComponentInstance(component, props, parent) {
842
+ return {
843
+ uid: uidCounter++,
844
+ component,
845
+ props,
846
+ element: null,
847
+ parent,
848
+ children: [],
849
+ effects: [],
850
+ _mountedHooks: [],
851
+ _unmountedHooks: [],
852
+ _updatedHooks: [],
853
+ _errorHooks: [],
854
+ provides: parent ? Object.create(parent.provides) : Object.create(null),
855
+ isMounted: false,
856
+ isUnmounted: false
857
+ };
858
+ }
859
+ var currentInstance = null;
860
+ function getCurrentInstance() {
861
+ return currentInstance;
862
+ }
863
+ function setCurrentInstance(instance) {
694
864
  const prev = currentInstance;
695
865
  currentInstance = instance;
696
866
  return prev;
697
867
  }
698
- function fireMountedHooks(instance) {
699
- for (const child of instance.children) {
700
- fireMountedHooks(child);
868
+ function withInstance(instance, fn) {
869
+ const prev = setCurrentInstance(instance);
870
+ try {
871
+ return fn();
872
+ } finally {
873
+ setCurrentInstance(prev);
874
+ }
875
+ }
876
+ function fireMountedHooks(instance) {
877
+ if (instance.isUnmounted) {
878
+ return;
879
+ }
880
+ for (const child of instance.children) {
881
+ fireMountedHooks(child);
882
+ }
883
+ if (!instance.isMounted) {
884
+ instance.isMounted = true;
885
+ for (const hook of instance._mountedHooks) {
886
+ hook();
887
+ }
888
+ }
889
+ }
890
+ function fireUnmountedHooks(instance) {
891
+ for (const child of instance.children) {
892
+ fireUnmountedHooks(child);
893
+ }
894
+ if (instance.isMounted && !instance.isUnmounted) {
895
+ instance.isUnmounted = true;
896
+ instance.isMounted = false;
897
+ for (const hook of instance._unmountedHooks) {
898
+ hook();
899
+ }
900
+ for (const dispose of instance.effects) {
901
+ dispose();
902
+ }
903
+ instance.effects.length = 0;
904
+ }
905
+ }
906
+ function fireUpdatedHooks(instance) {
907
+ for (const hook of instance._updatedHooks) {
908
+ hook();
909
+ }
910
+ }
911
+ var queuedUpdatedHooks = new Set;
912
+ function queueUpdatedHooks(instance) {
913
+ if (!instance || !instance.isMounted || instance.isUnmounted || instance._updatedHooks.length === 0 || queuedUpdatedHooks.has(instance)) {
914
+ return;
915
+ }
916
+ queuedUpdatedHooks.add(instance);
917
+ nextTick(() => {
918
+ queuedUpdatedHooks.delete(instance);
919
+ if (instance.isMounted && !instance.isUnmounted) {
920
+ fireUpdatedHooks(instance);
921
+ }
922
+ });
923
+ }
924
+ function handleComponentError(instance, err) {
925
+ let current = instance;
926
+ while (current) {
927
+ if (current._errorHooks.length > 0) {
928
+ for (const hook of current._errorHooks) {
929
+ hook(err);
930
+ }
931
+ return;
932
+ }
933
+ current = current.parent;
934
+ }
935
+ console.error("[Sinwan] Unhandled component error:", err);
936
+ }
937
+
938
+ // src/hydration/markers.ts
939
+ var COMP_ID_ATTR = "data-sinwan-id";
940
+ var COMP_ID_PREFIX = "c";
941
+ var TEXT_MARKER_OPEN = "sinwan-t:";
942
+ var TEXT_MARKER_CLOSE = "/sinwan-t";
943
+ var EVENT_ATTR = "data-sinwan-ev";
944
+ function compId(index) {
945
+ return `${COMP_ID_PREFIX}${index}`;
946
+ }
947
+ function textMarkerOpen(index) {
948
+ return `<!--${TEXT_MARKER_OPEN}${index}-->`;
949
+ }
950
+ function textMarkerCloseStr() {
951
+ return `<!--${TEXT_MARKER_CLOSE}-->`;
952
+ }
953
+ function parseTextOpenMarker(node) {
954
+ const data = node.data;
955
+ if (data.startsWith(TEXT_MARKER_OPEN)) {
956
+ const idx = parseInt(data.slice(TEXT_MARKER_OPEN.length), 10);
957
+ return Number.isNaN(idx) ? -1 : idx;
958
+ }
959
+ return -1;
960
+ }
961
+ function isTextCloseMarker(node) {
962
+ return node.data === TEXT_MARKER_CLOSE;
963
+ }
964
+
965
+ // src/server/stream.ts
966
+ function createHydratableStreamContext() {
967
+ return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
968
+ }
969
+ var VOID_ELEMENTS3 = new Set([
970
+ "area",
971
+ "base",
972
+ "br",
973
+ "col",
974
+ "embed",
975
+ "hr",
976
+ "img",
977
+ "input",
978
+ "link",
979
+ "meta",
980
+ "param",
981
+ "source",
982
+ "track",
983
+ "wbr"
984
+ ]);
985
+ function streamPage(page, data) {
986
+ const encoder = new TextEncoder;
987
+ return new ReadableStream({
988
+ async start(controller) {
989
+ try {
990
+ const element = await page(data);
991
+ await streamNode(element, controller, encoder);
992
+ controller.close();
993
+ } catch (error) {
994
+ controller.error(error);
995
+ }
996
+ }
997
+ });
998
+ }
999
+ function streamHydratablePage(component, props) {
1000
+ const encoder = new TextEncoder;
1001
+ const ctx = createHydratableStreamContext();
1002
+ return new ReadableStream({
1003
+ async start(controller) {
1004
+ try {
1005
+ await streamHydratableComponent(component, props ?? {}, controller, encoder, ctx, true);
1006
+ controller.close();
1007
+ } catch (error) {
1008
+ controller.error(error);
1009
+ }
1010
+ }
1011
+ });
1012
+ }
1013
+ function streamHydratableNode(node) {
1014
+ const encoder = new TextEncoder;
1015
+ const ctx = createHydratableStreamContext();
1016
+ return new ReadableStream({
1017
+ async start(controller) {
1018
+ try {
1019
+ await streamHydratableNodeToController(node, controller, encoder, ctx);
1020
+ controller.close();
1021
+ } catch (error) {
1022
+ controller.error(error);
1023
+ }
1024
+ }
1025
+ });
1026
+ }
1027
+ async function streamNode(node, controller, encoder) {
1028
+ if (node == null || typeof node === "boolean") {
1029
+ return;
1030
+ }
1031
+ if (typeof node === "string") {
1032
+ controller.enqueue(encoder.encode(escapeHtml(node)));
1033
+ return;
1034
+ }
1035
+ if (typeof node === "number") {
1036
+ controller.enqueue(encoder.encode(String(node)));
1037
+ return;
1038
+ }
1039
+ if (node instanceof HtmlEscapedString) {
1040
+ controller.enqueue(encoder.encode(node.value));
1041
+ return;
1042
+ }
1043
+ if (isSignal(node) || isComputed(node)) {
1044
+ controller.enqueue(encoder.encode(escapeHtml(String(node.value))));
1045
+ return;
1046
+ }
1047
+ if (Array.isArray(node)) {
1048
+ for (const child of node) {
1049
+ await streamNode(child, controller, encoder);
1050
+ }
1051
+ return;
1052
+ }
1053
+ if (node instanceof Promise) {
1054
+ const resolved = await node;
1055
+ await streamElement(resolved, controller, encoder);
1056
+ return;
1057
+ }
1058
+ await streamElement(node, controller, encoder);
1059
+ }
1060
+ async function streamElement(element, controller, encoder) {
1061
+ const { tag, props, children } = element;
1062
+ if (tag === Show || tag === For || tag === Switch || tag === Index || tag === Key || tag === Dynamic || tag === Portal) {
1063
+ await streamElement(tag(props), controller, encoder);
1064
+ return;
1065
+ }
1066
+ if (tag === Visible) {
1067
+ await streamElement(tag(props), controller, encoder);
1068
+ return;
1069
+ }
1070
+ if (isShowElement(element)) {
1071
+ const when = readReactive3(props.when);
1072
+ await streamNode(when ? resolveShowChildren2(element, when) : props.fallback, controller, encoder);
1073
+ return;
1074
+ }
1075
+ if (isForElement(element)) {
1076
+ await streamForElement(element, controller, encoder);
1077
+ return;
1078
+ }
1079
+ if (isSwitchElement(element)) {
1080
+ await streamNode(resolveSwitchContent2(element), controller, encoder);
1081
+ return;
1082
+ }
1083
+ if (isMatchElement(element)) {
1084
+ const when = readReactive3(props.when);
1085
+ await streamNode(when ? resolveMatchChildren2(element, when) : null, controller, encoder);
1086
+ return;
1087
+ }
1088
+ if (isIndexElement(element)) {
1089
+ await streamIndexElement(element, controller, encoder);
1090
+ return;
1091
+ }
1092
+ if (isKeyElement(element)) {
1093
+ const key = readReactive3(props.when);
1094
+ await streamNode(resolveKeyChildren2(element, key), controller, encoder);
1095
+ return;
1096
+ }
1097
+ if (isDynamicElement(element)) {
1098
+ const dynamicTag = readReactive3(props.component);
1099
+ const dynamic = createDynamicElement2(element, dynamicTag);
1100
+ if (dynamic) {
1101
+ await streamElement(dynamic, controller, encoder);
1102
+ }
1103
+ return;
1104
+ }
1105
+ if (isPortalElement(element)) {
1106
+ return;
1107
+ }
1108
+ if (typeof tag === "function") {
1109
+ const result = await tag(props);
1110
+ await streamNode(result, controller, encoder);
1111
+ return;
1112
+ }
1113
+ if (typeof tag === "string") {
1114
+ await streamIntrinsicElement(tag, props, children, controller, encoder);
1115
+ return;
1116
+ }
1117
+ await streamNode(children, controller, encoder);
1118
+ }
1119
+ async function streamIntrinsicElement(tag, props, children, controller, encoder) {
1120
+ const attrs = renderAttributes2(props);
1121
+ const dangerous = props.dangerouslySetInnerHTML;
1122
+ if (VOID_ELEMENTS3.has(tag)) {
1123
+ const html = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1124
+ controller.enqueue(encoder.encode(html));
1125
+ return;
701
1126
  }
702
- if (!instance.isMounted) {
703
- instance.isMounted = true;
704
- for (const hook of instance._mountedHooks) {
705
- hook();
1127
+ const openTag = attrs ? `<${tag}${attrs}>` : `<${tag}>`;
1128
+ controller.enqueue(encoder.encode(openTag));
1129
+ if (dangerous && typeof dangerous.__html === "string") {
1130
+ controller.enqueue(encoder.encode(dangerous.__html));
1131
+ } else {
1132
+ await streamNode(children, controller, encoder);
1133
+ }
1134
+ controller.enqueue(encoder.encode(`</${tag}>`));
1135
+ }
1136
+ function renderAttributes2(props) {
1137
+ let attrs = "";
1138
+ for (const [key, value] of Object.entries(props)) {
1139
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML" || isEventProp(key)) {
1140
+ continue;
706
1141
  }
1142
+ const resolvedValue = readReactive3(value);
1143
+ if (resolvedValue == null || resolvedValue === false)
1144
+ continue;
1145
+ attrs += renderServerAttribute(key, resolvedValue);
707
1146
  }
1147
+ return attrs;
708
1148
  }
709
- function fireUnmountedHooks(instance) {
710
- for (const child of instance.children) {
711
- fireUnmountedHooks(child);
1149
+ async function streamHydratableNodeToController(node, controller, encoder, ctx, isComponentRoot = false) {
1150
+ if (node == null || typeof node === "boolean") {
1151
+ return;
712
1152
  }
713
- if (instance.isMounted && !instance.isUnmounted) {
714
- instance.isUnmounted = true;
715
- instance.isMounted = false;
716
- for (const hook of instance._unmountedHooks) {
717
- hook();
1153
+ if (typeof node === "string") {
1154
+ enqueue(controller, encoder, escapeHtml(node));
1155
+ return;
1156
+ }
1157
+ if (typeof node === "number") {
1158
+ enqueue(controller, encoder, String(node));
1159
+ return;
1160
+ }
1161
+ if (node instanceof HtmlEscapedString) {
1162
+ enqueue(controller, encoder, node.value);
1163
+ return;
1164
+ }
1165
+ if (isSignal(node) || isComputed(node)) {
1166
+ const idx = ctx.textIndex++;
1167
+ enqueue(controller, encoder, `${textMarkerOpen(idx)}${escapeHtml(String(node.value))}${textMarkerCloseStr()}`);
1168
+ return;
1169
+ }
1170
+ if (Array.isArray(node)) {
1171
+ for (const child of node) {
1172
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
718
1173
  }
719
- for (const dispose of instance.effects) {
720
- dispose();
1174
+ return;
1175
+ }
1176
+ if (node instanceof Promise) {
1177
+ await streamHydratableElement(await node, controller, encoder, ctx, isComponentRoot);
1178
+ return;
1179
+ }
1180
+ await streamHydratableElement(node, controller, encoder, ctx, isComponentRoot);
1181
+ }
1182
+ async function streamHydratableElement(element, controller, encoder, ctx, isComponentRoot) {
1183
+ const { tag, props, children } = element;
1184
+ if (tag === "") {
1185
+ for (const child of children) {
1186
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
721
1187
  }
722
- instance.effects.length = 0;
1188
+ return;
1189
+ }
1190
+ if (tag === Show || tag === For || tag === Switch || tag === Index || tag === Key || tag === Dynamic || tag === Portal) {
1191
+ await streamHydratableElement(tag(props), controller, encoder, ctx, isComponentRoot);
1192
+ return;
1193
+ }
1194
+ if (tag === Visible) {
1195
+ await streamHydratableElement(tag(props), controller, encoder, ctx, isComponentRoot);
1196
+ return;
1197
+ }
1198
+ if (isShowElement(element)) {
1199
+ const when = readReactive3(props.when);
1200
+ await streamHydratableNodeToController(when ? resolveShowChildren2(element, when) : props.fallback, controller, encoder, ctx, isComponentRoot);
1201
+ return;
1202
+ }
1203
+ if (isForElement(element)) {
1204
+ await streamHydratableForElement(element, controller, encoder, ctx);
1205
+ return;
1206
+ }
1207
+ if (isSwitchElement(element)) {
1208
+ await streamHydratableNodeToController(resolveSwitchContent2(element), controller, encoder, ctx, isComponentRoot);
1209
+ return;
1210
+ }
1211
+ if (isMatchElement(element)) {
1212
+ const when = readReactive3(props.when);
1213
+ await streamHydratableNodeToController(when ? resolveMatchChildren2(element, when) : null, controller, encoder, ctx, isComponentRoot);
1214
+ return;
1215
+ }
1216
+ if (isIndexElement(element)) {
1217
+ await streamHydratableIndexElement(element, controller, encoder, ctx);
1218
+ return;
1219
+ }
1220
+ if (isKeyElement(element)) {
1221
+ const key = readReactive3(props.when);
1222
+ await streamHydratableNodeToController(resolveKeyChildren2(element, key), controller, encoder, ctx, isComponentRoot);
1223
+ return;
723
1224
  }
1225
+ if (isDynamicElement(element)) {
1226
+ const dynamicTag = readReactive3(props.component);
1227
+ const dynamic = createDynamicElement2(element, dynamicTag);
1228
+ if (dynamic) {
1229
+ await streamHydratableElement(dynamic, controller, encoder, ctx, isComponentRoot);
1230
+ }
1231
+ return;
1232
+ }
1233
+ if (isPortalElement(element)) {
1234
+ return;
1235
+ }
1236
+ if (typeof tag === "function") {
1237
+ await streamHydratableComponent(tag, props, controller, encoder, ctx, true);
1238
+ return;
1239
+ }
1240
+ if (typeof tag === "string") {
1241
+ await streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot);
1242
+ return;
1243
+ }
1244
+ await streamHydratableNodeToController(children, controller, encoder, ctx);
724
1245
  }
725
- function handleComponentError(instance, err) {
726
- let current = instance;
727
- while (current) {
728
- if (current._errorHooks.length > 0) {
729
- for (const hook of current._errorHooks) {
730
- hook(err);
731
- }
732
- return;
1246
+ async function streamHydratableComponent(component, props, controller, encoder, ctx, isComponentRoot) {
1247
+ const parentInstance = getCurrentInstance();
1248
+ const instance = createComponentInstance(component, props, parentInstance);
1249
+ if (parentInstance) {
1250
+ parentInstance.children.push(instance);
1251
+ }
1252
+ const prev = setCurrentInstance(instance);
1253
+ try {
1254
+ const result = await component(props);
1255
+ await streamHydratableNodeToController(result, controller, encoder, ctx, isComponentRoot);
1256
+ } finally {
1257
+ setCurrentInstance(prev);
1258
+ }
1259
+ }
1260
+ async function streamHydratableIntrinsic(tag, props, children, controller, encoder, ctx, isComponentRoot) {
1261
+ const attrs = renderHydratableAttributes(props, ctx, isComponentRoot);
1262
+ const dangerous = props.dangerouslySetInnerHTML;
1263
+ enqueue(controller, encoder, attrs ? `<${tag}${attrs}>` : `<${tag}>`);
1264
+ if (VOID_ELEMENTS3.has(tag)) {
1265
+ return;
1266
+ }
1267
+ if (dangerous && typeof dangerous.__html === "string") {
1268
+ enqueue(controller, encoder, dangerous.__html);
1269
+ } else {
1270
+ for (const child of children) {
1271
+ await streamHydratableNodeToController(child, controller, encoder, ctx);
733
1272
  }
734
- current = current.parent;
735
1273
  }
736
- console.error("[Sinwan] Unhandled component error:", err);
1274
+ enqueue(controller, encoder, `</${tag}>`);
1275
+ }
1276
+ async function streamHydratableForElement(element, controller, encoder, ctx) {
1277
+ const props = element.props;
1278
+ const each = readReactive3(props.each);
1279
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1280
+ if (props.fallback) {
1281
+ await streamHydratableNodeToController(props.fallback, controller, encoder, ctx);
1282
+ }
1283
+ return;
1284
+ }
1285
+ if (each.length === 0) {
1286
+ if (props.fallback) {
1287
+ await streamHydratableNodeToController(props.fallback, controller, encoder, ctx);
1288
+ }
1289
+ return;
1290
+ }
1291
+ for (let index = 0;index < each.length; index++) {
1292
+ await streamHydratableNodeToController(props.children(each[index], () => index), controller, encoder, ctx);
1293
+ }
1294
+ }
1295
+ async function streamHydratableIndexElement(element, controller, encoder, ctx) {
1296
+ const props = element.props;
1297
+ const each = readReactive3(props.each);
1298
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1299
+ if (props.fallback) {
1300
+ await streamHydratableNodeToController(props.fallback, controller, encoder, ctx);
1301
+ }
1302
+ return;
1303
+ }
1304
+ if (each.length === 0) {
1305
+ if (props.fallback) {
1306
+ await streamHydratableNodeToController(props.fallback, controller, encoder, ctx);
1307
+ }
1308
+ return;
1309
+ }
1310
+ for (let index = 0;index < each.length; index++) {
1311
+ await streamHydratableNodeToController(props.children(() => each[index], index), controller, encoder, ctx);
1312
+ }
1313
+ }
1314
+ function renderHydratableAttributes(props, ctx, isComponentRoot) {
1315
+ let attrs = "";
1316
+ if (isComponentRoot) {
1317
+ attrs += ` ${COMP_ID_ATTR}="${compId(ctx.componentIndex++)}"`;
1318
+ }
1319
+ const eventParts = [];
1320
+ for (const [key, value] of Object.entries(props)) {
1321
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
1322
+ continue;
1323
+ }
1324
+ if (isEventProp(key)) {
1325
+ eventParts.push(`${toEventName(key)}:${ctx.eventIndex++}`);
1326
+ continue;
1327
+ }
1328
+ const resolvedValue = readReactive3(value);
1329
+ if (resolvedValue == null || resolvedValue === false)
1330
+ continue;
1331
+ attrs += renderServerAttribute(key, resolvedValue);
1332
+ }
1333
+ if (eventParts.length > 0) {
1334
+ attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
1335
+ }
1336
+ return attrs;
1337
+ }
1338
+ function enqueue(controller, encoder, html) {
1339
+ controller.enqueue(encoder.encode(html));
1340
+ }
1341
+ async function streamForElement(element, controller, encoder) {
1342
+ const props = element.props;
1343
+ const each = readReactive3(props.each);
1344
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1345
+ if (props.fallback) {
1346
+ await streamNode(props.fallback, controller, encoder);
1347
+ }
1348
+ return;
1349
+ }
1350
+ if (each.length === 0) {
1351
+ if (props.fallback) {
1352
+ await streamNode(props.fallback, controller, encoder);
1353
+ }
1354
+ return;
1355
+ }
1356
+ for (let index = 0;index < each.length; index++) {
1357
+ await streamNode(props.children(each[index], () => index), controller, encoder);
1358
+ }
1359
+ }
1360
+ async function streamIndexElement(element, controller, encoder) {
1361
+ const props = element.props;
1362
+ const each = readReactive3(props.each);
1363
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1364
+ if (props.fallback) {
1365
+ await streamNode(props.fallback, controller, encoder);
1366
+ }
1367
+ return;
1368
+ }
1369
+ if (each.length === 0) {
1370
+ if (props.fallback) {
1371
+ await streamNode(props.fallback, controller, encoder);
1372
+ }
1373
+ return;
1374
+ }
1375
+ for (let index = 0;index < each.length; index++) {
1376
+ await streamNode(props.children(() => each[index], index), controller, encoder);
1377
+ }
1378
+ }
1379
+ function resolveShowChildren2(element, value) {
1380
+ const children = element.props.children ?? element.children;
1381
+ if (typeof children === "function") {
1382
+ return children(value);
1383
+ }
1384
+ return children;
1385
+ }
1386
+ function resolveSwitchContent2(element) {
1387
+ const props = element.props;
1388
+ const children = normalizeContent2(props.children ?? element.children);
1389
+ for (const child of children) {
1390
+ const match = getMatchElement2(child);
1391
+ if (!match) {
1392
+ continue;
1393
+ }
1394
+ const when = readReactive3(match.props.when);
1395
+ if (when) {
1396
+ return resolveMatchChildren2(match, when);
1397
+ }
1398
+ }
1399
+ return props.fallback;
1400
+ }
1401
+ function resolveMatchChildren2(element, value) {
1402
+ const children = element.props.children ?? element.children;
1403
+ if (typeof children === "function") {
1404
+ return children(value);
1405
+ }
1406
+ return children;
1407
+ }
1408
+ function resolveKeyChildren2(element, value) {
1409
+ const children = element.props.children ?? element.children;
1410
+ if (typeof children === "function") {
1411
+ return children(value);
1412
+ }
1413
+ return children;
1414
+ }
1415
+ function createDynamicElement2(element, tag) {
1416
+ if (typeof tag !== "string" && typeof tag !== "function") {
1417
+ return null;
1418
+ }
1419
+ const { component, ...props } = element.props;
1420
+ const children = normalizeContent2(props.children ?? element.children);
1421
+ return {
1422
+ tag,
1423
+ props,
1424
+ children
1425
+ };
1426
+ }
1427
+ function readReactive3(value) {
1428
+ return isSignal(value) || isComputed(value) ? value.value : value;
1429
+ }
1430
+ function normalizeContent2(content) {
1431
+ if (content == null || typeof content === "boolean") {
1432
+ return [];
1433
+ }
1434
+ return Array.isArray(content) ? content : [content];
1435
+ }
1436
+ function isElementLike2(value) {
1437
+ return value != null && typeof value === "object" && "tag" in value;
1438
+ }
1439
+ function getMatchElement2(value) {
1440
+ if (!isElementLike2(value)) {
1441
+ return null;
1442
+ }
1443
+ if (isMatchElement(value)) {
1444
+ return value;
1445
+ }
1446
+ return value.tag === Match ? Match(value.props) : null;
737
1447
  }
738
-
739
1448
  // src/server/hydration-markers.ts
740
1449
  function createHydrationContext() {
741
1450
  return { componentIndex: 0, textIndex: 0, eventIndex: 0 };
@@ -744,13 +1453,16 @@ async function renderToHydratableString(component, props) {
744
1453
  const ctx = createHydrationContext();
745
1454
  const mergedProps = props ?? {};
746
1455
  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);
1456
+ const prev = setCurrentInstance(instance);
1457
+ try {
1458
+ const result = await component(mergedProps);
1459
+ if (result && typeof result === "object" && "tag" in result) {
1460
+ return renderElementH(result, ctx, true);
1461
+ }
1462
+ return renderNodeH(result, ctx);
1463
+ } finally {
1464
+ setCurrentInstance(prev);
752
1465
  }
753
- return renderNodeH(result, ctx);
754
1466
  }
755
1467
  async function renderNodeToHydratableString(node) {
756
1468
  const ctx = createHydrationContext();
@@ -802,6 +1514,42 @@ function renderElementH(element, ctx, isComponentRoot) {
802
1514
  if (tag === "") {
803
1515
  return children.map((child) => renderNodeH(child, ctx)).join("");
804
1516
  }
1517
+ if (tag === Show || tag === For || tag === Switch || tag === Index || tag === Key || tag === Dynamic || tag === Portal) {
1518
+ return renderElementH(tag(props), ctx, isComponentRoot);
1519
+ }
1520
+ if (tag === Visible) {
1521
+ return renderElementH(tag(props), ctx, isComponentRoot);
1522
+ }
1523
+ if (isShowElement(element)) {
1524
+ const when = readReactive4(props.when);
1525
+ const content = when ? resolveShowChildren3(element, when) : props.fallback;
1526
+ return renderNodeMaybeRoot(content, ctx, isComponentRoot);
1527
+ }
1528
+ if (isForElement(element)) {
1529
+ return renderForElementH(element, ctx);
1530
+ }
1531
+ if (isSwitchElement(element)) {
1532
+ return renderNodeMaybeRoot(resolveSwitchContent3(element), ctx, isComponentRoot);
1533
+ }
1534
+ if (isMatchElement(element)) {
1535
+ const when = readReactive4(props.when);
1536
+ return renderNodeMaybeRoot(when ? resolveMatchChildren3(element, when) : null, ctx, isComponentRoot);
1537
+ }
1538
+ if (isIndexElement(element)) {
1539
+ return renderIndexElementH(element, ctx);
1540
+ }
1541
+ if (isKeyElement(element)) {
1542
+ const key = readReactive4(props.when);
1543
+ return renderNodeMaybeRoot(resolveKeyChildren3(element, key), ctx, isComponentRoot);
1544
+ }
1545
+ if (isDynamicElement(element)) {
1546
+ const dynamicTag = readReactive4(props.component);
1547
+ const dynamic = createDynamicElement3(element, dynamicTag);
1548
+ return dynamic ? renderElementH(dynamic, ctx, isComponentRoot) : "";
1549
+ }
1550
+ if (isPortalElement(element)) {
1551
+ return "";
1552
+ }
805
1553
  if (typeof tag === "function") {
806
1554
  return renderComponentH(tag, props, ctx);
807
1555
  }
@@ -811,15 +1559,21 @@ function renderElementH(element, ctx, isComponentRoot) {
811
1559
  return children.map((child) => renderNodeH(child, ctx)).join("");
812
1560
  }
813
1561
  function renderComponentH(component, props, ctx) {
814
- const parentInstance = globalThis.__SinwanCurrentInstance;
815
- const instance = createComponentInstance(component, props, null);
1562
+ const parentInstance = getCurrentInstance();
1563
+ const instance = createComponentInstance(component, props, parentInstance);
1564
+ if (parentInstance) {
1565
+ parentInstance.children.push(instance);
1566
+ }
816
1567
  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);
1568
+ try {
1569
+ const result = component(props);
1570
+ if (result && typeof result === "object" && "tag" in result) {
1571
+ return renderElementH(result, ctx, true);
1572
+ }
1573
+ return renderNodeH(result, ctx);
1574
+ } finally {
1575
+ setCurrentInstance(prev);
821
1576
  }
822
- return renderNodeH(result, ctx);
823
1577
  }
824
1578
  function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
825
1579
  let attrs = "";
@@ -828,8 +1582,9 @@ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
828
1582
  }
829
1583
  const eventParts = [];
830
1584
  for (const [key, value] of Object.entries(props)) {
831
- if (key === "children" || key === "dangerouslySetInnerHTML")
1585
+ if (key === "children" || key === "key" || key === "ref" || key === "dangerouslySetInnerHTML") {
832
1586
  continue;
1587
+ }
833
1588
  if (isEventProp(key)) {
834
1589
  const eventName = toEventName(key);
835
1590
  eventParts.push(`${eventName}:${ctx.eventIndex++}`);
@@ -841,13 +1596,7 @@ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
841
1596
  if (isSignal(value) || isComputed(value)) {
842
1597
  resolvedValue = value.value;
843
1598
  }
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))}"`;
1599
+ attrs += renderServerAttribute(key, resolvedValue);
851
1600
  }
852
1601
  if (eventParts.length > 0) {
853
1602
  attrs += ` ${EVENT_ATTR}="${eventParts.join(",")}"`;
@@ -862,8 +1611,107 @@ function renderIntrinsicH(tag, props, children, ctx, isComponentRoot) {
862
1611
  const childrenHtml = children.map((child) => renderNodeH(child, ctx)).join("");
863
1612
  return `<${tag}${attrs}>${childrenHtml}</${tag}>`;
864
1613
  }
1614
+ function renderNodeMaybeRoot(node, ctx, isComponentRoot) {
1615
+ if (isComponentRoot && node && typeof node === "object" && !Array.isArray(node) && "tag" in node) {
1616
+ return renderElementH(node, ctx, true);
1617
+ }
1618
+ return renderNodeH(node, ctx);
1619
+ }
1620
+ function renderForElementH(element, ctx) {
1621
+ const props = element.props;
1622
+ const each = readReactive4(props.each);
1623
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1624
+ return props.fallback ? renderNodeH(props.fallback, ctx) : "";
1625
+ }
1626
+ if (each.length === 0) {
1627
+ return props.fallback ? renderNodeH(props.fallback, ctx) : "";
1628
+ }
1629
+ return each.map((item, index) => renderNodeH(props.children(item, () => index), ctx)).join("");
1630
+ }
1631
+ function renderIndexElementH(element, ctx) {
1632
+ const props = element.props;
1633
+ const each = readReactive4(props.each);
1634
+ if (!Array.isArray(each) || typeof props.children !== "function") {
1635
+ return props.fallback ? renderNodeH(props.fallback, ctx) : "";
1636
+ }
1637
+ if (each.length === 0) {
1638
+ return props.fallback ? renderNodeH(props.fallback, ctx) : "";
1639
+ }
1640
+ return each.map((item, index) => renderNodeH(props.children(() => item, index), ctx)).join("");
1641
+ }
1642
+ function resolveShowChildren3(element, value) {
1643
+ const children = element.props.children ?? element.children;
1644
+ if (typeof children === "function") {
1645
+ return children(value);
1646
+ }
1647
+ return children;
1648
+ }
1649
+ function resolveSwitchContent3(element) {
1650
+ const props = element.props;
1651
+ const children = normalizeContent3(props.children ?? element.children);
1652
+ for (const child of children) {
1653
+ const match = getMatchElement3(child);
1654
+ if (!match) {
1655
+ continue;
1656
+ }
1657
+ const when = readReactive4(match.props.when);
1658
+ if (when) {
1659
+ return resolveMatchChildren3(match, when);
1660
+ }
1661
+ }
1662
+ return props.fallback;
1663
+ }
1664
+ function resolveMatchChildren3(element, value) {
1665
+ const children = element.props.children ?? element.children;
1666
+ if (typeof children === "function") {
1667
+ return children(value);
1668
+ }
1669
+ return children;
1670
+ }
1671
+ function resolveKeyChildren3(element, value) {
1672
+ const children = element.props.children ?? element.children;
1673
+ if (typeof children === "function") {
1674
+ return children(value);
1675
+ }
1676
+ return children;
1677
+ }
1678
+ function createDynamicElement3(element, tag) {
1679
+ if (typeof tag !== "string" && typeof tag !== "function") {
1680
+ return null;
1681
+ }
1682
+ const { component, ...props } = element.props;
1683
+ const children = normalizeContent3(props.children ?? element.children);
1684
+ return {
1685
+ tag,
1686
+ props,
1687
+ children
1688
+ };
1689
+ }
1690
+ function readReactive4(value) {
1691
+ return isSignal(value) || isComputed(value) ? value.value : value;
1692
+ }
1693
+ function normalizeContent3(content) {
1694
+ if (content == null || typeof content === "boolean") {
1695
+ return [];
1696
+ }
1697
+ return Array.isArray(content) ? content : [content];
1698
+ }
1699
+ function isElementLike3(value) {
1700
+ return value != null && typeof value === "object" && "tag" in value;
1701
+ }
1702
+ function getMatchElement3(value) {
1703
+ if (!isElementLike3(value)) {
1704
+ return null;
1705
+ }
1706
+ if (isMatchElement(value)) {
1707
+ return value;
1708
+ }
1709
+ return value.tag === Match ? Match(value.props) : null;
1710
+ }
865
1711
  export {
866
1712
  streamPage,
1713
+ streamHydratablePage,
1714
+ streamHydratableNode,
867
1715
  renderToString,
868
1716
  renderToHydratableString,
869
1717
  renderPage,
@@ -874,5 +1722,5 @@ export {
874
1722
  getPage
875
1723
  };
876
1724
 
877
- //# debugId=46FB02649D98C84264756E2164756E21
1725
+ //# debugId=46896ED83A2048E464756E2164756E21
878
1726
  //# sourceMappingURL=index.development.js.map