reactronic 0.22.312 → 0.22.315
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/dist/source/api.d.ts +4 -1
- package/build/dist/source/api.js +4 -1
- package/build/dist/source/impl/Hooks.d.ts +10 -49
- package/build/dist/source/impl/Hooks.js +22 -70
- package/build/dist/source/impl/Monitor.d.ts +1 -1
- package/build/dist/source/impl/Monitor.js +7 -3
- package/build/dist/source/impl/Operation.js +1 -1
- package/build/dist/source/impl/ReactiveArray.d.ts +33 -0
- package/build/dist/source/impl/ReactiveArray.js +42 -0
- package/build/dist/source/impl/ReactiveMap.d.ts +15 -0
- package/build/dist/source/impl/ReactiveMap.js +24 -0
- package/build/dist/source/util/Collection.d.ts +61 -0
- package/build/dist/source/util/Collection.js +275 -0
- package/package.json +8 -8
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { all, pause } from './util/Utils';
|
|
2
|
+
export { Collection, Item, CollectionReader } from './util/Collection';
|
|
2
3
|
export { SealedArray } from './util/SealedArray';
|
|
3
4
|
export { SealedMap } from './util/SealedMap';
|
|
4
5
|
export { SealedSet } from './util/SealedSet';
|
|
@@ -6,7 +7,9 @@ export { MemberOptions, SnapshotOptions, Kind, Reentrance, LoggingOptions, Profi
|
|
|
6
7
|
export { Worker } from './Worker';
|
|
7
8
|
export { Controller } from './Controller';
|
|
8
9
|
export { Ref, ToggleRef, BoolOnly, GivenTypeOnly } from './Ref';
|
|
9
|
-
export { ReactiveObject
|
|
10
|
+
export { ReactiveObject } from './impl/Hooks';
|
|
11
|
+
export { ReactiveArray } from './impl/ReactiveArray';
|
|
12
|
+
export { ReactiveMap } from './impl/ReactiveMap';
|
|
10
13
|
export { Changeset } from './impl/Changeset';
|
|
11
14
|
export { Transaction } from './impl/Transaction';
|
|
12
15
|
export { Monitor } from './impl/Monitor';
|
package/build/dist/source/api.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
export { all, pause } from './util/Utils';
|
|
2
|
+
export { Collection } from './util/Collection';
|
|
2
3
|
export { SealedArray } from './util/SealedArray';
|
|
3
4
|
export { SealedMap } from './util/SealedMap';
|
|
4
5
|
export { SealedSet } from './util/SealedSet';
|
|
5
6
|
export { Kind, Reentrance, LoggingLevel } from './Options';
|
|
6
7
|
export { Controller } from './Controller';
|
|
7
8
|
export { Ref, ToggleRef } from './Ref';
|
|
8
|
-
export { ReactiveObject
|
|
9
|
+
export { ReactiveObject } from './impl/Hooks';
|
|
10
|
+
export { ReactiveArray } from './impl/ReactiveArray';
|
|
11
|
+
export { ReactiveMap } from './impl/ReactiveMap';
|
|
9
12
|
export { Changeset } from './impl/Changeset';
|
|
10
13
|
export { Transaction } from './impl/Transaction';
|
|
11
14
|
export { Monitor } from './impl/Monitor';
|
|
@@ -4,55 +4,12 @@ import { LoggingOptions, ProfilingOptions } from '../Logging';
|
|
|
4
4
|
import { MemberName, ObjectHandle, StandaloneMode } from './Data';
|
|
5
5
|
import { Journal } from './Journal';
|
|
6
6
|
import { Monitor } from './Monitor';
|
|
7
|
-
export declare abstract class
|
|
8
|
-
constructor();
|
|
7
|
+
export declare abstract class HookedObject {
|
|
8
|
+
protected constructor(reactive: boolean);
|
|
9
9
|
[Symbol.toStringTag](): string;
|
|
10
10
|
}
|
|
11
|
-
export declare class
|
|
12
|
-
|
|
13
|
-
get length(): number;
|
|
14
|
-
set length(n: number);
|
|
15
|
-
get(n: number): T;
|
|
16
|
-
set(n: number, item: T): void;
|
|
17
|
-
toString(): string;
|
|
18
|
-
toLocaleString(): string;
|
|
19
|
-
pop(): T | undefined;
|
|
20
|
-
push(...items: T[]): number;
|
|
21
|
-
concat(...items: (T | ConcatArray<T>)[]): T[];
|
|
22
|
-
join(separator?: string): string;
|
|
23
|
-
reverse(): T[];
|
|
24
|
-
shift(): T | undefined;
|
|
25
|
-
slice(start?: number, end?: number): T[];
|
|
26
|
-
sort(compareFn?: (a: T, b: T) => number): this;
|
|
27
|
-
splice(start: number, deleteCount?: number): T[];
|
|
28
|
-
unshift(...items: T[]): number;
|
|
29
|
-
indexOf(searchElement: T, fromIndex?: number): number;
|
|
30
|
-
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
|
31
|
-
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
|
32
|
-
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
|
33
|
-
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
|
|
34
|
-
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
|
|
35
|
-
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
|
|
36
|
-
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
|
37
|
-
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
|
38
|
-
entries(): IterableIterator<[number, T]>;
|
|
39
|
-
keys(): IterableIterator<number>;
|
|
40
|
-
values(): IterableIterator<T>;
|
|
41
|
-
private get mutable();
|
|
42
|
-
}
|
|
43
|
-
export declare class ReactiveMap<K, V> extends ReactiveObject {
|
|
44
|
-
private m;
|
|
45
|
-
clear(): void;
|
|
46
|
-
delete(key: K): boolean;
|
|
47
|
-
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
|
|
48
|
-
get(key: K): V | undefined;
|
|
49
|
-
has(key: K): boolean;
|
|
50
|
-
set(key: K, value: V): this;
|
|
51
|
-
get size(): number;
|
|
52
|
-
entries(): IterableIterator<[K, V]>;
|
|
53
|
-
keys(): IterableIterator<K>;
|
|
54
|
-
values(): IterableIterator<V>;
|
|
55
|
-
private get mutable();
|
|
11
|
+
export declare abstract class ReactiveObject extends HookedObject {
|
|
12
|
+
constructor();
|
|
56
13
|
}
|
|
57
14
|
export declare class OptionsImpl implements MemberOptions {
|
|
58
15
|
readonly getter: Function;
|
|
@@ -76,7 +33,10 @@ export declare class Hooks implements ProxyHandler<ObjectHandle> {
|
|
|
76
33
|
static mainThreadBlockingWarningThreshold: number;
|
|
77
34
|
static asyncActionDurationWarningThreshold: number;
|
|
78
35
|
static sensitivity: boolean;
|
|
79
|
-
static readonly
|
|
36
|
+
static readonly transactional: Hooks;
|
|
37
|
+
static readonly reactive: Hooks;
|
|
38
|
+
readonly isReactive: boolean;
|
|
39
|
+
constructor(isReactive: boolean);
|
|
80
40
|
getPrototypeOf(h: ObjectHandle): object | null;
|
|
81
41
|
get(h: ObjectHandle, m: MemberName, receiver: any): any;
|
|
82
42
|
set(h: ObjectHandle, m: MemberName, value: any, receiver: any): boolean;
|
|
@@ -87,10 +47,11 @@ export declare class Hooks implements ProxyHandler<ObjectHandle> {
|
|
|
87
47
|
static decorateOperation(implicit: boolean, decorator: Function, options: Partial<MemberOptions>, proto: any, member: MemberName, pd: PropertyDescriptor | undefined): any;
|
|
88
48
|
static decorateOperationParametrized(decorator: Function, options: Partial<MemberOptions>): F<any>;
|
|
89
49
|
static acquireHandle(obj: any): ObjectHandle;
|
|
90
|
-
static
|
|
50
|
+
static createHandleForReactronicObject(proto: any, data: any, blank: any, hint: string, reactive: boolean): ObjectHandle;
|
|
91
51
|
static setProfilingMode(isOn: boolean, options?: Partial<ProfilingOptions>): void;
|
|
92
52
|
static sensitive<T>(sensitivity: boolean, func: F<T>, ...args: any[]): T;
|
|
93
53
|
static setHint<T>(obj: T, hint: string | undefined): T;
|
|
54
|
+
static getHint<T>(obj: T): string;
|
|
94
55
|
static createOperation: (h: ObjectHandle, m: MemberName, options: OptionsImpl) => F<any>;
|
|
95
56
|
static rememberOperationOptions: (proto: any, m: MemberName, getter: Function | undefined, setter: Function | undefined, enumerable: boolean, configurable: boolean, options: Partial<MemberOptions>, implicit: boolean) => OptionsImpl;
|
|
96
57
|
}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { UNDEF } from '../util/Utils';
|
|
2
|
-
import { Sealant } from '../util/Sealant';
|
|
3
2
|
import { Log, misuse } from '../util/Dbg';
|
|
4
3
|
import { Kind, Reentrance } from '../Options';
|
|
5
4
|
import { ObjectSnapshot, ObjectHandle, Subscription, Meta } from './Data';
|
|
6
5
|
import { Changeset, Dump, EMPTY_SNAPSHOT } from './Changeset';
|
|
7
|
-
export class
|
|
8
|
-
constructor() {
|
|
6
|
+
export class HookedObject {
|
|
7
|
+
constructor(reactive) {
|
|
9
8
|
const proto = new.target.prototype;
|
|
10
9
|
const initial = Meta.getFrom(proto, Meta.Initial);
|
|
11
|
-
const h = Hooks.
|
|
10
|
+
const h = Hooks.createHandleForReactronicObject(proto, this, initial, new.target.name, reactive);
|
|
12
11
|
return h.proxy;
|
|
13
12
|
}
|
|
14
13
|
[Symbol.toStringTag]() {
|
|
@@ -16,66 +15,9 @@ export class ReactiveObject {
|
|
|
16
15
|
return Dump.obj(h);
|
|
17
16
|
}
|
|
18
17
|
}
|
|
19
|
-
export class
|
|
20
|
-
constructor() {
|
|
21
|
-
super(...arguments);
|
|
22
|
-
this.a = new Array();
|
|
23
|
-
}
|
|
24
|
-
get length() { return this.a.length; }
|
|
25
|
-
set length(n) { this.a.length = n; }
|
|
26
|
-
get(n) { return this.a[n]; }
|
|
27
|
-
set(n, item) { this.mutable[n] = item; }
|
|
28
|
-
toString() { return this.a.toString(); }
|
|
29
|
-
toLocaleString() { return this.a.toLocaleString(); }
|
|
30
|
-
pop() { return this.mutable.pop(); }
|
|
31
|
-
push(...items) { return this.mutable.push(...items); }
|
|
32
|
-
concat(...items) { return this.a.concat(...items); }
|
|
33
|
-
join(separator) { return this.a.join(separator); }
|
|
34
|
-
reverse() { return this.mutable.reverse(); }
|
|
35
|
-
shift() { return this.mutable.shift(); }
|
|
36
|
-
slice(start, end) { return this.a.slice(start, end); }
|
|
37
|
-
sort(compareFn) { this.mutable.sort(compareFn); return this; }
|
|
38
|
-
splice(start, deleteCount, ...items) { return this.mutable.splice(start, deleteCount, ...items); }
|
|
39
|
-
unshift(...items) { return this.mutable.unshift(...items); }
|
|
40
|
-
indexOf(searchElement, fromIndex) { return this.a.indexOf(searchElement, fromIndex); }
|
|
41
|
-
lastIndexOf(searchElement, fromIndex) { return this.a.lastIndexOf(searchElement, fromIndex); }
|
|
42
|
-
every(predicate, thisArg) { return this.a.every(predicate, thisArg); }
|
|
43
|
-
some(predicate, thisArg) { return this.a.some(predicate, thisArg); }
|
|
44
|
-
forEach(callbackfn, thisArg) { return this.a.forEach(callbackfn, thisArg); }
|
|
45
|
-
map(callbackfn, thisArg) { return this.a.map(callbackfn, thisArg); }
|
|
46
|
-
filter(predicate, thisArg) { return this.a.filter(predicate, thisArg); }
|
|
47
|
-
reduce(callbackfn, initialValue) { return this.a.reduce(callbackfn, initialValue); }
|
|
48
|
-
reduceRight(callbackfn, initialValue) { return this.a.reduceRight(callbackfn, initialValue); }
|
|
49
|
-
entries() { return this.a.entries(); }
|
|
50
|
-
keys() { return this.a.keys(); }
|
|
51
|
-
values() { return this.a.values(); }
|
|
52
|
-
get mutable() {
|
|
53
|
-
const createCopy = this.a[Sealant.CreateCopy];
|
|
54
|
-
if (createCopy)
|
|
55
|
-
return this.a = createCopy.call(this.a);
|
|
56
|
-
return this.a;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
export class ReactiveMap extends ReactiveObject {
|
|
18
|
+
export class ReactiveObject extends HookedObject {
|
|
60
19
|
constructor() {
|
|
61
|
-
super(
|
|
62
|
-
this.m = new Map();
|
|
63
|
-
}
|
|
64
|
-
clear() { this.mutable.clear(); }
|
|
65
|
-
delete(key) { return this.mutable.delete(key); }
|
|
66
|
-
forEach(callbackfn, thisArg) { this.m.forEach(callbackfn, thisArg); }
|
|
67
|
-
get(key) { return this.m.get(key); }
|
|
68
|
-
has(key) { return this.m.has(key); }
|
|
69
|
-
set(key, value) { this.mutable.set(key, value); return this; }
|
|
70
|
-
get size() { return this.m.size; }
|
|
71
|
-
entries() { return this.m.entries(); }
|
|
72
|
-
keys() { return this.m.keys(); }
|
|
73
|
-
values() { return this.m.values(); }
|
|
74
|
-
get mutable() {
|
|
75
|
-
const createCopy = this.m[Sealant.CreateCopy];
|
|
76
|
-
if (createCopy)
|
|
77
|
-
return this.m = createCopy.call(this.m);
|
|
78
|
-
return this.m;
|
|
20
|
+
super(true);
|
|
79
21
|
}
|
|
80
22
|
}
|
|
81
23
|
const DEFAULT_OPTIONS = Object.freeze({
|
|
@@ -113,6 +55,9 @@ function merge(def, existing, patch, implicit) {
|
|
|
113
55
|
return patch !== undefined && (existing === def || !implicit) ? patch : existing;
|
|
114
56
|
}
|
|
115
57
|
export class Hooks {
|
|
58
|
+
constructor(isReactive) {
|
|
59
|
+
this.isReactive = isReactive;
|
|
60
|
+
}
|
|
116
61
|
getPrototypeOf(h) {
|
|
117
62
|
return Reflect.getPrototypeOf(h.data);
|
|
118
63
|
}
|
|
@@ -123,7 +68,8 @@ export class Hooks {
|
|
|
123
68
|
const os = cs.getObjectSnapshot(h, m);
|
|
124
69
|
result = os.data[m];
|
|
125
70
|
if (result instanceof Subscription && !result.isOperation) {
|
|
126
|
-
|
|
71
|
+
if (this.isReactive)
|
|
72
|
+
Changeset.markUsed(result, os, m, h, Kind.Plain, false);
|
|
127
73
|
result = result.content;
|
|
128
74
|
}
|
|
129
75
|
else
|
|
@@ -182,11 +128,11 @@ export class Hooks {
|
|
|
182
128
|
if (reactive) {
|
|
183
129
|
const get = function () {
|
|
184
130
|
const h = Hooks.acquireHandle(this);
|
|
185
|
-
return Hooks.
|
|
131
|
+
return Hooks.reactive.get(h, m, this);
|
|
186
132
|
};
|
|
187
133
|
const set = function (value) {
|
|
188
134
|
const h = Hooks.acquireHandle(this);
|
|
189
|
-
return Hooks.
|
|
135
|
+
return Hooks.reactive.set(h, m, value, this);
|
|
190
136
|
};
|
|
191
137
|
const enumerable = true;
|
|
192
138
|
const configurable = false;
|
|
@@ -235,16 +181,17 @@ export class Hooks {
|
|
|
235
181
|
throw misuse('only objects can be reactive');
|
|
236
182
|
const initial = Meta.getFrom(Object.getPrototypeOf(obj), Meta.Initial);
|
|
237
183
|
const os = new ObjectSnapshot(EMPTY_SNAPSHOT.changeset, EMPTY_SNAPSHOT, Object.assign({}, initial));
|
|
238
|
-
h = new ObjectHandle(obj, obj, Hooks.
|
|
184
|
+
h = new ObjectHandle(obj, obj, Hooks.reactive, os, obj.constructor.name);
|
|
239
185
|
Meta.set(os.data, Meta.Handle, h);
|
|
240
186
|
Meta.set(obj, Meta.Handle, h);
|
|
241
187
|
Meta.set(os.data, Meta.Revision, new Subscription(1));
|
|
242
188
|
}
|
|
243
189
|
return h;
|
|
244
190
|
}
|
|
245
|
-
static
|
|
191
|
+
static createHandleForReactronicObject(proto, data, blank, hint, reactive) {
|
|
246
192
|
const ctx = Changeset.edit();
|
|
247
|
-
const
|
|
193
|
+
const hooks = reactive ? Hooks.reactive : Hooks.transactional;
|
|
194
|
+
const h = new ObjectHandle(data, undefined, hooks, EMPTY_SNAPSHOT, hint);
|
|
248
195
|
ctx.getEditableObjectSnapshot(h, Meta.Handle, blank);
|
|
249
196
|
if (!Hooks.reactionsAutoStartDisabled)
|
|
250
197
|
for (const m in Meta.getFrom(proto, Meta.Reactions))
|
|
@@ -282,13 +229,18 @@ export class Hooks {
|
|
|
282
229
|
}
|
|
283
230
|
return obj;
|
|
284
231
|
}
|
|
232
|
+
static getHint(obj) {
|
|
233
|
+
const h = Hooks.acquireHandle(obj);
|
|
234
|
+
return h.hint;
|
|
235
|
+
}
|
|
285
236
|
}
|
|
286
237
|
Hooks.reactionsAutoStartDisabled = false;
|
|
287
238
|
Hooks.repetitiveUsageWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
288
239
|
Hooks.mainThreadBlockingWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
289
240
|
Hooks.asyncActionDurationWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
290
241
|
Hooks.sensitivity = false;
|
|
291
|
-
Hooks.
|
|
242
|
+
Hooks.transactional = new Hooks(false);
|
|
243
|
+
Hooks.reactive = new Hooks(true);
|
|
292
244
|
Hooks.createOperation = function (h, m, options) {
|
|
293
245
|
throw misuse('createOperation should never be called');
|
|
294
246
|
};
|
|
@@ -51,7 +51,8 @@ export class MonitorImpl extends Monitor {
|
|
|
51
51
|
return m;
|
|
52
52
|
}
|
|
53
53
|
static activate(mon, delay) {
|
|
54
|
-
|
|
54
|
+
const active = mon.counter > 0;
|
|
55
|
+
if (mon.internals.started === 0 && active) {
|
|
55
56
|
mon.duration = 0;
|
|
56
57
|
mon.internals.started = performance.now();
|
|
57
58
|
MonitorImpl.tick(mon);
|
|
@@ -60,7 +61,7 @@ export class MonitorImpl extends Monitor {
|
|
|
60
61
|
if (mon.internals.activationTimeout === undefined)
|
|
61
62
|
mon.internals.activationTimeout = setTimeout(() => Transaction.run({ hint: 'Monitor.activate', standalone: 'isolated' }, MonitorImpl.activate, mon, -1), delay);
|
|
62
63
|
}
|
|
63
|
-
else if (
|
|
64
|
+
else if (active)
|
|
64
65
|
mon.isActive = true;
|
|
65
66
|
}
|
|
66
67
|
static deactivate(mon, delay) {
|
|
@@ -80,7 +81,7 @@ export class MonitorImpl extends Monitor {
|
|
|
80
81
|
}
|
|
81
82
|
static tick(mon) {
|
|
82
83
|
if (mon.internals.started !== 0) {
|
|
83
|
-
Transaction.run(
|
|
84
|
+
Transaction.run(MONITOR_TICK_OPTIONS, () => {
|
|
84
85
|
const resolution = mon.internals.durationResolution;
|
|
85
86
|
mon.duration = Math.round(resolution * (performance.now() - mon.internals.started)) / resolution;
|
|
86
87
|
});
|
|
@@ -90,3 +91,6 @@ export class MonitorImpl extends Monitor {
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
}
|
|
94
|
+
const MONITOR_TICK_OPTIONS = Object.freeze({
|
|
95
|
+
hint: 'Monitor.tick',
|
|
96
|
+
});
|
|
@@ -9,7 +9,7 @@ import { Hooks, OptionsImpl } from './Hooks';
|
|
|
9
9
|
import { JournalImpl } from './Journal';
|
|
10
10
|
const BOOT_ARGS = [];
|
|
11
11
|
const BOOT_CAUSE = '<boot>';
|
|
12
|
-
const EMPTY_HANDLE = new ObjectHandle(undefined, undefined, Hooks.
|
|
12
|
+
const EMPTY_HANDLE = new ObjectHandle(undefined, undefined, Hooks.reactive, EMPTY_SNAPSHOT, '<empty>');
|
|
13
13
|
export class OperationController extends Controller {
|
|
14
14
|
constructor(h, m) {
|
|
15
15
|
super();
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { ReactiveObject } from './Hooks';
|
|
2
|
+
export declare class ReactiveArray<T> extends ReactiveObject {
|
|
3
|
+
private a;
|
|
4
|
+
get length(): number;
|
|
5
|
+
set length(n: number);
|
|
6
|
+
get(n: number): T;
|
|
7
|
+
set(n: number, item: T): void;
|
|
8
|
+
toString(): string;
|
|
9
|
+
toLocaleString(): string;
|
|
10
|
+
pop(): T | undefined;
|
|
11
|
+
push(...items: T[]): number;
|
|
12
|
+
concat(...items: (T | ConcatArray<T>)[]): T[];
|
|
13
|
+
join(separator?: string): string;
|
|
14
|
+
reverse(): T[];
|
|
15
|
+
shift(): T | undefined;
|
|
16
|
+
slice(start?: number, end?: number): T[];
|
|
17
|
+
sort(compareFn?: (a: T, b: T) => number): this;
|
|
18
|
+
splice(start: number, deleteCount?: number): T[];
|
|
19
|
+
unshift(...items: T[]): number;
|
|
20
|
+
indexOf(searchElement: T, fromIndex?: number): number;
|
|
21
|
+
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
|
22
|
+
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
|
23
|
+
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
|
24
|
+
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
|
|
25
|
+
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
|
|
26
|
+
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
|
|
27
|
+
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
|
28
|
+
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
|
29
|
+
entries(): IterableIterator<[number, T]>;
|
|
30
|
+
keys(): IterableIterator<number>;
|
|
31
|
+
values(): IterableIterator<T>;
|
|
32
|
+
private get mutable();
|
|
33
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { Sealant } from '../util/Sealant';
|
|
2
|
+
import { ReactiveObject } from './Hooks';
|
|
3
|
+
export class ReactiveArray extends ReactiveObject {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.a = new Array();
|
|
7
|
+
}
|
|
8
|
+
get length() { return this.a.length; }
|
|
9
|
+
set length(n) { this.a.length = n; }
|
|
10
|
+
get(n) { return this.a[n]; }
|
|
11
|
+
set(n, item) { this.mutable[n] = item; }
|
|
12
|
+
toString() { return this.a.toString(); }
|
|
13
|
+
toLocaleString() { return this.a.toLocaleString(); }
|
|
14
|
+
pop() { return this.mutable.pop(); }
|
|
15
|
+
push(...items) { return this.mutable.push(...items); }
|
|
16
|
+
concat(...items) { return this.a.concat(...items); }
|
|
17
|
+
join(separator) { return this.a.join(separator); }
|
|
18
|
+
reverse() { return this.mutable.reverse(); }
|
|
19
|
+
shift() { return this.mutable.shift(); }
|
|
20
|
+
slice(start, end) { return this.a.slice(start, end); }
|
|
21
|
+
sort(compareFn) { this.mutable.sort(compareFn); return this; }
|
|
22
|
+
splice(start, deleteCount, ...items) { return this.mutable.splice(start, deleteCount, ...items); }
|
|
23
|
+
unshift(...items) { return this.mutable.unshift(...items); }
|
|
24
|
+
indexOf(searchElement, fromIndex) { return this.a.indexOf(searchElement, fromIndex); }
|
|
25
|
+
lastIndexOf(searchElement, fromIndex) { return this.a.lastIndexOf(searchElement, fromIndex); }
|
|
26
|
+
every(predicate, thisArg) { return this.a.every(predicate, thisArg); }
|
|
27
|
+
some(predicate, thisArg) { return this.a.some(predicate, thisArg); }
|
|
28
|
+
forEach(callbackfn, thisArg) { return this.a.forEach(callbackfn, thisArg); }
|
|
29
|
+
map(callbackfn, thisArg) { return this.a.map(callbackfn, thisArg); }
|
|
30
|
+
filter(predicate, thisArg) { return this.a.filter(predicate, thisArg); }
|
|
31
|
+
reduce(callbackfn, initialValue) { return this.a.reduce(callbackfn, initialValue); }
|
|
32
|
+
reduceRight(callbackfn, initialValue) { return this.a.reduceRight(callbackfn, initialValue); }
|
|
33
|
+
entries() { return this.a.entries(); }
|
|
34
|
+
keys() { return this.a.keys(); }
|
|
35
|
+
values() { return this.a.values(); }
|
|
36
|
+
get mutable() {
|
|
37
|
+
const createCopy = this.a[Sealant.CreateCopy];
|
|
38
|
+
if (createCopy)
|
|
39
|
+
return this.a = createCopy.call(this.a);
|
|
40
|
+
return this.a;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ReactiveObject } from './Hooks';
|
|
2
|
+
export declare class ReactiveMap<K, V> extends ReactiveObject {
|
|
3
|
+
private m;
|
|
4
|
+
clear(): void;
|
|
5
|
+
delete(key: K): boolean;
|
|
6
|
+
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
|
|
7
|
+
get(key: K): V | undefined;
|
|
8
|
+
has(key: K): boolean;
|
|
9
|
+
set(key: K, value: V): this;
|
|
10
|
+
get size(): number;
|
|
11
|
+
entries(): IterableIterator<[K, V]>;
|
|
12
|
+
keys(): IterableIterator<K>;
|
|
13
|
+
values(): IterableIterator<V>;
|
|
14
|
+
private get mutable();
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Sealant } from '../util/Sealant';
|
|
2
|
+
import { ReactiveObject } from './Hooks';
|
|
3
|
+
export class ReactiveMap extends ReactiveObject {
|
|
4
|
+
constructor() {
|
|
5
|
+
super(...arguments);
|
|
6
|
+
this.m = new Map();
|
|
7
|
+
}
|
|
8
|
+
clear() { this.mutable.clear(); }
|
|
9
|
+
delete(key) { return this.mutable.delete(key); }
|
|
10
|
+
forEach(callbackfn, thisArg) { this.m.forEach(callbackfn, thisArg); }
|
|
11
|
+
get(key) { return this.m.get(key); }
|
|
12
|
+
has(key) { return this.m.has(key); }
|
|
13
|
+
set(key, value) { this.mutable.set(key, value); return this; }
|
|
14
|
+
get size() { return this.m.size; }
|
|
15
|
+
entries() { return this.m.entries(); }
|
|
16
|
+
keys() { return this.m.keys(); }
|
|
17
|
+
values() { return this.m.values(); }
|
|
18
|
+
get mutable() {
|
|
19
|
+
const createCopy = this.m[Sealant.CreateCopy];
|
|
20
|
+
if (createCopy)
|
|
21
|
+
return this.m = createCopy.call(this.m);
|
|
22
|
+
return this.m;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare type GetItemKey<T = unknown> = (item: T) => string | undefined;
|
|
2
|
+
export interface CollectionReader<T> {
|
|
3
|
+
readonly strict: boolean;
|
|
4
|
+
readonly count: number;
|
|
5
|
+
readonly addedCount: number;
|
|
6
|
+
readonly removedCount: number;
|
|
7
|
+
readonly isMergeInProgress: boolean;
|
|
8
|
+
lookup(key: string): Item<T> | undefined;
|
|
9
|
+
claim(key: string): Item<T> | undefined;
|
|
10
|
+
add(self: T): Item<T>;
|
|
11
|
+
remove(item: Item<T>): void;
|
|
12
|
+
move(item: Item<T>, after: Item<T>): void;
|
|
13
|
+
beginMerge(): void;
|
|
14
|
+
endMerge(error?: unknown): void;
|
|
15
|
+
resetAddedAndRemovedLists(): void;
|
|
16
|
+
items(): Generator<Item<T>>;
|
|
17
|
+
addedItems(reset?: boolean): Generator<Item<T>>;
|
|
18
|
+
removedItems(reset?: boolean): Generator<Item<T>>;
|
|
19
|
+
isAdded(item: Item<T>): boolean;
|
|
20
|
+
isMoved(item: Item<T>): boolean;
|
|
21
|
+
isRemoved(item: Item<T>): boolean;
|
|
22
|
+
isCurrent(item: Item<T>): boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface Item<T> {
|
|
25
|
+
readonly self: T;
|
|
26
|
+
readonly prev?: Item<T>;
|
|
27
|
+
aux?: Item<T>;
|
|
28
|
+
}
|
|
29
|
+
export declare class Collection<T> implements CollectionReader<T> {
|
|
30
|
+
readonly strict: boolean;
|
|
31
|
+
readonly getKey: GetItemKey<T>;
|
|
32
|
+
private map;
|
|
33
|
+
private tag;
|
|
34
|
+
private current;
|
|
35
|
+
private added;
|
|
36
|
+
private removed;
|
|
37
|
+
private lastNotFoundKey;
|
|
38
|
+
private strictNextItem?;
|
|
39
|
+
constructor(strict: boolean, getKey: GetItemKey<T>);
|
|
40
|
+
get count(): number;
|
|
41
|
+
get addedCount(): number;
|
|
42
|
+
get removedCount(): number;
|
|
43
|
+
get isMergeInProgress(): boolean;
|
|
44
|
+
lookup(key: string | undefined): Item<T> | undefined;
|
|
45
|
+
claim(key: string): Item<T> | undefined;
|
|
46
|
+
add(self: T): Item<T>;
|
|
47
|
+
remove(item: Item<T>): void;
|
|
48
|
+
move(item: Item<T>, after: Item<T>): void;
|
|
49
|
+
beginMerge(): void;
|
|
50
|
+
endMerge(error?: unknown): void;
|
|
51
|
+
resetAddedAndRemovedLists(): void;
|
|
52
|
+
items(): Generator<Item<T>>;
|
|
53
|
+
addedItems(reset?: boolean): Generator<Item<T>>;
|
|
54
|
+
removedItems(reset?: boolean): Generator<Item<T>>;
|
|
55
|
+
isAdded(item: Item<T>): boolean;
|
|
56
|
+
isMoved(item: Item<T>): boolean;
|
|
57
|
+
isRemoved(item: Item<T>): boolean;
|
|
58
|
+
isCurrent(item: Item<T>): boolean;
|
|
59
|
+
markAsMoved(item: Item<T>): void;
|
|
60
|
+
static createItem<T>(self: T): Item<T>;
|
|
61
|
+
}
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
export class Collection {
|
|
2
|
+
constructor(strict, getKey) {
|
|
3
|
+
this.strict = strict;
|
|
4
|
+
this.getKey = getKey;
|
|
5
|
+
this.map = new Map();
|
|
6
|
+
this.tag = ~0;
|
|
7
|
+
this.current = new ItemChain();
|
|
8
|
+
this.added = new ItemChain();
|
|
9
|
+
this.removed = new ItemChain();
|
|
10
|
+
this.lastNotFoundKey = undefined;
|
|
11
|
+
this.strictNextItem = undefined;
|
|
12
|
+
}
|
|
13
|
+
get count() {
|
|
14
|
+
return this.current.count;
|
|
15
|
+
}
|
|
16
|
+
get addedCount() {
|
|
17
|
+
return this.added.count;
|
|
18
|
+
}
|
|
19
|
+
get removedCount() {
|
|
20
|
+
return this.removed.count;
|
|
21
|
+
}
|
|
22
|
+
get isMergeInProgress() {
|
|
23
|
+
return this.tag > 0;
|
|
24
|
+
}
|
|
25
|
+
lookup(key) {
|
|
26
|
+
let result = undefined;
|
|
27
|
+
if (key !== undefined && key !== this.lastNotFoundKey) {
|
|
28
|
+
result = this.map.get(key);
|
|
29
|
+
if (result) {
|
|
30
|
+
if (this.getKey(result.self) !== key) {
|
|
31
|
+
this.lastNotFoundKey = key;
|
|
32
|
+
result = undefined;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else
|
|
36
|
+
this.lastNotFoundKey = key;
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
claim(key) {
|
|
41
|
+
const tag = this.tag;
|
|
42
|
+
if (tag < 0)
|
|
43
|
+
throw new Error('merge is not in progress');
|
|
44
|
+
let item = this.strictNextItem;
|
|
45
|
+
if (key !== (item ? this.getKey(item.self) : undefined))
|
|
46
|
+
item = this.lookup(key);
|
|
47
|
+
if (item) {
|
|
48
|
+
if (item.tag === tag)
|
|
49
|
+
throw new Error(`duplicate item: ${key}`);
|
|
50
|
+
item.tag = tag;
|
|
51
|
+
if (this.strict && item !== this.strictNextItem)
|
|
52
|
+
item.status = tag;
|
|
53
|
+
this.strictNextItem = item.next;
|
|
54
|
+
this.removed.exclude(item);
|
|
55
|
+
this.current.include(item);
|
|
56
|
+
}
|
|
57
|
+
return item;
|
|
58
|
+
}
|
|
59
|
+
add(self) {
|
|
60
|
+
const key = this.getKey(self);
|
|
61
|
+
if (this.lookup(key) !== undefined)
|
|
62
|
+
throw new Error(`key is already in use: ${key}`);
|
|
63
|
+
let tag = this.tag;
|
|
64
|
+
if (tag < 0) {
|
|
65
|
+
tag = ~this.tag + 1;
|
|
66
|
+
this.tag = ~tag;
|
|
67
|
+
}
|
|
68
|
+
const item = new ItemImpl(self, tag);
|
|
69
|
+
this.map.set(key, item);
|
|
70
|
+
this.lastNotFoundKey = undefined;
|
|
71
|
+
this.strictNextItem = undefined;
|
|
72
|
+
this.current.include(item);
|
|
73
|
+
this.added.aux(item);
|
|
74
|
+
return item;
|
|
75
|
+
}
|
|
76
|
+
remove(item) {
|
|
77
|
+
const t = item;
|
|
78
|
+
if (!this.isRemoved(t)) {
|
|
79
|
+
this.current.exclude(t);
|
|
80
|
+
this.removed.include(t);
|
|
81
|
+
t.tag--;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
move(item, after) {
|
|
85
|
+
throw new Error('not implemented');
|
|
86
|
+
}
|
|
87
|
+
beginMerge() {
|
|
88
|
+
if (this.isMergeInProgress)
|
|
89
|
+
throw new Error('merge is in progress already');
|
|
90
|
+
this.tag = ~this.tag + 1;
|
|
91
|
+
this.strictNextItem = this.current.first;
|
|
92
|
+
this.removed.grab(this.current, false);
|
|
93
|
+
this.added.reset();
|
|
94
|
+
}
|
|
95
|
+
endMerge(error) {
|
|
96
|
+
if (!this.isMergeInProgress)
|
|
97
|
+
throw new Error('merge is ended already');
|
|
98
|
+
this.tag = ~this.tag;
|
|
99
|
+
if (error === undefined) {
|
|
100
|
+
const currentCount = this.current.count;
|
|
101
|
+
if (currentCount > 0) {
|
|
102
|
+
const getKey = this.getKey;
|
|
103
|
+
if (currentCount > this.removed.count) {
|
|
104
|
+
const map = this.map;
|
|
105
|
+
for (const x of this.removed.items())
|
|
106
|
+
map.delete(getKey(x.self));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
const map = this.map = new Map();
|
|
110
|
+
for (const x of this.current.items())
|
|
111
|
+
map.set(getKey(x.self), x);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
else
|
|
115
|
+
this.map = new Map();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
this.current.grab(this.removed, true);
|
|
119
|
+
const getKey = this.getKey;
|
|
120
|
+
for (const x of this.added.itemsViaAux()) {
|
|
121
|
+
this.map.delete(getKey(x.self));
|
|
122
|
+
this.current.exclude(x);
|
|
123
|
+
}
|
|
124
|
+
this.added.reset();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
resetAddedAndRemovedLists() {
|
|
128
|
+
this.removed.reset();
|
|
129
|
+
this.added.reset();
|
|
130
|
+
}
|
|
131
|
+
*items() {
|
|
132
|
+
let x = this.current.first;
|
|
133
|
+
while (x !== undefined) {
|
|
134
|
+
const next = x.next;
|
|
135
|
+
yield x;
|
|
136
|
+
x = next;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
*addedItems(reset) {
|
|
140
|
+
let x = this.added.first;
|
|
141
|
+
while (x !== undefined) {
|
|
142
|
+
const next = x.aux;
|
|
143
|
+
if (!this.isRemoved(x))
|
|
144
|
+
yield x;
|
|
145
|
+
x = next;
|
|
146
|
+
}
|
|
147
|
+
if (reset)
|
|
148
|
+
this.added.reset();
|
|
149
|
+
}
|
|
150
|
+
*removedItems(reset) {
|
|
151
|
+
let x = this.removed.first;
|
|
152
|
+
while (x !== undefined) {
|
|
153
|
+
const next = x.next;
|
|
154
|
+
yield x;
|
|
155
|
+
x = next;
|
|
156
|
+
}
|
|
157
|
+
if (reset)
|
|
158
|
+
this.removed.reset();
|
|
159
|
+
}
|
|
160
|
+
isAdded(item) {
|
|
161
|
+
const t = item;
|
|
162
|
+
let tag = this.tag;
|
|
163
|
+
if (tag < 0)
|
|
164
|
+
tag = ~tag;
|
|
165
|
+
return t.status === ~tag && t.tag > 0;
|
|
166
|
+
}
|
|
167
|
+
isMoved(item) {
|
|
168
|
+
const t = item;
|
|
169
|
+
let tag = this.tag;
|
|
170
|
+
if (tag < 0)
|
|
171
|
+
tag = ~tag;
|
|
172
|
+
return t.status === tag && t.tag > 0;
|
|
173
|
+
}
|
|
174
|
+
isRemoved(item) {
|
|
175
|
+
const t = item;
|
|
176
|
+
const tag = this.tag;
|
|
177
|
+
return tag > 0 ? t.tag < tag : t.tag < tag - 1;
|
|
178
|
+
}
|
|
179
|
+
isCurrent(item) {
|
|
180
|
+
const t = item;
|
|
181
|
+
return t.tag === this.tag;
|
|
182
|
+
}
|
|
183
|
+
markAsMoved(item) {
|
|
184
|
+
const t = item;
|
|
185
|
+
if (t.tag > 0)
|
|
186
|
+
t.status = t.tag;
|
|
187
|
+
}
|
|
188
|
+
static createItem(self) {
|
|
189
|
+
return new ItemImpl(self, 0);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
class ItemImpl {
|
|
193
|
+
constructor(self, tag) {
|
|
194
|
+
this.self = self;
|
|
195
|
+
this.tag = tag;
|
|
196
|
+
this.status = ~tag;
|
|
197
|
+
this.next = undefined;
|
|
198
|
+
this.prev = undefined;
|
|
199
|
+
this.aux = undefined;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
class ItemChain {
|
|
203
|
+
constructor() {
|
|
204
|
+
this.count = 0;
|
|
205
|
+
this.first = undefined;
|
|
206
|
+
this.last = undefined;
|
|
207
|
+
}
|
|
208
|
+
*items() {
|
|
209
|
+
let x = this.first;
|
|
210
|
+
while (x !== undefined) {
|
|
211
|
+
const next = x.next;
|
|
212
|
+
yield x;
|
|
213
|
+
x = next;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
*itemsViaAux() {
|
|
217
|
+
let x = this.first;
|
|
218
|
+
while (x !== undefined) {
|
|
219
|
+
const next = x.aux;
|
|
220
|
+
yield x;
|
|
221
|
+
x = next;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
reset() {
|
|
225
|
+
this.count = 0;
|
|
226
|
+
this.first = undefined;
|
|
227
|
+
this.last = undefined;
|
|
228
|
+
}
|
|
229
|
+
grab(from, join) {
|
|
230
|
+
const head = from.first;
|
|
231
|
+
if (join && head) {
|
|
232
|
+
const last = this.last;
|
|
233
|
+
head.prev = last;
|
|
234
|
+
if (last)
|
|
235
|
+
this.last = last.next = head;
|
|
236
|
+
else
|
|
237
|
+
this.first = this.last = head;
|
|
238
|
+
this.count += from.count;
|
|
239
|
+
}
|
|
240
|
+
else {
|
|
241
|
+
this.count = from.count;
|
|
242
|
+
this.first = head;
|
|
243
|
+
this.last = from.last;
|
|
244
|
+
}
|
|
245
|
+
from.reset();
|
|
246
|
+
}
|
|
247
|
+
include(item) {
|
|
248
|
+
const last = this.last;
|
|
249
|
+
item.prev = last;
|
|
250
|
+
item.next = undefined;
|
|
251
|
+
if (last)
|
|
252
|
+
this.last = last.next = item;
|
|
253
|
+
else
|
|
254
|
+
this.first = this.last = item;
|
|
255
|
+
this.count++;
|
|
256
|
+
}
|
|
257
|
+
exclude(item) {
|
|
258
|
+
if (item.prev !== undefined)
|
|
259
|
+
item.prev.next = item.next;
|
|
260
|
+
if (item.next !== undefined)
|
|
261
|
+
item.next.prev = item.prev;
|
|
262
|
+
if (item === this.first)
|
|
263
|
+
this.first = item.next;
|
|
264
|
+
this.count--;
|
|
265
|
+
}
|
|
266
|
+
aux(item) {
|
|
267
|
+
item.aux = undefined;
|
|
268
|
+
const last = this.last;
|
|
269
|
+
if (last)
|
|
270
|
+
this.last = last.aux = item;
|
|
271
|
+
else
|
|
272
|
+
this.first = this.last = item;
|
|
273
|
+
this.count++;
|
|
274
|
+
}
|
|
275
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "reactronic",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.315",
|
|
4
4
|
"description": "Reactronic - Transactional Reactive State Management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "build/dist/source/api.js",
|
|
@@ -30,15 +30,15 @@
|
|
|
30
30
|
},
|
|
31
31
|
"homepage": "https://github.com/nezaboodka/reactronic/blob/master/README.md#readme",
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@types/node": "18.0.
|
|
34
|
-
"@types/react": "18.0.
|
|
35
|
-
"@typescript-eslint/eslint-plugin": "5.
|
|
36
|
-
"@typescript-eslint/parser": "5.
|
|
37
|
-
"ava": "4.3.
|
|
33
|
+
"@types/node": "18.0.5",
|
|
34
|
+
"@types/react": "18.0.15",
|
|
35
|
+
"@typescript-eslint/eslint-plugin": "5.30.6",
|
|
36
|
+
"@typescript-eslint/parser": "5.30.6",
|
|
37
|
+
"ava": "4.3.1",
|
|
38
38
|
"c8": "7.11.3",
|
|
39
|
-
"eslint": "8.
|
|
39
|
+
"eslint": "8.19.0",
|
|
40
40
|
"react": "18.2.0",
|
|
41
|
-
"ts-node": "10.
|
|
41
|
+
"ts-node": "10.9.1",
|
|
42
42
|
"typescript": "4.7.3"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|