json-wire-protocol
Advanced tools
Sorry, the diff of this file is not supported yet
+3
-0
@@ -0,1 +1,4 @@ | ||
| # 1.0.0 | ||
| - node only version with complete change of module interface. | ||
| # 0.2.0 | ||
@@ -2,0 +5,0 @@ - expose separator constant value. |
+103
-206
@@ -1,237 +0,134 @@ | ||
| (function(global, exports) { | ||
| var HAS_BUFFER = typeof Buffer !== 'undefined'; | ||
| var Transform = require('stream').Transform; | ||
| var SEPARATOR = ':'; | ||
| var SEPARATOR_CODE = SEPARATOR.charCodeAt(0); | ||
| var EventEmitter = | ||
| global.EventEmitter2 || | ||
| require('eventemitter2').EventEmitter2; | ||
| var SEPARATOR = ':'; | ||
| var SEPARATOR_CODE = SEPARATOR.charCodeAt(0); | ||
| /** | ||
| * First ocurrence of where string occurs in a buffer. | ||
| * | ||
| * NOTE: this is not UTF8 safe generally we expect to find the correct | ||
| * char fairly quickly unless the buffer is incorrectly formatted. | ||
| * | ||
| * @param {Buffer} buffer haystack. | ||
| * @param {String} string needle. | ||
| * @return {Numeric} -1 if not found index otherwise. | ||
| */ | ||
| function indexInBuffer(buffer, string) { | ||
| if (typeof buffer === 'string') | ||
| return buffer.indexOf(string); | ||
| if (buffer.length === 0) | ||
| return -1; | ||
| var index = 0; | ||
| var length = buffer.length; | ||
| do { | ||
| if (buffer[index] === SEPARATOR_CODE) | ||
| return index; | ||
| } while ( | ||
| ++index && index + 1 < length | ||
| ); | ||
| /** | ||
| * First ocurrence of where string occurs in a buffer. | ||
| * | ||
| * NOTE: this is not UTF8 safe generally we expect to find the correct | ||
| * char fairly quickly unless the buffer is incorrectly formatted. | ||
| * | ||
| * @param {Buffer} buffer haystack. | ||
| * @param {String} code needle. | ||
| * @return {Numeric} -1 if not found index otherwise. | ||
| */ | ||
| function indexInBuffer(buffer, code) { | ||
| if (buffer.length === 0) | ||
| return -1; | ||
| } | ||
| /** | ||
| * Wrapper for creating either a buffer or ArrayBuffer. | ||
| */ | ||
| function createByteContainer() { | ||
| if (HAS_BUFFER) | ||
| return new Buffer(0); | ||
| var index = 0; | ||
| var length = buffer.length; | ||
| return new Uint8Array(); | ||
| } | ||
| do { | ||
| if (buffer[index] === code) return index; | ||
| } while ( | ||
| ++index && index + 1 < length | ||
| ); | ||
| /** | ||
| * Join the contents of byte container a and b returning c. | ||
| */ | ||
| function concatByteContainers(a, b) { | ||
| if (HAS_BUFFER) | ||
| return Buffer.concat([a, b]); | ||
| return -1; | ||
| } | ||
| // make sure everything is unit8 | ||
| if (a instanceof ArrayBuffer) | ||
| a = new Uint8Array(a); | ||
| /** | ||
| * converts an object to a string representation suitable for storage on disk. | ||
| * Its very important to note that the length in the string refers to the utf8 | ||
| * size of the json content in bytes (as utf8) not the JS string length. | ||
| * | ||
| * @param {Object} object to stringify. | ||
| * @return {String} serialized object. | ||
| */ | ||
| function stringify(object) { | ||
| var json = JSON.stringify(object); | ||
| var len = Buffer.byteLength(json); | ||
| if (b instanceof ArrayBuffer) | ||
| b = new Uint8Array(b); | ||
| return len + SEPARATOR + json; | ||
| } | ||
| // sizes of originals | ||
| var aLen = a.length; | ||
| var bLen = b.length; | ||
| /** | ||
| * attempts to parse a given buffer or string. | ||
| * | ||
| * @param {Uint8Array|Buffer} input in byteLength:{json..} format. | ||
| * @return {Objec} JS object. | ||
| */ | ||
| function parse(input) { | ||
| var stream = new Stream(); | ||
| var result; | ||
| var array = new Uint8Array(aLen + bLen); | ||
| array.set(a); | ||
| array.set(b, aLen); | ||
| stream.once('data', function(data) { | ||
| result = data; | ||
| }); | ||
| // return new byte container | ||
| return array; | ||
| } | ||
| stream.write(input); | ||
| function sliceByteContainers(container, start, end) { | ||
| start = start || 0; | ||
| end = end || byteLength(container); | ||
| if (HAS_BUFFER) | ||
| return container.slice(start, end); | ||
| return container.subarray(start, end); | ||
| if (!result) { | ||
| throw new Error( | ||
| 'no command available from parsing:' + input | ||
| ); | ||
| } | ||
| /** | ||
| * Like Buffer.byteLength but works on ArrayBuffers too. | ||
| */ | ||
| function byteLength(input) { | ||
| if (typeof input === 'string') { | ||
| if (HAS_BUFFER) { | ||
| return Buffer.byteLength(input); | ||
| } | ||
| var encoder = new TextEncoder(); | ||
| var out = encoder.encode(input); | ||
| return out.length; | ||
| } | ||
| return result; | ||
| } | ||
| return input.length; | ||
| } | ||
| function Stream() { | ||
| this._pendingLength = null; | ||
| this._buffer = new Buffer(0); | ||
| function bytesToUtf8(container, start, end) { | ||
| if (!start) | ||
| start = 0; | ||
| Transform.call(this, { objectMode: true }); | ||
| } | ||
| if (!end) | ||
| end = byteLength(container); | ||
| Stream.prototype = { | ||
| __proto__: Transform.prototype, | ||
| if (HAS_BUFFER) | ||
| return container.toString('utf8', start, end); | ||
| _transform: function(chunk, encoding, cb) { | ||
| if (!this._pendingLength) { | ||
| var idx = indexInBuffer(chunk, SEPARATOR_CODE) | ||
| var decoder = new TextDecoder(); | ||
| var array = container.subarray(start, end); | ||
| // Nothing to do just buffer it... | ||
| if (idx === -1) { | ||
| this._buffer = Buffer.concat([this._buffer, chunk]); | ||
| return cb(); | ||
| } | ||
| return decoder.decode(array); | ||
| } | ||
| // number of bytes in the json segment... | ||
| var length = Buffer.concat([this._buffer, chunk.slice(0, idx)]); | ||
| this._pendingLength = parseInt(length.toString(), 10); | ||
| this._buffer = new Buffer(0); | ||
| /** | ||
| * converts an object to a string representation suitable for storage on disk. | ||
| * Its very important to note that the length in the string refers to the utf8 | ||
| * size of the json content in bytes (as utf8) not the JS string length. | ||
| * | ||
| * @param {Object} object to stringify. | ||
| * @return {String} serialized object. | ||
| */ | ||
| function stringify(object) { | ||
| var json = JSON.stringify(object); | ||
| var len = byteLength(json); | ||
| // We have transitioned to a pending length state so do another pass. | ||
| return this._transform(chunk.slice(idx + 1), encoding, cb); | ||
| } | ||
| return len + SEPARATOR + json; | ||
| } | ||
| // Total length too small nothing to do... | ||
| if (this._buffer.length + chunk.length < this._pendingLength) { | ||
| this._buffer = Buffer.concat([this._buffer, chunk]); | ||
| return cb(); | ||
| } | ||
| /** | ||
| * attempts to parse a given buffer or string. | ||
| * | ||
| * @param {Uint8Array|Buffer} input in byteLength:{json..} format. | ||
| * @return {Objec} JS object. | ||
| */ | ||
| function parse(input) { | ||
| var stream = new Stream(); | ||
| var result; | ||
| var buffer = Buffer.concat([this._buffer, chunk]); | ||
| var remainder = null; | ||
| stream.once('data', function(data) { | ||
| result = data; | ||
| }); | ||
| if (buffer.length > this._pendingLength) { | ||
| remainder = buffer.slice(this._pendingLength); | ||
| buffer = buffer.slice(0, this._pendingLength); | ||
| } | ||
| stream.write(input); | ||
| // Reset internal state and push current json string. | ||
| this._pendingLength = null; | ||
| this._buffer = new Buffer(0); | ||
| this.push(JSON.parse(buffer.toString())) | ||
| if (!result) { | ||
| throw new Error( | ||
| 'no command available from parsing:' + input | ||
| ); | ||
| // If we have any remaining data we need to keep processing... | ||
| if (remainder) { | ||
| return this._transform(remainder, encoding, cb); | ||
| } | ||
| return result; | ||
| // Otherwise we are done yay.... | ||
| return cb(); | ||
| } | ||
| function Stream() { | ||
| EventEmitter.call(this); | ||
| }; | ||
| this._pendingLength = null; | ||
| // zero length buffer so we can concat later | ||
| // this is always a unit8 array or a buffer. | ||
| this._buffer = createByteContainer(); | ||
| } | ||
| Stream.prototype = { | ||
| __proto__: EventEmitter.prototype, | ||
| _findLength: function() { | ||
| if (this._pendingLength === null) { | ||
| var idx = indexInBuffer(this._buffer, SEPARATOR); | ||
| if (idx === -1) | ||
| return; | ||
| // mark the length to read out of the rolling buffer. | ||
| this._pendingLength = parseInt( | ||
| bytesToUtf8(this._buffer, 0, idx), | ||
| 10 | ||
| ); | ||
| this._buffer = sliceByteContainers(this._buffer, idx + 1); | ||
| } | ||
| }, | ||
| _readBuffer: function() { | ||
| // if the buffer.length is < then we pendingLength need to buffer | ||
| // more data. | ||
| if (!this._pendingLength || this._buffer.length < this._pendingLength) | ||
| return false; | ||
| // extract remainder and parse json | ||
| var message = sliceByteContainers(this._buffer, 0, this._pendingLength); | ||
| this._buffer = sliceByteContainers(this._buffer, this._pendingLength); | ||
| this._pendingLength = null; | ||
| var result; | ||
| try { | ||
| message = bytesToUtf8(message); | ||
| result = JSON.parse(message); | ||
| } catch (e) { | ||
| this.emit('error', e); | ||
| return false; | ||
| } | ||
| this.emit('data', result); | ||
| return true; | ||
| }, | ||
| write: function(buffer) { | ||
| // append new buffer to whatever we have. | ||
| this._buffer = concatByteContainers(this._buffer, buffer); | ||
| do { | ||
| // attempt to find length of next message. | ||
| this._findLength(); | ||
| } while ( | ||
| // keep repeating while there are messages | ||
| this._readBuffer() | ||
| ); | ||
| } | ||
| }; | ||
| exports.parse = parse; | ||
| exports.stringify = stringify; | ||
| exports.Stream = Stream; | ||
| exports.separator = SEPARATOR; | ||
| }).apply( | ||
| null, | ||
| typeof window === 'undefined' ? | ||
| [global, module.exports] : | ||
| [window, window.jsonWireProtocol = {}] | ||
| ); | ||
| exports.parse = parse; | ||
| exports.stringify = stringify; | ||
| exports.Stream = Stream; | ||
| exports.separator = SEPARATOR; |
+5
-14
| { | ||
| "name": "json-wire-protocol", | ||
| "version": "0.2.1", | ||
| "main": "index.js", | ||
| "version": "1.0.0", | ||
| "main": "lib/index.js", | ||
| "description": "JSON wire protocol parser/writer (used in mozilla debugger protocol)", | ||
| "author": "James Lal", | ||
| "main": "lib/index.js", | ||
| "dependencies" : { | ||
| "eventemitter2": "~0.4" | ||
| }, | ||
| "repository": "git@github.com:lightsofapollo/json-wire-protocol.git", | ||
| "scripts": { | ||
| "test": "make test-node" | ||
| "test": "mocha" | ||
| }, | ||
| "devDependencies": { | ||
| "mocha": "~1.7", | ||
| "chai": "~1.6", | ||
| "test-agent": "~0.12" | ||
| "mocha": "^2.0.1" | ||
| }, | ||
| "license": "MIT", | ||
@@ -24,0 +15,0 @@ "engine": { |
| suite('json wire protocol', function() { | ||
| var assert = require('assert'); | ||
| var jsonWireProtocol = require('../'); | ||
| var subject; | ||
@@ -7,9 +10,3 @@ var TWO_BYTE = 'ž'; | ||
| function createBytes(content) { | ||
| if (HAS_BUFFER) { | ||
| return new Buffer(content); | ||
| } | ||
| // create text encode to initialize ArrayBuffer | ||
| var encoder = new TextEncoder(); | ||
| return encoder.encode(content).buffer; | ||
| } | ||
@@ -92,4 +89,2 @@ | ||
| return; | ||
| test('result after writing to stream', function() { | ||
@@ -96,0 +91,0 @@ assert.deepEqual( |
| ;!function(exports, undefined) { | ||
| var isArray = Array.isArray ? Array.isArray : function _isArray(obj) { | ||
| return Object.prototype.toString.call(obj) === "[object Array]"; | ||
| }; | ||
| var defaultMaxListeners = 10; | ||
| function init() { | ||
| this._events = {}; | ||
| if (this._conf) { | ||
| configure.call(this, this._conf); | ||
| } | ||
| } | ||
| function configure(conf) { | ||
| if (conf) { | ||
| this._conf = conf; | ||
| conf.delimiter && (this.delimiter = conf.delimiter); | ||
| conf.maxListeners && (this._events.maxListeners = conf.maxListeners); | ||
| conf.wildcard && (this.wildcard = conf.wildcard); | ||
| conf.newListener && (this.newListener = conf.newListener); | ||
| if (this.wildcard) { | ||
| this.listenerTree = {}; | ||
| } | ||
| } | ||
| } | ||
| function EventEmitter(conf) { | ||
| this._events = {}; | ||
| this.newListener = false; | ||
| configure.call(this, conf); | ||
| } | ||
| // | ||
| // Attention, function return type now is array, always ! | ||
| // It has zero elements if no any matches found and one or more | ||
| // elements (leafs) if there are matches | ||
| // | ||
| function searchListenerTree(handlers, type, tree, i) { | ||
| if (!tree) { | ||
| return []; | ||
| } | ||
| var listeners=[], leaf, len, branch, xTree, xxTree, isolatedBranch, endReached, | ||
| typeLength = type.length, currentType = type[i], nextType = type[i+1]; | ||
| if (i === typeLength && tree._listeners) { | ||
| // | ||
| // If at the end of the event(s) list and the tree has listeners | ||
| // invoke those listeners. | ||
| // | ||
| if (typeof tree._listeners === 'function') { | ||
| handlers && handlers.push(tree._listeners); | ||
| return [tree]; | ||
| } else { | ||
| for (leaf = 0, len = tree._listeners.length; leaf < len; leaf++) { | ||
| handlers && handlers.push(tree._listeners[leaf]); | ||
| } | ||
| return [tree]; | ||
| } | ||
| } | ||
| if ((currentType === '*' || currentType === '**') || tree[currentType]) { | ||
| // | ||
| // If the event emitted is '*' at this part | ||
| // or there is a concrete match at this patch | ||
| // | ||
| if (currentType === '*') { | ||
| for (branch in tree) { | ||
| if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+1)); | ||
| } | ||
| } | ||
| return listeners; | ||
| } else if(currentType === '**') { | ||
| endReached = (i+1 === typeLength || (i+2 === typeLength && nextType === '*')); | ||
| if(endReached && tree._listeners) { | ||
| // The next element has a _listeners, add it to the handlers. | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree, typeLength)); | ||
| } | ||
| for (branch in tree) { | ||
| if (branch !== '_listeners' && tree.hasOwnProperty(branch)) { | ||
| if(branch === '*' || branch === '**') { | ||
| if(tree[branch]._listeners && !endReached) { | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], typeLength)); | ||
| } | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); | ||
| } else if(branch === nextType) { | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i+2)); | ||
| } else { | ||
| // No match on this one, shift into the tree but not in the type array. | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[branch], i)); | ||
| } | ||
| } | ||
| } | ||
| return listeners; | ||
| } | ||
| listeners = listeners.concat(searchListenerTree(handlers, type, tree[currentType], i+1)); | ||
| } | ||
| xTree = tree['*']; | ||
| if (xTree) { | ||
| // | ||
| // If the listener tree will allow any match for this part, | ||
| // then recursively explore all branches of the tree | ||
| // | ||
| searchListenerTree(handlers, type, xTree, i+1); | ||
| } | ||
| xxTree = tree['**']; | ||
| if(xxTree) { | ||
| if(i < typeLength) { | ||
| if(xxTree._listeners) { | ||
| // If we have a listener on a '**', it will catch all, so add its handler. | ||
| searchListenerTree(handlers, type, xxTree, typeLength); | ||
| } | ||
| // Build arrays of matching next branches and others. | ||
| for(branch in xxTree) { | ||
| if(branch !== '_listeners' && xxTree.hasOwnProperty(branch)) { | ||
| if(branch === nextType) { | ||
| // We know the next element will match, so jump twice. | ||
| searchListenerTree(handlers, type, xxTree[branch], i+2); | ||
| } else if(branch === currentType) { | ||
| // Current node matches, move into the tree. | ||
| searchListenerTree(handlers, type, xxTree[branch], i+1); | ||
| } else { | ||
| isolatedBranch = {}; | ||
| isolatedBranch[branch] = xxTree[branch]; | ||
| searchListenerTree(handlers, type, { '**': isolatedBranch }, i+1); | ||
| } | ||
| } | ||
| } | ||
| } else if(xxTree._listeners) { | ||
| // We have reached the end and still on a '**' | ||
| searchListenerTree(handlers, type, xxTree, typeLength); | ||
| } else if(xxTree['*'] && xxTree['*']._listeners) { | ||
| searchListenerTree(handlers, type, xxTree['*'], typeLength); | ||
| } | ||
| } | ||
| return listeners; | ||
| } | ||
| function growListenerTree(type, listener) { | ||
| type = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | ||
| // | ||
| // Looks for two consecutive '**', if so, don't add the event at all. | ||
| // | ||
| for(var i = 0, len = type.length; i+1 < len; i++) { | ||
| if(type[i] === '**' && type[i+1] === '**') { | ||
| return; | ||
| } | ||
| } | ||
| var tree = this.listenerTree; | ||
| var name = type.shift(); | ||
| while (name) { | ||
| if (!tree[name]) { | ||
| tree[name] = {}; | ||
| } | ||
| tree = tree[name]; | ||
| if (type.length === 0) { | ||
| if (!tree._listeners) { | ||
| tree._listeners = listener; | ||
| } | ||
| else if(typeof tree._listeners === 'function') { | ||
| tree._listeners = [tree._listeners, listener]; | ||
| } | ||
| else if (isArray(tree._listeners)) { | ||
| tree._listeners.push(listener); | ||
| if (!tree._listeners.warned) { | ||
| var m = defaultMaxListeners; | ||
| if (typeof this._events.maxListeners !== 'undefined') { | ||
| m = this._events.maxListeners; | ||
| } | ||
| if (m > 0 && tree._listeners.length > m) { | ||
| tree._listeners.warned = true; | ||
| console.error('(node) warning: possible EventEmitter memory ' + | ||
| 'leak detected. %d listeners added. ' + | ||
| 'Use emitter.setMaxListeners() to increase limit.', | ||
| tree._listeners.length); | ||
| console.trace(); | ||
| } | ||
| } | ||
| } | ||
| return true; | ||
| } | ||
| name = type.shift(); | ||
| } | ||
| return true; | ||
| }; | ||
| // By default EventEmitters will print a warning if more than | ||
| // 10 listeners are added to it. This is a useful default which | ||
| // helps finding memory leaks. | ||
| // | ||
| // Obviously not all Emitters should be limited to 10. This function allows | ||
| // that to be increased. Set to zero for unlimited. | ||
| EventEmitter.prototype.delimiter = '.'; | ||
| EventEmitter.prototype.setMaxListeners = function(n) { | ||
| this._events || init.call(this); | ||
| this._events.maxListeners = n; | ||
| if (!this._conf) this._conf = {}; | ||
| this._conf.maxListeners = n; | ||
| }; | ||
| EventEmitter.prototype.event = ''; | ||
| EventEmitter.prototype.once = function(event, fn) { | ||
| this.many(event, 1, fn); | ||
| return this; | ||
| }; | ||
| EventEmitter.prototype.many = function(event, ttl, fn) { | ||
| var self = this; | ||
| if (typeof fn !== 'function') { | ||
| throw new Error('many only accepts instances of Function'); | ||
| } | ||
| function listener() { | ||
| if (--ttl === 0) { | ||
| self.off(event, listener); | ||
| } | ||
| fn.apply(this, arguments); | ||
| }; | ||
| listener._origin = fn; | ||
| this.on(event, listener); | ||
| return self; | ||
| }; | ||
| EventEmitter.prototype.emit = function() { | ||
| this._events || init.call(this); | ||
| var type = arguments[0]; | ||
| if (type === 'newListener' && !this.newListener) { | ||
| if (!this._events.newListener) { return false; } | ||
| } | ||
| // Loop through the *_all* functions and invoke them. | ||
| if (this._all) { | ||
| var l = arguments.length; | ||
| var args = new Array(l - 1); | ||
| for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; | ||
| for (i = 0, l = this._all.length; i < l; i++) { | ||
| this.event = type; | ||
| this._all[i].apply(this, args); | ||
| } | ||
| } | ||
| // If there is no 'error' event listener then throw. | ||
| if (type === 'error') { | ||
| if (!this._all && | ||
| !this._events.error && | ||
| !(this.wildcard && this.listenerTree.error)) { | ||
| if (arguments[1] instanceof Error) { | ||
| throw arguments[1]; // Unhandled 'error' event | ||
| } else { | ||
| throw new Error("Uncaught, unspecified 'error' event."); | ||
| } | ||
| return false; | ||
| } | ||
| } | ||
| var handler; | ||
| if(this.wildcard) { | ||
| handler = []; | ||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | ||
| searchListenerTree.call(this, handler, ns, this.listenerTree, 0); | ||
| } | ||
| else { | ||
| handler = this._events[type]; | ||
| } | ||
| if (typeof handler === 'function') { | ||
| this.event = type; | ||
| if (arguments.length === 1) { | ||
| handler.call(this); | ||
| } | ||
| else if (arguments.length > 1) | ||
| switch (arguments.length) { | ||
| case 2: | ||
| handler.call(this, arguments[1]); | ||
| break; | ||
| case 3: | ||
| handler.call(this, arguments[1], arguments[2]); | ||
| break; | ||
| // slower | ||
| default: | ||
| var l = arguments.length; | ||
| var args = new Array(l - 1); | ||
| for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; | ||
| handler.apply(this, args); | ||
| } | ||
| return true; | ||
| } | ||
| else if (handler) { | ||
| var l = arguments.length; | ||
| var args = new Array(l - 1); | ||
| for (var i = 1; i < l; i++) args[i - 1] = arguments[i]; | ||
| var listeners = handler.slice(); | ||
| for (var i = 0, l = listeners.length; i < l; i++) { | ||
| this.event = type; | ||
| listeners[i].apply(this, args); | ||
| } | ||
| return (listeners.length > 0) || this._all; | ||
| } | ||
| else { | ||
| return this._all; | ||
| } | ||
| }; | ||
| EventEmitter.prototype.on = function(type, listener) { | ||
| if (typeof type === 'function') { | ||
| this.onAny(type); | ||
| return this; | ||
| } | ||
| if (typeof listener !== 'function') { | ||
| throw new Error('on only accepts instances of Function'); | ||
| } | ||
| this._events || init.call(this); | ||
| // To avoid recursion in the case that type == "newListeners"! Before | ||
| // adding it to the listeners, first emit "newListeners". | ||
| this.emit('newListener', type, listener); | ||
| if(this.wildcard) { | ||
| growListenerTree.call(this, type, listener); | ||
| return this; | ||
| } | ||
| if (!this._events[type]) { | ||
| // Optimize the case of one listener. Don't need the extra array object. | ||
| this._events[type] = listener; | ||
| } | ||
| else if(typeof this._events[type] === 'function') { | ||
| // Adding the second element, need to change to array. | ||
| this._events[type] = [this._events[type], listener]; | ||
| } | ||
| else if (isArray(this._events[type])) { | ||
| // If we've already got an array, just append. | ||
| this._events[type].push(listener); | ||
| // Check for listener leak | ||
| if (!this._events[type].warned) { | ||
| var m = defaultMaxListeners; | ||
| if (typeof this._events.maxListeners !== 'undefined') { | ||
| m = this._events.maxListeners; | ||
| } | ||
| if (m > 0 && this._events[type].length > m) { | ||
| this._events[type].warned = true; | ||
| console.error('(node) warning: possible EventEmitter memory ' + | ||
| 'leak detected. %d listeners added. ' + | ||
| 'Use emitter.setMaxListeners() to increase limit.', | ||
| this._events[type].length); | ||
| console.trace(); | ||
| } | ||
| } | ||
| } | ||
| return this; | ||
| }; | ||
| EventEmitter.prototype.onAny = function(fn) { | ||
| if(!this._all) { | ||
| this._all = []; | ||
| } | ||
| if (typeof fn !== 'function') { | ||
| throw new Error('onAny only accepts instances of Function'); | ||
| } | ||
| // Add the function to the event listener collection. | ||
| this._all.push(fn); | ||
| return this; | ||
| }; | ||
| EventEmitter.prototype.addListener = EventEmitter.prototype.on; | ||
| EventEmitter.prototype.off = function(type, listener) { | ||
| if (typeof listener !== 'function') { | ||
| throw new Error('removeListener only takes instances of Function'); | ||
| } | ||
| var handlers,leafs=[]; | ||
| if(this.wildcard) { | ||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | ||
| leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); | ||
| } | ||
| else { | ||
| // does not use listeners(), so no side effect of creating _events[type] | ||
| if (!this._events[type]) return this; | ||
| handlers = this._events[type]; | ||
| leafs.push({_listeners:handlers}); | ||
| } | ||
| for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { | ||
| var leaf = leafs[iLeaf]; | ||
| handlers = leaf._listeners; | ||
| if (isArray(handlers)) { | ||
| var position = -1; | ||
| for (var i = 0, length = handlers.length; i < length; i++) { | ||
| if (handlers[i] === listener || | ||
| (handlers[i].listener && handlers[i].listener === listener) || | ||
| (handlers[i]._origin && handlers[i]._origin === listener)) { | ||
| position = i; | ||
| break; | ||
| } | ||
| } | ||
| if (position < 0) { | ||
| return this; | ||
| } | ||
| if(this.wildcard) { | ||
| leaf._listeners.splice(position, 1) | ||
| } | ||
| else { | ||
| this._events[type].splice(position, 1); | ||
| } | ||
| if (handlers.length === 0) { | ||
| if(this.wildcard) { | ||
| delete leaf._listeners; | ||
| } | ||
| else { | ||
| delete this._events[type]; | ||
| } | ||
| } | ||
| } | ||
| else if (handlers === listener || | ||
| (handlers.listener && handlers.listener === listener) || | ||
| (handlers._origin && handlers._origin === listener)) { | ||
| if(this.wildcard) { | ||
| delete leaf._listeners; | ||
| } | ||
| else { | ||
| delete this._events[type]; | ||
| } | ||
| } | ||
| } | ||
| return this; | ||
| }; | ||
| EventEmitter.prototype.offAny = function(fn) { | ||
| var i = 0, l = 0, fns; | ||
| if (fn && this._all && this._all.length > 0) { | ||
| fns = this._all; | ||
| for(i = 0, l = fns.length; i < l; i++) { | ||
| if(fn === fns[i]) { | ||
| fns.splice(i, 1); | ||
| return this; | ||
| } | ||
| } | ||
| } else { | ||
| this._all = []; | ||
| } | ||
| return this; | ||
| }; | ||
| EventEmitter.prototype.removeListener = EventEmitter.prototype.off; | ||
| EventEmitter.prototype.removeAllListeners = function(type) { | ||
| if (arguments.length === 0) { | ||
| !this._events || init.call(this); | ||
| return this; | ||
| } | ||
| if(this.wildcard) { | ||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | ||
| var leafs = searchListenerTree.call(this, null, ns, this.listenerTree, 0); | ||
| for (var iLeaf=0; iLeaf<leafs.length; iLeaf++) { | ||
| var leaf = leafs[iLeaf]; | ||
| leaf._listeners = null; | ||
| } | ||
| } | ||
| else { | ||
| if (!this._events[type]) return this; | ||
| this._events[type] = null; | ||
| } | ||
| return this; | ||
| }; | ||
| EventEmitter.prototype.listeners = function(type) { | ||
| if(this.wildcard) { | ||
| var handlers = []; | ||
| var ns = typeof type === 'string' ? type.split(this.delimiter) : type.slice(); | ||
| searchListenerTree.call(this, handlers, ns, this.listenerTree, 0); | ||
| return handlers; | ||
| } | ||
| this._events || init.call(this); | ||
| if (!this._events[type]) this._events[type] = []; | ||
| if (!isArray(this._events[type])) { | ||
| this._events[type] = [this._events[type]]; | ||
| } | ||
| return this._events[type]; | ||
| }; | ||
| EventEmitter.prototype.listenersAny = function() { | ||
| if(this._all) { | ||
| return this._all; | ||
| } | ||
| else { | ||
| return []; | ||
| } | ||
| }; | ||
| if (typeof define === 'function' && define.amd) { | ||
| define(function() { | ||
| return EventEmitter; | ||
| }); | ||
| } else { | ||
| exports.EventEmitter2 = EventEmitter; | ||
| } | ||
| }(typeof process !== 'undefined' && typeof process.title !== 'undefined' && typeof exports !== 'undefined' ? exports : window); | ||
| (function(global, exports) { | ||
| var HAS_BUFFER = typeof Buffer !== 'undefined'; | ||
| var SEPARATOR = ':'; | ||
| var SEPARATOR_CODE = SEPARATOR.charCodeAt(0); | ||
| var EventEmitter = | ||
| global.EventEmitter2 || | ||
| require('eventemitter2').EventEmitter2; | ||
| /** | ||
| * First ocurrence of where string occurs in a buffer. | ||
| * | ||
| * NOTE: this is not UTF8 safe generally we expect to find the correct | ||
| * char fairly quickly unless the buffer is incorrectly formatted. | ||
| * | ||
| * @param {Buffer} buffer haystack. | ||
| * @param {String} string needle. | ||
| * @return {Numeric} -1 if not found index otherwise. | ||
| */ | ||
| function indexInBuffer(buffer, string) { | ||
| if (typeof buffer === 'string') | ||
| return buffer.indexOf(string); | ||
| if (buffer.length === 0) | ||
| return -1; | ||
| var index = 0; | ||
| var length = buffer.length; | ||
| do { | ||
| if (buffer[index] === SEPARATOR_CODE) | ||
| return index; | ||
| } while ( | ||
| ++index && index + 1 < length | ||
| ); | ||
| return -1; | ||
| } | ||
| /** | ||
| * Wrapper for creating either a buffer or ArrayBuffer. | ||
| */ | ||
| function createByteContainer() { | ||
| if (HAS_BUFFER) | ||
| return new Buffer(0); | ||
| return new Uint8Array(); | ||
| } | ||
| /** | ||
| * Join the contents of byte container a and b returning c. | ||
| */ | ||
| function concatByteContainers(a, b) { | ||
| if (HAS_BUFFER) | ||
| return Buffer.concat([a, b]); | ||
| // make sure everything is unit8 | ||
| if (a instanceof ArrayBuffer) | ||
| a = new Uint8Array(a); | ||
| if (b instanceof ArrayBuffer) | ||
| b = new Uint8Array(b); | ||
| // sizes of originals | ||
| var aLen = a.length; | ||
| var bLen = b.length; | ||
| var array = new Uint8Array(aLen + bLen); | ||
| array.set(a); | ||
| array.set(b, aLen); | ||
| // return new byte container | ||
| return array; | ||
| } | ||
| function sliceByteContainers(container, start, end) { | ||
| start = start || 0; | ||
| end = end || byteLength(container); | ||
| if (HAS_BUFFER) | ||
| return container.slice(start, end); | ||
| return container.subarray(start, end); | ||
| } | ||
| /** | ||
| * Like Buffer.byteLength but works on ArrayBuffers too. | ||
| */ | ||
| function byteLength(input) { | ||
| if (typeof input === 'string') { | ||
| if (HAS_BUFFER) { | ||
| return Buffer.byteLength(input); | ||
| } | ||
| var encoder = new TextEncoder(); | ||
| var out = encoder.encode(input); | ||
| return out.length; | ||
| } | ||
| return input.length; | ||
| } | ||
| function bytesToUtf8(container, start, end) { | ||
| if (!start) | ||
| start = 0; | ||
| if (!end) | ||
| end = byteLength(container); | ||
| if (HAS_BUFFER) | ||
| return container.toString('utf8', start, end); | ||
| var decoder = new TextDecoder(); | ||
| var array = container.subarray(start, end); | ||
| return decoder.decode(array); | ||
| } | ||
| /** | ||
| * converts an object to a string representation suitable for storage on disk. | ||
| * Its very important to note that the length in the string refers to the utf8 | ||
| * size of the json content in bytes (as utf8) not the JS string length. | ||
| * | ||
| * @param {Object} object to stringify. | ||
| * @return {String} serialized object. | ||
| */ | ||
| function stringify(object) { | ||
| var json = JSON.stringify(object); | ||
| var len = byteLength(json); | ||
| return len + SEPARATOR + json; | ||
| } | ||
| /** | ||
| * attempts to parse a given buffer or string. | ||
| * | ||
| * @param {Uint8Array|Buffer} input in byteLength:{json..} format. | ||
| * @return {Objec} JS object. | ||
| */ | ||
| function parse(input) { | ||
| var stream = new Stream(); | ||
| var result; | ||
| stream.once('data', function(data) { | ||
| result = data; | ||
| }); | ||
| stream.write(input); | ||
| if (!result) { | ||
| throw new Error( | ||
| 'no command available from parsing:' + input | ||
| ); | ||
| } | ||
| return result; | ||
| } | ||
| function Stream() { | ||
| EventEmitter.call(this); | ||
| this._pendingLength = null; | ||
| // zero length buffer so we can concat later | ||
| // this is always a unit8 array or a buffer. | ||
| this._buffer = createByteContainer(); | ||
| } | ||
| Stream.prototype = { | ||
| __proto__: EventEmitter.prototype, | ||
| _findLength: function() { | ||
| if (this._pendingLength === null) { | ||
| var idx = indexInBuffer(this._buffer, SEPARATOR); | ||
| if (idx === -1) | ||
| return; | ||
| // mark the length to read out of the rolling buffer. | ||
| this._pendingLength = parseInt( | ||
| bytesToUtf8(this._buffer, 0, idx), | ||
| 10 | ||
| ); | ||
| this._buffer = sliceByteContainers(this._buffer, idx + 1); | ||
| } | ||
| }, | ||
| _readBuffer: function() { | ||
| // if the buffer.length is < then we pendingLength need to buffer | ||
| // more data. | ||
| if (!this._pendingLength || this._buffer.length < this._pendingLength) | ||
| return false; | ||
| // extract remainder and parse json | ||
| var message = sliceByteContainers(this._buffer, 0, this._pendingLength); | ||
| this._buffer = sliceByteContainers(this._buffer, this._pendingLength); | ||
| this._pendingLength = null; | ||
| var result; | ||
| try { | ||
| message = bytesToUtf8(message); | ||
| result = JSON.parse(message); | ||
| } catch (e) { | ||
| this.emit('error', e); | ||
| return false; | ||
| } | ||
| this.emit('data', result); | ||
| return true; | ||
| }, | ||
| write: function(buffer) { | ||
| // append new buffer to whatever we have. | ||
| this._buffer = concatByteContainers(this._buffer, buffer); | ||
| do { | ||
| // attempt to find length of next message. | ||
| this._findLength(); | ||
| } while ( | ||
| // keep repeating while there are messages | ||
| this._readBuffer() | ||
| ); | ||
| } | ||
| }; | ||
| exports.parse = parse; | ||
| exports.stringify = stringify; | ||
| exports.Stream = Stream; | ||
| exports.separator = SEPARATOR; | ||
| }).apply( | ||
| null, | ||
| typeof window === 'undefined' ? | ||
| [global, module.exports] : | ||
| [window, window.jsonWireProtocol = {}] | ||
| ); | ||
-24
| CONCAT_FILES=./node_modules/eventemitter2/lib/eventemitter2.js \ | ||
| ./lib/index.js | ||
| TARGET=json-wire-protocol.js | ||
| .PHONY: build | ||
| build: | ||
| cat $(CONCAT_FILES) > $(TARGET) | ||
| .PHONY: test | ||
| test: test-node test-browser | ||
| .PHONY: test-node | ||
| test-node: | ||
| ./node_modules/mocha/bin/mocha --ui tdd test/setup.js test/*-test.js | ||
| .PHONY: test-server | ||
| test-browser: | ||
| ./node_modules/test-agent/bin/js-test-agent test | ||
| .PHONY: test-server | ||
| test-server: | ||
| ./node_modules/test-agent/bin/js-test-agent server --growl | ||
| //all require paths must be absolute -- use __dirname | ||
| var Agent = require('test-agent'), | ||
| Apps = Agent.server, | ||
| Suite = Agent.Suite, | ||
| suite = new Suite({ | ||
| path: __dirname, | ||
| testDir: 'test/', | ||
| libDir: 'lib/', | ||
| testSuffix: '-test.js' | ||
| }); | ||
| server.use(Apps.Suite, suite); |
Sorry, the diff of this file is not supported yet
| <!DOCTYPE HTML> | ||
| <html> | ||
| <head> | ||
| <meta http-equiv="content-type" content="text/html; charset=utf-8" /> | ||
| <title>Serve the tests</title> | ||
| <link rel="stylesheet" href="/node_modules/test-agent/test-agent.css" type="text/css" charset="utf-8"> | ||
| <style type="text/css" media="all"> | ||
| body > iframe { | ||
| width: 100%; | ||
| height: 100%; | ||
| visibility: visible; | ||
| border: 1px solid white; | ||
| } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <!-- Test Agent UI will be loaded in here --> | ||
| <div id="test-agent-ui"> | ||
| </div> | ||
| <!-- Load the bare minimum to get the ui and runner working --> | ||
| <script src="/node_modules/test-agent/test-agent.js" type="text/javascript" charset="utf-8"></script> | ||
| <script src="./index.js" type="text/javascript" charset="utf-8"></script> | ||
| </body> | ||
| </html> | ||
| (function(window) { | ||
| var worker = new TestAgent.BrowserWorker({ | ||
| /* this is where your tests will be loaded into */ | ||
| sandbox: './sandbox.html' | ||
| }); | ||
| worker.use(TestAgent.BrowserWorker.Config, { | ||
| /* config file which lists all available tests for the ui */ | ||
| url: './config.json' | ||
| }); | ||
| worker.use(TestAgent.BrowserWorker.MochaDriver, { | ||
| /* path to mocha */ | ||
| mochaUrl: '/node_modules/mocha/mocha.js', | ||
| testHelperUrl: '../test/setup.js', | ||
| reporter: 'HTML', | ||
| ui: 'tdd' | ||
| }); | ||
| worker.use(TestAgent.BrowserWorker.Websocket); | ||
| worker.use(TestAgent.BrowserWorker.TestUi); | ||
| worker.use(TestAgent.BrowserWorker.ErrorReporting); | ||
| worker.on({ | ||
| 'open': function() { | ||
| setTimeout(worker.ready.bind(worker)); | ||
| }, | ||
| 'close': function() { | ||
| console.log('lost client trying to reconnect'); | ||
| } | ||
| }); | ||
| worker.config(); | ||
| worker.start(); | ||
| }(this)); | ||
| <!DOCTYPE HTML> | ||
| <html> | ||
| <head> | ||
| <meta http-equiv="content-type" content="text/html; charset=utf-8" /> | ||
| <title>Tests</title> | ||
| <link rel="stylesheet" type="text/css" href="/node_modules/node_modules/mocha.css" /> | ||
| <style type="text/css" media="all"> | ||
| iframe { | ||
| border: none; | ||
| padding: 0px; | ||
| } | ||
| </style> | ||
| <script type="text/javascript" charset="utf-8"> | ||
| </script> | ||
| </head> | ||
| <body> | ||
| <!-- | ||
| For Mocha HTML reporter | ||
| --> | ||
| <div id="mocha"> | ||
| </div> | ||
| <!-- | ||
| A central place to add to the dom then tear that down after your test | ||
| --> | ||
| <div id="test"> | ||
| </div> | ||
| </body> | ||
| </html> | ||
| (function() { | ||
| var isNode = typeof window === 'undefined'; | ||
| var ctx = isNode ? global : window; | ||
| function setupChai(chai) { | ||
| chai.Assertion.includeStack = true; | ||
| ctx.assert = chai.assert; | ||
| } | ||
| // node setup | ||
| if (isNode) { | ||
| setupChai(require('chai')); | ||
| global.jsonWireProtocol = require('../lib/index'); | ||
| } else { | ||
| require('/node_modules/eventemitter2/lib/eventemitter2.js'); | ||
| require('/lib/index.js'); | ||
| require('/node_modules/chai/chai.js', function() { | ||
| setupChai(window.chai); | ||
| }); | ||
| } | ||
| }( | ||
| )); |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
No repository
Supply chain riskPackage does not have a linked source code repository. Without this field, a package will have no reference to the location of the source code use to generate the package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
-100%1
-66.67%1
-50%1
-80%8683
-76.22%7
-53.33%211
-79.19%1
Infinity%- Removed
- Removed