@oscarpalmer/mora 0.38.0 → 0.39.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 (57) hide show
  1. package/dist/batch.d.mts +1 -3
  2. package/dist/batch.mjs +3 -4
  3. package/dist/constants.d.mts +16 -10
  4. package/dist/constants.mjs +12 -6
  5. package/dist/effect.d.mts +2 -3
  6. package/dist/effect.mjs +27 -8
  7. package/dist/helpers/is.d.mts +1 -3
  8. package/dist/helpers/is.mjs +8 -5
  9. package/dist/helpers/misc.d.mts +8 -4
  10. package/dist/helpers/misc.mjs +66 -4
  11. package/dist/helpers/proxy.d.mts +3 -4
  12. package/dist/helpers/proxy.mjs +26 -11
  13. package/dist/helpers/subscription.d.mts +1 -3
  14. package/dist/helpers/subscription.mjs +15 -7
  15. package/dist/helpers/value.d.mts +4 -5
  16. package/dist/helpers/value.mjs +18 -13
  17. package/dist/models.d.mts +38 -5
  18. package/dist/mora.full.mjs +555 -276
  19. package/dist/value/array.d.mts +1 -3
  20. package/dist/value/array.mjs +46 -22
  21. package/dist/value/computed.d.mts +1 -3
  22. package/dist/value/computed.mjs +17 -11
  23. package/dist/value/readonly.d.mts +2 -5
  24. package/dist/value/readonly.mjs +24 -16
  25. package/dist/value/signal.d.mts +1 -3
  26. package/dist/value/signal.mjs +9 -6
  27. package/dist/value/store.d.mts +1 -3
  28. package/dist/value/store.mjs +9 -6
  29. package/package.json +5 -12
  30. package/src/batch.ts +8 -4
  31. package/src/constants.ts +26 -16
  32. package/src/effect.ts +37 -7
  33. package/src/helpers/is.ts +18 -8
  34. package/src/helpers/misc.ts +121 -2
  35. package/src/helpers/proxy.ts +59 -9
  36. package/src/helpers/subscription.ts +34 -4
  37. package/src/helpers/value.ts +48 -15
  38. package/src/models.ts +42 -3
  39. package/src/value/array.ts +100 -21
  40. package/src/value/computed.ts +29 -10
  41. package/src/value/readonly.ts +38 -33
  42. package/src/value/signal.ts +11 -5
  43. package/src/value/store.ts +11 -5
  44. package/types/batch.d.ts +0 -11
  45. package/types/constants.d.ts +0 -16
  46. package/types/effect.d.ts +0 -13
  47. package/types/helpers/is.d.ts +0 -43
  48. package/types/helpers/proxy.d.ts +0 -11
  49. package/types/helpers/value.d.ts +0 -4
  50. package/types/index.d.ts +0 -9
  51. package/types/models.d.ts +0 -56
  52. package/types/subscription.d.ts +0 -11
  53. package/types/value/array.d.ts +0 -137
  54. package/types/value/computed.d.ts +0 -14
  55. package/types/value/reactive.d.ts +0 -36
  56. package/types/value/signal.d.ts +0 -26
  57. package/types/value/store.d.ts +0 -94
@@ -1,5 +1,4 @@
1
1
  import { ReactiveArray, ReactiveOptions } from "../models.mjs";
2
- //#region src/value/array.d.ts
3
2
  /**
4
3
  * Create a reactive array from a function result
5
4
  *
@@ -23,5 +22,4 @@ export declare function array<Item>(value: Promise<Item[]>, options?: ReactiveOp
23
22
  * @param options Reactivity options
24
23
  * @returns Reactive array
25
24
  */
