@oscarpalmer/mora 0.32.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 (45) hide show
  1. package/dist/batch.mjs +2 -2
  2. package/dist/constants.d.mts +2 -0
  3. package/dist/constants.mjs +3 -1
  4. package/dist/effect.d.mts +1 -0
  5. package/dist/effect.mjs +8 -5
  6. package/dist/helpers/is.d.mts +6 -6
  7. package/dist/helpers/misc.d.mts +4 -0
  8. package/dist/helpers/misc.mjs +9 -0
  9. package/dist/helpers/proxy.d.mts +8 -10
  10. package/dist/helpers/proxy.mjs +71 -33
  11. package/dist/helpers/subscription.d.mts +4 -4
  12. package/dist/helpers/subscription.mjs +16 -13
  13. package/dist/helpers/value.d.mts +8 -7
  14. package/dist/helpers/value.mjs +35 -22
  15. package/dist/models.d.mts +47 -24
  16. package/dist/models.mjs +1 -0
  17. package/dist/mora.full.mjs +307 -236
  18. package/dist/value/array.d.mts +1 -0
  19. package/dist/value/array.mjs +69 -61
  20. package/dist/value/computed.d.mts +3 -1
  21. package/dist/value/computed.mjs +39 -25
  22. package/dist/value/readonly.d.mts +3 -3
  23. package/dist/value/readonly.mjs +31 -24
  24. package/dist/value/signal.d.mts +1 -0
  25. package/dist/value/signal.mjs +27 -20
  26. package/dist/value/store.d.mts +1 -0
  27. package/dist/value/store.mjs +23 -41
  28. package/package.json +1 -1
  29. package/src/batch.ts +2 -2
  30. package/src/constants.ts +4 -0
  31. package/src/effect.ts +16 -11
  32. package/src/helpers/is.ts +6 -6
  33. package/src/helpers/misc.ts +15 -0
  34. package/src/helpers/proxy.ts +116 -89
  35. package/src/helpers/subscription.ts +46 -35
  36. package/src/helpers/value.ts +61 -35
  37. package/src/models.ts +54 -28
  38. package/src/value/array.ts +103 -124
  39. package/src/value/computed.ts +70 -40
  40. package/src/value/readonly.ts +54 -41
  41. package/src/value/signal.ts +46 -35
  42. package/src/value/store.ts +36 -83
  43. package/dist/value/reactive.d.mts +0 -4
  44. package/dist/value/reactive.mjs +0 -15
  45. package/src/value/reactive.ts +0 -27
