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
@@ -221,11 +221,88 @@ 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;
226
- var stackTop = -1;
224
+ var POOL_MAX = 4096;
225
+ var nodePool = [];
226
+ function createNode() {
227
+ return {
228
+ sig: null,
229
+ sub: null,
230
+ epoch: 0,
231
+ sigPrev: null,
232
+ sigNext: null,
233
+ subPrev: null,
234
+ subNext: null,
235
+ prevActive: null
236
+ };
237
+ }
238
+ function allocNode(sig, sub, epoch) {
239
+ const n = nodePool.pop();
240
+ if (n) {
241
+ n.sig = sig;
242
+ n.sub = sub;
243
+ n.epoch = epoch;
244
+ return n;
245
+ }
246
+ const fresh = createNode();
247
+ fresh.sig = sig;
248
+ fresh.sub = sub;
249
+ fresh.epoch = epoch;
250
+ return fresh;
251
+ }
252
+ function freeNode(node) {
253
+ node.sig = null;
254
+ node.sub = null;
255
+ node.sigPrev = null;
256
+ node.sigNext = null;
257
+ node.subPrev = null;
258
+ node.subNext = null;
259
+ node.prevActive = null;
260
+ if (nodePool.length < POOL_MAX) nodePool.push(node);
261
+ }
262
+ function linkSignal(sig, node) {
263
+ const oldHead = sig.subsHead ?? null;
264
+ node.sigPrev = null;
265
+ node.sigNext = oldHead;
266
+ if (oldHead) oldHead.sigPrev = node;
267
+ else sig.subsTail = node;
268
+ sig.subsHead = node;
269
+ sig.__sc = (sig.__sc ?? 0) + 1;
270
+ }
271
+ function unlinkSignal(node) {
272
+ const sig = node.sig;
273
+ if (!sig) return;
274
+ const prev = node.sigPrev;
275
+ const next = node.sigNext;
276
+ if (prev) prev.sigNext = next;
277
+ else sig.subsHead = next;
278
+ if (next) next.sigPrev = prev;
279
+ else sig.subsTail = prev;
280
+ sig.__sc = (sig.__sc ?? 1) - 1;
281
+ if (sig.__activeNode === node) sig.__activeNode = node.prevActive;
282
+ if (sig.__sc === 0) {
283
+ sig.subsHead = null;
284
+ sig.subsTail = null;
285
+ }
286
+ }
287
+ function linkSub(sub, node) {
288
+ const oldTail = sub.depsTail ?? null;
289
+ node.subPrev = oldTail;
290
+ node.subNext = null;
291
+ if (oldTail) oldTail.subNext = node;
292
+ else sub.depsHead = node;
293
+ sub.depsTail = node;
294
+ }
295
+ function unlinkSub(node) {
296
+ const sub = node.sub;
297
+ if (!sub) return;
298
+ const prev = node.subPrev;
299
+ const next = node.subNext;
300
+ if (prev) prev.subNext = next;
301
+ else sub.depsHead = next;
302
+ if (next) next.subPrev = prev;
303
+ else sub.depsTail = prev;
304
+ }
227
305
  var currentSubscriber = null;
228
- var SUBS = "__s";
229
306
  var notifyDepth = 0;
230
307
  var pendingQueue = [];
231
308
  var pendingSet = /* @__PURE__ */ new Set();
@@ -237,54 +314,111 @@ function safeInvoke(sub) {
237
314
  if (_isDev2) devWarn(`Subscriber threw during notification: ${err instanceof Error ? err.message : String(err)}`);
238
315
  }
239
316
  }
