reactronic 0.24.275 → 0.24.302
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/build/dist/source/Options.d.ts +10 -4
- package/build/dist/source/Options.js +8 -0
- package/build/dist/source/RxSystem.d.ts +2 -2
- package/build/dist/source/RxSystem.js +21 -10
- package/build/dist/source/api.d.ts +3 -3
- package/build/dist/source/api.js +2 -2
- package/build/dist/source/core/Changeset.d.ts +23 -21
- package/build/dist/source/core/Changeset.js +147 -144
- package/build/dist/source/core/Data.d.ts +19 -20
- package/build/dist/source/core/Data.js +9 -9
- package/build/dist/source/core/Indicator.d.ts +2 -2
- package/build/dist/source/core/Indicator.js +9 -8
- package/build/dist/source/core/Journal.d.ts +2 -2
- package/build/dist/source/core/Journal.js +36 -35
- package/build/dist/source/core/Mvcc.d.ts +12 -12
- package/build/dist/source/core/Mvcc.js +47 -62
- package/build/dist/source/core/MvccArray.d.ts +1 -0
- package/build/dist/source/core/MvccArray.js +4 -4
- package/build/dist/source/core/{Reaction.d.ts → Operation.d.ts} +21 -20
- package/build/dist/source/core/{Reaction.js → Operation.js} +187 -156
- package/build/dist/source/core/RxNode.d.ts +8 -6
- package/build/dist/source/core/RxNode.js +37 -18
- package/build/dist/source/core/Transaction.d.ts +61 -2
- package/build/dist/source/core/Transaction.js +181 -16
- package/build/dist/source/util/Utils.d.ts +1 -0
- package/build/dist/source/util/Utils.js +8 -0
- package/package.json +4 -4
|
@@ -4,22 +4,23 @@ import { Sealant } from "../util/Sealant.js";
|
|
|
4
4
|
import { SealedArray } from "../util/SealedArray.js";
|
|
5
5
|
import { SealedMap } from "../util/SealedMap.js";
|
|
6
6
|
import { SealedSet } from "../util/SealedSet.js";
|
|
7
|
-
import {
|
|
7
|
+
import { Isolation } from "../Options.js";
|
|
8
|
+
import { ObjectVersion, ObjectHandle, FieldVersion, Meta } from "./Data.js";
|
|
8
9
|
export const MAX_REVISION = Number.MAX_SAFE_INTEGER;
|
|
9
10
|
export const UNDEFINED_REVISION = MAX_REVISION - 1;
|
|
10
11
|
Object.defineProperty(ObjectHandle.prototype, "#this#", {
|
|
11
12
|
configurable: false, enumerable: false,
|
|
12
13
|
get() {
|
|
13
14
|
const result = {};
|
|
14
|
-
const data = Changeset.current().
|
|
15
|
-
for (const
|
|
16
|
-
const v = data[
|
|
17
|
-
if (v instanceof
|
|
18
|
-
result[
|
|
15
|
+
const data = Changeset.current().getObjectVersion(this, "#this#").data;
|
|
16
|
+
for (const fk in data) {
|
|
17
|
+
const v = data[fk];
|
|
18
|
+
if (v instanceof FieldVersion)
|
|
19
|
+
result[fk] = v.content;
|
|
19
20
|
else if (v === Meta.Raw)
|
|
20
|
-
result[
|
|
21
|
+
result[fk] = this.data[fk];
|
|
21
22
|
else
|
|
22
|
-
result[
|
|
23
|
+
result[fk] = v;
|
|
23
24
|
}
|
|
24
25
|
return result;
|
|
25
26
|
},
|
|
@@ -29,56 +30,83 @@ const EMPTY_MAP = Utils.freezeMap(new Map());
|
|
|
29
30
|
export class Changeset {
|
|
30
31
|
get hint() { var _a; return (_a = this.options.hint) !== null && _a !== void 0 ? _a : "noname"; }
|
|
31
32
|
get timestamp() { return this.revision; }
|
|
32
|
-
constructor(options) {
|
|
33
|
+
constructor(options, parent) {
|
|
33
34
|
this.id = ++Changeset.idGen;
|
|
34
35
|
this.options = options !== null && options !== void 0 ? options : DefaultSnapshotOptions;
|
|
36
|
+
this.parent = parent;
|
|
35
37
|
this.revision = UNDEFINED_REVISION;
|
|
36
38
|
this.bumper = 100;
|
|
37
39
|
this.items = new Map();
|
|
38
40
|
this.obsolete = [];
|
|
39
41
|
this.sealed = false;
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
let
|
|
43
|
-
if (
|
|
44
|
-
|
|
45
|
-
if (
|
|
46
|
-
h.editing =
|
|
43
|
+
lookupObjectVersion(h, fk, editing) {
|
|
44
|
+
let ov = h.editing;
|
|
45
|
+
if (ov && ov.changeset !== this) {
|
|
46
|
+
ov = this.items.get(h);
|
|
47
|
+
if (ov)
|
|
48
|
+
h.editing = ov;
|
|
47
49
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
const parent = this.parent;
|
|
51
|
+
if (!ov) {
|
|
52
|
+
if (!parent) {
|
|
53
|
+
ov = h.applied;
|
|
54
|
+
while (ov !== EMPTY_OBJECT_VERSION && ov.changeset.timestamp > this.timestamp)
|
|
55
|
+
ov = ov.former.objectVersion;
|
|
56
|
+
}
|
|
57
|
+
else
|
|
58
|
+
ov = parent.lookupObjectVersion(h, fk, editing);
|
|
52
59
|
}
|
|
53
|
-
|
|
60
|
+
else if (!editing && parent && !ov.changes.has(fk) && ov.former.objectVersion !== EMPTY_OBJECT_VERSION)
|
|
61
|
+
ov = parent.lookupObjectVersion(h, fk, editing);
|
|
62
|
+
return ov;
|
|
54
63
|
}
|
|
55
|
-
|
|
56
|
-
const r = this.
|
|
57
|
-
if (r ===
|
|
58
|
-
throw misuse(`${Dump.obj(h,
|
|
64
|
+
getObjectVersion(h, fk) {
|
|
65
|
+
const r = this.lookupObjectVersion(h, fk, false);
|
|
66
|
+
if (r === EMPTY_OBJECT_VERSION)
|
|
67
|
+
throw misuse(`${Dump.obj(h, fk)} is not yet available for T${this.id}[${this.hint}] because ${h.editing ? `T${h.editing.changeset.id}[${h.editing.changeset.hint}]` : ""} is not yet applied (last applied T${h.applied.changeset.id}[${h.applied.changeset.hint}])`);
|
|
59
68
|
return r;
|
|
60
69
|
}
|
|
61
|
-
|
|
62
|
-
let
|
|
63
|
-
const existing =
|
|
70
|
+
getEditableObjectVersion(h, fk, value, token) {
|
|
71
|
+
let ov = this.lookupObjectVersion(h, fk, true);
|
|
72
|
+
const existing = ov.data[fk];
|
|
64
73
|
if (existing !== Meta.Raw) {
|
|
65
|
-
if (this.
|
|
66
|
-
this.bumpBy(
|
|
67
|
-
const revision =
|
|
68
|
-
const data = Object.assign({},
|
|
74
|
+
if (this.isNewObjectVersionRequired(h, ov, fk, existing, value, token)) {
|
|
75
|
+
this.bumpBy(ov.changeset.timestamp);
|
|
76
|
+
const revision = fk === Meta.Handle ? 1 : ov.revision + 1;
|
|
77
|
+
const data = Object.assign({}, fk === Meta.Handle ? value : ov.data);
|
|
69
78
|
Meta.set(data, Meta.Handle, h);
|
|
70
|
-
Meta.set(data, Meta.Revision, new
|
|
71
|
-
|
|
72
|
-
this.items.set(h,
|
|
73
|
-
h.editing =
|
|
79
|
+
Meta.set(data, Meta.Revision, new FieldVersion(revision, this.id));
|
|
80
|
+
ov = new ObjectVersion(this, ov, data);
|
|
81
|
+
this.items.set(h, ov);
|
|
82
|
+
h.editing = ov;
|
|
74
83
|
h.editors++;
|
|
75
84
|
if (Log.isOn && Log.opt.write)
|
|
76
85
|
Log.write("║", " ++", `${Dump.obj(h)} - new snapshot is created (revision ${revision})`);
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
88
|
else
|
|
80
|
-
|
|
81
|
-
return
|
|
89
|
+
ov = EMPTY_OBJECT_VERSION;
|
|
90
|
+
return ov;
|
|
91
|
+
}
|
|
92
|
+
setFieldContent(h, fk, ov, content, receiver, sensitivity) {
|
|
93
|
+
let existing = ov.data[fk];
|
|
94
|
+
if (existing !== undefined || (ov.former.objectVersion.changeset === EMPTY_OBJECT_VERSION.changeset && (fk in h.data) === false)) {
|
|
95
|
+
if (existing === undefined || existing.content !== content || sensitivity) {
|
|
96
|
+
const existingContent = existing === null || existing === void 0 ? void 0 : existing.content;
|
|
97
|
+
if (ov.former.objectVersion.data[fk] === existing) {
|
|
98
|
+
existing = ov.data[fk] = new FieldVersion(content, this.id);
|
|
99
|
+
Changeset.markEdited(existingContent, content, true, ov, fk, h);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
existing.content = content;
|
|
103
|
+
existing.lastEditorChangesetId = this.id;
|
|
104
|
+
Changeset.markEdited(existingContent, content, true, ov, fk, h);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
Reflect.set(h.data, fk, content, receiver);
|
|
82
110
|
}
|
|
83
111
|
static takeSnapshot(obj) {
|
|
84
112
|
return obj[Meta.Handle]["#this#"];
|
|
@@ -90,25 +118,23 @@ export class Changeset {
|
|
|
90
118
|
Changeset.doDispose(ctx, h);
|
|
91
119
|
}
|
|
92
120
|
static doDispose(ctx, h) {
|
|
93
|
-
const
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
return
|
|
121
|
+
const ov = ctx.getEditableObjectVersion(h, Meta.Revision, Meta.Undefined);
|
|
122
|
+
if (ov !== EMPTY_OBJECT_VERSION)
|
|
123
|
+
ov.disposed = true;
|
|
124
|
+
return ov;
|
|
97
125
|
}
|
|
98
|
-
|
|
99
|
-
if (this.sealed &&
|
|
100
|
-
throw misuse(`observable property ${Dump.obj(h,
|
|
101
|
-
if (
|
|
126
|
+
isNewObjectVersionRequired(h, ov, fk, existing, value, token) {
|
|
127
|
+
if (this.sealed && ov.changeset !== EMPTY_OBJECT_VERSION.changeset)
|
|
128
|
+
throw misuse(`observable property ${Dump.obj(h, fk)} can only be modified inside transaction`);
|
|
129
|
+
if (fk !== Meta.Handle) {
|
|
102
130
|
if (value !== Meta.Handle) {
|
|
103
|
-
if (
|
|
131
|
+
if (ov.changeset !== this || ov.former.objectVersion !== EMPTY_OBJECT_VERSION) {
|
|
104
132
|
if (this.options.token !== undefined && token !== this.options.token)
|
|
105
|
-
throw misuse(`${this.hint} should not have side effects (trying to change ${Dump.snapshot(
|
|
133
|
+
throw misuse(`${this.hint} should not have side effects (trying to change ${Dump.snapshot(ov, fk)})`);
|
|
106
134
|
}
|
|
107
135
|
}
|
|
108
|
-
if (os === EMPTY_SNAPSHOT)
|
|
109
|
-
throw misuse(`${Dump.snapshot(os, m)} is not yet available for T${this.id}[${this.hint}] because of uncommitted ${h.editing ? `T${h.editing.changeset.id}[${h.editing.changeset.hint}]` : ""} (last committed T${h.head.changeset.id}[${h.head.changeset.hint}])`);
|
|
110
136
|
}
|
|
111
|
-
return
|
|
137
|
+
return ov.changeset !== this && !this.sealed;
|
|
112
138
|
}
|
|
113
139
|
acquire(outer) {
|
|
114
140
|
if (!this.sealed && this.revision === UNDEFINED_REVISION) {
|
|
@@ -128,16 +154,17 @@ export class Changeset {
|
|
|
128
154
|
rebase() {
|
|
129
155
|
let conflicts = undefined;
|
|
130
156
|
if (this.items.size > 0) {
|
|
131
|
-
this.items.forEach((
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
157
|
+
this.items.forEach((ov, h) => {
|
|
158
|
+
const theirs = this.parent ? this.parent.lookupObjectVersion(h, Meta.Handle, false) : h.applied;
|
|
159
|
+
if (ov.former.objectVersion !== theirs) {
|
|
160
|
+
const merged = this.merge(h, ov, theirs);
|
|
161
|
+
if (ov.conflicts.size > 0) {
|
|
135
162
|
if (!conflicts)
|
|
136
163
|
conflicts = [];
|
|
137
|
-
conflicts.push(
|
|
164
|
+
conflicts.push(ov);
|
|
138
165
|
}
|
|
139
166
|
if (Log.isOn && Log.opt.transaction)
|
|
140
|
-
Log.write("╠╝", "", `${Dump.snapshot2(h,
|
|
167
|
+
Log.write("╠╝", "", `${Dump.snapshot2(h, ov.changeset)} is merged with ${Dump.snapshot2(h, theirs.changeset)} among ${merged} properties with ${ov.conflicts.size} conflicts.`);
|
|
141
168
|
}
|
|
142
169
|
});
|
|
143
170
|
if (this.options.token === undefined) {
|
|
@@ -154,92 +181,68 @@ export class Changeset {
|
|
|
154
181
|
}
|
|
155
182
|
return conflicts;
|
|
156
183
|
}
|
|
157
|
-
merge(h, ours) {
|
|
184
|
+
merge(h, ours, theirs) {
|
|
158
185
|
let counter = 0;
|
|
159
|
-
const
|
|
160
|
-
const headDisposed = head.disposed;
|
|
186
|
+
const theirsDisposed = theirs.disposed;
|
|
161
187
|
const oursDisposed = ours.disposed;
|
|
162
|
-
const merged = Object.assign({},
|
|
163
|
-
ours.changes.forEach((o,
|
|
188
|
+
const merged = Object.assign({}, theirs.data);
|
|
189
|
+
ours.changes.forEach((o, fk) => {
|
|
164
190
|
counter++;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
191
|
+
const ourFieldVersion = ours.data[fk];
|
|
192
|
+
merged[fk] = ourFieldVersion;
|
|
193
|
+
if (theirsDisposed || oursDisposed) {
|
|
194
|
+
if (theirsDisposed !== oursDisposed) {
|
|
195
|
+
if (theirsDisposed || this.options.isolation !== Isolation.disjoinForInternalDisposal) {
|
|
169
196
|
if (Log.isOn && Log.opt.change)
|
|
170
|
-
Log.write("║╠", "", `${Dump.snapshot2(h, ours.changeset,
|
|
171
|
-
ours.conflicts.set(
|
|
197
|
+
Log.write("║╠", "", `${Dump.snapshot2(h, ours.changeset, fk)} <> ${Dump.snapshot2(h, theirs.changeset, fk)}`, 0, " *** CONFLICT ***");
|
|
198
|
+
ours.conflicts.set(fk, theirs);
|
|
172
199
|
}
|
|
173
200
|
}
|
|
174
201
|
}
|
|
175
202
|
else {
|
|
176
|
-
const conflict = Changeset.isConflicting(
|
|
203
|
+
const conflict = Changeset.isConflicting(theirs.data[fk], ours.former.objectVersion.data[fk]);
|
|
177
204
|
if (conflict)
|
|
178
|
-
ours.conflicts.set(
|
|
205
|
+
ours.conflicts.set(fk, theirs);
|
|
179
206
|
if (Log.isOn && Log.opt.change)
|
|
180
|
-
Log.write("║╠", "", `${Dump.snapshot2(h, ours.changeset,
|
|
207
|
+
Log.write("║╠", "", `${Dump.snapshot2(h, ours.changeset, fk)} ${conflict ? "<>" : "=="} ${Dump.snapshot2(h, theirs.changeset, fk)}`, 0, conflict ? " *** CONFLICT ***" : undefined);
|
|
181
208
|
}
|
|
182
209
|
});
|
|
183
210
|
Utils.copyAllMembers(merged, ours.data);
|
|
184
|
-
ours.former.
|
|
211
|
+
ours.former.objectVersion = theirs;
|
|
185
212
|
return counter;
|
|
186
213
|
}
|
|
187
|
-
|
|
214
|
+
seal() {
|
|
188
215
|
this.sealed = true;
|
|
189
|
-
this.items.forEach((os, h) => {
|
|
190
|
-
Changeset.sealObjectSnapshot(h, os);
|
|
191
|
-
h.editors--;
|
|
192
|
-
if (h.editors === 0)
|
|
193
|
-
h.editing = undefined;
|
|
194
|
-
if (!error) {
|
|
195
|
-
h.head = os;
|
|
196
|
-
if (Changeset.garbageCollectionSummaryInterval < Number.MAX_SAFE_INTEGER) {
|
|
197
|
-
Changeset.totalObjectSnapshotCount++;
|
|
198
|
-
if (os.former.snapshot === EMPTY_SNAPSHOT)
|
|
199
|
-
Changeset.totalObjectHandleCount++;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
if (Log.isOn) {
|
|
204
|
-
if (Log.opt.change && !error) {
|
|
205
|
-
this.items.forEach((os, h) => {
|
|
206
|
-
const members = [];
|
|
207
|
-
os.changes.forEach((o, m) => members.push(m.toString()));
|
|
208
|
-
const s = members.join(", ");
|
|
209
|
-
Log.write("║", "√", `${Dump.snapshot2(h, os.changeset)} (${s}) is ${os.former.snapshot === EMPTY_SNAPSHOT ? "constructed" : `applied over #${h.id}t${os.former.snapshot.changeset.id}s${os.former.snapshot.changeset.timestamp}`}`);
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
if (Log.opt.transaction)
|
|
213
|
-
Log.write(this.revision < UNDEFINED_REVISION ? "╚══" : "═══", `s${this.revision}`, `${this.hint} - ${error ? "CANCEL" : "APPLY"}(${this.items.size})${error ? ` - ${error}` : ""}`);
|
|
214
|
-
}
|
|
215
|
-
if (!error)
|
|
216
|
-
Changeset.propagateAllChangesThroughSubscriptions(this);
|
|
217
|
-
return this.obsolete;
|
|
218
216
|
}
|
|
219
|
-
|
|
220
|
-
if (!
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
217
|
+
sealObjectVersion(h, ov) {
|
|
218
|
+
if (!this.parent) {
|
|
219
|
+
if (!ov.disposed)
|
|
220
|
+
ov.changes.forEach((o, fk) => Changeset.sealFieldVersion(ov.data[fk], fk, h.proxy.constructor.name));
|
|
221
|
+
else
|
|
222
|
+
for (const fk in ov.former.objectVersion.data)
|
|
223
|
+
ov.data[fk] = Meta.Undefined;
|
|
224
|
+
if (Log.isOn)
|
|
225
|
+
Changeset.freezeObjectVersion(ov);
|
|
226
|
+
}
|
|
227
|
+
h.editors--;
|
|
228
|
+
if (h.editors === 0)
|
|
229
|
+
h.editing = undefined;
|
|
227
230
|
}
|
|
228
|
-
static
|
|
229
|
-
if (
|
|
230
|
-
const
|
|
231
|
-
if (
|
|
232
|
-
const sealedType = Object.getPrototypeOf(
|
|
231
|
+
static sealFieldVersion(fv, fk, typeName) {
|
|
232
|
+
if (fv instanceof FieldVersion) {
|
|
233
|
+
const content = fv.content;
|
|
234
|
+
if (content !== undefined && content !== null) {
|
|
235
|
+
const sealedType = Object.getPrototypeOf(content)[Sealant.SealedType];
|
|
233
236
|
if (sealedType)
|
|
234
|
-
|
|
237
|
+
fv.content = Sealant.seal(content, sealedType, typeName, fk);
|
|
235
238
|
}
|
|
236
239
|
}
|
|
237
240
|
}
|
|
238
|
-
static
|
|
239
|
-
Object.freeze(
|
|
240
|
-
Utils.freezeSet(
|
|
241
|
-
Utils.freezeMap(
|
|
242
|
-
return
|
|
241
|
+
static freezeObjectVersion(ov) {
|
|
242
|
+
Object.freeze(ov.data);
|
|
243
|
+
Utils.freezeSet(ov.changes);
|
|
244
|
+
Utils.freezeMap(ov.conflicts);
|
|
245
|
+
return ov;
|
|
243
246
|
}
|
|
244
247
|
triggerGarbageCollection() {
|
|
245
248
|
if (this.revision !== 0) {
|
|
@@ -264,16 +267,16 @@ export class Changeset {
|
|
|
264
267
|
unlinkHistory() {
|
|
265
268
|
if (Log.isOn && Log.opt.gc)
|
|
266
269
|
Log.write("", "[G]", `Dismiss history below t${this.id}s${this.revision} (${this.hint})`);
|
|
267
|
-
this.items.forEach((
|
|
268
|
-
if (Log.isOn && Log.opt.gc &&
|
|
269
|
-
Log.write(" ", " ", `${Dump.snapshot2(h,
|
|
270
|
+
this.items.forEach((ov, h) => {
|
|
271
|
+
if (Log.isOn && Log.opt.gc && ov.former.objectVersion !== EMPTY_OBJECT_VERSION)
|
|
272
|
+
Log.write(" ", " ", `${Dump.snapshot2(h, ov.former.objectVersion.changeset)} is ready for GC because overwritten by ${Dump.snapshot2(h, ov.changeset)}`);
|
|
270
273
|
if (Changeset.garbageCollectionSummaryInterval < Number.MAX_SAFE_INTEGER) {
|
|
271
|
-
if (
|
|
274
|
+
if (ov.former.objectVersion !== EMPTY_OBJECT_VERSION)
|
|
272
275
|
Changeset.totalObjectSnapshotCount--;
|
|
273
|
-
if (
|
|
276
|
+
if (ov.disposed)
|
|
274
277
|
Changeset.totalObjectHandleCount--;
|
|
275
278
|
}
|
|
276
|
-
|
|
279
|
+
ov.former.objectVersion = EMPTY_OBJECT_VERSION;
|
|
277
280
|
});
|
|
278
281
|
this.items = EMPTY_MAP;
|
|
279
282
|
this.obsolete = EMPTY_ARRAY;
|
|
@@ -281,11 +284,11 @@ export class Changeset {
|
|
|
281
284
|
Object.freeze(this);
|
|
282
285
|
}
|
|
283
286
|
static _init() {
|
|
284
|
-
const boot =
|
|
287
|
+
const boot = EMPTY_OBJECT_VERSION.changeset;
|
|
285
288
|
boot.acquire(boot);
|
|
286
|
-
boot.
|
|
289
|
+
boot.seal();
|
|
287
290
|
boot.triggerGarbageCollection();
|
|
288
|
-
Changeset.
|
|
291
|
+
Changeset.freezeObjectVersion(EMPTY_OBJECT_VERSION);
|
|
289
292
|
Changeset.idGen = 100;
|
|
290
293
|
Changeset.stampGen = 101;
|
|
291
294
|
Changeset.oldest = undefined;
|
|
@@ -311,47 +314,47 @@ Changeset.propagateAllChangesThroughSubscriptions = (changeset) => { };
|
|
|
311
314
|
Changeset.revokeAllSubscriptions = (changeset) => { };
|
|
312
315
|
Changeset.enqueueReactiveFunctionsToRun = (reactive) => { };
|
|
313
316
|
export class Dump {
|
|
314
|
-
static obj(h,
|
|
315
|
-
const member =
|
|
317
|
+
static obj(h, fk, stamp, changesetId, lastEditorChangesetId, value) {
|
|
318
|
+
const member = fk !== undefined ? `.${fk.toString()}` : "";
|
|
316
319
|
let result;
|
|
317
320
|
if (h !== undefined) {
|
|
318
321
|
const v = value !== undefined && value !== Meta.Undefined ? `[=${Dump.valueHint(value)}]` : "";
|
|
319
322
|
if (stamp === undefined)
|
|
320
323
|
result = `${h.hint}${member}${v} #${h.id}`;
|
|
321
324
|
else
|
|
322
|
-
result = `${h.hint}${member}${v} #${h.id}t${
|
|
325
|
+
result = `${h.hint}${member}${v} #${h.id}t${changesetId}s${stamp}${lastEditorChangesetId !== undefined ? `e${lastEditorChangesetId}` : ""}`;
|
|
323
326
|
}
|
|
324
327
|
else
|
|
325
328
|
result = `boot${member}`;
|
|
326
329
|
return result;
|
|
327
330
|
}
|
|
328
|
-
static snapshot2(h, s,
|
|
331
|
+
static snapshot2(h, s, fk, o) {
|
|
329
332
|
var _a;
|
|
330
|
-
return Dump.obj(h,
|
|
333
|
+
return Dump.obj(h, fk, s.timestamp, s.id, o === null || o === void 0 ? void 0 : o.lastEditorChangesetId, (_a = o === null || o === void 0 ? void 0 : o.content) !== null && _a !== void 0 ? _a : Meta.Undefined);
|
|
331
334
|
}
|
|
332
|
-
static snapshot(
|
|
333
|
-
const h = Meta.get(
|
|
334
|
-
const
|
|
335
|
-
return Dump.obj(h,
|
|
335
|
+
static snapshot(ov, fk) {
|
|
336
|
+
const h = Meta.get(ov.data, Meta.Handle);
|
|
337
|
+
const fv = fk !== undefined ? ov.data[fk] : undefined;
|
|
338
|
+
return Dump.obj(h, fk, ov.changeset.timestamp, ov.changeset.id, fv === null || fv === void 0 ? void 0 : fv.lastEditorChangesetId);
|
|
336
339
|
}
|
|
337
340
|
static conflicts(conflicts) {
|
|
338
341
|
return conflicts.map(ours => {
|
|
339
342
|
const items = [];
|
|
340
|
-
ours.conflicts.forEach((theirs,
|
|
341
|
-
items.push(Dump.conflictingMemberHint(
|
|
343
|
+
ours.conflicts.forEach((theirs, fk) => {
|
|
344
|
+
items.push(Dump.conflictingMemberHint(fk, ours, theirs));
|
|
342
345
|
});
|
|
343
346
|
return items.join(", ");
|
|
344
347
|
}).join(", ");
|
|
345
348
|
}
|
|
346
|
-
static conflictingMemberHint(
|
|
347
|
-
return `${theirs.changeset.hint} (${Dump.snapshot(theirs,
|
|
349
|
+
static conflictingMemberHint(fk, ours, theirs) {
|
|
350
|
+
return `${theirs.changeset.hint} (${Dump.snapshot(theirs, fk)})`;
|
|
348
351
|
}
|
|
349
352
|
}
|
|
350
353
|
Dump.valueHint = (value) => "???";
|
|
351
|
-
export const
|
|
354
|
+
export const EMPTY_OBJECT_VERSION = new ObjectVersion(new Changeset({ hint: "<boot>" }), undefined, {});
|
|
352
355
|
export const DefaultSnapshotOptions = Object.freeze({
|
|
353
356
|
hint: "noname",
|
|
354
|
-
|
|
357
|
+
isolation: Isolation.joinToCurrentTransaction,
|
|
355
358
|
journal: undefined,
|
|
356
359
|
logging: undefined,
|
|
357
360
|
token: undefined,
|
|
@@ -5,36 +5,35 @@ export type AbstractChangeset = {
|
|
|
5
5
|
readonly timestamp: number;
|
|
6
6
|
readonly sealed: boolean;
|
|
7
7
|
};
|
|
8
|
-
export declare class
|
|
8
|
+
export declare class FieldVersion<T = any> {
|
|
9
9
|
content: T;
|
|
10
10
|
observers?: Set<Observer>;
|
|
11
|
-
|
|
12
|
-
get
|
|
13
|
-
constructor(content: T);
|
|
11
|
+
lastEditorChangesetId: number;
|
|
12
|
+
get isLaunch(): boolean;
|
|
13
|
+
constructor(content: T, lastEditorChangesetId: number);
|
|
14
14
|
}
|
|
15
|
-
export type SeparationMode = boolean | "isolated" | "disposal";
|
|
16
15
|
export type Observer = {
|
|
17
16
|
readonly order: number;
|
|
18
|
-
readonly observables: Map<
|
|
17
|
+
readonly observables: Map<FieldVersion, Subscription> | undefined;
|
|
19
18
|
readonly obsoleteSince: number;
|
|
20
19
|
hint(nop?: boolean): string;
|
|
21
|
-
markObsoleteDueTo(observable:
|
|
20
|
+
markObsoleteDueTo(observable: FieldVersion, fk: FieldKey, changeset: AbstractChangeset, h: ObjectHandle, outer: string, since: number, reactive: Array<Observer>): void;
|
|
22
21
|
relaunchIfNotUpToDate(now: boolean, nothrow: boolean): void;
|
|
23
22
|
};
|
|
24
|
-
export type
|
|
23
|
+
export type FieldKey = PropertyKey;
|
|
25
24
|
export type Subscription = {
|
|
26
25
|
readonly memberHint: string;
|
|
27
26
|
readonly usageCount: number;
|
|
28
27
|
};
|
|
29
|
-
export declare class
|
|
28
|
+
export declare class ObjectVersion {
|
|
30
29
|
readonly changeset: AbstractChangeset;
|
|
31
30
|
readonly former: {
|
|
32
|
-
|
|
31
|
+
objectVersion: ObjectVersion;
|
|
33
32
|
};
|
|
34
33
|
readonly data: any;
|
|
35
|
-
readonly changes: Set<
|
|
36
|
-
readonly conflicts: Map<
|
|
37
|
-
constructor(changeset: AbstractChangeset, former:
|
|
34
|
+
readonly changes: Set<FieldKey>;
|
|
35
|
+
readonly conflicts: Map<FieldKey, ObjectVersion>;
|
|
36
|
+
constructor(changeset: AbstractChangeset, former: ObjectVersion | undefined, data: object);
|
|
38
37
|
get revision(): number;
|
|
39
38
|
get disposed(): boolean;
|
|
40
39
|
set disposed(value: boolean);
|
|
@@ -44,17 +43,17 @@ export declare class ObjectHandle {
|
|
|
44
43
|
readonly id: number;
|
|
45
44
|
readonly data: any;
|
|
46
45
|
readonly proxy: any;
|
|
47
|
-
|
|
48
|
-
editing?:
|
|
46
|
+
applied: ObjectVersion;
|
|
47
|
+
editing?: ObjectVersion;
|
|
49
48
|
editors: number;
|
|
50
49
|
hint: string;
|
|
51
|
-
constructor(data: any, proxy: any, handler: ProxyHandler<ObjectHandle>,
|
|
50
|
+
constructor(data: any, proxy: any, handler: ProxyHandler<ObjectHandle>, applied: ObjectVersion, hint: string);
|
|
52
51
|
static getHint(obj: object, full: boolean): string | undefined;
|
|
53
52
|
}
|
|
54
|
-
export type PatchSet = Map<object, Map<
|
|
53
|
+
export type PatchSet = Map<object, Map<FieldKey, ValuePatch>>;
|
|
55
54
|
export type ValuePatch = {
|
|
56
|
-
|
|
55
|
+
fieldKey: FieldKey;
|
|
57
56
|
patchKind: "update" | "add" | "remove";
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
freshContent: any;
|
|
58
|
+
formerContent: any;
|
|
60
59
|
};
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { Log } from "../util/Dbg.js";
|
|
2
2
|
import { Meta } from "./Meta.js";
|
|
3
3
|
export { Meta } from "./Meta.js";
|
|
4
|
-
export class
|
|
5
|
-
get
|
|
6
|
-
|
|
7
|
-
constructor(content) { this.content = content; }
|
|
4
|
+
export class FieldVersion {
|
|
5
|
+
get isLaunch() { return false; }
|
|
6
|
+
constructor(content, lastEditorChangesetId) { this.content = content; this.lastEditorChangesetId = lastEditorChangesetId; }
|
|
8
7
|
}
|
|
9
|
-
export class
|
|
8
|
+
export class ObjectVersion {
|
|
10
9
|
constructor(changeset, former, data) {
|
|
11
10
|
this.changeset = changeset;
|
|
12
|
-
this.former = {
|
|
11
|
+
this.former = { objectVersion: former || this };
|
|
13
12
|
this.data = data;
|
|
14
13
|
this.changes = new Set();
|
|
15
14
|
this.conflicts = new Map();
|
|
@@ -17,7 +16,8 @@ export class ObjectSnapshot {
|
|
|
17
16
|
Object.freeze(this);
|
|
18
17
|
}
|
|
19
18
|
get revision() {
|
|
20
|
-
|
|
19
|
+
var _a, _b;
|
|
20
|
+
return (_b = (_a = this.data[Meta.Revision]) === null || _a === void 0 ? void 0 : _a.content) !== null && _b !== void 0 ? _b : 0;
|
|
21
21
|
}
|
|
22
22
|
get disposed() { return this.revision < 0; }
|
|
23
23
|
set disposed(value) {
|
|
@@ -27,11 +27,11 @@ export class ObjectSnapshot {
|
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export class ObjectHandle {
|
|
30
|
-
constructor(data, proxy, handler,
|
|
30
|
+
constructor(data, proxy, handler, applied, hint) {
|
|
31
31
|
this.id = ++ObjectHandle.generator;
|
|
32
32
|
this.data = data;
|
|
33
33
|
this.proxy = proxy || new Proxy(this, handler);
|
|
34
|
-
this.
|
|
34
|
+
this.applied = applied;
|
|
35
35
|
this.editing = undefined;
|
|
36
36
|
this.editors = 0;
|
|
37
37
|
this.hint = hint;
|
|
@@ -16,9 +16,9 @@ export declare class IndicatorImpl extends Indicator {
|
|
|
16
16
|
busyDuration: number;
|
|
17
17
|
readonly internals: {
|
|
18
18
|
whenBusy: Promise<void> | undefined;
|
|
19
|
-
resolveWhenBusy: ((value?: void
|
|
19
|
+
resolveWhenBusy: ((value?: void) => void) | undefined;
|
|
20
20
|
whenIdle: Promise<void> | undefined;
|
|
21
|
-
resolveWhenIdle: ((value?: void
|
|
21
|
+
resolveWhenIdle: ((value?: void) => void) | undefined;
|
|
22
22
|
started: number;
|
|
23
23
|
activationDelay: number;
|
|
24
24
|
activationTimeout: undefined;
|
|
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import { Isolation } from "../Options.js";
|
|
10
11
|
import { ObservableObject, Mvcc } from "./Mvcc.js";
|
|
11
12
|
import { Transaction } from "./Transaction.js";
|
|
12
13
|
export class Indicator extends ObservableObject {
|
|
@@ -80,12 +81,12 @@ export class IndicatorImpl extends Indicator {
|
|
|
80
81
|
mon.leave(worker);
|
|
81
82
|
}
|
|
82
83
|
static doCreate(hint, activationDelay, deactivationDelay, durationResolution) {
|
|
83
|
-
const
|
|
84
|
-
Mvcc.setHint(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
return
|
|
84
|
+
const ind = new IndicatorImpl();
|
|
85
|
+
Mvcc.setHint(ind, hint);
|
|
86
|
+
ind.internals.activationDelay = activationDelay;
|
|
87
|
+
ind.internals.deactivationDelay = deactivationDelay;
|
|
88
|
+
ind.internals.durationResolution = durationResolution;
|
|
89
|
+
return ind;
|
|
89
90
|
}
|
|
90
91
|
static activate(mon, delay) {
|
|
91
92
|
const active = mon.counter > 0;
|
|
@@ -102,7 +103,7 @@ export class IndicatorImpl extends Indicator {
|
|
|
102
103
|
}
|
|
103
104
|
if (delay >= 0) {
|
|
104
105
|
if (mon.internals.activationTimeout === undefined)
|
|
105
|
-
mon.internals.activationTimeout = setTimeout(() => Transaction.run({ hint: "Indicator.activate",
|
|
106
|
+
mon.internals.activationTimeout = setTimeout(() => Transaction.run({ hint: "Indicator.activate", isolation: Isolation.disjoinFromOuterAndInnerTransactions }, IndicatorImpl.activate, mon, -1), delay);
|
|
106
107
|
}
|
|
107
108
|
else if (active)
|
|
108
109
|
mon.isBusy = true;
|
|
@@ -110,7 +111,7 @@ export class IndicatorImpl extends Indicator {
|
|
|
110
111
|
static deactivate(mon, delay) {
|
|
111
112
|
if (delay >= 0) {
|
|
112
113
|
clearTimeout(mon.internals.deactivationTimeout);
|
|
113
|
-
mon.internals.deactivationTimeout = setTimeout(() => Transaction.run({ hint: "Indicator.deactivate",
|
|
114
|
+
mon.internals.deactivationTimeout = setTimeout(() => Transaction.run({ hint: "Indicator.deactivate", isolation: Isolation.disjoinFromOuterAndInnerTransactions }, IndicatorImpl.deactivate, mon, -1), delay);
|
|
114
115
|
}
|
|
115
116
|
else if (mon.counter <= 0) {
|
|
116
117
|
mon.isBusy = false;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ObservableObject } from "./Mvcc.js";
|
|
2
|
-
import { ObjectHandle,
|
|
2
|
+
import { ObjectHandle, ObjectVersion, PatchSet } from "./Data.js";
|
|
3
3
|
export type Saver = (patch: PatchSet) => Promise<void>;
|
|
4
4
|
export declare abstract class Journal extends ObservableObject {
|
|
5
5
|
abstract capacity: number;
|
|
@@ -28,7 +28,7 @@ export declare class JournalImpl extends Journal {
|
|
|
28
28
|
saved(patch: PatchSet): void;
|
|
29
29
|
undo(count?: number): void;
|
|
30
30
|
redo(count?: number): void;
|
|
31
|
-
static buildPatch(hint: string, items: Map<ObjectHandle,
|
|
31
|
+
static buildPatch(hint: string, items: Map<ObjectHandle, ObjectVersion>): PatchSet;
|
|
32
32
|
static applyPatch(patch: PatchSet, undoing: boolean): void;
|
|
33
33
|
mergePatchToUnsaved(patch: PatchSet, undoing: boolean): void;
|
|
34
34
|
}
|