sibujs 1.4.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (190) hide show
  1. package/README.md +105 -119
  2. package/dist/browser.cjs +288 -80
  3. package/dist/browser.d.cts +19 -9
  4. package/dist/browser.d.ts +19 -9
  5. package/dist/browser.js +6 -6
  6. package/dist/build.cjs +1019 -313
  7. package/dist/build.d.cts +1 -1
  8. package/dist/build.d.ts +1 -1
  9. package/dist/build.js +15 -13
  10. package/dist/cdn.global.js +17 -16
  11. package/dist/chunk-2RA7SHDA.js +65 -0
  12. package/dist/chunk-2UPRY23K.js +80 -0
  13. package/dist/chunk-3JHCYHWN.js +125 -0
  14. package/dist/{chunk-ZWKZCBO6.js → chunk-3LR7GLWQ.js} +154 -33
  15. package/dist/{chunk-3AIRKM3B.js → chunk-3NSGB5JN.js} +115 -34
  16. package/dist/{chunk-3ARAQO7B.js → chunk-52YJLLRO.js} +29 -6
  17. package/dist/chunk-54EDRCEF.js +93 -0
  18. package/dist/chunk-7JDB7I65.js +1327 -0
  19. package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
  20. package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
  21. package/dist/{chunk-WR5D4EGH.js → chunk-GTBNNBJ6.js} +14 -2
  22. package/dist/chunk-HB24TBAF.js +121 -0
  23. package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
  24. package/dist/{chunk-JAKHTMQU.js → chunk-JA6667UN.js} +206 -46
  25. package/dist/{chunk-77L6NL3X.js → chunk-JXMMDLBY.js} +306 -183
  26. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  27. package/dist/{chunk-F3FA4F32.js → chunk-KLRMB5ZS.js} +135 -79
  28. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  29. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  30. package/dist/{chunk-TSOKIX5Z.js → chunk-MIUAXB7K.js} +126 -74
  31. package/dist/{chunk-QWZG56ET.js → chunk-ND2664SF.js} +558 -190
  32. package/dist/{chunk-JCI5M6U6.js → chunk-O2MNQFLP.js} +261 -79
  33. package/dist/{chunk-EWFVA3TJ.js → chunk-R73P76YZ.js} +1 -1
  34. package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
  35. package/dist/chunk-UCS6AMJ7.js +79 -0
  36. package/dist/{chunk-ZD6OAMTH.js → chunk-VLPPXTYG.js} +90 -35
  37. package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
  38. package/dist/{contracts-xo5ckdRP.d.cts → contracts-ey_Qh8ef.d.cts} +7 -8
  39. package/dist/{contracts-xo5ckdRP.d.ts → contracts-ey_Qh8ef.d.ts} +7 -8
  40. package/dist/{customElement-D2DJp_xn.d.cts → customElement-CPfIrbvg.d.cts} +18 -9
  41. package/dist/{customElement-D2DJp_xn.d.ts → customElement-CPfIrbvg.d.ts} +18 -9
  42. package/dist/data.cjs +452 -100
  43. package/dist/data.d.cts +20 -2
  44. package/dist/data.d.ts +20 -2
  45. package/dist/data.js +11 -9
  46. package/dist/devtools.cjs +535 -247
  47. package/dist/devtools.d.cts +1 -1
  48. package/dist/devtools.d.ts +1 -1
  49. package/dist/devtools.js +34 -30
  50. package/dist/ecosystem.cjs +499 -143
  51. package/dist/ecosystem.d.cts +13 -11
  52. package/dist/ecosystem.d.ts +13 -11
  53. package/dist/ecosystem.js +12 -11
  54. package/dist/extras.cjs +3639 -1629
  55. package/dist/extras.d.cts +11 -11
  56. package/dist/extras.d.ts +11 -11
  57. package/dist/extras.js +58 -45
  58. package/dist/index.cjs +1023 -313
  59. package/dist/index.d.cts +128 -55
  60. package/dist/index.d.ts +128 -55
  61. package/dist/index.js +28 -16
  62. package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
  63. package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
  64. package/dist/motion.cjs +90 -36
  65. package/dist/motion.d.cts +1 -1
  66. package/dist/motion.d.ts +1 -1
  67. package/dist/motion.js +4 -4
  68. package/dist/patterns.cjs +414 -81
  69. package/dist/patterns.d.cts +53 -20
  70. package/dist/patterns.d.ts +53 -20
  71. package/dist/patterns.js +7 -7
  72. package/dist/performance.cjs +364 -108
  73. package/dist/performance.d.cts +29 -17
  74. package/dist/performance.d.ts +29 -17
  75. package/dist/performance.js +13 -6
  76. package/dist/plugin-D30wlGW5.d.cts +71 -0
  77. package/dist/plugin-D30wlGW5.d.ts +71 -0
  78. package/dist/plugins.cjs +652 -271
  79. package/dist/plugins.d.cts +13 -6
  80. package/dist/plugins.d.ts +13 -6
  81. package/dist/plugins.js +116 -50
  82. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  83. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  84. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  85. package/dist/ssr.cjs +648 -219
  86. package/dist/ssr.d.cts +27 -7
  87. package/dist/ssr.d.ts +27 -7
  88. package/dist/ssr.js +12 -11
  89. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.cts} +9 -1
  90. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.ts} +9 -1
  91. package/dist/testing.cjs +252 -63
  92. package/dist/testing.d.cts +17 -4
  93. package/dist/testing.d.ts +17 -4
  94. package/dist/testing.js +100 -44
  95. package/dist/ui.cjs +576 -168
  96. package/dist/ui.d.cts +13 -16
  97. package/dist/ui.d.ts +13 -16
  98. package/dist/ui.js +20 -17
  99. package/dist/widgets.cjs +1001 -93
  100. package/dist/widgets.d.cts +104 -2
  101. package/dist/widgets.d.ts +104 -2
  102. package/dist/widgets.js +9 -7
  103. package/package.json +8 -2
  104. package/dist/chunk-32DY64NT.js +0 -282
  105. package/dist/chunk-3CRQALYP.js +0 -877
  106. package/dist/chunk-4EI4AG32.js +0 -482
  107. package/dist/chunk-4MYMUBRS.js +0 -21
  108. package/dist/chunk-6HLLIF3K.js +0 -398
  109. package/dist/chunk-6LSNVCS2.js +0 -937
  110. package/dist/chunk-6SA3QQES.js +0 -61
  111. package/dist/chunk-7BF6TK55.js +0 -1097
  112. package/dist/chunk-7TQKR4PP.js +0 -294
  113. package/dist/chunk-7V26P53V.js +0 -712
  114. package/dist/chunk-AZ3ISID5.js +0 -298
  115. package/dist/chunk-B7SWRFUT.js +0 -332
  116. package/dist/chunk-BGN5ZMP4.js +0 -26
  117. package/dist/chunk-BTU3TJDS.js +0 -365
  118. package/dist/chunk-BW3WT46K.js +0 -937
  119. package/dist/chunk-C6KFWOFV.js +0 -616
  120. package/dist/chunk-CHF5OHIA.js +0 -61
  121. package/dist/chunk-CHJ27IGK.js +0 -26
  122. package/dist/chunk-CMBFNA7L.js +0 -27
  123. package/dist/chunk-DAHRH4ON.js +0 -331
  124. package/dist/chunk-DKOHBI74.js +0 -924
  125. package/dist/chunk-DTCOOBMX.js +0 -725
  126. package/dist/chunk-EBGIRKQY.js +0 -616
  127. package/dist/chunk-EUZND3CB.js +0 -27
  128. package/dist/chunk-EVCZO745.js +0 -365
  129. package/dist/chunk-FGOEVHY3.js +0 -60
  130. package/dist/chunk-G3BOQPVO.js +0 -365
  131. package/dist/chunk-GCOK2LC3.js +0 -282
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-K5ZUMYVS.js +0 -89
  134. package/dist/chunk-KQPDEVVS.js +0 -398
  135. package/dist/chunk-L6JRBDNS.js +0 -60
  136. package/dist/chunk-LA6KQEDU.js +0 -712
  137. package/dist/chunk-MDVXJWFN.js +0 -304
  138. package/dist/chunk-MEZVEBPN.js +0 -2008
  139. package/dist/chunk-MK4ERFYL.js +0 -2249
  140. package/dist/chunk-MLKGABMK.js +0 -9
  141. package/dist/chunk-MQ5GOYPH.js +0 -2249
  142. package/dist/chunk-N6IZB6KJ.js +0 -567
  143. package/dist/chunk-NEKUBFPT.js +0 -60
  144. package/dist/chunk-NHUC2QWH.js +0 -282
  145. package/dist/chunk-NMRUZALC.js +0 -1097
  146. package/dist/chunk-NYVAC6P5.js +0 -37
  147. package/dist/chunk-OF7UZIVB.js +0 -725
  148. package/dist/chunk-P6W3STU4.js +0 -2249
  149. package/dist/chunk-PBHF5WKN.js +0 -616
  150. package/dist/chunk-PTQJDMRT.js +0 -146
  151. package/dist/chunk-PZEGYCF5.js +0 -61
  152. package/dist/chunk-QBMDLBU2.js +0 -975
  153. package/dist/chunk-RQGQSLQK.js +0 -725
  154. package/dist/chunk-SDLZDHKP.js +0 -107
  155. package/dist/chunk-TNQWPPE6.js +0 -37
  156. package/dist/chunk-UHNL42EF.js +0 -2730
  157. package/dist/chunk-UNXCEF6S.js +0 -21
  158. package/dist/chunk-V2XTI523.js +0 -347
  159. package/dist/chunk-VAU366PN.js +0 -2241
  160. package/dist/chunk-VMVDTCXB.js +0 -712
  161. package/dist/chunk-VRW3FULF.js +0 -725
  162. package/dist/chunk-WADYRCO2.js +0 -304
  163. package/dist/chunk-WILQZRO4.js +0 -282
  164. package/dist/chunk-WUHJISPP.js +0 -298
  165. package/dist/chunk-XYU6TZOW.js +0 -182
  166. package/dist/chunk-Y6GP4QGG.js +0 -276
  167. package/dist/chunk-YECR7UIA.js +0 -347
  168. package/dist/chunk-YUTWTI4B.js +0 -654
  169. package/dist/chunk-Z65KYU7I.js +0 -26
  170. package/dist/chunk-Z6POF5YC.js +0 -975
  171. package/dist/chunk-ZBJP6WFL.js +0 -482
  172. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  173. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  174. package/dist/contracts-DOrhwbke.d.cts +0 -245
  175. package/dist/contracts-DOrhwbke.d.ts +0 -245
  176. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  177. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  178. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  179. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  180. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  181. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  182. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  183. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  184. package/dist/ssr-3RXHP5ES.js +0 -38
  185. package/dist/ssr-6GIMY5MX.js +0 -38
  186. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  187. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  188. package/dist/ssr-WKUPVSSK.js +0 -36
  189. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  190. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
