sibujs 1.5.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/dist/browser.cjs +332 -121
  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 +1049 -344
  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-BMPL52BF.js → chunk-3DZP6OIT.js} +118 -66
  11. package/dist/chunk-3JHCYHWN.js +125 -0
  12. package/dist/{chunk-CZUGLNJS.js → chunk-45YP72ZQ.js} +3 -3
  13. package/dist/{chunk-JCDUJN2F.js → chunk-AMK2TYNW.js} +490 -153
  14. package/dist/{chunk-NHUC2QWH.js → chunk-CWBVQML6.js} +1 -1
  15. package/dist/{chunk-XHK6BDAJ.js → chunk-DRUZZAK4.js} +25 -8
  16. package/dist/{chunk-RJ46C3CS.js → chunk-GWWURC5M.js} +71 -20
  17. package/dist/{chunk-3X2YG6YM.js → chunk-JYD2PWXH.js} +59 -28
  18. package/dist/{chunk-2BYQDGN3.js → chunk-KGYT6UO6.js} +234 -63
  19. package/dist/{chunk-5X6PP2UK.js → chunk-LMLD24FC.js} +2 -2
  20. package/dist/{chunk-M4NLBH4I.js → chunk-LYTCUZ7H.js} +3 -2
  21. package/dist/{chunk-XUEEGU5O.js → chunk-NASX6ST2.js} +16 -4
  22. package/dist/{chunk-VQDZK23A.js → chunk-O6EFQ3KT.js} +181 -66
  23. package/dist/{chunk-BGN5ZMP4.js → chunk-OJ3P4ECI.js} +14 -2
  24. package/dist/chunk-ON5MMR2J.js +1327 -0
  25. package/dist/{chunk-SFKNRVCU.js → chunk-P2HSJDDN.js} +135 -79
  26. package/dist/chunk-QO3WC6FS.js +384 -0
  27. package/dist/{chunk-WZSPOOER.js → chunk-RDTDJCAB.js} +8 -5
  28. package/dist/{chunk-7GRNSCFT.js → chunk-TH2ILCYW.js} +312 -185
  29. package/dist/chunk-UCS6AMJ7.js +79 -0
  30. package/dist/{chunk-VAPYJN4X.js → chunk-V6C4FADE.js} +93 -23
  31. package/dist/{chunk-OUZZEE4S.js → chunk-WANSMF2L.js} +17 -11
  32. package/dist/{chunk-23VV7YD3.js → chunk-WIPZPFBQ.js} +25 -30
  33. package/dist/chunk-WZA53FXU.js +149 -0
  34. package/dist/{chunk-BGTHZHJ5.js → chunk-ZAQSMOED.js} +188 -44
  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 +536 -151
  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 +613 -266
  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 +602 -197
  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 +3500 -1608
  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 +1055 -344
  54. package/dist/index.d.cts +85 -8
  55. package/dist/index.d.ts +85 -8
  56. package/dist/index.js +32 -16
  57. package/dist/{introspect-BumjnBKr.d.cts → introspect-2TOlQ7oa.d.cts} +25 -3
  58. package/dist/{introspect-CZrlcaYy.d.ts → introspect-DnIpHQQz.d.ts} +25 -3
  59. package/dist/motion.cjs +122 -63
  60. package/dist/motion.js +4 -4
  61. package/dist/patterns.cjs +450 -110
  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 +373 -149
  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 +729 -301
  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 +736 -274
  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 +303 -76
  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 +589 -178
  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 +1103 -146
  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-K4G4ZQNR.js +0 -286
  136. package/dist/chunk-K5ZUMYVS.js +0 -89
  137. package/dist/chunk-KQPDEVVS.js +0 -398
  138. package/dist/chunk-L6JRBDNS.js +0 -60
  139. package/dist/chunk-LA6KQEDU.js +0 -712
  140. package/dist/chunk-MB6QFH3I.js +0 -2776
  141. package/dist/chunk-MDVXJWFN.js +0 -304
  142. package/dist/chunk-MEZVEBPN.js +0 -2008
  143. package/dist/chunk-MK4ERFYL.js +0 -2249
  144. package/dist/chunk-MLKGABMK.js +0 -9
  145. package/dist/chunk-MQ5GOYPH.js +0 -2249
  146. package/dist/chunk-MYRV7VDM.js +0 -742
  147. package/dist/chunk-N6IZB6KJ.js +0 -567
  148. package/dist/chunk-NEKUBFPT.js +0 -60
  149. package/dist/chunk-NMRUZALC.js +0 -1097
  150. package/dist/chunk-NYVAC6P5.js +0 -37
  151. package/dist/chunk-NZIIMDWI.js +0 -84
  152. package/dist/chunk-OF7UZIVB.js +0 -725
  153. package/dist/chunk-P3XWXJZU.js +0 -282
  154. package/dist/chunk-P6W3STU4.js +0 -2249
  155. package/dist/chunk-PBHF5WKN.js +0 -616
  156. package/dist/chunk-PDZQY43A.js +0 -616
  157. package/dist/chunk-PTQJDMRT.js +0 -146
  158. package/dist/chunk-PZEGYCF5.js +0 -61
  159. package/dist/chunk-QBMDLBU2.js +0 -975
  160. package/dist/chunk-QWZG56ET.js +0 -2744
  161. package/dist/chunk-RQGQSLQK.js +0 -725
  162. package/dist/chunk-SDLZDHKP.js +0 -107
  163. package/dist/chunk-TDGZL5CU.js +0 -365
  164. package/dist/chunk-TNQWPPE6.js +0 -37
  165. package/dist/chunk-TSOKIX5Z.js +0 -654
  166. package/dist/chunk-UHNL42EF.js +0 -2730
  167. package/dist/chunk-UNXCEF6S.js +0 -21
  168. package/dist/chunk-V2XTI523.js +0 -347
  169. package/dist/chunk-VAU366PN.js +0 -2241
  170. package/dist/chunk-VMVDTCXB.js +0 -712
  171. package/dist/chunk-VQNQZCWJ.js +0 -61
  172. package/dist/chunk-VRW3FULF.js +0 -725
  173. package/dist/chunk-WADYRCO2.js +0 -304
  174. package/dist/chunk-WILQZRO4.js +0 -282
  175. package/dist/chunk-WR5D4EGH.js +0 -26
  176. package/dist/chunk-WUHJISPP.js +0 -298
  177. package/dist/chunk-XYU6TZOW.js +0 -182
  178. package/dist/chunk-Y6GP4QGG.js +0 -276
  179. package/dist/chunk-YECR7UIA.js +0 -347
  180. package/dist/chunk-YUTWTI4B.js +0 -654
  181. package/dist/chunk-Z65KYU7I.js +0 -26
  182. package/dist/chunk-Z6POF5YC.js +0 -975
  183. package/dist/chunk-ZBJP6WFL.js +0 -482
  184. package/dist/chunk-ZD6OAMTH.js +0 -277
  185. package/dist/chunk-ZWKZCBO6.js +0 -317
  186. package/dist/contracts-DDrwxvJ-.d.cts +0 -245
  187. package/dist/contracts-DDrwxvJ-.d.ts +0 -245
  188. package/dist/contracts-DOrhwbke.d.cts +0 -245
  189. package/dist/contracts-DOrhwbke.d.ts +0 -245
  190. package/dist/contracts-xo5ckdRP.d.cts +0 -240
  191. package/dist/contracts-xo5ckdRP.d.ts +0 -240
  192. package/dist/customElement-BKQfbSZQ.d.cts +0 -262
  193. package/dist/customElement-BKQfbSZQ.d.ts +0 -262
  194. package/dist/customElement-D2DJp_xn.d.cts +0 -313
  195. package/dist/customElement-D2DJp_xn.d.ts +0 -313
  196. package/dist/customElement-yz8uyk-0.d.cts +0 -308
  197. package/dist/customElement-yz8uyk-0.d.ts +0 -308
  198. package/dist/introspect-Cb0zgpi2.d.cts +0 -477
  199. package/dist/introspect-Y2xNXGSf.d.ts +0 -477
  200. package/dist/plugin-Bek4RhJY.d.cts +0 -43
  201. package/dist/plugin-Bek4RhJY.d.ts +0 -43
  202. package/dist/ssr-3RXHP5ES.js +0 -38
  203. package/dist/ssr-6GIMY5MX.js +0 -38
  204. package/dist/ssr-BA6sxxUd.d.cts +0 -135
  205. package/dist/ssr-BA6sxxUd.d.ts +0 -135
  206. package/dist/ssr-WKUPVSSK.js +0 -36
  207. package/dist/tagFactory-Dl8QCLga.d.cts +0 -23
  208. 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,26 +173,39 @@ 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
 