240
- function track(effectFn, subscriber) {
241
- if (!subscriber) subscriber = effectFn;
242
- cleanup(subscriber);
243
- ++stackTop;
244
- if (stackTop >= stackCapacity) {
245
- stackCapacity *= 2;
246
- subscriberStack.length = stackCapacity;
247
- }
248
- subscriberStack[stackTop] = subscriber;
317
+ var subscriberEpochCounter = 0;
318
+ function retrack(effectFn, subscriber) {
319
+ const prev = currentSubscriber;
249
320
  currentSubscriber = subscriber;
321
+ const sub = subscriber;
322
+ const epoch = ++subscriberEpochCounter;
323
+ sub._epoch = epoch;
324
+ sub._structDirty = false;
325
+ for (let n = sub.depsHead ?? null; n !== null; n = n.subNext) {
326
+ const sig = n.sig;
327
+ n.prevActive = sig.__activeNode ?? null;
328
+ sig.__activeNode = n;
329
+ }
250
330
  try {
251
331
  effectFn();
252
332
  } finally {
253
- stackTop--;
254
- currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
333
+ currentSubscriber = prev;
334
+ let node = sub.depsHead ?? null;
335
+ while (node !== null) {
336
+ const next = node.subNext;
337
+ const sig = node.sig;
338
+ sig.__activeNode = node.prevActive;
339
+ node.prevActive = null;
340
+ if (node.epoch !== epoch) {
341
+ unlinkSub(node);
342
+ unlinkSignal(node);
343
+ freeNode(node);
344
+ }
345
+ node = next;
346
+ }
255
347
  }
256
- return () => cleanup(subscriber);
257
348
  }
258
349
  function recordDependency(signal2) {
259
350
  if (!currentSubscriber) return;
260
351
  const sub = currentSubscriber;
261
- if (sub._dep === signal2) return;
262
- const deps = sub._deps;
263
- if (deps) {
264
- if (deps.has(signal2)) return;
265
- deps.add(signal2);
266
- } 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;
271
- sub._dep = void 0;
272
- } else {
273
- sub._dep = signal2;
352
+ const sig = signal2;
353
+ const epoch = sub._epoch ?? 0;
354
+ const active = sig.__activeNode ?? null;
355
+ if (active !== null && active.sub === sub) {
356
+ active.epoch = epoch;
357
+ return;
358
+ }
359
+ const node = allocNode(signal2, sub, epoch);
360
+ node.prevActive = active;
361
+ sig.__activeNode = node;
362
+ linkSub(sub, node);
363
+ linkSignal(sig, node);
364
+ sub._structDirty = true;
365
+ }
366
+ function cleanup(subscriber) {
367
+ const sub = subscriber;
368
+ let node = sub.depsHead ?? null;
369
+ sub.depsHead = null;
370
+ sub.depsTail = null;
371
+ while (node) {
372
+ const next = node.subNext;
373
+ unlinkSignal(node);
374
+ freeNode(node);
375
+ node = next;
274
376
  }
275
- let subs = signal2[SUBS];
276
- if (!subs) {
277
- subs = /* @__PURE__ */ new Set();
278
- signal2[SUBS] = subs;
377
+ }
378
+ var maxSubscriberRepeats = 50;
379
+ var maxDrainIterations = 1e6;
380
+ var drainEpoch = 0;
381
+ function tickRepeat(sub) {
382
+ const s = sub;
383
+ if (s._runEpoch !== drainEpoch) {
384
+ s._runEpoch = drainEpoch;
385
+ s._runs = 1;
386
+ return false;
279
387
  }
280
- subs.add(currentSubscriber);
281
- if (subs.size === 1) {
282
- signal2.__f = currentSubscriber;
283
- } else if (signal2.__f !== void 0) {
284
- signal2.__f = void 0;
388
+ s._runs = (s._runs ?? 0) + 1;
389
+ return s._runs > maxSubscriberRepeats;
390
+ }
391
+ function cycleError(sub) {
392
+ if (typeof console !== "undefined") {
393
+ const name = sub.__name ?? "<unnamed>";
394
+ console.error(
395
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
396
+ );
397
+ }
398
+ }
399
+ function absoluteDrainError() {
400
+ if (typeof console !== "undefined") {
401
+ console.error(
402
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
403
+ );
404
+ }
405
+ }
406
+ function drainQueue() {
407
+ let i = 0;
408
+ while (i < pendingQueue.length) {
409
+ if (i >= maxDrainIterations) {
410
+ absoluteDrainError();
411
+ break;
412
+ }
413
+ const sub = pendingQueue[i++];
414
+ if (tickRepeat(sub)) {
415
+ cycleError(sub);
416
+ break;
417
+ }
418
+ pendingSet.delete(sub);
419
+ safeInvoke(sub);
285
420
  }
286
421
  }
287
- var maxDrainIterations = 1e5;
288
422
  function propagateDirty(sub) {
289
423
  sub();
290
424
  const rootSig = sub._sig;
@@ -294,131 +428,66 @@ function propagateDirty(sub) {
294
428
  stack.push(rootSig);
295
429
  while (stack.length > baseLen) {
296
430
  const sig = stack.pop();
297
- const first = sig.__f;
298
- if (first) {
299
- if (first._c) {
300
- const nSig = first._sig;
301
- if (!nSig._d) {
302
- nSig._d = true;
303
- stack.push(nSig);
304
- }
305
- } else if (!pendingSet.has(first)) {
306
- pendingSet.add(first);
307
- pendingQueue.push(first);
308
- }
309
- continue;
310
- }
311
- const subs = sig[SUBS];
312
- if (!subs) continue;
313
- for (const s of subs) {
314
- if (s._c) {
315
- const nSig = s._sig;
316
- if (nSig && !nSig._d) {
317
- nSig._d = true;
318
- stack.push(nSig);
319
- } else if (!nSig) {
320
- s();
431
+ let node = sig.subsHead ?? null;
432
+ while (node) {
433
+ const s = node.sub;
434
+ if (s) {
435
+ if (s._c) {
436
+ const nSig = s._sig;
437
+ if (nSig) {
438
+ if (!nSig._d) {
439
+ nSig._d = true;
440
+ stack.push(nSig);
441
+ }
442
+ } else {
443
+ s();
444
+ }
445
+ } else if (!pendingSet.has(s)) {
446
+ pendingSet.add(s);
447
+ pendingQueue.push(s);
321
448
  }
322
- } else if (!pendingSet.has(s)) {
323
- pendingSet.add(s);
324
- pendingQueue.push(s);
325
449
  }
450
+ node = node.sigNext;
326
451
  }
327
452
  }
328
453
  }
329
454
  function notifySubscribers(signal2) {
330
- const first = signal2.__f;
331
- if (first) {
332
- if (notifyDepth > 0) {
333
- if (first._c) {
334
- propagateDirty(first);
335
- } else if (!pendingSet.has(first)) {
336
- pendingSet.add(first);
337
- pendingQueue.push(first);
338
- }
339
- return;
340
- }
341
- notifyDepth++;
342
- try {
343
- if (first._c) {
344
- propagateDirty(first);
345
- } else {
346
- safeInvoke(first);
347
- }
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
- }
361
- } finally {
362
- notifyDepth--;
363
- if (notifyDepth === 0) {
364
- pendingQueue.length = 0;
365
- pendingSet.clear();
366
- }
367
- }
368
- return;
369
- }
370
- const subs = signal2[SUBS];
371
- if (!subs || subs.size === 0) return;
455
+ const sig = signal2;
456
+ const head = sig.subsHead;
457
+ if (!head) return;
372
458
  if (notifyDepth > 0) {
373
- for (const sub of subs) {
374
- if (sub._c) {
375
- propagateDirty(sub);
376
- } else if (!pendingSet.has(sub)) {
377
- pendingSet.add(sub);
378
- pendingQueue.push(sub);
459
+ let node = head;
460
+ while (node) {
461
+ const s = node.sub;
462
+ if (s) {
463
+ if (s._c) {
464
+ propagateDirty(s);
465
+ } else if (!pendingSet.has(s)) {
466
+ pendingSet.add(s);
467
+ pendingQueue.push(s);
468
+ }
379
469
  }
470
+ node = node.sigNext;
380
471
  }
381
472
  return;
382
473
  }
383
474
  notifyDepth++;
475
+ drainEpoch++;
384
476
  try {
385
- let directCount = 0;
386
- let hasComputedSub = false;
387
- 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]);
477
+ let node = head;
478
+ while (node) {
479
+ const s = node.sub;
480
+ if (s) {
481
+ if (s._c) {
482
+ propagateDirty(s);
483
+ } else if (!pendingSet.has(s)) {
484
+ pendingSet.add(s);
485
+ pendingQueue.push(s);
399
486
  }
400
487
  }
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;
418
- }
419
- safeInvoke(pendingQueue[i]);
420
- i++;
488
+ node = node.sigNext;
421
489
  }
490
+ drainQueue();
422
491
  } finally {
423
492
  notifyDepth--;
424
493
  if (notifyDepth === 0) {
@@ -427,37 +496,6 @@ function notifySubscribers(signal2) {
427
496
  }
428
497
  }
429
498
  }
430
- function cleanup(subscriber) {
431
- const sub = subscriber;
432
- const singleDep = sub._dep;
433
- 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
- }
442
- }
443
- sub._dep = void 0;
444
- return;
445
- }
446
- const deps = sub._deps;
447
- 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
- }
457
- }
458
- }
459
- deps.clear();
460
- }
461
499
 
