sibujs 2.1.0 → 3.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 (62) hide show
  1. package/dist/browser.cjs +324 -273
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +363 -330
  4. package/dist/build.js +10 -10
  5. package/dist/cdn.global.js +8 -8
  6. package/dist/{chunk-ZAQSMOED.js → chunk-2JQUV4Y3.js} +4 -4
  7. package/dist/{chunk-GWWURC5M.js → chunk-2KM2724A.js} +2 -2
  8. package/dist/{chunk-NASX6ST2.js → chunk-4YTVESDX.js} +1 -1
  9. package/dist/chunk-5WD7BYTZ.js +152 -0
  10. package/dist/{chunk-RDTDJCAB.js → chunk-6QZO7MMG.js} +48 -16
  11. package/dist/{chunk-DRUZZAK4.js → chunk-DF3GTP4Q.js} +7 -2
  12. package/dist/{chunk-AMK2TYNW.js → chunk-INBOWHQ3.js} +14 -11
  13. package/dist/{chunk-O6EFQ3KT.js → chunk-KH4OE6WY.js} +5 -5
  14. package/dist/{chunk-V6C4FADE.js → chunk-KZA7ANXP.js} +3 -3
  15. package/dist/chunk-L4DAT4WU.js +400 -0
  16. package/dist/{chunk-WANSMF2L.js → chunk-L52H775O.js} +4 -4
  17. package/dist/{chunk-45YP72ZQ.js → chunk-NEWH4O5U.js} +1 -1
  18. package/dist/{chunk-ON5MMR2J.js → chunk-RJIRT46U.js} +4 -4
  19. package/dist/{chunk-P2HSJDDN.js → chunk-STFTTMO2.js} +2 -2
  20. package/dist/{chunk-WIPZPFBQ.js → chunk-UKMXT5T6.js} +1 -1
  21. package/dist/{chunk-KGYT6UO6.js → chunk-V65KTDZW.js} +3 -3
  22. package/dist/{chunk-CWBVQML6.js → chunk-VSNLICTS.js} +1 -1
  23. package/dist/{chunk-3DZP6OIT.js → chunk-XDKP4T7G.js} +2 -2
  24. package/dist/{chunk-TH2ILCYW.js → chunk-XVYB3J6C.js} +27 -33
  25. package/dist/{chunk-OJ3P4ECI.js → chunk-YMOIAHWA.js} +1 -1
  26. package/dist/data.cjs +332 -298
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +353 -296
  29. package/dist/devtools.d.cts +1 -1
  30. package/dist/devtools.d.ts +1 -1
  31. package/dist/devtools.js +4 -4
  32. package/dist/ecosystem.cjs +332 -298
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +372 -328
  35. package/dist/extras.d.cts +1 -1
  36. package/dist/extras.d.ts +1 -1
  37. package/dist/extras.js +19 -19
  38. package/dist/index.cjs +363 -330
  39. package/dist/index.d.cts +26 -36
  40. package/dist/index.d.ts +26 -36
  41. package/dist/index.js +10 -10
  42. package/dist/{introspect-DnIpHQQz.d.ts → introspect-BZWKvQUZ.d.ts} +2 -3
  43. package/dist/{introspect-2TOlQ7oa.d.cts → introspect-DsJlDD2T.d.cts} +2 -3
  44. package/dist/motion.cjs +147 -123
  45. package/dist/motion.js +3 -3
  46. package/dist/patterns.cjs +332 -298
  47. package/dist/patterns.js +5 -5
  48. package/dist/performance.cjs +315 -268
  49. package/dist/performance.js +4 -4
  50. package/dist/plugins.cjs +332 -266
  51. package/dist/plugins.js +6 -6
  52. package/dist/ssr.cjs +340 -270
  53. package/dist/ssr.js +7 -7
  54. package/dist/testing.cjs +167 -146
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +324 -294
  57. package/dist/ui.js +6 -6
  58. package/dist/widgets.cjs +332 -298
  59. package/dist/widgets.js +6 -6
  60. package/package.json +1 -1
  61. package/dist/chunk-QO3WC6FS.js +0 -384
  62. package/dist/chunk-WZA53FXU.js +0 -149