180
185
  // src/reactivity/track.ts
181
186
  var _isDev2 = isDev();
182
- var subscriberStack = new Array(32);
183
- var stackCapacity = 32;
187
+ var STACK_INITIAL = 32;
188
+ var STACK_SHRINK_THRESHOLD = 128;
189
+ var subscriberStack = new Array(STACK_INITIAL);
190
+ var stackCapacity = STACK_INITIAL;
184
191
  var stackTop = -1;
185
192
  var currentSubscriber = null;
186
- var signalSubscribers = /* @__PURE__ */ new WeakMap();
187
193
  var SUBS = "__s";
194
+ function syncFastPath(signal2, subs) {
195
+ const size = subs.size;
196
+ if (size === 0) {
197
+ signal2.__f = void 0;
198
+ delete signal2[SUBS];
199
+ } else if (size === 1) {
200
+ signal2.__f = subs.values().next().value;
201
+ } else {
202
+ signal2.__f = void 0;
203
+ }
204
+ }
188
205
  var notifyDepth = 0;
189
206
  var pendingQueue = [];
190
207
  var pendingSet = /* @__PURE__ */ new Set();
208
+ var propagateStack = [];
191
209
  function safeInvoke(sub) {
192
210
  try {
193
211
  sub();
@@ -210,75 +228,133 @@ function track(effectFn, subscriber) {
210
228
  } finally {
211
229
  stackTop--;
212
230
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
231
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
232
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
233
+ subscriberStack.length = stackCapacity;
234
+ }
213
235
  }
214
236
  return () => cleanup(subscriber);
215
237
  }