462
500
  // src/core/ssr-context.ts
463
501
  var als = null;
@@ -486,92 +524,122 @@ function isSSR() {
486
524
 
487
525
  // src/core/signals/effect.ts
488
526
  var _g = globalThis;
527
+ var MAX_RERUNS = 100;
528
+ function flushUserCleanups(ctx) {
529
+ const list = ctx.userCleanups;
530
+ if (list.length === 0) return;
531
+ ctx.userCleanups = [];
532
+ for (let i = list.length - 1; i >= 0; i--) {
533
+ try {
534
+ list[i]();
535
+ } catch (err) {
536
+ if (typeof console !== "undefined") console.warn("[SibuJS effect] onCleanup threw:", err);
537
+ }
538
+ }
539
+ }
540
+ function drainReruns(ctx) {
541
+ let reruns = 1;
542
+ do {
543
+ ctx.rerunPending = false;
544
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
545
+ retrack(ctx.bodyFn, ctx.subscriber);
546
+ } while (ctx.rerunPending && ++reruns <= MAX_RERUNS);
547
+ if (ctx.rerunPending) {
548
+ ctx.rerunPending = false;
549
+ if (_g.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
550
+ console.error(
551
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
552
+ );
553
+ }
554
+ }
555
+ }
556
+ function disposeEffect(ctx) {
557
+ if (ctx.disposed) return;
558
+ ctx.disposed = true;
559
+ const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
560
+ if (h) {
561
+ try {
562
+ h.emit("effect:destroy", { effectFn: ctx.fn });
563
+ } catch {
564
+ }
565
+ }
566
+ try {
567
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
568
+ } catch (err) {
569
+ if (typeof console !== "undefined") {
570
+ console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
571
+ }
572
+ }
573
+ try {
574
+ cleanup(ctx.subscriber);
575
+ } catch (err) {
576
+ if (typeof console !== "undefined") {
577
+ console.warn("[SibuJS effect] dispose threw:", err);
578
+ }
579
+ }
580
+ }
489
581
  function effect(effectFn, options) {
490
582
  devAssert(typeof effectFn === "function", "effect: argument must be a function.");
491
583
  if (isSSR()) return () => {
492
584
  };
493
- const onError = options?.onError;
494
- let userCleanups = [];
495
- const onCleanup = (fn) => {
496
- userCleanups.push(fn);
585
+ const ctx = {
586
+ fn: effectFn,
587
+ onError: options?.onError,
588
+ userCleanups: [],
589
+ running: false,
590
+ rerunPending: false,
591
+ disposed: false,
592
+ onCleanup: null,
593
+ subscriber: null,
594
+ bodyFn: null
497
595
  };
498
- const runUserCleanups = () => {
499
- if (userCleanups.length === 0) return;
500
- const list = userCleanups;
501
- userCleanups = [];
502
- for (let i = list.length - 1; i >= 0; i--) {
503
- try {
504
- list[i]();
505
- } catch (err) {
506
- if (typeof console !== "undefined") {
507
- console.warn("[SibuJS effect] onCleanup threw:", err);
508
- }
509
- }
510
- }
596
+ ctx.onCleanup = (fn) => {
597
+ ctx.userCleanups.push(fn);
511
598
  };
512
- const invokeBody = () => effectFn(onCleanup);
513
- const wrappedFn = onError ? () => {
599
+ const onErrorCaptured = ctx.onError;
600
+ ctx.bodyFn = onErrorCaptured ? () => {
514
601
  try {
515
- invokeBody();
602
+ ctx.fn(ctx.onCleanup);
516
603
  } catch (err) {
517
- onError(err);
604
+ onErrorCaptured(err);
518
605
  }
519
- } : invokeBody;
520
- let cleanupHandle = () => {
606
+ } : () => {
607
+ ctx.fn(ctx.onCleanup);
521
608
  };
522
- let running = false;
523
- const subscriber = () => {
524
- 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
- }
609
+ const sub = (() => {
610
+ if (ctx.running) {
611
+ ctx.rerunPending = true;
530
612
  return;
531
613
  }
532
- running = true;
614
+ ctx.running = true;
533
615
  try {
534
- runUserCleanups();
535
- cleanupHandle();
536
- cleanupHandle = track(wrappedFn, subscriber);
616
+ ctx.rerunPending = false;
617
+ if (ctx.userCleanups.length > 0) flushUserCleanups(ctx);
618
+ retrack(ctx.bodyFn, sub);
619
+ if (ctx.rerunPending) drainReruns(ctx);
537
620
  } finally {
538
- running = false;
621
+ ctx.running = false;
622
+ ctx.rerunPending = false;
539
623
  }
540
- };
541
- running = true;
624
+ });
625
+ sub.depsHead = null;
626
+ sub.depsTail = null;
627
+ sub._epoch = 0;
628
+ sub._structDirty = false;
629
+ sub._runEpoch = 0;
630
+ sub._runs = 0;
631
+ ctx.subscriber = sub;
632
+ ctx.running = true;
542
633
  try {
543
- cleanupHandle = track(wrappedFn, subscriber);
634
+ retrack(ctx.bodyFn, ctx.subscriber);
635
+ if (ctx.rerunPending) drainReruns(ctx);
544
636
  } finally {
545
- running = false;
637
+ ctx.running = false;
638
+ ctx.rerunPending = false;
546
639
  }
547
640
  const hook = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
548
641
  if (hook) hook.emit("effect:create", { effectFn });
549
- let disposed = false;
550
- return () => {
551
- if (disposed) return;
552
- disposed = true;
553
- const h = _g.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
554
- if (h) {
555
- try {
556
- h.emit("effect:destroy", { effectFn });
557
- } catch {
558
- }
559
- }
560
- try {
561
- runUserCleanups();
562
- } catch (err) {
563
- if (typeof console !== "undefined") {
564
- console.warn("[SibuJS effect] onCleanup threw during dispose:", err);
565
- }
566
- }
567
- try {
568
- cleanupHandle();
569
- } catch (err) {
570
- if (typeof console !== "undefined") {
571
- console.warn("[SibuJS effect] dispose threw:", err);
572
- }
573
- }
574
- };
642
+ return () => disposeEffect(ctx);
575
643
  }
576
644
 
577
645
  // src/reactivity/batch.ts
@@ -587,32 +655,64 @@ function enqueueBatchedSignal(signal2) {
587
655
  var _g2 = globalThis;
588
656
  var _isDev3 = isDev();
589
657
  function signal(initial, options) {
590
- const state = { value: initial };
658
+ const state = {
659
+ value: initial,
660
+ __v: 0,
661
+ __sc: 0,
662
+ subsHead: null,
663
+ subsTail: null,
664
+ __activeNode: null,
665
+ __name: void 0
666
+ };
591
667
  const debugName = _isDev3 ? options?.name : void 0;
592
668
  const equalsFn = options?.equals;
593
- if (debugName) {
594
- state.__name = debugName;
595
- }
669
+ if (debugName) state.__name = debugName;
596
670
  function get() {
597
671
  recordDependency(state);
598
672
  return state.value;
599
673
  }
600
674
  get.__signal = state;
601
675
  if (debugName) get.__name = debugName;
602
- function set(next) {
603
- const newValue = typeof next === "function" ? next(state.value) : next;
604
- if (equalsFn ? equalsFn(state.value, newValue) : Object.is(newValue, state.value)) return;
605
- if (_isDev3) {
606
- const oldValue = state.value;
676
+ let set;
677
+ if (equalsFn) {
678
+ set = (next) => {
679
+ const prev = state.value;
680
+ const newValue = typeof next === "function" ? next(prev) : next;
681
+ if (equalsFn(prev, newValue)) return;
682
+ state.value = newValue;
683
+ state.__v++;
684
+ if (_isDev3) {
685
+ const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
686
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
687
+ }
688
+ if (!enqueueBatchedSignal(state)) {
689
+ notifySubscribers(state);
690
+ }
691
+ };
692
+ } else if (_isDev3) {
693
+ set = (next) => {
694
+ const prev = state.value;
695
+ const newValue = typeof next === "function" ? next(prev) : next;
696
+ if (Object.is(newValue, prev)) return;
607
697
  state.value = newValue;
698
+ state.__v++;
608
699
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
609
- if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue, newValue });
610
- } else {
700
+ if (hook) hook.emit("signal:update", { signal: state, name: debugName, oldValue: prev, newValue });
701
+ if (!enqueueBatchedSignal(state)) {
702
+ notifySubscribers(state);
703
+ }
704
+ };
705
+ } else {
706
+ set = (next) => {
707
+ const prev = state.value;
708
+ const newValue = typeof next === "function" ? next(prev) : next;
709
+ if (Object.is(newValue, prev)) return;
611
710
  state.value = newValue;
612
- }
613
- if (!enqueueBatchedSignal(state)) {
614
- notifySubscribers(state);
615
- }
711
+ state.__v++;
712
+ if (!enqueueBatchedSignal(state)) {
713
+ notifySubscribers(state);
714
+ }
715
+ };
616
716
  }
617
717
  if (_isDev3) {
618
718
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;