@esportsplus/reactivity 0.1.13 → 0.1.15

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/build/signal.d.ts CHANGED
@@ -1,43 +1,23 @@
1
- import { Changed, Event, Function, Listener, NeverAsync, Options, Scheduler, State, Type } from './types';
2
- declare class Core<T> {
1
+ import { Computed, Changed, Effect, Event, Function, Listener, NeverAsync, Options, Root, Scheduler, Signal, State, Type } from './types';
2
+ declare class Reactive<T> {
3
+ changed: Changed | null;
4
+ fn: Computed<T>['fn'] | Effect['fn'] | null;
3
5
  listeners: Record<Event, (Listener<any> | null)[]> | null;
4
- observers: Core<any>[] | null;
6
+ observers: Reactive<any>[] | null;
5
7
  root: Root | null;
6
- sources: Core<any>[] | null;
8
+ scheduler: Scheduler | null;
9
+ sources: Reactive<any>[] | null;
7
10
  state: State;
8
- updating: boolean;
11
+ task: Function | null;
12
+ tracking: boolean | null;
13
+ type: Type;
9
14
  value: T;
10
- constructor(state: State, value: T);
11
- get type(): Type | never;
15
+ constructor(state: State, type: Type, value: T);
12
16
  dispatch<D>(event: Event, data?: D): void;
13
17
  dispose(): void;
18
+ get(): T;
14
19
  on<T>(event: Event, listener: Listener<T>): void;
15
20
  once<T>(event: Event, listener: Listener<T>): void;
16
- }
17
- declare class Computed<T> extends Core<T> {
18
- changed: Changed;
19
- fn: NeverAsync<() => T>;
20
- constructor(fn: Computed<T>['fn'], options?: Options);
21
- get type(): Type;
22
- get(): T;
23
- }
24
- declare class Effect extends Core<null> {
25
- fn: NeverAsync<(node: Effect) => void>;
26
- task: Function;
27
- constructor(fn: Effect['fn']);
28
- get type(): Type;
29
- }
30
- declare class Root extends Core<null> {
31
- scheduler: Scheduler;
32
- tracking: boolean;
33
- constructor(scheduler: Scheduler, tracking: boolean);
34
- get type(): Type;
35
- }
36
- declare class Signal<T> extends Core<T> {
37
- changed: Changed;
38
- constructor(data: T, options?: Options);
39
- get type(): Type;
40
- get(): T;
41
21
  set(value: T): T;
42
22
  }
43
23
  declare const computed: <T>(fn: () => T extends unknown ? T : NeverAsync<T>, options?: Options) => Computed<T>;
@@ -45,7 +25,7 @@ declare const dispose: <T extends {
45
25
  dispose: VoidFunction;
46
26
  }>(dispose?: T | T[] | null | undefined) => T | T[] | null | undefined;
47
27
  declare const effect: (fn: Effect['fn']) => Effect;
48
- declare const root: <T>(fn: (root: Root) => T, scheduler?: Scheduler) => T;
28
+ declare const root: <T>(fn: (root: Root) => T, scheduler?: Scheduler | null) => T;
49
29
  declare const signal: <T>(value: T, options?: Options) => Signal<T>;
50
30
  export { computed, dispose, effect, root, signal };
51
- export { Computed, Effect, Root, Signal };
31
+ export { Reactive };
package/build/signal.js CHANGED
@@ -1,17 +1,22 @@
1
1
  import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
2
2
  import { isArray } from './utilities';
3
3
  let index = 0, observer = null, observers = null, scope = null;
4
- class Core {
4
+ class Reactive {
5
+ changed = null;
6
+ fn = null;
5
7
  listeners = null;
6
8
  observers = null;
7
9
  root;
10
+ scheduler = null;
8
11
  sources = null;
9
12
  state;
10
- updating = false;
13
+ task = null;
14
+ tracking = null;
15
+ type;
11
16
  value;
12
- constructor(state, value) {
17
+ constructor(state, type, value) {
13
18
  let root = null;
14
- if (this.type !== ROOT) {
19
+ if (type !== ROOT) {
15
20
  if (scope !== null) {
16
21
  root = scope;
17
22
  }
@@ -19,7 +24,7 @@ class Core {
19
24
  root = observer.root;
20
25
  }
21
26
  if (root == null) {
22
- if (this.type === EFFECT) {
27
+ if (type === EFFECT) {
23
28
  throw new Error(`Reactivity: 'effect' cannot be created without a reactive root`);
24
29
  }
25
30
  }
@@ -29,16 +34,14 @@ class Core {
29
34
  }
30
35
  this.root = root;
31
36
  this.state = state;
37
+ this.type = type;
32
38
  this.value = value;
33
39
  }
34
- get type() {
35
- throw new Error(`Reactivity: reactive primitives require 'type' getters`);
36
- }
37
40
  dispatch(event, data) {
38
41
  if (this.listeners === null || this.listeners[event] === undefined) {
39
42
  return;
40
43
  }
41
- let listeners = this.listeners[event], parameter = {
44
+ let listeners = this.listeners[event], values = {
42
45
  data,
43
46
  value: this.value
44
47
  };
@@ -48,7 +51,7 @@ class Core {
48
51
  continue;
49
52
  }
50
53
  try {
51
- listener(parameter);
54
+ listener(values);
52
55
  if (listener.once !== undefined) {
53
56
  listeners[i] = null;
54
57
  }
@@ -69,9 +72,31 @@ class Core {
69
72
  this.sources = null;
70
73
  this.state = DISPOSED;
71
74
  }
75
+ get() {
76
+ if (this.state === DISPOSED) {
77
+ return this.value;
78
+ }
79
+ if (observer !== null) {
80
+ if (observers === null) {
81
+ if (observer.sources !== null && observer.sources[index] == this) {
82
+ index++;
83
+ }
84
+ else {
85
+ observers = [this];
86
+ }
87
+ }
88
+ else {
89
+ observers.push(this);
90
+ }
91
+ }
92
+ if (this.type === COMPUTED || this.type === EFFECT) {
93
+ sync(this);
94
+ }
95
+ return this.value;
96
+ }
72
97
  on(event, listener) {
73
- if (this.updating) {
74
- listener.once = true;
98
+ if (this.state === DIRTY) {
99
+ return;
75
100
  }
76
101
  if (this.listeners === null) {
77
102
  this.listeners = { [event]: [listener] };
@@ -96,61 +121,15 @@ class Core {
96
121
  listener.once = true;
97
122
  this.on(event, listener);
98
123
  }
99
- }
100
- class Computed extends Core {
101
- changed;
102
- fn;
103
- constructor(fn, options) {
104
- super(DIRTY, undefined);
105
- this.changed = options?.changed || changed;
106
- this.fn = fn;
107
- }
108
- get type() {
109
- return COMPUTED;
110
- }
111
- get() {
112
- return read(this);
113
- }
114
- }
115
- class Effect extends Core {
116
- fn;
117
- task;
118
- constructor(fn) {
119
- super(DIRTY, null);
120
- this.fn = fn;
121
- this.task = () => read(this);
122
- update(this);
123
- }
124
- get type() {
125
- return EFFECT;
126
- }
127
- }
128
- class Root extends Core {
129
- scheduler;
130
- tracking;
131
- constructor(scheduler, tracking) {
132
- super(CLEAN, null);
133
- this.scheduler = scheduler;
134
- this.tracking = tracking;
135
- }
136
- get type() {
137
- return ROOT;
138
- }
139
- }
140
- class Signal extends Core {
141
- changed;
142
- constructor(data, options) {
143
- super(CLEAN, data);
144
- this.changed = options?.changed || changed;
145
- }
146
- get type() {
147
- return SIGNAL;
148
- }
149
- get() {
150
- return read(this);
151
- }
152
124
  set(value) {
153
- return write(this, value);
125
+ if (this.type !== SIGNAL && observer !== this) {
126
+ throw new Error(`Reactivity: 'set' method is only available on signals`);
127
+ }
128
+ if (this.changed(this.value, value)) {
129
+ this.value = value;
130
+ notify(this.observers, DIRTY);
131
+ }
132
+ return this.value;
154
133
  }
155
134
  }
156
135
  function changed(a, b) {
@@ -171,39 +150,17 @@ function notify(nodes, state) {
171
150
  }
172
151
  }
173
152
  }
174
- function read(node) {
175
- if (node.state === DISPOSED) {
176
- return node.value;
177
- }
178
- if (observer !== null) {
179
- if (observers === null) {
180
- if (observer.sources !== null && observer.sources[index] == node) {
181
- index++;
182
- }
183
- else {
184
- observers = [node];
185
- }
186
- }
187
- else {
188
- observers.push(node);
189
- }
190
- }
191
- if (node.type === COMPUTED || node.type === EFFECT) {
192
- sync(node);
193
- }
194
- return node.value;
195
- }
196
153
  function removeSourceObservers(node, start) {
197
154
  if (node.sources === null) {
198
155
  return;
199
156
  }
200
157
  for (let i = start, n = node.sources.length; i < n; i++) {
201
- let source = node.sources[i];
202
- if (source.observers === null) {
158
+ let observers = node.sources[i].observers;
159
+ if (observers === null) {
203
160
  continue;
204
161
  }
205
- source.observers[source.observers.indexOf(node)] = source.observers[source.observers.length - 1];
206
- source.observers.pop();
162
+ observers[observers.indexOf(node)] = observers[observers.length - 1];
163
+ observers.pop();
207
164
  }
208
165
  }
209
166
  function sync(node) {
@@ -229,9 +186,7 @@ function update(node) {
229
186
  observers = null;
230
187
  try {
231
188
  node.dispatch('update');
232
- node.updating = true;
233
189
  let value = node.fn.call(node);
234
- node.updating = false;
235
190
  if (observers) {
236
191
  removeSourceObservers(node, index);
237
192
  if (node.sources !== null && index > 0) {
@@ -245,7 +200,7 @@ function update(node) {
245
200
  }
246
201
  for (let i = index, n = node.sources.length; i < n; i++) {
247
202
  let source = node.sources[i];
248
- if (!source.observers) {
203
+ if (source.observers === null) {
249
204
  source.observers = [node];
250
205
  }
251
206
  else {
@@ -258,12 +213,7 @@ function update(node) {
258
213
  node.sources.length = index;
259
214
  }
260
215
  if (node.type === COMPUTED) {
261
- write(node, value);
262
- }
263
- }
264
- catch {
265
- if (node.state === DIRTY) {
266
- removeSourceObservers(node, 0);
216
+ node.set(value);
267
217
  }
268
218
  }
269
219
  finally {
@@ -273,15 +223,11 @@ function update(node) {
273
223
  }
274
224
  node.state = CLEAN;
275
225
  }
276
- function write(node, value) {
277
- if (node.changed(node.value, value)) {
278
- node.value = value;
279
- notify(node.observers, DIRTY);
280
- }
281
- return value;
282
- }
283
226
  const computed = (fn, options) => {
284
- return new Computed(fn, options);
227
+ let instance = new Reactive(DIRTY, COMPUTED, undefined);
228
+ instance.changed = options?.changed || changed;
229
+ instance.fn = fn;
230
+ return instance;
285
231
  };
286
232
  const dispose = (dispose) => {
287
233
  if (dispose == null) {
@@ -297,25 +243,33 @@ const dispose = (dispose) => {
297
243
  return dispose;
298
244
  };
299
245
  const effect = (fn) => {
300
- return new Effect(fn);
246
+ let instance = new Reactive(DIRTY, EFFECT, null);
247
+ instance.fn = fn;
248
+ instance.task = () => instance.get();
249
+ update(instance);
250
+ return instance;
301
251
  };
302
- const root = (fn, scheduler) => {
252
+ const root = (fn, scheduler = null) => {
303
253
  let o = observer, s = scope;
304
- if (scheduler === undefined) {
254
+ if (scheduler === null) {
305
255
  if (scope === null) {
306
256
  throw new Error('Reactivity: `root` cannot be created without a task scheduler');
307
257
  }
308
258
  scheduler = scope.scheduler;
309
259
  }
310
260
  observer = null;
311
- scope = new Root(scheduler, fn.length > 0);
261
+ scope = new Reactive(CLEAN, ROOT, null);
262
+ scope.scheduler = scheduler;
263
+ scope.tracking = fn.length > 0;
312
264
  let result = fn(scope);
313
265
  observer = o;
314
266
  scope = s;
315
267
  return result;
316
268
  };
317
269
  const signal = (value, options) => {
318
- return new Signal(value, options);
270
+ let instance = new Reactive(CLEAN, SIGNAL, value);
271
+ instance.changed = options?.changed || changed;
272
+ return instance;
319
273
  };
320
274
  export { computed, dispose, effect, root, signal };
321
- export { Computed, Effect, Root, Signal };
275
+ export { Reactive };
package/build/types.d.ts CHANGED
@@ -1,7 +1,18 @@
1
1
  import { Function, NeverAsync, Prettify } from '@esportsplus/typescript';
2
2
  import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
3
- import { Computed, Effect, Root, Signal } from './signal';
3
+ import { Reactive } from './signal';
4
+ type Base<T> = Omit<Reactive<T>, 'changed' | 'fn' | 'get' | 'scheduler' | 'set' | 'task' | 'tracking'>;
4
5
  type Changed = (a: unknown, b: unknown) => boolean;
6
+ type Computed<T> = {
7
+ changed: Changed;
8
+ fn: NeverAsync<() => T>;
9
+ get(): T;
10
+ } & Base<T>;
11
+ type Effect = {
12
+ fn: NeverAsync<(node: Effect) => void>;
13
+ root: Root;
14
+ task: Function;
15
+ } & Omit<Base<void>, 'value'>;
5
16
  type Event = string;
6
17
  type Listener<D> = {
7
18
  once?: boolean;
@@ -14,7 +25,17 @@ type Object = Record<PropertyKey, unknown>;
14
25
  type Options = {
15
26
  changed?: Changed;
16
27
  };
28
+ type Root = {
29
+ scheduler: Scheduler;
30
+ tracking: boolean;
31
+ value: void;
32
+ } & Omit<Reactive<void>, 'root'>;
17
33
  type Scheduler = (fn: Function) => unknown;
34
+ type Signal<T> = {
35
+ changed: Changed;
36
+ get(): T;
37
+ set(value: T): T;
38
+ } & Base<T>;
18
39
  type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
19
40
  type Type = typeof COMPUTED | typeof EFFECT | typeof ROOT | typeof SIGNAL;
20
41
  export type { Changed, Computed, Effect, Event, Function, Listener, Object, Options, NeverAsync, Prettify, Root, Scheduler, Signal, State, Type };
package/package.json CHANGED
@@ -16,5 +16,5 @@
16
16
  "prepublishOnly": "npm run build"
17
17
  },
18
18
  "types": "build/index.d.ts",
19
- "version": "0.1.13"
19
+ "version": "0.1.15"
20
20
  }
package/src/signal.ts CHANGED
@@ -1,28 +1,33 @@
1
1
  import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
2
- import { Changed, Event, Function, Listener, NeverAsync, Options, Scheduler, State, Type } from './types';
2
+ import { Computed, Changed, Effect, Event, Function, Listener, NeverAsync, Options, Root, Scheduler, Signal, State, Type } from './types';
3
3
  import { isArray } from './utilities';
4
4
 
5
5
 
6
6
  let index = 0,
7
- observer: Core<any> | null = null,
8
- observers: Core<any>[] | null = null,
7
+ observer: Reactive<any> | null = null,
8
+ observers: Reactive<any>[] | null = null,
9
9
  scope: Root | null = null;
10
10
 
11
11
 
12
- class Core<T> {
12
+ class Reactive<T> {
13
+ changed: Changed | null = null;
14
+ fn: Computed<T>['fn'] | Effect['fn'] | null = null;
13
15
  listeners: Record<Event, (Listener<any> | null)[]> | null = null;
14
- observers: Core<any>[] | null = null;
16
+ observers: Reactive<any>[] | null = null;
15
17
  root: Root | null;
16
- sources: Core<any>[] | null = null;
18
+ scheduler: Scheduler | null = null;
19
+ sources: Reactive<any>[] | null = null;
17
20
  state: State;
18
- updating: boolean = false;
21
+ task: Function | null = null;
22
+ tracking: boolean | null = null;
23
+ type: Type;
19
24
  value: T;
20
25
 
21
26
 
22
- constructor(state: State, value: T) {
27
+ constructor(state: State, type: Type, value: T) {
23
28
  let root = null;
24
29
 
25
- if (this.type !== ROOT) {
30
+ if (type !== ROOT) {
26
31
  if (scope !== null) {
27
32
  root = scope;
28
33
  }
@@ -31,7 +36,7 @@ class Core<T> {
31
36
  }
32
37
 
33
38
  if (root == null) {
34
- if (this.type === EFFECT) {
39
+ if (type === EFFECT) {
35
40
  throw new Error(`Reactivity: 'effect' cannot be created without a reactive root`);
36
41
  }
37
42
  }
@@ -42,22 +47,18 @@ class Core<T> {
42
47
 
43
48
  this.root = root;
44
49
  this.state = state;
50
+ this.type = type;
45
51
  this.value = value;
46
52
  }
47
53
 
48
54
 
49
- get type(): Type | never {
50
- throw new Error(`Reactivity: reactive primitives require 'type' getters`);
51
- }
52
-
53
-
54
55
  dispatch<D>(event: Event, data?: D) {
55
56
  if (this.listeners === null || this.listeners[event] === undefined) {
56
57
  return;
57
58
  }
58
59
 
59
60
  let listeners = this.listeners[event],
60
- parameter = {
61
+ values = {
61
62
  data,
62
63
  value: this.value
63
64
  };
@@ -70,7 +71,7 @@ class Core<T> {
70
71
  }
71
72
 
72
73
  try {
73
- listener(parameter);
74
+ listener(values);
74
75
 
75
76
  if (listener.once !== undefined) {
76
77
  listeners[i] = null;
@@ -97,9 +98,36 @@ class Core<T> {
97
98
  this.state = DISPOSED;
98
99
  }
99
100
 
101
+ get() {
102
+ if (this.state === DISPOSED) {
103
+ return this.value;
104
+ }
105
+
106
+ if (observer !== null) {
107
+ if (observers === null) {
108
+ if (observer.sources !== null && observer.sources[index] == this) {
109
+ index++;
110
+ }
111
+ else {
112
+ observers = [this];
113
+ }
114
+ }
115
+ else {
116
+ observers.push(this);
117
+ }
118
+ }
119
+
120
+ if (this.type === COMPUTED || this.type === EFFECT) {
121
+ sync(this);
122
+ }
123
+
124
+ return this.value;
125
+ }
126
+
100
127
  on<T>(event: Event, listener: Listener<T>) {
101
- if (this.updating) {
102
- listener.once = true;
128
+ // Events cannot be set within effects or computed's
129
+ if (this.state === DIRTY) {
130
+ return;
103
131
  }
104
132
 
105
133
  if (this.listeners === null) {
@@ -128,87 +156,18 @@ class Core<T> {
128
156
  listener.once = true;
129
157
  this.on(event, listener);
130
158
  }
131
- }
132
-
133
- class Computed<T> extends Core<T> {
134
- changed: Changed;
135
- fn: NeverAsync<() => T>;
136
-
137
-
138
- constructor(fn: Computed<T>['fn'], options?: Options) {
139
- super(DIRTY, undefined as T);
140
- this.changed = options?.changed || changed;
141
- this.fn = fn;
142
- }
143
-
144
-
145
- get type(): Type {
146
- return COMPUTED;
147
- }
148
-
149
-
150
- get() {
151
- return read(this);
152
- }
153
- }
154
-
155
- class Effect extends Core<null> {
156
- fn: NeverAsync<(node: Effect) => void>;
157
- task: Function;
158
-
159
-
160
- constructor(fn: Effect['fn']) {
161
- super(DIRTY, null);
162
- this.fn = fn;
163
- this.task = () => read(this);
164
-
165
- update(this);
166
- }
167
-
168
-
169
- get type(): Type {
170
- return EFFECT;
171
- }
172
- }
173
-
174
- class Root extends Core<null> {
175
- scheduler: Scheduler;
176
- tracking: boolean;
177
-
178
-
179
- constructor(scheduler: Scheduler, tracking: boolean) {
180
- super(CLEAN, null);
181
- this.scheduler = scheduler;
182
- this.tracking = tracking;
183
- }
184
-
185
-
186
- get type(): Type {
187
- return ROOT;
188
- }
189
- }
190
-
191
- class Signal<T> extends Core<T> {
192
- changed: Changed;
193
-
194
-
195
- constructor(data: T, options?: Options) {
196
- super(CLEAN, data);
197
- this.changed = options?.changed || changed;
198
- }
199
-
200
-
201
- get type(): Type {
202
- return SIGNAL;
203
- }
204
159
 
160
+ set(value: T): T {
161
+ if (this.type !== SIGNAL && observer !== this) {
162
+ throw new Error(`Reactivity: 'set' method is only available on signals`);
163
+ }
205
164
 
206
- get() {
207
- return read(this);
208
- }
165
+ if (this.changed!(this.value, value)) {
166
+ this.value = value;
167
+ notify(this.observers, DIRTY);
168
+ }
209
169
 
210
- set(value: T): T {
211
- return write(this, value);
170
+ return this.value;
212
171
  }
213
172
  }
214
173
 
@@ -217,7 +176,7 @@ function changed(a: unknown, b: unknown) {
217
176
  return a !== b;
218
177
  }
219
178
 
220
- function notify<T>(nodes: Core<T>[] | null, state: typeof CHECK | typeof DIRTY) {
179
+ function notify<T>(nodes: Reactive<T>[] | null, state: typeof CHECK | typeof DIRTY) {
221
180
  if (nodes === null) {
222
181
  return;
223
182
  }
@@ -236,50 +195,24 @@ function notify<T>(nodes: Core<T>[] | null, state: typeof CHECK | typeof DIRTY)
236
195
  }
237
196
  }
238
197
 
239
- function read<T>(node: Core<T>) {
240
- if (node.state === DISPOSED) {
241
- return node.value;
242
- }
243
-
244
- if (observer !== null) {
245
- if (observers === null) {
246
- if (observer.sources !== null && observer.sources[index] == node) {
247
- index++;
248
- }
249
- else {
250
- observers = [node];
251
- }
252
- }
253
- else {
254
- observers.push(node);
255
- }
256
- }
257
-
258
- if (node.type === COMPUTED || node.type === EFFECT) {
259
- sync(node);
260
- }
261
-
262
- return node.value;
263
- }
264
-
265
- function removeSourceObservers<T>(node: Core<T>, start: number) {
198
+ function removeSourceObservers<T>(node: Reactive<T>, start: number) {
266
199
  if (node.sources === null) {
267
200
  return;
268
201
  }
269
202
 
270
203
  for (let i = start, n = node.sources.length; i < n; i++) {
271
- let source = node.sources[i];
204
+ let observers = node.sources[i].observers;
272
205
 
273
- if (source.observers === null) {
206
+ if (observers === null) {
274
207
  continue;
275
208
  }
276
209
 
277
- source.observers[source.observers.indexOf(node)] = source.observers[source.observers.length - 1];
278
- source.observers.pop();
210
+ observers[observers.indexOf(node)] = observers[observers.length - 1];
211
+ observers.pop();
279
212
  }
280
213
  }
281
214
 
282
- function sync<T>(node: Core<T>) {
215
+ function sync<T>(node: Reactive<T>) {
283
216
  if (node.state === CHECK && node.sources !== null) {
284
217
  for (let i = 0, n = node.sources.length; i < n; i++) {
285
218
  sync(node.sources[i]);
@@ -294,14 +227,14 @@ function sync<T>(node: Core<T>) {
294
227
  }
295
228
 
296
229
  if (node.state === DIRTY) {
297
- update(node as Computed<T> | Effect);
230
+ update(node);
298
231
  }
299
232
  else {
300
233
  node.state = CLEAN;
301
234
  }
302
235
  }
303
236
 
304
- function update<T>(node: Computed<T> | Effect) {
237
+ function update<T>(node: Reactive<T>) {
305
238
  let i = index,
306
239
  o = observer,
307
240
  os = observers;
@@ -312,13 +245,10 @@ function update<T>(node: Computed<T> | Effect) {
312
245
 
313
246
  try {
314
247
  node.dispatch('update');
315
- node.updating = true;
316
248
 
317
249
  // @ts-ignore
318
250
  let value = node.fn.call(node);
319
251
 
320
- node.updating = false;
321
-
322
252
  if (observers) {
323
253
  removeSourceObservers(node, index);
324
254
 
@@ -336,7 +266,7 @@ function update<T>(node: Computed<T> | Effect) {
336
266
  for (let i = index, n = node.sources.length; i < n; i++) {
337
267
  let source = node.sources[i];
338
268
 
339
- if (!source.observers) {
269
+ if (source.observers === null) {
340
270
  source.observers = [node];
341
271
  }
342
272
  else {
@@ -350,12 +280,7 @@ function update<T>(node: Computed<T> | Effect) {
350
280
  }
351
281
 
352
282
  if (node.type === COMPUTED) {
353
- write(node as Computed<T>, value as T);
354
- }
355
- }
356
- catch {
357
- if (node.state === DIRTY) {
358
- removeSourceObservers(node, 0);
283
+ node.set(value as T);
359
284
  }
360
285
  }
361
286
  finally {
@@ -367,18 +292,14 @@ function update<T>(node: Computed<T> | Effect) {
367
292
  node.state = CLEAN;
368
293
  }
369
294
 
370
- function write<T>(node: Computed<T> | Signal<T>, value: T) {
371
- if (node.changed(node.value, value)) {
372
- node.value = value;
373
- notify(node.observers, DIRTY);
374
- }
375
295
 
376
- return value;
377
- }
296
+ const computed = <T>(fn: Computed<T>['fn'], options?: Options) => {
297
+ let instance = new Reactive(DIRTY, COMPUTED, undefined as T);
378
298
 
299
+ instance.changed = options?.changed || changed;
300
+ instance.fn = fn;
379
301
 
380
- const computed = <T>(fn: Computed<T>['fn'], options?: Options) => {
381
- return new Computed(fn, options);
302
+ return instance as Computed<T>;
382
303
  };
383
304
 
384
305
  const dispose = <T extends { dispose: VoidFunction }>(dispose?: T[] | T | null) => {
@@ -397,14 +318,21 @@ const dispose = <T extends { dispose: VoidFunction }>(dispose?: T[] | T | null)
397
318
  };
398
319
 
399
320
  const effect = (fn: Effect['fn']) => {
400
- return new Effect(fn);
321
+ let instance = new Reactive(DIRTY, EFFECT, null);
322
+
323
+ instance.fn = fn;
324
+ instance.task = () => instance.get();
325
+
326
+ update(instance);
327
+
328
+ return instance as Effect;
401
329
  };
402
330
 
403
- const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler?: Scheduler) => {
331
+ const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler: Scheduler | null = null) => {
404
332
  let o = observer,
405
333
  s = scope;
406
334
 
407
- if (scheduler === undefined) {
335
+ if (scheduler === null) {
408
336
  if (scope === null) {
409
337
  throw new Error('Reactivity: `root` cannot be created without a task scheduler');
410
338
  }
@@ -413,7 +341,10 @@ const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler?: Scheduler) => {
413
341
  }
414
342
 
415
343
  observer = null;
416
- scope = new Root(scheduler, fn.length > 0);
344
+
345
+ scope = new Reactive(CLEAN, ROOT, null) as any as Root;
346
+ scope.scheduler = scheduler as Scheduler;
347
+ scope.tracking = fn.length > 0;
417
348
 
418
349
  let result = fn(scope);
419
350
 
@@ -424,9 +355,13 @@ const root = <T>(fn: NeverAsync<(root: Root) => T>, scheduler?: Scheduler) => {
424
355
  };
425
356
 
426
357
  const signal = <T>(value: T, options?: Options) => {
427
- return new Signal(value, options);
358
+ let instance = new Reactive(CLEAN, SIGNAL, value);
359
+
360
+ instance.changed = options?.changed || changed;
361
+
362
+ return instance as Signal<T>;
428
363
  };
429
364
 
430
365
 
431
366
  export { computed, dispose, effect, root, signal };
432
- export { Computed, Effect, Root, Signal };
367
+ export { Reactive };
package/src/types.ts CHANGED
@@ -1,10 +1,24 @@
1
1
  import { Function, NeverAsync, Prettify } from '@esportsplus/typescript'
2
2
  import { CHECK, CLEAN, COMPUTED, DIRTY, DISPOSED, EFFECT, ROOT, SIGNAL } from './constants';
3
- import { Computed, Effect, Root, Signal } from './signal';
3
+ import { Reactive } from './signal';
4
4
 
5
5
 
6
+ type Base<T> = Omit<Reactive<T>, 'changed' | 'fn' | 'get' | 'scheduler' | 'set' | 'task' | 'tracking'>;
7
+
6
8
  type Changed = (a: unknown, b: unknown) => boolean;
7
9
 
10
+ type Computed<T> = {
11
+ changed: Changed;
12
+ fn: NeverAsync<() => T>;
13
+ get(): T;
14
+ } & Base<T>;
15
+
16
+ type Effect = {
17
+ fn: NeverAsync<(node: Effect) => void>;
18
+ root: Root;
19
+ task: Function;
20
+ } & Omit<Base<void>, 'value'>;
21
+
8
22
  type Event = string;
9
23
 
10
24
  type Listener<D> = {
@@ -19,8 +33,20 @@ type Options = {
19
33
  changed?: Changed;
20
34
  };
21
35
 
36
+ type Root = {
37
+ scheduler: Scheduler;
38
+ tracking: boolean;
39
+ value: void;
40
+ } & Omit<Reactive<void>, 'root'>;
41
+
22
42
  type Scheduler = (fn: Function) => unknown;
23
43
 
44
+ type Signal<T> = {
45
+ changed: Changed;
46
+ get(): T;
47
+ set(value: T): T;
48
+ } & Base<T>;
49
+
24
50
  type State = typeof CHECK | typeof CLEAN | typeof DIRTY | typeof DISPOSED;
25
51
 
26
52
  type Type = typeof COMPUTED | typeof EFFECT | typeof ROOT | typeof SIGNAL;