package/src/models.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type {GenericCallback, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
2
  import type {Subscription, Subscriptions} from '@oscarpalmer/atoms/subscription';
3
- import type {PROPERTY_LENGTH} from './constants';
3
+ import {SYMBOL_EFFECT, SYMBOL_STATE, type PROPERTY_LENGTH} from './constants';
4
4
 
5
5
  // #region Types
6
6
 
@@ -28,20 +28,37 @@ export type EffectState = {
28
28
  callback: GenericCallback;
29
29
  };
30
30
 
31
+ export type InternalComputed = {
32
+ [SYMBOL_EFFECT]: ComputedEffect;
33
+ } & InternalSignal;
34
+
35
+ export type InternalProxy = {
36
+ [SYMBOL_STATE]: ReactiveProxyState;
37
+ };
38
+
39
+ export type InternalReadonly = {
40
+ frozen: boolean;
41
+ } & InternalSignal;
42
+
43
+ export type InternalSignal = {
44
+ [SYMBOL_STATE]: SignalState;
45
+ };
46
+
31
47
  export type Reactive<Value> = {
32
48
  /**
33
- * JSON representation of the value
49
+ * _JSON_ representation of the value
34
50
  *
35
- * @returns JSON value
51
+ * @returns _JSON_ value
36
52
  */
37
53
  toJSON(): Value;
38
54
 
39
55
  /**
40
56
  * String representation of the value
41
57
  *
58
+ * @param json When `true`, returns the _JSON_ string representation of the value _(defaults to `false`)_
42
59
  * @returns Value as string
43
60
  */
44
- toString(): string;
61
+ toString(json?: boolean): string;
45
62
  };
46
63
 
47
64
  export type ReactiveArray<Item> = {
@@ -230,25 +247,21 @@ export type ReactiveArray<Item> = {
230
247
  /**
231
248
  * Subscribe to changes
232
249
  *
233
- * @param callback Callback for changes
250
+ * @param subscriber Callback for changes
234
251
  * @param copy Copy the array? _(defaults to `false`)_
235
252
  * @returns Subscription
236
253
  */
237
- subscribe(callback: (value: Item[]) => void, copy?: boolean): Subscription;
254
+ subscribe(subscriber: Subscriber<Item[]>, copy?: boolean): Subscription;
238
255
 
239
256
  /**
240
257
  * Subscribe to changes at a specific index
241
258
  *
242
259
  * @param index Index of item to subscribe to
243
- * @param callback Callback for changes
260
+ * @param subscriber Callback for changes
244
261
  * @param copy If the item is an array or record, should it be copied? _(defaults to `false`)_
245
262
  * @returns Subscription
246
263
  */
247
- subscribe(
248
- index: number,
249
- callback: (value: Item | undefined) => void,
250
- copy?: boolean,
251
- ): Subscription;
264
+ subscribe(index: number, subscriber: Subscriber<Item | undefined>, copy?: boolean): Subscription;
252
265
 
253
266
  /**
254
267
  * Add items to the beginning of the array
@@ -276,14 +289,20 @@ export type ReactiveOptions<Value> = {
276
289
  equal?: (first: Value, second: Value) => boolean;
277
290
  };
278
291
 
279
- export type ReactiveState<Value, Item = Value> = {
280
- computeds: Set<ComputedEffect>;
281
- effects: Set<EffectState>;
282
- equal: (first: Item, second: Item) => boolean;
283
- promise?: Promise<Value>;
292
+ export type ReactiveProxyState = {
293
+ isArray: boolean;
294
+ length?: Signal<number>;
295
+ mapped?: Map<string, [Computed<unknown>, ComputedEffect]>;
296
+ } & SignalState;
297
+
298
+ export type ReactiveState = {
299
+ computeds?: Set<ComputedEffect>;
300
+ effects?: Set<EffectState>;
301
+ equal?: (first: unknown, second: unknown) => boolean;
302
+ promise?: Promise<unknown>;
284
303
  promises?: Map<Key, Promise<never>>;
285
304
  subscriptions?: Subscriptions<GenericCallback>;
286
- value: Value;
305
+ value: unknown;
287
306
  };
288
307
 
289
308
  export type ReactiveStore<Store> = {
@@ -396,23 +415,23 @@ export type ReactiveStore<Store> = {
396
415
  /**
397
416
  * Subscribe to changes
398
417
  *
399
- * @param callback Callback for changes
418
+ * @param subscriber Callback for changes
400
419
  * @param copy Copy the store? _(defaults to `false`)_
401
420
  * @returns Subscription
402
421
  */
403
- subscribe(callback: (value: Store) => void, copy?: boolean): Subscription;
422
+ subscribe(subscriber: Subscriber<Store>, copy?: boolean): Subscription;
404
423
 
405
424
  /**
406
425
  * Subscribe to changes for a specific key
407
426
  *
408
427
  * @param key Key of the value to subscribe to
409
- * @param callback Callback for changes
428
+ * @param subscriber Callback for changes
410
429
  * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
411
430
  * @returns Subscription
412
431
  */
413
432
  subscribe<Key extends keyof Store>(
414
433
  key: Key,
415
- callback: (value: Store[Key] | undefined) => void,
434
+ subscriber: Subscriber<Store[Key] | undefined>,
416
435
  copy?: boolean,
417
436
  ): Subscription;
418
437
 
@@ -420,11 +439,11 @@ export type ReactiveStore<Store> = {
420
439
  * Subscribe to changes for a specific key
421
440
  *
422
441
  * @param key Key of the value to subscribe to
423
- * @param callback Callback for changes
442
+ * @param subscriber Callback for changes
424
443
  * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
425
444
  * @returns Subscription
426
445
  */
427
- subscribe(key: Key, callback: (value: unknown) => void, copy?: boolean): Subscription;
446
+ subscribe(key: Key, subscriber: Subscriber<unknown>, copy?: boolean): Subscription;
428
447
 
429
448
  /**
430
449
  * Update the value _(based on the current value)_
@@ -434,7 +453,7 @@ export type ReactiveStore<Store> = {
434
453
  update(callback: (value: Store) => Store): void;
435
454
  } & Reactive<Store>;
436
455
 
437
- export type ReadonlyInstances<Value> = {
456
+ type ReadonlyInstances<Value> = {
438
457
  frozen?: ReadonlyFrozenSignal<Value>;
439
458
  original?: ReadonlySignal<Value>;
440
459
  };
@@ -490,6 +509,10 @@ export type Signal<Value> = {
490
509
  } & Reactive<Value> &
491
510
  SimpleReactive<Value>;
492
511
 
512
+ export type SignalState = {
513
+ readonlies?: ReadonlyInstances<unknown>;
514
+ } & ReactiveState;
515
+
493
516
  type SimpleReactive<Value> = {
494
517
  /**
495
518
  * Get the value
@@ -509,20 +532,23 @@ type SimpleReactive<Value> = {
509
532
  /**
510
533
  * Subscribe to changes
511
534
  *
512
- * @param callback Callback for changes
535
+ * @param subscriber Callback for changes
513
536
  * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
514
537
  * @returns Subscription
515
538
  */
516
- subscribe(callback: (value: Value) => void, copy?: boolean): Subscription;
539
+ subscribe(subscriber: Subscriber<Value>, copy?: boolean): Subscription;
517
540
  };
518
541
 
519
542
  export type StoredSubscription = {
520
543
  callback: GenericCallback;
521
544
  copy: boolean;
522
545
  frozen: boolean;
523
- state: ReactiveState<unknown, never>;
546
+ instance: InternalSignal;
547
+ state: SignalState;
524
548
  };
525
549
 
550
+ export type Subscriber<Value> = (value: Value, subscription: Subscription) => void;
551
+
526
552
  export type SubscriptionType = 'copy' | 'frozen' | 'original' | 'readonly';
527
553
 
528
554
  // #endregion
@@ -6,7 +6,9 @@ import {
6
6
  NAME_ARRAY,
7
7
  NAME_MORA,
8
8
  PROPERTY_LENGTH,
9
+ SYMBOL_STATE,
9
10
  } from '../constants';
11
+ import {getState} from '../helpers/misc';
10
12
  import {
11
13
  emitProxyValues,
12
14
  getValueInProxy,
@@ -16,21 +18,92 @@ import {
16
18
  updateProxyValue,
17
19
  } from '../helpers/proxy';
18
20
  import {subscribeToProxy} from '../helpers/subscription';
19
- import {emitValue, equalArrays} from '../helpers/value';
20
- import type {
21
- Computed,
22
- ComputedEffect,
23
- ReactiveArray,
24
- ReactiveOptions,
25
- ReactiveState,
26
- ReadonlyInstances,
27
- Signal,
28
- } from '../models';
21
+ import {emitValue, equalArrays, getStringValue} from '../helpers/value';
22
+ import type {InternalProxy, ReactiveArray, ReactiveOptions, ReactiveState} from '../models';
29
23
  import {computed} from './computed';
30
- import {reactive} from './reactive';
31
24
  import {getReadonlyInstance} from './readonly';
32
25
  import {signal} from './signal';
33
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
+
34
107
  // #region Functions
35
108
 
36
109
  /**
@@ -66,131 +139,37 @@ export function array<Item>(
66
139
  */
67
140
  export function array<Item>(value: Item[], options?: ReactiveOptions<Item>): ReactiveArray<Item>;
68
141
 
69
- export function array<Item>(
70
- value: Item[] | (() => Item[] | Promise<Item[]>) | Promise<Item[]>,
71
- options?: ReactiveOptions<Item>,
72
- ): ReactiveArray<Item> {
73
- const [rx, state] = reactive<Item[], Item>([], options);
74
-
75
- const isArray = true;
76
- const length = signal(0);
77
- const mapped = new Map<number, [Computed<unknown>, ComputedEffect]>();
78
- const readonlies: ReadonlyInstances<Item[]> = {};
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(isArray, state, target, property, value, length),
87
- });
88
-
89
- function get(): Item[];
90
- function get(index: number): Item | undefined;
91
- function get(property: typeof PROPERTY_LENGTH): number;
92
- function get(value?: unknown): number | Item | Item[] | undefined;
93
-
94
- function get(value?: unknown): unknown {
95
- return value === PROPERTY_LENGTH
96
- ? length.get()
97
- : getValueInProxy(isArray, instance as never, state, mapped, value);
98
- }
99
-
100
- function set(first?: never, second?: never): void {
101
- setProxyValue<Item[], Item>(true, state, setArrayValue, setAtIndex, first, second);
102
- }
103
-
104
- const instance = {
105
- ...rx,
106
- asReadonly: (frozen?: never) => getReadonlyInstance(state, readonlies, frozen === true),
107
- at: (index: never) => get(index),
108
- clear: () => {
109
- state.value.length = 0;
110
- },
111
- filter: (callback: never) => computed(() => filter(get(), callback)),
112
- get: (value?: never) => get(value),
113
- map: (callback: never) => computed(() => get().map(callback)),
114
- notify: () => emitProxyValues(state, mapped),
115
- peek: (first?: never, second?: never) => peekArrayValue(state, length, first, second),
116
- pop: () => state.value.pop(),
117
- push: (...items: Item[]) => state.value.push(...items),
118
- select: (filter: never, map: never) => computed(() => select(get(), filter, map)),
119
- set: (first?: never, second?: never) => set(first, second),
120
- shift: () => state.value.shift(),
121
- splice: (from: never, to?: never, ...items: Item[]) =>
122
- state.value.splice(from, to ?? state.value.length, ...items),
123
- subscribe: (first: never, second?: never, third?: never) =>
124
- subscribeToProxy(isArray, instance as never, state, mapped, first, second, third),
125
- unshift: (...items: Item[]) => state.value.unshift(...items),
126
- update: (callback: never) => updateProxyValue(true, state, setArrayValue, setAtIndex, callback),
127
- };
128
-
129
- Object.defineProperties(instance, {
130
- [NAME_MORA]: {
131
- value: NAME_ARRAY,
132
- },
133
- length: {
134
- enumerable: true,
135
- get: () => length.get(),
136
- set: (value: number) => setArrayLength(state, value),
137
- },
138
- });
139
-
140
- set(value as never);
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);
145
+ }
141
146
 
142
- return Object.freeze(instance) as never;
147
+ function getArrayValues(this: InternalProxy, value?: unknown): unknown {
148
+ return value === PROPERTY_LENGTH
149
+ ? this[SYMBOL_STATE].length!.get()
150
+ : getValueInProxy.call(this, value);
143
151
  }
144
152
 
145
- function peekArrayValue<Item>(
146
- state: ReactiveState<Item[], Item>,
147
- length: Signal<number>,
148
- first?: unknown,
149
- second?: boolean,
150
- ): unknown {
151
- return first === PROPERTY_LENGTH ? length.peek() : peekValueInProxy(true, state, first, second);
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);
152
157
  }
