sibujs 2.0.0 → 2.2.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 +369 -276
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +411 -300
  4. package/dist/build.js +10 -10
  5. package/dist/cdn.global.js +8 -8
  6. package/dist/{chunk-JA6667UN.js → chunk-2JQUV4Y3.js} +4 -4
  7. package/dist/{chunk-3NSGB5JN.js → chunk-2KM2724A.js} +2 -2
  8. package/dist/{chunk-52YJLLRO.js → chunk-4YTVESDX.js} +1 -1
  9. package/dist/chunk-5WD7BYTZ.js +152 -0
  10. package/dist/{chunk-CC65Y57T.js → chunk-6QZO7MMG.js} +48 -16
  11. package/dist/{chunk-54EDRCEF.js → chunk-DF3GTP4Q.js} +7 -2
  12. package/dist/{chunk-ND2664SF.js → chunk-J63GPPCJ.js} +13 -9
  13. package/dist/{chunk-O2MNQFLP.js → chunk-KH4OE6WY.js} +5 -5
  14. package/dist/{chunk-3LR7GLWQ.js → chunk-KZA7ANXP.js} +3 -3
  15. package/dist/chunk-L4DAT4WU.js +400 -0
  16. package/dist/{chunk-WOMYAHHI.js → chunk-L52H775O.js} +4 -4
  17. package/dist/{chunk-ITX6OO3F.js → chunk-NEWH4O5U.js} +1 -1
  18. package/dist/{chunk-7JDB7I65.js → chunk-RJIRT46U.js} +4 -4
  19. package/dist/{chunk-KLRMB5ZS.js → chunk-STFTTMO2.js} +2 -2
  20. package/dist/{chunk-DFPFITST.js → chunk-UKMXT5T6.js} +1 -1
  21. package/dist/{chunk-SAHNHTFC.js → chunk-V65KTDZW.js} +3 -3
  22. package/dist/{chunk-R73P76YZ.js → chunk-VSNLICTS.js} +1 -1
  23. package/dist/{chunk-MIUAXB7K.js → chunk-XDKP4T7G.js} +2 -2
  24. package/dist/{chunk-JXMMDLBY.js → chunk-XVYB3J6C.js} +27 -29
  25. package/dist/{chunk-GTBNNBJ6.js → chunk-YMOIAHWA.js} +1 -1
  26. package/dist/data.cjs +382 -274
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +398 -284
  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 +382 -274
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +421 -299
  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 +413 -300
  39. package/dist/index.d.cts +16 -11
  40. package/dist/index.d.ts +16 -11
  41. package/dist/index.js +14 -10
  42. package/dist/{introspect-cY2pg9pW.d.ts → introspect-BZWKvQUZ.d.ts} +2 -1
  43. package/dist/{introspect-BWNjNw64.d.cts → introspect-DsJlDD2T.d.cts} +2 -1
  44. package/dist/motion.cjs +189 -149
  45. package/dist/motion.js +3 -3
  46. package/dist/patterns.cjs +382 -274
  47. package/dist/patterns.js +5 -5
  48. package/dist/performance.cjs +360 -260
  49. package/dist/performance.js +4 -4
  50. package/dist/plugins.cjs +376 -257
  51. package/dist/plugins.js +6 -6
  52. package/dist/ssr.cjs +383 -271
  53. package/dist/ssr.js +7 -7
  54. package/dist/testing.cjs +168 -109
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +373 -258
  57. package/dist/ui.js +6 -6
  58. package/dist/widgets.cjs +382 -274
  59. package/dist/widgets.js +6 -6
  60. package/package.json +1 -1
  61. package/dist/chunk-HB24TBAF.js +0 -121
  62. package/dist/chunk-VLPPXTYG.js +0 -332
