@esportsplus/reactivity 0.11.8 → 0.12.1

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.
@@ -1,4 +1,7 @@
1
- declare const REACTIVE: unique symbol;
1
+ declare const COMPUTED: unique symbol;
2
+ declare const REACTIVE_ARRAY: unique symbol;
3
+ declare const REACTIVE_OBJECT: unique symbol;
4
+ declare const SIGNAL: unique symbol;
2
5
  declare const STABILIZER_IDLE = 0;
3
6
  declare const STABILIZER_RESCHEDULE = 1;
4
7
  declare const STABILIZER_RUNNING = 2;
@@ -8,4 +11,4 @@ declare const STATE_CHECK: number;
8
11
  declare const STATE_DIRTY: number;
9
12
  declare const STATE_RECOMPUTING: number;
10
13
  declare const STATE_IN_HEAP: number;
11
- export { REACTIVE, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
14
+ export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
@@ -1,4 +1,7 @@
1
- const REACTIVE = Symbol('reactive');
1
+ const COMPUTED = Symbol('computed');
2
+ const REACTIVE_ARRAY = Symbol('reactive.array');
3
+ const REACTIVE_OBJECT = Symbol('reactive.object');
4
+ const SIGNAL = Symbol('signal');
2
5
  const STABILIZER_IDLE = 0;
3
6
  const STABILIZER_RESCHEDULE = 1;
4
7
  const STABILIZER_RUNNING = 2;
@@ -8,4 +11,4 @@ const STATE_CHECK = 1 << 0;
8
11
  const STATE_DIRTY = 1 << 1;
9
12
  const STATE_RECOMPUTING = 1 << 2;
10
13
  const STATE_IN_HEAP = 1 << 3;
11
- export { REACTIVE, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
14
+ export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING };
package/build/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as reactive } from './reactive/index.js';
2
+ export { STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED } from './constants.js';
2
3
  export * from './system.js';
3
4
  export * from './types.js';
package/build/index.js CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as reactive } from './reactive/index.js';
2
+ export { STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED } from './constants.js';
2
3
  export * from './system.js';
3
4
  export * from './types.js';
@@ -1,6 +1,6 @@
1
+ import { REACTIVE_ARRAY } from '../constants.js';
1
2
  import { Computed, Infer } from '../types.js';
2
3
  import { ReactiveObject } from './object.js';
3
- import { Disposable } from './disposable.js';
4
4
  type API<T> = Infer<T>[] & ReactiveArray<T>;