26
- export declare function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
27
- //#endregion
25
+ export declare function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
@@ -1,17 +1,17 @@
1
- import { METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, NAME_MORA, PROPERTY_LENGTH, SYMBOL_STATE } from "../constants.mjs";
1
+ import { MESSAGE_DEACTIVATED_ARRAY_FILTER, MESSAGE_DEACTIVATED_ARRAY_MAP, MESSAGE_DEACTIVATED_ARRAY_SELECT, METHODS_AFFECTING_LENGTH, METHODS_UPDATE, NAME_ARRAY, PROPERTY_LENGTH, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { emitValue, equalArrays, getJsonValue, getStringValue } from "../helpers/value.mjs";
3
- import { getState } from "../helpers/misc.mjs";
3
+ import { deactivateProxy, getReactiveActive, getState } from "../helpers/misc.mjs";
4
4
  import { subscribeToProxy } from "../helpers/subscription.mjs";
5
5
  import { computed } from "./computed.mjs";
6
- import { getValueInProxy, notifyProxyValue, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
6
+ import { getValueInProxy, notifyProxyValue, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyDerivations, updateProxyValue } from "../helpers/proxy.mjs";
7
7
  import { getReadonlySignal } from "./readonly.mjs";
8
8
  import { signal } from "./signal.mjs";
9
9
  import { filter } from "@oscarpalmer/atoms/array/filter";
10
10
  import { select } from "@oscarpalmer/atoms/array/select";
11
- //#region src/value/array.ts
11
+ // src/value/array.ts
12
12
  function ReactiveArray(value, options) {
13
13
  this[SYMBOL_STATE] = {
14
- ...getState(void 0, options),
14
+ ...getState(NAME_ARRAY, void 0, options),
15
15
  isArray: true,
16
16
  length: signal(0)
17
17
  };
@@ -21,15 +21,10 @@ function ReactiveArray(value, options) {
21
21
  });
22
22
  setProxyValue.call(this, value);
23
23
  }
24
- ReactiveArray.prototype[NAME_MORA] = NAME_ARRAY;
25
- Object.defineProperty(ReactiveArray.prototype, PROPERTY_LENGTH, {
26
- enumerable: true,
27
- get: getArrayLength,
28
- set: setArrayLength
29
- });
30
24
  ReactiveArray.prototype.asReadonly = getReadonlySignal;
31
25
  ReactiveArray.prototype.at = getArrayValues;
32
26
  ReactiveArray.prototype.clear = clearArrayValues;
27
+ ReactiveArray.prototype.deactivate = deactivateArray;
33
28
  ReactiveArray.prototype.filter = filterArrayValues;
34
29
  ReactiveArray.prototype.get = getArrayValues;
35
30
  ReactiveArray.prototype.map = mapArrayValues;
@@ -46,14 +41,33 @@ ReactiveArray.prototype.toString = getStringValue;
46
41
  ReactiveArray.prototype.toJSON = getJsonValue;
47
42
  ReactiveArray.prototype.unshift = unshiftArrayValues;
48
43
  ReactiveArray.prototype.update = updateProxyValue;
44
+ Object.defineProperties(ReactiveArray.prototype, {
45
+ [PROPERTY_LENGTH]: {
46
+ enumerable: true,
47
+ get: getArrayLength,
48
+ set: setArrayLength
49
+ },
50
+ active: {
51
+ enumerable: true,
52
+ get: getReactiveActive
53
+ }
54
+ });
49
55
  function array(value, options) {
50
56
  return new ReactiveArray(value, options);
51
57
  }
52
58
  function clearArrayValues() {
53
- this[SYMBOL_STATE].value.length = 0;
59
+ const state = this[SYMBOL_STATE];
60
+ if (state.active) state.value.length = 0;
61
+ }
62
+ function deactivateArray() {
63
+ const state = this[SYMBOL_STATE];
64
+ if (!state.active) return;
65
+ state.length?.deactivate?.();
66
+ deactivateProxy.call(this);
54
67
  }
55
68
  function filterArrayValues(callback) {
56
- return computed(() => filter(getArrayValues.call(this), callback));
69
+ if (!this[SYMBOL_STATE].active) throw new Error(MESSAGE_DEACTIVATED_ARRAY_FILTER);
70
+ return updateProxyDerivations(this, computed(() => filter(getArrayValues.call(this), callback)));
57
71
  }
58
72
  function getArrayLength() {
59
73
  return this[SYMBOL_STATE].length.get();
@@ -62,36 +76,47 @@ function getArrayValues(value) {
62
76
  return value === "length" ? this[SYMBOL_STATE].length.get() : getValueInProxy.call(this, value);
63
77
  }
64
78
  function mapArrayValues(callback) {
65
- return computed(() => getArrayValues.call(this).map(callback));
79
+ if (!this[SYMBOL_STATE].active) throw new Error(MESSAGE_DEACTIVATED_ARRAY_MAP);
80
+ return updateProxyDerivations(this, computed(() => getArrayValues.call(this).map((item, index, array) => callback(item, index, array))));
66
81
  }
67
82
  function peekArrayValue(first, second) {
68
83
  return first === "length" ? this[SYMBOL_STATE].length.peek() : peekValueInProxy.call(this, first, second);
69
84
  }
70
85
  function popArrayValue() {
71
- return this[SYMBOL_STATE].value.pop();
86
+ const state = this[SYMBOL_STATE];
87
+ return state.active ? state.value.pop() : void 0;
72
88
  }
73
89
  function pushArrayValues(...items) {
74
- return this[SYMBOL_STATE].value.push(...items);
90
+ const state = this[SYMBOL_STATE];
91
+ return state.active ? state.value.push(...items) : state.value.length;
75
92
  }
76
93
  function selectArrayValues(filter, map) {
77
- return computed(() => select(getArrayValues.call(this), filter, map));
94
+ if (!this[SYMBOL_STATE].active) throw new Error(MESSAGE_DEACTIVATED_ARRAY_SELECT);
95
+ return updateProxyDerivations(this, computed(() => select(getArrayValues.call(this), filter, map)));
78
96
  }
79
97
  function setArrayLength(value) {
80
- const values = this[SYMBOL_STATE].value;
98
+ const state = this[SYMBOL_STATE];
99
+ if (!state.active) return;
100
+ const values = state.value;
81
101
  if (typeof value === "number" && !Number.isNaN(value) && value >= 0 && value !== values.length) values.length = value;
82
102
  }
83
103
  function shiftArrayValue() {
84
- return this[SYMBOL_STATE].value.shift();
104
+ const state = this[SYMBOL_STATE];
105
+ return state.active ? state.value.shift() : void 0;
85
106
  }
86
107
  function spliceArrayValues(from, to, ...items) {
87
- const values = this[SYMBOL_STATE].value;
108
+ const state = this[SYMBOL_STATE];
109
+ if (!state.active) return [];
110
+ const values = state.value;
88
111
  return values.splice(from, to ?? values.length, ...items);
89
112
  }
90
113
  function unshiftArrayValues(...items) {
91
- return this[SYMBOL_STATE].value.unshift(...items);
114
+ const state = this[SYMBOL_STATE];
115
+ return state.active ? state.value.unshift(...items) : state.value.length;
92
116
  }
93
117
  function updateArray(instance, type, array) {
94
118
  const state = instance[SYMBOL_STATE];
119
+ if (!state.active) return () => void 0;
95
120
  const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
96
121
  const previousArray = affectsLength ? [] : array.slice();
97
122
  const previousLength = array.length;
@@ -104,5 +129,4 @@ function updateArray(instance, type, array) {
104
129
  return result;
105
130
  };
106
131
  }
107
- //#endregion
108
132
  export { array };
@@ -1,6 +1,5 @@
1
1
  import { Computed, InternalComputed, ReactiveOptions } from "../models.mjs";
2
2
  import { GenericCallback } from "@oscarpalmer/atoms/models";
3
- //#region src/value/computed.d.ts
4
3
  /**
5
4
  * Create a computed value
6
5
  *
@@ -9,5 +8,4 @@ import { GenericCallback } from "@oscarpalmer/atoms/models";
9
8
  * @returns Computed value
10
9
  */
11
10
  export declare function computed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Computed<Value>;
12
- export declare function internalComputed(callback: GenericCallback, options?: ReactiveOptions<unknown>): InternalComputed;
13
- //#endregion
11
+ export declare function internalComputed(callback: GenericCallback, options?: ReactiveOptions<unknown>): InternalComputed;
@@ -1,20 +1,24 @@
1
- import { ACTIVE, BATCH, NAME_COMPUTED, NAME_MORA, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY, SYMBOL_EFFECT, SYMBOL_STATE } from "../constants.mjs";
1
+ import { ACTIVE, BATCH, MESSAGE_COMPUTED_CALLBACK, NAME_COMPUTED, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY, SYMBOL_EFFECT, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { internalEffect, runEffect } from "../effect.mjs";
3
3
  import { getJsonValue, getStringValue, handleSignal, peekSignalValue } from "../helpers/value.mjs";
4
4
  import { flushHandlers } from "../batch.mjs";
5
- import { getState } from "../helpers/misc.mjs";
5
+ import { deactivateComputed, getReactiveActive, getState } from "../helpers/misc.mjs";
6
6
  import { subscribeToSignal } from "../helpers/subscription.mjs";
7
- //#region src/value/computed.ts
7
+ // src/value/computed.ts
8
8
  function Computed(callback, options) {
9
- this[SYMBOL_STATE] = getState(void 0, options);
9
+ this[SYMBOL_STATE] = getState(NAME_COMPUTED, void 0, options);
10
10
  this[SYMBOL_EFFECT] = getComputedEffect(this, callback);
11
11
  }
12
- Computed.prototype[NAME_MORA] = NAME_COMPUTED;
12
+ Computed.prototype.deactivate = deactivateComputed;
13
13
  Computed.prototype.get = getComputedValue;
14
14
  Computed.prototype.peek = peekSignalValue;
15
15
  Computed.prototype.subscribe = subscribeToSignal;
16
16
  Computed.prototype.toJSON = getJsonValue;
17
17
  Computed.prototype.toString = getStringValue;
18
+ Object.defineProperties(Computed.prototype, { active: {
19
+ enumerable: true,
20
+ get: getReactiveActive
21
+ } });
18
22
  /**
19
23
  * Create a computed value
20
24
  *
@@ -26,7 +30,7 @@ function computed(callback, options) {
26
30
  return getComputed(callback, options);
27
31
  }
28
32
  function getComputed(callback, options) {
29
- if (typeof callback !== "function") throw new TypeError("Computed callback must be a function");
33
+ if (typeof callback !== "function") throw new TypeError(MESSAGE_COMPUTED_CALLBACK);
30
34
  return new Computed(callback, options);
31
35
  }
32
36
  function getComputedEffect(instance, callback) {
@@ -49,8 +53,9 @@ function getComputedEffect(instance, callback) {
49
53
  return fx;
50
54
  }
51
55
  function getComputedValue() {
52
- const fx = this[SYMBOL_EFFECT];
53
56
  const state = this[SYMBOL_STATE];
57
+ if (!state.active) return state.value;
58
+ const fx = this[SYMBOL_EFFECT];
54
59
  if (ACTIVE.computed != null && fx !== ACTIVE.computed) {
55
60
  state.computeds ??= /* @__PURE__ */ new Set();
56
61
  state.computeds.add(ACTIVE.computed);
@@ -67,10 +72,12 @@ function internalComputed(callback, options) {
67
72
  }
68
73
  function setAndEmit(instance, value, notify) {
69
74
  const state = instance[SYMBOL_STATE];
70
- if (!notify && (state.equal ?? Object.is)(state.value, value)) return;
75
+ if (!state.active || !notify && (state.equal ?? Object.is)(state.value, value)) return;
71
76
  state.value = value;
72
- if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) computed.dirty = true;
73
- if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) BATCH.handlers.set(effect, effect);
77
+ if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) if (computed.instance.active) computed.dirty = true;
78
+ else state.computeds.delete(computed);
79
+ if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) if (effect.active) BATCH.handlers.set(effect, effect);
80
+ else state.effects.delete(effect);
74
81
  for (const type of SUBSCRIPTION_TYPES) {
75
82
  if (type === "frozen") continue;
76
83
  const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
@@ -81,5 +88,4 @@ function setAndEmit(instance, value, notify) {
81
88
  }
82
89
  flushHandlers();
83
90
  }
