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/plugins.cjs CHANGED
@@ -775,11 +775,88 @@ init_sanitize();
775
775
  // src/reactivity/track.ts
776
776
  init_dev();
777
777
  var _isDev2 = isDev();
778
- var subscriberStack = new Array(32);
779
- var stackCapacity = 32;
780
- var stackTop = -1;
778
+ var POOL_MAX = 4096;
779
+ var nodePool = [];
780
+ function createNode() {
781
+ return {
782
+ sig: null,
783
+ sub: null,
784
+ epoch: 0,
785
+ sigPrev: null,
786
+ sigNext: null,
787
+ subPrev: null,
788
+ subNext: null,
789
+ prevActive: null
790
+ };
791
+ }
792
+ function allocNode(sig, sub2, epoch) {
793
+ const n = nodePool.pop();
794
+ if (n) {
795
+ n.sig = sig;
796
+ n.sub = sub2;
797
+ n.epoch = epoch;
798
+ return n;
799
+ }
800
+ const fresh = createNode();
801
+ fresh.sig = sig;
802
+ fresh.sub = sub2;
803
+ fresh.epoch = epoch;
804
+ return fresh;
805
+ }
806
+ function freeNode(node) {
807
+ node.sig = null;
808
+ node.sub = null;
809
+ node.sigPrev = null;
810
+ node.sigNext = null;
811
+ node.subPrev = null;
812
+ node.subNext = null;
813
+ node.prevActive = null;
814
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
815
+ }
816
+ function linkSignal(sig, node) {
817
+ const oldHead = sig.subsHead ?? null;
818
+ node.sigPrev = null;
819
+ node.sigNext = oldHead;
820
+ if (oldHead) oldHead.sigPrev = node;
821
+ else sig.subsTail = node;
822
+ sig.subsHead = node;
823
+ sig.__sc = (sig.__sc ?? 0) + 1;
824
+ }
825
+ function unlinkSignal(node) {
826
+ const sig = node.sig;
827
+ if (!sig) return;
828
+ const prev = node.sigPrev;
829
+ const next = node.sigNext;
830
+ if (prev) prev.sigNext = next;
831
+ else sig.subsHead = next;
832
+ if (next) next.sigPrev = prev;
833
+ else sig.subsTail = prev;
834
+ sig.__sc = (sig.__sc ?? 1) - 1;
835
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
836
+ if (sig.__sc === 0) {
837
+ sig.subsHead = null;
838
+ sig.subsTail = null;
839
+ }
840
+ }
841
+ function linkSub(sub2, node) {
842
+ const oldTail = sub2.depsTail ?? null;
843
+ node.subPrev = oldTail;
844
+ node.subNext = null;
845
+ if (oldTail) oldTail.subNext = node;
846
+ else sub2.depsHead = node;
847
+ sub2.depsTail = node;
848
+ }
849
+ function unlinkSub(node) {
850
+ const sub2 = node.sub;
851
+ if (!sub2) return;
852
+ const prev = node.subPrev;
853
+ const next = node.subNext;
854
+ if (prev) prev.subNext = next;
855
+ else sub2.depsHead = next;
856
+ if (next) next.subPrev = prev;
857
+ else sub2.depsTail = prev;
858
+ }
781
859
  var currentSubscriber = null;
782
- var SUBS = "__s";
783
860
  var notifyDepth = 0;
784
861
  var pendingQueue = [];
785
862
  var pendingSet = /* @__PURE__ */ new Set();
@@ -791,54 +868,130 @@ function safeInvoke(sub2) {
791
868
  if (_isDev2) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
792
869
  }
793
870
  }
