sibujs 1.5.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 (207) hide show
  1. package/dist/browser.cjs +238 -69
  2. package/dist/browser.d.cts +5 -0
  3. package/dist/browser.d.ts +5 -0
  4. package/dist/browser.js +6 -6
  5. package/dist/build.cjs +916 -292
  6. package/dist/build.js +15 -13
  7. package/dist/cdn.global.js +17 -16
  8. package/dist/chunk-2RA7SHDA.js +65 -0
  9. package/dist/chunk-2UPRY23K.js +80 -0
  10. package/dist/chunk-3JHCYHWN.js +125 -0
  11. package/dist/{chunk-VAPYJN4X.js → chunk-3LR7GLWQ.js} +93 -23
  12. package/dist/{chunk-RJ46C3CS.js → chunk-3NSGB5JN.js} +71 -20
  13. package/dist/{chunk-XUEEGU5O.js → chunk-52YJLLRO.js} +16 -4
  14. package/dist/{chunk-XHK6BDAJ.js → chunk-54EDRCEF.js} +25 -8
  15. package/dist/chunk-7JDB7I65.js +1327 -0
  16. package/dist/{chunk-WZSPOOER.js → chunk-CC65Y57T.js} +8 -5
  17. package/dist/{chunk-23VV7YD3.js → chunk-DFPFITST.js} +25 -30
  18. package/dist/{chunk-BGN5ZMP4.js → chunk-GTBNNBJ6.js} +14 -2
  19. package/dist/chunk-HB24TBAF.js +121 -0
  20. package/dist/{chunk-CZUGLNJS.js → chunk-ITX6OO3F.js} +3 -3
  21. package/dist/{chunk-BGTHZHJ5.js → chunk-JA6667UN.js} +188 -44
  22. package/dist/{chunk-7GRNSCFT.js → chunk-JXMMDLBY.js} +306 -183
  23. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  24. package/dist/{chunk-SFKNRVCU.js → chunk-KLRMB5ZS.js} +135 -79
  25. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  26. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  27. package/dist/{chunk-BMPL52BF.js → chunk-MIUAXB7K.js} +118 -66
  28. package/dist/{chunk-JCDUJN2F.js → chunk-ND2664SF.js} +486 -153
  29. package/dist/{chunk-VQDZK23A.js → chunk-O2MNQFLP.js} +181 -66
  30. package/dist/{chunk-NHUC2QWH.js → chunk-R73P76YZ.js} +1 -1
  31. package/dist/{chunk-2BYQDGN3.js → chunk-SAHNHTFC.js} +234 -63
  32. package/dist/chunk-UCS6AMJ7.js +79 -0
  33. package/dist/{chunk-K4G4ZQNR.js → chunk-VLPPXTYG.js} +84 -38
  34. package/dist/{chunk-OUZZEE4S.js → chunk-WOMYAHHI.js} +17 -11
  35. package/dist/{customElement-BL3Uo8dL.d.cts → customElement-CPfIrbvg.d.cts} +14 -10
  36. package/dist/{customElement-BL3Uo8dL.d.ts → customElement-CPfIrbvg.d.ts} +14 -10
  37. package/dist/data.cjs +410 -99
  38. package/dist/data.d.cts +20 -2
  39. package/dist/data.d.ts +20 -2
  40. package/dist/data.js +11 -9
  41. package/dist/devtools.cjs +513 -223
  42. package/dist/devtools.d.cts +1 -1
  43. package/dist/devtools.d.ts +1 -1
  44. package/dist/devtools.js +12 -6
  45. package/dist/ecosystem.cjs +475 -144
  46. package/dist/ecosystem.d.cts +9 -7
  47. package/dist/ecosystem.d.ts +9 -7
  48. package/dist/ecosystem.js +12 -11
  49. package/dist/extras.cjs +3355 -1541
  50. package/dist/extras.d.cts +9 -9
  51. package/dist/extras.d.ts +9 -9
  52. package/dist/extras.js +58 -45
  53. package/dist/index.cjs +920 -292
  54. package/dist/index.d.cts +71 -8
  55. package/dist/index.d.ts +71 -8
  56. package/dist/index.js +28 -16
  57. package/dist/{introspect-BumjnBKr.d.cts → introspect-BWNjNw64.d.cts} +22 -2
  58. package/dist/{introspect-CZrlcaYy.d.ts → introspect-cY2pg9pW.d.ts} +22 -2
  59. package/dist/motion.cjs +77 -34
  60. package/dist/motion.js +4 -4
  61. package/dist/patterns.cjs +335 -69
  62. package/dist/patterns.d.cts +11 -12
  63. package/dist/patterns.d.ts +11 -12
  64. package/dist/patterns.js +7 -7
  65. package/dist/performance.cjs +279 -108
  66. package/dist/performance.d.cts +23 -16
  67. package/dist/performance.d.ts +23 -16
  68. package/dist/performance.js +13 -8
  69. package/dist/plugin-D30wlGW5.d.cts +71 -0
  70. package/dist/plugin-D30wlGW5.d.ts +71 -0
  71. package/dist/plugins.cjs +635 -260
  72. package/dist/plugins.d.cts +10 -3
  73. package/dist/plugins.d.ts +10 -3
  74. package/dist/plugins.js +106 -38
  75. package/dist/{ssr-Do_SiVoL.d.cts → ssr-CrVNy6Pa.d.cts} +9 -15
  76. package/dist/{ssr-Do_SiVoL.d.ts → ssr-CrVNy6Pa.d.ts} +9 -15
  77. package/dist/{ssr-4PBXAOO3.js → ssr-FXD2PPMC.js} +4 -3
  78. package/dist/ssr.cjs +642 -222
  79. package/dist/ssr.d.cts +26 -6
  80. package/dist/ssr.d.ts +26 -6
  81. package/dist/ssr.js +12 -11
  82. package/dist/{tagFactory-DaJ0YWX6.d.cts → tagFactory-S17H2qxu.d.cts} +9 -1
  83. package/dist/{tagFactory-DaJ0YWX6.d.ts → tagFactory-S17H2qxu.d.ts} +9 -1
  84. package/dist/testing.cjs +252 -63
  85. package/dist/testing.d.cts +17 -4
  86. package/dist/testing.d.ts +17 -4
  87. package/dist/testing.js +100 -44
  88. package/dist/ui.cjs +463 -137
  89. package/dist/ui.d.cts +1 -1
  90. package/dist/ui.d.ts +1 -1
  91. package/dist/ui.js +20 -17
  92. package/dist/widgets.cjs +977 -94
  93. package/dist/widgets.d.cts +104 -2
  94. package/dist/widgets.d.ts +104 -2
  95. package/dist/widgets.js +9 -7
  96. package/package.json +8 -2
  97. package/dist/chunk-32DY64NT.js +0 -282
  98. package/dist/chunk-3AIRKM3B.js +0 -1263
  99. package/dist/chunk-3ARAQO7B.js +0 -398
  100. package/dist/chunk-3CRQALYP.js +0 -877
  101. package/dist/chunk-4EI4AG32.js +0 -482
  102. package/dist/chunk-4MYMUBRS.js +0 -21
  103. package/dist/chunk-5ZYQ6KDD.js +0 -154
  104. package/dist/chunk-6BMPXPUW.js +0 -26
  105. package/dist/chunk-6HLLIF3K.js +0 -398
  106. package/dist/chunk-6LSNVCS2.js +0 -937
  107. package/dist/chunk-6SA3QQES.js +0 -61
  108. package/dist/chunk-77L6NL3X.js +0 -1097
  109. package/dist/chunk-7BF6TK55.js +0 -1097
  110. package/dist/chunk-7TQKR4PP.js +0 -294
  111. package/dist/chunk-7V26P53V.js +0 -712
  112. package/dist/chunk-AZ3ISID5.js +0 -298
  113. package/dist/chunk-B7SWRFUT.js +0 -332
  114. package/dist/chunk-BTU3TJDS.js +0 -365
  115. package/dist/chunk-BW3WT46K.js +0 -937
  116. package/dist/chunk-C6KFWOFV.js +0 -616
  117. package/dist/chunk-CHF5OHIA.js +0 -61
  118. package/dist/chunk-CHJ27IGK.js +0 -26
  119. package/dist/chunk-CMBFNA7L.js +0 -27
  120. package/dist/chunk-DAHRH4ON.js +0 -331
  121. package/dist/chunk-DKOHBI74.js +0 -924
  122. package/dist/chunk-DTCOOBMX.js +0 -725
  123. package/dist/chunk-EBGIRKQY.js +0 -616
  124. package/dist/chunk-EUZND3CB.js +0 -27
  125. package/dist/chunk-EVCZO745.js +0 -365
  126. package/dist/chunk-EWFVA3TJ.js +0 -282
  127. package/dist/chunk-F3FA4F32.js +0 -292
  128. package/dist/chunk-FGOEVHY3.js +0 -60
  129. package/dist/chunk-G3BOQPVO.js +0 -365
  130. package/dist/chunk-GCOK2LC3.js +0 -282
  131. package/dist/chunk-GJPXRJ45.js +0 -37
  132. package/dist/chunk-HGMJFBC7.js +0 -654
  133. package/dist/chunk-JAKHTMQU.js +0 -1000
  134. package/dist/chunk-JCI5M6U6.js +0 -956
  135. package/dist/chunk-K5ZUMYVS.js +0 -89
  136. package/dist/chunk-KQPDEVVS.js +0 -398
  137. package/dist/chunk-L6JRBDNS.js +0 -60
  138. package/dist/chunk-LA6KQEDU.js +0 -712
  139. package/dist/chunk-MB6QFH3I.js +0 -2776
  140. package/dist/chunk-MDVXJWFN.js +0 -304
  141. package/dist/chunk-MEZVEBPN.js +0 -2008
  142. package/dist/chunk-MK4ERFYL.js +0 -2249
  143. package/dist/chunk-MLKGABMK.js +0 -9
  144. package/dist/chunk-MQ5GOYPH.js +0 -2249
  145. package/dist/chunk-MYRV7VDM.js +0 -742
  146. package/dist/chunk-N6IZB6KJ.js +0 -567
  147. package/dist/chunk-NEKUBFPT.js +0 -60
  148. package/dist/chunk-NMRUZALC.js +0 -1097
  149. package/dist/chunk-NYVAC6P5.js +0 -37
  150. package/dist/chunk-NZIIMDWI.js +0 -84
  151. package/dist/chunk-OF7UZIVB.js +0 -725
  152. package/dist/chunk-P3XWXJZU.js +0 -282
  153. package/dist/chunk-P6W3STU4.js +0 -2249
  154. package/dist/chunk-PBHF5WKN.js +0 -616
  155. package/dist/chunk-PDZQY43A.js +0 -616
  156. package/dist/chunk-PTQJDMRT.js +0 -146
  157. package/dist/chunk-PZEGYCF5.js +0 -61
  158. package/dist/chunk-QBMDLBU2.js +0 -975
  159. package/dist/chunk-QWZG56ET.js +0 -2744
  160. package/dist/chunk-RQGQSLQK.js +0 -725
  161. package/dist/chunk-SDLZDHKP.js +0 -107
  162. package/dist/chunk-TDGZL5CU.js +0 -365
  163. package/dist/chunk-TNQWPPE6.js +0 -37
  164. package/dist/chunk-TSOKIX5Z.js +0 -654
  165. package/dist/chunk-UHNL42EF.js +0 -2730
  166. package/dist/chunk-UNXCEF6S.js +0 -21
  167. package/dist/chunk-V2XTI523.js +0 -347
  168. package/dist/chunk-VAU366PN.js +0 -2241
  169. package/dist/chunk-VMVDTCXB.js +0 -712
  170. package/dist/chunk-VQNQZCWJ.js +0 -61
  171. package/dist/chunk-VRW3FULF.js +0 -725
  172. package/dist/chunk-WADYRCO2.js +0 -304
  173. package/dist/chunk-WILQZRO4.js +0 -282
  174. package/dist/chunk-WR5D4EGH.js +0 -26
  175. package/dist/chunk-WUHJISPP.js +0 -298
  176. package/dist/chunk-XYU6TZOW.js +0 -182
  177. package/dist/chunk-Y6GP4QGG.js +0 -276
  178. package/dist/chunk-YECR7UIA.js +0 -347
  179. package/dist/chunk-YUTWTI4B.js +0 -654
  180. package/dist/chunk-Z65KYU7I.js +0 -26
  181. package/dist/chunk-Z6POF5YC.js +0 -975
  182. package/dist/chunk-ZBJP6WFL.js +0 -482
  183. package/dist/chunk-ZD6OAMTH.js +0 -277
  184. package/dist/chunk-ZWKZCBO6.js +0 -317
  185. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  186. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  187. package/dist/contracts-DOrhwbke.d.cts +0 -245
  188. package/dist/contracts-DOrhwbke.d.ts +0 -245
  189. package/dist/contracts-xo5ckdRP.d.cts +0 -240
  190. package/dist/contracts-xo5ckdRP.d.ts +0 -240
  191. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  192. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  193. package/dist/customElement-D2DJp_xn.d.cts +0 -313
  194. package/dist/customElement-D2DJp_xn.d.ts +0 -313
  195. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  196. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  197. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  198. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  199. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  200. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  201. package/dist/ssr-3RXHP5ES.js +0 -38
  202. package/dist/ssr-6GIMY5MX.js +0 -38
  203. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  204. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  205. package/dist/ssr-WKUPVSSK.js +0 -36
  206. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  207. package/dist/tagFactory-Dl8QCLga.d.ts +0 -23