84
- //#endregion
85
91
  export { computed, internalComputed };
@@ -1,5 +1,2 @@
1
- import { InternalSignal, ReadonlyFrozenSignal, ReadonlySignal, SignalState } from "../models.mjs";
2
- //#region src/value/readonly.d.ts
3
- export declare function getReadonlyInstance(state: SignalState, frozen: boolean): ReadonlySignal<unknown> | ReadonlyFrozenSignal<unknown>;
4
- export declare function getReadonlySignal<Value>(this: InternalSignal, frozen?: never): ReadonlySignal<Value>;
5
- //#endregion
1
+ import { InternalSignal, ReadonlySignal } from "../models.mjs";
2
+ export declare function getReadonlySignal<Value>(this: InternalSignal, frozen?: never): ReadonlySignal<Value>;
@@ -1,37 +1,45 @@
1
- import { NAME_MORA, NAME_READONLY, SUBSCRIPTION_TYPE_FROZEN, SUBSCRIPTION_TYPE_ORIGINAL, SYMBOL_STATE } from "../constants.mjs";
1
+ import { MESSAGE_DEACTIVATED_READONLY, SUBSCRIPTION_TYPE_FROZEN, SUBSCRIPTION_TYPE_ORIGINAL, SYMBOL_READONLY, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { getFrozenValue, getJsonValue, getSignalValue, getStringValue, peekSignalValue } from "../helpers/value.mjs";
3
+ import { deactivateReadonly, getReadonlyActive } from "../helpers/misc.mjs";
3
4
  import { subscribeToReadonly } from "../helpers/subscription.mjs";
4
- //#region src/value/readonly.ts
5
+ // src/value/readonly.ts
5
6
  function Readonly(state, frozen) {
6
- this[SUBSCRIPTION_TYPE_FROZEN] = frozen;
7
7
  this[SYMBOL_STATE] = state;
8
+ this[SYMBOL_READONLY] = {
9
+ active: true,
10
+ name: frozen ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_ORIGINAL
11
+ };
12
+ Object.defineProperty(this, SUBSCRIPTION_TYPE_FROZEN, { value: frozen });
8
13
  }
9
- Readonly.prototype[NAME_MORA] = NAME_READONLY;
14
+ Readonly.prototype.deactivate = deactivateReadonly;
10
15
  Readonly.prototype.get = getReadonlyValue;
11
16
  Readonly.prototype.peek = peekReadonlyValue;
12
17
  Readonly.prototype.subscribe = subscribeToReadonly;
13
18
  Readonly.prototype.toJSON = getJsonValue;
14
19
  Readonly.prototype.toString = getStringValue;
15
- function getReadonlyInstance(state, frozen) {
16
- return new Readonly(state, frozen);
17
- }
20
+ Object.defineProperties(Readonly.prototype, { active: {
21
+ enumerable: true,
22
+ get: getReadonlyActive
23
+ } });
18
24
  function getReadonlySignal(frozen) {
25
+ const state = this[SYMBOL_STATE];
26
+ if (!state.active) throw new Error(MESSAGE_DEACTIVATED_READONLY);
19
27
  const key = frozen === true ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_ORIGINAL;
20
- this[SYMBOL_STATE].readonlies ??= {};
21
- this[SYMBOL_STATE].readonlies[key] ??= getReadonlyInstance(this[SYMBOL_STATE], frozen === true);
22
- return this[SYMBOL_STATE].readonlies[key];
28
+ state.readonlies ??= {};
29
+ state.readonlies[key] ??= new Readonly(state, frozen === true);
30
+ return state.readonlies[key];
23
31
  }
24
32
  function getReadonlyValue() {
25
- return handleReadonlyValue(this, false, this[SUBSCRIPTION_TYPE_FROZEN]);
33
+ return handleReadonlyValue(this, false, this.frozen);
26
34
  }
27
35
  function handleReadonlyValue(instance, peek, frozen, copy) {
36
+ const state = instance[SYMBOL_READONLY];
28
37
  let value;
29
- if (peek) value = peekSignalValue.call(instance, copy === true);
30
- else value = getSignalValue.call(instance);
38
+ if (peek) value = peekSignalValue.call(state.active ? instance : [instance, state.value], copy === true);
39
+ else value = state.active ? getSignalValue.call(instance) : state.value;
31
40
  return frozen ? getFrozenValue(value) : value;
32
41
  }
33
42
  function peekReadonlyValue(copy) {
34
- return handleReadonlyValue(this, true, this[SUBSCRIPTION_TYPE_FROZEN], copy);
43
+ return handleReadonlyValue(this, true, this.frozen, copy);
35
44
  }
36
- //#endregion
37
- export { getReadonlyInstance, getReadonlySignal };
45
+ export { getReadonlySignal };
@@ -1,5 +1,4 @@
1
1
  import { ReactiveOptions, Signal } from "../models.mjs";
2
- //#region src/value/signal.d.ts
3
2
  /**
4
3
  * Create a reactive value from a function result
5
4
  *
@@ -23,5 +22,4 @@ export declare function signal<Value>(value: Promise<Value>, options?: ReactiveO
23
22
  * @param options Reactivity options
24
23
  * @returns Reactive value
25
24
  */
26
- export declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
27
- //#endregion
25
+ export declare function signal<Value>(value: Value, options?: ReactiveOptions<Value>): Signal<Value>;
@@ -1,15 +1,15 @@
1
- import { NAME_MORA, NAME_SIGNAL, SYMBOL_STATE } from "../constants.mjs";
1
+ import { NAME_SIGNAL, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { emitValue, getJsonValue, getSignalValue, getStringValue, handleSignal, peekSignalValue, updateSignal } from "../helpers/value.mjs";
3
- import { getState } from "../helpers/misc.mjs";
3
+ import { deactivateReactive, getReactiveActive, getState } from "../helpers/misc.mjs";
4
4
  import { subscribeToSignal } from "../helpers/subscription.mjs";
5
5
  import { getReadonlySignal } from "./readonly.mjs";
6
- //#region src/value/signal.ts
6
+ // src/value/signal.ts
7
7
  function Signal(value, options) {
8
- this[SYMBOL_STATE] = getState(void 0, options);
8
+ this[SYMBOL_STATE] = getState(NAME_SIGNAL, void 0, options);
9
9
  handleSignal(this, value, setAndEmit);
10
10
  }
11
- Signal.prototype[NAME_MORA] = NAME_SIGNAL;
12
11
  Signal.prototype.asReadonly = getReadonlySignal;
12
+ Signal.prototype.deactivate = deactivateReactive;
13
13
  Signal.prototype.get = getSignalValue;
14
14
  Signal.prototype.peek = peekSignalValue;
15
15
  Signal.prototype.set = setSignalValue;
@@ -17,6 +17,10 @@ Signal.prototype.subscribe = subscribeToSignal;
17
17
  Signal.prototype.toString = getStringValue;
18
18
  Signal.prototype.toJSON = getJsonValue;
19
19
  Signal.prototype.update = updateSignalValue;
20
+ Object.defineProperties(Signal.prototype, { active: {
21
+ enumerable: true,
22
+ get: getReactiveActive
23
+ } });
20
24
  function setAndEmit(instance, value) {
21
25
  const state = instance[SYMBOL_STATE];
22
26
  if (!(state.equal ?? Object.is)(state.value, value)) {
@@ -33,5 +37,4 @@ function signal(value, options) {
33
37
  function updateSignalValue(callback) {
34
38
  updateSignal(this, callback, setAndEmit);
35
39
  }
36
- //#endregion
37
40
  export { signal };
@@ -1,6 +1,5 @@
1
1
  import { ReactiveOptions, ReactiveStore } from "../models.mjs";
2
2
  import { PlainObject } from "@oscarpalmer/atoms/models";
3
- //#region src/value/store.d.ts
4
3
  /**
5
4
  * Create a reactive store from a function result
6
5
  *
@@ -24,5 +23,4 @@ export declare function store<Value extends PlainObject>(value: Promise<Value>,
24
23
  * @param options Reactivity options
25
24
  * @returns Reactive store
26
25
  */
27
- export declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
28
- //#endregion
26
+ export declare function store<Value extends PlainObject>(value: Value, options?: ReactiveOptions<Value>): ReactiveStore<Value>;
@@ -1,20 +1,20 @@
1
- import { NAME_MORA, NAME_STORE, SYMBOL_STATE } from "../constants.mjs";
1
+ import { NAME_STORE, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { getJsonValue, getStringValue } from "../helpers/value.mjs";
3
- import { getState } from "../helpers/misc.mjs";
3
+ import { deactivateProxy, getReactiveActive, getState } from "../helpers/misc.mjs";
4
4
  import { subscribeToProxy } from "../helpers/subscription.mjs";
5
5
  import { getValueInProxy, notifyProxyValue, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
6
6
  import { getReadonlySignal } from "./readonly.mjs";
7
- //#region src/value/store.ts
7
+ // src/value/store.ts
8
8
  function ReactiveStore(value, options) {
9
9
  this[SYMBOL_STATE] = {
10
- ...getState(void 0, options),
10
+ ...getState(NAME_STORE, void 0, options),
11
11
  isArray: false
12
12
  };
13
13
  this[SYMBOL_STATE].value = new Proxy({}, { set: (target, property, value) => setValueInProxy(this, target, property, value) });
14
14
  setProxyValue.call(this, value);
15
15
  }
16
- ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
17
16
  ReactiveStore.prototype.asReadonly = getReadonlySignal;
17
+ ReactiveStore.prototype.deactivate = deactivateProxy;
18
18
  ReactiveStore.prototype.get = getValueInProxy;
19
19
  ReactiveStore.prototype.notify = notifyProxyValue;
20
20
  ReactiveStore.prototype.peek = peekValueInProxy;
@@ -23,8 +23,11 @@ ReactiveStore.prototype.subscribe = subscribeToProxy;
23
23
  ReactiveStore.prototype.toJSON = getJsonValue;
24
24
  ReactiveStore.prototype.toString = getStringValue;
25
25
  ReactiveStore.prototype.update = updateProxyValue;
26
+ Object.defineProperties(ReactiveStore.prototype, { active: {
27
+ enumerable: true,
28
+ get: getReactiveActive
29
+ } });
26
30
  function store(value, options) {
27
31
  return new ReactiveStore(value, options);
28
32
  }
29
- //#endregion
30
33
  export { store };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/mora",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Signals and stuff…",
5
5
  "keywords": [
6
6
  "reactive",
@@ -39,21 +39,14 @@
39
39
  "test:leak": "vpx vp test run --detect-async-leaks --coverage"
40
40
  },
41
41
  "dependencies": {
42
- "@oscarpalmer/atoms": "^0.205"
42
+ "@oscarpalmer/atoms": "^0.222"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^26.6",
46
- "@vitest/coverage-istanbul": "5.0.1",
46
+ "@vitest/coverage-istanbul": "^5",
47
47
  "jsdom": "^30.1",
48
48
  "tsdown": "^0.23",
49
49
  "typescript": "^7",
50
- "vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
51
- "vite-plus": "1.0.0",
52
- "vitest": "5.0.1"
53
- },
54
- "overrides": {
55
- "vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
56
- "vitest": "5.0.1"
57
- },
58
- "packageManager": "npm@11.19.0"
50
+ "vite-plus": "^1.4"
51
+ }
59
52
  }
package/src/batch.ts CHANGED
@@ -1,8 +1,7 @@
1
- import {attempt} from '@oscarpalmer/atoms/result';
2
1
  import {BATCH} from './constants';
3
2
  import {runEffect} from './effect';
4
3
  import {getFrozenValue, peekSignalValue} from './helpers/value';
5
- import type {StoredSubscription} from './models';
4
+ import type {EffectState, StoredSubscription} from './models';
6
5
 
7
6
  // #region Functions
8
7
 
@@ -21,7 +20,12 @@ export function flushHandlers(): void {
21
20
  BATCH.handlers.clear();
22
21
 
23
22
  for (let index = 0; index < length; index += 1) {
24
- const [, handler] = handlers[index];
23
+ const [owner, handler] = handlers[index];
24
+
25
+ if (!owner.active) {
26
+ continue;
27
+ }
28
+
25
29
  const subscription = handler as StoredSubscription;
26
30
 
27
31
  if (typeof subscription.frozen === 'boolean') {
@@ -31,7 +35,7 @@ export function flushHandlers(): void {
31
35
  : peekSignalValue.call(subscription.instance, subscription.copy),
32
36
  );
33
37
  } else {
34
- runEffect(handler);
38
+ runEffect(handler as EffectState);
35
39
  }
36
40
  }
37
41
  }
package/src/constants.ts CHANGED
@@ -17,6 +17,22 @@ export const BATCH: Batch = {
17
17
  handlers: new Map(),
18
18
  };
19
19
 
20
+ export const MESSAGE_COMPUTED_CALLBACK = 'Computed callback must be a function';
21
+
22
+ export const MESSAGE_DEACTIVATED_ARRAY_FILTER = 'Cannot filter an inactive reactive array';
23
+
24
+ export const MESSAGE_DEACTIVATED_ARRAY_MAP = 'Cannot map an inactive reactive array';
25
+
26
+ export const MESSAGE_DEACTIVATED_ARRAY_SELECT = 'Cannot select from an inactive reactive array';
27
+
28
+ export const MESSAGE_DEACTIVATED_READONLY = 'Cannot create readonly variant of inactive signal';
29
+
30
+ export const MESSAGE_DEACTIVATED_SUBSCRIPTION = 'Cannot subscribe to inactive signal';
31
+
32
+ export const MESSAGE_EFFECT_CALLBACK = 'Effect callback must be a function';
33
+
34
+ export const MESSAGE_UPDATE_CALLBACK = 'Update callback must be a function';
35
+
20
36
  export const METHODS_AFFECTING_LENGTH: Set<string | symbol> = new Set([
21
37
  'pop',
22
38
  'push',
@@ -39,8 +55,6 @@ export const NAME_COMPUTED = 'computed';
39
55
 
40
56
  export const NAME_EFFECT = 'effect';
41
57
 
42
- export const NAME_MORA = '$mora';
43
-
44
58
  export const NAME_READONLY = 'readonly';
45
59
 
46
60
  export const NAME_SIGNAL = 'signal';
@@ -49,32 +63,28 @@ export const NAME_STORE = 'store';
49
63
 
50
64
  export const NAME_SUBSCRIPTION = 'subscription';
51
65
 
52
- export const NAME_ALL: Set<string> = new Set([
53
- NAME_ARRAY,
54
- NAME_COMPUTED,
55
- NAME_READONLY,
56
- NAME_SIGNAL,
57
- NAME_STORE,
58
- ]);
66
+ export const NAME_ALL: Set<string> = new Set([NAME_ARRAY, NAME_COMPUTED, NAME_SIGNAL, NAME_STORE]);
59
67
 
60
68
  export const PROPERTY_LENGTH = 'length';
61
69
 
62
- export const SYMBOL_EFFECT: symbol = Symbol('effect');
70
+ export const SYMBOL_EFFECT: unique symbol = Symbol('effect');
71
+
72
+ export const SYMBOL_READONLY: unique symbol = Symbol('readonly');
63
73
 
64
- export const SYMBOL_STATE: symbol = Symbol('state');
74
+ export const SYMBOL_STATE: unique symbol = Symbol('state');
65
75
 
66
76
  export const SUBSCRIPTION_PROPERTY: SubscriptionProperty = {
67
- key: NAME_MORA,
77
+ key: '$mora',
68
78
  value: NAME_SUBSCRIPTION,
69
79
  };
70
80
 
71
- export const SUBSCRIPTION_TYPE_COPY: SubscriptionType = 'copy';
81
+ export const SUBSCRIPTION_TYPE_COPY = 'copy' as const;
72
82
 
73
- export const SUBSCRIPTION_TYPE_FROZEN: SubscriptionType = 'frozen';
83
+ export const SUBSCRIPTION_TYPE_FROZEN = 'frozen' as const;
74
84
 
75
- export const SUBSCRIPTION_TYPE_ORIGINAL: SubscriptionType = 'original';
85
+ export const SUBSCRIPTION_TYPE_ORIGINAL = 'original' as const;
76
86
 
77
- export const SUBSCRIPTION_TYPE_READONLY: SubscriptionType = 'readonly';
87
+ export const SUBSCRIPTION_TYPE_READONLY = 'readonly' as const;
78
88
 
79
89
  export const SUBSCRIPTION_TYPES_COPY: Set<SubscriptionType> = new Set([
80
90
  SUBSCRIPTION_TYPE_COPY,
package/src/effect.ts CHANGED
@@ -1,22 +1,40 @@
1
+ import {noop} from '@oscarpalmer/atoms/function';
1
2
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {attempt} from '@oscarpalmer/atoms/result';
3
- import {ACTIVE, NAME_EFFECT, NAME_MORA, SYMBOL_STATE} from './constants';
4
- import type {Effect, EffectState} from './models';
3
+ import {ACTIVE, BATCH, MESSAGE_EFFECT_CALLBACK, NAME_EFFECT, SYMBOL_STATE} from './constants';
4
+ import type {Effect, EffectState, InternalEffect} from './models';
5
5
 
6
6
  // #region Instance
7
7
 
8
- function Effect(this: any, callback: GenericCallback): void {
9
- this[SYMBOL_STATE] = {callback};
8
+ function Effect(this: InternalEffect, callback: GenericCallback): void {
9
+ this[SYMBOL_STATE] = {
10
+ callback,
11
+ active: true,
12
+ name: NAME_EFFECT,
13
+ };
10
14
 
11
15
  runEffect(this[SYMBOL_STATE]);
12
16
  }
13
17
 
14
- Effect.prototype[NAME_MORA] = NAME_EFFECT;
18
+ Effect.prototype.stop = stopEffect;
19
+
20
+ Object.defineProperties(Effect.prototype, {
21
+ active: {
22
+ enumerable: true,
23
+ get: getActive,
24
+ },
25
+ });
15
26
 
16
27
  // #endregion
17
28
 
18
29
  // #region Functions
19
30
 
31
+ export function deactivateEffect(state: EffectState): void {
32
+ state.active = false;
33
+ state.callback = noop;
34
+
35
+ BATCH.handlers.delete(state);
36
+ }
37
+
20
38
  /**
21
39
  * Create an effect
22
40
  *
@@ -25,12 +43,16 @@ Effect.prototype[NAME_MORA] = NAME_EFFECT;
25
43
  */
26
44
  export function effect(callback: GenericCallback): Effect {
27
45
  if (typeof callback !== 'function') {
28
- throw new TypeError('Effect callback must be a function');
46
+ throw new TypeError(MESSAGE_EFFECT_CALLBACK);
29
47
  }
30
48
 
31
49
  return getEffect(callback)[0];
32
50
  }
33
51
 
52
+ function getActive(this: InternalEffect): boolean {
53
+ return this[SYMBOL_STATE].active;
54
+ }
55
+
34
56
  function getEffect(callback: GenericCallback): [Effect, EffectState] {
35
57
  // @ts-expect-error All good, no worries :-)
36
58
  const instance = new Effect(callback);
@@ -54,4 +76,12 @@ export function runEffect(state: EffectState): void {
54
76
  }
55
77
  }
56
78
 
79
+ function stopEffect(this: InternalEffect): void {
80
+ const state = this[SYMBOL_STATE];
81
+
82
+ if (state.active) {
83
+ deactivateEffect(state);
84
+ }
85
+ }
86
+
57
87
  // #endregion