sibujs 2.0.0 → 2.1.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 (60) hide show
  1. package/dist/browser.cjs +144 -102
  2. package/dist/browser.js +4 -4
  3. package/dist/build.cjs +183 -102
  4. package/dist/build.js +10 -10
  5. package/dist/cdn.global.js +7 -7
  6. package/dist/{chunk-MIUAXB7K.js → chunk-3DZP6OIT.js} +2 -2
  7. package/dist/{chunk-ITX6OO3F.js → chunk-45YP72ZQ.js} +1 -1
  8. package/dist/{chunk-ND2664SF.js → chunk-AMK2TYNW.js} +13 -9
  9. package/dist/{chunk-R73P76YZ.js → chunk-CWBVQML6.js} +1 -1
  10. package/dist/{chunk-54EDRCEF.js → chunk-DRUZZAK4.js} +1 -1
  11. package/dist/{chunk-3NSGB5JN.js → chunk-GWWURC5M.js} +2 -2
  12. package/dist/{chunk-SAHNHTFC.js → chunk-KGYT6UO6.js} +3 -3
  13. package/dist/{chunk-52YJLLRO.js → chunk-NASX6ST2.js} +1 -1
  14. package/dist/{chunk-O2MNQFLP.js → chunk-O6EFQ3KT.js} +5 -5
  15. package/dist/{chunk-GTBNNBJ6.js → chunk-OJ3P4ECI.js} +1 -1
  16. package/dist/{chunk-7JDB7I65.js → chunk-ON5MMR2J.js} +4 -4
  17. package/dist/{chunk-KLRMB5ZS.js → chunk-P2HSJDDN.js} +2 -2
  18. package/dist/{chunk-VLPPXTYG.js → chunk-QO3WC6FS.js} +145 -93
  19. package/dist/{chunk-CC65Y57T.js → chunk-RDTDJCAB.js} +1 -1
  20. package/dist/{chunk-JXMMDLBY.js → chunk-TH2ILCYW.js} +8 -4
  21. package/dist/{chunk-3LR7GLWQ.js → chunk-V6C4FADE.js} +3 -3
  22. package/dist/{chunk-WOMYAHHI.js → chunk-WANSMF2L.js} +4 -4
  23. package/dist/{chunk-DFPFITST.js → chunk-WIPZPFBQ.js} +1 -1
  24. package/dist/{chunk-HB24TBAF.js → chunk-WZA53FXU.js} +38 -10
  25. package/dist/{chunk-JA6667UN.js → chunk-ZAQSMOED.js} +4 -4
  26. package/dist/data.cjs +176 -102
  27. package/dist/data.js +6 -6
  28. package/dist/devtools.cjs +148 -91
  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 +176 -102
  33. package/dist/ecosystem.js +7 -7
  34. package/dist/extras.cjs +182 -104
  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 +185 -102
  39. package/dist/index.d.cts +15 -1
  40. package/dist/index.d.ts +15 -1
  41. package/dist/index.js +14 -10
  42. package/dist/{introspect-BWNjNw64.d.cts → introspect-2TOlQ7oa.d.cts} +3 -1
  43. package/dist/{introspect-cY2pg9pW.d.ts → introspect-DnIpHQQz.d.ts} +3 -1
  44. package/dist/motion.cjs +78 -62
  45. package/dist/motion.js +3 -3
  46. package/dist/patterns.cjs +176 -102
  47. package/dist/patterns.js +5 -5
  48. package/dist/performance.cjs +142 -89
  49. package/dist/performance.js +4 -4
  50. package/dist/plugins.cjs +142 -89
  51. package/dist/plugins.js +6 -6
  52. package/dist/ssr.cjs +144 -102
  53. package/dist/ssr.js +7 -7
  54. package/dist/testing.cjs +66 -28
  55. package/dist/testing.js +2 -2
  56. package/dist/ui.cjs +174 -89
  57. package/dist/ui.js +6 -6
  58. package/dist/widgets.cjs +176 -102
  59. package/dist/widgets.js +6 -6
  60. package/package.json +1 -1
