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
package/dist/motion.cjs CHANGED
@@ -289,7 +289,8 @@ function devWarn(message) {
289
289
 
290
290
  // src/reactivity/track.ts
291
291
  var _isDev2 = isDev();
292
- var subscriberStack = new Array(32);
292
+ var STACK_INITIAL = 32;
293
+ var subscriberStack = new Array(STACK_INITIAL);
293
294
  var currentSubscriber = null;
294
295
  var SUBS = "__s";
295
296
  var notifyDepth = 0;
@@ -306,33 +307,84 @@ function safeInvoke(sub) {
306
307
  function recordDependency(signal2) {
307
308
  if (!currentSubscriber) return;
308
309
  const sub = currentSubscriber;
309
- if (sub._dep === signal2) return;
310
+ const epoch = sub._epoch;
311
+ if (sub._dep === signal2) {
312
+ sub._depEpoch = epoch;
313
+ return;
314
+ }
310
315
  const deps = sub._deps;
311
316
  if (deps) {
312
- if (deps.has(signal2)) return;
313
- deps.add(signal2);
317
+ deps.set(signal2, epoch);
314
318
  } else if (sub._dep !== void 0) {
315
- const set = /* @__PURE__ */ new Set();
316
- set.add(sub._dep);
317
- set.add(signal2);
318
- sub._deps = set;
319
+ const map = /* @__PURE__ */ new Map();
320
+ map.set(sub._dep, sub._depEpoch);
321
+ map.set(signal2, epoch);
322
+ sub._deps = map;
319
323
  sub._dep = void 0;
324
+ sub._depEpoch = void 0;
320
325
  } else {
321
326
  sub._dep = signal2;
327
+ sub._depEpoch = epoch;
322
328
  }
323
- let subs = signal2[SUBS];
329
+ const sig = signal2;
330
+ let subs = sig[SUBS];
324
331
  if (!subs) {
325
332
  subs = /* @__PURE__ */ new Set();
326
- signal2[SUBS] = subs;
333
+ sig[SUBS] = subs;
327
334
  }
335
+ const prevSize = subs.size;
328
336
  subs.add(currentSubscriber);
329
- if (subs.size === 1) {
330
- signal2.__f = currentSubscriber;
331
- } else if (signal2.__f !== void 0) {
332
- signal2.__f = void 0;
337
+ if (subs.size !== prevSize) {
338
+ if (subs.size === 1) {
339
+ sig.__f = currentSubscriber;
340
+ } else if (sig.__f !== void 0) {
341
+ sig.__f = void 0;
342
+ }
343
+ }
344
+ }
345
+ var maxSubscriberRepeats = 50;
346
+ var maxDrainIterations = 1e6;
347
+ var drainEpoch = 0;
348
+ function tickRepeat(sub) {
349
+ const s = sub;
350
+ if (s._runEpoch !== drainEpoch) {
351
+ s._runEpoch = drainEpoch;
352
+ s._runs = 1;
353
+ return false;
354
+ }
355
+ return ++s._runs > maxSubscriberRepeats;
356
+ }
357
+ function cycleError(sub) {
358
+ if (typeof console !== "undefined") {
359
+ const name = sub.__name ?? "<unnamed>";
360
+ console.error(
361
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
362
+ );
363
+ }
364
+ }
365
+ function absoluteDrainError() {
366
+ if (typeof console !== "undefined") {
367
+ console.error(
368
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
369
+ );
370
+ }
371
+ }
372
+ function drainQueue() {
373
+ let i = 0;
374
+ while (i < pendingQueue.length) {
375
+ if (i >= maxDrainIterations) {
376
+ absoluteDrainError();
377
+ break;
378
+ }
379
+ const sub = pendingQueue[i++];
380
+ if (tickRepeat(sub)) {
381
+ cycleError(sub);
382
+ break;
383
+ }
384
+ pendingSet.delete(sub);
385
+ safeInvoke(sub);
333
386
  }
334
387
  }
335
- var maxDrainIterations = 1e5;
336
388
  function propagateDirty(sub) {
337
389
  sub();
338
390
  const rootSig = sub._sig;
@@ -387,25 +439,16 @@ function notifySubscribers(signal2) {
387
439
  return;
388
440
  }
389
441
  notifyDepth++;
442
+ drainEpoch++;
390
443
  try {
391
444
  if (first._c) {
392
445
  propagateDirty(first);
446
+ } else if (tickRepeat(first)) {
447
+ cycleError(first);
393
448
  } else {
394
449
  safeInvoke(first);
395
450
  }
396
- let i = 0;
397
- while (i < pendingQueue.length) {
398
- if (i >= maxDrainIterations) {
399
- if (typeof console !== "undefined") {
400
- console.error(
401
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
402
- );
403
- }
404
- break;
405
- }
406
- safeInvoke(pendingQueue[i]);
407
- i++;
408
- }
451
+ drainQueue();
409
452
  } finally {
410
453
  notifyDepth--;
411
454
  if (notifyDepth === 0) {
@@ -429,44 +472,17 @@ function notifySubscribers(signal2) {
429
472
  return;
430
473
  }
431
474
  notifyDepth++;
475
+ drainEpoch++;
432
476
  try {
433
- let directCount = 0;
434
- let hasComputedSub = false;
435
477
  for (const sub of subs) {
436
- if (sub._c) hasComputedSub = true;
437
- pendingQueue[directCount++] = sub;
438
- }
439
- if (!hasComputedSub) {
440
- for (let i2 = 0; i2 < directCount; i2++) {
441
- safeInvoke(pendingQueue[i2]);
442
- }
443
- } else {
444
- for (let i2 = 0; i2 < directCount; i2++) {
445
- if (pendingQueue[i2]._c) {
446
- propagateDirty(pendingQueue[i2]);
447
- }
448
- }
449
- for (let i2 = 0; i2 < directCount; i2++) {
450
- const sub = pendingQueue[i2];
451
- if (!sub._c && !pendingSet.has(sub)) {
452
- pendingSet.add(sub);
453
- safeInvoke(sub);
454
- }
455
- }
456
- }
457
- let i = directCount;
458
- while (i < pendingQueue.length) {
459
- if (i - directCount >= maxDrainIterations) {
460
- if (typeof console !== "undefined") {
461
- console.error(
462
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
463
- );
464
- }
465
- break;
478
+ if (sub._c) {
479
+ propagateDirty(sub);
480
+ } else if (!pendingSet.has(sub)) {
481
+ pendingSet.add(sub);
482
+ pendingQueue.push(sub);
466
483
  }
467
- safeInvoke(pendingQueue[i]);
468
- i++;
469
484
  }
485
+ drainQueue();
470
486
  } finally {
471
487
  notifyDepth--;
472
488
  if (notifyDepth === 0) {
package/dist/motion.js CHANGED
@@ -19,9 +19,9 @@ import {
19
19
  stagger,
20
20
  transition,
21
21
  viewTransition
22
- } from "./chunk-52YJLLRO.js";
23
- import "./chunk-CC65Y57T.js";
24
- import "./chunk-VLPPXTYG.js";
22
+ } from "./chunk-NASX6ST2.js";
23
+ import "./chunk-RDTDJCAB.js";
24
+ import "./chunk-QO3WC6FS.js";
25
25
  import "./chunk-LMLD24FC.js";
26
26
  export {
27
27
  TransitionGroup,
package/dist/patterns.cjs CHANGED
@@ -61,11 +61,24 @@ function devWarn(message) {
61
61
 
62
62
  // src/reactivity/track.ts
63
63
  var _isDev2 = isDev();
64
- var subscriberStack = new Array(32);
65
- var stackCapacity = 32;
64
+ var STACK_INITIAL = 32;
65
+ var STACK_SHRINK_THRESHOLD = 128;
66
+ var subscriberStack = new Array(STACK_INITIAL);
67
+ var stackCapacity = STACK_INITIAL;
66
68
  var stackTop = -1;
67
69
  var currentSubscriber = null;
68
70
  var SUBS = "__s";
71
+ function syncFastPath(signal2, subs) {
72
+ const size = subs.size;
73
+ if (size === 0) {
74
+ signal2.__f = void 0;
75
+ delete signal2[SUBS];
76
+ } else if (size === 1) {
77
+ signal2.__f = subs.values().next().value;
78
+ } else {
79
+ signal2.__f = void 0;
80
+ }
81
+ }
69
82
  var notifyDepth = 0;
70
83
  var pendingQueue = [];
71
84
  var pendingSet = /* @__PURE__ */ new Set();
@@ -78,13 +91,45 @@ function safeInvoke(sub) {
78
91
  }
79
92
  }
80
93
  var trackingSuspended = false;
94
+ var subscriberEpochCounter = 0;
81
95
  function retrack(effectFn, subscriber) {
82
96
  const prev = currentSubscriber;
83
97
  currentSubscriber = subscriber;
98
+ const sub = subscriber;
99
+ const epoch = ++subscriberEpochCounter;
100
+ sub._epoch = epoch;
84
101
  try {
85
102
  effectFn();
86
103
  } finally {
87
104
  currentSubscriber = prev;
105
+ pruneStaleDeps(sub, epoch);
106
+ }
107
+ }
108
+ function pruneStaleDeps(sub, currentEpoch) {
109
+ if (sub._dep !== void 0) {
110
+ if (sub._depEpoch !== currentEpoch) {
111
+ const sig = sub._dep;
112
+ const subs = sig[SUBS];
113
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
114
+ sub._dep = void 0;
115
+ sub._depEpoch = void 0;
116
+ }
117
+ return;
118
+ }
119
+ const deps = sub._deps;
120
+ if (!deps || deps.size === 0) return;
121
+ let stales;
122
+ for (const [signal2, epoch] of deps) {
123
+ if (epoch !== currentEpoch) {
124
+ (stales ?? (stales = [])).push(signal2);
125
+ }
126
+ }
127
+ if (!stales) return;
128
+ for (const signal2 of stales) {
129
+ deps.delete(signal2);
130
+ const sig = signal2;
131
+ const subs = sig[SUBS];
132
+ if (subs?.delete(sub)) syncFastPath(sig, subs);
88
133
  }
89
134
  }
90
135
  function track(effectFn, subscriber) {
@@ -102,36 +147,49 @@ function track(effectFn, subscriber) {
102
147
  } finally {
103
148
  stackTop--;
104
149
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
150
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
151
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
152
+ subscriberStack.length = stackCapacity;
153
+ }
105
154
  }
106
155
  return () => cleanup(subscriber);
107
156
  }
108
157
  function recordDependency(signal2) {
109
158
  if (!currentSubscriber) return;
110
159
  const sub = currentSubscriber;
111
- if (sub._dep === signal2) return;
160
+ const epoch = sub._epoch;
161
+ if (sub._dep === signal2) {
162
+ sub._depEpoch = epoch;
163
+ return;
164
+ }
112
165
  const deps = sub._deps;
113
166
  if (deps) {
114
- if (deps.has(signal2)) return;
115
- deps.add(signal2);
167
+ deps.set(signal2, epoch);
116
168
  } else if (sub._dep !== void 0) {
117
- const set = /* @__PURE__ */ new Set();
118
- set.add(sub._dep);
119
- set.add(signal2);
120
- sub._deps = set;
169
+ const map = /* @__PURE__ */ new Map();
170
+ map.set(sub._dep, sub._depEpoch);
171
+ map.set(signal2, epoch);
172
+ sub._deps = map;
121
173
  sub._dep = void 0;
174
+ sub._depEpoch = void 0;
122
175
  } else {
123
176
  sub._dep = signal2;
177
+ sub._depEpoch = epoch;
124
178
  }
125
- let subs = signal2[SUBS];
179
+ const sig = signal2;
180
+ let subs = sig[SUBS];
126
181
  if (!subs) {
127
182
  subs = /* @__PURE__ */ new Set();
128
- signal2[SUBS] = subs;
183
+ sig[SUBS] = subs;
129
184
  }
185
+ const prevSize = subs.size;
130
186
  subs.add(currentSubscriber);
131
- if (subs.size === 1) {
132
- signal2.__f = currentSubscriber;
133
- } else if (signal2.__f !== void 0) {
134
- signal2.__f = void 0;
187
+ if (subs.size !== prevSize) {
188
+ if (subs.size === 1) {
189
+ sig.__f = currentSubscriber;
190
+ } else if (sig.__f !== void 0) {
191
+ sig.__f = void 0;
192
+ }
135
193
  }
136
194
  }
137
195
  function queueSignalNotification(signal2) {
@@ -146,24 +204,55 @@ function queueSignalNotification(signal2) {
146
204
  }
147
205
  }
148
206
  }
149
- var maxDrainIterations = 1e5;
207
+ var maxSubscriberRepeats = 50;
208
+ var maxDrainIterations = 1e6;
209
+ var drainEpoch = 0;
210
+ function tickRepeat(sub) {
211
+ const s = sub;
212
+ if (s._runEpoch !== drainEpoch) {
213
+ s._runEpoch = drainEpoch;
214
+ s._runs = 1;
215
+ return false;
216
+ }
217
+ return ++s._runs > maxSubscriberRepeats;
218
+ }
219
+ function cycleError(sub) {
220
+ if (typeof console !== "undefined") {
221
+ const name = sub.__name ?? "<unnamed>";
222
+ console.error(
223
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
224
+ );
225
+ }
226
+ }
227
+ function absoluteDrainError() {
228
+ if (typeof console !== "undefined") {
229
+ console.error(
230
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
231
+ );
232
+ }
233
+ }
234
+ function drainQueue() {
235
+ let i = 0;
236
+ while (i < pendingQueue.length) {
237
+ if (i >= maxDrainIterations) {
238
+ absoluteDrainError();
239
+ break;
240
+ }
241
+ const sub = pendingQueue[i++];
242
+ if (tickRepeat(sub)) {
243
+ cycleError(sub);
244
+ break;
245
+ }
246
+ pendingSet.delete(sub);
247
+ safeInvoke(sub);
248
+ }
249
+ }
150
250
  function drainNotificationQueue() {
151
251
  if (notifyDepth > 0) return;
152
252
  notifyDepth++;
253
+ drainEpoch++;
153
254
  try {
154
- let i = 0;
155
- while (i < pendingQueue.length) {
156
- if (i >= maxDrainIterations) {
157
- if (typeof console !== "undefined") {
158
- console.error(
159
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
160
- );
161
- }
162
- break;
163
- }
164
- safeInvoke(pendingQueue[i]);
165
- i++;
166
- }
255
+ drainQueue();
167
256
  } finally {
168
257
  notifyDepth--;
169
258
  if (notifyDepth === 0) {
@@ -226,25 +315,16 @@ function notifySubscribers(signal2) {
226
315
  return;
227
316
  }
228
317
  notifyDepth++;
318
+ drainEpoch++;
229
319
  try {
230
320
  if (first._c) {
231
321
  propagateDirty(first);
322
+ } else if (tickRepeat(first)) {
323
+ cycleError(first);
232
324
  } else {
233
325
  safeInvoke(first);
234
326
  }
235
- let i = 0;
236
- while (i < pendingQueue.length) {
237
- if (i >= maxDrainIterations) {
238
- if (typeof console !== "undefined") {
239
- console.error(
240
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
241
- );
242
- }
243
- break;
244
- }
245
- safeInvoke(pendingQueue[i]);
246
- i++;
247
- }
327
+ drainQueue();
248
328
  } finally {
249
329
  notifyDepth--;
250
330
  if (notifyDepth === 0) {
@@ -268,44 +348,17 @@ function notifySubscribers(signal2) {
268
348
  return;
269
349
  }
270
350
  notifyDepth++;
351
+ drainEpoch++;
271
352
  try {
272
- let directCount = 0;
273
- let hasComputedSub = false;
274
353
  for (const sub of subs) {
275
- if (sub._c) hasComputedSub = true;
276
- pendingQueue[directCount++] = sub;
277
- }
278
- if (!hasComputedSub) {
279
- for (let i2 = 0; i2 < directCount; i2++) {
280
- safeInvoke(pendingQueue[i2]);
281
- }
282
- } else {
283
- for (let i2 = 0; i2 < directCount; i2++) {
284
- if (pendingQueue[i2]._c) {
285
- propagateDirty(pendingQueue[i2]);
286
- }
287
- }
288
- for (let i2 = 0; i2 < directCount; i2++) {
289
- const sub = pendingQueue[i2];
290
- if (!sub._c && !pendingSet.has(sub)) {
291
- pendingSet.add(sub);
292
- safeInvoke(sub);
293
- }
294
- }
295
- }
296
- let i = directCount;
297
- while (i < pendingQueue.length) {
298
- if (i - directCount >= maxDrainIterations) {
299
- if (typeof console !== "undefined") {
300
- console.error(
301
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
302
- );
303
- }
304
- break;
354
+ if (sub._c) {
355
+ propagateDirty(sub);
356
+ } else if (!pendingSet.has(sub)) {
357
+ pendingSet.add(sub);
358
+ pendingQueue.push(sub);
305
359
  }
306
- safeInvoke(pendingQueue[i]);
307
- i++;
308
360
  }
361
+ drainQueue();
309
362
  } finally {
310
363
  notifyDepth--;
311
364
  if (notifyDepth === 0) {
@@ -318,29 +371,22 @@ function cleanup(subscriber) {
318
371
  const sub = subscriber;
319
372
  const singleDep = sub._dep;
320
373
  if (singleDep !== void 0) {
321
- const subs = singleDep[SUBS];
322
- if (subs) {
323
- subs.delete(subscriber);
324
- if (singleDep.__f === subscriber) {
325
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
326
- } else if (subs.size === 1 && singleDep.__f === void 0) {
327
- singleDep.__f = subs.values().next().value;
328
- }
374
+ const sig = singleDep;
375
+ const subs = sig[SUBS];
376
+ if (subs?.delete(subscriber)) {
377
+ syncFastPath(sig, subs);
329
378
  }
330
379
  sub._dep = void 0;
380
+ sub._depEpoch = void 0;
331
381
  return;
332
382
  }
333
383
  const deps = sub._deps;
334
384
  if (!deps || deps.size === 0) return;
335
- for (const signal2 of deps) {
336
- const subs = signal2[SUBS];
337
- if (subs) {
338
- subs.delete(subscriber);
339
- if (signal2.__f === subscriber) {
340
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
341
- } else if (subs.size === 1 && signal2.__f === void 0) {
342
- signal2.__f = subs.values().next().value;
343
- }
385
+ for (const signal2 of deps.keys()) {
386
+ const sig = signal2;
387
+ const subs = sig[SUBS];
388
+ if (subs?.delete(subscriber)) {
389
+ syncFastPath(sig, subs);
344
390
  }
345
391
  }
346
392
  deps.clear();
@@ -537,29 +583,57 @@ function effect(effectFn, options) {
537
583
  let cleanupHandle = () => {
538
584
  };
539
585
  let running = false;
586
+ let rerunPending = false;
587
+ const MAX_RERUNS = 100;
540
588
  const subscriber = () => {
541
589
  if (running) {
542
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
543
- console.warn(
544
- "[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."
545
- );
546
- }
590
+ rerunPending = true;
547
591
  return;
548
592
  }
549
593
  running = true;
550
594
  try {
551
- runUserCleanups();
552
- cleanupHandle();
553
- cleanupHandle = track(wrappedFn, subscriber);
595
+ let reruns = 0;
596
+ do {
597
+ rerunPending = false;
598
+ runUserCleanups();
599
+ cleanupHandle();
600
+ cleanupHandle = track(wrappedFn, subscriber);
601
+ if (++reruns > MAX_RERUNS) {
602
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
603
+ console.error(
604
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
605
+ );
606
+ }
607
+ rerunPending = false;
608
+ break;
609
+ }
610
+ } while (rerunPending);
554
611
  } finally {
555
612
  running = false;
613
+ rerunPending = false;
556
614
  }
557
615
  };
558
616
  running = true;
559
617
  try {
560
- cleanupHandle = track(wrappedFn, subscriber);
618
+ let reruns = 0;
619
+ do {
620
+ rerunPending = false;
621
+ runUserCleanups();
622
+ cleanupHandle();
623
+ cleanupHandle = track(wrappedFn, subscriber);
624
+ if (++reruns > MAX_RERUNS) {
625
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
626
+ console.error(
627
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
628
+ );
629
+ }
630
+ rerunPending = false;
631
+ break;
632
+ }
633
+ } while (rerunPending);
561
634
  } finally {
562
635
  running = false;
636
+ rerunPending = false;
563
637
  }
564
638
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
565
639
  if (hook) hook.emit("effect:create", { effectFn });
package/dist/patterns.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  optimisticList,
6
6
  persisted,
7
7
  timeline
8
- } from "./chunk-3LR7GLWQ.js";
8
+ } from "./chunk-V6C4FADE.js";
9
9
  import {
10
10
  RenderProp,
11
11
  assertType,
@@ -22,11 +22,11 @@ import {
22
22
  withProps,
23
23
  withWrapper
24
24
  } from "./chunk-CNZ35WI2.js";
25
- import "./chunk-54EDRCEF.js";
26
- import "./chunk-HB24TBAF.js";
25
+ import "./chunk-DRUZZAK4.js";
26
+ import "./chunk-WZA53FXU.js";
27
27
  import "./chunk-2RA7SHDA.js";
28
- import "./chunk-CC65Y57T.js";
29
- import "./chunk-VLPPXTYG.js";
28
+ import "./chunk-RDTDJCAB.js";
29
+ import "./chunk-QO3WC6FS.js";
30
30
  import "./chunk-LMLD24FC.js";
31
31
  export {
32
32
  RenderProp,