UNPKG

shared-updated

Version:

Modern fork of shared (Kevin Jones), updated for latest Node.js and MongoDB

1,321 lines (1,320 loc) 159 kB
// Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='../defs/lib.d.ts' /> /// <reference path='../defs/node-0.8.d.ts' /> /// <reference path='../defs/mongodb.d.ts' /> /// <reference path='../defs/rsvp.d.ts' /> // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> /// <reference path='utils.ts' /> /// <reference path='debug.ts' /> var shared; (function (shared) { (function (utils) { var Tree = require('bintrees').RBTree; var Map = (function () { function Map(hashfn) { utils.dassert(utils.isValue(hashfn)); this._size = 0; this._hashfn = hashfn; this._tree = new Tree(function (a, b) { return a.hash - b.hash; }); } Map.prototype.size = function () { return this._size; }; Map.prototype.find = function (key) { utils.dassert(utils.isValue(key)); var h = this._hashfn(key); var entries = this._tree.find({ hash: h }); if(entries !== null) { for(var i = 0; i < entries.values.length; i++) { if(utils.isEqual(key, entries.values[i].key)) { return entries.values[i].value; } } } return null; }; Map.prototype.insert = function (key, value) { utils.dassert(utils.isValue(key)); utils.dassert(utils.isValue(value)); var h = this._hashfn(key); var entries = this._tree.find({ hash: h }); if(entries !== null) { var free = null; for(var i = 0; i < entries.values.length; i++) { if(entries.values[i].key === null) { if(free === null) { free = i; } } else if(utils.isEqual(key, entries.values[i].key)) { return false; } } if(free !== null) { entries.values[free] = { key: key, value: value }; } else { entries.values.push({ key: key, value: value }); } } else { this._tree.insert({ hash: h, values: [ { key: key, value: value } ] }); } this._size++; return true; }; Map.prototype.findOrInsert = function (key, proto) { if (typeof proto === "undefined") { proto = { }; } var val = this.find(key); if(val !== null) { return val; } else { this.insert(key, proto); return proto; } }; Map.prototype.remove = function (key) { utils.dassert(utils.isValue(key)); var h = this._hashfn(key); var entries = this._tree.find({ hash: h }); if(entries !== null) { var found = true; for(var i = 0; i < entries.values.length; i++) { if(utils.isEqual(key, entries.values[i].key)) { entries.values[i].key = null; entries.values[i].value = null; this._size--; return true; } } } return false; }; Map.prototype.apply = function (handler) { var it = this._tree.iterator(); while(it.next()) { var row = it.data(); for(var i = 0; i < row.values.length; i++) { if(row.values[i].key !== null) { if(handler(row.values[i].key, row.values[i].value) === false) { return false; } } } } return true; }; Map.prototype.removeAll = function () { this._tree.clear(); this._size = 0; }; return Map; })(); utils.Map = Map; /** * A simple string set */ var StringSet = (function () { function StringSet(names) { if (typeof names === "undefined") { names = []; } this._map = new Map(function (k) { return utils.hash(k.toString()); }); this._id = 0; for(var i = 0; i < names.length; i++) { this.put(names[i]); } } StringSet.prototype.put = function (key) { var ok = this._map.insert(key, this._id); if(ok) { this._id++; } return ok; }; StringSet.prototype.has = function (key) { return this._map.find(key) !== null; }; StringSet.prototype.id = function (key) { return this._map.find(key); }; StringSet.prototype.remove = function (key) { return this._map.remove(key); }; StringSet.prototype.size = function () { return this._map.size(); }; StringSet.prototype.removeAll = function () { return this._map.removeAll(); }; StringSet.prototype.apply = function (handler) { return this._map.apply(function (key, value) { return handler(key); }); }; return StringSet; })(); utils.StringSet = StringSet; /** * A simple queue, items can be added/removed from the * head/tail with random access and assertions thrown in. */ var Queue = (function () { function Queue() { this._elems = []; } Queue.prototype.size = function () { return this._elems.length; }; Queue.prototype.empty = function () { return this.size() === 0; }; Queue.prototype.front = function () { return this.at(0); }; Queue.prototype.back = function () { return this.at(this.size() - 1); }; Queue.prototype.at = function (i) { utils.dassert(i >= 0 && i < this.size()); return this._elems[i]; }; Queue.prototype.setAt = function (i, value) { utils.dassert(i >= 0 && i < this.size()); this._elems[i] = value; }; Queue.prototype.push = function (value) { this._elems.push(value); }; Queue.prototype.pop = function () { utils.dassert(!this.empty()); return this._elems.pop(); }; Queue.prototype.unshift = function (value) { this._elems.unshift(value); }; Queue.prototype.shift = function () { utils.dassert(!this.empty()); return this._elems.shift(); }; Queue.prototype.array = function () { return this._elems; }; Queue.prototype.first = function (match) { for(var i = 0; i < this._elems.length; i++) { if(match(this._elems[i])) { return this._elems[i]; } } return null; }; Queue.prototype.filter = function (match) { var matched = new Queue(); for(var i = 0; i < this._elems.length; i++) { if(match(this._elems[i])) { matched.push(this._elems[i]); } } return matched; }; Queue.prototype.apply = function (func) { for(var i = 0; i < this._elems.length; i++) { func(this._elems[i]); } }; return Queue; })(); utils.Queue = Queue; })(shared.utils || (shared.utils = {})); var utils = shared.utils; // module utils })(shared || (shared = {})); // module shared var __extends = this.__extends || function (d, b) { function __() { this.constructor = d; } __.prototype = b.prototype; d.prototype = new __(); }; // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> /// <reference path='collect.ts' /> var shared; (function (shared) { (function (utils) { var fs = require('fs'); var assert = require('assert'); var util = require('util'); var cluster = require('cluster'); /** * Log message levels, should really be an enum. * Logs include messages for current level and higher. NONE turns off * logging. */ utils.LogLevel = { INFO: 1, WARN: 2, FATAL: 3, NONE: 4 }; /** * Writable FD */ var WriteableFD = (function () { function WriteableFD(fd) { this._fd = fd; } WriteableFD.prototype.write = function (str) { var b = new Buffer(str); fs.writeSync(this._fd, b, 0, b.length, null); }; return WriteableFD; })(); utils.WriteableFD = WriteableFD; /** * Logger helper */ var Logger = (function () { function Logger(to, prefix, level, debug, next) { this._to = to; this._prefix = prefix; this._level = level; this._debug = new utils.StringSet(debug); this._next = next; } Logger.prototype.logLevel = function () { return this._level; }; Logger.prototype.isDebugLogging = function (component) { return this._debug.has(component); }; Logger.prototype.enableDebugLogging = function (component, on) { if(utils.isValue(on) && !on) { this._debug.remove(component); } else { this._debug.put(component); } }; Logger.prototype.disableDebugLogging = function () { this._debug.removeAll(); }; Logger.prototype.debug = function (component, fmt) { var msgs = []; for (var _i = 0; _i < (arguments.length - 2); _i++) { msgs[_i] = arguments[_i + 2]; } if(this.isDebugLogging(component)) { var f = component + ': ' + fmt; this.log(utils.LogLevel.INFO, f, msgs); if(this._next) { this._next.log(utils.LogLevel.INFO, f, msgs); } } }; Logger.prototype.info = function (fmt) { var msgs = []; for (var _i = 0; _i < (arguments.length - 1); _i++) { msgs[_i] = arguments[_i + 1]; } this.log(utils.LogLevel.INFO, fmt, msgs); if(this._next) { this._next.log(utils.LogLevel.INFO, fmt, msgs); } }; Logger.prototype.warn = function (fmt) { var msgs = []; for (var _i = 0; _i < (arguments.length - 1); _i++) { msgs[_i] = arguments[_i + 1]; } this.log(utils.LogLevel.WARN, fmt, msgs); if(this._next) { this._next.log(utils.LogLevel.INFO, fmt, msgs); } }; Logger.prototype.fatal = function (fmt) { var msgs = []; for (var _i = 0; _i < (arguments.length - 1); _i++) { msgs[_i] = arguments[_i + 1]; } this.log(utils.LogLevel.FATAL, fmt, msgs); if(this._next) { this._next.log(utils.LogLevel.FATAL, fmt, msgs); } }; Logger.prototype.write = function (msg) { this._to.write(msg); if(this._next) { this._next.write(msg); } }; Logger.prototype.trace = function (fmt) { var msgs = []; for (var _i = 0; _i < (arguments.length - 1); _i++) { msgs[_i] = arguments[_i + 1]; } var e = new Error(); e.name = 'Trace'; e.message = utils.dateFormat(this._prefix, fmt, msgs); Error.captureStackTrace(e, arguments.callee); this.write(e.stack + '\n'); }; Logger.prototype.log = function (type, fmt, msgs) { switch(type) { case utils.LogLevel.INFO: if(this.logLevel() <= utils.LogLevel.INFO) { this._to.write(utils.dateFormat(this._prefix + ' INFO', fmt, msgs)); } break; case utils.LogLevel.WARN: if(this.logLevel() <= utils.LogLevel.WARN) { this._to.write(utils.dateFormat(this._prefix + ' WARNING', fmt, msgs)); } break; case utils.LogLevel.FATAL: if(this.logLevel() <= utils.LogLevel.FATAL) { var err = utils.dateFormat(this._prefix + ' FATAL', fmt, msgs); this._to.write(err); if(!utils.isValue(this._next)) { throw new Error('Fatal error: ' + err); } } break; case utils.LogLevel.NONE: break; default: dassert(false); break; } if(this._next) { this._next.log(utils.LogLevel.INFO, fmt, msgs); } }; return Logger; })(); utils.Logger = Logger; /** * File logger */ var FileLogger = (function (_super) { __extends(FileLogger, _super); function FileLogger(fileprefix, prefix, level, subjects, next) { var w = this.openLog(fileprefix); _super.call(this, w, prefix, level, subjects, next); } FileLogger.prototype.openLog = function (fileprefix) { var i = 0; while(true) { var name = fileprefix + '-' + process.pid + '-' + i; try { var fd = fs.openSync(name, 'ax', '0666'); return new WriteableFD(fd); } catch (e) { // Try again with another suffix i++; if(i === 10) { throw e; } } } }; return FileLogger; })(Logger); utils.FileLogger = FileLogger; var _defaultLogger = null; /** * Set a logger to be used as the default for modules. */ function setdefaultLogger(logger) { dassert(utils.isValue(logger)); _defaultLogger = logger; } utils.setdefaultLogger = setdefaultLogger; /** * Obtains the default logger. If one has not been set then logging is * to process.stdout at the INFO level. */ function defaultLogger() { if(!_defaultLogger) { var prefix = 'master'; if(cluster.worker) { prefix = 'work ' + cluster.worker.id; } _defaultLogger = new Logger(process.stdout, prefix, utils.LogLevel.INFO, []); } return _defaultLogger; } utils.defaultLogger = defaultLogger; var _assertsEnabled = true; /** * Enable/Disable internal asserts. */ function enableAsserts(on) { _assertsEnabled = on; } utils.enableAsserts = enableAsserts; /** * Are assert enabled? */ function assertsEnabled() { return _assertsEnabled; } utils.assertsEnabled = assertsEnabled; /** * Switchable assert handler. */ function dassert(test) { if(_assertsEnabled) { assert.ok(test); } } utils.dassert = dassert; })(shared.utils || (shared.utils = {})); var utils = shared.utils; // module utils })(shared || (shared = {})); // module shared // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> /// <reference path='debug.ts' /> var shared; (function (shared) { (function (utils) { var _ = require('underscore'); var os = require('os'); /* * String hash, see http://www.cse.yorku.ca/~oz/hash.html */ function hash(str, prime) { utils.dassert(isValue(str)); var hash = 5381; if(isValue(prime)) { hash = prime; } var len = str.length; for(var i = 0; i < len; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash = hash & hash; } return hash; } utils.hash = hash; /** * Deep Equals */ function isEqual(x, y) { return _.isEqual(x, y); } utils.isEqual = isEqual; /** * Non-null or undefined value */ function isValue(arg) { return arg !== undefined && arg !== null; } utils.isValue = isValue; /** * Non-null object value */ function isObject(value) { return (value && typeof value === 'object' && !(value instanceof Array)); } utils.isObject = isObject; /** * Non-null array value */ function isArray(value) { return (value && typeof value === 'object' && (value instanceof Array)); } utils.isArray = isArray; /** * Non-null object or array value */ function isObjectOrArray(value) { return (value && typeof value === 'object'); } utils.isObjectOrArray = isObjectOrArray; /** * Corrected type of value. * Arrays & null are not 'objects' */ function typeOf(value) { var s = typeof value; if(s === 'object') { if(value) { if(value instanceof Array) { s = 'array'; } } else { s = 'null'; } } return s; } utils.typeOf = typeOf; /** * Corrected type of value. * Arrays & null are not 'objects' * Objects return their prototype type. */ function treatAs(value) { var s = typeof value; if(s === 'object') { if(value) { return Object.prototype.toString.call(value).match(/^\[object\s(.*)\]$/)[1]; } else { s = 'null'; } } return s; } utils.treatAs = treatAs; function cloneArray(obj) { utils.dassert(isArray(obj)); return obj.slice(0); } utils.cloneArray = cloneArray; function cloneObject(obj) { utils.dassert(isObject(obj)); var temp = { }; for(var key in obj) { temp[key] = obj[key]; } return temp; } utils.cloneObject = cloneObject; function clone(obj) { if(isObject(obj)) { return cloneObject(obj); } else { return cloneArray(obj); } } utils.clone = clone; // ES5 9.2 function toInteger(val) { var v = +val;// toNumber conversion if(isNaN(v)) { return 0; } if(v === 0 || v === Infinity || v == -Infinity) { return v; } if(v < 0) { return -1 * Math.floor(-v); } else { return Math.floor(v); } } utils.toInteger = toInteger; function dateFormat(type, fmt, args) { return new Date().toISOString() + ' ' + format(type, fmt, args); } utils.dateFormat = dateFormat; function format(type, fmt, args) { var m = ''; if(type !== null && type.length > 0) { m += (type + ' '); } var i = 0; var len = args.length; var str = m + String(fmt).replace(/%[sdj%]/g, function (x) { if(x === '%%') { return '%'; } if(i >= len) { return x; } switch(x) { case '%s': return String(args[i++]); case '%d': return Number(args[i++]).toString(); case '%j': return JSON.stringify(args[i++]); default: return x; } }); str += '\n'; for(var x = args[i]; i < len; x = args[++i]) { if(x === null || typeof x !== 'object') { str += x + '\n'; } else { str += JSON.stringify(x, null, ' ') + '\n'; } } return str; } utils.format = format; var _hostInfo = null; function hostInfo() { if(_hostInfo === null) { _hostInfo = os.hostname(); var ifaces = os.networkInterfaces(); for(var dev in ifaces) { var alias = 0; ifaces[dev].forEach(function (details) { if(details.family === 'IPv4' && details.address !== '127.0.0.1') { _hostInfo += ' [' + details.address + ']'; ++alias; } }); } } return _hostInfo; } utils.hostInfo = hostInfo; function exceptionInfo(e) { if(e instanceof Error) { return e.stack; } else { return JSON.stringify(e); } } utils.exceptionInfo = exceptionInfo; })(shared.utils || (shared.utils = {})); var utils = shared.utils; // module utils })(shared || (shared = {})); // module shared // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> var shared; (function (shared) { (function (utils) { var ObjectID = require('mongodb').ObjectID; /* * A network wide unique id wrapper. * Pragmatically it must be a UUID and exposable as a string. */ utils.uidStringLength = 24; function UID() { return new ObjectID(); } utils.UID = UID; function isUID(a) { return (a instanceof ObjectID); } utils.isUID = isUID; function makeUID(id) { var uid = new ObjectID(id); utils.dassert(isUID(uid) && uid.toString() == id.toLowerCase()); return uid; } utils.makeUID = makeUID; function toObjectID(id) { return id; } utils.toObjectID = toObjectID; /* * Identifiable object helper */ var UniqueObject = (function () { function UniqueObject() { this._id = null; } UniqueObject.prototype.id = function () { if(this._id === null) { this._id = UID(); } return this._id; }; return UniqueObject; })(); utils.UniqueObject = UniqueObject; /* * Map specialized for using id keys. A bodge until generics are supported. */ var IdMap = (function () { function IdMap() { this._map = new shared.utils.Map(shared.utils.hash); } IdMap.prototype.size = function () { return this._map.size(); }; IdMap.prototype.find = function (key) { return this._map.find(key.toString()); }; IdMap.prototype.insert = function (key, value) { return this._map.insert(key.toString(), value); }; IdMap.prototype.findOrInsert = function (key, proto) { if (typeof proto === "undefined") { proto = { }; } return this._map.findOrInsert(key.toString(), proto); }; IdMap.prototype.remove = function (key) { return this._map.remove(key.toString()); }; IdMap.prototype.apply = function (handler) { return this._map.apply(function (k, v) { return handler(makeUID(k), v); }); }; IdMap.prototype.removeAll = function () { this._map.removeAll(); }; return IdMap; })(); utils.IdMap = IdMap; })(shared.utils || (shared.utils = {})); var utils = shared.utils; // module utils })(shared || (shared = {})); // module shared // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> /// <reference path='utils.ts' /> /// <reference path='collect.ts' /> /// <reference path='id.ts' /> var shared; (function (shared) { (function (types) { var TypeDesc = (function (_super) { __extends(TypeDesc, _super); function TypeDesc(isobj, props) { _super.call(this); this._isobj = isobj; this._props = props; } TypeDesc.prototype.isobj = function () { return this._isobj; }; TypeDesc.prototype.isarray = function () { return !this._isobj; }; TypeDesc.prototype.props = function () { return this._props; }; TypeDesc.prototype.typeDesc = function () { var props = 'o#'; if(this.isarray()) { props = 'a#'; } for(var i = 0; i < this._props.length; i++) { props += this._props[i]; props += '#'; } return props; }; return TypeDesc; })(shared.utils.UniqueObject); types.TypeDesc = TypeDesc; var TypeStore = (function () { function TypeStore() { shared.utils.dassert(TypeStore._instance == null); this._tree = new shared.utils.Map(shared.utils.hash); } TypeStore.instance = function instance() { if(!TypeStore._instance) { TypeStore._instance = new TypeStore(); } return TypeStore._instance; }; TypeStore.prototype.type = function (obj) { shared.utils.dassert(shared.utils.isObjectOrArray(obj)); var p = TypeStore.props(obj); var td = this._tree.find(p); if(td === null) { var ps = p.split('#'); ps.shift(); ps.pop(); td = new TypeDesc(shared.utils.isObject(obj), ps); this._tree.insert(p, td); } return td; }; TypeStore.props = function props(obj) { shared.utils.dassert(shared.utils.isObjectOrArray(obj)); var props = 'o#'; if(obj instanceof Array) { props = 'a#'; } for(var prop in obj) { if(obj.hasOwnProperty(prop)) { props += prop; props += '#'; } } return props; }; return TypeStore; })(); types.TypeStore = TypeStore; })(shared.types || (shared.types = {})); var types = shared.types; // types })(shared || (shared = {})); // shared // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> /// <reference path='utils.ts' /> /// <reference path='id.ts' /> var shared; (function (shared) { (function (serial) { /* * Object/Array reference holder. Used to represent a reference when * de-serialising data. */ var Reference = (function () { function Reference(id) { shared.utils.dassert(shared.utils.isUID(id)); this._id = id; } Reference.prototype.id = function () { return this._id; }; return Reference; })(); serial.Reference = Reference; /* * Append serialized form of an object/array onto the supplied string. * Returns the passed string. */ function writeObject(rh, obj, to, identify) { if (typeof to === "undefined") { to = ''; } if (typeof identify === "undefined") { identify = false; } shared.utils.dassert(shared.utils.isObjectOrArray(rh)); shared.utils.dassert(shared.utils.isObjectOrArray(obj)); if(obj instanceof Array) { to += '['; } else { to += '{'; } if(identify) { to += rh.valueId(obj) + ' '; to += rh.valueRev(obj) + ' '; } var k = Object.keys(obj); for(var i = 0; i < k.length; i++) { to = writeValue(rh, k[i], to); to += ":"; to = writeValue(rh, obj[k[i]], to); if(i < k.length - 1) { to += ','; } } if(obj instanceof Array) { to += ']'; } else { to += '}'; } return to; } serial.writeObject = writeObject; /* * Append serialized form of a value onto the supplied string. * Object/Array values are serialised by reference, see writeObject() for * full serialisation of object/array properties. Returns the passed string. */ function writeValue(rh, value, to) { if (typeof to === "undefined") { to = ''; } shared.utils.dassert(shared.utils.isObject(rh)); var type = shared.utils.treatAs(value); switch(type) { case 'null': to += 'null'; break; case 'undefined': to += 'undefined'; break; case 'number': case 'Number': case 'boolean': case 'Boolean': to += value.toString(); break; case 'string': case 'String': to += JSON.stringify(value); break; case 'Date': to += JSON.stringify(value.toString()); break; case 'Object': case 'Array': to += '<' + rh.valueId(value) + '>'; break; case 'function': case 'RegExp': case 'Error': to += 'null'; break; default: shared.utils.defaultLogger().fatal('Unexpected type: %s', type); break; } return to; } serial.writeValue = writeValue; function readObject(str, proto) { shared.utils.dassert(str.length > 1 && (str.charAt(0) === '[' || str.charAt(0) === '{') && (str.charAt(str.length - 1) === ']' || str.charAt(str.length - 1) === '}')); // Check is we have a proto & its the right type if(str.charAt(0) === '{') { if(!shared.utils.isValue(proto)) { proto = { }; } else { shared.utils.dassert(shared.utils.isObject(proto)); } } else { if(!shared.utils.isValue(proto)) { proto = []; } else { shared.utils.dassert(shared.utils.isArray(proto)); // Prop delete does not work well on arrays so zero proto proto.length = 0; } } // Read props var rs = new ReadStream(str.substr(1, str.length - 2)); var keys = Object.keys(proto); var k = 0; while(true) { rs.skipWS(); if(rs.eof()) { break; } // Read prop name var prop = rs.readNextValue(); shared.utils.dassert(typeof prop === 'string'); // Delete rest of proto props if does not match what is being read if(k !== -1 && prop != keys[k]) { for(var i = k; i < keys.length; i++) { delete proto[keys[i]]; } k = -1; } // Skip ':' rs.skipWS(); shared.utils.dassert(!rs.eof()); shared.utils.dassert(rs.peek() === ':'); rs.skip(); rs.skipWS(); // Read value & assign var value = rs.readNextValue(); proto[prop] = value; // Skip ',' if present rs.skipWS(); if(!rs.eof()) { shared.utils.dassert(rs.peek() === ','); rs.skip(); rs.skipWS(); } else { break; } } return proto; } serial.readObject = readObject; /* * Read a value as encoded by writeValue. The passed string must contain * one complete value with no leading or trailing characters. May return * null if passed 'null'. */ function readValue(str) { shared.utils.dassert(shared.utils.isValue(str)); var rs = new ReadStream(str); return rs.readNextValue(); } serial.readValue = readValue; var ReadStream = (function () { function ReadStream(from) { shared.utils.dassert(shared.utils.isValue(from)); this._from = from; this._at = 0; } ReadStream._numberPat = /^-?(0|([1-9][0-9]*))(\.[0-9]+)?([eE][-+][0-9]+)?/; ReadStream.prototype.eof = function () { return this._at >= this._from.length; }; ReadStream.prototype.skip = function (n) { if (typeof n === "undefined") { n = 1; } this._at += n; }; ReadStream.prototype.skipWS = function () { while(this._at < this._from.length && (this._from[this._at] === ' ' || this._from[this._at] === '\t')) { this._at++; } }; ReadStream.prototype.peek = function (n) { if (typeof n === "undefined") { n = 0; } shared.utils.dassert(this._at + n < this._from.length); return this._from[this._at + n]; }; ReadStream.prototype.readNextValue = /* * Read a value as encoded by writeValue. The passed string must contain * one complete value with no leading or trailing characters. May return * null if passed 'null'. */ function () { // Simple things first if(this._from.substr(this._at, 4) === 'null') { this._at += 4; return null; } else if(this._from.substr(this._at, 9) === 'undefined') { this._at += 9; return undefined; } else if(this._from.substr(this._at, 4) === 'true') { this._at += 4; return true; } else if(this._from.substr(this._at, 5) === 'false') { this._at += 5; return false; } else if(this._from.substr(this._at, 3) === 'NaN') { this._at += 3; return NaN; } else if(this._from.substr(this._at, 8) === 'Infinity') { this._at += 8; return Infinity; } else if(this._from.substr(this._at, 9) === '-Infinity') { this._at += 9; return -Infinity; } // JSON escaped string? if(this._from.charAt(this._at) === '"') { var end = this._at + 1; while(end < this._from.length) { if(this._from.charAt(end) === '\\') { end += 1; } else if(this._from.charAt(end) === '"') { break; } end += 1; } if(end < this._from.length) { var s = this._from.substr(this._at, end - this._at + 1); this._at = end + 1; return JSON.parse(s); } } // Reference? if(this._from.charAt(this._at) === '<' && this._from.charAt(this._at + 1 + shared.utils.uidStringLength) === '>') { var id = this._from.substr(this._at + 1, shared.utils.uidStringLength); this._at += (2 + shared.utils.uidStringLength); return new Reference(shared.utils.makeUID(id)); } // Maybe a number var l = this.numberLength(); if(l) { var n = parseFloat(this._from.substr(this._at)); shared.utils.dassert(!isNaN(n)); this._at += l; return n; } shared.utils.defaultLogger().fatal('Unexpected value encoding: %s', this._from.substr(this._at)); }; ReadStream.prototype.numberLength = function () { var ex = ReadStream._numberPat.exec(this._from.substr(this._at)); if(ex) { return ex[0].length; } else { return 0; } }; return ReadStream; })(); })(shared.serial || (shared.serial = {})); var serial = shared.serial; // tracker })(shared || (shared = {})); // shared // Copyright (c) Kevin Jones. All rights reserved. Licensed under the Apache // License, Version 2.0. See LICENSE.txt in the project root for complete // license information. /// <reference path='import.ts' /> /// <reference path='utils.ts' /> /// <reference path='id.ts' /> /// <reference path='types.ts' /> /// <reference path='serial.ts' /> /* * Tracking provides a core service to enabling monitoring of how objects * an arrays are changed over some period. It has similar motives to the * proposed Object.observe model but is specifically designed to be * node portable & suitable for distributed transactions. * * This code generates raw tracking logs. They need post-processing for * most use cases, see mtx.ts for code that does this in this case. */ var shared; (function (shared) { (function (tracker) { var Buffer = require('buffer'); /* * Exception for indicating the cache is missing an object * needed for navigation. */ var UnknownReference = (function () { // Id of missing object function UnknownReference(id, prop, missing) { this._id = id; this._prop = prop; this._missing = missing; } UnknownReference.prototype.id = function () { return this._id; }; UnknownReference.prototype.prop = function () { return this._prop; }; UnknownReference.prototype.missing = function () { return this._missing; }; return UnknownReference; })(); tracker.UnknownReference = UnknownReference; /* * Recover the tracker for an object/array, may return null */ function getTrackerUnsafe(value) { if(value._tracker === undefined) { return null; } return value._tracker; } tracker.getTrackerUnsafe = getTrackerUnsafe; /* * Recover the tracker for an object/array */ function getTracker(value) { shared.utils.dassert(shared.utils.isObject(value._tracker)); return value._tracker; } tracker.getTracker = getTracker; /* * Test if object is tracked */ function isTracked(value) { return shared.utils.isObject(value._tracker); } tracker.isTracked = isTracked; /* * Object/Array tracker. Construct this over an object/array and it will * attach itself to that object/array as a non-enumerable '_tracker' property. * This is kind of odd, but saves doing object->tracker lookups. The downside * is to avoid a circular ref many tracker methods must be passed the objects * they are tracking as this is not recorded in the tracker itself. * * The tracker wraps the enumerable properties of the object/array so that * it can log reads to other objects/arrays and any mutations. The log can * be accessed via changes(). * * The mechanics here are messy so I have simply tried to write this as correct * rather than as quick & correct. A bit of extra thought can probably * improve the performance a lot. */ var Tracker = (function () { function Tracker(tc, obj, id, rev) { if (typeof id === "undefined") { id = shared.utils.UID(); } shared.utils.dassert(shared.utils.isObject(tc)); shared.utils.dassert(shared.utils.isUID(id)); // Error check if(obj === null || typeof (obj) !== 'object') { shared.utils.defaultLogger().fatal('Trying to track non-object/array type'); } if(obj.hasOwnProperty('_tracker')) { shared.utils.defaultLogger().fatal('Trying to track already tracked object or array'); } // Init this._tc = tc; this._rev = rev || 0; this._id = id; this._lastTx = -1; this._id = id; this._type = shared.types.TypeStore.instance().type(obj); this._userdata = null; this._ref = 0; // Add tracker to object Object.defineProperty(obj, '_tracker', { value: this }); // Start tracking if(obj instanceof Array) { trackArray(obj); } for(var prop in obj) { this.track(obj, prop); } } Tracker.prototype.kill = /* * When trackers die they lose connection to the cache. Normally * they die when changes to the object can not be undone and so * the object needs to be refreshed from the master cache. */ function () { this._tc = null; }; Tracker.prototype.isDead = /* * Has this tracker/object combo died */ function () { return this._tc === null; }; Tracker.prototype.tc = /** * Get the tracker cache this tracker is using */ function () { return this._tc; }; Tracker.prototype.id = /** * Get the unique object id */ function () { return this._id; }; Tracker.prototype.type = /** * Get the objects (pre-changes) type */ function () { return this._type; }; Tracker.prototype.rev = /** * Get/Increment the object revision, returning new value */ function (by) { if(by !== undefined) { this._rev += by; } return this._rev; }; Tracker.prototype.setRev = /** * Set object rev to a value, must be >= to existing rev */ function (to) { if(to