@esportsplus/reactivity 0.30.3 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,5 +12,6 @@ declare const STATE_CHECK: number;
12
12
  declare const STATE_DIRTY: number;
13
13
  declare const STATE_RECOMPUTING: number;
14
14
  declare const STATE_IN_HEAP: number;
15
+ declare const STATE_COMPUTED: number;
15
16
  declare const STATE_NOTIFY_MASK: number;
16
- export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
17
+ export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
@@ -12,5 +12,6 @@ const STATE_CHECK = 1 << 0;
12
12
  const STATE_DIRTY = 1 << 1;
13
13
  const STATE_RECOMPUTING = 1 << 2;
14
14
  const STATE_IN_HEAP = 1 << 3;
15
+ const STATE_COMPUTED = 1 << 4;
15
16
  const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
16
- export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
17
+ export { COMPUTED, REACTIVE_ARRAY, REACTIVE_OBJECT, PACKAGE_NAME, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING };
@@ -49,21 +49,19 @@ class ReactiveObject {
49
49
  return root(() => {
50
50
  let node = computed(value);
51
51
  if (isPromise(node.value)) {
52
- let factory = node, version = 0;
53
- node = signal(undefined);
52
+ let factory = node, out = signal(undefined), v = 0;
54
53
  (this.disposers ??= []).push(effect(() => {
55
- let id = ++version;
56
- read(factory).then((v) => {
57
- if (id !== version) {
54
+ let id = ++v;
55
+ read(factory).then((resolved) => {
56
+ if (id !== v) {
58
57
  return;
59
58
  }
60
- write(node, v);
59
+ write(out, resolved);
61
60
  });
62
61
  }));
62
+ return out;
63
63
  }
64
- else {
65
- (this.disposers ??= []).push(() => dispose(node));
66
- }
64
+ (this.disposers ??= []).push(() => dispose(node));
67
65
  return node;
68
66
  });
69
67
  }
package/build/system.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Computed, Signal } from './types.js';
2
+ declare const asyncComputed: <T>(fn: Computed<Promise<T>>["fn"]) => Signal<T | undefined>;
2
3
  declare const computed: <T>(fn: Computed<T>["fn"]) => Computed<T>;
3
4
  declare const dispose: <T>(computed: Computed<T>) => void;
4
5
  declare const effect: <T>(fn: Computed<T>["fn"]) => () => void;
@@ -12,5 +13,5 @@ declare const root: {
12
13
  };
13
14
  declare const signal: <T>(value: T) => Signal<T>;
14
15
  declare const write: <T>(signal: Signal<T>, value: T) => void;
15
- export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
16
+ export { asyncComputed, computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
16
17
  export type { Computed, Signal };
package/build/system.js CHANGED
@@ -1,6 +1,6 @@
1
- import { COMPUTED, SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
1
+ import { SIGNAL, STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED, STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING } from './constants.js';
2
2
  import { isObject } from '@esportsplus/utilities';
3
- let depth = 0, heap = new Array(64), heap_i = 0, heap_n = 0, linkPool = [], linkPoolMax = 1000, microtask = queueMicrotask, notified = false, observer = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0;
3
+ let depth = 0, heap = new Array(64), heap_i = 0, heap_n = 0, linkPoolHead = null, microtask = queueMicrotask, notified = false, observer = null, scope = null, stabilizer = STABILIZER_IDLE, version = 0;
4
4
  function cleanup(computed) {
5
5
  if (!computed.cleanup) {
6
6
  return;
@@ -82,9 +82,10 @@ function link(dep, sub) {
82
82
  prevSub.sub === sub) {
83
83
  return;
84
84
  }
85
- let pooled = linkPool.pop(), newLink = sub.depsTail =
85
+ let pooled = linkPoolHead, newLink = sub.depsTail =
86
86
  dep.subsTail = pooled
87
- ? (pooled.dep = dep,
87
+ ? (linkPoolHead = pooled.nextDep,
88
+ pooled.dep = dep,
88
89
  pooled.sub = sub,
89
90
  pooled.nextDep = nextDep,
90
91
  pooled.prevSub = prevSub,
@@ -136,7 +137,7 @@ function recompute(computed, del) {
136
137
  let o = observer, ok = true, value;
137
138
  observer = computed;
138
139
  computed.depsTail = null;
139
- computed.state = STATE_RECOMPUTING;
140
+ computed.state = STATE_COMPUTED | STATE_RECOMPUTING;
140
141
  depth++;
141
142
  version++;
142
143
  try {
@@ -147,7 +148,7 @@ function recompute(computed, del) {
147
148
  }
148
149
  depth--;
149
150
  observer = o;
150
- computed.state = STATE_NONE;
151
+ computed.state = STATE_COMPUTED;
151
152
  let depsTail = computed.depsTail, remove = depsTail ? depsTail.nextDep : computed.deps;
152
153
  if (remove) {
153
154
  do {
@@ -220,21 +221,20 @@ function unlink(link) {
220
221
  if (prevSub) {
221
222
  prevSub.nextSub = nextSub;
222
223
  }
223
- else if ((dep.subs = nextSub) === null && dep.type === COMPUTED) {
224
+ else if ((dep.subs = nextSub) === null && dep.state & STATE_COMPUTED) {
224
225
  dispose(dep);
225
226
  }
226
- if (linkPool.length < linkPoolMax) {
227
- link.dep = link.sub = null;
228
- link.nextDep = link.nextSub = link.prevSub = null;
229
- linkPool.push(link);
230
- }
227
+ link.dep = link.sub = null;
228
+ link.nextSub = link.prevSub = null;
229
+ link.nextDep = linkPoolHead;
230
+ linkPoolHead = link;
231
231
  return nextDep;
232
232
  }
233
233
  function update(computed) {
234
234
  if (computed.state & STATE_CHECK) {
235
235
  for (let link = computed.deps; link; link = link.nextDep) {
236
236
  let dep = link.dep;
237
- if (dep.type === COMPUTED) {
237
+ if (dep.state & STATE_COMPUTED) {
238
238
  update(dep);
239
239
  if (computed.state & STATE_DIRTY) {
240
240
  break;
@@ -245,8 +245,20 @@ function update(computed) {
245
245
  if (computed.state & STATE_DIRTY) {
246
246
  recompute(computed, true);
247
247
  }
248
- computed.state = STATE_NONE;
248
+ computed.state = STATE_COMPUTED;
249
249
  }
250
+ const asyncComputed = (fn) => {
251
+ let factory = computed(fn), node = signal(undefined), v = 0;
252
+ onCleanup(effect(() => {
253
+ let id = ++v;
254
+ read(factory).then((value) => {
255
+ if (id === v) {
256
+ write(node, value);
257
+ }
258
+ }, () => { });
259
+ }));
260
+ return node;
261
+ };
250
262
  const computed = (fn) => {
251
263
  let self = {
252
264
  cleanup: null,
@@ -256,10 +268,9 @@ const computed = (fn) => {
256
268
  height: 0,
257
269
  nextHeap: undefined,
258
270
  prevHeap: null,
259
- state: STATE_NONE,
271
+ state: STATE_COMPUTED,
260
272
  subs: null,
261
273
  subsTail: null,
262
- type: COMPUTED,
263
274
  value: undefined,
264
275
  };
265
276
  self.prevHeap = self;
@@ -303,7 +314,7 @@ const effect = (fn) => {
303
314
  };
304
315
  };
305
316
  const isComputed = (value) => {
306
- return isObject(value) && value.type === COMPUTED;
317
+ return isObject(value) && !!(value.state & STATE_COMPUTED);
307
318
  };
308
319
  const isSignal = (value) => {
309
320
  return isObject(value) && value.type === SIGNAL;
@@ -328,7 +339,7 @@ const onCleanup = (fn) => {
328
339
  const read = (node) => {
329
340
  if (observer) {
330
341
  link(node, observer);
331
- if (node.type === COMPUTED) {
342
+ if (node.state & STATE_COMPUTED) {
332
343
  let height = node.height;
333
344
  if (height >= observer.height) {
334
345
  observer.height = height + 1;
@@ -353,7 +364,7 @@ const root = (fn) => {
353
364
  observer = null;
354
365
  root.disposables = 0;
355
366
  if (tracking) {
356
- scope = self = { cleanup: null };
367
+ scope = self = { cleanup: null, state: STATE_COMPUTED };
357
368
  value = fn(c = () => dispose(self));
358
369
  }
359
370
  else {
@@ -391,4 +402,4 @@ const write = (signal, value) => {
391
402
  }
392
403
  schedule();
393
404
  };
394
- export { computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
405
+ export { asyncComputed, computed, dispose, effect, isComputed, isSignal, onCleanup, read, root, signal, write };
package/build/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants.js';
2
+ import { SIGNAL } from './constants.js';
3
3
  import { ReactiveArray } from './reactive/index.js';
4
4
  interface Computed<T> {
5
5
  cleanup: VoidFunction | VoidFunction[] | null;
@@ -9,10 +9,9 @@ interface Computed<T> {
9
9
  height: number;
10
10
  nextHeap: Computed<unknown> | undefined;
11
11
  prevHeap: Computed<unknown>;
12
- state: typeof STATE_CHECK | typeof STATE_DIRTY | typeof STATE_IN_HEAP | typeof STATE_NONE | typeof STATE_RECOMPUTING;
12
+ state: number;
13
13
  subs: Link | null;
14
14
  subsTail: Link | null;
15
- type: typeof COMPUTED;
16
15
  value: T;
17
16
  }
18
17
  interface Link {
package/package.json CHANGED
@@ -36,7 +36,7 @@
36
36
  },
37
37
  "type": "module",
38
38
  "types": "build/index.d.ts",
39
- "version": "0.30.3",
39
+ "version": "0.31.0",
40
40
  "scripts": {
41
41
  "build": "tsc",
42
42
  "build:test": "pnpm build && vite build --config test/vite.config.ts",
package/src/constants.ts CHANGED
@@ -26,6 +26,8 @@ const STATE_RECOMPUTING = 1 << 2;
26
26
 
27
27
  const STATE_IN_HEAP = 1 << 3;
28
28
 
29
+ const STATE_COMPUTED = 1 << 4;
30
+
29
31
  const STATE_NOTIFY_MASK = (STATE_CHECK | STATE_DIRTY);
30
32
 
31
33
 
@@ -35,5 +37,5 @@ export {
35
37
  PACKAGE_NAME,
36
38
  SIGNAL,
37
39
  STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
38
- STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
40
+ STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
39
41
  };
@@ -72,29 +72,29 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
72
72
  let node: Computed<ReturnType<T>> | Signal<ReturnType<T> | undefined> = computed(value);
73
73
 
74
74
  if (isPromise(node.value)) {
75
- let factory = node,
76
- version = 0;
77
-
78
- node = signal<ReturnType<T> | undefined>(undefined);
75
+ let factory = node as Computed<ReturnType<T>>,
76
+ out = signal<ReturnType<T> | undefined>(undefined),
77
+ v = 0;
79
78
 
80
79
  (this.disposers ??= []).push(
81
80
  effect(() => {
82
- let id = ++version;
81
+ let id = ++v;
83
82
 
84
- (read(factory) as Promise<ReturnType<T>>).then((v) => {
85
- if (id !== version) {
83
+ (read(factory) as Promise<ReturnType<T>>).then((resolved) => {
84
+ if (id !== v) {
86
85
  return;
87
86
  }
88
87
 
89
- write(node as Signal<typeof v>, v);
88
+ write(out as Signal<typeof resolved>, resolved);
90
89
  });
91
90
  })
92
- )
93
- }
94
- else {
95
- (this.disposers ??= []).push(() => dispose(node as Computed<ReturnType<T>>));
91
+ );
92
+
93
+ return out;
96
94
  }
97
95
 
96
+ (this.disposers ??= []).push(() => dispose(node as Computed<ReturnType<T>>));
97
+
98
98
  return node;
99
99
  });
100
100
  }
package/src/system.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import {
2
- COMPUTED,
3
2
  SIGNAL,
4
3
  STABILIZER_IDLE, STABILIZER_RESCHEDULE, STABILIZER_RUNNING, STABILIZER_SCHEDULED,
5
- STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_NOTIFY_MASK, STATE_RECOMPUTING
4
+ STATE_CHECK, STATE_COMPUTED, STATE_DIRTY, STATE_IN_HEAP, STATE_NOTIFY_MASK, STATE_RECOMPUTING
6
5
  } from './constants';
7
6
  import { Computed, Link, Signal } from './types';
8
7
  import { isObject } from '@esportsplus/utilities';
@@ -12,8 +11,7 @@ let depth = 0,
12
11
  heap: (Computed<unknown> | undefined)[] = new Array(64),
13
12
  heap_i = 0,
14
13
  heap_n = 0,
15
- linkPool: Link[] = [],
16
- linkPoolMax = 1000,
14
+ linkPoolHead: Link | null = null,
17
15
  microtask = queueMicrotask,
18
16
  notified = false,
19
17
  observer: Computed<unknown> | null = null,
@@ -138,11 +136,12 @@ function link<T>(dep: Signal<T> | Computed<T>, sub: Computed<T>) {
138
136
  return;
139
137
  }
140
138
 
141
- let pooled = linkPool.pop(),
139
+ let pooled = linkPoolHead,
142
140
  newLink =
143
141
  sub.depsTail =
144
142
  dep.subsTail = pooled
145
- ? (pooled.dep = dep,
143
+ ? (linkPoolHead = pooled.nextDep,
144
+ pooled.dep = dep,
146
145
  pooled.sub = sub,
147
146
  pooled.nextDep = nextDep,
148
147
  pooled.prevSub = prevSub,
@@ -206,7 +205,7 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
206
205
 
207
206
  observer = computed;
208
207
  computed.depsTail = null;
209
- computed.state = STATE_RECOMPUTING;
208
+ computed.state = STATE_COMPUTED | STATE_RECOMPUTING;
210
209
 
211
210
  depth++;
212
211
  version++;
@@ -220,7 +219,7 @@ function recompute<T>(computed: Computed<T>, del: boolean) {
220
219
 
221
220
  depth--;
222
221
  observer = o;
223
- computed.state = STATE_NONE;
222
+ computed.state = STATE_COMPUTED;
224
223
 
225
224
  let depsTail = computed.depsTail as Link | null,
226
225
  remove = depsTail ? depsTail.nextDep : computed.deps;
@@ -323,16 +322,14 @@ function unlink(link: Link): Link | null {
323
322
  if (prevSub) {
324
323
  prevSub.nextSub = nextSub;
325
324
  }
326
- else if ((dep.subs = nextSub) === null && dep.type === COMPUTED) {
327
- dispose(dep);
325
+ else if ((dep.subs = nextSub) === null && (dep as Computed<unknown>).state & STATE_COMPUTED) {
326
+ dispose(dep as Computed<unknown>);
328
327
  }
329
328
 
330
- // Release link back to pool
331
- if (linkPool.length < linkPoolMax) {
332
- link.dep = link.sub = null as any;
333
- link.nextDep = link.nextSub = link.prevSub = null;
334
- linkPool.push(link);
335
- }
329
+ link.dep = link.sub = null as any;
330
+ link.nextSub = link.prevSub = null;
331
+ link.nextDep = linkPoolHead;
332
+ linkPoolHead = link;
336
333
 
337
334
  return nextDep;
338
335
  }
@@ -342,8 +339,8 @@ function update<T>(computed: Computed<T>): void {
342
339
  for (let link = computed.deps; link; link = link.nextDep) {
343
340
  let dep = link.dep;
344
341
 
345
- if (dep.type === COMPUTED) {
346
- update(dep);
342
+ if ((dep as Computed<unknown>).state & STATE_COMPUTED) {
343
+ update(dep as Computed<unknown>);
347
344
 
348
345
  if (computed.state & STATE_DIRTY) {
349
346
  break;
@@ -356,10 +353,28 @@ function update<T>(computed: Computed<T>): void {
356
353
  recompute(computed, true);
357
354
  }
358
355
 
359
- computed.state = STATE_NONE;
356
+ computed.state = STATE_COMPUTED;
360
357
  }
361
358
 
362
359
 
360
+ const asyncComputed = <T>(fn: Computed<Promise<T>>['fn']): Signal<T | undefined> => {
361
+ let factory = computed(fn),
362
+ node = signal<T | undefined>(undefined),
363
+ v = 0;
364
+
365
+ onCleanup(effect(() => {
366
+ let id = ++v;
367
+
368
+ (read(factory) as Promise<T>).then((value) => {
369
+ if (id === v) {
370
+ write(node, value);
371
+ }
372
+ }, () => {});
373
+ }));
374
+
375
+ return node;
376
+ };
377
+
363
378
  const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
364
379
  let self: Computed<T> = {
365
380
  cleanup: null,
@@ -369,10 +384,9 @@ const computed = <T>(fn: Computed<T>['fn']): Computed<T> => {
369
384
  height: 0,
370
385
  nextHeap: undefined,
371
386
  prevHeap: null as any,
372
- state: STATE_NONE,
387
+ state: STATE_COMPUTED,
373
388
  subs: null,
374
389
  subsTail: null,
375
- type: COMPUTED,
376
390
  value: undefined as T,
377
391
  };
378
392
 
@@ -429,7 +443,7 @@ const effect = <T>(fn: Computed<T>['fn']) => {
429
443
  };
430
444
 
431
445
  const isComputed = (value: unknown): value is Computed<unknown> => {
432
- return isObject(value) && value.type === COMPUTED;
446
+ return isObject(value) && !!((value as unknown as Computed<unknown>).state & STATE_COMPUTED);
433
447
  };
434
448
 
435
449
  const isSignal = (value: unknown): value is Signal<unknown> => {
@@ -462,14 +476,14 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
462
476
  if (observer) {
463
477
  link(node, observer);
464
478
 
465
- if (node.type === COMPUTED) {
466
- let height = node.height;
479
+ if ((node as Computed<unknown>).state & STATE_COMPUTED) {
480
+ let height = (node as Computed<T>).height;
467
481
 
468
482
  if (height >= observer.height) {
469
483
  observer.height = height + 1;
470
484
  }
471
485
 
472
- if (height >= heap_i || node.state & STATE_NOTIFY_MASK) {
486
+ if (height >= heap_i || (node as Computed<T>).state & STATE_NOTIFY_MASK) {
473
487
  if (!notified) {
474
488
  notified = true;
475
489
 
@@ -480,7 +494,7 @@ const read = <T>(node: Signal<T> | Computed<T>): T => {
480
494
  }
481
495
  }
482
496
 
483
- update(node);
497
+ update(node as Computed<T>);
484
498
  }
485
499
  }
486
500
  }
@@ -501,7 +515,7 @@ const root = <T>(fn: ((dispose: VoidFunction) => T) | (() => T)) => {
501
515
  root.disposables = 0;
502
516
 
503
517
  if (tracking) {
504
- scope = self = { cleanup: null } as Computed<unknown>;
518
+ scope = self = { cleanup: null, state: STATE_COMPUTED } as Computed<unknown>;
505
519
  value = (fn as (dispose: VoidFunction) => T)(c = () => dispose(self!));
506
520
  }
507
521
  else {
@@ -553,7 +567,7 @@ const write = <T>(signal: Signal<T>, value: T) => {
553
567
 
554
568
 
555
569
  export {
556
- computed,
570
+ asyncComputed, computed,
557
571
  dispose,
558
572
  effect,
559
573
  isComputed, isSignal,
package/src/types.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { ts } from '@esportsplus/typescript';
2
- import { COMPUTED, SIGNAL, STATE_CHECK, STATE_DIRTY, STATE_IN_HEAP, STATE_NONE, STATE_RECOMPUTING } from './constants';
2
+ import { SIGNAL } from './constants';
3
3
  import { ReactiveArray } from './reactive';
4
4
 
5
5
 
@@ -11,15 +11,9 @@ interface Computed<T> {
11
11
  height: number;
12
12
  nextHeap: Computed<unknown> | undefined;
13
13
  prevHeap: Computed<unknown>;
14
- state:
15
- typeof STATE_CHECK |
16
- typeof STATE_DIRTY |
17
- typeof STATE_IN_HEAP |
18
- typeof STATE_NONE |
19
- typeof STATE_RECOMPUTING;
14
+ state: number;
20
15
  subs: Link | null;
21
16
  subsTail: Link | null;
22
- type: typeof COMPUTED;
23
17
  value: T;
24
18
  }
25
19