@estjs/signals 0.0.15-beta.1 → 0.0.15-beta.11

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.
@@ -348,6 +348,7 @@ type SignalType<T> = T extends Signal<infer V> ? V : never;
348
348
  * const empty = signal(); // undefined
349
349
  * ```
350
350
  */
351
+ declare function signal<T>(value: Signal<T>): Signal<T>;
351
352
  declare function signal<T>(value?: T): Signal<T>;
352
353
  /**
353
354
  * Create a new shallow signal with the given initial value.
@@ -363,6 +364,7 @@ declare function signal<T>(value?: T): Signal<T>;
363
364
  * // Only state.nested is reactive, not state.nested.value
364
365
  * ```
365
366
  */
367
+ declare function shallowSignal<T>(value: Signal<T>): Signal<T>;
366
368
  declare function shallowSignal<T>(value?: T): Signal<T>;
367
369
  /**
368
370
  * Type guard to check if a value is a Signal instance.
@@ -992,7 +994,7 @@ declare function startBatch(): void;
992
994
  * End batch update
993
995
  *
994
996
  * Decreases batch depth.
995
- * When depth reaches zero, flush all queued Effects and clean up.
997
+ * When depth reaches zero, flush all queued Effects.
996
998
  *
997
999
  * ## Cleanup Process
998
1000
  *
@@ -1001,6 +1003,12 @@ declare function startBatch(): void;
1001
1003
  * 2. Job queue is automatically cleared by flushJobs()
1002
1004
  * 3. Temporary flags (QUEUED, DIRTY) are cleared by effect execution
1003
1005
  *
1006
+ * ## Development Mode Checks
1007
+ *
1008
+ * In development mode, this function performs additional validation:
1009
+ * - Detects unbalanced batch calls (endBatch without startBatch)
1010
+ * - Prevents batchDepth from becoming negative
1011
+ * - Provides clear error messages to help debug batch management issues
1004
1012
  */
1005
1013
  declare function endBatch(): void;
1006
1014
  /**
package/dist/signals.d.ts CHANGED
@@ -348,6 +348,7 @@ type SignalType<T> = T extends Signal<infer V> ? V : never;
348
348
  * const empty = signal(); // undefined
349
349
  * ```
350
350
  */
351
+ declare function signal<T>(value: Signal<T>): Signal<T>;
351
352
  declare function signal<T>(value?: T): Signal<T>;
352
353
  /**
353
354
  * Create a new shallow signal with the given initial value.
@@ -363,6 +364,7 @@ declare function signal<T>(value?: T): Signal<T>;
363
364
  * // Only state.nested is reactive, not state.nested.value
364
365
  * ```
365
366
  */
367
+ declare function shallowSignal<T>(value: Signal<T>): Signal<T>;
366
368
  declare function shallowSignal<T>(value?: T): Signal<T>;
367
369
  /**
368
370
  * Type guard to check if a value is a Signal instance.
@@ -992,7 +994,7 @@ declare function startBatch(): void;
992
994
  * End batch update
993
995
  *
994
996
  * Decreases batch depth.
995
- * When depth reaches zero, flush all queued Effects and clean up.
997
+ * When depth reaches zero, flush all queued Effects.
996
998
  *
997
999
  * ## Cleanup Process
998
1000
  *
@@ -1001,6 +1003,12 @@ declare function startBatch(): void;
1001
1003
  * 2. Job queue is automatically cleared by flushJobs()
1002
1004
  * 3. Temporary flags (QUEUED, DIRTY) are cleared by effect execution
1003
1005
  *
1006
+ * ## Development Mode Checks
1007
+ *
1008
+ * In development mode, this function performs additional validation:
1009
+ * - Detects unbalanced batch calls (endBatch without startBatch)
1010
+ * - Prevents batchDepth from becoming negative
1011
+ * - Provides clear error messages to help debug batch management issues
1004
1012
  */
1005
1013
  declare function endBatch(): void;
1006
1014
  /**
@@ -2,11 +2,6 @@
2
2
 
3
3
  var shared = require('@estjs/shared');
4
4
 
5
- /**
6
- * @estjs/signals v0.0.15-beta.1
7
- * (c) 2023-Present jiangxd <jiangxd2016@gmail.com>
8
- * @license MIT
9
- **/
10
5
  var __defProp = Object.defineProperty;
