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/index.cjs CHANGED
@@ -169,6 +169,7 @@ __export(index_exports, {
169
169
  registerComponent: () => registerComponent,
170
170
  registerDisposer: () => registerDisposer,
171
171
  resolveComponent: () => resolveComponent,
172
+ retrack: () => retrack,
172
173
  rp: () => rp,
173
174
  rt: () => rt,
174
175
  ruby: () => ruby,
@@ -179,6 +180,7 @@ __export(index_exports, {
179
180
  section: () => section,
180
181
  select: () => select,
181
182
  setGlobalErrorHandler: () => setGlobalErrorHandler,
183
+ setMaxDrainIterations: () => setMaxDrainIterations,
182
184
  show: () => show,
183
185
  signal: () => signal,
184
186
  slot: () => slot,
@@ -316,11 +318,24 @@ function isUrlAttribute(attr) {
316
318
 
317
319
  // src/reactivity/track.ts
318
320
  var _isDev2 = isDev();
319
- var subscriberStack = new Array(32);
320
- var stackCapacity = 32;
321
+ var STACK_INITIAL = 32;
322
+ var STACK_SHRINK_THRESHOLD = 128;
323
+ var subscriberStack = new Array(STACK_INITIAL);
324
+ var stackCapacity = STACK_INITIAL;
321
325
  var stackTop = -1;
322
326
  var currentSubscriber = null;
323
327
  var SUBS = "__s";
328
+ function syncFastPath(signal2, subs) {
329
+ const size = subs.size;
330
+ if (size === 0) {
331
+ signal2.__f = void 0;
332
+ delete signal2[SUBS];
333
+ } else if (size === 1) {
334
+ signal2.__f = subs.values().next().value;
335
+ } else {
336
+ signal2.__f = void 0;
337
+ }
338
+ }
324
339
  var notifyDepth = 0;
325
340
  var pendingQueue = [];
326
341
  var pendingSet = /* @__PURE__ */ new Set();
@@ -334,13 +349,45 @@ function safeInvoke(sub2) {
334
349
  }
335
350
  var suspendDepth = 0;
336
351
  var trackingSuspended = false;
352
+ var subscriberEpochCounter = 0;
337
353
  function retrack(effectFn, subscriber) {
338
354
  const prev = currentSubscriber;
339
355
  currentSubscriber = subscriber;
356
+ const sub2 = subscriber;
357
+ const epoch = ++subscriberEpochCounter;
358
+ sub2._epoch = epoch;
340
359
  try {
341
360
  effectFn();
342
361
  } finally {
343
362
  currentSubscriber = prev;
363
+ pruneStaleDeps(sub2, epoch);
364
+ }
365
+ }
366
+ function pruneStaleDeps(sub2, currentEpoch) {
367
+ if (sub2._dep !== void 0) {
368
+ if (sub2._depEpoch !== currentEpoch) {
369
+ const sig = sub2._dep;
370
+ const subs = sig[SUBS];
371
+ if (subs?.delete(sub2)) syncFastPath(sig, subs);
372
+ sub2._dep = void 0;
373
+ sub2._depEpoch = void 0;
374
+ }
375
+ return;
376
+ }
377
+ const deps = sub2._deps;
378
+ if (!deps || deps.size === 0) return;
379
+ let stales;
380
+ for (const [signal2, epoch] of deps) {
381
+ if (epoch !== currentEpoch) {
382
+ (stales ?? (stales = [])).push(signal2);
383
+ }
384
+ }
385
+ if (!stales) return;
386
+ for (const signal2 of stales) {
387
+ deps.delete(signal2);
388
+ const sig = signal2;
389
+ const subs = sig[SUBS];
390
+ if (subs?.delete(sub2)) syncFastPath(sig, subs);
344
391
  }
345
392
  }
346
393
  function track(effectFn, subscriber) {
@@ -358,6 +405,10 @@ function track(effectFn, subscriber) {
358
405
  } finally {
359
406
  stackTop--;
360
407
  currentSubscriber = stackTop >= 0 ? subscriberStack[stackTop] : null;
408
+ if (stackTop < 0 && stackCapacity > STACK_SHRINK_THRESHOLD) {
409
+ stackCapacity = Math.max(STACK_INITIAL, stackCapacity >>> 1);
410
+ subscriberStack.length = stackCapacity;
411
+ }
361
412
  }
362
413
  return () => cleanup(subscriber);
363
414
  }
@@ -393,30 +444,39 @@ function untracked(fn) {
393
444
  function recordDependency(signal2) {
394
445
  if (!currentSubscriber) return;
395
446
  const sub2 = currentSubscriber;
396
- if (sub2._dep === signal2) return;
447
+ const epoch = sub2._epoch;
448
+ if (sub2._dep === signal2) {
449
+ sub2._depEpoch = epoch;
450
+ return;
451
+ }
397
452
  const deps = sub2._deps;
398
453
  if (deps) {
399
- if (deps.has(signal2)) return;
400
- deps.add(signal2);
454
+ deps.set(signal2, epoch);
401
455
  } else if (sub2._dep !== void 0) {
402
- const set = /* @__PURE__ */ new Set();
403
- set.add(sub2._dep);
404
- set.add(signal2);
405
- sub2._deps = set;
456
+ const map2 = /* @__PURE__ */ new Map();
457
+ map2.set(sub2._dep, sub2._depEpoch);
458
+ map2.set(signal2, epoch);
459
+ sub2._deps = map2;
406
460
  sub2._dep = void 0;
461
+ sub2._depEpoch = void 0;
407
462
  } else {
408
463
  sub2._dep = signal2;
464
+ sub2._depEpoch = epoch;
409
465
  }
410
- let subs = signal2[SUBS];
466
+ const sig = signal2;
467
+ let subs = sig[SUBS];
411
468
  if (!subs) {
412
469
  subs = /* @__PURE__ */ new Set();
413
- signal2[SUBS] = subs;
470
+ sig[SUBS] = subs;
414
471
  }
472
+ const prevSize = subs.size;
415
473
  subs.add(currentSubscriber);
416
- if (subs.size === 1) {
417
- signal2.__f = currentSubscriber;
418
- } else if (signal2.__f !== void 0) {
419
- signal2.__f = void 0;
474
+ if (subs.size !== prevSize) {
475
+ if (subs.size === 1) {
476
+ sig.__f = currentSubscriber;
477
+ } else if (sig.__f !== void 0) {
478
+ sig.__f = void 0;
479
+ }
420
480
  }
421
481
  }
422
482
  function queueSignalNotification(signal2) {
@@ -431,24 +491,60 @@ function queueSignalNotification(signal2) {
431
491
  }
432
492
  }
433
493
  }
434
- var maxDrainIterations = 1e5;
494
+ var maxSubscriberRepeats = 50;
495
+ var maxDrainIterations = 1e6;
496
+ var drainEpoch = 0;
497
+ function setMaxDrainIterations(n) {
498
+ const prev = maxDrainIterations;
499
+ if (Number.isFinite(n) && n > 0) maxDrainIterations = Math.floor(n);
500
+ return prev;
501
+ }
502
+ function tickRepeat(sub2) {
503
+ const s2 = sub2;
504
+ if (s2._runEpoch !== drainEpoch) {
505
+ s2._runEpoch = drainEpoch;
506
+ s2._runs = 1;
507
+ return false;
508
+ }
509
+ return ++s2._runs > maxSubscriberRepeats;
510
+ }
511
+ function cycleError(sub2) {
512
+ if (typeof console !== "undefined") {
513
+ const name = sub2.__name ?? "<unnamed>";
514
+ console.error(
515
+ `[SibuJS] subscriber "${name}" fired more than ${maxSubscriberRepeats} times \u2014 likely a write-reads-self cycle between effects/signals. Breaking to prevent infinite loop.`
516
+ );
517
+ }
518
+ }
519
+ function absoluteDrainError() {
520
+ if (typeof console !== "undefined") {
521
+ console.error(
522
+ `[SibuJS] Notification drain exceeded ${maxDrainIterations} iterations \u2014 absolute safety net tripped. Breaking to prevent infinite loop.`
523
+ );
524
+ }
525
+ }
526
+ function drainQueue() {
527
+ let i2 = 0;
528
+ while (i2 < pendingQueue.length) {
529
+ if (i2 >= maxDrainIterations) {
530
+ absoluteDrainError();
531
+ break;
532
+ }
533
+ const sub2 = pendingQueue[i2++];
534
+ if (tickRepeat(sub2)) {
535
+ cycleError(sub2);
536
+ break;
537
+ }
538
+ pendingSet.delete(sub2);
539
+ safeInvoke(sub2);
540
+ }
541
+ }
435
542
  function drainNotificationQueue() {
436
543
  if (notifyDepth > 0) return;
437
544
  notifyDepth++;
545
+ drainEpoch++;
438
546
  try {
439
- let i2 = 0;
440
- while (i2 < pendingQueue.length) {
441
- if (i2 >= maxDrainIterations) {
442
- if (typeof console !== "undefined") {
443
- console.error(
444
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
445
- );
446
- }
447
- break;
448
- }
449
- safeInvoke(pendingQueue[i2]);
450
- i2++;
451
- }
547
+ drainQueue();
452
548
  } finally {
453
549
  notifyDepth--;
454
550
  if (notifyDepth === 0) {
@@ -511,25 +607,16 @@ function notifySubscribers(signal2) {
511
607
  return;
512
608
  }
513
609
  notifyDepth++;
610
+ drainEpoch++;
514
611
  try {
515
612
  if (first._c) {
516
613
  propagateDirty(first);
614
+ } else if (tickRepeat(first)) {
615
+ cycleError(first);
517
616
  } else {
518
617
  safeInvoke(first);
519
618
  }
520
- let i2 = 0;
521
- while (i2 < pendingQueue.length) {
522
- if (i2 >= maxDrainIterations) {
523
- if (typeof console !== "undefined") {
524
- console.error(
525
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
526
- );
527
- }
528
- break;
529
- }
530
- safeInvoke(pendingQueue[i2]);
531
- i2++;
532
- }
619
+ drainQueue();
533
620
  } finally {
534
621
  notifyDepth--;
535
622
  if (notifyDepth === 0) {
@@ -553,44 +640,17 @@ function notifySubscribers(signal2) {
553
640
  return;
554
641
  }
555
642
  notifyDepth++;
643
+ drainEpoch++;
556
644
  try {
557
- let directCount = 0;
558
- let hasComputedSub = false;
559
645
  for (const sub2 of subs) {
560
- if (sub2._c) hasComputedSub = true;
561
- pendingQueue[directCount++] = sub2;
562
- }
563
- if (!hasComputedSub) {
564
- for (let i3 = 0; i3 < directCount; i3++) {
565
- safeInvoke(pendingQueue[i3]);
566
- }
567
- } else {
568
- for (let i3 = 0; i3 < directCount; i3++) {
569
- if (pendingQueue[i3]._c) {
570
- propagateDirty(pendingQueue[i3]);
571
- }
572
- }
573
- for (let i3 = 0; i3 < directCount; i3++) {
574
- const sub2 = pendingQueue[i3];
575
- if (!sub2._c && !pendingSet.has(sub2)) {
576
- pendingSet.add(sub2);
577
- safeInvoke(sub2);
578
- }
579
- }
580
- }
581
- let i2 = directCount;
582
- while (i2 < pendingQueue.length) {
583
- if (i2 - directCount >= maxDrainIterations) {
584
- if (typeof console !== "undefined") {
585
- console.error(
586
- `[SibuJS] Notification queue exceeded ${maxDrainIterations} iterations \u2014 likely an effect that writes to a signal it reads. Breaking to prevent infinite loop.`
587
- );
588
- }
589
- break;
646
+ if (sub2._c) {
647
+ propagateDirty(sub2);
648
+ } else if (!pendingSet.has(sub2)) {
649
+ pendingSet.add(sub2);
650
+ pendingQueue.push(sub2);
590
651
  }
591
- safeInvoke(pendingQueue[i2]);
592
- i2++;
593
652
  }
653
+ drainQueue();
594
654
  } finally {
595
655
  notifyDepth--;
596
656
  if (notifyDepth === 0) {
@@ -603,29 +663,22 @@ function cleanup(subscriber) {
603
663
  const sub2 = subscriber;
604
664
  const singleDep = sub2._dep;
605
665
  if (singleDep !== void 0) {
606
- const subs = singleDep[SUBS];
607
- if (subs) {
608
- subs.delete(subscriber);
609
- if (singleDep.__f === subscriber) {
610
- singleDep.__f = subs.size === 1 ? subs.values().next().value : void 0;
611
- } else if (subs.size === 1 && singleDep.__f === void 0) {
612
- singleDep.__f = subs.values().next().value;
613
- }
666
+ const sig = singleDep;
667
+ const subs = sig[SUBS];
668
+ if (subs?.delete(subscriber)) {
669
+ syncFastPath(sig, subs);
614
670
  }
615
671
  sub2._dep = void 0;
672
+ sub2._depEpoch = void 0;
616
673
  return;
617
674
  }
618
675
  const deps = sub2._deps;
619
676
  if (!deps || deps.size === 0) return;
620
- for (const signal2 of deps) {
621
- const subs = signal2[SUBS];
622
- if (subs) {
623
- subs.delete(subscriber);
624
- if (signal2.__f === subscriber) {
625
- signal2.__f = subs.size === 1 ? subs.values().next().value : void 0;
626
- } else if (subs.size === 1 && signal2.__f === void 0) {
627
- signal2.__f = subs.values().next().value;
628
- }
677
+ for (const signal2 of deps.keys()) {
678
+ const sig = signal2;
679
+ const subs = sig[SUBS];
680
+ if (subs?.delete(subscriber)) {
681
+ syncFastPath(sig, subs);
629
682
  }
630
683
  }
631
684
  deps.clear();
@@ -2412,29 +2465,57 @@ function effect(effectFn, options) {
2412
2465
  let cleanupHandle = () => {
2413
2466
  };
2414
2467
  let running = false;
2468
+ let rerunPending = false;
2469
+ const MAX_RERUNS = 100;
2415
2470
  const subscriber = () => {
2416
2471
  if (running) {
2417
- if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
2418
- console.warn(
2419
- "[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."
2420
- );
2421
- }
2472
+ rerunPending = true;
2422
2473
  return;
2423
2474
  }
2424
2475
  running = true;
2425
2476
  try {
2426
- runUserCleanups();
2427
- cleanupHandle();
2428
- cleanupHandle = track(wrappedFn, subscriber);
2477
+ let reruns = 0;
2478
+ do {
2479
+ rerunPending = false;
2480
+ runUserCleanups();
2481
+ cleanupHandle();
2482
+ cleanupHandle = track(wrappedFn, subscriber);
2483
+ if (++reruns > MAX_RERUNS) {
2484
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
2485
+ console.error(
2486
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
2487
+ );
2488
+ }
2489
+ rerunPending = false;
2490
+ break;
2491
+ }
2492
+ } while (rerunPending);
2429
2493
  } finally {
2430
2494
  running = false;
2495
+ rerunPending = false;
2431
2496
  }
2432
2497
  };
2433
2498
  running = true;
2434
2499
  try {
2435
- cleanupHandle = track(wrappedFn, subscriber);
2500
+ let reruns = 0;
2501
+ do {
2502
+ rerunPending = false;
2503
+ runUserCleanups();
2504
+ cleanupHandle();
2505
+ cleanupHandle = track(wrappedFn, subscriber);
2506
+ if (++reruns > MAX_RERUNS) {
2507
+ if (_g2.__SIBU_DEV_WARN__ !== false && typeof console !== "undefined") {
2508
+ console.error(
2509
+ `[SibuJS] effect re-requested itself ${MAX_RERUNS}+ times on initial run \u2014 likely a write-reads-self cycle. Breaking to prevent infinite loop.`
2510
+ );
2511
+ }
2512
+ rerunPending = false;
2513
+ break;
2514
+ }
2515
+ } while (rerunPending);
2436
2516
  } finally {
2437
2517
  running = false;
2518
+ rerunPending = false;
2438
2519
  }
2439
2520
  const hook = _g2.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
2440
2521
  if (hook) hook.emit("effect:create", { effectFn });
@@ -4434,6 +4515,7 @@ function Loading(props = {}) {
4434
4515
  registerComponent,
4435
4516
  registerDisposer,
4436
4517
  resolveComponent,
4518
+ retrack,
4437
4519
  rp,
4438
4520
  rt,
4439
4521
  ruby,
@@ -4444,6 +4526,7 @@ function Loading(props = {}) {
4444
4526
  section,
4445
4527
  select,
4446
4528
  setGlobalErrorHandler,
4529
+ setMaxDrainIterations,
4447
4530
  show,
4448
4531
  signal,
4449
4532
  slot,
package/dist/index.d.cts CHANGED
@@ -1349,6 +1349,18 @@ interface TransitionState {
1349
1349
  */
1350
1350
  declare function transition(): TransitionState;
1351
1351
 
1352
+ type Subscriber = () => void;
1353
+ /**
1354
+ * Re-run a subscriber body. Stale deps (present before but not re-read
1355
+ * during this run) are pruned at end via epoch comparison — fixes the
1356
+ * conditional-derived over-subscription problem without paying the
1357
+ * full Set.delete + re-subscribe cost of `track()`'s cleanup phase.
1358
+ *
1359
+ * Used by `derived` on every pull. Uses a simple save/restore of
1360
+ * `currentSubscriber` instead of the stackTop push/pop — measurably
1361
+ * faster on deep chains where this function runs per-level.
1362
+ */
1363
+ declare function retrack(effectFn: () => void, subscriber: Subscriber): void;
1352
1364
  /**
1353
1365
  * Execute a function without tracking any signal reads as dependencies.
1354
1366
  * Useful for reading signals inside effects without creating subscriptions.
@@ -1357,6 +1369,8 @@ declare function transition(): TransitionState;
1357
1369
  * @returns The return value of fn
1358
1370
  */
1359
1371
  declare function untracked<T>(fn: () => T): T;
1372
+ /** Raise/lower the absolute drain iteration safety net. Returns previous value. */
1373
+ declare function setMaxDrainIterations(n: number): number;
1360
1374
 
1361
1375
  /**
1362
1376
  * Bind a dynamic attribute where both name and value can change reactively.
@@ -1534,4 +1548,4 @@ interface LoadingProps {
1534
1548
  */
1535
1549
  declare function Loading(props?: LoadingProps): HTMLElement;
1536
1550
 
1537
- export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
1551
+ export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
package/dist/index.d.ts CHANGED
@@ -1349,6 +1349,18 @@ interface TransitionState {
1349
1349
  */
1350
1350
  declare function transition(): TransitionState;
1351
1351
 
1352
+ type Subscriber = () => void;
1353
+ /**
1354
+ * Re-run a subscriber body. Stale deps (present before but not re-read
1355
+ * during this run) are pruned at end via epoch comparison — fixes the
1356
+ * conditional-derived over-subscription problem without paying the
1357
+ * full Set.delete + re-subscribe cost of `track()`'s cleanup phase.
1358
+ *
1359
+ * Used by `derived` on every pull. Uses a simple save/restore of
1360
+ * `currentSubscriber` instead of the stackTop push/pop — measurably
1361
+ * faster on deep chains where this function runs per-level.
1362
+ */
1363
+ declare function retrack(effectFn: () => void, subscriber: Subscriber): void;
1352
1364
  /**
1353
1365
  * Execute a function without tracking any signal reads as dependencies.
1354
1366
  * Useful for reading signals inside effects without creating subscriptions.
@@ -1357,6 +1369,8 @@ declare function transition(): TransitionState;
1357
1369
  * @returns The return value of fn
1358
1370
  */
1359
1371
  declare function untracked<T>(fn: () => T): T;
1372
+ /** Raise/lower the absolute drain iteration safety net. Returns previous value. */
1373
+ declare function setMaxDrainIterations(n: number): number;
1360
1374
 
1361
1375
  /**
1362
1376
  * Bind a dynamic attribute where both name and value can change reactively.
@@ -1534,4 +1548,4 @@ interface LoadingProps {
1534
1548
  */
1535
1549
  declare function Loading(props?: LoadingProps): HTMLElement;
1536
1550
 
1537
- export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
1551
+ export { type Accessor, type ActionFn, type AnchorProps, type ArrayActions, type AsyncDerivedState, type AudioProps, type ButtonProps, type Context, DynamicComponent, type EffectBody, type EffectOptions, ErrorBoundary, type ErrorBoundaryProps, ErrorDisplay, type ErrorDisplayProps, type ErrorSeverity, type FormProps, Fragment, type ImgProps, type InputProps, type InputType, KeepAlive, type KeepAliveOptions, type LabelProps, Loading, type LoadingProps, type LongPressOptions, type MediaProps, NodeChild, NodeChildren, type OnCleanup, type OptionProps, Portal, type Ref, type SSRStore, type SelectProps, type SignalOptions, type SlotFn, type Slots, type StoreActions, Suspense, type SuspenseProps, TagProps, type TextareaProps, type TypedTagFunction, type VideoProps, __resetIdCounter, a, abbr, action, address, area, array, article, aside, asyncDerived, audio, autoResize, b, base, batch, bdi, bdo, bindDynamic, blockquote, body, br, button, canvas, caption, catchError, catchErrorAsync, center, checkLeaks, circle, cite, clickOutside, clipPath, code, col, colgroup, context, copyOnClick, createId, customElement, data, datalist, dd, deepEqual, deepSignal, defer, defs, del, derived, details, dfn, dialog, disableSSR, dispose, div, dl, dt, each, effect, ellipse, em, embed, enableSSR, enqueueBatchedSignal, fieldset, figcaption, figure, font, footer, form, g, getSSRStore, getSlot, h1, h2, h3, h4, h5, h6, head, header, hr, html, i, iframe, img, input, ins, isBatching, isSSR, kbd, label, lazy, legend, li, line, linearGradient, link, longPress, main, map, mark, marker, marquee, mask, match, math, menu, meta, meter, mount, nav, nextTick, noscript, object, ol, on, onCleanup, onMount, onUnmount, optgroup, option, output, p, param, path, pattern, picture, polygon, polyline, portal, pre, progress, q, radialGradient, reactiveArray, rect, ref, registerComponent, registerDisposer, resolveComponent, retrack, rp, rt, ruby, runInSSRContext, s, samp, script, section, select, setGlobalErrorHandler, setMaxDrainIterations, show, signal, slot, small, source, span, stop, store, strict, strictEffect, strong, style, sub, summary, sup, svg, symbol, table, takePendingError, tbody, td, template, text, textarea, tfoot, th, thead, time, title, tr, track, transition, trapFocus, tspan, u, ul, unregisterComponent, untracked, use, var_, video, watch, when, withSSR, writable };
package/dist/index.js CHANGED
@@ -44,7 +44,7 @@ import {
44
44
  unregisterComponent,
45
45
  when,
46
46
  writable
47
- } from "./chunk-ND2664SF.js";
47
+ } from "./chunk-AMK2TYNW.js";
48
48
  import {
49
49
  __resetIdCounter,
50
50
  createId
@@ -186,26 +186,26 @@ import {
186
186
  use,
187
187
  var_,
188
188
  video
189
- } from "./chunk-R73P76YZ.js";
189
+ } from "./chunk-CWBVQML6.js";
190
190
  import {
191
191
  watch
192
- } from "./chunk-ITX6OO3F.js";
192
+ } from "./chunk-45YP72ZQ.js";
193
193
  import {
194
194
  trustHTML
195
195
  } from "./chunk-JYD2PWXH.js";
196
196
  import {
197
197
  context
198
- } from "./chunk-GTBNNBJ6.js";
198
+ } from "./chunk-OJ3P4ECI.js";
199
199
  import {
200
200
  SVG_NS,
201
201
  tagFactory
202
- } from "./chunk-KLRMB5ZS.js";
202
+ } from "./chunk-P2HSJDDN.js";
203
203
  import {
204
204
  bindDynamic
205
- } from "./chunk-DFPFITST.js";
205
+ } from "./chunk-WIPZPFBQ.js";
206
206
  import {
207
207
  derived
208
- } from "./chunk-54EDRCEF.js";
208
+ } from "./chunk-DRUZZAK4.js";
209
209
  import {
210
210
  checkLeaks,
211
211
  dispose,
@@ -215,7 +215,7 @@ import "./chunk-UCS6AMJ7.js";
215
215
  import {
216
216
  effect,
217
217
  on
218
- } from "./chunk-HB24TBAF.js";
218
+ } from "./chunk-WZA53FXU.js";
219
219
  import {
220
220
  disableSSR,
221
221
  enableSSR,
@@ -229,10 +229,12 @@ import {
229
229
  enqueueBatchedSignal,
230
230
  isBatching,
231
231
  signal
232
- } from "./chunk-CC65Y57T.js";
232
+ } from "./chunk-RDTDJCAB.js";
233
233
  import {
234
+ retrack,
235
+ setMaxDrainIterations,
234
236
  untracked
235
- } from "./chunk-VLPPXTYG.js";
237
+ } from "./chunk-QO3WC6FS.js";
236
238
  import "./chunk-LMLD24FC.js";
237
239
  export {
238
240
  DynamicComponent,
@@ -384,6 +386,7 @@ export {
384
386
  registerComponent,
385
387
  registerDisposer,
386
388
  resolveComponent,
389
+ retrack,
387
390
  rp,
388
391
  rt,
389
392
  ruby,
@@ -394,6 +397,7 @@ export {
394
397
  section,
395
398
  select,
396
399
  setGlobalErrorHandler,
400
+ setMaxDrainIterations,
397
401
  show,
398
402
  signal,
399
403
  slot,
@@ -474,7 +474,9 @@ declare function getSubscriberCount(getter: () => unknown): number;
474
474
  * Get the dependency list of an effect or computed subscriber function.
475
475
  * Returns signal references that the subscriber depends on.
476
476
  *
477
- * Note: This reads the _deps Set that track.ts maintains on subscriber functions.
477
+ * Note: This reads the internal dep storage that track.ts maintains on
478
+ * subscriber functions. Handles both the single-dep fast path (`_dep`)
479
+ * and the multi-dep Map (`_deps`).
478
480
  */
479
481
  declare function getDependencies(subscriberFn: () => void): ReactiveSignal[];
480
482
  /**
@@ -474,7 +474,9 @@ declare function getSubscriberCount(getter: () => unknown): number;
474
474
  * Get the dependency list of an effect or computed subscriber function.
475
475
  * Returns signal references that the subscriber depends on.
476
476
  *
477
- * Note: This reads the _deps Set that track.ts maintains on subscriber functions.
477
+ * Note: This reads the internal dep storage that track.ts maintains on
478
+ * subscriber functions. Handles both the single-dep fast path (`_dep`)
479
+ * and the multi-dep Map (`_deps`).
478
480
  */
479
481
  declare function getDependencies(subscriberFn: () => void): ReactiveSignal[];
480
482
  /**