@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.
- package/build/constants.d.ts +2 -1
- package/build/constants.js +2 -1
- package/build/reactive/array.js +24 -22
- package/build/reactive/object.js +10 -11
- package/build/system.d.ts +2 -1
- package/build/system.js +35 -23
- package/build/types.d.ts +2 -3
- package/package.json +3 -2
- package/src/constants.ts +3 -1
- package/src/reactive/array.ts +27 -26
- package/src/reactive/object.ts +17 -14
- package/src/system.ts +47 -31
- package/src/types.ts +2 -8
- package/tests/array.ts +906 -0
- package/tests/async-computed.ts +195 -0
- package/tests/bench/array.ts +227 -0
- package/tests/bench/reactive-object.ts +54 -0
- package/tests/bench/system.ts +317 -0
- package/tests/compiler.ts +326 -0
- package/tests/effects.ts +211 -0
- package/tests/nested.ts +293 -0
- package/tests/objects.ts +185 -0
- package/tests/primitives.ts +280 -0
- package/tests/reactive.ts +534 -0
- package/tests/system.ts +1014 -0
- package/tests/tsconfig.json +17 -0
- package/vitest.config.ts +18 -0
- package/test/arrays.ts +0 -146
- package/test/debug.ts +0 -7
- package/test/effects.ts +0 -168
- package/test/index.ts +0 -8
- package/test/nested.ts +0 -201
- package/test/objects.ts +0 -106
- package/test/primitives.ts +0 -87
- package/test/range.ts +0 -45
- package/test/vite.config.ts +0 -41
package/build/constants.d.ts
CHANGED
|
@@ -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 };
|
package/build/constants.js
CHANGED
|
@@ -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 };
|
package/build/reactive/array.js
CHANGED
|
@@ -18,10 +18,10 @@ class ReactiveArray extends Array {
|
|
|
18
18
|
return read(this._length);
|
|
19
19
|
}
|
|
20
20
|
set $length(value) {
|
|
21
|
-
if (value >
|
|
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,
|
|
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 >=
|
|
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,
|
|
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
|
-
|
|
84
|
-
listeners.
|
|
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,
|
|
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
|
|
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,
|
|
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(
|
|
153
|
-
for (let i = 0
|
|
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(),
|
|
158
|
-
for (let i = 0
|
|
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
|
|
168
|
-
let
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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,
|
|
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
|
}
|
package/build/reactive/object.js
CHANGED
|
@@ -8,8 +8,9 @@ class ReactiveObject {
|
|
|
8
8
|
if (data == null) {
|
|
9
9
|
return;
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
|
|
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,
|
|
52
|
-
node = signal(undefined);
|
|
52
|
+
let factory = node, out = signal(undefined), v = 0;
|
|
53
53
|
(this.disposers ??= []).push(effect(() => {
|
|
54
|
-
let id = ++
|
|
55
|
-
read(factory).then((
|
|
56
|
-
if (id !==
|
|
54
|
+
let id = ++v;
|
|
55
|
+
read(factory).then((resolved) => {
|
|
56
|
+
if (id !== v) {
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
|
-
write(
|
|
59
|
+
write(out, resolved);
|
|
60
60
|
});
|
|
61
61
|
}));
|
|
62
|
+
return out;
|
|
62
63
|
}
|
|
63
|
-
|
|
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 {
|
|
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,
|
|
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 =
|
|
85
|
+
let pooled = linkPoolHead, newLink = sub.depsTail =
|
|
86
86
|
dep.subsTail = pooled
|
|
87
|
-
? (
|
|
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
|
|
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 =
|
|
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.
|
|
224
|
+
else if ((dep.subs = nextSub) === null && dep.state & STATE_COMPUTED) {
|
|
223
225
|
dispose(dep);
|
|
224
226
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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.
|
|
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 =
|
|
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:
|
|
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.
|
|
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.
|
|
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 {
|
|
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:
|
|
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.
|
|
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
|
};
|
package/src/reactive/array.ts
CHANGED
|
@@ -69,11 +69,11 @@ class ReactiveArray<T> extends Array<T> {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
set $length(value: number) {
|
|
72
|
-
if (value >
|
|
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,
|
|
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 >=
|
|
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,
|
|
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
|
-
|
|
158
|
-
listeners.
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
256
|
+
let n = this.length,
|
|
257
|
+
before = new Array(n) as T[];
|
|
251
258
|
|
|
252
|
-
for (let i = 0
|
|
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
|
-
|
|
260
|
-
order = new Array(this.length);
|
|
266
|
+
order = new Array(n);
|
|
261
267
|
|
|
262
|
-
for (let i = 0
|
|
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
|
|
275
|
-
let
|
|
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
|
-
|
|
283
|
+
order[i] = list.length === 1 ? list[0] : list[list.length - 1];
|
|
284
284
|
|
|
285
|
-
|
|
286
|
-
|
|
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,
|
|
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]);
|
package/src/reactive/object.ts
CHANGED
|
@@ -14,8 +14,11 @@ class ReactiveObject<T extends Record<PropertyKey, unknown>> {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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 = ++
|
|
81
|
+
let id = ++v;
|
|
80
82
|
|
|
81
|
-
(read(factory) as Promise<ReturnType<T>>).then((
|
|
82
|
-
if (id !==
|
|
83
|
+
(read(factory) as Promise<ReturnType<T>>).then((resolved) => {
|
|
84
|
+
if (id !== v) {
|
|
83
85
|
return;
|
|
84
86
|
}
|
|
85
87
|
|
|
86
|
-
write(
|
|
88
|
+
write(out as Signal<typeof resolved>, resolved);
|
|
87
89
|
});
|
|
88
90
|
})
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
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
|
}
|