@adbl/cells 0.0.0 → 0.0.2
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/library/classes.js +233 -74
- package/library/root.js +1 -1
- package/package.json +1 -1
- package/types/library/classes.d.ts +67 -23
- package/types/library/root.d.ts +2 -2
- package/jsconfig.json +0 -25
package/library/classes.js
CHANGED
|
@@ -25,6 +25,8 @@
|
|
|
25
25
|
* An AbortSignal to be used to ignore the effect if it is aborted.
|
|
26
26
|
* @property {string} [name]
|
|
27
27
|
* The name of the effect for debugging purposes.
|
|
28
|
+
* @property {boolean} [weak]
|
|
29
|
+
* Whether the effect should be weakly referenced. This means that the effect will be garbage collected if there are no other references to it.
|
|
28
30
|
* @property {number} [priority]
|
|
29
31
|
* The priority of the effect. Higher priority effects are executed first. The default priority is 0.
|
|
30
32
|
*/
|
|
@@ -47,24 +49,81 @@
|
|
|
47
49
|
|
|
48
50
|
import { activeComputedValues, root } from './root.js';
|
|
49
51
|
|
|
52
|
+
/**
|
|
53
|
+
* @template T
|
|
54
|
+
* @typedef {{
|
|
55
|
+
* deref: () => T | undefined
|
|
56
|
+
* }} Reference
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/** @template T */
|
|
60
|
+
class Effect {
|
|
61
|
+
/**
|
|
62
|
+
* @type {EffectOptions | undefined}
|
|
63
|
+
*/
|
|
64
|
+
options;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @type {WeakRef<(newValue: T) => void> | ((newValue: T) => void) }
|
|
68
|
+
*/
|
|
69
|
+
#callback;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* @param {(newValue: T) => void} callback
|
|
73
|
+
* @param {EffectOptions} [options]
|
|
74
|
+
*/
|
|
75
|
+
constructor(callback, options) {
|
|
76
|
+
if (options?.weak) {
|
|
77
|
+
this.#callback = new WeakRef(callback);
|
|
78
|
+
} else {
|
|
79
|
+
this.#callback = callback;
|
|
80
|
+
}
|
|
81
|
+
this.options = options;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Returns the callback function, if it still exists.
|
|
86
|
+
* @returns {((newValue: T) => void) | undefined}
|
|
87
|
+
*/
|
|
88
|
+
get callback() {
|
|
89
|
+
if (this.#callback instanceof WeakRef) {
|
|
90
|
+
return this.#callback.deref();
|
|
91
|
+
}
|
|
92
|
+
return this.#callback;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
50
96
|
/**
|
|
51
97
|
* @template T
|
|
52
98
|
*/
|
|
53
99
|
export class Cell {
|
|
54
100
|
/**
|
|
55
|
-
* @type {Array<
|
|
56
|
-
* effect: (newValue: T) => void,
|
|
57
|
-
* options?: EffectOptions,
|
|
58
|
-
* })>}
|
|
101
|
+
* @type {Array<Effect<T>>}
|
|
59
102
|
* @protected
|
|
60
103
|
*/
|
|
61
|
-
|
|
104
|
+
__effects = [];
|
|
62
105
|
|
|
63
106
|
/**
|
|
64
|
-
* @type {Array<WeakRef<DerivedCell<any
|
|
107
|
+
* @type {Array<[WeakRef<DerivedCell<any>>, () => any]>}
|
|
65
108
|
* @protected
|
|
66
109
|
*/
|
|
67
|
-
|
|
110
|
+
__derivedCells = [];
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* @readonly
|
|
114
|
+
*/
|
|
115
|
+
get effects() {
|
|
116
|
+
return this.__effects;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* @readonly
|
|
121
|
+
* @returns {Array<DerivedCell<any>>}
|
|
122
|
+
*/
|
|
123
|
+
get derivedCells() {
|
|
124
|
+
// @ts-ignore
|
|
125
|
+
return this.__derivedCells.map((cell) => cell.deref()).filter(Boolean);
|
|
126
|
+
}
|
|
68
127
|
|
|
69
128
|
/**
|
|
70
129
|
* @protected @type T
|
|
@@ -84,9 +143,22 @@ export class Cell {
|
|
|
84
143
|
* @returns {T} The value of the Cell.
|
|
85
144
|
*/
|
|
86
145
|
valueOf() {
|
|
146
|
+
return this.revalued;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get value() {
|
|
87
150
|
return this.wvalue;
|
|
88
151
|
}
|
|
89
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Stringifies the value of the Cell.
|
|
155
|
+
* @returns {string}
|
|
156
|
+
*/
|
|
157
|
+
toString() {
|
|
158
|
+
// @ts-ignore
|
|
159
|
+
return this.wvalue.toString();
|
|
160
|
+
}
|
|
161
|
+
|
|
90
162
|
/**
|
|
91
163
|
* The value stored in the Cell.
|
|
92
164
|
* @protected @type {T}
|
|
@@ -95,12 +167,15 @@ export class Cell {
|
|
|
95
167
|
const currentlyComputedValue = activeComputedValues.at(-1);
|
|
96
168
|
|
|
97
169
|
if (currentlyComputedValue !== undefined) {
|
|
98
|
-
const isAlreadySubscribed = this.
|
|
99
|
-
(ref) => ref.deref() === currentlyComputedValue
|
|
170
|
+
const isAlreadySubscribed = this.__derivedCells.some(
|
|
171
|
+
(ref) => ref[0].deref() === currentlyComputedValue[0]
|
|
100
172
|
);
|
|
101
173
|
if (isAlreadySubscribed) return this.wvalue;
|
|
102
174
|
|
|
103
|
-
this.
|
|
175
|
+
this.__derivedCells.push([
|
|
176
|
+
new WeakRef(currentlyComputedValue[0]),
|
|
177
|
+
currentlyComputedValue[1],
|
|
178
|
+
]);
|
|
104
179
|
}
|
|
105
180
|
|
|
106
181
|
return this.wvalue;
|
|
@@ -138,26 +213,25 @@ export class Cell {
|
|
|
138
213
|
};
|
|
139
214
|
}
|
|
140
215
|
|
|
141
|
-
if (options?.name) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
);
|
|
146
|
-
}
|
|
216
|
+
if (options?.name && this.isListeningTo(options.name)) {
|
|
217
|
+
throw new Error(
|
|
218
|
+
`An effect with the name "${options.name}" is already listening to this cell.`
|
|
219
|
+
);
|
|
147
220
|
}
|
|
148
221
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
}
|
|
222
|
+
const isAlreadySubscribed = this.__effects.some((effect) => {
|
|
223
|
+
return effect.callback === callback;
|
|
224
|
+
});
|
|
152
225
|
|
|
153
|
-
|
|
226
|
+
if (!isAlreadySubscribed) {
|
|
227
|
+
this.__effects.push(new Effect(callback, options));
|
|
228
|
+
}
|
|
154
229
|
|
|
155
|
-
this.
|
|
230
|
+
this.__effects.sort((a, b) => {
|
|
156
231
|
const aPriority = a.options?.priority ?? 0;
|
|
157
232
|
const bPriority = b.options?.priority ?? 0;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
233
|
+
|
|
234
|
+
if (aPriority === bPriority) return 0;
|
|
161
235
|
return aPriority < bPriority ? 1 : -1;
|
|
162
236
|
});
|
|
163
237
|
|
|
@@ -166,12 +240,46 @@ export class Cell {
|
|
|
166
240
|
|
|
167
241
|
/**
|
|
168
242
|
* Creates an effect that is immediately executed with the current value of the cell, and then added to the list of effects for the cell.
|
|
169
|
-
* @param {(newValue: T) => void}
|
|
243
|
+
* @param {(newValue: T) => void} callback - The effect callback to add.
|
|
244
|
+
* @param {Partial<EffectOptions>} [options] - The options for the effect.
|
|
170
245
|
* @returns {() => void} A function that can be called to remove the effect.
|
|
171
246
|
*/
|
|
172
|
-
runAndListen(
|
|
173
|
-
|
|
174
|
-
|
|
247
|
+
runAndListen(callback, options) {
|
|
248
|
+
const cb = callback;
|
|
249
|
+
|
|
250
|
+
cb(this.wvalue);
|
|
251
|
+
|
|
252
|
+
if (options?.signal?.aborted) {
|
|
253
|
+
return () => {};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
options?.signal?.addEventListener('abort', () => {
|
|
257
|
+
this.ignore(cb);
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
if (options?.once) return () => this.ignore(cb);
|
|
261
|
+
|
|
262
|
+
if (options?.name && this.isListeningTo(options.name)) {
|
|
263
|
+
const message = `An effect with the name "${options.name}" is already listening to this cell.`;
|
|
264
|
+
throw new Error(message);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const isAlreadySubscribed = this.__effects.some((e) => {
|
|
268
|
+
return e.callback === callback;
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
if (!isAlreadySubscribed) {
|
|
272
|
+
this.__effects.push(new Effect(cb, options));
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
this.__effects.sort((a, b) => {
|
|
276
|
+
const aPriority = a.options?.priority ?? 0;
|
|
277
|
+
const bPriority = b.options?.priority ?? 0;
|
|
278
|
+
if (aPriority === bPriority) return 0;
|
|
279
|
+
return aPriority < bPriority ? 1 : -1;
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
return () => this.ignore(cb);
|
|
175
283
|
}
|
|
176
284
|
|
|
177
285
|
/**
|
|
@@ -179,10 +287,12 @@ export class Cell {
|
|
|
179
287
|
* @param {(newValue: T) => void} callback - The effect callback to remove.
|
|
180
288
|
*/
|
|
181
289
|
ignore(callback) {
|
|
182
|
-
const index = this.
|
|
290
|
+
const index = this.__effects.findIndex((e) => {
|
|
291
|
+
return e.callback === callback;
|
|
292
|
+
});
|
|
183
293
|
if (index === -1) return;
|
|
184
294
|
|
|
185
|
-
this.
|
|
295
|
+
this.__effects.splice(index, 1);
|
|
186
296
|
}
|
|
187
297
|
|
|
188
298
|
/**
|
|
@@ -191,7 +301,9 @@ export class Cell {
|
|
|
191
301
|
* @returns {boolean} `true` if the cell is listening to a watcher with the specified name, `false` otherwise.
|
|
192
302
|
*/
|
|
193
303
|
isListeningTo(name) {
|
|
194
|
-
return this.
|
|
304
|
+
return this.__effects.some((effect) => {
|
|
305
|
+
return effect?.options?.name === name && effect.callback;
|
|
306
|
+
});
|
|
195
307
|
}
|
|
196
308
|
|
|
197
309
|
/**
|
|
@@ -199,12 +311,12 @@ export class Cell {
|
|
|
199
311
|
* @param {string} name - The name of the watcher to stop listening to.
|
|
200
312
|
*/
|
|
201
313
|
stopListeningTo(name) {
|
|
202
|
-
const effectIndex = this.
|
|
203
|
-
|
|
204
|
-
);
|
|
314
|
+
const effectIndex = this.__effects.findIndex((e) => {
|
|
315
|
+
return e.options?.name === name;
|
|
316
|
+
});
|
|
205
317
|
if (effectIndex === -1) return;
|
|
206
318
|
|
|
207
|
-
this.
|
|
319
|
+
this.__effects.splice(effectIndex, 1);
|
|
208
320
|
}
|
|
209
321
|
|
|
210
322
|
/**
|
|
@@ -213,7 +325,10 @@ export class Cell {
|
|
|
213
325
|
*/
|
|
214
326
|
update() {
|
|
215
327
|
// Run watchers.
|
|
216
|
-
for (const
|
|
328
|
+
for (const effect of this.__effects) {
|
|
329
|
+
const watcher = effect.callback;
|
|
330
|
+
if (watcher === undefined) continue;
|
|
331
|
+
|
|
217
332
|
if (root.batchNestingLevel > 0) {
|
|
218
333
|
root.batchedEffects.set(watcher, [this.wvalue]);
|
|
219
334
|
continue;
|
|
@@ -222,16 +337,40 @@ export class Cell {
|
|
|
222
337
|
watcher(this.wvalue);
|
|
223
338
|
}
|
|
224
339
|
|
|
340
|
+
// Remove dead effects.
|
|
341
|
+
this.__effects = this.__effects.filter((effect) => effect.callback);
|
|
342
|
+
|
|
225
343
|
// Run computed dependents.
|
|
226
|
-
const computedDependents = this.
|
|
344
|
+
const computedDependents = this.__derivedCells;
|
|
227
345
|
if (computedDependents !== undefined) {
|
|
228
346
|
for (const dependent of computedDependents) {
|
|
229
|
-
|
|
347
|
+
// global effects
|
|
348
|
+
for (const [options, effect] of root.globalPreEffects) {
|
|
349
|
+
if (options.ignoreDerivedCells) continue;
|
|
350
|
+
|
|
351
|
+
effect(this.wvalue);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
const deref = dependent[0].deref();
|
|
355
|
+
if (deref === undefined) continue;
|
|
356
|
+
|
|
357
|
+
const computedCell = deref;
|
|
358
|
+
const computedFn = dependent[1];
|
|
359
|
+
|
|
360
|
+
if (root.batchNestingLevel > 0) {
|
|
361
|
+
root.batchedEffects.set(
|
|
362
|
+
() => computedCell.setValue(computedFn()),
|
|
363
|
+
[]
|
|
364
|
+
);
|
|
365
|
+
} else {
|
|
366
|
+
computedCell.setValue(computedFn());
|
|
367
|
+
}
|
|
368
|
+
computedCell.update();
|
|
230
369
|
}
|
|
231
370
|
}
|
|
232
|
-
// Periodically
|
|
233
|
-
this.
|
|
234
|
-
(ref) => ref.deref() !== undefined
|
|
371
|
+
// Periodically drop dead references.
|
|
372
|
+
this.__derivedCells = this.__derivedCells.filter(
|
|
373
|
+
(ref) => ref[0].deref() !== undefined
|
|
235
374
|
);
|
|
236
375
|
|
|
237
376
|
// global effects
|
|
@@ -380,8 +519,10 @@ export class Cell {
|
|
|
380
519
|
|
|
381
520
|
/**
|
|
382
521
|
* Checks if the provided value is an instance of the Cell class.
|
|
383
|
-
* @
|
|
384
|
-
* @
|
|
522
|
+
* @template [T=any]
|
|
523
|
+
* @template [U=any]
|
|
524
|
+
* @param {Cell<T> | U} value - The value to check.
|
|
525
|
+
* @returns {value is Cell<T>} True if the value is an instance of Cell, false otherwise.
|
|
385
526
|
*/
|
|
386
527
|
static isCell = (value) => value instanceof Cell;
|
|
387
528
|
|
|
@@ -452,6 +593,8 @@ export class Cell {
|
|
|
452
593
|
|
|
453
594
|
async function run(input = initialInput) {
|
|
454
595
|
pending.value = true;
|
|
596
|
+
error.value = null;
|
|
597
|
+
data.value = null;
|
|
455
598
|
try {
|
|
456
599
|
initialInput = input;
|
|
457
600
|
const result = await getter(/** @type {X} */ (input));
|
|
@@ -510,19 +653,12 @@ export class Cell {
|
|
|
510
653
|
* @extends {Cell<T>}
|
|
511
654
|
*/
|
|
512
655
|
export class DerivedCell extends Cell {
|
|
513
|
-
/**
|
|
514
|
-
* @type {() => T}
|
|
515
|
-
* @protected
|
|
516
|
-
*/
|
|
517
|
-
computedFn;
|
|
518
|
-
|
|
519
656
|
/**
|
|
520
657
|
* @param {() => T} computedFn - A function that generates the value of the computed.
|
|
521
658
|
*/
|
|
522
659
|
constructor(computedFn) {
|
|
523
660
|
super();
|
|
524
|
-
this
|
|
525
|
-
activeComputedValues.push(this);
|
|
661
|
+
activeComputedValues.push([this, computedFn]);
|
|
526
662
|
this.setValue(computedFn());
|
|
527
663
|
activeComputedValues.pop();
|
|
528
664
|
}
|
|
@@ -540,26 +676,6 @@ export class DerivedCell extends Cell {
|
|
|
540
676
|
set value(_) {
|
|
541
677
|
throw new Error('Cannot set a derived Cell value.');
|
|
542
678
|
}
|
|
543
|
-
|
|
544
|
-
/**
|
|
545
|
-
* Updates the current value with the result of the computed function.
|
|
546
|
-
*/
|
|
547
|
-
update() {
|
|
548
|
-
// global effects
|
|
549
|
-
for (const [options, effect] of root.globalPreEffects) {
|
|
550
|
-
if (options.ignoreDerivedCells) continue;
|
|
551
|
-
|
|
552
|
-
effect(this.wvalue);
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
if (root.batchNestingLevel > 0) {
|
|
556
|
-
root.batchedEffects.set(() => this.setValue(this.computedFn()), []);
|
|
557
|
-
} else {
|
|
558
|
-
this.setValue(this.computedFn());
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
super.update();
|
|
562
|
-
}
|
|
563
679
|
}
|
|
564
680
|
|
|
565
681
|
/**
|
|
@@ -599,7 +715,7 @@ export class SourceCell extends Cell {
|
|
|
599
715
|
|
|
600
716
|
const isEqual = this.options.equals
|
|
601
717
|
? this.options.equals(oldValue, value)
|
|
602
|
-
: oldValue
|
|
718
|
+
: deepEqual(oldValue, value);
|
|
603
719
|
|
|
604
720
|
if (isEqual) return;
|
|
605
721
|
|
|
@@ -614,7 +730,7 @@ export class SourceCell extends Cell {
|
|
|
614
730
|
}
|
|
615
731
|
}
|
|
616
732
|
|
|
617
|
-
this.setValue(value);
|
|
733
|
+
this.setValue(this.options?.shallowProxied ? value : this.proxy(value));
|
|
618
734
|
this.update();
|
|
619
735
|
}
|
|
620
736
|
|
|
@@ -636,10 +752,53 @@ export class SourceCell extends Cell {
|
|
|
636
752
|
return this.proxy(Reflect.get(target, prop));
|
|
637
753
|
},
|
|
638
754
|
set: (target, prop, value) => {
|
|
755
|
+
const formerValue = Reflect.get(target, prop);
|
|
639
756
|
Reflect.set(target, prop, value);
|
|
640
|
-
|
|
757
|
+
|
|
758
|
+
const isEqual = deepEqual(formerValue, value);
|
|
759
|
+
if (!isEqual) this.update();
|
|
760
|
+
|
|
641
761
|
return true;
|
|
642
762
|
},
|
|
643
763
|
});
|
|
644
764
|
}
|
|
645
765
|
}
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
* Recursively compares two values for deep equality.
|
|
769
|
+
* @param {any} a - The first value to compare.
|
|
770
|
+
* @param {any} b - The second value to compare.
|
|
771
|
+
* @returns {boolean} - True if the values are deeply equal, false otherwise.
|
|
772
|
+
*/
|
|
773
|
+
function deepEqual(a, b) {
|
|
774
|
+
if (a === b) return true;
|
|
775
|
+
|
|
776
|
+
if (
|
|
777
|
+
typeof a !== typeof b ||
|
|
778
|
+
typeof a !== 'object' ||
|
|
779
|
+
a === null ||
|
|
780
|
+
b === null
|
|
781
|
+
)
|
|
782
|
+
return false;
|
|
783
|
+
|
|
784
|
+
if (Array.isArray(a)) {
|
|
785
|
+
const aLength = a.length;
|
|
786
|
+
if (!Array.isArray(b) || aLength !== b.length) return false;
|
|
787
|
+
|
|
788
|
+
for (let i = 0; i < aLength; i++) {
|
|
789
|
+
if (!deepEqual(a[i], b[i])) return false;
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
const keysA = Object.keys(a);
|
|
793
|
+
const keysB = Object.keys(b);
|
|
794
|
+
const keysALength = keysA.length;
|
|
795
|
+
if (keysALength !== keysB.length) return false;
|
|
796
|
+
|
|
797
|
+
for (let i = 0; i < keysALength; i++) {
|
|
798
|
+
const key = keysA[i];
|
|
799
|
+
if (a === b) return true;
|
|
800
|
+
if (!(key in b) || !deepEqual(a[key], b[key])) return false;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
return true;
|
|
804
|
+
}
|
package/library/root.js
CHANGED
|
@@ -39,6 +39,6 @@ export const root = {
|
|
|
39
39
|
/**
|
|
40
40
|
* A value representing the computed values that are currently being calculated.
|
|
41
41
|
* It is an array so it can keep track of nested computed values.
|
|
42
|
-
* @type {DerivedCell[]}
|
|
42
|
+
* @type {[DerivedCell, () => any][]}
|
|
43
43
|
*/
|
|
44
44
|
export const activeComputedValues = [];
|
package/package.json
CHANGED
|
@@ -96,31 +96,33 @@ export class Cell<T> {
|
|
|
96
96
|
static batch: (callback: () => void) => void;
|
|
97
97
|
/**
|
|
98
98
|
* Checks if the provided value is an instance of the Cell class.
|
|
99
|
-
* @
|
|
100
|
-
* @
|
|
99
|
+
* @template [T=any]
|
|
100
|
+
* @template [U=any]
|
|
101
|
+
* @param {Cell<T> | U} value - The value to check.
|
|
102
|
+
* @returns {value is Cell<T>} True if the value is an instance of Cell, false otherwise.
|
|
101
103
|
*/
|
|
102
|
-
static isCell: (value:
|
|
104
|
+
static isCell: <T_3 = any, U = any>(value: U | Cell<T_3>) => value is Cell<T_3>;
|
|
103
105
|
/**
|
|
104
106
|
* @template T
|
|
105
107
|
* Flattens the provided value by returning the value if it is not a Cell instance, or the value of the Cell instance if it is.
|
|
106
108
|
* @param {T | Cell<T>} value - The value to be flattened.
|
|
107
109
|
* @returns {T} The flattened value.
|
|
108
110
|
*/
|
|
109
|
-
static flatten: <
|
|
111
|
+
static flatten: <T_4>(value: T_4 | Cell<T_4>) => T_4;
|
|
110
112
|
/**
|
|
111
113
|
* Flattens an array by applying the `flatten` function to each element.
|
|
112
114
|
* @template T
|
|
113
115
|
* @param {Array<T | Cell<T>>} array - The array to be flattened.
|
|
114
116
|
* @returns {Array<T>} A new array with the flattened elements.
|
|
115
117
|
*/
|
|
116
|
-
static flattenArray: <
|
|
118
|
+
static flattenArray: <T_5>(array: (T_5 | Cell<T_5>)[]) => T_5[];
|
|
117
119
|
/**
|
|
118
120
|
* Flattens an object by applying the `flatten` function to each value.
|
|
119
121
|
* @template {object} T
|
|
120
122
|
* @param {T} object - The object to be flattened.
|
|
121
123
|
* @returns {{ [K in keyof T]: T[K] extends Cell<infer U> ? U : T[K] }} A new object with the flattened values.
|
|
122
124
|
*/
|
|
123
|
-
static flattenObject: <
|
|
125
|
+
static flattenObject: <T_6 extends object>(object: T_6) => { [K in keyof T_6]: T_6[K] extends Cell<infer U_1> ? U_1 : T_6[K]; };
|
|
124
126
|
/**
|
|
125
127
|
* Wraps an asynchronous function with managed state.
|
|
126
128
|
*
|
|
@@ -140,21 +142,24 @@ export class Cell<T> {
|
|
|
140
142
|
*/
|
|
141
143
|
static async<X, Y>(getter: (input: X) => Promise<Y>): AsyncRequestAtoms<X, Y>;
|
|
142
144
|
/**
|
|
143
|
-
* @type {Array<
|
|
144
|
-
* effect: (newValue: T) => void,
|
|
145
|
-
* options?: EffectOptions,
|
|
146
|
-
* })>}
|
|
145
|
+
* @type {Array<Effect<T>>}
|
|
147
146
|
* @protected
|
|
148
147
|
*/
|
|
149
|
-
protected
|
|
150
|
-
effect: (newValue: T) => void;
|
|
151
|
-
options?: EffectOptions;
|
|
152
|
-
})>;
|
|
148
|
+
protected __effects: Array<Effect<T>>;
|
|
153
149
|
/**
|
|
154
|
-
* @type {Array<WeakRef<DerivedCell<any
|
|
150
|
+
* @type {Array<[WeakRef<DerivedCell<any>>, () => any]>}
|
|
155
151
|
* @protected
|
|
156
152
|
*/
|
|
157
|
-
protected
|
|
153
|
+
protected __derivedCells: Array<[WeakRef<DerivedCell<any>>, () => any]>;
|
|
154
|
+
/**
|
|
155
|
+
* @readonly
|
|
156
|
+
*/
|
|
157
|
+
readonly get effects(): Effect<T>[];
|
|
158
|
+
/**
|
|
159
|
+
* @readonly
|
|
160
|
+
* @returns {Array<DerivedCell<any>>}
|
|
161
|
+
*/
|
|
162
|
+
readonly get derivedCells(): DerivedCell<any>[];
|
|
158
163
|
/**
|
|
159
164
|
* @protected @type T
|
|
160
165
|
*/
|
|
@@ -169,6 +174,12 @@ export class Cell<T> {
|
|
|
169
174
|
* @returns {T} The value of the Cell.
|
|
170
175
|
*/
|
|
171
176
|
valueOf(): T;
|
|
177
|
+
get value(): T;
|
|
178
|
+
/**
|
|
179
|
+
* Stringifies the value of the Cell.
|
|
180
|
+
* @returns {string}
|
|
181
|
+
*/
|
|
182
|
+
toString(): string;
|
|
172
183
|
/**
|
|
173
184
|
* The value stored in the Cell.
|
|
174
185
|
* @protected @type {T}
|
|
@@ -188,10 +199,11 @@ export class Cell<T> {
|
|
|
188
199
|
listen(callback: (newValue: T) => void, options?: EffectOptions | undefined): () => void;
|
|
189
200
|
/**
|
|
190
201
|
* Creates an effect that is immediately executed with the current value of the cell, and then added to the list of effects for the cell.
|
|
191
|
-
* @param {(newValue: T) => void}
|
|
202
|
+
* @param {(newValue: T) => void} callback - The effect callback to add.
|
|
203
|
+
* @param {Partial<EffectOptions>} [options] - The options for the effect.
|
|
192
204
|
* @returns {() => void} A function that can be called to remove the effect.
|
|
193
205
|
*/
|
|
194
|
-
runAndListen(
|
|
206
|
+
runAndListen(callback: (newValue: T) => void, options?: Partial<EffectOptions> | undefined): () => void;
|
|
195
207
|
/**
|
|
196
208
|
* Removes the specified effect callback from the list of effects for this cell.
|
|
197
209
|
* @param {(newValue: T) => void} callback - The effect callback to remove.
|
|
@@ -203,6 +215,11 @@ export class Cell<T> {
|
|
|
203
215
|
* @returns {boolean} `true` if the cell is listening to a watcher with the specified name, `false` otherwise.
|
|
204
216
|
*/
|
|
205
217
|
isListeningTo(name: string): boolean;
|
|
218
|
+
/**
|
|
219
|
+
* Removes the watcher with the specified name from the list of effects for this cell.
|
|
220
|
+
* @param {string} name - The name of the watcher to stop listening to.
|
|
221
|
+
*/
|
|
222
|
+
stopListeningTo(name: string): void;
|
|
206
223
|
/**
|
|
207
224
|
* Updates the root object and notifies any registered watchers and computed dependents.
|
|
208
225
|
* This method is called whenever the root object's value changes.
|
|
@@ -225,11 +242,6 @@ export class DerivedCell<T> extends Cell<T> {
|
|
|
225
242
|
* @param {() => T} computedFn - A function that generates the value of the computed.
|
|
226
243
|
*/
|
|
227
244
|
constructor(computedFn: () => T);
|
|
228
|
-
/**
|
|
229
|
-
* @type {() => T}
|
|
230
|
-
* @protected
|
|
231
|
-
*/
|
|
232
|
-
protected computedFn: () => T;
|
|
233
245
|
/**
|
|
234
246
|
* @readonly
|
|
235
247
|
*/
|
|
@@ -302,6 +314,10 @@ export type EffectOptions = {
|
|
|
302
314
|
* The name of the effect for debugging purposes.
|
|
303
315
|
*/
|
|
304
316
|
name?: string | undefined;
|
|
317
|
+
/**
|
|
318
|
+
* Whether the effect should be weakly referenced. This means that the effect will be garbage collected if there are no other references to it.
|
|
319
|
+
*/
|
|
320
|
+
weak?: boolean | undefined;
|
|
305
321
|
/**
|
|
306
322
|
* The priority of the effect. Higher priority effects are executed first. The default priority is 0.
|
|
307
323
|
*/
|
|
@@ -322,3 +338,31 @@ export type CellOptions<T> = {
|
|
|
322
338
|
equals?: ((oldValue: T, newValue: T) => boolean) | undefined;
|
|
323
339
|
};
|
|
324
340
|
export type NeverIfAny<T> = 0 extends (1 & T) ? never : T;
|
|
341
|
+
export type Reference<T> = {
|
|
342
|
+
deref: () => T | undefined;
|
|
343
|
+
};
|
|
344
|
+
/**
|
|
345
|
+
* @template T
|
|
346
|
+
* @typedef {{
|
|
347
|
+
* deref: () => T | undefined
|
|
348
|
+
* }} Reference
|
|
349
|
+
*/
|
|
350
|
+
/** @template T */
|
|
351
|
+
declare class Effect<T> {
|
|
352
|
+
/**
|
|
353
|
+
* @param {(newValue: T) => void} callback
|
|
354
|
+
* @param {EffectOptions} [options]
|
|
355
|
+
*/
|
|
356
|
+
constructor(callback: (newValue: T) => void, options?: EffectOptions | undefined);
|
|
357
|
+
/**
|
|
358
|
+
* @type {EffectOptions | undefined}
|
|
359
|
+
*/
|
|
360
|
+
options: EffectOptions | undefined;
|
|
361
|
+
/**
|
|
362
|
+
* Returns the callback function, if it still exists.
|
|
363
|
+
* @returns {((newValue: T) => void) | undefined}
|
|
364
|
+
*/
|
|
365
|
+
get callback(): ((newValue: T) => void) | undefined;
|
|
366
|
+
#private;
|
|
367
|
+
}
|
|
368
|
+
export {};
|
package/types/library/root.d.ts
CHANGED
|
@@ -7,9 +7,9 @@ export namespace root {
|
|
|
7
7
|
/**
|
|
8
8
|
* A value representing the computed values that are currently being calculated.
|
|
9
9
|
* It is an array so it can keep track of nested computed values.
|
|
10
|
-
* @type {DerivedCell[]}
|
|
10
|
+
* @type {[DerivedCell, () => any][]}
|
|
11
11
|
*/
|
|
12
|
-
export const activeComputedValues: import("./classes.js").DerivedCell<any
|
|
12
|
+
export const activeComputedValues: [import("./classes.js").DerivedCell<any>, () => any][];
|
|
13
13
|
export type Watchable = import('./classes.js').Cell<any>;
|
|
14
14
|
export type DerivedCell = import('./classes.js').DerivedCell<any>;
|
|
15
15
|
export type GlobalEffectOptions = {
|
package/jsconfig.json
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ESNext",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"noUnusedLocals": true,
|
|
7
|
-
"noUnusedParameters": true,
|
|
8
|
-
"noImplicitAny": true,
|
|
9
|
-
"allowUnreachableCode": false,
|
|
10
|
-
"allowJs": true,
|
|
11
|
-
|
|
12
|
-
"noImplicitReturns": true,
|
|
13
|
-
|
|
14
|
-
"noEmit": false,
|
|
15
|
-
"emitDeclarationOnly": true,
|
|
16
|
-
"declaration": true,
|
|
17
|
-
|
|
18
|
-
"checkJs": true,
|
|
19
|
-
"strict": true,
|
|
20
|
-
|
|
21
|
-
"outDir": "types"
|
|
22
|
-
},
|
|
23
|
-
"include": ["library/**/*", "index.js"],
|
|
24
|
-
"exclude": ["types/**/*", "tests/**/*"]
|
|
25
|
-
}
|