@oscarpalmer/mora 0.31.0 → 0.32.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 (54) hide show
  1. package/dist/batch.d.mts +4 -5
  2. package/dist/batch.mjs +4 -2
  3. package/dist/constants.d.mts +29 -19
  4. package/dist/constants.mjs +17 -2
  5. package/dist/effect.d.mts +4 -5
  6. package/dist/effect.mjs +2 -2
  7. package/dist/helpers/is.d.mts +14 -9
  8. package/dist/helpers/is.mjs +6 -0
  9. package/dist/helpers/proxy.d.mts +12 -10
  10. package/dist/helpers/proxy.mjs +32 -14
  11. package/dist/helpers/subscription.d.mts +8 -0
  12. package/dist/helpers/subscription.mjs +27 -0
  13. package/dist/helpers/value.d.mts +8 -6
  14. package/dist/helpers/value.mjs +30 -4
  15. package/dist/index.d.mts +3 -2
  16. package/dist/models.d.mts +45 -87
  17. package/dist/mora.full.mjs +461 -285
  18. package/dist/value/array.d.mts +4 -5
  19. package/dist/value/array.mjs +22 -69
  20. package/dist/value/computed.d.mts +3 -4
  21. package/dist/value/computed.mjs +14 -14
  22. package/dist/value/reactive.d.mts +2 -3
  23. package/dist/value/reactive.mjs +0 -1
  24. package/dist/value/readonly.d.mts +3 -5
  25. package/dist/value/readonly.mjs +15 -19
  26. package/dist/value/signal.d.mts +4 -5
  27. package/dist/value/signal.mjs +9 -22
  28. package/dist/value/store.d.mts +4 -5
  29. package/dist/value/store.mjs +16 -56
  30. package/package.json +6 -6
  31. package/src/batch.ts +15 -5
  32. package/src/constants.ts +32 -2
  33. package/src/effect.ts +6 -2
  34. package/src/helpers/is.ts +10 -0
  35. package/src/helpers/proxy.ts +99 -49
  36. package/src/helpers/subscription.ts +78 -0
  37. package/src/helpers/value.ts +69 -4
  38. package/src/index.ts +1 -1
  39. package/src/models.ts +38 -81
  40. package/src/value/array.ts +61 -150
  41. package/src/value/computed.ts +36 -13
  42. package/src/value/reactive.ts +8 -5
  43. package/src/value/readonly.ts +26 -25
  44. package/src/value/signal.ts +19 -22
  45. package/src/value/store.ts +35 -114
  46. package/types/constants.d.ts +1 -1
  47. package/types/index.d.ts +1 -1
  48. package/types/value/array.d.ts +1 -1
  49. package/types/value/computed.d.ts +0 -3
  50. package/types/value/signal.d.ts +1 -1
  51. package/types/value/store.d.ts +1 -1
  52. package/dist/subscription.d.mts +0 -7
  53. package/dist/subscription.mjs +0 -27
  54. package/src/subscription.ts +0 -45
package/src/effect.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import {ACTIVE, NAME_EFFECT} from './constants';
2
+ import {ACTIVE, NAME_EFFECT, NAME_MORA} from './constants';
3
3
  import type {Effect, EffectState} from './models';
4
4
 
5
+ // #region Functions
6
+
5
7
  /**
6
8
  * Create an effect
7
9
  *
@@ -22,7 +24,7 @@ function getEffect(callback: GenericCallback): [Effect, EffectState] {
22
24
  };
23
25
 
24
26
  const instance = Object.freeze({
25
- $mora: NAME_EFFECT,
27
+ [NAME_MORA]: NAME_EFFECT,
26
28
  });
27
29
 
28
30
  runEffect(state);
@@ -45,3 +47,5 @@ export function runEffect(state: EffectState): void {
45
47
  ACTIVE.effect = previousEffect;
46
48
  }
47
49
  }
50
+
51
+ // #endregion
package/src/helpers/is.ts CHANGED
@@ -19,6 +19,8 @@ import type {
19
19
  Signal,
20
20
  } from '../models';
21
21
 
22
+ // #region Functions
23
+
22
24
  /**
23
25
  * Is the value a computed signal?
24
26
  *
@@ -92,6 +94,14 @@ export function isReactiveStore<Value extends PlainObject>(
92
94
  return isMora<ReactiveStore<Value>>(value, NAME_STORE);
93
95
  }
94
96
 
97
+ /**
98
+ * Is the value a readonly signal?
99
+ *
100
+ * @param value Value to check
101
+ * @returns True if value is a {@link ReadonlySignal}
102
+ */
95
103
  export function isReadonlySignal<Value>(value: unknown): value is ReadonlySignal<Value> {
96
104
  return isMora<ReadonlySignal<Value>>(value, NAME_READONLY);
97
105
  }
