@oscarpalmer/mora 0.31.0 → 0.33.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 (58) hide show
  1. package/dist/batch.d.mts +4 -5
  2. package/dist/batch.mjs +4 -2
  3. package/dist/constants.d.mts +31 -19
  4. package/dist/constants.mjs +19 -2
  5. package/dist/effect.d.mts +5 -5
  6. package/dist/effect.mjs +8 -5
  7. package/dist/helpers/is.d.mts +14 -9
  8. package/dist/helpers/is.mjs +6 -0
  9. package/dist/helpers/misc.d.mts +4 -0
  10. package/dist/helpers/misc.mjs +9 -0
  11. package/dist/helpers/proxy.d.mts +10 -10
  12. package/dist/helpers/proxy.mjs +83 -27
  13. package/dist/helpers/subscription.d.mts +8 -0
  14. package/dist/helpers/subscription.mjs +30 -0
  15. package/dist/helpers/value.d.mts +10 -7
  16. package/dist/helpers/value.mjs +56 -17
  17. package/dist/index.d.mts +3 -2
  18. package/dist/models.d.mts +82 -101
  19. package/dist/models.mjs +1 -0
  20. package/dist/mora.full.mjs +659 -412
  21. package/dist/value/array.d.mts +5 -5
  22. package/dist/value/array.mjs +70 -109
  23. package/dist/value/computed.d.mts +5 -4
  24. package/dist/value/computed.mjs +45 -31
  25. package/dist/value/readonly.d.mts +4 -6
  26. package/dist/value/readonly.mjs +34 -31
  27. package/dist/value/signal.d.mts +5 -5
  28. package/dist/value/signal.mjs +28 -34
  29. package/dist/value/store.d.mts +5 -5
  30. package/dist/value/store.mjs +25 -83
  31. package/package.json +6 -6
  32. package/src/batch.ts +15 -5
  33. package/src/constants.ts +36 -2
  34. package/src/effect.ts +20 -11
  35. package/src/helpers/is.ts +16 -6
  36. package/src/helpers/misc.ts +15 -0
  37. package/src/helpers/proxy.ts +151 -74
  38. package/src/helpers/subscription.ts +89 -0
  39. package/src/helpers/value.ts +121 -30
  40. package/src/index.ts +1 -1
  41. package/src/models.ts +81 -98
  42. package/src/value/array.ts +116 -226
  43. package/src/value/computed.ts +96 -43
  44. package/src/value/readonly.ts +68 -54
  45. package/src/value/signal.ts +54 -46
  46. package/src/value/store.ts +42 -168
  47. package/types/constants.d.ts +1 -1
  48. package/types/index.d.ts +1 -1
  49. package/types/value/array.d.ts +1 -1
  50. package/types/value/computed.d.ts +0 -3
  51. package/types/value/signal.d.ts +1 -1
  52. package/types/value/store.d.ts +1 -1
  53. package/dist/subscription.d.mts +0 -7
  54. package/dist/subscription.mjs +0 -27
  55. package/dist/value/reactive.d.mts +0 -5
  56. package/dist/value/reactive.mjs +0 -16
  57. package/src/subscription.ts +0 -45
  58. package/src/value/reactive.ts +0 -24
@@ -1,37 +1,111 @@
1
1
  import {select} from '@oscarpalmer/atoms/array';
2
2
  import {filter} from '@oscarpalmer/atoms/array/filter';
3
- import {noop} from '@oscarpalmer/atoms/function';
4
- import {isPlainObject} from '@oscarpalmer/atoms/is';
5
- import type {GenericCallback} from '@oscarpalmer/atoms/models';
6
3
  import {
7
4
  METHODS_AFFECTING_LENGTH,
8
5
  METHODS_UPDATE,
9
6
  NAME_ARRAY,
10
7
  NAME_MORA,
11
8
  PROPERTY_LENGTH,
9
+ SYMBOL_STATE,
12
10
  } from '../constants';
11
+ import {getState} from '../helpers/misc';
13
12
  import {
14
- emityProxyValues,
15
- getReactiveValueInProxy,
13
+ emitProxyValues,
14
+ getValueInProxy,
15
+ peekValueInProxy,
16
16
  setProxyValue,
17
17
  setValueInProxy,
18
+ updateProxyValue,
18
19
  } from '../helpers/proxy';