871
+ var subscriberEpochCounter = 0;
872
+ function retrack(effectFn, subscriber) {
873
+ const prev = currentSubscriber;
874
+ currentSubscriber = subscriber;
875
+ const sub2 = subscriber;
876
+ const epoch = ++subscriberEpochCounter;
877
+ sub2._epoch = epoch;
878
+ sub2._structDirty = false;
879
+ for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
880
+ const sig = n.sig;
881
+ n.prevActive = sig.__activeNode ?? null;
882
+ sig.__activeNode = n;
883
+ }
884
+ try {
885
+ effectFn();
886
+ } finally {
887
+ currentSubscriber = prev;
888
+ let node = sub2.depsHead ?? null;
889
+ while (node !== null) {
890
+ const next = node.subNext;
891
+ const sig = node.sig;
892
+ sig.__activeNode = node.prevActive;
893
+ node.prevActive = null;
894
+ if (node.epoch !== epoch) {
895
+ unlinkSub(node);
896
+ unlinkSignal(node);
897
+ freeNode(node);
898
+ }
899
+ node = next;
900
+ }
901
+ }
902
+ }
794
903
  function track(effectFn, subscriber) {
795
904
  if (!subscriber) subscriber = effectFn;
796
905
  cleanup(subscriber);
797
- ++stackTop;
798
- if (stackTop >= stackCapacity) {
799
- stackCapacity *= 2;
800
- subscriberStack.length = stackCapacity;
801
- }
802
- subscriberStack[stackTop] = subscriber;
906
+ const prev = currentSubscriber;
803
907
  currentSubscriber = subscriber;
804
908
  try {
805
909
  effectFn();
806
910
  } finally {
807
- stackTop--;
808
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
911
+ currentSubscriber = prev;
912
+ const sub3 = subscriber;
913
+ for (let n = sub3.depsHead ?? null; n !== null; n = n.subNext) {
914
+ const sig = n.sig;
915
+ sig.__activeNode = n.prevActive;
916
+ n.prevActive = null;
917
+ }
809
918
  }
810
- return () => cleanup(subscriber);
919
+ const sub2 = subscriber;
920
+ return sub2._dispose ?? (sub2._dispose = () => cleanup(subscriber));
811
921
  }
