reactronic 0.22.311 → 0.22.314
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/README.md +1 -1
- package/build/dist/source/api.d.ts +4 -1
- package/build/dist/source/api.js +4 -1
- package/build/dist/source/impl/Changeset.d.ts +3 -3
- package/build/dist/source/impl/Changeset.js +15 -13
- package/build/dist/source/impl/Hooks.d.ts +10 -49
- package/build/dist/source/impl/Hooks.js +28 -76
- package/build/dist/source/impl/Journal.js +1 -1
- 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 +5 -5
- 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/impl/Transaction.js +1 -1
- package/build/dist/source/util/Collection.d.ts +61 -0
- package/build/dist/source/util/Collection.js +275 -0
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://www.npmjs.com/package/reactronic)
|
|
5
5
|
[](https://bundlephobia.com/result?p=reactronic)
|
|
6
6
|

|
|
7
|
-

|
|
8
8
|
[](https://gitlab.com/nezaboodka/nevod.website/-/blob/master/README.md)
|
|
9
9
|
|
|
10
10
|
# **Reactronic** - Transactional Reactive State Management
|
|
@@ -1,4 +1,5 @@
|
|
|
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';
|
|
@@ -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';
|
|
@@ -29,9 +29,9 @@ export declare class Changeset implements AbstractChangeset {
|
|
|
29
29
|
static propagateAllChangesThroughSubscriptions: (changeset: Changeset) => void;
|
|
30
30
|
static revokeAllSubscriptions: (changeset: Changeset) => void;
|
|
31
31
|
static enqueueReactionsToRun: (reactions: Array<Subscriber>) => void;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
lookupObjectSnapshot(h: ObjectHandle, m: MemberName): ObjectSnapshot;
|
|
33
|
+
getObjectSnapshot(h: ObjectHandle, m: MemberName): ObjectSnapshot;
|
|
34
|
+
getEditableObjectSnapshot(h: ObjectHandle, m: MemberName, value: any, token?: any): ObjectSnapshot;
|
|
35
35
|
static takeSnapshot<T>(obj: T): T;
|
|
36
36
|
static dispose(obj: any): void;
|
|
37
37
|
static doDispose(ctx: Changeset, h: ObjectHandle): ObjectSnapshot;
|
|
@@ -11,7 +11,7 @@ Object.defineProperty(ObjectHandle.prototype, '#this', {
|
|
|
11
11
|
configurable: false, enumerable: false,
|
|
12
12
|
get() {
|
|
13
13
|
const result = {};
|
|
14
|
-
const data = Changeset.current().
|
|
14
|
+
const data = Changeset.current().getObjectSnapshot(this, '#this').data;
|
|
15
15
|
for (const m in data) {
|
|
16
16
|
const v = data[m];
|
|
17
17
|
if (v instanceof Subscription)
|
|
@@ -38,7 +38,7 @@ export class Changeset {
|
|
|
38
38
|
}
|
|
39
39
|
get hint() { var _a; return (_a = this.options.hint) !== null && _a !== void 0 ? _a : 'noname'; }
|
|
40
40
|
get timestamp() { return this.revision; }
|
|
41
|
-
|
|
41
|
+
lookupObjectSnapshot(h, m) {
|
|
42
42
|
let os = h.editing;
|
|
43
43
|
if (os && os.changeset !== this) {
|
|
44
44
|
os = this.items.get(h);
|
|
@@ -52,14 +52,14 @@ export class Changeset {
|
|
|
52
52
|
}
|
|
53
53
|
return os;
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
const r = this.
|
|
55
|
+
getObjectSnapshot(h, m) {
|
|
56
|
+
const r = this.lookupObjectSnapshot(h, m);
|
|
57
57
|
if (r === EMPTY_SNAPSHOT)
|
|
58
|
-
throw misuse(`
|
|
58
|
+
throw misuse(`cannot use data from a transaction started after the current one T${this.id}[${this.hint}]: ${Dump.obj(h, m)} (head is T${h.head.changeset.id}[${h.head.changeset.hint}]${h.editing ? `, uncommitted T${h.editing.changeset.id}[${h.editing.changeset.hint}]` : ''})`);
|
|
59
59
|
return r;
|
|
60
60
|
}
|
|
61
|
-
|
|
62
|
-
let os = this.
|
|
61
|
+
getEditableObjectSnapshot(h, m, value, token) {
|
|
62
|
+
let os = this.lookupObjectSnapshot(h, m);
|
|
63
63
|
const existing = os.data[m];
|
|
64
64
|
if (existing !== Meta.Nonreactive) {
|
|
65
65
|
if (this.isNewSnapshotRequired(h, os, m, existing, value, token)) {
|
|
@@ -90,7 +90,7 @@ export class Changeset {
|
|
|
90
90
|
Changeset.doDispose(ctx, h);
|
|
91
91
|
}
|
|
92
92
|
static doDispose(ctx, h) {
|
|
93
|
-
const os = ctx.
|
|
93
|
+
const os = ctx.getEditableObjectSnapshot(h, Meta.Revision, Meta.Undefined);
|
|
94
94
|
if (os !== EMPTY_SNAPSHOT)
|
|
95
95
|
os.disposed = true;
|
|
96
96
|
return os;
|
|
@@ -98,13 +98,15 @@ export class Changeset {
|
|
|
98
98
|
isNewSnapshotRequired(h, os, m, existing, value, token) {
|
|
99
99
|
if (this.sealed && os.changeset !== EMPTY_SNAPSHOT.changeset)
|
|
100
100
|
throw misuse(`reactive property ${Dump.obj(h, m)} can only be modified inside transaction`);
|
|
101
|
-
if (m !== Meta.Handle
|
|
102
|
-
if (
|
|
103
|
-
if (
|
|
104
|
-
|
|
101
|
+
if (m !== Meta.Handle) {
|
|
102
|
+
if (value !== Meta.Handle) {
|
|
103
|
+
if (os.changeset !== this || os.former.snapshot !== EMPTY_SNAPSHOT) {
|
|
104
|
+
if (this.options.token !== undefined && token !== this.options.token)
|
|
105
|
+
throw misuse(`${this.hint} should not have side effects (trying to change ${Dump.snapshot(os, m)})`);
|
|
106
|
+
}
|
|
105
107
|
}
|
|
106
108
|
if (os === EMPTY_SNAPSHOT)
|
|
107
|
-
throw misuse(`
|
|
109
|
+
throw misuse(`cannot use data from a transaction started after the current one T${this.id}[${this.hint}]: ${Dump.snapshot(os, m)} (head is T${h.head.changeset.id}[${h.head.changeset.hint}]${h.editing ? `, uncommitted T${h.editing.changeset.id}[${h.editing.changeset.hint}]` : ''})`);
|
|
108
110
|
}
|
|
109
111
|
return os.changeset !== this && !this.sealed;
|
|
110
112
|
}
|
|
@@ -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
|
}
|
|
@@ -120,10 +65,11 @@ export class Hooks {
|
|
|
120
65
|
let result;
|
|
121
66
|
if (m !== Meta.Handle) {
|
|
122
67
|
const cs = Changeset.current();
|
|
123
|
-
const os = cs.
|
|
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
|
|
@@ -134,7 +80,7 @@ export class Hooks {
|
|
|
134
80
|
return result;
|
|
135
81
|
}
|
|
136
82
|
set(h, m, value, receiver) {
|
|
137
|
-
const os = Changeset.edit().
|
|
83
|
+
const os = Changeset.edit().getEditableObjectSnapshot(h, m, value);
|
|
138
84
|
if (os !== EMPTY_SNAPSHOT) {
|
|
139
85
|
let curr = os.data[m];
|
|
140
86
|
if (curr !== undefined || (os.former.snapshot.changeset === EMPTY_SNAPSHOT.changeset && (m in h.data) === false)) {
|
|
@@ -158,18 +104,18 @@ export class Hooks {
|
|
|
158
104
|
return true;
|
|
159
105
|
}
|
|
160
106
|
has(h, m) {
|
|
161
|
-
const os = Changeset.current().
|
|
107
|
+
const os = Changeset.current().getObjectSnapshot(h, m);
|
|
162
108
|
return m in os.data || m in h.data;
|
|
163
109
|
}
|
|
164
110
|
getOwnPropertyDescriptor(h, m) {
|
|
165
|
-
const os = Changeset.current().
|
|
111
|
+
const os = Changeset.current().getObjectSnapshot(h, m);
|
|
166
112
|
const pd = Reflect.getOwnPropertyDescriptor(os.data, m);
|
|
167
113
|
if (pd)
|
|
168
114
|
pd.configurable = pd.writable = true;
|
|
169
115
|
return pd;
|
|
170
116
|
}
|
|
171
117
|
ownKeys(h) {
|
|
172
|
-
const os = Changeset.current().
|
|
118
|
+
const os = Changeset.current().getObjectSnapshot(h, Meta.Handle);
|
|
173
119
|
const result = [];
|
|
174
120
|
for (const m of Object.getOwnPropertyNames(os.data)) {
|
|
175
121
|
const value = os.data[m];
|
|
@@ -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,17 +181,18 @@ 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
|
|
248
|
-
|
|
193
|
+
const hooks = reactive ? Hooks.reactive : Hooks.transactional;
|
|
194
|
+
const h = new ObjectHandle(data, undefined, hooks, EMPTY_SNAPSHOT, hint);
|
|
195
|
+
ctx.getEditableObjectSnapshot(h, Meta.Handle, blank);
|
|
249
196
|
if (!Hooks.reactionsAutoStartDisabled)
|
|
250
197
|
for (const m in Meta.getFrom(proto, Meta.Reactions))
|
|
251
198
|
h.proxy[m][Meta.Controller].markObsolete();
|
|
@@ -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
|
};
|
|
@@ -97,7 +97,7 @@ export class JournalImpl extends Journal {
|
|
|
97
97
|
if (!disposed) {
|
|
98
98
|
op.forEach((vp, m) => {
|
|
99
99
|
const value = undoing ? vp.formerValue : vp.freshValue;
|
|
100
|
-
const os = ctx.
|
|
100
|
+
const os = ctx.getEditableObjectSnapshot(h, m, value);
|
|
101
101
|
if (os.changeset === ctx) {
|
|
102
102
|
os.data[m] = new Subscription(value);
|
|
103
103
|
const existing = os.former.snapshot.data[m];
|
|
@@ -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();
|
|
@@ -103,7 +103,7 @@ export class OperationController extends Controller {
|
|
|
103
103
|
}
|
|
104
104
|
peek(args) {
|
|
105
105
|
const ctx = Changeset.current();
|
|
106
|
-
const os = ctx.
|
|
106
|
+
const os = ctx.lookupObjectSnapshot(this.objectHandle, this.memberName);
|
|
107
107
|
const op = this.acquireFromSnapshot(os, args);
|
|
108
108
|
const isValid = op.options.kind !== Kind.Transaction && op.cause !== BOOT_CAUSE &&
|
|
109
109
|
(ctx === op.changeset || ctx.timestamp < op.obsoleteSince) &&
|
|
@@ -120,7 +120,7 @@ export class OperationController extends Controller {
|
|
|
120
120
|
const h = this.objectHandle;
|
|
121
121
|
const m = this.memberName;
|
|
122
122
|
const ctx = Changeset.edit();
|
|
123
|
-
const os = ctx.
|
|
123
|
+
const os = ctx.getEditableObjectSnapshot(h, m, Meta.Handle, this);
|
|
124
124
|
let op = this.acquireFromSnapshot(os, undefined);
|
|
125
125
|
if (op.changeset !== os.changeset) {
|
|
126
126
|
const op2 = new Operation(this, os.changeset, op);
|
|
@@ -140,10 +140,10 @@ export class OperationController extends Controller {
|
|
|
140
140
|
const standalone = os.changeset.sealed || os.former.snapshot !== EMPTY_SNAPSHOT;
|
|
141
141
|
op = Transaction.run({ hint, standalone, token: this }, () => {
|
|
142
142
|
const h = this.objectHandle;
|
|
143
|
-
let r2 = Changeset.current().
|
|
143
|
+
let r2 = Changeset.current().getObjectSnapshot(h, m);
|
|
144
144
|
let op2 = r2.data[m];
|
|
145
145
|
if (op2.controller !== this) {
|
|
146
|
-
r2 = Changeset.edit().
|
|
146
|
+
r2 = Changeset.edit().getEditableObjectSnapshot(h, m, Meta.Handle, this);
|
|
147
147
|
const t = new Operation(this, r2.changeset, op2);
|
|
148
148
|
if (args)
|
|
149
149
|
t.args = args;
|
|
@@ -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
|
+
}
|
|
@@ -301,7 +301,7 @@ class TransactionImpl extends Transaction {
|
|
|
301
301
|
Changeset._init();
|
|
302
302
|
}
|
|
303
303
|
}
|
|
304
|
-
TransactionImpl.none = new TransactionImpl({ hint: '
|
|
304
|
+
TransactionImpl.none = new TransactionImpl({ hint: '<none>' });
|
|
305
305
|
TransactionImpl.curr = TransactionImpl.none;
|
|
306
306
|
TransactionImpl.inspection = false;
|
|
307
307
|
TransactionImpl.frameStartTime = 0;
|
|
@@ -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.314",
|
|
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": {
|