sibujs 4.6.0 → 4.8.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 (94) hide show
  1. package/README.md +1 -1
  2. package/dist/browser.cjs +62 -8
  3. package/dist/browser.d.cts +23 -0
  4. package/dist/browser.d.ts +23 -0
  5. package/dist/browser.js +6 -5
  6. package/dist/build.cjs +2388 -1004
  7. package/dist/build.d.cts +147 -44
  8. package/dist/build.d.ts +147 -44
  9. package/dist/build.js +1410 -564
  10. package/dist/cdn.dev.global.js +14 -12
  11. package/dist/cdn.full.dev.global.js +15 -13
  12. package/dist/cdn.full.global.js +14 -12
  13. package/dist/cdn.global.js +12 -10
  14. package/dist/chunk-3ACAAQIN.js +224 -0
  15. package/dist/{chunk-7NCARGJW.js → chunk-44MTL7ED.js} +6 -6
  16. package/dist/{chunk-BAAG6ZTI.js → chunk-4WMSTZ7J.js} +8 -5
  17. package/dist/{chunk-H3SRKIYX.js → chunk-62BDYGAG.js} +8 -0
  18. package/dist/{chunk-DKGBBKOF.js → chunk-C5PEWEGH.js} +20 -5
  19. package/dist/{chunk-3ISI6ACU.js → chunk-C7AA2CZX.js} +1 -1
  20. package/dist/{chunk-UZQ6ALFS.js → chunk-CZPRSWSS.js} +1 -1
  21. package/dist/{chunk-D33YTSX5.js → chunk-DSID757X.js} +5 -4
  22. package/dist/{chunk-OXUY2A6L.js → chunk-EGUQLBHE.js} +22 -8
  23. package/dist/{chunk-2INLLLMZ.js → chunk-EXXSN3BW.js} +202 -26
  24. package/dist/{chunk-HURREPU2.js → chunk-FC6KCJSA.js} +87 -150
  25. package/dist/{chunk-HYXDKS4N.js → chunk-GSGNX35Q.js} +5 -5
  26. package/dist/{chunk-7GQCWFOE.js → chunk-J3YQ457H.js} +171 -85
  27. package/dist/{chunk-ORMZXBKQ.js → chunk-JUTFX2RM.js} +18 -10
  28. package/dist/{chunk-QE4TTDU3.js → chunk-KJ3UKABJ.js} +12 -11
  29. package/dist/{chunk-XRRZKZYX.js → chunk-LI4W3U6C.js} +1 -1
  30. package/dist/{chunk-47M47FOM.js → chunk-O6TRVMJI.js} +58 -6
  31. package/dist/{chunk-JSXPZCET.js → chunk-Q5J2LVKH.js} +1 -1
  32. package/dist/{chunk-5MT6SJ3P.js → chunk-QGMSESDD.js} +10 -5
  33. package/dist/{chunk-AIF3Z2T7.js → chunk-RGLPEBDE.js} +13 -10
  34. package/dist/{chunk-5Q4R7HCL.js → chunk-TQQUN4WR.js} +11 -2
  35. package/dist/{chunk-PK6FK2G2.js → chunk-UCNP3HVM.js} +183 -30
  36. package/dist/{chunk-XC4MEKGA.js → chunk-V3LHZSU5.js} +8 -2
  37. package/dist/{chunk-VVWPJ543.js → chunk-VWT3VXPL.js} +55 -20
  38. package/dist/{chunk-SXXVZMKZ.js → chunk-ZVLHNEEL.js} +17 -35
  39. package/dist/{contracts-DRIuclVT.d.cts → contracts-CLqzJnOV.d.ts} +28 -17
  40. package/dist/{contracts-DRIuclVT.d.ts → contracts-CTOJXu-x.d.cts} +28 -17
  41. package/dist/{customElement-MmInOW1U.d.ts → customElement-pYDKUzWl.d.cts} +11 -3
  42. package/dist/{customElement-MmInOW1U.d.cts → customElement-pYDKUzWl.d.ts} +11 -3
  43. package/dist/data.cjs +205 -39
  44. package/dist/data.d.cts +119 -3
  45. package/dist/data.d.ts +119 -3
  46. package/dist/data.js +6 -6
  47. package/dist/devtools.cjs +18 -5
  48. package/dist/devtools.js +5 -5
  49. package/dist/ecosystem.cjs +462 -58
  50. package/dist/ecosystem.d.cts +2 -1
  51. package/dist/ecosystem.d.ts +2 -1
  52. package/dist/ecosystem.js +9 -8
  53. package/dist/extras.cjs +852 -296
  54. package/dist/extras.d.cts +5 -4
  55. package/dist/extras.d.ts +5 -4
  56. package/dist/extras.js +24 -23
  57. package/dist/index.cjs +721 -176
  58. package/dist/index.d.cts +220 -169
  59. package/dist/index.d.ts +220 -169
  60. package/dist/index.js +16 -12
  61. package/dist/motion.cjs +12 -4
  62. package/dist/motion.js +3 -3
  63. package/dist/patterns.cjs +37 -21
  64. package/dist/patterns.d.cts +2 -1
  65. package/dist/patterns.d.ts +2 -1
  66. package/dist/patterns.js +6 -6
  67. package/dist/performance.cjs +48 -6
  68. package/dist/performance.d.cts +12 -2
  69. package/dist/performance.d.ts +12 -2
  70. package/dist/performance.js +8 -6
  71. package/dist/plugins.cjs +672 -261
  72. package/dist/plugins.d.cts +131 -13
  73. package/dist/plugins.d.ts +131 -13
  74. package/dist/plugins.js +83 -47
  75. package/dist/{ssr-XOTUASDO.js → ssr-5OWAH3U2.js} +4 -2
  76. package/dist/{ssr-Bli9XRW5.d.cts → ssr-C8hgIAw7.d.cts} +6 -0
  77. package/dist/{ssr-Bli9XRW5.d.ts → ssr-C8hgIAw7.d.ts} +6 -0
  78. package/dist/ssr.cjs +509 -142
  79. package/dist/ssr.d.cts +55 -5
  80. package/dist/ssr.d.ts +55 -5
  81. package/dist/ssr.js +11 -9
  82. package/dist/{tagFactory-DFstCLQV.d.cts → tagFactory-8qL9LCIx.d.cts} +58 -19
  83. package/dist/{tagFactory-DkaNVUNV.d.ts → tagFactory-BL2fymez.d.ts} +58 -19
  84. package/dist/testing.cjs +17 -4
  85. package/dist/testing.js +4 -4
  86. package/dist/types-CJFViL6Q.d.cts +26 -0
  87. package/dist/types-CJFViL6Q.d.ts +26 -0
  88. package/dist/ui.cjs +635 -254
  89. package/dist/ui.d.cts +3 -2
  90. package/dist/ui.d.ts +3 -2
  91. package/dist/ui.js +9 -8
  92. package/dist/widgets.cjs +22 -14
  93. package/dist/widgets.js +7 -7
  94. package/package.json +8 -5
