@esportsplus/reactivity 0.7.1 → 0.8.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.
package/build/index.d.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export { default as reactive } from './reactive/index.js';
2
- export { computed, dispose, oncleanup, root, signal, stabilize } from './signal.js';
3
- export { default as scheduler } from './scheduler.js';
2
+ export * from './signal.js';
4
3
  export * from './types.js';
package/build/index.js CHANGED
@@ -1,4 +1,3 @@
1
1
  export { default as reactive } from './reactive/index.js';
2
- export { computed, dispose, oncleanup, root, signal, stabilize } from './signal.js';
3
- export { default as scheduler } from './scheduler.js';
2
+ export * from './signal.js';
4
3
  export * from './types.js';
@@ -1,4 +1,4 @@
1
- import { isArray, isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
1
+ import { isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
2
2
  import { computed, dispose, isComputed, read } from '../signal.js';
3
3
  import object from './object.js';
4
4
  import { Disposable } from './disposable.js';
@@ -162,10 +162,7 @@ function factory(input) {
162
162
  let items = [];
163
163
  for (let i = 0, n = input.length; i < n; i++) {
164
164
  let value = input[i];
165
- if (isArray(value)) {
166
- items[i] = array(value);
167
- }
168
- else if (isFunction(value)) {
165
+ if (isFunction(value)) {
169
166
  items[i] = computed(value);
170
167
  }
171
168
  else if (isObject(value)) {
@@ -196,7 +193,7 @@ export default function array(input) {
196
193
  if (isNumber(key)) {
197
194
  let host = wrapped[key];
198
195
  if (host === undefined || !isComputed(host)) {
199
- wrapped[key] = factory([value])[0];
196
+ a.splice(key, 1, value);
200
197
  }
201
198
  else {
202
199
  return false;
@@ -1,5 +1,5 @@
1
1
  import CustomFunction from '@esportsplus/custom-function';
2
- declare class ReactivePromise<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
2
+ declare class ReactiveAsyncFunction<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
3
3
  private arguments;
4
4
  private okay;
5
5
  private response;
@@ -9,5 +9,5 @@ declare class ReactivePromise<A extends unknown[], R extends Promise<unknown>> e
9
9
  get input(): A | null;
10
10
  get ok(): boolean | null;
11
11
  }
12
- declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => ReactivePromise<A, R>;
12
+ declare const _default: <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => ReactiveAsyncFunction<A, R>;
13
13
  export default _default;
@@ -1,7 +1,7 @@
1
1
  import CustomFunction from '@esportsplus/custom-function';
2
2
  import { read, root, signal } from '../signal.js';
3
3
  let { set } = signal;
4
- class ReactivePromise extends CustomFunction {
4
+ class ReactiveAsyncFunction extends CustomFunction {
5
5
  arguments;
6
6
  okay;
7
7
  response;
@@ -44,5 +44,5 @@ class ReactivePromise extends CustomFunction {
44
44
  }
45
45
  }
46
46
  export default (fn) => {
47
- return new ReactivePromise(fn);
47
+ return new ReactiveAsyncFunction(fn);
48
48
  };
@@ -1,13 +1,14 @@
1
+ import { Computed } from '../types.js';
1
2
  import array from './array.js';
3
+ import async from './async.js';
2
4
  import object from './object.js';
3
- import promise from './promise.js';
4
- type API<T> = T extends (...args: infer A) => Promise<infer R> ? ReturnType<typeof promise<A, Promise<R>>> : T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
5
- type Guard<T> = T extends (...args: unknown[]) => Promise<unknown> ? T : T extends {
5
+ type API<T> = T extends (...args: infer A) => Promise<infer R> ? ReturnType<typeof async<A, Promise<R>>> : T extends (...args: unknown[]) => unknown ? void : T extends Record<PropertyKey, unknown> ? ReturnType<typeof object<T>> : T extends unknown[] ? ReturnType<typeof array<T>> : never;
6
+ type Input<T> = T extends (...args: unknown[]) => Promise<unknown> ? T : T extends (...args: unknown[]) => unknown ? Computed<T>['fn'] : T extends {
6
7
  dispose: any;
7
8
  } | {
8
9
  signals: any;
9
10
  } ? {
10
11
  never: '[ dispose, signals ] are reserved keys';
11
12
  } : T extends Record<PropertyKey, unknown> | unknown[] ? T : never;
12
- declare const _default: <T>(data: Guard<T>) => API<T>;
13
+ declare const _default: <T>(input: Input<T>) => API<T>;
13
14
  export default _default;
@@ -1,16 +1,21 @@
1
- import { isArray, isObject, isPromise } from '@esportsplus/utilities';
1
+ import { isArray, isAsyncFunction, isFunction, isObject } from '@esportsplus/utilities';
2
+ import { computed } from '../signal.js';
2
3
  import array from './array.js';
4
+ import async from './async.js';
3
5
  import object from './object.js';
4
- import promise from './promise.js';
5
- export default (data) => {
6
- if (isArray(data)) {
7
- return array(data);
6
+ export default (input) => {
7
+ if (isArray(input)) {
8
+ return array(input);
8
9
  }
9
- else if (isObject(data)) {
10
- return object(data);
10
+ else if (isAsyncFunction(input)) {
11
+ return async(input);
11
12
  }
12
- else if (isPromise(data)) {
13
- return promise(data);
13
+ else if (isFunction(input)) {
14
+ computed(input);
15
+ return undefined;
14
16
  }
15
- throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(data)}`);
17
+ else if (isObject(input)) {
18
+ return object(input);
19
+ }
20
+ throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
16
21
  };
@@ -1,8 +1,8 @@
1
- import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf, isObject } from '@esportsplus/utilities';
1
+ import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf } from '@esportsplus/utilities';
2
2
  import array from './array.js';
3
3
  import { computed, dispose, read, signal } from '../signal.js';
4
4
  import { Disposable } from './disposable.js';
5
- import promise from './promise.js';
5
+ import async from './async.js';
6
6
  let { set } = signal;
7
7
  class ReactiveObject extends Disposable {
8
8
  disposable = {};
@@ -20,13 +20,13 @@ class ReactiveObject extends Disposable {
20
20
  return a;
21
21
  },
22
22
  set(v) {
23
- set(t, !!t.value);
24
23
  a = disposable[key] = array(v);
24
+ set(t, !!t.value);
25
25
  }
26
26
  });
27
27
  }
28
- if (isAsyncFunction(value)) {
29
- let p = promise(value);
28
+ else if (isAsyncFunction(value)) {
29
+ let p = async(value);
30
30
  defineProperty(this, key, {
31
31
  enumerable: true,
32
32
  get() {
@@ -43,28 +43,11 @@ class ReactiveObject extends Disposable {
43
43
  }
44
44
  });
45
45
  }
46
- else if (isObject(value)) {
47
- let o = disposable[key] = new ReactiveObject(value), t = triggers[key] = signal(false);
48
- defineProperty(this, key, {
49
- enumerable: true,
50
- get() {
51
- read(t);
52
- return o;
53
- },
54
- set(v) {
55
- set(t, !!t.value);
56
- o = disposable[key] = new ReactiveObject(v);
57
- }
58
- });
59
- }
60
46
  else {
61
47
  let s = signal(value);
62
48
  defineProperty(this, key, {
63
49
  enumerable: true,
64
50
  get() {
65
- if (s === undefined) {
66
- s = signal(value);
67
- }
68
51
  return read(s);
69
52
  },
70
53
  set(v) {
package/build/signal.d.ts CHANGED
@@ -1,15 +1,14 @@
1
1
  import { Computed, Signal } from './types.js';
2
2
  declare const computed: <T>(fn: Computed<T>["fn"]) => Computed<T>;
3
- declare const dispose: <T>(el: Computed<T>) => void;
3
+ declare const dispose: <T>(computed: Computed<T>) => void;
4
4
  declare const isComputed: (value: unknown) => value is Computed<unknown>;
5
- declare const isReactive: (value: unknown) => value is Computed<unknown> | Signal<unknown>;
6
5
  declare const isSignal: (value: unknown) => value is Signal<unknown>;
7
- declare const oncleanup: (fn: VoidFunction) => typeof fn;
8
- declare const read: <T>(el: Signal<T> | Computed<T>) => T;
6
+ declare const onCleanup: (fn: VoidFunction) => typeof fn;
7
+ declare const read: <T>(node: Signal<T> | Computed<T>) => T;
9
8
  declare const root: <T>(fn: () => T) => T;
10
9
  declare const signal: {
11
10
  <T>(value: T): Signal<T>;
12
- set<T>(el: Signal<T>, v: T): void;
11
+ set<T>(signal: Signal<T>, value: T): void;
13
12
  };
14
13
  declare const stabilize: () => void;
15
- export { computed, dispose, isComputed, isReactive, isSignal, oncleanup, read, root, signal, stabilize };
14
+ export { computed, dispose, isComputed, isSignal, onCleanup, read, root, signal, stabilize };
package/build/signal.js CHANGED
@@ -1,64 +1,64 @@
1
- import { isArray, isObject } from '@esportsplus/utilities';
1
+ import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
2
2
  import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
3
- import { state } from './scheduler.js';
4
- let dirtyHeap = new Array(2000), maxDirty = 0, markedHeap = false, minDirty = 0, observer = null;
3
+ let heap = new Array(2000), iHeap = 0, nHeap = 0, notifiedHeap = false, observer = null, scheduled = false, scheduler = null;
5
4
  function cleanup(node) {
6
5
  if (!node.cleanup) {
7
6
  return;
8
7
  }
9
- if (isArray(node.cleanup)) {
10
- for (let i = 0; i < node.cleanup.length; i++) {
11
- node.cleanup[i]();
8
+ let cleanup = node.cleanup;
9
+ if (isArray(cleanup)) {
10
+ for (let i = 0; i < cleanup.length; i++) {
11
+ cleanup[i]();
12
12
  }
13
13
  }
14
14
  else {
15
- node.cleanup();
15
+ cleanup();
16
16
  }
17
17
  node.cleanup = null;
18
18
  }
19
- function deleteFromHeap(n) {
20
- let state = n.state;
19
+ function deleteFromHeap(computed) {
20
+ let state = computed.state;
21
21
  if (!(state & STATE_IN_HEAP)) {
22
22
  return;
23
23
  }
24
- n.state = state & ~STATE_IN_HEAP;
25
- let height = n.height;
26
- if (n.prevHeap === n) {
27
- dirtyHeap[height] = undefined;
24
+ computed.state = state & ~STATE_IN_HEAP;
25
+ let height = computed.height;
26
+ if (computed.prevHeap === computed) {
27
+ heap[height] = undefined;
28
28
  }
29
29
  else {
30
- let next = n.nextHeap, dhh = dirtyHeap[height], end = next ?? dhh;
31
- if (n === dhh) {
32
- dirtyHeap[height] = next;
30
+ let next = computed.nextHeap, dhh = heap[height], end = next ?? dhh;
31
+ if (computed === dhh) {
32
+ heap[height] = next;
33
33
  }
34
34
  else {
35
- n.prevHeap.nextHeap = next;
35
+ computed.prevHeap.nextHeap = next;
36
36
  }
37
- end.prevHeap = n.prevHeap;
37
+ end.prevHeap = computed.prevHeap;
38
38
  }
39
- n.nextHeap = undefined;
40
- n.prevHeap = n;
39
+ computed.nextHeap = undefined;
40
+ computed.prevHeap = computed;
41
41
  }
42
- function insertIntoHeap(n) {
43
- let state = n.state;
42
+ function insertIntoHeap(computed) {
43
+ let state = computed.state;
44
44
  if (state & STATE_IN_HEAP) {
45
45
  return;
46
46
  }
47
- n.state = state | STATE_IN_HEAP;
48
- let height = n.height, heapAtHeight = dirtyHeap[height];
47
+ computed.state = state | STATE_IN_HEAP;
48
+ let height = computed.height, heapAtHeight = heap[height];
49
49
  if (heapAtHeight === undefined) {
50
- dirtyHeap[height] = n;
50
+ heap[height] = computed;
51
51
  }
52
52
  else {
53
53
  let tail = heapAtHeight.prevHeap;
54
- tail.nextHeap = n;
55
- n.prevHeap = tail;
56
- heapAtHeight.prevHeap = n;
57
- }
58
- if (height > maxDirty) {
59
- maxDirty = height;
60
- if (height >= dirtyHeap.length) {
61
- dirtyHeap.length += 250;
54
+ tail.nextHeap = computed;
55
+ computed.prevHeap = tail;
56
+ heapAtHeight.prevHeap = computed;
57
+ }
58
+ if (height > nHeap) {
59
+ nHeap = height;
60
+ if (height >= heap.length) {
61
+ heap.length += 250;
62
62
  }
63
63
  }
64
64
  }
@@ -96,49 +96,38 @@ function link(dep, sub) {
96
96
  dep.subs = newLink;
97
97
  }
98
98
  }
99
- function markHeap() {
100
- if (markedHeap) {
101
- return;
102
- }
103
- markedHeap = true;
104
- for (let i = 0; i <= maxDirty; i++) {
105
- for (let el = dirtyHeap[i]; el !== undefined; el = el.nextHeap) {
106
- markNode(el);
107
- }
108
- }
109
- }
110
- function markNode(el, newState = STATE_DIRTY) {
111
- let state = el.state;
99
+ function notify(computed, newState = STATE_DIRTY) {
100
+ let state = computed.state;
112
101
  if ((state & (STATE_CHECK | STATE_DIRTY)) >= newState) {
113
102
  return;
114
103
  }
115
- el.state = state | newState;
116
- for (let link = el.subs; link !== null; link = link.nextSub) {
117
- markNode(link.sub, STATE_CHECK);
104
+ computed.state = state | newState;
105
+ for (let link = computed.subs; link !== null; link = link.nextSub) {
106
+ notify(link.sub, STATE_CHECK);
118
107
  }
119
108
  }
120
- function recompute(el, del) {
109
+ function recompute(computed, del) {
121
110
  if (del) {
122
- deleteFromHeap(el);
111
+ deleteFromHeap(computed);
123
112
  }
124
113
  else {
125
- el.nextHeap = undefined;
126
- el.prevHeap = el;
114
+ computed.nextHeap = undefined;
115
+ computed.prevHeap = computed;
127
116
  }
128
- cleanup(el);
117
+ cleanup(computed);
129
118
  let o = observer, ok = true, value;
130
- observer = el;
131
- el.depsTail = null;
132
- el.state = STATE_RECOMPUTING;
119
+ observer = computed;
120
+ computed.depsTail = null;
121
+ computed.state = STATE_RECOMPUTING;
133
122
  try {
134
- value = el.fn(oncleanup);
123
+ value = computed.fn(onCleanup);
135
124
  }
136
125
  catch (e) {
137
126
  ok = false;
138
127
  }
139
128
  observer = o;
140
- el.state = STATE_NONE;
141
- let depsTail = el.depsTail, toRemove = depsTail !== null ? depsTail.nextDep : el.deps;
129
+ computed.state = STATE_NONE;
130
+ let depsTail = computed.depsTail, toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
142
131
  if (toRemove !== null) {
143
132
  do {
144
133
  toRemove = unlink(toRemove);
@@ -147,21 +136,25 @@ function recompute(el, del) {
147
136
  depsTail.nextDep = null;
148
137
  }
149
138
  else {
150
- el.deps = null;
139
+ computed.deps = null;
151
140
  }
152
141
  }
153
- if (ok && value !== el.value) {
154
- el.value = value;
155
- for (let s = el.subs; s !== null; s = s.nextSub) {
156
- let o = s.sub, state = o.state;
142
+ if (ok && value !== computed.value) {
143
+ computed.value = value;
144
+ for (let c = computed.subs; c !== null; c = c.nextSub) {
145
+ let o = c.sub, state = o.state;
157
146
  if (state & STATE_CHECK) {
158
147
  o.state = state | STATE_DIRTY;
159
148
  }
160
149
  insertIntoHeap(o);
161
150
  }
162
151
  }
163
- if (state.value === STATE_NONE) {
164
- root(() => signal.set(state, STATE_DIRTY));
152
+ if (!scheduled && scheduler) {
153
+ scheduled = true;
154
+ scheduler(stabilize);
155
+ }
156
+ else {
157
+ throw new Error('@esportsplus/reactivity: stabilize.scheduler has not been set to process updates.');
165
158
  }
166
159
  }
167
160
  function unlink(link) {
@@ -183,22 +176,22 @@ function unlink(link) {
183
176
  }
184
177
  return nextDep;
185
178
  }
186
- function update(el) {
187
- if (el.state & STATE_CHECK) {
188
- for (let d = el.deps; d; d = d.nextDep) {
189
- let dep = d.dep;
179
+ function update(computed) {
180
+ if (computed.state & STATE_CHECK) {
181
+ for (let link = computed.deps; link; link = link.nextDep) {
182
+ let dep = link.dep;
190
183
  if ('fn' in dep) {
191
184
  update(dep);
192
185
  }
193
- if (el.state & STATE_DIRTY) {
186
+ if (computed.state & STATE_DIRTY) {
194
187
  break;
195
188
  }
196
189
  }
197
190
  }
198
- if (el.state & STATE_DIRTY) {
199
- recompute(el, true);
191
+ if (computed.state & STATE_DIRTY) {
192
+ recompute(computed, true);
200
193
  }
201
- el.state = STATE_NONE;
194
+ computed.state = STATE_NONE;
202
195
  }
203
196
  const computed = (fn) => {
204
197
  let self = {
@@ -232,25 +225,22 @@ const computed = (fn) => {
232
225
  }
233
226
  return self;
234
227
  };
235
- const dispose = (el) => {
236
- deleteFromHeap(el);
237
- let dep = el.deps;
228
+ const dispose = (computed) => {
229
+ deleteFromHeap(computed);
230
+ let dep = computed.deps;
238
231
  while (dep !== null) {
239
232
  dep = unlink(dep);
240
233
  }
241
- el.deps = null;
242
- cleanup(el);
234
+ computed.deps = null;
235
+ cleanup(computed);
243
236
  };
244
237
  const isComputed = (value) => {
245
238
  return isObject(value) && REACTIVE in value && 'fn' in value;
246
239
  };
247
- const isReactive = (value) => {
248
- return isObject(value) && REACTIVE in value;
249
- };
250
240
  const isSignal = (value) => {
251
241
  return isObject(value) && REACTIVE in value && 'fn' in value === false;
252
242
  };
253
- const oncleanup = (fn) => {
243
+ const onCleanup = (fn) => {
254
244
  if (!observer) {
255
245
  return fn;
256
246
  }
@@ -266,22 +256,29 @@ const oncleanup = (fn) => {
266
256
  }
267
257
  return fn;
268
258
  };
269
- const read = (el) => {
259
+ const read = (node) => {
270
260
  if (observer) {
271
- link(el, observer);
272
- if ('fn' in el) {
273
- let height = el.height;
261
+ link(node, observer);
262
+ if ('fn' in node) {
263
+ let height = node.height;
274
264
  if (height >= observer.height) {
275
265
  observer.height = height + 1;
276
266
  }
277
- if (height >= minDirty ||
278
- el.state & (STATE_DIRTY | STATE_CHECK)) {
279
- markHeap();
280
- update(el);
267
+ if (height >= iHeap ||
268
+ node.state & (STATE_DIRTY | STATE_CHECK)) {
269
+ if (!notifiedHeap) {
270
+ notifiedHeap = true;
271
+ for (let i = 0; i <= nHeap; i++) {
272
+ for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
273
+ notify(computed);
274
+ }
275
+ }
276
+ }
277
+ update(node);
281
278
  }
282
279
  }
283
280
  }
284
- return el.value;
281
+ return node.value;
285
282
  };
286
283
  const root = (fn) => {
287
284
  let o = observer;
@@ -298,28 +295,36 @@ const signal = (value) => {
298
295
  value,
299
296
  };
300
297
  };
301
- signal.set = (el, v) => {
302
- if (el.value === v) {
298
+ signal.set = (signal, value) => {
299
+ if (signal.value === value) {
303
300
  return;
304
301
  }
305
- el.value = v;
306
- for (let link = el.subs; link !== null; link = link.nextSub) {
307
- markedHeap = false;
302
+ notifiedHeap = false;
303
+ signal.value = value;
304
+ for (let link = signal.subs; link !== null; link = link.nextSub) {
308
305
  insertIntoHeap(link.sub);
309
306
  }
310
307
  };
311
308
  const stabilize = () => {
312
309
  root(() => {
313
- for (minDirty = 0; minDirty <= maxDirty; minDirty++) {
314
- let el = dirtyHeap[minDirty];
315
- dirtyHeap[minDirty] = undefined;
316
- while (el !== undefined) {
317
- let next = el.nextHeap;
318
- recompute(el, false);
319
- el = next;
310
+ for (iHeap = 0; iHeap <= nHeap; iHeap++) {
311
+ let computed = heap[iHeap];
312
+ heap[iHeap] = undefined;
313
+ while (computed !== undefined) {
314
+ let next = computed.nextHeap;
315
+ recompute(computed, false);
316
+ computed = next;
320
317
  }
321
318
  }
322
- signal.set(state, STATE_NONE);
319
+ scheduled = false;
323
320
  });
324
321
  };
325
- export { computed, dispose, isComputed, isReactive, isSignal, oncleanup, read, root, signal, stabilize };
322
+ defineProperty(stabilize, 'scheduler', {
323
+ get() {
324
+ return scheduler;
325
+ },
326
+ set(s) {
327
+ scheduler = s;
328
+ },
329
+ });
330
+ export { computed, dispose, isComputed, isSignal, onCleanup, read, root, signal, stabilize };
package/build/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
2
- import { oncleanup } from './signal.js';
2
+ import { onCleanup } from './signal.js';
3
3
  import { ReactiveArray } from './reactive/array.js';
4
4
  import { ReactiveObject } from './reactive/object.js';
5
5
  interface Computed<T> extends Signal<T> {
@@ -7,7 +7,7 @@ interface Computed<T> extends Signal<T> {
7
7
  cleanup: VoidFunction | VoidFunction[] | null;
8
8
  deps: Link | null;
9
9
  depsTail: Link | null;
10
- fn: (oc?: typeof oncleanup) => T;
10
+ fn: (oc?: typeof onCleanup) => T;
11
11
  height: number;
12
12
  nextHeap: Computed<unknown> | undefined;
13
13
  prevHeap: Computed<unknown>;
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.7.1",
15
+ "version": "0.8.1",
16
16
  "scripts": {
17
17
  "build": "tsc && tsc-alias",
18
18
  "-": "-"
package/src/index.ts CHANGED
@@ -1,4 +1,3 @@
1
1
  export { default as reactive } from './reactive';
2
- export { computed, dispose, oncleanup, root, signal, stabilize } from './signal';
3
- export { default as scheduler } from './scheduler';
2
+ export * from './signal';
4
3
  export * from './types';
@@ -1,4 +1,4 @@
1
- import { isArray, isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
1
+ import { isFunction, isInstanceOf, isNumber, isObject } from '@esportsplus/utilities';
2
2
  import { computed, dispose, isComputed, read } from '~/signal';
3
3
  import { Computed, Infer } from '~/types';
4
4
  import object, { ReactiveObject } from './object';
@@ -284,10 +284,7 @@ function factory<T>(input: T[]) {
284
284
  for (let i = 0, n = input.length; i < n; i++) {
285
285
  let value = input[i];
286
286
 
287
- if (isArray(value)) {
288
- items[i] = array(value);
289
- }
290
- else if (isFunction(value)) {
287
+ if (isFunction(value)) {
291
288
  items[i] = computed(value as Computed<T>['fn']);
292
289
  }
293
290
  else if (isObject(value)) {
@@ -325,7 +322,7 @@ export default function array<T>(input: T[]) {
325
322
  let host = wrapped[key];
326
323
 
327
324
  if (host === undefined || !isComputed(host)) {
328
- wrapped[key] = factory([value] as T[])[0];
325
+ a.splice(key, 1, value);
329
326
  }
330
327
  else {
331
328
  return false;
@@ -6,7 +6,7 @@ import { Signal } from '~/types';
6
6
  let { set } = signal;
7
7
 
8
8
 
9
- class ReactivePromise<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
9
+ class ReactiveAsyncFunction<A extends unknown[], R extends Promise<unknown>> extends CustomFunction {
10
10
  private arguments: Signal<A | null>;
11
11
  private okay: Signal<boolean | null>;
12
12
  private response: Signal<Awaited<R> | null>;
@@ -63,5 +63,5 @@ class ReactivePromise<A extends unknown[], R extends Promise<unknown>> extends C
63
63
 
64
64
 
65
65
  export default <A extends unknown[], R extends Promise<unknown>>(fn: (...args: A) => R) => {
66
- return new ReactivePromise(fn);
66
+ return new ReactiveAsyncFunction(fn);
67
67
  };
@@ -1,38 +1,48 @@
1
- import { isArray, isObject, isPromise } from '@esportsplus/utilities';
1
+ import { isArray, isAsyncFunction, isFunction, isObject } from '@esportsplus/utilities';
2
+ import { computed } from '~/signal';
3
+ import { Computed } from '~/types';
2
4
  import array from './array';
5
+ import async from './async';
3
6
  import object from './object';
4
- import promise from './promise';
5
7
 
6
8
 
7
9
  type API<T> =
8
10
  T extends (...args: infer A) => Promise<infer R>
9
- ? ReturnType<typeof promise<A, Promise<R>>>
10
- : T extends Record<PropertyKey, unknown>
11
- ? ReturnType<typeof object<T>>
12
- : T extends unknown[]
13
- ? ReturnType<typeof array<T>>
14
- : never;
11
+ ? ReturnType<typeof async<A, Promise<R>>>
12
+ : T extends (...args: unknown[]) => unknown
13
+ ? void
14
+ : T extends Record<PropertyKey, unknown>
15
+ ? ReturnType<typeof object<T>>
16
+ : T extends unknown[]
17
+ ? ReturnType<typeof array<T>>
18
+ : never;
15
19
 
16
- type Guard<T> =
20
+ type Input<T> =
17
21
  T extends (...args: unknown[]) => Promise<unknown>
18
22
  ? T
19
- : T extends { dispose: any } | { signals: any }
20
- ? { never: '[ dispose, signals ] are reserved keys' }
21
- : T extends Record<PropertyKey, unknown> | unknown[]
22
- ? T
23
- : never;
23
+ : T extends (...args: unknown[]) => unknown
24
+ ? Computed<T>['fn']
25
+ : T extends { dispose: any } | { signals: any }
26
+ ? { never: '[ dispose, signals ] are reserved keys' }
27
+ : T extends Record<PropertyKey, unknown> | unknown[]
28
+ ? T
29
+ : never;
24
30
 
25
31
 
26
- export default <T>(data: Guard<T>): API<T> => {
27
- if (isArray(data)) {
28
- return array(data) as API<T>;
32
+ export default <T>(input: Input<T>): API<T> => {
33
+ if (isArray(input)) {
34
+ return array(input) as API<T>;
29
35
  }
30
- else if (isObject(data)) {
31
- return object(data) as API<T>;
36
+ else if (isAsyncFunction(input)) {
37
+ return async(input) as API<T>;
32
38
  }
33
- else if (isPromise(data)) {
34
- return promise(data) as API<T>;
39
+ else if (isFunction(input)) {
40
+ computed(input as Computed<T>['fn']);
41
+ return undefined as API<T>;
42
+ }
43
+ else if (isObject(input)) {
44
+ return object(input) as API<T>;
35
45
  }
36
46
 
37
- throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(data)}`);
47
+ throw new Error(`@esportsplus/reactivity: 'reactive' received invalid input - ${JSON.stringify(input)}`);
38
48
  };
@@ -1,9 +1,9 @@
1
- import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf, isObject, Prettify } from '@esportsplus/utilities';
1
+ import { defineProperty, isArray, isAsyncFunction, isFunction, isInstanceOf, Prettify } from '@esportsplus/utilities';
2
2
  import array, { ReactiveArray } from './array';
3
3
  import { computed, dispose, read, signal } from '~/signal';
4
4
  import { Computed, Infer, Signal } from '~/types';
5
5
  import { Disposable } from './disposable';
6
- import promise from './promise';
6
+ import async from './async';
7
7
 
8
8
 
9
9
  type API<T extends Record<PropertyKey, unknown>> = Prettify<{ [K in keyof T]: Infer<T[K]> }> & ReactiveObject<T>;
@@ -39,13 +39,13 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
39
39
  return a;
40
40
  },
41
41
  set(v: typeof value) {
42
- set(t, !!t.value);
43
42
  a = disposable[key] = array(v);
43
+ set(t, !!t.value);
44
44
  }
45
45
  });
46
46
  }
47
- if (isAsyncFunction(value)) {
48
- let p = promise(value);
47
+ else if (isAsyncFunction(value)) {
48
+ let p = async(value);
49
49
 
50
50
  defineProperty(this, key, {
51
51
  enumerable: true,
@@ -64,32 +64,12 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> extends Disposable
64
64
  }
65
65
  });
66
66
  }
67
- else if (isObject(value)) {
68
- let o = disposable[key] = new ReactiveObject(value),
69
- t = triggers[key] = signal(false);
70
-
71
- defineProperty(this, key, {
72
- enumerable: true,
73
- get() {
74
- read(t);
75
- return o;
76
- },
77
- set(v: typeof value) {
78
- set(t, !!t.value);
79
- o = disposable[key] = new ReactiveObject(v);
80
- }
81
- });
82
- }
83
67
  else {
84
68
  let s = signal(value);
85
69
 
86
70
  defineProperty(this, key, {
87
71
  enumerable: true,
88
72
  get() {
89
- if (s === undefined) {
90
- s = signal(value);
91
- }
92
-
93
73
  return read(s as Signal<typeof value>);
94
74
  },
95
75
  set(v: typeof value) {
package/src/signal.ts CHANGED
@@ -1,14 +1,15 @@
1
- import { isArray, isObject } from '@esportsplus/utilities';
1
+ import { defineProperty, isArray, isObject } from '@esportsplus/utilities';
2
2
  import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
3
3
  import { Computed, Link, Signal, } from './types';
4
- import { state } from './scheduler';
5
4
 
6
5
 
7
- let dirtyHeap: (Computed<unknown> | undefined)[] = new Array(2000),
8
- maxDirty = 0,
9
- markedHeap = false,
10
- minDirty = 0,
11
- observer: Computed<unknown> | null = null;
6
+ let heap: (Computed<unknown> | undefined)[] = new Array(2000),
7
+ iHeap = 0,
8
+ nHeap = 0,
9
+ notifiedHeap = false,
10
+ observer: Computed<unknown> | null = null,
11
+ scheduled = false,
12
+ scheduler: ((task: VoidFunction) => void) | null = null;
12
13
 
13
14
 
14
15
  function cleanup<T>(node: Computed<T>): void {
@@ -16,80 +17,82 @@ function cleanup<T>(node: Computed<T>): void {
16
17
  return;
17
18
  }
18
19
 
19
- if (isArray(node.cleanup)) {
20
- for (let i = 0; i < node.cleanup.length; i++) {
21
- node.cleanup[i]();
20
+ let cleanup = node.cleanup;
21
+
22
+ if (isArray(cleanup)) {
23
+ for (let i = 0; i < cleanup.length; i++) {
24
+ cleanup[i]();
22
25
  }
23
26
  }
24
27
  else {
25
- node.cleanup();
28
+ cleanup();
26
29
  }
27
30
 
28
31
  node.cleanup = null;
29
32
  }
30
33
 
31
- function deleteFromHeap<T>(n: Computed<T>) {
32
- let state = n.state;
34
+ function deleteFromHeap<T>(computed: Computed<T>) {
35
+ let state = computed.state;
33
36
 
34
37
  if (!(state & STATE_IN_HEAP)) {
35
38
  return;
36
39
  }
37
40
 
38
- n.state = state & ~STATE_IN_HEAP;
41
+ computed.state = state & ~STATE_IN_HEAP;
39
42
 
40
- let height = n.height;
43
+ let height = computed.height;
41
44
 
42
- if (n.prevHeap === n) {
43
- dirtyHeap[height] = undefined;
45
+ if (computed.prevHeap === computed) {
46
+ heap[height] = undefined;
44
47
  }
45
48
  else {
46
- let next = n.nextHeap,
47
- dhh = dirtyHeap[height]!,
49
+ let next = computed.nextHeap,
50
+ dhh = heap[height]!,
48
51
  end = next ?? dhh;
49
52
 
50
- if (n === dhh) {
51
- dirtyHeap[height] = next;
53
+ if (computed === dhh) {
54
+ heap[height] = next;
52
55
  }
53
56
  else {
54
- n.prevHeap.nextHeap = next;
57
+ computed.prevHeap.nextHeap = next;
55
58
  }
56
59
 
57
- end.prevHeap = n.prevHeap;
60
+ end.prevHeap = computed.prevHeap;
58
61
  }
59
62
 
60
- n.nextHeap = undefined;
61
- n.prevHeap = n;
63
+ computed.nextHeap = undefined;
64
+ computed.prevHeap = computed;
62
65
  }
63
66
 
64
- function insertIntoHeap<T>(n: Computed<T>) {
65
- let state = n.state;
67
+ function insertIntoHeap<T>(computed: Computed<T>) {
68
+ let state = computed.state;
66
69
 
67
70
  if (state & STATE_IN_HEAP) {
68
71
  return;
69
72
  }
70
73
 
71
- n.state = state | STATE_IN_HEAP;
74
+ computed.state = state | STATE_IN_HEAP;
72
75
 
73
- let height = n.height,
74
- heapAtHeight = dirtyHeap[height];
76
+ let height = computed.height,
77
+ heapAtHeight = heap[height];
75
78
 
76
79
  if (heapAtHeight === undefined) {
77
- dirtyHeap[height] = n;
80
+ heap[height] = computed;
78
81
  }
79
82
  else {
80
83
  let tail = heapAtHeight.prevHeap;
81
84
 
82
- tail.nextHeap = n;
83
- n.prevHeap = tail;
84
- heapAtHeight.prevHeap = n;
85
+ tail.nextHeap = computed;
86
+ computed.prevHeap = tail;
87
+ heapAtHeight.prevHeap = computed;
85
88
  }
86
89
 
87
- if (height > maxDirty) {
88
- maxDirty = height;
90
+ if (height > nHeap) {
91
+ nHeap = height;
89
92
 
90
93
  // Simple auto adjust to avoid manual management within apps.
91
- if (height >= dirtyHeap.length) {
92
- dirtyHeap.length += 250;
94
+ if (height >= heap.length) {
95
+ heap.length += 250;
93
96
  }
94
97
  }
95
98
  }
@@ -139,65 +142,51 @@ function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
139
142
  }
140
143
  }
141
144
 
142
- function markHeap() {
143
- if (markedHeap) {
144
- return;
145
- }
146
-
147
- markedHeap = true;
148
-
149
- for (let i = 0; i <= maxDirty; i++) {
150
- for (let el = dirtyHeap[i]; el !== undefined; el = el.nextHeap) {
151
- markNode(el);
152
- }
153
- }
154
- }
155
-
156
- function markNode<T>(el: Computed<T>, newState = STATE_DIRTY) {
157
- let state = el.state;
145
+ function notify<T>(computed: Computed<T>, newState = STATE_DIRTY) {
146
+ let state = computed.state;
158
147
 
159
148
  if ((state & (STATE_CHECK | STATE_DIRTY)) >= newState) {
160
149
  return;
161
150
  }
162
151
 
163
- el.state = state | newState;
152
+ computed.state = state | newState;
164
153
 
165
- for (let link = el.subs; link !== null; link = link.nextSub) {
166
- markNode(link.sub, STATE_CHECK);
154
+ for (let link = computed.subs; link !== null; link = link.nextSub) {
155
+ notify(link.sub, STATE_CHECK);
167
156
  }
168
157
  }
169
158
 
170
- function recompute<T>(el: Computed<T>, del: boolean) {
159
+ function recompute<T>(computed: Computed<T>, del: boolean) {
171
160
  if (del) {
172
- deleteFromHeap(el);
161
+ deleteFromHeap(computed);
173
162
  }
174
163
  else {
175
- el.nextHeap = undefined;
176
- el.prevHeap = el;
164
+ computed.nextHeap = undefined;
165
+ computed.prevHeap = computed;
177
166
  }
178
167
 
179
- cleanup(el);
168
+ cleanup(computed);
180
169
 
181
170
  let o = observer,
182
171
  ok = true,
183
172
  value;
184
173
 
185
- observer = el;
186
- el.depsTail = null;
187
- el.state = STATE_RECOMPUTING;
174
+ observer = computed;
175
+ computed.depsTail = null;
176
+ computed.state = STATE_RECOMPUTING;
188
177
 
189
178
  try {
190
- value = el.fn(oncleanup);
179
+ value = computed.fn(onCleanup);
191
180
  }
192
181
  catch (e) {
193
182
  ok = false;
194
183
  }
195
184
 
196
185
  observer = o;
197
- el.state = STATE_NONE;
186
+ computed.state = STATE_NONE;
198
187
 
199
- let depsTail = el.depsTail as Link | null,
200
- toRemove = depsTail !== null ? depsTail.nextDep : el.deps;
188
+ let depsTail = computed.depsTail as Link | null,
189
+ toRemove = depsTail !== null ? depsTail.nextDep : computed.deps;
201
190
 
202
191
  if (toRemove !== null) {
203
192
  do {
@@ -209,15 +198,15 @@ function recompute<T>(el: Computed<T>, del: boolean) {
209
198
  depsTail.nextDep = null;
210
199
  }
211
200
  else {
212
- el.deps = null;
201
+ computed.deps = null;
213
202
  }
214
203
  }
215
204
 
216
- if (ok && value !== el.value) {
217
- el.value = value as T;
205
+ if (ok && value !== computed.value) {
206
+ computed.value = value as T;
218
207
 
219
- for (let s = el.subs; s !== null; s = s.nextSub) {
220
- let o = s.sub,
208
+ for (let c = computed.subs; c !== null; c = c.nextSub) {
209
+ let o = c.sub,
221
210
  state = o.state;
222
211
 
223
212
  if (state & STATE_CHECK) {
@@ -228,8 +217,12 @@ function recompute<T>(el: Computed<T>, del: boolean) {
228
217
  }
229
218
  }
230
219
 
231
- if (state.value === STATE_NONE) {
232
- root(() => signal.set(state, STATE_DIRTY));
220
+ if (!scheduled && scheduler) {
221
+ scheduled = true;
222
+ scheduler(stabilize);
223
+ }
224
+ else {
225
+ throw new Error('@esportsplus/reactivity: stabilize.scheduler has not been set to process updates.');
233
226
  }
234
227
  }
235
228
 
@@ -261,26 +254,26 @@ function unlink(link: Link): Link | null {
261
254
  return nextDep;
262
255
  }
263
256
 
264
- function update<T>(el: Computed<T>): void {
265
- if (el.state & STATE_CHECK) {
266
- for (let d = el.deps; d; d = d.nextDep) {
267
- let dep = d.dep;
257
+ function update<T>(computed: Computed<T>): void {
258
+ if (computed.state & STATE_CHECK) {
259
+ for (let link = computed.deps; link; link = link.nextDep) {
260
+ let dep = link.dep;
268
261
 
269
262
  if ('fn' in dep) {
270
263
  update(dep);
271
264
  }
272
265
 
273
- if (el.state & STATE_DIRTY) {
266
+ if (computed.state & STATE_DIRTY) {
274
267
  break;
275
268
  }
276
269
  }
277
270
  }
278
271
 
279
- if (el.state & STATE_DIRTY) {
280
- recompute(el, true);
272
+ if (computed.state & STATE_DIRTY) {
273
+ recompute(computed, true);
281
274
  }
282
275
 
283
- el.state = STATE_NONE;
276
+ computed.state = STATE_NONE;
284
277
  }
285
278
 
286
279
 
@@ -321,33 +314,29 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
321
314
  return self;
322
315
  };
323
316
 
324
- const dispose = <T>(el: Computed<T>) => {
325
- deleteFromHeap(el);
317
+ const dispose = <T>(computed: Computed<T>) => {
318
+ deleteFromHeap(computed);
326
319
 
327
- let dep = el.deps;
320
+ let dep = computed.deps;
328
321
 
329
322
  while (dep !== null) {
330
323
  dep = unlink(dep);
331
324
  }
332
325
 
333
- el.deps = null;
326
+ computed.deps = null;
334
327
 
335
- cleanup(el);
336
- }
328
+ cleanup(computed);
329
+ };
337
330
 
338
331
  const isComputed = (value: unknown): value is Computed<unknown> => {
339
332
  return isObject(value) && REACTIVE in value && 'fn' in value;
340
333
  };
341
334
 
342
- const isReactive = (value: unknown): value is Computed<unknown> | Signal<unknown> => {
343
- return isObject(value) && REACTIVE in value;
344
- };
345
-
346
335
  const isSignal = (value: unknown): value is Signal<unknown> => {
347
336
  return isObject(value) && REACTIVE in value && 'fn' in value === false;
348
337
  };
349
338
 
350
- const oncleanup = (fn: VoidFunction): typeof fn => {
339
+ const onCleanup = (fn: VoidFunction): typeof fn => {
351
340
  if (!observer) {
352
341
  return fn;
353
342
  }
@@ -367,28 +356,37 @@ const oncleanup = (fn: VoidFunction): typeof fn => {
367
356
  return fn;
368
357
  };
369
358
 
370
- const read = <T>(el: Signal<T> | Computed<T>): T => {
359
+ const read = <T>(node: Signal<T> | Computed<T>): T => {
371
360
  if (observer) {
372
- link(el, observer);
361
+ link(node, observer);
373
362
 
374
- if ('fn' in el) {
375
- let height = el.height;
363
+ if ('fn' in node) {
364
+ let height = node.height;
376
365
 
377
366
  if (height >= observer.height) {
378
367
  observer.height = height + 1;
379
368
  }
380
369
 
381
370
  if (
382
- height >= minDirty ||
383
- el.state & (STATE_DIRTY | STATE_CHECK)
371
+ height >= iHeap ||
372
+ node.state & (STATE_DIRTY | STATE_CHECK)
384
373
  ) {
385
- markHeap();
386
- update(el);
374
+ if (!notifiedHeap) {
375
+ notifiedHeap = true;
376
+
377
+ for (let i = 0; i <= nHeap; i++) {
378
+ for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
379
+ notify(computed);
380
+ }
381
+ }
382
+ }
383
+
384
+ update(node);
387
385
  }
388
386
  }
389
387
  }
390
388
 
391
- return el.value;
389
+ return node.value;
392
390
  };
393
391
 
394
392
  const root = <T>(fn: () => T) => {
@@ -412,45 +410,54 @@ const signal = <T>(value: T): Signal<T> => {
412
410
  };
413
411
  };
414
412
 
415
- signal.set = <T>(el: Signal<T>, v: T) => {
416
- if (el.value === v) {
413
+ signal.set = <T>(signal: Signal<T>, value: T) => {
414
+ if (signal.value === value) {
417
415
  return;
418
416
  }
419
417
 
420
- el.value = v;
418
+ notifiedHeap = false;
419
+ signal.value = value;
421
420
 
422
- for (let link = el.subs; link !== null; link = link.nextSub) {
423
- markedHeap = false;
421
+ for (let link = signal.subs; link !== null; link = link.nextSub) {
424
422
  insertIntoHeap(link.sub);
425
423
  }
426
424
  };
427
425
 
428
426
  const stabilize = () => {
429
427
  root(() => {
430
- for (minDirty = 0; minDirty <= maxDirty; minDirty++) {
431
- let el = dirtyHeap[minDirty];
428
+ for (iHeap = 0; iHeap <= nHeap; iHeap++) {
429
+ let computed = heap[iHeap];
432
430
 
433
- dirtyHeap[minDirty] = undefined;
431
+ heap[iHeap] = undefined;
434
432
 
435
- while (el !== undefined) {
436
- let next = el.nextHeap;
433
+ while (computed !== undefined) {
434
+ let next = computed.nextHeap;
437
435
 
438
- recompute(el, false);
436
+ recompute(computed, false);
439
437
 
440
- el = next;
438
+ computed = next;
441
439
  }
442
440
  }
443
441
 
444
- signal.set(state, STATE_NONE);
442
+ scheduled = false;
445
443
  });
446
444
  };
447
445
 
446
+ defineProperty(stabilize, 'scheduler', {
447
+ get() {
448
+ return scheduler;
449
+ },
450
+ set(s: typeof scheduler) {
451
+ scheduler = s;
452
+ },
453
+ });
454
+
448
455
 
449
456
  export {
450
457
  computed,
451
458
  dispose,
452
- isComputed, isReactive, isSignal,
453
- oncleanup,
459
+ isComputed, isSignal,
460
+ onCleanup,
454
461
  read, root,
455
462
  signal, stabilize
456
463
  };
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { REACTIVE, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
2
- import { oncleanup } from './signal';
2
+ import { onCleanup } from './signal';
3
3
  import { ReactiveArray } from './reactive/array';
4
4
  import { ReactiveObject } from './reactive/object';
5
5
 
@@ -9,7 +9,7 @@ interface Computed<T> extends Signal<T> {
9
9
  cleanup: VoidFunction | VoidFunction[] | null;
10
10
  deps: Link | null;
11
11
  depsTail: Link | null;
12
- fn: (oc?: typeof oncleanup) => T;
12
+ fn: (oc?: typeof onCleanup) => T;
13
13
  height: number;
14
14
  nextHeap: Computed<unknown> | undefined;
15
15
  prevHeap: Computed<unknown>;
@@ -1,4 +0,0 @@
1
- declare const scheduler: (schedule: (task: VoidFunction) => void) => void;
2
- declare const state: import("./types.js").Signal<number>;
3
- export default scheduler;
4
- export { state };
@@ -1,17 +0,0 @@
1
- import { STATE_DIRTY, STATE_NONE } from './constants.js';
2
- import { computed, dispose, read, signal, stabilize } from './signal.js';
3
- let c = null;
4
- const scheduler = (schedule) => {
5
- if (c) {
6
- dispose(c);
7
- }
8
- c = computed(() => {
9
- if (read(state) !== STATE_DIRTY) {
10
- return;
11
- }
12
- schedule(stabilize);
13
- });
14
- };
15
- const state = signal(STATE_NONE);
16
- export default scheduler;
17
- export { state };
package/src/scheduler.ts DELETED
@@ -1,27 +0,0 @@
1
- import { STATE_DIRTY, STATE_NONE } from './constants';
2
- import { computed, dispose, read, signal, stabilize } from './signal';
3
- import { Computed } from './types';
4
-
5
-
6
- let c: Computed<void> | null = null;
7
-
8
-
9
- const scheduler = (schedule: (task: VoidFunction) => void) => {
10
- if (c) {
11
- dispose(c);
12
- }
13
-
14
- c = computed(() => {
15
- if (read(state) !== STATE_DIRTY) {
16
- return;
17
- }
18
-
19
- schedule(stabilize);
20
- });
21
- };
22
-
23
- const state = signal(STATE_NONE);
24
-
25
-
26
- export default scheduler;
27
- export { state };