var rsvp = require('rsvp'); var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; var createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); var Cookie = function () { function Cookie(node, heimdall) { classCallCheck(this, Cookie); this._node = node; this._restoreNode = node.parent; this._heimdall = heimdall; this._stopped = false; } createClass(Cookie, [{ key: "stop", value: function stop() { var monitor = void 0; if (this._heimdall.current !== this._node) { console.log("Cannot stop: not the current node."); return; } else if (this.stopped === true) { console.log("Cannot stop: already stopped."); return; } this._stopped = true; this._heimdall._recordTime(); this._heimdall._session.current = this._restoreNode; } }, { key: "resume", value: function resume() { if (this._stopped === false) { console.log("Cannot resume: not stopped."); return; } this._stopped = false; this._restoreNode = this._heimdall.current; this._heimdall._session.current = this._node; } }, { key: "stats", get: function get() { return this._node.stats.own; } }]); return Cookie; }(); var HeimdallNode = function () { function HeimdallNode(heimdall, id, data) { classCallCheck(this, HeimdallNode); this._heimdall = heimdall; this._id = heimdall.generateNextId(); this.id = id; if (!(_typeof(this.id) === 'object' && this.id !== null && typeof this.id.name === 'string')) { throw new TypeError('HeimdallNode#id.name must be a string'); } this.stats = { own: data, time: { self: 0 } }; this._children = []; this.parent = null; } createClass(HeimdallNode, [{ key: 'visitPreOrder', value: function visitPreOrder(cb) { cb(this); for (var i = 0; i < this._children.length; i++) { this._children[i].visitPreOrder(cb); } } }, { key: 'visitPostOrder', value: function visitPostOrder(cb) { for (var i = 0; i < this._children.length; i++) { this._children[i].visitPostOrder(cb); } cb(this); } }, { key: 'forEachChild', value: function forEachChild(cb) { for (var i = 0; i < this._children.length; ++i) { cb(this._children[i]); } } }, { key: 'remove', value: function remove() { if (!this.parent) { throw new Error('Cannot remove the root heimdalljs node.'); } if (this._heimdall.current === this) { throw new Error('Cannot remove an active heimdalljs node.'); } return this.parent.removeChild(this); } }, { key: 'toJSON', value: function toJSON() { return { _id: this._id, id: this.id, stats: this.stats, children: this._children.map(function (child) { return child._id; }) }; } }, { key: 'toJSONSubgraph', value: function toJSONSubgraph() { var nodes = []; this.visitPreOrder(function (node) { return nodes.push(node.toJSON()); }); return nodes; } }, { key: 'addChild', value: function addChild(node) { if (node.parent) { throw new TypeError('Node ' + node._id + ' already has a parent. Cannot add to ' + this._id); } this._children.push(node); node.parent = this; } }, { key: 'removeChild', value: function removeChild(child) { var index = this._children.indexOf(child); if (index < 0) { throw new Error('Child(' + child._id + ') not found in Parent(' + this._id + '). Something is very wrong.'); } this._children.splice(index, 1); child.parent = null; return child; } }, { key: 'isRoot', get: function get() { return this.parent === null; } }]); return HeimdallNode; }(); // provides easily interceptable indirection. var Dict = function () { function Dict() { classCallCheck(this, Dict); this._storage = {}; } createClass(Dict, [{ key: 'has', value: function has(key) { return key in this._storage; } }, { key: 'get', value: function get(key) { return this._storage[key]; } }, { key: 'set', value: function set(key, value) { return this._storage[key] = value; } }, { key: 'delete', value: function _delete(key) { delete this._storage[key]; } }]); return Dict; }(); var HeimdallSession = function () { function HeimdallSession() { classCallCheck(this, HeimdallSession); this.reset(); } createClass(HeimdallSession, [{ key: 'reset', value: function reset() { this._nextId = 0; this.current = undefined; this.root = null; this.previousTimeNS = 0; this.monitorSchemas = new Dict(); this.configs = new Dict(); } }, { key: 'generateNextId', value: function generateNextId() { return this._nextId++; } }]); return HeimdallSession; }(); var timeNS = void 0; // adapted from // https://gist.github.com/paulirish/5438650 var now = void 0; if ((typeof performance === 'undefined' ? 'undefined' : _typeof(performance)) === 'object' && typeof performance.now === 'function') { now = function now() { return performance.now.call(performance); }; } else { now = Date.now || function () { return new Date().getTime(); }; } var dateOffset = now(); function timeFromDate() { var timeMS = now() - dateOffset; return Math.floor(timeMS * 1e6); } function timeFromHRTime() { var hrtime = process.hrtime(); return hrtime[0] * 1e9 + hrtime[1]; } if ((typeof process === 'undefined' ? 'undefined' : _typeof(process)) === 'object' && typeof process.hrtime === 'function') { timeNS = timeFromHRTime; } else { timeNS = timeFromDate; } var timeNS$1 = timeNS; var Heimdall = function () { function Heimdall(session) { classCallCheck(this, Heimdall); if (arguments.length < 1) { session = new HeimdallSession(); } this._session = session; this._reset(false); } createClass(Heimdall, [{ key: '_reset', value: function _reset(resetSession) { if (resetSession !== false) { this._session.reset(); } if (!this.root) { // The first heimdall to start will create the session and root. Subsequent // heimdall instances continue to use the existing graph this.start('heimdall'); this._session.root = this._session.current; } } }, { key: 'start', value: function start(name, Schema) { var id = void 0; var data = void 0; if (typeof name === 'string') { id = { name: name }; } else { id = name; } if (typeof Schema === 'function') { data = new Schema(); } else { data = {}; } this._recordTime(); var node = new HeimdallNode(this, id, data); if (this.current) { this.current.addChild(node); } this._session.current = node; return new Cookie(node, this); } }, { key: '_recordTime', value: function _recordTime() { var time = timeNS$1(); // always true except for root if (this.current) { var delta = time - this._session.previousTimeNS; this.current.stats.time.self += delta; } this._session.previousTimeNS = time; } }, { key: 'node', value: function node(name, Schema, callback, context) { if (arguments.length < 3) { callback = Schema; Schema = undefined; } var cookie = this.start(name, Schema); // NOTE: only works in very specific scenarios, specifically promises must // not escape their parents lifetime. In theory, promises could be augmented // to support those more advanced scenarios. return new rsvp.Promise(function (resolve) { return resolve(callback.call(context, cookie._node.stats.own)); }).finally(function () { return cookie.stop(); }); } }, { key: 'hasMonitor', value: function hasMonitor(name) { return this._session.monitorSchemas.has(name); } }, { key: 'registerMonitor', value: function registerMonitor(name, Schema) { if (name === 'own' || name === 'time') { throw new Error('Cannot register monitor at namespace "' + name + '". "own" and "time" are reserved'); } if (this.hasMonitor(name)) { throw new Error('A monitor for "' + name + '" is already registered"'); } this._session.monitorSchemas.set(name, Schema); } }, { key: 'statsFor', value: function statsFor(name) { var stats = this.current.stats; var Schema = void 0; if (!stats[name]) { Schema = this._session.monitorSchemas.get(name); if (!Schema) { throw new Error('No monitor registered for "' + name + '"'); } stats[name] = new Schema(); } return stats[name]; } }, { key: 'configFor', value: function configFor(name) { var config = this._session.configs.get(name); if (!config) { config = this._session.configs.set(name, {}); } return config; } }, { key: 'toJSON', value: function toJSON() { return { nodes: this.root.toJSONSubgraph() }; } }, { key: 'visitPreOrder', value: function visitPreOrder(cb) { return this.root.visitPreOrder(cb); } }, { key: 'visitPostOrder', value: function visitPostOrder(cb) { return this.root.visitPostOrder(cb); } }, { key: 'generateNextId', value: function generateNextId() { return this._session.generateNextId(); } }, { key: 'current', get: function get() { return this._session.current; } }, { key: 'root', get: function get() { return this._session.root; } }, { key: 'stack', get: function get() { var stack = []; var top = this.current; while (top !== undefined && top !== this.root) { stack.unshift(top); top = top.parent; } return stack.map(function (node) { return node.id.name; }); } }]); return Heimdall; }(); function setupSession(global) { // The name of the property encodes the session/node compatibilty version if (!global._heimdall_session_2) { global._heimdall_session_2 = new HeimdallSession(); } } setupSession(process); var index = new Heimdall(process._heimdall_session_2); module.exports = index;