@esportsplus/reactivity 0.30.3 → 0.31.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.
@@ -135,7 +135,7 @@ The library requires a build-time transformer to convert `reactive()` calls into
135
135
  ```typescript
136
136
  // vite.config.ts
137
137
  import { defineConfig } from 'vite';
138
- import reactivity from '@esportsplus/reactivity/plugins/vite';
138
+ import reactivity from '@esportsplus/reactivity/compiler/vite';
139
139
 
140
140
  export default defineConfig({
141
141
  plugins: [
@@ -153,7 +153,7 @@ For direct TypeScript compilation using `ttsc` or `ts-patch`:
153
153
  {
154
154
  "compilerOptions": {
155
155
  "plugins": [
156
- { "transform": "@esportsplus/reactivity/plugins/tsc" }
156
+ { "transform": "@esportsplus/reactivity/compiler/tsc" }
157
157
  ]
158
158
  }
159
159
  }
@@ -221,9 +221,9 @@ let user = new ReactiveObject_1();
221
221
  | `reactive(() => expr)` | Creates a computed value (compile-time only) |
222
222
  | `reactive({...})` | Creates a reactive object with signals and computeds |
223
223
  | `reactive([...])` | Creates a reactive array |
224
- | `effect(fn)` | Runs a function that re-executes when dependencies change |
225
- | `root(fn)` | Creates an untracked scope for effects |
226
- | `onCleanup(fn)` | Registers a cleanup function for the current effect |
224
+ | `effect(fn)` | Runs a function that re-executes when dependencies change. Returns a dispose function |
225
+ | `root(fn)` | Creates an untracked scope. If `fn` accepts an argument, a dispose function is provided |
226
+ | `onCleanup(fn)` | Registers a cleanup function for the current effect/computed |
227
227
 
228
228
  ### Low-Level Functions
229
229
 
@@ -235,6 +235,7 @@ These are typically only used by the transformer output:
235
235
  | `computed(fn)` | Creates a raw computed |
236
236
  | `read(node)` | Reads a signal or computed value |
237
237
  | `write(signal, value)` | Sets a signal value |
238
+ | `asyncComputed(fn)` | Creates a signal that resolves an async computed. Initial value is `undefined` |
238
239
  | `dispose(computed)` | Disposes a computed and its dependencies |
239
240
 
240
241
  ### Type Guards
@@ -271,7 +272,9 @@ Symbol constants for type identification:
271
272
  |------|-------------|
272
273
  | `Signal<T>` | Signal node type |
273
274
  | `Computed<T>` | Computed node type |
275
+ | `Link` | Dependency graph link between nodes |
274
276
  | `Reactive<T>` | Utility type for inferring reactive object/array types |
277
+ | `TransformResult` | Compiler transform output metadata |
275
278
 
276
279
  ## ReactiveArray
277
280
 
@@ -279,14 +282,14 @@ Symbol constants for type identification:
279
282
 
280
283
  | Method | Description |
281
284
  |--------|-------------|
282
- | `$length()` | Returns the reactive length (tracks reads) |
283
- | `$set(index, value)` | Sets an item at index reactively |
284
- | `clear()` | Removes all items and disposes nested reactive objects |
285
- | `dispose()` | Disposes all nested reactive objects |
286
- | `on(event, listener)` | Subscribes to an array event |
285
+ | `clear()` | Removes all items, disposes nested reactive objects, and dispatches `clear` event |
286
+ | `concat(...items)` | Appends items **in place** (mutating, returns `this`). Unlike `Array.prototype.concat` |
287
+ | `dispatch(event, value?)` | Manually dispatches an event to registered listeners |
288
+ | `dispose()` | Disposes all nested reactive objects and empties the array |
289
+ | `on(event, listener)` | Subscribes to an array event. Deduplicates by reference |
287
290
  | `once(event, listener)` | Subscribes to an event once |
288
291
 
289
- All standard array methods (`push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse`, `concat`) are supported and trigger corresponding events.
292
+ All standard array methods (`push`, `pop`, `shift`, `unshift`, `splice`, `sort`, `reverse`) are overridden and trigger corresponding events. Empty calls to `push`, `unshift`, and `concat` are no-ops.
290
293
 
291
294
  ### Events
292
295
 
@@ -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 };
@@ -191,6 +191,9 @@ class ReactiveArray extends Array {
191
191
  return removed;
192
192
  }
193
193
  unshift(...items) {
194
+ if (!items.length) {
195
+ return this.length;
196
+ }
194
197
  let length = super.unshift(...items);
195
198
  write(this._length, length);
196
199
  this.dispatch('unshift', { items });
@@ -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.1",
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
  };
@@ -309,6 +309,10 @@ class ReactiveArray<T> extends Array<T> {
309
309
  }
310
310
 
311
311
  unshift(...items: T[]) {
312
+ if (!items.length) {
313
+ return this.length;
314
+ }
315
+
312
316
  let length = super.unshift(...items);
313
317
 
314
318
  write(this._length, length);
@@ -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