@estjs/signals 0.0.17-beta.7 → 0.0.18-beta.1

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.
@@ -1,4 +1,4 @@
1
- import { isFunction, isObject, warn, error, isPlainObject, isArray, isSet, isMap, isWeakMap, isWeakSet, hasChanged, isStringNumber, hasOwn, isUndefined } from '@estjs/shared';
1
+ import { isArray, isFunction, isObject, warn, error, isPlainObject, isSet, isMap, isWeakMap, isWeakSet, hasChanged, isDate, isRegExp, isNull, isPromise, hasOwn, isStringNumber, isUndefined } from '@estjs/shared';
2
2
 
3
3
  // src/signal.ts
4
4
 
@@ -305,6 +305,17 @@ function isValidLink(checkLink, sub) {
305
305
  }
306
306
  var targetMap = /* @__PURE__ */ new WeakMap();
307
307
  var triggerVersion = 0;
308
+ var triggerEffects = [];
309
+ var triggerDepth = 0;
310
+ function startTriggerEffects() {
311
+ const effects = triggerDepth === 0 ? triggerEffects : [];
312
+ triggerDepth++;
313
+ return effects;
314
+ }
315
+ function endTriggerEffects(effects) {
316
+ effects.length = 0;
317
+ triggerDepth--;
318
+ }
308
319
  function collectTriggeredEffects(dep, effects, version) {
309
320
  if (!dep) {
310
321
  return;
@@ -335,46 +346,127 @@ function track(target, key) {
335
346
  linkReactiveNode(dep, activeSub);
336
347
  }
337
348
  function trigger(target, type, key, newValue) {
338
- var _a5;
349
+ var _a6;
339
350
  const depsMap = targetMap.get(target);
340
351
  if (!depsMap) {
341
352
  return;
342
353
  }
343
- const effects = [];
354
+ const effects = startTriggerEffects();
344
355
  const version = ++triggerVersion;
345
- if (key !== void 0) {
346
- if (Array.isArray(key)) {
347
- for (const element of key) {
348
- collectTriggeredEffects(depsMap.get(element), effects, version);
356
+ try {
357
+ if (key !== void 0) {
358
+ if (isArray(key)) {
359
+ for (const element of key) {
360
+ collectTriggeredEffects(depsMap.get(element), effects, version);
361
+ }
362
+ } else {
363
+ collectTriggeredEffects(depsMap.get(key), effects, version);
349
364
  }
350
- } else {
351
- collectTriggeredEffects(depsMap.get(key), effects, version);
352
365
  }
366
+ if (type === "ADD" || type === "DELETE" || type === "CLEAR") {
367
+ const iterationKey = isArray(target) ? ARRAY_ITERATE_KEY : ITERATE_KEY;
368
+ collectTriggeredEffects(depsMap.get(iterationKey), effects, version);
369
+ }
370
+ for (const effect2 of effects) {
371
+ if (isFunction(effect2.onTrigger)) {
372
+ effect2.onTrigger({
373
+ effect: effect2,
374
+ target,
375
+ type,
376
+ key,
377
+ newValue
378
+ });
379
+ }
380
+ if (effect2.flag & 2 /* WATCHING */) {
381
+ (_a6 = effect2.notify) == null ? void 0 : _a6.call(effect2);
382
+ } else if (effect2.flag & 1 /* MUTABLE */) {
383
+ effect2.flag |= 16 /* DIRTY */;
384
+ if (effect2.subLink) {
385
+ propagate(effect2.subLink);
386
+ }
387
+ }
388
+ }
389
+ } finally {
390
+ endTriggerEffects(effects);
353
391
  }
354
- if (type === "ADD" || type === "DELETE" || type === "CLEAR") {
355
- const iterationKey = Array.isArray(target) ? ARRAY_ITERATE_KEY : ITERATE_KEY;
356
- collectTriggeredEffects(depsMap.get(iterationKey), effects, version);
357
- }
358
- for (const effect2 of effects) {
359
- if (isFunction(effect2.onTrigger)) {
360
- effect2.onTrigger({
361
- effect: effect2,
362
- target,
363
- type,
364
- key,
365
- newValue
366
- });
392
+ }
393
+ function trackNode(node) {
394
+ if (!activeSub || isUntracking) return;
395
+ linkReactiveNode(node, activeSub);
396
+ }
397
+ function triggerNode(node) {
398
+ var _a6;
399
+ const link = node.subLink;
400
+ if (!link) return;
401
+ const effects = startTriggerEffects();
402
+ const version = ++triggerVersion;
403
+ try {
404
+ for (let current = link; current; current = current.nextSubLink) {
405
+ const effect2 = current.subNode;
406
+ if (effect2._triggerVersion === version) {
407
+ continue;
408
+ }
409
+ effect2._triggerVersion = version;
410
+ effects.push(effect2);
367
411
  }
368
- if (effect2.flag & 2 /* WATCHING */) {
369
- (_a5 = effect2.notify) == null ? void 0 : _a5.call(effect2);
370
- } else if (effect2.flag & 1 /* MUTABLE */) {
371
- effect2.flag |= 16 /* DIRTY */;
372
- if (effect2.subLink) {
373
- propagate(effect2.subLink);
412
+ for (const effect2 of effects) {
413
+ if (effect2.flag & 2 /* WATCHING */) {
414
+ (_a6 = effect2.notify) == null ? void 0 : _a6.call(effect2);
415
+ } else if (effect2.flag & 1 /* MUTABLE */) {
416
+ effect2.flag |= 16 /* DIRTY */;
417
+ if (effect2.subLink) {
418
+ propagate(effect2.subLink);
419
+ }
374
420
  }
375
421
  }
422
+ } finally {
423
+ endTriggerEffects(effects);
376
424
  }
377
425
  }
426
+ var ReactiveProperty = class {
427
+ constructor(_target, _key) {
428
+ this._target = _target;
429
+ this._key = _key;
430
+ this.flag = 1 /* MUTABLE */;
431
+ }
432
+ /**
433
+ * Read the property value and track the dependency.
434
+ *
435
+ * Uses trackNode(this) — bypasses targetMap/Dep. The ReactiveProperty itself
436
+ * IS the depNode, so linkReactiveNode connects the active subscriber directly
437
+ * to this node's subLink chain.
438
+ *
439
+ * Nested proxy wrapping and signal auto-unwrapping are handled by the caller
440
+ * (the reactive proxy get trap), keeping this method self-contained and free
441
+ * of a circular dependency on the rest of reactive.ts.
442
+ */
443
+ getValue() {
444
+ trackNode(this);
445
+ return this._target[this._key];
446
+ }
447
+ /**
448
+ * Update the property value and notify subscribers.
449
+ *
450
+ * Uses triggerNode(this) — directly walks the subLink chain, bypassing
451
+ * targetMap + collectTriggeredEffects.
452
+ *
453
+ * @param rawValue - The new (already unrawed) value.
454
+ * @param oldValue - The previous value, read BEFORE Reflect.set.
455
+ */
456
+ setValue(rawValue, oldValue) {
457
+ if (Object.is(oldValue, rawValue)) {
458
+ return;
459
+ }
460
+ triggerNode(this);
461
+ }
462
+ /**
463
+ * Invalidate this property (called on delete).
464
+ * Notifies subscribers that the value is gone.
465
+ */
466
+ invalidate() {
467
+ triggerNode(this);
468
+ }
469
+ };
378
470
  var reactiveCaches = /* @__PURE__ */ new WeakMap();
379
471
  var shallowReactiveCaches = /* @__PURE__ */ new WeakMap();
380
472
  function toRaw(value) {
@@ -433,7 +525,7 @@ function createArrayInstrumentations() {
433
525
  const isShallowMode = isShallow(this);
434
526
  track(arr, ARRAY_ITERATE_KEY);
435
527
  const res = Array.prototype[key].apply(arr, args);
436
- if (!Array.isArray(res)) {
528
+ if (!isArray(res)) {
437
529
  return res;
438
530
  }
439
531
  return res.map((item) => isObject(item) ? reactiveImpl(item, isShallowMode) : item);
@@ -468,7 +560,7 @@ function createArrayInstrumentations() {
468
560
  if (done) {
469
561
  return { value, done };
470
562
  }
471
- if (Array.isArray(value)) {
563
+ if (isArray(value)) {
472
564
  return {
473
565
  value: value.map((v) => isObject(v) ? reactiveImpl(v, isShallowMode) : v),
474
566
  done
@@ -530,6 +622,32 @@ var arrayHandlers = (shallow) => ({
530
622
  }
531
623
  }
532
624
  return result;
625
+ },
626
+ has: (target, key) => {
627
+ const result = Reflect.has(target, key);
628
+ if (typeof key !== "symbol") {
629
+ if (isStringNumber(key)) {
630
+ track(target, key);
631
+ }
632
+ track(target, ARRAY_KEY);
633
+ }
634
+ return result;
635
+ },
636
+ deleteProperty: (target, key) => {
637
+ const hadKey = hasOwn(target, key);
638
+ const result = Reflect.deleteProperty(target, key);
639
+ if (hadKey && result) {
640
+ if (isStringNumber(key)) {
641
+ trigger(target, TriggerOpTypes.DELETE, [key, ARRAY_ITERATE_KEY, ARRAY_KEY]);
642
+ } else {
643
+ trigger(target, TriggerOpTypes.DELETE, key);
644
+ }
645
+ }
646
+ return result;
647
+ },
648
+ ownKeys: (target) => {
649
+ track(target, ARRAY_ITERATE_KEY);
650
+ return Reflect.ownKeys(target);
533
651
  }
534
652
  });
535
653
  var shallowArrayHandlers = arrayHandlers(true);
@@ -696,7 +814,7 @@ var collectionInstrumentations = {
696
814
  if (isShallowMode) {
697
815
  return { value, done };
698
816
  }
699
- if (Array.isArray(value)) {
817
+ if (isArray(value)) {
700
818
  return {
701
819
  value: value.map((v) => isObject(v) ? reactiveImpl(v) : v),
702
820
  done
@@ -887,9 +1005,43 @@ var weakInstrumentations = {
887
1005
  return result;
888
1006
  }
889
1007
  };
1008
+ var targetPropertyMaps = /* @__PURE__ */ new WeakMap();
1009
+ function getTargetDepSize(target, key) {
1010
+ var _a6, _b2;
1011
+ const rawTarget = (_a6 = target["_RAW" /* RAW */]) != null ? _a6 : target;
1012
+ const prop = (_b2 = targetPropertyMaps.get(rawTarget)) == null ? void 0 : _b2.get(key);
1013
+ if (!(prop == null ? void 0 : prop.subLink)) return 0;
1014
+ let size = 0;
1015
+ for (let link = prop.subLink; link; link = link.nextSubLink) size++;
1016
+ return size;
1017
+ }
1018
+ function getPropertyMap(target) {
1019
+ let map = targetPropertyMaps.get(target);
1020
+ if (!map) {
1021
+ map = /* @__PURE__ */ new Map();
1022
+ targetPropertyMaps.set(target, map);
1023
+ }
1024
+ return map;
1025
+ }
1026
+ function trackProperty(target, key) {
1027
+ if (!activeSub) {
1028
+ return target[key];
1029
+ }
1030
+ const pm = getPropertyMap(target);
1031
+ let prop = pm.get(key);
1032
+ if (!prop) {
1033
+ prop = new ReactiveProperty(target, key);
1034
+ pm.set(key, prop);
1035
+ }
1036
+ return prop.getValue();
1037
+ }
890
1038
  var objectHandlers = (shallow) => ({
891
1039
  /**
892
1040
  * Reads an object property, unwraps signals, and tracks the access.
1041
+ *
1042
+ * Own properties use the per-property signal fast path (trackNode),
1043
+ * which bypasses the targetMap → Map → Dep indirection.
1044
+ * Prototype-chain properties fall back to the standard track() path.
893
1045
  */
894
1046
  get(target, key, receiver) {
895
1047
  if (key === "_RAW" /* RAW */) {
@@ -901,6 +1053,14 @@ var objectHandlers = (shallow) => ({
901
1053
  if (key === "_IS_SHALLOW" /* IS_SHALLOW */) {
902
1054
  return shallow;
903
1055
  }
1056
+ if (activeSub && hasOwn(target, key)) {
1057
+ const result = trackProperty(target, key);
1058
+ const unwrapped = isSignal(result) ? result.value : result;
1059
+ if (isObject(unwrapped) && !shallow) {
1060
+ return reactiveImpl(unwrapped);
1061
+ }
1062
+ return unwrapped;
1063
+ }
904
1064
  const value = Reflect.get(target, key, receiver);
905
1065
  const valueUnwrapped = isSignal(value) ? value.value : value;
906
1066
  track(target, key);
@@ -914,6 +1074,14 @@ var objectHandlers = (shallow) => ({
914
1074
  const oldValue = Reflect.get(target, key, receiver);
915
1075
  const rawValue = toRaw(value);
916
1076
  const result = Reflect.set(target, key, rawValue, receiver);
1077
+ if (hadKey) {
1078
+ const pm = targetPropertyMaps.get(target);
1079
+ const prop = pm == null ? void 0 : pm.get(key);
1080
+ if (prop) {
1081
+ prop.setValue(rawValue, oldValue);
1082
+ return result;
1083
+ }
1084
+ }
917
1085
  if (hasStoredValueChanged(rawValue, oldValue)) {
918
1086
  trigger(target, hadKey ? TriggerOpTypes.SET : TriggerOpTypes.ADD, key, rawValue);
919
1087
  }
@@ -923,6 +1091,12 @@ var objectHandlers = (shallow) => ({
923
1091
  const hadKey = hasOwn(target, key);
924
1092
  const result = Reflect.deleteProperty(target, key);
925
1093
  if (hadKey && result) {
1094
+ const pm = targetPropertyMaps.get(target);
1095
+ const prop = pm == null ? void 0 : pm.get(key);
1096
+ if (prop) {
1097
+ pm.delete(key);
1098
+ prop.invalidate();
1099
+ }
926
1100
  trigger(target, TriggerOpTypes.DELETE, key, void 0);
927
1101
  }
928
1102
  return result;
@@ -1154,6 +1328,7 @@ function isSignal(value) {
1154
1328
  }
1155
1329
  var queue = /* @__PURE__ */ new Set();
1156
1330
  var activePreFlushCbs = /* @__PURE__ */ new Set();
1331
+ var activePostFlushCbs = /* @__PURE__ */ new Set();
1157
1332
  var p = Promise.resolve();
1158
1333
  var isFlushPending = false;
1159
1334
  var RECURSION_LIMIT = 100;
@@ -1174,6 +1349,10 @@ function queuePreFlushCb(cb) {
1174
1349
  activePreFlushCbs.add(cb);
1175
1350
  queueFlush();
1176
1351
  }
1352
+ function queuePostFlushJob(cb) {
1353
+ activePostFlushCbs.add(cb);
1354
+ queueFlush();
1355
+ }
1177
1356
  function flushJobs() {
1178
1357
  isFlushPending = false;
1179
1358
  flushPreFlushCbs();
@@ -1186,7 +1365,7 @@ function flushJobs() {
1186
1365
  `[Scheduler] Maximum recursive flush count (${RECURSION_LIMIT}) exceeded. This usually means an effect or watch callback is mutating a reactive dependency it also reads, causing an infinite update loop. The remaining queued jobs have been dropped to keep the app responsive.`
1187
1366
  );
1188
1367
  }
1189
- return;
1368
+ break;
1190
1369
  }
1191
1370
  const jobs = [...queue];
1192
1371
  queue.clear();
@@ -1200,6 +1379,7 @@ function flushJobs() {
1200
1379
  }
1201
1380
  }
1202
1381
  }
1382
+ flushPostFlushCbs();
1203
1383
  }
1204
1384
  function flushPreFlushCbs() {
1205
1385
  const callbacks = Array.from(activePreFlushCbs);
@@ -1214,6 +1394,20 @@ function flushPreFlushCbs() {
1214
1394
  }
1215
1395
  }
1216
1396
  }
1397
+ function flushPostFlushCbs() {
1398
+ if (activePostFlushCbs.size === 0) return;
1399
+ const callbacks = Array.from(activePostFlushCbs);
1400
+ activePostFlushCbs.clear();
1401
+ for (const callback of callbacks) {
1402
+ try {
1403
+ callback();
1404
+ } catch (_error) {
1405
+ {
1406
+ error("Error executing post-flush callback:", _error);
1407
+ }
1408
+ }
1409
+ }
1410
+ }
1217
1411
  function createScheduler(effect2, flush) {
1218
1412
  switch (flush) {
1219
1413
  case "sync":
@@ -1266,7 +1460,7 @@ var EffectScope = class {
1266
1460
  constructor(detached = false, parent = void 0) {
1267
1461
  this.detached = detached;
1268
1462
  this.parent = parent;
1269
- this._active = true;
1463
+ this._state = 0 /* Active */;
1270
1464
  // Use Sets instead of arrays for O(1) add/delete instead of O(n) indexOf+splice
1271
1465
  this.effects = /* @__PURE__ */ new Set();
1272
1466
  this.scopes = /* @__PURE__ */ new Set();
@@ -1277,36 +1471,46 @@ var EffectScope = class {
1277
1471
  }
1278
1472
  }
1279
1473
  get active() {
1280
- return this._active;
1474
+ return this._state !== 2 /* Disposed */;
1475
+ }
1476
+ /** Whether the scope is paused. */
1477
+ get isPaused() {
1478
+ return this._state === 1 /* Paused */;
1479
+ }
1480
+ /** Whether the scope has been stopped / disposed. */
1481
+ get isDisposed() {
1482
+ return this._state === 2 /* Disposed */;
1281
1483
  }
1282
1484
  pause() {
1283
- var _a5;
1284
- if (!this._active) {
1485
+ var _a6;
1486
+ if (this._state !== 0 /* Active */) {
1285
1487
  return;
1286
1488
  }
1489
+ this._state = 1 /* Paused */;
1287
1490
  for (const scope of this.scopes) {
1288
1491
  scope.pause();
1289
1492
  }
1290
1493
  for (const effect2 of this.effects) {
1291
- (_a5 = effect2.pause) == null ? void 0 : _a5.call(effect2);
1494
+ (_a6 = effect2.pause) == null ? void 0 : _a6.call(effect2);
1292
1495
  }
1293
1496
  }
1294
1497
  resume() {
1295
- var _a5;
1296
- if (!this._active) {
1498
+ var _a6;
1499
+ if (this._state !== 1 /* Paused */) {
1297
1500
  return;
1298
1501
  }
1502
+ this._state = 0 /* Active */;
1299
1503
  for (const scope of this.scopes) {
1300
1504
  scope.resume();
1301
1505
  }
1302
1506
  for (const effect2 of this.effects) {
1303
- (_a5 = effect2.resume) == null ? void 0 : _a5.call(effect2);
1507
+ (_a6 = effect2.resume) == null ? void 0 : _a6.call(effect2);
1304
1508
  }
1305
1509
  }
1306
1510
  run(fn) {
1307
- if (!this._active) {
1511
+ if (this._state === 2 /* Disposed */) {
1308
1512
  {
1309
- warn("cannot run an inactive effect scope.");
1513
+ warn("cannot run a disposed effect scope.");
1310
1514
  }
1311
1515
  return;
1312
1516
  }
@@ -1319,27 +1523,42 @@ var EffectScope = class {
1319
1523
  }
1320
1524
  }
1321
1525
  stop(fromParent = false) {
1322
- if (!this._active) {
1526
+ if (this._state === 2 /* Disposed */) {
1323
1527
  return;
1324
1528
  }
1325
- this._active = false;
1326
- for (const scope of this.scopes) {
1327
- scope.stop(true);
1328
- }
1329
- this.scopes.clear();
1330
- const effects = Array.from(this.effects);
1331
- this.effects.clear();
1332
- for (const effect2 of effects) {
1333
- effect2.stop();
1334
- }
1335
- for (let i = 0; i < this.cleanups.length; i++) {
1336
- this.cleanups[i]();
1337
- }
1338
- this.cleanups.length = 0;
1339
- if (!fromParent && this.parent) {
1340
- this.parent.scopes.delete(this);
1529
+ this._state = 2 /* Disposed */;
1530
+ try {
1531
+ for (const scope of this.scopes) {
1532
+ try {
1533
+ scope.stop(true);
1534
+ } catch (error_) {
1535
+ if (true) error("[EffectScope] child scope disposal threw:", error_);
1536
+ }
1537
+ }
1538
+ this.scopes.clear();
1539
+ const effects = Array.from(this.effects);
1540
+ this.effects.clear();
1541
+ for (const effect2 of effects) {
1542
+ try {
1543
+ effect2.stop();
1544
+ } catch (error_) {
1545
+ if (true) error("[EffectScope] effect disposal threw:", error_);
1546
+ }
1547
+ }
1548
+ for (let i = 0; i < this.cleanups.length; i++) {
1549
+ try {
1550
+ this.cleanups[i]();
1551
+ } catch (error_) {
1552
+ if (true) error("[EffectScope] cleanup threw:", error_);
1553
+ }
1554
+ }
1555
+ this.cleanups.length = 0;
1556
+ } finally {
1557
+ if (!fromParent && this.parent) {
1558
+ this.parent.scopes.delete(this);
1559
+ }
1560
+ this.parent = void 0;
1341
1561
  }
1342
- this.parent = void 0;
1343
1562
  }
1344
1563
  _record(effect2) {
1345
1564
  this.effects.add(effect2);
@@ -1507,9 +1726,9 @@ var EffectImpl = class {
1507
1726
  const prevSub = startTracking(this);
1508
1727
  try {
1509
1728
  return this.fn();
1510
- } catch (error5) {
1729
+ } catch (error6) {
1511
1730
  this.flag |= 16 /* DIRTY */;
1512
- throw error5;
1731
+ throw error6;
1513
1732
  } finally {
1514
1733
  this.flag &= -513 /* RUNNING */;
1515
1734
  endTracking(this, prevSub);
@@ -1535,13 +1754,13 @@ var EffectImpl = class {
1535
1754
  * @returns {void}
1536
1755
  */
1537
1756
  notify() {
1538
- var _a5, _b2, _c, _d;
1757
+ var _a6, _b2, _c, _d;
1539
1758
  const flags = this.flag;
1540
1759
  if (!this._active || flags & (256 /* PAUSED */ | 512 /* RUNNING */ | 16 /* DIRTY */)) {
1541
1760
  return;
1542
1761
  }
1543
1762
  this.flag = flags | 16 /* DIRTY */;
1544
- if ((_a5 = this.options) == null ? void 0 : _a5.onTrigger) {
1763
+ if ((_a6 = this.options) == null ? void 0 : _a6.onTrigger) {
1545
1764
  this.options.onTrigger({
1546
1765
  effect: this,
1547
1766
  target: {},
@@ -1574,7 +1793,7 @@ var EffectImpl = class {
1574
1793
  * @returns {void}
1575
1794
  */
1576
1795
  stop() {
1577
- var _a5;
1796
+ var _a6;
1578
1797
  if (!this._active) {
1579
1798
  return;
1580
1799
  }
@@ -1604,7 +1823,7 @@ var EffectImpl = class {
1604
1823
  );
1605
1824
  }
1606
1825
  }
1607
- if ((_a5 = this.options) == null ? void 0 : _a5.onStop) {
1826
+ if ((_a6 = this.options) == null ? void 0 : _a6.onStop) {
1608
1827
  this.options.onStop();
1609
1828
  }
1610
1829
  }
@@ -1751,7 +1970,7 @@ var ComputedImpl = class {
1751
1970
  const hadValue = oldValue !== NO_VALUE;
1752
1971
  const prevSub = startTracking(this);
1753
1972
  try {
1754
- const newValue = this.getter();
1973
+ const newValue = this.getter(hadValue ? oldValue : void 0);
1755
1974
  const flags = this.flag;
1756
1975
  const subs = this.subLink;
1757
1976
  const clearMask = ~(16 /* DIRTY */ | 32 /* PENDING */);
@@ -1871,13 +2090,13 @@ function cloneInitialState(value, seen = /* @__PURE__ */ new WeakMap()) {
1871
2090
  if (seen.has(value)) {
1872
2091
  return seen.get(value);
1873
2092
  }
1874
- if (value instanceof Date) {
2093
+ if (isDate(value)) {
1875
2094
  return new Date(value.getTime());
1876
2095
  }
1877
- if (value instanceof RegExp) {
2096
+ if (isRegExp(value)) {
1878
2097
  return new RegExp(value.source, value.flags);
1879
2098
  }
1880
- if (value instanceof Map) {
2099
+ if (isMap(value)) {
1881
2100
  const clone2 = /* @__PURE__ */ new Map();
1882
2101
  seen.set(value, clone2);
1883
2102
  value.forEach((mapValue, mapKey) => {
@@ -1885,7 +2104,7 @@ function cloneInitialState(value, seen = /* @__PURE__ */ new WeakMap()) {
1885
2104
  });
1886
2105
  return clone2;
1887
2106
  }
1888
- if (value instanceof Set) {
2107
+ if (isSet(value)) {
1889
2108
  const clone2 = /* @__PURE__ */ new Set();
1890
2109
  seen.set(value, clone2);
1891
2110
  value.forEach((setValue) => {
@@ -1893,7 +2112,7 @@ function cloneInitialState(value, seen = /* @__PURE__ */ new WeakMap()) {
1893
2112
  });
1894
2113
  return clone2;
1895
2114
  }
1896
- if (Array.isArray(value)) {
2115
+ if (isArray(value)) {
1897
2116
  const clone2 = [];
1898
2117
  seen.set(value, clone2);
1899
2118
  value.forEach((item, index) => {
@@ -1902,7 +2121,7 @@ function cloneInitialState(value, seen = /* @__PURE__ */ new WeakMap()) {
1902
2121
  return clone2;
1903
2122
  }
1904
2123
  const prototype = Object.getPrototypeOf(value);
1905
- if (prototype !== Object.prototype && prototype !== null) {
2124
+ if (prototype !== Object.prototype && !isNull(prototype)) {
1906
2125
  return value;
1907
2126
  }
1908
2127
  const clone = Object.create(prototype);
@@ -1933,11 +2152,58 @@ function createOptionsStore(options) {
1933
2152
  };
1934
2153
  }
1935
2154
  set.add(callback);
1936
- return () => set.delete(callback);
2155
+ const unsubscribe = () => set.delete(callback);
2156
+ onScopeDispose(
2157
+ unsubscribe,
2158
+ /* failSilently */
2159
+ true
2160
+ );
2161
+ return unsubscribe;
2162
+ };
2163
+ let activeTransaction = null;
2164
+ const flushTransaction = (transaction) => {
2165
+ if (transaction.depth === 0 && transaction.pending === 0 && !transaction.notified) {
2166
+ transaction.notified = true;
2167
+ notify(reactiveState);
2168
+ }
1937
2169
  };
1938
2170
  const commit = (mutate) => {
1939
- batch(mutate);
1940
- notify(reactiveState);
2171
+ const transaction = activeTransaction != null ? activeTransaction : {
2172
+ depth: 0,
2173
+ pending: 0,
2174
+ notified: false
2175
+ };
2176
+ const previousTransaction = activeTransaction;
2177
+ transaction.depth++;
2178
+ let result;
2179
+ try {
2180
+ activeTransaction = transaction;
2181
+ result = batch(mutate);
2182
+ } catch (error6) {
2183
+ transaction.depth--;
2184
+ activeTransaction = previousTransaction;
2185
+ throw error6;
2186
+ }
2187
+ activeTransaction = previousTransaction;
2188
+ if (isPromise(result)) {
2189
+ transaction.pending++;
2190
+ transaction.depth--;
2191
+ return result.then(
2192
+ (value) => {
2193
+ transaction.pending--;
2194
+ flushTransaction(transaction);
2195
+ return value;
2196
+ },
2197
+ (error6) => {
2198
+ transaction.pending--;
2199
+ flushTransaction(transaction);
2200
+ throw error6;
2201
+ }
2202
+ );
2203
+ }
2204
+ transaction.depth--;
2205
+ flushTransaction(transaction);
2206
+ return result;
1941
2207
  };
1942
2208
  const defaultActions = {
1943
2209
  $patch(payload) {
@@ -1960,7 +2226,15 @@ function createOptionsStore(options) {
1960
2226
  actionCallbacks.delete(callback);
1961
2227
  },
1962
2228
  $reset() {
1963
- commit(() => Object.assign(reactiveState, initState));
2229
+ commit(() => {
2230
+ const fresh = cloneInitialState(initState);
2231
+ for (const key of Object.keys(reactiveState)) {
2232
+ if (!Object.prototype.hasOwnProperty.call(fresh, key)) {
2233
+ delete reactiveState[key];
2234
+ }
2235
+ }
2236
+ Object.assign(reactiveState, fresh);
2237
+ });
1964
2238
  }
1965
2239
  };
1966
2240
  const store = {};
@@ -1978,7 +2252,7 @@ function createOptionsStore(options) {
1978
2252
  value: reactiveState,
1979
2253
  enumerable: true,
1980
2254
  configurable: true,
1981
- writable: true
2255
+ writable: false
1982
2256
  });
1983
2257
  Object.assign(store, defaultActions);
1984
2258
  if (getters) {
@@ -1998,9 +2272,7 @@ function createOptionsStore(options) {
1998
2272
  const action = actions[key];
1999
2273
  if (action) {
2000
2274
  store[key] = (...args) => {
2001
- const result = action.apply(store, args);
2002
- actionCallbacks.forEach((callback) => callback(reactiveState));
2003
- return result;
2275
+ return commit(() => action.apply(store, args));
2004
2276
  };
2005
2277
  }
2006
2278
  }
@@ -2072,6 +2344,12 @@ var RefImpl = class extends (_b = SignalImpl, _a4 = "_IS_REF" /* IS_REF */, _b)
2072
2344
  if (sub) {
2073
2345
  linkReactiveNode(this, sub);
2074
2346
  }
2347
+ if (this.flag & 16 /* DIRTY */ && this.shouldUpdate()) {
2348
+ const subs = this.subLink;
2349
+ if (subs) {
2350
+ shallowPropagate(subs);
2351
+ }
2352
+ }
2075
2353
  return this._value;
2076
2354
  }
2077
2355
  /**
@@ -2087,6 +2365,7 @@ var RefImpl = class extends (_b = SignalImpl, _a4 = "_IS_REF" /* IS_REF */, _b)
2087
2365
  newValue = newValue.value;
2088
2366
  }
2089
2367
  if (hasChanged(this._value, newValue)) {
2368
+ this._oldValue = this._rawValue;
2090
2369
  this._rawValue = newValue;
2091
2370
  this._value = newValue;
2092
2371
  this.flag |= 16 /* DIRTY */;
@@ -2108,6 +2387,45 @@ function ref(value = void 0) {
2108
2387
  function isRef(value) {
2109
2388
  return !!value && !!value["_IS_REF" /* IS_REF */];
2110
2389
  }
2390
+ var _a5;
2391
+ _a5 = "_IS_COMPUTED" /* IS_COMPUTED */;
2392
+ var ObjectPropertyRef = class {
2393
+ constructor(obj, key, defaultValue) {
2394
+ this.obj = obj;
2395
+ this.key = key;
2396
+ this.defaultValue = defaultValue;
2397
+ this[_a5] = true;
2398
+ }
2399
+ get value() {
2400
+ const value = this.obj[this.key];
2401
+ return value !== void 0 ? value : this.defaultValue;
2402
+ }
2403
+ set value(value) {
2404
+ this.obj[this.key] = value;
2405
+ }
2406
+ peek() {
2407
+ return untrack(() => this.value);
2408
+ }
2409
+ };
2410
+ function unref(value) {
2411
+ if (isSignal(value) || isRef(value) || isComputed(value)) {
2412
+ return value.value;
2413
+ }
2414
+ if (isFunction(value)) {
2415
+ return value();
2416
+ }
2417
+ return value;
2418
+ }
2419
+ function toRef(obj, key, defaultValue) {
2420
+ return new ObjectPropertyRef(obj, key, defaultValue);
2421
+ }
2422
+ function toRefs(obj) {
2423
+ const result = {};
2424
+ for (const key of Object.keys(obj)) {
2425
+ result[key] = toRef(obj, key);
2426
+ }
2427
+ return result;
2428
+ }
2111
2429
  var INITIAL_WATCHER_VALUE = {};
2112
2430
  var _traverseSeen = /* @__PURE__ */ new Set();
2113
2431
  var _traverseBusy = false;
@@ -2118,7 +2436,7 @@ function traverseValue(value, seen) {
2118
2436
  seen.add(value);
2119
2437
  if (isSignal(value) || isComputed(value)) {
2120
2438
  traverseValue(value.value, seen);
2121
- } else if (Array.isArray(value)) {
2439
+ } else if (isArray(value)) {
2122
2440
  for (const element of value) {
2123
2441
  traverseValue(element, seen);
2124
2442
  }
@@ -2168,7 +2486,7 @@ function resolveSingleSource(source) {
2168
2486
  return () => source;
2169
2487
  }
2170
2488
  function resolveSource(source) {
2171
- if (Array.isArray(source)) {
2489
+ if (isArray(source)) {
2172
2490
  const getters = source.map((s) => resolveSingleSource(s));
2173
2491
  return () => getters.map((g) => g());
2174
2492
  }
@@ -2179,7 +2497,7 @@ function watch(source, callback, options = {}) {
2179
2497
  let oldValue = INITIAL_WATCHER_VALUE;
2180
2498
  let lastValue;
2181
2499
  let active = true;
2182
- const isSingleReactive = !Array.isArray(source) && isReactive(source);
2500
+ const isSingleReactive = !isArray(source) && isReactive(source);
2183
2501
  const needTraverse = deep && !isSingleReactive;
2184
2502
  const getter = resolveSource(source);
2185
2503
  let cleanup;
@@ -2188,8 +2506,8 @@ function watch(source, callback, options = {}) {
2188
2506
  cleanup = void 0;
2189
2507
  try {
2190
2508
  fn();
2191
- } catch (error5) {
2192
- warn("[watch] cleanup handler threw:", error5);
2509
+ } catch (error6) {
2510
+ warn("[watch] cleanup handler threw:", error6);
2193
2511
  }
2194
2512
  };
2195
2513
  };
@@ -2238,4 +2556,4 @@ function watch(source, callback, options = {}) {
2238
2556
  return stop2;
2239
2557
  }
2240
2558
 
2241
- export { EffectScope, TriggerOpTypes, batch, computed, createStore, effect, effectScope, endBatch, getBatchDepth, getCurrentScope, isBatching, isComputed, isEffect, isReactive, isRef, isShallow, isSignal, memoEffect, nextTick, onScopeDispose, queueJob, queuePreFlushCb, reactive, ref, setCurrentScope, shallowReactive, shallowSignal, signal, startBatch, stop, toRaw, toReactive, trigger, untrack, watch };
2559
+ export { EffectScope, TriggerOpTypes, batch, computed, createStore, effect, effectScope, endBatch, getBatchDepth, getCurrentScope, getTargetDepSize, isBatching, isComputed, isEffect, isReactive, isRef, isShallow, isSignal, memoEffect, nextTick, onScopeDispose, queueJob, queuePostFlushJob, queuePreFlushCb, reactive, ref, setCurrentScope, shallowReactive, shallowSignal, signal, startBatch, stop, toRaw, toReactive, toRef, toRefs, trigger, unref, untrack, watch };