@esportsplus/reactivity 0.30.2 → 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 };
@@ -18,10 +18,10 @@ class ReactiveArray extends Array {
18
18
  return read(this._length);
19
19
  }
20
20
  set $length(value) {
21
- if (value > super.length) {
21
+ if (value > this.length) {
22
22
  throw Error(`@esportsplus/reactivity: cannot set length to a value larger than the current length, use splice instead.`);
23
23
  }
24
- this.splice(value, super.length);
24
+ this.splice(value, this.length);
25
25
  }
26
26
  $set(i, value) {
27
27
  let prev = this[i];
@@ -29,7 +29,7 @@ class ReactiveArray extends Array {
29
29
  return;
30
30
  }
31
31
  this[i] = value;
32
- if (i >= super.length) {
32
+ if (i >= this.length) {
33
33
  write(this._length, i + 1);
34
34
  }
35
35
  this.dispatch('set', { index: i, item: value });
@@ -55,7 +55,7 @@ class ReactiveArray extends Array {
55
55
  }
56
56
  }
57
57
  if (added.length) {
58
- write(this._length, super.length);
58
+ write(this._length, this.length);
59
59
  this.dispatch('concat', { items: added });
60
60
  }
61
61
  return this;
@@ -65,6 +65,7 @@ class ReactiveArray extends Array {
65
65
  if (!listeners) {
66
66
  return;
67
67
  }
68
+ let dirty = false;
68
69
  for (let i = 0, n = listeners.length; i < n; i++) {
69
70
  let listener = listeners[i];
70
71
  if (listener === null) {
@@ -73,15 +74,19 @@ class ReactiveArray extends Array {
73
74
  try {
74
75
  listener(value);
75
76
  if (listener.once !== undefined) {
77
+ dirty = true;
76
78
  listeners[i] = null;
77
79
  }
78
80
  }
79
81
  catch {
82
+ dirty = true;
80
83
  listeners[i] = null;
81
84
  }
82
85
  }
83
- while (listeners.length && listeners[listeners.length - 1] === null) {
84
- listeners.pop();
86
+ if (dirty) {
87
+ while (listeners.length && listeners[listeners.length - 1] === null) {
88
+ listeners.pop();
89
+ }
85
90
  }
86
91
  }
87
92
  dispose() {
@@ -120,14 +125,14 @@ class ReactiveArray extends Array {
120
125
  let item = super.pop();
121
126
  if (item !== undefined) {
122
127
  dispose(item);
123
- write(this._length, super.length);
128
+ write(this._length, this.length);
124
129
  this.dispatch('pop', { item });
125
130
  }
126
131
  return item;
127
132
  }
128
133
  push(...items) {
129
134
  if (!items.length) {
130
- return super.length;
135
+ return this.length;
131
136
  }
132
137
  let length = super.push(...items);
133
138
  write(this._length, length);
@@ -143,19 +148,19 @@ class ReactiveArray extends Array {
143
148
  let item = super.shift();
144
149
  if (item !== undefined) {
145
150
  dispose(item);
146
- write(this._length, super.length);
151
+ write(this._length, this.length);
147
152
  this.dispatch('shift', { item });
148
153
  }
149
154
  return item;
150
155
  }
151
156
  sort(fn) {
152
- let before = new Array(this.length);
153
- for (let i = 0, n = before.length; i < n; i++) {
157
+ let n = this.length, before = new Array(n);
158
+ for (let i = 0; i < n; i++) {
154
159
  before[i] = this[i];
155
160
  }
156
161
  super.sort(fn);
157
- let buckets = new Map(), cursors = new Map(), order = new Array(this.length);
158
- for (let i = 0, n = before.length; i < n; i++) {
162
+ let buckets = new Map(), order = new Array(n);
163
+ for (let i = 0; i < n; i++) {
159
164
  let value = before[i], list = buckets.get(value);
160
165
  if (!list) {
161
166
  buckets.set(value, [i]);
@@ -164,15 +169,12 @@ class ReactiveArray extends Array {
164
169
  list.push(i);
165
170
  }
166
171
  }
167
- for (let i = 0, n = this.length; i < n; i++) {
168
- let value = this[i], list = buckets.get(value);
169
- if (!list) {
170
- order[i] = i;
171
- continue;
172
+ for (let i = 0; i < n; i++) {
173
+ let list = buckets.get(this[i]);
174
+ order[i] = list.length === 1 ? list[0] : list[list.length - 1];
175
+ if (list.length > 1) {
176
+ list.pop();
172
177
  }
173
- let cursor = cursors.get(value) || 0;
174
- order[i] = list[cursor];
175
- cursors.set(value, cursor + 1);
176
178
  }
177
179
  this.dispatch('sort', { order });
178
180
  return this;
@@ -180,7 +182,7 @@ class ReactiveArray extends Array {
180
182
  splice(start, deleteCount = this.length, ...items) {
181
183
  let removed = super.splice(start, deleteCount, ...items);
182
184
  if (items.length > 0 || removed.length > 0) {
183
- write(this._length, super.length);
185
+ write(this._length, this.length);
184
186
  for (let i = 0, n = removed.length; i < n; i++) {
185
187
  dispose(removed[i]);
186
188
  }
@@ -8,8 +8,9 @@ class ReactiveObject {
8
8
  if (data == null) {
9
9
  return;
10
10
  }
11
- for (let key in data) {
12
- let value = data[key], type = typeof value;
11
+ let keys = Object.keys(data);
12
+ for (let i = 0, n = keys.length; i < n; i++) {
13
+ let key = keys[i], value = data[key], type = typeof value;
13
14
  if (type === 'function') {
14
15
  let node = this[COMPUTED](value);
15
16
  defineProperty(this, key, {
@@ -48,21 +49,19 @@ class ReactiveObject {
48
49
  return root(() => {
49
50
  let node = computed(value);
50
51
  if (isPromise(node.value)) {
51
- let factory = node, version = 0;
52
- node = signal(undefined);
52
+ let factory = node, out = signal(undefined), v = 0;
53
53
  (this.disposers ??= []).push(effect(() => {
54
- let id = ++version;
55
- read(factory).then((v) => {
56
- if (id !== version) {
54
+ let id = ++v;
55
+ read(factory).then((resolved) => {
56
+ if (id !== v) {
57
57
  return;
58
58
  }
59
- write(node, v);
59
+ write(out, resolved);
60
60
  });
61
61
  }));
62
+ return out;
62
63
  }
63
- else {
64
- (this.disposers ??= []).push(() => dispose(node));
65
- }
64
+ (this.disposers ??= []).push(() => dispose(node));
66
65
  return node;
67
66
  });
68
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,
@@ -112,7 +113,7 @@ function link(dep, sub) {
112
113
  dep.subs = newLink;
113
114
  }
114
115
  }
115
- function notify(computed, newState = STATE_DIRTY) {
116
+ function notify(computed, newState) {
116
117
  let state = computed.state;
117
118
  if ((state & STATE_NOTIFY_MASK) >= newState) {
118
119
  return;
@@ -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 {
@@ -202,6 +203,7 @@ function stabilize() {
202
203
  }
203
204
  observer = o;
204
205
  if (stabilizer === STABILIZER_RESCHEDULE) {
206
+ stabilizer = STABILIZER_SCHEDULED;
205
207
  microtask(stabilize);
206
208
  }
207
209
  else {
@@ -219,21 +221,20 @@ function unlink(link) {
219
221
  if (prevSub) {
220
222
  prevSub.nextSub = nextSub;
221
223
  }
222
- else if ((dep.subs = nextSub) === null && dep.type === COMPUTED) {
224
+ else if ((dep.subs = nextSub) === null && dep.state & STATE_COMPUTED) {
223
225
  dispose(dep);
224
226
  }
225
- if (linkPool.length < linkPoolMax) {
226
- link.dep = link.sub = null;
227
- link.nextDep = link.nextSub = link.prevSub = null;
228
- linkPool.push(link);
229
- }
227
+ link.dep = link.sub = null;
228
+ link.nextSub = link.prevSub = null;
229
+ link.nextDep = linkPoolHead;
230
+ linkPoolHead = link;
230
231
  return nextDep;
231
232
  }
232
233
  function update(computed) {
233
234
  if (computed.state & STATE_CHECK) {
234
235
  for (let link = computed.deps; link; link = link.nextDep) {
235
236
  let dep = link.dep;
236
- if (dep.type === COMPUTED) {
237
+ if (dep.state & STATE_COMPUTED) {
237
238
  update(dep);
238
239
  if (computed.state & STATE_DIRTY) {
239
240
  break;
@@ -244,8 +245,20 @@ function update(computed) {
244
245
  if (computed.state & STATE_DIRTY) {
245
246
  recompute(computed, true);
246
247
  }
247
- computed.state = STATE_NONE;
248
+ computed.state = STATE_COMPUTED;
248
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
+ };
249
262
  const computed = (fn) => {
250
263
  let self = {
251
264
  cleanup: null,
@@ -255,10 +268,9 @@ const computed = (fn) => {
255
268
  height: 0,
256
269
  nextHeap: undefined,
257
270
  prevHeap: null,
258
- state: STATE_NONE,
271
+ state: STATE_COMPUTED,
259
272
  subs: null,
260
273
  subsTail: null,
261
- type: COMPUTED,
262
274
  value: undefined,
263
275
  };
264
276
  self.prevHeap = self;
@@ -302,7 +314,7 @@ const effect = (fn) => {
302
314
  };
303
315
  };
304
316
  const isComputed = (value) => {
305
- return isObject(value) && value.type === COMPUTED;
317
+ return isObject(value) && !!(value.state & STATE_COMPUTED);
306
318
  };
307
319
  const isSignal = (value) => {
308
320
  return isObject(value) && value.type === SIGNAL;
@@ -327,7 +339,7 @@ const onCleanup = (fn) => {
327
339
  const read = (node) => {
328
340
  if (observer) {
329
341
  link(node, observer);
330
- if (node.type === COMPUTED) {
342
+ if (node.state & STATE_COMPUTED) {
331
343
  let height = node.height;
332
344
  if (height >= observer.height) {
333
345
  observer.height = height + 1;
@@ -337,7 +349,7 @@ const read = (node) => {
337
349
  notified = true;
338
350
  for (let i = 0; i <= heap_n; i++) {
339
351
  for (let computed = heap[i]; computed !== undefined; computed = computed.nextHeap) {
340
- notify(computed);
352
+ notify(computed, STATE_DIRTY);
341
353
  }
342
354
  }
343
355
  }
@@ -352,7 +364,7 @@ const root = (fn) => {
352
364
  observer = null;
353
365
  root.disposables = 0;
354
366
  if (tracking) {
355
- scope = self = { cleanup: null };
367
+ scope = self = { cleanup: null, state: STATE_COMPUTED };
356
368
  value = fn(c = () => dispose(self));
357
369
  }
358
370
  else {
@@ -380,14 +392,14 @@ const write = (signal, value) => {
380
392
  if (signal.value === value) {
381
393
  return;
382
394
  }
383
- notified = false;
384
395
  signal.value = value;
385
396
  if (signal.subs === null) {
386
397
  return;
387
398
  }
399
+ notified = false;
388
400
  for (let link = signal.subs; link; link = link.nextSub) {
389
401
  insertIntoHeap(link.sub);
390
402
  }
391
403
  schedule();
392
404
  };
393
- 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
@@ -6,7 +6,8 @@
6
6
  "devDependencies": {
7
7
  "@esportsplus/typescript": "^0.29.0",
8
8
  "@types/node": "^25.0.9",
9
- "vite": "^7.3.1"
9
+ "vite": "^7.3.1",
10
+ "vitest": "^4.0.18"
10
11
  },
11
12
  "exports": {
12
13
  ".": {
@@ -35,7 +36,7 @@
35
36
  },
36
37
  "type": "module",
37
38
  "types": "build/index.d.ts",
38
- "version": "0.30.2",
39
+ "version": "0.31.0",
39
40
  "scripts": {
40
41
  "build": "tsc",
41
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
  };
@@ -69,11 +69,11 @@ class ReactiveArray<T> extends Array<T> {
69
69
  }
70
70
 
71
71
  set $length(value: number) {
72
- if (value > super.length) {
72
+ if (value > this.length) {
73
73
  throw Error(`@esportsplus/reactivity: cannot set length to a value larger than the current length, use splice instead.`);
74
74
  }
75
75
 
76
- this.splice(value, super.length);
76
+ this.splice(value, this.length);
77
77
  }
78
78
 
79
79
 
@@ -86,7 +86,7 @@ class ReactiveArray<T> extends Array<T> {
86
86
 
87
87
  this[i] = value;
88
88
 
89
- if (i >= super.length) {
89
+ if (i >= this.length) {
90
90
  write(this._length, i + 1);
91
91
  }
92
92
 
@@ -121,7 +121,7 @@ class ReactiveArray<T> extends Array<T> {
121
121
  }
122
122
 
123
123
  if (added.length) {
124
- write(this._length, super.length);
124
+ write(this._length, this.length);
125
125
  this.dispatch('concat', { items: added });
126
126
  }
127
127
 
@@ -135,6 +135,8 @@ class ReactiveArray<T> extends Array<T> {
135
135
  return;
136
136
  }
137
137
 
138
+ let dirty = false;
139
+
138
140
  for (let i = 0, n = listeners.length; i < n; i++) {
139
141
  let listener = listeners[i];
140
142
 
@@ -146,16 +148,20 @@ class ReactiveArray<T> extends Array<T> {
146
148
  listener(value);
147
149
 
148
150
  if (listener.once !== undefined) {
151
+ dirty = true;
149
152
  listeners[i] = null;
150
153
  }
151
154
  }
152
155
  catch {
156
+ dirty = true;
153
157
  listeners[i] = null;
154
158
  }
155
159
  }
156
160
 
157
- while (listeners.length && listeners[listeners.length - 1] === null) {
158
- listeners.pop();
161
+ if (dirty) {
162
+ while (listeners.length && listeners[listeners.length - 1] === null) {
163
+ listeners.pop();
164
+ }
159
165
  }
160
166
  }
161
167
 
@@ -205,7 +211,7 @@ class ReactiveArray<T> extends Array<T> {
205
211
 
206
212
  if (item !== undefined) {
207
213
  dispose(item);
208
- write(this._length, super.length);
214
+ write(this._length, this.length);
209
215
 
210
216
  this.dispatch('pop', { item });
211
217
  }
@@ -215,7 +221,7 @@ class ReactiveArray<T> extends Array<T> {
215
221
 
216
222
  push(...items: T[]) {
217
223
  if (!items.length) {
218
- return super.length;
224
+ return this.length;
219
225
  }
220
226
 
221
227
  let length = super.push(...items);
@@ -238,7 +244,7 @@ class ReactiveArray<T> extends Array<T> {
238
244
 
239
245
  if (item !== undefined) {
240
246
  dispose(item);
241
- write(this._length, super.length);
247
+ write(this._length, this.length);
242
248
 
243
249
  this.dispatch('shift', { item });
244
250
  }
@@ -247,19 +253,19 @@ class ReactiveArray<T> extends Array<T> {
247
253
  }
248
254
 
249
255
  sort(fn?: (a: T, b: T) => number) {
250
- let before = new Array(this.length) as T[];
256
+ let n = this.length,
257
+ before = new Array(n) as T[];
251
258
 
252
- for (let i = 0, n = before.length; i < n; i++) {
259
+ for (let i = 0; i < n; i++) {
253
260
  before[i] = this[i];
254
261
  }
255
262
 
256
263
  super.sort(fn);
257
264
 
258
265
  let buckets = new Map<any, number[]>(),
259
- cursors = new Map<any, number>(),
260
- order = new Array(this.length);
266
+ order = new Array(n);
261
267
 
262
- for (let i = 0, n = before.length; i < n; i++) {
268
+ for (let i = 0; i < n; i++) {
263
269
  let value = before[i],
264
270
  list = buckets.get(value);
265
271
 
@@ -271,19 +277,14 @@ class ReactiveArray<T> extends Array<T> {
271
277
  }
272
278
  }
273
279
 
274
- for (let i = 0, n = this.length; i < n; i++) {
275
- let value = this[i],
276
- list = buckets.get(value);
277
-
278
- if (!list) {
279
- order[i] = i;
280
- continue;
281
- }
280
+ for (let i = 0; i < n; i++) {
281
+ let list = buckets.get(this[i])!;
282
282
 
283
- let cursor = cursors.get(value) || 0;
283
+ order[i] = list.length === 1 ? list[0] : list[list.length - 1];
284
284
 
285
- order[i] = list[cursor];
286
- cursors.set(value, cursor + 1);
285
+ if (list.length > 1) {
286
+ list.pop();
287
+ }
287
288
  }
288
289
 
289
290
  this.dispatch('sort', { order });
@@ -295,7 +296,7 @@ class ReactiveArray<T> extends Array<T> {
295
296
  let removed = super.splice(start, deleteCount, ...items);
296
297
 
297
298
  if (items.length > 0 || removed.length > 0) {
298
- write(this._length, super.length);
299
+ write(this._length, this.length);
299
300
 
300
301
  for (let i = 0, n = removed.length; i < n; i++) {
301
302
  dispose(removed[i]);
@@ -14,8 +14,11 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
14
14
  return;
15
15
  }
16
16
 
17
- for (let key in data) {
18
- let value = data[key as keyof T],
17
+ let keys = Object.keys(data);
18
+
19
+ for (let i = 0, n = keys.length; i < n; i++) {
20
+ let key = keys[i],
21
+ value = data[key as keyof T],
19
22
  type = typeof value;
20
23
 
21
24
  if (type === 'function') {
@@ -69,29 +72,29 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
69
72
  let node: Computed<ReturnType<T>> | Signal<ReturnType<T> | undefined> = computed(value);
70
73
 
71
74
  if (isPromise(node.value)) {
72
- let factory = node,
73
- version = 0;
74
-
75
- node = signal<ReturnType<T> | undefined>(undefined);
75
+ let factory = node as Computed<ReturnType<T>>,
76
+ out = signal<ReturnType<T> | undefined>(undefined),
77
+ v = 0;
76
78
 
77
79
  (this.disposers ??= []).push(
78
80
  effect(() => {
79
- let id = ++version;
81
+ let id = ++v;
80
82
 
81
- (read(factory) as Promise<ReturnType<T>>).then((v) => {
82
- if (id !== version) {
83
+ (read(factory) as Promise<ReturnType<T>>).then((resolved) => {
84
+ if (id !== v) {
83
85
  return;
84
86
  }
85
87
 
86
- write(node as Signal<typeof v>, v);
88
+ write(out as Signal<typeof resolved>, resolved);
87
89
  });
88
90
  })
89
- )
90
- }
91
- else {
92
- (this.disposers ??= []).push(() => dispose(node as Computed<ReturnType<T>>));
91
+ );
92
+
93
+ return out;
93
94
  }
94
95
 
96
+ (this.disposers ??= []).push(() => dispose(node as Computed<ReturnType<T>>));
97
+
95
98
  return node;
96
99
  });
97
100
  }