package/dist/devtools.cjs CHANGED
@@ -24,6 +24,7 @@ __export(devtools_exports, {
24
24
  captureSignalGraph: () => captureSignalGraph,
25
25
  checkLeaks: () => checkLeaks,
26
26
  clearDebugValues: () => clearDebugValues,
27
+ clearHMRModule: () => clearHMRModule,
27
28
  clearHMRState: () => clearHMRState,
28
29
  clearPerformanceData: () => clearPerformanceData,
29
30
  createDevtoolsOverlay: () => createDevtoolsOverlay,
@@ -37,6 +38,7 @@ __export(devtools_exports, {
37
38
  diffSignalGraphs: () => diffSignalGraphs,
38
39
  disableDebug: () => disableDebug,
39
40
  enableDebug: () => enableDebug,
41
+ exposeHMR: () => exposeHMR,
40
42
  formatError: () => formatError,
41
43
  getActiveDevTools: () => getActiveDevTools,
42
44
  getDebugValues: () => getDebugValues,
@@ -142,7 +144,10 @@ function runCleanups(component) {
142
144
  for (const cleanup2 of cleanups) {
143
145
  try {
144
146
  cleanup2();
145
- } catch {
147
+ } catch (err) {
148
+ if (typeof console !== "undefined") {
149
+ console.warn("[SibuJS debug] cleanup threw:", err);
150
+ }
146
151
  }
147
152
  }
148
153
  trackedCleanups.delete(component);
@@ -168,12 +173,12 @@ function isDev() {
168
173
  var _isDev = isDev();
169
174
  function devAssert(condition, message) {
170
175
  if (_isDev && !condition) {
171
- throw new Error(`[Sibu] ${message}`);
176
+ throw new Error(`[SibuJS] ${message}`);
172
177
  }
173
178
  }
174
179
  function devWarn(message) {
175
180
  if (_isDev) {
176
- console.warn(`[Sibu] ${message}`);
181
+ console.warn(`[SibuJS] ${message}`);
177
182
  }
178
183
  }
179
184
 
@@ -183,11 +188,11 @@ var subscriberStack = new Array(32);
183
188
  var stackCapacity = 32;
184
189
  var stackTop = -1;
185
190
  var currentSubscriber = null;
186
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
187
191
  var SUBS = "__s";
188
192
  var notifyDepth = 0;
189
193
  var pendingQueue = [];
190
194
  var pendingSet = /* @__PURE__ */ new Set();
195
+ var propagateStack = [];
191
196
  function safeInvoke(sub) {
192
197
  try {
193
198
  sub();
@@ -233,7 +238,6 @@ function recordDependency(signal2) {
233
238
  let subs = signal2[SUBS];
234
239
  if (!subs) {
235
240
  subs = /* @__PURE__ */ new Set();
236
- signalSubscribers.set(signal2, subs);
237
241
  signal2[SUBS] = subs;
238
242
  }
239
243
  subs.add(currentSubscriber);
@@ -243,42 +247,46 @@ function recordDependency(signal2) {
243
247
  signal2.__f = void 0;
244
248
  }
245
249
  }
250
+ var maxDrainIterations = 1e5;
246
251
  function propagateDirty(sub) {
247
252
  sub();
248
- let sig = sub._sig;
249
- while (sig) {
253
+ const rootSig = sub._sig;
254
+ if (!rootSig) return;
255
+ const stack = propagateStack;
256
+ const baseLen = stack.length;
257
+ stack.push(rootSig);
258
+ while (stack.length > baseLen) {
259
+ const sig = stack.pop();
250
260
  const first = sig.__f;
251
261
  if (first) {
252
262
  if (first._c) {
253
263
  const nSig = first._sig;
254
- nSig._d = true;
255
- sig = nSig;
256
- continue;
257
- }
258
- if (!pendingSet.has(first)) {
264
+ if (!nSig._d) {
265
+ nSig._d = true;
266
+ stack.push(nSig);
267
+ }
268
+ } else if (!pendingSet.has(first)) {
259
269
  pendingSet.add(first);
260
270
  pendingQueue.push(first);
261
271
  }
262
- break;
272
+ continue;
263
273
  }
264
274
  const subs = sig[SUBS];
265
- if (!subs) break;
266
- let nextSig;
275
+ if (!subs) continue;
267
276
  for (const s of subs) {
268
277
  if (s._c) {
269
- s();
270
278
  const nSig = s._sig;
271
- if (nSig && !nextSig) {
272
- nextSig = nSig;
273
- } else if (nSig) {
274
- propagateDirty(s);
279
+ if (nSig && !nSig._d) {
280
+ nSig._d = true;
281
+ stack.push(nSig);
282
+ } else if (!nSig) {
283
+ s();
275
284
  }
276
285
  } else if (!pendingSet.has(s)) {
277
286
  pendingSet.add(s);
278
287
  pendingQueue.push(s);
279
288
  }
280
289
  }
281
- sig = nextSig;
282
290
  }
283
291
  }
284
292
  function notifySubscribers(signal2) {
@@ -302,13 +310,23 @@ function notifySubscribers(signal2) {
302
310
  }
303
311
  let i = 0;
304
312
  while (i < pendingQueue.length) {
313
+ if (i >= maxDrainIterations) {
314
+ if (typeof console !== "undefined") {
315
+ console.error(
316
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
317
+ );
318
+ }
319
+ break;
320
+ }
305
321
  safeInvoke(pendingQueue[i]);
306
322
  i++;
307
323
  }
308
324
  } finally {
309
- pendingQueue.length = 0;
310
- pendingSet.clear();
311
325
  notifyDepth--;
326
+ if (notifyDepth === 0) {
327
+ pendingQueue.length = 0;
328
+ pendingSet.clear();
329
+ }
312
330
  }
313
331
  return;
314
332
  }
@@ -328,30 +346,48 @@ function notifySubscribers(signal2) {
328
346
  notifyDepth++;
329
347
  try {
330
348
  let directCount = 0;
349
+ let hasComputedSub = false;
331
350
  for (const sub of subs) {
351
+ if (sub._c) hasComputedSub = true;
332
352
  pendingQueue[directCount++] = sub;
333
353
  }
334
- for (let i2 = 0; i2 < directCount; i2++) {
335
- if (pendingQueue[i2]._c) {
336
- propagateDirty(pendingQueue[i2]);
354
+ if (!hasComputedSub) {
355
+ for (let i2 = 0; i2 < directCount; i2++) {
356
+ safeInvoke(pendingQueue[i2]);
337
357
  }
338
- }
339
- for (let i2 = 0; i2 < directCount; i2++) {
340
- if (!pendingQueue[i2]._c) {
341
- if (!pendingSet.has(pendingQueue[i2])) {
342
- safeInvoke(pendingQueue[i2]);
358
+ } else {
359
+ for (let i2 = 0; i2 < directCount; i2++) {
360
+ if (pendingQueue[i2]._c) {
361
+ propagateDirty(pendingQueue[i2]);
362
+ }
363
+ }
364
+ for (let i2 = 0; i2 < directCount; i2++) {
365
+ const sub = pendingQueue[i2];
366
+ if (!sub._c && !pendingSet.has(sub)) {
367
+ pendingSet.add(sub);
368
+ safeInvoke(sub);
343
369
  }
344
370
  }
345
371
  }
346
372
  let i = directCount;
347
373
  while (i < pendingQueue.length) {
374
+ if (i - directCount >= maxDrainIterations) {
375
+ if (typeof console !== "undefined") {
376
+ console.error(
377
+ `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
378
+ );
379
+ }
380
+ break;
381
+ }
348
382
  safeInvoke(pendingQueue[i]);
349
383
  i++;
350
384
  }
351
385
  } finally {
352
- pendingQueue.length = 0;
353
- pendingSet.clear();
354
386
  notifyDepth--;
387
+ if (notifyDepth === 0) {
388
+ pendingQueue.length = 0;
389
+ pendingSet.clear();
390
+ }
355
391
  }
356
392
  }
357
393
  function cleanup(subscriber) {
@@ -362,7 +398,9 @@ function cleanup(subscriber) {
362
398
  if (subs) {
363
399
  subs.delete(subscriber);
364
400
  if (singleDep.__f === subscriber) {
365
- singleDep.__f = void 0;
401
+ singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
402
+ } else if (subs.size === 1 && singleDep.__f === void 0) {
403
+ singleDep.__f = subs.values().next().value;
366
404
  }
367
405
  }
368
406
  sub._dep = void 0;
@@ -375,7 +413,9 @@ function cleanup(subscriber) {
375
413
  if (subs) {
376
414
  subs.delete(subscriber);
377
415
  if (signal2.__f === subscriber) {
378
- signal2.__f = void 0;
416
+ signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
417
+ } else if (subs.size === 1 && signal2.__f === void 0) {
418
+ signal2.__f = subs.values().next().value;
379
419
  }
380
420
  }
381
421
  }
@@ -429,7 +469,17 @@ function signal(initial, options) {
429
469
  return [get, set];
430
470
  }
431
471
 
472
+ // src/utils/sanitize.ts
473
+ function stripHtml(html) {
474
+ return String(html).replace(/<[^>]*>/g, "");
475
+ }
476
+
432
477
  // src/devtools/devtools.ts
478
+ function getSibuNamespace() {
479
+ const g = globalThis;
480
+ if (!g.__SIBU__) g.__SIBU__ = { version: "1.0.0" };
481
+ return g.__SIBU__;
482
+ }
433
483
  function createGlobalHook() {
434
484
  const listeners = /* @__PURE__ */ new Map();
435
485
  const events = [];
@@ -474,13 +524,16 @@ function getOrCreateHook() {
474
524
  }
475
525
  var activeDevTools = null;
476
526
  var nextNodeId = 0;
527
+ var inferNameArmed = false;
477
528
  function getActiveDevTools() {
478
529
  return activeDevTools;
479
530
  }
480
531
  function initDevTools(config) {
481
532
  const maxEvents = config?.maxEvents ?? 1e3;
482
533
  const enabled = config?.enabled ?? isDev();
534
+ const expose = config?.expose ?? isDev();
483
535
  if (!enabled) return createNoopApi();
536
+ inferNameArmed = true;
484
537
  const hook = getOrCreateHook();
485
538
  nextNodeId = 0;
486
539
  const eventLog = [];
@@ -497,6 +550,7 @@ function initDevTools(config) {
497
550
  createdAt: Date.now()
498
551
  };
499
552
  hook.nodes.set(nextNodeId, node);
553
+ emit();
500
554
  });
501
555
  hook.on("signal:update", (payload) => {
502
556
  if (!isActive) return;
@@ -511,6 +565,7 @@ function initDevTools(config) {
511
565
  newValue: p.newValue,
512
566
  timestamp: Date.now()
513
567
  });
568
+ emit();
514
569
  });
515
570
  hook.on("computed:create", (payload) => {
516
571
  const p = payload;
@@ -525,6 +580,7 @@ function initDevTools(config) {
525
580
  createdAt: Date.now()
526
581
  };
527
582
  hook.nodes.set(nextNodeId, node);
583
+ emit();
528
584
  });
529
585
  hook.on("computed:update", (payload) => {
530
586
  const p = payload;
@@ -537,6 +593,7 @@ function initDevTools(config) {
537
593
  newValue: p.newValue,
538
594
  timestamp: Date.now()
539
595
  });
596
+ emit();
540
597
  });
541
598
  hook.on("effect:create", (payload) => {
542
599
  nextNodeId++;
@@ -544,6 +601,7 @@ function initDevTools(config) {
544
601
  const p = payload;
545
602
  const node = { id: nextNodeId, type: "effect", name, ref: p.effectFn, createdAt: Date.now() };
546
603
  hook.nodes.set(nextNodeId, node);
604
+ emit();
547
605
  });
548
606
  hook.on("effect:run", (payload) => {
549
607
  const p = payload;
@@ -554,6 +612,7 @@ function initDevTools(config) {
554
612
  duration: 0,
555
613
  timestamp: Date.now()
556
614
  });
615
+ emit();
557
616
  });
558
617
  hook.on("app:init", (payload) => {
559
618
  const p = payload;
@@ -563,12 +622,19 @@ function initDevTools(config) {
563
622
  duration: p.duration,
564
623
  timestamp: Date.now()
565
624
  });
625
+ emit();
566
626
  if (typeof document !== "undefined") {
567
- queueMicrotask(() => discoverComponents(hook, eventLog, maxEvents));
627
+ queueMicrotask(() => {
628
+ discoverComponents(hook, eventLog, maxEvents);
629
+ emit();
630
+ });
568
631
  }
569
632
  });
570
633
  function record(event) {
571
- if (isActive) pushEvent(eventLog, maxEvents, event);
634
+ if (isActive) {
635
+ pushEvent(eventLog, maxEvents, event);
636
+ emit();
637
+ }
572
638
  }
573
639
  function getEvents(filter) {
574
640
  if (!filter) return eventLog.slice();
@@ -583,9 +649,11 @@ function initDevTools(config) {
583
649
  }
584
650
  function registerComponent(name, element, state) {
585
651
  hook.components.set(name, { element, state });
652
+ emit();
586
653
  }
587
654
  function unregisterComponent(name) {
588
655
  hook.components.delete(name);
656
+ emit();
589
657
  }
590
658
  function getComponents() {
591
659
  return new Map(hook.components);
@@ -612,6 +680,12 @@ function initDevTools(config) {
612
680
  }
613
681
  return result;
614
682
  }
683
+ let domObserver = null;
684
+ const activeHighlightTimers = /* @__PURE__ */ new Set();
685
+ let changeVersion = 0;
686
+ function emit() {
687
+ changeVersion++;
688
+ }
615
689
  let isActive = enabled;
616
690
  function isEnabled() {
617
691
  return isActive;
@@ -624,32 +698,75 @@ function initDevTools(config) {
624
698
  for (const [name, entry] of hook.components) snap[name] = entry.state ? { ...entry.state } : {};
625
699
  return snap;
626
700
  }
701
+ function restoreHighlight(el) {
702
+ const prevOutline = el.dataset.sibuHighlightPrevOutline;
703
+ const prevOffset = el.dataset.sibuHighlightPrevOffset;
704
+ el.style.outline = prevOutline ?? "";
705
+ el.style.outlineOffset = prevOffset ?? "";
706
+ delete el.dataset.sibuHighlightPrevOutline;
707
+ delete el.dataset.sibuHighlightPrevOffset;
708
+ el.removeAttribute("data-sibu-highlight");
709
+ }
627
710
  function highlightElement(name) {
628
711
  const prev = document.querySelector("[data-sibu-highlight]");
629
- if (prev) {
630
- prev.style.outline = "";
631
- prev.removeAttribute("data-sibu-highlight");
632
- }
712
+ if (prev instanceof HTMLElement) restoreHighlight(prev);
713
+ const entry = hook.components.get(name);
714
+ const el = entry?.element;
715
+ if (!el || !el.isConnected) return;
716
+ el.dataset.sibuHighlightPrevOutline = el.style.outline || "";
717
+ el.dataset.sibuHighlightPrevOffset = el.style.outlineOffset || "";
718
+ el.style.outline = "2px solid #89b4fa";
719
+ el.setAttribute("data-sibu-highlight", "true");
720
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
721
+ const timer = setTimeout(() => {
722
+ activeHighlightTimers.delete(timer);
723
+ if (!el.isConnected) return;
724
+ restoreHighlight(el);
725
+ }, 3e3);
726
+ activeHighlightTimers.add(timer);
727
+ }
728
+ function getElementHTML(name, max = 2e3) {
633
729
  const entry = hook.components.get(name);
634
- if (entry?.element?.isConnected) {
635
- entry.element.style.outline = "2px solid #89b4fa";
636
- entry.element.setAttribute("data-sibu-highlight", "true");
637
- entry.element.scrollIntoView({ behavior: "smooth", block: "center" });
638
- setTimeout(() => {
639
- entry.element.style.outline = "";
640
- entry.element.removeAttribute("data-sibu-highlight");
641
- }, 3e3);
730
+ const el = entry?.element;
731
+ if (!el) return null;
732
+ try {
733
+ const raw = el.outerHTML || "";
734
+ const cleaned = stripHtml(raw);
735
+ return cleaned.length > max ? `${cleaned.substring(0, max)}...` : cleaned;
736
+ } catch (err) {
737
+ if (typeof console !== "undefined") {
738
+ console.warn("[SibuJS devtools] getElementHTML failed:", err);
739
+ }
740
+ return null;
642
741
  }
643
742
  }
644
743
  function destroy() {
744
+ if (domObserver) {
745
+ domObserver.disconnect();
746
+ domObserver = null;
747
+ }
748
+ for (const t of activeHighlightTimers) clearTimeout(t);
749
+ activeHighlightTimers.clear();
750
+ if (typeof document !== "undefined") {
751
+ const prev = document.querySelector("[data-sibu-highlight]");
752
+ if (prev instanceof HTMLElement) restoreHighlight(prev);
753
+ }
645
754
  eventLog.length = 0;
646
755
  hook.nodes.clear();
647
756
  hook.components.clear();
648
757
  hook.events.length = 0;
649
758
  isActive = false;
759
+ inferNameArmed = false;
650
760
  activeDevTools = null;
651
761
  const g = globalThis;
762
+ if (g.__SIBU__) {
763
+ delete g.__SIBU__.devtools;
764
+ delete g.__SIBU__.data;
765
+ delete g.__SIBU__.changeVersion;
766
+ }
652
767
  if (g.__SIBU_DEVTOOLS__ === api) delete g.__SIBU_DEVTOOLS__;
768
+ delete g.__SIBU_DEVTOOLS_VERSION__;
769
+ delete g.__SIBU_DEVTOOLS_DATA__;
653
770
  if (g.__SIBU_DEVTOOLS_GLOBAL_HOOK__ === hook) delete g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
654
771
  }
655
772
  const api = {
@@ -664,164 +781,155 @@ function initDevTools(config) {
664
781
  setEnabled,
665
782
  snapshot,
666
783
  highlightElement,
784
+ getElementHTML,
667
785
  destroy
668
786
  };
669
- if (typeof window !== "undefined") {
670
- window.__SIBU_DEVTOOLS__ = api;
671
- let changeVersion = 0;
672
- const origPush = eventLog.push.bind(eventLog);
673
- eventLog.push = (...args) => {
674
- changeVersion++;
675
- return origPush(...args);
676
- };
677
- const origNodeSet = hook.nodes.set.bind(hook.nodes);
678
- hook.nodes.set = (k, v) => {
679
- changeVersion++;
680
- return origNodeSet(k, v);
681
- };
682
- const origCompSet = hook.components.set.bind(hook.components);
683
- hook.components.set = (k, v) => {
684
- changeVersion++;
685
- return origCompSet(k, v);
686
- };
687
- window.__SIBU_DEVTOOLS_VERSION__ = () => changeVersion;
688
- window.__SIBU_DEVTOOLS_DATA__ = () => {
689
- const sArr = [];
690
- for (const [, node] of hook.nodes) {
691
- let val = "";
692
- try {
693
- let raw;
694
- if (node.type === "signal" && node.ref && "value" in node.ref) {
695
- raw = node.ref.value;
696
- } else if (node.type === "computed" && node.ref) {
697
- if (node.ref._d && node.ref._g) {
698
- try {
699
- raw = node.ref._g();
700
- } catch {
701
- raw = node.ref._v;
702
- }
703
- } else {
787
+ function buildData() {
788
+ const sArr = [];
789
+ for (const [, node] of hook.nodes) {
790
+ let val = "";
791
+ try {
792
+ let raw;
793
+ if (node.type === "signal" && node.ref && "value" in node.ref) {
794
+ raw = node.ref.value;
795
+ } else if (node.type === "computed" && node.ref) {
796
+ if (node.ref._d && node.ref._g) {
797
+ try {
798
+ raw = node.ref._g();
799
+ } catch {
704
800
  raw = node.ref._v;
705
801
  }
706
- } else if (node.type === "effect") {
707
- raw = void 0;
708
- } else if (node.ref && "value" in node.ref) {
709
- raw = node.ref.value;
802
+ } else {
803
+ raw = node.ref._v;
710
804
  }
711
- if (raw === void 0) val = "undefined";
712
- else if (raw === null) val = "null";
713
- else if (typeof raw === "object") val = JSON.stringify(raw);
714
- else val = String(raw);
715
- } catch {
716
- val = "?";
805
+ } else if (node.type === "effect") {
806
+ raw = void 0;
807
+ } else if (node.ref && "value" in node.ref) {
808
+ raw = node.ref.value;
717
809
  }
718
- const fullVal = val;
719
- const shortVal = val.length > 80 ? `${val.substring(0, 80)}...` : val;
720
- const subs = node.ref?.__s;
721
- sArr.push({
722
- id: node.id,
723
- n: node.name,
724
- tp: node.type,
725
- v: shortVal,
726
- fv: fullVal,
727
- sc: subs instanceof Set ? subs.size : 0
728
- });
810
+ if (raw === void 0) val = "undefined";
811
+ else if (raw === null) val = "null";
812
+ else if (typeof raw === "object") val = JSON.stringify(raw);
813
+ else val = String(raw);
814
+ } catch {
815
+ val = "?";
729
816
  }
730
- function walkElement(el, depth) {
731
- if (depth > 8) return [];
732
- const result = [];
733
- const max = Math.min(el.children.length, 50);
734
- for (let i = 0; i < max; i++) {
735
- const child = el.children[i];
736
- const txtParts = [];
737
- for (let ti = 0; ti < child.childNodes.length; ti++) {
738
- const cn = child.childNodes[ti];
739
- if (cn.nodeType === 3) {
740
- const t = cn.textContent?.trim();
741
- if (t) txtParts.push(t);
742
- } else if (cn.nodeType === 1) {
743
- const el2 = cn;
744
- let directTxt = "";
745
- for (let ci = 0; ci < el2.childNodes.length; ci++) {
746
- if (el2.childNodes[ci].nodeType === 3) {
747
- const t2 = el2.childNodes[ci].textContent?.trim();
748
- if (t2) {
749
- directTxt = t2;
750
- break;
751
- }
817
+ const fullVal = val;
818
+ const shortVal = val.length > 80 ? `${val.substring(0, 80)}...` : val;
819
+ const subs = node.ref?.__s;
820
+ sArr.push({
821
+ id: node.id,
822
+ n: node.name,
823
+ tp: node.type,
824
+ v: shortVal,
825
+ fv: fullVal,
826
+ sc: subs instanceof Set ? subs.size : 0
827
+ });
828
+ }
829
+ function walkElement(el, depth) {
830
+ if (depth > 8) return [];
831
+ const result = [];
832
+ const max = Math.min(el.children.length, 50);
833
+ for (let i = 0; i < max; i++) {
834
+ const child = el.children[i];
835
+ const txtParts = [];
836
+ for (let ti = 0; ti < child.childNodes.length; ti++) {
837
+ const cn = child.childNodes[ti];
838
+ if (cn.nodeType === 3) {
839
+ const t = cn.textContent?.trim();
840
+ if (t) txtParts.push(t);
841
+ } else if (cn.nodeType === 1) {
842
+ const el2 = cn;
843
+ let directTxt = "";
844
+ for (let ci = 0; ci < el2.childNodes.length; ci++) {
845
+ if (el2.childNodes[ci].nodeType === 3) {
846
+ const t2 = el2.childNodes[ci].textContent?.trim();
847
+ if (t2) {
848
+ directTxt = t2;
849
+ break;
752
850
  }
753
851
  }
754
- if (directTxt) txtParts.push(directTxt);
755
852
  }
853
+ if (directTxt) txtParts.push(directTxt);
756
854
  }
757
- let txt = txtParts.join(" | ");
758
- if (txt.length > 120) txt = `${txt.substring(0, 120)}...`;
759
- let html = "";
760
- try {
761
- const outer = child.outerHTML || "";
762
- html = outer.length > 500 ? `${outer.substring(0, 500)}...` : outer;
763
- } catch {
764
- html = "";
765
- }
766
- const ev = child.__sibu_events__ || [];
767
- result.push({
768
- tg: child.tagName ? child.tagName.toLowerCase() : "?",
769
- id: child.id || "",
770
- cl: (child.className || "").toString().split(" ").slice(0, 3).join(" "),
771
- txt: txt.length > 60 ? `${txt.substring(0, 60)}...` : txt,
772
- html,
773
- ev,
774
- ch: child.childElementCount > 0 ? walkElement(child, depth + 1) : []
775
- });
776
855
  }
777
- return result;
778
- }
779
- const cArr = [];
780
- for (const [name, entry] of hook.components) {
781
- const el = entry.element;
782
- cArr.push({
783
- n: name,
784
- tg: el?.tagName ? el.tagName.toLowerCase() : "?",
785
- ch: el?.childElementCount || 0,
786
- cn: !!el?.isConnected,
787
- kids: el ? walkElement(el, 0) : []
856
+ let txt = txtParts.join(" | ");
857
+ if (txt.length > 120) txt = `${txt.substring(0, 120)}...`;
858
+ const attrs = [];
859
+ try {
860
+ const al = child.attributes;
861
+ for (let ai = 0; ai < al.length && ai < 20; ai++) attrs.push(al[ai].name);
862
+ } catch {
863
+ }
864
+ const ev = child.__sibu_events__ || [];
865
+ result.push({
866
+ tg: child.tagName ? child.tagName.toLowerCase() : "?",
867
+ id: child.id || "",
868
+ cl: (child.className || "").toString().split(" ").slice(0, 3).join(" "),
869
+ txt: txt.length > 60 ? `${txt.substring(0, 60)}...` : txt,
870
+ attrs,
871
+ ev,
872
+ ch: child.childElementCount > 0 ? walkElement(child, depth + 1) : []
788
873
  });
789
874
  }
790
- const eArr = [];
791
- const start = eventLog.length > 500 ? eventLog.length - 500 : 0;
792
- for (let i = start; i < eventLog.length; i++) {
793
- const e = eventLog[i];
794
- let detail = "";
795
- let ov = "";
796
- let nv = "";
797
- let key = "";
798
- if (e.type === "state-change") {
799
- const sc = e;
800
- key = sc.key || "";
801
- try {
802
- ov = sc.oldValue === void 0 ? "undefined" : sc.oldValue === null ? "null" : typeof sc.oldValue === "object" ? JSON.stringify(sc.oldValue, null, 2) : String(sc.oldValue);
803
- } catch {
804
- ov = "?";
805
- }
806
- try {
807
- nv = sc.newValue === void 0 ? "undefined" : sc.newValue === null ? "null" : typeof sc.newValue === "object" ? JSON.stringify(sc.newValue, null, 2) : String(sc.newValue);
808
- } catch {
809
- nv = "?";
810
- }
811
- const ovShort = ov.length > 40 ? `${ov.substring(0, 40)}...` : ov;
812
- const nvShort = nv.length > 40 ? `${nv.substring(0, 40)}...` : nv;
813
- detail = `${ovShort} \u2192 ${nvShort}`;
814
- } else if (e.type === "render") {
815
- detail = `${e.duration.toFixed(1)}ms`;
875
+ return result;
876
+ }
877
+ const cArr = [];
878
+ for (const [name, entry] of hook.components) {
879
+ const el = entry.element;
880
+ cArr.push({
881
+ n: name,
882
+ tg: el?.tagName ? el.tagName.toLowerCase() : "?",
883
+ ch: el?.childElementCount || 0,
884
+ cn: !!el?.isConnected,
885
+ kids: el ? walkElement(el, 0) : []
886
+ });
887
+ }
888
+ const eArr = [];
889
+ const start = eventLog.length > 500 ? eventLog.length - 500 : 0;
890
+ for (let i = start; i < eventLog.length; i++) {
891
+ const e = eventLog[i];
892
+ let detail = "";
893
+ let ov = "";
894
+ let nv = "";
895
+ let key = "";
896
+ if (e.type === "state-change") {
897
+ const sc = e;
898
+ key = sc.key || "";
899
+ try {
900
+ ov = sc.oldValue === void 0 ? "undefined" : sc.oldValue === null ? "null" : typeof sc.oldValue === "object" ? JSON.stringify(sc.oldValue, null, 2) : String(sc.oldValue);
901
+ } catch {
902
+ ov = "?";
816
903
  }
817
- eArr.push({ t: e.type, c: e.component, ts: e.timestamp, d: detail, ov, nv, k: key });
904
+ try {
905
+ nv = sc.newValue === void 0 ? "undefined" : sc.newValue === null ? "null" : typeof sc.newValue === "object" ? JSON.stringify(sc.newValue, null, 2) : String(sc.newValue);
906
+ } catch {
907
+ nv = "?";
908
+ }
909
+ const ovShort = ov.length > 40 ? `${ov.substring(0, 40)}...` : ov;
910
+ const nvShort = nv.length > 40 ? `${nv.substring(0, 40)}...` : nv;
911
+ detail = `${ovShort} \u2192 ${nvShort}`;
912
+ } else if (e.type === "render") {
913
+ detail = `${e.duration.toFixed(1)}ms`;
818
914
  }
819
- return JSON.stringify({ s: sArr, c: cArr, e: eArr });
820
- };
915
+ eArr.push({ t: e.type, c: e.component, ts: e.timestamp, d: detail, ov, nv, k: key });
916
+ }
917
+ return JSON.stringify({ s: sArr, c: cArr, e: eArr });
918
+ }
919
+ if (expose && typeof window !== "undefined") {
920
+ const ns = getSibuNamespace();
921
+ ns.devtools = api;
922
+ ns.data = buildData;
923
+ ns.changeVersion = () => changeVersion;
924
+ const w = window;
925
+ w.__SIBU_DEVTOOLS__ = api;
926
+ w.__SIBU_DEVTOOLS_VERSION__ = () => changeVersion;
927
+ w.__SIBU_DEVTOOLS_DATA__ = buildData;
821
928
  }
822
929
  activeDevTools = api;
823
930
  if (typeof document !== "undefined") {
824
- const observer = new MutationObserver((mutations) => {
931
+ domObserver = new MutationObserver((mutations) => {
932
+ let changed = false;
825
933
  for (const m of mutations) {
826
934
  for (const node of m.addedNodes) {
827
935
  if (node instanceof HTMLElement) {
@@ -829,6 +937,7 @@ function initDevTools(config) {
829
937
  if (name && !hook.components.has(name)) {
830
938
  hook.components.set(name, { element: node });
831
939
  pushEvent(eventLog, maxEvents, { type: "mount", component: name, element: node, timestamp: Date.now() });
940
+ changed = true;
832
941
  }
833
942
  }
834
943
  }
@@ -838,11 +947,14 @@ function initDevTools(config) {
838
947
  if (name && hook.components.has(name)) {
839
948
  pushEvent(eventLog, maxEvents, { type: "unmount", component: name, timestamp: Date.now() });
840
949
  hook.components.delete(name);
950
+ changed = true;
841
951
  }
842
952
  }
843
953
  }
844
954
  }
955
+ if (changed) emit();
845
956
  });
957
+ const observer = domObserver;
846
958
  queueMicrotask(() => {
847
959
  if (!document.body) return;
848
960
  observer.observe(document.body, { childList: true, subtree: true });
@@ -884,11 +996,13 @@ function initDevTools(config) {
884
996
  }
885
997
  });
886
998
  }
999
+ emit();
887
1000
  });
888
1001
  }
889
1002
  return api;
890
1003
  }
891
1004
  function inferName() {
1005
+ if (!inferNameArmed || !isDev()) return "anonymous";
892
1006
  try {
893
1007
  const stack = new Error().stack || "";
894
1008
  for (const line of stack.split("\n")) {
@@ -945,6 +1059,7 @@ function createNoopApi() {
945
1059
  setEnabled: noop,
946
1060
  snapshot: () => ({}),
947
1061
  highlightElement: noop,
1062
+ getElementHTML: ((_n) => null),
948
1063
  destroy: noop
949
1064
  };
950
1065
  }
@@ -976,17 +1091,90 @@ function devState(name, initial) {
976
1091
  return [get, trackedSet];
977
1092
  }
978
1093
 
1094
+ // src/core/rendering/dispose.ts
1095
+ var elementDisposers = /* @__PURE__ */ new WeakMap();
1096
+ var _isDev4 = isDev();
1097
+ var activeBindingCount = 0;
1098
+ function dispose(node) {
1099
+ const stack = [node];
1100
+ const order = [];
1101
+ while (stack.length > 0) {
1102
+ const current = stack.pop();
1103
+ order.push(current);
1104
+ const children = Array.from(current.childNodes);
1105
+ for (let i = 0; i < children.length; i++) {
1106
+ stack.push(children[i]);
1107
+ }
1108
+ }
1109
+ for (let i = order.length - 1; i >= 0; i--) {
1110
+ const current = order[i];
1111
+ const disposers = elementDisposers.get(current);
1112
+ if (disposers) {
1113
+ const snapshot = disposers.slice();
1114
+ elementDisposers.delete(current);
1115
+ if (_isDev4) activeBindingCount -= snapshot.length;
1116
+ for (const d of snapshot) {
1117
+ try {
1118
+ d();
1119
+ } catch (err) {
1120
+ if (_isDev4 && typeof console !== "undefined") {
1121
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
1122
+ }
1123
+ }
1124
+ }
1125
+ let extraPasses = 0;
1126
+ while (extraPasses++ < 8) {
1127
+ const added = elementDisposers.get(current);
1128
+ if (!added || added.length === 0) break;
1129
+ const moreSnapshot = added.slice();
1130
+ elementDisposers.delete(current);
1131
+ if (_isDev4) activeBindingCount -= moreSnapshot.length;
1132
+ for (const d of moreSnapshot) {
1133
+ try {
1134
+ d();
1135
+ } catch (err) {
1136
+ if (_isDev4 && typeof console !== "undefined") {
1137
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
1138
+ }
1139
+ }
1140
+ }
1141
+ }
1142
+ }
1143
+ }
1144
+ }
1145
+
979
1146
  // src/devtools/hmr.ts
1147
+ var HMR_STORE_MAX_SIZE = 200;
980
1148
  var hmrStateStore = /* @__PURE__ */ new Map();
1149
+ var hmrStoreOverflowWarned = false;
1150
+ function hmrStoreSet(id, value) {
1151
+ if (hmrStateStore.has(id)) hmrStateStore.delete(id);
1152
+ hmrStateStore.set(id, value);
1153
+ if (hmrStateStore.size > HMR_STORE_MAX_SIZE) {
1154
+ const oldestKey = hmrStateStore.keys().next().value;
1155
+ if (oldestKey !== void 0) hmrStateStore.delete(oldestKey);
1156
+ if (!hmrStoreOverflowWarned) {
1157
+ hmrStoreOverflowWarned = true;
1158
+ console.warn(
1159
+ `[sibujs/hmr] HMR state store exceeded ${HMR_STORE_MAX_SIZE} entries \u2014 oldest entries are being evicted. Call clearHMRModule(id) from your module's accept/dispose handlers to avoid this.`
1160
+ );
1161
+ }
1162
+ }
1163
+ }
1164
+ function clearHMRModule(id) {
1165
+ hmrStateStore.delete(id);
1166
+ hmrRegistry.delete(id);
1167
+ hmrRegistry.delete(`boundary:${id}`);
1168
+ }
981
1169
  var hmrRegistry = /* @__PURE__ */ new Map();
982
1170
  function hmrState(id, initial) {
983
1171
  const restored = hmrStateStore.has(id) ? hmrStateStore.get(id) : initial;
984
1172
  const [get, set] = signal(restored);
985
1173
  function hmrSet(next) {
986
1174
  set(next);
987
- hmrStateStore.set(id, get());
1175
+ hmrStoreSet(id, get());
988
1176
  }
989
- hmrStateStore.set(id, restored);
1177
+ hmrStoreSet(id, restored);
990
1178
  return [get, hmrSet];
991
1179
  }
992
1180
  function registerHMR(id, component, container) {
@@ -1012,13 +1200,15 @@ function registerHMR(id, component, container) {
1012
1200
  }
1013
1201
  reg.disposeCallbacks.length = 0;
1014
1202
  const newElement = newComponent();
1015
- if (reg.currentElement?.parentNode) {
1016
- reg.currentElement.parentNode.replaceChild(newElement, reg.currentElement);
1203
+ const oldElement = reg.currentElement;
1204
+ if (oldElement?.parentNode) {
1205
+ oldElement.parentNode.replaceChild(newElement, oldElement);
1017
1206
  }
1207
+ if (oldElement) dispose(oldElement);
1018
1208
  reg.component = newComponent;
1019
1209
  reg.currentElement = newElement;
1020
1210
  }
1021
- function dispose() {
1211
+ function dispose2() {
1022
1212
  const reg = hmrRegistry.get(id);
1023
1213
  if (!reg) return;
1024
1214
  for (const cb of reg.disposeCallbacks) {
@@ -1027,12 +1217,14 @@ function registerHMR(id, component, container) {
1027
1217
  } catch {
1028
1218
  }
1029
1219
  }
1030
- if (reg.currentElement?.parentNode) {
1031
- reg.currentElement.parentNode.removeChild(reg.currentElement);
1220
+ if (reg.currentElement) {
1221
+ const el = reg.currentElement;
1222
+ if (el.parentNode) el.parentNode.removeChild(el);
1223
+ dispose(el);
1032
1224
  }
1033
1225
  hmrRegistry.delete(id);
1034
1226
  }
1035
- return { update, dispose };
1227
+ return { update, dispose: dispose2 };
1036
1228
  }
1037
1229
  function createHMRBoundary(id) {
1038
1230
  let currentElement = null;
@@ -1069,10 +1261,15 @@ function createHMRBoundary(id) {
1069
1261
  }
1070
1262
  }
1071
1263
  disposeCallbacks.length = 0;
1072
- if (currentComponent && currentElement?.parentNode) {
1073
- const newElement = currentComponent();
1074
- currentElement.parentNode.replaceChild(newElement, currentElement);
1075
- currentElement = newElement;
1264
+ if (currentComponent && currentElement) {
1265
+ const oldEl = currentElement;
1266
+ const parent = oldEl.parentNode;
1267
+ if (parent) {
1268
+ const newElement = currentComponent();
1269
+ parent.replaceChild(newElement, oldEl);
1270
+ dispose(oldEl);
1271
+ currentElement = newElement;
1272
+ }
1076
1273
  }
1077
1274
  for (const cb of acceptCallbacks) {
1078
1275
  try {
@@ -1092,6 +1289,19 @@ function createHMRBoundary(id) {
1092
1289
  function clearHMRState() {
1093
1290
  hmrStateStore.clear();
1094
1291
  hmrRegistry.clear();
1292
+ hmrStoreOverflowWarned = false;
1293
+ }
1294
+ function exposeHMR() {
1295
+ const g = globalThis;
1296
+ if (!g.__SIBU__) g.__SIBU__ = { version: "1.0.0" };
1297
+ g.__SIBU__.hmr = {
1298
+ hmrState,
1299
+ registerHMR,
1300
+ createHMRBoundary,
1301
+ clearHMRState,
1302
+ clearHMRModule,
1303
+ isHMRAvailable
1304
+ };
1095
1305
  }
1096
1306
  function isHMRAvailable() {
1097
1307
  const g = globalThis;
@@ -1227,9 +1437,28 @@ function formatError(error, context) {
1227
1437
  }
1228
1438
 
1229
1439
  // src/core/ssr-context.ts
1230
- var ssrMode = false;
1440
+ var als = null;
1441
+ try {
1442
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
1443
+ const req = Function("return typeof require==='function'?require:null")();
1444
+ if (req) {
1445
+ const mod = req("node:async_hooks");
1446
+ als = new mod.AsyncLocalStorage();
1447
+ }
1448
+ }
1449
+ } catch {
1450
+ als = null;
1451
+ }
1452
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
1453
+ function getSSRStore() {
1454
+ if (als) {
1455
+ const s = als.getStore();
1456
+ if (s) return s;
1457
+ }
1458
+ return fallbackStore;
1459
+ }
1231
1460
  function isSSR() {
1232
- return ssrMode;
1461
+ return getSSRStore().ssr;
1233
1462
  }
1234
1463
 
1235
1464
  // src/core/signals/effect.ts
@@ -1239,26 +1468,86 @@ function effect(effectFn, options) {
1239
1468
  if (isSSR()) return () => {
1240
1469
  };
1241
1470
  const onError = options?.onError;
1471
+ let userCleanups = [];
1472
+ const onCleanup = (fn) => {
1473
+ userCleanups.push(fn);
1474
+ };
1475
+ const runUserCleanups = () => {
1476
+ if (userCleanups.length === 0) return;
1477
+ const list = userCleanups;
1478
+ userCleanups = [];
1479
+ for (let i = list.length - 1; i >= 0; i--) {
1480
+ try {
1481
+ list[i]();
1482
+ } catch (err) {
1483
+ if (typeof console !== "undefined") {
1484
+ console.warn("[SibuJS effect] onCleanup threw:", err);
1485
+ }
1486
+ }
1487
+ }
1488
+ };
1489
+ const invokeBody = () => effectFn(onCleanup);
1242
1490
  const wrappedFn = onError ? () => {
1243
1491
  try {
1244
- effectFn();
1492
+ invokeBody();
1245
1493
  } catch (err) {
1246
1494
  onError(err);
1247
1495
  }
1248
- } : effectFn;
1496
+ } : invokeBody;
1249
1497
  let cleanupHandle = () => {
1250
1498
  };
1499
+ let running = false;
1251
1500
  const subscriber = () => {
1252
- cleanupHandle();
1253
- cleanupHandle = track(wrappedFn, subscriber);
1501
+ if (running) {
1502
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1503
+ console.warn(
1504
+ "[SibuJS] effect re-entered itself while running \u2014 the triggering update will be ignored. Wrap mutual writes in `batch()` or split the effect to avoid this."
1505
+ );
1506
+ }
1507
+ return;
1508
+ }
1509
+ running = true;
1510
+ try {
1511
+ runUserCleanups();
1512
+ cleanupHandle();
1513
+ cleanupHandle = track(wrappedFn, subscriber);
1514
+ } finally {
1515
+ running = false;
1516
+ }
1254
1517
  };
1255
- cleanupHandle = track(wrappedFn, subscriber);
1518
+ running = true;
1519
+ try {
1520
+ cleanupHandle = track(wrappedFn, subscriber);
1521
+ } finally {
1522
+ running = false;
1523
+ }
1256
1524
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1257
1525
  if (hook) hook.emit("effect:create", { effectFn });
1526
+ let disposed = false;
1258
1527
  return () => {
1528
+ if (disposed) return;
1529
+ disposed = true;
1259
1530
  const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1260
- if (h) h.emit("effect:destroy", { effectFn });
1261
- cleanupHandle();
1531
+ if (h) {
1532
+ try {
1533
+ h.emit("effect:destroy", { effectFn });
1534
+ } catch {
1535
+ }
1536
+ }
1537
+ try {
1538
+ runUserCleanups();
1539
+ } catch (err) {
1540
+ if (typeof console !== "undefined") {
1541
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
1542
+ }
1543
+ }
1544
+ try {
1545
+ cleanupHandle();
1546
+ } catch (err) {
1547
+ if (typeof console !== "undefined") {
1548
+ console.warn("[SibuJS effect] dispose threw:", err);
1549
+ }
1550
+ }
1262
1551
  };
1263
1552
  }
1264
1553
 
@@ -1268,13 +1557,13 @@ function debugValue(value, formatter) {
1268
1557
  const format = formatter ?? ((v) => String(v));
1269
1558
  const entry = { value: void 0, label: "" };
1270
1559
  debugValues.push(entry);
1271
- const dispose = effect(() => {
1560
+ const dispose2 = effect(() => {
1272
1561
  const resolved = value();
1273
1562
  entry.value = resolved;
1274
1563
  entry.label = format(resolved);
1275
1564
  });
1276
1565
  return () => {
1277
- dispose();
1566
+ dispose2();
1278
1567
  const idx = debugValues.indexOf(entry);
1279
1568
  if (idx !== -1) debugValues.splice(idx, 1);
1280
1569
  };
@@ -1354,11 +1643,11 @@ function createDevtoolsOverlay(options) {
1354
1643
  const getPanels = () => {
1355
1644
  return [...panels];
1356
1645
  };
1357
- const dispose = () => {
1646
+ const dispose2 = () => {
1358
1647
  panels.length = 0;
1359
1648
  setEnabled(false);
1360
1649
  };
1361
- return { isEnabled, toggle, addPanel, removePanel, getPanels, dispose };
1650
+ return { isEnabled, toggle, addPanel, removePanel, getPanels, dispose: dispose2 };
1362
1651
  }
1363
1652
 
1364
1653
  // src/devtools/introspect.ts
@@ -1386,25 +1675,24 @@ function inspectSignal(getter) {
1386
1675
  subscriberCount: subs ? subs.size : 0
1387
1676
  };
1388
1677
  }
1389
- function walkDependencyGraph(getter, maxDepth = 10) {
1678
+ function walkDependencyGraph(getter, maxDepth = 10, visited = /* @__PURE__ */ new WeakSet()) {
1390
1679
  const signal2 = getter.__signal;
1391
- if (!signal2 || maxDepth <= 0) {
1680
+ if (!signal2 || maxDepth <= 0 || visited.has(signal2)) {
1392
1681
  return { name: getSignalName(getter), subscribers: 0, downstream: [] };
1393
1682
  }
1683
+ visited.add(signal2);
1394
1684
  const subs = signal2[SUBS2];
1395
1685
  const downstream = [];
1396
1686
  if (subs) {
1397
1687
  for (const sub of subs) {
1398
1688
  const subSig = sub._sig;
1399
- if (subSig) {
1689
+ if (subSig && !visited.has(subSig)) {
1400
1690
  const subName = subSig.__name;
1401
- const subSubs = subSig[SUBS2];
1402
- downstream.push({
1403
- name: subName,
1404
- subscribers: subSubs ? subSubs.size : 0,
1405
- downstream: []
1406
- // Could recurse but we keep it simple
1407
- });
1691
+ const fakeGetter = (() => void 0);
1692
+ const tag = fakeGetter;
1693
+ tag.__signal = subSig;
1694
+ if (subName !== void 0) tag.__name = subName;
1695
+ downstream.push(walkDependencyGraph(fakeGetter, maxDepth - 1, visited));
1408
1696
  }
1409
1697
  }
1410
1698
  }
@@ -1534,6 +1822,7 @@ function createTraceProfiler() {
1534
1822
  captureSignalGraph,
1535
1823
  checkLeaks,
1536
1824
  clearDebugValues,
1825
+ clearHMRModule,
1537
1826
  clearHMRState,
1538
1827
  clearPerformanceData,
1539
1828
  createDevtoolsOverlay,
@@ -1547,6 +1836,7 @@ function createTraceProfiler() {
1547
1836
  diffSignalGraphs,
1548
1837
  disableDebug,
1549
1838
  enableDebug,
1839
+ exposeHMR,
1550
1840
  formatError,
1551
1841
  getActiveDevTools,
1552
1842
  getDebugValues,