@@ -139,60 +139,68 @@ import {
139
139
  use,
140
140
  var_,
141
141
  video
142
- } from "./chunk-32DY64NT.js";
142
+ } from "./chunk-R73P76YZ.js";
143
143
  import {
144
144
  watch
145
- } from "./chunk-NYVAC6P5.js";
145
+ } from "./chunk-ITX6OO3F.js";
146
+ import {
147
+ trustHTML
148
+ } from "./chunk-JYD2PWXH.js";
146
149
  import {
147
150
  context
148
- } from "./chunk-BGN5ZMP4.js";
151
+ } from "./chunk-GTBNNBJ6.js";
149
152
  import {
150
153
  SVG_NS,
151
154
  bindChildNode,
152
155
  tagFactory
153
- } from "./chunk-F3FA4F32.js";
156
+ } from "./chunk-KLRMB5ZS.js";
154
157
  import {
155
158
  bindAttribute,
156
- bindDynamic,
159
+ bindDynamic
160
+ } from "./chunk-DFPFITST.js";
161
+ import {
162
+ derived
163
+ } from "./chunk-54EDRCEF.js";
164
+ import {
157
165
  checkLeaks,
158
166
  dispose,
159
167
  registerDisposer
160
- } from "./chunk-PTQJDMRT.js";
161
- import {
162
- derived
163
- } from "./chunk-NEKUBFPT.js";
168
+ } from "./chunk-2UPRY23K.js";
164
169
  import {
165
170
  isUrlAttribute,
171
+ sanitizeSrcset,
166
172
  sanitizeUrl
167
- } from "./chunk-CMBFNA7L.js";
173
+ } from "./chunk-UCS6AMJ7.js";
168
174
  import {
169
175
  effect,
170
176
  on
171
- } from "./chunk-CHF5OHIA.js";
177
+ } from "./chunk-HB24TBAF.js";
172
178
  import {
173
179
  disableSSR,
174
180
  enableSSR,
181
+ getSSRStore,
175
182
  isSSR,
183
+ runInSSRContext,
176
184
  withSSR
177
- } from "./chunk-EUZND3CB.js";
185
+ } from "./chunk-2RA7SHDA.js";
178
186
  import {
179
187
  batch,
180
188
  enqueueBatchedSignal,
181
189
  isBatching,
182
190
  signal
183
- } from "./chunk-WZSPOOER.js";
191
+ } from "./chunk-CC65Y57T.js";
184
192
  import {
185
193
  notifySubscribers,
186
194
  recordDependency,
187
195
  track,
188
196
  untracked
189
- } from "./chunk-ZD6OAMTH.js";
197
+ } from "./chunk-VLPPXTYG.js";
190
198
  import {
191
199
  __export,
192
200
  devAssert,
193
201
  devWarn,
194
202
  isDev
195
- } from "./chunk-5X6PP2UK.js";
203
+ } from "./chunk-LMLD24FC.js";
196
204
 
197
205
  // index.ts
198
206
  var index_exports = {};
