reactronic 0.22.307 → 0.22.310
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 +8 -0
- package/build/dist/source/Controller.d.ts +12 -0
- package/build/dist/source/Controller.js +6 -0
- package/build/dist/source/Logging.d.ts +38 -0
- package/build/dist/source/Logging.js +113 -0
- package/build/dist/source/Options.d.ts +38 -0
- package/build/dist/source/Options.js +21 -0
- package/build/dist/source/Ref.d.ts +34 -0
- package/build/dist/source/Ref.js +90 -0
- package/build/dist/source/Rx.d.ts +27 -0
- package/build/dist/source/Rx.js +58 -0
- package/build/dist/source/Worker.d.ts +8 -0
- package/build/dist/source/Worker.js +2 -0
- package/build/dist/source/api.d.ts +14 -0
- package/build/dist/source/api.js +42 -0
- package/build/dist/source/impl/Changeset.d.ts +60 -0
- package/build/dist/source/impl/Changeset.js +361 -0
- package/build/dist/source/impl/Data.d.ts +60 -0
- package/build/dist/source/impl/Data.js +51 -0
- package/build/dist/source/impl/Hooks.d.ts +96 -0
- package/build/dist/source/impl/Hooks.js +310 -0
- package/build/dist/source/impl/Journal.d.ts +34 -0
- package/build/dist/source/impl/Journal.js +149 -0
- package/build/dist/source/impl/Meta.d.ts +13 -0
- package/build/dist/source/impl/Meta.js +33 -0
- package/build/dist/source/impl/Monitor.d.ts +32 -0
- package/build/dist/source/impl/Monitor.js +97 -0
- package/build/dist/source/impl/Operation.d.ts +93 -0
- package/build/dist/source/impl/Operation.js +722 -0
- package/build/dist/source/impl/Transaction.d.ts +30 -0
- package/build/dist/source/impl/Transaction.js +313 -0
- package/build/dist/source/util/Dbg.d.ts +15 -0
- package/build/dist/source/util/Dbg.js +96 -0
- package/build/dist/source/util/Sealant.d.ts +14 -0
- package/build/dist/source/util/Sealant.js +30 -0
- package/build/dist/source/util/SealedArray.d.ts +16 -0
- package/build/dist/source/util/SealedArray.js +28 -0
- package/build/dist/source/util/SealedMap.d.ts +13 -0
- package/build/dist/source/util/SealedMap.js +21 -0
- package/build/dist/source/util/SealedSet.d.ts +13 -0
- package/build/dist/source/util/SealedSet.js +21 -0
- package/build/dist/source/util/Utils.d.ts +9 -0
- package/build/dist/source/util/Utils.js +62 -0
- package/package.json +11 -11
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.options = exports.cached = exports.reaction = exports.transaction = exports.isnonreactive = exports.sensitive = exports.nonreactive = exports.Rx = void 0;
|
|
4
|
+
const Dbg_1 = require("./util/Dbg");
|
|
5
|
+
const Options_1 = require("./Options");
|
|
6
|
+
const Data_1 = require("./impl/Data");
|
|
7
|
+
const Changeset_1 = require("./impl/Changeset");
|
|
8
|
+
const Hooks_1 = require("./impl/Hooks");
|
|
9
|
+
const Operation_1 = require("./impl/Operation");
|
|
10
|
+
class Rx {
|
|
11
|
+
static getRevisionOf(obj) { return obj[Data_1.Meta.Revision]; }
|
|
12
|
+
static why(brief = false) { return brief ? Operation_1.OperationController.briefWhy() : Operation_1.OperationController.why(); }
|
|
13
|
+
static getController(method) { return Operation_1.OperationController.of(method); }
|
|
14
|
+
static pullLastResult(method, args) { return Rx.getController(method).pullLastResult(args); }
|
|
15
|
+
static configureCurrentOperation(options) { return Operation_1.OperationController.configureImpl(undefined, options); }
|
|
16
|
+
static takeSnapshot(obj) { return Changeset_1.Changeset.takeSnapshot(obj); }
|
|
17
|
+
static dispose(obj) { Changeset_1.Changeset.dispose(obj); }
|
|
18
|
+
static get reactionsAutoStartDisabled() { return Hooks_1.Hooks.reactionsAutoStartDisabled; }
|
|
19
|
+
static set reactionsAutoStartDisabled(value) { Hooks_1.Hooks.reactionsAutoStartDisabled = value; }
|
|
20
|
+
static get isLogging() { return Dbg_1.Log.isOn; }
|
|
21
|
+
static get loggingOptions() { return Dbg_1.Log.opt; }
|
|
22
|
+
static setLoggingMode(isOn, options) { Dbg_1.Log.setMode(isOn, options); }
|
|
23
|
+
static setLoggingHint(obj, name) { Hooks_1.Hooks.setHint(obj, name); }
|
|
24
|
+
static getLoggingHint(obj, full = false) { return Data_1.ObjectHandle.getHint(obj, full); }
|
|
25
|
+
static setProfilingMode(isOn, options) { Hooks_1.Hooks.setProfilingMode(isOn, options); }
|
|
26
|
+
}
|
|
27
|
+
exports.Rx = Rx;
|
|
28
|
+
function nonreactive(func, ...args) {
|
|
29
|
+
return Operation_1.OperationController.runWithin(undefined, func, ...args);
|
|
30
|
+
}
|
|
31
|
+
exports.nonreactive = nonreactive;
|
|
32
|
+
function sensitive(sensitivity, func, ...args) {
|
|
33
|
+
return Hooks_1.Hooks.sensitive(sensitivity, func, ...args);
|
|
34
|
+
}
|
|
35
|
+
exports.sensitive = sensitive;
|
|
36
|
+
function isnonreactive(proto, prop) {
|
|
37
|
+
return Hooks_1.Hooks.decorateData(false, proto, prop);
|
|
38
|
+
}
|
|
39
|
+
exports.isnonreactive = isnonreactive;
|
|
40
|
+
function transaction(proto, prop, pd) {
|
|
41
|
+
const opts = { kind: Options_1.Kind.Transaction };
|
|
42
|
+
return Hooks_1.Hooks.decorateOperation(true, transaction, opts, proto, prop, pd);
|
|
43
|
+
}
|
|
44
|
+
exports.transaction = transaction;
|
|
45
|
+
function reaction(proto, prop, pd) {
|
|
46
|
+
const opts = { kind: Options_1.Kind.Reaction, throttling: -1 };
|
|
47
|
+
return Hooks_1.Hooks.decorateOperation(true, reaction, opts, proto, prop, pd);
|
|
48
|
+
}
|
|
49
|
+
exports.reaction = reaction;
|
|
50
|
+
function cached(proto, prop, pd) {
|
|
51
|
+
const opts = { kind: Options_1.Kind.Cache, noSideEffects: true };
|
|
52
|
+
return Hooks_1.Hooks.decorateOperation(true, cached, opts, proto, prop, pd);
|
|
53
|
+
}
|
|
54
|
+
exports.cached = cached;
|
|
55
|
+
function options(value) {
|
|
56
|
+
return Hooks_1.Hooks.decorateOperationParametrized(options, value);
|
|
57
|
+
}
|
|
58
|
+
exports.options = options;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export { all, pause } from './util/Utils';
|
|
2
|
+
export { SealedArray } from './util/SealedArray';
|
|
3
|
+
export { SealedMap } from './util/SealedMap';
|
|
4
|
+
export { SealedSet } from './util/SealedSet';
|
|
5
|
+
export { MemberOptions, SnapshotOptions, Kind, Reentrance, LoggingOptions, ProfilingOptions, LoggingLevel } from './Options';
|
|
6
|
+
export { Worker } from './Worker';
|
|
7
|
+
export { Controller } from './Controller';
|
|
8
|
+
export { Ref, ToggleRef, BoolOnly, GivenTypeOnly } from './Ref';
|
|
9
|
+
export { ReactiveObject, ReactiveArray, ReactiveMap } from './impl/Hooks';
|
|
10
|
+
export { Changeset } from './impl/Changeset';
|
|
11
|
+
export { Transaction } from './impl/Transaction';
|
|
12
|
+
export { Monitor } from './impl/Monitor';
|
|
13
|
+
export { Journal } from './impl/Journal';
|
|
14
|
+
export { Rx, nonreactive, sensitive, isnonreactive, transaction, reaction, cached, options } from './Rx';
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.options = exports.cached = exports.reaction = exports.transaction = exports.isnonreactive = exports.sensitive = exports.nonreactive = exports.Rx = exports.Journal = exports.Monitor = exports.Transaction = exports.Changeset = exports.ReactiveMap = exports.ReactiveArray = exports.ReactiveObject = exports.ToggleRef = exports.Ref = exports.Controller = exports.LoggingLevel = exports.Reentrance = exports.Kind = exports.SealedSet = exports.SealedMap = exports.SealedArray = exports.pause = exports.all = void 0;
|
|
4
|
+
var Utils_1 = require("./util/Utils");
|
|
5
|
+
Object.defineProperty(exports, "all", { enumerable: true, get: function () { return Utils_1.all; } });
|
|
6
|
+
Object.defineProperty(exports, "pause", { enumerable: true, get: function () { return Utils_1.pause; } });
|
|
7
|
+
var SealedArray_1 = require("./util/SealedArray");
|
|
8
|
+
Object.defineProperty(exports, "SealedArray", { enumerable: true, get: function () { return SealedArray_1.SealedArray; } });
|
|
9
|
+
var SealedMap_1 = require("./util/SealedMap");
|
|
10
|
+
Object.defineProperty(exports, "SealedMap", { enumerable: true, get: function () { return SealedMap_1.SealedMap; } });
|
|
11
|
+
var SealedSet_1 = require("./util/SealedSet");
|
|
12
|
+
Object.defineProperty(exports, "SealedSet", { enumerable: true, get: function () { return SealedSet_1.SealedSet; } });
|
|
13
|
+
var Options_1 = require("./Options");
|
|
14
|
+
Object.defineProperty(exports, "Kind", { enumerable: true, get: function () { return Options_1.Kind; } });
|
|
15
|
+
Object.defineProperty(exports, "Reentrance", { enumerable: true, get: function () { return Options_1.Reentrance; } });
|
|
16
|
+
Object.defineProperty(exports, "LoggingLevel", { enumerable: true, get: function () { return Options_1.LoggingLevel; } });
|
|
17
|
+
var Controller_1 = require("./Controller");
|
|
18
|
+
Object.defineProperty(exports, "Controller", { enumerable: true, get: function () { return Controller_1.Controller; } });
|
|
19
|
+
var Ref_1 = require("./Ref");
|
|
20
|
+
Object.defineProperty(exports, "Ref", { enumerable: true, get: function () { return Ref_1.Ref; } });
|
|
21
|
+
Object.defineProperty(exports, "ToggleRef", { enumerable: true, get: function () { return Ref_1.ToggleRef; } });
|
|
22
|
+
var Hooks_1 = require("./impl/Hooks");
|
|
23
|
+
Object.defineProperty(exports, "ReactiveObject", { enumerable: true, get: function () { return Hooks_1.ReactiveObject; } });
|
|
24
|
+
Object.defineProperty(exports, "ReactiveArray", { enumerable: true, get: function () { return Hooks_1.ReactiveArray; } });
|
|
25
|
+
Object.defineProperty(exports, "ReactiveMap", { enumerable: true, get: function () { return Hooks_1.ReactiveMap; } });
|
|
26
|
+
var Changeset_1 = require("./impl/Changeset");
|
|
27
|
+
Object.defineProperty(exports, "Changeset", { enumerable: true, get: function () { return Changeset_1.Changeset; } });
|
|
28
|
+
var Transaction_1 = require("./impl/Transaction");
|
|
29
|
+
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return Transaction_1.Transaction; } });
|
|
30
|
+
var Monitor_1 = require("./impl/Monitor");
|
|
31
|
+
Object.defineProperty(exports, "Monitor", { enumerable: true, get: function () { return Monitor_1.Monitor; } });
|
|
32
|
+
var Journal_1 = require("./impl/Journal");
|
|
33
|
+
Object.defineProperty(exports, "Journal", { enumerable: true, get: function () { return Journal_1.Journal; } });
|
|
34
|
+
var Rx_1 = require("./Rx");
|
|
35
|
+
Object.defineProperty(exports, "Rx", { enumerable: true, get: function () { return Rx_1.Rx; } });
|
|
36
|
+
Object.defineProperty(exports, "nonreactive", { enumerable: true, get: function () { return Rx_1.nonreactive; } });
|
|
37
|
+
Object.defineProperty(exports, "sensitive", { enumerable: true, get: function () { return Rx_1.sensitive; } });
|
|
38
|
+
Object.defineProperty(exports, "isnonreactive", { enumerable: true, get: function () { return Rx_1.isnonreactive; } });
|
|
39
|
+
Object.defineProperty(exports, "transaction", { enumerable: true, get: function () { return Rx_1.transaction; } });
|
|
40
|
+
Object.defineProperty(exports, "reaction", { enumerable: true, get: function () { return Rx_1.reaction; } });
|
|
41
|
+
Object.defineProperty(exports, "cached", { enumerable: true, get: function () { return Rx_1.cached; } });
|
|
42
|
+
Object.defineProperty(exports, "options", { enumerable: true, get: function () { return Rx_1.options; } });
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Kind, SnapshotOptions } from '../Options';
|
|
2
|
+
import { AbstractChangeset, ObjectSnapshot, MemberName, ObjectHandle, Subscription, Subscriber } from './Data';
|
|
3
|
+
export declare const MAX_REVISION: number;
|
|
4
|
+
export declare const UNDEFINED_REVISION: number;
|
|
5
|
+
export declare class Changeset implements AbstractChangeset {
|
|
6
|
+
static idGen: number;
|
|
7
|
+
private static stampGen;
|
|
8
|
+
private static pending;
|
|
9
|
+
private static oldest;
|
|
10
|
+
static garbageCollectionSummaryInterval: number;
|
|
11
|
+
static lastGarbageCollectionSummaryTimestamp: number;
|
|
12
|
+
static totalObjectHandleCount: number;
|
|
13
|
+
static totalObjectSnapshotCount: number;
|
|
14
|
+
readonly id: number;
|
|
15
|
+
readonly options: SnapshotOptions;
|
|
16
|
+
get hint(): string;
|
|
17
|
+
get timestamp(): number;
|
|
18
|
+
private revision;
|
|
19
|
+
private bumper;
|
|
20
|
+
items: Map<ObjectHandle, ObjectSnapshot>;
|
|
21
|
+
reactions: Subscriber[];
|
|
22
|
+
sealed: boolean;
|
|
23
|
+
constructor(options: SnapshotOptions | null);
|
|
24
|
+
static current: () => Changeset;
|
|
25
|
+
static edit: () => Changeset;
|
|
26
|
+
static markUsed: (subscription: Subscription, os: ObjectSnapshot, m: MemberName, h: ObjectHandle, kind: Kind, weak: boolean) => void;
|
|
27
|
+
static markEdited: (oldValue: any, newValue: any, edited: boolean, os: ObjectSnapshot, m: MemberName, h: ObjectHandle) => void;
|
|
28
|
+
static isConflicting: (oldValue: any, newValue: any) => boolean;
|
|
29
|
+
static propagateAllChangesThroughSubscriptions: (changeset: Changeset) => void;
|
|
30
|
+
static revokeAllSubscriptions: (changeset: Changeset) => void;
|
|
31
|
+
static enqueueReactionsToRun: (reactions: Array<Subscriber>) => void;
|
|
32
|
+
seekSnapshot(h: ObjectHandle, m: MemberName): ObjectSnapshot;
|
|
33
|
+
getRelevantSnapshot(h: ObjectHandle, m: MemberName): ObjectSnapshot;
|
|
34
|
+
getEditableSnapshot(h: ObjectHandle, m: MemberName, value: any, token?: any): ObjectSnapshot;
|
|
35
|
+
static takeSnapshot<T>(obj: T): T;
|
|
36
|
+
static dispose(obj: any): void;
|
|
37
|
+
static doDispose(ctx: Changeset, h: ObjectHandle): ObjectSnapshot;
|
|
38
|
+
private isNewSnapshotRequired;
|
|
39
|
+
acquire(outer: Changeset): void;
|
|
40
|
+
bumpBy(timestamp: number): void;
|
|
41
|
+
rebase(): ObjectSnapshot[] | undefined;
|
|
42
|
+
private merge;
|
|
43
|
+
applyOrDiscard(error?: any): Array<Subscriber>;
|
|
44
|
+
static sealObjectSnapshot(h: ObjectHandle, os: ObjectSnapshot): void;
|
|
45
|
+
static sealSubscription(subscription: Subscription | symbol, m: MemberName, typeName: string): void;
|
|
46
|
+
static freezeObjectSnapshot(os: ObjectSnapshot): ObjectSnapshot;
|
|
47
|
+
triggerGarbageCollection(): void;
|
|
48
|
+
private unlinkHistory;
|
|
49
|
+
static _init(): void;
|
|
50
|
+
}
|
|
51
|
+
export declare class Dump {
|
|
52
|
+
static valueHint: (value: any, m?: MemberName) => string;
|
|
53
|
+
static obj(h: ObjectHandle | undefined, m?: MemberName | undefined, stamp?: number, snapshotId?: number, originSnapshotId?: number, value?: any): string;
|
|
54
|
+
static snapshot2(h: ObjectHandle, s: AbstractChangeset, m?: MemberName, o?: Subscription): string;
|
|
55
|
+
static snapshot(os: ObjectSnapshot, m?: MemberName): string;
|
|
56
|
+
static conflicts(conflicts: ObjectSnapshot[]): string;
|
|
57
|
+
static conflictingMemberHint(m: MemberName, ours: ObjectSnapshot, theirs: ObjectSnapshot): string;
|
|
58
|
+
}
|
|
59
|
+
export declare const EMPTY_SNAPSHOT: ObjectSnapshot;
|
|
60
|
+
export declare const DefaultSnapshotOptions: SnapshotOptions;
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DefaultSnapshotOptions = exports.EMPTY_SNAPSHOT = exports.Dump = exports.Changeset = exports.UNDEFINED_REVISION = exports.MAX_REVISION = void 0;
|
|
4
|
+
const Utils_1 = require("../util/Utils");
|
|
5
|
+
const Dbg_1 = require("../util/Dbg");
|
|
6
|
+
const Sealant_1 = require("../util/Sealant");
|
|
7
|
+
const SealedArray_1 = require("../util/SealedArray");
|
|
8
|
+
const SealedMap_1 = require("../util/SealedMap");
|
|
9
|
+
const SealedSet_1 = require("../util/SealedSet");
|
|
10
|
+
const Data_1 = require("./Data");
|
|
11
|
+
exports.MAX_REVISION = Number.MAX_SAFE_INTEGER;
|
|
12
|
+
exports.UNDEFINED_REVISION = exports.MAX_REVISION - 1;
|
|
13
|
+
Object.defineProperty(Data_1.ObjectHandle.prototype, '#this', {
|
|
14
|
+
configurable: false, enumerable: false,
|
|
15
|
+
get() {
|
|
16
|
+
const result = {};
|
|
17
|
+
const data = Changeset.current().getRelevantSnapshot(this, '#this').data;
|
|
18
|
+
for (const m in data) {
|
|
19
|
+
const v = data[m];
|
|
20
|
+
if (v instanceof Data_1.Subscription)
|
|
21
|
+
result[m] = v.content;
|
|
22
|
+
else if (v === Data_1.Meta.Nonreactive)
|
|
23
|
+
result[m] = this.data[m];
|
|
24
|
+
else
|
|
25
|
+
result[m] = v;
|
|
26
|
+
}
|
|
27
|
+
return result;
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
const EMPTY_ARRAY = Object.freeze([]);
|
|
31
|
+
const EMPTY_MAP = Utils_1.Utils.freezeMap(new Map());
|
|
32
|
+
class Changeset {
|
|
33
|
+
constructor(options) {
|
|
34
|
+
this.id = ++Changeset.idGen;
|
|
35
|
+
this.options = options !== null && options !== void 0 ? options : exports.DefaultSnapshotOptions;
|
|
36
|
+
this.revision = exports.UNDEFINED_REVISION;
|
|
37
|
+
this.bumper = 100;
|
|
38
|
+
this.items = new Map();
|
|
39
|
+
this.reactions = [];
|
|
40
|
+
this.sealed = false;
|
|
41
|
+
}
|
|
42
|
+
get hint() { var _a; return (_a = this.options.hint) !== null && _a !== void 0 ? _a : 'noname'; }
|
|
43
|
+
get timestamp() { return this.revision; }
|
|
44
|
+
seekSnapshot(h, m) {
|
|
45
|
+
let os = h.editing;
|
|
46
|
+
if (os && os.changeset !== this) {
|
|
47
|
+
os = this.items.get(h);
|
|
48
|
+
if (os)
|
|
49
|
+
h.editing = os;
|
|
50
|
+
}
|
|
51
|
+
if (!os) {
|
|
52
|
+
os = h.head;
|
|
53
|
+
while (os !== exports.EMPTY_SNAPSHOT && os.changeset.timestamp > this.timestamp)
|
|
54
|
+
os = os.former.snapshot;
|
|
55
|
+
}
|
|
56
|
+
return os;
|
|
57
|
+
}
|
|
58
|
+
getRelevantSnapshot(h, m) {
|
|
59
|
+
const r = this.seekSnapshot(h, m);
|
|
60
|
+
if (r === exports.EMPTY_SNAPSHOT)
|
|
61
|
+
throw (0, Dbg_1.misuse)(`object ${Dump.obj(h)} doesn't exist in snapshot v${this.revision} (${this.hint})`);
|
|
62
|
+
return r;
|
|
63
|
+
}
|
|
64
|
+
getEditableSnapshot(h, m, value, token) {
|
|
65
|
+
let os = this.seekSnapshot(h, m);
|
|
66
|
+
const existing = os.data[m];
|
|
67
|
+
if (existing !== Data_1.Meta.Nonreactive) {
|
|
68
|
+
if (this.isNewSnapshotRequired(h, os, m, existing, value, token)) {
|
|
69
|
+
this.bumpBy(os.changeset.timestamp);
|
|
70
|
+
const revision = m === Data_1.Meta.Handle ? 1 : os.revision + 1;
|
|
71
|
+
const data = Object.assign({}, m === Data_1.Meta.Handle ? value : os.data);
|
|
72
|
+
Data_1.Meta.set(data, Data_1.Meta.Handle, h);
|
|
73
|
+
Data_1.Meta.set(data, Data_1.Meta.Revision, new Data_1.Subscription(revision));
|
|
74
|
+
os = new Data_1.ObjectSnapshot(this, os, data);
|
|
75
|
+
this.items.set(h, os);
|
|
76
|
+
h.editing = os;
|
|
77
|
+
h.editors++;
|
|
78
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.write)
|
|
79
|
+
Dbg_1.Log.write('║', ' ⎘⎘', `${Dump.obj(h)} - new snapshot is created (revision ${revision})`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else
|
|
83
|
+
os = exports.EMPTY_SNAPSHOT;
|
|
84
|
+
return os;
|
|
85
|
+
}
|
|
86
|
+
static takeSnapshot(obj) {
|
|
87
|
+
return obj[Data_1.Meta.Handle]['#this'];
|
|
88
|
+
}
|
|
89
|
+
static dispose(obj) {
|
|
90
|
+
const ctx = Changeset.edit();
|
|
91
|
+
const h = Data_1.Meta.get(obj, Data_1.Meta.Handle);
|
|
92
|
+
if (h !== undefined)
|
|
93
|
+
Changeset.doDispose(ctx, h);
|
|
94
|
+
}
|
|
95
|
+
static doDispose(ctx, h) {
|
|
96
|
+
const os = ctx.getEditableSnapshot(h, Data_1.Meta.Revision, Data_1.Meta.Undefined);
|
|
97
|
+
if (os !== exports.EMPTY_SNAPSHOT)
|
|
98
|
+
os.disposed = true;
|
|
99
|
+
return os;
|
|
100
|
+
}
|
|
101
|
+
isNewSnapshotRequired(h, os, m, existing, value, token) {
|
|
102
|
+
if (this.sealed && os.changeset !== exports.EMPTY_SNAPSHOT.changeset)
|
|
103
|
+
throw (0, Dbg_1.misuse)(`reactive property ${Dump.obj(h, m)} can only be modified inside transaction`);
|
|
104
|
+
if (m !== Data_1.Meta.Handle && value !== Data_1.Meta.Handle) {
|
|
105
|
+
if (os.changeset !== this || os.former.snapshot !== exports.EMPTY_SNAPSHOT) {
|
|
106
|
+
if (this.options.token !== undefined && token !== this.options.token)
|
|
107
|
+
throw (0, Dbg_1.misuse)(`${this.hint} should not have side effects (trying to change ${Dump.snapshot(os, m)})`);
|
|
108
|
+
}
|
|
109
|
+
if (os === exports.EMPTY_SNAPSHOT)
|
|
110
|
+
throw (0, Dbg_1.misuse)(`member ${Dump.snapshot(os, m)} doesn't exist in snapshot v${this.revision} (${this.hint})`);
|
|
111
|
+
}
|
|
112
|
+
return os.changeset !== this && !this.sealed;
|
|
113
|
+
}
|
|
114
|
+
acquire(outer) {
|
|
115
|
+
if (!this.sealed && this.revision === exports.UNDEFINED_REVISION) {
|
|
116
|
+
const ahead = this.options.token === undefined || outer.revision === exports.UNDEFINED_REVISION;
|
|
117
|
+
this.revision = ahead ? Changeset.stampGen : outer.revision;
|
|
118
|
+
Changeset.pending.push(this);
|
|
119
|
+
if (Changeset.oldest === undefined)
|
|
120
|
+
Changeset.oldest = this;
|
|
121
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.transaction)
|
|
122
|
+
Dbg_1.Log.write('╔══', `v${this.revision}`, `${this.hint}`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
bumpBy(timestamp) {
|
|
126
|
+
if (timestamp > this.bumper)
|
|
127
|
+
this.bumper = timestamp;
|
|
128
|
+
}
|
|
129
|
+
rebase() {
|
|
130
|
+
let conflicts = undefined;
|
|
131
|
+
if (this.items.size > 0) {
|
|
132
|
+
this.items.forEach((os, h) => {
|
|
133
|
+
if (os.former.snapshot !== h.head) {
|
|
134
|
+
const merged = this.merge(h, os);
|
|
135
|
+
if (os.conflicts.size > 0) {
|
|
136
|
+
if (!conflicts)
|
|
137
|
+
conflicts = [];
|
|
138
|
+
conflicts.push(os);
|
|
139
|
+
}
|
|
140
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.transaction)
|
|
141
|
+
Dbg_1.Log.write('╠╝', '', `${Dump.snapshot2(h, os.changeset)} is merged with ${Dump.snapshot2(h, h.head.changeset)} among ${merged} properties with ${os.conflicts.size} conflicts.`);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
if (this.options.token === undefined) {
|
|
145
|
+
if (this.bumper > 100) {
|
|
146
|
+
this.bumper = this.revision;
|
|
147
|
+
this.revision = ++Changeset.stampGen;
|
|
148
|
+
}
|
|
149
|
+
else
|
|
150
|
+
this.revision = this.bumper + 1;
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
this.revision = this.bumper;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return conflicts;
|
|
157
|
+
}
|
|
158
|
+
merge(h, ours) {
|
|
159
|
+
let counter = 0;
|
|
160
|
+
const head = h.head;
|
|
161
|
+
const headDisposed = head.disposed;
|
|
162
|
+
const oursDisposed = ours.disposed;
|
|
163
|
+
const merged = Object.assign({}, head.data);
|
|
164
|
+
ours.changes.forEach((o, m) => {
|
|
165
|
+
counter++;
|
|
166
|
+
merged[m] = ours.data[m];
|
|
167
|
+
if (headDisposed || oursDisposed) {
|
|
168
|
+
if (headDisposed !== oursDisposed) {
|
|
169
|
+
if (headDisposed || this.options.standalone !== 'disposal') {
|
|
170
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.change)
|
|
171
|
+
Dbg_1.Log.write('║╠', '', `${Dump.snapshot2(h, ours.changeset, m)} <> ${Dump.snapshot2(h, head.changeset, m)}`, 0, ' *** CONFLICT ***');
|
|
172
|
+
ours.conflicts.set(m, head);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const conflict = Changeset.isConflicting(head.data[m], ours.former.snapshot.data[m]);
|
|
178
|
+
if (conflict)
|
|
179
|
+
ours.conflicts.set(m, head);
|
|
180
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.change)
|
|
181
|
+
Dbg_1.Log.write('║╠', '', `${Dump.snapshot2(h, ours.changeset, m)} ${conflict ? '<>' : '=='} ${Dump.snapshot2(h, head.changeset, m)}`, 0, conflict ? ' *** CONFLICT ***' : undefined);
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
Utils_1.Utils.copyAllMembers(merged, ours.data);
|
|
185
|
+
ours.former.snapshot = head;
|
|
186
|
+
return counter;
|
|
187
|
+
}
|
|
188
|
+
applyOrDiscard(error) {
|
|
189
|
+
this.sealed = true;
|
|
190
|
+
this.items.forEach((os, h) => {
|
|
191
|
+
Changeset.sealObjectSnapshot(h, os);
|
|
192
|
+
h.editors--;
|
|
193
|
+
if (h.editors === 0)
|
|
194
|
+
h.editing = undefined;
|
|
195
|
+
if (!error) {
|
|
196
|
+
h.head = os;
|
|
197
|
+
if (Changeset.garbageCollectionSummaryInterval < Number.MAX_SAFE_INTEGER) {
|
|
198
|
+
Changeset.totalObjectSnapshotCount++;
|
|
199
|
+
if (os.former.snapshot === exports.EMPTY_SNAPSHOT)
|
|
200
|
+
Changeset.totalObjectHandleCount++;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
if (Dbg_1.Log.isOn) {
|
|
205
|
+
if (Dbg_1.Log.opt.change && !error) {
|
|
206
|
+
this.items.forEach((os, h) => {
|
|
207
|
+
const members = [];
|
|
208
|
+
os.changes.forEach((o, m) => members.push(m.toString()));
|
|
209
|
+
const s = members.join(', ');
|
|
210
|
+
Dbg_1.Log.write('║', '√', `${Dump.snapshot2(h, os.changeset)} (${s}) is ${os.former.snapshot === exports.EMPTY_SNAPSHOT ? 'constructed' : `applied on top of ${Dump.snapshot2(h, os.former.snapshot.changeset)}`}`);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
if (Dbg_1.Log.opt.transaction)
|
|
214
|
+
Dbg_1.Log.write(this.revision < exports.UNDEFINED_REVISION ? '╚══' : '═══', `v${this.revision}`, `${this.hint} - ${error ? 'CANCEL' : 'APPLY'}(${this.items.size})${error ? ` - ${error}` : ''}`);
|
|
215
|
+
}
|
|
216
|
+
if (!error)
|
|
217
|
+
Changeset.propagateAllChangesThroughSubscriptions(this);
|
|
218
|
+
return this.reactions;
|
|
219
|
+
}
|
|
220
|
+
static sealObjectSnapshot(h, os) {
|
|
221
|
+
if (!os.disposed)
|
|
222
|
+
os.changes.forEach((o, m) => Changeset.sealSubscription(os.data[m], m, h.proxy.constructor.name));
|
|
223
|
+
else
|
|
224
|
+
for (const m in os.former.snapshot.data)
|
|
225
|
+
os.data[m] = Data_1.Meta.Undefined;
|
|
226
|
+
if (Dbg_1.Log.isOn)
|
|
227
|
+
Changeset.freezeObjectSnapshot(os);
|
|
228
|
+
}
|
|
229
|
+
static sealSubscription(subscription, m, typeName) {
|
|
230
|
+
if (subscription instanceof Data_1.Subscription) {
|
|
231
|
+
const value = subscription.content;
|
|
232
|
+
if (value !== undefined && value !== null) {
|
|
233
|
+
const sealedType = Object.getPrototypeOf(value)[Sealant_1.Sealant.SealedType];
|
|
234
|
+
if (sealedType)
|
|
235
|
+
subscription.content = Sealant_1.Sealant.seal(value, sealedType, typeName, m);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
static freezeObjectSnapshot(os) {
|
|
240
|
+
Object.freeze(os.data);
|
|
241
|
+
Utils_1.Utils.freezeSet(os.changes);
|
|
242
|
+
Utils_1.Utils.freezeMap(os.conflicts);
|
|
243
|
+
return os;
|
|
244
|
+
}
|
|
245
|
+
triggerGarbageCollection() {
|
|
246
|
+
if (this.revision !== 0) {
|
|
247
|
+
if (this === Changeset.oldest) {
|
|
248
|
+
const p = Changeset.pending;
|
|
249
|
+
p.sort((a, b) => a.revision - b.revision);
|
|
250
|
+
let i = 0;
|
|
251
|
+
while (i < p.length && p[i].sealed) {
|
|
252
|
+
p[i].unlinkHistory();
|
|
253
|
+
i++;
|
|
254
|
+
}
|
|
255
|
+
Changeset.pending = p.slice(i);
|
|
256
|
+
Changeset.oldest = Changeset.pending[0];
|
|
257
|
+
const now = Date.now();
|
|
258
|
+
if (now - Changeset.lastGarbageCollectionSummaryTimestamp > Changeset.garbageCollectionSummaryInterval) {
|
|
259
|
+
Dbg_1.Log.write('', '[G]', `Total object/snapshot count: ${Changeset.totalObjectHandleCount}/${Changeset.totalObjectSnapshotCount}`);
|
|
260
|
+
Changeset.lastGarbageCollectionSummaryTimestamp = now;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
unlinkHistory() {
|
|
266
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.gc)
|
|
267
|
+
Dbg_1.Log.write('', '[G]', `Dismiss history below v${this.revision}t${this.id} (${this.hint})`);
|
|
268
|
+
this.items.forEach((os, h) => {
|
|
269
|
+
if (Dbg_1.Log.isOn && Dbg_1.Log.opt.gc && os.former.snapshot !== exports.EMPTY_SNAPSHOT)
|
|
270
|
+
Dbg_1.Log.write(' ', ' ', `${Dump.snapshot2(h, os.former.snapshot.changeset)} is ready for GC because overwritten by ${Dump.snapshot2(h, os.changeset)}`);
|
|
271
|
+
if (Changeset.garbageCollectionSummaryInterval < Number.MAX_SAFE_INTEGER) {
|
|
272
|
+
if (os.former.snapshot !== exports.EMPTY_SNAPSHOT)
|
|
273
|
+
Changeset.totalObjectSnapshotCount--;
|
|
274
|
+
if (os.disposed)
|
|
275
|
+
Changeset.totalObjectHandleCount--;
|
|
276
|
+
}
|
|
277
|
+
os.former.snapshot = exports.EMPTY_SNAPSHOT;
|
|
278
|
+
});
|
|
279
|
+
this.items = EMPTY_MAP;
|
|
280
|
+
this.reactions = EMPTY_ARRAY;
|
|
281
|
+
if (Dbg_1.Log.isOn)
|
|
282
|
+
Object.freeze(this);
|
|
283
|
+
}
|
|
284
|
+
static _init() {
|
|
285
|
+
const boot = exports.EMPTY_SNAPSHOT.changeset;
|
|
286
|
+
boot.acquire(boot);
|
|
287
|
+
boot.applyOrDiscard();
|
|
288
|
+
boot.triggerGarbageCollection();
|
|
289
|
+
Changeset.freezeObjectSnapshot(exports.EMPTY_SNAPSHOT);
|
|
290
|
+
Changeset.idGen = 100;
|
|
291
|
+
Changeset.stampGen = 101;
|
|
292
|
+
Changeset.oldest = undefined;
|
|
293
|
+
SealedArray_1.SealedArray.prototype;
|
|
294
|
+
SealedMap_1.SealedMap.prototype;
|
|
295
|
+
SealedSet_1.SealedSet.prototype;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
exports.Changeset = Changeset;
|
|
299
|
+
Changeset.idGen = -1;
|
|
300
|
+
Changeset.stampGen = 1;
|
|
301
|
+
Changeset.pending = [];
|
|
302
|
+
Changeset.oldest = undefined;
|
|
303
|
+
Changeset.garbageCollectionSummaryInterval = Number.MAX_SAFE_INTEGER;
|
|
304
|
+
Changeset.lastGarbageCollectionSummaryTimestamp = Date.now();
|
|
305
|
+
Changeset.totalObjectHandleCount = 0;
|
|
306
|
+
Changeset.totalObjectSnapshotCount = 0;
|
|
307
|
+
Changeset.current = Utils_1.UNDEF;
|
|
308
|
+
Changeset.edit = Utils_1.UNDEF;
|
|
309
|
+
Changeset.markUsed = Utils_1.UNDEF;
|
|
310
|
+
Changeset.markEdited = Utils_1.UNDEF;
|
|
311
|
+
Changeset.isConflicting = Utils_1.UNDEF;
|
|
312
|
+
Changeset.propagateAllChangesThroughSubscriptions = (changeset) => { };
|
|
313
|
+
Changeset.revokeAllSubscriptions = (changeset) => { };
|
|
314
|
+
Changeset.enqueueReactionsToRun = (reactions) => { };
|
|
315
|
+
class Dump {
|
|
316
|
+
static obj(h, m, stamp, snapshotId, originSnapshotId, value) {
|
|
317
|
+
const member = m !== undefined ? `.${m.toString()}` : '';
|
|
318
|
+
let result;
|
|
319
|
+
if (h !== undefined) {
|
|
320
|
+
const v = value !== undefined && value !== Data_1.Meta.Undefined ? `[=${Dump.valueHint(value)}]` : '';
|
|
321
|
+
if (stamp === undefined)
|
|
322
|
+
result = `${h.hint}${member}${v} #${h.id}`;
|
|
323
|
+
else
|
|
324
|
+
result = `${h.hint}${member}${v} #${h.id}t${snapshotId}v${stamp}${originSnapshotId !== undefined && originSnapshotId !== 0 ? `t${originSnapshotId}` : ''}`;
|
|
325
|
+
}
|
|
326
|
+
else
|
|
327
|
+
result = `boot${member}`;
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
static snapshot2(h, s, m, o) {
|
|
331
|
+
var _a;
|
|
332
|
+
return Dump.obj(h, m, s.timestamp, s.id, o === null || o === void 0 ? void 0 : o.originSnapshotId, (_a = o === null || o === void 0 ? void 0 : o.content) !== null && _a !== void 0 ? _a : Data_1.Meta.Undefined);
|
|
333
|
+
}
|
|
334
|
+
static snapshot(os, m) {
|
|
335
|
+
const h = Data_1.Meta.get(os.data, Data_1.Meta.Handle);
|
|
336
|
+
const value = m !== undefined ? os.data[m] : undefined;
|
|
337
|
+
return Dump.obj(h, m, os.changeset.timestamp, os.changeset.id, value === null || value === void 0 ? void 0 : value.originSnapshotId);
|
|
338
|
+
}
|
|
339
|
+
static conflicts(conflicts) {
|
|
340
|
+
return conflicts.map(ours => {
|
|
341
|
+
const items = [];
|
|
342
|
+
ours.conflicts.forEach((theirs, m) => {
|
|
343
|
+
items.push(Dump.conflictingMemberHint(m, ours, theirs));
|
|
344
|
+
});
|
|
345
|
+
return items.join(', ');
|
|
346
|
+
}).join(', ');
|
|
347
|
+
}
|
|
348
|
+
static conflictingMemberHint(m, ours, theirs) {
|
|
349
|
+
return `${theirs.changeset.hint} (${Dump.snapshot(theirs, m)})`;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
exports.Dump = Dump;
|
|
353
|
+
Dump.valueHint = (value, m) => '???';
|
|
354
|
+
exports.EMPTY_SNAPSHOT = new Data_1.ObjectSnapshot(new Changeset({ hint: '<empty>' }), undefined, {});
|
|
355
|
+
exports.DefaultSnapshotOptions = Object.freeze({
|
|
356
|
+
hint: 'noname',
|
|
357
|
+
standalone: false,
|
|
358
|
+
journal: undefined,
|
|
359
|
+
logging: undefined,
|
|
360
|
+
token: undefined,
|
|
361
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export { Meta } from './Meta';
|
|
2
|
+
export interface AbstractChangeset {
|
|
3
|
+
readonly id: number;
|
|
4
|
+
readonly hint: string;
|
|
5
|
+
readonly timestamp: number;
|
|
6
|
+
readonly sealed: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare class Subscription {
|
|
9
|
+
content: any;
|
|
10
|
+
subscribers?: Set<Subscriber>;
|
|
11
|
+
get isOperation(): boolean;
|
|
12
|
+
get originSnapshotId(): number | undefined;
|
|
13
|
+
constructor(content: any);
|
|
14
|
+
}
|
|
15
|
+
export declare type StandaloneMode = boolean | 'isolated' | 'disposal';
|
|
16
|
+
export interface Subscriber {
|
|
17
|
+
readonly order: number;
|
|
18
|
+
readonly subscriptions: Map<Subscription, SubscriptionInfo> | undefined;
|
|
19
|
+
readonly obsoleteSince: number;
|
|
20
|
+
hint(nop?: boolean): string;
|
|
21
|
+
markObsoleteDueTo(subscription: Subscription, m: MemberName, changeset: AbstractChangeset, h: ObjectHandle, outer: string, since: number, reactions: Array<Subscriber>): void;
|
|
22
|
+
runIfNotUpToDate(now: boolean, nothrow: boolean): void;
|
|
23
|
+
}
|
|
24
|
+
export declare type MemberName = PropertyKey;
|
|
25
|
+
export interface SubscriptionInfo {
|
|
26
|
+
readonly memberHint: string;
|
|
27
|
+
readonly usageCount: number;
|
|
28
|
+
}
|
|
29
|
+
export declare class ObjectSnapshot {
|
|
30
|
+
readonly changeset: AbstractChangeset;
|
|
31
|
+
readonly former: {
|
|
32
|
+
snapshot: ObjectSnapshot;
|
|
33
|
+
};
|
|
34
|
+
readonly data: any;
|
|
35
|
+
readonly changes: Set<MemberName>;
|
|
36
|
+
readonly conflicts: Map<MemberName, ObjectSnapshot>;
|
|
37
|
+
constructor(changeset: AbstractChangeset, former: ObjectSnapshot | undefined, data: object);
|
|
38
|
+
get revision(): number;
|
|
39
|
+
get disposed(): boolean;
|
|
40
|
+
set disposed(value: boolean);
|
|
41
|
+
}
|
|
42
|
+
export declare class ObjectHandle {
|
|
43
|
+
private static generator;
|
|
44
|
+
readonly id: number;
|
|
45
|
+
readonly data: any;
|
|
46
|
+
readonly proxy: any;
|
|
47
|
+
head: ObjectSnapshot;
|
|
48
|
+
editing?: ObjectSnapshot;
|
|
49
|
+
editors: number;
|
|
50
|
+
hint: string;
|
|
51
|
+
constructor(data: any, proxy: any, handler: ProxyHandler<ObjectHandle>, head: ObjectSnapshot, hint: string);
|
|
52
|
+
static getHint(obj: object, full: boolean): string | undefined;
|
|
53
|
+
}
|
|
54
|
+
export declare type PatchSet = Map<object, Map<MemberName, ValuePatch>>;
|
|
55
|
+
export interface ValuePatch {
|
|
56
|
+
memberName: MemberName;
|
|
57
|
+
patchKind: 'update' | 'add' | 'remove';
|
|
58
|
+
freshValue: any;
|
|
59
|
+
formerValue: any;
|
|
60
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ObjectHandle = exports.ObjectSnapshot = exports.Subscription = exports.Meta = void 0;
|
|
4
|
+
const Dbg_1 = require("../util/Dbg");
|
|
5
|
+
const Meta_1 = require("./Meta");
|
|
6
|
+
var Meta_2 = require("./Meta");
|
|
7
|
+
Object.defineProperty(exports, "Meta", { enumerable: true, get: function () { return Meta_2.Meta; } });
|
|
8
|
+
class Subscription {
|
|
9
|
+
constructor(content) { this.content = content; }
|
|
10
|
+
get isOperation() { return false; }
|
|
11
|
+
get originSnapshotId() { return 0; }
|
|
12
|
+
}
|
|
13
|
+
exports.Subscription = Subscription;
|
|
14
|
+
class ObjectSnapshot {
|
|
15
|
+
constructor(changeset, former, data) {
|
|
16
|
+
this.changeset = changeset;
|
|
17
|
+
this.former = { snapshot: former || this };
|
|
18
|
+
this.data = data;
|
|
19
|
+
this.changes = new Set();
|
|
20
|
+
this.conflicts = new Map();
|
|
21
|
+
if (Dbg_1.Log.isOn)
|
|
22
|
+
Object.freeze(this);
|
|
23
|
+
}
|
|
24
|
+
get revision() {
|
|
25
|
+
return this.data[Meta_1.Meta.Revision].content;
|
|
26
|
+
}
|
|
27
|
+
get disposed() { return this.revision < 0; }
|
|
28
|
+
set disposed(value) {
|
|
29
|
+
const rev = this.revision;
|
|
30
|
+
if (rev < 0 !== value)
|
|
31
|
+
this.data[Meta_1.Meta.Revision].content = ~rev;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.ObjectSnapshot = ObjectSnapshot;
|
|
35
|
+
class ObjectHandle {
|
|
36
|
+
constructor(data, proxy, handler, head, hint) {
|
|
37
|
+
this.id = ++ObjectHandle.generator;
|
|
38
|
+
this.data = data;
|
|
39
|
+
this.proxy = proxy || new Proxy(this, handler);
|
|
40
|
+
this.head = head;
|
|
41
|
+
this.editing = undefined;
|
|
42
|
+
this.editors = 0;
|
|
43
|
+
this.hint = hint;
|
|
44
|
+
}
|
|
45
|
+
static getHint(obj, full) {
|
|
46
|
+
const h = Meta_1.Meta.get(obj, Meta_1.Meta.Handle);
|
|
47
|
+
return h !== undefined ? (full ? `${h.hint}#${h.id}` : h.hint) : undefined;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.ObjectHandle = ObjectHandle;
|
|
51
|
+
ObjectHandle.generator = 19;
|