UNPKG

pushinka

Version:

Pushinka: Transactional, Reactive, and Asynchronous State Management

1,073 lines 44.5 kB
"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