package/dist/index.cjs CHANGED
@@ -28,8 +28,9 @@ __export(index_exports, {
28
28
  Loading: () => Loading,
29
29
  MAX_DRAIN_TEARDOWNS: () => MAX_DRAIN_TEARDOWNS,
30
30
  Portal: () => Portal,
31
- SVG_NS: () => SVG_NS2,
31
+ SVG_NS: () => SVG_NS3,
32
32
  Suspense: () => Suspense,
33
+ __renderParsedTemplate: () => __renderParsedTemplate,
33
34
  __resetIdCounter: () => __resetIdCounter,
34
35
  a: () => a,
35
36
  abbr: () => abbr,
@@ -411,6 +412,49 @@ function registerDisposer(node, teardown) {
411
412
  const capture = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
412
413
  if (capture) capture.entries.push([node, teardown]);
413
414
  }
415
+ function withDisposerRollback(build) {
416
+ const frame = { entries: [], closed: false };
417
+ const captured = frame.entries;
418
+ registrationCaptures.push(frame);
419
+ let result;
420
+ try {
421
+ result = build();
422
+ } catch (err) {
423
+ let executed = 0;
424
+ try {
425
+ while (captured.length > 0) {
426
+ if (executed >= MAX_DRAIN_TEARDOWNS) {
427
+ reportDrainRunaway("rollback", executed, captured.length);
428
+ break;
429
+ }
430
+ const [node, teardown] = captured.pop();
431
+ if (!unregisterDisposer(node, teardown)) continue;
432
+ executed++;
433
+ try {
434
+ teardown();
435
+ } catch (cleanupErr) {
436
+ reportError(cleanupErr, { phase: "cleanup", name: "disposer" });
437
+ }
438
+ }
439
+ } finally {
440
+ frame.closed = true;
441
+ captured.length = 0;
442
+ registrationCaptures.pop();
443
+ }
444
+ throw err;
445
+ }
446
+ frame.closed = true;
447
+ registrationCaptures.pop();
448
+ const parent = resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
449
+ if (parent) {
450
+ for (const entry of captured) {
451
+ if (elementDisposers.get(entry[0])?.includes(entry[1])) parent.entries.push(entry);
452
+ }
453
+ frame.forwardTo = parent;
454
+ }
455
+ captured.length = 0;
456
+ return result;
457
+ }
414
458
  function unregisterDisposer(node, teardown) {
415
459
  const disposers = elementDisposers.get(node);
416
460
  if (!disposers) return false;
@@ -490,6 +534,11 @@ function checkLeaks(warnThreshold = 0) {
490
534
  function stripControlChars(value) {
491
535
  return value.replace(/[\x00-\x20\x7f-\x9f]+/g, "");
492
536
  }
537
+ var ASCII_UPPER = /[A-Z]/;
538
+ var ASCII_UPPER_G = /[A-Z]/g;
539
+ function canonicalAttrName(name) {
540
+ return ASCII_UPPER.test(name) ? name.replace(ASCII_UPPER_G, (ch) => String.fromCharCode(ch.charCodeAt(0) + 32)) : name;
541
+ }
493
542
  function isEventHandlerAttr(name) {
494
543
  if (name.length < 3) return false;
495
544
  const lower = name.toLowerCase();
@@ -501,6 +550,9 @@ function sanitizeUrl(url) {
501
550
  const probe = stripControlChars(url).trim();
502
551
  if (!probe) return "";
503
552
  const output2 = url.replace(OUTER_TRIM, "");
553
+ if (probe.length >= 2 && (probe[0] === "\\" || probe[0] === "/") && (probe[1] === "\\" || probe[1] === "/") && (probe[0] === "\\" || probe[1] === "\\")) {
554
+ return "";
555
+ }
504
556
  const lower = probe.toLowerCase();
505
557
  let schemeEnd = -1;
506
558
  for (let i2 = 0; i2 < lower.length; i2++) {
@@ -517,19 +569,58 @@ function sanitizeUrl(url) {
517
569
  if (SAFE_URL_PROTOCOLS.indexOf(scheme) === -1) return "";
518
570
  return output2;
519
571
  }
520
- var SRCSET_DESCRIPTOR = /^\s+\d+(\.\d+)?[wx]$/;
572
+ var SRCSET_DESCRIPTOR = /^\d+(\.\d+)?[wx]$/;
573
+ function isSrcsetWhitespace(ch) {
574
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
575
+ }
521
576
  function sanitizeSrcset(value) {
522
- const parts = value.split(",");
577
+ const input2 = String(value);
523
578
  const out = [];
524
- for (let i2 = 0; i2 < parts.length; i2++) {
525
- const part = parts[i2].trim();
526
- if (!part) continue;
527
- const m = part.match(/^(\S+)(\s+.+)?$/);
528
- if (!m) continue;
529
- if (m[2] !== void 0 && !SRCSET_DESCRIPTOR.test(m[2])) continue;
530
- const safe = sanitizeUrl(m[1]);
579
+ let i2 = 0;
580
+ const n = input2.length;
581
+ while (i2 < n) {
582
+ while (i2 < n && (isSrcsetWhitespace(input2[i2]) || input2[i2] === ",")) i2++;
583
+ if (i2 >= n) break;
584
+ const urlStart = i2;
585
+ while (i2 < n && !isSrcsetWhitespace(input2[i2])) i2++;
586
+ let url = input2.slice(urlStart, i2);
587
+ const descriptors = [];
588
+ if (url.endsWith(",")) {
589
+ url = url.replace(/,+$/, "");
590
+ } else {
591
+ let current = "";
592
+ let inParens = false;
593
+ while (i2 < n) {
594
+ const ch = input2[i2];
595
+ if (inParens) {
596
+ current += ch;
597
+ if (ch === ")") inParens = false;
598
+ i2++;
599
+ continue;
600
+ }
601
+ if (isSrcsetWhitespace(ch)) {
602
+ if (current) descriptors.push(current);
603
+ current = "";
604
+ i2++;
605
+ continue;
606
+ }
607
+ if (ch === ",") {
608
+ i2++;
609
+ break;
610
+ }
611
+ if (ch === "(") inParens = true;
612
+ current += ch;
613
+ i2++;
614
+ }
615
+ if (current) descriptors.push(current);
616
+ }
617
+ if (url === "") continue;
618
+ if (descriptors.length > 1) continue;
619
+ if (descriptors.length === 1 && !SRCSET_DESCRIPTOR.test(descriptors[0])) continue;
620
+ const safe = sanitizeUrl(url);
531
621
  if (!safe) continue;
532
- out.push(m[2] ? `${safe}${m[2]}` : safe);
622
+ if (safe.startsWith(",") || safe.endsWith(",")) continue;
623
+ out.push(descriptors.length === 1 ? `${safe} ${descriptors[0]}` : safe);
533
624
  }
534
625
  return out.join(", ");
535
626
  }
@@ -588,8 +679,16 @@ var BLOCKED_CSS_NEEDLES = [
588
679
  "behavior:",
589
680
  "@import",
590
681
  "image-set(",
591
- "filter:progid"
682
+ "filter:progid",
683
+ // CSS Images 4 `image("…")` and CSS Values 5 `src("…")` take a URL as a
684
+ // plain STRING, so they fetch without ever spelling `url(`. Support is thin
685
+ // today, which is exactly when a blocklist should get ahead of it.
686
+ "image(",
687
+ "src("
592
688
  ];
689
+ var CSS_REASON_MOZ_BINDING = 4;
690
+ var CSS_REASON_BEHAVIOR = 5;
691
+ var CSS_REASON_PROGID = 8;
593
692
  function describeElement(el) {
594
693
  if (!el) return "an element";
595
694
  let out = el.tagName.toLowerCase();
@@ -613,7 +712,9 @@ function warnDroppedDeclaration(value, reason, ctx) {
613
712
  "behavior: loads and runs a remote HTC script",
614
713
  "@import pulls in a remote stylesheet",
615
714
  "image-set() fetches remote images, the same exfiltration channel as url()",
616
- "filter:progid activates legacy scriptable filters"
715
+ "filter:progid activates legacy scriptable filters",
716
+ "image() fetches its string argument as a URL, the same exfiltration channel as url()",
717
+ "src() fetches its string argument as a URL, the same exfiltration channel as url()"
617
718
  ];
618
719
  const declaration = ctx?.property ? `${ctx.property}: ${value}` : value;
619
720
  const where = describeElement(ctx?.element);
@@ -628,27 +729,110 @@ function warnDroppedDeclaration(value, reason, ctx) {
628
729
  return `style declaration "${declaration}" was dropped by the style sanitizer on ${where} \u2014 ${reasons[reason]}. The element renders without it, which usually looks like a missing image rather than an error. Sanctioned alternatives: for a content image use an <img> element (or a <picture>); for decoration, put the rule in a stylesheet and set a class instead \u2014 stylesheet CSS is not sanitized, only inline style attributes are. If this declaration is not yours, it came from untrusted data and the drop just prevented an exfiltration.`;
629
730
  });
630
731
  }
732
+ function normalizeCssForScan(value) {
733
+ const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
734
+ return normalized.toLowerCase().replace(/\s+/g, "");
735
+ }
631
736
  function blockedCssReason(value) {
632
737
  if (!CSS_DANGER_GATE.test(value)) return -1;
633
- const normalized = value.includes("\\") ? decodeCssEscapes(value) : value;
634
- const lower = normalized.toLowerCase().replace(/\s+/g, "");
738
+ const lower = normalizeCssForScan(value);
635
739
  for (let i2 = 0; i2 < BLOCKED_CSS_NEEDLES.length; i2++) {
636
740
  if (lower.includes(BLOCKED_CSS_NEEDLES[i2])) return i2;
637
741
  }
638
742
  return -1;
639
743
  }
640
- function sanitizeCSSValue(value, context2) {
641
- const reason = blockedCssReason(value);
744
+ function sanitizeCSSDeclaration(property, value, context2) {
745
+ const reason = blockedDeclarationReason(property, value);
642
746
  if (reason === -1) return value;
643
- warnDroppedDeclaration(value, reason, context2);
747
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
644
748
  return "";
645
749
  }
750
+ function blockedDeclarationReason(property, value) {
751
+ const reason = blockedCssReason(value);
752
+ if (reason !== -1) return reason;
753
+ const name = canonicalAttrName(property);
754
+ if (name === "behavior") return CSS_REASON_BEHAVIOR;
755
+ if (name === "-moz-binding") return CSS_REASON_MOZ_BINDING;
756
+ if ((name === "filter" || name === "-ms-filter") && normalizeCssForScan(value).includes("progid:")) {
757
+ return CSS_REASON_PROGID;
758
+ }
759
+ return -1;
760
+ }
761
+ function parseStyleDeclarations(css) {
762
+ const chunks = [];
763
+ let buf = "";
764
+ let depth = 0;
765
+ let quote2 = "";
766
+ for (let i2 = 0; i2 < css.length; i2++) {
767
+ const ch = css[i2];
768
+ if (quote2) {
769
+ buf += ch;
770
+ if (ch === "\\" && i2 + 1 < css.length) buf += css[++i2];
771
+ else if (ch === quote2) quote2 = "";
772
+ continue;
773
+ }
774
+ if (ch === "\\") {
775
+ buf += ch;
776
+ if (i2 + 1 < css.length) buf += css[++i2];
777
+ continue;
778
+ }
779
+ if (ch === "/" && css[i2 + 1] === "*") {
780
+ const end = css.indexOf("*/", i2 + 2);
781
+ if (end === -1) return null;
782
+ i2 = end + 1;
783
+ continue;
784
+ }
785
+ if (ch === '"' || ch === "'") quote2 = ch;
786
+ else if (ch === "(" || ch === "[") depth++;
787
+ else if (ch === ")" || ch === "]") {
788
+ if (--depth < 0) return null;
789
+ } else if (ch === "{" || ch === "}") return null;
790
+ else if (ch === ";" && depth === 0) {
791
+ chunks.push(buf);
792
+ buf = "";
793
+ continue;
794
+ }
795
+ buf += ch;
796
+ }
797
+ if (quote2 || depth !== 0) return null;
798
+ chunks.push(buf);
799
+ const out = [];
800
+ for (const chunk of chunks) {
801
+ const colon = chunk.indexOf(":");
802
+ if (colon === -1) continue;
803
+ const rawName = chunk.slice(0, colon).trim();
804
+ if (!/^(--[\w-]+|-?[a-zA-Z_][\w-]*)$/.test(rawName)) continue;
805
+ let value = chunk.slice(colon + 1).trim();
806
+ const bang = /!\s*important\s*$/i.exec(value);
807
+ if (bang) value = value.slice(0, bang.index).trim();
808
+ if (value === "") continue;
809
+ const property = rawName.startsWith("--") ? rawName : canonicalAttrName(rawName);
810
+ out.push([property, value, bang !== null]);
811
+ }
812
+ return out;
813
+ }
814
+ function sanitizeStyleDeclarationList(cssText, context2) {
815
+ const input2 = String(cssText);
816
+ const declarations = parseStyleDeclarations(input2);
817
+ if (declarations === null) {
818
+ if (blockedCssReason(input2) !== -1) warnDroppedDeclaration(input2, blockedCssReason(input2), context2);
819
+ return "";
820
+ }
821
+ const kept = [];
822
+ for (const [property, value, important] of declarations) {
823
+ const reason = blockedDeclarationReason(property, value);
824
+ if (reason !== -1) {
825
+ warnDroppedDeclaration(value, reason, { property, element: context2?.element });
826
+ continue;
827
+ }
828
+ kept.push(`${property}: ${value}${important ? " !important" : ""}`);
829
+ }
830
+ return kept.join("; ");
831
+ }
646
832
  function sanitizeStyleAttribute(cssText, context2) {
647
833
  const input2 = String(cssText);
648
834
  if (input2.trim() === "") return "";
649
- if (typeof document === "undefined") {
650
- return sanitizeCSSValue(input2, context2) === "" ? "" : input2;
651
- }
835
+ if (typeof document === "undefined") return sanitizeStyleDeclarationList(input2, context2);
652
836
  const probe = document.createElement("div");
653
837
  try {
654
838
  probe.style.cssText = input2;
@@ -659,8 +843,7 @@ function sanitizeStyleAttribute(cssText, context2) {
659
843
  for (let i2 = 0; i2 < probe.style.length; i2++) {
660
844
  const property = probe.style[i2];
661
845
  const value = probe.style.getPropertyValue(property);
662
- let reason = blockedCssReason(value);
663
- if (reason === -1) reason = blockedCssReason(`${property}:${value}`);
846
+ const reason = blockedDeclarationReason(property, value);
664
847
  if (reason !== -1) {
665
848
  warnDroppedDeclaration(value, reason, { property, element: context2?.element });
666
849
  continue;
@@ -692,6 +875,13 @@ var HTML_CONTENT_ATTRIBUTES = /* @__PURE__ */ new Set(["srcdoc"]);
692
875
  function isHtmlContentAttribute(name) {
693
876
  return HTML_CONTENT_ATTRIBUTES.has(name.toLowerCase());
694
877
  }
878
+ function isUrlAttribute(attr) {
879
+ return URL_ATTRIBUTES.has(attr.toLowerCase());
880
+ }
881
+ function isPolicyAttribute(name) {
882
+ const lower = name.toLowerCase();
883
+ return lower === "style" || URL_ATTRIBUTES.has(lower);
884
+ }
695
885
  function sanitizeAttributeString(attr, value, context2) {
696
886
  const lower = attr.toLowerCase();
697
887
  if (lower === "srcset") return sanitizeSrcset(value);
@@ -699,27 +889,233 @@ function sanitizeAttributeString(attr, value, context2) {
699
889
  if (lower === "style") return sanitizeStyleAttribute(value, context2);
700
890
  return value;
701
891
  }
892
+ function resolveAttributeValue(attr, value, context2) {
893
+ if (!isPolicyAttribute(attr)) return value;
894
+ const safe = sanitizeAttributeString(attr, value, context2);
895
+ return safe === "" ? null : safe;
896
+ }
702
897
 
703
- // src/utils/setSafeAttribute.ts
898
+ // src/utils/metaRefresh.ts
899
+ function isAsciiWhitespace(ch) {
900
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r" || ch === "\f";
901
+ }
902
+ function trimAsciiWhitespace(value) {
903
+ let start = 0;
904
+ let end = value.length;
905
+ while (start < end && isAsciiWhitespace(value[start])) start++;
906
+ while (end > start && isAsciiWhitespace(value[end - 1])) end--;
907
+ return value.slice(start, end);
908
+ }
909
+ var forbidden = (reason) => ({ kind: "forbidden", reason });
910
+ function parseMetaRefreshContent(content) {
911
+ const raw = trimAsciiWhitespace(stripControlChars(content));
912
+ if (raw === "") return forbidden(DEV ? "empty content" : "");
913
+ let i2 = 0;
914
+ while (i2 < raw.length && raw[i2] >= "0" && raw[i2] <= "9") i2++;
915
+ if (i2 === 0) return forbidden(DEV ? "missing or non-numeric delay" : "");
916
+ const delay = Number.parseInt(raw.slice(0, i2), 10);
917
+ if (!Number.isFinite(delay)) return forbidden(DEV ? "unparseable delay" : "");
918
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
919
+ if (i2 === raw.length) return { kind: "delay-only", delay };
920
+ if (raw[i2] !== ";" && raw[i2] !== ",") return forbidden(DEV ? "unexpected text after delay" : "");
921
+ i2++;
922
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
923
+ const keyStart = i2;
924
+ while (i2 < raw.length && !isAsciiWhitespace(raw[i2]) && raw[i2] !== "=") i2++;
925
+ const key = raw.slice(keyStart, i2).toLowerCase();
926
+ if (key !== "url") return forbidden(DEV ? `unsupported refresh key ${JSON.stringify(key)}` : "");
927
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
928
+ if (raw[i2] !== "=") return forbidden(DEV ? "missing '=' after url" : "");
929
+ i2++;
930
+ while (i2 < raw.length && isAsciiWhitespace(raw[i2])) i2++;
931
+ let destination;
932
+ const quote2 = raw[i2];
933
+ if (quote2 === '"' || quote2 === "'") {
934
+ const close = raw.indexOf(quote2, i2 + 1);
935
+ if (close === -1) return forbidden(DEV ? "unterminated quoted destination" : "");
936
+ destination = raw.slice(i2 + 1, close);
937
+ const rest = trimAsciiWhitespace(raw.slice(close + 1));
938
+ if (rest !== "") return forbidden(DEV ? "trailing text after quoted destination" : "");
939
+ } else {
940
+ destination = trimAsciiWhitespace(raw.slice(i2));
941
+ if (/[;,]/.test(destination)) return forbidden(DEV ? "multiple refresh assignments" : "");
942
+ }
943
+ if (destination === "") return forbidden(DEV ? "empty destination" : "");
944
+ const sanitizedDestination = sanitizeUrl(destination);
945
+ if (sanitizedDestination === "") return forbidden(DEV ? "destination uses a disallowed protocol" : "");
946
+ return { kind: "allowed", delay, destination, sanitizedDestination };
947
+ }
948
+ function resolveMetaRefreshPolicy(attributes) {
949
+ let httpEquiv;
950
+ let content;
951
+ let duplicateName;
952
+ const seen = /* @__PURE__ */ new Set();
953
+ for (const [key, value] of attributes) {
954
+ const canonical = canonicalAttrName(key);
955
+ if (seen.has(canonical)) duplicateName = canonical;
956
+ seen.add(canonical);
957
+ if (canonical === "http-equiv") httpEquiv = value;
958
+ else if (canonical === "content") content = value;
959
+ }
960
+ if (duplicateName !== void 0) {
961
+ return forbidden(DEV ? `duplicate case-insensitive attribute ${JSON.stringify(duplicateName)}` : "");
962
+ }
963
+ if (typeof httpEquiv !== "string") return { kind: "not-refresh" };
964
+ if (trimAsciiWhitespace(stripControlChars(httpEquiv)).toLowerCase() !== "refresh") {
965
+ return { kind: "not-refresh" };
966
+ }
967
+ if (typeof content !== "string") return { kind: "not-refresh" };
968
+ return parseMetaRefreshContent(content);
969
+ }
970
+
971
+ // src/utils/elementPolicy.ts
704
972
  var HTML_NS = "http://www.w3.org/1999/xhtml";
705
973
  var SVG_NS = "http://www.w3.org/2000/svg";
974
+ var BLOCKED_ELEMENTS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
975
+ function canonicalTagName(tag) {
976
+ return canonicalAttrName(tag);
977
+ }
978
+ function isBlockedElement(tag) {
979
+ return BLOCKED_ELEMENTS.has(canonicalTagName(tag));
980
+ }
981
+ var CODE_TEXT_ELEMENTS = /* @__PURE__ */ new Set(["script", "style"]);
982
+ function isCodeTextElement(tag) {
983
+ return CODE_TEXT_ELEMENTS.has(canonicalTagName(tag));
984
+ }
985
+ var ContextualRefusal = {
986
+ None: 0,
987
+ /** The element would carry a refresh directive the shared policy forbids. */
988
+ MetaRefreshForbidden: 1,
989
+ /** A `<meta>` with a reactive binding may not carry any refresh directive. */
990
+ MetaRefreshReactive: 2,
991
+ /** A runtime value may not choose which program a `<script>` runs. */
992
+ ScriptSource: 3,
993
+ /** An SVG animation may not target a URL, handler or document attribute. */
994
+ AnimationTarget: 4,
995
+ /** A runtime-written `href` may not become an applied stylesheet. */
996
+ StylesheetSource: 5
997
+ };
998
+ var SCRIPT_SOURCE_ATTRIBUTES = /* @__PURE__ */ new Set(["src", "href", "xlink:href", "type", "language"]);
999
+ var SVG_ANIMATION_ELEMENTS = /* @__PURE__ */ new Set(["animate", "set", "animateMotion", "animateTransform", "animateColor"]);
1000
+ var reactiveMetaElements = /* @__PURE__ */ new WeakSet();
1001
+ function isHtmlNamespace(el) {
1002
+ return el.namespaceURI === HTML_NS || el.namespaceURI === null;
1003
+ }
1004
+ function metaSnapshot(el, canonical, value) {
1005
+ const snapshot = /* @__PURE__ */ new Map();
1006
+ const attrs = el.attributes;
1007
+ for (let i2 = 0; i2 < attrs.length; i2++) {
1008
+ const attr = attrs[i2];
1009
+ if (attr.name === canonical) continue;
1010
+ snapshot.set(attr.name, attr.value);
1011
+ }
1012
+ if (canonical !== null && value !== null) snapshot.set(canonical, value);
1013
+ return snapshot;
1014
+ }
1015
+ function claimReactiveMeta(el) {
1016
+ if (reactiveMetaElements.has(el)) return;
1017
+ reactiveMetaElements.add(el);
1018
+ if (el.getAttribute("content") === null) return;
1019
+ if (resolveMetaRefreshPolicy(metaSnapshot(el, null, null)).kind !== "not-refresh") {
1020
+ el.removeAttribute("content");
1021
+ }
1022
+ }
1023
+ function metaRefusal(el, name, value, origin) {
1024
+ if (!isHtmlNamespace(el)) return ContextualRefusal.None;
1025
+ const canonical = canonicalAttrName(name);
1026
+ if (canonical !== "http-equiv" && canonical !== "content") return ContextualRefusal.None;
1027
+ if (origin === "reactive") claimReactiveMeta(el);
1028
+ if (value === null) return ContextualRefusal.None;
1029
+ if (canonical === "content" && !el.hasAttribute("http-equiv")) return ContextualRefusal.None;
1030
+ const decision = resolveMetaRefreshPolicy(metaSnapshot(el, canonical, value));
1031
+ if (decision.kind === "forbidden") return ContextualRefusal.MetaRefreshForbidden;
1032
+ if (decision.kind !== "not-refresh" && reactiveMetaElements.has(el)) return ContextualRefusal.MetaRefreshReactive;
1033
+ return ContextualRefusal.None;
1034
+ }
1035
+ function animationRefusal(name, value) {
1036
+ if (value === null || canonicalAttrName(name) !== "attributename") return ContextualRefusal.None;
1037
+ const target = stripControlChars(value);
1038
+ return isUrlAttribute(target) || isEventHandlerAttr(target) || isHtmlContentAttribute(target) ? ContextualRefusal.AnimationTarget : ContextualRefusal.None;
1039
+ }
1040
+ var runtimeHrefLinks = /* @__PURE__ */ new WeakSet();
1041
+ function relHasStylesheet(rel) {
1042
+ if (rel === null) return false;
1043
+ const lower = canonicalAttrName(rel);
1044
+ if (!lower.includes("stylesheet")) return false;
1045
+ const tokens = lower.split(/[\t\n\f\r ]+/);
1046
+ for (let i2 = 0; i2 < tokens.length; i2++) if (tokens[i2] === "stylesheet") return true;
1047
+ return false;
1048
+ }
1049
+ function stylesheetRefusal(el, name, value, origin) {
1050
+ if (!isHtmlNamespace(el)) return ContextualRefusal.None;
1051
+ const canonical = canonicalAttrName(name);
1052
+ if (canonical === "href") {
1053
+ if (origin === "static" || value === null) return ContextualRefusal.None;
1054
+ if (relHasStylesheet(el.getAttribute("rel"))) return ContextualRefusal.StylesheetSource;
1055
+ runtimeHrefLinks.add(el);
1056
+ return ContextualRefusal.None;
1057
+ }
1058
+ if (canonical === "rel") {
1059
+ if (!relHasStylesheet(value)) return ContextualRefusal.None;
1060
+ return runtimeHrefLinks.has(el) && el.hasAttribute("href") ? ContextualRefusal.StylesheetSource : ContextualRefusal.None;
1061
+ }
1062
+ return ContextualRefusal.None;
1063
+ }
1064
+ function contextualAttributeRefusal(el, name, value, origin) {
1065
+ const local = el.localName;
1066
+ if (local === "meta") return metaRefusal(el, name, value, origin);
1067
+ if (local === "link") return stylesheetRefusal(el, name, value, origin);
1068
+ if (local === "script") {
1069
+ if (origin === "static" || value === null) return ContextualRefusal.None;
1070
+ return SCRIPT_SOURCE_ATTRIBUTES.has(canonicalAttrName(name)) ? ContextualRefusal.ScriptSource : ContextualRefusal.None;
1071
+ }
1072
+ if (SVG_ANIMATION_ELEMENTS.has(local) && el.namespaceURI === SVG_NS) return animationRefusal(name, value);
1073
+ return ContextualRefusal.None;
1074
+ }
1075
+ function settleAttributeProvenance(el, name, origin) {
1076
+ if (el.localName !== "link" || canonicalAttrName(name) !== "href") return;
1077
+ if (origin !== "static" && el.hasAttribute("href")) runtimeHrefLinks.add(el);
1078
+ else runtimeHrefLinks.delete(el);
1079
+ }
1080
+
1081
+ // src/utils/setSafeAttribute.ts
1082
+ var HTML_NS2 = "http://www.w3.org/1999/xhtml";
1083
+ var SVG_NS2 = "http://www.w3.org/2000/svg";
706
1084
  var XLINK_NS = "http://www.w3.org/1999/xlink";
707
1085
  var XML_NS = "http://www.w3.org/XML/1998/namespace";
708
1086
  var BOOLEAN_IDL_ATTRS = /* @__PURE__ */ new Set(["checked", "disabled", "selected"]);
709
1087
  function isHtmlElement(el) {
710
- return el.namespaceURI === null || el.namespaceURI === HTML_NS;
1088
+ return el.namespaceURI === null || el.namespaceURI === HTML_NS2;
1089
+ }
1090
+ function warnContextualRefusal(label2, name, refusal) {
1091
+ devWarnLazy(() => {
1092
+ const reasons = [
1093
+ "",
1094
+ 'the element would become a <meta http-equiv="refresh"> directive the shared refresh policy forbids (a destination outside the URL allowlist, or a directive too malformed to read unambiguously)',
1095
+ "a <meta> whose http-equiv or content is reactive may never carry a refresh directive: a browser schedules the navigation as soon as the directive is valid, and nothing can withdraw it when the state changes back",
1096
+ "a runtime value may not choose the program a <script> runs; name the script in static template source, or load it through Head({ script }) as an explicit trust decision",
1097
+ "an SVG animation may not target a URL, event-handler or nested-document attribute; its to / values would be written into that attribute without passing the URL policy",
1098
+ 'a runtime-chosen href may not become an applied <link rel="stylesheet">: a well-formed URL is not a trusted stylesheet. Name the stylesheet in static template source, or load a runtime-chosen one through Head({ link }) as an explicit trust decision'
1099
+ ];
1100
+ return `${label2}: refusing "${name}" in this element's context \u2014 ${reasons[refusal]}. The write was not performed.`;
1101
+ });
711
1102
  }
712
1103
  function setProp(el, key, val) {
713
1104
  el[key] = val;
714
1105
  }
715
1106
  function namespaceFor(el, name) {
716
- if (el.namespaceURI !== SVG_NS) return null;
1107
+ if (el.namespaceURI !== SVG_NS2) return null;
717
1108
  const lower = name.toLowerCase();
718
1109
  if (lower.startsWith("xlink:")) return XLINK_NS;
719
1110
  if (lower.startsWith("xml:")) return XML_NS;
720
1111
  return null;
721
1112
  }
722
1113
  function setSafeAttribute(el, name, value, options = {}) {
1114
+ const written = commitSafeAttribute(el, name, value, options);
1115
+ if (el.localName === "link") settleAttributeProvenance(el, name, options.reactive ? "reactive" : "runtime");
1116
+ return written;
1117
+ }
1118
+ function commitSafeAttribute(el, name, value, options) {
723
1119
  const ns = namespaceFor(el, name);
724
1120
  const localName = ns ? name.slice(name.indexOf(":") + 1) : name;
725
1121
  if (isEventHandlerAttr(name)) {
@@ -732,6 +1128,14 @@ function setSafeAttribute(el, name, value, options = {}) {
732
1128
  else el.removeAttribute(name);
733
1129
  return false;
734
1130
  }
1131
+ const pending = value == null || value === false ? null : value === true ? "" : String(value);
1132
+ const refusal = contextualAttributeRefusal(el, name, pending, options.reactive ? "reactive" : "runtime");
1133
+ if (refusal !== ContextualRefusal.None) {
1134
+ warnContextualRefusal(options.label ?? "setSafeAttribute", name, refusal);
1135
+ if (ns) el.removeAttributeNS(ns, localName);
1136
+ else el.removeAttribute(name);
1137
+ return false;
1138
+ }
735
1139
  if (value == null) {
736
1140
  if (ns) el.removeAttributeNS(ns, localName);
737
1141
  else el.removeAttribute(name);
@@ -775,13 +1179,32 @@ function setSafeAttribute(el, name, value, options = {}) {
775
1179
  setProp(el, idlName, idlName === "checked" ? Boolean(value) : str);
776
1180
  return true;
777
1181
  }
778
- const safe = sanitizeAttributeString(name, str, { element: el });
1182
+ const safe = str === "" ? "" : resolveAttributeValue(name, str, { element: el });
1183
+ if (safe === null) {
1184
+ if (ns) el.removeAttributeNS(ns, localName);
1185
+ else el.removeAttribute(name);
1186
+ return true;
1187
+ }
779
1188
  const current = ns ? el.getAttributeNS(ns, localName) : el.getAttribute(name);
780
1189
  if (current === safe) return true;
781
1190
  if (ns) el.setAttributeNS(ns, name, safe);
782
1191
  else el.setAttribute(name, safe);
783
1192
  return true;
784
1193
  }
1194
+ function setTrustedAttribute(el, name, value) {
1195
+ if (isHtmlContentAttribute(name)) {
1196
+ if (DEV) devWarn(`html: refusing static attribute "${name}" \u2014 the browser parses it as a nested document.`);
1197
+ return false;
1198
+ }
1199
+ const refusal = contextualAttributeRefusal(el, name, value, "static");
1200
+ if (refusal !== ContextualRefusal.None) {
1201
+ warnContextualRefusal("html (static attribute)", name, refusal);
1202
+ return false;
1203
+ }
1204
+ el.setAttribute(name, value);
1205
+ if (el.localName === "link") settleAttributeProvenance(el, name, "static");
1206
+ return true;
1207
+ }
785
1208
 
786
1209
  // src/reactivity/track-core.ts
787
1210
  var POOL_MAX = 4096;
@@ -1274,7 +1697,7 @@ function forEachSubscriber(signal2, visit) {
1274
1697
  }
1275
1698
 
1276
1699
  // src/reactivity/track.ts
1277
- var _runtimeVersion = true ? "4.6.0" : "dev";
1700
+ var _runtimeVersion = true ? "4.8.0" : "dev";
1278
1701
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
1279
1702
  function resolveReactiveApi() {
1280
1703
  const g2 = globalThis;
@@ -1345,7 +1768,7 @@ function bindAttribute(el, attr, getter) {
1345
1768
  reportError(err, { phase: "binding", name: `bindAttribute("${attr}")`, node: el });
1346
1769
  return;
1347
1770
  }
1348
- setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")` });
1771
+ setSafeAttribute(el, attr, value, { label: `bindAttribute("${attr}")`, reactive: true });
1349
1772
  }
1350
1773
  return reactiveBinding2(commit, el);
1351
1774
  }
@@ -1370,7 +1793,7 @@ function bindDynamic(el, nameGetter, valueGetter) {
1370
1793
  el.removeAttribute(prevName);
1371
1794
  prevName = null;
1372
1795
  }
1373
- if (!setSafeAttribute(el, name, value, { label: "bindDynamic" })) return;
1796
+ if (!setSafeAttribute(el, name, value, { label: "bindDynamic", reactive: true })) return;
1374
1797
  prevName = name;
1375
1798
  }
1376
1799
  const teardown = reactiveBinding2(commit, el);
@@ -1551,11 +1974,7 @@ function bindChildNode(placeholder, getter) {
1551
1974
  }
1552
1975
 
1553
1976
  // src/core/rendering/tagFactory.ts
1554
- var SVG_NS2 = "http://www.w3.org/2000/svg";
1555
- var BLOCKED_TAGS = /* @__PURE__ */ new Set(["script", "iframe", "object", "embed", "frame", "frameset"]);
1556
- function isBlockedTag(tag) {
1557
- return BLOCKED_TAGS.has(tag.toLowerCase());
1558
- }
1977
+ var SVG_NS3 = "http://www.w3.org/2000/svg";
1559
1978
  var CLOBBER_RISKY_IDS = /* @__PURE__ */ new Set([
1560
1979
  "config",
1561
1980
  "location",
@@ -1582,8 +2001,35 @@ function toKebab(prop) {
1582
2001
  }
1583
2002
  function applyStyle(el, style2) {
1584
2003
  if (typeof style2 === "function") {
2004
+ const getter = style2;
2005
+ let written = [];
2006
+ let wroteString = false;
1585
2007
  const teardown = reactiveBinding2(() => {
1586
- el.setAttribute("style", sanitizeStyleAttribute(String(style2()), { element: el }));
2008
+ const value = getter();
2009
+ if (value === null || typeof value !== "object") {
2010
+ written = [];
2011
+ wroteString = true;
2012
+ el.setAttribute("style", sanitizeStyleAttribute(String(value), { element: el }));
2013
+ return;
2014
+ }
2015
+ const decl = el.style;
2016
+ if (wroteString) {
2017
+ el.removeAttribute("style");
2018
+ wroteString = false;
2019
+ }
2020
+ const next = [];
2021
+ for (const prop in value) {
2022
+ if (!Object.hasOwn(value, prop)) continue;
2023
+ const val = value[prop];
2024
+ if (val == null) continue;
2025
+ const name = toKebab(prop);
2026
+ next.push(name);
2027
+ decl.setProperty(name, sanitizeCSSDeclaration(name, String(val), { element: el }));
2028
+ }
2029
+ for (let i2 = 0; i2 < written.length; i2++) {
2030
+ if (next.indexOf(written[i2]) === -1) decl.removeProperty(written[i2]);
2031
+ }
2032
+ written = next;
1587
2033
  }, el);
1588
2034
  registerDisposer(el, teardown);
1589
2035
  return;
@@ -1594,16 +2040,17 @@ function applyStyle(el, style2) {
1594
2040
  }
1595
2041
  const htmlEl = el;
1596
2042
  for (const prop in style2) {
2043
+ if (!Object.hasOwn(style2, prop)) continue;
1597
2044
  const val = style2[prop];
1598
2045
  const name = toKebab(prop);
1599
2046
  if (typeof val === "function") {
1600
2047
  const getter = val;
1601
2048
  const teardown = reactiveBinding2(() => {
1602
- htmlEl.style.setProperty(name, sanitizeCSSValue(String(getter()), { property: name, element: el }));
2049
+ htmlEl.style.setProperty(name, sanitizeCSSDeclaration(name, String(getter()), { element: el }));
1603
2050
  }, el);
1604
2051
  registerDisposer(el, teardown);
1605
2052
  } else {
1606
- htmlEl.style.setProperty(name, sanitizeCSSValue(String(val), { property: name, element: el }));
2053
+ htmlEl.style.setProperty(name, sanitizeCSSDeclaration(name, String(val), { element: el }));
1607
2054
  }
1608
2055
  }
1609
2056
  }
@@ -1614,7 +2061,9 @@ function applyClass(el, cls) {
1614
2061
  }
1615
2062
  if (typeof cls === "function") {
1616
2063
  const teardown = reactiveBinding2(() => {
1617
- el.setAttribute("class", cls());
2064
+ const value = cls();
2065
+ if (value == null || value === false) el.removeAttribute("class");
2066
+ else el.setAttribute("class", value);
1618
2067
  }, el);
1619
2068
  registerDisposer(el, teardown);
1620
2069
  return;
@@ -1623,6 +2072,7 @@ function applyClass(el, cls) {
1623
2072
  let hasReactive = false;
1624
2073
  let result = "";
1625
2074
  for (const name in obj) {
2075
+ if (!Object.hasOwn(obj, name)) continue;
1626
2076
  const val = obj[name];
1627
2077
  if (typeof val === "function") {
1628
2078
  hasReactive = true;
@@ -1634,6 +2084,7 @@ function applyClass(el, cls) {
1634
2084
  const update = () => {
1635
2085
  let r = "";
1636
2086
  for (const name in obj) {
2087
+ if (!Object.hasOwn(obj, name)) continue;
1637
2088
  const val = obj[name];
1638
2089
  const active = typeof val === "function" ? val() : val;
1639
2090
  if (active) r = r ? `${r} ${name}` : name;
@@ -1696,8 +2147,8 @@ function appendChildren(el, nodes) {
1696
2147
  }
1697
2148
  }
1698
2149
  }
1699
- var tagFactory = (tag, ns) => {
1700
- const blocked = isBlockedTag(tag);
2150
+ function tagFactory(tag, ns) {
2151
+ const blocked = isBlockedElement(tag);
1701
2152
  return (first, second) => {
1702
2153
  if (blocked) {
1703
2154
  throw new Error(`tagFactory: refusing to create <${tag}> \u2014 tag is blocked for security reasons.`);
@@ -1745,7 +2196,9 @@ var tagFactory = (tag, ns) => {
1745
2196
  const pClass = props.class;
1746
2197
  if (pClass != null) applyClass(el, pClass);
1747
2198
  const pId = props.id;
1748
- if (pId != null) {
2199
+ if (typeof pId === "function") {
2200
+ registerDisposer(el, bindAttribute(el, "id", pId));
2201
+ } else if (pId != null) {
1749
2202
  if (DEV && typeof pId === "string" && CLOBBER_RISKY_IDS.has(pId.toLowerCase())) {
1750
2203
  devWarn(
1751
2204
  `tagFactory: element id="${pId}" matches a common global and may cause DOM clobbering. Avoid setting ids from untrusted input.`
@@ -1758,6 +2211,7 @@ var tagFactory = (tag, ns) => {
1758
2211
  const pOn = props.on;
1759
2212
  if (pOn) {
1760
2213
  for (const ev in pOn) {
2214
+ if (!Object.hasOwn(pOn, ev)) continue;
1761
2215
  const handler = pOn[ev];
1762
2216
  if (typeof handler === "function") {
1763
2217
  el.addEventListener(ev, handler);
@@ -1784,6 +2238,7 @@ var tagFactory = (tag, ns) => {
1784
2238
  continue;
1785
2239
  // already handled above / below
1786
2240
  default: {
2241
+ if (!Object.hasOwn(props, key)) continue;
1787
2242
  const value = props[key];
1788
2243
  if (value == null) continue;
1789
2244
  if (isEventHandlerAttr(key)) continue;
@@ -1795,12 +2250,10 @@ var tagFactory = (tag, ns) => {
1795
2250
  }
1796
2251
  }
1797
2252
  }
1798
- if (props.onElement && typeof props.onElement === "function") {
1799
- props.onElement(el);
1800
- }
2253
+ if (typeof props.onElement === "function") props.onElement(el);
1801
2254
  return el;
1802
2255
  };
1803
- };
2256
+ }
1804
2257
 
1805
2258
  // src/core/rendering/html.ts
1806
2259
  var html = tagFactory("html");
@@ -1875,7 +2328,7 @@ var param = tagFactory("param");
1875
2328
  var picture = tagFactory("picture");
1876
2329
  var portal = tagFactory("portal");
1877
2330
  var source = tagFactory("source");
1878
- var svg = tagFactory("svg", SVG_NS2);
2331
+ var svg = tagFactory("svg", SVG_NS3);
1879
2332
  var math = tagFactory("math");
1880
2333
  var canvas = tagFactory("canvas");
1881
2334
  var noscript = tagFactory("noscript");
@@ -1916,26 +2369,26 @@ var base = tagFactory("base");
1916
2369
  var link = tagFactory("link");
1917
2370
  var meta = tagFactory("meta");
1918
2371
  var style = tagFactory("style");
1919
- var circle = tagFactory("circle", SVG_NS2);
1920
- var ellipse = tagFactory("ellipse", SVG_NS2);
1921
- var g = tagFactory("g", SVG_NS2);
1922
- var line = tagFactory("line", SVG_NS2);
1923
- var path = tagFactory("path", SVG_NS2);
1924
- var polygon = tagFactory("polygon", SVG_NS2);
1925
- var polyline = tagFactory("polyline", SVG_NS2);
1926
- var rect = tagFactory("rect", SVG_NS2);
1927
- var text = tagFactory("text", SVG_NS2);
1928
- var tspan = tagFactory("tspan", SVG_NS2);
1929
- var defs = tagFactory("defs", SVG_NS2);
1930
- var clipPath = tagFactory("clipPath", SVG_NS2);
1931
- var mask = tagFactory("mask", SVG_NS2);
1932
- var pattern = tagFactory("pattern", SVG_NS2);
1933
- var linearGradient = tagFactory("linearGradient", SVG_NS2);
1934
- var radialGradient = tagFactory("radialGradient", SVG_NS2);
1935
- var stop = tagFactory("stop", SVG_NS2);
1936
- var use = tagFactory("use", SVG_NS2);
1937
- var symbol = tagFactory("symbol", SVG_NS2);
1938
- var marker = tagFactory("marker", SVG_NS2);
2372
+ var circle = tagFactory("circle", SVG_NS3);
2373
+ var ellipse = tagFactory("ellipse", SVG_NS3);
2374
+ var g = tagFactory("g", SVG_NS3);
2375
+ var line = tagFactory("line", SVG_NS3);
2376
+ var path = tagFactory("path", SVG_NS3);
2377
+ var polygon = tagFactory("polygon", SVG_NS3);
2378
+ var polyline = tagFactory("polyline", SVG_NS3);
2379
+ var rect = tagFactory("rect", SVG_NS3);
2380
+ var text = tagFactory("text", SVG_NS3);
2381
+ var tspan = tagFactory("tspan", SVG_NS3);
2382
+ var defs = tagFactory("defs", SVG_NS3);
2383
+ var clipPath = tagFactory("clipPath", SVG_NS3);
2384
+ var mask = tagFactory("mask", SVG_NS3);
2385
+ var pattern = tagFactory("pattern", SVG_NS3);
2386
+ var linearGradient = tagFactory("linearGradient", SVG_NS3);
2387
+ var radialGradient = tagFactory("radialGradient", SVG_NS3);
2388
+ var stop = tagFactory("stop", SVG_NS3);
2389
+ var use = tagFactory("use", SVG_NS3);
2390
+ var symbol = tagFactory("symbol", SVG_NS3);
2391
+ var marker = tagFactory("marker", SVG_NS3);
1939
2392
  var center = tagFactory("center");
1940
2393
  var font = tagFactory("font");
1941
2394
  var marquee = tagFactory("marquee");
@@ -2049,10 +2502,13 @@ function safeCall(cb, hookName) {
2049
2502
  return void 0;
2050
2503
  }
2051
2504
  }
2052
- function runMountCallback(callback, hookName, element) {
2505
+ function runMountCallback(callback, hookName, element, isDisposed) {
2053
2506
  const cleanup3 = safeCall(callback, hookName);
2054
- if (typeof cleanup3 === "function" && element) {
2055
- registerDisposer(element, cleanup3);
2507
+ if (typeof cleanup3 !== "function") return;
2508
+ if (isDisposed() || !element.isConnected) {
2509
+ safeCall(cleanup3, "onUnmount");
2510
+ } else {
2511
+ onUnmount(cleanup3, element);
2056
2512
  }
2057
2513
  }
2058
2514
  var mountWatchers = /* @__PURE__ */ new WeakMap();
@@ -2214,25 +2670,26 @@ function onMount(callback, element) {
2214
2670
  if (typeof document === "undefined") return;
2215
2671
  if (element) {
2216
2672
  let disposed = false;
2673
+ const isDisposed = () => disposed;
2217
2674
  registerDisposer(element, () => {
2218
2675
  disposed = true;
2219
2676
  });
2220
2677
  if (element.isConnected) {
2221
2678
  queueMicrotask(() => {
2222
2679
  if (disposed) return;
2223
- runMountCallback(callback, "onMount", element);
2680
+ runMountCallback(callback, "onMount", element, isDisposed);
2224
2681
  });
2225
2682
  return;
2226
2683
  }
2227
2684
  queueMicrotask(() => {
2228
2685
  if (disposed) return;
2229
2686
  if (element.isConnected) {
2230
- runMountCallback(callback, "onMount", element);
2687
+ runMountCallback(callback, "onMount", element, isDisposed);
2231
2688
  return;
2232
2689
  }
2233
2690
  const unregister = registerMountWatcher(element, () => {
2234
2691
  if (disposed) return;
2235
- runMountCallback(callback, "onMount", element);
2692
+ runMountCallback(callback, "onMount", element, isDisposed);
2236
2693
  });
2237
2694
  registerDisposer(element, unregister);
2238
2695
  });
@@ -2269,6 +2726,14 @@ function onCleanup(callback, element) {
2269
2726
  registerDisposer(element, callback);
2270
2727
  }
2271
2728
 
2729
+ // src/core/devtoolsHook.ts
2730
+ function emitDevtools(hook, event, payload) {
2731
+ try {
2732
+ hook.emit(event, payload);
2733
+ } catch {
2734
+ }
2735
+ }
2736
+
2272
2737
  // src/core/ssr-context.ts
2273
2738
  var SSR_KEY = /* @__PURE__ */ Symbol.for("sibujs.ssr.v1");
2274
2739
  function detectSSRShared() {
@@ -2506,12 +2971,17 @@ function effect(effectFn, options) {
2506
2971
  try {
2507
2972
  retrack2(ctx.bodyFn, ctx.subscriber);
2508
2973
  if (ctx.rerunPending) drainReruns(ctx);
2974
+ } catch (err) {
2975
+ ctx.disposed = true;
2976
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2977
+ cleanup2(ctx.subscriber);
2978
+ throw err;
2509
2979
  } finally {
2510
2980
  ctx.running = false;
2511
2981
  ctx.rerunPending = false;
2512
2982
  }
2513
2983
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2514
- if (hook) hook.emit("effect:create", { effectFn });
2984
+ if (hook) emitDevtools(hook, "effect:create", { effectFn });
2515
2985
  return () => disposeEffect(ctx);
2516
2986
  }
2517
2987
 
@@ -2598,7 +3068,7 @@ function signal(initial, options) {
2598
3068
  state2.__v++;
2599
3069
  if (DEV) {
2600
3070
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2601
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3071
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
2602
3072
  }
2603
3073
  if (!enqueueBatchedSignal(state2)) {
2604
3074
  notifySubscribers2(state2);
@@ -2612,7 +3082,7 @@ function signal(initial, options) {
2612
3082
  state2.value = newValue;
2613
3083
  state2.__v++;
2614
3084
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2615
- if (hook) hook.emit("signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
3085
+ if (hook) emitDevtools(hook, "signal:update", { signal: state2, name: debugName, oldValue: prev, newValue });
2616
3086
  if (!enqueueBatchedSignal(state2)) {
2617
3087
  notifySubscribers2(state2);
2618
3088
  }
@@ -2631,7 +3101,7 @@ function signal(initial, options) {
2631
3101
  }
2632
3102
  if (DEV) {
2633
3103
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2634
- if (hook) hook.emit("signal:create", { signal: state2, name: debugName, getter: get, initial });
3104
+ if (hook) emitDevtools(hook, "signal:create", { signal: state2, name: debugName, getter: get, initial });
2635
3105
  }
2636
3106
  return [get, set];
2637
3107
  }
@@ -3444,36 +3914,34 @@ function Loading(props = {}) {
3444
3914
  const { text: text2, variant = "spinner", size = "md" } = props;
3445
3915
  const sizeClass = size !== "md" ? ` sibu-loading-${size}` : "";
3446
3916
  if (variant === "dots") {
3447
- return div({
3917
+ return div(
3918
+ {
3919
+ class: `sibu-loading${sizeClass}`,
3920
+ role: "status",
3921
+ "aria-live": "polite",
3922
+ // When there's no visible text, give the live region an accessible name
3923
+ // so it isn't announced as an empty status.
3924
+ "aria-label": text2 ? void 0 : "Loading"
3925
+ },
3926
+ [
3927
+ div({ class: "sibu-loading-dots" }, [
3928
+ span({ class: "sibu-loading-dot" }),
3929
+ span({ class: "sibu-loading-dot" }),
3930
+ span({ class: "sibu-loading-dot" })
3931
+ ]),
3932
+ text2 ? span({ class: "sibu-loading-text" }, text2) : null
3933
+ ]
3934
+ );
3935
+ }
3936
+ return div(
3937
+ {
3448
3938
  class: `sibu-loading${sizeClass}`,
3449
3939
  role: "status",
3450
3940
  "aria-live": "polite",
3451
- // When there's no visible text, give the live region an accessible name
3452
- // so it isn't announced as an empty status.
3453
- "aria-label": text2 ? void 0 : "Loading",
3454
- nodes: [
3455
- div({
3456
- class: "sibu-loading-dots",
3457
- nodes: [
3458
- span({ class: "sibu-loading-dot" }),
3459
- span({ class: "sibu-loading-dot" }),
3460
- span({ class: "sibu-loading-dot" })
3461
- ]
3462
- }),
3463
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
3464
- ].filter(Boolean)
3465
- });
3466
- }
3467
- return div({
3468
- class: `sibu-loading${sizeClass}`,
3469
- role: "status",
3470
- "aria-live": "polite",
3471
- "aria-label": text2 ? void 0 : "Loading",
3472
- nodes: [
3473
- div({ class: "sibu-loading-spinner" }),
3474
- text2 ? span({ class: "sibu-loading-text", nodes: text2 }) : null
3475
- ].filter(Boolean)
3476
- });
3941
+ "aria-label": text2 ? void 0 : "Loading"
3942
+ },
3943
+ [div({ class: "sibu-loading-spinner" }), text2 ? span({ class: "sibu-loading-text" }, text2) : null]
3944
+ );
3477
3945
  }
3478
3946
 
3479
3947
  // src/core/rendering/action.ts
@@ -3579,13 +4047,35 @@ registerAction("copyOnClick", copyOnClick);
3579
4047
  registerAction("autoResize", autoResize);
3580
4048
  registerAction("trapFocus", trapFocus);
3581
4049
 
4050
+ // src/utils/adoptThenable.ts
4051
+ function adoptThenable(value) {
4052
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
4053
+ let then;
4054
+ try {
4055
+ then = value.then;
4056
+ } catch (error) {
4057
+ return Promise.reject(error);
4058
+ }
4059
+ if (typeof then !== "function") return null;
4060
+ return new Promise((resolve, reject) => {
4061
+ queueMicrotask(() => {
4062
+ try {
4063
+ Reflect.apply(then, value, [resolve, reject]);
4064
+ } catch (error) {
4065
+ reject(error);
4066
+ }
4067
+ });
4068
+ });
4069
+ }
4070
+
3582
4071
  // src/core/rendering/catch.ts
3583
4072
  var globalErrorHandler = null;
3584
4073
  function catchError(fn, onError) {
3585
4074
  try {
3586
4075
  const result = fn();
3587
- if (result && typeof result.then === "function") {
3588
- result.catch((err) => {
4076
+ const adopted = adoptThenable(result);
4077
+ if (adopted) {
4078
+ adopted.then(void 0, (err) => {
3589
4079
  if (onError) {
3590
4080
  onError(err, "async");
3591
4081
  } else if (globalErrorHandler) {
@@ -3712,6 +4202,10 @@ function attachBranch(parent, anchor, branch, attachedOnce, where) {
3712
4202
  parent.insertBefore(node, anchor.nextSibling);
3713
4203
  return { node, owned };
3714
4204
  }
4205
+ function releaseBranchNode(node, owned) {
4206
+ if (owned) dispose(node);
4207
+ node.parentNode?.removeChild(node);
4208
+ }
3715
4209
  function when(condition, thenBranch, elseBranch) {
3716
4210
  const anchor = document.createComment("when");
3717
4211
  let currentNode = null;
@@ -3725,16 +4219,15 @@ function when(condition, thenBranch, elseBranch) {
3725
4219
  const show2 = condition();
3726
4220
  const parent = anchor.parentNode;
3727
4221
  if (!parent) return;
3728
- if (initialized && show2 === lastCondition) return;
4222
+ if (initialized && Object.is(show2, lastCondition)) return;
3729
4223
  lastCondition = show2;
3730
4224
  const focused = currentNode ? captureFocusWithin([currentNode]) : null;
3731
4225
  if (currentNode?.parentNode) {
3732
- if (currentNodeOwned) dispose(currentNode);
3733
- currentNode.parentNode.removeChild(currentNode);
4226
+ releaseBranchNode(currentNode, currentNodeOwned);
3734
4227
  currentNode = null;
3735
4228
  }
3736
4229
  const branch = show2 ? thenBranch : elseBranch !== void 0 ? elseBranch : null;
3737
- const attached = attachBranch(parent, anchor, branch, attachedOnce, "when");
4230
+ const attached = untracked2(() => attachBranch(parent, anchor, branch, attachedOnce, "when"));
3738
4231
  currentNode = attached.node;
3739
4232
  currentNodeOwned = attached.owned;
3740
4233
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "when");
@@ -3744,6 +4237,8 @@ function when(condition, thenBranch, elseBranch) {
3744
4237
  registerDisposer(anchor, () => {
3745
4238
  disposed = true;
3746
4239
  stopBinding();
4240
+ if (currentNode) releaseBranchNode(currentNode, currentNodeOwned);
4241
+ currentNode = null;
3747
4242
  });
3748
4243
  if (!initialized) {
3749
4244
  queueMicrotask(() => {
@@ -3769,12 +4264,11 @@ function match(value, cases, fallback) {
3769
4264
  lastKey = key;
3770
4265
  const focused = currentNode ? captureFocusWithin([currentNode]) : null;
3771
4266
  if (currentNode?.parentNode) {
3772
- if (currentNodeOwned) dispose(currentNode);
3773
- currentNode.parentNode.removeChild(currentNode);
4267
+ releaseBranchNode(currentNode, currentNodeOwned);
3774
4268
  currentNode = null;
3775
4269
  }
3776
4270
  const branch = Object.hasOwn(cases, key) ? cases[key] : fallback;
3777
- const attached = attachBranch(parent, anchor, branch ?? null, attachedOnce, "match");
4271
+ const attached = untracked2(() => attachBranch(parent, anchor, branch ?? null, attachedOnce, "match"));
3778
4272
  currentNode = attached.node;
3779
4273
  currentNodeOwned = attached.owned;
3780
4274
  restoreFocusWithin(focused, currentNode ? [currentNode] : [], "match");
@@ -3784,6 +4278,8 @@ function match(value, cases, fallback) {
3784
4278
  registerDisposer(anchor, () => {
3785
4279
  disposed = true;
3786
4280
  stopBinding();
4281
+ if (currentNode) releaseBranchNode(currentNode, currentNodeOwned);
4282
+ currentNode = null;
3787
4283
  });
3788
4284
  if (!initialized) {
3789
4285
  queueMicrotask(() => {
@@ -3804,11 +4300,17 @@ var componentRegistry = globalSingleton(
3804
4300
  /* @__PURE__ */ Symbol.for("sibujs.components.registry.v1"),
3805
4301
  () => /* @__PURE__ */ new Map()
3806
4302
  );
4303
+ var registryVersion = globalSingleton(/* @__PURE__ */ Symbol.for("sibujs.components.registryVersion.v1"), () => signal(0));
4304
+ function bumpRegistry() {
4305
+ const [read, write] = registryVersion;
4306
+ write(untracked2(read) + 1);
4307
+ }
3807
4308
  function registerComponent(name, component) {
3808
4309
  componentRegistry.set(name, component);
4310
+ bumpRegistry();
3809
4311
  }
3810
4312
  function unregisterComponent(name) {
3811
- componentRegistry.delete(name);
4313
+ if (componentRegistry.delete(name)) bumpRegistry();
3812
4314
  }
3813
4315
  function resolveComponent(name) {
3814
4316
  const component = componentRegistry.get(name);
@@ -3819,14 +4321,21 @@ function resolveComponent(name) {
3819
4321
  }
3820
4322
  function DynamicComponent(is) {
3821
4323
  const container = div({ class: "sibu-dynamic" });
4324
+ const NONE = {};
4325
+ let shown = NONE;
3822
4326
  function render() {
3823
4327
  const target = is();
3824
- let el;
4328
+ let component;
3825
4329
  if (typeof target === "function") {
3826
- el = target();
4330
+ component = target;
3827
4331
  } else {
3828
- el = resolveComponent(target);
4332
+ registryVersion[0]();
4333
+ component = componentRegistry.get(target);
3829
4334
  }
4335
+ const next = component ?? `missing:${target}`;
4336
+ if (next === shown) return;
4337
+ const el = untracked2(() => component ? component() : div(`[Component "${target}" not found]`));
4338
+ shown = next;
3830
4339
  replaceChildrenSafely(container, el);
3831
4340
  }
3832
4341
  const untrack = track2(render);
@@ -4090,31 +4599,29 @@ function Fragment(nodes) {
4090
4599
  if (Array.isArray(child)) {
4091
4600
  for (const nested of child) {
4092
4601
  if (nested == null || typeof nested === "boolean") continue;
4093
- frag.appendChild(resolveChild(nested));
4602
+ appendChild(frag, nested);
4094
4603
  }
4095
4604
  } else {
4096
- frag.appendChild(resolveChild(child));
4605
+ appendChild(frag, child);
4097
4606
  }
4098
4607
  }
4099
4608
  return frag;
4100
4609
  }
4101
- function resolveChild(child) {
4102
- if (child == null) {
4103
- return document.createTextNode("");
4104
- }
4610
+ function appendChild(frag, child) {
4105
4611
  if (child instanceof Node) {
4106
- return child;
4612
+ frag.appendChild(child);
4613
+ return;
4107
4614
  }
4108
4615
  if (typeof child === "function") {
4109
- const result = child();
4110
- if (result instanceof Node) return result;
4111
- return document.createTextNode(String(result ?? ""));
4616
+ const placeholder = document.createComment("");
4617
+ frag.appendChild(placeholder);
4618
+ registerDisposer(placeholder, bindChildNode(placeholder, child));
4619
+ return;
4112
4620
  }
4113
- return document.createTextNode(String(child));
4621
+ frag.appendChild(document.createTextNode(String(child)));
4114
4622
  }
4115
4623
 
4116
4624
  // src/core/rendering/htm.ts
4117
- var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
4118
4625
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
4119
4626
  "area",
4120
4627
  "base",
@@ -4337,7 +4844,7 @@ function parseTemplate(strings) {
4337
4844
  children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: [] } });
4338
4845
  } else {
4339
4846
  const inner = parseChildren();
4340
- if (RAW_TEXT_TAGS.has(tag.toLowerCase())) {
4847
+ if (isCodeTextElement(tag)) {
4341
4848
  for (let i2 = 0; i2 < inner.length; i2++) {
4342
4849
  if (inner[i2].t === 2) {
4343
4850
  throw new Error(
@@ -4363,12 +4870,12 @@ function parseTemplate(strings) {
4363
4870
  return parseChildren();
4364
4871
  }
4365
4872
  function executeElement(tmpl, values) {
4366
- const el = tmpl.svg ? document.createElementNS(SVG_NS2, tmpl.tag) : document.createElement(tmpl.tag);
4873
+ const el = tmpl.svg ? document.createElementNS(SVG_NS3, tmpl.tag) : document.createElement(tmpl.tag);
4367
4874
  for (let i2 = 0; i2 < tmpl.attrs.length; i2++) {
4368
4875
  const attr = tmpl.attrs[i2];
4369
4876
  switch (attr.t) {
4370
4877
  case 0:
4371
- el.setAttribute(attr.name, attr.value);
4878
+ setTrustedAttribute(el, attr.name, attr.value);
4372
4879
  break;
4373
4880
  case 1: {
4374
4881
  const name = attr.name;
@@ -4401,7 +4908,7 @@ function executeElement(tmpl, values) {
4401
4908
  break;
4402
4909
  }
4403
4910
  case 4:
4404
- el.setAttribute(attr.name, "");
4911
+ setTrustedAttribute(el, attr.name, "");
4405
4912
  break;
4406
4913
  }
4407
4914
  }
@@ -4446,6 +4953,12 @@ function html2(strings, ...values) {
4446
4953
  tmpl = parseTemplate(strings);
4447
4954
  cache.set(strings, tmpl);
4448
4955
  }
4956
+ return renderTemplate(tmpl, values);
4957
+ }
4958
+ function __renderParsedTemplate(tree, values) {
4959
+ return renderTemplate(tree, values);
4960
+ }
4961
+ function renderTemplate(tmpl, values) {
4449
4962
  if (tmpl.length === 1 && tmpl[0].t === 0) {
4450
4963
  return executeElement(tmpl[0].el, values);
4451
4964
  }
@@ -4580,25 +5093,77 @@ function mount(component, container) {
4580
5093
  typeof component === "function" || component instanceof Node,
4581
5094
  "mount: first argument must be a component function or a DOM Node."
4582
5095
  );
4583
- const startTime = typeof performance !== "undefined" ? performance.now() : 0;
4584
- const node = typeof component === "function" ? component() : component;
4585
- const duration = typeof performance !== "undefined" ? performance.now() - startTime : 0;
4586
- container.appendChild(node);
4587
- const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
4588
- if (hook) {
4589
- hook.emit("app:init", { rootElement: node, container, duration });
5096
+ const now = () => typeof performance !== "undefined" ? performance.now() : 0;
5097
+ const startTime = now();
5098
+ let range = null;
5099
+ const attach = (built) => {
5100
+ if (built instanceof DocumentFragment) {
5101
+ const start = document.createComment("");
5102
+ const end = document.createComment("");
5103
+ built.insertBefore(start, built.firstChild);
5104
+ built.appendChild(end);
5105
+ range = { start, end };
5106
+ }
5107
+ container.appendChild(built);
5108
+ };
5109
+ let node;
5110
+ if (typeof component === "function") {
5111
+ node = withDisposerRollback(() => {
5112
+ const built = component();
5113
+ attach(built);
5114
+ return built;
5115
+ });
5116
+ } else {
5117
+ node = component;
5118
+ attach(node);
4590
5119
  }
5120
+ const duration = now() - startTime;
5121
+ const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
5122
+ if (hook) emitDevtools(hook, "app:init", { rootElement: node, container, duration });
5123
+ const mounted = range;
4591
5124
  return {
4592
5125
  node,
4593
5126
  unmount() {
4594
- if (hook) hook.emit("app:unmount", { rootElement: node });
4595
- dispose(node);
4596
- if (node.parentNode) {
4597
- node.parentNode.removeChild(node);
5127
+ if (hook) emitDevtools(hook, "app:unmount", { rootElement: node });
5128
+ if (mounted) {
5129
+ unmountRange(mounted.start, mounted.end);
5130
+ return;
4598
5131
  }
5132
+ dispose(node);
5133
+ node.parentNode?.removeChild(node);
4599
5134
  }
4600
5135
  };
4601
5136
  }
5137
+ function unmountRange(start, end) {
5138
+ const parent = start.parentNode;
5139
+ if (!parent && !end.parentNode) return;
5140
+ let budget = MAX_DRAIN_TEARDOWNS;
5141
+ for (let n = start.nextSibling; n && n !== end; n = n.nextSibling) budget++;
5142
+ let before = start;
5143
+ let after = null;
5144
+ let intact = true;
5145
+ for (; ; ) {
5146
+ if (start.parentNode !== parent || end.parentNode !== parent) intact = false;
5147
+ else if (start.previousSibling !== before || end.nextSibling !== after) {
5148
+ before = start.previousSibling;
5149
+ after = end.nextSibling;
5150
+ intact = !!(start.compareDocumentPosition(end) & 4);
5151
+ }
5152
+ const n = start.nextSibling;
5153
+ if (!intact || n === end) break;
5154
+ if (budget-- <= 0) {
5155
+ reportDrainRunaway("mount", MAX_DRAIN_TEARDOWNS, 1);
5156
+ return;
5157
+ }
5158
+ dispose(n);
5159
+ if (n.parentNode === parent) parent.removeChild(n);
5160
+ }
5161
+ if (!intact) {
5162
+ reportError(new Error("[SibuJS mount] fragment markers lost"), { phase: "cleanup", name: "mount" });
5163
+ }
5164
+ start.parentNode?.removeChild(start);
5165
+ end.parentNode?.removeChild(end);
5166
+ }
4602
5167
 
4603
5168
  // src/core/rendering/portal.ts
4604
5169
  function Portal(nodes, target) {
@@ -5034,17 +5599,17 @@ function derived(getter, options) {
5034
5599
  cs._d = false;
5035
5600
  cs._init = true;
5036
5601
  };
5037
- track2(() => {
5038
- let threw = true;
5039
- try {
5602
+ try {
5603
+ track2(() => {
5040
5604
  cs._v = getter();
5041
5605
  cs._d = false;
5042
5606
  cs._init = true;
5043
- threw = false;
5044
- } finally {
5045
- if (threw) cs._d = true;
5046
- }
5047
- }, markDirty);
5607
+ }, markDirty);
5608
+ } catch (err) {
5609
+ disposed = true;
5610
+ cleanup2(markDirty);
5611
+ throw err;
5612
+ }
5048
5613
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
5049
5614
  const validate = () => {
5050
5615
  if (!cs._d || disposed || pendingError !== void 0) return;
@@ -5063,7 +5628,7 @@ function derived(getter, options) {
5063
5628
  if (disposed) cleanup2(markDirty);
5064
5629
  }
5065
5630
  if (hook && !disposed && !Object.is(oldValue, cs._v)) {
5066
- hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
5631
+ emitDevtools(hook, "computed:update", { signal: cs, oldValue, newValue: cs._v });
5067
5632
  }
5068
5633
  };
5069
5634
  cs._validate = validate;
@@ -5114,7 +5679,7 @@ function derived(getter, options) {
5114
5679
  }
5115
5680
  }
5116
5681
  };
5117
- if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
5682
+ if (hook) emitDevtools(hook, "computed:create", { signal: cs, name: debugName, getter: computedGetter });
5118
5683
  return computedGetter;
5119
5684
  }
5120
5685
 
@@ -5482,7 +6047,7 @@ function enhance(target, setup) {
5482
6047
  bindNode(el, () => {
5483
6048
  const v = value();
5484
6049
  const next = v == null ? null : String(v);
5485
- setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()" });
6050
+ setSafeAttribute(el, name, next, { syncValueProperty: false, label: "enhance attr()", reactive: true });
5486
6051
  })
5487
6052
  );
5488
6053
  });
@@ -5754,27 +6319,6 @@ function trustHTML(html3) {
5754
6319
  }
5755
6320
  var DEFAULT_MAX_SSR_BYTES = 1024 * 1024;
5756
6321
 
5757
- // src/utils/adoptThenable.ts
5758
- function adoptThenable(value) {
5759
- if (value === null || typeof value !== "object" && typeof value !== "function") return null;
5760
- let then;
5761
- try {
5762
- then = value.then;
5763
- } catch (error) {
5764
- return Promise.reject(error);
5765
- }
5766
- if (typeof then !== "function") return null;
5767
- return new Promise((resolve, reject) => {
5768
- queueMicrotask(() => {
5769
- try {
5770
- Reflect.apply(then, value, [resolve, reject]);
5771
- } catch (error) {
5772
- reject(error);
5773
- }
5774
- });
5775
- });
5776
- }
5777
-
5778
6322
  // src/reactivity/concurrent.ts
5779
6323
  function defer(getter) {
5780
6324
  const [value, setValue] = signal(getter());
@@ -5875,6 +6419,7 @@ function nextTick() {
5875
6419
  Portal,
5876
6420
  SVG_NS,
5877
6421
  Suspense,
6422
+ __renderParsedTemplate,
5878
6423
  __resetIdCounter,
5879
6424
  a,
5880
6425
  abbr,