reactronic 0.22.310 → 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/build/dist/source/Buffer.js +2 -6
- package/build/dist/source/Controller.js +1 -5
- package/build/dist/source/Logging.js +1 -4
- package/build/dist/source/Options.js +5 -9
- package/build/dist/source/Ref.js +6 -11
- package/build/dist/source/Rx.js +46 -57
- package/build/dist/source/Worker.js +1 -2
- package/build/dist/source/api.js +13 -42
- package/build/dist/source/impl/Changeset.js +87 -92
- package/build/dist/source/impl/Data.js +10 -17
- package/build/dist/source/impl/Hooks.js +54 -62
- package/build/dist/source/impl/Journal.js +22 -27
- package/build/dist/source/impl/Meta.js +1 -5
- package/build/dist/source/impl/Monitor.js +9 -14
- package/build/dist/source/impl/Operation.js +150 -156
- package/build/dist/source/impl/Transaction.js +27 -31
- package/build/dist/source/util/Dbg.js +4 -11
- package/build/dist/source/util/Sealant.js +5 -9
- package/build/dist/source/util/SealedArray.js +10 -14
- package/build/dist/source/util/SealedMap.js +8 -12
- package/build/dist/source/util/SealedSet.js +8 -12
- package/build/dist/source/util/Utils.js +4 -11
- package/package.json +5 -5
|
@@ -1,19 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const Monitor_1 = require("./Monitor");
|
|
11
|
-
const Hooks_1 = require("./Hooks");
|
|
12
|
-
const Journal_1 = require("./Journal");
|
|
1
|
+
import { Log, misuse } from '../util/Dbg';
|
|
2
|
+
import { Kind, Reentrance } from '../Options';
|
|
3
|
+
import { Controller } from '../Controller';
|
|
4
|
+
import { ObjectHandle, Subscription, Meta } from './Data';
|
|
5
|
+
import { Changeset, Dump, EMPTY_SNAPSHOT, MAX_REVISION } from './Changeset';
|
|
6
|
+
import { Transaction } from './Transaction';
|
|
7
|
+
import { MonitorImpl } from './Monitor';
|
|
8
|
+
import { Hooks, OptionsImpl } from './Hooks';
|
|
9
|
+
import { JournalImpl } from './Journal';
|
|
13
10
|
const BOOT_ARGS = [];
|
|
14
11
|
const BOOT_CAUSE = '<boot>';
|
|
15
|
-
const EMPTY_HANDLE = new
|
|
16
|
-
class OperationController extends
|
|
12
|
+
const EMPTY_HANDLE = new ObjectHandle(undefined, undefined, Hooks.handler, EMPTY_SNAPSHOT, '<empty>');
|
|
13
|
+
export class OperationController extends Controller {
|
|
17
14
|
constructor(h, m) {
|
|
18
15
|
super();
|
|
19
16
|
this.objectHandle = h;
|
|
@@ -27,7 +24,7 @@ class OperationController extends Controller_1.Controller {
|
|
|
27
24
|
get error() { return this.use().operation.error; }
|
|
28
25
|
get stamp() { return this.use().snapshot.changeset.timestamp; }
|
|
29
26
|
get isUpToDate() { return this.use().isUpToDate; }
|
|
30
|
-
markObsolete() {
|
|
27
|
+
markObsolete() { Transaction.run({ hint: Log.isOn ? `markObsolete(${Dump.obj(this.objectHandle, this.memberName)})` : 'markObsolete()' }, OperationController.markObsolete, this); }
|
|
31
28
|
pullLastResult(args) { return this.useOrRun(true, args).content; }
|
|
32
29
|
useOrRun(weak, args) {
|
|
33
30
|
var _a;
|
|
@@ -39,27 +36,27 @@ class OperationController extends Controller_1.Controller {
|
|
|
39
36
|
&& (!weak || op.cause === BOOT_CAUSE || !op.successor ||
|
|
40
37
|
op.successor.transaction.isFinished)) {
|
|
41
38
|
const outerOpts = (_a = Operation.current) === null || _a === void 0 ? void 0 : _a.options;
|
|
42
|
-
const standalone = weak || opts.standalone || opts.kind ===
|
|
43
|
-
(opts.kind ===
|
|
44
|
-
(opts.kind ===
|
|
45
|
-
oc.snapshot.former.snapshot !==
|
|
39
|
+
const standalone = weak || opts.standalone || opts.kind === Kind.Reaction ||
|
|
40
|
+
(opts.kind === Kind.Transaction && outerOpts && (outerOpts.noSideEffects || outerOpts.kind === Kind.Cache)) ||
|
|
41
|
+
(opts.kind === Kind.Cache && (oc.snapshot.changeset.sealed ||
|
|
42
|
+
oc.snapshot.former.snapshot !== EMPTY_SNAPSHOT));
|
|
46
43
|
const token = opts.noSideEffects ? this : undefined;
|
|
47
44
|
const oc2 = this.run(oc, standalone, opts, token, args);
|
|
48
45
|
const ctx2 = oc2.operation.changeset;
|
|
49
46
|
if (!weak || ctx === ctx2 || (ctx2.sealed && ctx.timestamp >= ctx2.timestamp))
|
|
50
47
|
oc = oc2;
|
|
51
48
|
}
|
|
52
|
-
else if (
|
|
49
|
+
else if (Log.isOn && Log.opt.operation && (opts.logging === undefined ||
|
|
53
50
|
opts.logging.operation === undefined || opts.logging.operation === true))
|
|
54
|
-
|
|
51
|
+
Log.write(Transaction.current.isFinished ? '' : '║', ' (=)', `${Dump.snapshot2(oc.operation.controller.objectHandle, oc.changeset, this.memberName)} result is reused from T${oc.operation.transaction.id}[${oc.operation.transaction.hint}]`);
|
|
55
52
|
const t = oc.operation;
|
|
56
|
-
|
|
53
|
+
Changeset.markUsed(t, oc.snapshot, this.memberName, this.objectHandle, t.options.kind, weak);
|
|
57
54
|
return t;
|
|
58
55
|
}
|
|
59
56
|
static of(method) {
|
|
60
|
-
const ctl =
|
|
57
|
+
const ctl = Meta.get(method, Meta.Controller);
|
|
61
58
|
if (!ctl)
|
|
62
|
-
throw
|
|
59
|
+
throw misuse(`given method is not decorated as reactronic one: ${method.name}`);
|
|
63
60
|
return ctl;
|
|
64
61
|
}
|
|
65
62
|
static configureImpl(self, options) {
|
|
@@ -69,10 +66,10 @@ class OperationController extends Controller_1.Controller {
|
|
|
69
66
|
else
|
|
70
67
|
op = Operation.current;
|
|
71
68
|
if (!op)
|
|
72
|
-
throw
|
|
73
|
-
op.options = new
|
|
74
|
-
if (
|
|
75
|
-
|
|
69
|
+
throw misuse('reactronic decorator is only applicable to methods');
|
|
70
|
+
op.options = new OptionsImpl(op.options.getter, op.options.setter, op.options, options, false);
|
|
71
|
+
if (Log.isOn && Log.opt.write)
|
|
72
|
+
Log.write('║', ' ✎', `${op.hint()}.options are changed`);
|
|
76
73
|
return op.options;
|
|
77
74
|
}
|
|
78
75
|
static runWithin(op, func, ...args) {
|
|
@@ -105,10 +102,10 @@ class OperationController extends Controller_1.Controller {
|
|
|
105
102
|
return op ? op.dependencies() : ['Rx.dependencies should be called from inside of reactive method'];
|
|
106
103
|
}
|
|
107
104
|
peek(args) {
|
|
108
|
-
const ctx =
|
|
105
|
+
const ctx = Changeset.current();
|
|
109
106
|
const os = ctx.seekSnapshot(this.objectHandle, this.memberName);
|
|
110
107
|
const op = this.acquireFromSnapshot(os, args);
|
|
111
|
-
const isValid = op.options.kind !==
|
|
108
|
+
const isValid = op.options.kind !== Kind.Transaction && op.cause !== BOOT_CAUSE &&
|
|
112
109
|
(ctx === op.changeset || ctx.timestamp < op.obsoleteSince) &&
|
|
113
110
|
(!op.options.triggeringArgs || args === undefined ||
|
|
114
111
|
op.args.length === args.length && op.args.every((t, i) => t === args[i])) || os.disposed;
|
|
@@ -116,20 +113,20 @@ class OperationController extends Controller_1.Controller {
|
|
|
116
113
|
}
|
|
117
114
|
use() {
|
|
118
115
|
const oc = this.peek(undefined);
|
|
119
|
-
|
|
116
|
+
Changeset.markUsed(oc.operation, oc.snapshot, this.memberName, this.objectHandle, oc.operation.options.kind, true);
|
|
120
117
|
return oc;
|
|
121
118
|
}
|
|
122
119
|
edit() {
|
|
123
120
|
const h = this.objectHandle;
|
|
124
121
|
const m = this.memberName;
|
|
125
|
-
const ctx =
|
|
126
|
-
const os = ctx.getEditableSnapshot(h, m,
|
|
122
|
+
const ctx = Changeset.edit();
|
|
123
|
+
const os = ctx.getEditableSnapshot(h, m, Meta.Handle, this);
|
|
127
124
|
let op = this.acquireFromSnapshot(os, undefined);
|
|
128
125
|
if (op.changeset !== os.changeset) {
|
|
129
126
|
const op2 = new Operation(this, os.changeset, op);
|
|
130
127
|
os.data[m] = op2.reenterOver(op);
|
|
131
128
|
ctx.bumpBy(os.former.snapshot.changeset.timestamp);
|
|
132
|
-
|
|
129
|
+
Changeset.markEdited(op, op2, true, os, m, h);
|
|
133
130
|
op = op2;
|
|
134
131
|
}
|
|
135
132
|
return { operation: op, isUpToDate: true, changeset: ctx, snapshot: os };
|
|
@@ -138,21 +135,21 @@ class OperationController extends Controller_1.Controller {
|
|
|
138
135
|
const m = this.memberName;
|
|
139
136
|
let op = os.data[m];
|
|
140
137
|
if (op.controller !== this) {
|
|
141
|
-
if (os.changeset !==
|
|
142
|
-
const hint =
|
|
143
|
-
const standalone = os.changeset.sealed || os.former.snapshot !==
|
|
144
|
-
op =
|
|
138
|
+
if (os.changeset !== EMPTY_SNAPSHOT.changeset) {
|
|
139
|
+
const hint = Log.isOn ? `${Dump.obj(this.objectHandle, m)}/boot` : 'MethodController/init';
|
|
140
|
+
const standalone = os.changeset.sealed || os.former.snapshot !== EMPTY_SNAPSHOT;
|
|
141
|
+
op = Transaction.run({ hint, standalone, token: this }, () => {
|
|
145
142
|
const h = this.objectHandle;
|
|
146
|
-
let r2 =
|
|
143
|
+
let r2 = Changeset.current().getRelevantSnapshot(h, m);
|
|
147
144
|
let op2 = r2.data[m];
|
|
148
145
|
if (op2.controller !== this) {
|
|
149
|
-
r2 =
|
|
146
|
+
r2 = Changeset.edit().getEditableSnapshot(h, m, Meta.Handle, this);
|
|
150
147
|
const t = new Operation(this, r2.changeset, op2);
|
|
151
148
|
if (args)
|
|
152
149
|
t.args = args;
|
|
153
150
|
t.cause = BOOT_CAUSE;
|
|
154
151
|
r2.data[m] = t;
|
|
155
|
-
|
|
152
|
+
Changeset.markEdited(op2, t, true, r2, m, h);
|
|
156
153
|
op2 = t;
|
|
157
154
|
}
|
|
158
155
|
return op2;
|
|
@@ -165,29 +162,29 @@ class OperationController extends Controller_1.Controller {
|
|
|
165
162
|
t.cause = BOOT_CAUSE;
|
|
166
163
|
os.data[m] = t;
|
|
167
164
|
op = t;
|
|
168
|
-
if (
|
|
169
|
-
|
|
165
|
+
if (Log.isOn && Log.opt.write)
|
|
166
|
+
Log.write('║', ' ⎘⎘', `${Dump.obj(this.objectHandle, m)} - new snapshot is created outside of transaction (revision ${os.revision})`);
|
|
170
167
|
}
|
|
171
168
|
}
|
|
172
169
|
return op;
|
|
173
170
|
}
|
|
174
171
|
run(existing, standalone, options, token, args) {
|
|
175
|
-
const hint =
|
|
172
|
+
const hint = Log.isOn ? `${Dump.obj(this.objectHandle, this.memberName)}${args && args.length > 0 && (typeof args[0] === 'number' || typeof args[0] === 'string') ? ` - ${args[0]}` : ''}` : `${Dump.obj(this.objectHandle, this.memberName)}`;
|
|
176
173
|
let oc = existing;
|
|
177
174
|
const opts = { hint, standalone, journal: options.journal, logging: options.logging, token };
|
|
178
|
-
const result =
|
|
175
|
+
const result = Transaction.run(opts, (argsx) => {
|
|
179
176
|
if (!oc.operation.transaction.isCanceled) {
|
|
180
177
|
oc = this.edit();
|
|
181
|
-
if (
|
|
182
|
-
|
|
178
|
+
if (Log.isOn && Log.opt.operation)
|
|
179
|
+
Log.write('║', ' 𝑓', `${oc.operation.why()}`);
|
|
183
180
|
oc.operation.run(this.objectHandle.proxy, argsx);
|
|
184
181
|
}
|
|
185
182
|
else {
|
|
186
183
|
oc = this.peek(argsx);
|
|
187
|
-
if (oc.operation.options.kind ===
|
|
184
|
+
if (oc.operation.options.kind === Kind.Transaction || !oc.isUpToDate) {
|
|
188
185
|
oc = this.edit();
|
|
189
|
-
if (
|
|
190
|
-
|
|
186
|
+
if (Log.isOn && Log.opt.operation)
|
|
187
|
+
Log.write('║', ' 𝑓', `${oc.operation.why()}`);
|
|
191
188
|
oc.operation.run(this.objectHandle.proxy, argsx);
|
|
192
189
|
}
|
|
193
190
|
}
|
|
@@ -199,15 +196,14 @@ class OperationController extends Controller_1.Controller {
|
|
|
199
196
|
static markObsolete(self) {
|
|
200
197
|
const oc = self.peek(undefined);
|
|
201
198
|
const ctx = oc.changeset;
|
|
202
|
-
oc.operation.markObsoleteDueTo(oc.operation, self.memberName,
|
|
199
|
+
oc.operation.markObsoleteDueTo(oc.operation, self.memberName, EMPTY_SNAPSHOT.changeset, EMPTY_HANDLE, BOOT_CAUSE, ctx.timestamp, ctx.reactions);
|
|
203
200
|
}
|
|
204
201
|
}
|
|
205
|
-
|
|
206
|
-
class Operation extends Data_1.Subscription {
|
|
202
|
+
class Operation extends Subscription {
|
|
207
203
|
constructor(controller, changeset, former) {
|
|
208
204
|
super(undefined);
|
|
209
205
|
this.margin = Operation.current ? Operation.current.margin + 1 : 1;
|
|
210
|
-
this.transaction =
|
|
206
|
+
this.transaction = Transaction.current;
|
|
211
207
|
this.controller = controller;
|
|
212
208
|
this.changeset = changeset;
|
|
213
209
|
this.subscriptions = new Map();
|
|
@@ -228,7 +224,7 @@ class Operation extends Data_1.Subscription {
|
|
|
228
224
|
}
|
|
229
225
|
get isOperation() { return true; }
|
|
230
226
|
get originSnapshotId() { return this.changeset.id; }
|
|
231
|
-
hint() { return `${
|
|
227
|
+
hint() { return `${Dump.snapshot2(this.controller.objectHandle, this.changeset, this.controller.memberName)}`; }
|
|
232
228
|
get order() { return this.options.order; }
|
|
233
229
|
get ['#this']() {
|
|
234
230
|
return `Operation: ${this.why()}`;
|
|
@@ -237,7 +233,7 @@ class Operation extends Data_1.Subscription {
|
|
|
237
233
|
let cause;
|
|
238
234
|
if (this.cause)
|
|
239
235
|
cause = ` << ${this.cause}`;
|
|
240
|
-
else if (this.controller.options.kind ===
|
|
236
|
+
else if (this.controller.options.kind === Kind.Transaction)
|
|
241
237
|
cause = ' << operation';
|
|
242
238
|
else
|
|
243
239
|
cause = ` << T${this.changeset.id}[${this.changeset.hint}]`;
|
|
@@ -247,19 +243,19 @@ class Operation extends Data_1.Subscription {
|
|
|
247
243
|
return this.why();
|
|
248
244
|
}
|
|
249
245
|
dependencies() {
|
|
250
|
-
throw
|
|
246
|
+
throw misuse('not implemented yet');
|
|
251
247
|
}
|
|
252
248
|
wrap(func) {
|
|
253
249
|
const wrappedForOperation = (...args) => {
|
|
254
|
-
if (
|
|
255
|
-
|
|
250
|
+
if (Log.isOn && Log.opt.step && this.result)
|
|
251
|
+
Log.writeAs({ margin2: this.margin }, '║', '‾\\', `${this.hint()} - step in `, 0, ' │');
|
|
256
252
|
const started = Date.now();
|
|
257
253
|
const result = OperationController.runWithin(this, func, ...args);
|
|
258
254
|
const ms = Date.now() - started;
|
|
259
|
-
if (
|
|
260
|
-
|
|
261
|
-
if (ms >
|
|
262
|
-
|
|
255
|
+
if (Log.isOn && Log.opt.step && this.result)
|
|
256
|
+
Log.writeAs({ margin2: this.margin }, '║', '_/', `${this.hint()} - step out `, 0, this.started > 0 ? ' │' : '');
|
|
257
|
+
if (ms > Hooks.mainThreadBlockingWarningThreshold)
|
|
258
|
+
Log.write('', '[!]', this.why(), ms, ' *** main thread is too busy ***');
|
|
263
259
|
return result;
|
|
264
260
|
};
|
|
265
261
|
return wrappedForOperation;
|
|
@@ -267,7 +263,7 @@ class Operation extends Data_1.Subscription {
|
|
|
267
263
|
run(proxy, args) {
|
|
268
264
|
if (args)
|
|
269
265
|
this.args = args;
|
|
270
|
-
this.obsoleteSince =
|
|
266
|
+
this.obsoleteSince = MAX_REVISION;
|
|
271
267
|
if (!this.error)
|
|
272
268
|
OperationController.runWithin(this, Operation.run, this, proxy);
|
|
273
269
|
else
|
|
@@ -279,14 +275,14 @@ class Operation extends Data_1.Subscription {
|
|
|
279
275
|
const skip = !subscription.isOperation &&
|
|
280
276
|
changeset === this.changeset;
|
|
281
277
|
if (!skip) {
|
|
282
|
-
const why = `${
|
|
283
|
-
const isReaction = this.options.kind ===
|
|
278
|
+
const why = `${Dump.snapshot2(h, changeset, m, subscription)} << ${outer}`;
|
|
279
|
+
const isReaction = this.options.kind === Kind.Reaction;
|
|
284
280
|
this.obsoleteDueTo = why;
|
|
285
281
|
this.obsoleteSince = since;
|
|
286
|
-
if (
|
|
287
|
-
|
|
282
|
+
if (Log.isOn && (Log.opt.obsolete || ((_a = this.options.logging) === null || _a === void 0 ? void 0 : _a.obsolete)))
|
|
283
|
+
Log.write(Log.opt.transaction && !Changeset.current().sealed ? '║' : ' ', isReaction ? '█' : '▒', isReaction && changeset === EMPTY_SNAPSHOT.changeset
|
|
288
284
|
? `${this.hint()} is a reaction and will run automatically (order ${this.options.order})`
|
|
289
|
-
: `${this.hint()} is obsolete due to ${
|
|
285
|
+
: `${this.hint()} is obsolete due to ${Dump.snapshot2(h, changeset, m)} since v${since}${isReaction ? ` and will run automatically (order ${this.options.order})` : ''}`);
|
|
290
286
|
this.unsubscribeFromAllSubscriptions();
|
|
291
287
|
if (isReaction)
|
|
292
288
|
reactions.push(this);
|
|
@@ -296,10 +292,10 @@ class Operation extends Data_1.Subscription {
|
|
|
296
292
|
if (tran.changeset === changeset) {
|
|
297
293
|
}
|
|
298
294
|
else if (!tran.isFinished && this !== subscription)
|
|
299
|
-
tran.cancel(new Error(`T${tran.id}[${tran.hint}] is canceled due to obsolete ${
|
|
295
|
+
tran.cancel(new Error(`T${tran.id}[${tran.hint}] is canceled due to obsolete ${Dump.snapshot2(h, changeset, m)} changed by T${changeset.id}[${changeset.hint}]`), null);
|
|
300
296
|
}
|
|
301
|
-
else if (
|
|
302
|
-
|
|
297
|
+
else if (Log.isOn && (Log.opt.obsolete || ((_c = this.options.logging) === null || _c === void 0 ? void 0 : _c.obsolete)))
|
|
298
|
+
Log.write(' ', 'x', `${this.hint()} is not obsolete due to its own change to ${Dump.snapshot2(h, changeset, m)}`);
|
|
303
299
|
}
|
|
304
300
|
}
|
|
305
301
|
runIfNotUpToDate(now, nothrow) {
|
|
@@ -312,15 +308,15 @@ class Operation extends Data_1.Subscription {
|
|
|
312
308
|
const op = this.controller.useOrRun(false, undefined);
|
|
313
309
|
if (op.result instanceof Promise)
|
|
314
310
|
op.result.catch(error => {
|
|
315
|
-
if (op.options.kind ===
|
|
316
|
-
|
|
311
|
+
if (op.options.kind === Kind.Reaction)
|
|
312
|
+
misuse(`reaction ${op.hint()} failed and will not run anymore: ${error}`, error);
|
|
317
313
|
});
|
|
318
314
|
}
|
|
319
315
|
catch (e) {
|
|
320
316
|
if (!nothrow)
|
|
321
317
|
throw e;
|
|
322
|
-
else if (this.options.kind ===
|
|
323
|
-
|
|
318
|
+
else if (this.options.kind === Kind.Reaction)
|
|
319
|
+
misuse(`reaction ${this.hint()} failed and will not run anymore: ${e}`, e);
|
|
324
320
|
}
|
|
325
321
|
}
|
|
326
322
|
}
|
|
@@ -332,35 +328,35 @@ class Operation extends Data_1.Subscription {
|
|
|
332
328
|
}
|
|
333
329
|
}
|
|
334
330
|
isNotUpToDate() {
|
|
335
|
-
return !this.error && (this.options.kind ===
|
|
331
|
+
return !this.error && (this.options.kind === Kind.Transaction ||
|
|
336
332
|
!this.successor || this.successor.transaction.isCanceled);
|
|
337
333
|
}
|
|
338
334
|
reenterOver(head) {
|
|
339
335
|
let error = undefined;
|
|
340
336
|
const opponent = head.successor;
|
|
341
337
|
if (opponent && !opponent.transaction.isFinished) {
|
|
342
|
-
if (
|
|
343
|
-
|
|
338
|
+
if (Log.isOn && Log.opt.obsolete)
|
|
339
|
+
Log.write('║', ' [!]', `${this.hint()} is trying to re-enter over ${opponent.hint()}`);
|
|
344
340
|
switch (head.options.reentrance) {
|
|
345
|
-
case
|
|
341
|
+
case Reentrance.PreventWithError:
|
|
346
342
|
if (!opponent.transaction.isCanceled)
|
|
347
|
-
throw
|
|
343
|
+
throw misuse(`${head.hint()} (${head.why()}) is not reentrant over ${opponent.hint()} (${opponent.why()})`);
|
|
348
344
|
error = new Error(`T${this.transaction.id}[${this.transaction.hint}] is on hold/PreventWithError due to canceled T${opponent.transaction.id}[${opponent.transaction.hint}]`);
|
|
349
345
|
this.transaction.cancel(error, opponent.transaction);
|
|
350
346
|
break;
|
|
351
|
-
case
|
|
347
|
+
case Reentrance.WaitAndRestart:
|
|
352
348
|
error = new Error(`T${this.transaction.id}[${this.transaction.hint}] is on hold/WaitAndRestart due to active T${opponent.transaction.id}[${opponent.transaction.hint}]`);
|
|
353
349
|
this.transaction.cancel(error, opponent.transaction);
|
|
354
350
|
break;
|
|
355
|
-
case
|
|
351
|
+
case Reentrance.CancelAndWaitPrevious:
|
|
356
352
|
error = new Error(`T${this.transaction.id}[${this.transaction.hint}] is on hold/CancelAndWaitPrevious due to active T${opponent.transaction.id}[${opponent.transaction.hint}]`);
|
|
357
353
|
this.transaction.cancel(error, opponent.transaction);
|
|
358
354
|
opponent.transaction.cancel(new Error(`T${opponent.transaction.id}[${opponent.transaction.hint}] is canceled due to re-entering T${this.transaction.id}[${this.transaction.hint}]`), null);
|
|
359
355
|
break;
|
|
360
|
-
case
|
|
356
|
+
case Reentrance.CancelPrevious:
|
|
361
357
|
opponent.transaction.cancel(new Error(`T${opponent.transaction.id}[${opponent.transaction.hint}] is canceled due to re-entering T${this.transaction.id}[${this.transaction.hint}]`), null);
|
|
362
358
|
break;
|
|
363
|
-
case
|
|
359
|
+
case Reentrance.RunSideBySide:
|
|
364
360
|
break;
|
|
365
361
|
}
|
|
366
362
|
}
|
|
@@ -382,8 +378,8 @@ class Operation extends Data_1.Subscription {
|
|
|
382
378
|
enter() {
|
|
383
379
|
if (this.options.monitor)
|
|
384
380
|
this.monitorEnter(this.options.monitor);
|
|
385
|
-
if (
|
|
386
|
-
|
|
381
|
+
if (Log.isOn && Log.opt.operation)
|
|
382
|
+
Log.write('║', '‾\\', `${this.hint()} - enter`, undefined, ` [ ${Dump.obj(this.controller.objectHandle, this.controller.memberName)} ]`);
|
|
387
383
|
this.started = Date.now();
|
|
388
384
|
}
|
|
389
385
|
leaveOrAsync() {
|
|
@@ -397,11 +393,11 @@ class Operation extends Data_1.Subscription {
|
|
|
397
393
|
this.leave(false, ' ⚐', '- finished ', 'ERR ──┘');
|
|
398
394
|
throw error;
|
|
399
395
|
});
|
|
400
|
-
if (
|
|
401
|
-
if (
|
|
402
|
-
|
|
403
|
-
else if (
|
|
404
|
-
|
|
396
|
+
if (Log.isOn) {
|
|
397
|
+
if (Log.opt.operation)
|
|
398
|
+
Log.write('║', '_/', `${this.hint()} - leave... `, 0, 'ASYNC ──┐');
|
|
399
|
+
else if (Log.opt.transaction)
|
|
400
|
+
Log.write('║', ' ', `${this.why()} ...`, 0, 'ASYNC');
|
|
405
401
|
}
|
|
406
402
|
}
|
|
407
403
|
else {
|
|
@@ -412,10 +408,10 @@ class Operation extends Data_1.Subscription {
|
|
|
412
408
|
leave(main, op, message, highlight = undefined) {
|
|
413
409
|
const ms = Date.now() - this.started;
|
|
414
410
|
this.started = -this.started;
|
|
415
|
-
if (
|
|
416
|
-
|
|
417
|
-
if (ms > (main ?
|
|
418
|
-
|
|
411
|
+
if (Log.isOn && Log.opt.operation)
|
|
412
|
+
Log.write('║', `${op}`, `${this.hint()} ${message}`, ms, highlight);
|
|
413
|
+
if (ms > (main ? Hooks.mainThreadBlockingWarningThreshold : Hooks.asyncActionDurationWarningThreshold))
|
|
414
|
+
Log.write('', '[!]', this.why(), ms, main ? ' *** main thread is too busy ***' : ' *** async is too long ***');
|
|
419
415
|
this.cause = undefined;
|
|
420
416
|
if (this.options.monitor)
|
|
421
417
|
this.monitorLeave(this.options.monitor);
|
|
@@ -424,19 +420,19 @@ class Operation extends Data_1.Subscription {
|
|
|
424
420
|
const options = {
|
|
425
421
|
hint: 'Monitor.enter',
|
|
426
422
|
standalone: 'isolated',
|
|
427
|
-
logging:
|
|
423
|
+
logging: Log.isOn && Log.opt.monitor ? undefined : Log.global
|
|
428
424
|
};
|
|
429
|
-
OperationController.runWithin(undefined,
|
|
425
|
+
OperationController.runWithin(undefined, Transaction.run, options, MonitorImpl.enter, mon, this.transaction);
|
|
430
426
|
}
|
|
431
427
|
monitorLeave(mon) {
|
|
432
|
-
|
|
428
|
+
Transaction.off(() => {
|
|
433
429
|
const leave = () => {
|
|
434
430
|
const options = {
|
|
435
431
|
hint: 'Monitor.leave',
|
|
436
432
|
standalone: 'isolated',
|
|
437
|
-
logging:
|
|
433
|
+
logging: Log.isOn && Log.opt.monitor ? undefined : Log.DefaultLevel
|
|
438
434
|
};
|
|
439
|
-
OperationController.runWithin(undefined,
|
|
435
|
+
OperationController.runWithin(undefined, Transaction.run, options, MonitorImpl.leave, mon, this.transaction);
|
|
440
436
|
};
|
|
441
437
|
this.transaction.whenFinished().then(leave, leave);
|
|
442
438
|
});
|
|
@@ -453,11 +449,11 @@ class Operation extends Data_1.Subscription {
|
|
|
453
449
|
x.runIfNotUpToDate(true, true);
|
|
454
450
|
}
|
|
455
451
|
static markUsed(subscription, os, m, h, kind, weak) {
|
|
456
|
-
if (kind !==
|
|
452
|
+
if (kind !== Kind.Transaction) {
|
|
457
453
|
const op = Operation.current;
|
|
458
|
-
if (op && op.options.kind !==
|
|
459
|
-
op.transaction ===
|
|
460
|
-
const ctx =
|
|
454
|
+
if (op && op.options.kind !== Kind.Transaction &&
|
|
455
|
+
op.transaction === Transaction.current && m !== Meta.Handle) {
|
|
456
|
+
const ctx = Changeset.current();
|
|
461
457
|
if (ctx !== os.changeset)
|
|
462
458
|
ctx.bumpBy(os.changeset.timestamp);
|
|
463
459
|
const t = weak ? -1 : ctx.timestamp;
|
|
@@ -468,8 +464,8 @@ class Operation extends Data_1.Subscription {
|
|
|
468
464
|
}
|
|
469
465
|
static markEdited(oldValue, newValue, edited, os, m, h) {
|
|
470
466
|
edited ? os.changes.add(m) : os.changes.delete(m);
|
|
471
|
-
if (
|
|
472
|
-
edited ?
|
|
467
|
+
if (Log.isOn && Log.opt.write)
|
|
468
|
+
edited ? Log.write('║', ' ✎', `${Dump.snapshot2(h, os.changeset, m)} is changed from ${valueHint(oldValue, m)} to ${valueHint(newValue, m)}`) : Log.write('║', ' ✎', `${Dump.snapshot2(h, os.changeset, m)} is changed from ${valueHint(oldValue, m)} to ${valueHint(newValue, m)}`, undefined, ' (same as previous)');
|
|
473
469
|
}
|
|
474
470
|
static isConflicting(oldValue, newValue) {
|
|
475
471
|
let result = oldValue !== newValue;
|
|
@@ -482,7 +478,7 @@ class Operation extends Data_1.Subscription {
|
|
|
482
478
|
const since = changeset.timestamp;
|
|
483
479
|
const reactions = changeset.reactions;
|
|
484
480
|
changeset.items.forEach((os, h) => {
|
|
485
|
-
Operation.propagateMemberChangeThroughSubscriptions(false, since, os,
|
|
481
|
+
Operation.propagateMemberChangeThroughSubscriptions(false, since, os, Meta.Revision, h, reactions);
|
|
486
482
|
if (!os.disposed)
|
|
487
483
|
os.changes.forEach((o, m) => Operation.propagateMemberChangeThroughSubscriptions(false, since, os, m, h, reactions));
|
|
488
484
|
else
|
|
@@ -490,11 +486,11 @@ class Operation extends Data_1.Subscription {
|
|
|
490
486
|
Operation.propagateMemberChangeThroughSubscriptions(true, since, os, m, h, reactions);
|
|
491
487
|
});
|
|
492
488
|
reactions.sort(compareReactionsByOrder);
|
|
493
|
-
(_a = changeset.options.journal) === null || _a === void 0 ? void 0 : _a.edited(
|
|
489
|
+
(_a = changeset.options.journal) === null || _a === void 0 ? void 0 : _a.edited(JournalImpl.buildPatch(changeset.hint, changeset.items));
|
|
494
490
|
}
|
|
495
491
|
static revokeAllSubscriptions(changeset) {
|
|
496
492
|
changeset.items.forEach((os, h) => {
|
|
497
|
-
Operation.propagateMemberChangeThroughSubscriptions(true, changeset.timestamp, os,
|
|
493
|
+
Operation.propagateMemberChangeThroughSubscriptions(true, changeset.timestamp, os, Meta.Revision, h, undefined);
|
|
498
494
|
os.changes.forEach((o, m) => Operation.propagateMemberChangeThroughSubscriptions(true, changeset.timestamp, os, m, h, undefined));
|
|
499
495
|
});
|
|
500
496
|
}
|
|
@@ -503,10 +499,10 @@ class Operation extends Data_1.Subscription {
|
|
|
503
499
|
const curr = os.data[m];
|
|
504
500
|
if (reactions) {
|
|
505
501
|
const former = os.former.snapshot.data[m];
|
|
506
|
-
if (former !== undefined && former instanceof
|
|
502
|
+
if (former !== undefined && former instanceof Subscription) {
|
|
507
503
|
const why = `T${os.changeset.id}[${os.changeset.hint}]`;
|
|
508
504
|
if (former instanceof Operation) {
|
|
509
|
-
if ((former.obsoleteSince ===
|
|
505
|
+
if ((former.obsoleteSince === MAX_REVISION || former.obsoleteSince <= 0)) {
|
|
510
506
|
former.obsoleteDueTo = why;
|
|
511
507
|
former.obsoleteSince = timestamp;
|
|
512
508
|
former.unsubscribeFromAllSubscriptions();
|
|
@@ -524,17 +520,17 @@ class Operation extends Data_1.Subscription {
|
|
|
524
520
|
}
|
|
525
521
|
if (curr instanceof Operation) {
|
|
526
522
|
if (curr.changeset === os.changeset && curr.subscriptions !== undefined) {
|
|
527
|
-
if (
|
|
523
|
+
if (Hooks.repetitiveUsageWarningThreshold < Number.MAX_SAFE_INTEGER) {
|
|
528
524
|
curr.subscriptions.forEach((info, v) => {
|
|
529
|
-
if (info.usageCount >
|
|
530
|
-
|
|
525
|
+
if (info.usageCount > Hooks.repetitiveUsageWarningThreshold)
|
|
526
|
+
Log.write('', '[!]', `${curr.hint()} uses ${info.memberHint} ${info.usageCount} times (consider remembering it in a local variable)`, 0, ' *** WARNING ***');
|
|
531
527
|
});
|
|
532
528
|
}
|
|
533
529
|
if (unsubscribe)
|
|
534
530
|
curr.unsubscribeFromAllSubscriptions();
|
|
535
531
|
}
|
|
536
532
|
}
|
|
537
|
-
else if (curr instanceof
|
|
533
|
+
else if (curr instanceof Subscription && curr.subscribers) {
|
|
538
534
|
}
|
|
539
535
|
}
|
|
540
536
|
static enqueueReactionsToRun(reactions) {
|
|
@@ -560,8 +556,8 @@ class Operation extends Data_1.Subscription {
|
|
|
560
556
|
(_a = this.subscriptions) === null || _a === void 0 ? void 0 : _a.forEach((info, value) => {
|
|
561
557
|
var _a;
|
|
562
558
|
value.subscribers.delete(this);
|
|
563
|
-
if (
|
|
564
|
-
|
|
559
|
+
if (Log.isOn && (Log.opt.read || ((_a = this.options.logging) === null || _a === void 0 ? void 0 : _a.read)))
|
|
560
|
+
Log.write(Log.opt.transaction && !Changeset.current().sealed ? '║' : ' ', '-', `${this.hint()} is unsubscribed from ${info.memberHint}`);
|
|
565
561
|
});
|
|
566
562
|
this.subscriptions = undefined;
|
|
567
563
|
}
|
|
@@ -570,25 +566,25 @@ class Operation extends Data_1.Subscription {
|
|
|
570
566
|
const ok = Operation.canSubscribe(subscription, os, m, h, timestamp);
|
|
571
567
|
if (ok) {
|
|
572
568
|
let times = 0;
|
|
573
|
-
if (
|
|
569
|
+
if (Hooks.repetitiveUsageWarningThreshold < Number.MAX_SAFE_INTEGER) {
|
|
574
570
|
const existing = this.subscriptions.get(subscription);
|
|
575
571
|
times = existing ? existing.usageCount + 1 : 1;
|
|
576
572
|
}
|
|
577
573
|
if (this.subscriptions !== undefined) {
|
|
578
574
|
if (!subscription.subscribers)
|
|
579
575
|
subscription.subscribers = new Set();
|
|
580
|
-
const info = { memberHint:
|
|
576
|
+
const info = { memberHint: Dump.snapshot2(h, os.changeset, m), usageCount: times };
|
|
581
577
|
subscription.subscribers.add(this);
|
|
582
578
|
this.subscriptions.set(subscription, info);
|
|
583
|
-
if (
|
|
584
|
-
|
|
579
|
+
if (Log.isOn && (Log.opt.read || ((_a = this.options.logging) === null || _a === void 0 ? void 0 : _a.read)))
|
|
580
|
+
Log.write('║', ' ∞ ', `${this.hint()} is subscribed to ${Dump.snapshot2(h, os.changeset, m)}${info.usageCount > 1 ? ` (${info.usageCount} times)` : ''}`);
|
|
585
581
|
}
|
|
586
|
-
else if (
|
|
587
|
-
|
|
582
|
+
else if (Log.isOn && (Log.opt.read || ((_b = this.options.logging) === null || _b === void 0 ? void 0 : _b.read)))
|
|
583
|
+
Log.write('║', ' x ', `${this.hint()} is obsolete and is NOT subscribed to ${Dump.snapshot2(h, os.changeset, m)}`);
|
|
588
584
|
}
|
|
589
585
|
else {
|
|
590
|
-
if (
|
|
591
|
-
|
|
586
|
+
if (Log.isOn && (Log.opt.read || ((_c = this.options.logging) === null || _c === void 0 ? void 0 : _c.read)))
|
|
587
|
+
Log.write('║', ' x ', `${this.hint()} is NOT subscribed to already obsolete ${Dump.snapshot2(h, os.changeset, m)}`);
|
|
592
588
|
}
|
|
593
589
|
return ok;
|
|
594
590
|
}
|
|
@@ -603,37 +599,37 @@ class Operation extends Data_1.Subscription {
|
|
|
603
599
|
const operation = (...args) => {
|
|
604
600
|
return ctl.useOrRun(false, args).result;
|
|
605
601
|
};
|
|
606
|
-
|
|
602
|
+
Meta.set(operation, Meta.Controller, ctl);
|
|
607
603
|
return operation;
|
|
608
604
|
}
|
|
609
605
|
static rememberOperationOptions(proto, m, getter, setter, enumerable, configurable, options, implicit) {
|
|
610
|
-
const initial =
|
|
606
|
+
const initial = Meta.acquire(proto, Meta.Initial);
|
|
611
607
|
let op = initial[m];
|
|
612
608
|
const ctl = op ? op.controller : new OperationController(EMPTY_HANDLE, m);
|
|
613
|
-
const opts = op ? op.options :
|
|
614
|
-
initial[m] = op = new Operation(ctl,
|
|
615
|
-
if (op.options.kind ===
|
|
616
|
-
const reactions =
|
|
609
|
+
const opts = op ? op.options : OptionsImpl.INITIAL;
|
|
610
|
+
initial[m] = op = new Operation(ctl, EMPTY_SNAPSHOT.changeset, new OptionsImpl(getter, setter, opts, options, implicit));
|
|
611
|
+
if (op.options.kind === Kind.Reaction && op.options.throttling < Number.MAX_SAFE_INTEGER) {
|
|
612
|
+
const reactions = Meta.acquire(proto, Meta.Reactions);
|
|
617
613
|
reactions[m] = op;
|
|
618
614
|
}
|
|
619
|
-
else if (op.options.kind ===
|
|
620
|
-
const reactions =
|
|
615
|
+
else if (op.options.kind === Kind.Reaction && op.options.throttling >= Number.MAX_SAFE_INTEGER) {
|
|
616
|
+
const reactions = Meta.getFrom(proto, Meta.Reactions);
|
|
621
617
|
delete reactions[m];
|
|
622
618
|
}
|
|
623
619
|
return op.options;
|
|
624
620
|
}
|
|
625
621
|
static init() {
|
|
626
622
|
Object.freeze(BOOT_ARGS);
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
623
|
+
Log.getMergedLoggingOptions = getMergedLoggingOptions;
|
|
624
|
+
Dump.valueHint = valueHint;
|
|
625
|
+
Changeset.markUsed = Operation.markUsed;
|
|
626
|
+
Changeset.markEdited = Operation.markEdited;
|
|
627
|
+
Changeset.isConflicting = Operation.isConflicting;
|
|
628
|
+
Changeset.propagateAllChangesThroughSubscriptions = Operation.propagateAllChangesThroughSubscriptions;
|
|
629
|
+
Changeset.revokeAllSubscriptions = Operation.revokeAllSubscriptions;
|
|
630
|
+
Changeset.enqueueReactionsToRun = Operation.enqueueReactionsToRun;
|
|
631
|
+
Hooks.createOperation = Operation.createOperation;
|
|
632
|
+
Hooks.rememberOperationOptions = Operation.rememberOperationOptions;
|
|
637
633
|
Promise.prototype.then = reactronicHookedThen;
|
|
638
634
|
try {
|
|
639
635
|
Object.defineProperty(globalThis, 'rWhy', {
|
|
@@ -669,8 +665,8 @@ function valueHint(value, m) {
|
|
|
669
665
|
else if (value instanceof Map)
|
|
670
666
|
result = `Map(${value.size})`;
|
|
671
667
|
else if (value instanceof Operation)
|
|
672
|
-
result = `${
|
|
673
|
-
else if (value ===
|
|
668
|
+
result = `${Dump.snapshot2(value.controller.objectHandle, value.changeset, m)}`;
|
|
669
|
+
else if (value === Meta.Undefined)
|
|
674
670
|
result = 'undefined';
|
|
675
671
|
else if (typeof (value) === 'string')
|
|
676
672
|
result = `"${value.toString().slice(0, 20)}"`;
|
|
@@ -681,18 +677,18 @@ function valueHint(value, m) {
|
|
|
681
677
|
return result;
|
|
682
678
|
}
|
|
683
679
|
function getMergedLoggingOptions(local) {
|
|
684
|
-
const t =
|
|
685
|
-
let res =
|
|
686
|
-
res =
|
|
680
|
+
const t = Transaction.current;
|
|
681
|
+
let res = Log.merge(t.options.logging, t.id > 1 ? 31 + t.id % 6 : 37, t.id > 1 ? `T${t.id}` : `-${Changeset.idGen.toString().replace(/[0-9]/g, '-')}`, Log.global);
|
|
682
|
+
res = Log.merge({ margin1: t.margin }, undefined, undefined, res);
|
|
687
683
|
if (Operation.current)
|
|
688
|
-
res =
|
|
684
|
+
res = Log.merge({ margin2: Operation.current.margin }, undefined, undefined, res);
|
|
689
685
|
if (local)
|
|
690
|
-
res =
|
|
686
|
+
res = Log.merge(local, undefined, undefined, res);
|
|
691
687
|
return res;
|
|
692
688
|
}
|
|
693
689
|
const ORIGINAL_PROMISE_THEN = Promise.prototype.then;
|
|
694
690
|
function reactronicHookedThen(resolve, reject) {
|
|
695
|
-
const tran =
|
|
691
|
+
const tran = Transaction.current;
|
|
696
692
|
if (!tran.isFinished) {
|
|
697
693
|
if (!resolve)
|
|
698
694
|
resolve = resolveReturn;
|
|
@@ -711,12 +707,10 @@ function reactronicHookedThen(resolve, reject) {
|
|
|
711
707
|
function compareReactionsByOrder(a, b) {
|
|
712
708
|
return a.order - b.order;
|
|
713
709
|
}
|
|
714
|
-
function resolveReturn(value) {
|
|
710
|
+
export function resolveReturn(value) {
|
|
715
711
|
return value;
|
|
716
712
|
}
|
|
717
|
-
|
|
718
|
-
function rejectRethrow(error) {
|
|
713
|
+
export function rejectRethrow(error) {
|
|
719
714
|
throw error;
|
|
720
715
|
}
|
|
721
|
-
exports.rejectRethrow = rejectRethrow;
|
|
722
716
|
Operation.init();
|