153
158
 
154
- function setArrayLength<Item>(state: ReactiveState<Item[], Item>, value: number): void {
159
+ function setArrayLength(state: ReactiveState, value: number): void {
155
160
  if (
156
161
  typeof value === 'number' &&
157
162
  !Number.isNaN(value) &&
158
163
  value >= 0 &&
159
- value !== state.value.length
164
+ value !== (state.value as unknown[]).length
160
165
  ) {
161
- state.value.length = value;
166
+ (state.value as unknown[]).length = value;
162
167
  }
163
168
  }
164
169
 
165
- function setArrayValue<Value, Item = Value>(state: ReactiveState<Value, Item>, value: Value): void {
166
- (state.value as unknown[]).splice(
167
- 0,
168
- (state.value as unknown[]).length,
169
- ...((value as unknown[]) ?? []),
170
- );
171
- }
172
-
173
- function setAtIndex<Value, Item = Value>(
174
- state: ReactiveState<Value, Item>,
175
- index: unknown,
176
- value: Item,
177
- ): void {
178
- const actual =
179
- (index as number) < 0
180
- ? (state.value as unknown[]).length + (index as number)
181
- : (index as number);
182
-
183
- if (actual > -1) {
184
- (state.value as unknown[])[actual] = value;
185
- }
186
- }
170
+ function updateArray(instance: InternalProxy, type: string, array: unknown[]): unknown {
171
+ const state = instance[SYMBOL_STATE];
187
172
 
188
- function updateArray<Item>(
189
- type: string,
190
- array: Item[],
191
- state: ReactiveState<Item[], Item>,
192
- length: Signal<number>,
193
- ): unknown {
194
173
  const affectsLength = METHODS_AFFECTING_LENGTH.has(type);
195
174
  const previousArray = affectsLength ? [] : array.slice();
196
175
  const previousLength = array.length;
@@ -201,9 +180,9 @@ function updateArray<Item>(
201
180
  if (
202
181
  affectsLength ? array.length !== previousLength : !equalArrays(state, previousArray, array)
203
182
  ) {
204
- emitValue(state);
183
+ emitValue(instance);
205
184
 
206
- length.set(array.length);
185
+ state.length!.set(array.length);
207
186
  }
208
187
 
209
188
  return result;
@@ -1,18 +1,48 @@
1
+ import type {GenericAsyncCallback, GenericCallback} from '@oscarpalmer/atoms/models';
1
2
  import {flushHandlers} from '../batch';
2
3
  import {
3
4
  ACTIVE,
4
5
  BATCH,
5
6
  NAME_COMPUTED,
6
7
  NAME_MORA,
7
- SUBSCRIPTION_TYPES_COPY,
8
8
  SUBSCRIPTION_TYPE_FROZEN,
9
9
  SUBSCRIPTION_TYPES,
10
+ SUBSCRIPTION_TYPES_COPY,
11
+ SYMBOL_EFFECT,
12
+ SYMBOL_STATE,
10
13
  } from '../constants';
11
14
  import {internalEffect, runEffect} from '../effect';
15
+ import {getState} from '../helpers/misc';
12
16
  import {subscribeToSignal} from '../helpers/subscription';
13
- import {handleSimpleValue, peekSimpleValue} from '../helpers/value';
14
- import type {Computed, ComputedEffect, ReactiveOptions, ReactiveState} from '../models';
15
- import {reactive} from './reactive';
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
16
46
 
17
47
  // #region Functions
18
48
 
@@ -31,36 +61,22 @@ export function computed<Value>(
31
61
  }
32
62
 
33
63
  function getComputed<Value>(
34
- callback: () => Value | Promise<Value>,
64
+ callback: GenericCallback,
35
65
  options?: ReactiveOptions<Value>,
36
66
  ): [Computed<Value>, ComputedEffect] {
37
67
  if (typeof callback !== 'function') {
38
68
  throw new TypeError('Computed callback must be a function');
39
69
  }
40
70
 
41
- const [rx, state] = reactive<Value>(undefined as never, options);
42
-
43
- let fx: ComputedEffect;
44
-
45
- const instance = {
46
- ...rx,
47
- get: () => getValue(state, fx),
48
- peek: (copy?: never) => peekSimpleValue(state.value, copy === true),
49
- subscribe: (callback: never, copy?: never) => subscribeToSignal(state, callback, copy === true),
50
- };
71
+ // @ts-expect-error All good, no worries :-)
72
+ const instance = new Computed(callback, options);
51
73
 
52
- Object.defineProperty(instance, NAME_MORA, {
53
- value: NAME_COMPUTED,
54
- });
55
-
56
- fx = getComputedEffect(state, callback);
57
-
58
- return [Object.freeze(instance) as never, fx];
74
+ return [instance as never, instance[SYMBOL_EFFECT]];
59
75
  }
60
76
 
61
- function getComputedEffect<Value>(
62
- state: ReactiveState<Value, Value>,
63
- callback: () => Value | Promise<Value>,
77
+ function getComputedEffect(
78
+ instance: InternalSignal,
79
+ callback: GenericAsyncCallback,
64
80
  ): ComputedEffect {
65
81
  const fx: ComputedEffect = {
66
82
  dirty: true,
@@ -73,7 +89,7 @@ function getComputedEffect<Value>(
73
89
 
74
90
  ACTIVE.computed = fx;
75
91
 
76
- handleSimpleValue(state, callback, setAndEmit, () => {
92
+ handleSignalValue(instance, callback, setAndEmit, () => {
77
93
  ACTIVE.computed = previousComputed;
78
94
  });
79
95
 
@@ -84,12 +100,19 @@ function getComputedEffect<Value>(
84
100
  return fx;
85
101
  }
86
102
 
87
- 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
+
88
107
  if (ACTIVE.computed != null && fx !== ACTIVE.computed) {
108
+ state.computeds ??= new Set();
109
+
89
110
  state.computeds.add(ACTIVE.computed);
90
111
  }
91
112
 
92
113
  if (ACTIVE.effect != null && ACTIVE.effect !== fx.instance) {
114
+ state.effects ??= new Set();
115
+
93
116
  state.effects.add(ACTIVE.effect);
94
117
  }
95
118
 
@@ -100,26 +123,32 @@ function getValue<Value>(state: ReactiveState<Value, Value>, fx: ComputedEffect)
100
123
  return state.value;
101
124
  }
102
125
 
103
- export function internalComputed<Value>(
104
- callback: () => Value | Promise<Value>,
105
- options?: ReactiveOptions<Value>,
106
- ): [Computed<Value>, ComputedEffect] {
126
+ export function internalComputed(
127
+ callback: GenericCallback,
128
+ options?: ReactiveOptions<unknown>,
129
+ ): [Computed<unknown>, ComputedEffect] {
107
130
  return getComputed(callback, options);
108
131
  }
109
132
 
110
- function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): void {
111
- 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)) {
112
137
  return;
113
138
  }
114
139
 
115
140
  state.value = value;
116
141
 
117
- for (const computed of state.computeds) {
118
- computed.dirty = true;
142
+ if (state.computeds != null && state.computeds.size > 0) {
143
+ for (const computed of state.computeds) {
144
+ computed.dirty = true;
145
+ }
119
146
  }
120
147
 
121
- for (const effect of state.effects) {
122
- BATCH.handlers.set(effect, effect);
148
+ if (state.effects != null && state.effects.size > 0) {
149
+ for (const effect of state.effects) {
150
+ BATCH.handlers.set(effect, effect);
151
+ }
123
152
  }
124
153
 
125
154
  for (const type of SUBSCRIPTION_TYPES) {
@@ -127,12 +156,13 @@ function setAndEmit<Value>(state: ReactiveState<Value, Value>, value: Value): vo
127
156
  continue;
128
157
  }
129
158
 
130
- const copy = SUBSCRIPTION_TYPES_COPY.has(type);
131
159
  const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
132
160
 
133
- if (subscriptions != null) {
161
+ if (subscriptions != null && subscriptions.size > 0) {
162
+ const copy = SUBSCRIPTION_TYPES_COPY.has(type);
163
+
134
164
  for (const [, callback] of subscriptions) {
135
- callback(peekSimpleValue(state.value, copy));
165
+ callback(peekSignalValue.call(instance, copy));
136
166
  }
137
167
  }
138
168
  }