@@ -221,11 +221,24 @@ function devWarn(message) {
221
221
 
222
222
  // src/reactivity/track.ts
223
223
  var _isDev2 = isDev();
224
- var subscriberStack = new Array(32);
225
- var stackCapacity = 32;
224
+ var STACK_INITIAL = 32;
225
+ var STACK_SHRINK_THRESHOLD = 128;
226
+ var subscriberStack = new Array(STACK_INITIAL);
227
+ var stackCapacity = STACK_INITIAL;
226
228
  var stackTop = -1;
227
229
  var currentSubscriber = null;
228
230
  var SUBS = "__s";
231
+ function syncFastPath(signal2, subs) {
232
+ const size = subs.size;
233
+ if (size === 0) {
234
+ signal2.__f = void 0;
235
+ delete signal2[SUBS];
236
+ } else if (size === 1) {
237
+ signal2.__f = subs.values().next().value;
238
+ } else {
239
+ signal2.__f = void 0;
240
+ }
241
+ }
229
242
  var notifyDepth = 0;
230
243
  var pendingQueue = [];
231
244
  var pendingSet = /* @__PURE__ */ new Set();
@@ -252,39 +265,94 @@ function track(effectFn, subscriber) {
252
265
  } finally {
253
266
  stackTop--;
254
267
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
268
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
269
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
270
+ subscriberStack.length = stackCapacity;
271
+ }
255
272
  }
256
273
  return () => cleanup(subscriber);
257
274
  }