package/dist/index.cjs CHANGED
@@ -169,6 +169,7 @@ __export(index_exports, {
169
169
  registerComponent: () => registerComponent,
170
170
  registerDisposer: () => registerDisposer,
171
171
  resolveComponent: () => resolveComponent,
172
+ retrack: () => retrack,
172
173
  rp: () => rp,
173
174
  rt: () => rt,
174
175
  ruby: () => ruby,
@@ -179,6 +180,7 @@ __export(index_exports, {
179
180
  section: () => section,
180
181
  select: () => select,
181
182
  setGlobalErrorHandler: () => setGlobalErrorHandler,
183
+ setMaxDrainIterations: () => setMaxDrainIterations,
182
184
  show: () => show,
183
185
  signal: () => signal,
184
186
  slot: () => slot,
@@ -316,11 +318,89 @@ function isUrlAttribute(attr) {
316
318
 
317
319
  // src/reactivity/track.ts
318
320
  var _isDev2 = isDev();
319
- var subscriberStack = new Array(32);
320
- var stackCapacity = 32;
321
- var stackTop = -1;
321
+ var POOL_MAX = 4096;
322
+ var nodePool = [];
323
+ function createNode() {
324
+ return {
325
+ sig: null,
326
+ sub: null,
327
+ epoch: 0,
328
+ sigPrev: null,
329
+ sigNext: null,
330
+ subPrev: null,
331
+ subNext: null,
332
+ prevActive: null
333
+ };
334
+ }
335
+ function allocNode(sig, sub2, epoch) {
336
+ const n = nodePool.pop();
337
+ if (n) {
338
+ n.sig = sig;
339
+ n.sub = sub2;
340
+ n.epoch = epoch;
341
+ return n;
342
+ }
343
+ const fresh = createNode();
344
+ fresh.sig = sig;
345
+ fresh.sub = sub2;
346
+ fresh.epoch = epoch;
347
+ return fresh;
348
+ }
349
+ function freeNode(node) {
350
+ node.sig = null;
351
+ node.sub = null;
352
+ node.sigPrev = null;
353
+ node.sigNext = null;
354
+ node.subPrev = null;
355
+ node.subNext = null;
356
+ node.prevActive = null;
357
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
358
+ }
359
+ function linkSignal(sig, node) {
360
+ const oldHead = sig.subsHead ?? null;
361
+ node.sigPrev = null;
362
+ node.sigNext = oldHead;
363
+ if (oldHead) oldHead.sigPrev = node;
364
+ else sig.subsTail = node;
365
+ sig.subsHead = node;
366
+ sig.__sc = (sig.__sc ?? 0) + 1;
367
+ }
368
+ function unlinkSignal(node) {
369
+ const sig = node.sig;
370
+ if (!sig) return;
371
+ const prev = node.sigPrev;
372
+ const next = node.sigNext;
373
+ if (prev) prev.sigNext = next;
374
+ else sig.subsHead = next;
375
+ if (next) next.sigPrev = prev;
376
+ else sig.subsTail = prev;
377
+ sig.__sc = (sig.__sc ?? 1) - 1;
378
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
379
+ if (sig.__sc === 0) {
380
+ sig.subsHead = null;
381
+ sig.subsTail = null;
382
+ }
383
+ }
384
+ function linkSub(sub2, node) {
385
+ const oldTail = sub2.depsTail ?? null;
386
+ node.subPrev = oldTail;
387
+ node.subNext = null;
388
+ if (oldTail) oldTail.subNext = node;
389
+ else sub2.depsHead = node;
390
+ sub2.depsTail = node;
391
+ }
392
+ function unlinkSub(node) {
393
+ const sub2 = node.sub;
394
+ if (!sub2) return;
395
+ const prev = node.subPrev;
396
+ const next = node.subNext;
397
+ if (prev) prev.subNext = next;
398
+ else sub2.depsHead = next;
399
+ if (next) next.subPrev = prev;
400
+ else sub2.depsTail = prev;
401
+ }
322
402
  var currentSubscriber = null;
323
- var SUBS = "__s";
403
+ var suspendSavedSub = null;
324
404
  var notifyDepth = 0;
325
405
  var pendingQueue = [];
326
406
  var pendingSet = /* @__PURE__ */ new Set();
@@ -334,41 +414,9 @@ function safeInvoke(sub2) {
334
414
  }
335
415
  var suspendDepth = 0;
336
416
  var trackingSuspended = false;
337
- function retrack(effectFn, subscriber) {
338
- const prev = currentSubscriber;
339
- currentSubscriber = subscriber;
340
- try {
341
- effectFn();
342
- } finally {
343
- currentSubscriber = prev;
344
- }
345
- }
346
- function track(effectFn, subscriber) {
347
- if (!subscriber) subscriber = effectFn;
348
- cleanup(subscriber);
349
- ++stackTop;
350
- if (stackTop >= stackCapacity) {
351
- stackCapacity *= 2;
352
- subscriberStack.length = stackCapacity;
353
- }
354
- subscriberStack[stackTop] = subscriber;
355
- currentSubscriber = subscriber;
356
- try {
357
- effectFn();
358
- } finally {
359
- stackTop--;
360
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
361
- }
362
- return () => cleanup(subscriber);
363
- }
364
417
  function suspendTracking() {
365
418
  if (suspendDepth === 0) {
366
- ++stackTop;
367
- if (stackTop >= stackCapacity) {
368
- stackCapacity *= 2;
369
- subscriberStack.length = stackCapacity;
370
- }
371
- subscriberStack[stackTop] = null;
419
+ suspendSavedSub = currentSubscriber;
372
420
  currentSubscriber = null;
373
421
  trackingSuspended = true;
374
422
  }
@@ -377,8 +425,8 @@ function suspendTracking() {
377
425
  function resumeTracking() {
378
426
  suspendDepth--;
379
427
  if (suspendDepth === 0) {
380
- stackTop--;
381
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
428
+ currentSubscriber = suspendSavedSub;
429
+ suspendSavedSub = null;
382
430
  trackingSuspended = false;
383
431
  }
384
432
  }
@@ -390,65 +438,141 @@ function untracked(fn) {
390
438
  resumeTracking();
391
439
  }
392
440
  }
441
+ var subscriberEpochCounter = 0;
442
+ function retrack(effectFn, subscriber) {
443
+ const prev = currentSubscriber;
444
+ currentSubscriber = subscriber;
445
+ const sub2 = subscriber;
446
+ const epoch = ++subscriberEpochCounter;
447
+ sub2._epoch = epoch;
448
+ sub2._structDirty = false;
449
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
450
+ const sig = n.sig;
451
+ n.prevActive = sig.__activeNode ?? null;
452
+ sig.__activeNode = n;
453
+ }
454
+ try {
455
+ effectFn();
456
+ } finally {
457
+ currentSubscriber = prev;
458
+ let node = sub2.depsHead ?? null;
459
+ while (node !== null) {
460
+ const next = node.subNext;
461
+ const sig = node.sig;
462
+ sig.__activeNode = node.prevActive;
463
+ node.prevActive = null;
464
+ if (node.epoch !== epoch) {
465
+ unlinkSub(node);
466
+ unlinkSignal(node);
467
+ freeNode(node);
468
+ }
469
+ node = next;
470
+ }
471
+ }
472
+ }
473
+ function track(effectFn, subscriber) {
474
+ if (!subscriber) subscriber = effectFn;
475
+ cleanup(subscriber);
476
+ const prev = currentSubscriber;
477
+ currentSubscriber = subscriber;
478
+ try {
479
+ effectFn();
480
+ } finally {
481
+ currentSubscriber = prev;
482
+ const sub3 = subscriber;
483
+ for (let n = sub3.depsHead ?? null; n !== null; n = n.subNext) {
484
+ const sig = n.sig;
485
+ sig.__activeNode = n.prevActive;
486
+ n.prevActive = null;
487
+ }
488
+ }
489
+ const sub2 = subscriber;
490
+ return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
491
+ }
393
492
  function recordDependency(signal2) {
394
493
  if (!currentSubscriber) return;
395
494
  const sub2 = currentSubscriber;
396
- if (sub2._dep === signal2) return;
397
- const deps = sub2._deps;
398
- if (deps) {
399
- if (deps.has(signal2)) return;
400
- deps.add(signal2);
401
- } else if (sub2._dep !== void 0) {
402
- const set = /* @__PURE__ */ new Set();
403
- set.add(sub2._dep);
404
- set.add(signal2);
405
- sub2._deps = set;
406
- sub2._dep = void 0;
407
- } else {
408
- sub2._dep = signal2;
495
+ const sig = signal2;
496
+ const epoch = sub2._epoch ?? 0;
497
+ const active = sig.__activeNode ?? null;
498
+ if (active !== null && active.sub === sub2) {
499
+ active.epoch = epoch;
500
+ return;
409
501
  }
410
- let subs = signal2[SUBS];
411
- if (!subs) {
412
- subs = /* @__PURE__ */ new Set();
413
- signal2[SUBS] = subs;
502
+ const node = allocNode(signal2, sub2, epoch);
503
+ node.prevActive = active;
504
+ sig.__activeNode = node;
505
+ linkSub(sub2, node);
506
+ linkSignal(sig, node);
507
+ sub2._structDirty = true;
508
+ }
509
+ function cleanup(subscriber) {
510
+ const sub2 = subscriber;
511
+ let node = sub2.depsHead ?? null;
512
+ sub2.depsHead = null;
513
+ sub2.depsTail = null;
514
+ while (node) {
515
+ const next = node.subNext;
516
+ unlinkSignal(node);
517
+ freeNode(node);
518
+ node = next;
414
519
  }
415
- subs.add(currentSubscriber);
416
- if (subs.size === 1) {
417
- signal2.__f = currentSubscriber;
418
- } else if (signal2.__f !== void 0) {
419
- signal2.__f = void 0;
520
+ }
521
+ var maxSubscriberRepeats = 50;
522
+ var maxDrainIterations = 1e6;
523
+ var drainEpoch = 0;
524
+ function setMaxDrainIterations(n) {
525
+ const prev = maxDrainIterations;
526
+ if (Number.isFinite(n) && n > 0) maxDrainIterations = Math.floor(n);
527
+ return prev;
528
+ }
529
+ function tickRepeat(sub2) {
530
+ const s2 = sub2;
531
+ if (s2._runEpoch !== drainEpoch) {
532
+ s2._runEpoch = drainEpoch;
533
+ s2._runs = 1;
534
+ return false;
535
+ }
536
+ s2._runs = (s2._runs ?? 0) + 1;
537
+ return s2._runs > maxSubscriberRepeats;
538
+ }
539
+ function cycleError(sub2) {
540
+ if (typeof console !== "undefined") {
541
+ const name = sub2.__name ?? "<unnamed>";
542
+ console.error(
543
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
544
+ );
420
545
  }
421
546
  }
422
- function queueSignalNotification(signal2) {
423
- const subs = signal2[SUBS];
424
- if (!subs) return;
425
- for (const sub2 of subs) {
426
- if (sub2._c) {
427
- propagateDirty(sub2);
428
- } else if (!pendingSet.has(sub2)) {
429
- pendingSet.add(sub2);
430
- pendingQueue.push(sub2);
547
+ function absoluteDrainError() {
548
+ if (typeof console !== "undefined") {
549
+ console.error(
550
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
551
+ );
552
+ }
553
+ }
554
+ function drainQueue() {
555
+ let i2 = 0;
556
+ while (i2 < pendingQueue.length) {
557
+ if (i2 >= maxDrainIterations) {
558
+ absoluteDrainError();
559
+ break;
560
+ }
561
+ const sub2 = pendingQueue[i2++];
562
+ if (tickRepeat(sub2)) {
563
+ cycleError(sub2);
564
+ break;
431
565
  }
566
+ pendingSet.delete(sub2);
567
+ safeInvoke(sub2);
432
568
  }
433
569
  }
434
- var maxDrainIterations = 1e5;
435
570
  function drainNotificationQueue() {
436
571
  if (notifyDepth > 0) return;
437
572
  notifyDepth++;
573
+ drainEpoch++;
438
574
  try {
439
- let i2 = 0;
440
- while (i2 < pendingQueue.length) {
441
- if (i2 >= maxDrainIterations) {
442
- if (typeof console !== "undefined") {
443
- console.error(
444
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
445
- );
446
- }
447
- break;
448
- }
449
- safeInvoke(pendingQueue[i2]);
450
- i2++;
451
- }
575
+ drainQueue();
452
576
  } finally {
453
577
  notifyDepth--;
454
578
  if (notifyDepth === 0) {
@@ -466,131 +590,82 @@ function propagateDirty(sub2) {
466
590
  stack.push(rootSig);
467
591
  while (stack.length > baseLen) {
468
592
  const sig = stack.pop();
469
- const first = sig.__f;
470
- if (first) {
471
- if (first._c) {
472
- const nSig = first._sig;
473
- if (!nSig._d) {
474
- nSig._d = true;
475
- stack.push(nSig);
593
+ let node = sig.subsHead ?? null;
594
+ while (node) {
595
+ const s2 = node.sub;
596
+ if (s2) {
597
+ if (s2._c) {
598
+ const nSig = s2._sig;
599
+ if (nSig) {
600
+ if (!nSig._d) {
601
+ nSig._d = true;
602
+ stack.push(nSig);
603
+ }
604
+ } else {
605
+ s2();
606
+ }
607
+ } else if (!pendingSet.has(s2)) {
608
+ pendingSet.add(s2);
609
+ pendingQueue.push(s2);
476
610
  }
477
- } else if (!pendingSet.has(first)) {
478
- pendingSet.add(first);
479
- pendingQueue.push(first);
480
611
  }
481
- continue;
612
+ node = node.sigNext;
482
613
  }
483
- const subs = sig[SUBS];
484
- if (!subs) continue;
485
- for (const s2 of subs) {
614
+ }
615
+ }
616
+ function queueSignalNotification(signal2) {
617
+ const sig = signal2;
618
+ let node = sig.subsHead ?? null;
619
+ while (node) {
620
+ const s2 = node.sub;
621
+ if (s2) {
486
622
  if (s2._c) {
487
- const nSig = s2._sig;
488
- if (nSig && !nSig._d) {
489
- nSig._d = true;
490
- stack.push(nSig);
491
- } else if (!nSig) {
492
- s2();
493
- }
623
+ propagateDirty(s2);
494
624
  } else if (!pendingSet.has(s2)) {
495
625
  pendingSet.add(s2);
496
626
  pendingQueue.push(s2);
497
627
  }
498
628
  }
629
+ node = node.sigNext;
499
630
  }
500
631
  }
501
632
  function notifySubscribers(signal2) {
502
- const first = signal2.__f;
503
- if (first) {
504
- if (notifyDepth > 0) {
505
- if (first._c) {
506
- propagateDirty(first);
507
- } else if (!pendingSet.has(first)) {
508
- pendingSet.add(first);
509
- pendingQueue.push(first);
510
- }
511
- return;
512
- }
513
- notifyDepth++;
514
- try {
515
- if (first._c) {
516
- propagateDirty(first);
517
- } else {
518
- safeInvoke(first);
519
- }
520
- let i2 = 0;
521
- while (i2 < pendingQueue.length) {
522
- if (i2 >= maxDrainIterations) {
523
- if (typeof console !== "undefined") {
524
- console.error(
525
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
526
- );
527
- }
528
- break;
529
- }
530
- safeInvoke(pendingQueue[i2]);
531
- i2++;
532
- }
533
- } finally {
534
- notifyDepth--;
535
- if (notifyDepth === 0) {
536
- pendingQueue.length = 0;
537
- pendingSet.clear();
538
- }
539
- }
540
- return;
541
- }
542
- const subs = signal2[SUBS];
543
- if (!subs || subs.size === 0) return;
633
+ const sig = signal2;
634
+ const head2 = sig.subsHead;
635
+ if (!head2) return;
544
636
  if (notifyDepth > 0) {
545
- for (const sub2 of subs) {
546
- if (sub2._c) {
547
- propagateDirty(sub2);
548
- } else if (!pendingSet.has(sub2)) {
549
- pendingSet.add(sub2);
550
- pendingQueue.push(sub2);
637
+ let node = head2;
638
+ while (node) {
639
+ const s2 = node.sub;
640
+ if (s2) {
641
+ if (s2._c) {
642
+ propagateDirty(s2);
643
+ } else if (!pendingSet.has(s2)) {
644
+ pendingSet.add(s2);
645
+ pendingQueue.push(s2);
646
+ }
551
647
  }
648
+ node = node.sigNext;
552
649
  }
553
650
  return;
554
651
  }
555
652
  notifyDepth++;
653
+ drainEpoch++;
556
654
  try {
557
- let directCount = 0;
558
- let hasComputedSub = false;
559
- for (const sub2 of subs) {
560
- if (sub2._c) hasComputedSub = true;
561
- pendingQueue[directCount++] = sub2;
562
- }
563
- if (!hasComputedSub) {
564
- for (let i3 = 0; i3 < directCount; i3++) {
565
- safeInvoke(pendingQueue[i3]);
566
- }
567
- } else {
568
- for (let i3 = 0; i3 < directCount; i3++) {
569
- if (pendingQueue[i3]._c) {
570
- propagateDirty(pendingQueue[i3]);
571
- }
572
- }
573
- for (let i3 = 0; i3 < directCount; i3++) {
574
- const sub2 = pendingQueue[i3];
575
- if (!sub2._c && !pendingSet.has(sub2)) {
576
- pendingSet.add(sub2);
577
- safeInvoke(sub2);
578
- }
579
- }
580
- }
581
- let i2 = directCount;
582
- while (i2 < pendingQueue.length) {
583
- if (i2 - directCount >= maxDrainIterations) {
584
- if (typeof console !== "undefined") {
585
- console.error(
586
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
587
- );
655
+ let node = head2;
656
+ while (node) {
657
+ const s2 = node.sub;
658
+ if (s2) {
659
+ if (s2._c) {
660
+ propagateDirty(s2);
661
+ } else if (!pendingSet.has(s2)) {
662
+ pendingSet.add(s2);
663
+ pendingQueue.push(s2);
588
664
  }
589
- break;
590
665
  }
591
- safeInvoke(pendingQueue[i2]);
592
- i2++;
666
+ node = node.sigNext;
593
667
  }
668
+ drainQueue();
594
669
  } finally {
595
670
  notifyDepth--;
596
671
  if (notifyDepth === 0) {
@@ -599,37 +674,6 @@ function notifySubscribers(signal2) {
599
674
  }
600
675
  }
601
676
  }
602
- function cleanup(subscriber) {
603
- const sub2 = subscriber;
604
- const singleDep = sub2._dep;
605
- if (singleDep !== void 0) {
606
- const subs = singleDep[SUBS];
607
- if (subs) {
608
- subs.delete(subscriber);
609
- if (singleDep.__f === subscriber) {
610
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
611
- } else if (subs.size === 1 && singleDep.__f === void 0) {
612
- singleDep.__f = subs.values().next().value;
613
- }
614
- }
615
- sub2._dep = void 0;
616
- return;
617
- }
618
- const deps = sub2._deps;
619
- if (!deps || deps.size === 0) return;
620
- for (const signal2 of deps) {
621
- const subs = signal2[SUBS];
622
- if (subs) {
623
- subs.delete(subscriber);
624
- if (signal2.__f === subscriber) {
625
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
626
- } else if (subs.size === 1 && signal2.__f === void 0) {
627
- signal2.__f = subs.values().next().value;
628
- }
629
- }
630
- }
631
- deps.clear();
632
- }
633
677
 
634
678
  // src/reactivity/bindAttribute.ts
635
679
  var _isDev3 = isDev();
@@ -2269,32 +2313,64 @@ function flushBatch() {
2269
2313
  var _g = globalThis;
2270
2314
  var _isDev9 = isDev();
2271
2315
  function signal(initial, options) {
2272
- const state = { value: initial };
2316
+ const state = {
2317
+ value: initial,
2318
+ __v: 0,
2319
+ __sc: 0,
2320
+ subsHead: null,
2321
+ subsTail: null,
2322
+ __activeNode: null,
2323
+ __name: void 0
2324
+ };
2273
2325
  const debugName = _isDev9 ? options?.name : void 0;
2274
2326
  const equalsFn = options?.equals;
2275
- if (debugName) {
2276
- state.__name = debugName;
2277
- }
2327
+ if (debugName) state.__name = debugName;
2278
2328
  function get() {
2279
2329
  recordDependency(state);
2280
2330
  return state.value;
2281
2331
  }
2282
2332
  get.__signal = state;
2283
2333
  if (debugName) get.__name = debugName;
2284
- function set(next) {
2285
- const newValue = typeof next === "function" ? next(state.value) : next;
2286
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
2287
- if (_isDev9) {
2288
- const oldValue = state.value;
2334
+ let set;
2335
+ if (equalsFn) {
2336
+ set = (next) => {
2337
+ const prev = state.value;
2338
+ const newValue = typeof next === "function" ? next(prev) : next;
2339
+ if (equalsFn(prev, newValue)) return;
2340
+ state.value = newValue;
2341
+ state.__v++;
2342
+ if (_isDev9) {
2343
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2344
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
2345
+ }
2346
+ if (!enqueueBatchedSignal(state)) {
2347
+ notifySubscribers(state);
2348
+ }
2349
+ };
2350
+ } else if (_isDev9) {
2351
+ set = (next) => {
2352
+ const prev = state.value;
2353
+ const newValue = typeof next === "function" ? next(prev) : next;
2354
+ if (Object.is(newValue, prev)) return;
2289
2355
  state.value = newValue;
2356
+ state.__v++;
2290
2357
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2291
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
2292
- } else {
2358
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
2359
+ if (!enqueueBatchedSignal(state)) {
2360
+ notifySubscribers(state);
2361
+ }
2362
+ };
2363
+ } else {
2364
+ set = (next) => {
2365
+ const prev = state.value;
2366
+ const newValue = typeof next === "function" ? next(prev) : next;
2367
+ if (Object.is(newValue, prev)) return;
2293
2368
  state.value = newValue;
2294
- }
2295
- if (!enqueueBatchedSignal(state)) {
2296
- notifySubscribers(state);
2297
- }
2369
+ state.__v++;
2370
+ if (!enqueueBatchedSignal(state)) {
2371
+ notifySubscribers(state);
2372
+ }
2373
+ };
2298
2374
  }
2299
2375
  if (_isDev9) {
2300
2376
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -2378,92 +2454,122 @@ function on(deps, handler) {
2378
2454
  }
2379
2455
  };
2380
2456
  }
2457
+ var MAX_RERUNS = 100;
2458
+ function flushUserCleanups(ctx) {
2459
+ const list = ctx.userCleanups;
2460
+ if (list.length === 0) return;
2461
+ ctx.userCleanups = [];
2462
+ for (let i2 = list.length - 1; i2 >= 0; i2--) {
2463
+ try {
2464
+ list[i2]();
2465
+ } catch (err) {
2466
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
2467
+ }
2468
+ }
2469
+ }
2470
+ function drainReruns(ctx) {
2471
+ let reruns = 1;
2472
+ do {
2473
+ ctx.rerunPending = false;
2474
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2475
+ retrack(ctx.bodyFn, ctx.subscriber);
2476
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
2477
+ if (ctx.rerunPending) {
2478
+ ctx.rerunPending = false;
2479
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
2480
+ console.error(
2481
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
2482
+ );
2483
+ }
2484
+ }
2485
+ }
2486
+ function disposeEffect(ctx) {
2487
+ if (ctx.disposed) return;
2488
+ ctx.disposed = true;
2489
+ const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2490
+ if (h) {
2491
+ try {
2492
+ h.emit("effect:destroy", { effectFn: ctx.fn });
2493
+ } catch {
2494
+ }
2495
+ }
2496
+ try {
2497
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2498
+ } catch (err) {
2499
+ if (typeof console !== "undefined") {
2500
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
2501
+ }
2502
+ }
2503
+ try {
2504
+ cleanup(ctx.subscriber);
2505
+ } catch (err) {
2506
+ if (typeof console !== "undefined") {
2507
+ console.warn("[SibuJS effect] dispose threw:", err);
2508
+ }
2509
+ }
2510
+ }
2381
2511
  function effect(effectFn, options) {
2382
2512
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
2383
2513
  if (isSSR()) return () => {
2384
2514
  };
2385
- const onError = options?.onError;
2386
- let userCleanups = [];
2387
- const onCleanup2 = (fn) => {
2388
- userCleanups.push(fn);
2515
+ const ctx = {
2516
+ fn: effectFn,
2517
+ onError: options?.onError,
2518
+ userCleanups: [],
2519
+ running: false,
2520
+ rerunPending: false,
2521
+ disposed: false,
2522
+ onCleanup: null,
2523
+ subscriber: null,
2524
+ bodyFn: null
2389
2525
  };
2390
- const runUserCleanups = () => {
2391
- if (userCleanups.length === 0) return;
2392
- const list = userCleanups;
2393
- userCleanups = [];
2394
- for (let i2 = list.length - 1; i2 >= 0; i2--) {
2395
- try {
2396
- list[i2]();
2397
- } catch (err) {
2398
- if (typeof console !== "undefined") {
2399
- console.warn("[SibuJS effect] onCleanup threw:", err);
2400
- }
2401
- }
2402
- }
2526
+ ctx.onCleanup = (fn) => {
2527
+ ctx.userCleanups.push(fn);
2403
2528
  };
2404
- const invokeBody = () => effectFn(onCleanup2);
2405
- const wrappedFn = onError ? () => {
2529
+ const onErrorCaptured = ctx.onError;
2530
+ ctx.bodyFn = onErrorCaptured ? () => {
2406
2531
  try {
2407
- invokeBody();
2532
+ ctx.fn(ctx.onCleanup);
2408
2533
  } catch (err) {
2409
- onError(err);
2534
+ onErrorCaptured(err);
2410
2535
  }
2411
- } : invokeBody;
2412
- let cleanupHandle = () => {
2536
+ } : () => {
2537
+ ctx.fn(ctx.onCleanup);
2413
2538
  };
2414
- let running = false;
2415
- const subscriber = () => {
2416
- if (running) {
2417
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
2418
- console.warn(
2419
- "[SibuJS] effect re-entered itself while running \u2014 the triggering update will be ignored. Wrap mutual writes in `batch()` or split the effect to avoid this."
2420
- );
2421
- }
2539
+ const sub2 = (() => {
2540
+ if (ctx.running) {
2541
+ ctx.rerunPending = true;
2422
2542
  return;
2423
2543
  }
2424
- running = true;
2544
+ ctx.running = true;
2425
2545
  try {
2426
- runUserCleanups();
2427
- cleanupHandle();
2428
- cleanupHandle = track(wrappedFn, subscriber);
2546
+ ctx.rerunPending = false;
2547
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
2548
+ retrack(ctx.bodyFn, sub2);
2549
+ if (ctx.rerunPending) drainReruns(ctx);
2429
2550
  } finally {
2430
- running = false;
2551
+ ctx.running = false;
2552
+ ctx.rerunPending = false;
2431
2553
  }
2432
- };
2433
- running = true;
2554
+ });
2555
+ sub2.depsHead = null;
2556
+ sub2.depsTail = null;
2557
+ sub2._epoch = 0;
2558
+ sub2._structDirty = false;
2559
+ sub2._runEpoch = 0;
2560
+ sub2._runs = 0;
2561
+ ctx.subscriber = sub2;
2562
+ ctx.running = true;
2434
2563
  try {
2435
- cleanupHandle = track(wrappedFn, subscriber);
2564
+ retrack(ctx.bodyFn, ctx.subscriber);
2565
+ if (ctx.rerunPending) drainReruns(ctx);
2436
2566
  } finally {
2437
- running = false;
2567
+ ctx.running = false;
2568
+ ctx.rerunPending = false;
2438
2569
  }
2439
2570
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2440
2571
  if (hook) hook.emit("effect:create", { effectFn });
2441
- let disposed = false;
2442
- return () => {
2443
- if (disposed) return;
2444
- disposed = true;
2445
- const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2446
- if (h) {
2447
- try {
2448
- h.emit("effect:destroy", { effectFn });
2449
- } catch {
2450
- }
2451
- }
2452
- try {
2453
- runUserCleanups();
2454
- } catch (err) {
2455
- if (typeof console !== "undefined") {
2456
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
2457
- }
2458
- }
2459
- try {
2460
- cleanupHandle();
2461
- } catch (err) {
2462
- if (typeof console !== "undefined") {
2463
- console.warn("[SibuJS effect] dispose threw:", err);
2464
- }
2465
- }
2466
- };
2572
+ return () => disposeEffect(ctx);
2467
2573
  }
2468
2574
 
2469
2575
  // src/core/signals/derived.ts
@@ -2474,6 +2580,7 @@ function derived(getter, options) {
2474
2580
  const cs = {};
2475
2581
  cs._d = false;
2476
2582
  cs._g = getter;
2583
+ cs.__v = 0;
2477
2584
  const markDirty = () => {
2478
2585
  if (cs._d) return;
2479
2586
  cs._d = true;
@@ -2503,11 +2610,14 @@ function derived(getter, options) {
2503
2610
  evaluating = true;
2504
2611
  let threw = true;
2505
2612
  try {
2613
+ const prev = cs._v;
2506
2614
  retrack(() => {
2507
- cs._v = getter();
2615
+ const next = getter();
2616
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
2508
2617
  cs._d = false;
2509
2618
  threw = false;
2510
2619
  }, markDirty);
2620
+ if (!Object.is(prev, cs._v)) cs.__v++;
2511
2621
  } finally {
2512
2622
  evaluating = false;
2513
2623
  if (threw) cs._d = true;
@@ -2527,6 +2637,7 @@ function derived(getter, options) {
2527
2637
  cs._d = false;
2528
2638
  threw = false;
2529
2639
  }, markDirty);
2640
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
2530
2641
  } finally {
2531
2642
  evaluating = false;
2532
2643
  if (threw) cs._d = true;
@@ -4434,6 +4545,7 @@ function Loading(props = {}) {
4434
4545
  registerComponent,
4435
4546
  registerDisposer,
4436
4547
  resolveComponent,
4548
+ retrack,
4437
4549
  rp,
4438
4550
  rt,
4439
4551
  ruby,
@@ -4444,6 +4556,7 @@ function Loading(props = {}) {
4444
4556
  section,
4445
4557
  select,
4446
4558
  setGlobalErrorHandler,
4559
+ setMaxDrainIterations,
4447
4560
  show,
4448
4561
  signal,
4449
4562
  slot,