@oscarpalmer/mora 0.35.0 → 0.37.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.
@@ -1,9 +1,9 @@
1
1
  import { InternalArray, InternalComputed, InternalStore } from "../models.mjs";
2
2
  import { Key } from "@oscarpalmer/atoms/models";
3
3
  //#region src/helpers/proxy.d.ts
4
- export declare function emitProxyValues(this: InternalArray | InternalStore): void;
5
4
  export declare function getReactiveValueInProxy(instance: InternalArray | InternalStore, key: Key): InternalComputed;
6
5
  export declare function getValueInProxy(this: InternalArray | InternalStore, first?: unknown): unknown;
6
+ export declare function notifyProxyValue(this: InternalArray | InternalStore): void;
7
7
  export declare function peekValueInProxy(this: InternalArray | InternalStore, first?: unknown, second?: boolean): unknown;
8
8
  export declare function setProxyValue(this: InternalArray | InternalStore, first?: unknown, second?: unknown): void;
9
9
  export declare function setValueInProxy(instance: InternalArray | InternalStore, target: object, property: PropertyKey, value: unknown): boolean;
@@ -1,17 +1,9 @@
1
- import { SYMBOL_STATE } from "../constants.mjs";
1
+ import { SYMBOL_EFFECT, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { emitValue, getSignalValue, peekSignalValue } from "./value.mjs";
3
3
  import { startBatch, stopBatch } from "../batch.mjs";
4
4
  import { internalComputed } from "../value/computed.mjs";
5
5
  import { isKey, isPlainObject } from "@oscarpalmer/atoms/is";
6
6
  //#region src/helpers/proxy.ts
7
- function emitProxyValues() {
8
- const state = this[SYMBOL_STATE];
9
- state.mapped ??= /* @__PURE__ */ new Map();
10
- const values = [...state.mapped.values()];
11
- const { length } = values;
12
- for (let index = 0; index < length; index += 1) values[index][1].dirty = true;
13
- emitValue(this);
14
- }
15
7
  function getReactiveValueInProxy(instance, key) {
16
8
  const state = instance[SYMBOL_STATE];
17
9
  state.mapped ??= /* @__PURE__ */ new Map();
@@ -21,7 +13,7 @@ function getReactiveValueInProxy(instance, key) {
21
13
  item = internalComputed(() => state.isArray ? instance.get().at(key) : instance.get()[key]);
22
14
  state.mapped.set(mapKey, item);
23
15
  }
24
- return item[0];
16
+ return item;
25
17
  }
26
18
  function getValueInProxy(first) {
27
19
  let signal;
@@ -34,6 +26,17 @@ function isProxyKey(isArray, value) {
34
26
  function isProxyObject(isArray, value) {
35
27
  return value == null || (isArray ? Array.isArray(value) : isPlainObject(value));
36
28
  }
29
+ function notifyProxyValue() {
30
+ const state = this[SYMBOL_STATE];
31
+ state.mapped ??= /* @__PURE__ */ new Map();
32
+ const values = [...state.mapped.values()];
33
+ const { length } = values;
34
+ for (let index = 0; index < length; index += 1) {
35
+ values[index][SYMBOL_EFFECT].dirty = true;
36
+ values[index][SYMBOL_EFFECT].notify = true;
37
+ }
38
+ emitValue(this, true);
39
+ }
37
40
  function peekValueInProxy(first, second) {
38
41
  const state = this[SYMBOL_STATE];
39
42
  let value;
@@ -132,4 +135,4 @@ function updateProxyValue(callback) {
132
135
  setProxyValue.call(this, callback(this[SYMBOL_STATE].value));
133
136
  }
134
137
  //#endregion
135
- export { emitProxyValues, getReactiveValueInProxy, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue };
138
+ export { getReactiveValueInProxy, getValueInProxy, notifyProxyValue, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue };
@@ -1,12 +1,13 @@
1
1
  import { InternalStateful } from "../models.mjs";
2
2
  //#region src/helpers/value.d.ts
3
- export declare function emitValue(instance: InternalStateful): void;
3
+ type SetValue = (instance: InternalStateful, value: unknown, notify: boolean) => void;
4
+ export declare function emitValue(instance: InternalStateful, notify?: boolean): void;
4
5
  export declare function equalArrays(first: unknown[], second: unknown[], equal?: (first: unknown, second: unknown) => boolean): boolean;
5
6
  export declare function getFrozenValue(value: unknown): unknown;
6
7
  export declare function getJsonValue(this: InternalStateful): unknown;
7
8
  export declare function getSignalValue(this: InternalStateful): unknown;
8
9
  export declare function getStringValue(this: InternalStateful, json?: boolean): string;
9
- export declare function handleSignal(instance: InternalStateful, origin: unknown, setValue: (instance: InternalStateful, value: unknown) => void, onAfter?: () => void): void;
10
+ export declare function handleSignal(instance: InternalStateful, origin: unknown, setValue: SetValue, onAfter?: () => void, notify?: boolean): void;
10
11
  export declare function peekSignalValue(this: InternalStateful | [InternalStateful, unknown], copy?: boolean): unknown;
11
12
  export declare function updateSignal(instance: InternalStateful, callback: (value: unknown) => unknown, setValue: (instance: InternalStateful, value: unknown) => void): void;
12
13
  //#endregion
@@ -1,11 +1,18 @@
1
- import { ACTIVE, BATCH, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY, SUBSCRIPTION_TYPE_FROZEN, SYMBOL_STATE } from "../constants.mjs";
1
+ import { ACTIVE, BATCH, SUBSCRIPTION_TYPES, SUBSCRIPTION_TYPES_COPY, SUBSCRIPTION_TYPE_FROZEN, SYMBOL_EFFECT, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { flushHandlers } from "../batch.mjs";
3
3
  import { isPlainObject } from "@oscarpalmer/atoms/is";
4
4
  //#region src/helpers/value.ts
5
- function emitValue(instance) {
5
+ function emitValue(instance, notify) {
6
6
  const state = instance[SYMBOL_STATE];
7
7
  if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) computed.dirty = true;
8
8
  if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) BATCH.handlers.set(effect, effect);
9
+ if (notify ?? false) {
10
+ const mapped = instance[SYMBOL_STATE].mapped;
11
+ for (const [, item] of mapped) {
12
+ const fx = item[SYMBOL_EFFECT];
13
+ BATCH.handlers.set(fx.instance, fx.instance);
14
+ }
15
+ }
9
16
  for (const type of SUBSCRIPTION_TYPES) {
10
17
  const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
11
18
  if (subscriptions != null && subscriptions.size > 0) for (const [subscription, callback] of subscriptions) BATCH.handlers.set(subscription, {
@@ -56,7 +63,7 @@ function getSignalValue() {
56
63
  function getStringValue(json) {
57
64
  return json === true ? JSON.stringify(this[SYMBOL_STATE].value) : String(this[SYMBOL_STATE].value);
58
65
  }
59
- function handleSignal(instance, origin, setValue, onAfter) {
66
+ function handleSignal(instance, origin, setValue, onAfter, notify) {
60
67
  const state = instance[SYMBOL_STATE];
61
68
  try {
62
69
  let actual = typeof origin === "function" ? origin() : origin;
@@ -65,12 +72,12 @@ function handleSignal(instance, origin, setValue, onAfter) {
65
72
  actual.then((value) => {
66
73
  if (actual === state.promise) {
67
74
  state.promise = void 0;
68
- setValue(instance, value);
75
+ setValue(instance, value, notify ?? false);
69
76
  }
70
77
  }).catch(() => {
71
78
  if (actual === state.promise) state.promise = void 0;
72
79
  });
73
- } else setValue(instance, actual);
80
+ } else setValue(instance, actual, notify ?? false);
74
81
  } catch {} finally {
75
82
  onAfter?.();
76
83
  }
package/dist/models.d.mts CHANGED
@@ -15,6 +15,7 @@ export type Computed<Value> = Reactive<Value>;
15
15
  export type ComputedEffect = {
16
16
  dirty: boolean;
17
17
  instance: EffectState;
18
+ notify: boolean;
18
19
  };
19
20
  export type Effect = {};
20
21
  export type EffectState = {
@@ -266,7 +267,7 @@ export type ReactiveOptions<Value> = {
266
267
  export type ReactiveProxyState = {
267
268
  isArray: boolean;
268
269
  length?: Signal<number>;
269
- mapped?: Map<string, [InternalComputed, ComputedEffect]>;
270
+ mapped?: Map<string, InternalComputed>;
270
271
  } & SignalState;
271
272
  export type ReactiveState<Value = unknown, Item = Value> = {
272
273
  computeds?: Set<ComputedEffect>;
@@ -109,10 +109,17 @@ function isPlainObject(value) {
109
109
  }
110
110
  //#endregion
111
111
  //#region src/helpers/value.ts
112
- function emitValue(instance) {
112
+ function emitValue(instance, notify) {
113
113
  const state = instance[SYMBOL_STATE];
114
114
  if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) computed.dirty = true;
115
115
  if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) BATCH.handlers.set(effect, effect);
116
+ if (notify ?? false) {
117
+ const mapped = instance[SYMBOL_STATE].mapped;
118
+ for (const [, item] of mapped) {
119
+ const fx = item[SYMBOL_EFFECT];
120
+ BATCH.handlers.set(fx.instance, fx.instance);
121
+ }
122
+ }
116
123
  for (const type of SUBSCRIPTION_TYPES) {
117
124
  const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
118
125
  if (subscriptions != null && subscriptions.size > 0) for (const [subscription, callback] of subscriptions) BATCH.handlers.set(subscription, {
@@ -163,7 +170,7 @@ function getSignalValue() {
163
170
  function getStringValue(json) {
164
171
  return json === true ? JSON.stringify(this[SYMBOL_STATE].value) : String(this[SYMBOL_STATE].value);
165
172
  }
166
- function handleSignal(instance, origin, setValue, onAfter) {
173
+ function handleSignal(instance, origin, setValue, onAfter, notify) {
167
174
  const state = instance[SYMBOL_STATE];
168
175
  try {
169
176
  let actual = typeof origin === "function" ? origin() : origin;
@@ -172,12 +179,12 @@ function handleSignal(instance, origin, setValue, onAfter) {
172
179
  actual.then((value) => {
173
180
  if (actual === state.promise) {
174
181
  state.promise = void 0;
175
- setValue(instance, value);
182
+ setValue(instance, value, notify ?? false);
176
183
  }
177
184
  }).catch(() => {
178
185
  if (actual === state.promise) state.promise = void 0;
179
186
  });
180
- } else setValue(instance, actual);
187
+ } else setValue(instance, actual, notify ?? false);
181
188
  } catch {} finally {
182
189
  onAfter?.();
183
190
  }
@@ -405,7 +412,6 @@ function lastIndexOf(array, ...parameters) {
405
412
  return findValue(FIND_VALUE_INDEX, array, parameters, true);
406
413
  }
407
414
  indexOf.last = lastIndexOf;
408
- Object.defineProperty(indexOf, "last", { value: lastIndexOf });
409
415
  //#endregion
410
416
  //#region node_modules/@oscarpalmer/atoms/dist/array/find.mjs
411
417
  function find(array, ...parameters) {
@@ -415,7 +421,6 @@ function findLast(array, ...parameters) {
415
421
  return findValue(FIND_VALUE_ITEM, array, parameters, true);
416
422
  }
417
423
  find.last = findLast;
418
- Object.defineProperty(find, "last", { value: findLast });
419
424
  //#endregion
420
425
  //#region node_modules/@oscarpalmer/atoms/dist/array/select.mjs
421
426
  function reverseSelect(array, ...parameters) {
@@ -434,7 +439,6 @@ function selectValues(array, parameters) {
434
439
  }).matched;
435
440
  }
436
441
  select.reverse = reverseSelect;
437
- Object.defineProperty(select, "reverse", { value: reverseSelect });
438
442
  //#endregion
439
443
  //#region node_modules/@oscarpalmer/atoms/dist/array/filter.mjs
440
444
  function exclude(array, ...parameters) {
@@ -444,7 +448,6 @@ function filter(array, ...parameters) {
444
448
  return findValues("all", array, parameters).matched;
445
449
  }
446
450
  filter.remove = exclude;
447
- Object.defineProperty(filter, "remove", { value: exclude });
448
451
  //#endregion
449
452
  //#region src/helpers/misc.ts
450
453
  function getState(value, options) {
@@ -454,7 +457,7 @@ function getState(value, options) {
454
457
  };
455
458
  }
456
459
  //#endregion
457
- //#region node_modules/@oscarpalmer/atoms/dist/internal/abort.mjs
460
+ //#region node_modules/@oscarpalmer/atoms/dist/internal/aborter.mjs
458
461
  function createAborter(value, onAbort) {
459
462
  if (!(value instanceof AbortSignal)) return;
460
463
  value.addEventListener(ABORT_EVENT, onAbort, ABORT_OPTIONS);
@@ -472,40 +475,32 @@ const SUBSCRIPTION_NAME = "subscription";
472
475
  const SUBSCRIPTION_PROPERTY = "$subscription";
473
476
  const SUBSCRIPTION_STORE = "subscriptions";
474
477
  function Subscription(state, property, parameters) {
475
- Object.defineProperty(this, SUBSCRIPTION_SYMBOL, { value: state });
478
+ this[SUBSCRIPTION_SYMBOL] = state;
479
+ this[property.key] = property.value;
476
480
  state.aborter = createAborter(parameters.signal, () => this.unsubscribe(subscriptions, this, state));
477
- Object.defineProperties(this, {
478
- [property.key]: { value: property.value },
479
- active: {
480
- enumerable: true,
481
- get: () => (state.parameters?.isActive?.() ?? true) && state.active
482
- }
483
- });
484
481
  addSubscription(this);
485
482
  }
486
- Object.defineProperties(Subscription.prototype, {
487
- [SUBSCRIPTION_PROPERTY]: { value: SUBSCRIPTION_NAME },
488
- unsubscribe: { value: removeSubscription }
483
+ Subscription.prototype[SUBSCRIPTION_PROPERTY] = SUBSCRIPTION_NAME;
484
+ Subscription.prototype.unsubscribe = removeSubscription;
485
+ Object.defineProperty(Subscription.prototype, "active", {
486
+ enumerable: true,
487
+ get: getSubscriptionActive
489
488
  });
490
489
  function Subscriptions(parameters) {
491
490
  const { keys, property } = createSubscriptionsParameters(parameters);
492
- Object.defineProperty(this, SUBSCRIPTION_SYMBOL, { value: createSubscriptionsState(property, keys) });
491
+ this[SUBSCRIPTION_SYMBOL] = createSubscriptionsState(property, keys);
493
492
  }
493
+ Subscriptions.prototype[SUBSCRIPTION_PROPERTY] = SUBSCRIPTION_STORE;
494
+ Subscriptions.prototype.clear = clearSubscriptions;
495
+ Subscriptions.prototype.create = createSubscription;
494
496
  Object.defineProperties(Subscriptions.prototype, {
495
- [SUBSCRIPTION_PROPERTY]: { value: SUBSCRIPTION_STORE },
496
- clear: { value: clearSubscriptions },
497
- create: { value: createSubscription },
498
497
  items: {
499
498
  enumerable: true,
500
- get() {
501
- return this[SUBSCRIPTION_SYMBOL].items;
502
- }
499
+ get: getSubscriptionsItems
503
500
  },
504
501
  values: {
505
502
  enumerable: true,
506
- get() {
507
- return this[SUBSCRIPTION_SYMBOL].values;
508
- }
503
+ get: getSubscriptionsValues
509
504
  }
510
505
  });
511
506
  function addSubscription(instance) {
@@ -568,6 +563,16 @@ function getExistingSubscription(subscriptions, state) {
568
563
  const { parameters } = state;
569
564
  return isKey(parameters.key) ? values.from.keyed?.get(parameters.key)?.get(parameters.value) : values.from.any.get(parameters.value);
570
565
  }
566
+ function getSubscriptionActive() {
567
+ const state = this[SUBSCRIPTION_SYMBOL];
568
+ return (state.parameters?.isActive?.() ?? true) && state.active;
569
+ }
570
+ function getSubscriptionsItems() {
571
+ return this[SUBSCRIPTION_SYMBOL].items;
572
+ }
573
+ function getSubscriptionsValues() {
574
+ return this[SUBSCRIPTION_SYMBOL].values;
575
+ }
571
576
  function createSubscriptionParameters(input) {
572
577
  const values = isPlainObject(input) ? input : {};
573
578
  if (values.value == null) throw new Error(SUBSCRIPTION_INVALID_VALUE);
@@ -703,17 +708,17 @@ Computed.prototype.toString = getStringValue;
703
708
  * @returns Computed value
704
709
  */
705
710
  function computed(callback, options) {
706
- return getComputed(callback, options)[0];
711
+ return getComputed(callback, options);
707
712
  }
708
713
  function getComputed(callback, options) {
709
714
  if (typeof callback !== "function") throw new TypeError("Computed callback must be a function");
710
- const instance = new Computed(callback, options);
711
- return [instance, instance[SYMBOL_EFFECT]];
715
+ return new Computed(callback, options);
712
716
  }
713
717
  function getComputedEffect(instance, callback) {
714
718
  const fx = {
715
719
  dirty: true,
716
- instance: void 0
720
+ instance: void 0,
721
+ notify: false
717
722
  };
718
723
  fx.instance = internalEffect(() => {
719
724
  if (fx.dirty) {
@@ -721,7 +726,8 @@ function getComputedEffect(instance, callback) {
721
726
  ACTIVE.computed = fx;
722
727
  handleSignal(instance, callback, setAndEmit$1, () => {
723
728
  ACTIVE.computed = previousComputed;
724
- });
729
+ fx.notify = false;
730
+ }, fx.notify);
725
731
  fx.dirty = false;
726
732
  }
727
733
  });
@@ -744,9 +750,9 @@ function getComputedValue() {
744
750
  function internalComputed(callback, options) {
745
751
  return getComputed(callback, options);
746
752
  }
747
- function setAndEmit$1(instance, value) {
753
+ function setAndEmit$1(instance, value, notify) {
748
754
  const state = instance[SYMBOL_STATE];
749
- if ((state.equal ?? Object.is)(state.value, value)) return;
755
+ if (!notify && (state.equal ?? Object.is)(state.value, value)) return;
750
756
  state.value = value;
751
757
  if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) computed.dirty = true;
752
758
  if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) BATCH.handlers.set(effect, effect);
@@ -762,14 +768,6 @@ function setAndEmit$1(instance, value) {
762
768
  }
763
769
  //#endregion
764
770
  //#region src/helpers/proxy.ts
765
- function emitProxyValues() {
766
- const state = this[SYMBOL_STATE];
767
- state.mapped ??= /* @__PURE__ */ new Map();
768
- const values = [...state.mapped.values()];
769
- const { length } = values;
770
- for (let index = 0; index < length; index += 1) values[index][1].dirty = true;
771
- emitValue(this);
772
- }
773
771
  function getReactiveValueInProxy(instance, key) {
774
772
  const state = instance[SYMBOL_STATE];
775
773
  state.mapped ??= /* @__PURE__ */ new Map();
@@ -779,7 +777,7 @@ function getReactiveValueInProxy(instance, key) {
779
777
  item = internalComputed(() => state.isArray ? instance.get().at(key) : instance.get()[key]);
780
778
  state.mapped.set(mapKey, item);
781
779
  }
782
- return item[0];
780
+ return item;
783
781
  }
784
782
  function getValueInProxy(first) {
785
783
  let signal;
@@ -792,6 +790,17 @@ function isProxyKey(isArray, value) {
792
790
  function isProxyObject(isArray, value) {
793
791
  return value == null || (isArray ? Array.isArray(value) : isPlainObject(value));
794
792
  }
793
+ function notifyProxyValue() {
794
+ const state = this[SYMBOL_STATE];
795
+ state.mapped ??= /* @__PURE__ */ new Map();
796
+ const values = [...state.mapped.values()];
797
+ const { length } = values;
798
+ for (let index = 0; index < length; index += 1) {
799
+ values[index][SYMBOL_EFFECT].dirty = true;
800
+ values[index][SYMBOL_EFFECT].notify = true;
801
+ }
802
+ emitValue(this, true);
803
+ }
795
804
  function peekValueInProxy(first, second) {
796
805
  const state = this[SYMBOL_STATE];
797
806
  let value;
@@ -979,7 +988,7 @@ ReactiveArray.prototype.clear = clearArrayValues;
979
988
  ReactiveArray.prototype.filter = filterArrayValues;
980
989
  ReactiveArray.prototype.get = getArrayValues;
981
990
  ReactiveArray.prototype.map = mapArrayValues;
982
- ReactiveArray.prototype.notify = emitProxyValues;
991
+ ReactiveArray.prototype.notify = notifyProxyValue;
983
992
  ReactiveArray.prototype.peek = peekArrayValue;
984
993
  ReactiveArray.prototype.pop = popArrayValue;
985
994
  ReactiveArray.prototype.push = pushArrayValues;
@@ -1063,7 +1072,7 @@ function ReactiveStore(value, options) {
1063
1072
  ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
1064
1073
  ReactiveStore.prototype.asReadonly = getReadonlySignal;
1065
1074
  ReactiveStore.prototype.get = getValueInProxy;
1066
- ReactiveStore.prototype.notify = emitProxyValues;
1075
+ ReactiveStore.prototype.notify = notifyProxyValue;
1067
1076
  ReactiveStore.prototype.peek = peekValueInProxy;
1068
1077
  ReactiveStore.prototype.set = setProxyValue;
1069
1078
  ReactiveStore.prototype.subscribe = subscribeToProxy;
@@ -3,7 +3,7 @@ import { emitValue, equalArrays, getJsonValue, getStringValue } from "../helpers
3
3
  import { getState } from "../helpers/misc.mjs";
4
4
  import { subscribeToProxy } from "../helpers/subscription.mjs";
5
5
  import { computed } from "./computed.mjs";
6
- import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
6
+ import { getValueInProxy, notifyProxyValue, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
7
7
  import { getReadonlySignal } from "./readonly.mjs";
8
8
  import { signal } from "./signal.mjs";
9
9
  import { select } from "@oscarpalmer/atoms/array";
@@ -33,7 +33,7 @@ ReactiveArray.prototype.clear = clearArrayValues;
33
33
  ReactiveArray.prototype.filter = filterArrayValues;
34
34
  ReactiveArray.prototype.get = getArrayValues;
35
35
  ReactiveArray.prototype.map = mapArrayValues;
36
- ReactiveArray.prototype.notify = emitProxyValues;
36
+ ReactiveArray.prototype.notify = notifyProxyValue;
37
37
  ReactiveArray.prototype.peek = peekArrayValue;
38
38
  ReactiveArray.prototype.pop = popArrayValue;
39
39
  ReactiveArray.prototype.push = pushArrayValues;
@@ -1,4 +1,4 @@
1
- import { Computed, ComputedEffect, InternalComputed, ReactiveOptions } from "../models.mjs";
1
+ import { Computed, InternalComputed, ReactiveOptions } from "../models.mjs";
2
2
  import { GenericCallback } from "@oscarpalmer/atoms/models";
3
3
  //#region src/value/computed.d.ts
4
4
  declare function Computed(this: any, callback: GenericCallback, options?: ReactiveOptions<unknown>): void;
@@ -10,5 +10,5 @@ declare function Computed(this: any, callback: GenericCallback, options?: Reacti
10
10
  * @returns Computed value
11
11
  */
12
12
  export declare function computed<Value>(callback: () => Value | Promise<Value>, options?: ReactiveOptions<Value>): Computed<Value>;
13
- export declare function internalComputed(callback: GenericCallback, options?: ReactiveOptions<unknown>): [InternalComputed, ComputedEffect];
13
+ export declare function internalComputed(callback: GenericCallback, options?: ReactiveOptions<unknown>): InternalComputed;
14
14
  //#endregion
@@ -23,17 +23,17 @@ Computed.prototype.toString = getStringValue;
23
23
  * @returns Computed value
24
24
  */
25
25
  function computed(callback, options) {
26
- return getComputed(callback, options)[0];
26
+ return getComputed(callback, options);
27
27
  }
28
28
  function getComputed(callback, options) {
29
29
  if (typeof callback !== "function") throw new TypeError("Computed callback must be a function");
30
- const instance = new Computed(callback, options);
31
- return [instance, instance[SYMBOL_EFFECT]];
30
+ return new Computed(callback, options);
32
31
  }
33
32
  function getComputedEffect(instance, callback) {
34
33
  const fx = {
35
34
  dirty: true,
36
- instance: void 0
35
+ instance: void 0,
36
+ notify: false
37
37
  };
38
38
  fx.instance = internalEffect(() => {
39
39
  if (fx.dirty) {
@@ -41,7 +41,8 @@ function getComputedEffect(instance, callback) {
41
41
  ACTIVE.computed = fx;
42
42
  handleSignal(instance, callback, setAndEmit, () => {
43
43
  ACTIVE.computed = previousComputed;
44
- });
44
+ fx.notify = false;
45
+ }, fx.notify);
45
46
  fx.dirty = false;
46
47
  }
47
48
  });
@@ -64,9 +65,9 @@ function getComputedValue() {
64
65
  function internalComputed(callback, options) {
65
66
  return getComputed(callback, options);
66
67
  }
67
- function setAndEmit(instance, value) {
68
+ function setAndEmit(instance, value, notify) {
68
69
  const state = instance[SYMBOL_STATE];
69
- if ((state.equal ?? Object.is)(state.value, value)) return;
70
+ if (!notify && (state.equal ?? Object.is)(state.value, value)) return;
70
71
  state.value = value;
71
72
  if (state.computeds != null && state.computeds.size > 0) for (const computed of state.computeds) computed.dirty = true;
72
73
  if (state.effects != null && state.effects.size > 0) for (const effect of state.effects) BATCH.handlers.set(effect, effect);
@@ -2,7 +2,7 @@ import { NAME_MORA, NAME_STORE, SYMBOL_STATE } from "../constants.mjs";
2
2
  import { getJsonValue, getStringValue } from "../helpers/value.mjs";
3
3
  import { getState } from "../helpers/misc.mjs";
4
4
  import { subscribeToProxy } from "../helpers/subscription.mjs";
5
- import { emitProxyValues, getValueInProxy, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
5
+ import { getValueInProxy, notifyProxyValue, peekValueInProxy, setProxyValue, setValueInProxy, updateProxyValue } from "../helpers/proxy.mjs";
6
6
  import { getReadonlySignal } from "./readonly.mjs";
7
7
  //#region src/value/store.ts
8
8
  function ReactiveStore(value, options) {
@@ -16,7 +16,7 @@ function ReactiveStore(value, options) {
16
16
  ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
17
17
  ReactiveStore.prototype.asReadonly = getReadonlySignal;
18
18
  ReactiveStore.prototype.get = getValueInProxy;
19
- ReactiveStore.prototype.notify = emitProxyValues;
19
+ ReactiveStore.prototype.notify = notifyProxyValue;
20
20
  ReactiveStore.prototype.peek = peekValueInProxy;
21
21
  ReactiveStore.prototype.set = setProxyValue;
22
22
  ReactiveStore.prototype.subscribe = subscribeToProxy;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/mora",
3
- "version": "0.35.0",
3
+ "version": "0.37.0",
4
4
  "description": "Signals and stuff…",
5
5
  "keywords": [
6
6
  "reactive",
@@ -39,12 +39,12 @@
39
39
  "test:leak": "vpx vp test run --detect-async-leaks --coverage"
40
40
  },
41
41
  "dependencies": {
42
- "@oscarpalmer/atoms": "^0.203"
42
+ "@oscarpalmer/atoms": "^0.204"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^26.5",
46
46
  "@vitest/coverage-istanbul": "^4.1",
47
- "jsdom": "^30",
47
+ "jsdom": "^30.1",
48
48
  "tsdown": "^0.23",
49
49
  "typescript": "^6",
50
50
  "vite": "npm:@voidzero-dev/vite-plus-core@latest",
@@ -6,28 +6,13 @@ import type {
6
6
  PlainObject,
7
7
  } from '@oscarpalmer/atoms/models';
8
8
  import {startBatch, stopBatch} from '../batch';
9
- import {PROPERTY_LENGTH, SYMBOL_STATE} from '../constants';
9
+ import {PROPERTY_LENGTH, SYMBOL_EFFECT, SYMBOL_STATE} from '../constants';
10
10
  import type {InternalArray, InternalComputed, InternalStore, ReactiveProxyState} from '../models';
11
11
  import {internalComputed} from '../value/computed';
12
12
  import {emitValue, getSignalValue, peekSignalValue} from './value';
13
13
 
14
14
  // #region Functions
15
15
 
16
- export function emitProxyValues(this: InternalArray | InternalStore): void {
17
- const state = this[SYMBOL_STATE];
18
-
19
- state.mapped ??= new Map();
20
-
21
- const values = [...state.mapped.values()];
22
- const {length} = values;
23
-
24
- for (let index = 0; index < length; index += 1) {
25
- values[index][1].dirty = true;
26
- }
27
-
28
- emitValue(this);
29
- }
30
-
31
16
  export function getReactiveValueInProxy(
32
17
  instance: InternalArray | InternalStore,
33
18
  key: Key,
@@ -50,7 +35,7 @@ export function getReactiveValueInProxy(
50
35
  state.mapped.set(mapKey, item);
51
36
  }
52
37
 
53
- return item[0];
38
+ return item;
54
39
  }
55
40
 
56
41
  export function getValueInProxy(this: InternalArray | InternalStore, first?: unknown): unknown {
@@ -71,6 +56,22 @@ function isProxyObject(isArray: boolean, value: unknown): value is ArrayOrPlainO
71
56
  return value == null || (isArray ? Array.isArray(value) : isPlainObject(value));
72
57
  }
73
58
 
59
+ export function notifyProxyValue(this: InternalArray | InternalStore): void {
60
+ const state = this[SYMBOL_STATE];
61
+
62
+ state.mapped ??= new Map();
63
+
64
+ const values = [...state.mapped.values()];
65
+ const {length} = values;
66
+
67
+ for (let index = 0; index < length; index += 1) {
68
+ values[index][SYMBOL_EFFECT].dirty = true;
69
+ values[index][SYMBOL_EFFECT].notify = true;
70
+ }
71
+
72
+ emitValue(this, true);
73
+ }
74
+
74
75
  export function peekValueInProxy(
75
76
  this: InternalArray | InternalStore,
76
77
  first?: unknown,
@@ -9,13 +9,20 @@ import {
9
9
  SUBSCRIPTION_TYPE_FROZEN,
10
10
  SUBSCRIPTION_TYPES,
11
11
  SUBSCRIPTION_TYPES_COPY,
12
+ SYMBOL_EFFECT,
12
13
  SYMBOL_STATE,
13
14
  } from '../constants';
14
- import type {InternalStateful} from '../models';
15
+ import type {InternalArray, InternalComputed, InternalStateful, InternalStore} from '../models';
16
+
17
+ // #region Types
18
+
19
+ type SetValue = (instance: InternalStateful, value: unknown, notify: boolean) => void;
20
+
21
+ // #endregion
15
22
 
16
23
  // #region Functions
17
24
 
18
- export function emitValue(instance: InternalStateful): void {
25
+ export function emitValue(instance: InternalStateful, notify?: boolean): void {
19
26
  const state = instance[SYMBOL_STATE];
20
27
 
21
28
  if (state.computeds != null && state.computeds.size > 0) {
@@ -30,6 +37,18 @@ export function emitValue(instance: InternalStateful): void {
30
37
  }
31
38
  }
32
39
 
40
+ if (notify ?? false) {
41
+ const proxy = instance as InternalArray | InternalStore;
42
+ const state = proxy[SYMBOL_STATE];
43
+ const mapped = state.mapped as Map<unknown, InternalComputed>;
44
+
45
+ for (const [, item] of mapped) {
46
+ const fx = item[SYMBOL_EFFECT];
47
+
48
+ BATCH.handlers.set(fx.instance, fx.instance);
49
+ }
50
+ }
51
+
33
52
  for (const type of SUBSCRIPTION_TYPES) {
34
53
  const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
35
54
 
@@ -136,8 +155,9 @@ export function getStringValue(this: InternalStateful, json?: boolean): string {
136
155
  export function handleSignal(
137
156
  instance: InternalStateful,
138
157
  origin: unknown,
139
- setValue: (instance: InternalStateful, value: unknown) => void,
158
+ setValue: SetValue,
140
159
  onAfter?: () => void,
160
+ notify?: boolean,
141
161
  ): void {
142
162
  const state = instance[SYMBOL_STATE];
143
163
 
@@ -152,7 +172,7 @@ export function handleSignal(
152
172
  if (actual === state.promise) {
153
173
  state.promise = undefined;
154
174
 
155
- setValue(instance, value);
175
+ setValue(instance, value, notify ?? false);
156
176
  }
157
177
  })
158
178
  .catch(() => {
@@ -161,7 +181,7 @@ export function handleSignal(
161
181
  }
162
182
  });
163
183
  } else {
164
- setValue(instance, actual);
184
+ setValue(instance, actual, notify ?? false);
165
185
  }
166
186
  } catch {
167
187
  // ?
package/src/models.ts CHANGED
@@ -20,6 +20,7 @@ export type Computed<Value> = Reactive<Value>;
20
20
  export type ComputedEffect = {
21
21
  dirty: boolean;
22
22
  instance: EffectState;
23
+ notify: boolean;
23
24
  };
24
25
 
25
26
  export type Effect = {};
@@ -324,7 +325,7 @@ export type ReactiveOptions<Value> = {
324
325
  export type ReactiveProxyState = {
325
326
  isArray: boolean;
326
327
  length?: Signal<number>;
327
- mapped?: Map<string, [InternalComputed, ComputedEffect]>;
328
+ mapped?: Map<string, InternalComputed>;
328
329
  } & SignalState;
329
330
 
330
331
  export type ReactiveState<Value = unknown, Item = Value> = {
@@ -10,7 +10,7 @@ import {
10
10
  } from '../constants';
11
11
  import {getState} from '../helpers/misc';
12
12
  import {
13
- emitProxyValues,
13
+ notifyProxyValue,
14
14
  getValueInProxy,
15
15
  peekValueInProxy,
16
16
  setProxyValue,
@@ -58,7 +58,7 @@ ReactiveArray.prototype.clear = clearArrayValues;
58
58
  ReactiveArray.prototype.filter = filterArrayValues;
59
59
  ReactiveArray.prototype.get = getArrayValues;
60
60
  ReactiveArray.prototype.map = mapArrayValues;
61
- ReactiveArray.prototype.notify = emitProxyValues;
61
+ ReactiveArray.prototype.notify = notifyProxyValue;
62
62
  ReactiveArray.prototype.peek = peekArrayValue;
63
63
  ReactiveArray.prototype.pop = popArrayValue;
64
64
  ReactiveArray.prototype.push = pushArrayValues;
@@ -53,21 +53,19 @@ export function computed<Value>(
53
53
  callback: () => Value | Promise<Value>,
54
54
  options?: ReactiveOptions<Value>,
55
55
  ): Computed<Value> {
56
- return getComputed(callback, options)[0] as Computed<Value>;
56
+ return getComputed(callback, options) as Computed<Value>;
57
57
  }
58
58
 
59
59
  function getComputed<Value>(
60
60
  callback: GenericCallback,
61
61
  options?: ReactiveOptions<Value>,
62
- ): [InternalComputed, ComputedEffect] {
62
+ ): InternalComputed {
63
63
  if (typeof callback !== 'function') {
64
64
  throw new TypeError('Computed callback must be a function');
65
65
  }
66
66
 
67
67
  // @ts-expect-error All good, no worries :-)
68
- const instance = new Computed(callback, options);
69
-
70
- return [instance, instance[SYMBOL_EFFECT]];
68
+ return new Computed(callback, options);
71
69
  }
72
70
 
73
71
  function getComputedEffect(
@@ -77,6 +75,7 @@ function getComputedEffect(
77
75
  const fx: ComputedEffect = {
78
76
  dirty: true,
79
77
  instance: undefined as never,
78
+ notify: false,
80
79
  };
81
80
 
82
81
  fx.instance = internalEffect(() => {
@@ -85,9 +84,17 @@ function getComputedEffect(
85
84
 
86
85
  ACTIVE.computed = fx;
87
86
 
88
- handleSignal(instance, callback, setAndEmit, () => {
89
- ACTIVE.computed = previousComputed;
90
- });
87
+ handleSignal(
88
+ instance,
89
+ callback,
90
+ setAndEmit,
91
+ () => {
92
+ ACTIVE.computed = previousComputed;
93
+
94
+ fx.notify = false;
95
+ },
96
+ fx.notify,
97
+ );
91
98
 
92
99
  fx.dirty = false;
93
100
  }
@@ -122,14 +129,14 @@ function getComputedValue(this: InternalComputed): unknown {
122
129
  export function internalComputed(
123
130
  callback: GenericCallback,
124
131
  options?: ReactiveOptions<unknown>,
125
- ): [InternalComputed, ComputedEffect] {
132
+ ): InternalComputed {
126
133
  return getComputed(callback, options);
127
134
  }
128
135
 
129
- function setAndEmit(instance: InternalStateful, value: unknown): void {
136
+ function setAndEmit(instance: InternalStateful, value: unknown, notify: boolean): void {
130
137
  const state = instance[SYMBOL_STATE];
131
138
 
132
- if ((state.equal ?? Object.is)(state.value, value)) {
139
+ if (!notify && (state.equal ?? Object.is)(state.value, value)) {
133
140
  return;
134
141
  }
135
142
 
@@ -2,7 +2,7 @@ import type {PlainObject} from '@oscarpalmer/atoms/models';
2
2
  import {NAME_MORA, NAME_STORE, SYMBOL_STATE} from '../constants';
3
3
  import {getState} from '../helpers/misc';
4
4
  import {
5
- emitProxyValues,
5
+ notifyProxyValue,
6
6
  getValueInProxy,
7
7
  peekValueInProxy,
8
8
  setProxyValue,
@@ -36,7 +36,7 @@ ReactiveStore.prototype[NAME_MORA] = NAME_STORE;
36
36
 
37
37
  ReactiveStore.prototype.asReadonly = getReadonlySignal;
38
38
  ReactiveStore.prototype.get = getValueInProxy;
39
- ReactiveStore.prototype.notify = emitProxyValues;
39
+ ReactiveStore.prototype.notify = notifyProxyValue;
40
40
  ReactiveStore.prototype.peek = peekValueInProxy;
41
41
  ReactiveStore.prototype.set = setProxyValue;
42
42
  ReactiveStore.prototype.subscribe = subscribeToProxy;