sibujs 4.4.0 → 4.6.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 (112) hide show
  1. package/README.md +11 -0
  2. package/dist/browser.cjs +339 -130
  3. package/dist/browser.d.cts +61 -16
  4. package/dist/browser.d.ts +61 -16
  5. package/dist/browser.js +8 -6
  6. package/dist/build.cjs +273 -82
  7. package/dist/build.js +15 -16
  8. package/dist/cdn.dev.global.js +12 -12
  9. package/dist/cdn.full.dev.global.js +11 -11
  10. package/dist/cdn.full.global.js +10 -10
  11. package/dist/cdn.global.js +12 -12
  12. package/dist/{chunk-IXKSNWV5.js → chunk-2INLLLMZ.js} +1 -1
  13. package/dist/chunk-3ISI6ACU.js +44 -0
  14. package/dist/{chunk-VCTAEPSB.js → chunk-47M47FOM.js} +11 -3
  15. package/dist/chunk-5HZXGZ6T.js +24 -0
  16. package/dist/{chunk-KKLW7YWL.js → chunk-5MT6SJ3P.js} +275 -144
  17. package/dist/{chunk-3BTTCZ5J.js → chunk-5Q4R7HCL.js} +42 -4
  18. package/dist/{chunk-ADM46X22.js → chunk-7GQCWFOE.js} +157 -61
  19. package/dist/{chunk-4Z3SPQ67.js → chunk-7NCARGJW.js} +16 -8
  20. package/dist/{chunk-5DXA2J44.js → chunk-7XHATCIH.js} +5 -2
  21. package/dist/{chunk-LQFGQNMV.js → chunk-AIF3Z2T7.js} +302 -39
  22. package/dist/{chunk-UCALUKB5.js → chunk-BAAG6ZTI.js} +98 -29
  23. package/dist/{chunk-NVNJH22U.js → chunk-D33YTSX5.js} +2 -2
  24. package/dist/{chunk-M2F7TZHH.js → chunk-DKGBBKOF.js} +136 -80
  25. package/dist/{chunk-NIOYEGBQ.js → chunk-HURREPU2.js} +27 -13
  26. package/dist/{chunk-HFCOH2GN.js → chunk-HYXDKS4N.js} +153 -110
  27. package/dist/chunk-J6FW5TV6.js +233 -0
  28. package/dist/{chunk-36S2YPP4.js → chunk-JSXPZCET.js} +1 -1
  29. package/dist/chunk-NYNYSPK7.js +318 -0
  30. package/dist/{chunk-HQSEH5F6.js → chunk-ORMZXBKQ.js} +108 -45
  31. package/dist/{chunk-LYVUX7NT.js → chunk-OXUY2A6L.js} +287 -130
  32. package/dist/{chunk-6GSIXTWX.js → chunk-PK6FK2G2.js} +187 -66
  33. package/dist/{chunk-W2EQ7X2L.js → chunk-QE4TTDU3.js} +47 -9
  34. package/dist/{chunk-XLO7SLIX.js → chunk-SXXVZMKZ.js} +262 -70
  35. package/dist/{chunk-R25EFXXC.js → chunk-TUCPL2HB.js} +3 -3
  36. package/dist/{chunk-RR7M3FHM.js → chunk-UZQ6ALFS.js} +3 -3
  37. package/dist/{chunk-PNIRUQ4C.js → chunk-VVWPJ543.js} +6 -8
  38. package/dist/{chunk-7GIHSAWB.js → chunk-XC4MEKGA.js} +3 -3
  39. package/dist/{chunk-SKPTERHP.js → chunk-XRRZKZYX.js} +55 -31
  40. package/dist/{contracts-DBdg9J_a.d.cts → contracts-DRIuclVT.d.cts} +10 -2
  41. package/dist/{contracts-DBdg9J_a.d.ts → contracts-DRIuclVT.d.ts} +10 -2
  42. package/dist/{customElement-CNZxEB9G.d.ts → customElement-MmInOW1U.d.cts} +59 -10
  43. package/dist/{customElement-CNZxEB9G.d.cts → customElement-MmInOW1U.d.ts} +59 -10
  44. package/dist/data.cjs +293 -125
  45. package/dist/data.d.cts +47 -9
  46. package/dist/data.d.ts +47 -9
  47. package/dist/data.js +12 -9
  48. package/dist/devtools.cjs +121 -58
  49. package/dist/devtools.js +7 -8
  50. package/dist/dispose-GEIG2KOF.js +28 -0
  51. package/dist/ecosystem.cjs +391 -112
  52. package/dist/ecosystem.d.cts +31 -7
  53. package/dist/ecosystem.d.ts +31 -7
  54. package/dist/ecosystem.js +12 -12
  55. package/dist/extras.cjs +2250 -879
  56. package/dist/extras.d.cts +10 -9
  57. package/dist/extras.d.ts +10 -9
  58. package/dist/extras.js +39 -28
  59. package/dist/index.cjs +273 -82
  60. package/dist/index.d.cts +44 -154
  61. package/dist/index.d.ts +44 -154
  62. package/dist/index.js +24 -27
  63. package/dist/motion.cjs +137 -42
  64. package/dist/motion.js +5 -5
  65. package/dist/patterns.cjs +397 -48
  66. package/dist/patterns.d.cts +32 -9
  67. package/dist/patterns.d.ts +32 -9
  68. package/dist/patterns.js +8 -8
  69. package/dist/performance.cjs +338 -217
  70. package/dist/performance.d.cts +2 -2
  71. package/dist/performance.d.ts +2 -2
  72. package/dist/performance.js +8 -9
  73. package/dist/plugin-DVgSnTfK.d.cts +112 -0
  74. package/dist/plugin-DVgSnTfK.d.ts +112 -0
  75. package/dist/plugins.cjs +587 -203
  76. package/dist/plugins.d.cts +3 -3
  77. package/dist/plugins.d.ts +3 -3
  78. package/dist/plugins.js +35 -23
  79. package/dist/signal-EotCj4hS.d.cts +110 -0
  80. package/dist/signal-EotCj4hS.d.ts +110 -0
  81. package/dist/{ssr-BiPRdZ6n.d.cts → ssr-Bli9XRW5.d.cts} +5 -0
  82. package/dist/{ssr-BiPRdZ6n.d.ts → ssr-Bli9XRW5.d.ts} +5 -0
  83. package/dist/{ssr-Y7XOEPEN.js → ssr-XOTUASDO.js} +4 -5
  84. package/dist/ssr.cjs +214 -73
  85. package/dist/ssr.d.cts +9 -3
  86. package/dist/ssr.d.ts +9 -3
  87. package/dist/ssr.js +11 -12
  88. package/dist/{startup-BMpaiMhP.d.ts → startup-BLfSeL15.d.cts} +73 -22
  89. package/dist/{startup-BMpaiMhP.d.cts → startup-BLfSeL15.d.ts} +73 -22
  90. package/dist/tagFactory-DFstCLQV.d.cts +117 -0
  91. package/dist/tagFactory-DkaNVUNV.d.ts +117 -0
  92. package/dist/testing.cjs +2481 -2153
  93. package/dist/testing.d.cts +56 -5
  94. package/dist/testing.d.ts +56 -5
  95. package/dist/testing.js +580 -307
  96. package/dist/ui.cjs +798 -328
  97. package/dist/ui.d.cts +40 -7
  98. package/dist/ui.d.ts +40 -7
  99. package/dist/ui.js +151 -56
  100. package/dist/widgets.cjs +319 -285
  101. package/dist/widgets.js +9 -10
  102. package/package.json +2 -2
  103. package/dist/chunk-2WLZ6757.js +0 -149
  104. package/dist/chunk-7NBDXVHS.js +0 -60
  105. package/dist/chunk-CCSJMTRN.js +0 -15
  106. package/dist/chunk-QKRPLZ2V.js +0 -108
  107. package/dist/chunk-WWV3SJ3L.js +0 -131
  108. package/dist/dispose-46BOMMQJ.js +0 -19
  109. package/dist/plugin-D30wlGW5.d.cts +0 -71
  110. package/dist/plugin-D30wlGW5.d.ts +0 -71
  111. package/dist/tagFactory-Bzupt4Pj.d.cts +0 -55
  112. package/dist/tagFactory-Bzupt4Pj.d.ts +0 -55