package/dist/devtools.cjs CHANGED
@@ -45,7 +45,7 @@ __export(devtools_exports, {
45
45
  getDependencies: () => getDependencies,
46
46
  getPerformanceReport: () => getPerformanceReport,
47
47
  getSignalName: () => getSignalName,
48
- getSubscriberCount: () => getSubscriberCount,
48
+ getSubscriberCount: () => getSubscriberCount2,
49
49
  hmrState: () => hmrState,
50
50
  initDevTools: () => initDevTools,
51
51
  inspectSignal: () => inspectSignal,
@@ -184,24 +184,88 @@ function devWarn(message) {
184
184
 
185
185
  // src/reactivity/track.ts
186
186
  var _isDev2 = isDev();
187
- var STACK_INITIAL = 32;
188
- var STACK_SHRINK_THRESHOLD = 128;
189
- var subscriberStack = new Array(STACK_INITIAL);
190
- var stackCapacity = STACK_INITIAL;
191
- var stackTop = -1;
192
- var currentSubscriber = null;
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
- }
187
+ var POOL_MAX = 4096;
188
+ var nodePool = [];
189
+ function createNode() {
190
+ return {
191
+ sig: null,
192
+ sub: null,
193
+ epoch: 0,
194
+ sigPrev: null,
195
+ sigNext: null,
196
+ subPrev: null,
197
+ subNext: null,
198
+ prevActive: null
199
+ };
200
+ }
201
+ function allocNode(sig, sub, epoch) {
202
+ const n = nodePool.pop();
203
+ if (n) {
204
+ n.sig = sig;
205
+ n.sub = sub;
206
+ n.epoch = epoch;
207
+ return n;
208
+ }
209
+ const fresh = createNode();
210
+ fresh.sig = sig;
211
+ fresh.sub = sub;
212
+ fresh.epoch = epoch;
213
+ return fresh;
214
+ }
215
+ function freeNode(node) {
216
+ node.sig = null;
217
+ node.sub = null;
218
+ node.sigPrev = null;
219
+ node.sigNext = null;
220
+ node.subPrev = null;
221
+ node.subNext = null;
222
+ node.prevActive = null;
223
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
224
+ }
225
+ function linkSignal(sig, node) {
226
+ const oldHead = sig.subsHead ?? null;
227
+ node.sigPrev = null;
228
+ node.sigNext = oldHead;
229
+ if (oldHead) oldHead.sigPrev = node;
230
+ else sig.subsTail = node;
231
+ sig.subsHead = node;
232
+ sig.__sc = (sig.__sc ?? 0) + 1;
233
+ }
234
+ function unlinkSignal(node) {
235
+ const sig = node.sig;
236
+ if (!sig) return;
237
+ const prev = node.sigPrev;
238
+ const next = node.sigNext;
239
+ if (prev) prev.sigNext = next;
240
+ else sig.subsHead = next;
241
+ if (next) next.sigPrev = prev;
242
+ else sig.subsTail = prev;
243
+ sig.__sc = (sig.__sc ?? 1) - 1;
244
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
245
+ if (sig.__sc === 0) {
246
+ sig.subsHead = null;
247
+ sig.subsTail = null;
248
+ }
249
+ }
250
+ function linkSub(sub, node) {
251
+ const oldTail = sub.depsTail ?? null;
252
+ node.subPrev = oldTail;
253
+ node.subNext = null;
254
+ if (oldTail) oldTail.subNext = node;
255
+ else sub.depsHead = node;
256
+ sub.depsTail = node;
257
+ }
258
+ function unlinkSub(node) {
259
+ const sub = node.sub;
260
+ if (!sub) return;
261
+ const prev = node.subPrev;
262
+ const next = node.subNext;
263
+ if (prev) prev.subNext = next;
264
+ else sub.depsHead = next;
265
+ if (next) next.subPrev = prev;
266
+ else sub.depsTail = prev;
204
267
  }
268
+ var currentSubscriber = null;
205
269
  var notifyDepth = 0;
206
270
  var pendingQueue = [];
207
271
  var pendingSet = /* @__PURE__ */ new Set();
@@ -213,64 +277,65 @@ function safeInvoke(sub) {
213
277
  if (_isDev2) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
214
278
  }
215
279
  }
216
- function track(effectFn, subscriber) {
217
- if (!subscriber) subscriber = effectFn;
218
- cleanup(subscriber);
219
- ++stackTop;
220
- if (stackTop >= stackCapacity) {
221
- stackCapacity *= 2;
222
- subscriberStack.length = stackCapacity;
223
- }
224
- subscriberStack[stackTop] = subscriber;
280
+ var subscriberEpochCounter = 0;
281
+ function retrack(effectFn, subscriber) {
282
+ const prev = currentSubscriber;
225
283
  currentSubscriber = subscriber;
284
+ const sub = subscriber;
285
+ const epoch = ++subscriberEpochCounter;
286
+ sub._epoch = epoch;
287
+ sub._structDirty = false;
288
+ for (let n = sub.depsHead ?? null; n !== null; n = n.subNext) {
289
+ const sig = n.sig;
290
+ n.prevActive = sig.__activeNode ?? null;
291
+ sig.__activeNode = n;
292
+ }
226
293
  try {
227
294
  effectFn();
228
295
  } finally {
229
- stackTop--;
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;
296
+ currentSubscriber = prev;
297
+ let node = sub.depsHead ?? null;
298
+ while (node !== null) {
299
+ const next = node.subNext;
300
+ const sig = node.sig;
301
+ sig.__activeNode = node.prevActive;
302
+ node.prevActive = null;
303
+ if (node.epoch !== epoch) {
304
+ unlinkSub(node);
305
+ unlinkSignal(node);
306
+ freeNode(node);
307
+ }
308
+ node = next;
234
309
  }
235
310
  }
236
- return () => cleanup(subscriber);
237
311
  }
238
312
  function recordDependency(signal2) {
239
313
  if (!currentSubscriber) return;
240
314
  const sub = currentSubscriber;
241
- const epoch = sub._epoch;
242
- if (sub._dep === signal2) {
243
- sub._depEpoch = epoch;
315
+ const sig = signal2;
316
+ const epoch = sub._epoch ?? 0;
317
+ const active = sig.__activeNode ?? null;
318
+ if (active !== null && active.sub === sub) {
319
+ active.epoch = epoch;
244
320
  return;
245
321
  }
246
- const deps = sub._deps;
247
- if (deps) {
248
- deps.set(signal2, epoch);
249
- } else if (sub._dep !== void 0) {
250
- const map = /* @__PURE__ */ new Map();
251
- map.set(sub._dep, sub._depEpoch);
252
- map.set(signal2, epoch);
253
- sub._deps = map;
254
- sub._dep = void 0;
255
- sub._depEpoch = void 0;
256
- } else {
257
- sub._dep = signal2;
258
- sub._depEpoch = epoch;
259
- }
260
- const sig = signal2;
261
- let subs = sig[SUBS];
262
- if (!subs) {
263
- subs = /* @__PURE__ */ new Set();
264
- sig[SUBS] = subs;
265
- }
266
- const prevSize = subs.size;
267
- subs.add(currentSubscriber);
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
- }
322
+ const node = allocNode(signal2, sub, epoch);
323
+ node.prevActive = active;
324
+ sig.__activeNode = node;
325
+ linkSub(sub, node);
326
+ linkSignal(sig, node);
327
+ sub._structDirty = true;
328
+ }
329
+ function cleanup(subscriber) {
330
+ const sub = subscriber;
331
+ let node = sub.depsHead ?? null;
332
+ sub.depsHead = null;
333
+ sub.depsTail = null;
334
+ while (node) {
335
+ const next = node.subNext;
336
+ unlinkSignal(node);
337
+ freeNode(node);
338
+ node = next;
274
339
  }
275
340
  }
276
341
  var maxSubscriberRepeats = 50;
@@ -283,7 +348,8 @@ function tickRepeat(sub) {
283
348
  s._runs = 1;
284
349
  return false;
285
350
  }
286
- return ++s._runs > maxSubscriberRepeats;
351
+ s._runs = (s._runs ?? 0) + 1;
352
+ return s._runs > maxSubscriberRepeats;
287
353
  }
288
354
  function cycleError(sub) {
289
355
  if (typeof console !== "undefined") {
@@ -325,93 +391,64 @@ function propagateDirty(sub) {
325
391
  stack.push(rootSig);
326
392
  while (stack.length > baseLen) {
327
393
  const sig = stack.pop();
328
- const first = sig.__f;
329
- if (first) {
330
- if (first._c) {
331
- const nSig = first._sig;
332
- if (!nSig._d) {
333
- nSig._d = true;
334
- stack.push(nSig);
335
- }
336
- } else if (!pendingSet.has(first)) {
337
- pendingSet.add(first);
338
- pendingQueue.push(first);
339
- }
340
- continue;
341
- }
342
- const subs = sig[SUBS];
343
- if (!subs) continue;
344
- for (const s of subs) {
345
- if (s._c) {
346
- const nSig = s._sig;
347
- if (nSig && !nSig._d) {
348
- nSig._d = true;
349
- stack.push(nSig);
350
- } else if (!nSig) {
351
- s();
394
+ let node = sig.subsHead ?? null;
395
+ while (node) {
396
+ const s = node.sub;
397
+ if (s) {
398
+ if (s._c) {
399
+ const nSig = s._sig;
400
+ if (nSig) {
401
+ if (!nSig._d) {
402
+ nSig._d = true;
403
+ stack.push(nSig);
404
+ }
405
+ } else {
406
+ s();
407
+ }
408
+ } else if (!pendingSet.has(s)) {
409
+ pendingSet.add(s);
410
+ pendingQueue.push(s);
352
411
  }
353
- } else if (!pendingSet.has(s)) {
354
- pendingSet.add(s);
355
- pendingQueue.push(s);
356
412
  }
413
+ node = node.sigNext;
357
414
  }
358
415
  }
359
416
  }
360
417
  function notifySubscribers(signal2) {
361
- const first = signal2.__f;
362
- if (first) {
363
- if (notifyDepth > 0) {
364
- if (first._c) {
365
- propagateDirty(first);
366
- } else if (!pendingSet.has(first)) {
367
- pendingSet.add(first);
368
- pendingQueue.push(first);
369
- }
370
- return;
371
- }
372
- notifyDepth++;
373
- drainEpoch++;
374
- try {
375
- if (first._c) {
376
- propagateDirty(first);
377
- } else if (tickRepeat(first)) {
378
- cycleError(first);
379
- } else {
380
- safeInvoke(first);
381
- }
382
- drainQueue();
383
- } finally {
384
- notifyDepth--;
385
- if (notifyDepth === 0) {
386
- pendingQueue.length = 0;
387
- pendingSet.clear();
388
- }
389
- }
390
- return;
391
- }
392
- const subs = signal2[SUBS];
393
- if (!subs || subs.size === 0) return;
418
+ const sig = signal2;
419
+ const head = sig.subsHead;
420
+ if (!head) return;
394
421
  if (notifyDepth > 0) {
395
- for (const sub of subs) {
396
- if (sub._c) {
397
- propagateDirty(sub);
398
- } else if (!pendingSet.has(sub)) {
399
- pendingSet.add(sub);
400
- pendingQueue.push(sub);
422
+ let node = head;
423
+ while (node) {
424
+ const s = node.sub;
425
+ if (s) {
426
+ if (s._c) {
427
+ propagateDirty(s);
428
+ } else if (!pendingSet.has(s)) {
429
+ pendingSet.add(s);
430
+ pendingQueue.push(s);
431
+ }
401
432
  }
433
+ node = node.sigNext;
402
434
  }
403
435
  return;
404
436
  }
405
437
  notifyDepth++;
406
438
  drainEpoch++;
407
439
  try {
408
- for (const sub of subs) {
409
- if (sub._c) {
410
- propagateDirty(sub);
411
- } else if (!pendingSet.has(sub)) {
412
- pendingSet.add(sub);
413
- pendingQueue.push(sub);
440
+ let node = head;
441
+ while (node) {
442
+ const s = node.sub;
443
+ if (s) {
444
+ if (s._c) {
445
+ propagateDirty(s);
446
+ } else if (!pendingSet.has(s)) {
447
+ pendingSet.add(s);
448
+ pendingQueue.push(s);
449
+ }
414
450
  }
451
+ node = node.sigNext;
415
452
  }
416
453
  drainQueue();
417
454
  } finally {
@@ -422,29 +459,26 @@ function notifySubscribers(signal2) {
422
459
  }
423
460
  }
424
461
  }
425
- function cleanup(subscriber) {
462
+ function getSubscriberCount(signal2) {
463
+ return signal2.__sc ?? 0;
464
+ }
465
+ function getSubscriberDeps(subscriber) {
426
466
  const sub = subscriber;
427
- const singleDep = sub._dep;
428
- if (singleDep !== void 0) {
429
- const sig = singleDep;
430
- const subs = sig[SUBS];
431
- if (subs?.delete(subscriber)) {
432
- syncFastPath(sig, subs);
433
- }
434
- sub._dep = void 0;
435
- sub._depEpoch = void 0;
436
- return;
467
+ const out = [];
468
+ let node = sub.depsHead ?? null;
469
+ while (node) {
470
+ if (node.sig) out.push(node.sig);
471
+ node = node.subNext;
437
472
  }
438
- const deps = sub._deps;
439
- if (!deps || deps.size === 0) return;
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);
445
- }
473
+ return out;
474
+ }
475
+ function forEachSubscriber(signal2, visit) {
476
+ let node = signal2.subsHead ?? null;
477
+ while (node) {
478
+ const s = node.sub;
479
+ if (s) visit(s);
480
+ node = node.sigNext;
446
481
  }
447
- deps.clear();
448
482
  }
449
483
 
450
484
  // src/reactivity/batch.ts
@@ -460,32 +494,64 @@ function enqueueBatchedSignal(signal2) {
460
494
  var _g = globalThis;
461
495
  var _isDev3 = isDev();
462
496
  function signal(initial, options) {
463
- const state = { value: initial };
497
+ const state = {
498
+ value: initial,
499
+ __v: 0,
500
+ __sc: 0,
501
+ subsHead: null,
502
+ subsTail: null,
503
+ __activeNode: null,
504
+ __name: void 0
505
+ };
464
506
  const debugName = _isDev3 ? options?.name : void 0;
465
507
  const equalsFn = options?.equals;
466
- if (debugName) {
467
- state.__name = debugName;
468
- }
508
+ if (debugName) state.__name = debugName;
469
509
  function get() {
470
510
  recordDependency(state);
471
511
  return state.value;
472
512
  }
473
513
  get.__signal = state;
474
514
  if (debugName) get.__name = debugName;
475
- function set(next) {
476
- const newValue = typeof next === "function" ? next(state.value) : next;
477
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
478
- if (_isDev3) {
479
- const oldValue = state.value;
515
+ let set;
516
+ if (equalsFn) {
517
+ set = (next) => {
518
+ const prev = state.value;
519
+ const newValue = typeof next === "function" ? next(prev) : next;
520
+ if (equalsFn(prev, newValue)) return;
480
521
  state.value = newValue;
522
+ state.__v++;
523
+ if (_isDev3) {
524
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
525
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
526
+ }
527
+ if (!enqueueBatchedSignal(state)) {
528
+ notifySubscribers(state);
529
+ }
530
+ };
531
+ } else if (_isDev3) {
532
+ set = (next) => {
533
+ const prev = state.value;
534
+ const newValue = typeof next === "function" ? next(prev) : next;
535
+ if (Object.is(newValue, prev)) return;
536
+ state.value = newValue;
537
+ state.__v++;
481
538
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
482
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
483
- } else {
539
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
540
+ if (!enqueueBatchedSignal(state)) {
541
+ notifySubscribers(state);
542
+ }
543
+ };
544
+ } else {
545
+ set = (next) => {
546
+ const prev = state.value;
547
+ const newValue = typeof next === "function" ? next(prev) : next;
548
+ if (Object.is(newValue, prev)) return;
484
549
  state.value = newValue;
485
- }
486
- if (!enqueueBatchedSignal(state)) {
487
- notifySubscribers(state);
488
- }
550
+ state.__v++;
551
+ if (!enqueueBatchedSignal(state)) {
552
+ notifySubscribers(state);
553
+ }
554
+ };
489
555
  }
490
556
  if (_isDev3) {
491
557
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -694,13 +760,14 @@ function initDevTools(config) {
694
760
  } catch {
695
761
  value = "<error>";
696
762
  }
697
- const subs = node.ref?.__s;
698
763
  result.push({
699
764
  id: node.id,
700
765
  name: node.name,
701
766
  type: node.type,
702
767
  value,
703
- subscriberCount: subs instanceof Set ? subs.size : 0
768
+ // __sc is the O(1) subscriber count maintained by the reactivity
769
+ // core (track.ts) on every linked-list splice.
770
+ subscriberCount: node.ref?.__sc ?? 0
704
771
  });
705
772
  }
706
773
  return result;
@@ -841,14 +908,13 @@ function initDevTools(config) {
841
908
  }
842
909
  const fullVal = val;
843
910
  const shortVal = val.length > 80 ? `${val.substring(0, 80)}...` : val;
844
- const subs = node.ref?.__s;
845
911
  sArr.push({
846
912
  id: node.id,
847
913
  n: node.name,
848
914
  tp: node.type,
849
915
  v: shortVal,
850
916
  fv: fullVal,
851
- sc: subs instanceof Set ? subs.size : 0
917
+ sc: node.ref?.__sc ?? 0
852
918
  });
853
919
  }
854
920
  function walkElement(el, depth) {
@@ -1488,120 +1554,122 @@ function isSSR() {
1488
1554
 
1489
1555
  // src/core/signals/effect.ts
1490
1556
  var _g2 = globalThis;
1557
+ var MAX_RERUNS = 100;
1558
+ function flushUserCleanups(ctx) {
1559
+ const list = ctx.userCleanups;
1560
+ if (list.length === 0) return;
1561
+ ctx.userCleanups = [];
1562
+ for (let i = list.length - 1; i >= 0; i--) {
1563
+ try {
1564
+ list[i]();
1565
+ } catch (err) {
1566
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
1567
+ }
1568
+ }
1569
+ }
1570
+ function drainReruns(ctx) {
1571
+ let reruns = 1;
1572
+ do {
1573
+ ctx.rerunPending = false;
1574
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1575
+ retrack(ctx.bodyFn, ctx.subscriber);
1576
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
1577
+ if (ctx.rerunPending) {
1578
+ ctx.rerunPending = false;
1579
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1580
+ console.error(
1581
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1582
+ );
1583
+ }
1584
+ }
1585
+ }
1586
+ function disposeEffect(ctx) {
1587
+ if (ctx.disposed) return;
1588
+ ctx.disposed = true;
1589
+ const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1590
+ if (h) {
1591
+ try {
1592
+ h.emit("effect:destroy", { effectFn: ctx.fn });
1593
+ } catch {
1594
+ }
1595
+ }
1596
+ try {
1597
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1598
+ } catch (err) {
1599
+ if (typeof console !== "undefined") {
1600
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
1601
+ }
1602
+ }
1603
+ try {
1604
+ cleanup(ctx.subscriber);
1605
+ } catch (err) {
1606
+ if (typeof console !== "undefined") {
1607
+ console.warn("[SibuJS effect] dispose threw:", err);
1608
+ }
1609
+ }
1610
+ }
1491
1611
  function effect(effectFn, options) {
1492
1612
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
1493
1613
  if (isSSR()) return () => {
1494
1614
  };
1495
- const onError = options?.onError;
1496
- let userCleanups = [];
1497
- const onCleanup = (fn) => {
1498
- userCleanups.push(fn);
1615
+ const ctx = {
1616
+ fn: effectFn,
1617
+ onError: options?.onError,
1618
+ userCleanups: [],
1619
+ running: false,
1620
+ rerunPending: false,
1621
+ disposed: false,
1622
+ onCleanup: null,
1623
+ subscriber: null,
1624
+ bodyFn: null
1499
1625
  };
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
- }
1626
+ ctx.onCleanup = (fn) => {
1627
+ ctx.userCleanups.push(fn);
1513
1628
  };
1514
- const invokeBody = () => effectFn(onCleanup);
1515
- const wrappedFn = onError ? () => {
1629
+ const onErrorCaptured = ctx.onError;
1630
+ ctx.bodyFn = onErrorCaptured ? () => {
1516
1631
  try {
1517
- invokeBody();
1632
+ ctx.fn(ctx.onCleanup);
1518
1633
  } catch (err) {
1519
- onError(err);
1634
+ onErrorCaptured(err);
1520
1635
  }
1521
- } : invokeBody;
1522
- let cleanupHandle = () => {
1636
+ } : () => {
1637
+ ctx.fn(ctx.onCleanup);
1523
1638
  };
1524
- let running = false;
1525
- let rerunPending = false;
1526
- const MAX_RERUNS = 100;
1527
- const subscriber = () => {
1528
- if (running) {
1529
- rerunPending = true;
1639
+ const sub = (() => {
1640
+ if (ctx.running) {
1641
+ ctx.rerunPending = true;
1530
1642
  return;
1531
1643
  }
1532
- running = true;
1644
+ ctx.running = true;
1533
1645
  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);
1646
+ ctx.rerunPending = false;
1647
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1648
+ retrack(ctx.bodyFn, sub);
1649
+ if (ctx.rerunPending) drainReruns(ctx);
1550
1650
  } finally {
1551
- running = false;
1552
- rerunPending = false;
1651
+ ctx.running = false;
1652
+ ctx.rerunPending = false;
1553
1653
  }
1554
- };
1555
- running = true;
1654
+ });
1655
+ sub.depsHead = null;
1656
+ sub.depsTail = null;
1657
+ sub._epoch = 0;
1658
+ sub._structDirty = false;
1659
+ sub._runEpoch = 0;
1660
+ sub._runs = 0;
1661
+ ctx.subscriber = sub;
1662
+ ctx.running = true;
1556
1663
  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);
1664
+ retrack(ctx.bodyFn, ctx.subscriber);
1665
+ if (ctx.rerunPending) drainReruns(ctx);
1573
1666
  } finally {
1574
- running = false;
1575
- rerunPending = false;
1667
+ ctx.running = false;
1668
+ ctx.rerunPending = false;
1576
1669
  }
1577
1670
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1578
1671
  if (hook) hook.emit("effect:create", { effectFn });
1579
- let disposed = false;
1580
- return () => {
1581
- if (disposed) return;
1582
- disposed = true;
1583
- const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
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
- }
1604
- };
1672
+ return () => disposeEffect(ctx);
1605
1673
  }
1606
1674
 
1607
1675
  // src/devtools/debugValue.ts
@@ -1704,32 +1772,24 @@ function createDevtoolsOverlay(options) {
1704
1772
  }
1705
1773
 
1706
1774
  // src/devtools/introspect.ts
1707
- var SUBS2 = "__s";
1708
1775
  function getSignalName(getter) {
1709
1776
  return getter.__name;
1710
1777
  }
1711
- function getSubscriberCount(getter) {
1778
+ function getSubscriberCount2(getter) {
1712
1779
  const signal2 = getter.__signal;
1713
1780
  if (!signal2) return 0;
1714
- const subs = signal2[SUBS2];
1715
- return subs ? subs.size : 0;
1781
+ return getSubscriberCount(signal2);
1716
1782
  }
1717
1783
  function getDependencies(subscriberFn) {
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);
1784
+ return getSubscriberDeps(subscriberFn);
1724
1785
  }
1725
1786
  function inspectSignal(getter) {
1726
1787
  const signal2 = getter.__signal;
1727
1788
  if (!signal2) return null;
1728
- const subs = signal2[SUBS2];
1729
1789
  return {
1730
1790
  name: getter.__name,
1731
1791
  signal: signal2,
1732
- subscriberCount: subs ? subs.size : 0
1792
+ subscriberCount: getSubscriberCount(signal2)
1733
1793
  };
1734
1794
  }
1735
1795
  function walkDependencyGraph(getter, maxDepth = 10, visited = /* @__PURE__ */ new WeakSet()) {
@@ -1738,24 +1798,21 @@ function walkDependencyGraph(getter, maxDepth = 10, visited = /* @__PURE__ */ ne
1738
1798
  return { name: getSignalName(getter), subscribers: 0, downstream: [] };
1739
1799
  }
1740
1800
  visited.add(signal2);
1741
- const subs = signal2[SUBS2];
1742
1801
  const downstream = [];
1743
- if (subs) {
1744
- for (const sub of subs) {
1745
- const subSig = sub._sig;
1746
- if (subSig && !visited.has(subSig)) {
1747
- const subName = subSig.__name;
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));
1753
- }
1802
+ forEachSubscriber(signal2, (sub) => {
1803
+ const subSig = sub._sig;
1804
+ if (subSig && !visited.has(subSig)) {
1805
+ const subName = subSig.__name;
1806
+ const fakeGetter = (() => void 0);
1807
+ const tag = fakeGetter;
1808
+ tag.__signal = subSig;
1809
+ if (subName !== void 0) tag.__name = subName;
1810
+ downstream.push(walkDependencyGraph(fakeGetter, maxDepth - 1, visited));
1754
1811
  }
1755
- }
1812
+ });
1756
1813
  return {
1757
1814
  name: getSignalName(getter),
1758
- subscribers: subs ? subs.size : 0,
1815
+ subscribers: getSubscriberCount(signal2),
1759
1816
  downstream
1760
1817
  };
1761
1818
  }