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/extras.cjs CHANGED
@@ -145,7 +145,7 @@ __export(extras_exports, {
145
145
  getPerformanceReport: () => getPerformanceReport,
146
146
  getQueryData: () => getQueryData,
147
147
  getSignalName: () => getSignalName,
148
- getSubscriberCount: () => getSubscriberCount,
148
+ getSubscriberCount: () => getSubscriberCount2,
149
149
  globalStore: () => globalStore,
150
150
  healthCheck: () => healthCheck,
151
151
  hmrState: () => hmrState,
@@ -332,24 +332,88 @@ function devWarn(message) {
332
332
 
333
333
  // src/reactivity/track.ts
334
334
  var _isDev2 = isDev();
335
- var STACK_INITIAL = 32;
336
- var STACK_SHRINK_THRESHOLD = 128;
337
- var subscriberStack = new Array(STACK_INITIAL);
338
- var stackCapacity = STACK_INITIAL;
339
- var stackTop = -1;
340
- var currentSubscriber = null;
341
- var SUBS = "__s";
342
- function syncFastPath(signal2, subs) {
343
- const size = subs.size;
344
- if (size === 0) {
345
- signal2.__f = void 0;
346
- delete signal2[SUBS];
347
- } else if (size === 1) {
348
- signal2.__f = subs.values().next().value;
349
- } else {
350
- signal2.__f = void 0;
351
- }
335
+ var POOL_MAX = 4096;
336
+ var nodePool = [];
337
+ function createNode() {
338
+ return {
339
+ sig: null,
340
+ sub: null,
341
+ epoch: 0,
342
+ sigPrev: null,
343
+ sigNext: null,
344
+ subPrev: null,
345
+ subNext: null,
346
+ prevActive: null
347
+ };
348
+ }
349
+ function allocNode(sig, sub2, epoch) {
350
+ const n = nodePool.pop();
351
+ if (n) {
352
+ n.sig = sig;
353
+ n.sub = sub2;
354
+ n.epoch = epoch;
355
+ return n;
356
+ }
357
+ const fresh = createNode();
358
+ fresh.sig = sig;
359
+ fresh.sub = sub2;
360
+ fresh.epoch = epoch;
361
+ return fresh;
362
+ }
363
+ function freeNode(node) {
364
+ node.sig = null;
365
+ node.sub = null;
366
+ node.sigPrev = null;
367
+ node.sigNext = null;
368
+ node.subPrev = null;
369
+ node.subNext = null;
370
+ node.prevActive = null;
371
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
372
+ }
373
+ function linkSignal(sig, node) {
374
+ const oldHead = sig.subsHead ?? null;
375
+ node.sigPrev = null;
376
+ node.sigNext = oldHead;
377
+ if (oldHead) oldHead.sigPrev = node;
378
+ else sig.subsTail = node;
379
+ sig.subsHead = node;
380
+ sig.__sc = (sig.__sc ?? 0) + 1;
381
+ }
382
+ function unlinkSignal(node) {
383
+ const sig = node.sig;
384
+ if (!sig) return;
385
+ const prev = node.sigPrev;
386
+ const next = node.sigNext;
387
+ if (prev) prev.sigNext = next;
388
+ else sig.subsHead = next;
389
+ if (next) next.sigPrev = prev;
390
+ else sig.subsTail = prev;
391
+ sig.__sc = (sig.__sc ?? 1) - 1;
392
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
393
+ if (sig.__sc === 0) {
394
+ sig.subsHead = null;
395
+ sig.subsTail = null;
396
+ }
397
+ }
398
+ function linkSub(sub2, node) {
399
+ const oldTail = sub2.depsTail ?? null;
400
+ node.subPrev = oldTail;
401
+ node.subNext = null;
402
+ if (oldTail) oldTail.subNext = node;
403
+ else sub2.depsHead = node;
404
+ sub2.depsTail = node;
405
+ }
406
+ function unlinkSub(node) {
407
+ const sub2 = node.sub;
408
+ if (!sub2) return;
409
+ const prev = node.subPrev;
410
+ const next = node.subNext;
411
+ if (prev) prev.subNext = next;
412
+ else sub2.depsHead = next;
413
+ if (next) next.subPrev = prev;
414
+ else sub2.depsTail = prev;
352
415
  }
416
+ var currentSubscriber = null;
353
417
  var notifyDepth = 0;
354
418
  var pendingQueue = [];
355
419
  var pendingSet = /* @__PURE__ */ new Set();
@@ -369,110 +433,77 @@ function retrack(effectFn, subscriber) {
369
433
  const sub2 = subscriber;
370
434
  const epoch = ++subscriberEpochCounter;
371
435
  sub2._epoch = epoch;
436
+ sub2._structDirty = false;
437
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
438
+ const sig = n.sig;
439
+ n.prevActive = sig.__activeNode ?? null;
440
+ sig.__activeNode = n;
441
+ }
372
442
  try {
373
443
  effectFn();
374
444
  } finally {
375
445
  currentSubscriber = prev;
376
- pruneStaleDeps(sub2, epoch);
377
- }
378
- }
379
- function pruneStaleDeps(sub2, currentEpoch) {
380
- if (sub2._dep !== void 0) {
381
- if (sub2._depEpoch !== currentEpoch) {
382
- const sig = sub2._dep;
383
- const subs = sig[SUBS];
384
- if (subs?.delete(sub2)) syncFastPath(sig, subs);
385
- sub2._dep = void 0;
386
- sub2._depEpoch = void 0;
387
- }
388
- return;
389
- }
390
- const deps = sub2._deps;
391
- if (!deps || deps.size === 0) return;
392
- let stales;
393
- for (const [signal2, epoch] of deps) {
394
- if (epoch !== currentEpoch) {
395
- (stales ?? (stales = [])).push(signal2);
446
+ let node = sub2.depsHead ?? null;
447
+ while (node !== null) {
448
+ const next = node.subNext;
449
+ const sig = node.sig;
450
+ sig.__activeNode = node.prevActive;
451
+ node.prevActive = null;
452
+ if (node.epoch !== epoch) {
453
+ unlinkSub(node);
454
+ unlinkSignal(node);
455
+ freeNode(node);
456
+ }
457
+ node = next;
396
458
  }
397
459
  }
398
- if (!stales) return;
399
- for (const signal2 of stales) {
400
- deps.delete(signal2);
401
- const sig = signal2;
402
- const subs = sig[SUBS];
403
- if (subs?.delete(sub2)) syncFastPath(sig, subs);
404
- }
405
460
  }
406
461
  function track(effectFn, subscriber) {
407
462
  if (!subscriber) subscriber = effectFn;
408
463
  cleanup(subscriber);
409
- ++stackTop;
410
- if (stackTop >= stackCapacity) {
411
- stackCapacity *= 2;
412
- subscriberStack.length = stackCapacity;
413
- }
414
- subscriberStack[stackTop] = subscriber;
464
+ const prev = currentSubscriber;
415
465
  currentSubscriber = subscriber;
416
466
  try {
417
467
  effectFn();
418
468
  } finally {
419
- stackTop--;
420
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
421
- if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
422
- stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
423
- subscriberStack.length = stackCapacity;
469
+ currentSubscriber = prev;
470
+ const sub3 = subscriber;
471
+ for (let n = sub3.depsHead ?? null; n !== null; n = n.subNext) {
472
+ const sig = n.sig;
473
+ sig.__activeNode = n.prevActive;
474
+ n.prevActive = null;
424
475
  }
425
476
  }
426
- return () => cleanup(subscriber);
477
+ const sub2 = subscriber;
478
+ return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
427
479
  }
428
480
  function recordDependency(signal2) {
429
481
  if (!currentSubscriber) return;
430
482
  const sub2 = currentSubscriber;
431
- const epoch = sub2._epoch;
432
- if (sub2._dep === signal2) {
433
- sub2._depEpoch = epoch;
434
- return;
435
- }
436
- const deps = sub2._deps;
437
- if (deps) {
438
- deps.set(signal2, epoch);
439
- } else if (sub2._dep !== void 0) {
440
- const map2 = /* @__PURE__ */ new Map();
441
- map2.set(sub2._dep, sub2._depEpoch);
442
- map2.set(signal2, epoch);
443
- sub2._deps = map2;
444
- sub2._dep = void 0;
445
- sub2._depEpoch = void 0;
446
- } else {
447
- sub2._dep = signal2;
448
- sub2._depEpoch = epoch;
449
- }
450
483
  const sig = signal2;
451
- let subs = sig[SUBS];
452
- if (!subs) {
453
- subs = /* @__PURE__ */ new Set();
454
- sig[SUBS] = subs;
455
- }
456
- const prevSize = subs.size;
457
- subs.add(currentSubscriber);
458
- if (subs.size !== prevSize) {
459
- if (subs.size === 1) {
460
- sig.__f = currentSubscriber;
461
- } else if (sig.__f !== void 0) {
462
- sig.__f = void 0;
463
- }
484
+ const epoch = sub2._epoch ?? 0;
485
+ const active = sig.__activeNode ?? null;
486
+ if (active !== null && active.sub === sub2) {
487
+ active.epoch = epoch;
488
+ return;
464
489
  }
490
+ const node = allocNode(signal2, sub2, epoch);
491
+ node.prevActive = active;
492
+ sig.__activeNode = node;
493
+ linkSub(sub2, node);
494
+ linkSignal(sig, node);
495
+ sub2._structDirty = true;
465
496
  }
466
- function queueSignalNotification(signal2) {
467
- const subs = signal2[SUBS];
468
- if (!subs) return;
469
- for (const sub2 of subs) {
470
- if (sub2._c) {
471
- propagateDirty(sub2);
472
- } else if (!pendingSet.has(sub2)) {
473
- pendingSet.add(sub2);
474
- pendingQueue.push(sub2);
475
- }
497
+ function cleanup(subscriber) {
498
+ const sub2 = subscriber;
499
+ let node = sub2.depsHead ?? null;
500
+ sub2.depsHead = null;
501
+ sub2.depsTail = null;
502
+ while (node) {
503
+ const next = node.subNext;
504
+ unlinkSignal(node);
505
+ freeNode(node);
506
+ node = next;
476
507
  }
477
508
  }
478
509
  var maxSubscriberRepeats = 50;
@@ -485,7 +516,8 @@ function tickRepeat(sub2) {
485
516
  s2._runs = 1;
486
517
  return false;
487
518
  }
488
- return ++s2._runs > maxSubscriberRepeats;
519
+ s2._runs = (s2._runs ?? 0) + 1;
520
+ return s2._runs > maxSubscriberRepeats;
489
521
  }
490
522
  function cycleError(sub2) {
491
523
  if (typeof console !== "undefined") {
@@ -541,93 +573,80 @@ function propagateDirty(sub2) {
541
573
  stack.push(rootSig);
542
574
  while (stack.length > baseLen) {
543
575
  const sig = stack.pop();
544
- const first = sig.__f;
545
- if (first) {
546
- if (first._c) {
547
- const nSig = first._sig;
548
- if (!nSig._d) {
549
- nSig._d = true;
550
- stack.push(nSig);
576
+ let node = sig.subsHead ?? null;
577
+ while (node) {
578
+ const s2 = node.sub;
579
+ if (s2) {
580
+ if (s2._c) {
581
+ const nSig = s2._sig;
582
+ if (nSig) {
583
+ if (!nSig._d) {
584
+ nSig._d = true;
585
+ stack.push(nSig);
586
+ }
587
+ } else {
588
+ s2();
589
+ }
590
+ } else if (!pendingSet.has(s2)) {
591
+ pendingSet.add(s2);
592
+ pendingQueue.push(s2);
551
593
  }
552
- } else if (!pendingSet.has(first)) {
553
- pendingSet.add(first);
554
- pendingQueue.push(first);
555
594
  }
556
- continue;
595
+ node = node.sigNext;
557
596
  }
558
- const subs = sig[SUBS];
559
- if (!subs) continue;
560
- for (const s2 of subs) {
597
+ }
598
+ }
599
+ function queueSignalNotification(signal2) {
600
+ const sig = signal2;
601
+ let node = sig.subsHead ?? null;
602
+ while (node) {
603
+ const s2 = node.sub;
604
+ if (s2) {
561
605
  if (s2._c) {
562
- const nSig = s2._sig;
563
- if (nSig && !nSig._d) {
564
- nSig._d = true;
565
- stack.push(nSig);
566
- } else if (!nSig) {
567
- s2();
568
- }
606
+ propagateDirty(s2);
569
607
  } else if (!pendingSet.has(s2)) {
570
608
  pendingSet.add(s2);
571
609
  pendingQueue.push(s2);
572
610
  }
573
611
  }
612
+ node = node.sigNext;
574
613
  }
575
614
  }
576
615
  function notifySubscribers(signal2) {
577
- const first = signal2.__f;
578
- if (first) {
579
- if (notifyDepth > 0) {
580
- if (first._c) {
581
- propagateDirty(first);
582
- } else if (!pendingSet.has(first)) {
583
- pendingSet.add(first);
584
- pendingQueue.push(first);
585
- }
586
- return;
587
- }
588
- notifyDepth++;
589
- drainEpoch++;
590
- try {
591
- if (first._c) {
592
- propagateDirty(first);
593
- } else if (tickRepeat(first)) {
594
- cycleError(first);
595
- } else {
596
- safeInvoke(first);
597
- }
598
- drainQueue();
599
- } finally {
600
- notifyDepth--;
601
- if (notifyDepth === 0) {
602
- pendingQueue.length = 0;
603
- pendingSet.clear();
604
- }
605
- }
606
- return;
607
- }
608
- const subs = signal2[SUBS];
609
- if (!subs || subs.size === 0) return;
616
+ const sig = signal2;
617
+ const head2 = sig.subsHead;
618
+ if (!head2) return;
610
619
  if (notifyDepth > 0) {
611
- for (const sub2 of subs) {
612
- if (sub2._c) {
613
- propagateDirty(sub2);
614
- } else if (!pendingSet.has(sub2)) {
615
- pendingSet.add(sub2);
616
- pendingQueue.push(sub2);
620
+ let node = head2;
621
+ while (node) {
622
+ const s2 = node.sub;
623
+ if (s2) {
624
+ if (s2._c) {
625
+ propagateDirty(s2);
626
+ } else if (!pendingSet.has(s2)) {
627
+ pendingSet.add(s2);
628
+ pendingQueue.push(s2);
629
+ }
617
630
  }
631
+ node = node.sigNext;
618
632
  }
619
633
  return;
620
634
  }
621
635
  notifyDepth++;
622
636
  drainEpoch++;
623
637
  try {
624
- for (const sub2 of subs) {
625
- if (sub2._c) {
626
- propagateDirty(sub2);
627
- } else if (!pendingSet.has(sub2)) {
628
- pendingSet.add(sub2);
629
- pendingQueue.push(sub2);
638
+ let node = head2;
639
+ while (node) {
640
+ const s2 = node.sub;
641
+ if (s2) {
642
+ if (s2._c) {
643
+ propagateDirty(s2);
644
+ } else if (!pendingSet.has(s2)) {
645
+ pendingSet.add(s2);
646
+ pendingQueue.push(s2);
647
+ }
630
648
  }
649
+ node = node.sigNext;
631
650
  }
632
651
  drainQueue();
633
652
  } finally {
@@ -638,29 +657,26 @@ function notifySubscribers(signal2) {
638
657
  }
639
658
  }
640
659
  }
641
- function cleanup(subscriber) {
660
+ function getSubscriberCount(signal2) {
661
+ return signal2.__sc ?? 0;
662
+ }
663
+ function getSubscriberDeps(subscriber) {
642
664
  const sub2 = subscriber;
643
- const singleDep = sub2._dep;
644
- if (singleDep !== void 0) {
645
- const sig = singleDep;
646
- const subs = sig[SUBS];
647
- if (subs?.delete(subscriber)) {
648
- syncFastPath(sig, subs);
649
- }
650
- sub2._dep = void 0;
651
- sub2._depEpoch = void 0;
652
- return;
665
+ const out = [];
666
+ let node = sub2.depsHead ?? null;
667
+ while (node) {
668
+ if (node.sig) out.push(node.sig);
669
+ node = node.subNext;
653
670
  }
654
- const deps = sub2._deps;
655
- if (!deps || deps.size === 0) return;
656
- for (const signal2 of deps.keys()) {
657
- const sig = signal2;
658
- const subs = sig[SUBS];
659
- if (subs?.delete(subscriber)) {
660
- syncFastPath(sig, subs);
661
- }
671
+ return out;
672
+ }
673
+ function forEachSubscriber(signal2, visit) {
674
+ let node = signal2.subsHead ?? null;
675
+ while (node) {
676
+ const s2 = node.sub;
677
+ if (s2) visit(s2);
678
+ node = node.sigNext;
662
679
  }
663
- deps.clear();
664
680
  }
665
681
 
666
682
  // src/core/signals/derived.ts
@@ -671,6 +687,7 @@ function derived(getter, options) {
671
687
  const cs = {};
672
688
  cs._d = false;
673
689
  cs._g = getter;
690
+ cs.__v = 0;
674
691
  const markDirty = () => {
675
692
  if (cs._d) return;
676
693
  cs._d = true;
@@ -700,11 +717,14 @@ function derived(getter, options) {
700
717
  evaluating = true;
701
718
  let threw = true;
702
719
  try {
720
+ const prev = cs._v;
703
721
  retrack(() => {
704
- cs._v = getter();
722
+ const next = getter();
723
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
705
724
  cs._d = false;
706
725
  threw = false;
707
726
  }, markDirty);
727
+ if (!Object.is(prev, cs._v)) cs.__v++;
708
728
  } finally {
709
729
  evaluating = false;
710
730
  if (threw) cs._d = true;
@@ -724,6 +744,7 @@ function derived(getter, options) {
724
744
  cs._d = false;
725
745
  threw = false;
726
746
  }, markDirty);
747
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
727
748
  } finally {
728
749
  evaluating = false;
729
750
  if (threw) cs._d = true;
@@ -770,120 +791,122 @@ function isSSR() {
770
791
 
771
792
  // src/core/signals/effect.ts
772
793
  var _g = globalThis;
794
+ var MAX_RERUNS = 100;
795
+ function flushUserCleanups(ctx) {
796
+ const list = ctx.userCleanups;
797
+ if (list.length === 0) return;
798
+ ctx.userCleanups = [];
799
+ for (let i2 = list.length - 1; i2 >= 0; i2--) {
800
+ try {
801
+ list[i2]();
802
+ } catch (err) {
803
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
804
+ }
805
+ }
806
+ }
807
+ function drainReruns(ctx) {
808
+ let reruns = 1;
809
+ do {
810
+ ctx.rerunPending = false;
811
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
812
+ retrack(ctx.bodyFn, ctx.subscriber);
813
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
814
+ if (ctx.rerunPending) {
815
+ ctx.rerunPending = false;
816
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
817
+ console.error(
818
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
819
+ );
820
+ }
821
+ }
822
+ }
823
+ function disposeEffect(ctx) {
824
+ if (ctx.disposed) return;
825
+ ctx.disposed = true;
826
+ const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
827
+ if (h) {
828
+ try {
829
+ h.emit("effect:destroy", { effectFn: ctx.fn });
830
+ } catch {
831
+ }
832
+ }
833
+ try {
834
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
835
+ } catch (err) {
836
+ if (typeof console !== "undefined") {
837
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
838
+ }
839
+ }
840
+ try {
841
+ cleanup(ctx.subscriber);
842
+ } catch (err) {
843
+ if (typeof console !== "undefined") {
844
+ console.warn("[SibuJS effect] dispose threw:", err);
845
+ }
846
+ }
847
+ }
773
848
  function effect(effectFn, options) {
774
849
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
775
850
  if (isSSR()) return () => {
776
851
  };
777
- const onError = options?.onError;
778
- let userCleanups = [];
779
- const onCleanup = (fn) => {
780
- userCleanups.push(fn);
781
- };
782
- const runUserCleanups = () => {
783
- if (userCleanups.length === 0) return;
784
- const list = userCleanups;
785
- userCleanups = [];
786
- for (let i2 = list.length - 1; i2 >= 0; i2--) {
787
- try {
788
- list[i2]();
789
- } catch (err) {
790
- if (typeof console !== "undefined") {
791
- console.warn("[SibuJS effect] onCleanup threw:", err);
792
- }
793
- }
794
- }
795
- };
796
- const invokeBody = () => effectFn(onCleanup);
797
- const wrappedFn = onError ? () => {
852
+ const ctx = {
853
+ fn: effectFn,
854
+ onError: options?.onError,
855
+ userCleanups: [],
856
+ running: false,
857
+ rerunPending: false,
858
+ disposed: false,
859
+ onCleanup: null,
860
+ subscriber: null,
861
+ bodyFn: null
862
+ };
863
+ ctx.onCleanup = (fn) => {
864
+ ctx.userCleanups.push(fn);
865
+ };
866
+ const onErrorCaptured = ctx.onError;
867
+ ctx.bodyFn = onErrorCaptured ? () => {
798
868
  try {
799
- invokeBody();
869
+ ctx.fn(ctx.onCleanup);
800
870
  } catch (err) {
801
- onError(err);
871
+ onErrorCaptured(err);
802
872
  }
803
- } : invokeBody;
804
- let cleanupHandle = () => {
873
+ } : () => {
874
+ ctx.fn(ctx.onCleanup);
805
875
  };
806
- let running = false;
807
- let rerunPending = false;
808
- const MAX_RERUNS = 100;
809
- const subscriber = () => {
810
- if (running) {
811
- rerunPending = true;
876
+ const sub2 = (() => {
877
+ if (ctx.running) {
878
+ ctx.rerunPending = true;
812
879
  return;
813
880
  }
814
- running = true;
881
+ ctx.running = true;
815
882
  try {
816
- let reruns = 0;
817
- do {
818
- rerunPending = false;
819
- runUserCleanups();
820
- cleanupHandle();
821
- cleanupHandle = track(wrappedFn, subscriber);
822
- if (++reruns > MAX_RERUNS) {
823
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
824
- console.error(
825
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
826
- );
827
- }
828
- rerunPending = false;
829
- break;
830
- }
831
- } while (rerunPending);
883
+ ctx.rerunPending = false;
884
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
885
+ retrack(ctx.bodyFn, sub2);
886
+ if (ctx.rerunPending) drainReruns(ctx);
832
887
  } finally {
833
- running = false;
834
- rerunPending = false;
888
+ ctx.running = false;
889
+ ctx.rerunPending = false;
835
890
  }
836
- };
837
- running = true;
891
+ });
892
+ sub2.depsHead = null;
893
+ sub2.depsTail = null;
894
+ sub2._epoch = 0;
895
+ sub2._structDirty = false;
896
+ sub2._runEpoch = 0;
897
+ sub2._runs = 0;
898
+ ctx.subscriber = sub2;
899
+ ctx.running = true;
838
900
  try {
839
- let reruns = 0;
840
- do {
841
- rerunPending = false;
842
- runUserCleanups();
843
- cleanupHandle();
844
- cleanupHandle = track(wrappedFn, subscriber);
845
- if (++reruns > MAX_RERUNS) {
846
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
847
- console.error(
848
- `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
849
- );
850
- }
851
- rerunPending = false;
852
- break;
853
- }
854
- } while (rerunPending);
901
+ retrack(ctx.bodyFn, ctx.subscriber);
902
+ if (ctx.rerunPending) drainReruns(ctx);
855
903
  } finally {
856
- running = false;
857
- rerunPending = false;
904
+ ctx.running = false;
905
+ ctx.rerunPending = false;
858
906
  }
859
907
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
860
908
  if (hook) hook.emit("effect:create", { effectFn });
861
- let disposed = false;
862
- return () => {
863
- if (disposed) return;
864
- disposed = true;
865
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
866
- if (h) {
867
- try {
868
- h.emit("effect:destroy", { effectFn });
869
- } catch {
870
- }
871
- }
872
- try {
873
- runUserCleanups();
874
- } catch (err) {
875
- if (typeof console !== "undefined") {
876
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
877
- }
878
- }
879
- try {
880
- cleanupHandle();
881
- } catch (err) {
882
- if (typeof console !== "undefined") {
883
- console.warn("[SibuJS effect] dispose threw:", err);
884
- }
885
- }
886
- };
909
+ return () => disposeEffect(ctx);
887
910
  }
888
911
 
889
912
  // src/reactivity/batch.ts
@@ -920,32 +943,64 @@ function flushBatch() {
920
943
  var _g2 = globalThis;
921
944
  var _isDev3 = isDev();
922
945
  function signal(initial, options) {
923
- const state = { value: initial };
946
+ const state = {
947
+ value: initial,
948
+ __v: 0,
949
+ __sc: 0,
950
+ subsHead: null,
951
+ subsTail: null,
952
+ __activeNode: null,
953
+ __name: void 0
954
+ };
924
955
  const debugName = _isDev3 ? options?.name : void 0;
925
956
  const equalsFn = options?.equals;
926
- if (debugName) {
927
- state.__name = debugName;
928
- }
957
+ if (debugName) state.__name = debugName;
929
958
  function get() {
930
959
  recordDependency(state);
931
960
  return state.value;
932
961
  }
933
962
  get.__signal = state;
934
963
  if (debugName) get.__name = debugName;
935
- function set(next) {
936
- const newValue = typeof next === "function" ? next(state.value) : next;
937
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
938
- if (_isDev3) {
939
- const oldValue = state.value;
964
+ let set;
965
+ if (equalsFn) {
966
+ set = (next) => {
967
+ const prev = state.value;
968
+ const newValue = typeof next === "function" ? next(prev) : next;
969
+ if (equalsFn(prev, newValue)) return;
940
970
  state.value = newValue;
971
+ state.__v++;
972
+ if (_isDev3) {
973
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
974
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
975
+ }
976
+ if (!enqueueBatchedSignal(state)) {
977
+ notifySubscribers(state);
978
+ }
979
+ };
980
+ } else if (_isDev3) {
981
+ set = (next) => {
982
+ const prev = state.value;
983
+ const newValue = typeof next === "function" ? next(prev) : next;
984
+ if (Object.is(newValue, prev)) return;
985
+ state.value = newValue;
986
+ state.__v++;
941
987
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
942
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
943
- } else {
988
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
989
+ if (!enqueueBatchedSignal(state)) {
990
+ notifySubscribers(state);
991
+ }
992
+ };
993
+ } else {
994
+ set = (next) => {
995
+ const prev = state.value;
996
+ const newValue = typeof next === "function" ? next(prev) : next;
997
+ if (Object.is(newValue, prev)) return;
944
998
  state.value = newValue;
945
- }
946
- if (!enqueueBatchedSignal(state)) {
947
- notifySubscribers(state);
948
- }
999
+ state.__v++;
1000
+ if (!enqueueBatchedSignal(state)) {
1001
+ notifySubscribers(state);
1002
+ }
1003
+ };
949
1004
  }
950
1005
  if (_isDev3) {
951
1006
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -9252,13 +9307,14 @@ function initDevTools(config) {
9252
9307
  } catch {
9253
9308
  value = "<error>";
9254
9309
  }
9255
- const subs = node.ref?.__s;
9256
9310
  result.push({
9257
9311
  id: node.id,
9258
9312
  name: node.name,
9259
9313
  type: node.type,
9260
9314
  value,
9261
- subscriberCount: subs instanceof Set ? subs.size : 0
9315
+ // __sc is the O(1) subscriber count maintained by the reactivity
9316
+ // core (track.ts) on every linked-list splice.
9317
+ subscriberCount: node.ref?.__sc ?? 0
9262
9318
  });
9263
9319
  }
9264
9320
  return result;
@@ -9399,14 +9455,13 @@ function initDevTools(config) {
9399
9455
  }
9400
9456
  const fullVal = val;
9401
9457
  const shortVal = val.length > 80 ? `${val.substring(0, 80)}...` : val;
9402
- const subs = node.ref?.__s;
9403
9458
  sArr.push({
9404
9459
  id: node.id,
9405
9460
  n: node.name,
9406
9461
  tp: node.type,
9407
9462
  v: shortVal,
9408
9463
  fv: fullVal,
9409
- sc: subs instanceof Set ? subs.size : 0
9464
+ sc: node.ref?.__sc ?? 0
9410
9465
  });
9411
9466
  }
9412
9467
  function walkElement(el, depth) {
@@ -10033,32 +10088,24 @@ function startMeasure(profiler) {
10033
10088
  }
10034
10089
 
10035
10090
  // src/devtools/introspect.ts
10036
- var SUBS2 = "__s";
10037
10091
  function getSignalName(getter) {
10038
10092
  return getter.__name;
10039
10093
  }
10040
- function getSubscriberCount(getter) {
10094
+ function getSubscriberCount2(getter) {
10041
10095
  const signal2 = getter.__signal;
10042
10096
  if (!signal2) return 0;
10043
- const subs = signal2[SUBS2];
10044
- return subs ? subs.size : 0;
10097
+ return getSubscriberCount(signal2);
10045
10098
  }
10046
10099
  function getDependencies(subscriberFn) {
10047
- const fn = subscriberFn;
10048
- const singleDep = fn._dep;
10049
- if (singleDep !== void 0) return [singleDep];
10050
- const deps = fn._deps;
10051
- if (!deps) return [];
10052
- return deps instanceof Map ? Array.from(deps.keys()) : Array.from(deps);
10100
+ return getSubscriberDeps(subscriberFn);
10053
10101
  }
10054
10102
  function inspectSignal(getter) {
10055
10103
  const signal2 = getter.__signal;
10056
10104
  if (!signal2) return null;
10057
- const subs = signal2[SUBS2];
10058
10105
  return {
10059
10106
  name: getter.__name,
10060
10107
  signal: signal2,
10061
- subscriberCount: subs ? subs.size : 0
10108
+ subscriberCount: getSubscriberCount(signal2)
10062
10109
  };
10063
10110
  }
10064
10111
  function walkDependencyGraph(getter, maxDepth = 10, visited = /* @__PURE__ */ new WeakSet()) {
@@ -10067,24 +10114,21 @@ function walkDependencyGraph(getter, maxDepth = 10, visited = /* @__PURE__ */ ne
10067
10114
  return { name: getSignalName(getter), subscribers: 0, downstream: [] };
10068
10115
  }
10069
10116
  visited.add(signal2);
10070
- const subs = signal2[SUBS2];
10071
10117
  const downstream = [];
10072
- if (subs) {
10073
- for (const sub2 of subs) {
10074
- const subSig = sub2._sig;
10075
- if (subSig && !visited.has(subSig)) {
10076
- const subName = subSig.__name;
10077
- const fakeGetter = (() => void 0);
10078
- const tag = fakeGetter;
10079
- tag.__signal = subSig;
10080
- if (subName !== void 0) tag.__name = subName;
10081
- downstream.push(walkDependencyGraph(fakeGetter, maxDepth - 1, visited));
10082
- }
10118
+ forEachSubscriber(signal2, (sub2) => {
10119
+ const subSig = sub2._sig;
10120
+ if (subSig && !visited.has(subSig)) {
10121
+ const subName = subSig.__name;
10122
+ const fakeGetter = (() => void 0);
10123
+ const tag = fakeGetter;
10124
+ tag.__signal = subSig;
10125
+ if (subName !== void 0) tag.__name = subName;
10126
+ downstream.push(walkDependencyGraph(fakeGetter, maxDepth - 1, visited));
10083
10127
  }
10084
- }
10128
+ });
10085
10129
  return {
10086
10130
  name: getSignalName(getter),
10087
- subscribers: subs ? subs.size : 0,
10131
+ subscribers: getSubscriberCount(signal2),
10088
10132
  downstream
10089
10133
  };
10090
10134
  }