216
238
  function recordDependency(signal2) {
217
239
  if (!currentSubscriber) return;
218
240
  const sub = currentSubscriber;
219
- if (sub._dep === signal2) return;
241
+ const epoch = sub._epoch;
242
+ if (sub._dep === signal2) {
243
+ sub._depEpoch = epoch;
244
+ return;
245
+ }
220
246
  const deps = sub._deps;
221
247
  if (deps) {
222
- if (deps.has(signal2)) return;
223
- deps.add(signal2);
248
+ deps.set(signal2, epoch);
224
249
  } else if (sub._dep !== void 0) {
225
- const set = /* @__PURE__ */ new Set();
226
- set.add(sub._dep);
227
- set.add(signal2);
228
- sub._deps = set;
250
+ const map = /* @__PURE__ */ new Map();
251
+ map.set(sub._dep, sub._depEpoch);
252
+ map.set(signal2, epoch);
253
+ sub._deps = map;
229
254
  sub._dep = void 0;
255
+ sub._depEpoch = void 0;
230
256
  } else {
231
257
  sub._dep = signal2;
258
+ sub._depEpoch = epoch;
232
259
  }
233
- let subs = signal2[SUBS];
260
+ const sig = signal2;
261
+ let subs = sig[SUBS];
234
262
  if (!subs) {
235
263
  subs = /* @__PURE__ */ new Set();
236
- signalSubscribers.set(signal2, subs);
237
- signal2[SUBS] = subs;
264
+ sig[SUBS] = subs;
238
265
  }
266
+ const prevSize = subs.size;
239
267
  subs.add(currentSubscriber);
240
- if (subs.size === 1) {
241
- signal2.__f = currentSubscriber;
242
- } else if (signal2.__f !== void 0) {
243
- signal2.__f = void 0;
268
+ if (subs.size !== prevSize) {
269
+ if (subs.size === 1) {
270
+ sig.__f = currentSubscriber;
271
+ } else if (sig.__f !== void 0) {
272
+ sig.__f = void 0;
273
+ }
274
+ }
275
+ }
276
+ var maxSubscriberRepeats = 50;
277
+ var maxDrainIterations = 1e6;
278
+ var drainEpoch = 0;
279
+ function tickRepeat(sub) {
280
+ const s = sub;
281
+ if (s._runEpoch !== drainEpoch) {
282
+ s._runEpoch = drainEpoch;
283
+ s._runs = 1;
284
+ return false;
285
+ }
286
+ return ++s._runs > maxSubscriberRepeats;
287
+ }
288
+ function cycleError(sub) {
289
+ if (typeof console !== "undefined") {
290
+ const name = sub.__name ?? "<unnamed>";
291
+ console.error(
292
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
293
+ );
294
+ }
295
+ }
296
+ function absoluteDrainError() {
297
+ if (typeof console !== "undefined") {
298
+ console.error(
299
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
300
+ );
301
+ }
302
+ }
303
+ function drainQueue() {
304
+ let i = 0;
305
+ while (i < pendingQueue.length) {
306
+ if (i >= maxDrainIterations) {
307
+ absoluteDrainError();
308
+ break;
309
+ }
310
+ const sub = pendingQueue[i++];
311
+ if (tickRepeat(sub)) {
312
+ cycleError(sub);
313
+ break;
314
+ }
315
+ pendingSet.delete(sub);
316
+ safeInvoke(sub);
244
317
  }
245
318
  }
246
319
  function propagateDirty(sub) {
247
320
  sub();
248
- let sig = sub._sig;
249
- while (sig) {
321
+ const rootSig = sub._sig;
322
+ if (!rootSig) return;
323
+ const stack = propagateStack;
324
+ const baseLen = stack.length;
325
+ stack.push(rootSig);
326
+ while (stack.length > baseLen) {
327
+ const sig = stack.pop();
250
328
  const first = sig.__f;
251
329
  if (first) {
252
330
  if (first._c) {
253
331
  const nSig = first._sig;
254
- nSig._d = true;
255
- sig = nSig;
256
- continue;
257
- }
258
- if (!pendingSet.has(first)) {
332
+ if (!nSig._d) {
333
+ nSig._d = true;
334
+ stack.push(nSig);
335
+ }
336
+ } else if (!pendingSet.has(first)) {
259
337
  pendingSet.add(first);
260
338
  pendingQueue.push(first);
261
339
  }
262
- break;
340
+ continue;
263
341
  }
264
342
  const subs = sig[SUBS];
265
- if (!subs) break;
266
- let nextSig;
343
+ if (!subs) continue;
267
344
  for (const s of subs) {
268
345
  if (s._c) {
269
- s();
270
346
  const nSig = s._sig;
271
- if (nSig && !nextSig) {
272
- nextSig = nSig;
273
- } else if (nSig) {
274
- propagateDirty(s);
347
+ if (nSig && !nSig._d) {
348
+ nSig._d = true;
349
+ stack.push(nSig);
350
+ } else if (!nSig) {
351
+ s();
275
352
  }
276
353
  } else if (!pendingSet.has(s)) {
277
354
  pendingSet.add(s);
278
355
  pendingQueue.push(s);
279
356
  }
280
357
  }
281
- sig = nextSig;
282
358
  }
283
359
  }
284
360
  function notifySubscribers(signal2) {
@@ -294,21 +370,22 @@ function notifySubscribers(signal2) {
294
370
  return;
295
371
  }
296
372
  notifyDepth++;
373
+ drainEpoch++;
297
374
  try {
298
375
  if (first._c) {
299
376
  propagateDirty(first);
377
+ } else if (tickRepeat(first)) {
378
+ cycleError(first);
300
379
  } else {
301
380
  safeInvoke(first);
302
381
  }
303
- let i = 0;
304
- while (i < pendingQueue.length) {
305
- safeInvoke(pendingQueue[i]);
306
- i++;
307
- }
382
+ drainQueue();
308
383
  } finally {
309
- pendingQueue.length = 0;
310
- pendingSet.clear();
311
384
  notifyDepth--;
385
+ if (notifyDepth === 0) {
386
+ pendingQueue.length = 0;
387
+ pendingSet.clear();
388
+ }
312
389
  }
313
390
  return;
314
391
  }
@@ -326,57 +403,45 @@ function notifySubscribers(signal2) {
326
403
  return;
327
404
  }
328
405
  notifyDepth++;
406
+ drainEpoch++;
329
407
  try {
330
- let directCount = 0;
331
408
  for (const sub of subs) {
332
- pendingQueue[directCount++] = sub;
333
- }
334
- for (let i2 = 0; i2 < directCount; i2++) {
335
- if (pendingQueue[i2]._c) {
336
- propagateDirty(pendingQueue[i2]);
337
- }
338
- }
339
- for (let i2 = 0; i2 < directCount; i2++) {
340
- if (!pendingQueue[i2]._c) {
341
- if (!pendingSet.has(pendingQueue[i2])) {
342
- safeInvoke(pendingQueue[i2]);
343
- }
409
+ if (sub._c) {
410
+ propagateDirty(sub);
411
+ } else if (!pendingSet.has(sub)) {
412
+ pendingSet.add(sub);
413
+ pendingQueue.push(sub);
344
414
  }
345
415
  }
346
- let i = directCount;
347
- while (i < pendingQueue.length) {
348
- safeInvoke(pendingQueue[i]);
349
- i++;
350
- }
416
+ drainQueue();
351
417
  } finally {
352
- pendingQueue.length = 0;
353
- pendingSet.clear();
354
418
  notifyDepth--;
419
+ if (notifyDepth === 0) {
420
+ pendingQueue.length = 0;
421
+ pendingSet.clear();
422
+ }
355
423
  }
356
424
  }
357
425
  function cleanup(subscriber) {
358
426
  const sub = subscriber;
359
427
  const singleDep = sub._dep;
360
428
  if (singleDep !== void 0) {
361
- const subs = singleDep[SUBS];
362
- if (subs) {
363
- subs.delete(subscriber);
364
- if (singleDep.__f === subscriber) {
365
- singleDep.__f = void 0;
366
- }
429
+ const sig = singleDep;
430
+ const subs = sig[SUBS];
431
+ if (subs?.delete(subscriber)) {
432
+ syncFastPath(sig, subs);
367
433
  }
368
434
  sub._dep = void 0;
435
+ sub._depEpoch = void 0;
369
436
  return;
370
437
  }
371
438
  const deps = sub._deps;
372
439
  if (!deps || deps.size === 0) return;
373
- for (const signal2 of deps) {
374
- const subs = signal2[SUBS];
375
- if (subs) {
376
- subs.delete(subscriber);
377
- if (signal2.__f === subscriber) {
378
- signal2.__f = void 0;
379
- }
440
+ for (const signal2 of deps.keys()) {
441
+ const sig = signal2;
442
+ const subs = sig[SUBS];
443
+ if (subs?.delete(subscriber)) {
444
+ syncFastPath(sig, subs);
380
445
  }
381
446
  }
382
447
  deps.clear();
@@ -429,7 +494,17 @@ function signal(initial, options) {
429
494
  return [get, set];
430
495
  }
431
496
 
497
+ // src/utils/sanitize.ts
498
+ function stripHtml(html) {
499
+ return String(html).replace(/<[^>]*>/g, "");
500
+ }
501
+
432
502
  // src/devtools/devtools.ts
503
+ function getSibuNamespace() {
504
+ const g = globalThis;
505
+ if (!g.__SIBU__) g.__SIBU__ = { version: "1.0.0" };
506
+ return g.__SIBU__;
507
+ }
433
508
  function createGlobalHook() {
434
509
  const listeners = /* @__PURE__ */ new Map();
435
510
  const events = [];
@@ -474,13 +549,16 @@ function getOrCreateHook() {
474
549
  }
475
550
  var activeDevTools = null;
476
551
  var nextNodeId = 0;
552
+ var inferNameArmed = false;
477
553
  function getActiveDevTools() {
478
554
  return activeDevTools;
479
555
  }
480
556
  function initDevTools(config) {
481
557
  const maxEvents = config?.maxEvents ?? 1e3;
482
558
  const enabled = config?.enabled ?? isDev();
559
+ const expose = config?.expose ?? isDev();
483
560
  if (!enabled) return createNoopApi();
561
+ inferNameArmed = true;
484
562
  const hook = getOrCreateHook();
485
563
  nextNodeId = 0;
486
564
  const eventLog = [];
@@ -497,6 +575,7 @@ function initDevTools(config) {
497
575
  createdAt: Date.now()
498
576
  };
499
577
  hook.nodes.set(nextNodeId, node);
578
+ emit();
500
579
  });
501
580
  hook.on("signal:update", (payload) => {
502
581
  if (!isActive) return;
@@ -511,6 +590,7 @@ function initDevTools(config) {
511
590
  newValue: p.newValue,
512
591
  timestamp: Date.now()
513
592
  });
593
+ emit();
514
594
  });
515
595
  hook.on("computed:create", (payload) => {
516
596
  const p = payload;
@@ -525,6 +605,7 @@ function initDevTools(config) {
525
605
  createdAt: Date.now()
526
606
  };
527
607
  hook.nodes.set(nextNodeId, node);
608
+ emit();
528
609
  });
529
610
  hook.on("computed:update", (payload) => {
530
611
  const p = payload;
@@ -537,6 +618,7 @@ function initDevTools(config) {
537
618
  newValue: p.newValue,
538
619
  timestamp: Date.now()
539
620
  });
621
+ emit();
540
622
  });
541
623
  hook.on("effect:create", (payload) => {
542
624
  nextNodeId++;
@@ -544,6 +626,7 @@ function initDevTools(config) {
544
626
  const p = payload;
545
627
  const node = { id: nextNodeId, type: "effect", name, ref: p.effectFn, createdAt: Date.now() };
546
628
  hook.nodes.set(nextNodeId, node);
629
+ emit();
547
630
  });
548
631
  hook.on("effect:run", (payload) => {
549
632
  const p = payload;
@@ -554,6 +637,7 @@ function initDevTools(config) {
554
637
  duration: 0,
555
638
  timestamp: Date.now()
556
639
  });
640
+ emit();
557
641
  });
558
642
  hook.on("app:init", (payload) => {
559
643
  const p = payload;
@@ -563,12 +647,19 @@ function initDevTools(config) {
563
647
  duration: p.duration,
564
648
  timestamp: Date.now()
565
649
  });
650
+ emit();
566
651
  if (typeof document !== "undefined") {
567
- queueMicrotask(() => discoverComponents(hook, eventLog, maxEvents));
652
+ queueMicrotask(() => {
653
+ discoverComponents(hook, eventLog, maxEvents);
654
+ emit();
655
+ });
568
656
  }
569
657
  });
570
658
  function record(event) {
571
- if (isActive) pushEvent(eventLog, maxEvents, event);
659
+ if (isActive) {
660
+ pushEvent(eventLog, maxEvents, event);
661
+ emit();
662
+ }
572
663
  }
573
664
  function getEvents(filter) {
574
665
  if (!filter) return eventLog.slice();
@@ -583,9 +674,11 @@ function initDevTools(config) {
583
674
  }
584
675
  function registerComponent(name, element, state) {
585
676
  hook.components.set(name, { element, state });
677
+ emit();
586
678
  }
587
679
  function unregisterComponent(name) {
588
680
  hook.components.delete(name);
681
+ emit();
589
682
  }
590
683
  function getComponents() {
591
684
  return new Map(hook.components);
@@ -612,6 +705,12 @@ function initDevTools(config) {
612
705
  }
613
706
  return result;
614
707
  }
708
+ let domObserver = null;
709
+ const activeHighlightTimers = /* @__PURE__ */ new Set();
710
+ let changeVersion = 0;
711
+ function emit() {
712
+ changeVersion++;
713
+ }
615
714
  let isActive = enabled;
616
715
  function isEnabled() {
617
716
  return isActive;
@@ -624,32 +723,75 @@ function initDevTools(config) {
624
723
  for (const [name, entry] of hook.components) snap[name] = entry.state ? { ...entry.state } : {};
625
724
  return snap;
626
725
  }
726
+ function restoreHighlight(el) {
727
+ const prevOutline = el.dataset.sibuHighlightPrevOutline;
728
+ const prevOffset = el.dataset.sibuHighlightPrevOffset;
729
+ el.style.outline = prevOutline ?? "";
730
+ el.style.outlineOffset = prevOffset ?? "";
731
+ delete el.dataset.sibuHighlightPrevOutline;
732
+ delete el.dataset.sibuHighlightPrevOffset;
733
+ el.removeAttribute("data-sibu-highlight");
734
+ }
627
735
  function highlightElement(name) {
628
736
  const prev = document.querySelector("[data-sibu-highlight]");
629
- if (prev) {
630
- prev.style.outline = "";
631
- prev.removeAttribute("data-sibu-highlight");
632
- }
737
+ if (prev instanceof HTMLElement) restoreHighlight(prev);
738
+ const entry = hook.components.get(name);
739
+ const el = entry?.element;
740
+ if (!el || !el.isConnected) return;
741
+ el.dataset.sibuHighlightPrevOutline = el.style.outline || "";
742
+ el.dataset.sibuHighlightPrevOffset = el.style.outlineOffset || "";
743
+ el.style.outline = "2px solid #89b4fa";
744
+ el.setAttribute("data-sibu-highlight", "true");
745
+ el.scrollIntoView({ behavior: "smooth", block: "center" });
746
+ const timer = setTimeout(() => {
747
+ activeHighlightTimers.delete(timer);
748
+ if (!el.isConnected) return;
749
+ restoreHighlight(el);
750
+ }, 3e3);
751
+ activeHighlightTimers.add(timer);
752
+ }
753
+ function getElementHTML(name, max = 2e3) {
633
754
  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);
755
+ const el = entry?.element;
756
+ if (!el) return null;
757
+ try {
758
+ const raw = el.outerHTML || "";
759
+ const cleaned = stripHtml(raw);
760
+ return cleaned.length > max ? `${cleaned.substring(0, max)}...` : cleaned;
761
+ } catch (err) {
762
+ if (typeof console !== "undefined") {
763
+ console.warn("[SibuJS devtools] getElementHTML failed:", err);
764
+ }
765
+ return null;
642
766
  }
643
767
  }
644
768
  function destroy() {
769
+ if (domObserver) {
770
+ domObserver.disconnect();
771
+ domObserver = null;
772
+ }
773
+ for (const t of activeHighlightTimers) clearTimeout(t);
774
+ activeHighlightTimers.clear();
775
+ if (typeof document !== "undefined") {
776
+ const prev = document.querySelector("[data-sibu-highlight]");
777
+ if (prev instanceof HTMLElement) restoreHighlight(prev);
778
+ }
645
779
  eventLog.length = 0;
646
780
  hook.nodes.clear();
647
781
  hook.components.clear();
648
782
  hook.events.length = 0;
649
783
  isActive = false;
784
+ inferNameArmed = false;
650
785
  activeDevTools = null;
651
786
  const g = globalThis;
787
+ if (g.__SIBU__) {
788
+ delete g.__SIBU__.devtools;
789
+ delete g.__SIBU__.data;
790
+ delete g.__SIBU__.changeVersion;
791
+ }
652
792
  if (g.__SIBU_DEVTOOLS__ === api) delete g.__SIBU_DEVTOOLS__;
793
+ delete g.__SIBU_DEVTOOLS_VERSION__;
794
+ delete g.__SIBU_DEVTOOLS_DATA__;
653
795
  if (g.__SIBU_DEVTOOLS_GLOBAL_HOOK__ === hook) delete g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
654
796
  }
655
797
  const api = {
@@ -664,164 +806,155 @@ function initDevTools(config) {
664
806
  setEnabled,
665
807
  snapshot,
666
808
  highlightElement,
809
+ getElementHTML,
667
810
  destroy
668
811
  };
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 {
812
+ function buildData() {
813
+ const sArr = [];
814
+ for (const [, node] of hook.nodes) {
815
+ let val = "";
816
+ try {
817
+ let raw;
818
+ if (node.type === "signal" && node.ref && "value" in node.ref) {
819
+ raw = node.ref.value;
820
+ } else if (node.type === "computed" && node.ref) {
821
+ if (node.ref._d && node.ref._g) {
822
+ try {
823
+ raw = node.ref._g();
824
+ } catch {
704
825
  raw = node.ref._v;
705
826
  }
706
- } else if (node.type === "effect") {
707
- raw = void 0;
708
- } else if (node.ref && "value" in node.ref) {
709
- raw = node.ref.value;
827
+ } else {
828
+ raw = node.ref._v;
710
829
  }
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 = "?";
830
+ } else if (node.type === "effect") {
831
+ raw = void 0;
832
+ } else if (node.ref && "value" in node.ref) {
833
+ raw = node.ref.value;
717
834
  }
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
- });
835
+ if (raw === void 0) val = "undefined";
836
+ else if (raw === null) val = "null";
837
+ else if (typeof raw === "object") val = JSON.stringify(raw);
838
+ else val = String(raw);
839
+ } catch {
840
+ val = "?";
729
841
  }
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
- }
842
+ const fullVal = val;
843
+ const shortVal = val.length > 80 ? `${val.substring(0, 80)}...` : val;
844
+ const subs = node.ref?.__s;
845
+ sArr.push({
846
+ id: node.id,
847
+ n: node.name,
848
+ tp: node.type,
849
+ v: shortVal,
850
+ fv: fullVal,
851
+ sc: subs instanceof Set ? subs.size : 0
852
+ });
853
+ }
854
+ function walkElement(el, depth) {
855
+ if (depth > 8) return [];
856
+ const result = [];
857
+ const max = Math.min(el.children.length, 50);
858
+ for (let i = 0; i < max; i++) {
859
+ const child = el.children[i];
860
+ const txtParts = [];
861
+ for (let ti = 0; ti < child.childNodes.length; ti++) {
862
+ const cn = child.childNodes[ti];
863
+ if (cn.nodeType === 3) {
864
+ const t = cn.textContent?.trim();
865
+ if (t) txtParts.push(t);
866
+ } else if (cn.nodeType === 1) {
867
+ const el2 = cn;
868
+ let directTxt = "";
869
+ for (let ci = 0; ci < el2.childNodes.length; ci++) {
870
+ if (el2.childNodes[ci].nodeType === 3) {
871
+ const t2 = el2.childNodes[ci].textContent?.trim();
872
+ if (t2) {
873
+ directTxt = t2;
874
+ break;
752
875
  }
753
876
  }
754
- if (directTxt) txtParts.push(directTxt);
755
877
  }
878
+ if (directTxt) txtParts.push(directTxt);
756
879
  }
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
880
  }
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) : []
881
+ let txt = txtParts.join(" | ");
882
+ if (txt.length > 120) txt = `${txt.substring(0, 120)}...`;
883
+ const attrs = [];
884
+ try {
885
+ const al = child.attributes;
886
+ for (let ai = 0; ai < al.length && ai < 20; ai++) attrs.push(al[ai].name);
887
+ } catch {
888
+ }
889
+ const ev = child.__sibu_events__ || [];
890
+ result.push({
891
+ tg: child.tagName ? child.tagName.toLowerCase() : "?",
892
+ id: child.id || "",
893
+ cl: (child.className || "").toString().split(" ").slice(0, 3).join(" "),
894
+ txt: txt.length > 60 ? `${txt.substring(0, 60)}...` : txt,
895
+ attrs,
896
+ ev,
897
+ ch: child.childElementCount > 0 ? walkElement(child, depth + 1) : []
788
898
  });
789
899
  }
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`;
900
+ return result;
901
+ }
902
+ const cArr = [];
903
+ for (const [name, entry] of hook.components) {
904
+ const el = entry.element;
905
+ cArr.push({
906
+ n: name,
907
+ tg: el?.tagName ? el.tagName.toLowerCase() : "?",
908
+ ch: el?.childElementCount || 0,
909
+ cn: !!el?.isConnected,
910
+ kids: el ? walkElement(el, 0) : []
911
+ });
912
+ }
913
+ const eArr = [];
914
+ const start = eventLog.length > 500 ? eventLog.length - 500 : 0;
915
+ for (let i = start; i < eventLog.length; i++) {
916
+ const e = eventLog[i];
917
+ let detail = "";
918
+ let ov = "";
919
+ let nv = "";
920
+ let key = "";
921
+ if (e.type === "state-change") {
922
+ const sc = e;
923
+ key = sc.key || "";
924
+ try {
925
+ ov = sc.oldValue === void 0 ? "undefined" : sc.oldValue === null ? "null" : typeof sc.oldValue === "object" ? JSON.stringify(sc.oldValue, null, 2) : String(sc.oldValue);
926
+ } catch {
927
+ ov = "?";
928
+ }
929
+ try {
930
+ nv = sc.newValue === void 0 ? "undefined" : sc.newValue === null ? "null" : typeof sc.newValue === "object" ? JSON.stringify(sc.newValue, null, 2) : String(sc.newValue);
931
+ } catch {
932
+ nv = "?";
816
933
  }
817
- eArr.push({ t: e.type, c: e.component, ts: e.timestamp, d: detail, ov, nv, k: key });
934
+ const ovShort = ov.length > 40 ? `${ov.substring(0, 40)}...` : ov;
935
+ const nvShort = nv.length > 40 ? `${nv.substring(0, 40)}...` : nv;
936
+ detail = `${ovShort} \u2192 ${nvShort}`;
937
+ } else if (e.type === "render") {
938
+ detail = `${e.duration.toFixed(1)}ms`;
818
939
  }
819
- return JSON.stringify({ s: sArr, c: cArr, e: eArr });
820
- };
940
+ eArr.push({ t: e.type, c: e.component, ts: e.timestamp, d: detail, ov, nv, k: key });
941
+ }
942
+ return JSON.stringify({ s: sArr, c: cArr, e: eArr });
943
+ }
944
+ if (expose && typeof window !== "undefined") {
945
+ const ns = getSibuNamespace();
946
+ ns.devtools = api;
947
+ ns.data = buildData;
948
+ ns.changeVersion = () => changeVersion;
949
+ const w = window;
950
+ w.__SIBU_DEVTOOLS__ = api;
951
+ w.__SIBU_DEVTOOLS_VERSION__ = () => changeVersion;
952
+ w.__SIBU_DEVTOOLS_DATA__ = buildData;
821
953
  }
822
954
  activeDevTools = api;
823
955
  if (typeof document !== "undefined") {
824
- const observer = new MutationObserver((mutations) => {
956
+ domObserver = new MutationObserver((mutations) => {
957
+ let changed = false;
825
958
  for (const m of mutations) {
826
959
  for (const node of m.addedNodes) {
827
960
  if (node instanceof HTMLElement) {
@@ -829,6 +962,7 @@ function initDevTools(config) {
829
962
  if (name && !hook.components.has(name)) {
830
963
  hook.components.set(name, { element: node });
831
964
  pushEvent(eventLog, maxEvents, { type: "mount", component: name, element: node, timestamp: Date.now() });
965
+ changed = true;
832
966
  }
833
967
  }
834
968
  }
@@ -838,11 +972,14 @@ function initDevTools(config) {
838
972
  if (name && hook.components.has(name)) {
839
973
  pushEvent(eventLog, maxEvents, { type: "unmount", component: name, timestamp: Date.now() });
840
974
  hook.components.delete(name);
975
+ changed = true;
841
976
  }
842
977
  }
843
978
  }
844
979
  }
980
+ if (changed) emit();
845
981
  });
982
+ const observer = domObserver;
846
983
  queueMicrotask(() => {
847
984
  if (!document.body) return;
848
985
  observer.observe(document.body, { childList: true, subtree: true });
@@ -884,11 +1021,13 @@ function initDevTools(config) {
884
1021
  }
885
1022
  });
886
1023
  }
1024
+ emit();
887
1025
  });
888
1026
  }
889
1027
  return api;
890
1028
  }
891
1029
  function inferName() {
1030
+ if (!inferNameArmed || !isDev()) return "anonymous";
892
1031
  try {
893
1032
  const stack = new Error().stack || "";
894
1033
  for (const line of stack.split("\n")) {
@@ -945,6 +1084,7 @@ function createNoopApi() {
945
1084
  setEnabled: noop,
946
1085
  snapshot: () => ({}),
947
1086
  highlightElement: noop,
1087
+ getElementHTML: ((_n) => null),
948
1088
  destroy: noop
949
1089
  };
950
1090
  }
@@ -976,17 +1116,90 @@ function devState(name, initial) {
976
1116
  return [get, trackedSet];
977
1117
  }
978
1118
 
1119
+ // src/core/rendering/dispose.ts
1120
+ var elementDisposers = /* @__PURE__ */ new WeakMap();
1121
+ var _isDev4 = isDev();
1122
+ var activeBindingCount = 0;
1123
+ function dispose(node) {
1124
+ const stack = [node];
1125
+ const order = [];
1126
+ while (stack.length > 0) {
1127
+ const current = stack.pop();
1128
+ order.push(current);
1129
+ const children = Array.from(current.childNodes);
1130
+ for (let i = 0; i < children.length; i++) {
1131
+ stack.push(children[i]);
1132
+ }
1133
+ }
1134
+ for (let i = order.length - 1; i >= 0; i--) {
1135
+ const current = order[i];
1136
+ const disposers = elementDisposers.get(current);
1137
+ if (disposers) {
1138
+ const snapshot = disposers.slice();
1139
+ elementDisposers.delete(current);
1140
+ if (_isDev4) activeBindingCount -= snapshot.length;
1141
+ for (const d of snapshot) {
1142
+ try {
1143
+ d();
1144
+ } catch (err) {
1145
+ if (_isDev4 && typeof console !== "undefined") {
1146
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
1147
+ }
1148
+ }
1149
+ }
1150
+ let extraPasses = 0;
1151
+ while (extraPasses++ < 8) {
1152
+ const added = elementDisposers.get(current);
1153
+ if (!added || added.length === 0) break;
1154
+ const moreSnapshot = added.slice();
1155
+ elementDisposers.delete(current);
1156
+ if (_isDev4) activeBindingCount -= moreSnapshot.length;
1157
+ for (const d of moreSnapshot) {
1158
+ try {
1159
+ d();
1160
+ } catch (err) {
1161
+ if (_isDev4 && typeof console !== "undefined") {
1162
+ console.warn("[SibuJS] Disposer threw during cleanup:", err);
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ }
1169
+ }
1170
+
979
1171
  // src/devtools/hmr.ts
1172
+ var HMR_STORE_MAX_SIZE = 200;
980
1173
  var hmrStateStore = /* @__PURE__ */ new Map();
1174
+ var hmrStoreOverflowWarned = false;
1175
+ function hmrStoreSet(id, value) {
1176
+ if (hmrStateStore.has(id)) hmrStateStore.delete(id);
1177
+ hmrStateStore.set(id, value);
1178
+ if (hmrStateStore.size > HMR_STORE_MAX_SIZE) {
1179
+ const oldestKey = hmrStateStore.keys().next().value;
1180
+ if (oldestKey !== void 0) hmrStateStore.delete(oldestKey);
1181
+ if (!hmrStoreOverflowWarned) {
1182
+ hmrStoreOverflowWarned = true;
1183
+ console.warn(
1184
+ `[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.`
1185
+ );
1186
+ }
1187
+ }
1188
+ }
1189
+ function clearHMRModule(id) {
1190
+ hmrStateStore.delete(id);
1191
+ hmrRegistry.delete(id);
1192
+ hmrRegistry.delete(`boundary:${id}`);
1193
+ }
981
1194
  var hmrRegistry = /* @__PURE__ */ new Map();
982
1195
  function hmrState(id, initial) {
983
1196
  const restored = hmrStateStore.has(id) ? hmrStateStore.get(id) : initial;
984
1197
  const [get, set] = signal(restored);
985
1198
  function hmrSet(next) {
986
1199
  set(next);
987
- hmrStateStore.set(id, get());
1200
+ hmrStoreSet(id, get());
988
1201
  }
989
- hmrStateStore.set(id, restored);
1202
+ hmrStoreSet(id, restored);
990
1203
  return [get, hmrSet];
991
1204
  }
992
1205
  function registerHMR(id, component, container) {
@@ -1012,13 +1225,15 @@ function registerHMR(id, component, container) {
1012
1225
  }
1013
1226
  reg.disposeCallbacks.length = 0;
1014
1227
  const newElement = newComponent();
1015
- if (reg.currentElement?.parentNode) {
1016
- reg.currentElement.parentNode.replaceChild(newElement, reg.currentElement);
1228
+ const oldElement = reg.currentElement;
1229
+ if (oldElement?.parentNode) {
1230
+ oldElement.parentNode.replaceChild(newElement, oldElement);
1017
1231
  }
1232
+ if (oldElement) dispose(oldElement);
1018
1233
  reg.component = newComponent;
1019
1234
  reg.currentElement = newElement;
1020
1235
  }
1021
- function dispose() {
1236
+ function dispose2() {
1022
1237
  const reg = hmrRegistry.get(id);
1023
1238
  if (!reg) return;
1024
1239
  for (const cb of reg.disposeCallbacks) {
@@ -1027,12 +1242,14 @@ function registerHMR(id, component, container) {
1027
1242
  } catch {
1028
1243
  }
1029
1244
  }
1030
- if (reg.currentElement?.parentNode) {
1031
- reg.currentElement.parentNode.removeChild(reg.currentElement);
1245
+ if (reg.currentElement) {
1246
+ const el = reg.currentElement;
1247
+ if (el.parentNode) el.parentNode.removeChild(el);
1248
+ dispose(el);
1032
1249
  }
1033
1250
  hmrRegistry.delete(id);
1034
1251
  }
1035
- return { update, dispose };
1252
+ return { update, dispose: dispose2 };
1036
1253
  }
1037
1254
  function createHMRBoundary(id) {
1038
1255
  let currentElement = null;
@@ -1069,10 +1286,15 @@ function createHMRBoundary(id) {
1069
1286
  }
1070
1287
  }
1071
1288
  disposeCallbacks.length = 0;
1072
- if (currentComponent && currentElement?.parentNode) {
1073
- const newElement = currentComponent();
1074
- currentElement.parentNode.replaceChild(newElement, currentElement);
1075
- currentElement = newElement;
1289
+ if (currentComponent && currentElement) {
1290
+ const oldEl = currentElement;
1291
+ const parent = oldEl.parentNode;
1292
+ if (parent) {
1293
+ const newElement = currentComponent();
1294
+ parent.replaceChild(newElement, oldEl);
1295
+ dispose(oldEl);
1296
+ currentElement = newElement;
1297
+ }
1076
1298
  }
1077
1299
  for (const cb of acceptCallbacks) {
1078
1300
  try {
@@ -1092,6 +1314,19 @@ function createHMRBoundary(id) {
1092
1314
  function clearHMRState() {
1093
1315
  hmrStateStore.clear();
1094
1316
  hmrRegistry.clear();
1317
+ hmrStoreOverflowWarned = false;
1318
+ }
1319
+ function exposeHMR() {
1320
+ const g = globalThis;
1321
+ if (!g.__SIBU__) g.__SIBU__ = { version: "1.0.0" };
1322
+ g.__SIBU__.hmr = {
1323
+ hmrState,
1324
+ registerHMR,
1325
+ createHMRBoundary,
1326
+ clearHMRState,
1327
+ clearHMRModule,
1328
+ isHMRAvailable
1329
+ };
1095
1330
  }
1096
1331
  function isHMRAvailable() {
1097
1332
  const g = globalThis;
@@ -1227,9 +1462,28 @@ function formatError(error, context) {
1227
1462
  }
1228
1463
 
1229
1464
  // src/core/ssr-context.ts
1230
- var ssrMode = false;
1465
+ var als = null;
1466
+ try {
1467
+ if (typeof process !== "undefined" && process.versions && process.versions.node) {
1468
+ const req = Function("return typeof require==='function'?require:null")();
1469
+ if (req) {
1470
+ const mod = req("node:async_hooks");
1471
+ als = new mod.AsyncLocalStorage();
1472
+ }
1473
+ }
1474
+ } catch {
1475
+ als = null;
1476
+ }
1477
+ var fallbackStore = { ssr: false, suspenseIdCounter: 0 };
1478
+ function getSSRStore() {
1479
+ if (als) {
1480
+ const s = als.getStore();
1481
+ if (s) return s;
1482
+ }
1483
+ return fallbackStore;
1484
+ }
1231
1485
  function isSSR() {
1232
- return ssrMode;
1486
+ return getSSRStore().ssr;
1233
1487
  }
1234
1488
 
1235
1489
  // src/core/signals/effect.ts
@@ -1239,26 +1493,114 @@ function effect(effectFn, options) {
1239
1493
  if (isSSR()) return () => {
1240
1494
  };
1241
1495
  const onError = options?.onError;
1496
+ let userCleanups = [];
1497
+ const onCleanup = (fn) => {
1498
+ userCleanups.push(fn);
1499
+ };
1500
+ const runUserCleanups = () => {
1501
+ if (userCleanups.length === 0) return;
1502
+ const list = userCleanups;
1503
+ userCleanups = [];
1504
+ for (let i = list.length - 1; i >= 0; i--) {
1505
+ try {
1506
+ list[i]();
1507
+ } catch (err) {
1508
+ if (typeof console !== "undefined") {
1509
+ console.warn("[SibuJS effect] onCleanup threw:", err);
1510
+ }
1511
+ }
1512
+ }
1513
+ };
1514
+ const invokeBody = () => effectFn(onCleanup);
1242
1515
  const wrappedFn = onError ? () => {
1243
1516
  try {
1244
- effectFn();
1517
+ invokeBody();
1245
1518
  } catch (err) {
1246
1519
  onError(err);
1247
1520
  }
1248
- } : effectFn;
1521
+ } : invokeBody;
1249
1522
  let cleanupHandle = () => {
1250
1523
  };
1524
+ let running = false;
1525
+ let rerunPending = false;
1526
+ const MAX_RERUNS = 100;
1251
1527
  const subscriber = () => {
1252
- cleanupHandle();
1253
- cleanupHandle = track(wrappedFn, subscriber);
1528
+ if (running) {
1529
+ rerunPending = true;
1530
+ return;
1531
+ }
1532
+ running = true;
1533
+ try {
1534
+ let reruns = 0;
1535
+ do {
1536
+ rerunPending = false;
1537
+ runUserCleanups();
1538
+ cleanupHandle();
1539
+ cleanupHandle = track(wrappedFn, subscriber);
1540
+ if (++reruns > MAX_RERUNS) {
1541
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1542
+ console.error(
1543
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1544
+ );
1545
+ }
1546
+ rerunPending = false;
1547
+ break;
1548
+ }
1549
+ } while (rerunPending);
1550
+ } finally {
1551
+ running = false;
1552
+ rerunPending = false;
1553
+ }
1254
1554
  };
1255
- cleanupHandle = track(wrappedFn, subscriber);
1555
+ running = true;
1556
+ try {
1557
+ let reruns = 0;
1558
+ do {
1559
+ rerunPending = false;
1560
+ runUserCleanups();
1561
+ cleanupHandle();
1562
+ cleanupHandle = track(wrappedFn, subscriber);
1563
+ if (++reruns > MAX_RERUNS) {
1564
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1565
+ console.error(
1566
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1567
+ );
1568
+ }
1569
+ rerunPending = false;
1570
+ break;
1571
+ }
1572
+ } while (rerunPending);
1573
+ } finally {
1574
+ running = false;
1575
+ rerunPending = false;
1576
+ }
1256
1577
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1257
1578
  if (hook) hook.emit("effect:create", { effectFn });
1579
+ let disposed = false;
1258
1580
  return () => {
1581
+ if (disposed) return;
1582
+ disposed = true;
1259
1583
  const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1260
- if (h) h.emit("effect:destroy", { effectFn });
1261
- cleanupHandle();
1584
+ if (h) {
1585
+ try {
1586
+ h.emit("effect:destroy", { effectFn });
1587
+ } catch {
1588
+ }
1589
+ }
1590
+ try {
1591
+ runUserCleanups();
1592
+ } catch (err) {
1593
+ if (typeof console !== "undefined") {
1594
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
1595
+ }
1596
+ }
1597
+ try {
1598
+ cleanupHandle();
1599
+ } catch (err) {
1600
+ if (typeof console !== "undefined") {
1601
+ console.warn("[SibuJS effect] dispose threw:", err);
1602
+ }
1603
+ }
1262
1604
  };
1263
1605
  }
1264
1606
 
@@ -1268,13 +1610,13 @@ function debugValue(value, formatter) {
1268
1610
  const format = formatter ?? ((v) => String(v));
1269
1611
  const entry = { value: void 0, label: "" };
1270
1612
  debugValues.push(entry);
1271
- const dispose = effect(() => {
1613
+ const dispose2 = effect(() => {
1272
1614
  const resolved = value();
1273
1615
  entry.value = resolved;
1274
1616
  entry.label = format(resolved);
1275
1617
  });
1276
1618
  return () => {
1277
- dispose();
1619
+ dispose2();
1278
1620
  const idx = debugValues.indexOf(entry);
1279
1621
  if (idx !== -1) debugValues.splice(idx, 1);
1280
1622
  };
@@ -1354,11 +1696,11 @@ function createDevtoolsOverlay(options) {
1354
1696
  const getPanels = () => {
1355
1697
  return [...panels];
1356
1698
  };
1357
- const dispose = () => {
1699
+ const dispose2 = () => {
1358
1700
  panels.length = 0;
1359
1701
  setEnabled(false);
1360
1702
  };
1361
- return { isEnabled, toggle, addPanel, removePanel, getPanels, dispose };
1703
+ return { isEnabled, toggle, addPanel, removePanel, getPanels, dispose: dispose2 };
1362
1704
  }
1363
1705
 
1364
1706
  // src/devtools/introspect.ts
@@ -1373,8 +1715,12 @@ function getSubscriberCount(getter) {
1373
1715
  return subs ? subs.size : 0;
1374
1716
  }
1375
1717
  function getDependencies(subscriberFn) {
1376
- const deps = subscriberFn._deps;
1377
- return deps ? Array.from(deps) : [];
1718
+ const fn = subscriberFn;
1719
+ const singleDep = fn._dep;
1720
+ if (singleDep !== void 0) return [singleDep];
1721
+ const deps = fn._deps;
1722
+ if (!deps) return [];
1723
+ return deps instanceof Map ? Array.from(deps.keys()) : Array.from(deps);
1378
1724
  }
1379
1725
  function inspectSignal(getter) {
1380
1726
  const signal2 = getter.__signal;
@@ -1386,25 +1732,24 @@ function inspectSignal(getter) {
1386
1732
  subscriberCount: subs ? subs.size : 0
1387
1733
  };
1388
1734
  }
1389
- function walkDependencyGraph(getter, maxDepth = 10) {
1735
+ function walkDependencyGraph(getter, maxDepth = 10, visited = /* @__PURE__ */ new WeakSet()) {
1390
1736
  const signal2 = getter.__signal;
1391
- if (!signal2 || maxDepth <= 0) {
1737
+ if (!signal2 || maxDepth <= 0 || visited.has(signal2)) {
1392
1738
  return { name: getSignalName(getter), subscribers: 0, downstream: [] };
1393
1739
  }
1740
+ visited.add(signal2);
1394
1741
  const subs = signal2[SUBS2];
1395
1742
  const downstream = [];
1396
1743
  if (subs) {
1397
1744
  for (const sub of subs) {
1398
1745
  const subSig = sub._sig;
1399
- if (subSig) {
1746
+ if (subSig && !visited.has(subSig)) {
1400
1747
  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
- });
1748
+ const fakeGetter = (() => void 0);
1749
+ const tag = fakeGetter;
1750
+ tag.__signal = subSig;
1751
+ if (subName !== void 0) tag.__name = subName;
1752
+ downstream.push(walkDependencyGraph(fakeGetter, maxDepth - 1, visited));
1408
1753
  }
1409
1754
  }
1410
1755
  }
@@ -1534,6 +1879,7 @@ function createTraceProfiler() {
1534
1879
  captureSignalGraph,
1535
1880
  checkLeaks,
1536
1881
  clearDebugValues,
1882
+ clearHMRModule,
1537
1883
  clearHMRState,
1538
1884
  clearPerformanceData,
1539
1885
  createDevtoolsOverlay,
@@ -1547,6 +1893,7 @@ function createTraceProfiler() {
1547
1893
  diffSignalGraphs,
1548
1894
  disableDebug,
1549
1895
  enableDebug,
1896
+ exposeHMR,
1550
1897
  formatError,
1551
1898
  getActiveDevTools,
1552
1899
  getDebugValues,