11
6
  var __defProps = Object.defineProperties;
12
7
  var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
@@ -34,11 +29,11 @@ var TriggerOpTypes = {
34
29
  DELETE: "DELETE",
35
30
  CLEAR: "CLEAR"
36
31
  };
37
- var SIGNAL_KEY = Symbol("Signal_Key" );
38
- var ARRAY_KEY = Symbol("Array_Key" );
39
- var COLLECTION_KEY = Symbol("Collection_Key" );
40
- var WEAK_COLLECTION_KEY = Symbol("WeakCollection_Key" );
41
- var ARRAY_ITERATE_KEY = Symbol("Array_Iterate_Key" );
32
+ var SIGNAL_KEY = /* @__PURE__ */ Symbol("Signal_Key" );
33
+ var ARRAY_KEY = /* @__PURE__ */ Symbol("Array_Key" );
34
+ var COLLECTION_KEY = /* @__PURE__ */ Symbol("Collection_Key" );
35
+ var WEAK_COLLECTION_KEY = /* @__PURE__ */ Symbol("WeakCollection_Key" );
36
+ var ARRAY_ITERATE_KEY = /* @__PURE__ */ Symbol("Array_Iterate_Key" );
42
37
 
43
38
  // src/propagation.ts
44
39
  function propagate(link) {
@@ -375,9 +370,9 @@ function trigger(target, type, key, newValue) {
375
370
  }
376
371
  }
