UNPKG

11.5 kBJavaScriptView Raw
1var rsvp = require('rsvp');
2
3var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
4 return typeof obj;
5} : function (obj) {
6 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
7};
8
9
10
11
12
13
14
15
16
17
18
19var classCallCheck = function (instance, Constructor) {
20 if (!(instance instanceof Constructor)) {
21 throw new TypeError("Cannot call a class as a function");
22 }
23};
24
25var createClass = function () {
26 function defineProperties(target, props) {
27 for (var i = 0; i < props.length; i++) {
28 var descriptor = props[i];
29 descriptor.enumerable = descriptor.enumerable || false;
30 descriptor.configurable = true;
31 if ("value" in descriptor) descriptor.writable = true;
32 Object.defineProperty(target, descriptor.key, descriptor);
33 }
34 }
35
36 return function (Constructor, protoProps, staticProps) {
37 if (protoProps) defineProperties(Constructor.prototype, protoProps);
38 if (staticProps) defineProperties(Constructor, staticProps);
39 return Constructor;
40 };
41}();
42
43var Cookie = function () {
44 function Cookie(node, heimdall) {
45 classCallCheck(this, Cookie);
46
47 this._node = node;
48 this._restoreNode = node.parent;
49 this._heimdall = heimdall;
50 this._stopped = false;
51 }
52
53 createClass(Cookie, [{
54 key: "stop",
55 value: function stop() {
56 var monitor = void 0;
57
58 if (this._heimdall.current !== this._node) {
59 return;
60 } else if (this.stopped === true) {
61 return;
62 }
63
64 this._stopped = true;
65 this._heimdall._recordTime();
66 this._heimdall._session.current = this._restoreNode;
67 }
68 }, {
69 key: "resume",
70 value: function resume() {
71 if (this._stopped === false) {
72 return;
73 }
74
75 this._stopped = false;
76 this._restoreNode = this._heimdall.current;
77 this._heimdall._session.current = this._node;
78 }
79 }, {
80 key: "stats",
81 get: function get() {
82 return this._node.stats.own;
83 }
84 }]);
85 return Cookie;
86}();
87
88var HeimdallNode = function () {
89 function HeimdallNode(heimdall, id, data) {
90 classCallCheck(this, HeimdallNode);
91
92 this._heimdall = heimdall;
93
94 this._id = heimdall.generateNextId();
95 this.id = id;
96
97 if (!(_typeof(this.id) === 'object' && this.id !== null && typeof this.id.name === 'string')) {
98 throw new TypeError('HeimdallNode#id.name must be a string');
99 }
100
101 this.stats = {
102 own: data,
103 time: { self: 0 }
104 };
105
106 this._children = [];
107
108 this.parent = null;
109 }
110
111 createClass(HeimdallNode, [{
112 key: 'visitPreOrder',
113 value: function visitPreOrder(cb) {
114 cb(this);
115
116 for (var i = 0; i < this._children.length; i++) {
117 this._children[i].visitPreOrder(cb);
118 }
119 }
120 }, {
121 key: 'visitPostOrder',
122 value: function visitPostOrder(cb) {
123 for (var i = 0; i < this._children.length; i++) {
124 this._children[i].visitPostOrder(cb);
125 }
126
127 cb(this);
128 }
129 }, {
130 key: 'forEachChild',
131 value: function forEachChild(cb) {
132 for (var i = 0; i < this._children.length; ++i) {
133 cb(this._children[i]);
134 }
135 }
136 }, {
137 key: 'remove',
138 value: function remove() {
139 if (!this.parent) {
140 throw new Error('Cannot remove the root heimdalljs node.');
141 }
142 if (this._heimdall.current === this) {
143 throw new Error('Cannot remove an active heimdalljs node.');
144 }
145
146 return this.parent.removeChild(this);
147 }
148 }, {
149 key: 'toJSON',
150 value: function toJSON() {
151 return {
152 _id: this._id,
153 id: this.id,
154 stats: this.stats,
155 children: this._children.map(function (child) {
156 return child._id;
157 })
158 };
159 }
160 }, {
161 key: 'toJSONSubgraph',
162 value: function toJSONSubgraph() {
163 var nodes = [];
164
165 this.visitPreOrder(function (node) {
166 return nodes.push(node.toJSON());
167 });
168
169 return nodes;
170 }
171 }, {
172 key: 'addChild',
173 value: function addChild(node) {
174 if (node.parent) {
175 throw new TypeError('Node ' + node._id + ' already has a parent. Cannot add to ' + this._id);
176 }
177
178 this._children.push(node);
179
180 node.parent = this;
181 }
182 }, {
183 key: 'removeChild',
184 value: function removeChild(child) {
185 var index = this._children.indexOf(child);
186
187 if (index < 0) {
188 throw new Error('Child(' + child._id + ') not found in Parent(' + this._id + '). Something is very wrong.');
189 }
190 this._children.splice(index, 1);
191
192 child.parent = null;
193
194 return child;
195 }
196 }, {
197 key: 'isRoot',
198 get: function get() {
199 return this.parent === null;
200 }
201 }]);
202 return HeimdallNode;
203}();
204
205// provides easily interceptable indirection.
206var Dict = function () {
207 function Dict() {
208 classCallCheck(this, Dict);
209
210 this._storage = {};
211 }
212
213 createClass(Dict, [{
214 key: 'has',
215 value: function has(key) {
216 return key in this._storage;
217 }
218 }, {
219 key: 'get',
220 value: function get(key) {
221 return this._storage[key];
222 }
223 }, {
224 key: 'set',
225 value: function set(key, value) {
226 return this._storage[key] = value;
227 }
228 }, {
229 key: 'delete',
230 value: function _delete(key) {
231 delete this._storage[key];
232 }
233 }]);
234 return Dict;
235}();
236
237var HeimdallSession = function () {
238 function HeimdallSession() {
239 classCallCheck(this, HeimdallSession);
240
241 this.reset();
242 }
243
244 createClass(HeimdallSession, [{
245 key: 'reset',
246 value: function reset() {
247 this._nextId = 0;
248 this.current = undefined;
249 this.root = null;
250 this.previousTimeNS = 0;
251 this.monitorSchemas = new Dict();
252 this.configs = new Dict();
253 }
254 }, {
255 key: 'generateNextId',
256 value: function generateNextId() {
257 return this._nextId++;
258 }
259 }]);
260 return HeimdallSession;
261}();
262
263var timeNS = void 0;
264
265// adapted from
266// https://gist.github.com/paulirish/5438650
267var now = void 0;
268if ((typeof performance === 'undefined' ? 'undefined' : _typeof(performance)) === 'object' && typeof performance.now === 'function') {
269 now = function now() {
270 return performance.now.call(performance);
271 };
272} else {
273 now = Date.now || function () {
274 return new Date().getTime();
275 };
276}
277
278var dateOffset = now();
279
280function timeFromDate() {
281 var timeMS = now() - dateOffset;
282
283 return Math.floor(timeMS * 1e6);
284}
285
286function timeFromHRTime() {
287 var hrtime = process.hrtime();
288 return hrtime[0] * 1e9 + hrtime[1];
289}
290
291if ((typeof process === 'undefined' ? 'undefined' : _typeof(process)) === 'object' && typeof process.hrtime === 'function') {
292 timeNS = timeFromHRTime;
293} else {
294 timeNS = timeFromDate;
295}
296
297var timeNS$1 = timeNS;
298
299var Heimdall = function () {
300 function Heimdall(session) {
301 classCallCheck(this, Heimdall);
302
303 if (arguments.length < 1) {
304 session = new HeimdallSession();
305 }
306
307 this._session = session;
308 this._reset(false);
309 }
310
311 createClass(Heimdall, [{
312 key: '_reset',
313 value: function _reset(resetSession) {
314 if (resetSession !== false) {
315 this._session.reset();
316 }
317
318 if (!this.root) {
319 // The first heimdall to start will create the session and root. Subsequent
320 // heimdall instances continue to use the existing graph
321 this.start('heimdall');
322 this._session.root = this._session.current;
323 }
324 }
325 }, {
326 key: 'start',
327 value: function start(name, Schema) {
328 var id = void 0;
329 var data = void 0;
330
331 if (typeof name === 'string') {
332 id = { name: name };
333 } else {
334 id = name;
335 }
336
337 if (typeof Schema === 'function') {
338 data = new Schema();
339 } else {
340 data = {};
341 }
342
343 this._recordTime();
344
345 var node = new HeimdallNode(this, id, data);
346 if (this.current) {
347 this.current.addChild(node);
348 }
349
350 this._session.current = node;
351
352 return new Cookie(node, this);
353 }
354 }, {
355 key: '_recordTime',
356 value: function _recordTime() {
357 var time = timeNS$1();
358
359 // always true except for root
360 if (this.current) {
361 var delta = time - this._session.previousTimeNS;
362 this.current.stats.time.self += delta;
363 }
364 this._session.previousTimeNS = time;
365 }
366 }, {
367 key: 'node',
368 value: function node(name, Schema, callback, context) {
369 if (arguments.length < 3) {
370 callback = Schema;
371 Schema = undefined;
372 }
373
374 var cookie = this.start(name, Schema);
375
376 // NOTE: only works in very specific scenarios, specifically promises must
377 // not escape their parents lifetime. In theory, promises could be augmented
378 // to support those more advanced scenarios.
379 return new rsvp.Promise(function (resolve) {
380 return resolve(callback.call(context, cookie._node.stats.own));
381 }).finally(function () {
382 return cookie.stop();
383 });
384 }
385 }, {
386 key: 'hasMonitor',
387 value: function hasMonitor(name) {
388 return this._session.monitorSchemas.has(name);
389 }
390 }, {
391 key: 'registerMonitor',
392 value: function registerMonitor(name, Schema) {
393 if (name === 'own' || name === 'time') {
394 throw new Error('Cannot register monitor at namespace "' + name + '". "own" and "time" are reserved');
395 }
396 if (this.hasMonitor(name)) {
397 throw new Error('A monitor for "' + name + '" is already registered"');
398 }
399
400 this._session.monitorSchemas.set(name, Schema);
401 }
402 }, {
403 key: 'statsFor',
404 value: function statsFor(name) {
405 var stats = this.current.stats;
406 var Schema = void 0;
407
408 if (!stats[name]) {
409 Schema = this._session.monitorSchemas.get(name);
410 if (!Schema) {
411 throw new Error('No monitor registered for "' + name + '"');
412 }
413 stats[name] = new Schema();
414 }
415
416 return stats[name];
417 }
418 }, {
419 key: 'configFor',
420 value: function configFor(name) {
421 var config = this._session.configs.get(name);
422
423 if (!config) {
424 config = this._session.configs.set(name, {});
425 }
426
427 return config;
428 }
429 }, {
430 key: 'toJSON',
431 value: function toJSON() {
432 return { nodes: this.root.toJSONSubgraph() };
433 }
434 }, {
435 key: 'visitPreOrder',
436 value: function visitPreOrder(cb) {
437 return this.root.visitPreOrder(cb);
438 }
439 }, {
440 key: 'visitPostOrder',
441 value: function visitPostOrder(cb) {
442 return this.root.visitPostOrder(cb);
443 }
444 }, {
445 key: 'generateNextId',
446 value: function generateNextId() {
447 return this._session.generateNextId();
448 }
449 }, {
450 key: 'current',
451 get: function get() {
452 return this._session.current;
453 }
454 }, {
455 key: 'root',
456 get: function get() {
457 return this._session.root;
458 }
459 }, {
460 key: 'stack',
461 get: function get() {
462 var stack = [];
463 var top = this.current;
464
465 while (top !== undefined && top !== this.root) {
466 stack.unshift(top);
467 top = top.parent;
468 }
469
470 return stack.map(function (node) {
471 return node.id.name;
472 });
473 }
474 }]);
475 return Heimdall;
476}();
477
478function setupSession(global) {
479
480 // The name of the property encodes the session/node compatibilty version
481 if (!global._heimdall_session_2) {
482 global._heimdall_session_2 = new HeimdallSession();
483 }
484}
485
486setupSession(process);
487
488var index = new Heimdall(process._heimdall_session_2);
489
490module.exports = index;