pushinka
Version:
Pushinka: Transactional, Reactive, and Asynchronous State Management
1,073 lines • 44.5 kB
JavaScript
"use strict";
var __extends = (this && this.__extends) || (function () {
var extendStatics = function (d, b) {
extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return extendStatics(d, b);
};
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __generator = (this && this.__generator) || function (thisArg, body) {
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
case 0: case 1: t = op; break;
case 4: _.label++; return { value: op[1], done: false };
case 5: _.label++; y = op[1]; op = [0]; continue;
case 7: op = _.ops.pop(); _.trys.pop(); continue;
default:
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
if (t[2]) _.ops.pop();
_.trys.pop(); continue;
}
op = body.call(thisArg, _);
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
}
};
Object.defineProperty(exports, "__esModule", { value: true });
var Utils_1 = require("./Utils");
var Log_1 = require("./Log");
var Member_1 = require("./Member");
var Record_1 = require("./Record");
var Handle_1 = require("./Handle");
var Monitor_1 = require("./Monitor");
function state(proto, prop) {
return prop ? Hooks.decorateField(state, proto, prop) : Hooks.decorateClass(state, proto);
}
exports.state = state;
function stateless(proto, prop) {
return Hooks.decorateField(stateless, proto, prop);
}
exports.stateless = stateless;
function transaction(proto, prop, pd) {
return Hooks.decorateMethod(transaction, proto, prop, pd);
}
exports.transaction = transaction;
function separateTransaction(proto, prop, pd) {
return Hooks.decorateMethod(separateTransaction, proto, prop, pd);
}
exports.separateTransaction = separateTransaction;
function reaction(proto, prop, pd) {
return Hooks.decorateMethod(reaction, proto, prop, pd);
}
exports.reaction = reaction;
var Transaction = (function () {
function Transaction(hint) {
this.id = 0;
this._snapshot = 0;
this.changeset = new Map();
this.pending = 0;
this.sealed = false;
this._error = undefined;
this.resultPromise = undefined;
this.resultResolve = Utils_1.forbidden;
this.resultReject = Utils_1.forbidden;
this.conflicts = undefined;
this.reactions = [];
this.effect = { tran: undefined };
this.id = ++Transaction.id;
this.hint = hint;
}
Object.defineProperty(Transaction.prototype, "snapshot", {
get: function () { return this._snapshot; },
enumerable: true,
configurable: true
});
Object.defineProperty(Transaction.prototype, "error", {
get: function () { return this._error; },
enumerable: true,
configurable: true
});
Object.defineProperty(Transaction, "current", {
get: function () { return Transaction.running; },
enumerable: true,
configurable: true
});
Transaction.prototype.run = function (func) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (this.sealed && Transaction.running !== this)
throw new Error("E601: cannot run sealed transaction");
return this._run.apply(this, [func].concat(args));
};
Transaction.prototype.commit = function () {
if (this.pending > 0)
throw new Error("E602: cannot commit transaction having pending async operations");
if (this._error)
throw new Error("E603: cannot commit discarded transaction: " + this._error);
this.seal();
};
Transaction.prototype.seal = function () {
if (!this.sealed)
this.run(Transaction.seal, this);
return this;
};
Transaction.prototype.discard = function (error) {
if (error === void 0) { error = "transaction discarded"; }
if (!this._error)
this._error = error;
if (!this.sealed)
this.run(Transaction.seal, this);
return this;
};
Transaction.prototype.finished = function () {
return this.sealed && this.pending === 0;
};
Transaction.prototype.whenFinished = function (includingReactions) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
switch (_a.label) {
case 0:
if (!!this.finished()) return [3, 2];
return [4, this.acquirePromise()];
case 1:
_a.sent();
_a.label = 2;
case 2:
if (!(includingReactions && this.effect.tran)) return [3, 4];
return [4, this.effect.tran.whenFinished(true)];
case 3:
_a.sent();
_a.label = 4;
case 4: return [2];
}
});
});
};
Transaction.prototype.undo = function () {
var _this = this;
Transaction.runAs("Tran#" + this.hint + ".undo", false, function () {
_this.changeset.forEach(function (r, h) {
r.edits.forEach(function (prop) {
if (r.prev.backup) {
var prevValue = r.prev.backup.data[prop];
var t = Transaction.tryGetForEdit(h, prop, prevValue);
if (t)
t.data[prop] = prevValue;
}
});
});
});
};
Transaction.run = function (func) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
return Transaction.runAs.apply(Transaction, ["noname", false, func].concat(args));
};
Transaction.runAs = function (hint, root, func) {
var args = [];
for (var _i = 3; _i < arguments.length; _i++) {
args[_i - 3] = arguments[_i];
}
var t = (root || !Transaction.running) ? new Transaction(hint) : Transaction.running;
root = t !== Transaction.running;
var result;
try {
result = t.run.apply(t, [func].concat(args));
if (root) {
if (result instanceof Promise)
result = t.whenFinished(false);
t.seal();
}
}
catch (error) {
t.discard(error);
throw error;
}
if (t._error)
throw t._error;
return result;
};
Transaction.prototype._run = function (func) {
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
var outer = Transaction.running;
var result;
try {
this.pending++;
Transaction.running = this;
result = func.apply(void 0, args);
if (this.sealed && this.pending === 1 && !this._error)
this.checkConflictsResolved();
}
catch (e) {
this._error = this._error || e;
throw e;
}
finally {
this.pending--;
if (this.finished()) {
!this._error ? this.performCommit() : this.performDiscard();
Utils_1.Utils.freezeMap(this.changeset);
Object.freeze(this);
}
Transaction.running = outer;
}
if (this.reactions.length > 0) {
try {
Cache.ensureAllUpToDate(this.hint, this.reactions, this.effect);
}
finally {
if (!this.finished())
this.reactions = [];
}
}
return result;
};
Transaction.seal = function (t) {
t.sealed = true;
};
Transaction.prototype.getRelevant = function (h) {
this.acquireSnapshot();
var result = h.editing;
if (result && result.tran !== this) {
result = this.changeset.get(h);
if (result)
h.editing = result;
}
if (!result) {
result = h.head;
while (result && result.tran.snapshot > this.snapshot)
result = result.prev.record;
}
return result;
};
Transaction.getForView = function (h) {
var t = Transaction.running;
var result = t ? t.getRelevant(h) : h.head;
if (!result)
throw new Error("E607: internal error");
return result;
};
Transaction.tryGetForEdit = function (h, prop, value) {
var c = Cache.running;
if (c && c.member.decorator === reaction && c.owner !== h && !(h.proxy instanceof Monitor) && value !== P_HANDLE)
throw new Error("E608: object cannot be changed outside of reaction");
var t = Transaction.running;
if (!t)
throw new Error("E609: object cannot be changed outside of transaction");
var r = t.getRelevant(h);
if (!r || !Tools.equal(r.data[prop], value)) {
var data = r ? r.data : value;
if (!r || r.tran !== t) {
data = Utils_1.Utils.replicate(data, {});
r = new Record(h.head, t, data);
t.changeset.set(h, r);
h.editing = r;
h.editors++;
}
var v = r.prev.record ? r.prev.record.data[prop] : undefined;
Cache.registerPropEdit(r, prop, !Tools.equal(v, value) && value !== P_HANDLE, value ? value.toString() : value);
}
else
r = undefined;
return r;
};
Transaction.getForEdit = function (h, prop) {
var result = Transaction.tryGetForEdit(h, prop, P_HANDLE);
if (!result)
throw new Error("unknown error");
return result;
};
Transaction.prototype.checkConflictsResolved = function () {
var _this = this;
this.conflicts = undefined;
this.changeset.forEach(function (r, h) {
if (Transaction.determineConflicts(r, h.head, r.conflicts)) {
if (!_this.conflicts)
_this.conflicts = [];
_this.conflicts.push(r);
}
});
if (this.conflicts)
this.tryResolveConflicts(this.conflicts);
};
Transaction.prototype.tryResolveConflicts = function (conflicts) {
this._error = this._error || new Error(this.hint + " conflicts with other transactions on: " + Transaction.formatConflictsSummary(conflicts));
};
Transaction.determineConflicts = function (ours, head, conflicts) {
if (head.tran.snapshot > ours.tran.snapshot) {
var garbage_1 = head.edits.has(P_GC);
ours.edits.forEach(function (prop) {
var theirs = head;
while (theirs && theirs.tran.snapshot > ours.tran.snapshot) {
if (theirs.edits.has(prop)) {
if (!Tools.equal(theirs.data[prop], ours.data[prop]))
conflicts.set(prop, theirs);
theirs = ours;
}
else if (prop === P_GC || garbage_1) {
conflicts.set(prop, theirs);
theirs = ours;
}
else
theirs = theirs.prev.record;
}
});
}
return conflicts.size > 0;
};
Transaction.formatConflictsSummary = function (conflicts) {
return conflicts.map(function (ours) {
var items = [];
ours.conflicts.forEach(function (theirs, prop) {
items.push(Transaction.formatConflictingProperty(prop, ours, theirs));
});
return items.join(", ");
}).join(", ");
};
Transaction.formatConflictingProperty = function (prop, ours, theirs) {
return Hint.Record(theirs, false, prop);
};
Transaction.prototype.acquireSnapshot = function () {
if (this._snapshot === 0) {
this._snapshot = Transaction.snapshot;
Transaction.stack.push(this);
if (Dbg.level >= 1)
Dbg.log("╔═══", "v" + this.snapshot, "" + this.hint);
}
};
Transaction.prototype.releaseSnapshot = function () {
if (this._snapshot !== 0) {
if (Transaction.stack[0] === this) {
var i = 0;
for (var _i = 0, _a = Transaction.stack; _i < _a.length; _i++) {
var x = _a[_i];
if (x.finished()) {
if (!x.error)
x.cleanup();
}
else
break;
i++;
}
Transaction.stack = Transaction.stack.slice(i);
}
}
};
Transaction.prototype.performCommit = function () {
if (this.changeset.size > 0) {
this._snapshot = ++Transaction.snapshot;
Transaction.applyChanges(true, this.changeset);
if (Dbg.level >= 1)
Dbg.log(this.snapshot > 0 ? "╚═══" : "═══", "v" + this.snapshot, this.hint + " - COMMIT(" + this.changeset.size + ")");
Cache.propagateChanges(this.changeset, this.reactions);
}
else if (Dbg.level >= 1)
Dbg.log(this.snapshot > 0 ? "╚═══" : "═══", "v" + this.snapshot, this.hint + " - DRY");
this.releaseSnapshot();
if (this.resultPromise)
this.resultResolve();
};
Transaction.prototype.performDiscard = function () {
Transaction.applyChanges(false, this.changeset);
if (Dbg.level >= 2)
Dbg.log("╚═══", "v" + this.snapshot, this.hint + " - DISCARD(" + this.changeset.size + ") - " + this._error);
this.releaseSnapshot();
if (this.resultPromise)
this.resultReject(this._error);
};
Transaction.applyChanges = function (success, changeset) {
changeset.forEach(function (r, h) {
r.seal(h.proxy);
h.editors--;
if (h.editors === 0)
h.editing = undefined;
if (success)
h.head = r;
if (Dbg.level >= 1) {
var props_1 = [];
r.edits.forEach(function (prop) { return props_1.push(prop.toString()); });
Dbg.log("║", "M", Hint.Record(r) + ": " + props_1.join(", "));
}
});
};
Transaction.acquireHandle = function (obj) {
if (obj !== Object(obj) || Array.isArray(obj))
throw new Error("E604: only objects can be registered in pushinka store");
var h = Utils_1.Utils.get(obj, P_HANDLE);
if (!h) {
if (Transaction.running)
Transaction.running.acquireSnapshot();
var r = new Record(undefined, notran, {});
h = new Handle(obj, Hooks.global, r, obj);
Utils_1.Utils.set(r.data, P_HANDLE, h);
r.seal(h.proxy);
Utils_1.Utils.set(obj, P_HANDLE, h);
}
return h;
};
Transaction.prototype.acquirePromise = function () {
var _this = this;
if (!this.resultPromise) {
this.resultPromise = new Promise(function (resolve, reject) {
_this.resultResolve = resolve;
_this.resultReject = reject;
});
}
return this.resultPromise;
};
Transaction.prototype.cleanup = function () {
if (Dbg.level >= 2)
Dbg.log("", "gc", "t" + this.id + ": " + this.hint);
this.changeset.forEach(function (r, h) {
if (Dbg.level >= 2 && r.prev.record)
Dbg.log("", "gc", Hint.Record(r.prev.record) + " is ready for GC (overwritten by " + Hint.Record(r) + "}");
Record.dismiss(r.prev.record);
r.prev.record = undefined;
});
};
Transaction._wrap = function (t, c, inc, dec, func) {
var f = c ? c.wrap(func) : func;
if (inc)
t.run(function () { return t.pending++; });
var tran = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return t._run(function () {
if (dec)
t.pending--;
return f.apply(void 0, args);
});
};
return tran;
};
Transaction.id = 9;
Transaction.snapshot = 10;
Transaction.running = undefined;
Transaction.stack = [];
Transaction.history = [];
Transaction.historyCapacity = 5;
return Transaction;
}());
exports.Transaction = Transaction;
var Latency;
(function (Latency) {
Latency[Latency["Immediate"] = 0] = "Immediate";
Latency[Latency["Lazy"] = -1] = "Lazy";
Latency[Latency["Manual"] = -2] = "Manual";
})(Latency || (Latency = {}));
var Reaction = (function () {
function Reaction() {
}
Object.defineProperty(Reaction.prototype, "isInvalidated", {
get: function () { return this.invalidator !== undefined; },
enumerable: true,
configurable: true
});
Reaction.get = function (method) {
var proxy = Utils_1.Utils.get(method, P_CACHE);
if (!proxy)
throw new Error("given method is not a reaction");
return proxy;
};
Reaction.dismissAll = function () {
var objects = [];
for (var _i = 0; _i < arguments.length; _i++) {
objects[_i] = arguments[_i];
}
var t = notran;
Transaction.runAs("dismiss", false, function () {
t = Transaction.current || notran;
for (var _i = 0, objects_1 = objects; _i < objects_1.length; _i++) {
var x = objects_1[_i];
x[P_GC] = P_GC;
}
});
return t;
};
return Reaction;
}());
exports.Reaction = Reaction;
var CacheProxy = (function (_super) {
__extends(CacheProxy, _super);
function CacheProxy(handle, member) {
var _this = _super.call(this) || this;
_this.handle = handle;
_this.member = member;
_this.blank = new Cache(_this.handle, _this.member, notran.id, -1, undefined);
Cache.freeze(_this.blank);
Cache.dismiss(_this.blank);
return _this;
}
Object.defineProperty(CacheProxy.prototype, "latency", {
get: function () { return this.obtain(false, false).cache.latency; },
set: function (value) { this.alter(latency, value); },
enumerable: true,
configurable: true
});
Object.defineProperty(CacheProxy.prototype, "monitor", {
get: function () { return this.obtain(false, false).cache.monitor; },
set: function (value) { this.alter(monitor, value); },
enumerable: true,
configurable: true
});
Object.defineProperty(CacheProxy.prototype, "cachedResult", {
get: function () { return this.obtain(true, false).cache.cachedResult; },
enumerable: true,
configurable: true
});
Object.defineProperty(CacheProxy.prototype, "cachedAsyncResult", {
get: function () { return this.obtain(true, false).cache.cachedAsyncResult; },
enumerable: true,
configurable: true
});
Object.defineProperty(CacheProxy.prototype, "cachedError", {
get: function () { return this.obtain(true, false).cache.cachedError; },
enumerable: true,
configurable: true
});
Object.defineProperty(CacheProxy.prototype, "invalidator", {
get: function () { return this.obtain(true, false).cache.invalidator; },
enumerable: true,
configurable: true
});
CacheProxy.prototype.invalidate = function (invalidator) { return invalidator ? Cache.enforceInvalidation(this.obtain(false, false).cache, "", 0) : false; };
CacheProxy.prototype.obtain = function (register, edit) {
var r = edit ? Transaction.getForEdit(this.handle, this.member.name) : Transaction.getForView(this.handle);
var c = r.data[this.member.name] || this.blank;
if (edit && c.invalidator) {
c = new Cache(this.handle, this.member, r.tran.id, c.latency, c.monitor);
r.data[this.member.name] = c;
}
if (register)
Cache.registerPropView(r, c.member.name);
return { cache: c, record: r };
};
CacheProxy.prototype.alter = function (action, value) {
var _this = this;
var a1 = this.obtain(false, false);
var c1 = a1.cache;
Transaction.runAs(Hint.Handle(c1.owner) + "." + c1.member.name.toString() + "." + action.name + " = " + value, false, function () {
var a2 = _this.obtain(false, true);
var c2 = a2.cache;
action(c2, value);
});
};
return CacheProxy;
}(Reaction));
var P_HANDLE = Symbol("p.handle");
var P_CACHE = Symbol("p.cache");
var P_GC = Symbol("p.gc");
var notran = new Transaction("notran");
var Hooks = (function () {
function Hooks() {
}
Hooks.prototype.getPrototypeOf = function (h) {
return h.proto;
};
Hooks.prototype.get = function (h, prop, receiver) {
var value;
var member = h.decorators[prop];
if (!member || member.decorator === state) {
var r = Transaction.getForView(h);
value = r.data[prop];
if (value === undefined && !r.data.hasOwnProperty(prop))
value = Reflect.get(h.proto, prop, receiver);
Cache.registerPropView(r, prop);
}
else {
value = h.stateless[prop];
if (member.decorator !== stateless && value === undefined)
value = h.stateless[prop] = Cache.trap(h, member);
}
return value;
};
Hooks.prototype.set = function (h, prop, value, receiver) {
var member = h.decorators[prop];
if (!member || member.decorator === state) {
var r = Transaction.tryGetForEdit(h, prop, value);
if (r)
r.data[prop] = value;
}
else {
if (member.decorator !== stateless)
throw new Error("not yet supported");
h.stateless[prop] = value;
}
return true;
};
Hooks.decorateClass = function (decor, origCtor) {
var ctor = origCtor;
if (decor === state) {
ctor = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var r = new Record(undefined, notran, {});
var h = new Handle(undefined, Hooks.global, r, this);
Utils_1.Utils.set(r.data, P_HANDLE, h);
r.seal(h.proxy);
origCtor.call.apply(origCtor, [h.proxy].concat(args));
return h.proxy;
};
ctor.prototype = origCtor.prototype;
Object.assign(ctor, origCtor);
}
Utils_1.Utils.decorate(ctor, decor);
return ctor;
};
Hooks.decorateField = function (decor, target, prop) {
var member = new Member_1.Member(decor, prop);
Utils_1.Utils.acquireDecorationTable(target)[prop] = member;
if (decor !== stateless) {
var get = function () {
var h = Transaction.acquireHandle(this);
return Hooks.global.get(h, prop, this);
};
var set = function (value) {
var h = Transaction.acquireHandle(this);
return Hooks.global.set(h, prop, value, this);
};
var enumerable = true;
var configurable = false;
return Object.defineProperty(target, prop, { get: get, set: set, enumerable: enumerable, configurable: configurable });
}
};
Hooks.decorateMethod = function (decor, target, prop, pd) {
var member = new Member_1.Member(decor, prop, pd.value);
Utils_1.Utils.acquireDecorationTable(target)[prop] = member;
var get = function () {
var b = Utils_1.Utils.decorationOf(this.constructor) === state;
var h = b ? Utils_1.Utils.get(this, P_HANDLE) : Transaction.acquireHandle(this);
return Hooks.global.get(h, prop, this);
};
var enumerable = pd ? pd.enumerable === true : true;
var configurable = false;
return Object.defineProperty(target, prop, { get: get, enumerable: enumerable, configurable: configurable });
};
Hooks.global = new Hooks();
return Hooks;
}());
var Handle = (function (_super) {
__extends(Handle, _super);
function Handle() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Handle;
}(Handle_1.XHandle));
var Record = (function (_super) {
__extends(Record, _super);
function Record() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Record;
}(Record_1.XRecord));
var Monitor = (function (_super) {
__extends(Monitor, _super);
function Monitor(name) {
var _this = _super.call(this) || this;
Tools.setHint(_this, name);
return _this;
}
Monitor = __decorate([
state,
__metadata("design:paramtypes", [String])
], Monitor);
return Monitor;
}(Monitor_1.XMonitor));
exports.Monitor = Monitor;
var Cache = (function () {
function Cache(owner, member, actionId, latency, monitor) {
this.margin = Cache.running ? Cache.running.margin + 1 : 0;
this.owner = owner;
this.member = member;
this.actionId = actionId;
this.latency = latency;
this.monitor = monitor;
this.invalidator = this.hint;
this.observables = new Map();
}
Object.defineProperty(Cache.prototype, "hint", {
get: function () { return Hint.Handle(this.owner) + "t" + this.actionId + "." + this.member.name.toString(); },
enumerable: true,
configurable: true
});
Cache.run = function (c, func) {
var args = [];
for (var _i = 2; _i < arguments.length; _i++) {
args[_i - 2] = arguments[_i];
}
var result = undefined;
var outer = Cache.running;
try {
Cache.running = c;
result = func.apply(void 0, args);
}
catch (e) {
if (c)
c.cachedError = e;
throw e;
}
finally {
Cache.running = outer;
}
return result;
};
Cache.prototype.wrap = function (func) {
var _this = this;
var caching = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
return Cache.run.apply(Cache, [_this, func].concat(args));
};
return caching;
};
Cache.prototype.ensureUpToDate = function (now) {
var _this = this;
var args = [];
for (var _i = 1; _i < arguments.length; _i++) {
args[_i - 1] = arguments[_i];
}
if (now || this.latency === 0) {
if ((this.member.decorator !== reaction || this.invalidator) && !this.cachedError) {
var proxy = this.owner.proxy;
var result = Reflect.get(proxy, this.member.name, proxy).apply(void 0, args);
if (result instanceof Promise)
result.catch(function (error) { });
}
}
else
setTimeout(function () { return _this.ensureUpToDate(true); }, this.latency);
};
Cache.registerPropView = function (r, prop) {
var c = Cache.running;
if (c && c.member.decorator === reaction && prop !== P_HANDLE) {
var observables = c.observables.get(prop);
if (!observables)
c.observables.set(prop, observables = new Set());
observables.add(r);
if (Dbg.level >= 3)
Dbg.log("║", "v", c.hint + ": " + Hint.Record(r) + "." + prop.toString());
}
};
Cache.registerPropEdit = function (r, prop, edited, valueHint) {
edited ? r.edits.add(prop) : r.edits.delete(prop);
if (Dbg.level >= 4)
Dbg.log("║", " ", Hint.Record(r) + "." + prop.toString() + " = " + valueHint);
};
Cache.propagateChanges = function (changeset, reactions) {
changeset.forEach(function (r, h) {
var garbage = r.edits.has(P_GC);
if (r.prev.record) {
var prev_1 = r.prev.record;
prev_1.observers.forEach(function (prevObservers, prop) {
if (garbage || r.edits.has(prop))
prevObservers.forEach(function (c) { return c.invalidate(Hint.Record(r, false, prop), reactions); });
else
Cache.retainPrevObservers(r, prop, prev_1, prevObservers);
});
}
r.edits.forEach(function (prop) {
Cache.markOverwritten(r.prev.record, prop, reactions);
var c = r.data[prop];
if (c instanceof Cache) {
var cause = c.linkWithAllObservables();
if (cause)
c.invalidate(cause, reactions);
}
});
});
};
Cache.acquireSetOfObservers = function (r, prop) {
var result = r.observers.get(prop);
if (!result) {
r.observers.set(prop, result = new Set());
if (Dbg.level >= 4)
Dbg.log("", " [ Observers:", Hint.Record(r, false, prop) + " = new ]");
var x = r.prev.record;
while (x && !x.observers.get(prop) && x.data[prop] === r.data[prop]) {
x.observers.set(prop, result);
if (Dbg.level >= 4)
Dbg.log("", " [ Observers:", Hint.Record(x, false, prop) + " = " + Hint.Record(r, false, prop) + " ]");
x = x.prev.record;
}
}
return result;
};
Cache.prototype.linkWithAllObservables = function () {
var _this = this;
var invalidator = undefined;
var subscriptions = [];
this.observables.forEach(function (observables, prop) {
observables.forEach(function (r) {
Cache.acquireSetOfObservers(r, prop).add(_this);
if (Dbg.level >= 2)
subscriptions.push(Hint.Record(r, true, prop));
if (!invalidator && r.overwritten.has(prop))
invalidator = Hint.Record(r, false, prop);
});
});
if (Dbg.level >= 2 && subscriptions.length > 0)
Dbg.log("", " ∞", Hint.Record(Transaction.getForView(this.owner), false, this.member.name) + ": " + subscriptions.join(", "));
return invalidator;
};
Cache.retainPrevObservers = function (r, prop, prev, prevObservers) {
var thisObservers = r.observers.get(prop);
if (thisObservers) {
thisObservers.forEach(function (c) { return prevObservers.add(c); });
if (Dbg.level >= 4)
Dbg.log("", " [ Observers:", Hint.Record(prev, false, prop) + "(" + prevObservers.size + ") += " + Hint.Record(r, false, prop) + "(" + thisObservers.size + ") ]");
}
r.observers.set(prop, prevObservers);
if (Dbg.level >= 4)
Dbg.log("", " [ Observers:", Hint.Record(r, false, prop) + " = " + Hint.Record(prev, false, prop) + "(" + prevObservers.size + ") ]");
return prevObservers;
};
Cache.prototype.invalidate = function (invalidator, reactions) {
if (!this.invalidator) {
this.invalidator = invalidator;
var r = Transaction.getForView(this.owner);
if (r.data[this.member.name] === this)
Cache.markOverwritten(r, this.member.name, reactions);
if (Dbg.level >= 1)
Dbg.log("", " x", this.hint + " is invalidated (by " + invalidator + ")");
if (this.latency > Latency.Lazy) {
reactions.push(this);
if (Dbg.level >= 1)
Dbg.log("", " ■", this.hint + " will run automatically");
}
}
};
Cache.enforceInvalidation = function (c, invalidator, latency) {
var reactions = [];
c.invalidate(invalidator, reactions);
if (latency === 0)
Cache.ensureAllUpToDate(invalidator, reactions, {});
else
setTimeout(function () { return Cache.ensureAllUpToDate(invalidator, reactions, {}); }, latency);
return true;
};
Cache.markOverwritten = function (self, prop, reactions) {
var _loop_1 = function () {
var r = self;
r.overwritten.add(prop);
var o = r.observers.get(prop);
if (o)
o.forEach(function (c) { return c.invalidate(Hint.Record(r, false, prop), reactions); });
self = self.prev.record;
};
while (self && !self.overwritten.has(prop)) {
_loop_1();
}
};
Cache.ensureAllUpToDate = function (hint, reactions, effect) {
Transaction.runAs("Reactions(" + reactions.length + "): " + hint, true, function () {
effect.tran = Transaction.current || notran;
for (var _i = 0, reactions_1 = reactions; _i < reactions_1.length; _i++) {
var x = reactions_1[_i];
x.ensureUpToDate(false);
}
});
};
Cache.trap = function (h, m) {
var proxy = new CacheProxy(h, m);
var reactiveInvoke = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var cr = proxy.obtain(true, false);
var c = cr.cache;
var r = cr.record;
if (c.member.decorator !== reaction || c.invalidator || c.latency < Latency.Lazy) {
return Transaction.runAs.apply(Transaction, [Hint.Handle(h) + "." + m.name.toString(), c.member.decorator === separateTransaction, function () {
var argsx = [];
for (var _i = 0; _i < arguments.length; _i++) {
argsx[_i] = arguments[_i];
}
var c1 = c;
var cr2 = proxy.obtain(false, true);
var c2 = cr2.cache;
var r2 = cr2.record;
var monitor = c1.monitor;
Cache.enterMonitor(monitor);
try {
c2.args = argsx;
c2.invalidator = undefined;
c2.cachedResult = Cache.run.apply(Cache, [c2, function () {
var argsy = [];
for (var _i = 0; _i < arguments.length; _i++) {
argsy[_i] = arguments[_i];
}
var _a;
return (_a = c2.member.body).call.apply(_a, [c2.owner.proxy].concat(argsy));
}].concat(argsx));
}
finally {
if (c2.cachedResult instanceof Promise)
c2.cachedResult = c2.cachedResult.then(function (result) {
Cache.leaveMonitor(monitor);
c2.cachedAsyncResult = result;
return result;
}, function (error) {
Cache.leaveMonitor(monitor);
throw error;
});
else
Cache.leaveMonitor(monitor);
}
Cache.registerPropView(r2, c2.member.name);
Cache.registerPropEdit(r2, c.member.name, true, c2.hint);
return c2.cachedResult;
}].concat(args));
}
else {
Cache.registerPropView(r, c.member.name);
return c.cachedResult;
}
};
Utils_1.Utils.set(reactiveInvoke, P_CACHE, proxy);
return reactiveInvoke;
};
Cache.enterMonitor = function (monitor) {
if (monitor)
Transaction.runAs("Monitor.enter", true, function () {
return Cache.run(undefined, function () { return monitor.enter(); });
});
};
Cache.leaveMonitor = function (monitor) {
if (monitor)
Transaction.runAs("Monitor.leave", true, function () {
return Cache.run(undefined, function () { return monitor.leave(); });
});
};
Cache.freeze = function (c) {
Utils_1.Utils.freezeMap(c.observables);
Object.freeze(c);
};
Cache.dismiss = function (c) {
};
Cache.logCtx = function (op) {
var t = Transaction.running;
var color = 31 + (t ? t.id : 0) % 6;
var operation = t ? "t" + t.id + " " + op : "v" + Transaction.snapshot + " " + op;
var margin = Cache.running ? Cache.running.margin : 0;
return { color: color, operation: operation, margin: margin };
};
Cache.running = undefined;
return Cache;
}());
function latency(c, value) {
c.latency = value;
}
function monitor(c, value) {
c.monitor = value;
}
var Tools = (function () {
function Tools() {
}
Tools.setHint = function (obj, hint) {
var h = Utils_1.Utils.get(obj, P_HANDLE);
if (h)
h.hint = hint;
};
Tools.equal = function (oldValue, newValue) {
var result;
if (oldValue instanceof Cache) {
if (newValue instanceof Cache)
result = oldValue.member.decorator !== reaction || oldValue.cachedResult === newValue.cachedResult;
else if (newValue instanceof Function)
result = oldValue.member.body === newValue;
else
result = false;
}
else if (Array.isArray(oldValue)) {
if (Array.isArray(newValue))
result = newValue === oldValue;
else
result = false;
}
else
result = newValue === oldValue;
return result;
};
return Tools;
}());
exports.Tools = Tools;
var primise_then_original = Promise.prototype.then;
var promise_then_pushinka = function (onsuccess, onfailure) {
var t = Transaction.running;
if (t) {
if (onsuccess) {
onsuccess = Transaction._wrap(t, Cache.running, true, true, onsuccess);
onfailure = Transaction._wrap(t, Cache.running, false, true, onfailure || Utils_1.rethrow);
}
else if (onfailure)
onfailure = Transaction._wrap(t, Cache.running, false, false, onfailure);
}
return primise_then_original.call(this, onsuccess, onfailure);
};
Promise.prototype.then = promise_then_pushinka;
var Dbg = (function (_super) {
__extends(Dbg, _super);
function Dbg() {
return _super !== null && _super.apply(this, arguments) || this;
}
Dbg.log = function (operation, marker, message) {
Log_1.Log.print(Cache.logCtx(operation), marker, message);
};
return Dbg;
}(Log_1.Log));
var Hint = (function () {
function Hint() {
}
Hint.Handle = function (h, typeless) {
return "" + (h.hint ? h.hint + "/" : "") + (typeless ? "" : h.type) + "#" + h.id;
};
Hint.Record = function (r, typeless, prop) {
var h = Hint.handleOfRecord(r);
return prop !== undefined ?
Hint.Handle(h, typeless) + "t" + r.tran.id + "." + prop.toString() :
Hint.Handle(h, typeless) + "t" + r.tran.id;
};
Hint.handleOfRecord = function (r) {
return Utils_1.Utils.get(r.data, P_HANDLE);
};
return Hint;
}());
notran.commit();
//# sourceMappingURL=Transaction.js.map