377
372
  if (type === "ADD" || type === "DELETE" || type === "CLEAR") {
378
- const ITERATE_KEY2 = Symbol("iterate");
379
- const ARRAY_ITERATE_KEY2 = Symbol("arrayIterate");
380
- const iterationKey = Array.isArray(target) ? ARRAY_ITERATE_KEY2 : ITERATE_KEY2;
373
+ const ITERATE_KEY = /* @__PURE__ */ Symbol("iterate");
374
+ const ARRAY_ITERATE_KEY2 = /* @__PURE__ */ Symbol("arrayIterate");
375
+ const iterationKey = Array.isArray(target) ? ARRAY_ITERATE_KEY2 : ITERATE_KEY;
381
376
  const iterationDep = depsMap.get(iterationKey);
382
377
  if (iterationDep) {
383
378
  iterationDep.forEach((effect2) => effects.add(effect2));
@@ -884,44 +879,18 @@ function isReactive(target) {
884
879
  }
885
880
  function reactive(target) {
886
881
  if (isReactive(target)) {
887
- {
888
- shared.warn(
889
- "[Reactive] Target is already reactive. Returning existing reactive proxy to avoid double wrapping."
890
- );
891
- }
892
882
  return target;
893
883
  }
894
884
  if (isSignal(target)) {
895
- {
896
- shared.warn(
897
- "[Reactive] Creating a reactive proxy from a signal is not recommended. Use the signal directly or access its value property."
898
- );
899
- }
900
885
  return target;
901
886
  }
902
887
  return reactiveImpl(target);
903
888
  }
904
889
  function shallowReactive(target) {
905
890
  if (isReactive(target)) {
906
- {
907
- if (isShallow(target)) {
908
- shared.warn(
909
- "[ShallowReactive] Target is already a shallow reactive proxy. Returning existing proxy to avoid double wrapping."
910
- );
911
- } else {
912
- shared.warn(
913
- "[ShallowReactive] Target is already a deep reactive proxy. Cannot convert deep reactive to shallow reactive. Returning existing proxy."
914
- );
915
- }
916
- }
917
891
  return target;
918
892
  }
919
893
  if (isSignal(target)) {
920
- {
921
- shared.warn(
922
- "[ShallowReactive] Creating a reactive proxy from a signal is not recommended. Use the signal directly or access its value property."
923
- );
924
- }
925
894
  return target;
926
895
  }
927
896
  return reactiveImpl(target, true);
@@ -947,7 +916,7 @@ var SignalImpl = class {
947
916
  // Mark whether it's shallow reactive
948
917
  // @ts-ignore
949
918
  this[_a] = true;
950
- this._oldValue = this._rawValue = value;
919
+ this._rawValue = value;
951
920
  if (shallow) {
952
921
  this._value = shared.isObject(value) ? shallowReactive(value) : value;
953
922
  } else {
@@ -964,7 +933,8 @@ var SignalImpl = class {
964
933
  if (sub) {
965
934
  linkReactiveNode(this, sub);
966
935
  }
967
- if (this.flag & 16 /* DIRTY */ && this.shouldUpdate()) {
936
+ const flags = this.flag;
937
+ if (flags & 16 /* DIRTY */ && this.shouldUpdate()) {
968
938
  const subs = this.subLink;
969
939
  if (subs) {
970
940
  shallowPropagate2(subs);
@@ -986,7 +956,11 @@ var SignalImpl = class {
986
956
  if (!shared.hasChanged(this._rawValue, value)) {
987
957
  return;
988
958
  }
989
- this.flag |= 16 /* DIRTY */;
959
+ if (!("_oldValue" in this)) {
960
+ this._oldValue = this._rawValue;
961
+ }
962
+ const flags = this.flag;
963
+ this.flag = flags | 16 /* DIRTY */;
990
964
  this._rawValue = value;
991
965
  const shallow = this["_IS_SHALLOW" /* IS_SHALLOW */];
992
966
  if (shallow) {
@@ -1002,7 +976,12 @@ var SignalImpl = class {
1002
976
  // Check if the value should be update
1003
977
  shouldUpdate() {
1004
978
  this.flag &= -17 /* DIRTY */;
1005
- return shared.hasChanged(this._oldValue, this._oldValue = this._rawValue);
979
+ if (!("_oldValue" in this)) {
980
+ return true;
981
+ }
982
+ const changed = shared.hasChanged(this._oldValue, this._rawValue);
983
+ this._oldValue = this._rawValue;
984
+ return changed;
1006
985
  }
1007
986
  // Get current value without triggering dependency tracking
1008
987
  peek() {
@@ -1040,11 +1019,6 @@ function signal(value) {
1040
1019
  }
1041
1020
  function shallowSignal(value) {
1042
1021
  if (isSignal(value)) {
1043
- {
1044
- shared.warn(
1045
- "Creating a shallow signal with another signal is not recommended. The value will be unwrapped."
1046
- );
1047
- }
1048
1022
  value = value.peek();
1049
1023
  }
1050
1024
  return new SignalImpl(value, true);
@@ -1063,8 +1037,8 @@ function nextTick(fn) {
1063
1037
  try {
1064
1038
  fn();
1065
1039
  resolve();
1066
- } catch (error6) {
1067
- reject(error6);
1040
+ } catch (error5) {
1041
+ reject(error5);
1068
1042
  }
1069
1043
  });
1070
1044
  });
@@ -1147,17 +1121,13 @@ function startBatch() {
1147
1121
  function endBatch() {
1148
1122
  if (batchDepth === 0) {
1149
1123
  shared.warn(
1150
- "[Batch] endBatch() called without matching startBatch(). This may indicate unbalanced batch calls in your code."
1124
+ "[Batch] endBatch() called without matching startBatch(). This indicates unbalanced batch calls in your code. Make sure every startBatch() has a corresponding endBatch(), or use the batch() function which handles this automatically."
1151
1125
  );
1152
1126
  return;
1153
1127
  }
1154
- if (--batchDepth === 0) {
1128
+ batchDepth--;
1129
+ if (batchDepth === 0) {
1155
1130
  flushJobs();
1156
- if (batchDepth !== 0) {
1157
- shared.error(
1158
- `[Batch] Batch depth is not zero after endBatch(). Current depth: ${batchDepth}. This indicates a bug in batch management.`
1159
- );
1160
- }
1161
1131
  }
1162
1132
  }
1163
1133
  function isBatching() {
@@ -1181,7 +1151,7 @@ var EffectImpl = class {
1181
1151
  this.flag = 2 /* WATCHING */ | 16 /* DIRTY */;
1182
1152
  // @ts-ignore
1183
1153
  this[_a2] = true;
1184
- // ===== State management =====
1154
+ // State management
1185
1155
  this._active = true;
1186
1156
  this.fn = fn;
1187
1157
  if (options) {
@@ -1299,9 +1269,9 @@ var EffectImpl = class {
1299
1269
  const prevSub = startTracking(this);
1300
1270
  try {
1301
1271
  return this.fn();
1302
- } catch (error6) {
1272
+ } catch (error5) {
1303
1273
  this.flag |= 16 /* DIRTY */;
1304
- throw error6;
1274
+ throw error5;
1305
1275
  } finally {
1306
1276
  this.flag &= -1025 /* STOP */;
1307
1277
  endTracking(this, prevSub);
@@ -1427,7 +1397,7 @@ function memoEffect(fn, initialState, options) {
1427
1397
  };
1428
1398
  return effect(effectFn, options);
1429
1399
  }
1430
- var NO_VALUE = Symbol("computed-no-value");
1400
+ var NO_VALUE = /* @__PURE__ */ Symbol("computed-no-value");
1431
1401
  var _a3;
1432
1402
  _a3 = "_IS_COMPUTED" /* IS_COMPUTED */;
1433
1403
  var ComputedImpl = class {
@@ -1443,7 +1413,7 @@ var ComputedImpl = class {
1443
1413
  this.flag = 1 /* MUTABLE */ | 16 /* DIRTY */;
1444
1414
  //@ts-ignore
1445
1415
  this[_a3] = true;
1446
- // ===== Cache =====
1416
+ // Cache
1447
1417
  // Use symbol sentinel to distinguish "no value" from undefined/null values
1448
1418
  this._value = NO_VALUE;
1449
1419
  this.getter = getter;
@@ -1517,6 +1487,7 @@ var ComputedImpl = class {
1517
1487
  try {
1518
1488
  const newValue = this.getter();
1519
1489
  const flags = this.flag;
1490
+ const subs = this.subLink;
1520
1491
  const clearMask = ~(16 /* DIRTY */ | 32 /* PENDING */);
1521
1492
  const valueChanged = !hadValue || shared.hasChanged(oldValue, newValue);
1522
1493
  if (valueChanged) {
@@ -1531,17 +1502,19 @@ var ComputedImpl = class {
1531
1502
  newValue
1532
1503
  });
1533
1504
  }
1534
- if (this.subLink) {
1535
- shallowPropagate(this.subLink);
1505
+ if (subs) {
1506
+ shallowPropagate(subs);
1536
1507
  }
1537
1508
  } else {
1538
1509
  this.flag = flags & clearMask;
1539
1510
  }
1540
1511
  } catch (_error) {
1541
- this.flag &= -49;
1512
+ const clearMask = -49;
1513
+ this.flag &= clearMask;
1514
+ this.flag |= 16 /* DIRTY */;
1542
1515
  {
1543
1516
  shared.error(
1544
- "[Computed] Error occurred while computing value. Check your getter function for errors.",
1517
+ "[Computed] Error occurred while computing value.\nThe computed will retry on next access.\nCommon causes:\n - Accessing undefined properties\n - Circular dependencies\n - Exceptions in getter function\nCheck your getter function for errors.",
1545
1518
  _error
1546
1519
  );
1547
1520
  }
@@ -1617,6 +1590,10 @@ function createOptionsStore(options) {
1617
1590
  const reactiveState = reactive(state);
1618
1591
  const subscriptions = /* @__PURE__ */ new Set();
1619
1592
  const actionCallbacks = /* @__PURE__ */ new Set();
1593
+ const notifySubscribers = (state2) => {
1594
+ subscriptions.forEach((callback) => callback(state2));
1595
+ actionCallbacks.forEach((callback) => callback(state2));
1596
+ };
1620
1597
  const defaultActions = {
1621
1598
  patch$(payload) {
1622
1599
  if (!payload) {
@@ -1626,8 +1603,7 @@ function createOptionsStore(options) {
1626
1603
  batch(() => {
1627
1604
  Object.assign(reactiveState, payload);
1628
1605
  });
1629
- subscriptions.forEach((callback) => callback(reactiveState));
1630
- actionCallbacks.forEach((callback) => callback(reactiveState));
1606
+ notifySubscribers(reactiveState);
1631
1607
  },
1632
1608
  subscribe$(callback) {
1633
1609
  if (!callback) {
@@ -1650,8 +1626,7 @@ function createOptionsStore(options) {
1650
1626
  batch(() => {
1651
1627
  Object.assign(reactiveState, initState);
1652
1628
  });
1653
- subscriptions.forEach((callback) => callback(reactiveState));
1654
- actionCallbacks.forEach((callback) => callback(reactiveState));
1629
+ notifySubscribers(reactiveState);
1655
1630
  }
1656
1631
  };
1657
1632
  const store = __spreadValues(__spreadProps(__spreadValues({}, reactiveState), {
@@ -1661,8 +1636,21 @@ function createOptionsStore(options) {
1661
1636
  for (const key in getters) {
1662
1637
  const getter = getters[key];
1663
1638
  if (getter) {
1639
+ let accessCount = 0;
1640
+ let lastWarnTime = 0;
1664
1641
  Object.defineProperty(store, key, {
1665
1642
  get() {
1643
+ {
1644
+ accessCount++;
1645
+ const now = Date.now();
1646
+ if (accessCount > 100 && now - lastWarnTime > 1e3) {
1647
+ shared.warn(
1648
+ `Getter '${key}' has been accessed ${accessCount} times. Consider caching the result if the value is used frequently. Note: Getters are computed properties that recalculate on every access.`
1649
+ );
1650
+ lastWarnTime = now;
1651
+ accessCount = 0;
1652
+ }
1653
+ }
1666
1654
  return computed(() => getter.call(store, reactiveState)).value;
1667
1655
  },
1668
1656
  enumerable: true,
@@ -1696,11 +1684,11 @@ function createClassStore(StoreClass) {
1696
1684
  Object.getOwnPropertyNames(StoreClass.prototype).forEach((key) => {
1697
1685
  const descriptor = Object.getOwnPropertyDescriptor(StoreClass.prototype, key);
1698
1686
  if (descriptor) {
1699
- if (typeof descriptor.get === "function") {
1687
+ if (shared.isFunction(descriptor.get)) {
1700
1688
  getters[key] = function() {
1701
1689
  return descriptor.get.call(this);
1702
1690
  };
1703
- } else if (typeof descriptor.value === "function" && key !== "constructor") {
1691
+ } else if (shared.isFunction(descriptor.value) && key !== "constructor") {
1704
1692
  actions[key] = function(...args) {
1705
1693
  return descriptor.value.apply(this, args);
1706
1694
  };
@@ -1720,13 +1708,13 @@ function createStore(storeDefinition) {
1720
1708
  }
1721
1709
  return () => {
1722
1710
  let options;
1723
- if (typeof storeDefinition === "function") {
1711
+ if (shared.isFunction(storeDefinition)) {
1724
1712
  options = createClassStore(storeDefinition);
1725
1713
  } else {
1726
1714
  options = storeDefinition;
1727
1715
  }
1728
1716
  const store = createOptionsStore(options);
1729
- if (typeof storeDefinition === "function") {
1717
+ if (shared.isFunction(storeDefinition)) {
1730
1718
  Object.keys(options.actions || {}).forEach((key) => {
1731
1719
  store[key] = options.actions[key].bind(store);
1732
1720
  });
@@ -1768,15 +1756,9 @@ var RefImpl = class extends (_b = SignalImpl, _a4 = "_IS_REF" /* IS_REF */, _b)
1768
1756
  };
1769
1757
  function ref(value = void 0) {
1770
1758
  if (isRef(value)) {
1771
- {
1772
- shared.info("Creating a ref with another ref is not recommended. The value will be unwrapped.");
1773
- }
1774
1759
  return value;
1775
1760
  }
1776
1761
  if (isSignal(value)) {
1777
- {
1778
- shared.info("Creating a ref with a signal is not recommended. The value will be unwrapped.");
1779
- }
1780
1762
  return new RefImpl(value.peek());
1781
1763
  }
1782
1764
  return new RefImpl(value);