@@ -276,6 +284,7 @@ __export(index_exports, {
276
284
  footer: () => footer,
277
285
  form: () => form,
278
286
  g: () => g,
287
+ getSSRStore: () => getSSRStore,
279
288
  getSlot: () => getSlot,
280
289
  h1: () => h1,
281
290
  h2: () => h2,
@@ -311,8 +320,6 @@ __export(index_exports, {
311
320
  mask: () => mask,
312
321
  match: () => match,
313
322
  math: () => math,
314
- memo: () => memo,
315
- memoFn: () => memoFn,
316
323
  menu: () => menu,
317
324
  meta: () => meta,
318
325
  meter: () => meter,
@@ -350,6 +357,7 @@ __export(index_exports, {
350
357
  rp: () => rp,
351
358
  rt: () => rt,
352
359
  ruby: () => ruby,
360
+ runInSSRContext: () => runInSSRContext,
353
361
  s: () => s,
354
362
  samp: () => samp,
355
363
  script: () => script,
@@ -375,6 +383,7 @@ __export(index_exports, {
375
383
  symbol: () => symbol,
376
384
  table: () => table,
377
385
  tagFactory: () => tagFactory,
386
+ takePendingError: () => takePendingError,
378
387
  tbody: () => tbody,
379
388
  td: () => td,
380
389
  template: () => template,
@@ -389,6 +398,7 @@ __export(index_exports, {
389
398
  track: () => track2,
390
399
  transition: () => transition,
391
400
  trapFocus: () => trapFocus,
401
+ trustHTML: () => trustHTML,
392
402
  tspan: () => tspan,
393
403
  u: () => u,
394
404
  ul: () => ul,
@@ -404,6 +414,8 @@ __export(index_exports, {
404
414
  });
405
415
 
406
416
  // src/core/rendering/htm.ts
417
+ var _isDev = isDev();
418
+ var RAW_TEXT_TAGS = /* @__PURE__ */ new Set(["script", "style"]);
407
419
  var VOID_ELEMENTS = /* @__PURE__ */ new Set([
408
420
  "area",
409
421
  "base",
@@ -588,6 +600,15 @@ function parseTemplate(strings) {
588
600
  children.push({ t: 0, el: { tag, svg: SVG_TAGS.has(tag), attrs, children: [] } });
589
601
  } else {
590
602
  const inner = parseChildren();
603
+ if (RAW_TEXT_TAGS.has(tag.toLowerCase())) {
604
+ for (let i2 = 0; i2 < inner.length; i2++) {
605
+ if (inner[i2].t === 2) {
606
+ throw new Error(
607
+ `html: dynamic \${...} expressions are not allowed inside <${tag}> (raw-text context). Build the content separately and append it as a Node.`
608
+ );
609
+ }
610
+ }
611
+ }
591
612
  if (template2[pos] === "<" && pos + 1 < len && template2[pos + 1] === "/") {
592
613
  pos += 2;
593
614
  readTagName();
@@ -614,27 +635,50 @@ function executeElement(tmpl, values) {
614
635
  break;
615
636
  case 1: {
616
637
  const name = attr.name;
617
- if (name[0] === "o" && name[1] === "n") break;
638
+ const lname = name.toLowerCase();
639
+ if (lname[0] === "o" && lname[1] === "n") break;
618
640
  const val = values[attr.idx];
619
641
  if (typeof val === "function") {
620
642
  registerDisposer(el, bindAttribute(el, name, val));
621
643
  } else if (val != null) {
622
644
  const str = String(val);
623
- el.setAttribute(name, isUrlAttribute(name) ? sanitizeUrl(str) : str);
645
+ if (lname === "srcset") {
646
+ el.setAttribute(name, sanitizeSrcset(str));
647
+ } else if (isUrlAttribute(lname)) {
648
+ el.setAttribute(name, sanitizeUrl(str));
649
+ } else {
650
+ el.setAttribute(name, str);
651
+ }
624
652
  }
625
653
  break;
626
654
  }
627
655
  case 2: {
628
656
  let val = attr.statics[0];
629
657
  for (let j = 0; j < attr.exprs.length; j++) {
630
- val += String(values[attr.exprs[j]]) + attr.statics[j + 1];
658
+ const ev = values[attr.exprs[j]];
659
+ val += (ev == null ? "" : String(ev)) + attr.statics[j + 1];
660
+ }
661
+ const lname2 = attr.name.toLowerCase();
662
+ if (lname2 === "srcset") {
663
+ el.setAttribute(attr.name, sanitizeSrcset(val));
664
+ } else if (isUrlAttribute(lname2)) {
665
+ el.setAttribute(attr.name, sanitizeUrl(val));
666
+ } else {
667
+ el.setAttribute(attr.name, val);
631
668
  }
632
- el.setAttribute(attr.name, val);
633
669
  break;
634
670
  }
635
- case 3:
636
- el.addEventListener(attr.name, values[attr.idx]);
671
+ case 3: {
672
+ const fn = values[attr.idx];
673
+ if (typeof fn === "function") {
674
+ el.addEventListener(attr.name, fn);
675
+ } else if (_isDev) {
676
+ devWarn(
677
+ `html: on:${attr.name} handler is not a function (got ${typeof fn}). Event listener was not attached.`
678
+ );
679
+ }
637
680
  break;
681
+ }
638
682
  case 4:
639
683
  el.setAttribute(attr.name, "");
640
684
  break;
@@ -728,7 +772,7 @@ function html(strings, ...values) {
728
772
  function mount(component, container) {
729
773
  if (!container) {
730
774
  throw new Error(
731
- "[Sibu] mount: container element not found. Make sure the DOM element exists before calling mount()."
775
+ "[SibuJS mount] container element not found. Make sure the DOM element exists before calling mount()."
732
776
  );
733
777
  }
734
778
  devAssert(
@@ -756,7 +800,7 @@ function mount(component, container) {
756
800
  }
757
801
 
758
802
  // src/core/rendering/each.ts
759
- var _isDev = isDev();
803
+ var _isDev2 = isDev();
760
804
  function resolveNodeChild(child) {
761
805
  if (typeof child === "function") {
762
806
  return resolveNodeChild(child());
@@ -851,17 +895,31 @@ function each(getArray, render, options) {
851
895
  node = existing;
852
896
  } else {
853
897
  const itemKey = key;
854
- const itemGetter = () => getArray()[keyIndexMap.get(itemKey)];
898
+ const itemGetter = () => untracked(() => getArray()[keyIndexMap.get(itemKey)]);
855
899
  const indexGetter = () => keyIndexMap.get(itemKey);
856
900
  try {
857
901
  node = resolveNodeChild(render(itemGetter, indexGetter));
858
902
  } catch (err) {
859
- if (_isDev) {
903
+ if (_isDev2) {
860
904
  devWarn(
861
905
  `each: render threw for item at index ${i2} (key="${newKeys[i2]}"): ${err instanceof Error ? err.message : String(err)}`
862
906
  );
863
907
  }
864
908
  node = document.createComment(`each:error:${i2}`);
909
+ const errorObj = err instanceof Error ? err : new Error(String(err));
910
+ queueMicrotask(() => {
911
+ try {
912
+ const target = anchor.parentNode;
913
+ if (target?.dispatchEvent) {
914
+ target.dispatchEvent(
915
+ new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
916
+ );
917
+ } else if (_isDev2) {
918
+ devWarn(`each: error not surfaced \u2014 anchor detached: ${errorObj.message}`);
919
+ }
920
+ } catch {
921
+ }
922
+ });
865
923
  }
866
924
  }
867
925
  workMap.set(key, node);
@@ -930,7 +988,8 @@ function each(getArray, render, options) {
930
988
  workMap = tmp;
931
989
  initialized = true;
932
990
  };
933
- track(update);
991
+ const untrack = track(update);
992
+ registerDisposer(anchor, untrack);
934
993
  if (!initialized) {
935
994
  queueMicrotask(() => {
936
995
  if (!initialized && anchor.parentNode) {
@@ -977,24 +1036,36 @@ function Portal(nodes, target) {
977
1036
  const anchor = document.createComment("portal");
978
1037
  const container = target || document.body;
979
1038
  let portalContent = null;
1039
+ let disposed = false;
980
1040
  queueMicrotask(() => {
1041
+ if (disposed) return;
981
1042
  try {
982
1043
  portalContent = nodes();
983
1044
  container.appendChild(portalContent);
984
1045
  } catch (err) {
985
- console.error("[Portal] Render error:", err);
1046
+ if (typeof console !== "undefined") {
1047
+ console.error("[Portal] Render error:", err);
1048
+ }
1049
+ const errorObj = err instanceof Error ? err : new Error(String(err));
1050
+ queueMicrotask(() => {
1051
+ try {
1052
+ const target2 = anchor.parentNode;
1053
+ if (target2?.dispatchEvent) {
1054
+ target2.dispatchEvent(
1055
+ new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error: errorObj } })
1056
+ );
1057
+ }
1058
+ } catch {
1059
+ }
1060
+ });
986
1061
  }
987
1062
  });
988
- const observer = new MutationObserver(() => {
989
- if (!anchor.isConnected && portalContent) {
1063
+ registerDisposer(anchor, () => {
1064
+ disposed = true;
1065
+ if (portalContent) {
1066
+ dispose(portalContent);
990
1067
  portalContent.remove();
991
1068
  portalContent = null;
992
- observer.disconnect();
993
- }
994
- });
995
- queueMicrotask(() => {
996
- if (anchor.parentNode) {
997
- observer.observe(anchor.parentNode, { childList: true });
998
1069
  }
999
1070
  });
1000
1071
  return anchor;
@@ -1030,7 +1101,8 @@ function DynamicComponent(is) {
1030
1101
  }
1031
1102
  container.replaceChildren(el);
1032
1103
  }
1033
- track(render);
1104
+ const untrack = track(render);
1105
+ registerDisposer(container, untrack);
1034
1106
  return container;
1035
1107
  }
1036
1108
 
@@ -1120,11 +1192,16 @@ function KeepAlive(activeKey, cases, options) {
1120
1192
  const anchor = document.createComment("keep-alive");
1121
1193
  const cache2 = /* @__PURE__ */ new Map();
1122
1194
  const lruOrder = [];
1123
- const max = options?.max ?? 0;
1195
+ const max = options?.max ?? 10;
1196
+ if (max === 0 && isDev()) {
1197
+ devWarn("KeepAlive: unbounded cache (max: 0). Cached subtrees will never be evicted \u2014 set `max` to bound memory.");
1198
+ }
1124
1199
  let currentKey;
1125
1200
  let currentNode = null;
1126
1201
  let initialized = false;
1202
+ let disposed = false;
1127
1203
  const update = () => {
1204
+ if (disposed) return;
1128
1205
  const key = activeKey();
1129
1206
  const parent = anchor.parentNode;
1130
1207
  if (!parent) return;
@@ -1164,12 +1241,23 @@ function KeepAlive(activeKey, cases, options) {
1164
1241
  currentNode = node;
1165
1242
  initialized = true;
1166
1243
  };
1167
- track(update);
1244
+ const untrack = track(update);
1168
1245
  if (!initialized) {
1169
1246
  queueMicrotask(() => {
1170
1247
  if (!initialized && anchor.parentNode) update();
1171
1248
  });
1172
1249
  }
1250
+ registerDisposer(anchor, () => {
1251
+ disposed = true;
1252
+ untrack();
1253
+ for (const node of cache2.values()) {
1254
+ dispose(node);
1255
+ if (node.parentNode) node.parentNode.removeChild(node);
1256
+ }
1257
+ cache2.clear();
1258
+ lruOrder.length = 0;
1259
+ currentNode = null;
1260
+ });
1173
1261
  return anchor;
1174
1262
  }
1175
1263
 
@@ -1319,7 +1407,7 @@ function store(initialState) {
1319
1407
  },
1320
1408
  set() {
1321
1409
  throw new Error(
1322
- "[Sibu] store: Direct mutation is not allowed. Use actions.setState() to update store properties."
1410
+ "[SibuJS store] Direct mutation is not allowed. Use actions.setState() to update store properties."
1323
1411
  );
1324
1412
  }
1325
1413
  });
@@ -1333,16 +1421,20 @@ function store(initialState) {
1333
1421
  const setState = (patch) => {
1334
1422
  const current = getSnapshot();
1335
1423
  const nextState = typeof patch === "function" ? patch(current) : patch;
1336
- Object.entries(nextState).forEach(([key, value]) => {
1337
- if (key in signals) {
1338
- signals[key][1](value);
1339
- }
1424
+ batch(() => {
1425
+ Object.entries(nextState).forEach(([key, value]) => {
1426
+ if (key in signals) {
1427
+ signals[key][1](value);
1428
+ }
1429
+ });
1340
1430
  });
1341
1431
  };
1342
1432
  const reset = () => {
1343
- Object.keys(initialState).forEach((key) => {
1344
- const setter = signals[key][1];
1345
- setter(initialState[key]);
1433
+ batch(() => {
1434
+ Object.keys(initialState).forEach((key) => {
1435
+ const setter = signals[key][1];
1436
+ setter(initialState[key]);
1437
+ });
1346
1438
  });
1347
1439
  };
1348
1440
  const subscribe = (callback) => {
@@ -1389,16 +1481,6 @@ function ref(initial) {
1389
1481
  };
1390
1482
  }
1391
1483
 
1392
- // src/core/signals/memo.ts
1393
- function memo(factory) {
1394
- return derived(factory);
1395
- }
1396
-
1397
- // src/core/signals/memoFn.ts
1398
- function memoFn(callback) {
1399
- return derived(callback);
1400
- }
1401
-
1402
1484
  // src/core/signals/array.ts
1403
1485
  function array(initial = []) {
1404
1486
  const [arr, setArr] = signal([...initial]);
@@ -1486,7 +1568,8 @@ function reactiveArray(initial = []) {
1486
1568
  function get() {
1487
1569
  recordDependency(signal2);
1488
1570
  if (snapshot === null) {
1489
- snapshot = Object.freeze([...data2]);
1571
+ const copy = data2.slice();
1572
+ snapshot = Object.freeze(copy);
1490
1573
  }
1491
1574
  return snapshot;
1492
1575
  }
@@ -1596,21 +1679,67 @@ function deepEqual(a2, b2, seen) {
1596
1679
  if (a2 == null || b2 == null) return false;
1597
1680
  if (typeof a2 !== typeof b2) return false;
1598
1681
  if (typeof a2 !== "object") return false;
1599
- if (a2 instanceof Date && b2 instanceof Date) return a2.getTime() === b2.getTime();
1600
- if (a2 instanceof RegExp && b2 instanceof RegExp) return a2.toString() === b2.toString();
1601
- if (!seen) seen = /* @__PURE__ */ new Set();
1602
- if (seen.has(a2)) return true;
1603
- seen.add(a2);
1682
+ const objA = a2;
1683
+ const objB = b2;
1684
+ if (objA.constructor !== objB.constructor) return false;
1685
+ if (a2 instanceof Date) return a2.getTime() === b2.getTime();
1686
+ if (a2 instanceof RegExp) {
1687
+ const rb = b2;
1688
+ return a2.source === rb.source && a2.flags === rb.flags;
1689
+ }
1690
+ if (!seen) seen = /* @__PURE__ */ new Map();
1691
+ let peers = seen.get(objA);
1692
+ if (peers?.has(objB)) return true;
1693
+ if (!peers) {
1694
+ peers = /* @__PURE__ */ new Set();
1695
+ seen.set(objA, peers);
1696
+ }
1697
+ peers.add(objB);
1698
+ if (a2 instanceof Map) {
1699
+ const mb = b2;
1700
+ if (a2.size !== mb.size) return false;
1701
+ for (const [k, v] of a2) {
1702
+ if (!mb.has(k)) return false;
1703
+ if (!deepEqual(v, mb.get(k), seen)) return false;
1704
+ }
1705
+ return true;
1706
+ }
1707
+ if (a2 instanceof Set) {
1708
+ const sb = b2;
1709
+ if (a2.size !== sb.size) return false;
1710
+ for (const item of a2) {
1711
+ if (!sb.has(item)) return false;
1712
+ }
1713
+ return true;
1714
+ }
1715
+ if (a2 instanceof ArrayBuffer) {
1716
+ const viewA = new Uint8Array(a2);
1717
+ const viewB = new Uint8Array(b2);
1718
+ if (viewA.length !== viewB.length) return false;
1719
+ for (let i2 = 0; i2 < viewA.length; i2++) {
1720
+ if (viewA[i2] !== viewB[i2]) return false;
1721
+ }
1722
+ return true;
1723
+ }
1724
+ if (ArrayBuffer.isView(a2) && ArrayBuffer.isView(b2)) {
1725
+ const ta = a2;
1726
+ const tb = b2;
1727
+ if (ta.length !== tb.length) return false;
1728
+ for (let i2 = 0; i2 < ta.length; i2++) {
1729
+ if (ta[i2] !== tb[i2]) return false;
1730
+ }
1731
+ return true;
1732
+ }
1604
1733
  if (Array.isArray(a2)) {
1605
1734
  if (!Array.isArray(b2)) return false;
1606
1735
  if (a2.length !== b2.length) return false;
1607
1736
  return a2.every((val, i2) => deepEqual(val, b2[i2], seen));
1608
1737
  }
1609
- const keysA = Object.keys(a2);
1610
- const keysB = Object.keys(b2);
1738
+ const keysA = Object.keys(objA);
1739
+ const keysB = Object.keys(objB);
1611
1740
  if (keysA.length !== keysB.length) return false;
1612
1741
  return keysA.every(
1613
- (key) => deepEqual(a2[key], b2[key], seen)
1742
+ (key) => deepEqual(objA[key], objB[key], seen)
1614
1743
  );
1615
1744
  }
1616
1745
  function deepSignal(initial) {
@@ -1636,26 +1765,34 @@ function asyncDerived(factory, initial) {
1636
1765
  effect(() => {
1637
1766
  tick();
1638
1767
  const currentRun = ++runId;
1639
- setLoading(true);
1640
- setError(null);
1768
+ batch(() => {
1769
+ setLoading(true);
1770
+ setError(null);
1771
+ });
1641
1772
  let promise;
1642
1773
  try {
1643
1774
  promise = factory();
1644
1775
  } catch (err) {
1645
- setError(err);
1646
- setLoading(false);
1776
+ batch(() => {
1777
+ setError(err);
1778
+ setLoading(false);
1779
+ });
1647
1780
  return;
1648
1781
  }
1649
1782
  promise.then(
1650
1783
  (result) => {
1651
1784
  if (currentRun !== runId) return;
1652
- setValue(result);
1653
- setLoading(false);
1785
+ batch(() => {
1786
+ setValue(result);
1787
+ setLoading(false);
1788
+ });
1654
1789
  },
1655
1790
  (err) => {
1656
1791
  if (currentRun !== runId) return;
1657
- setError(err);
1658
- setLoading(false);
1792
+ batch(() => {
1793
+ setError(err);
1794
+ setLoading(false);
1795
+ });
1659
1796
  }
1660
1797
  );
1661
1798
  });
@@ -1670,32 +1807,198 @@ function asyncDerived(factory, initial) {
1670
1807
  // src/core/rendering/lifecycle.ts
1671
1808
  function safeCall(cb, hookName) {
1672
1809
  try {
1673
- cb();
1810
+ return cb();
1674
1811
  } catch (err) {
1675
1812
  devWarn(`${hookName}: callback threw: ${err instanceof Error ? err.message : String(err)}`);
1813
+ return void 0;
1814
+ }
1815
+ }
1816
+ function runMountCallback(callback, hookName, element) {
1817
+ const cleanup = safeCall(callback, hookName);
1818
+ if (typeof cleanup === "function" && element) {
1819
+ registerDisposer(element, cleanup);
1820
+ }
1821
+ }
1822
+ var mountWatchers = /* @__PURE__ */ new WeakMap();
1823
+ var unmountWatchers = /* @__PURE__ */ new WeakMap();
1824
+ var watchedMountElements = /* @__PURE__ */ new Set();
1825
+ var watchedUnmountElements = /* @__PURE__ */ new Set();
1826
+ var sharedObserver = null;
1827
+ var mutationCounter = 0;
1828
+ var FULL_SWEEP_INTERVAL = 256;
1829
+ function fireMount(el) {
1830
+ const cbs = mountWatchers.get(el);
1831
+ if (!cbs) return;
1832
+ mountWatchers.delete(el);
1833
+ watchedMountElements.delete(el);
1834
+ for (const cb of cbs) {
1835
+ try {
1836
+ cb();
1837
+ } catch {
1838
+ }
1839
+ }
1840
+ }
1841
+ function fireUnmount(el) {
1842
+ const cbs = unmountWatchers.get(el);
1843
+ if (!cbs) return;
1844
+ queueMicrotask(() => {
1845
+ if (el.isConnected) return;
1846
+ const stillCbs = unmountWatchers.get(el);
1847
+ if (!stillCbs) return;
1848
+ unmountWatchers.delete(el);
1849
+ watchedUnmountElements.delete(el);
1850
+ for (const cb of stillCbs) {
1851
+ try {
1852
+ cb();
1853
+ } catch {
1854
+ }
1855
+ }
1856
+ });
1857
+ }
1858
+ function visitAddedNode(node) {
1859
+ if (watchedMountElements.size === 0) return;
1860
+ if (node.nodeType !== 1) return;
1861
+ const el = node;
1862
+ if (watchedMountElements.has(el) && el.isConnected) {
1863
+ fireMount(el);
1864
+ }
1865
+ if (el.firstElementChild) {
1866
+ for (const watched of Array.from(watchedMountElements)) {
1867
+ if (watched !== el && watched.isConnected && el.contains(watched)) {
1868
+ fireMount(watched);
1869
+ }
1870
+ }
1871
+ }
1872
+ }
1873
+ function visitRemovedNode(node) {
1874
+ if (watchedUnmountElements.size === 0) return;
1875
+ if (node.nodeType !== 1) return;
1876
+ const el = node;
1877
+ if (watchedUnmountElements.has(el) && !el.isConnected) {
1878
+ fireUnmount(el);
1879
+ }
1880
+ if (el.firstElementChild) {
1881
+ for (const watched of Array.from(watchedUnmountElements)) {
1882
+ if (watched !== el && !watched.isConnected && el.contains(watched)) {
1883
+ fireUnmount(watched);
1884
+ }
1885
+ }
1886
+ }
1887
+ }
1888
+ function fullSweep() {
1889
+ if (watchedMountElements.size > 0) {
1890
+ for (const el of Array.from(watchedMountElements)) {
1891
+ if (el.isConnected) fireMount(el);
1892
+ }
1893
+ }
1894
+ if (watchedUnmountElements.size > 0) {
1895
+ for (const el of Array.from(watchedUnmountElements)) {
1896
+ if (!el.isConnected) fireUnmount(el);
1897
+ }
1898
+ }
1899
+ }
1900
+ function ensureObserver() {
1901
+ if (sharedObserver || typeof document === "undefined") return;
1902
+ sharedObserver = new MutationObserver((mutations) => {
1903
+ for (const m of mutations) {
1904
+ if (m.type !== "childList") continue;
1905
+ if (m.addedNodes.length > 0) {
1906
+ for (let i2 = 0; i2 < m.addedNodes.length; i2++) {
1907
+ visitAddedNode(m.addedNodes[i2]);
1908
+ }
1909
+ }
1910
+ if (m.removedNodes.length > 0) {
1911
+ for (let i2 = 0; i2 < m.removedNodes.length; i2++) {
1912
+ visitRemovedNode(m.removedNodes[i2]);
1913
+ }
1914
+ }
1915
+ }
1916
+ mutationCounter += mutations.length;
1917
+ if (mutationCounter >= FULL_SWEEP_INTERVAL) {
1918
+ mutationCounter = 0;
1919
+ fullSweep();
1920
+ }
1921
+ maybeDisconnectObserver();
1922
+ });
1923
+ sharedObserver.observe(document.body, { childList: true, subtree: true });
1924
+ }
1925
+ function maybeDisconnectObserver() {
1926
+ if (!sharedObserver) return;
1927
+ if (watchedMountElements.size === 0 && watchedUnmountElements.size === 0) {
1928
+ sharedObserver.disconnect();
1929
+ sharedObserver = null;
1930
+ mutationCounter = 0;
1676
1931
  }
1677
1932
  }
1933
+ function registerMountWatcher(element, cb) {
1934
+ let list = mountWatchers.get(element);
1935
+ if (!list) {
1936
+ list = [];
1937
+ mountWatchers.set(element, list);
1938
+ }
1939
+ list.push(cb);
1940
+ watchedMountElements.add(element);
1941
+ ensureObserver();
1942
+ return () => {
1943
+ const cbs = mountWatchers.get(element);
1944
+ if (cbs) {
1945
+ const idx = cbs.indexOf(cb);
1946
+ if (idx !== -1) cbs.splice(idx, 1);
1947
+ if (cbs.length === 0) {
1948
+ mountWatchers.delete(element);
1949
+ watchedMountElements.delete(element);
1950
+ }
1951
+ }
1952
+ maybeDisconnectObserver();
1953
+ };
1954
+ }
1955
+ function registerUnmountWatcher(element, cb) {
1956
+ let list = unmountWatchers.get(element);
1957
+ if (!list) {
1958
+ list = [];
1959
+ unmountWatchers.set(element, list);
1960
+ }
1961
+ list.push(cb);
1962
+ watchedUnmountElements.add(element);
1963
+ ensureObserver();
1964
+ return () => {
1965
+ const cbs = unmountWatchers.get(element);
1966
+ if (cbs) {
1967
+ const idx = cbs.indexOf(cb);
1968
+ if (idx !== -1) cbs.splice(idx, 1);
1969
+ if (cbs.length === 0) {
1970
+ unmountWatchers.delete(element);
1971
+ watchedUnmountElements.delete(element);
1972
+ }
1973
+ }
1974
+ maybeDisconnectObserver();
1975
+ };
1976
+ }
1678
1977
  function onMount(callback, element) {
1679
1978
  if (typeof document === "undefined") return;
1680
1979
  if (element) {
1980
+ let disposed = false;
1981
+ registerDisposer(element, () => {
1982
+ disposed = true;
1983
+ });
1681
1984
  if (element.isConnected) {
1682
1985
  queueMicrotask(() => {
1683
- safeCall(callback, "onMount");
1986
+ if (disposed) return;
1987
+ runMountCallback(callback, "onMount", element);
1684
1988
  });
1685
1989
  return;
1686
1990
  }
1687
- const observer = new MutationObserver(() => {
1688
- if (element.isConnected) {
1689
- observer.disconnect();
1690
- safeCall(callback, "onMount");
1691
- }
1692
- });
1693
1991
  queueMicrotask(() => {
1992
+ if (disposed) return;
1694
1993
  if (element.isConnected) {
1695
- safeCall(callback, "onMount");
1696
- } else {
1697
- observer.observe(document.body, { childList: true, subtree: true });
1994
+ runMountCallback(callback, "onMount", element);
1995
+ return;
1698
1996
  }
1997
+ const unregister = registerMountWatcher(element, () => {
1998
+ if (disposed) return;
1999
+ runMountCallback(callback, "onMount", element);
2000
+ });
2001
+ registerDisposer(element, unregister);
1699
2002
  });
1700
2003
  } else {
1701
2004
  queueMicrotask(() => {
@@ -1704,20 +2007,24 @@ function onMount(callback, element) {
1704
2007
  }
1705
2008
  }
1706
2009
  function onUnmount(callback, element) {
1707
- const startObserving = () => {
1708
- const observer = new MutationObserver(() => {
1709
- if (!element.isConnected) {
1710
- observer.disconnect();
1711
- safeCall(callback, "onUnmount");
1712
- }
1713
- });
1714
- observer.observe(document.body, { childList: true, subtree: true });
2010
+ if (typeof document === "undefined") return;
2011
+ let fired = false;
2012
+ const fireOnce = () => {
2013
+ if (fired) return;
2014
+ fired = true;
2015
+ safeCall(callback, "onUnmount");
2016
+ };
2017
+ registerDisposer(element, fireOnce);
2018
+ const startWatching = () => {
2019
+ if (fired) return;
2020
+ const unregister = registerUnmountWatcher(element, fireOnce);
2021
+ registerDisposer(element, unregister);
1715
2022
  };
1716
2023
  if (element.isConnected) {
1717
- startObserving();
2024
+ startWatching();
1718
2025
  } else {
1719
2026
  onMount(() => {
1720
- startObserving();
2027
+ startWatching();
1721
2028
  return void 0;
1722
2029
  }, element);
1723
2030
  }
@@ -1734,7 +2041,7 @@ function strict(fn) {
1734
2041
  try {
1735
2042
  fn();
1736
2043
  } catch (err) {
1737
- console.warn("[Sibu strict] second run threw:", err);
2044
+ console.warn("[SibuJS strict] second run threw:", err);
1738
2045
  }
1739
2046
  });
1740
2047
  }
@@ -1750,7 +2057,7 @@ function strictEffect(fn) {
1750
2057
  try {
1751
2058
  secondTeardown = effect(fn);
1752
2059
  } catch (err) {
1753
- console.warn("[Sibu strictEffect] second run threw:", err);
2060
+ console.warn("[SibuJS strictEffect] second run threw:", err);
1754
2061
  }
1755
2062
  });
1756
2063
  return () => {
@@ -1776,9 +2083,11 @@ function nextTick() {
1776
2083
  function defer(getter) {
1777
2084
  const [value, setValue] = signal(getter());
1778
2085
  let pending = false;
2086
+ let disposed = false;
1779
2087
  let latest = value();
1780
2088
  const flush = () => {
1781
2089
  pending = false;
2090
+ if (disposed) return;
1782
2091
  setValue(latest);
1783
2092
  };
1784
2093
  const schedule = () => {
@@ -1792,11 +2101,17 @@ function defer(getter) {
1792
2101
  }
1793
2102
  });
1794
2103
  };
1795
- track(() => {
2104
+ const teardown = track(() => {
1796
2105
  latest = getter();
1797
2106
  schedule();
1798
2107
  });
1799
- return value;
2108
+ const accessor = (() => value());
2109
+ accessor.dispose = () => {
2110
+ if (disposed) return;
2111
+ disposed = true;
2112
+ teardown();
2113
+ };
2114
+ return accessor;
1800
2115
  }
1801
2116
  var IDLE_FALLBACK_MS = 16;
1802
2117
  function scheduleIdle(fn) {
@@ -1837,32 +2152,56 @@ function transition() {
1837
2152
  }
1838
2153
 
1839
2154
  // src/core/rendering/lazy.ts
2155
+ var PENDING_ERROR = "__sibuPendingError";
2156
+ function dispatchPropagate(node, error) {
2157
+ const fire = () => {
2158
+ try {
2159
+ if (!node.parentNode) return false;
2160
+ node.dispatchEvent(new CustomEvent("sibu:error-propagate", { bubbles: true, detail: { error } }));
2161
+ return true;
2162
+ } catch {
2163
+ return false;
2164
+ }
2165
+ };
2166
+ if (node.parentNode && fire()) return;
2167
+ queueMicrotask(() => {
2168
+ if (fire()) return;
2169
+ node[PENDING_ERROR] = error;
2170
+ });
2171
+ }
2172
+ function takePendingError(node) {
2173
+ const rec = node;
2174
+ const err = rec[PENDING_ERROR];
2175
+ if (err instanceof Error) {
2176
+ delete rec[PENDING_ERROR];
2177
+ return err;
2178
+ }
2179
+ return void 0;
2180
+ }
1840
2181
  function lazy(importFn) {
1841
2182
  let cached = null;
1842
2183
  return function LazyComponent() {
1843
2184
  if (cached) {
1844
2185
  return cached();
1845
2186
  }
1846
- const [_status, setStatus] = signal("loading");
1847
- const [_error, setError] = signal(null);
1848
2187
  const container = div({ class: "sibu-lazy" });
2188
+ let disposed = false;
1849
2189
  importFn().then((mod) => {
2190
+ if (disposed) return;
1850
2191
  cached = mod.default;
1851
2192
  const rendered = cached();
1852
2193
  container.replaceChildren(rendered);
1853
- setStatus("loaded");
1854
2194
  }).catch((err) => {
2195
+ if (disposed) return;
1855
2196
  const errorObj = err instanceof Error ? err : new Error(String(err));
1856
- setError(errorObj);
1857
- setStatus("error");
1858
- container.replaceChildren(
1859
- div({
1860
- class: "sibu-lazy-error",
1861
- nodes: `Failed to load component: ${errorObj.message}`
1862
- })
1863
- );
2197
+ devWarn(`[SibuJS] lazy() failed to load component: ${errorObj.message}`);
2198
+ container.replaceChildren(div({ class: "sibu-lazy-error" }, `Failed to load component: ${errorObj.message}`));
2199
+ dispatchPropagate(container, errorObj);
2200
+ });
2201
+ container.appendChild(span("sibu-lazy-loading", "Loading..."));
2202
+ registerDisposer(container, () => {
2203
+ disposed = true;
1864
2204
  });
1865
- container.appendChild(span({ class: "sibu-lazy-loading", nodes: "Loading..." }));
1866
2205
  return container;
1867
2206
  };
1868
2207
  }
@@ -1870,32 +2209,48 @@ function Suspense({ nodes, fallback }) {
1870
2209
  const container = div({ class: "sibu-suspense" });
1871
2210
  const fallbackEl = fallback();
1872
2211
  container.appendChild(fallbackEl);
2212
+ let suspenseDisposed = false;
2213
+ let observer = null;
2214
+ registerDisposer(container, () => {
2215
+ suspenseDisposed = true;
2216
+ if (observer) {
2217
+ observer.disconnect();
2218
+ observer = null;
2219
+ }
2220
+ });
1873
2221
  queueMicrotask(() => {
2222
+ if (suspenseDisposed) return;
1874
2223
  try {
1875
2224
  const childEl = nodes();
1876
2225
  if (childEl.classList.contains("sibu-lazy")) {
1877
- const observer = new MutationObserver(() => {
2226
+ if (!childEl.querySelector(".sibu-lazy-loading")) {
2227
+ container.replaceChildren(childEl);
2228
+ return;
2229
+ }
2230
+ observer = new MutationObserver(() => {
2231
+ if (suspenseDisposed) return;
1878
2232
  const loading = childEl.querySelector(".sibu-lazy-loading");
1879
2233
  if (!loading) {
1880
- observer.disconnect();
2234
+ observer?.disconnect();
2235
+ observer = null;
1881
2236
  container.replaceChildren(childEl);
1882
2237
  }
1883
2238
  });
1884
2239
  observer.observe(childEl, { childList: true, subtree: true });
1885
- if (!childEl.querySelector(".sibu-lazy-loading")) {
1886
- container.replaceChildren(childEl);
1887
- }
1888
2240
  } else {
1889
2241
  container.replaceChildren(childEl);
1890
2242
  }
1891
- } catch {
2243
+ } catch (err) {
2244
+ const errorObj = err instanceof Error ? err : new Error(String(err));
2245
+ devWarn(`[SibuJS] Suspense nodes() threw: ${errorObj.message}`);
2246
+ dispatchPropagate(container, errorObj);
1892
2247
  }
1893
2248
  });
1894
2249
  return container;
1895
2250
  }
1896
2251
 
1897
2252
  // src/components/ErrorDisplay.ts
1898
- var _isDev2 = isDev();
2253
+ var _isDev3 = isDev();
1899
2254
  var STYLES = `
1900
2255
  .sibu-error-display {
1901
2256
  border: 1px solid var(--sibu-err-border, #e5484d);
@@ -1981,20 +2336,21 @@ var STYLES = `
1981
2336
  font-weight: 600;
1982
2337
  }
1983
2338
  .sibu-error-display .sibu-err-copy-btn {
1984
- background: transparent;
1985
- border: 1px solid #3a3a4e;
2339
+ background: rgba(0, 0, 0, 0.22);
2340
+ border: 1px solid rgba(255, 255, 255, 0.15);
1986
2341
  border-radius: 4px;
1987
- color: #a0a3b8;
2342
+ color: rgba(255, 255, 255, 0.85);
1988
2343
  cursor: pointer;
1989
2344
  padding: 2px 10px;
1990
- font-size: 0.95em;
2345
+ font-size: 0.78em;
1991
2346
  font-family: inherit;
1992
2347
  transition: all 0.12s ease;
2348
+ flex-shrink: 0;
1993
2349
  }
1994
2350
  .sibu-error-display .sibu-err-copy-btn:hover {
1995
- background: #2a2a3e;
1996
- color: #e5e7eb;
1997
- border-color: #4a4a5e;
2351
+ background: rgba(0, 0, 0, 0.35);
2352
+ color: white;
2353
+ border-color: rgba(255, 255, 255, 0.3);
1998
2354
  }
1999
2355
 
2000
2356
  .sibu-error-display .sibu-err-stack {
@@ -2119,21 +2475,25 @@ function normalizeError(err) {
2119
2475
  cause: null
2120
2476
  };
2121
2477
  }
2122
- function buildCopyText(err, meta2) {
2478
+ function buildCopyText(err, meta2, headline) {
2123
2479
  const lines = [];
2480
+ lines.push(headline);
2124
2481
  lines.push(`[${err.code}] ${err.message}`);
2125
2482
  if (err.stack) {
2126
2483
  lines.push("");
2484
+ lines.push("Stack Trace:");
2127
2485
  lines.push(err.stack);
2128
2486
  }
2129
- if (err.cause) {
2487
+ let cause = err.cause;
2488
+ while (cause) {
2130
2489
  lines.push("");
2131
2490
  lines.push("Caused by:");
2132
- lines.push(` [${err.cause.code}] ${err.cause.message}`);
2133
- if (err.cause.stack) {
2134
- const indented = err.cause.stack.split("\n").map((l) => ` ${l}`).join("\n");
2491
+ lines.push(` [${cause.code}] ${cause.message}`);
2492
+ if (cause.stack) {
2493
+ const indented = cause.stack.split("\n").map((l) => ` ${l}`).join("\n");
2135
2494
  lines.push(indented);
2136
2495
  }
2496
+ cause = cause.cause;
2137
2497
  }
2138
2498
  if (meta2 && Object.keys(meta2).length > 0) {
2139
2499
  lines.push("");
@@ -2143,9 +2503,13 @@ function buildCopyText(err, meta2) {
2143
2503
  }
2144
2504
  }
2145
2505
  lines.push("");
2146
- lines.push(`At: ${(/* @__PURE__ */ new Date()).toISOString()}`);
2506
+ lines.push("Environment:");
2507
+ lines.push(` Timestamp: ${(/* @__PURE__ */ new Date()).toISOString()}`);
2508
+ if (typeof location !== "undefined") {
2509
+ lines.push(` URL: ${location.href}`);
2510
+ }
2147
2511
  if (typeof navigator !== "undefined" && navigator.userAgent) {
2148
- lines.push(`UA: ${navigator.userAgent}`);
2512
+ lines.push(` User Agent: ${navigator.userAgent}`);
2149
2513
  }
2150
2514
  return lines.join("\n");
2151
2515
  }
@@ -2197,7 +2561,7 @@ function ErrorDisplay(props) {
2197
2561
  injectStyles();
2198
2562
  const severity = props.severity ?? "error";
2199
2563
  const normalized = normalizeError(props.error);
2200
- const showDetails = props.alwaysShowDetails ?? _isDev2;
2564
+ const showDetails = props.alwaysShowDetails ?? _isDev3;
2201
2565
  const headline = props.title ?? normalized.message;
2202
2566
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace("T", " ").slice(0, 19);
2203
2567
  const [copyLabel, setCopyLabel] = signal("Copy");
@@ -2206,7 +2570,7 @@ function ErrorDisplay(props) {
2206
2570
  nodes: () => copyLabel(),
2207
2571
  on: {
2208
2572
  click: () => {
2209
- const text2 = buildCopyText(normalized, props.metadata);
2573
+ const text2 = buildCopyText(normalized, props.metadata, headline);
2210
2574
  if (typeof navigator !== "undefined" && navigator.clipboard) {
2211
2575
  navigator.clipboard.writeText(text2).then(
2212
2576
  () => {
@@ -2227,6 +2591,7 @@ function ErrorDisplay(props) {
2227
2591
  nodes: [
2228
2592
  code({ class: "sibu-err-icon", nodes: normalized.code }),
2229
2593
  h3({ class: "sibu-err-title", nodes: headline }),
2594
+ copyBtn,
2230
2595
  span({ class: "sibu-err-timestamp", nodes: timestamp })
2231
2596
  ]
2232
2597
  });
@@ -2238,25 +2603,12 @@ function ErrorDisplay(props) {
2238
2603
  nodes: [
2239
2604
  div({
2240
2605
  class: "sibu-err-section-head",
2241
- nodes: [span({ nodes: "Stack Trace" }), copyBtn]
2606
+ nodes: [span({ nodes: "Stack Trace" })]
2242
2607
  }),
2243
2608
  renderFrames(normalized.frames)
2244
2609
  ]
2245
2610
  })
2246
2611
  );
2247
- } else if (showDetails) {
2248
- bodyChildren.push(
2249
- div({
2250
- class: "sibu-err-section",
2251
- nodes: [
2252
- div({
2253
- class: "sibu-err-section-head",
2254
- nodes: [span({ nodes: "Details" }), copyBtn]
2255
- }),
2256
- div({ class: "sibu-err-stack", nodes: "(no stack available)" })
2257
- ]
2258
- })
2259
- );
2260
2612
  }
2261
2613
  if (showDetails) {
2262
2614
  bodyChildren.push(...renderCauseChain(normalized.cause));
@@ -2272,37 +2624,6 @@ function ErrorDisplay(props) {
2272
2624
  })
2273
2625
  );
2274
2626
  }
2275
- if (showDetails && typeof navigator !== "undefined" && navigator.userAgent) {
2276
- bodyChildren.push(
2277
- div({
2278
- class: "sibu-err-section",
2279
- nodes: [
2280
- div({ class: "sibu-err-section-head", nodes: [span({ nodes: "Environment" })] }),
2281
- div({
2282
- class: "sibu-err-meta",
2283
- nodes: (() => {
2284
- const dl2 = document.createElement("dl");
2285
- dl2.className = "sibu-err-meta";
2286
- const entries = [
2287
- ["User Agent", navigator.userAgent],
2288
- ["URL", typeof location !== "undefined" ? location.href : "(n/a)"],
2289
- ["Timestamp", (/* @__PURE__ */ new Date()).toISOString()]
2290
- ];
2291
- for (const [k, v] of entries) {
2292
- const dt2 = document.createElement("dt");
2293
- dt2.textContent = k;
2294
- const dd2 = document.createElement("dd");
2295
- dd2.textContent = v;
2296
- dl2.appendChild(dt2);
2297
- dl2.appendChild(dd2);
2298
- }
2299
- return dl2;
2300
- })()
2301
- })
2302
- ]
2303
- })
2304
- );
2305
- }
2306
2627
  const actionButtons = [];
2307
2628
  if (props.onRetry) {
2308
2629
  actionButtons.push(
@@ -2497,6 +2818,7 @@ function injectStyles2() {
2497
2818
  stylesInjected = true;
2498
2819
  }
2499
2820
  }
2821
+ var FALLBACK_CACHE_MAX = 50;
2500
2822
  var fallbackCache = /* @__PURE__ */ new WeakMap();
2501
2823
  function getMemoizedFallback(fallbackFn, error, retry) {
2502
2824
  let cache2 = fallbackCache.get(fallbackFn);
@@ -2505,27 +2827,42 @@ function getMemoizedFallback(fallbackFn, error, retry) {
2505
2827
  fallbackCache.set(fallbackFn, cache2);
2506
2828
  }
2507
2829
  const key = error.message;
2508
- if (!cache2.has(key)) {
2509
- cache2.set(key, fallbackFn(error, retry));
2830
+ let factory = cache2.get(key);
2831
+ if (factory) {
2832
+ cache2.delete(key);
2833
+ cache2.set(key, factory);
2834
+ } else {
2835
+ factory = () => fallbackFn(error, retry);
2836
+ cache2.set(key, factory);
2837
+ if (cache2.size > FALLBACK_CACHE_MAX) {
2838
+ const oldestKey = cache2.keys().next().value;
2839
+ if (oldestKey !== void 0) cache2.delete(oldestKey);
2840
+ }
2510
2841
  }
2511
- return cache2.get(key);
2842
+ return factory();
2512
2843
  }
2513
2844
  function ErrorBoundary({ nodes, fallback, onError, resetKeys }) {
2514
2845
  injectStyles2();
2515
2846
  const [error, setError] = signal(null);
2516
2847
  const retry = () => {
2517
2848
  if (fallback) {
2518
- fallbackCache.delete(fallback);
2849
+ const cur = error();
2850
+ const inner = fallbackCache.get(fallback);
2851
+ if (cur && inner) inner.delete(cur.message);
2519
2852
  }
2520
2853
  setError(null);
2521
2854
  };
2855
+ let resetKeysTeardown = null;
2522
2856
  if (resetKeys && resetKeys.length > 0) {
2523
2857
  let initialized = false;
2524
- effect(() => {
2858
+ resetKeysTeardown = effect(() => {
2525
2859
  for (const k of resetKeys) {
2526
2860
  try {
2527
2861
  k();
2528
- } catch {
2862
+ } catch (err) {
2863
+ if (typeof console !== "undefined") {
2864
+ console.warn("[SibuJS ErrorBoundary] resetKeys getter threw:", err);
2865
+ }
2529
2866
  }
2530
2867
  }
2531
2868
  if (!initialized) {
@@ -2538,7 +2875,15 @@ function ErrorBoundary({ nodes, fallback, onError, resetKeys }) {
2538
2875
  const handleError = (e) => {
2539
2876
  const errorObj = e instanceof Error ? e : new Error(String(e));
2540
2877
  setError(errorObj);
2541
- onError?.(errorObj);
2878
+ if (onError) {
2879
+ try {
2880
+ onError(errorObj);
2881
+ } catch (cbErr) {
2882
+ if (typeof console !== "undefined") {
2883
+ console.error("[SibuJS ErrorBoundary] onError callback threw:", cbErr);
2884
+ }
2885
+ }
2886
+ }
2542
2887
  return errorObj;
2543
2888
  };
2544
2889
  const defaultFallback = (err, retryFn) => {
@@ -2590,7 +2935,7 @@ function ErrorBoundary({ nodes, fallback, onError, resetKeys }) {
2590
2935
  }
2591
2936
  }
2592
2937
  });
2593
- container.addEventListener("sibu:error-propagate", (e) => {
2938
+ const propagateListener = (e) => {
2594
2939
  if (error()) return;
2595
2940
  e.stopPropagation();
2596
2941
  const customEvent = e;
@@ -2598,6 +2943,30 @@ function ErrorBoundary({ nodes, fallback, onError, resetKeys }) {
2598
2943
  if (propagatedError) {
2599
2944
  handleError(propagatedError);
2600
2945
  }
2946
+ };
2947
+ container.addEventListener("sibu:error-propagate", propagateListener);
2948
+ onMount(() => {
2949
+ const walker = document.createTreeWalker(container, NodeFilter.SHOW_ELEMENT);
2950
+ const collected = [];
2951
+ let node = walker.currentNode;
2952
+ while (node) {
2953
+ const pending = takePendingError(node);
2954
+ if (pending) collected.push(pending);
2955
+ node = walker.nextNode();
2956
+ }
2957
+ if (collected.length === 1) {
2958
+ handleError(collected[0]);
2959
+ } else if (collected.length > 1) {
2960
+ const Agg = globalThis.AggregateError;
2961
+ handleError(
2962
+ Agg ? new Agg(collected, `${collected.length} pre-mount errors caught by ErrorBoundary`) : new Error(collected.map((e) => e.message).join("; "))
2963
+ );
2964
+ }
2965
+ return void 0;
2966
+ }, container);
2967
+ registerDisposer(container, () => {
2968
+ if (resetKeysTeardown) resetKeysTeardown();
2969
+ container.removeEventListener("sibu:error-propagate", propagateListener);
2601
2970
  });
2602
2971
  return container;
2603
2972
  }
@@ -2719,8 +3088,6 @@ export {
2719
3088
  setGlobalErrorHandler,
2720
3089
  store,
2721
3090
  ref,
2722
- memo,
2723
- memoFn,
2724
3091
  array,
2725
3092
  reactiveArray,
2726
3093
  deepEqual,
@@ -2735,6 +3102,7 @@ export {
2735
3102
  nextTick,
2736
3103
  defer,
2737
3104
  transition,
3105
+ takePendingError,
2738
3106
  lazy,
2739
3107
  Suspense,
2740
3108
  ErrorDisplay,