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,30 @@
|
|
|
1
|
+
import { F } from '../util/Utils';
|
|
2
|
+
import { Worker } from '../Worker';
|
|
3
|
+
import { SnapshotOptions } from '../Options';
|
|
4
|
+
import { Changeset } from './Changeset';
|
|
5
|
+
export declare abstract class Transaction implements Worker {
|
|
6
|
+
static get current(): Transaction;
|
|
7
|
+
abstract readonly id: number;
|
|
8
|
+
abstract readonly hint: string;
|
|
9
|
+
abstract readonly options: SnapshotOptions;
|
|
10
|
+
abstract readonly timestamp: number;
|
|
11
|
+
abstract readonly error: Error | undefined;
|
|
12
|
+
abstract readonly changeset: Changeset;
|
|
13
|
+
abstract readonly margin: number;
|
|
14
|
+
abstract run<T>(func: F<T>, ...args: any[]): T;
|
|
15
|
+
abstract inspect<T>(func: F<T>, ...args: any[]): T;
|
|
16
|
+
abstract apply(): void;
|
|
17
|
+
abstract seal(): this;
|
|
18
|
+
abstract wrap<T>(func: F<T>, secondary: boolean): F<T>;
|
|
19
|
+
abstract cancel(error: Error, retryAfterOrIgnore?: Worker | null): this;
|
|
20
|
+
abstract readonly isCanceled: boolean;
|
|
21
|
+
abstract readonly isFinished: boolean;
|
|
22
|
+
whenFinished(): Promise<void>;
|
|
23
|
+
static create(options: SnapshotOptions | null): Transaction;
|
|
24
|
+
static run<T>(options: SnapshotOptions | null, func: F<T>, ...args: any[]): T;
|
|
25
|
+
static standalone<T>(func: F<T>, ...args: any[]): T;
|
|
26
|
+
static off<T>(func: F<T>, ...args: any[]): T;
|
|
27
|
+
static isFrameOver(everyN?: number, timeLimit?: number): boolean;
|
|
28
|
+
static requestNextFrame(sleepTime?: number): Promise<void>;
|
|
29
|
+
static get isCanceled(): boolean;
|
|
30
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { UNDEF, pause } from '../util/Utils';
|
|
11
|
+
import { Log, misuse, error, fatal } from '../util/Dbg';
|
|
12
|
+
import { Changeset, Dump } from './Changeset';
|
|
13
|
+
export class Transaction {
|
|
14
|
+
static get current() { return TransactionImpl.current; }
|
|
15
|
+
whenFinished() {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () { });
|
|
17
|
+
}
|
|
18
|
+
static create(options) { return new TransactionImpl(options); }
|
|
19
|
+
static run(options, func, ...args) { return TransactionImpl.run(options, func, ...args); }
|
|
20
|
+
static standalone(func, ...args) { return TransactionImpl.standalone(func, ...args); }
|
|
21
|
+
static off(func, ...args) { return TransactionImpl.off(func, ...args); }
|
|
22
|
+
static isFrameOver(everyN = 1, timeLimit = 10) { return TransactionImpl.isFrameOver(everyN, timeLimit); }
|
|
23
|
+
static requestNextFrame(sleepTime = 0) { return TransactionImpl.requestNextFrame(sleepTime); }
|
|
24
|
+
static get isCanceled() { return TransactionImpl.current.isCanceled; }
|
|
25
|
+
}
|
|
26
|
+
class TransactionImpl extends Transaction {
|
|
27
|
+
constructor(options) {
|
|
28
|
+
super();
|
|
29
|
+
this.margin = TransactionImpl.curr !== undefined ? TransactionImpl.curr.margin + 1 : -1;
|
|
30
|
+
this.changeset = new Changeset(options);
|
|
31
|
+
this.pending = 0;
|
|
32
|
+
this.sealed = false;
|
|
33
|
+
this.canceled = undefined;
|
|
34
|
+
this.after = undefined;
|
|
35
|
+
this.promise = undefined;
|
|
36
|
+
this.resolve = UNDEF;
|
|
37
|
+
this.reject = UNDEF;
|
|
38
|
+
}
|
|
39
|
+
static get current() { return TransactionImpl.curr; }
|
|
40
|
+
get id() { return this.changeset.id; }
|
|
41
|
+
get hint() { return this.changeset.hint; }
|
|
42
|
+
get options() { return this.changeset.options; }
|
|
43
|
+
get timestamp() { return this.changeset.timestamp; }
|
|
44
|
+
get error() { return this.canceled; }
|
|
45
|
+
run(func, ...args) {
|
|
46
|
+
this.guard();
|
|
47
|
+
return this.runImpl(undefined, func, ...args);
|
|
48
|
+
}
|
|
49
|
+
inspect(func, ...args) {
|
|
50
|
+
const restore = TransactionImpl.inspection;
|
|
51
|
+
try {
|
|
52
|
+
TransactionImpl.inspection = true;
|
|
53
|
+
if (Log.isOn && Log.opt.transaction)
|
|
54
|
+
Log.write(' ', ' ', `T${this.id}[${this.hint}] is being inspected by T${TransactionImpl.curr.id}[${TransactionImpl.curr.hint}]`);
|
|
55
|
+
return this.runImpl(undefined, func, ...args);
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
TransactionImpl.inspection = restore;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
apply() {
|
|
62
|
+
if (this.pending > 0)
|
|
63
|
+
throw misuse('cannot apply transaction having active operations running');
|
|
64
|
+
if (this.canceled)
|
|
65
|
+
throw misuse(`cannot apply transaction that is already canceled: ${this.canceled}`);
|
|
66
|
+
this.seal();
|
|
67
|
+
}
|
|
68
|
+
seal() {
|
|
69
|
+
if (!this.sealed)
|
|
70
|
+
this.run(TransactionImpl.seal, this, undefined, undefined);
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
wrap(func, error) {
|
|
74
|
+
this.guard();
|
|
75
|
+
const self = this;
|
|
76
|
+
const inspect = TransactionImpl.inspection;
|
|
77
|
+
if (!inspect)
|
|
78
|
+
self.run(TransactionImpl.wrapperEnter, self, error);
|
|
79
|
+
else
|
|
80
|
+
self.inspect(TransactionImpl.wrapperEnter, self, error);
|
|
81
|
+
const wrappedForTransaction = (...args) => {
|
|
82
|
+
if (!inspect)
|
|
83
|
+
return self.runImpl(undefined, TransactionImpl.wrapperLeave, self, error, func, ...args);
|
|
84
|
+
else
|
|
85
|
+
return self.inspect(TransactionImpl.wrapperLeave, self, error, func, ...args);
|
|
86
|
+
};
|
|
87
|
+
return wrappedForTransaction;
|
|
88
|
+
}
|
|
89
|
+
static wrapperEnter(t, error) {
|
|
90
|
+
if (!error)
|
|
91
|
+
t.pending++;
|
|
92
|
+
}
|
|
93
|
+
static wrapperLeave(t, error, func, ...args) {
|
|
94
|
+
t.pending--;
|
|
95
|
+
const result = func(...args);
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
cancel(error, restartAfter) {
|
|
99
|
+
this.runImpl(undefined, TransactionImpl.seal, this, error, restartAfter === null ? TransactionImpl.none : restartAfter);
|
|
100
|
+
return this;
|
|
101
|
+
}
|
|
102
|
+
get isCanceled() {
|
|
103
|
+
return this.canceled !== undefined;
|
|
104
|
+
}
|
|
105
|
+
get isFinished() {
|
|
106
|
+
return this.sealed && this.pending === 0;
|
|
107
|
+
}
|
|
108
|
+
whenFinished() {
|
|
109
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
110
|
+
if (!this.isFinished)
|
|
111
|
+
yield this.acquirePromise();
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
static run(options, func, ...args) {
|
|
115
|
+
const t = TransactionImpl.acquire(options);
|
|
116
|
+
const root = t !== TransactionImpl.curr;
|
|
117
|
+
t.guard();
|
|
118
|
+
let result = t.runImpl(options === null || options === void 0 ? void 0 : options.logging, func, ...args);
|
|
119
|
+
if (root) {
|
|
120
|
+
if (result instanceof Promise) {
|
|
121
|
+
result = TransactionImpl.off(() => {
|
|
122
|
+
return t.wrapToRetry(t.wrapToWaitUntilFinish(result), func, ...args);
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
t.seal();
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
static standalone(func, ...args) {
|
|
130
|
+
return TransactionImpl.run({ standalone: true }, func, ...args);
|
|
131
|
+
}
|
|
132
|
+
static off(func, ...args) {
|
|
133
|
+
const outer = TransactionImpl.curr;
|
|
134
|
+
try {
|
|
135
|
+
TransactionImpl.curr = TransactionImpl.none;
|
|
136
|
+
return func(...args);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
TransactionImpl.curr = outer;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
static isFrameOver(everyN = 1, timeLimit = 10) {
|
|
143
|
+
TransactionImpl.frameOverCounter++;
|
|
144
|
+
let result = TransactionImpl.frameOverCounter % everyN === 0;
|
|
145
|
+
if (result) {
|
|
146
|
+
const ms = performance.now() - TransactionImpl.frameStartTime;
|
|
147
|
+
result = ms > timeLimit;
|
|
148
|
+
}
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
static requestNextFrame(sleepTime = 0) {
|
|
152
|
+
return pause(sleepTime);
|
|
153
|
+
}
|
|
154
|
+
static acquire(options) {
|
|
155
|
+
const curr = TransactionImpl.curr;
|
|
156
|
+
if ((options === null || options === void 0 ? void 0 : options.standalone) || curr.isFinished || curr.options.standalone === 'isolated')
|
|
157
|
+
return new TransactionImpl(options);
|
|
158
|
+
else
|
|
159
|
+
return TransactionImpl.curr;
|
|
160
|
+
}
|
|
161
|
+
guard() {
|
|
162
|
+
if (this.sealed && TransactionImpl.curr !== this)
|
|
163
|
+
throw misuse('cannot run transaction that is already sealed');
|
|
164
|
+
}
|
|
165
|
+
wrapToRetry(p, func, ...args) {
|
|
166
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
167
|
+
try {
|
|
168
|
+
const result = yield p;
|
|
169
|
+
if (this.canceled)
|
|
170
|
+
throw this.canceled;
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
if (this.after !== TransactionImpl.none) {
|
|
175
|
+
if (this.after) {
|
|
176
|
+
yield this.after.whenFinished();
|
|
177
|
+
const options = {
|
|
178
|
+
hint: `${this.hint} - restart after T${this.after.id}`,
|
|
179
|
+
standalone: this.options.standalone === 'isolated' ? 'isolated' : true,
|
|
180
|
+
logging: this.changeset.options.logging,
|
|
181
|
+
token: this.changeset.options.token,
|
|
182
|
+
};
|
|
183
|
+
return TransactionImpl.run(options, func, ...args);
|
|
184
|
+
}
|
|
185
|
+
else
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
else
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
wrapToWaitUntilFinish(p) {
|
|
194
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
195
|
+
const result = yield p;
|
|
196
|
+
yield this.whenFinished();
|
|
197
|
+
return result;
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
runImpl(logging, func, ...args) {
|
|
201
|
+
let result;
|
|
202
|
+
const outer = TransactionImpl.curr;
|
|
203
|
+
try {
|
|
204
|
+
if (outer === TransactionImpl.none) {
|
|
205
|
+
TransactionImpl.frameStartTime = performance.now();
|
|
206
|
+
TransactionImpl.frameOverCounter = 0;
|
|
207
|
+
}
|
|
208
|
+
TransactionImpl.curr = this;
|
|
209
|
+
this.pending++;
|
|
210
|
+
this.changeset.acquire(outer.changeset);
|
|
211
|
+
result = func(...args);
|
|
212
|
+
if (this.sealed && this.pending === 1) {
|
|
213
|
+
if (!this.canceled)
|
|
214
|
+
this.checkForConflicts();
|
|
215
|
+
else if (!this.after)
|
|
216
|
+
throw this.canceled;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
catch (e) {
|
|
220
|
+
if (!TransactionImpl.inspection)
|
|
221
|
+
this.cancel(e);
|
|
222
|
+
throw e;
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
this.pending--;
|
|
226
|
+
if (this.sealed && this.pending === 0) {
|
|
227
|
+
const reactions = this.applyOrDiscard();
|
|
228
|
+
TransactionImpl.curr = outer;
|
|
229
|
+
TransactionImpl.off(Changeset.enqueueReactionsToRun, reactions);
|
|
230
|
+
}
|
|
231
|
+
else
|
|
232
|
+
TransactionImpl.curr = outer;
|
|
233
|
+
}
|
|
234
|
+
return result;
|
|
235
|
+
}
|
|
236
|
+
static seal(t, error, after) {
|
|
237
|
+
if (!t.canceled && error) {
|
|
238
|
+
t.canceled = error;
|
|
239
|
+
t.after = after;
|
|
240
|
+
if (Log.isOn && Log.opt.transaction) {
|
|
241
|
+
Log.write('║', ' [!]', `${error.message}`, undefined, ' *** CANCEL ***');
|
|
242
|
+
if (after && after !== TransactionImpl.none)
|
|
243
|
+
Log.write('║', ' [!]', `T${t.id}[${t.hint}] will be restarted${t !== after ? ` after T${after.id}[${after.hint}]` : ''}`);
|
|
244
|
+
}
|
|
245
|
+
Changeset.revokeAllSubscriptions(t.changeset);
|
|
246
|
+
}
|
|
247
|
+
t.sealed = true;
|
|
248
|
+
}
|
|
249
|
+
checkForConflicts() {
|
|
250
|
+
const conflicts = this.changeset.rebase();
|
|
251
|
+
if (conflicts)
|
|
252
|
+
this.tryResolveConflicts(conflicts);
|
|
253
|
+
}
|
|
254
|
+
tryResolveConflicts(conflicts) {
|
|
255
|
+
throw error(`T${this.id}[${this.hint}] conflicts with: ${Dump.conflicts(conflicts)}`, undefined);
|
|
256
|
+
}
|
|
257
|
+
applyOrDiscard() {
|
|
258
|
+
let reactions;
|
|
259
|
+
try {
|
|
260
|
+
if (Log.isOn && Log.opt.change)
|
|
261
|
+
Log.write('╠═', '', '', undefined, 'changes');
|
|
262
|
+
reactions = this.changeset.applyOrDiscard(this.canceled);
|
|
263
|
+
this.changeset.triggerGarbageCollection();
|
|
264
|
+
if (this.promise) {
|
|
265
|
+
if (this.canceled && !this.after)
|
|
266
|
+
this.reject(this.canceled);
|
|
267
|
+
else
|
|
268
|
+
this.resolve();
|
|
269
|
+
}
|
|
270
|
+
if (Log.isOn)
|
|
271
|
+
Object.freeze(this);
|
|
272
|
+
}
|
|
273
|
+
catch (e) {
|
|
274
|
+
fatal(e);
|
|
275
|
+
throw e;
|
|
276
|
+
}
|
|
277
|
+
return reactions;
|
|
278
|
+
}
|
|
279
|
+
acquirePromise() {
|
|
280
|
+
if (!this.promise) {
|
|
281
|
+
this.promise = new Promise((resolve, reject) => {
|
|
282
|
+
this.resolve = resolve;
|
|
283
|
+
this.reject = reject;
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
return this.promise;
|
|
287
|
+
}
|
|
288
|
+
static getCurrentChangeset() {
|
|
289
|
+
return TransactionImpl.curr.changeset;
|
|
290
|
+
}
|
|
291
|
+
static getEditableChangeset() {
|
|
292
|
+
if (TransactionImpl.inspection)
|
|
293
|
+
throw misuse('cannot make changes during transaction inspection');
|
|
294
|
+
return TransactionImpl.curr.changeset;
|
|
295
|
+
}
|
|
296
|
+
static _init() {
|
|
297
|
+
Changeset.current = TransactionImpl.getCurrentChangeset;
|
|
298
|
+
Changeset.edit = TransactionImpl.getEditableChangeset;
|
|
299
|
+
TransactionImpl.none.sealed = true;
|
|
300
|
+
TransactionImpl.none.changeset.applyOrDiscard();
|
|
301
|
+
Changeset._init();
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
TransactionImpl.none = new TransactionImpl({ hint: 'Transaction.off' });
|
|
305
|
+
TransactionImpl.curr = TransactionImpl.none;
|
|
306
|
+
TransactionImpl.inspection = false;
|
|
307
|
+
TransactionImpl.frameStartTime = 0;
|
|
308
|
+
TransactionImpl.frameOverCounter = 0;
|
|
309
|
+
TransactionImpl._init();
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LoggingOptions } from '../Logging';
|
|
2
|
+
export declare function error(message: string, dump: Error | undefined): Error;
|
|
3
|
+
export declare function misuse(message: string, dump?: any): Error;
|
|
4
|
+
export declare function fatal(error: Error): Error;
|
|
5
|
+
export declare class Log {
|
|
6
|
+
static DefaultLevel: LoggingOptions;
|
|
7
|
+
static isOn: boolean;
|
|
8
|
+
static global: LoggingOptions;
|
|
9
|
+
static get opt(): LoggingOptions;
|
|
10
|
+
static getMergedLoggingOptions: (local: Partial<LoggingOptions> | undefined) => LoggingOptions;
|
|
11
|
+
static setMode(isOn: boolean, options?: LoggingOptions): void;
|
|
12
|
+
static write(bar: string, tran: string, message: string, ms?: number, highlight?: string | undefined, dump?: any): void;
|
|
13
|
+
static writeAs(options: Partial<LoggingOptions> | undefined, bar: string, tran: string, message: string, ms?: number, highlight?: string | undefined, dump?: any): void;
|
|
14
|
+
static merge(t: Partial<LoggingOptions> | undefined, color: number | undefined, prefix: string | undefined, existing: LoggingOptions): LoggingOptions;
|
|
15
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export function error(message, dump) {
|
|
2
|
+
if (Log.isOn && Log.opt.error)
|
|
3
|
+
Log.write('█', ' ███', message, undefined, ' *** ERROR ***', dump);
|
|
4
|
+
return new Error(message);
|
|
5
|
+
}
|
|
6
|
+
export function misuse(message, dump) {
|
|
7
|
+
const error = new Error(message);
|
|
8
|
+
Log.write(' ', ' ███', message, undefined, ' *** ERROR / MISUSE ***', dump !== null && dump !== void 0 ? dump : error);
|
|
9
|
+
return error;
|
|
10
|
+
}
|
|
11
|
+
export function fatal(error) {
|
|
12
|
+
Log.write(' ', ' ███', error.message, undefined, ' *** FATAL ***', error);
|
|
13
|
+
return error;
|
|
14
|
+
}
|
|
15
|
+
export class Log {
|
|
16
|
+
static get opt() { return this.getMergedLoggingOptions(undefined); }
|
|
17
|
+
static setMode(isOn, options) {
|
|
18
|
+
Log.global = options || Log.DefaultLevel;
|
|
19
|
+
if (isOn) {
|
|
20
|
+
const t = Log.global;
|
|
21
|
+
const o = Object.keys(Log.global).filter(x => t[x] === true).join(', ');
|
|
22
|
+
Log.write('', '', `Reactronic logging is turned on: ${o}`);
|
|
23
|
+
Log.write('', '', 'Member-level logging can be configured with @options({ logging: ... }) decorator');
|
|
24
|
+
}
|
|
25
|
+
else if (Log.isOn)
|
|
26
|
+
Log.write('', '', 'Reactronic logging is turned off');
|
|
27
|
+
Log.isOn = isOn;
|
|
28
|
+
}
|
|
29
|
+
static write(bar, tran, message, ms = 0, highlight = undefined, dump) {
|
|
30
|
+
Log.writeAs(undefined, bar, tran, message, ms, highlight, dump);
|
|
31
|
+
}
|
|
32
|
+
static writeAs(options, bar, tran, message, ms = 0, highlight = undefined, dump) {
|
|
33
|
+
const t = Log.getMergedLoggingOptions(options);
|
|
34
|
+
const margin1 = ' '.repeat(t.margin1 >= 0 ? t.margin1 : 0);
|
|
35
|
+
const margin2 = ' '.repeat(t.margin2);
|
|
36
|
+
const enabled = (options && options.enabled !== undefined) ? options.enabled : t.enabled;
|
|
37
|
+
if (enabled) {
|
|
38
|
+
console.log('\x1b[37m%s\x1b[0m \x1b[' + t.color + 'm%s %s%s\x1b[0m \x1b[' + t.color + 'm%s%s\x1b[0m \x1b[' + t.color + 'm%s\x1b[0m%s', '', t.prefix, t.transaction ? margin1 : '', t.transaction ? bar : bar.replace(/./g, ' '), margin2, tran, message, (highlight !== undefined ? `${highlight}` : '') + (ms > 2 ? ` [ ${ms}ms ]` : ''));
|
|
39
|
+
if (dump)
|
|
40
|
+
console.log(dump);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
static merge(t, color, prefix, existing) {
|
|
44
|
+
const result = !t ? Object.assign({}, existing) : {
|
|
45
|
+
enabled: t.enabled !== undefined ? t.enabled : existing.enabled,
|
|
46
|
+
transaction: t.transaction !== undefined ? t.transaction : existing.transaction,
|
|
47
|
+
operation: t.operation !== undefined ? t.operation : existing.operation,
|
|
48
|
+
step: t.step !== undefined ? t.step : existing.step,
|
|
49
|
+
monitor: t.monitor !== undefined ? t.monitor : existing.monitor,
|
|
50
|
+
read: t.read !== undefined ? t.read : existing.read,
|
|
51
|
+
write: t.write !== undefined ? t.write : existing.write,
|
|
52
|
+
change: t.change !== undefined ? t.change : existing.change,
|
|
53
|
+
obsolete: t.obsolete !== undefined ? t.obsolete : existing.obsolete,
|
|
54
|
+
error: t.error !== undefined ? t.error : existing.error,
|
|
55
|
+
warning: t.warning !== undefined ? t.warning : existing.warning,
|
|
56
|
+
gc: t.gc !== undefined ? t.gc : existing.gc,
|
|
57
|
+
color: t.color !== undefined ? t.color : existing.color,
|
|
58
|
+
prefix: t.prefix !== undefined ? t.prefix : existing.prefix,
|
|
59
|
+
margin1: t.margin1 !== undefined ? t.margin1 : existing.margin1,
|
|
60
|
+
margin2: t.margin2 !== undefined ? t.margin2 : existing.margin2,
|
|
61
|
+
};
|
|
62
|
+
if (color !== undefined)
|
|
63
|
+
result.color = color;
|
|
64
|
+
if (prefix !== undefined)
|
|
65
|
+
result.prefix = prefix;
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
Log.DefaultLevel = {
|
|
70
|
+
enabled: true,
|
|
71
|
+
error: false,
|
|
72
|
+
warning: false,
|
|
73
|
+
transaction: false,
|
|
74
|
+
operation: false,
|
|
75
|
+
step: false,
|
|
76
|
+
monitor: false,
|
|
77
|
+
read: false,
|
|
78
|
+
write: false,
|
|
79
|
+
change: false,
|
|
80
|
+
obsolete: false,
|
|
81
|
+
gc: false,
|
|
82
|
+
color: 37,
|
|
83
|
+
prefix: '',
|
|
84
|
+
margin1: 0,
|
|
85
|
+
margin2: 0,
|
|
86
|
+
};
|
|
87
|
+
Log.isOn = false;
|
|
88
|
+
Log.global = Log.DefaultLevel;
|
|
89
|
+
Log.getMergedLoggingOptions = (local) => Log.global;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface Sealable<T> {
|
|
2
|
+
toMutable(): T;
|
|
3
|
+
[Sealant.SealedType]: object;
|
|
4
|
+
}
|
|
5
|
+
export interface Sealed<T> {
|
|
6
|
+
[Sealant.CreateCopy]?: () => T;
|
|
7
|
+
}
|
|
8
|
+
export declare abstract class Sealant {
|
|
9
|
+
static readonly SealedType: unique symbol;
|
|
10
|
+
static readonly CreateCopy: unique symbol;
|
|
11
|
+
static seal<T extends Sealable<T>>(collection: T, sealedType: object, typeName: string, member: any): T;
|
|
12
|
+
static toMutable<T extends Sealable<T>>(collection: T): T;
|
|
13
|
+
static error(collection: Sealed<any>): Error;
|
|
14
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { Log, misuse } from './Dbg';
|
|
2
|
+
export class Sealant {
|
|
3
|
+
static seal(collection, sealedType, typeName, member) {
|
|
4
|
+
let result = collection;
|
|
5
|
+
const createCopy = result[Sealant.CreateCopy];
|
|
6
|
+
if (createCopy)
|
|
7
|
+
result = createCopy.call(result);
|
|
8
|
+
if (Log.isOn && Log.opt.write)
|
|
9
|
+
Log.write('║', ' ', `${typeName}.${member.toString()} - collection is sealed`);
|
|
10
|
+
Object.setPrototypeOf(result, sealedType);
|
|
11
|
+
Object.freeze(result);
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
static toMutable(collection) {
|
|
15
|
+
const a = collection;
|
|
16
|
+
const createCopy = a[Sealant.CreateCopy];
|
|
17
|
+
if (createCopy)
|
|
18
|
+
collection = createCopy.call(collection);
|
|
19
|
+
return collection;
|
|
20
|
+
}
|
|
21
|
+
static error(collection) {
|
|
22
|
+
return misuse('use toMutable to create mutable copy of sealed collection');
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
Sealant.SealedType = Symbol('rxSealedType');
|
|
26
|
+
Sealant.CreateCopy = Symbol('rxCreateCopy');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Sealant, Sealed } from './Sealant';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Array<T> {
|
|
4
|
+
toMutable(): Array<T>;
|
|
5
|
+
[Sealant.SealedType]: object;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare abstract class SealedArray<T> extends Array<T> implements Sealed<Array<T>> {
|
|
9
|
+
pop(): T | undefined;
|
|
10
|
+
push(...items: T[]): number;
|
|
11
|
+
sort(compareFn?: (a: T, b: T) => number): this;
|
|
12
|
+
splice(start: number, deleteCount?: number): T[];
|
|
13
|
+
unshift(...items: T[]): number;
|
|
14
|
+
[Sealant.CreateCopy](): Array<T>;
|
|
15
|
+
slice(start?: number, end?: number): T[];
|
|
16
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { Sealant } from './Sealant';
|
|
2
|
+
export class SealedArray extends Array {
|
|
3
|
+
pop() { throw Sealant.error(this); }
|
|
4
|
+
push(...items) { throw Sealant.error(this); }
|
|
5
|
+
sort(compareFn) { throw Sealant.error(this); }
|
|
6
|
+
splice(start, deleteCount, ...items) { throw Sealant.error(this); }
|
|
7
|
+
unshift(...items) { throw Sealant.error(this); }
|
|
8
|
+
[Sealant.CreateCopy]() { return this.slice(); }
|
|
9
|
+
slice(start, end) {
|
|
10
|
+
const result = super.slice(start, end);
|
|
11
|
+
Object.setPrototypeOf(result, Array.prototype);
|
|
12
|
+
return result;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(Array.prototype, 'toMutable', {
|
|
16
|
+
configurable: false, enumerable: false,
|
|
17
|
+
value() {
|
|
18
|
+
return Sealant.toMutable(this);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
Object.defineProperty(Array.prototype, Sealant.SealedType, {
|
|
22
|
+
value: SealedArray.prototype,
|
|
23
|
+
configurable: false, enumerable: false, writable: false,
|
|
24
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Sealant, Sealed } from './Sealant';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Map<K, V> {
|
|
4
|
+
toMutable(): Map<K, V>;
|
|
5
|
+
[Sealant.SealedType]: object;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare abstract class SealedMap<K, V> extends Map<K, V> implements Sealed<Map<K, V>> {
|
|
9
|
+
clear(): void;
|
|
10
|
+
delete(key: K): boolean;
|
|
11
|
+
set(key: K, value: V): this;
|
|
12
|
+
[Sealant.CreateCopy](): Map<K, V>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Sealant } from './Sealant';
|
|
2
|
+
export class SealedMap extends Map {
|
|
3
|
+
clear() { throw Sealant.error(this); }
|
|
4
|
+
delete(key) { throw Sealant.error(this); }
|
|
5
|
+
set(key, value) { throw Sealant.error(this); }
|
|
6
|
+
[Sealant.CreateCopy]() { return new Map(this.entries()); }
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(Map.prototype, 'toMutable', {
|
|
9
|
+
configurable: false, enumerable: false,
|
|
10
|
+
value() {
|
|
11
|
+
return Sealant.toMutable(this);
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(Map.prototype, Sealant.SealedType, {
|
|
15
|
+
value: SealedMap.prototype,
|
|
16
|
+
configurable: false, enumerable: false, writable: false,
|
|
17
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Sealant, Sealed } from './Sealant';
|
|
2
|
+
declare global {
|
|
3
|
+
interface Set<T> {
|
|
4
|
+
toMutable(): Set<T>;
|
|
5
|
+
[Sealant.SealedType]: object;
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export declare abstract class SealedSet<T> extends Set<T> implements Sealed<Set<T>> {
|
|
9
|
+
add(value: T): this;
|
|
10
|
+
clear(): void;
|
|
11
|
+
delete(value: T): boolean;
|
|
12
|
+
[Sealant.CreateCopy](): Set<T>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Sealant } from './Sealant';
|
|
2
|
+
export class SealedSet extends Set {
|
|
3
|
+
add(value) { throw Sealant.error(this); }
|
|
4
|
+
clear() { throw Sealant.error(this); }
|
|
5
|
+
delete(value) { throw Sealant.error(this); }
|
|
6
|
+
[Sealant.CreateCopy]() { return new Set(this.values()); }
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(Set.prototype, 'toMutable', {
|
|
9
|
+
configurable: false, enumerable: false,
|
|
10
|
+
value() {
|
|
11
|
+
return Sealant.toMutable(this);
|
|
12
|
+
},
|
|
13
|
+
});
|
|
14
|
+
Object.defineProperty(Set.prototype, Sealant.SealedType, {
|
|
15
|
+
value: SealedSet.prototype,
|
|
16
|
+
configurable: false, enumerable: false, writable: false,
|
|
17
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare type F<T> = (...args: any[]) => T;
|
|
2
|
+
export declare class Utils {
|
|
3
|
+
static freezeSet<T>(obj?: Set<T>): Set<T> | undefined;
|
|
4
|
+
static freezeMap<K, V>(obj?: Map<K, V>): Map<K, V> | undefined;
|
|
5
|
+
static copyAllMembers(source: any, target: any): any;
|
|
6
|
+
}
|
|
7
|
+
export declare function UNDEF(...args: any[]): never;
|
|
8
|
+
export declare function all(promises: Array<Promise<any>>): Promise<any[]>;
|
|
9
|
+
export declare function pause<T>(timeout: number): Promise<T>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export class Utils {
|
|
11
|
+
static freezeSet(obj) {
|
|
12
|
+
if (obj instanceof Set) {
|
|
13
|
+
const pd = { configurable: false, enumerable: false, get: UNDEF, set: UNDEF };
|
|
14
|
+
Object.defineProperty(obj, 'add', pd);
|
|
15
|
+
Object.defineProperty(obj, 'delete', pd);
|
|
16
|
+
Object.defineProperty(obj, 'clear', pd);
|
|
17
|
+
Object.freeze(obj);
|
|
18
|
+
}
|
|
19
|
+
return obj;
|
|
20
|
+
}
|
|
21
|
+
static freezeMap(obj) {
|
|
22
|
+
if (obj instanceof Map) {
|
|
23
|
+
const pd = { configurable: false, enumerable: false, get: UNDEF, set: UNDEF };
|
|
24
|
+
Object.defineProperty(obj, 'set', pd);
|
|
25
|
+
Object.defineProperty(obj, 'delete', pd);
|
|
26
|
+
Object.defineProperty(obj, 'clear', pd);
|
|
27
|
+
Object.freeze(obj);
|
|
28
|
+
}
|
|
29
|
+
return obj;
|
|
30
|
+
}
|
|
31
|
+
static copyAllMembers(source, target) {
|
|
32
|
+
for (const m of Object.getOwnPropertyNames(source))
|
|
33
|
+
target[m] = source[m];
|
|
34
|
+
for (const m of Object.getOwnPropertySymbols(source))
|
|
35
|
+
target[m] = source[m];
|
|
36
|
+
return target;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export function UNDEF(...args) {
|
|
40
|
+
throw new Error('this method should never be called');
|
|
41
|
+
}
|
|
42
|
+
export function all(promises) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
let error;
|
|
45
|
+
const result = yield Promise.all(promises.map(x => x.catch(e => { error = error || e; return e; })));
|
|
46
|
+
if (error)
|
|
47
|
+
throw error;
|
|
48
|
+
return result;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
export function pause(timeout) {
|
|
52
|
+
return new Promise(function (resolve) {
|
|
53
|
+
setTimeout(resolve.bind(null, () => resolve), timeout);
|
|
54
|
+
});
|
|
55
|
+
}
|