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/build.cjs CHANGED
@@ -1204,6 +1204,7 @@ __export(index_exports, {
1204
1204
  registerComponent: () => registerComponent,
1205
1205
  registerDisposer: () => registerDisposer,
1206
1206
  resolveComponent: () => resolveComponent,
1207
+ retrack: () => retrack,
1207
1208
  rp: () => rp,
1208
1209
  rt: () => rt,
1209
1210
  ruby: () => ruby,
@@ -1214,6 +1215,7 @@ __export(index_exports, {
1214
1215
  section: () => section,
1215
1216
  select: () => select,
1216
1217
  setGlobalErrorHandler: () => setGlobalErrorHandler,
1218
+ setMaxDrainIterations: () => setMaxDrainIterations,
1217
1219
  show: () => show,
1218
1220
  signal: () => signal,
1219
1221
  slot: () => slot,
@@ -1350,11 +1352,89 @@ function isUrlAttribute(attr) {
1350
1352
 
1351
1353
  // src/reactivity/track.ts
1352
1354
  var _isDev2 = isDev();
1353
- var subscriberStack = new Array(32);
1354
- var stackCapacity = 32;
1355
- var stackTop = -1;
1355
+ var POOL_MAX = 4096;
1356
+ var nodePool = [];
1357
+ function createNode() {
1358
+ return {
1359
+ sig: null,
1360
+ sub: null,
1361
+ epoch: 0,
1362
+ sigPrev: null,
1363
+ sigNext: null,
1364
+ subPrev: null,
1365
+ subNext: null,
1366
+ prevActive: null
1367
+ };
1368
+ }
1369
+ function allocNode(sig, sub2, epoch) {
1370
+ const n = nodePool.pop();
1371
+ if (n) {
1372
+ n.sig = sig;
1373
+ n.sub = sub2;
1374
+ n.epoch = epoch;
1375
+ return n;
1376
+ }
1377
+ const fresh = createNode();
1378
+ fresh.sig = sig;
1379
+ fresh.sub = sub2;
1380
+ fresh.epoch = epoch;
1381
+ return fresh;
1382
+ }
1383
+ function freeNode(node) {
1384
+ node.sig = null;
1385
+ node.sub = null;
1386
+ node.sigPrev = null;
1387
+ node.sigNext = null;
1388
+ node.subPrev = null;
1389
+ node.subNext = null;
1390
+ node.prevActive = null;
1391
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
1392
+ }
1393
+ function linkSignal(sig, node) {
1394
+ const oldHead = sig.subsHead ?? null;
1395
+ node.sigPrev = null;
1396
+ node.sigNext = oldHead;
1397
+ if (oldHead) oldHead.sigPrev = node;
1398
+ else sig.subsTail = node;
1399
+ sig.subsHead = node;
1400
+ sig.__sc = (sig.__sc ?? 0) + 1;
1401
+ }
1402
+ function unlinkSignal(node) {
1403
+ const sig = node.sig;
1404
+ if (!sig) return;
1405
+ const prev = node.sigPrev;
1406
+ const next = node.sigNext;
1407
+ if (prev) prev.sigNext = next;
1408
+ else sig.subsHead = next;
1409
+ if (next) next.sigPrev = prev;
1410
+ else sig.subsTail = prev;
1411
+ sig.__sc = (sig.__sc ?? 1) - 1;
1412
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
1413
+ if (sig.__sc === 0) {
1414
+ sig.subsHead = null;
1415
+ sig.subsTail = null;
1416
+ }
1417
+ }
1418
+ function linkSub(sub2, node) {
1419
+ const oldTail = sub2.depsTail ?? null;
1420
+ node.subPrev = oldTail;
1421
+ node.subNext = null;
1422
+ if (oldTail) oldTail.subNext = node;
1423
+ else sub2.depsHead = node;
1424
+ sub2.depsTail = node;
1425
+ }
1426
+ function unlinkSub(node) {
1427
+ const sub2 = node.sub;
1428
+ if (!sub2) return;
1429
+ const prev = node.subPrev;
1430
+ const next = node.subNext;
1431
+ if (prev) prev.subNext = next;
1432
+ else sub2.depsHead = next;
1433
+ if (next) next.subPrev = prev;
1434
+ else sub2.depsTail = prev;
1435
+ }
1356
1436
  var currentSubscriber = null;
1357
- var SUBS = "__s";
1437
+ var suspendSavedSub = null;
1358
1438
  var notifyDepth = 0;
1359
1439
  var pendingQueue = [];
1360
1440
  var pendingSet = /* @__PURE__ */ new Set();
@@ -1368,41 +1448,9 @@ function safeInvoke(sub2) {
1368
1448
  }
1369
1449
  var suspendDepth = 0;
1370
1450
  var trackingSuspended = false;
1371
- function retrack(effectFn, subscriber) {
1372
- const prev = currentSubscriber;
1373
- currentSubscriber = subscriber;
1374
- try {
1375
- effectFn();
1376
- } finally {
1377
- currentSubscriber = prev;
1378
- }
1379
- }
1380
- function track(effectFn, subscriber) {
1381
- if (!subscriber) subscriber = effectFn;
1382
- cleanup(subscriber);
1383
- ++stackTop;
1384
- if (stackTop >= stackCapacity) {
1385
- stackCapacity *= 2;
1386
- subscriberStack.length = stackCapacity;
1387
- }
1388
- subscriberStack[stackTop] = subscriber;
1389
- currentSubscriber = subscriber;
1390
- try {
1391
- effectFn();
1392
- } finally {
1393
- stackTop--;
1394
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
1395
- }
1396
- return () => cleanup(subscriber);
1397
- }
1398
1451
  function suspendTracking() {
1399
1452
  if (suspendDepth === 0) {
1400
- ++stackTop;
1401
- if (stackTop >= stackCapacity) {
1402
- stackCapacity *= 2;
1403
- subscriberStack.length = stackCapacity;
1404
- }
1405
- subscriberStack[stackTop] = null;
1453
+ suspendSavedSub = currentSubscriber;
1406
1454
  currentSubscriber = null;
1407
1455
  trackingSuspended = true;
1408
1456
  }
@@ -1411,8 +1459,8 @@ function suspendTracking() {
1411
1459
  function resumeTracking() {
1412
1460
  suspendDepth--;
1413
1461
  if (suspendDepth === 0) {
1414
- stackTop--;
1415
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
1462
+ currentSubscriber = suspendSavedSub;
1463
+ suspendSavedSub = null;
1416
1464
  trackingSuspended = false;
1417
1465
  }
1418
1466
  }
@@ -1424,65 +1472,141 @@ function untracked(fn) {
1424
1472
  resumeTracking();
1425
1473
  }
1426
1474
  }
1475
+ var subscriberEpochCounter = 0;
1476
+ function retrack(effectFn, subscriber) {
1477
+ const prev = currentSubscriber;
1478
+ currentSubscriber = subscriber;
1479
+ const sub2 = subscriber;
1480
+ const epoch = ++subscriberEpochCounter;
1481
+ sub2._epoch = epoch;
1482
+ sub2._structDirty = false;
1483
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
1484
+ const sig = n.sig;
1485
+ n.prevActive = sig.__activeNode ?? null;
1486
+ sig.__activeNode = n;
1487
+ }
1488
+ try {
1489
+ effectFn();
1490
+ } finally {
1491
+ currentSubscriber = prev;
1492
+ let node = sub2.depsHead ?? null;
1493
+ while (node !== null) {
1494
+ const next = node.subNext;
1495
+ const sig = node.sig;
1496
+ sig.__activeNode = node.prevActive;
1497
+ node.prevActive = null;
1498
+ if (node.epoch !== epoch) {
1499
+ unlinkSub(node);
1500
+ unlinkSignal(node);
1501
+ freeNode(node);
1502
+ }
1503
+ node = next;
1504
+ }
1505
+ }
1506
+ }
1507
+ function track(effectFn, subscriber) {
1508
+ if (!subscriber) subscriber = effectFn;
1509
+ cleanup(subscriber);
1510
+ const prev = currentSubscriber;
1511
+ currentSubscriber = subscriber;
1512
+ try {
1513
+ effectFn();
1514
+ } finally {
1515
+ currentSubscriber = prev;
1516
+ const sub3 = subscriber;
1517
+ for (let n = sub3.depsHead ?? null; n !== null; n = n.subNext) {
1518
+ const sig = n.sig;
1519
+ sig.__activeNode = n.prevActive;
1520
+ n.prevActive = null;
1521
+ }
1522
+ }
1523
+ const sub2 = subscriber;
1524
+ return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
1525
+ }
1427
1526
  function recordDependency(signal2) {
1428
1527
  if (!currentSubscriber) return;
1429
1528
  const sub2 = currentSubscriber;
1430
- if (sub2._dep === signal2) return;
1431
- const deps = sub2._deps;
1432
- if (deps) {
1433
- if (deps.has(signal2)) return;
1434
- deps.add(signal2);
1435
- } else if (sub2._dep !== void 0) {
1436
- const set = /* @__PURE__ */ new Set();
1437
- set.add(sub2._dep);
1438
- set.add(signal2);
1439
- sub2._deps = set;
1440
- sub2._dep = void 0;
1441
- } else {
1442
- sub2._dep = signal2;
1529
+ const sig = signal2;
1530
+ const epoch = sub2._epoch ?? 0;
1531
+ const active = sig.__activeNode ?? null;
1532
+ if (active !== null && active.sub === sub2) {
1533
+ active.epoch = epoch;
1534
+ return;
1443
1535
  }
1444
- let subs = signal2[SUBS];
1445
- if (!subs) {
1446
- subs = /* @__PURE__ */ new Set();
1447
- signal2[SUBS] = subs;
1536
+ const node = allocNode(signal2, sub2, epoch);
1537
+ node.prevActive = active;
1538
+ sig.__activeNode = node;
1539
+ linkSub(sub2, node);
1540
+ linkSignal(sig, node);
1541
+ sub2._structDirty = true;
1542
+ }
1543
+ function cleanup(subscriber) {
1544
+ const sub2 = subscriber;
1545
+ let node = sub2.depsHead ?? null;
1546
+ sub2.depsHead = null;
1547
+ sub2.depsTail = null;
1548
+ while (node) {
1549
+ const next = node.subNext;
1550
+ unlinkSignal(node);
1551
+ freeNode(node);
1552
+ node = next;
1553
+ }
1554
+ }
1555
+ var maxSubscriberRepeats = 50;
1556
+ var maxDrainIterations = 1e6;
1557
+ var drainEpoch = 0;
1558
+ function setMaxDrainIterations(n) {
1559
+ const prev = maxDrainIterations;
1560
+ if (Number.isFinite(n) && n > 0) maxDrainIterations = Math.floor(n);
1561
+ return prev;
1562
+ }
1563
+ function tickRepeat(sub2) {
1564
+ const s2 = sub2;
1565
+ if (s2._runEpoch !== drainEpoch) {
1566
+ s2._runEpoch = drainEpoch;
1567
+ s2._runs = 1;
1568
+ return false;
1448
1569
  }
1449
- subs.add(currentSubscriber);
1450
- if (subs.size === 1) {
1451
- signal2.__f = currentSubscriber;
1452
- } else if (signal2.__f !== void 0) {
1453
- signal2.__f = void 0;
1570
+ s2._runs = (s2._runs ?? 0) + 1;
1571
+ return s2._runs > maxSubscriberRepeats;
1572
+ }
1573
+ function cycleError(sub2) {
1574
+ if (typeof console !== "undefined") {
1575
+ const name = sub2.__name ?? "<unnamed>";
1576
+ console.error(
1577
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
1578
+ );
1454
1579
  }
1455
1580
  }
1456
- function queueSignalNotification(signal2) {
1457
- const subs = signal2[SUBS];
1458
- if (!subs) return;
1459
- for (const sub2 of subs) {
1460
- if (sub2._c) {
1461
- propagateDirty(sub2);
1462
- } else if (!pendingSet.has(sub2)) {
1463
- pendingSet.add(sub2);
1464
- pendingQueue.push(sub2);
1581
+ function absoluteDrainError() {
1582
+ if (typeof console !== "undefined") {
1583
+ console.error(
1584
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
1585
+ );
1586
+ }
1587
+ }
1588
+ function drainQueue() {
1589
+ let i2 = 0;
1590
+ while (i2 < pendingQueue.length) {
1591
+ if (i2 >= maxDrainIterations) {
1592
+ absoluteDrainError();
1593
+ break;
1594
+ }
1595
+ const sub2 = pendingQueue[i2++];
1596
+ if (tickRepeat(sub2)) {
1597
+ cycleError(sub2);
1598
+ break;
1465
1599
  }
1600
+ pendingSet.delete(sub2);
1601
+ safeInvoke(sub2);
1466
1602
  }
1467
1603
  }
1468
- var maxDrainIterations = 1e5;
1469
1604
  function drainNotificationQueue() {
1470
1605
  if (notifyDepth > 0) return;
1471
1606
  notifyDepth++;
1607
+ drainEpoch++;
1472
1608
  try {
1473
- let i2 = 0;
1474
- while (i2 < pendingQueue.length) {
1475
- if (i2 >= maxDrainIterations) {
1476
- if (typeof console !== "undefined") {
1477
- console.error(
1478
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
1479
- );
1480
- }
1481
- break;
1482
- }
1483
- safeInvoke(pendingQueue[i2]);
1484
- i2++;
1485
- }
1609
+ drainQueue();
1486
1610
  } finally {
1487
1611
  notifyDepth--;
1488
1612
  if (notifyDepth === 0) {
@@ -1500,131 +1624,82 @@ function propagateDirty(sub2) {
1500
1624
  stack.push(rootSig);
1501
1625
  while (stack.length > baseLen) {
1502
1626
  const sig = stack.pop();
1503
- const first = sig.__f;
1504
- if (first) {
1505
- if (first._c) {
1506
- const nSig = first._sig;
1507
- if (!nSig._d) {
1508
- nSig._d = true;
1509
- stack.push(nSig);
1627
+ let node = sig.subsHead ?? null;
1628
+ while (node) {
1629
+ const s2 = node.sub;
1630
+ if (s2) {
1631
+ if (s2._c) {
1632
+ const nSig = s2._sig;
1633
+ if (nSig) {
1634
+ if (!nSig._d) {
1635
+ nSig._d = true;
1636
+ stack.push(nSig);
1637
+ }
1638
+ } else {
1639
+ s2();
1640
+ }
1641
+ } else if (!pendingSet.has(s2)) {
1642
+ pendingSet.add(s2);
1643
+ pendingQueue.push(s2);
1510
1644
  }
1511
- } else if (!pendingSet.has(first)) {
1512
- pendingSet.add(first);
1513
- pendingQueue.push(first);
1514
1645
  }
1515
- continue;
1646
+ node = node.sigNext;
1516
1647
  }
1517
- const subs = sig[SUBS];
1518
- if (!subs) continue;
1519
- for (const s2 of subs) {
1648
+ }
1649
+ }
1650
+ function queueSignalNotification(signal2) {
1651
+ const sig = signal2;
1652
+ let node = sig.subsHead ?? null;
1653
+ while (node) {
1654
+ const s2 = node.sub;
1655
+ if (s2) {
1520
1656
  if (s2._c) {
1521
- const nSig = s2._sig;
1522
- if (nSig && !nSig._d) {
1523
- nSig._d = true;
1524
- stack.push(nSig);
1525
- } else if (!nSig) {
1526
- s2();
1527
- }
1657
+ propagateDirty(s2);
1528
1658
  } else if (!pendingSet.has(s2)) {
1529
1659
  pendingSet.add(s2);
1530
1660
  pendingQueue.push(s2);
1531
1661
  }
1532
1662
  }
1663
+ node = node.sigNext;
1533
1664
  }
1534
1665
  }
1535
1666
  function notifySubscribers(signal2) {
1536
- const first = signal2.__f;
1537
- if (first) {
1538
- if (notifyDepth > 0) {
1539
- if (first._c) {
1540
- propagateDirty(first);
1541
- } else if (!pendingSet.has(first)) {
1542
- pendingSet.add(first);
1543
- pendingQueue.push(first);
1544
- }
1545
- return;
1546
- }
1547
- notifyDepth++;
1548
- try {
1549
- if (first._c) {
1550
- propagateDirty(first);
1551
- } else {
1552
- safeInvoke(first);
1553
- }
1554
- let i2 = 0;
1555
- while (i2 < pendingQueue.length) {
1556
- if (i2 >= maxDrainIterations) {
1557
- if (typeof console !== "undefined") {
1558
- console.error(
1559
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
1560
- );
1561
- }
1562
- break;
1563
- }
1564
- safeInvoke(pendingQueue[i2]);
1565
- i2++;
1566
- }
1567
- } finally {
1568
- notifyDepth--;
1569
- if (notifyDepth === 0) {
1570
- pendingQueue.length = 0;
1571
- pendingSet.clear();
1572
- }
1573
- }
1574
- return;
1575
- }
1576
- const subs = signal2[SUBS];
1577
- if (!subs || subs.size === 0) return;
1667
+ const sig = signal2;
1668
+ const head2 = sig.subsHead;
1669
+ if (!head2) return;
1578
1670
  if (notifyDepth > 0) {
1579
- for (const sub2 of subs) {
1580
- if (sub2._c) {
1581
- propagateDirty(sub2);
1582
- } else if (!pendingSet.has(sub2)) {
1583
- pendingSet.add(sub2);
1584
- pendingQueue.push(sub2);
1671
+ let node = head2;
1672
+ while (node) {
1673
+ const s2 = node.sub;
1674
+ if (s2) {
1675
+ if (s2._c) {
1676
+ propagateDirty(s2);
1677
+ } else if (!pendingSet.has(s2)) {
1678
+ pendingSet.add(s2);
1679
+ pendingQueue.push(s2);
1680
+ }
1585
1681
  }
1682
+ node = node.sigNext;
1586
1683
  }
1587
1684
  return;
1588
1685
  }
1589
1686
  notifyDepth++;
1687
+ drainEpoch++;
1590
1688
  try {
1591
- let directCount = 0;
1592
- let hasComputedSub = false;
1593
- for (const sub2 of subs) {
1594
- if (sub2._c) hasComputedSub = true;
1595
- pendingQueue[directCount++] = sub2;
1596
- }
1597
- if (!hasComputedSub) {
1598
- for (let i3 = 0; i3 < directCount; i3++) {
1599
- safeInvoke(pendingQueue[i3]);
1600
- }
1601
- } else {
1602
- for (let i3 = 0; i3 < directCount; i3++) {
1603
- if (pendingQueue[i3]._c) {
1604
- propagateDirty(pendingQueue[i3]);
1605
- }
1606
- }
1607
- for (let i3 = 0; i3 < directCount; i3++) {
1608
- const sub2 = pendingQueue[i3];
1609
- if (!sub2._c && !pendingSet.has(sub2)) {
1610
- pendingSet.add(sub2);
1611
- safeInvoke(sub2);
1612
- }
1613
- }
1614
- }
1615
- let i2 = directCount;
1616
- while (i2 < pendingQueue.length) {
1617
- if (i2 - directCount >= maxDrainIterations) {
1618
- if (typeof console !== "undefined") {
1619
- console.error(
1620
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
1621
- );
1689
+ let node = head2;
1690
+ while (node) {
1691
+ const s2 = node.sub;
1692
+ if (s2) {
1693
+ if (s2._c) {
1694
+ propagateDirty(s2);
1695
+ } else if (!pendingSet.has(s2)) {
1696
+ pendingSet.add(s2);
1697
+ pendingQueue.push(s2);
1622
1698
  }
1623
- break;
1624
1699
  }
1625
- safeInvoke(pendingQueue[i2]);
1626
- i2++;
1700
+ node = node.sigNext;
1627
1701
  }
1702
+ drainQueue();
1628
1703
  } finally {
1629
1704
  notifyDepth--;
1630
1705
  if (notifyDepth === 0) {
@@ -1633,37 +1708,6 @@ function notifySubscribers(signal2) {
1633
1708
  }
1634
1709
  }
1635
1710
  }
1636
- function cleanup(subscriber) {
1637
- const sub2 = subscriber;
1638
- const singleDep = sub2._dep;
1639
- if (singleDep !== void 0) {
1640
- const subs = singleDep[SUBS];
1641
- if (subs) {
1642
- subs.delete(subscriber);
1643
- if (singleDep.__f === subscriber) {
1644
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
1645
- } else if (subs.size === 1 && singleDep.__f === void 0) {
1646
- singleDep.__f = subs.values().next().value;
1647
- }
1648
- }
1649
- sub2._dep = void 0;
1650
- return;
1651
- }
1652
- const deps = sub2._deps;
1653
- if (!deps || deps.size === 0) return;
1654
- for (const signal2 of deps) {
1655
- const subs = signal2[SUBS];
1656
- if (subs) {
1657
- subs.delete(subscriber);
1658
- if (signal2.__f === subscriber) {
1659
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
1660
- } else if (subs.size === 1 && signal2.__f === void 0) {
1661
- signal2.__f = subs.values().next().value;
1662
- }
1663
- }
1664
- }
1665
- deps.clear();
1666
- }
1667
1711
 
1668
1712
  // src/reactivity/bindAttribute.ts
1669
1713
  var _isDev3 = isDev();
@@ -3303,32 +3347,64 @@ function flushBatch() {
3303
3347
  var _g = globalThis;
3304
3348
  var _isDev9 = isDev();
3305
3349
  function signal(initial, options) {
3306
- const state = { value: initial };
3350
+ const state = {
3351
+ value: initial,
3352
+ __v: 0,
3353
+ __sc: 0,
3354
+ subsHead: null,
3355
+ subsTail: null,
3356
+ __activeNode: null,
3357
+ __name: void 0
3358
+ };
3307
3359
  const debugName = _isDev9 ? options?.name : void 0;
3308
3360
  const equalsFn = options?.equals;
3309
- if (debugName) {
3310
- state.__name = debugName;
3311
- }
3361
+ if (debugName) state.__name = debugName;
3312
3362
  function get() {
3313
3363
  recordDependency(state);
3314
3364
  return state.value;
3315
3365
  }
3316
3366
  get.__signal = state;
3317
3367
  if (debugName) get.__name = debugName;
3318
- function set(next) {
3319
- const newValue = typeof next === "function" ? next(state.value) : next;
3320
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
3321
- if (_isDev9) {
3322
- const oldValue = state.value;
3368
+ let set;
3369
+ if (equalsFn) {
3370
+ set = (next) => {
3371
+ const prev = state.value;
3372
+ const newValue = typeof next === "function" ? next(prev) : next;
3373
+ if (equalsFn(prev, newValue)) return;
3374
+ state.value = newValue;
3375
+ state.__v++;
3376
+ if (_isDev9) {
3377
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3378
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
3379
+ }
3380
+ if (!enqueueBatchedSignal(state)) {
3381
+ notifySubscribers(state);
3382
+ }
3383
+ };
3384
+ } else if (_isDev9) {
3385
+ set = (next) => {
3386
+ const prev = state.value;
3387
+ const newValue = typeof next === "function" ? next(prev) : next;
3388
+ if (Object.is(newValue, prev)) return;
3323
3389
  state.value = newValue;
3390
+ state.__v++;
3324
3391
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3325
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
3326
- } else {
3392
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
3393
+ if (!enqueueBatchedSignal(state)) {
3394
+ notifySubscribers(state);
3395
+ }
3396
+ };
3397
+ } else {
3398
+ set = (next) => {
3399
+ const prev = state.value;
3400
+ const newValue = typeof next === "function" ? next(prev) : next;
3401
+ if (Object.is(newValue, prev)) return;
3327
3402
  state.value = newValue;
3328
- }
3329
- if (!enqueueBatchedSignal(state)) {
3330
- notifySubscribers(state);
3331
- }
3403
+ state.__v++;
3404
+ if (!enqueueBatchedSignal(state)) {
3405
+ notifySubscribers(state);
3406
+ }
3407
+ };
3332
3408
  }
3333
3409
  if (_isDev9) {
3334
3410
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -3412,92 +3488,122 @@ function on(deps, handler) {
3412
3488
  }
3413
3489
  };
3414
3490
  }
3491
+ var MAX_RERUNS = 100;
3492
+ function flushUserCleanups(ctx) {
3493
+ const list = ctx.userCleanups;
3494
+ if (list.length === 0) return;
3495
+ ctx.userCleanups = [];
3496
+ for (let i2 = list.length - 1; i2 >= 0; i2--) {
3497
+ try {
3498
+ list[i2]();
3499
+ } catch (err) {
3500
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
3501
+ }
3502
+ }
3503
+ }
3504
+ function drainReruns(ctx) {
3505
+ let reruns = 1;
3506
+ do {
3507
+ ctx.rerunPending = false;
3508
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
3509
+ retrack(ctx.bodyFn, ctx.subscriber);
3510
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
3511
+ if (ctx.rerunPending) {
3512
+ ctx.rerunPending = false;
3513
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
3514
+ console.error(
3515
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
3516
+ );
3517
+ }
3518
+ }
3519
+ }
3520
+ function disposeEffect(ctx) {
3521
+ if (ctx.disposed) return;
3522
+ ctx.disposed = true;
3523
+ const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3524
+ if (h) {
3525
+ try {
3526
+ h.emit("effect:destroy", { effectFn: ctx.fn });
3527
+ } catch {
3528
+ }
3529
+ }
3530
+ try {
3531
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
3532
+ } catch (err) {
3533
+ if (typeof console !== "undefined") {
3534
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
3535
+ }
3536
+ }
3537
+ try {
3538
+ cleanup(ctx.subscriber);
3539
+ } catch (err) {
3540
+ if (typeof console !== "undefined") {
3541
+ console.warn("[SibuJS effect] dispose threw:", err);
3542
+ }
3543
+ }
3544
+ }
3415
3545
  function effect(effectFn, options) {
3416
3546
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
3417
3547
  if (isSSR()) return () => {
3418
3548
  };
3419
- const onError = options?.onError;
3420
- let userCleanups = [];
3421
- const onCleanup2 = (fn) => {
3422
- userCleanups.push(fn);
3549
+ const ctx = {
3550
+ fn: effectFn,
3551
+ onError: options?.onError,
3552
+ userCleanups: [],
3553
+ running: false,
3554
+ rerunPending: false,
3555
+ disposed: false,
3556
+ onCleanup: null,
3557
+ subscriber: null,
3558
+ bodyFn: null
3423
3559
  };
3424
- const runUserCleanups = () => {
3425
- if (userCleanups.length === 0) return;
3426
- const list = userCleanups;
3427
- userCleanups = [];
3428
- for (let i2 = list.length - 1; i2 >= 0; i2--) {
3429
- try {
3430
- list[i2]();
3431
- } catch (err) {
3432
- if (typeof console !== "undefined") {
3433
- console.warn("[SibuJS effect] onCleanup threw:", err);
3434
- }
3435
- }
3436
- }
3560
+ ctx.onCleanup = (fn) => {
3561
+ ctx.userCleanups.push(fn);
3437
3562
  };
3438
- const invokeBody = () => effectFn(onCleanup2);
3439
- const wrappedFn = onError ? () => {
3563
+ const onErrorCaptured = ctx.onError;
3564
+ ctx.bodyFn = onErrorCaptured ? () => {
3440
3565
  try {
3441
- invokeBody();
3566
+ ctx.fn(ctx.onCleanup);
3442
3567
  } catch (err) {
3443
- onError(err);
3568
+ onErrorCaptured(err);
3444
3569
  }
3445
- } : invokeBody;
3446
- let cleanupHandle = () => {
3570
+ } : () => {
3571
+ ctx.fn(ctx.onCleanup);
3447
3572
  };
3448
- let running = false;
3449
- const subscriber = () => {
3450
- if (running) {
3451
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
3452
- console.warn(
3453
- "[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."
3454
- );
3455
- }
3573
+ const sub2 = (() => {
3574
+ if (ctx.running) {
3575
+ ctx.rerunPending = true;
3456
3576
  return;
3457
3577
  }
3458
- running = true;
3578
+ ctx.running = true;
3459
3579
  try {
3460
- runUserCleanups();
3461
- cleanupHandle();
3462
- cleanupHandle = track(wrappedFn, subscriber);
3580
+ ctx.rerunPending = false;
3581
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
3582
+ retrack(ctx.bodyFn, sub2);
3583
+ if (ctx.rerunPending) drainReruns(ctx);
3463
3584
  } finally {
3464
- running = false;
3585
+ ctx.running = false;
3586
+ ctx.rerunPending = false;
3465
3587
  }
3466
- };
3467
- running = true;
3588
+ });
3589
+ sub2.depsHead = null;
3590
+ sub2.depsTail = null;
3591
+ sub2._epoch = 0;
3592
+ sub2._structDirty = false;
3593
+ sub2._runEpoch = 0;
3594
+ sub2._runs = 0;
3595
+ ctx.subscriber = sub2;
3596
+ ctx.running = true;
3468
3597
  try {
3469
- cleanupHandle = track(wrappedFn, subscriber);
3598
+ retrack(ctx.bodyFn, ctx.subscriber);
3599
+ if (ctx.rerunPending) drainReruns(ctx);
3470
3600
  } finally {
3471
- running = false;
3601
+ ctx.running = false;
3602
+ ctx.rerunPending = false;
3472
3603
  }
3473
3604
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3474
3605
  if (hook) hook.emit("effect:create", { effectFn });
3475
- let disposed = false;
3476
- return () => {
3477
- if (disposed) return;
3478
- disposed = true;
3479
- const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
3480
- if (h) {
3481
- try {
3482
- h.emit("effect:destroy", { effectFn });
3483
- } catch {
3484
- }
3485
- }
3486
- try {
3487
- runUserCleanups();
3488
- } catch (err) {
3489
- if (typeof console !== "undefined") {
3490
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
3491
- }
3492
- }
3493
- try {
3494
- cleanupHandle();
3495
- } catch (err) {
3496
- if (typeof console !== "undefined") {
3497
- console.warn("[SibuJS effect] dispose threw:", err);
3498
- }
3499
- }
3500
- };
3606
+ return () => disposeEffect(ctx);
3501
3607
  }
3502
3608
 
3503
3609
  // src/core/signals/derived.ts
@@ -3508,6 +3614,7 @@ function derived(getter, options) {
3508
3614
  const cs = {};
3509
3615
  cs._d = false;
3510
3616
  cs._g = getter;
3617
+ cs.__v = 0;
3511
3618
  const markDirty = () => {
3512
3619
  if (cs._d) return;
3513
3620
  cs._d = true;
@@ -3537,11 +3644,14 @@ function derived(getter, options) {
3537
3644
  evaluating = true;
3538
3645
  let threw = true;
3539
3646
  try {
3647
+ const prev = cs._v;
3540
3648
  retrack(() => {
3541
- cs._v = getter();
3649
+ const next = getter();
3650
+ cs._v = equals && cs._v !== void 0 ? equals(cs._v, next) ? cs._v : next : next;
3542
3651
  cs._d = false;
3543
3652
  threw = false;
3544
3653
  }, markDirty);
3654
+ if (!Object.is(prev, cs._v)) cs.__v++;
3545
3655
  } finally {
3546
3656
  evaluating = false;
3547
3657
  if (threw) cs._d = true;
@@ -3561,6 +3671,7 @@ function derived(getter, options) {
3561
3671
  cs._d = false;
3562
3672
  threw = false;
3563
3673
  }, markDirty);
3674
+ if (!Object.is(oldValue, cs._v)) cs.__v++;
3564
3675
  } finally {
3565
3676
  evaluating = false;
3566
3677
  if (threw) cs._d = true;