258
275
  function recordDependency(signal2) {
259
276
  if (!currentSubscriber) return;
260
277
  const sub = currentSubscriber;
261
- if (sub._dep === signal2) return;
278
+ const epoch = sub._epoch;
279
+ if (sub._dep === signal2) {
280
+ sub._depEpoch = epoch;
281
+ return;
282
+ }
262
283
  const deps = sub._deps;
263
284
  if (deps) {
264
- if (deps.has(signal2)) return;
265
- deps.add(signal2);
285
+ deps.set(signal2, epoch);
266
286
  } else if (sub._dep !== void 0) {
267
- const set = /* @__PURE__ */ new Set();
268
- set.add(sub._dep);
269
- set.add(signal2);
270
- sub._deps = set;
287
+ const map = /* @__PURE__ */ new Map();
288
+ map.set(sub._dep, sub._depEpoch);
289
+ map.set(signal2, epoch);
290
+ sub._deps = map;
271
291
  sub._dep = void 0;
292
+ sub._depEpoch = void 0;
272
293
  } else {
273
294
  sub._dep = signal2;
295
+ sub._depEpoch = epoch;
274
296
  }
275
- let subs = signal2[SUBS];
297
+ const sig = signal2;
298
+ let subs = sig[SUBS];
276
299
  if (!subs) {
277
300
  subs = /* @__PURE__ */ new Set();
278
- signal2[SUBS] = subs;
301
+ sig[SUBS] = subs;
279
302
  }
303
+ const prevSize = subs.size;
280
304
  subs.add(currentSubscriber);
281
- if (subs.size === 1) {
282
- signal2.__f = currentSubscriber;
283
- } else if (signal2.__f !== void 0) {
284
- signal2.__f = void 0;
305
+ if (subs.size !== prevSize) {
306
+ if (subs.size === 1) {
307
+ sig.__f = currentSubscriber;
308
+ } else if (sig.__f !== void 0) {
309
+ sig.__f = void 0;
310
+ }
311
+ }
312
+ }
313
+ var maxSubscriberRepeats = 50;
314
+ var maxDrainIterations = 1e6;
315
+ var drainEpoch = 0;
316
+ function tickRepeat(sub) {
317
+ const s = sub;
318
+ if (s._runEpoch !== drainEpoch) {
319
+ s._runEpoch = drainEpoch;
320
+ s._runs = 1;
321
+ return false;
322
+ }
323
+ return ++s._runs > maxSubscriberRepeats;
324
+ }
325
+ function cycleError(sub) {
326
+ if (typeof console !== "undefined") {
327
+ const name = sub.__name ?? "<unnamed>";
328
+ console.error(
329
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
330
+ );
331
+ }
332
+ }
333
+ function absoluteDrainError() {
334
+ if (typeof console !== "undefined") {
335
+ console.error(
336
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
337
+ );
338
+ }
339
+ }
340
+ function drainQueue() {
341
+ let i = 0;
342
+ while (i < pendingQueue.length) {
343
+ if (i >= maxDrainIterations) {
344
+ absoluteDrainError();
345
+ break;
346
+ }
347
+ const sub = pendingQueue[i++];
348
+ if (tickRepeat(sub)) {
349
+ cycleError(sub);
350
+ break;
351
+ }
352
+ pendingSet.delete(sub);
353
+ safeInvoke(sub);
285
354
  }
286
355
  }
287
- var maxDrainIterations = 1e5;
288
356
  function propagateDirty(sub) {
289
357
  sub();
290
358
  const rootSig = sub._sig;
@@ -339,25 +407,16 @@ function notifySubscribers(signal2) {
339
407
  return;
340
408
  }
341
409
  notifyDepth++;
410
+ drainEpoch++;
342
411
  try {
343
412
  if (first._c) {
344
413
  propagateDirty(first);
414
+ } else if (tickRepeat(first)) {
415
+ cycleError(first);
345
416
  } else {
346
417
  safeInvoke(first);
347
418
  }
348
- let i = 0;
349
- while (i < pendingQueue.length) {
350
- if (i >= maxDrainIterations) {
351
- if (typeof console !== "undefined") {
352
- console.error(
353
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
354
- );
355
- }
356
- break;
357
- }
358
- safeInvoke(pendingQueue[i]);
359
- i++;
360
- }
419
+ drainQueue();
361
420
  } finally {
362
421
  notifyDepth--;
363
422
  if (notifyDepth === 0) {
@@ -381,44 +440,17 @@ function notifySubscribers(signal2) {
381
440
  return;
382
441
  }
383
442
  notifyDepth++;
443
+ drainEpoch++;
384
444
  try {
385
- let directCount = 0;
386
- let hasComputedSub = false;
387
445
  for (const sub of subs) {
388
- if (sub._c) hasComputedSub = true;
389
- pendingQueue[directCount++] = sub;
390
- }
391
- if (!hasComputedSub) {
392
- for (let i2 = 0; i2 < directCount; i2++) {
393
- safeInvoke(pendingQueue[i2]);
394
- }
395
- } else {
396
- for (let i2 = 0; i2 < directCount; i2++) {
397
- if (pendingQueue[i2]._c) {
398
- propagateDirty(pendingQueue[i2]);
399
- }
400
- }
401
- for (let i2 = 0; i2 < directCount; i2++) {
402
- const sub = pendingQueue[i2];
403
- if (!sub._c && !pendingSet.has(sub)) {
404
- pendingSet.add(sub);
405
- safeInvoke(sub);
406
- }
407
- }
408
- }
409
- let i = directCount;
410
- while (i < pendingQueue.length) {
411
- if (i - directCount >= maxDrainIterations) {
412
- if (typeof console !== "undefined") {
413
- console.error(
414
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
415
- );
416
- }
417
- break;
446
+ if (sub._c) {
447
+ propagateDirty(sub);
448
+ } else if (!pendingSet.has(sub)) {
449
+ pendingSet.add(sub);
450
+ pendingQueue.push(sub);
418
451
  }
419
- safeInvoke(pendingQueue[i]);
420
- i++;
421
452
  }
453
+ drainQueue();
422
454
  } finally {
423
455
  notifyDepth--;
424
456
  if (notifyDepth === 0) {
@@ -431,29 +463,22 @@ function cleanup(subscriber) {
431
463
  const sub = subscriber;
432
464
  const singleDep = sub._dep;
433
465
  if (singleDep !== void 0) {
434
- const subs = singleDep[SUBS];
435
- if (subs) {
436
- subs.delete(subscriber);
437
- if (singleDep.__f === subscriber) {
438
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
439
- } else if (subs.size === 1 && singleDep.__f === void 0) {
440
- singleDep.__f = subs.values().next().value;
441
- }
466
+ const sig = singleDep;
467
+ const subs = sig[SUBS];
468
+ if (subs?.delete(subscriber)) {
469
+ syncFastPath(sig, subs);
442
470
  }
443
471
  sub._dep = void 0;
472
+ sub._depEpoch = void 0;
444
473
  return;
445
474
  }
446
475
  const deps = sub._deps;
447
476
  if (!deps || deps.size === 0) return;
448
- for (const signal2 of deps) {
449
- const subs = signal2[SUBS];
450
- if (subs) {
451
- subs.delete(subscriber);
452
- if (signal2.__f === subscriber) {
453
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
454
- } else if (subs.size === 1 && signal2.__f === void 0) {
455
- signal2.__f = subs.values().next().value;
456
- }
477
+ for (const signal2 of deps.keys()) {
478
+ const sig = signal2;
479
+ const subs = sig[SUBS];
480
+ if (subs?.delete(subscriber)) {
481
+ syncFastPath(sig, subs);
457
482
  }
458
483
  }
459
484
  deps.clear();
@@ -520,29 +545,57 @@ function effect(effectFn, options) {
520
545
  let cleanupHandle = () => {
521
546
  };
522
547
  let running = false;
548
+ let rerunPending = false;
549
+ const MAX_RERUNS = 100;
523
550
  const subscriber = () => {
524
551
  if (running) {
525
- if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
526
- console.warn(
527
- "[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."
528
- );
529
- }
552
+ rerunPending = true;
530
553
  return;
531
554
  }
532
555
  running = true;
533
556
  try {
534
- runUserCleanups();
535
- cleanupHandle();
536
- cleanupHandle = track(wrappedFn, subscriber);
557
+ let reruns = 0;
558
+ do {
559
+ rerunPending = false;
560
+ runUserCleanups();
561
+ cleanupHandle();
562
+ cleanupHandle = track(wrappedFn, subscriber);
563
+ if (++reruns > MAX_RERUNS) {
564
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
565
+ console.error(
566
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
567
+ );
568
+ }
569
+ rerunPending = false;
570
+ break;
571
+ }
572
+ } while (rerunPending);
537
573
  } finally {
538
574
  running = false;
575
+ rerunPending = false;
539
576
  }
540
577
  };
541
578
  running = true;
542
579
  try {
543
- cleanupHandle = track(wrappedFn, subscriber);
580
+ let reruns = 0;
581
+ do {
582
+ rerunPending = false;
583
+ runUserCleanups();
584
+ cleanupHandle();
585
+ cleanupHandle = track(wrappedFn, subscriber);
586
+ if (++reruns > MAX_RERUNS) {
587
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
588
+ console.error(
589
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
590
+ );
591
+ }
592
+ rerunPending = false;
593
+ break;
594
+ }
595
+ } while (rerunPending);
544
596
  } finally {
545
597
  running = false;
598
+ rerunPending = false;
546
599
  }
547
600
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
548
601
  if (hook) hook.emit("effect:create", { effectFn });
@@ -33,15 +33,15 @@ import {
33
33
  transitionState,
34
34
  uniqueId,
35
35
  yieldToMain
36
- } from "./chunk-MIUAXB7K.js";
36
+ } from "./chunk-3DZP6OIT.js";
37
37
  import {
38
38
  trustHTML
39
39
  } from "./chunk-JYD2PWXH.js";
40
40
  import "./chunk-UCS6AMJ7.js";
41
- import "./chunk-HB24TBAF.js";
41
+ import "./chunk-WZA53FXU.js";
42
42
  import "./chunk-2RA7SHDA.js";
43
- import "./chunk-CC65Y57T.js";
44
- import "./chunk-VLPPXTYG.js";
43
+ import "./chunk-RDTDJCAB.js";
44
+ import "./chunk-QO3WC6FS.js";
45
45
  import "./chunk-LMLD24FC.js";
46
46
  export {
47
47
  DOMPool,
package/dist/plugins.cjs CHANGED
@@ -775,11 +775,24 @@ 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;
778
+ var STACK_INITIAL = 32;
779
+ var STACK_SHRINK_THRESHOLD = 128;
780
+ var subscriberStack = new Array(STACK_INITIAL);
781
+ var stackCapacity = STACK_INITIAL;
780
782
  var stackTop = -1;
781
783
  var currentSubscriber = null;
782
784
  var SUBS = "__s";
785
+ function syncFastPath(signal2, subs) {
786
+ const size = subs.size;
787
+ if (size === 0) {
788
+ signal2.__f = void 0;
789
+ delete signal2[SUBS];
790
+ } else if (size === 1) {
791
+ signal2.__f = subs.values().next().value;
792
+ } else {
793
+ signal2.__f = void 0;
794
+ }
795
+ }
783
796
  var notifyDepth = 0;
784
797
  var pendingQueue = [];
785
798
  var pendingSet = /* @__PURE__ */ new Set();
@@ -806,39 +819,94 @@ function track(effectFn, subscriber) {
806
819
  } finally {
807
820
  stackTop--;
808
821
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
822
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
823
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
824
+ subscriberStack.length = stackCapacity;
825
+ }
809
826
  }
810
827
  return () => cleanup(subscriber);
811
828
  }
812
829
  function recordDependency(signal2) {
813
830
  if (!currentSubscriber) return;
814
831
  const sub2 = currentSubscriber;
815
- if (sub2._dep === signal2) return;
832
+ const epoch = sub2._epoch;
833
+ if (sub2._dep === signal2) {
834
+ sub2._depEpoch = epoch;
835
+ return;
836
+ }
816
837
  const deps = sub2._deps;
817
838
  if (deps) {
818
- if (deps.has(signal2)) return;
819
- deps.add(signal2);
839
+ deps.set(signal2, epoch);
820
840
  } 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;
841
+ const map2 = /* @__PURE__ */ new Map();
842
+ map2.set(sub2._dep, sub2._depEpoch);
843
+ map2.set(signal2, epoch);
844
+ sub2._deps = map2;
825
845
  sub2._dep = void 0;
846
+ sub2._depEpoch = void 0;
826
847
  } else {
827
848
  sub2._dep = signal2;
849
+ sub2._depEpoch = epoch;
828
850
  }
829
- let subs = signal2[SUBS];
851
+ const sig = signal2;
852
+ let subs = sig[SUBS];
830
853
  if (!subs) {
831
854
  subs = /* @__PURE__ */ new Set();
832
- signal2[SUBS] = subs;
855
+ sig[SUBS] = subs;
833
856
  }
857
+ const prevSize = subs.size;
834
858
  subs.add(currentSubscriber);
835
- if (subs.size === 1) {
836
- signal2.__f = currentSubscriber;
837
- } else if (signal2.__f !== void 0) {
838
- signal2.__f = void 0;
859
+ if (subs.size !== prevSize) {
860
+ if (subs.size === 1) {
861
+ sig.__f = currentSubscriber;
862
+ } else if (sig.__f !== void 0) {
863
+ sig.__f = void 0;
864
+ }
865
+ }
866
+ }
867
+ var maxSubscriberRepeats = 50;
868
+ var maxDrainIterations = 1e6;
869
+ var drainEpoch = 0;
870
+ function tickRepeat(sub2) {
871
+ const s2 = sub2;
872
+ if (s2._runEpoch !== drainEpoch) {
873
+ s2._runEpoch = drainEpoch;
874
+ s2._runs = 1;
875
+ return false;
876
+ }
877
+ return ++s2._runs > maxSubscriberRepeats;
878
+ }
879
+ function cycleError(sub2) {
880
+ if (typeof console !== "undefined") {
881
+ const name = sub2.__name ?? "<unnamed>";
882
+ console.error(
883
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
884
+ );
885
+ }
886
+ }
887
+ function absoluteDrainError() {
888
+ if (typeof console !== "undefined") {
889
+ console.error(
890
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
891
+ );
892
+ }
893
+ }
894
+ function drainQueue() {
895
+ let i2 = 0;
896
+ while (i2 < pendingQueue.length) {
897
+ if (i2 >= maxDrainIterations) {
898
+ absoluteDrainError();
899
+ break;
900
+ }
901
+ const sub2 = pendingQueue[i2++];
902
+ if (tickRepeat(sub2)) {
903
+ cycleError(sub2);
904
+ break;
905
+ }
906
+ pendingSet.delete(sub2);
907
+ safeInvoke(sub2);
839
908
  }
840
909
  }
841
- var maxDrainIterations = 1e5;
842
910
  function propagateDirty(sub2) {
843
911
  sub2();
844
912
  const rootSig = sub2._sig;
@@ -893,25 +961,16 @@ function notifySubscribers(signal2) {
893
961
  return;
894
962
  }
895
963
  notifyDepth++;
964
+ drainEpoch++;
896
965
  try {
897
966
  if (first._c) {
898
967
  propagateDirty(first);
968
+ } else if (tickRepeat(first)) {
969
+ cycleError(first);
899
970
  } else {
900
971
  safeInvoke(first);
901
972
  }
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
- }
973
+ drainQueue();
915
974
  } finally {
916
975
  notifyDepth--;
917
976
  if (notifyDepth === 0) {
@@ -935,44 +994,17 @@ function notifySubscribers(signal2) {
935
994
  return;
936
995
  }
937
996
  notifyDepth++;
997
+ drainEpoch++;
938
998
  try {
939
- let directCount = 0;
940
- let hasComputedSub = false;
941
999
  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]);
953
- }
954
- }
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;
1000
+ if (sub2._c) {
1001
+ propagateDirty(sub2);
1002
+ } else if (!pendingSet.has(sub2)) {
1003
+ pendingSet.add(sub2);
1004
+ pendingQueue.push(sub2);
972
1005
  }
973
- safeInvoke(pendingQueue[i2]);
974
- i2++;
975
1006
  }
1007
+ drainQueue();
976
1008
  } finally {
977
1009
  notifyDepth--;
978
1010
  if (notifyDepth === 0) {
@@ -985,29 +1017,22 @@ function cleanup(subscriber) {
985
1017
  const sub2 = subscriber;
986
1018
  const singleDep = sub2._dep;
987
1019
  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
- }
1020
+ const sig = singleDep;
1021
+ const subs = sig[SUBS];
1022
+ if (subs?.delete(subscriber)) {
1023
+ syncFastPath(sig, subs);
996
1024
  }
997
1025
  sub2._dep = void 0;
1026
+ sub2._depEpoch = void 0;
998
1027
  return;
999
1028
  }
1000
1029
  const deps = sub2._deps;
1001
1030
  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
- }
1031
+ for (const signal2 of deps.keys()) {
1032
+ const sig = signal2;
1033
+ const subs = sig[SUBS];
1034
+ if (subs?.delete(subscriber)) {
1035
+ syncFastPath(sig, subs);
1011
1036
  }
1012
1037
  }
1013
1038
  deps.clear();
@@ -1703,29 +1728,57 @@ function effect(effectFn, options) {
1703
1728
  let cleanupHandle = () => {
1704
1729
  };
1705
1730
  let running = false;
1731
+ let rerunPending = false;
1732
+ const MAX_RERUNS = 100;
1706
1733
  const subscriber = () => {
1707
1734
  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
- }
1735
+ rerunPending = true;
1713
1736
  return;
1714
1737
  }
1715
1738
  running = true;
1716
1739
  try {
1717
- runUserCleanups();
1718
- cleanupHandle();
1719
- cleanupHandle = track(wrappedFn, subscriber);
1740
+ let reruns = 0;
1741
+ do {
1742
+ rerunPending = false;
1743
+ runUserCleanups();
1744
+ cleanupHandle();
1745
+ cleanupHandle = track(wrappedFn, subscriber);
1746
+ if (++reruns > MAX_RERUNS) {
1747
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1748
+ console.error(
1749
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1750
+ );
1751
+ }
1752
+ rerunPending = false;
1753
+ break;
1754
+ }
1755
+ } while (rerunPending);
1720
1756
  } finally {
1721
1757
  running = false;
1758
+ rerunPending = false;
1722
1759
  }
1723
1760
  };
1724
1761
  running = true;
1725
1762
  try {
1726
- cleanupHandle = track(wrappedFn, subscriber);
1763
+ let reruns = 0;
1764
+ do {
1765
+ rerunPending = false;
1766
+ runUserCleanups();
1767
+ cleanupHandle();
1768
+ cleanupHandle = track(wrappedFn, subscriber);
1769
+ if (++reruns > MAX_RERUNS) {
1770
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
1771
+ console.error(
1772
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
1773
+ );
1774
+ }
1775
+ rerunPending = false;
1776
+ break;
1777
+ }
1778
+ } while (rerunPending);
1727
1779
  } finally {
1728
1780
  running = false;
1781
+ rerunPending = false;
1729
1782
  }
1730
1783
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1731
1784
  if (hook) hook.emit("effect:create", { effectFn });
package/dist/plugins.js CHANGED
@@ -32,13 +32,13 @@ import {
32
32
  } from "./chunk-3JHCYHWN.js";
33
33
  import {
34
34
  span
35
- } from "./chunk-R73P76YZ.js";
35
+ } from "./chunk-CWBVQML6.js";
36
36
  import {
37
37
  escapeScriptJson,
38
38
  renderToString
39
39
  } from "./chunk-JYD2PWXH.js";
40
- import "./chunk-KLRMB5ZS.js";
41
- import "./chunk-DFPFITST.js";
40
+ import "./chunk-P2HSJDDN.js";
41
+ import "./chunk-WIPZPFBQ.js";
42
42
  import {
43
43
  dispose,
44
44
  registerDisposer
@@ -48,14 +48,14 @@ import {
48
48
  } from "./chunk-UCS6AMJ7.js";
49
49
  import {
50
50
  effect
51
- } from "./chunk-HB24TBAF.js";
51
+ } from "./chunk-WZA53FXU.js";
52
52
  import "./chunk-2RA7SHDA.js";
53
53
  import {
54
54
  signal
55
- } from "./chunk-CC65Y57T.js";
55
+ } from "./chunk-RDTDJCAB.js";
56
56
  import {
57
57
  track
58
- } from "./chunk-VLPPXTYG.js";
58
+ } from "./chunk-QO3WC6FS.js";
59
59
  import "./chunk-LMLD24FC.js";
60
60
 
61
61
  // src/plugins/i18n.ts