5
5
  type Events<T> = {
6
6
  pop: {
@@ -27,19 +27,22 @@ type Events<T> = {
27
27
  items: Item<T>[];
28
28
  };
29
29
  };
30
- type Item<T> = Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never> | T;
30
+ type Item<T> = T | Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never>;
31
31
  type Listener<V> = {
32
32
  once?: boolean;
33
33
  (value: V): void;
34
34
  };
35
35
  type Value<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : T extends Array<infer U> ? API<U> : T;
36
- declare class ReactiveArray<T> extends Disposable {
36
+ declare class ReactiveArray<T> {
37
+ [REACTIVE_ARRAY]: boolean;
38
+ disposables: number;
37
39
  private data;
38
40
  private listeners;
39
41
  private proxy;
40
42
  constructor(data: Item<T>[], proxy: API<T>);
41
43
  get length(): number;
42
44
  set length(n: number);
45
+ private cleanup;
43
46
  at(i: number): T | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never>;
44
47
  dispatch<K extends keyof Events<T>, V>(event: K, value?: V): void;
45
48
  dispose(): void;
@@ -54,5 +57,7 @@ declare class ReactiveArray<T> extends Disposable {
54
57
  splice(start: number, deleteCount?: number, ...input: T[]): Item<T>[];
55
58
  unshift(...input: T[]): number;
56
59
  }
60
+ declare const isReactiveArray: (value: any) => value is ReactiveArray<any>;
57
61
  export default function array<T>(input: T[]): API<T>;
62
+ export { isReactiveArray };
58
63
  export type { API as ReactiveArray };
@@ -1,13 +1,14 @@
1
- import { isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
1
+ import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
2
+ import { REACTIVE_ARRAY } from '../constants.js';
2
3
  import { computed, dispose, isComputed, read } from '../system.js';
3
- import object from './object.js';
4
- import { Disposable } from './disposable.js';
5
- class ReactiveArray extends Disposable {
4
+ import object, { isReactiveObject } from './object.js';
5
+ class ReactiveArray {
6
+ [REACTIVE_ARRAY] = true;
7
+ disposables = 0;
6
8
  data;
7
9
  listeners = null;
8
10
  proxy;
9
11
  constructor(data, proxy) {
10
- super();
11
12
  this.data = data;
12
13
  this.proxy = proxy;
13
14
  }
@@ -20,6 +21,14 @@ class ReactiveArray extends Disposable {
20
21
  }
21
22
  this.splice(n);
22
23
  }
24
+ cleanup(item) {
25
+ if (isReactiveObject(item)) {
26
+ item.dispose();
27
+ }
28
+ else if (isComputed(item)) {
29
+ dispose(item);
30
+ }
31
+ }
23
32
  at(i) {
24
33
  let value = this.data[i];
25
34
  if (isComputed(value)) {
@@ -49,17 +58,9 @@ class ReactiveArray extends Disposable {
49
58
  }
50
59
  }
51
60
  dispose() {
52
- let data = this.data;
53
- for (let i = 0, n = data.length; i < n; i++) {
54
- let value = data[i];
55
- if (isInstanceOf(value, Disposable)) {
56
- value.dispose();
57
- }
58
- else if (isComputed(value)) {
59
- dispose(value);
60
- }
61
+ for (let i = 0, n = this.data.length; i < n; i++) {
62
+ this.cleanup(this.data[i]);
61
63
  }
62
- this.listeners = null;
63
64
  }
64
65
  map(fn, i, n) {
65
66
  let { data, proxy } = this, values = [];
@@ -103,9 +104,7 @@ class ReactiveArray extends Disposable {
103
104
  pop() {
104
105
  let item = this.data.pop();
105
106
  if (item !== undefined) {
106
- if (isComputed(item)) {
107
- dispose(item);
108
- }
107
+ this.cleanup(item);
109
108
  this.dispatch('pop', { item });
110
109
  }
111
110
  return item;
@@ -123,9 +122,7 @@ class ReactiveArray extends Disposable {
123
122
  shift() {
124
123
  let item = this.data.shift();
125
124
  if (item !== undefined) {
126
- if (isComputed(item)) {
127
- dispose(item);
128
- }
125
+ this.cleanup(item);
129
126
  this.dispatch('shift', { item });
130
127
  }
131
128
  return item;
@@ -139,10 +136,7 @@ class ReactiveArray extends Disposable {
139
136
  let items = factory(input), removed = this.data.splice(start, deleteCount, ...items);
140
137
  if (items.length > 0 || removed.length > 0) {
141
138
  for (let i = 0, n = removed.length; i < n; i++) {
142
- let item = removed[i];
143
- if (isComputed(item)) {
144
- dispose(item);
145
- }
139
+ this.cleanup(removed[i]);
146
140
  }
147
141
  this.dispatch('splice', {
148
142
  deleteCount,
@@ -174,6 +168,9 @@ function factory(input) {
174
168
  }
175
169
  return items;
176
170
  }
171
+ const isReactiveArray = (value) => {
172
+ return isObject(value) && REACTIVE_ARRAY in value;
173
+ };
177
174
  export default function array(input) {
178
175
  let proxy = new Proxy({}, {
179
176
  get(_, key) {
@@ -184,29 +181,24 @@ export default function array(input) {
184
181
  }
185
182
  return value;
186
183
  }
187
- else if (key in a) {
188
- return a[key];
184
+ else if (key in array) {
185
+ return array[key];
189
186
  }
190
187
  return wrapped[key];
191
188
  },
192
189
  set(_, key, value) {
193
190
  if (isNumber(key)) {
194
- let host = wrapped[key];
195
- if (host === undefined || !isComputed(host)) {
196
- a.splice(key, 1, value);
197
- }
198
- else {
199
- return false;
200
- }
191
+ array.splice(key, 1, value);
201
192
  return true;
202
193
  }
203
194
  else if (key === 'length') {
204
- return a.length = value;
195
+ return array.length = value;
205
196
  }
206
197
  return false;
207
198
  }
208
199
  }), wrapped = factory(input);
209
- let a = new ReactiveArray(wrapped, proxy);
200
+ let array = new ReactiveArray(wrapped, proxy);
210
201
  return proxy;
211
202
  }
212
203
  ;
204
+ export { isReactiveArray };
@@ -3,10 +3,7 @@ import object from './object.js';
3
3
  type API<T> = T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
4
4
  type Input<T> = T extends {
5
5
  dispose: any;
6
- } | {
7
- signals: any;
8
- } ? {
9
- never: '[ dispose, signals ] are reserved keys';
10
- } : T;
6
+ } ? never : T extends Record<PropertyKey, unknown> ? T : T extends unknown[] ? Input<T[number]>[] : never;
11
7
  declare const _default: <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>) => API<T>;
12
8
  export default _default;
9
+ export type { Input };
@@ -1,12 +1,22 @@
1
1
  import { isArray, isObject } from '@esportsplus/utilities';
2
+ import { onCleanup, root } from '../system.js';
2
3
  import array from './array.js';
3
4
  import object from './object.js';
4
5
  export default (input) => {
5
- if (isArray(input)) {
6
- return array(input);
7
- }
8
- else if (isObject(input)) {
9
- return object(input);
10
- }
11
- throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
6
+ let value;
7
+ return root(() => {
8
+ if (isArray(input)) {
9
+ value = array(input);
10
+ }
11
+ else if (isObject(input)) {
12
+ value = object(input);
13
+ }
14
+ if (value) {
15
+ if (root.disposables) {
16
+ onCleanup(() => value.dispose());
17
+ }
18
+ return value;
19
+ }
20
+ throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
21
+ });
12
22
  };
@@ -1,13 +1,15 @@
1
1
  import { Prettify } from '@esportsplus/utilities';
2
2
  import { Infer } from '../types.js';
3
- import { Disposable } from './disposable.js';
3
+ import { REACTIVE_OBJECT } from '../constants.js';
4
4
  type API<T extends Record<PropertyKey, unknown>> = Prettify<{
5
5
  [K in keyof T]: Infer<T[K]>;
6
6
  }> & ReactiveObject<T>;
7
- declare class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable {
8
- private disposable;
7
+ declare class ReactiveObject<T extends Record<PropertyKey, unknown>> {
8
+ [REACTIVE_OBJECT]: boolean;
9
9
  constructor(data: T);
10
10
  dispose(): void;
11
11
  }
12
- export default function object<T extends Record<PropertyKey, unknown>>(input: T): API<T>;
12
+ declare const isReactiveObject: (value: any) => value is ReactiveObject<any>;
13
+ export default function object<T extends Record<PropertyKey, unknown>>(input: T): ReactiveObject<T>;
14
+ export { isReactiveObject };
13
15
  export type { API as ReactiveObject };
@@ -1,26 +1,18 @@
1
- import { defineProperty, isArray, isFunction, isInstanceOf, isPromise } from '@esportsplus/utilities';
2
- import array from './array.js';
3
- import { computed, dispose, effect, read, root, signal } from '../system.js';
4
- import { Disposable } from './disposable.js';
5
- let { set } = signal;
6
- class ReactiveObject extends Disposable {
7
- disposable = {};
1
+ import { defineProperty, isArray, isFunction, isObject, isPromise } from '@esportsplus/utilities';
2
+ import { computed, dispose, effect, isComputed, read, root, set, signal } from '../system.js';
3
+ import { REACTIVE_OBJECT } from '../constants.js';
4
+ import array, { isReactiveArray } from './array.js';
5
+ class ReactiveObject {
6
+ [REACTIVE_OBJECT] = true;
8
7
  constructor(data) {
9
- super();
10
- let disposable = this.disposable, triggers = {};
11
8
  for (let key in data) {
12
9
  let value = data[key];
13
10
  if (isArray(value)) {
14
- let a = disposable[key] = array(value), t = triggers[key] = signal(false);
11
+ let a = array(value);
15
12
  defineProperty(this, key, {
16
13
  enumerable: true,
17
14
  get() {
18
- read(t);
19
15
  return a;
20
- },
21
- set(v) {
22
- a = disposable[key] = array(v);
23
- set(t, !!t.value);
24
16
  }
25
17
  });
26
18
  }
@@ -31,7 +23,7 @@ class ReactiveObject extends Disposable {
31
23
  get() {
32
24
  if (c === undefined) {
33
25
  root(() => {
34
- c = disposable[key] = computed(value);
26
+ c = computed(value);
35
27
  if (isPromise(c.value)) {
36
28
  let factory = c, version = 0;
37
29
  c = signal(undefined);
@@ -66,19 +58,23 @@ class ReactiveObject extends Disposable {
66
58
  }
67
59
  }
68
60
  dispose() {
69
- for (let key in this.disposable) {
70
- let value = this.disposable[key];
71
- if (isInstanceOf(value, Disposable)) {
61
+ let value;
62
+ for (let key in this) {
63
+ value = this[key];
64
+ if (isReactiveArray(value) || isReactiveObject(value)) {
72
65
  value.dispose();
73
66
  }
74
- else {
67
+ else if (isComputed(value)) {
75
68
  dispose(value);
76
69
  }
77
70
  }
78
- this.disposable = {};
79
71
  }
80
72
  }
73
+ const isReactiveObject = (value) => {
74
+ return isObject(value) && REACTIVE_OBJECT in value;
75
+ };
81
76
  export default function object(input) {
82
77
  return new ReactiveObject(input);
83
78
  }
84
79
  ;
80
+ export { isReactiveObject };
package/build/system.d.ts CHANGED
@@ -6,9 +6,11 @@ declare const isComputed: (value: unknown) => value is Computed<unknown>;
6
6
  declare const isSignal: (value: unknown) => value is Signal<unknown>;
7
7
  declare const onCleanup: (fn: VoidFunction) => typeof fn;
8
8
  declare const read: <T>(node: Signal<T> | Computed<T>) => T;
9
- declare const root: <T>(fn: () => T) => T;
10
- declare const signal: {
11
- <T>(value: T): Signal<T>;
12
- set<T>(signal: Signal<T>, value: T): void;
9
+ declare const root: {
10
+ <T>(fn: () => T): T;
11
+ disposables: number;
13
12
  };
14
- export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal };
13
+ declare const set: <T>(signal: Signal<T>, value: T) => void;
14
+ declare const signal: <T>(value: T) => Signal<T>;
15
+ export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, set, signal };
16
+ export type { Computed, Signal };
package/build/system.js CHANGED
@@ -1,20 +1,20 @@
1
1
  import { isArray, isObject } from '@esportsplus/utilities';
2
- import { REACTIVE, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
3
- let depth = 0, heap = new Array(2000), index = 0, length = 0, notified = false, observer = null, scheduler = queueMicrotask, stabilizer = STABILIZER_IDLE, version = 0;
4
- function cleanup(node) {
5
- if (!node.cleanup) {
2
+ import { COMPUTED, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
3
+ let depth = 0, heap = new Array(2048), index = 0, length = 0, microtask = queueMicrotask, notified = false, observer = null, stabilizer = STABILIZER_IDLE, version = 0;
4
+ function cleanup(computed) {
5
+ if (!computed.cleanup) {
6
6
  return;
7
7
  }
8
- let cleanup = node.cleanup;
9
- if (isArray(cleanup)) {
10
- for (let i = 0; i < cleanup.length; i++) {
11
- cleanup[i]();
8
+ let value = computed.cleanup;
9
+ if (isArray(value)) {
10
+ for (let i = 0, n = value.length; i < n; i++) {
11
+ value[i]();
12
12
  }
13
13
  }
14
14
  else {
15
- cleanup();
15
+ value();
16
16
  }
17
- node.cleanup = null;
17
+ computed.cleanup = null;
18
18
  }
19
19
  function deleteFromHeap(computed) {
20
20
  let state = computed.state;
@@ -58,7 +58,7 @@ function insertIntoHeap(computed) {
58
58
  if (height > length) {
59
59
  length = height;
60
60
  if (height >= heap.length) {
61
- heap.length *= 1.5;
61
+ heap.length = Math.round(heap.length * 1.5);
62
62
  }
63
63
  }
64
64
  }
@@ -138,11 +138,11 @@ function recompute(computed, del) {
138
138
  depth--;
139
139
  observer = o;
140
140
  computed.state = STATE_NONE;
141
- let depsTail = computed.depsTail, toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
142
- if (toRemove !== null) {
141
+ let depsTail = computed.depsTail, remove = depsTail !== null ? depsTail.nextDep : computed.deps;
142
+ if (remove !== null) {
143
143
  do {
144
- toRemove = unlink(toRemove);
145
- } while (toRemove !== null);
144
+ remove = unlink(remove);
145
+ } while (remove !== null);
146
146
  if (depsTail !== null) {
147
147
  depsTail.nextDep = null;
148
148
  }
@@ -153,11 +153,11 @@ function recompute(computed, del) {
153
153
  if (ok && value !== computed.value) {
154
154
  computed.value = value;
155
155
  for (let c = computed.subs; c !== null; c = c.nextSub) {
156
- let o = c.sub, state = o.state;
156
+ let s = c.sub, state = s.state;
157
157
  if (state & STATE_CHECK) {
158
- o.state = state | STATE_DIRTY;
158
+ s.state = state | STATE_DIRTY;
159
159
  }
160
- insertIntoHeap(o);
160
+ insertIntoHeap(s);
161
161
  }
162
162
  schedule();
163
163
  }
@@ -165,7 +165,7 @@ function recompute(computed, del) {
165
165
  function schedule() {
166
166
  if (stabilizer === STABILIZER_IDLE && !depth) {
167
167
  stabilizer = STABILIZER_SCHEDULED;
168
- scheduler(stabilize);
168
+ microtask(stabilize);
169
169
  }
170
170
  else if (stabilizer === STABILIZER_RUNNING) {
171
171
  stabilizer = STABILIZER_RESCHEDULE;
@@ -184,7 +184,7 @@ function stabilize() {
184
184
  }
185
185
  }
186
186
  if (stabilizer === STABILIZER_RESCHEDULE) {
187
- scheduler(stabilize);
187
+ microtask(stabilize);
188
188
  }
189
189
  else {
190
190
  stabilizer = STABILIZER_IDLE;
@@ -202,11 +202,8 @@ function unlink(link) {
202
202
  if (prevSub !== null) {
203
203
  prevSub.nextSub = nextSub;
204
204
  }
205
- else {
206
- dep.subs = nextSub;
207
- if (nextSub === null && 'fn' in dep) {
208
- dispose(dep);
209
- }
205
+ else if ((dep.subs = nextSub) === null && 'fn' in dep) {
206
+ dispose(dep);
210
207
  }
211
208
  return nextDep;
212
209
  }
@@ -216,9 +213,9 @@ function update(computed) {
216
213
  let dep = link.dep;
217
214
  if ('fn' in dep) {
218
215
  update(dep);
219
- }
220
- if (computed.state & STATE_DIRTY) {
221
- break;
216
+ if (computed.state & STATE_DIRTY) {
217
+ break;
218
+ }
222
219
  }
223
220
  }
224
221
  }
@@ -229,7 +226,7 @@ function update(computed) {
229
226
  }
230
227
  const computed = (fn) => {
231
228
  let self = {
232
- [REACTIVE]: true,
229
+ [COMPUTED]: true,
233
230
  cleanup: null,
234
231
  deps: null,
235
232
  depsTail: null,
@@ -254,9 +251,11 @@ const computed = (fn) => {
254
251
  schedule();
255
252
  }
256
253
  link(self, observer);
254
+ onCleanup(() => dispose(self));
257
255
  }
258
256
  else {
259
257
  recompute(self, false);
258
+ root.disposables++;
260
259
  }
261
260
  return self;
262
261
  };
@@ -276,24 +275,23 @@ const effect = (fn) => {
276
275
  };
277
276
  };
278
277
  const isComputed = (value) => {
279
- return isObject(value) && REACTIVE in value && 'fn' in value;
278
+ return isObject(value) && COMPUTED in value;
280
279
  };
281
280
  const isSignal = (value) => {
282
- return isObject(value) && REACTIVE in value && 'fn' in value === false;
281
+ return isObject(value) && SIGNAL in value;
283
282
  };
284
283
  const onCleanup = (fn) => {
285
284
  if (!observer) {
286
285
  return fn;
287
286
  }
288
- let node = observer;
289
- if (!node.cleanup) {
290
- node.cleanup = fn;
287
+ if (!observer.cleanup) {
288
+ observer.cleanup = fn;
291
289
  }
292
- else if (isArray(node.cleanup)) {
293
- node.cleanup.push(fn);
290
+ else if (isArray(observer.cleanup)) {
291
+ observer.cleanup.push(fn);
294
292
  }
295
293
  else {
296
- node.cleanup = [node.cleanup, fn];
294
+ observer.cleanup = [observer.cleanup, fn];
297
295
  }
298
296
  return fn;
299
297
  };
@@ -322,21 +320,16 @@ const read = (node) => {
322
320
  return node.value;
323
321
  };
324
322
  const root = (fn) => {
325
- let o = observer;
323
+ let d = root.disposables, o = observer;
326
324
  observer = null;
325
+ root.disposables = 0;
327
326
  let value = fn();
328
327
  observer = o;
328
+ root.disposables = d;
329
329
  return value;
330
330
  };
331
- const signal = (value) => {
332
- return {
333
- [REACTIVE]: true,
334
- subs: null,
335
- subsTail: null,
336
- value,
337
- };
338
- };
339
- signal.set = (signal, value) => {
331
+ root.disposables = 0;
332
+ const set = (signal, value) => {
340
333
  if (signal.value === value) {
341
334
  return;
342
335
  }
@@ -350,4 +343,12 @@ signal.set = (signal, value) => {
350
343
  }
351
344
  schedule();
352
345
  };
353
- export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal };
346
+ const signal = (value) => {
347
+ return {
348
+ [SIGNAL]: true,
349
+ subs: null,
350
+ subsTail: null,
351
+ value,
352
+ };
353
+ };
354
+ export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, set, signal };
package/build/types.d.ts CHANGED
@@ -1,17 +1,19 @@
1
- import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
2
- import { onCleanup } from './system.js';
1
+ import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
3
2
  import { ReactiveArray } from './reactive/array.js';
4
3
  import { ReactiveObject } from './reactive/object.js';
5
- interface Computed<T> extends Signal<T> {
6
- [REACTIVE]: true;
4
+ interface Computed<T> {
5
+ [COMPUTED]: true;
7
6
  cleanup: VoidFunction | VoidFunction[] | null;
8
7
  deps: Link | null;
9
8
  depsTail: Link | null;
10
- fn: (oc?: typeof onCleanup) => T;
9
+ fn: (onCleanup?: (fn: VoidFunction) => typeof fn) => T;
11
10
  height: number;
12
11
  nextHeap: Computed<unknown> | undefined;
13
12
  prevHeap: Computed<unknown>;
14
13
  state: typeof STATE_CHECK | typeof STATE_DIRTY | typeof STATE_IN_HEAP | typeof STATE_NONE | typeof STATE_RECOMPUTING;
14
+ subs: Link | null;
15
+ subsTail: Link | null;
16
+ value: T;
15
17
  }
16
18
  type Infer<T> = T extends (...args: unknown[]) => Promise<infer R> ? R | undefined : T extends (...args: any[]) => infer R ? R : T extends (infer U)[] ? ReactiveArray<U> : T extends ReactiveObject<any> ? T : T extends Record<PropertyKey, unknown> ? {
17
19
  [K in keyof T]: T[K];
@@ -26,7 +28,7 @@ interface Link {
26
28
  }
27
29
  type Reactive<T> = T extends Record<PropertyKey, unknown> ? ReactiveObject<T> : ReactiveArray<T>;
28
30
  type Signal<T> = {
29
- [REACTIVE]: true;
31
+ [SIGNAL]: true;
30
32
  subs: Link | null;
31
33
  subsTail: Link | null;
32
34
  value: T;
package/build/types.js CHANGED
@@ -1 +1 @@
1
- import { REACTIVE } from './constants.js';
1
+ import { COMPUTED, SIGNAL } from './constants.js';
package/package.json CHANGED
@@ -12,7 +12,7 @@
12
12
  "private": false,
13
13
  "type": "module",
14
14
  "types": "build/index.d.ts",
15
- "version": "0.11.8",
15
+ "version": "0.12.1",
16
16
  "scripts": {
17
17
  "build": "tsc && tsc-alias",
18
18
  "-": "-"
package/src/constants.ts CHANGED
@@ -1,4 +1,10 @@
1
- const REACTIVE = Symbol('reactive');
1
+ const COMPUTED = Symbol('computed');
2
+
3
+ const REACTIVE_ARRAY = Symbol('reactive.array');
4
+
5
+ const REACTIVE_OBJECT = Symbol('reactive.object');
6
+
7
+ const SIGNAL = Symbol('signal');
2
8
 
3
9
 
4
10
  const STABILIZER_IDLE = 0;
@@ -22,7 +28,10 @@ const STATE_IN_HEAP = 1 << 3;
22
28
 
23
29
 
24
30
  export {
25
- REACTIVE,
31
+ COMPUTED,
32
+ REACTIVE_ARRAY,
33
+ REACTIVE_OBJECT,
34
+ SIGNAL,
26
35
  STABILIZER_IDLE,
27
36
  STABILIZER_RESCHEDULE,
28
37
  STABILIZER_RUNNING,
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
1
  export { default as reactive } from './reactive';
2
+ export { STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED } from './constants';
2
3
  export * from './system';
3
4
  export * from './types';
@@ -1,8 +1,8 @@
1
- import { isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
1
+ import { isFunction, isNumber, isObject } from '@esportsplus/utilities';
2
+ import { REACTIVE_ARRAY } from '~/constants';
2
3
  import { computed, dispose, isComputed, read } from '~/system';
3
4
  import { Computed, Infer } from '~/types';
4
- import object, { ReactiveObject } from './object';
5
- import { Disposable } from './disposable';
5
+ import object, { isReactiveObject, ReactiveObject } from './object';
6
6
 
7
7
 
8
8
  type API<T> = Infer<T>[] & ReactiveArray<T>;
@@ -33,7 +33,7 @@ type Events<T> = {
33
33
  };
34
34
  };
35
35
 
36
- type Item<T> = Computed<T> | API<T> | ReactiveObject<T extends Record<PropertyKey, unknown> ? T : never> | T;
36
+ type Item<T> = T | Computed<T> | API<T> | ReactiveObject< T extends Record<PropertyKey, unknown> ? T : never >;
37
37
 
38
38
  type Listener<V> = {
39
39
  once?: boolean;
@@ -48,15 +48,16 @@ type Value<T> =
48
48
  : T;
49
49
 
50
50
 
51
- class ReactiveArray<T> extends Disposable {
51
+ class ReactiveArray<T> {
52
+ [REACTIVE_ARRAY] = true;
53
+ disposables: number = 0;
54
+
52
55
  private data: Item<T>[];
53
56
  private listeners: Record<string, (Listener<any> | null)[]> | null = null;
54
57
  private proxy: API<T>;
55
58
 
56
59
 
57
60
  constructor(data: Item<T>[], proxy: API<T>) {
58
- super();
59
-
60
61
  this.data = data;
61
62
  this.proxy = proxy;
62
63
  }
@@ -75,6 +76,16 @@ class ReactiveArray<T> extends Disposable {
75
76
  }
76
77
 
77
78
 
79
+ private cleanup(item: Item<T>) {
80
+ if (isReactiveObject(item)) {
81
+ item.dispose();
82
+ }
83
+ else if (isComputed(item)) {
84
+ dispose(item);
85
+ }
86
+ }
87
+
88
+
78
89
  at(i: number) {
79
90
  let value = this.data[i];
80
91
 
@@ -113,20 +124,9 @@ class ReactiveArray<T> extends Disposable {
113
124
  }
114
125
 
115
126
  dispose() {
116
- let data = this.data;
117
-
118
- for (let i = 0, n = data.length; i < n; i++) {
119
- let value = data[i];
120
-
121
- if (isInstanceOf(value, Disposable)) {
122
- value.dispose();
123
- }
124
- else if (isComputed(value)) {
125
- dispose(value);
126
- }
127
+ for (let i = 0, n = this.data.length; i < n; i++) {
128
+ this.cleanup(this.data[i]);
127
129
  }
128
-
129
- this.listeners = null;
130
130
  }
131
131
 
132
132
  map<R>(
@@ -194,10 +194,7 @@ class ReactiveArray<T> extends Disposable {
194
194
  let item = this.data.pop();
195
195
 
196
196
  if (item !== undefined) {
197
- if (isComputed(item)) {
198
- dispose(item);
199
- }
200
-
197
+ this.cleanup(item);
201
198
  this.dispatch('pop', { item });
202
199
  }
203
200
 
@@ -224,10 +221,7 @@ class ReactiveArray<T> extends Disposable {
224
221
  let item = this.data.shift();
225
222
 
226
223
  if (item !== undefined) {
227
- if (isComputed(item)) {
228
- dispose(item);
229
- }
230
-
224
+ this.cleanup(item);
231
225
  this.dispatch('shift', { item });
232
226
  }
233
227
 
@@ -250,11 +244,7 @@ class ReactiveArray<T> extends Disposable {
250
244
 
251
245
  if (items.length > 0 || removed.length > 0) {
252
246
  for (let i = 0, n = removed.length; i < n; i++) {
253
- let item = removed[i];
254
-
255
- if (isComputed(item)) {
256
- dispose(item);
257
- }
247
+ this.cleanup(removed[i]);
258
248
  }
259
249
 
260
250
  this.dispatch('splice', {
@@ -299,6 +289,11 @@ function factory<T>(input: T[]) {
299
289
  }
300
290
 
301
291
 
292
+ const isReactiveArray = (value: any): value is ReactiveArray<any> => {
293
+ return isObject(value) && REACTIVE_ARRAY in value;
294
+ };
295
+
296
+
302
297
  export default function array<T>(input: T[]) {
303
298
  let proxy = new Proxy({}, {
304
299
  get(_: any, key: any) {
@@ -311,27 +306,19 @@ export default function array<T>(input: T[]) {
311
306
 
312
307
  return value;
313
308
  }
314
- else if (key in a) {
315
- return a[key as keyof typeof a];
309
+ else if (key in array) {
310
+ return array[key as keyof typeof array];
316
311
  }
317
312
 
318
313
  return wrapped[key];
319
314
  },
320
315
  set(_: any, key: any, value: any) {
321
316
  if (isNumber(key)) {
322
- let host = wrapped[key];
323
-
324
- if (host === undefined || !isComputed(host)) {
325
- a.splice(key, 1, value);
326
- }
327
- else {
328
- return false;
329
- }
330
-
317
+ array.splice(key, 1, value);
331
318
  return true;
332
319
  }
333
320
  else if (key === 'length') {
334
- return a.length = value;
321
+ return array.length = value;
335
322
  }
336
323
 
337
324
  return false;
@@ -339,8 +326,9 @@ export default function array<T>(input: T[]) {
339
326
  }) as API<T>,
340
327
  wrapped = factory(input);
341
328
 
342
- let a = new ReactiveArray(wrapped, proxy);
329
+ let array = new ReactiveArray(wrapped, proxy);
343
330
 
344
331
  return proxy;
345
332
  };
333
+ export { isReactiveArray };
346
334
  export type { API as ReactiveArray };
@@ -1,4 +1,5 @@
1
1
  import { isArray, isObject } from '@esportsplus/utilities';
2
+ import { onCleanup, root } from '~/system';
2
3
  import array from './array';
3
4
  import object from './object';
4
5
 
@@ -11,18 +12,35 @@ type API<T> =
11
12
  : never;
12
13
 
13
14
  type Input<T> =
14
- T extends { dispose: any } | { signals: any }
15
- ? { never: '[ dispose, signals ] are reserved keys' }
16
- : T;
15
+ T extends { dispose: any }
16
+ ? never
17
+ : T extends Record<PropertyKey, unknown>
18
+ ? T
19
+ : T extends unknown[]
20
+ ? Input<T[number]>[]
21
+ : never;
17
22
 
18
23
 
19
24
  export default <T extends Record<PropertyKey, unknown> | unknown[]>(input: Input<T>): API<T> => {
20
- if (isArray(input)) {
21
- return array(input) as API<T>;
22
- }
23
- else if (isObject(input)) {
24
- return object(input) as API<T>;
25
- }
26
-
27
- throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
28
- };
25
+ let value: API<T> | undefined;
26
+
27
+ return root(() => {
28
+ if (isArray(input)) {
29
+ value = array(input) as API<T>;
30
+ }
31
+ else if (isObject(input)) {
32
+ value = object(input) as API<T>;
33
+ }
34
+
35
+ if (value) {
36
+ if (root.disposables) {
37
+ onCleanup(() => value!.dispose());
38
+ }
39
+
40
+ return value;
41
+ }
42
+
43
+ throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
44
+ });
45
+ };
46
+ export type { Input };
@@ -1,45 +1,28 @@
1
- import { defineProperty, isArray, isFunction, isInstanceOf, isPromise, Prettify } from '@esportsplus/utilities';
2
- import array, { ReactiveArray } from './array';
3
- import { computed, dispose, effect, read, root, signal } from '~/system';
1
+ import { defineProperty, isArray, isFunction, isObject, isPromise, Prettify } from '@esportsplus/utilities';
2
+ import { computed, dispose, effect, isComputed, read, root, set, signal } from '~/system';
4
3
  import { Computed, Infer, Signal } from '~/types';
5
- import { Disposable } from './disposable';
4
+ import { REACTIVE_OBJECT } from '~/constants';
5
+ import array, { isReactiveArray } from './array';
6
6
 
7
7
 
8
8
  type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: Infer<T[K]> }> & ReactiveObject<T>;
9
9
 
10
10
 
11
- let { set } = signal;
12
-
13
-
14
- class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable {
15
- private disposable: Record<
16
- PropertyKey,
17
- Computed<any> | ReactiveArray<any> | ReactiveObject<any>
18
- > = {};
11
+ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
12
+ [REACTIVE_OBJECT] = true;
19
13
 
20
14
 
21
15
  constructor(data: T) {
22
- super();
23
-
24
- let disposable = this.disposable,
25
- triggers: Record<string, Signal<boolean>> = {};
26
-
27
16
  for (let key in data) {
28
17
  let value = data[key];
29
18
 
30
19
  if (isArray(value)) {
31
- let a = disposable[key] = array(value),
32
- t = triggers[key] = signal(false);
20
+ let a = array(value);
33
21
 
34
22
  defineProperty(this, key, {
35
23
  enumerable: true,
36
24
  get() {
37
- read(t);
38
25
  return a;
39
- },
40
- set(v: typeof value) {
41
- a = disposable[key] = array(v);
42
- set(t, !!t.value);
43
26
  }
44
27
  });
45
28
  }
@@ -51,7 +34,7 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
51
34
  get() {
52
35
  if (c === undefined) {
53
36
  root(() => {
54
- c = disposable[key] = computed(value as Computed<T[typeof key]>['fn']);
37
+ c = computed(value as Computed<T[typeof key]>['fn']);
55
38
 
56
39
  if (isPromise(c.value)) {
57
40
  let factory = c,
@@ -67,7 +50,7 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
67
50
  return;
68
51
  }
69
52
 
70
- set(c!, value);
53
+ set(c as Signal<typeof value>, value);
71
54
  });
72
55
  });
73
56
  }
@@ -96,23 +79,29 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
96
79
 
97
80
 
98
81
  dispose() {
99
- for (let key in this.disposable) {
100
- let value = this.disposable[key];
82
+ let value;
101
83
 
102
- if (isInstanceOf(value, Disposable)) {
84
+ for (let key in this) {
85
+ value = this[key];
86
+
87
+ if (isReactiveArray(value) || isReactiveObject(value)) {
103
88
  value.dispose();
104
89
  }
105
- else {
90
+ else if (isComputed(value)) {
106
91
  dispose(value);
107
92
  }
108
93
  }
109
-
110
- this.disposable = {};
111
94
  }
112
95
  }
113
96
 
114
97
 
98
+ const isReactiveObject = (value: any): value is ReactiveObject<any> => {
99
+ return isObject(value) && REACTIVE_OBJECT in value;
100
+ };
101
+
102
+
115
103
  export default function object<T extends Record<PropertyKey, unknown>>(input: T) {
116
- return new ReactiveObject(input) as API<T>;
104
+ return new ReactiveObject<T>(input);
117
105
  };
106
+ export { isReactiveObject };
118
107
  export type { API as ReactiveObject };
package/src/system.ts CHANGED
@@ -1,40 +1,40 @@
1
1
  import { isArray, isObject } from '@esportsplus/utilities';
2
2
  import {
3
- REACTIVE,
3
+ COMPUTED, SIGNAL,
4
4
  STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
5
5
  STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING
6
6
  } from './constants';
7
- import { Computed, Link, Signal, } from './types';
7
+ import { Computed, Link, Signal } from './types';
8
8
 
9
9
 
10
10
  let depth = 0,
11
- heap: (Computed<unknown> | undefined)[] = new Array(2000),
11
+ heap: (Computed<unknown> | undefined)[] = new Array(2048),
12
12
  index = 0,
13
13
  length = 0,
14
+ microtask = queueMicrotask,
14
15
  notified = false,
15
16
  observer: Computed<unknown> | null = null,
16
- scheduler = queueMicrotask,
17
17
  stabilizer = STABILIZER_IDLE,
18
18
  version = 0;
19
19
 
20
20
 
21
- function cleanup<T>(node: Computed<T>): void {
22
- if (!node.cleanup) {
21
+ function cleanup<T>(computed: Computed<T>): void {
22
+ if (!computed.cleanup) {
23
23
  return;
24
24
  }
25
25
 
26
- let cleanup = node.cleanup;
26
+ let value = computed.cleanup;
27
27
 
28
- if (isArray(cleanup)) {
29
- for (let i = 0; i < cleanup.length; i++) {
30
- cleanup[i]();
28
+ if (isArray(value)) {
29
+ for (let i = 0, n = value.length; i < n; i++) {
30
+ value[i]();
31
31
  }
32
32
  }
33
33
  else {
34
- cleanup();
34
+ value();
35
35
  }
36
36
 
37
- node.cleanup = null;
37
+ computed.cleanup = null;
38
38
  }
39
39
 
40
40
  function deleteFromHeap<T>(computed: Computed<T>) {
@@ -98,7 +98,7 @@ function insertIntoHeap<T>(computed: Computed<T>) {
98
98
 
99
99
  // Simple auto adjust to avoid manual management within apps.
100
100
  if (height >= heap.length) {
101
- heap.length *= 1.5;
101
+ heap.length = Math.round(heap.length * 1.5);
102
102
  }
103
103
  }
104
104
  }
@@ -208,13 +208,13 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
208
208
  computed.state = STATE_NONE;
209
209
 
210
210
  let depsTail = computed.depsTail as Link | null,
211
- toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
211
+ remove = depsTail !== null ? depsTail.nextDep : computed.deps;
212
212
 
213
- if (toRemove !== null) {
213
+ if (remove !== null) {
214
214
  do {
215
- toRemove = unlink(toRemove);
215
+ remove = unlink(remove);
216
216
  }
217
- while (toRemove !== null);
217
+ while (remove !== null);
218
218
 
219
219
  if (depsTail !== null) {
220
220
  depsTail.nextDep = null;
@@ -228,14 +228,14 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
228
228
  computed.value = value as T;
229
229
 
230
230
  for (let c = computed.subs; c !== null; c = c.nextSub) {
231
- let o = c.sub,
232
- state = o.state;
231
+ let s = c.sub,
232
+ state = s.state;
233
233
 
234
234
  if (state & STATE_CHECK) {
235
- o.state = state | STATE_DIRTY;
235
+ s.state = state | STATE_DIRTY;
236
236
  }
237
237
 
238
- insertIntoHeap(o);
238
+ insertIntoHeap(s);
239
239
  }
240
240
 
241
241
  schedule();
@@ -245,7 +245,7 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
245
245
  function schedule() {
246
246
  if (stabilizer === STABILIZER_IDLE && !depth) {
247
247
  stabilizer = STABILIZER_SCHEDULED;
248
- scheduler(stabilize);
248
+ microtask(stabilize);
249
249
  }
250
250
  else if (stabilizer === STABILIZER_RUNNING) {
251
251
  stabilizer = STABILIZER_RESCHEDULE;
@@ -271,7 +271,7 @@ function stabilize() {
271
271
  }
272
272
 
273
273
  if (stabilizer === STABILIZER_RESCHEDULE) {
274
- scheduler(stabilize);
274
+ microtask(stabilize);
275
275
  }
276
276
  else {
277
277
  stabilizer = STABILIZER_IDLE;
@@ -293,12 +293,8 @@ function unlink(link: Link): Link | null {
293
293
  if (prevSub !== null) {
294
294
  prevSub.nextSub = nextSub;
295
295
  }
296
- else {
297
- dep.subs = nextSub;
298
-
299
- if (nextSub === null && 'fn' in dep) {
300
- dispose(dep);
301
- }
296
+ else if ((dep.subs = nextSub) === null && 'fn' in dep) {
297
+ dispose(dep);
302
298
  }
303
299
 
304
300
  return nextDep;
@@ -311,10 +307,10 @@ function update<T>(computed: Computed<T>): void {
311
307
 
312
308
  if ('fn' in dep) {
313
309
  update(dep);
314
- }
315
310
 
316
- if (computed.state & STATE_DIRTY) {
317
- break;
311
+ if (computed.state & STATE_DIRTY) {
312
+ break;
313
+ }
318
314
  }
319
315
  }
320
316
  }
@@ -329,7 +325,7 @@ function update<T>(computed: Computed<T>): void {
329
325
 
330
326
  const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
331
327
  let self: Computed<T> = {
332
- [REACTIVE]: true,
328
+ [COMPUTED]: true,
333
329
  cleanup: null,
334
330
  deps: null,
335
331
  depsTail: null,
@@ -357,9 +353,11 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
357
353
  }
358
354
 
359
355
  link(self, observer);
356
+ onCleanup(() => dispose(self));
360
357
  }
361
358
  else {
362
359
  recompute(self, false);
360
+ root.disposables++;
363
361
  }
364
362
 
365
363
  return self;
@@ -388,11 +386,11 @@ const effect = <T>(fn: Computed<T>['fn']) => {
388
386
  };
389
387
 
390
388
  const isComputed = (value: unknown): value is Computed<unknown> => {
391
- return isObject(value) && REACTIVE in value && 'fn' in value;
389
+ return isObject(value) && COMPUTED in value;
392
390
  };
393
391
 
394
392
  const isSignal = (value: unknown): value is Signal<unknown> => {
395
- return isObject(value) && REACTIVE in value && 'fn' in value === false;
393
+ return isObject(value) && SIGNAL in value;
396
394
  };
397
395
 
398
396
  const onCleanup = (fn: VoidFunction): typeof fn => {
@@ -400,16 +398,14 @@ const onCleanup = (fn: VoidFunction): typeof fn => {
400
398
  return fn;
401
399
  }
402
400
 
403
- let node = observer;
404
-
405
- if (!node.cleanup) {
406
- node.cleanup = fn;
401
+ if (!observer.cleanup) {
402
+ observer.cleanup = fn;
407
403
  }
408
- else if (isArray(node.cleanup)) {
409
- node.cleanup.push(fn);
404
+ else if (isArray(observer.cleanup)) {
405
+ observer.cleanup.push(fn);
410
406
  }
411
407
  else {
412
- node.cleanup = [node.cleanup, fn];
408
+ observer.cleanup = [observer.cleanup, fn];
413
409
  }
414
410
 
415
411
  return fn;
@@ -449,27 +445,23 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
449
445
  };
450
446
 
451
447
  const root = <T>(fn: () => T) => {
452
- let o = observer;
448
+ let d = root.disposables,
449
+ o = observer;
453
450
 
454
451
  observer = null;
452
+ root.disposables = 0;
455
453
 
456
454
  let value = fn();
457
455
 
458
456
  observer = o;
457
+ root.disposables = d;
459
458
 
460
459
  return value;
461
460
  };
462
461
 
463
- const signal = <T>(value: T): Signal<T> => {
464
- return {
465
- [REACTIVE]: true,
466
- subs: null,
467
- subsTail: null,
468
- value,
469
- };
470
- };
462
+ root.disposables = 0;
471
463
 
472
- signal.set = <T>(signal: Signal<T>, value: T) => {
464
+ const set = <T>(signal: Signal<T>, value: T) => {
473
465
  if (signal.value === value) {
474
466
  return;
475
467
  }
@@ -488,6 +480,15 @@ signal.set = <T>(signal: Signal<T>, value: T) => {
488
480
  schedule();
489
481
  };
490
482
 
483
+ const signal = <T>(value: T): Signal<T> => {
484
+ return {
485
+ [SIGNAL]: true,
486
+ subs: null,
487
+ subsTail: null,
488
+ value,
489
+ };
490
+ };
491
+
491
492
 
492
493
  export {
493
494
  computed,
@@ -496,5 +497,6 @@ export {
496
497
  isComputed, isSignal,
497
498
  onCleanup,
498
499
  read, root,
499
- signal
500
- };
500
+ set, signal
501
+ };
502
+ export type { Computed, Signal };
package/src/types.ts CHANGED
@@ -1,15 +1,14 @@
1
- import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
2
- import { onCleanup } from './system';
1
+ import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
3
2
  import { ReactiveArray } from './reactive/array';
4
3
  import { ReactiveObject } from './reactive/object';
5
4
 
6
5
 
7
- interface Computed<T> extends Signal<T> {
8
- [REACTIVE]: true;
6
+ interface Computed<T> {
7
+ [COMPUTED]: true;
9
8
  cleanup: VoidFunction | VoidFunction[] | null;
10
9
  deps: Link | null;
11
10
  depsTail: Link | null;
12
- fn: (oc?: typeof onCleanup) => T;
11
+ fn: (onCleanup?: (fn: VoidFunction) => typeof fn) => T;
13
12
  height: number;
14
13
  nextHeap: Computed<unknown> | undefined;
15
14
  prevHeap: Computed<unknown>;
@@ -19,6 +18,9 @@ interface Computed<T> extends Signal<T> {
19
18
  typeof STATE_IN_HEAP |
20
19
  typeof STATE_NONE |
21
20
  typeof STATE_RECOMPUTING;
21
+ subs: Link | null;
22
+ subsTail: Link | null;
23
+ value: T;
22
24
  }
23
25
 
24
26
  type Infer<T> =
@@ -48,7 +50,7 @@ type Reactive<T> = T extends Record<PropertyKey, unknown>
48
50
  : ReactiveArray<T>;
49
51
 
50
52
  type Signal<T> = {
51
- [REACTIVE]: true;
53
+ [SIGNAL]: true;
52
54
  subs: Link | null;
53
55
  subsTail: Link | null;
54
56
  value: T;
@@ -1,4 +0,0 @@
1
- declare class Disposable {
2
- dispose(): void;
3
- }
4
- export { Disposable };
@@ -1,6 +0,0 @@
1
- class Disposable {
2
- dispose() {
3
- throw new Error('@esportsplus/reactivity: Disposable should not be instantiated directly.');
4
- }
5
- }
6
- export { Disposable };
@@ -1,8 +0,0 @@
1
- class Disposable {
2
- dispose(): void {
3
- throw new Error('@esportsplus/reactivity: Disposable should not be instantiated directly.');
4
- }
5
- }
6
-
7
-
8
- export { Disposable };