106
+
107
+ // #endregion
@@ -1,3 +1,4 @@
1
+ import {isKey, isPlainObject} from '@oscarpalmer/atoms/is';
1
2
  import type {ArrayOrPlainObject, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
3
  import {PROPERTY_LENGTH} from '../constants';
3
4
  import type {
@@ -6,23 +7,30 @@ import type {
6
7
  ReactiveArray,
7
8
  ReactiveState,
8
9
  ReactiveStore,
9
- SetValueInProxyParameters,
10
+ Signal,
10
11
  } from '../models';
11
12
  import {internalComputed} from '../value/computed';
12
- import {emitValue} from './value';
13
+ import {emitValue, getSimpleValue, peekSimpleValue} from './value';
13
14
 
14
- export function emityProxyValues<Value>(
15
- state: ReactiveState<Value, Value>,
16
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
17
- ): void;
15
+ // #region Types
18
16
 
19
- export function emityProxyValues<Value>(
20
- state: ReactiveState<Value[], Value>,
21
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
22
- ): void;
17
+ type SetObjectCallback<Value, Item> = (
18
+ state: ReactiveState<Value, Item>,
19
+ value: Value | undefined,
20
+ ) => void;
21
+
22
+ type SetPropertyCallback<Value, Item> = (
23
+ state: ReactiveState<Value, Item>,
24
+ property: unknown,
25
+ value: Item,
26
+ ) => void;
27
+
28
+ // #endregion
23
29
 
24
- export function emityProxyValues(
25
- state: ReactiveState<unknown, unknown>,
30
+ // #region Functions
31
+
32
+ export function emitProxyValues<Value, Item = Value>(
33
+ state: ReactiveState<Value, Item>,
26
34
  mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
27
35
  ): void {
28
36
  const values = [...mapped.values()];
@@ -35,27 +43,15 @@ export function emityProxyValues(
35
43
  emitValue(state);
36
44
  }
37
45
 
38
- export function getReactiveValueInProxy<Value, Result = Value>(
39
- array: ReactiveArray<Value>,
40
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
41
- index: number,
42
- isArray: true,
43
- ): Computed<Result>;
44
-
45
- export function getReactiveValueInProxy<Value extends PlainObject>(
46
- store: ReactiveStore<Value>,
47
- mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
48
- key: Key,
49
- isArray: false,
50
- ): Computed<unknown>;
51
-
52
- export function getReactiveValueInProxy(
53
- reactive: ReactiveArray<unknown> | ReactiveStore<PlainObject>,
46
+ export function getReactiveValueInProxy<Value>(
47
+ reactive: ReactiveArray<Value> | ReactiveStore<Value>,
54
48
  mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
55
49
  key: Key,
56
50
  isArray: boolean,
57
51
  ): Computed<unknown> {
58
- let item = mapped.get(key);
52
+ const mapKey = String(key);
53
+
54
+ let item = mapped.get(mapKey);
59
55
 
60
56
  if (item == null) {
61
57
  item = internalComputed(() =>
@@ -64,37 +60,72 @@ export function getReactiveValueInProxy(
64
60
  : (reactive.get() as PlainObject)[key],
65
61
  );
66
62
 
67
- mapped.set(key, item);
63
+ mapped.set(mapKey, item);
68
64
  }
69
65
 
70
66
  return item[0];
71
67
  }
72
68
 
69
+ export function getValueInProxy<Value, Item = Value>(
70
+ isArray: boolean,
71
+ instance: ReactiveArray<Value> | ReactiveStore<Value>,
72
+ state: ReactiveState<Value, Item>,
73
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
74
+ first?: unknown,
75
+ ): unknown {
76
+ if (isArray ? typeof first === 'number' : isKey(first)) {
77
+ return getReactiveValueInProxy(instance, mapped, first as Key, isArray).get();
78
+ }
79
+
80
+ return getSimpleValue(state);
81
+ }
82
+
83
+ function isProxyObject(isArray: boolean, value: unknown): value is ArrayOrPlainObject {
84
+ return value == null || (isArray ? Array.isArray(value) : isPlainObject(value));
85
+ }
86
+
87
+ export function peekValueInProxy<Value, Item = Value>(
88
+ isArray: boolean,
89
+ state: ReactiveState<Value, Item>,
90
+ first?: unknown,
91
+ second?: boolean,
92
+ ): unknown {
93
+ let value: unknown;
94
+
95
+ if (isArray ? typeof first === 'number' : isKey(first)) {
96
+ value = isArray
97
+ ? (state.value as unknown[]).at(first as number)
98
+ : (state.value as PlainObject)[first as Key];
99
+ } else {
100
+ value = state.value;
101
+ }
102
+
103
+ return peekSimpleValue(value, first === true || second === true);
104
+ }
105
+
73
106
  export function setProxyValue<Value, Item = Value>(
74
- array: boolean,
107
+ isArray: boolean,
75
108
  state: ReactiveState<Value, Item>,
76
- isObject: (value: unknown) => value is Value | undefined,
77
- isProperty: (value: unknown) => boolean,
78
- setObject: (state: ReactiveState<Value, Item>, value: Value | undefined) => void,
79
- setProperty: (state: ReactiveState<Value, Item>, property: unknown, value: Item) => void,
109
+ setObject: SetObjectCallback<Value, Item>,
110
+ setProperty: SetPropertyCallback<Value, Item>,
80
111
  first?: unknown,
81
112
  second?: unknown,
82
113
  ): void {
83
- if (array && first === PROPERTY_LENGTH) {
114
+ if (isArray && first === PROPERTY_LENGTH) {
84
115
  (state.value as unknown[]).length = second as number;
85
116
 
86
117
  return;
87
118
  }
88
119
 
89
- if (isObject(first)) {
90
- setObject(state, first);
120
+ if (isProxyObject(isArray, first)) {
121
+ setObject(state, first as Value);
91
122
 
92
123
  return;
93
124
  }
94
125
 
95
- const property = isProperty(first);
126
+ const property = isArray ? typeof first === 'number' : isKey(first);
96
127
 
97
- if (array && property && Number.isNaN(first)) {
128
+ if (isArray && property && Number.isNaN(first)) {
98
129
  return;
99
130
  }
100
131
 
@@ -127,10 +158,10 @@ export function setProxyValue<Value, Item = Value>(
127
158
  return;
128
159
  }
129
160
 
130
- if (!property && isObject(value) && state.promise === actual) {
161
+ if (!property && isProxyObject(isArray, value) && state.promise === actual) {
131
162
  state.promise = undefined;
132
163
 
133
- setObject(state, value);
164
+ setObject(state, value as Value);
134
165
  }
135
166
  })
136
167
  .catch(() => {
@@ -142,16 +173,19 @@ export function setProxyValue<Value, Item = Value>(
142
173
  });
143
174
  } else if (property) {
144
175
  setProperty(state, first, actual as Item);
145
- } else if (isObject(actual)) {
146
- setObject(state, actual);
176
+ } else if (isProxyObject(isArray, actual)) {
177
+ setObject(state, actual as Value);
147
178
  }
148
179
  }
149
180
 
150
- export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
151
- parameters: SetValueInProxyParameters<Value, Equal>,
181
+ export function setValueInProxy<Value, Item = Value>(
182
+ isArray: boolean,
183
+ state: ReactiveState<Value, Item>,
184
+ target: object,
185
+ property: PropertyKey,
186
+ value: unknown,
187
+ length?: Signal<number>,
152
188
  ): boolean {
153
- const {isArray, length, property, state, target, value} = parameters;
154
-
155
189
  if (isArray) {
156
190
  const isIndex = !Number.isNaN(Number(property));
157
191
  const isLength = property === PROPERTY_LENGTH;
@@ -161,7 +195,7 @@ export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
161
195
  }
162
196
  }
163
197
 
164
- const previous = Reflect.get(target, property);
198
+ const previous = Reflect.get(target as object, property);
165
199
 
166
200
  if (!state.equal(previous as never, value as never)) {
167
201
  Reflect.set(target, property, value);
@@ -175,3 +209,19 @@ export function setValueInProxy<Value extends ArrayOrPlainObject, Equal>(
175
209
 
176
210
  return true;
177
211
  }
212
+
213
+ export function updateProxyValue<Value, Item = Value>(
214
+ isArray: boolean,
215
+ state: ReactiveState<Value, Item>,
216
+ setObject: SetObjectCallback<Value, Item>,
217
+ setProperty: SetPropertyCallback<Value, Item>,
218
+ callback: unknown,
219
+ ): void {
220
+ if (typeof callback !== 'function') {
221
+ throw new TypeError('Callback must be a function');
222
+ }
223
+
224
+ setProxyValue(isArray, state, setObject, setProperty, callback(state.value));
225
+ }
226
+
227
+ // #endregion
@@ -0,0 +1,78 @@
1
+ import {isKey} from '@oscarpalmer/atoms/is';
2
+ import type {Key} from '@oscarpalmer/atoms/models';
3
+ import {subscriptions, type Subscription} from '@oscarpalmer/atoms/subscription';
4
+ import {
5
+ SUBSCRIPTION_PROPERTY,
6
+ SUBSCRIPTION_TYPE_COPY,
7
+ SUBSCRIPTION_TYPE_FROZEN,
8
+ SUBSCRIPTION_TYPE_ORIGINAL,
9
+ SUBSCRIPTION_TYPES,
10
+ SUBSCRIPTION_TYPES_COPY,
11
+ } from '../constants';
12
+ import type {
13
+ Computed,
14
+ ComputedEffect,
15
+ ReactiveArray,
16
+ ReactiveState,
17
+ ReactiveStore,
18
+ SubscriptionType,
19
+ } from '../models';
20
+ import {getReactiveValueInProxy} from './proxy';
21
+ import {getFrozenValue, peekSimpleValue} from './value';
22
+
23
+ export function subscribeToProxy<Value, Item = Value>(
24
+ isArray: boolean,
25
+ instance: ReactiveArray<Value> | ReactiveStore<Value>,
26
+ state: ReactiveState<Value, Item>,
27
+ mapped: Map<Key, [Computed<unknown>, ComputedEffect]>,
28
+ first: Key | ((value: unknown) => unknown),
29
+ second?: ((value: unknown) => unknown) | boolean,
30
+ third?: boolean,
31
+ ): Subscription {
32
+ if (isKey(first)) {
33
+ return getReactiveValueInProxy(instance as never, mapped, first, isArray).subscribe(
34
+ second as never,
35
+ third as never,
36
+ );
37
+ }
38
+
39
+ return subscribeToSignal(state, first, second === true);
40
+ }
41
+
42
+ export function subscribeToReactive<Value, Item = Value>(
43
+ type: SubscriptionType,
44
+ state: ReactiveState<Value, Item>,
45
+ callback: (value: unknown) => unknown,
46
+ ): Subscription {
47
+ state.subscriptions ??= subscriptions({
48
+ keys: SUBSCRIPTION_TYPES,
49
+ property: SUBSCRIPTION_PROPERTY,
50
+ });
51
+
52
+ const [subscription, existing] = state.subscriptions.create({
53
+ key: type,
54
+ value: callback,
55
+ });
56
+
57
+ if (!existing) {
58
+ callback(
59
+ type === SUBSCRIPTION_TYPE_FROZEN
60
+ ? getFrozenValue(state.value)
61
+ : peekSimpleValue(state.value, SUBSCRIPTION_TYPES_COPY.has(type)),
62
+ );
63
+ }
64
+
65
+ return subscription;
66
+ }
67
+
68
+ export function subscribeToSignal<Value, Item = Value>(
69
+ state: ReactiveState<Value, Item>,
70
+ callback: (value: unknown) => unknown,
71
+ copy: boolean,
72
+ ): Subscription {
73
+ return subscribeToReactive(
74
+ copy ? SUBSCRIPTION_TYPE_COPY : SUBSCRIPTION_TYPE_ORIGINAL,
75
+ state,
76
+ callback,
77
+ );
78
+ }
@@ -1,18 +1,41 @@
1
+ import {isPlainObject} from '@oscarpalmer/atoms/is';
1
2
  import {flushHandlers} from '../batch';
2
- import {ACTIVE, ARRAY_OFFSET, ARRAY_PEEK, ARRAY_THRESHOLD, BATCH} from '../constants';
3
+ import {
4
+ ACTIVE,
5
+ ARRAY_OFFSET,
6
+ ARRAY_PEEK,
7
+ ARRAY_THRESHOLD,
8
+ BATCH,
9
+ SUBSCRIPTION_TYPES_COPY,
10
+ SUBSCRIPTION_TYPE_FROZEN,
11
+ SUBSCRIPTION_TYPES,
12
+ } from '../constants';
3
13
  import type {ReactiveState} from '../models';
4
14
 
15
+ // #region Functions
16
+
5
17
  export function emitValue<Value>(state: ReactiveState<Value, never>): void {
6
18
  for (const computed of state.computeds) {
7
19
  computed.dirty = true;
8
20
  }
9
21
 
10
22
  for (const effect of state.effects) {
11
- BATCH.handlers.add(effect);
23
+ BATCH.handlers.set(effect, effect);
12
24
  }
13
25
 
14
- for (const [, subscription] of state.subscriptions) {
15
- BATCH.handlers.add(subscription);
26
+ for (const type of SUBSCRIPTION_TYPES) {
27
+ const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
28
+
29
+ if (subscriptions != null) {
30
+ for (const [subscription, callback] of subscriptions) {
31
+ BATCH.handlers.set(subscription, {
32
+ callback,
33
+ state,
34
+ frozen: type === SUBSCRIPTION_TYPE_FROZEN,
35
+ copy: SUBSCRIPTION_TYPES_COPY.has(type),
36
+ });
37
+ }
38
+ }
16
39
  }
17
40
 
18
41
  if (BATCH.depth === 0) {
@@ -55,6 +78,18 @@ export function equalArrays<Value>(
55
78
  return true;
56
79
  }
57
80
 
81
+ export function getFrozenValue(value: unknown): unknown {
82
+ let frozen = value;
83
+
84
+ if (Array.isArray(frozen)) {
85
+ frozen = Object.freeze(frozen.slice());
86
+ } else if (isPlainObject(frozen)) {
87
+ frozen = Object.freeze({...frozen});
88
+ }
89
+
90
+ return frozen;
91
+ }
92
+
58
93
  export function getSimpleValue<Value>(state: ReactiveState<Value, never>): Value {
59
94
  if (ACTIVE.computed != null) {
60
95
  state.computeds.add(ACTIVE.computed);
@@ -101,3 +136,33 @@ export function handleSimpleValue<Value>(
101
136
  onAfter?.();
102
137
  }
103
138
  }
139
+
140
+ export function peekSimpleValue(value: unknown, copy: boolean): unknown {
141
+ let peeked: unknown = value;
142
+
143
+ if (!copy) {
144
+ return peeked;
145
+ }
146
+
147
+ if (Array.isArray(peeked)) {
148
+ peeked = peeked.slice();
149
+ } else if (isPlainObject(peeked)) {
150
+ peeked = {...peeked};
151
+ }
152
+
153
+ return peeked;
154
+ }
155
+
156
+ export function updateSimpleValue<Value>(
157
+ state: ReactiveState<Value, Value>,
158
+ callback: (value: Value) => Value,
159
+ setValue: (state: ReactiveState<Value, Value>, value: Value) => void,
160
+ ): void {
161
+ if (typeof callback !== 'function') {
162
+ throw new TypeError('Callback must be a function');
163
+ }
164
+
165
+ handleSimpleValue(state, callback(state.value), setValue);
166
+ }
167
+
168
+ // #endregion
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export type {Subscription} from '@oscarpalmer/atoms/subscription';
1
2
  export {startBatch, stopBatch} from './batch';
2
3
  export {effect} from './effect';
3
4
  export {
@@ -16,7 +17,6 @@ export type {
16
17
  ReactiveStore,
17
18
  ReadonlySignal,
18
19
  Signal,
19
- Unsubscribe,
20
20
  } from './models';
21
21
  export {array} from './value/array';
22
22
  export {computed} from './value/computed';
package/src/models.ts CHANGED
@@ -1,6 +1,9 @@
1
1
  import type {GenericCallback, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
+ import type {Subscription, Subscriptions} from '@oscarpalmer/atoms/subscription';
2
3
  import type {PROPERTY_LENGTH} from './constants';
3
4
 
5
+ // #region Types
6
+
4
7
  export type Active = {
5
8
  computed?: ComputedEffect;
6
9
  effect?: EffectState;
@@ -9,7 +12,7 @@ export type Active = {
9
12
  export type Batch = {
10
13
  depth: number;
11
14
  flushing: boolean;
12
- handlers: Set<EffectState | Subscription>;
15
+ handlers: Map<EffectState | Subscription, EffectState | StoredSubscription>;
13
16
  };
14
17
 
15
18
  export type Computed<Value> = Reactive<Value> & SimpleReactive<Value>;
@@ -228,18 +231,24 @@ export type ReactiveArray<Item> = {
228
231
  * Subscribe to changes
229
232
  *
230
233
  * @param callback Callback for changes
231
- * @returns Unsubscribe callback
234
+ * @param copy Copy the array? _(defaults to `false`)_
235
+ * @returns Subscription
232
236
  */
233
- subscribe(callback: (value: Item[]) => void): Unsubscribe;
237
+ subscribe(callback: (value: Item[]) => void, copy?: boolean): Subscription;
234
238
 
235
239
  /**
236
240
  * Subscribe to changes at a specific index
237
241
  *
238
242
  * @param index Index of item to subscribe to
239
243
  * @param callback Callback for changes
240
- * @returns Unsubscribe callback
244
+ * @param copy If the item is an array or record, should it be copied? _(defaults to `false`)_
245
+ * @returns Subscription
241
246
  */
242
- subscribe(index: number, callback: (value: Item | undefined) => void): Unsubscribe;
247
+ subscribe(
248
+ index: number,
249
+ callback: (value: Item | undefined) => void,
250
+ copy?: boolean,
251
+ ): Subscription;
243
252
 
244
253
  /**
245
254
  * Add items to the beginning of the array
@@ -248,21 +257,6 @@ export type ReactiveArray<Item> = {
248
257
  */
249
258
  unshift(...items: Item[]): number;
250
259
 
251
- /**
252
- * Unsubscribe from changes for a specific index
253
- *
254
- * @param index Index of the value to unsubscribe from
255
- * @param callback Callback to unsubscribe
256
- */
257
- unsubscribe(index: number, callback: (item: Item | undefined) => void): void;
258
-
259
- /**
260
- * Unsubscribe from changes
261
- *
262
- * @param callback Callback to unsubscribe
263
- */
264
- unsubscribe(callback: (array: Item[]) => void): void;
265
-
266
260
  /**
267
261
  * Update the value _(based on the current value)_
268
262
  *
@@ -282,13 +276,13 @@ export type ReactiveOptions<Value> = {
282
276
  equal?: (first: Value, second: Value) => boolean;
283
277
  };
284
278
 
285
- export type ReactiveState<Value, Equal> = {
279
+ export type ReactiveState<Value, Item = Value> = {
286
280
  computeds: Set<ComputedEffect>;
287
281
  effects: Set<EffectState>;
288
- equal: (first: Equal, second: Equal) => boolean;
282
+ equal: (first: Item, second: Item) => boolean;
289
283
  promise?: Promise<Value>;
290
284
  promises?: Map<Key, Promise<never>>;
291
- subscriptions: Map<GenericCallback, Subscription>;
285
+ subscriptions?: Subscriptions<GenericCallback>;
292
286
  value: Value;
293
287
  };
294
288
 
@@ -403,56 +397,34 @@ export type ReactiveStore<Store> = {
403
397
  * Subscribe to changes
404
398
  *
405
399
  * @param callback Callback for changes
406
- * @returns Unsubscribe callback
400
+ * @param copy Copy the store? _(defaults to `false`)_
401
+ * @returns Subscription
407
402
  */
408
- subscribe(callback: (value: Store) => void): Unsubscribe;
403
+ subscribe(callback: (value: Store) => void, copy?: boolean): Subscription;
409
404
 
410
405
  /**
411
406
  * Subscribe to changes for a specific key
412
407
  *
413
408
  * @param key Key of the value to subscribe to
414
409
  * @param callback Callback for changes
415
- * @returns Unsubscribe callback
410
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
411
+ * @returns Subscription
416
412
  */
417
413
  subscribe<Key extends keyof Store>(
418
414
  key: Key,
419
415
  callback: (value: Store[Key] | undefined) => void,
420
- ): Unsubscribe;
416
+ copy?: boolean,
417
+ ): Subscription;
421
418
 
422
419
  /**
423
420
  * Subscribe to changes for a specific key
424
421
  *
425
422
  * @param key Key of the value to subscribe to
426
423
  * @param callback Callback for changes
427
- * @returns Unsubscribe callback
428
- */
429
- subscribe(key: Key, callback: (value: unknown) => void): Unsubscribe;
430
-
431
- /**
432
- * Unsubscribe from changes for a specific key
433
- *
434
- * @param key Key of the value to unsubscribe from
435
- * @param callback Callback to unsubscribe
436
- */
437
- unsubscribe<Key extends keyof Store>(
438
- key: Key,
439
- callback: (value: Store[Key] | undefined) => void,
440
- ): void;
441
-
442
- /**
443
- * Unsubscribe from changes for a specific key
444
- *
445
- * @param key Key of the value to unsubscribe from
446
- * @param callback Callback to unsubscribe
447
- */
448
- unsubscribe(key: Key, callback: (value: unknown | undefined) => void): void;
449
-
450
- /**
451
- * Unsubscribe from changes
452
- *
453
- * @param callback Callback to unsubscribe
424
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
425
+ * @returns Subscription
454
426
  */
455
- unsubscribe(callback: (value: Store) => void): void;
427
+ subscribe(key: Key, callback: (value: unknown) => void, copy?: boolean): Subscription;
456
428
 
457
429
  /**
458
430
  * Update the value _(based on the current value)_
@@ -483,15 +455,6 @@ export type ReadonlySignalValue<Value> = Value extends unknown[]
483
455
  ? Readonly<Value>
484
456
  : Value;
485
457
 
486
- export type SetValueInProxyParameters<Value, Equal> = {
487
- target: Value;
488
- property: PropertyKey;
489
- value: unknown;
490
- state: ReactiveState<Value, Equal>;
491
- isArray: boolean;
492
- length?: Signal<number>;
493
- };
494
-
495
458
  export type Signal<Value> = {
496
459
  /**
497
460
  * Get a readonly version of the signal
@@ -538,34 +501,28 @@ type SimpleReactive<Value> = {
538
501
  /**
539
502
  * Get the value _(without reactivity)_
540
503
  *
504
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
541
505
  * @returns Current value
542
506
  */
543
- peek(): Value;
507
+ peek(copy?: boolean): Value;
544
508
 
545
509
  /**
546
510
  * Subscribe to changes
547
511
  *
548
512
  * @param callback Callback for changes
549
- * @returns Unsubscribe callback
550
- */
551
- subscribe(callback: (value: Value) => void): Unsubscribe;
552
-
553
- /**
554
- * Unsubscribe from changes
555
- *
556
- * @param callback Callback to unsubscribe
513
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
514
+ * @returns Subscription
557
515
  */
558
- unsubscribe(callback: (value: Value) => void): void;
516
+ subscribe(callback: (value: Value) => void, copy?: boolean): Subscription;
559
517
  };
560
518
 
561
- export type Subscription = {
519
+ export type StoredSubscription = {
562
520
  callback: GenericCallback;
521
+ copy: boolean;
522
+ frozen: boolean;
563
523
  state: ReactiveState<unknown, never>;
564
-
565
- destroy(): void;
566
524
  };
567
525
 
568
- /**
569
- * Unsubscribe from changes
570
- */
571
- export type Unsubscribe = () => void;
526
+ export type SubscriptionType = 'copy' | 'frozen' | 'original' | 'readonly';
527
+
528
+ // #endregion