19
- import {emitValue, equalArrays, getSimpleValue} from '../helpers/value';
20
- import type {
21
- Computed,
22
- ComputedEffect,
23
- ReactiveArray,
24
- ReactiveOptions,
25
- ReactiveState,
26
- ReadonlyInstances,
27
- Signal,
28
- } from '../models';
29
- import {subscribe, unsubscribe} from '../subscription';
20
+ import {subscribeToProxy} from '../helpers/subscription';
21
+ import {emitValue, equalArrays, getStringValue} from '../helpers/value';
22
+ import type {InternalProxy, ReactiveArray, ReactiveOptions, ReactiveState} from '../models';
30
23
  import {computed} from './computed';
31
- import {reactive} from './reactive';
32
24
  import {getReadonlyInstance} from './readonly';
33
25
  import {signal} from './signal';
34
26
 
27
+ // #region Instance
28
+
29
+ function ReactiveArray(this: any, value: unknown, options?: ReactiveOptions<unknown>) {
30
+ this[SYMBOL_STATE] = {
31
+ ...getState(undefined, options),
32
+ isArray: true,
33
+ length: signal(0),
34
+ };
35
+
36
+ this[SYMBOL_STATE].value = new Proxy([], {
37
+ get: (target, property) =>
38
+ METHODS_UPDATE.has(property as string)
39
+ ? updateArray(this, property as string, target)
40
+ : Reflect.get(target, property),
41
+ set: (target, property, value) => setValueInProxy(this, target, property, value),
42
+ });
43
+
44
+ Object.defineProperty(this, PROPERTY_LENGTH, {
45
+ enumerable: true,
46
+ get: () => this[SYMBOL_STATE].length.get(),
47
+ set: (value: number) => setArrayLength(this[SYMBOL_STATE], value),
48
+ });
49
+
50
+ setProxyValue.call(this, value);
51
+ }
52
+
53
+ ReactiveArray.prototype[NAME_MORA] = NAME_ARRAY;
54
+
55
+ ReactiveArray.prototype.asReadonly = getReadonlyInstance;
56
+ ReactiveArray.prototype.at = getArrayValues;
57
+ ReactiveArray.prototype.get = getArrayValues;
58
+ ReactiveArray.prototype.notify = emitProxyValues;
59
+ ReactiveArray.prototype.peek = peekArrayValue;
60
+ ReactiveArray.prototype.set = setProxyValue;
61
+ ReactiveArray.prototype.subscribe = subscribeToProxy;
62
+ ReactiveArray.prototype.toString = getStringValue;
63
+ ReactiveArray.prototype.update = updateProxyValue;
64
+
65
+ ReactiveArray.prototype.clear = function () {
66
+ this[SYMBOL_STATE].value.length = 0;
67
+ };
68
+
69
+ ReactiveArray.prototype.filter = function (callback: never) {
70
+ return computed(() => filter(getArrayValues.call(this) as never, callback) as never);
71
+ };
72
+
73
+ ReactiveArray.prototype.map = function (callback: never) {
74
+ return computed(() => (getArrayValues.call(this) as unknown[]).map(callback));
75
+ };
76
+
77
+ ReactiveArray.prototype.pop = function () {
78
+ return this[SYMBOL_STATE].value.pop();
79
+ };
80
+
81
+ ReactiveArray.prototype.push = function (...items: unknown[]) {
82
+ return this[SYMBOL_STATE].value.push(...items);
83
+ };
84
+
85
+ ReactiveArray.prototype.select = function (filter: never, map: never) {
86
+ return computed(() => select(getArrayValues.call(this) as unknown[], filter, map));
87
+ };
88
+
89
+ ReactiveArray.prototype.shift = function () {
90
+ return this[SYMBOL_STATE].value.shift();
91
+ };
92
+
93
+ ReactiveArray.prototype.splice = function (from: never, to?: never, ...items: unknown[]) {
94
+ return this[SYMBOL_STATE].value.splice(from, to ?? this[SYMBOL_STATE].value.length, ...items);
95
+ };
96
+
97
+ ReactiveArray.prototype.toJSON = function () {
98
+ return this[SYMBOL_STATE].value;
99
+ };
100
+
101
+ ReactiveArray.prototype.unshift = function (...items: unknown[]) {
102
+ return this[SYMBOL_STATE].value.unshift(...items);
103
+ };
104
+
105
+ // #endregion
106
+
107
+ // #region Functions
108
+
35
109
  /**
36
110
  * Create a reactive array from a function result
37
111
  *
@@ -65,238 +139,54 @@ export function array<Item>(
65
139
  */
66
140
  export function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