package/dist/patterns.cjs CHANGED
@@ -147,6 +147,25 @@ function reportError(error, context) {
147
147
  });
148
148
  }
149
149
 
150
+ // src/core/rendering/dispose.ts
151
+ function resolveCapture(capture) {
152
+ let frame = capture;
153
+ while (frame?.closed) frame = frame.forwardTo;
154
+ return frame ?? null;
155
+ }
156
+ var registrationCaptures = [];
157
+ function currentDisposerCapture() {
158
+ return resolveCapture(registrationCaptures[registrationCaptures.length - 1]);
159
+ }
160
+ function beginDisposerCapture(capture) {
161
+ if (capture === null && registrationCaptures.length === 0) return false;
162
+ registrationCaptures.push(resolveCapture(capture));
163
+ return true;
164
+ }
165
+ function endDisposerCapture() {
166
+ registrationCaptures.pop();
167
+ }
168
+
150
169
  // src/reactivity/track-core.ts
151
170
  var POOL_MAX = 4096;
152
171
  var nodePool = [];
@@ -294,8 +313,18 @@ function untracked(fn) {
294
313
  var subscriberEpochCounter = 0;
295
314
  function retrack(effectFn, subscriber) {
296
315
  const prev = currentSubscriber;
316
+ let savedDepth = 0;
317
+ let savedSuspendSub = null;
318
+ if (trackingSuspended) {
319
+ savedDepth = suspendDepth;
320
+ savedSuspendSub = suspendSavedSub;
321
+ suspendDepth = 0;
322
+ suspendSavedSub = null;
323
+ trackingSuspended = false;
324
+ }
297
325
  currentSubscriber = subscriber;
298
326
  const sub = subscriber;
327
+ if (sub._cap === void 0) sub._cap = currentDisposerCapture();
299
328
  const epoch = ++subscriberEpochCounter;
300
329
  sub._epoch = epoch;
301
330
  sub._structDirty = false;
@@ -308,6 +337,11 @@ function retrack(effectFn, subscriber) {
308
337
  effectFn();
309
338
  } finally {
310
339
  currentSubscriber = prev;
340
+ if (savedDepth !== 0) {
341
+ suspendDepth = savedDepth;
342
+ suspendSavedSub = savedSuspendSub;
343
+ trackingSuspended = true;
344
+ }
311
345
  let node = sub.depsHead ?? null;
312
346
  while (node !== null) {
313
347
  const next = node.subNext;
@@ -327,11 +361,25 @@ function track(effectFn, subscriber) {
327
361
  if (!subscriber) return reactiveBinding(effectFn);
328
362
  cleanup(subscriber);
329
363
  const prev = currentSubscriber;
364
+ let savedDepth = 0;
365
+ let savedSuspendSub = null;
366
+ if (trackingSuspended) {
367
+ savedDepth = suspendDepth;
368
+ savedSuspendSub = suspendSavedSub;
369
+ suspendDepth = 0;
370
+ suspendSavedSub = null;
371
+ trackingSuspended = false;
372
+ }
330
373
  currentSubscriber = subscriber;
331
374
  try {
332
375
  effectFn();
333
376
  } finally {
334
377
  currentSubscriber = prev;
378
+ if (savedDepth !== 0) {
379
+ suspendDepth = savedDepth;
380
+ suspendSavedSub = savedSuspendSub;
381
+ trackingSuspended = true;
382
+ }
335
383
  const sub2 = subscriber;
336
384
  for (let n = sub2.depsHead ?? null; n !== null; n = n.subNext) {
337
385
  const sig = n.sig;
@@ -468,7 +516,12 @@ function drainQueue() {
468
516
  quarantined.add(sub);
469
517
  continue;
470
518
  }
471
- safeInvoke(sub);
519
+ const pushed = beginDisposerCapture(sub._cap ?? null);
520
+ try {
521
+ safeInvoke(sub);
522
+ } finally {
523
+ if (pushed) endDisposerCapture();
524
+ }
472
525
  }
473
526
  }
474
527
  function drainNotificationQueue() {
@@ -502,8 +555,9 @@ function propagateDirty(sub) {
502
555
  if (s._c) {
503
556
  const nSig = s._sig;
504
557
  if (nSig) {
505
- if (!nSig._d) {
558
+ if (!nSig._d || nSig._f === true) {
506
559
  nSig._d = true;
560
+ nSig._f = false;
507
561
  stack.push(nSig);
508
562
  }
509
563
  } else {
@@ -603,7 +657,7 @@ function forEachSubscriber(signal2, visit) {
603
657
  }
604
658
 
605
659
  // src/reactivity/track.ts
606
- var _runtimeVersion = true ? "4.4.0" : "dev";
660
+ var _runtimeVersion = true ? "4.6.0" : "dev";
607
661
  var REGISTRY_KEY = /* @__PURE__ */ Symbol.for("sibujs.reactive.v1");
608
662
  function resolveReactiveApi() {
609
663
  const g = globalThis;
@@ -797,12 +851,24 @@ function stripUnsafeKeys(obj) {
797
851
  function machine(config) {
798
852
  const [state2, setState] = signal(config.initial);
799
853
  const [context, setContext] = signal(config.context || {});
800
- const initialDef = config.states[config.initial];
801
- if (initialDef?.entry) {
802
- initialDef.entry(context());
854
+ let currentState = config.initial;
855
+ let currentContext = context();
856
+ const queue = [];
857
+ let processing = false;
858
+ function drain() {
859
+ processing = true;
860
+ try {
861
+ while (queue.length > 0) {
862
+ step(queue.shift());
863
+ }
864
+ } catch (err) {
865
+ queue.length = 0;
866
+ throw err;
867
+ } finally {
868
+ processing = false;
869
+ }
803
870
  }
804
- function send(event) {
805
- const currentState = state2();
871
+ function step(event) {
806
872
  const stateDef = config.states[currentState];
807
873
  if (!stateDef?.on) return;
808
874
  const transition = stateDef.on[event];
@@ -817,28 +883,51 @@ function machine(config) {
817
883
  guard = transition.guard;
818
884
  action = transition.action;
819
885
  }
820
- const ctx = context();
886
+ const ctx = currentContext;
821
887
  if (guard && !guard(ctx)) return;
822
888
  if (stateDef.exit) {
823
889
  stateDef.exit(ctx);
824
890
  }
891
+ let nextContext = ctx;
825
892
  if (action) {
826
893
  const rawPatch = action(ctx);
827
- const next = { ...ctx, ...stripUnsafeKeys(rawPatch) };
828
- setContext(next);
894
+ nextContext = { ...ctx, ...stripUnsafeKeys(rawPatch) };
829
895
  }
830
- setState(target);
896
+ currentState = target;
897
+ currentContext = nextContext;
898
+ batch(() => {
899
+ if (action) setContext(nextContext);
900
+ setState(target);
901
+ });
831
902
  const targetDef = config.states[target];
832
903
  if (targetDef?.entry) {
833
- targetDef.entry(context());
904
+ targetDef.entry(currentContext);
834
905
  }
835
906
  }
907
+ function send(event) {
908
+ queue.push(event);
909
+ if (processing) return;
910
+ drain();
911
+ }
912
+ const initialDef = config.states[config.initial];
913
+ if (initialDef?.entry) {
914
+ processing = true;
915
+ try {
916
+ initialDef.entry(currentContext);
917
+ } catch (err) {
918
+ queue.length = 0;
919
+ throw err;
920
+ } finally {
921
+ processing = false;
922
+ }
923
+ if (queue.length > 0) drain();
924
+ }
836
925
  function matches(s) {
837
926
  return state2() === s;
838
927
  }
839
928
  function can(event) {
840
- const currentState = state2();
841
- const stateDef = config.states[currentState];
929
+ const currentState2 = state2();
930
+ const stateDef = config.states[currentState2];
842
931
  if (!stateDef?.on) return false;
843
932
  const transition = stateDef.on[event];
844
933
  if (!transition) return false;
@@ -1390,10 +1479,14 @@ function derived(getter, options) {
1390
1479
  const cs = {};
1391
1480
  cs._d = false;
1392
1481
  cs._init = false;
1482
+ cs._f = false;
1393
1483
  cs._g = getter;
1394
1484
  cs.__v = 0;
1485
+ let evaluating = false;
1486
+ let disposed = false;
1487
+ let pendingError;
1395
1488
  const markDirty = () => {
1396
- if (cs._d) return;
1489
+ if (cs._d || disposed) return;
1397
1490
  cs._d = true;
1398
1491
  };
1399
1492
  markDirty._c = 1;
@@ -1416,33 +1509,53 @@ function derived(getter, options) {
1416
1509
  }
1417
1510
  }, markDirty);
1418
1511
  const hook = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1419
- let evaluating = false;
1420
1512
  const validate = () => {
1421
- if (!cs._d) return;
1513
+ if (!cs._d || disposed || pendingError !== void 0) return;
1422
1514
  const oldValue = cs._v;
1423
1515
  evaluating = true;
1424
1516
  try {
1425
1517
  retrack2(recompute, markDirty);
1426
1518
  if (!Object.is(oldValue, cs._v)) cs.__v++;
1519
+ } catch (err) {
1520
+ pendingError = { error: err };
1521
+ cs.__v++;
1522
+ if (disposed) cs._d = true;
1523
+ else cs._f = true;
1427
1524
  } finally {
1428
1525
  evaluating = false;
1526
+ if (disposed) cleanup2(markDirty);
1429
1527
  }
1430
- if (hook && !Object.is(oldValue, cs._v)) {
1528
+ if (hook && !disposed && !Object.is(oldValue, cs._v)) {
1431
1529
  hook.emit("computed:update", { signal: cs, oldValue, newValue: cs._v });
1432
1530
  }
1433
1531
  };
1434
1532
  cs._validate = validate;
1533
+ const throwPending = () => {
1534
+ const { error } = pendingError;
1535
+ pendingError = void 0;
1536
+ if (disposed) cs._d = false;
1537
+ throw error;
1538
+ };
1435
1539
  function computedGetter() {
1436
1540
  if (evaluating) {
1437
1541
  throw new Error(
1438
1542
  `[SibuJS] Circular dependency detected in derived${debugName ? ` "${debugName}"` : ""}. A derived signal cannot read itself (directly or through a chain).`
1439
1543
  );
1440
1544
  }
1441
- if (isTrackingSuspended2()) {
1442
- if (cs._d) validate();
1545
+ if (isTrackingSuspended2() || disposed) {
1546
+ if (cs._d) {
1547
+ validate();
1548
+ if (pendingError !== void 0) throwPending();
1549
+ }
1443
1550
  return cs._v;
1444
1551
  }
1445
- if (cs._d) validate();
1552
+ if (cs._d) {
1553
+ validate();
1554
+ if (pendingError !== void 0) {
1555
+ recordDependency2(cs);
1556
+ throwPending();
1557
+ }
1558
+ }
1446
1559
  recordDependency2(cs);
1447
1560
  return cs._v;
1448
1561
  }
@@ -1451,12 +1564,28 @@ function derived(getter, options) {
1451
1564
  cs.__name = debugName;
1452
1565
  }
1453
1566
  computedGetter.__signal = cs;
1567
+ computedGetter.dispose = () => {
1568
+ if (disposed) return;
1569
+ disposed = true;
1570
+ cs._d = pendingError !== void 0;
1571
+ cleanup2(markDirty);
1572
+ const h = globalThis.__SIBU_DEVTOOLS_GLOBAL_HOOK__;
1573
+ if (h) {
1574
+ try {
1575
+ h.emit("computed:destroy", { signal: cs, getter: computedGetter });
1576
+ } catch {
1577
+ }
1578
+ }
1579
+ };
1454
1580
  if (hook) hook.emit("computed:create", { signal: cs, name: debugName, getter: computedGetter });
1455
1581
  return computedGetter;
1456
1582
  }
1457
1583
 
1458
1584
  // src/patterns/timeTravel.ts
1459
1585
  function timeline(initial, maxHistory = 100) {
1586
+ if (!Number.isSafeInteger(maxHistory) || maxHistory < 1) {
1587
+ throw new RangeError(`[timeline] maxHistory must be a positive safe integer, got ${maxHistory}`);
1588
+ }
1460
1589
  const [history, setHistory] = signal([initial]);
1461
1590
  const [index, setIndex] = signal(0);
1462
1591
  const value = derived(() => history()[index()]);
@@ -1503,7 +1632,126 @@ function timeline(initial, maxHistory = 100) {
1503
1632
  setIndex(targetIndex);
1504
1633
  }
1505
1634
  }
1506
- return { value, set, undo, redo, canUndo, canRedo, history, index, reset, jumpTo };
1635
+ function dispose() {
1636
+ value.dispose();
1637
+ canUndo.dispose();
1638
+ canRedo.dispose();
1639
+ }
1640
+ return { value, set, undo, redo, canUndo, canRedo, history, index, reset, jumpTo, dispose };
1641
+ }
1642
+
1643
+ // src/utils/adoption.ts
1644
+ var nativeThen = Promise.prototype.then;
1645
+ function probeNative(target, onRejected, onOpen) {
1646
+ let decided = false;
1647
+ const decide = (rejected) => {
1648
+ if (decided) return;
1649
+ decided = true;
1650
+ (rejected ? onRejected : onOpen)();
1651
+ };
1652
+ try {
1653
+ Reflect.apply(nativeThen, target, [void 0, () => decide(true)]);
1654
+ } catch {
1655
+ queueMicrotask(() => decide(true));
1656
+ return;
1657
+ }
1658
+ queueMicrotask(() => decide(false));
1659
+ }
1660
+ function adopt(value) {
1661
+ if (value === null || typeof value !== "object" && typeof value !== "function") return null;
1662
+ let then;
1663
+ try {
1664
+ then = value.then;
1665
+ } catch (error) {
1666
+ return {
1667
+ promise: Promise.reject(error),
1668
+ failedEarly: true,
1669
+ probe: (onRejected) => queueMicrotask(onRejected)
1670
+ };
1671
+ }
1672
+ if (typeof then !== "function") return null;
1673
+ let resolvePromise;
1674
+ let rejectPromise;
1675
+ const promise = new Promise((resolve, reject) => {
1676
+ resolvePromise = resolve;
1677
+ rejectPromise = reject;
1678
+ });
1679
+ let state2 = "invoking";
1680
+ let nativeTarget;
1681
+ const waiters = [];
1682
+ const transition = (next) => {
1683
+ state2 = next;
1684
+ if (next === "invoking") return;
1685
+ for (const waiter of waiters.splice(0)) waiter();
1686
+ };
1687
+ const fulfil = (result) => {
1688
+ resolvePromise(result);
1689
+ transition("fulfilled");
1690
+ };
1691
+ const fail = (error) => {
1692
+ rejectPromise(error);
1693
+ transition("rejected");
1694
+ };
1695
+ const follow = (result) => {
1696
+ if (result === promise) {
1697
+ fail(new TypeError("adopt: a thenable resolved to its own adoption"));
1698
+ return;
1699
+ }
1700
+ if (result === null || typeof result !== "object" && typeof result !== "function") {
1701
+ fulfil(result);
1702
+ return;
1703
+ }
1704
+ let nextThen;
1705
+ try {
1706
+ nextThen = result.then;
1707
+ } catch (error) {
1708
+ fail(error);
1709
+ return;
1710
+ }
1711
+ if (typeof nextThen !== "function") {
1712
+ fulfil(result);
1713
+ return;
1714
+ }
1715
+ invokeLater(result, nextThen);
1716
+ };
1717
+ const invokeLater = (target, targetThen) => {
1718
+ if (targetThen === nativeThen) {
1719
+ nativeTarget = target;
1720
+ transition("native");
1721
+ } else {
1722
+ transition("invoking");
1723
+ }
1724
+ queueMicrotask(() => {
1725
+ let done = false;
1726
+ const onResolve = (result) => {
1727
+ if (done) return;
1728
+ done = true;
1729
+ follow(result);
1730
+ };
1731
+ const onReject = (error) => {
1732
+ if (done) return;
1733
+ done = true;
1734
+ fail(error);
1735
+ };
1736
+ try {
1737
+ Reflect.apply(targetThen, target, [onResolve, onReject]);
1738
+ } catch (error) {
1739
+ onReject(error);
1740
+ }
1741
+ if (!done && state2 === "invoking") transition("pending");
1742
+ });
1743
+ };
1744
+ invokeLater(value, then);
1745
+ const probe = (onRejected, onOpen) => {
1746
+ const decide = () => {
1747
+ if (state2 === "native") probeNative(nativeTarget, onRejected, onOpen);
1748
+ else (state2 === "rejected" ? onRejected : onOpen)();
1749
+ };
1750
+ if (state2 === "native") probeNative(nativeTarget, onRejected, onOpen);
1751
+ else if (state2 === "invoking") waiters.push(decide);
1752
+ else queueMicrotask(state2 === "rejected" ? onRejected : onOpen);
1753
+ };
1754
+ return { promise, failedEarly: false, probe };
1507
1755
  }
1508
1756
 
1509
1757
  // src/patterns/globalStore.ts
@@ -1540,8 +1788,51 @@ function deepClone(value) {
1540
1788
  function globalStore(config) {
1541
1789
  const initialState = deepClone(config.state);
1542
1790
  const [getState, setState] = signal({ ...initialState });
1543
- const listeners = /* @__PURE__ */ new Set();
1791
+ const subscriptions = /* @__PURE__ */ new Map();
1544
1792
  const middlewares = config.middleware || [];
1793
+ function notifyListeners(state2) {
1794
+ if (subscriptions.size === 0) return;
1795
+ for (const subscription of Array.from(subscriptions.values())) {
1796
+ if (!subscription.active) continue;
1797
+ try {
1798
+ subscription.callback(state2);
1799
+ } catch (err) {
1800
+ reportError(err, { phase: "event", name: "globalStore(subscribe)" });
1801
+ }
1802
+ }
1803
+ }
1804
+ const operations = [];
1805
+ let running = false;
1806
+ function perform(operation, rethrowOwn = true) {
1807
+ operations.push(operation);
1808
+ if (running) return;
1809
+ running = true;
1810
+ let callerError;
1811
+ let callerFailed = false;
1812
+ try {
1813
+ let first = true;
1814
+ let cursor = 0;
1815
+ while (cursor < operations.length) {
1816
+ const next = operations[cursor];
1817
+ operations[cursor++] = void 0;
1818
+ try {
1819
+ next();
1820
+ } catch (err) {
1821
+ if (first && rethrowOwn) {
1822
+ callerFailed = true;
1823
+ callerError = err;
1824
+ } else {
1825
+ reportError(err, { phase: "event", name: "globalStore(dispatch)" });
1826
+ }
1827
+ }
1828
+ first = false;
1829
+ }
1830
+ } finally {
1831
+ operations.length = 0;
1832
+ running = false;
1833
+ }
1834
+ if (callerFailed) throw callerError;
1835
+ }
1545
1836
  function dispatch(action, payload) {
1546
1837
  const actionFn = config.actions[action];
1547
1838
  if (!actionFn) throw new Error(`Unknown action: ${String(action)}`);
@@ -1550,38 +1841,96 @@ function globalStore(config) {
1550
1841
  const rawPatch = actionFn(current, payload);
1551
1842
  const patch = stripUnsafeKeys(rawPatch);
1552
1843
  setState({ ...current, ...patch });
1553
- const newState = getState();
1554
- for (const listener of listeners) {
1555
- listener(newState);
1556
- }
1844
+ notifyListeners(getState());
1557
1845
  };
1558
- if (middlewares.length === 0) {
1559
- execute();
1560
- return;
1561
- }
1562
- let index = 0;
1563
- const next = () => {
1564
- if (index < middlewares.length) {
1565
- const mw = middlewares[index++];
1566
- mw(getState(), String(action), payload, next);
1567
- } else {
1846
+ perform(() => {
1847
+ if (middlewares.length === 0) {
1568
1848
  execute();
1849
+ return;
1569
1850
  }
1570
- };
1571
- next();
1851
+ const runFrom = (index) => {
1852
+ if (index >= middlewares.length) {
1853
+ execute();
1854
+ return;
1855
+ }
1856
+ let called = false;
1857
+ let failed = false;
1858
+ let synchronous = true;
1859
+ const next = () => {
1860
+ if (failed && !called) {
1861
+ if (DEV)
1862
+ devWarn(
1863
+ `globalStore: middleware ${index} next() called after it failed for "${String(action)}"; ignored.`
1864
+ );
1865
+ called = true;
1866
+ return;
1867
+ }
1868
+ if (called) {
1869
+ if (DEV)
1870
+ devWarn(
1871
+ `globalStore: middleware ${index} next() called more than once for "${String(action)}"; ignored.`
1872
+ );
1873
+ return;
1874
+ }
1875
+ called = true;
1876
+ if (synchronous) {
1877
+ runFrom(index + 1);
1878
+ return;
1879
+ }
1880
+ const proceed = () => perform(() => runFrom(index + 1), false);
1881
+ if (!adoption) {
1882
+ proceed();
1883
+ return;
1884
+ }
1885
+ adoption.probe(() => {
1886
+ failed = true;
1887
+ if (DEV)
1888
+ devWarn(
1889
+ `globalStore: middleware ${index} next() called after it failed for "${String(action)}"; ignored.`
1890
+ );
1891
+ }, proceed);
1892
+ };
1893
+ let adoption = null;
1894
+ try {
1895
+ adoption = adopt(middlewares[index](getState(), String(action), payload, next));
1896
+ } catch (err) {
1897
+ failed = true;
1898
+ throw err;
1899
+ } finally {
1900
+ synchronous = false;
1901
+ }
1902
+ if (adoption) {
1903
+ if (adoption.failedEarly) failed = true;
1904
+ adoption.promise.then(void 0, (err) => {
1905
+ failed = true;
1906
+ reportError(err, { phase: "async", name: "globalStore(middleware)" });
1907
+ });
1908
+ }
1909
+ };
1910
+ runFrom(0);
1911
+ });
1572
1912
  }
1573
1913
  function select(selector) {
1574
1914
  return () => selector(getState());
1575
1915
  }
1576
1916
  function subscribe(callback) {
1577
- listeners.add(callback);
1578
- return () => listeners.delete(callback);
1917
+ let subscription = subscriptions.get(callback);
1918
+ if (!subscription) {
1919
+ subscription = { callback, active: true };
1920
+ subscriptions.set(callback, subscription);
1921
+ }
1922
+ const own = subscription;
1923
+ return () => {
1924
+ if (subscriptions.get(callback) !== own) return;
1925
+ own.active = false;
1926
+ subscriptions.delete(callback);
1927
+ };
1579
1928
  }
1580
1929
  function reset() {
1581
- setState({ ...initialState });
1582
- for (const listener of listeners) {
1583
- listener(getState());
1584
- }
1930
+ perform(() => {
1931
+ setState({ ...initialState });
1932
+ notifyListeners(getState());
1933
+ });
1585
1934
  }
1586
1935
  return { getState, select, dispatch, subscribe, reset };
1587
1936
  }
@@ -1705,7 +2054,7 @@ function validateProps(props, schema) {
1705
2054
  const errors = [];
1706
2055
  for (const [key, def] of Object.entries(schema)) {
1707
2056
  const propDef = typeof def === "function" ? { type: def } : def;
1708
- if (result[key] == null && propDef.default !== void 0) {
2057
+ if (result[key] === void 0 && propDef.default !== void 0) {
1709
2058
  result[key] = typeof propDef.default === "function" ? propDef.default() : propDef.default;
1710
2059
  }
1711
2060
  if (propDef.required && result[key] == null) {
@@ -1731,7 +2080,7 @@ ${errors.map((e) => ` - ${e}`).join("\n")}`);
1731
2080
  for (const [key, def] of Object.entries(schema)) {
1732
2081
  if (typeof def === "function") continue;
1733
2082
  const fallback = def.default;
1734
- if (result[key] == null && fallback !== void 0) {
2083
+ if (result[key] === void 0 && fallback !== void 0) {
1735
2084
  result[key] = typeof fallback === "function" ? fallback() : fallback;
1736
2085
  }
1737
2086
  }
@@ -1,4 +1,4 @@
1
- export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, b as assertType, c as compose, d as createGuard, e as createSlots, f as defineComponent, g as defineSlottedComponent, h as defineStrictComponent, v as validateProps, i as validators, w as withBoundary, j as withDefaults, k as withProps, l as withWrapper } from './contracts-DBdg9J_a.cjs';
1
+ export { C as ComponentProps, P as PropDef, a as PropSchema, R as RenderProp, V as Validator, W as WithDefaultsProps, b as assertType, c as compose, d as createGuard, e as createSlots, f as defineComponent, g as defineSlottedComponent, h as defineStrictComponent, v as validateProps, i as validators, w as withBoundary, j as withDefaults, k as withProps, l as withWrapper } from './contracts-DRIuclVT.cjs';
2
2
 
3
3
  interface MachineConfig<S extends string, E extends string, C extends object = Record<string, unknown>> {
4
4
  initial: S;
@@ -36,10 +36,10 @@ declare function machine<S extends string, E extends string, C extends object =
36
36
  * @param key Storage key
37
37
  * @param initial Default value when no persisted value exists
38
38
  * @param options Storage options
39
- * @returns The same `[getter, setter]` tuple as `signal()`. For localStorage
40
- * (cross-tab sync on), the setter additionally carries a non-enumerable
41
- * `dispose()` method — call `setter.dispose()` on unmount to remove the
42
- * `storage` event listener; otherwise it lives for the page's lifetime.
39
+ * @returns The same `[getter, setter]` tuple as `signal()`. The setter also
40
+ * carries a non-enumerable `dispose()` method — call `setter.dispose()` on
41
+ * unmount to stop persisting and remove the cross-tab `storage` listener;
42
+ * otherwise both live for the page's lifetime.
43
43
  *
44
44
  * @example
45
45
  * ```ts
@@ -94,15 +94,21 @@ interface PersistOptions<T = unknown> {
94
94
  * Must be synchronous — see `encrypt` docs. */
95
95
  decrypt?: (value: string) => string;
96
96
  }
97
+ /** Setter returned by {@link persisted}: writes like a `signal()` setter, released with `dispose()`. */
98
+ type PersistedSetter<T> = ((next: T | ((prev: T) => T)) => void) & {
99
+ /** Stop persisting changes and remove the cross-tab `storage` listener. */
100
+ dispose(): void;
101
+ };
97
102
  /**
98
103
  * A signal mirrored into persistent storage, rehydrated on creation.
99
104
  *
100
105
  * @param key Storage key.
101
106
  * @param initial Value used when storage holds nothing (or holds junk).
102
107
  * @param options Storage backend, serializer and version/migration settings.
103
- * @returns A `[accessor, setter]` pair with the same shape as `signal`.
108
+ * @returns A `[accessor, setter]` pair with the same shape as `signal`; the
109
+ * setter carries `dispose()`.
104
110
  */
105
- declare function persisted<T>(key: string, initial: T, options?: PersistOptions<T>): [() => T, (next: T | ((prev: T) => T)) => void];
111
+ declare function persisted<T>(key: string, initial: T, options?: PersistOptions<T>): [() => T, PersistedSetter<T>];
106
112
 
107
113
  /**
108
114
  * optimistic provides optimistic UI updates that can be reverted on failure.
@@ -202,13 +208,30 @@ interface TimeTravelReturn<T> {
202
208
  index: () => number;
203
209
  reset: () => void;
204
210
  jumpTo: (index: number) => void;
211
+ /**
212
+ * Dispose the internal `value`, `canUndo` and `canRedo` deriveds and remove
213
+ * them from DevTools. Afterwards they return their last values. Idempotent.
214
+ */
215
+ dispose: () => void;
205
216
  }
206
217
  /**
207
218
  * timeline wraps a state value with undo/redo history.
219
+ *
220
+ * @param maxHistory How many entries (including the current one) to keep. Must
221
+ * be a positive safe integer — `0`, negative, fractional, `NaN` or infinite
222
+ * capacities throw a `RangeError`, since they would evict the current value
223
+ * and break the index invariant.
208
224
  */
209
225
  declare function timeline<T>(initial: T, maxHistory?: number): TimeTravelReturn<T>;
210
226
 
211
- type Middleware<S> = (state: S, action: string, payload: unknown, next: () => void) => void;
227
+ /**
228
+ * Store middleware. Call `next()` once to continue the chain — synchronously or
229
+ * later (after an `await`, from a timer). A middleware may be `async`: a
230
+ * rejection is reported through the runtime error handler, and a middleware
231
+ * that fails (throws, or rejects) before calling `next()` never continues —
232
+ * a later `next()` from it is ignored.
233
+ */
234
+ type Middleware<S> = (state: S, action: string, payload: unknown, next: () => void) => void | PromiseLike<void>;
212
235
  type Selector<S, R> = (state: S) => R;
213
236
  /**
214
237
  * Action-map constraint.
@@ -242,4 +265,4 @@ declare function globalStore<S extends object, A extends StoreActionMap<S>>(conf
242
265
  middleware?: Middleware<S>[];
243
266
  }): GlobalStore<S, A>;
244
267
 
245
- export { type GlobalStore, type MachineConfig, type MachineReturn, type Middleware, type PersistOptions, type Selector, type StoreActionMap, type TimeTravelReturn, globalStore, machine, optimistic, optimisticList, persisted, timeline };
268
+ export { type GlobalStore, type MachineConfig, type MachineReturn, type Middleware, type PersistOptions, type PersistedSetter, type Selector, type StoreActionMap, type TimeTravelReturn, globalStore, machine, optimistic, optimisticList, persisted, timeline };