812
922
  function recordDependency(signal2) {
813
923
  if (!currentSubscriber) return;
814
924
  const sub2 = currentSubscriber;
815
- if (sub2._dep === signal2) return;
816
- const deps = sub2._deps;
817
- if (deps) {
818
- if (deps.has(signal2)) return;
819
- deps.add(signal2);
820
- } else if (sub2._dep !== void 0) {
821
- const set = /* @__PURE__ */ new Set();
822
- set.add(sub2._dep);
823
- set.add(signal2);
824
- sub2._deps = set;
825
- sub2._dep = void 0;
826
- } else {
827
- sub2._dep = signal2;
925
+ const sig = signal2;
926
+ const epoch = sub2._epoch ?? 0;
927
+ const active = sig.__activeNode ?? null;
928
+ if (active !== null && active.sub === sub2) {
929
+ active.epoch = epoch;
930
+ return;
828
931
  }
829
- let subs = signal2[SUBS];
830
- if (!subs) {
831
- subs = /* @__PURE__ */ new Set();
832
- signal2[SUBS] = subs;
932
+ const node = allocNode(signal2, sub2, epoch);
933
+ node.prevActive = active;
934
+ sig.__activeNode = node;
935
+ linkSub(sub2, node);
936
+ linkSignal(sig, node);
937
+ sub2._structDirty = true;
938
+ }
939
+ function cleanup(subscriber) {
940
+ const sub2 = subscriber;
941
+ let node = sub2.depsHead ?? null;
942
+ sub2.depsHead = null;
943
+ sub2.depsTail = null;
944
+ while (node) {
945
+ const next = node.subNext;
946
+ unlinkSignal(node);
947
+ freeNode(node);
948
+ node = next;
949
+ }
950
+ }
951
+ var maxSubscriberRepeats = 50;
952
+ var maxDrainIterations = 1e6;
953
+ var drainEpoch = 0;
954
+ function tickRepeat(sub2) {
955
+ const s2 = sub2;
956
+ if (s2._runEpoch !== drainEpoch) {
957
+ s2._runEpoch = drainEpoch;
958
+ s2._runs = 1;
959
+ return false;
960
+ }
961
+ s2._runs = (s2._runs ?? 0) + 1;
962
+ return s2._runs > maxSubscriberRepeats;
963
+ }
964
+ function cycleError(sub2) {
965
+ if (typeof console !== "undefined") {
966
+ const name = sub2.__name ?? "<unnamed>";
967
+ console.error(
968
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
969
+ );
833
970
  }
834
- subs.add(currentSubscriber);
835
- if (subs.size === 1) {
836
- signal2.__f = currentSubscriber;
837
- } else if (signal2.__f !== void 0) {
838
- signal2.__f = void 0;
971
+ }
972
+ function absoluteDrainError() {
973
+ if (typeof console !== "undefined") {
974
+ console.error(
975
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
976
+ );
977
+ }
978
+ }
979
+ function drainQueue() {
980
+ let i2 = 0;
981
+ while (i2 < pendingQueue.length) {
982
+ if (i2 >= maxDrainIterations) {
983
+ absoluteDrainError();
984
+ break;
985
+ }
986
+ const sub2 = pendingQueue[i2++];
987
+ if (tickRepeat(sub2)) {
988
+ cycleError(sub2);
989
+ break;
990
+ }
991
+ pendingSet.delete(sub2);
992
+ safeInvoke(sub2);
839
993
  }
840
994
  }
841
- var maxDrainIterations = 1e5;
842
995
  function propagateDirty(sub2) {
843
996
  sub2();
844
997
  const rootSig = sub2._sig;
@@ -848,131 +1001,66 @@ function propagateDirty(sub2) {
848
1001
  stack.push(rootSig);
849
1002
  while (stack.length > baseLen) {
850
1003
  const sig = stack.pop();
851
- const first = sig.__f;
852
- if (first) {
853
- if (first._c) {
854
- const nSig = first._sig;
855
- if (!nSig._d) {
856
- nSig._d = true;
857
- stack.push(nSig);
858
- }
859
- } else if (!pendingSet.has(first)) {
860
- pendingSet.add(first);
861
- pendingQueue.push(first);
862
- }
863
- continue;
864
- }
865
- const subs = sig[SUBS];
866
- if (!subs) continue;
867
- for (const s2 of subs) {
868
- if (s2._c) {
869
- const nSig = s2._sig;
870
- if (nSig && !nSig._d) {
871
- nSig._d = true;
872
- stack.push(nSig);
873
- } else if (!nSig) {
874
- s2();
1004
+ let node = sig.subsHead ?? null;
1005
+ while (node) {
1006
+ const s2 = node.sub;
1007
+ if (s2) {
1008
+ if (s2._c) {
1009
+ const nSig = s2._sig;
1010
+ if (nSig) {
1011
+ if (!nSig._d) {
1012
+ nSig._d = true;
1013
+ stack.push(nSig);
1014
+ }
1015
+ } else {
1016
+ s2();
1017
+ }
1018
+ } else if (!pendingSet.has(s2)) {
1019
+ pendingSet.add(s2);
1020
+ pendingQueue.push(s2);
875
1021
  }
876
- } else if (!pendingSet.has(s2)) {
877
- pendingSet.add(s2);
878
- pendingQueue.push(s2);
879
1022
  }
1023
+ node = node.sigNext;
880
1024
  }
881
1025
  }
882
1026
  }
883
1027
  function notifySubscribers(signal2) {
884
- const first = signal2.__f;
885
- if (first) {
886
- if (notifyDepth > 0) {
887
- if (first._c) {
888
- propagateDirty(first);
889
- } else if (!pendingSet.has(first)) {
890
- pendingSet.add(first);
891
- pendingQueue.push(first);
892
- }
893
- return;
894
- }
895
- notifyDepth++;
896
- try {
897
- if (first._c) {
898
- propagateDirty(first);
899
- } else {
900
- safeInvoke(first);
901
- }
902
- let i2 = 0;
903
- while (i2 < pendingQueue.length) {
904
- if (i2 >= maxDrainIterations) {
905
- if (typeof console !== "undefined") {
906
- console.error(
907
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
908
- );
909
- }
910
- break;
911
- }
912
- safeInvoke(pendingQueue[i2]);
913
- i2++;
914
- }
915
- } finally {
916
- notifyDepth--;
917
- if (notifyDepth === 0) {
918
- pendingQueue.length = 0;
919
- pendingSet.clear();
920
- }
921
- }
922
- return;
923
- }
924
- const subs = signal2[SUBS];
925
- if (!subs || subs.size === 0) return;
1028
+ const sig = signal2;
1029
+ const head2 = sig.subsHead;
1030
+ if (!head2) return;
926
1031
  if (notifyDepth > 0) {
927
- for (const sub2 of subs) {
928
- if (sub2._c) {
929
- propagateDirty(sub2);
930
- } else if (!pendingSet.has(sub2)) {
931
- pendingSet.add(sub2);
932
- pendingQueue.push(sub2);
1032
+ let node = head2;
1033
+ while (node) {
1034
+ const s2 = node.sub;
1035
+ if (s2) {
1036
+ if (s2._c) {
1037
+ propagateDirty(s2);
1038
+ } else if (!pendingSet.has(s2)) {
1039
+ pendingSet.add(s2);
1040
+ pendingQueue.push(s2);
1041
+ }
933
1042
  }
1043
+ node = node.sigNext;
934
1044
  }
935
1045
  return;
936
1046
  }
937
1047
  notifyDepth++;
1048
+ drainEpoch++;
938
1049
  try {
939
- let directCount = 0;
940
- let hasComputedSub = false;
941
- for (const sub2 of subs) {
942
- if (sub2._c) hasComputedSub = true;
943
- pendingQueue[directCount++] = sub2;
944
- }
945
- if (!hasComputedSub) {
946
- for (let i3 = 0; i3 < directCount; i3++) {
947
- safeInvoke(pendingQueue[i3]);
948
- }
949
- } else {
950
- for (let i3 = 0; i3 < directCount; i3++) {
951
- if (pendingQueue[i3]._c) {
952
- propagateDirty(pendingQueue[i3]);
1050
+ let node = head2;
1051
+ while (node) {
1052
+ const s2 = node.sub;
1053
+ if (s2) {
1054
+ if (s2._c) {
1055
+ propagateDirty(s2);
1056
+ } else if (!pendingSet.has(s2)) {
1057
+ pendingSet.add(s2);
1058
+ pendingQueue.push(s2);
953
1059
  }
954
1060
  }
955
- for (let i3 = 0; i3 < directCount; i3++) {
956
- const sub2 = pendingQueue[i3];
957
- if (!sub2._c && !pendingSet.has(sub2)) {
958
- pendingSet.add(sub2);
959
- safeInvoke(sub2);
960
- }
961
- }
962
- }
963
- let i2 = directCount;
964
- while (i2 < pendingQueue.length) {
965
- if (i2 - directCount >= maxDrainIterations) {
966
- if (typeof console !== "undefined") {
967
- console.error(
968
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
969
- );
970
- }
971
- break;
972
- }
973
- safeInvoke(pendingQueue[i2]);
974
- i2++;
1061
+ node = node.sigNext;
975
1062
  }
1063
+ drainQueue();
976
1064
  } finally {
977
1065
  notifyDepth--;
978
1066
  if (notifyDepth === 0) {
@@ -981,37 +1069,6 @@ function notifySubscribers(signal2) {
981
1069
  }
982
1070
  }
983
1071
  }
984
- function cleanup(subscriber) {
985
- const sub2 = subscriber;
986
- const singleDep = sub2._dep;
987
- if (singleDep !== void 0) {
988
- const subs = singleDep[SUBS];
989
- if (subs) {
990
- subs.delete(subscriber);
991
- if (singleDep.__f === subscriber) {
992
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
993
- } else if (subs.size === 1 && singleDep.__f === void 0) {
994
- singleDep.__f = subs.values().next().value;
995
- }
996
- }
997
- sub2._dep = void 0;
998
- return;
999
- }
1000
- const deps = sub2._deps;
1001
- if (!deps || deps.size === 0) return;
1002
- for (const signal2 of deps) {
1003
- const subs = signal2[SUBS];
1004
- if (subs) {
1005
- subs.delete(subscriber);
1006
- if (signal2.__f === subscriber) {
1007
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
1008
- } else if (subs.size === 1 && signal2.__f === void 0) {
1009
- signal2.__f = subs.values().next().value;
1010
- }
1011
- }
1012
- }
1013
- deps.clear();
1014
- }
1015
1072
 
1016
1073
  // src/reactivity/bindAttribute.ts
1017
1074
  var _isDev3 = isDev();
@@ -1603,32 +1660,64 @@ init_dev();
1603
1660
  var _g = globalThis;
1604
1661
  var _isDev7 = isDev();
1605
1662
  function signal(initial, options) {
1606
- const state = { value: initial };
1663
+ const state = {
1664
+ value: initial,
1665
+ __v: 0,
1666
+ __sc: 0,
1667
+ subsHead: null,
1668
+ subsTail: null,
1669
+ __activeNode: null,
1670
+ __name: void 0
1671
+ };
1607
1672
  const debugName = _isDev7 ? options?.name : void 0;
1608
1673
  const equalsFn = options?.equals;
1609
- if (debugName) {
1610
- state.__name = debugName;
1611
- }
1674
+ if (debugName) state.__name = debugName;
1612
1675
  function get() {
1613
1676
  recordDependency(state);
1614
1677
  return state.value;
1615
1678
  }
1616
1679
  get.__signal = state;
1617
1680
  if (debugName) get.__name = debugName;
1618
- function set(next) {
1619
- const newValue = typeof next === "function" ? next(state.value) : next;
1620
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
1621
- if (_isDev7) {
1622
- const oldValue = state.value;
1681
+ let set;
1682
+ if (equalsFn) {
1683
+ set = (next) => {
1684
+ const prev = state.value;
1685
+ const newValue = typeof next === "function" ? next(prev) : next;
1686
+ if (equalsFn(prev, newValue)) return;
1687
+ state.value = newValue;
1688
+ state.__v++;
1689
+ if (_isDev7) {
1690
+ const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1691
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
1692
+ }
1693
+ if (!enqueueBatchedSignal(state)) {
1694
+ notifySubscribers(state);
1695
+ }
1696
+ };
1697
+ } else if (_isDev7) {
1698
+ set = (next) => {
1699
+ const prev = state.value;
1700
+ const newValue = typeof next === "function" ? next(prev) : next;
1701
+ if (Object.is(newValue, prev)) return;
1623
1702
  state.value = newValue;
1703
+ state.__v++;
1624
1704
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1625
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
1626
- } else {
1705
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
1706
+ if (!enqueueBatchedSignal(state)) {
1707
+ notifySubscribers(state);
1708
+ }
1709
+ };
1710
+ } else {
1711
+ set = (next) => {
1712
+ const prev = state.value;
1713
+ const newValue = typeof next === "function" ? next(prev) : next;
1714
+ if (Object.is(newValue, prev)) return;
1627
1715
  state.value = newValue;
1628
- }
1629
- if (!enqueueBatchedSignal(state)) {
1630
- notifySubscribers(state);
1631
- }
1716
+ state.__v++;
1717
+ if (!enqueueBatchedSignal(state)) {
1718
+ notifySubscribers(state);
1719
+ }
1720
+ };
1632
1721
  }
1633
1722
  if (_isDev7) {
1634
1723
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
@@ -1669,92 +1758,122 @@ function getAvailableLocales() {
1669
1758
  init_dev();
1670
1759
  init_ssr_context();
1671
1760
  var _g2 = globalThis;
1761
+ var MAX_RERUNS = 100;
1762
+ function flushUserCleanups(ctx) {
1763
+ const list = ctx.userCleanups;
1764
+ if (list.length === 0) return;
1765
+ ctx.userCleanups = [];
1766
+ for (let i2 = list.length - 1; i2 >= 0; i2--) {
1767
+ try {
1768
+ list[i2]();
1769
+ } catch (err) {
1770
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
1771
+ }
1772
+ }
1773
+ }
1774
+ function drainReruns(ctx) {
1775
+ let reruns = 1;
1776
+ do {
1777
+ ctx.rerunPending = false;
1778
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1779
+ retrack(ctx.bodyFn, ctx.subscriber);
1780
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
1781
+ if (ctx.rerunPending) {
1782
+ ctx.rerunPending = false;
1783
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1784
+ console.error(
1785
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1786
+ );
1787
+ }
1788
+ }
1789
+ }
1790
+ function disposeEffect(ctx) {
1791
+ if (ctx.disposed) return;
1792
+ ctx.disposed = true;
1793
+ const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1794
+ if (h) {
1795
+ try {
1796
+ h.emit("effect:destroy", { effectFn: ctx.fn });
1797
+ } catch {
1798
+ }
1799
+ }
1800
+ try {
1801
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1802
+ } catch (err) {
1803
+ if (typeof console !== "undefined") {
1804
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
1805
+ }
1806
+ }
1807
+ try {
1808
+ cleanup(ctx.subscriber);
1809
+ } catch (err) {
1810
+ if (typeof console !== "undefined") {
1811
+ console.warn("[SibuJS effect] dispose threw:", err);
1812
+ }
1813
+ }
1814
+ }
1672
1815
  function effect(effectFn, options) {
1673
1816
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
1674
1817
  if (isSSR()) return () => {
1675
1818
  };
1676
- const onError = options?.onError;
1677
- let userCleanups = [];
1678
- const onCleanup = (fn) => {
1679
- userCleanups.push(fn);
1819
+ const ctx = {
1820
+ fn: effectFn,
1821
+ onError: options?.onError,
1822
+ userCleanups: [],
1823
+ running: false,
1824
+ rerunPending: false,
1825
+ disposed: false,
1826
+ onCleanup: null,
1827
+ subscriber: null,
1828
+ bodyFn: null
1680
1829
  };
1681
- const runUserCleanups = () => {
1682
- if (userCleanups.length === 0) return;
1683
- const list = userCleanups;
1684
- userCleanups = [];
1685
- for (let i2 = list.length - 1; i2 >= 0; i2--) {
1686
- try {
1687
- list[i2]();
1688
- } catch (err) {
1689
- if (typeof console !== "undefined") {
1690
- console.warn("[SibuJS effect] onCleanup threw:", err);
1691
- }
1692
- }
1693
- }
1830
+ ctx.onCleanup = (fn) => {
1831
+ ctx.userCleanups.push(fn);
1694
1832
  };
1695
- const invokeBody = () => effectFn(onCleanup);
1696
- const wrappedFn = onError ? () => {
1833
+ const onErrorCaptured = ctx.onError;
1834
+ ctx.bodyFn = onErrorCaptured ? () => {
1697
1835
  try {
1698
- invokeBody();
1836
+ ctx.fn(ctx.onCleanup);
1699
1837
  } catch (err) {
1700
- onError(err);
1838
+ onErrorCaptured(err);
1701
1839
  }
1702
- } : invokeBody;
1703
- let cleanupHandle = () => {
1840
+ } : () => {
1841
+ ctx.fn(ctx.onCleanup);
1704
1842
  };
1705
- let running = false;
1706
- const subscriber = () => {
1707
- if (running) {
1708
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1709
- console.warn(
1710
- "[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."
1711
- );
1712
- }
1843
+ const sub2 = (() => {
1844
+ if (ctx.running) {
1845
+ ctx.rerunPending = true;
1713
1846
  return;
1714
1847
  }
1715
- running = true;
1848
+ ctx.running = true;
1716
1849
  try {
1717
- runUserCleanups();
1718
- cleanupHandle();
1719
- cleanupHandle = track(wrappedFn, subscriber);
1850
+ ctx.rerunPending = false;
1851
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
1852
+ retrack(ctx.bodyFn, sub2);
1853
+ if (ctx.rerunPending) drainReruns(ctx);
1720
1854
  } finally {
1721
- running = false;
1855
+ ctx.running = false;
1856
+ ctx.rerunPending = false;
1722
1857
  }
1723
- };
1724
- running = true;
1858
+ });
1859
+ sub2.depsHead = null;
1860
+ sub2.depsTail = null;
1861
+ sub2._epoch = 0;
1862
+ sub2._structDirty = false;
1863
+ sub2._runEpoch = 0;
1864
+ sub2._runs = 0;
1865
+ ctx.subscriber = sub2;
1866
+ ctx.running = true;
1725
1867
  try {
1726
- cleanupHandle = track(wrappedFn, subscriber);
1868
+ retrack(ctx.bodyFn, ctx.subscriber);
1869
+ if (ctx.rerunPending) drainReruns(ctx);
1727
1870
  } finally {
1728
- running = false;
1871
+ ctx.running = false;
1872
+ ctx.rerunPending = false;
1729
1873
  }
1730
1874
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1731
1875
  if (hook) hook.emit("effect:create", { effectFn });
1732
- let disposed = false;
1733
- return () => {
1734
- if (disposed) return;
1735
- disposed = true;
1736
- const h = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1737
- if (h) {
1738
- try {
1739
- h.emit("effect:destroy", { effectFn });
1740
- } catch {
1741
- }
1742
- }
1743
- try {
1744
- runUserCleanups();
1745
- } catch (err) {
1746
- if (typeof console !== "undefined") {
1747
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
1748
- }
1749
- }
1750
- try {
1751
- cleanupHandle();
1752
- } catch (err) {
1753
- if (typeof console !== "undefined") {
1754
- console.warn("[SibuJS effect] dispose threw:", err);
1755
- }
1756
- }
1757
- };
1876
+ return () => disposeEffect(ctx);
1758
1877
  }
1759
1878
 
1760
1879
  // src/plugins/router.ts