@oscarpalmer/mora 0.10.0 → 0.11.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.
package/src/computed.ts CHANGED
@@ -1,52 +1,33 @@
1
- import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
1
  import {batchDepth, batchedHandlers} from './batch';
3
2
  import {type Effect, activeEffect, effect, runEffect} from './effect';
4
- import {
5
- type Subscription,
6
- type Unsubscribe,
7
- subscribe,
8
- unsubscribe,
9
- } from './subscription';
10
-
11
- export type ComputedState<Value> = {
12
- computeds: Set<Computed<unknown>>;
3
+ import {Reactive, type ReactiveState} from './reactive';
4
+
5
+ type ComputedEffect = {
13
6
  dirty: boolean;
14
- effect: Effect;
15
- effects: Set<Effect>;
16
- subscriptions: Map<GenericCallback, Subscription>;
17
- value: Value;
7
+ instance: Effect;
18
8
  };
19
9
 
20
10
  export type InternalComputed = {
21
- readonly state: ComputedState<unknown>;
11
+ readonly effect: ComputedEffect;
12
+ readonly state: ReactiveState<unknown>;
22
13
  };
23
14
 
24
15
  export let activeComputed: Computed<unknown> | undefined;
25
16
 
26
- export class Computed<Value> {
27
- private declare readonly $mora: string;
28
-
29
- private readonly state: ComputedState<Value>;
17
+ export class Computed<Value> extends Reactive<Value> {
18
+ private readonly effect: ComputedEffect = {
19
+ dirty: true,
20
+ instance: undefined as never,
21
+ };
30
22
 
31
23
  constructor(callback: () => Value) {
32
- Object.defineProperty(this, '$mora', {
33
- value: 'computed',
34
- });
24
+ super('computed', undefined as never);
35
25
 
36
- this.state = {
37
- computeds: new Set(),
38
- dirty: true,
39
- effect: undefined as never,
40
- effects: new Set(),
41
- subscriptions: new Map(),
42
- value: undefined as never,
43
- };
44
-
45
- this.state.effect = effect(() => {
46
- if (this.state.dirty) {
26
+ this.effect.instance = effect(() => {
27
+ if (this.effect.dirty) {
47
28
  const previousComputed = activeComputed;
48
29
 
49
- activeComputed = this;
30
+ activeComputed = this as never;
50
31
 
51
32
  const value = callback();
52
33
 
@@ -56,7 +37,7 @@ export class Computed<Value> {
56
37
  this.state.value = value;
57
38
 
58
39
  for (const computed of this.state.computeds) {
59
- computed.state.dirty = true;
40
+ computed.effect.dirty = true;
60
41
  }
61
42
 
62
43
  for (const effect of this.state.effects) {
@@ -68,7 +49,7 @@ export class Computed<Value> {
68
49
  }
69
50
  }
70
51
 
71
- this.state.dirty = false;
52
+ this.effect.dirty = false;
72
53
  }
73
54
  });
74
55
  }
@@ -81,37 +62,16 @@ export class Computed<Value> {
81
62
  this.state.computeds.add(activeComputed);
82
63
  }
83
64
 
84
- if (activeEffect != null && activeEffect !== this.state.effect) {
65
+ if (activeEffect != null && activeEffect !== this.effect.instance) {
85
66
  this.state.effects.add(activeEffect);
86
67
  }
87
68
 
88
- if (this.state.dirty && batchDepth === 0) {
89
- runEffect(this.state.effect);
69
+ if (this.effect.dirty && batchDepth === 0) {
70
+ runEffect(this.effect.instance);
90
71
  }
91
72
 
92
73
  return this.state.value;
93
74
  }
94
-
95
- /**
96
- * Get the value _(without reactivity)_
97
- */
98
- peek(): Value {
99
- return this.state.value;
100
- }
101
-
102
- /**
103
- * Subscribe to changes
104
- */
105
- subscribe(callback: (value: Value) => void): Unsubscribe {
106
- return subscribe(this.state, callback);
107
- }
108
-
109
- /**
110
- * Unsubscribe from changes
111
- */
112
- unsubscribe(callback: (value: Value) => void): void {
113
- unsubscribe(this.state, callback);
114
- }
115
75
  }
116
76
 
117
77
  /**
package/src/is.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type {PlainObject} from '@oscarpalmer/atoms/models';
2
2
  import type {Computed} from './computed';
3
3
  import type {Effect} from './effect';
4
+ import type {Reactive} from './reactive';
4
5
  import type {Signal} from './signal';
5
6
 
6
7
  /**
@@ -26,6 +27,10 @@ function isMora<T>(value: unknown, name: string): value is T {
26
27
  );
27
28
  }
28
29
 
30
+ export function isReactive(value: unknown): value is Reactive<unknown> {
31
+ return isComputed(value) || isSignal(value);
32
+ }
33
+
29
34
  /**
30
35
  * Is the value a signal?
31
36
  */
@@ -0,0 +1,53 @@
1
+ import type {Computed} from './computed';
2
+ import type {Effect} from './effect';
3
+ import {
4
+ type Subscription,
5
+ type Unsubscribe,
6
+ subscribe,
7
+ unsubscribe,
8
+ } from './subscription';
9
+
10
+ export abstract class Reactive<Value> {
11
+ protected readonly state: ReactiveState<Value> = {
12
+ computeds: new Set(),
13
+ effects: new Set(),
14
+ subscriptions: new Map(),
15
+ value: undefined as never,
16
+ };
17
+
18
+ constructor(name: string, value: Value) {
19
+ this.state.value = value;
20
+
21
+ Object.defineProperty(this, '$mora', {
22
+ value: name,
23
+ });
24
+ }
25
+
26
+ /**
27
+ * Get the value _(without reactivity)_
28
+ */
29
+ peek(): Value {
30
+ return this.state.value;
31
+ }
32
+
33
+ /**
34
+ * Subscribe to changes
35
+ */
36
+ subscribe(callback: (value: Value) => void): Unsubscribe {
37
+ return subscribe(this.state, callback);
38
+ }
39
+
40
+ /**
41
+ * Unsubscribe from changes
42
+ */
43
+ unsubscribe(callback: (value: Value) => void): void {
44
+ unsubscribe(this.state, callback);
45
+ }
46
+ }
47
+
48
+ export type ReactiveState<Value> = {
49
+ computeds: Set<Computed<unknown>>;
50
+ effects: Set<Effect>;
51
+ subscriptions: Map<(value: Value) => void, Subscription<Value>>;
52
+ value: Value;
53
+ };
package/src/signal.ts CHANGED
@@ -1,37 +1,11 @@
1
- import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
1
  import {batchDepth, batchedHandlers, flushEffects} from './batch';
3
- import {type Computed, type InternalComputed, activeComputed} from './computed';
4
- import {type Effect, activeEffect} from './effect';
5
- import {
6
- type Subscription,
7
- type Unsubscribe,
8
- subscribe,
9
- unsubscribe,
10
- } from './subscription';
11
-
12
- export type SignalState<Value> = {
13
- computeds: Set<Computed<unknown>>;
14
- effects: Set<Effect>;
15
- subscriptions: Map<GenericCallback, Subscription>;
16
- value: Value;
17
- };
18
-
19
- export class Signal<Value> {
20
- private declare readonly $mora: string;
21
-
22
- private readonly state: SignalState<Value>;
2
+ import {type InternalComputed, activeComputed} from './computed';
3
+ import {activeEffect} from './effect';
4
+ import {Reactive} from './reactive';
23
5
 
6
+ export class Signal<Value> extends Reactive<Value> {
24
7
  constructor(value: Value) {
25
- Object.defineProperty(this, '$mora', {
26
- value: 'signal',
27
- });
28
-
29
- this.state = {
30
- value,
31
- computeds: new Set(),
32
- effects: new Set(),
33
- subscriptions: new Map(),
34
- };
8
+ super('signal', value);
35
9
  }
36
10
 
37
11
  /**
@@ -49,13 +23,6 @@ export class Signal<Value> {
49
23
  return this.state.value;
50
24
  }
51
25
 
52
- /**
53
- * Get the value _(without reactivity)_
54
- */
55
- peek(): Value {
56
- return this.state.value;
57
- }
58
-
59
26
  /**
60
27
  * Set the value
61
28
  */
@@ -67,7 +34,7 @@ export class Signal<Value> {
67
34
  this.state.value = value;
68
35
 
69
36
  for (const computed of this.state.computeds) {
70
- (computed as unknown as InternalComputed).state.dirty = true;
37
+ (computed as unknown as InternalComputed).effect.dirty = true;
71
38
  }
72
39
 
73
40
  for (const effect of this.state.effects) {
@@ -75,7 +42,7 @@ export class Signal<Value> {
75
42
  }
76
43
 
77
44
  for (const [, subscription] of this.state.subscriptions) {
78
- batchedHandlers.add(subscription);
45
+ batchedHandlers.add(subscription as never);
79
46
  }
80
47
 
81
48
  if (batchDepth === 0) {
@@ -83,20 +50,6 @@ export class Signal<Value> {
83
50
  }
84
51
  }
85
52
 
86
- /**
87
- * Subscribe to changes
88
- */
89
- subscribe(callback: (value: Value) => void): Unsubscribe {
90
- return subscribe(this.state, callback);
91
- }
92
-
93
- /**
94
- * Unsubscribe from changes
95
- */
96
- unsubscribe(callback: (value: Value) => void): void {
97
- unsubscribe(this.state, callback);
98
- }
99
-
100
53
  /**
101
54
  * Update the value
102
55
  */
@@ -1,23 +1,25 @@
1
1
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
2
- import type {ComputedState} from './computed';
3
- import type {SignalState} from './signal';
2
+ import type {ReactiveState} from './reactive';
4
3
 
5
- export class Subscription {
4
+ export class Subscription<Value> {
6
5
  constructor(
7
- public state: ComputedState<unknown> | SignalState<unknown>,
6
+ public state: ReactiveState<Value>,
8
7
  public callback: GenericCallback,
9
8
  ) {
10
9
  callback(state.value);
11
10
  }
12
11
  }
13
12
 
13
+ /**
14
+ * Unsubscribe from changes
15
+ */
14
16
  export type Unsubscribe = () => void;
15
17
 
16
18
  export function noop(): void {}
17
19
 
18
- export function subscribe(
19
- state: ComputedState<unknown> | SignalState<unknown>,
20
- callback: GenericCallback,
20
+ export function subscribe<Value>(
21
+ state: ReactiveState<Value>,
22
+ callback: (value: Value) => void,
21
23
  ): Unsubscribe {
22
24
  if (typeof callback !== 'function' || state.subscriptions.has(callback)) {
23
25
  return noop;
@@ -30,9 +32,9 @@ export function subscribe(
30
32
  };
31
33
  }
32
34
 
33
- export function unsubscribe(
34
- state: ComputedState<unknown> | SignalState<unknown>,
35
- callback: GenericCallback,
35
+ export function unsubscribe<Value>(
36
+ state: ReactiveState<Value>,
37
+ callback: (value: Value) => void,
36
38
  ): void {
37
39
  const subscription = state.subscriptions.get(callback);
38
40
 
package/types/batch.d.cts CHANGED
@@ -6,22 +6,17 @@ declare class Effect {
6
6
  private readonly state;
7
7
  constructor(callback: GenericCallback);
8
8
  }
9
- export type ComputedState<Value> = {
10
- computeds: Set<Computed<unknown>>;
11
- dirty: boolean;
12
- effect: Effect;
13
- effects: Set<Effect>;
14
- subscriptions: Map<GenericCallback, Subscription>;
15
- value: Value;
16
- };
17
- declare class Computed<Value> {
18
- private readonly $mora;
19
- private readonly state;
9
+ declare class Computed<Value> extends Reactive<Value> {
10
+ private readonly effect;
20
11
  constructor(callback: () => Value);
21
12
  /**
22
13
  * Get the value
23
14
  */
24
15
  get(): Value;
16
+ }
17
+ declare abstract class Reactive<Value> {
18
+ protected readonly state: ReactiveState<Value>;
19
+ constructor(name: string, value: Value);
25
20
  /**
26
21
  * Get the value _(without reactivity)_
27
22
  */
@@ -35,17 +30,20 @@ declare class Computed<Value> {
35
30
  */
36
31
  unsubscribe(callback: (value: Value) => void): void;
37
32
  }
38
- export type SignalState<Value> = {
33
+ export type ReactiveState<Value> = {
39
34
  computeds: Set<Computed<unknown>>;
40
35
  effects: Set<Effect>;
41
- subscriptions: Map<GenericCallback, Subscription>;
36
+ subscriptions: Map<(value: Value) => void, Subscription<Value>>;
42
37
  value: Value;
43
38
  };
44
- declare class Subscription {
45
- state: ComputedState<unknown> | SignalState<unknown>;
39
+ declare class Subscription<Value> {
40
+ state: ReactiveState<Value>;
46
41
  callback: GenericCallback;
47
- constructor(state: ComputedState<unknown> | SignalState<unknown>, callback: GenericCallback);
42
+ constructor(state: ReactiveState<Value>, callback: GenericCallback);
48
43
  }
44
+ /**
45
+ * Unsubscribe from changes
46
+ */
49
47
  export type Unsubscribe = () => void;
50
48
  export declare function flushEffects(): void;
51
49
  /**
@@ -56,7 +54,7 @@ export declare function startBatch(): void;
56
54
  * Stop batching effects and flush _(run)_ them
57
55
  */
58
56
  export declare function stopBatch(): void;
59
- export declare const batchedHandlers: Set<Effect | Subscription>;
57
+ export declare const batchedHandlers: Set<Effect | Subscription<unknown>>;
60
58
  export declare let batchDepth: number;
61
59
 
62
60
  export {};
package/types/batch.d.ts CHANGED
@@ -9,5 +9,5 @@ export declare function startBatch(): void;
9
9
  * Stop batching effects and flush _(run)_ them
10
10
  */
11
11
  export declare function stopBatch(): void;
12
- export declare const batchedHandlers: Set<Effect | Subscription>;
12
+ export declare const batchedHandlers: Set<Effect | Subscription<unknown>>;
13
13
  export declare let batchDepth: number;
@@ -6,26 +6,30 @@ declare class Effect {
6
6
  private readonly state;
7
7
  constructor(callback: GenericCallback);
8
8
  }
9
- export type ComputedState<Value> = {
10
- computeds: Set<Computed<unknown>>;
9
+ export type ComputedEffect = {
11
10
  dirty: boolean;
12
- effect: Effect;
13
- effects: Set<Effect>;
14
- subscriptions: Map<GenericCallback, Subscription>;
15
- value: Value;
11
+ instance: Effect;
16
12
  };
17
13
  export type InternalComputed = {
18
- readonly state: ComputedState<unknown>;
14
+ readonly effect: ComputedEffect;
15
+ readonly state: ReactiveState<unknown>;
19
16
  };
20
17
  export declare let activeComputed: Computed<unknown> | undefined;
21
- export declare class Computed<Value> {
22
- private readonly $mora;
23
- private readonly state;
18
+ export declare class Computed<Value> extends Reactive<Value> {
19
+ private readonly effect;
24
20
  constructor(callback: () => Value);
25
21
  /**
26
22
  * Get the value
27
23
  */
28
24
  get(): Value;
25
+ }
26
+ /**
27
+ * Create a computed value
28
+ */
29
+ export declare function computed<Value>(callback: () => Value): Computed<Value>;
30
+ declare abstract class Reactive<Value> {
31
+ protected readonly state: ReactiveState<Value>;
32
+ constructor(name: string, value: Value);
29
33
  /**
30
34
  * Get the value _(without reactivity)_
31
35
  */
@@ -39,21 +43,20 @@ export declare class Computed<Value> {
39
43
  */
40
44
  unsubscribe(callback: (value: Value) => void): void;
41
45
  }
42
- /**
43
- * Create a computed value
44
- */
45
- export declare function computed<Value>(callback: () => Value): Computed<Value>;
46
- export type SignalState<Value> = {
46
+ export type ReactiveState<Value> = {
47
47
  computeds: Set<Computed<unknown>>;
48
48
  effects: Set<Effect>;
49
- subscriptions: Map<GenericCallback, Subscription>;
49
+ subscriptions: Map<(value: Value) => void, Subscription<Value>>;
50
50
  value: Value;
51
51
  };
52
- declare class Subscription {
53
- state: ComputedState<unknown> | SignalState<unknown>;
52
+ declare class Subscription<Value> {
53
+ state: ReactiveState<Value>;
54
54
  callback: GenericCallback;
55
- constructor(state: ComputedState<unknown> | SignalState<unknown>, callback: GenericCallback);
55
+ constructor(state: ReactiveState<Value>, callback: GenericCallback);
56
56
  }
57
+ /**
58
+ * Unsubscribe from changes
59
+ */
57
60
  export type Unsubscribe = () => void;
58
61
 
59
62
  export {};
@@ -1,40 +1,24 @@
1
- import type { GenericCallback } from '@oscarpalmer/atoms/models';
2
1
  import { type Effect } from './effect';
3
- import { type Subscription, type Unsubscribe } from './subscription';
4
- export type ComputedState<Value> = {
5
- computeds: Set<Computed<unknown>>;
2
+ import { Reactive, type ReactiveState } from './reactive';
3
+ type ComputedEffect = {
6
4
  dirty: boolean;
7
- effect: Effect;
8
- effects: Set<Effect>;
9
- subscriptions: Map<GenericCallback, Subscription>;
10
- value: Value;
5
+ instance: Effect;
11
6
  };
12
7
  export type InternalComputed = {
13
- readonly state: ComputedState<unknown>;
8
+ readonly effect: ComputedEffect;
9
+ readonly state: ReactiveState<unknown>;
14
10
  };
15
11
  export declare let activeComputed: Computed<unknown> | undefined;
16
- export declare class Computed<Value> {
17
- private readonly $mora;
18
- private readonly state;
12
+ export declare class Computed<Value> extends Reactive<Value> {
13
+ private readonly effect;
19
14
  constructor(callback: () => Value);
20
15
  /**
21
16
  * Get the value
22
17
  */
23
18
  get(): Value;
24
- /**
25
- * Get the value _(without reactivity)_
26
- */
27
- peek(): Value;
28
- /**
29
- * Subscribe to changes
30
- */
31
- subscribe(callback: (value: Value) => void): Unsubscribe;
32
- /**
33
- * Unsubscribe from changes
34
- */
35
- unsubscribe(callback: (value: Value) => void): void;
36
19
  }
37
20
  /**
38
21
  * Create a computed value
39
22
  */
40
23
  export declare function computed<Value>(callback: () => Value): Computed<Value>;
24
+ export {};
package/types/index.d.cts CHANGED
@@ -10,14 +10,21 @@ export declare class Effect {
10
10
  * Create an effect
11
11
  */
12
12
  export declare function effect(callback: GenericCallback): Effect;
13
- export declare class Computed<Value> {
14
- private readonly $mora;
15
- private readonly state;
13
+ export declare class Computed<Value> extends Reactive<Value> {
14
+ private readonly effect;
16
15
  constructor(callback: () => Value);
17
16
  /**
18
17
  * Get the value
19
18
  */
20
19
  get(): Value;
20
+ }
21
+ /**
22
+ * Create a computed value
23
+ */
24
+ export declare function computed<Value>(callback: () => Value): Computed<Value>;
25
+ declare abstract class Reactive<Value> {
26
+ protected readonly state: ReactiveState<Value>;
27
+ constructor(name: string, value: Value);
21
28
  /**
22
29
  * Get the value _(without reactivity)_
23
30
  */
@@ -31,41 +38,37 @@ export declare class Computed<Value> {
31
38
  */
32
39
  unsubscribe(callback: (value: Value) => void): void;
33
40
  }
41
+ export type ReactiveState<Value> = {
42
+ computeds: Set<Computed<unknown>>;
43
+ effects: Set<Effect>;
44
+ subscriptions: Map<(value: Value) => void, Subscription<Value>>;
45
+ value: Value;
46
+ };
47
+ declare class Subscription<Value> {
48
+ state: ReactiveState<Value>;
49
+ callback: GenericCallback;
50
+ constructor(state: ReactiveState<Value>, callback: GenericCallback);
51
+ }
34
52
  /**
35
- * Create a computed value
53
+ * Unsubscribe from changes
36
54
  */
37
- export declare function computed<Value>(callback: () => Value): Computed<Value>;
38
- export declare class Signal<Value> {
39
- private readonly $mora;
40
- private readonly state;
55
+ export type Unsubscribe = () => void;
56
+ export declare class Signal<Value> extends Reactive<Value> {
41
57
  constructor(value: Value);
42
58
  /**
43
59
  * Get the value
44
60
  */
45
61
  get(): Value;
46
- /**
47
- * Get the value _(without reactivity)_
48
- */
49
- peek(): Value;
50
62
  /**
51
63
  * Set the value
52
64
  */
53
65
  set(value: Value): void;
54
- /**
55
- * Subscribe to changes
56
- */
57
- subscribe(callback: (value: Value) => void): Unsubscribe;
58
- /**
59
- * Unsubscribe from changes
60
- */
61
- unsubscribe(callback: (value: Value) => void): void;
62
66
  /**
63
67
  * Update the value
64
68
  */
65
69
  update(callback: (value: Value) => Value): void;
66
70
  }
67
71
  export declare function signal<Value>(value: Value): Signal<Value>;
68
- export type Unsubscribe = () => void;
69
72
  /**
70
73
  * Is the value a computed signal?
71
74
  */
package/types/is.d.cts CHANGED
@@ -6,14 +6,17 @@ declare class Effect {
6
6
  private readonly state;
7
7
  constructor(callback: GenericCallback);
8
8
  }
9
- declare class Computed<Value> {
10
- private readonly $mora;
11
- private readonly state;
9
+ declare class Computed<Value> extends Reactive<Value> {
10
+ private readonly effect;
12
11
  constructor(callback: () => Value);
13
12
  /**
14
13
  * Get the value
15
14
  */
16
15
  get(): Value;
16
+ }
17
+ declare abstract class Reactive<Value> {
18
+ protected readonly state: ReactiveState<Value>;
19
+ constructor(name: string, value: Value);
17
20
  /**
18
21
  * Get the value _(without reactivity)_
19
22
  */
@@ -27,36 +30,36 @@ declare class Computed<Value> {
27
30
  */
28
31
  unsubscribe(callback: (value: Value) => void): void;
29
32
  }
30
- declare class Signal<Value> {
31
- private readonly $mora;
32
- private readonly state;
33
+ export type ReactiveState<Value> = {
34
+ computeds: Set<Computed<unknown>>;
35
+ effects: Set<Effect>;
36
+ subscriptions: Map<(value: Value) => void, Subscription<Value>>;
37
+ value: Value;
38
+ };
39
+ declare class Subscription<Value> {
40
+ state: ReactiveState<Value>;
41
+ callback: GenericCallback;
42
+ constructor(state: ReactiveState<Value>, callback: GenericCallback);
43
+ }
44
+ /**
45
+ * Unsubscribe from changes
46
+ */
47
+ export type Unsubscribe = () => void;
48
+ declare class Signal<Value> extends Reactive<Value> {
33
49
  constructor(value: Value);
34
50
  /**
35
51
  * Get the value
36
52
  */
37
53
  get(): Value;
38
- /**
39
- * Get the value _(without reactivity)_
40
- */
41
- peek(): Value;
42
54
  /**
43
55
  * Set the value
44
56
  */
45
57
  set(value: Value): void;
46
- /**
47
- * Subscribe to changes
48
- */
49
- subscribe(callback: (value: Value) => void): Unsubscribe;
50
- /**
51
- * Unsubscribe from changes
52
- */
53
- unsubscribe(callback: (value: Value) => void): void;
54
58
  /**
55
59
  * Update the value
56
60
  */
57
61
  update(callback: (value: Value) => Value): void;
58
62
  }
59
- export type Unsubscribe = () => void;
60
63
  /**
61
64
  * Is the value a computed signal?
62
65
  */
@@ -65,6 +68,7 @@ export declare function isComputed(value: unknown): value is Computed<unknown>;
65
68
  * Is the value an effect?
66
69
  */
67
70
  export declare function isEffect(value: unknown): value is Effect;
71
+ export declare function isReactive(value: unknown): value is Reactive<unknown>;
68
72
  /**
69
73
  * Is the value a signal?
70
74
  */