reactronic 0.22.308 → 0.22.311
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/LICENSE +202 -21
- package/README.md +3 -4
- package/build/dist/source/Buffer.d.ts +8 -0
- package/build/dist/source/Buffer.js +4 -0
- package/build/dist/source/Controller.d.ts +12 -0
- package/build/dist/source/Controller.js +2 -0
- package/build/dist/source/Logging.d.ts +38 -0
- package/build/dist/source/Logging.js +110 -0
- package/build/dist/source/Options.d.ts +38 -0
- package/build/dist/source/Options.js +17 -0
- package/build/dist/source/Ref.d.ts +34 -0
- package/build/dist/source/Ref.js +85 -0
- package/build/dist/source/Rx.d.ts +27 -0
- package/build/dist/source/Rx.js +47 -0
- package/build/dist/source/Worker.d.ts +8 -0
- package/build/dist/source/Worker.js +1 -0
- package/build/dist/source/api.d.ts +14 -0
- package/build/dist/source/api.js +13 -0
- package/build/dist/source/impl/Changeset.d.ts +60 -0
- package/build/dist/source/impl/Changeset.js +356 -0
- package/build/dist/source/impl/Data.d.ts +60 -0
- package/build/dist/source/impl/Data.js +44 -0
- package/build/dist/source/impl/Hooks.d.ts +96 -0
- package/build/dist/source/impl/Hooks.js +302 -0
- package/build/dist/source/impl/Journal.d.ts +34 -0
- package/build/dist/source/impl/Journal.js +144 -0
- package/build/dist/source/impl/Meta.d.ts +13 -0
- package/build/dist/source/impl/Meta.js +29 -0
- package/build/dist/source/impl/Monitor.d.ts +32 -0
- package/build/dist/source/impl/Monitor.js +92 -0
- package/build/dist/source/impl/Operation.d.ts +93 -0
- package/build/dist/source/impl/Operation.js +716 -0
- package/build/dist/source/impl/Transaction.d.ts +30 -0
- package/build/dist/source/impl/Transaction.js +309 -0
- package/build/dist/source/util/Dbg.d.ts +15 -0
- package/build/dist/source/util/Dbg.js +89 -0
- package/build/dist/source/util/Sealant.d.ts +14 -0
- package/build/dist/source/util/Sealant.js +26 -0
- package/build/dist/source/util/SealedArray.d.ts +16 -0
- package/build/dist/source/util/SealedArray.js +24 -0
- package/build/dist/source/util/SealedMap.d.ts +13 -0
- package/build/dist/source/util/SealedMap.js +17 -0
- package/build/dist/source/util/SealedSet.d.ts +13 -0
- package/build/dist/source/util/SealedSet.js +17 -0
- package/build/dist/source/util/Utils.d.ts +9 -0
- package/build/dist/source/util/Utils.js +55 -0
- package/package.json +14 -14
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { F } from '../util/Utils';
|
|
2
|
+
import { MemberOptions, Kind, Reentrance } from '../Options';
|
|
3
|
+
import { LoggingOptions, ProfilingOptions } from '../Logging';
|
|
4
|
+
import { MemberName, ObjectHandle, StandaloneMode } from './Data';
|
|
5
|
+
import { Journal } from './Journal';
|
|
6
|
+
import { Monitor } from './Monitor';
|
|
7
|
+
export declare abstract class ReactiveObject {
|
|
8
|
+
constructor();
|
|
9
|
+
[Symbol.toStringTag](): string;
|
|
10
|
+
}
|
|
11
|
+
export declare class ReactiveArray<T> extends ReactiveObject {
|
|
12
|
+
private a;
|
|
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();
|
|
56
|
+
}
|
|
57
|
+
export declare class OptionsImpl implements MemberOptions {
|
|
58
|
+
readonly getter: Function;
|
|
59
|
+
readonly setter: Function;
|
|
60
|
+
readonly kind: Kind;
|
|
61
|
+
readonly standalone: StandaloneMode;
|
|
62
|
+
readonly order: number;
|
|
63
|
+
readonly noSideEffects: boolean;
|
|
64
|
+
readonly triggeringArgs: boolean;
|
|
65
|
+
readonly throttling: number;
|
|
66
|
+
readonly reentrance: Reentrance;
|
|
67
|
+
readonly journal: Journal | undefined;
|
|
68
|
+
readonly monitor: Monitor | null;
|
|
69
|
+
readonly logging?: Partial<LoggingOptions>;
|
|
70
|
+
static readonly INITIAL: Readonly<OptionsImpl>;
|
|
71
|
+
constructor(getter: Function | undefined, setter: Function | undefined, existing: OptionsImpl, patch: Partial<OptionsImpl>, implicit: boolean);
|
|
72
|
+
}
|
|
73
|
+
export declare class Hooks implements ProxyHandler<ObjectHandle> {
|
|
74
|
+
static reactionsAutoStartDisabled: boolean;
|
|
75
|
+
static repetitiveUsageWarningThreshold: number;
|
|
76
|
+
static mainThreadBlockingWarningThreshold: number;
|
|
77
|
+
static asyncActionDurationWarningThreshold: number;
|
|
78
|
+
static sensitivity: boolean;
|
|
79
|
+
static readonly handler: Hooks;
|
|
80
|
+
getPrototypeOf(h: ObjectHandle): object | null;
|
|
81
|
+
get(h: ObjectHandle, m: MemberName, receiver: any): any;
|
|
82
|
+
set(h: ObjectHandle, m: MemberName, value: any, receiver: any): boolean;
|
|
83
|
+
has(h: ObjectHandle, m: MemberName): boolean;
|
|
84
|
+
getOwnPropertyDescriptor(h: ObjectHandle, m: MemberName): PropertyDescriptor | undefined;
|
|
85
|
+
ownKeys(h: ObjectHandle): Array<string | symbol>;
|
|
86
|
+
static decorateData(reactive: boolean, proto: any, m: MemberName): any;
|
|
87
|
+
static decorateOperation(implicit: boolean, decorator: Function, options: Partial<MemberOptions>, proto: any, member: MemberName, pd: PropertyDescriptor | undefined): any;
|
|
88
|
+
static decorateOperationParametrized(decorator: Function, options: Partial<MemberOptions>): F<any>;
|
|
89
|
+
static acquireHandle(obj: any): ObjectHandle;
|
|
90
|
+
static createHandleForReactiveObject(proto: any, data: any, blank: any, hint: string): ObjectHandle;
|
|
91
|
+
static setProfilingMode(isOn: boolean, options?: Partial<ProfilingOptions>): void;
|
|
92
|
+
static sensitive<T>(sensitivity: boolean, func: F<T>, ...args: any[]): T;
|
|
93
|
+
static setHint<T>(obj: T, hint: string | undefined): T;
|
|
94
|
+
static createOperation: (h: ObjectHandle, m: MemberName, options: OptionsImpl) => F<any>;
|
|
95
|
+
static rememberOperationOptions: (proto: any, m: MemberName, getter: Function | undefined, setter: Function | undefined, enumerable: boolean, configurable: boolean, options: Partial<MemberOptions>, implicit: boolean) => OptionsImpl;
|
|
96
|
+
}
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { UNDEF } from '../util/Utils';
|
|
2
|
+
import { Sealant } from '../util/Sealant';
|
|
3
|
+
import { Log, misuse } from '../util/Dbg';
|
|
4
|
+
import { Kind, Reentrance } from '../Options';
|
|
5
|
+
import { ObjectSnapshot, ObjectHandle, Subscription, Meta } from './Data';
|
|
6
|
+
import { Changeset, Dump, EMPTY_SNAPSHOT } from './Changeset';
|
|
7
|
+
export class ReactiveObject {
|
|
8
|
+
constructor() {
|
|
9
|
+
const proto = new.target.prototype;
|
|
10
|
+
const initial = Meta.getFrom(proto, Meta.Initial);
|
|
11
|
+
const h = Hooks.createHandleForReactiveObject(proto, this, initial, new.target.name);
|
|
12
|
+
return h.proxy;
|
|
13
|
+
}
|
|
14
|
+
[Symbol.toStringTag]() {
|
|
15
|
+
const h = Meta.get(this, Meta.Handle);
|
|
16
|
+
return Dump.obj(h);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class ReactiveArray extends ReactiveObject {
|
|
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 {
|
|
60
|
+
constructor() {
|
|
61
|
+
super(...arguments);
|
|
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;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const DEFAULT_OPTIONS = Object.freeze({
|
|
82
|
+
kind: Kind.Plain,
|
|
83
|
+
standalone: false,
|
|
84
|
+
order: 0,
|
|
85
|
+
noSideEffects: false,
|
|
86
|
+
triggeringArgs: false,
|
|
87
|
+
throttling: Number.MAX_SAFE_INTEGER,
|
|
88
|
+
reentrance: Reentrance.PreventWithError,
|
|
89
|
+
journal: undefined,
|
|
90
|
+
monitor: null,
|
|
91
|
+
logging: undefined,
|
|
92
|
+
});
|
|
93
|
+
export class OptionsImpl {
|
|
94
|
+
constructor(getter, setter, existing, patch, implicit) {
|
|
95
|
+
this.getter = getter !== undefined ? getter : existing.getter;
|
|
96
|
+
this.setter = setter !== undefined ? setter : existing.setter;
|
|
97
|
+
this.kind = merge(DEFAULT_OPTIONS.kind, existing.kind, patch.kind, implicit);
|
|
98
|
+
this.standalone = merge(DEFAULT_OPTIONS.standalone, existing.standalone, patch.standalone, implicit);
|
|
99
|
+
this.order = merge(DEFAULT_OPTIONS.order, existing.order, patch.order, implicit);
|
|
100
|
+
this.noSideEffects = merge(DEFAULT_OPTIONS.noSideEffects, existing.noSideEffects, patch.noSideEffects, implicit);
|
|
101
|
+
this.triggeringArgs = merge(DEFAULT_OPTIONS.triggeringArgs, existing.triggeringArgs, patch.triggeringArgs, implicit);
|
|
102
|
+
this.throttling = merge(DEFAULT_OPTIONS.throttling, existing.throttling, patch.throttling, implicit);
|
|
103
|
+
this.reentrance = merge(DEFAULT_OPTIONS.reentrance, existing.reentrance, patch.reentrance, implicit);
|
|
104
|
+
this.journal = merge(DEFAULT_OPTIONS.journal, existing.journal, patch.journal, implicit);
|
|
105
|
+
this.monitor = merge(DEFAULT_OPTIONS.monitor, existing.monitor, patch.monitor, implicit);
|
|
106
|
+
this.logging = merge(DEFAULT_OPTIONS.logging, existing.logging, patch.logging, implicit);
|
|
107
|
+
if (Log.isOn)
|
|
108
|
+
Object.freeze(this);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
OptionsImpl.INITIAL = Object.freeze(new OptionsImpl(UNDEF, UNDEF, Object.assign({ getter: UNDEF, setter: UNDEF }, DEFAULT_OPTIONS), {}, false));
|
|
112
|
+
function merge(def, existing, patch, implicit) {
|
|
113
|
+
return patch !== undefined && (existing === def || !implicit) ? patch : existing;
|
|
114
|
+
}
|
|
115
|
+
export class Hooks {
|
|
116
|
+
getPrototypeOf(h) {
|
|
117
|
+
return Reflect.getPrototypeOf(h.data);
|
|
118
|
+
}
|
|
119
|
+
get(h, m, receiver) {
|
|
120
|
+
let result;
|
|
121
|
+
if (m !== Meta.Handle) {
|
|
122
|
+
const cs = Changeset.current();
|
|
123
|
+
const os = cs.getRelevantSnapshot(h, m);
|
|
124
|
+
result = os.data[m];
|
|
125
|
+
if (result instanceof Subscription && !result.isOperation) {
|
|
126
|
+
Changeset.markUsed(result, os, m, h, Kind.Plain, false);
|
|
127
|
+
result = result.content;
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
result = Reflect.get(h.data, m, receiver);
|
|
131
|
+
}
|
|
132
|
+
else
|
|
133
|
+
result = h;
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
set(h, m, value, receiver) {
|
|
137
|
+
const os = Changeset.edit().getEditableSnapshot(h, m, value);
|
|
138
|
+
if (os !== EMPTY_SNAPSHOT) {
|
|
139
|
+
let curr = os.data[m];
|
|
140
|
+
if (curr !== undefined || (os.former.snapshot.changeset === EMPTY_SNAPSHOT.changeset && (m in h.data) === false)) {
|
|
141
|
+
if (curr === undefined || curr.content !== value || Hooks.sensitivity) {
|
|
142
|
+
const existing = curr === null || curr === void 0 ? void 0 : curr.content;
|
|
143
|
+
if (os.former.snapshot.data[m] === curr) {
|
|
144
|
+
curr = os.data[m] = new Subscription(value);
|
|
145
|
+
Changeset.markEdited(existing, value, true, os, m, h);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
curr.content = value;
|
|
149
|
+
Changeset.markEdited(existing, value, true, os, m, h);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else
|
|
154
|
+
Reflect.set(h.data, m, value, receiver);
|
|
155
|
+
}
|
|
156
|
+
else
|
|
157
|
+
h.data[m] = value;
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
has(h, m) {
|
|
161
|
+
const os = Changeset.current().getRelevantSnapshot(h, m);
|
|
162
|
+
return m in os.data || m in h.data;
|
|
163
|
+
}
|
|
164
|
+
getOwnPropertyDescriptor(h, m) {
|
|
165
|
+
const os = Changeset.current().getRelevantSnapshot(h, m);
|
|
166
|
+
const pd = Reflect.getOwnPropertyDescriptor(os.data, m);
|
|
167
|
+
if (pd)
|
|
168
|
+
pd.configurable = pd.writable = true;
|
|
169
|
+
return pd;
|
|
170
|
+
}
|
|
171
|
+
ownKeys(h) {
|
|
172
|
+
const os = Changeset.current().getRelevantSnapshot(h, Meta.Handle);
|
|
173
|
+
const result = [];
|
|
174
|
+
for (const m of Object.getOwnPropertyNames(os.data)) {
|
|
175
|
+
const value = os.data[m];
|
|
176
|
+
if (!(value instanceof Subscription) || !value.isOperation)
|
|
177
|
+
result.push(m);
|
|
178
|
+
}
|
|
179
|
+
return result;
|
|
180
|
+
}
|
|
181
|
+
static decorateData(reactive, proto, m) {
|
|
182
|
+
if (reactive) {
|
|
183
|
+
const get = function () {
|
|
184
|
+
const h = Hooks.acquireHandle(this);
|
|
185
|
+
return Hooks.handler.get(h, m, this);
|
|
186
|
+
};
|
|
187
|
+
const set = function (value) {
|
|
188
|
+
const h = Hooks.acquireHandle(this);
|
|
189
|
+
return Hooks.handler.set(h, m, value, this);
|
|
190
|
+
};
|
|
191
|
+
const enumerable = true;
|
|
192
|
+
const configurable = false;
|
|
193
|
+
return Object.defineProperty(proto, m, { get, set, enumerable, configurable });
|
|
194
|
+
}
|
|
195
|
+
else
|
|
196
|
+
Meta.acquire(proto, Meta.Initial)[m] = Meta.Nonreactive;
|
|
197
|
+
}
|
|
198
|
+
static decorateOperation(implicit, decorator, options, proto, member, pd) {
|
|
199
|
+
var _a, _b, _c, _d;
|
|
200
|
+
if (pd === undefined || pd === proto)
|
|
201
|
+
pd = EMPTY_PROP_DESCRIPTOR;
|
|
202
|
+
const enumerable = (_a = pd.enumerable) !== null && _a !== void 0 ? _a : true;
|
|
203
|
+
const configurable = (_b = pd.configurable) !== null && _b !== void 0 ? _b : true;
|
|
204
|
+
const opts = Hooks.rememberOperationOptions(proto, member, (_c = pd.value) !== null && _c !== void 0 ? _c : pd.get, (_d = pd.value) !== null && _d !== void 0 ? _d : pd.set, true, configurable, options, implicit);
|
|
205
|
+
if (opts.getter === opts.setter) {
|
|
206
|
+
const bootstrap = function () {
|
|
207
|
+
const h = Hooks.acquireHandle(this);
|
|
208
|
+
const operation = Hooks.createOperation(h, member, opts);
|
|
209
|
+
Object.defineProperty(h.data, member, { value: operation, enumerable, configurable });
|
|
210
|
+
return operation;
|
|
211
|
+
};
|
|
212
|
+
return Object.defineProperty(proto, member, { get: bootstrap, enumerable, configurable: true });
|
|
213
|
+
}
|
|
214
|
+
else if (opts.setter === UNDEF) {
|
|
215
|
+
const bootstrap = function () {
|
|
216
|
+
const h = Hooks.acquireHandle(this);
|
|
217
|
+
const operation = Hooks.createOperation(h, member, opts);
|
|
218
|
+
Object.defineProperty(h.data, member, { get: operation, enumerable, configurable });
|
|
219
|
+
return operation.call(this);
|
|
220
|
+
};
|
|
221
|
+
return Object.defineProperty(proto, member, { get: bootstrap, enumerable, configurable: true });
|
|
222
|
+
}
|
|
223
|
+
else
|
|
224
|
+
throw misuse(`${proto.constructor.name}.${member.toString()} has setter and cannot be decorated with @${decorator.name}`);
|
|
225
|
+
}
|
|
226
|
+
static decorateOperationParametrized(decorator, options) {
|
|
227
|
+
return function (proto, prop, pd) {
|
|
228
|
+
return Hooks.decorateOperation(false, decorator, options, proto, prop, pd);
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
static acquireHandle(obj) {
|
|
232
|
+
let h = obj[Meta.Handle];
|
|
233
|
+
if (!h) {
|
|
234
|
+
if (obj !== Object(obj) || Array.isArray(obj))
|
|
235
|
+
throw misuse('only objects can be reactive');
|
|
236
|
+
const initial = Meta.getFrom(Object.getPrototypeOf(obj), Meta.Initial);
|
|
237
|
+
const os = new ObjectSnapshot(EMPTY_SNAPSHOT.changeset, EMPTY_SNAPSHOT, Object.assign({}, initial));
|
|
238
|
+
h = new ObjectHandle(obj, obj, Hooks.handler, os, obj.constructor.name);
|
|
239
|
+
Meta.set(os.data, Meta.Handle, h);
|
|
240
|
+
Meta.set(obj, Meta.Handle, h);
|
|
241
|
+
Meta.set(os.data, Meta.Revision, new Subscription(1));
|
|
242
|
+
}
|
|
243
|
+
return h;
|
|
244
|
+
}
|
|
245
|
+
static createHandleForReactiveObject(proto, data, blank, hint) {
|
|
246
|
+
const ctx = Changeset.edit();
|
|
247
|
+
const h = new ObjectHandle(data, undefined, Hooks.handler, EMPTY_SNAPSHOT, hint);
|
|
248
|
+
ctx.getEditableSnapshot(h, Meta.Handle, blank);
|
|
249
|
+
if (!Hooks.reactionsAutoStartDisabled)
|
|
250
|
+
for (const m in Meta.getFrom(proto, Meta.Reactions))
|
|
251
|
+
h.proxy[m][Meta.Controller].markObsolete();
|
|
252
|
+
return h;
|
|
253
|
+
}
|
|
254
|
+
static setProfilingMode(isOn, options) {
|
|
255
|
+
if (isOn) {
|
|
256
|
+
Hooks.repetitiveUsageWarningThreshold = options && options.repetitiveUsageWarningThreshold !== undefined ? options.repetitiveUsageWarningThreshold : 10;
|
|
257
|
+
Hooks.mainThreadBlockingWarningThreshold = options && options.mainThreadBlockingWarningThreshold !== undefined ? options.mainThreadBlockingWarningThreshold : 14;
|
|
258
|
+
Hooks.asyncActionDurationWarningThreshold = options && options.asyncActionDurationWarningThreshold !== undefined ? options.asyncActionDurationWarningThreshold : 300;
|
|
259
|
+
Changeset.garbageCollectionSummaryInterval = options && options.garbageCollectionSummaryInterval !== undefined ? options.garbageCollectionSummaryInterval : 100;
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
Hooks.repetitiveUsageWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
263
|
+
Hooks.mainThreadBlockingWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
264
|
+
Hooks.asyncActionDurationWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
265
|
+
Changeset.garbageCollectionSummaryInterval = Number.MAX_SAFE_INTEGER;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
static sensitive(sensitivity, func, ...args) {
|
|
269
|
+
const restore = Hooks.sensitivity;
|
|
270
|
+
Hooks.sensitivity = sensitivity;
|
|
271
|
+
try {
|
|
272
|
+
return func(...args);
|
|
273
|
+
}
|
|
274
|
+
finally {
|
|
275
|
+
Hooks.sensitivity = restore;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
static setHint(obj, hint) {
|
|
279
|
+
if (hint) {
|
|
280
|
+
const h = Hooks.acquireHandle(obj);
|
|
281
|
+
h.hint = hint;
|
|
282
|
+
}
|
|
283
|
+
return obj;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
Hooks.reactionsAutoStartDisabled = false;
|
|
287
|
+
Hooks.repetitiveUsageWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
288
|
+
Hooks.mainThreadBlockingWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
289
|
+
Hooks.asyncActionDurationWarningThreshold = Number.MAX_SAFE_INTEGER;
|
|
290
|
+
Hooks.sensitivity = false;
|
|
291
|
+
Hooks.handler = new Hooks();
|
|
292
|
+
Hooks.createOperation = function (h, m, options) {
|
|
293
|
+
throw misuse('createOperation should never be called');
|
|
294
|
+
};
|
|
295
|
+
Hooks.rememberOperationOptions = function (proto, m, getter, setter, enumerable, configurable, options, implicit) {
|
|
296
|
+
throw misuse('rememberOperationOptions should never be called');
|
|
297
|
+
};
|
|
298
|
+
const EMPTY_PROP_DESCRIPTOR = {
|
|
299
|
+
configurable: true,
|
|
300
|
+
enumerable: true,
|
|
301
|
+
value: undefined,
|
|
302
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { ReactiveObject } from './Hooks';
|
|
2
|
+
import { ObjectHandle, ObjectSnapshot, PatchSet } from './Data';
|
|
3
|
+
export declare type Saver = (patch: PatchSet) => Promise<void>;
|
|
4
|
+
export declare abstract class Journal extends ReactiveObject {
|
|
5
|
+
abstract capacity: number;
|
|
6
|
+
abstract readonly edits: ReadonlyArray<PatchSet>;
|
|
7
|
+
abstract readonly unsaved: PatchSet;
|
|
8
|
+
abstract readonly canUndo: boolean;
|
|
9
|
+
abstract readonly canRedo: boolean;
|
|
10
|
+
abstract edited(patch: PatchSet): void;
|
|
11
|
+
abstract saved(patch: PatchSet): void;
|
|
12
|
+
abstract undo(count?: number): void;
|
|
13
|
+
abstract redo(count?: number): void;
|
|
14
|
+
static create(): Journal;
|
|
15
|
+
}
|
|
16
|
+
export declare class JournalImpl extends Journal {
|
|
17
|
+
private _capacity;
|
|
18
|
+
private _edits;
|
|
19
|
+
private _unsaved;
|
|
20
|
+
private _position;
|
|
21
|
+
get capacity(): number;
|
|
22
|
+
set capacity(value: number);
|
|
23
|
+
get edits(): ReadonlyArray<PatchSet>;
|
|
24
|
+
get unsaved(): PatchSet;
|
|
25
|
+
get canUndo(): boolean;
|
|
26
|
+
get canRedo(): boolean;
|
|
27
|
+
edited(p: PatchSet): void;
|
|
28
|
+
saved(patch: PatchSet): void;
|
|
29
|
+
undo(count?: number): void;
|
|
30
|
+
redo(count?: number): void;
|
|
31
|
+
static buildPatch(hint: string, items: Map<ObjectHandle, ObjectSnapshot>): PatchSet;
|
|
32
|
+
static applyPatch(patch: PatchSet, undoing: boolean): void;
|
|
33
|
+
mergePatchToUnsaved(patch: PatchSet, undoing: boolean): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { ReactiveObject } from './Hooks';
|
|
2
|
+
import { Meta, Subscription } from './Data';
|
|
3
|
+
import { Changeset, EMPTY_SNAPSHOT } from './Changeset';
|
|
4
|
+
import { Transaction } from './Transaction';
|
|
5
|
+
import { Sealant } from '../util/Sealant';
|
|
6
|
+
export class Journal extends ReactiveObject {
|
|
7
|
+
static create() { return new JournalImpl(); }
|
|
8
|
+
}
|
|
9
|
+
export class JournalImpl extends Journal {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this._capacity = 5;
|
|
13
|
+
this._edits = [];
|
|
14
|
+
this._unsaved = new Map();
|
|
15
|
+
this._position = 0;
|
|
16
|
+
}
|
|
17
|
+
get capacity() { return this._capacity; }
|
|
18
|
+
set capacity(value) { this._capacity = value; if (value < this._edits.length)
|
|
19
|
+
this._edits.splice(0, this._edits.length - value); }
|
|
20
|
+
get edits() { return this._edits; }
|
|
21
|
+
get unsaved() { return this._unsaved; }
|
|
22
|
+
get canUndo() { return this._edits.length > 0 && this._position > 0; }
|
|
23
|
+
get canRedo() { return this._position < this._edits.length; }
|
|
24
|
+
edited(p) {
|
|
25
|
+
Transaction.run({ hint: 'EditJournal.edited', standalone: 'isolated' }, () => {
|
|
26
|
+
const items = this._edits = this._edits.toMutable();
|
|
27
|
+
if (items.length >= this._capacity)
|
|
28
|
+
items.shift();
|
|
29
|
+
else
|
|
30
|
+
items.splice(this._position);
|
|
31
|
+
this.mergePatchToUnsaved(p, false);
|
|
32
|
+
items.push(p);
|
|
33
|
+
this._position = items.length;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
saved(patch) {
|
|
37
|
+
if (this._unsaved === patch)
|
|
38
|
+
this._unsaved = new Map();
|
|
39
|
+
else
|
|
40
|
+
throw new Error('not implemented');
|
|
41
|
+
}
|
|
42
|
+
undo(count = 1) {
|
|
43
|
+
Transaction.run({ hint: 'Journal.undo', standalone: 'isolated' }, () => {
|
|
44
|
+
let i = this._position - 1;
|
|
45
|
+
while (i >= 0 && count > 0) {
|
|
46
|
+
const patch = this._edits[i];
|
|
47
|
+
JournalImpl.applyPatch(patch, true);
|
|
48
|
+
this.mergePatchToUnsaved(patch, true);
|
|
49
|
+
i--, count--;
|
|
50
|
+
}
|
|
51
|
+
this._position = i + 1;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
redo(count = 1) {
|
|
55
|
+
Transaction.run({ hint: 'Journal.redo', standalone: 'isolated' }, () => {
|
|
56
|
+
let i = this._position;
|
|
57
|
+
while (i < this._edits.length && count > 0) {
|
|
58
|
+
const patch = this._edits[i];
|
|
59
|
+
JournalImpl.applyPatch(patch, false);
|
|
60
|
+
this.mergePatchToUnsaved(patch, false);
|
|
61
|
+
i++, count--;
|
|
62
|
+
}
|
|
63
|
+
this._position = i;
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
static buildPatch(hint, items) {
|
|
67
|
+
const patch = new Map();
|
|
68
|
+
items.forEach((os, h) => {
|
|
69
|
+
const op = new Map();
|
|
70
|
+
const former = os.former.snapshot !== EMPTY_SNAPSHOT ? os.former.snapshot.data : undefined;
|
|
71
|
+
os.changes.forEach(m => {
|
|
72
|
+
const vp = {
|
|
73
|
+
memberName: m, patchKind: 'update',
|
|
74
|
+
freshValue: unseal(os.data[m]), formerValue: undefined,
|
|
75
|
+
};
|
|
76
|
+
if (former)
|
|
77
|
+
vp.formerValue = unseal(former[m]);
|
|
78
|
+
op.set(m, vp);
|
|
79
|
+
});
|
|
80
|
+
if (!former) {
|
|
81
|
+
const vp = {
|
|
82
|
+
memberName: Meta.Revision, patchKind: 'remove',
|
|
83
|
+
freshValue: Meta.Undefined, formerValue: undefined,
|
|
84
|
+
};
|
|
85
|
+
op.set(Meta.Revision, vp);
|
|
86
|
+
}
|
|
87
|
+
patch.set(h.proxy, op);
|
|
88
|
+
});
|
|
89
|
+
return patch;
|
|
90
|
+
}
|
|
91
|
+
static applyPatch(patch, undoing) {
|
|
92
|
+
const ctx = Changeset.edit();
|
|
93
|
+
patch.forEach((op, obj) => {
|
|
94
|
+
const h = Meta.get(obj, Meta.Handle);
|
|
95
|
+
const rev = op.get(Meta.Revision);
|
|
96
|
+
const disposed = rev && (undoing ? rev.formerValue : rev.freshValue) === Meta.Undefined;
|
|
97
|
+
if (!disposed) {
|
|
98
|
+
op.forEach((vp, m) => {
|
|
99
|
+
const value = undoing ? vp.formerValue : vp.freshValue;
|
|
100
|
+
const os = ctx.getEditableSnapshot(h, m, value);
|
|
101
|
+
if (os.changeset === ctx) {
|
|
102
|
+
os.data[m] = new Subscription(value);
|
|
103
|
+
const existing = os.former.snapshot.data[m];
|
|
104
|
+
Changeset.markEdited(existing, value, existing !== value, os, m, h);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
Changeset.doDispose(ctx, h);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
mergePatchToUnsaved(patch, undoing) {
|
|
113
|
+
const unsaved = this._unsaved = this._unsaved.toMutable();
|
|
114
|
+
patch.forEach((op, obj) => {
|
|
115
|
+
let result = unsaved.get(obj);
|
|
116
|
+
if (!result)
|
|
117
|
+
unsaved.set(obj, result = new Map());
|
|
118
|
+
op.forEach((vp, m) => {
|
|
119
|
+
let merged = result.get(m);
|
|
120
|
+
if (!merged)
|
|
121
|
+
result.set(m, merged = {
|
|
122
|
+
memberName: m, patchKind: 'update',
|
|
123
|
+
freshValue: undefined, formerValue: undefined,
|
|
124
|
+
});
|
|
125
|
+
const value = undoing ? vp.formerValue : vp.freshValue;
|
|
126
|
+
const former = undoing ? vp.freshValue : vp.formerValue;
|
|
127
|
+
if (value !== merged.formerValue) {
|
|
128
|
+
merged.freshValue = value;
|
|
129
|
+
merged.formerValue = former;
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
result.delete(m);
|
|
133
|
+
if (result.size === 0)
|
|
134
|
+
unsaved.delete(obj);
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function unseal(subscription) {
|
|
141
|
+
const result = subscription.content;
|
|
142
|
+
const createCopy = result === null || result === void 0 ? void 0 : result[Sealant.CreateCopy];
|
|
143
|
+
return createCopy !== undefined ? createCopy.call(result) : result;
|
|
144
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare abstract class Meta {
|
|
2
|
+
static readonly Handle: unique symbol;
|
|
3
|
+
static readonly Revision: unique symbol;
|
|
4
|
+
static readonly Controller: unique symbol;
|
|
5
|
+
static readonly Initial: unique symbol;
|
|
6
|
+
static readonly Reactions: unique symbol;
|
|
7
|
+
static readonly Nonreactive: unique symbol;
|
|
8
|
+
static readonly Undefined: unique symbol;
|
|
9
|
+
static get<T>(obj: any, sym: symbol): T;
|
|
10
|
+
static set(obj: any, sym: symbol, value: any): any;
|
|
11
|
+
static acquire(proto: any, sym: symbol): any;
|
|
12
|
+
static getFrom<T = any>(proto: any, sym: symbol): T;
|
|
13
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const EMPTY_META = Object.freeze({});
|
|
2
|
+
export class Meta {
|
|
3
|
+
static get(obj, sym) {
|
|
4
|
+
return obj[sym];
|
|
5
|
+
}
|
|
6
|
+
static set(obj, sym, value) {
|
|
7
|
+
Object.defineProperty(obj, sym, { value, configurable: false, enumerable: false });
|
|
8
|
+
return obj;
|
|
9
|
+
}
|
|
10
|
+
static acquire(proto, sym) {
|
|
11
|
+
let meta = proto[sym];
|
|
12
|
+
if (!proto.hasOwnProperty(sym)) {
|
|
13
|
+
meta = Object.assign({}, meta);
|
|
14
|
+
Meta.set(proto, sym, meta);
|
|
15
|
+
}
|
|
16
|
+
return meta;
|
|
17
|
+
}
|
|
18
|
+
static getFrom(proto, sym) {
|
|
19
|
+
var _a;
|
|
20
|
+
return (_a = proto[sym]) !== null && _a !== void 0 ? _a : EMPTY_META;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
Meta.Handle = Symbol('rx-handle');
|
|
24
|
+
Meta.Revision = Symbol('rx-revision');
|
|
25
|
+
Meta.Controller = Symbol('rx-controller');
|
|
26
|
+
Meta.Initial = Symbol('rx-initial');
|
|
27
|
+
Meta.Reactions = Symbol('rx-reactions');
|
|
28
|
+
Meta.Nonreactive = Symbol('rx-nonreactive');
|
|
29
|
+
Meta.Undefined = Symbol('rx-undefined');
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Worker } from '../Worker';
|
|
2
|
+
import { ReactiveObject } from './Hooks';
|
|
3
|
+
export declare abstract class Monitor extends ReactiveObject {
|
|
4
|
+
abstract readonly isActive: boolean;
|
|
5
|
+
abstract readonly counter: number;
|
|
6
|
+
abstract readonly workers: ReadonlySet<Worker>;
|
|
7
|
+
abstract readonly duration: number;
|
|
8
|
+
static create(hint: string, activationDelay: number, deactivationDelay: number, durationResolution: number): Monitor;
|
|
9
|
+
}
|
|
10
|
+
export declare class MonitorImpl extends Monitor {
|
|
11
|
+
isActive: boolean;
|
|
12
|
+
counter: number;
|
|
13
|
+
workers: Set<Worker>;
|
|
14
|
+
duration: number;
|
|
15
|
+
internals: {
|
|
16
|
+
started: number;
|
|
17
|
+
activationDelay: number;
|
|
18
|
+
activationTimeout: undefined;
|
|
19
|
+
deactivationDelay: number;
|
|
20
|
+
deactivationTimeout: undefined;
|
|
21
|
+
durationResolution: number;
|
|
22
|
+
};
|
|
23
|
+
enter(worker: Worker): void;
|
|
24
|
+
leave(worker: Worker): void;
|
|
25
|
+
static create(hint: string, activationDelay: number, deactivationDelay: number, durationResolution: number): MonitorImpl;
|
|
26
|
+
static enter(mon: MonitorImpl, worker: Worker): void;
|
|
27
|
+
static leave(mon: MonitorImpl, worker: Worker): void;
|
|
28
|
+
private static doCreate;
|
|
29
|
+
private static activate;
|
|
30
|
+
private static deactivate;
|
|
31
|
+
private static tick;
|
|
32
|
+
}
|