67
141
 
68
- export function array<Item>(
69
- value: Item[] | (() => Item[] | Promise<Item[]>) | Promise<Item[]>,
70
- options?: ReactiveOptions<Item>,
71
- ): ReactiveArray<Item> {
72
- const [rx, state] = reactive<Item[], Item>([], options);
73
-
74
- const indiced = new Map<number, [Computed<unknown>, ComputedEffect]>();
75
- const length = signal(0);
76
- const readonlies: ReadonlyInstances<Item[]> = {};
77
-
78
- let instance: Record<string, unknown> = {};
79
-
80
- state.value = new Proxy([], {
81
- get: (target: Item[], property: PropertyKey) =>
82
- METHODS_UPDATE.has(property as string)
83
- ? updateArray(property as string, target, state, length)
84
- : Reflect.get(target, property),
85
- set: (target: Item[], property: PropertyKey, value: Item) =>
86
- setValueInProxy({
87
- length,
88
- property,
89
- state,
90
- target,
91
- value,
92
- isArray: true,
93
- }),
94
- });
95
-
96
- function get(): Item[];
97
- function get(index: number): Item | undefined;
98
- function get(property: typeof PROPERTY_LENGTH): number;
99
- function get(value?: unknown): number | Item | Item[] | undefined;
100
-
101
- function get(value?: unknown): number | Item | Item[] | undefined {
102
- return getArrayValue(instance as ReactiveArray<Item>, indiced, state, length, value);
103
- }
104
-
105
- function set(first?: unknown, second?: unknown): void {
106
- setProxyValue<Item[], Item>(
107
- true,
108
- state,
109
- isArrayValue,
110
- isArrayIndex,
111
- setArray,
112
- setAtIndex,
113
- first,
114
- second,
115
- );
116
- }
117
-
118
- const handlers = {
119
- ...rx,
120
- get: (value?: number | typeof PROPERTY_LENGTH) => get(value),
121
- peek: (first?: unknown, second?: boolean) => peekArrayValue(state, length, first, second),
122
- subscribe: (first: number | GenericCallback, second?: GenericCallback) => {
123
- if (typeof first === 'number' && typeof second === 'function') {
124
- return getReactiveValueInProxy(
125
- instance as ReactiveArray<Item>,
126
- indiced,
127
- first,
128
- true,
129
- ).subscribe(second);
130
- }
131
-
132
- return typeof first === 'function' ? subscribe(state, first) : noop;
133
- },
134
- unsubscribe: (first: number | GenericCallback, second?: GenericCallback) => {
135
- if (typeof first === 'number' && typeof second === 'function') {
136
- getReactiveValueInProxy(instance as ReactiveArray<Item>, indiced, first, true)?.unsubscribe(
137
- second,
138
- );
139
- } else if (typeof first === 'function') {
140
- unsubscribe(state, first);
141
- }
142
- },
143
- };
144
-
145
- instance = {
146
- ...handlers,
147
- asReadonly: (frozen?: unknown) =>
148
- getReadonlyInstance(state, readonlies, handlers, frozen === true),
149
- at: (index: number): Item | undefined => get(index),
150
- clear: () => {
151
- state.value.length = 0;
152
- },
153
- filter: (callback: (item: Item, index: number, array: Item[]) => boolean) =>
154
- computed(() => filter(get(), callback)),
155
- map: <Mapped>(callback: (item: Item, index: number, array: Item[]) => Mapped) =>
156
- computed(() => get().map(callback)),
157
- notify: () => {
158
- emityProxyValues(state, indiced);
159
- },
160
- pop: () => state.value.pop(),
161
- push: (...items: Item[]) => state.value.push(...items),
162
- select: <Mapped>(
163
- filter: (item: Item, index: number, array: Item[]) => boolean,
164
- map: (item: Item, index: number, array: Item[]) => Mapped,
165
- ) => computed(() => select(get() as Item[], filter, map)),
166
- set: (first?: unknown, second?: unknown) => {
167
- set(first, second);
168
- },
169
- shift: () => state.value.shift(),
170
- splice: (from: number, to?: number, ...items: Item[]) =>
171
- state.value.splice(from, to ?? state.value.length, ...items),
172
- unshift: (...items: Item[]) => state.value.unshift(...items),
173
- update: (callback: (value: Item[]) => Item[]) =>
174
- updateArrayValue(instance as ReactiveArray<Item>, state, callback),
175
- };
176
-
177
- Object.defineProperties(instance, {
178
- [NAME_MORA]: {
179
- enumerable: false,
180
- value: NAME_ARRAY,
181
- },
182
- length: {
183
- enumerable: true,
184
- get: () => length.get(),
185
- set: (value: number) => setArrayLength(state, value),
186
- },
187
- });
188
-
189
- set(value);
190
-
191
- return Object.freeze(instance) as ReactiveArray<Item>;
192
- }
193
-
194
- function getArrayValue<Item>(
195
- instance: ReactiveArray<Item>,
196
- indiced: Map<number, [Computed<unknown>, ComputedEffect]>,
197
- state: ReactiveState<Item[], Item>,
198
- length: Signal<number>,
199
- first?: unknown,
200
- ): number | Item | Item[] | undefined {
201
- if (typeof first === 'number') {
202
- return getReactiveValueInProxy(instance, indiced, first, true).get();
203
- }
204
-
205
- return first === PROPERTY_LENGTH ? length.get() : getSimpleValue(state);
142
+ export function array(value: unknown, options?: ReactiveOptions<unknown>): ReactiveArray<unknown> {
143
+ // @ts-expect-error All good, no worries :-)
144
+ return new ReactiveArray(value, options);
206
145
  }
207
146
 
208
- function isArrayIndex(value: unknown): boolean {
209
- return typeof value === 'number';
147
+ function getArrayValues(this: InternalProxy, value?: unknown): unknown {
148
+ return value === PROPERTY_LENGTH
149
+ ? this[SYMBOL_STATE].length!.get()
150
+ : getValueInProxy.call(this, value);
210
151
  }
211
152
 
212
- function isArrayValue<Item>(value: unknown): value is Item[] | undefined {
213
- return value == null || Array.isArray(value);
153
+ function peekArrayValue(this: InternalProxy, first?: unknown, second?: boolean): unknown {
154
+ return first === PROPERTY_LENGTH
155
+ ? this[SYMBOL_STATE].length!.peek()
156
+ : peekValueInProxy.call(this, first, second);
214
157
  }
215
158
 
216
- function peekArrayValue<Item>(
217
- state: ReactiveState<Item[], Item>,
218
- length: Signal<number>,
219
- first?: unknown,
220
- second?: boolean,
221
- ): number | Item | Item[] | undefined {
222
- if (first === PROPERTY_LENGTH) {
223
- return length.peek();
159
+ function setArrayLength(state: ReactiveState, value: number): void {
160
+ if (
161
+ typeof value === 'number' &&
162
+ !Number.isNaN(value) &&
163
+ value >= 0 &&
164
+ value !== (state.value as unknown[]).length
165
+ ) {
166
+ (state.value as unknown[]).length = value;
224
167
  }
225
-
226
- let value: Item | Item[] | undefined;
227
-
228
- if (typeof first === 'number') {
229
- value = state.value.at(first);
230
- } else {
231
- value = state.value;
232
- }
233
-
234
- if (!(first === true || second === true)) {
235
- return value;
236
- }
237
-
238
- if (Array.isArray(value)) {
239
- return value.slice();
240
- }
241
-
242
- if (isPlainObject(value)) {
243
- return {...value};
244
- }
245
-
246
- return value;
247
168
  }
248
169
 
249
- function setArray<Item>(state: ReactiveState<Item[], Item>, value: Item[] | undefined): void {
250
- state.value.splice(0, state.value.length, ...(value ?? []));
251
- }
170
+ function updateArray(instance: InternalProxy, type: string, array: unknown[]): unknown {
171
+ const state = instance[SYMBOL_STATE];
252
172
 
253
- function setArrayLength<Item>(state: ReactiveState<Item[], Item>, value: number): void {
254
- if (typeof value === 'number' && value >= 0 && value !== state.value.length) {
255
- state.value.length = value;
256
- }
257
- }
258
-
259
- function setAtIndex<Item>(state: ReactiveState<Item[], Item>, index: unknown, value: Item): void {
260
- const actual = (index as number) < 0 ? state.value.length + (index as number) : (index as number);
261
-
262
- if (actual > -1) {
263
- state.value[actual] = value;
264
- }
265
- }
266
-
267
- function updateArray<Item>(
268
- type: string,
269
- array: Item[],
270
- state: ReactiveState<Item[], Item>,
271
- length: Signal<number>,
272
- ): unknown {
273
173
  const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
274
174
  const previousArray = affectsLength ? [] : array.slice();
275
175
  const previousLength = array.length;
276
176
 
277
- return (...args: unknown[]): unknown => {
177
+ return (...args: unknown[]) => {
278
178
  const result = (array[type as never] as (...args: unknown[]) => unknown)(...args);
279
179
 
280
180
  if (
281
181
  affectsLength ? array.length !== previousLength : !equalArrays(state, previousArray, array)
282
182
  ) {
283
- emitValue(state);
183
+ emitValue(instance);
284
184
 
285
- length.set(array.length);
185
+ state.length!.set(array.length);
286
186
  }
287
187
 
288
188
  return result;
289
189
  };
290
190
  }
291
191
 
292
- function updateArrayValue<Item>(
293
- instance: ReactiveArray<Item>,
294
- state: ReactiveState<Item[], Item>,
295
- callback: (value: Item[]) => Item[],
296
- ): void {
297
- const updated = callback(state.value);
298
-
299
- if (updated == null || Array.isArray(updated)) {
300
- instance.set(updated);
301
- }
302
- }
192
+ // #endregion
@@ -1,10 +1,50 @@
1
+ import type {GenericAsyncCallback, GenericCallback} from '@oscarpalmer/atoms/models';
1
2
  import {flushHandlers} from '../batch';
2
- import {ACTIVE, BATCH, NAME_COMPUTED, NAME_MORA} from '../constants';
3
+ import {
4
+ ACTIVE,
5
+ BATCH,
6
+ NAME_COMPUTED,
7
+ NAME_MORA,
8
+ SUBSCRIPTION_TYPE_FROZEN,
9
+ SUBSCRIPTION_TYPES,
10
+ SUBSCRIPTION_TYPES_COPY,
11
+ SYMBOL_EFFECT,
12
+ SYMBOL_STATE,
13
+ } from '../constants';
3
14
  import {internalEffect, runEffect} from '../effect';
4
- import {handleSimpleValue} from '../helpers/value';
5
- import type {Computed, ComputedEffect, ReactiveOptions, ReactiveState} from '../models';
6
- import {subscribe} from '../subscription';
7
- import {reactive} from './reactive';
15
+ import {getState} from '../helpers/misc';
16
+ import {subscribeToSignal} from '../helpers/subscription';
17
+ import {getStringValue, handleSignalValue, peekSignalValue} from '../helpers/value';
18
+ import type {
19
+ Computed,
20
+ ComputedEffect,
21
+ ReactiveOptions,
22
+ InternalComputed,
23
+ InternalSignal,
24
+ } from '../models';
25
+
26
+ // #region Instance
27
+
28
+ function Computed(this: any, callback: GenericCallback, options?: ReactiveOptions<unknown>) {
29
+ this[SYMBOL_STATE] = getState(undefined, options);
30
+
31
+ this[SYMBOL_EFFECT] = getComputedEffect(this, callback);
32
+ }
33
+
34
+ Computed.prototype[NAME_MORA] = NAME_COMPUTED;
35
+
36
+ Computed.prototype.get = getComputedValue;
37
+ Computed.prototype.peek = peekSignalValue;
38
+ Computed.prototype.subscribe = subscribeToSignal;
39
+ Computed.prototype.toString = getStringValue;
40
+
41
+ Computed.prototype.toJSON = function () {
42
+ return this[SYMBOL_STATE].value;
43
+ };
44
+
45
+ // #endregion
46
+
47
+ // #region Functions
8
48
 
9
49
  /**
10
50
  * Create a computed value
@@ -21,36 +61,22 @@ export function computed<Value>(
21
61
  }
22
62
 
23
63
  function getComputed<Value>(
24
- callback: () => Value | Promise<Value>,
64
+ callback: GenericCallback,
25
65
  options?: ReactiveOptions<Value>,
26
66
  ): [Computed<Value>, ComputedEffect] {
27
- const [rx, state] = reactive<Value>(undefined as never, options);
28
-
29
- let fx: ComputedEffect;
30
-
31
- const instance = {
32
- ...rx,
33
- get: () => getValue(state, fx),
34
- peek: () => state.value,
35
- subscribe: (callback: (value: Value) => void) => subscribe(state, callback),
36
- unsubscribe: (callback: (value: Value) => void) => {
37
- state.subscriptions.delete(callback);
38
- },
39
- };
40
-
41
- Object.defineProperty(instance, NAME_MORA, {
42
- enumerable: false,
43
- value: NAME_COMPUTED,
44
- });
67
+ if (typeof callback !== 'function') {
68
+ throw new TypeError('Computed callback must be a function');
69
+ }
45
70
 
46
- fx = getComputedEffect(state, callback);
71
+ // @ts-expect-error All good, no worries :-)
72
+ const instance = new Computed(callback, options);
47
73
 
48
- return [Object.freeze(instance), fx];
74
+ return [instance as never, instance[SYMBOL_EFFECT]];
49
75
  }
50
76
 
51
- function getComputedEffect<Value>(
52
- state: ReactiveState<Value, Value>,
53
- callback: () => Value | Promise<Value>,
77
+ function getComputedEffect(
78
+ instance: InternalSignal,
79
+ callback: GenericAsyncCallback,
54
80
  ): ComputedEffect {
55
81
  const fx: ComputedEffect = {
56
82
  dirty: true,
@@ -63,7 +89,7 @@ function getComputedEffect<Value>(
63
89
 
64
90
  ACTIVE.computed = fx;
65
91
 
66
- handleSimpleValue(state, callback, setAndEmit, () => {
92
+ handleSignalValue(instance, callback, setAndEmit, () => {
67
93
  ACTIVE.computed = previousComputed;
68
94
  });
69
95
 
@@ -74,12 +100,19 @@ function getComputedEffect<Value>(
74
100
  return fx;
75
101
  }
76
102
 
77
- function getValue<Value>(state: ReactiveState<Value, Value>, fx: ComputedEffect): Value {
103
+ function getComputedValue(this: InternalComputed): unknown {
104
+ const fx = this[SYMBOL_EFFECT];
105
+ const state = this[SYMBOL_STATE];
106
+
78
107
  if (ACTIVE.computed != null && fx !== ACTIVE.computed) {
108
+ state.computeds ??= new Set();
109
+
79
110
  state.computeds.add(ACTIVE.computed);
80
111
  }
81
112
 
82
113
  if (ACTIVE.effect != null && ACTIVE.effect !== fx.instance) {
114
+ state.effects ??= new Set();
115
+
83
116
  state.effects.add(ACTIVE.effect);
84
117
  }
85
118
 
@@ -90,31 +123,51 @@ function getValue<Value>(state: ReactiveState<Value, Value>, fx: ComputedEffect)
90
123
  return state.value;
91
124
  }
92
125
 
93
- export function internalComputed<Value>(
94
- callback: () => Value | Promise<Value>,
95
- options?: ReactiveOptions<Value>,
96
- ): [Computed<Value>, ComputedEffect] {
126
+ export function internalComputed(
127
+ callback: GenericCallback,
128
+ options?: ReactiveOptions<unknown>,
129
+ ): [Computed<unknown>, ComputedEffect] {
97
130
  return getComputed(callback, options);
98
131
  }
99
132
 
100
- function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): void {
101
- if (state.equal(state.value, value)) {
133
+ function setAndEmit(instance: InternalSignal, value: unknown): void {
134
+ const state = instance[SYMBOL_STATE];
135
+
136
+ if ((state.equal ?? Object.is)(state.value as never, value as never)) {
102
137
  return;
103
138
  }
104
139
 
105
140
  state.value = value;
106
141
 
107
- for (const computed of state.computeds) {
108
- computed.dirty = true;
142
+ if (state.computeds != null && state.computeds.size > 0) {
143
+ for (const computed of state.computeds) {
144
+ computed.dirty = true;
145
+ }
109
146
  }
110
147
 
111
- for (const effect of state.effects) {
112
- BATCH.handlers.add(effect);
148
+ if (state.effects != null && state.effects.size > 0) {
149
+ for (const effect of state.effects) {
150
+ BATCH.handlers.set(effect, effect);
151
+ }
113
152
  }
114
153
 
115
- for (const [, subscription] of state.subscriptions) {
116
- subscription.callback(value);
154
+ for (const type of SUBSCRIPTION_TYPES) {
155
+ if (type === SUBSCRIPTION_TYPE_FROZEN) {
156
+ continue;
157
+ }
158
+
159
+ const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
160
+
161
+ if (subscriptions != null && subscriptions.size > 0) {
162
+ const copy = SUBSCRIPTION_TYPES_COPY.has(type);
163
+
164
+ for (const [, callback] of subscriptions) {
165
+ callback(peekSignalValue.call(instance, copy));
166
+ }
167
+ }
117
168
  }
118
169
 
119
170
  flushHandlers();
120
171
  }
172
+
173
+ // #endregion