@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
@@ -0,0 +1,89 @@
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_TYPE_READONLY,
10
+ SUBSCRIPTION_TYPES,
11
+ SUBSCRIPTION_TYPES_COPY,
12
+ SYMBOL_STATE,
13
+ } from '../constants';
14
+ import type {
15
+ InternalProxy,
16
+ InternalReadonly,
17
+ InternalSignal,
18
+ Subscriber,
19
+ SubscriptionType,
20
+ } from '../models';
21
+ import {getReactiveValueInProxy} from './proxy';
22
+ import {getFrozenValue, peekSignalValue} from './value';
23
+
24
+ // #region Functions
25
+
26
+ export function subscribeToProxy(
27
+ this: InternalProxy,
28
+ first: Key | Subscriber<unknown>,
29
+ second?: Subscriber<unknown> | boolean,
30
+ third?: boolean,
31
+ ): Subscription {
32
+ if (isKey(first)) {
33
+ return getReactiveValueInProxy(this as never, first).subscribe(second as never, third as never);
34
+ }
35
+
36
+ return subscribeToSignal.call(this, first, second);
37
+ }
38
+
39
+ function subscribeToReactive(
40
+ this: InternalSignal,
41
+ type: SubscriptionType,
42
+ subscriber: Subscriber<unknown>,
43
+ ): Subscription {
44
+ this[SYMBOL_STATE].subscriptions ??= subscriptions({
45
+ keys: SUBSCRIPTION_TYPES,
46
+ property: SUBSCRIPTION_PROPERTY,
47
+ });
48
+
49
+ const [subscription, existing] = this[SYMBOL_STATE].subscriptions.create({
50
+ key: type,
51
+ value: subscriber,
52
+ });
53
+
54
+ if (!existing) {
55
+ subscriber(
56
+ type === SUBSCRIPTION_TYPE_FROZEN
57
+ ? getFrozenValue(this[SYMBOL_STATE].value)
58
+ : peekSignalValue.call(this, SUBSCRIPTION_TYPES_COPY.has(type)),
59
+ subscription,
60
+ );
61
+ }
62
+
63
+ return subscription;
64
+ }
65
+
66
+ export function subscribeToReadonly(
67
+ this: InternalReadonly,
68
+ subscriber: Subscriber<unknown>,
69
+ ): Subscription {
70
+ return subscribeToReactive.call(
71
+ this,
72
+ this.frozen ? SUBSCRIPTION_TYPE_FROZEN : SUBSCRIPTION_TYPE_READONLY,
73
+ subscriber,
74
+ );
75
+ }
76
+
77
+ export function subscribeToSignal(
78
+ this: InternalSignal,
79
+ subscriber: Subscriber<unknown>,
80
+ copy?: unknown,
81
+ ): Subscription {
82
+ return subscribeToReactive.call(
83
+ this,
84
+ copy === true ? SUBSCRIPTION_TYPE_COPY : SUBSCRIPTION_TYPE_ORIGINAL,
85
+ subscriber,
86
+ );
87
+ }
88
+
89
+ // #endregion
@@ -1,18 +1,49 @@
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 type {ReactiveState} from '../models';
4
-
5
- export function emitValue<Value>(state: ReactiveState<Value, never>): void {
6
- for (const computed of state.computeds) {
7
- computed.dirty = true;
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
+ SYMBOL_STATE,
13
+ } from '../constants';
14
+ import type {ReactiveState, InternalSignal} from '../models';
15
+
16
+ // #region Functions
17
+
18
+ export function emitValue(instance: InternalSignal): void {
19
+ const state = instance[SYMBOL_STATE];
20
+
21
+ if (state.computeds != null && state.computeds.size > 0) {
22
+ for (const computed of state.computeds) {
23
+ computed.dirty = true;
24
+ }
8
25
  }
9
26
 
10
- for (const effect of state.effects) {
11
- BATCH.handlers.add(effect);
27
+ if (state.effects != null && state.effects.size > 0) {
28
+ for (const effect of state.effects) {
29
+ BATCH.handlers.set(effect, effect);
30
+ }
12
31
  }
13
32
 
14
- for (const [, subscription] of state.subscriptions) {
15
- BATCH.handlers.add(subscription);
33
+ for (const type of SUBSCRIPTION_TYPES) {
34
+ const subscriptions = state.subscriptions?.values.to.keyed?.get(type);
35
+
36
+ if (subscriptions != null && subscriptions.size > 0) {
37
+ for (const [subscription, callback] of subscriptions) {
38
+ BATCH.handlers.set(subscription, {
39
+ callback,
40
+ instance,
41
+ state,
42
+ frozen: type === SUBSCRIPTION_TYPE_FROZEN,
43
+ copy: SUBSCRIPTION_TYPES_COPY.has(type),
44
+ });
45
+ }
46
+ }
16
47
  }
17
48
 
18
49
  if (BATCH.depth === 0) {
@@ -20,17 +51,15 @@ export function emitValue<Value>(state: ReactiveState<Value, never>): void {
20
51
  }
21
52
  }
22
53
 
23
- export function equalArrays<Value>(
24
- state: ReactiveState<Value[], Value>,
25
- first: Value[],
26
- second: Value[],
27
- ): boolean {
28
- let {length} = first;
54
+ export function equalArrays(state: ReactiveState, first: unknown[], second: unknown[]): boolean {
55
+ const {length} = first;
29
56
 
30
57
  if (length !== second.length) {
31
58
  return false;
32
59
  }
33
60
 
61
+ const eq = state.equal ?? Object.is;
62
+
34
63
  let offset = 0;
35
64
 
36
65
  if (length >= ARRAY_THRESHOLD) {
@@ -38,16 +67,21 @@ export function equalArrays<Value>(
38
67
  offset = offset > ARRAY_OFFSET ? ARRAY_OFFSET : offset;
39
68
 
40
69
  for (let index = 0; index < offset; index += 1) {
41
- if (!state.equal(first[index], second[index])) {
70
+ if (
71
+ !(
72
+ eq(first[index], second[index]) &&
73
+ eq(first[length - index - 1], second[length - index - 1])
74
+ )
75
+ ) {
42
76
  return false;
43
77
  }
44
78
  }
45
79
  }
46
80
 
47
- length -= offset;
81
+ const end = length - offset;
48
82
 
49
- for (let index = offset; index < length; index += 1) {
50
- if (!state.equal(first[index], second[index])) {
83
+ for (let index = offset; index < end; index += 1) {
84
+ if (!eq(first[index], second[index])) {
51
85
  return false;
52
86
  }
53
87
  }
@@ -55,24 +89,48 @@ export function equalArrays<Value>(
55
89
  return true;
56
90
  }
57
91
 
58
- export function getSimpleValue<Value>(state: ReactiveState<Value, never>): Value {
92
+ export function getFrozenValue(value: unknown): unknown {
93
+ let frozen = value;
94
+
95
+ if (Array.isArray(frozen)) {
96
+ frozen = Object.freeze(frozen.slice());
97
+ } else if (isPlainObject(frozen)) {
98
+ frozen = Object.freeze({...frozen});
99
+ }
100
+
101
+ return frozen;
102
+ }
103
+
104
+ export function getSignalValue(this: InternalSignal): unknown {
59
105
  if (ACTIVE.computed != null) {
60
- state.computeds.add(ACTIVE.computed);
106
+ this[SYMBOL_STATE].computeds ??= new Set();
107
+
108
+ this[SYMBOL_STATE].computeds.add(ACTIVE.computed);
61
109
  }
62
110
 
63
111
  if (ACTIVE.effect != null) {
64
- state.effects.add(ACTIVE.effect);
112
+ this[SYMBOL_STATE].effects ??= new Set();
113
+
114
+ this[SYMBOL_STATE].effects.add(ACTIVE.effect);
65
115
  }
66
116
 
67
- return state.value;
117
+ return this[SYMBOL_STATE].value;
118
+ }
119
+
120
+ export function getStringValue(this: InternalSignal, json?: boolean): string {
121
+ return json === true
122
+ ? JSON.stringify(this[SYMBOL_STATE].value)
123
+ : String(this[SYMBOL_STATE].value);
68
124
  }
69
125
 
70
- export function handleSimpleValue<Value>(
71
- state: ReactiveState<Value, Value>,
72
- origin: Value | (() => Value | Promise<Value>) | Promise<Value>,
73
- setValue: (state: ReactiveState<Value, Value>, value: Value) => void,
126
+ export function handleSignalValue(
127
+ instance: InternalSignal,
128
+ origin: unknown,
129
+ setValue: (instance: InternalSignal, value: unknown) => void,
74
130
  onAfter?: () => void,
75
131
  ): void {
132
+ const state = instance[SYMBOL_STATE];
133
+
76
134
  try {
77
135
  let actual = typeof origin === 'function' ? (origin as () => unknown)() : origin;
78
136
 
@@ -84,7 +142,7 @@ export function handleSimpleValue<Value>(
84
142
  if (actual === state.promise) {
85
143
  state.promise = undefined;
86
144
 
87
- setValue(state, value);
145
+ setValue(instance, value);
88
146
  }
89
147
  })
90
148
  .catch(() => {
@@ -93,7 +151,7 @@ export function handleSimpleValue<Value>(
93
151
  }
94
152
  });
95
153
  } else {
96
- setValue(state, actual as Value);
154
+ setValue(instance, actual);
97
155
  }
98
156
  } catch {
99
157
  // ?
@@ -101,3 +159,36 @@ export function handleSimpleValue<Value>(
101
159
  onAfter?.();
102
160
  }
103
161
  }
162
+
163
+ export function peekSignalValue(
164
+ this: InternalSignal | [InternalSignal, unknown],
165
+ copy?: boolean,
166
+ ): unknown {
167
+ let peeked: unknown = Array.isArray(this) ? this[1] : this[SYMBOL_STATE].value;
168
+
169
+ if (copy !== true) {
170
+ return peeked;
171
+ }
172
+
173
+ if (Array.isArray(peeked)) {
174
+ peeked = peeked.slice();
175
+ } else if (isPlainObject(peeked)) {
176
+ peeked = {...peeked};
177
+ }
178
+
179
+ return peeked;
180
+ }
181
+
182
+ export function updateSignalValue(
183
+ instance: InternalSignal,
184
+ callback: (value: unknown) => unknown,
185
+ setValue: (instance: InternalSignal, value: unknown) => void,
186
+ ): void {
187
+ if (typeof callback !== 'function') {
188
+ throw new TypeError('Callback must be a function');
189
+ }
190
+
191
+ handleSignalValue(instance, callback(instance[SYMBOL_STATE].value), setValue);
192
+ }
193
+
194
+ // #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,5 +1,8 @@
1
1
  import type {GenericCallback, Key, PlainObject} from '@oscarpalmer/atoms/models';
2
- import type {PROPERTY_LENGTH} from './constants';
2
+ import type {Subscription, Subscriptions} from '@oscarpalmer/atoms/subscription';
3
+ import {SYMBOL_EFFECT, SYMBOL_STATE, type PROPERTY_LENGTH} from './constants';
4
+
5
+ // #region Types
3
6
 
4
7
  export type Active = {
5
8
  computed?: ComputedEffect;
@@ -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>;
@@ -25,20 +28,37 @@ export type EffectState = {
25
28
  callback: GenericCallback;
26
29
  };
27
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
+
28
47
  export type Reactive<Value> = {
29
48
  /**
30
- * JSON representation of the value
49
+ * _JSON_ representation of the value
31
50
  *
32
- * @returns JSON value
51
+ * @returns _JSON_ value
33
52
  */
34
53
  toJSON(): Value;
35
54
 
36
55
  /**
37
56
  * String representation of the value
38
57
  *
58
+ * @param json When `true`, returns the _JSON_ string representation of the value _(defaults to `false`)_
39
59
  * @returns Value as string
40
60
  */
41
- toString(): string;
61
+ toString(json?: boolean): string;
42
62
  };
43
63
 
44
64
  export type ReactiveArray<Item> = {
@@ -227,19 +247,21 @@ export type ReactiveArray<Item> = {
227
247
  /**
228
248
  * Subscribe to changes
229
249
  *
230
- * @param callback Callback for changes
231
- * @returns Unsubscribe callback
250
+ * @param subscriber Callback for changes
251
+ * @param copy Copy the array? _(defaults to `false`)_
252
+ * @returns Subscription
232
253
  */
233
- subscribe(callback: (value: Item[]) => void): Unsubscribe;
254
+ subscribe(subscriber: Subscriber<Item[]>, copy?: boolean): Subscription;
234
255
 
235
256
  /**
236
257
  * Subscribe to changes at a specific index
237
258
  *
238
259
  * @param index Index of item to subscribe to
239
- * @param callback Callback for changes
240
- * @returns Unsubscribe callback
260
+ * @param subscriber Callback for changes
261
+ * @param copy If the item is an array or record, should it be copied? _(defaults to `false`)_
262
+ * @returns Subscription
241
263
  */
242
- subscribe(index: number, callback: (value: Item | undefined) => void): Unsubscribe;
264
+ subscribe(index: number, subscriber: Subscriber<Item | undefined>, copy?: boolean): Subscription;
243
265
 
244
266
  /**
245
267
  * Add items to the beginning of the array
@@ -248,21 +270,6 @@ export type ReactiveArray<Item> = {
248
270
  */
249
271
  unshift(...items: Item[]): number;
250
272
 
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
273
  /**
267
274
  * Update the value _(based on the current value)_
268
275
  *
@@ -282,14 +289,20 @@ export type ReactiveOptions<Value> = {
282
289
  equal?: (first: Value, second: Value) => boolean;
283
290
  };
284
291
 
285
- export type ReactiveState<Value, Equal> = {
286
- computeds: Set<ComputedEffect>;
287
- effects: Set<EffectState>;
288
- equal: (first: Equal, second: Equal) => boolean;
289
- 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>;
290
303
  promises?: Map<Key, Promise<never>>;
291
- subscriptions: Map<GenericCallback, Subscription>;
292
- value: Value;
304
+ subscriptions?: Subscriptions<GenericCallback>;
305
+ value: unknown;
293
306
  };
294
307
 
295
308
  export type ReactiveStore<Store> = {
@@ -402,57 +415,35 @@ export type ReactiveStore<Store> = {
402
415
  /**
403
416
  * Subscribe to changes
404
417
  *
405
- * @param callback Callback for changes
406
- * @returns Unsubscribe callback
418
+ * @param subscriber Callback for changes
419
+ * @param copy Copy the store? _(defaults to `false`)_
420
+ * @returns Subscription
407
421
  */
408
- subscribe(callback: (value: Store) => void): Unsubscribe;
422
+ subscribe(subscriber: Subscriber<Store>, copy?: boolean): Subscription;
409
423
 
410
424
  /**
411
425
  * Subscribe to changes for a specific key
412
426
  *
413
427
  * @param key Key of the value to subscribe to
414
- * @param callback Callback for changes
415
- * @returns Unsubscribe callback
428
+ * @param subscriber Callback for changes
429
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
430
+ * @returns Subscription
416
431
  */
417
432
  subscribe<Key extends keyof Store>(
418
433
  key: Key,
419
- callback: (value: Store[Key] | undefined) => void,
420
- ): Unsubscribe;
434
+ subscriber: Subscriber<Store[Key] | undefined>,
435
+ copy?: boolean,
436
+ ): Subscription;
421
437
 
422
438
  /**
423
439
  * Subscribe to changes for a specific key
424
440
  *
425
441
  * @param key Key of the value to subscribe to
426
- * @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
442
+ * @param subscriber Callback for changes
443
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
444
+ * @returns Subscription
454
445
  */
455
- unsubscribe(callback: (value: Store) => void): void;
446
+ subscribe(key: Key, subscriber: Subscriber<unknown>, copy?: boolean): Subscription;
456
447
 
457
448
  /**
458
449
  * Update the value _(based on the current value)_
@@ -462,7 +453,7 @@ export type ReactiveStore<Store> = {
462
453
  update(callback: (value: Store) => Store): void;
463
454
  } & Reactive<Store>;
464
455
 
465
- export type ReadonlyInstances<Value> = {
456
+ type ReadonlyInstances<Value> = {
466
457
  frozen?: ReadonlyFrozenSignal<Value>;
467
458
  original?: ReadonlySignal<Value>;
468
459
  };
@@ -483,15 +474,6 @@ export type ReadonlySignalValue<Value> = Value extends unknown[]
483
474
  ? Readonly<Value>
484
475
  : Value;
485
476
 
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
477
  export type Signal<Value> = {
496
478
  /**
497
479
  * Get a readonly version of the signal
@@ -527,6 +509,10 @@ export type Signal<Value> = {
527
509
  } & Reactive<Value> &
528
510
  SimpleReactive<Value>;
529
511
 
512
+ export type SignalState = {
513
+ readonlies?: ReadonlyInstances<unknown>;
514
+ } & ReactiveState;
515
+
530
516
  type SimpleReactive<Value> = {
531
517
  /**
532
518
  * Get the value
@@ -538,34 +524,31 @@ type SimpleReactive<Value> = {
538
524
  /**
539
525
  * Get the value _(without reactivity)_
540
526
  *
527
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
541
528
  * @returns Current value
542
529
  */
543
- peek(): Value;
530
+ peek(copy?: boolean): Value;
544
531
 
545
532
  /**
546
533
  * Subscribe to changes
547
534
  *
548
- * @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
535
+ * @param subscriber Callback for changes
536
+ * @param copy If the value is an array or record, should it be copied? _(defaults to `false`)_
537
+ * @returns Subscription
557
538
  */
558
- unsubscribe(callback: (value: Value) => void): void;
539
+ subscribe(subscriber: Subscriber<Value>, copy?: boolean): Subscription;
559
540
  };
560
541
 
561
- export type Subscription = {
542
+ export type StoredSubscription = {
562
543
  callback: GenericCallback;
563
- state: ReactiveState<unknown, never>;
564
-
565
- destroy(): void;
544
+ copy: boolean;
545
+ frozen: boolean;
546
+ instance: InternalSignal;
547
+ state: SignalState;
566
548
  };
567
549
 
568
- /**
569
- * Unsubscribe from changes
570
- */
571
- export type Unsubscribe = () => void;
550
+ export type Subscriber<Value> = (value: Value, subscription: Subscription) => void;
551
+
552
+ export type SubscriptionType = 'copy' | 'frozen' | 'original' | 'readonly';
553
+
554
+ // #endregion