Comparing version 1.0.0 to 1.1.0
{ | ||
"name": "ddp.js", | ||
"main": "dist/ddp.js", | ||
"version": "1.0.0-rc1", | ||
"version": "1.1.0", | ||
"homepage": "https://github.com/mondora/ddp.js", | ||
@@ -11,2 +11,3 @@ "authors": [ | ||
"moduleType": [ | ||
"amd", | ||
"globals", | ||
@@ -24,6 +25,4 @@ "node" | ||
"node_modules", | ||
"bower_components", | ||
"test", | ||
"tests" | ||
"test" | ||
] | ||
} |
@@ -0,1 +1,16 @@ | ||
##1.1.0 (July 11, 2015) | ||
Moved the code to use ES6. In the process, I also refactored it a bit to use | ||
less "exotic" patterns, but there _should be_ no breaking changes to the public | ||
API. | ||
Two enhancements: | ||
1. a `status` property (`connected` / `disconnected`) is now available on the | ||
instance | ||
1. it's now possible to call methods `sub`, `unsub`, and `method` right after | ||
creating the instance. Calls are queued and performed after the `connected` | ||
event | ||
##1.0.0 (January 11, 2015) | ||
@@ -2,0 +17,0 @@ |
527
dist/ddp.js
@@ -1,13 +0,22 @@ | ||
var DDP = | ||
/******/ (function(modules) { // webpackBootstrap | ||
(function webpackUniversalModuleDefinition(root, factory) { | ||
if(typeof exports === 'object' && typeof module === 'object') | ||
module.exports = factory(require("wolfy87-eventemitter")); | ||
else if(typeof define === 'function' && define.amd) | ||
define(["wolfy87-eventemitter"], factory); | ||
else if(typeof exports === 'object') | ||
exports["DDP"] = factory(require("wolfy87-eventemitter")); | ||
else | ||
root["DDP"] = factory(root["wolfy87-eventemitter"]); | ||
})(this, function(__WEBPACK_EXTERNAL_MODULE_1__) { | ||
return /******/ (function(modules) { // webpackBootstrap | ||
/******/ // The module cache | ||
/******/ var installedModules = {}; | ||
/******/ | ||
/******/ // The require function | ||
/******/ function __webpack_require__(moduleId) { | ||
/******/ | ||
/******/ // Check if module is in cache | ||
/******/ if(installedModules[moduleId]) | ||
/******/ return installedModules[moduleId].exports; | ||
/******/ | ||
/******/ // Create a new module (and put it into the cache) | ||
@@ -19,23 +28,23 @@ /******/ var module = installedModules[moduleId] = { | ||
/******/ }; | ||
/******/ | ||
/******/ // Execute the module function | ||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); | ||
/******/ | ||
/******/ // Flag the module as loaded | ||
/******/ module.loaded = true; | ||
/******/ | ||
/******/ // Return the exports of the module | ||
/******/ return module.exports; | ||
/******/ } | ||
/******/ | ||
/******/ | ||
/******/ // expose the modules object (__webpack_modules__) | ||
/******/ __webpack_require__.m = modules; | ||
/******/ | ||
/******/ // expose the module cache | ||
/******/ __webpack_require__.c = installedModules; | ||
/******/ | ||
/******/ // __webpack_public_path__ | ||
/******/ __webpack_require__.p = ""; | ||
/******/ | ||
/******/ // Load entry module and return exports | ||
@@ -51,267 +60,343 @@ /******/ return __webpack_require__(0); | ||
var EventEmitter = __webpack_require__(1); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
var DDP = function (options) { | ||
// Configuration | ||
this._endpoint = options.endpoint; | ||
this._SocketConstructor = options.SocketConstructor; | ||
// Init | ||
this._init(); | ||
}; | ||
DDP.prototype = Object.create(EventEmitter.prototype); | ||
DDP.prototype.constructor = DDP; | ||
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; }; })(); | ||
DDP.prototype._init = function () { | ||
__webpack_require__(2).call(this); | ||
__webpack_require__(3).call(this); | ||
__webpack_require__(4).call(this); | ||
__webpack_require__(5).call(this); | ||
__webpack_require__(6).call(this); | ||
}; | ||
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
DDP.prototype.connect = function () { | ||
var c = __webpack_require__(7); | ||
this._socket.send({ | ||
msg: "connect", | ||
version: c.DDP_VERSION, | ||
support: [c.DDP_VERSION] | ||
}); | ||
}; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
DDP.prototype.method = function (name, params) { | ||
var id = __webpack_require__(8).uniqueId(); | ||
this._socket.send({ | ||
msg: "method", | ||
id: id, | ||
method: name, | ||
params: params | ||
}); | ||
return id; | ||
}; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
DDP.prototype.ping = function () { | ||
var id = __webpack_require__(8).uniqueId(); | ||
this._socket.send({ | ||
msg: "ping", | ||
id: id | ||
}); | ||
return id; | ||
}; | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } | ||
DDP.prototype.pong = function (id) { | ||
this._socket.send({ | ||
msg: "pong", | ||
id: id | ||
}); | ||
return id; | ||
}; | ||
var _wolfy87Eventemitter = __webpack_require__(1); | ||
DDP.prototype.sub = function (name, params) { | ||
var id = __webpack_require__(8).uniqueId(); | ||
this._socket.send({ | ||
msg: "sub", | ||
id: id, | ||
name: name, | ||
params: params | ||
}); | ||
return id; | ||
}; | ||
var _wolfy87Eventemitter2 = _interopRequireDefault(_wolfy87Eventemitter); | ||
DDP.prototype.unsub = function (id) { | ||
this._socket.send({ | ||
msg: "unsub", | ||
id: id | ||
}); | ||
return id; | ||
}; | ||
var _queue = __webpack_require__(2); | ||
module.exports = DDP; | ||
var _queue2 = _interopRequireDefault(_queue); | ||
var _socket = __webpack_require__(3); | ||
var _socket2 = _interopRequireDefault(_socket); | ||
var _utils = __webpack_require__(4); | ||
var DDP_VERSION = "1"; | ||
var PUBLIC_EVENTS = [ | ||
// Subscription messages | ||
"ready", "nosub", "added", "changed", "removed", | ||
// Method messages | ||
"result", "updated", | ||
// Error messages | ||
"error"]; | ||
var RECONNECT_INTERVAL = 10000; | ||
var DDP = (function (_EventEmitter) { | ||
_inherits(DDP, _EventEmitter); | ||
_createClass(DDP, [{ | ||
key: "emit", | ||
value: function emit() { | ||
var _this = this; | ||
var args = arguments; | ||
setTimeout(function () { | ||
_get(Object.getPrototypeOf(DDP.prototype), "emit", _this).apply(_this, args); | ||
}, 0); | ||
} | ||
}]); | ||
function DDP(options) { | ||
var _this2 = this; | ||
_classCallCheck(this, DDP); | ||
_get(Object.getPrototypeOf(DDP.prototype), "constructor", this).call(this); | ||
this.status = "disconnected"; | ||
this.messageQueue = new _queue2["default"](function (message) { | ||
if (_this2.status === "connected") { | ||
_this2.socket.send(message); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}); | ||
this.socket = new _socket2["default"](options.SocketConstructor, options.endpoint); | ||
this.socket.on("open", function () { | ||
// When the socket opens, send the `connect` message | ||
// to establish the DDP connection | ||
_this2.socket.send({ | ||
msg: "connect", | ||
version: DDP_VERSION, | ||
support: [DDP_VERSION] | ||
}); | ||
}); | ||
this.socket.on("close", function () { | ||
_this2.status = "disconnected"; | ||
_this2.messageQueue.empty(); | ||
_this2.emit("disconnected"); | ||
// Schedule a reconnection | ||
setTimeout(_this2.socket.connect.bind(_this2.socket), RECONNECT_INTERVAL); | ||
}); | ||
this.socket.on("message:in", function (message) { | ||
if (message.msg === "connected") { | ||
_this2.status = "connected"; | ||
_this2.messageQueue.process(); | ||
_this2.emit("connected"); | ||
} else if (message.msg === "ping") { | ||
// Reply with a `pong` message to prevent the server from | ||
// closing the connection | ||
_this2.socket.send({ msg: "pong", id: message.id }); | ||
} else if ((0, _utils.contains)(PUBLIC_EVENTS, message.msg)) { | ||
_this2.emit(message.msg, message); | ||
} | ||
}); | ||
this.socket.connect(); | ||
} | ||
_createClass(DDP, [{ | ||
key: "method", | ||
value: function method(name, params) { | ||
var id = (0, _utils.uniqueId)(); | ||
this.messageQueue.push({ | ||
msg: "method", | ||
id: id, | ||
method: name, | ||
params: params | ||
}); | ||
return id; | ||
} | ||
}, { | ||
key: "sub", | ||
value: function sub(name, params) { | ||
var id = (0, _utils.uniqueId)(); | ||
this.messageQueue.push({ | ||
msg: "sub", | ||
id: id, | ||
name: name, | ||
params: params | ||
}); | ||
return id; | ||
} | ||
}, { | ||
key: "unsub", | ||
value: function unsub(id) { | ||
this.messageQueue.push({ | ||
msg: "unsub", | ||
id: id | ||
}); | ||
return id; | ||
} | ||
}]); | ||
return DDP; | ||
})(_wolfy87Eventemitter2["default"]); | ||
exports["default"] = DDP; | ||
module.exports = exports["default"]; | ||
/***/ }, | ||
/* 1 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/***/ function(module, exports) { | ||
module.exports = EventEmitter; | ||
module.exports = __WEBPACK_EXTERNAL_MODULE_1__; | ||
/***/ }, | ||
/* 2 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/***/ function(module, exports) { | ||
/* | ||
* Set up the _socket proxy | ||
*/ | ||
"use strict"; | ||
module.exports = function () { | ||
// _socket is a proxy for the _rawSocket, with the purpose of exposing a | ||
// more consistent event api | ||
var EventEmitter = __webpack_require__(1); | ||
this._socket = new EventEmitter(); | ||
this._socket.send = (function (object) { | ||
var message = JSON.stringify(object); | ||
this._rawSocket.send(message); | ||
// Emit a copy of the object, as we don't know who might be listening | ||
this._socket.emit("message:out", JSON.parse(message)); | ||
}).bind(this); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
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; }; })(); | ||
/***/ }, | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
/* | ||
* Maintain a DDP connection with the server | ||
*/ | ||
var Queue = (function () { | ||
"use strict"; | ||
/* | ||
* As the name implies, `consumer` is the (sole) consumer of the queue. | ||
* It gets called with each element of the queue and its return value | ||
* serves as a ack, determining whether the element is removed or not from | ||
* the queue, allowing then subsequent elements to be processed. | ||
*/ | ||
module.exports = function () { | ||
// Register handlers for the `_socket` events that are responsible for | ||
// establishing and maintaining the DDP connection | ||
this._socket.on("open", (function () { | ||
// When the socket opens, send the `connect` message | ||
// to establish the DDP connection | ||
this.connect(); | ||
}).bind(this)); | ||
this._socket.on("close", (function () { | ||
// When the socket closes, emit the `disconnected` event to the DDP | ||
// connection, and try reconnecting after a timeout | ||
this.emit("disconnected"); | ||
setTimeout( | ||
__webpack_require__(6).bind(this), | ||
__webpack_require__(7).RECONNECT_INTERVAL | ||
); | ||
}).bind(this)); | ||
this._socket.on("message:in", (function (message) { | ||
// When the `connected` message is received, emit the `connected` event | ||
// to the DDP connection | ||
if (message.msg === "connected") { | ||
this.emit("connected"); | ||
function Queue(consumer) { | ||
_classCallCheck(this, Queue); | ||
this.consumer = consumer; | ||
this.queue = []; | ||
} | ||
_createClass(Queue, [{ | ||
key: "push", | ||
value: function push(element) { | ||
this.queue.push(element); | ||
this.process(); | ||
} | ||
}).bind(this)); | ||
}; | ||
}, { | ||
key: "process", | ||
value: function process() { | ||
var _this = this; | ||
setTimeout(function () { | ||
if (_this.queue.length !== 0) { | ||
var ack = _this.consumer(_this.queue[0]); | ||
if (ack) { | ||
_this.queue.shift(); | ||
_this.process(); | ||
} | ||
} | ||
}, 0); | ||
} | ||
}, { | ||
key: "empty", | ||
value: function empty() { | ||
this.queue = []; | ||
} | ||
}]); | ||
return Queue; | ||
})(); | ||
exports["default"] = Queue; | ||
module.exports = exports["default"]; | ||
/***/ }, | ||
/* 4 */ | ||
/* 3 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/* | ||
* Emits subscription and method related events | ||
*/ | ||
"use strict"; | ||
module.exports = function () { | ||
this._socket.on("message:in", (function (message) { | ||
var msgs = [ | ||
// Subscription messages | ||
"ready", | ||
"nosub", | ||
"added", | ||
"changed", | ||
"removed", | ||
// Method messages | ||
"result", | ||
"updated" | ||
]; | ||
if (__webpack_require__(8).contains(msgs, message.msg)) { | ||
this.emit(message.msg, message); | ||
} | ||
}).bind(this)); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
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; }; })(); | ||
/***/ }, | ||
/* 5 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
/* | ||
* Responds to ping messages | ||
*/ | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } | ||
"use strict"; | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
module.exports = function () { | ||
this._socket.on("message:in", (function (message) { | ||
if (message.msg === "ping") { | ||
this.pong(message.id); | ||
} | ||
}).bind(this)); | ||
}; | ||
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } | ||
var _wolfy87Eventemitter = __webpack_require__(1); | ||
/***/ }, | ||
/* 6 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
var _wolfy87Eventemitter2 = _interopRequireDefault(_wolfy87Eventemitter); | ||
/* | ||
* Create the socket instance and register event listeners | ||
*/ | ||
var Socket = (function (_EventEmitter) { | ||
_inherits(Socket, _EventEmitter); | ||
"use strict"; | ||
_createClass(Socket, [{ | ||
key: "emit", | ||
value: function emit() { | ||
var _this = this; | ||
module.exports = function () { | ||
// The `open`, `error` and `close` events are simply proxy-ed to `_socket`. | ||
// The `message` event is instead parsed into a js object (if possible) and | ||
// then passed as a parameter of the `message:in` event | ||
this._rawSocket = new this._SocketConstructor(this._endpoint); | ||
this._rawSocket.onopen = this._socket.emit.bind(this._socket, "open"); | ||
this._rawSocket.onerror = this._socket.emit.bind(this._socket, "error"); | ||
this._rawSocket.onclose = this._socket.emit.bind(this._socket, "close"); | ||
this._rawSocket.onmessage = (function (message) { | ||
var object; | ||
try { | ||
object = JSON.parse(message.data); | ||
} catch (ignore) { | ||
// Simply ignore the malformed message and return | ||
return; | ||
var args = arguments; | ||
setTimeout(function () { | ||
_get(Object.getPrototypeOf(Socket.prototype), "emit", _this).apply(_this, args); | ||
}, 0); | ||
} | ||
// Outside the try-catch block as it must only catch JSON parsing | ||
// errors, not errors that may occur inside a "message:in" event handler | ||
this._socket.emit("message:in", object); | ||
}).bind(this); | ||
}; | ||
}]); | ||
function Socket(SocketConstructor, endpoint) { | ||
_classCallCheck(this, Socket); | ||
/***/ }, | ||
/* 7 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
_get(Object.getPrototypeOf(Socket.prototype), "constructor", this).call(this); | ||
this.SocketConstructor = SocketConstructor; | ||
this.endpoint = endpoint; | ||
} | ||
"use strict"; | ||
_createClass(Socket, [{ | ||
key: "send", | ||
value: function send(object) { | ||
var message = JSON.stringify(object); | ||
this.rawSocket.send(message); | ||
// Emit a copy of the object, as the listener might mutate it. | ||
this.emit("message:out", JSON.parse(message)); | ||
} | ||
}, { | ||
key: "connect", | ||
value: function connect() { | ||
var _this2 = this; | ||
module.exports = { | ||
DDP_VERSION: "1", | ||
DEFAULT_PING_INTERVAL: 10000, | ||
RECONNECT_INTERVAL: 10000 | ||
}; | ||
this.rawSocket = new this.SocketConstructor(this.endpoint); | ||
/* | ||
* The `open`, `error` and `close` events are simply proxy-ed to `_socket`. | ||
* The `message` event is instead parsed into a js object (if possible) and | ||
* then passed as a parameter of the `message:in` event | ||
*/ | ||
this.rawSocket.onopen = function () { | ||
return _this2.emit("open"); | ||
}; | ||
this.rawSocket.onerror = function (error) { | ||
return _this2.emit("error", error); | ||
}; | ||
this.rawSocket.onclose = function () { | ||
return _this2.emit("close"); | ||
}; | ||
this.rawSocket.onmessage = function (message) { | ||
var object; | ||
try { | ||
object = JSON.parse(message.data); | ||
} catch (ignore) { | ||
// Simply ignore the malformed message and return | ||
return; | ||
} | ||
// Outside the try-catch block as it must only catch JSON parsing | ||
// errors, not errors that may occur inside a "message:in" event handler | ||
_this2.emit("message:in", object); | ||
}; | ||
} | ||
}]); | ||
return Socket; | ||
})(_wolfy87Eventemitter2["default"]); | ||
exports["default"] = Socket; | ||
module.exports = exports["default"]; | ||
/***/ }, | ||
/* 8 */ | ||
/***/ function(module, exports, __webpack_require__) { | ||
/* 4 */ | ||
/***/ function(module, exports) { | ||
"use strict"; | ||
var uniqueId = (function () { | ||
var i = 0; | ||
return function () { | ||
return (i++).toString(); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { | ||
value: true | ||
}); | ||
exports.uniqueId = uniqueId; | ||
exports.contains = contains; | ||
var i = 0; | ||
var contains = function (array, element) { | ||
function uniqueId() { | ||
return (i++).toString(); | ||
} | ||
function contains(array, element) { | ||
return array.indexOf(element) !== -1; | ||
}; | ||
} | ||
module.exports = { | ||
uniqueId: uniqueId, | ||
contains: contains | ||
}; | ||
/***/ } | ||
/******/ ]) | ||
/******/ ]) | ||
}); | ||
; |
{ | ||
"name": "ddp.js", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"description": "ddp javascript client", | ||
"main": "src/ddp.js", | ||
"main": "dist/ddp.js", | ||
"scripts": { | ||
"build": "./node_modules/.bin/webpack", | ||
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha", | ||
"test": "./node_modules/.bin/_mocha" | ||
"build": "gulp build", | ||
"coveralls": "cat ./coverage/lcov.info | coveralls", | ||
"dev": "gulp", | ||
"lint": "eslint src/", | ||
"test": "istanbul cover _mocha --report lcovonly -- -R spec --compilers js:babel/register" | ||
}, | ||
@@ -27,8 +29,18 @@ "repository": { | ||
"devDependencies": { | ||
"babel": "^5.6.14", | ||
"babel-core": "^5.6.17", | ||
"babel-loader": "^5.3.1", | ||
"chai": "^3.0.0", | ||
"coveralls": "^2.11.2", | ||
"istanbul": "^0.3.5", | ||
"mocha": "^2.1.0", | ||
"should": "^4.4.2", | ||
"sinon": "^1.12.2", | ||
"webpack": "^1.4.15" | ||
"eslint": "^0.24.1", | ||
"gulp": "^3.9.0", | ||
"gulp-eslint": "^0.15.0", | ||
"gulp-spawn-mocha": "^2.2.1", | ||
"gulp-util": "^3.0.6", | ||
"istanbul": "^0.3.17", | ||
"mocha": "^2.2.5", | ||
"node-libs-browser": "^0.5.2", | ||
"sinon": "^1.15.4", | ||
"sinon-chai": "^2.8.0", | ||
"webpack": "^1.10.1" | ||
}, | ||
@@ -35,0 +47,0 @@ "dependencies": { |
@@ -7,3 +7,3 @@ [![Build Status](https://travis-ci.org/mondora/ddp.js.svg?branch=master)](https://travis-ci.org/mondora/ddp.js) | ||
#WARNING | ||
Breaking changes from 0.6.x to 1.0.0, [read the | ||
Breaking changes from ~0.6.0 to >=1.0.0, [read the | ||
CHANGELOG](https://github.com/mondora/ddp.js/blob/master/CHANGELOG.md) for more | ||
@@ -47,25 +47,25 @@ info. | ||
console.log("Connected"); | ||
}); | ||
var subId = ddp.sub("myCollection"); | ||
ddp.on("ready", function (message) { | ||
if (message.id === subId) { | ||
console.log("Subscruption to myCollection ready"); | ||
} | ||
}); | ||
ddp.on("added", function (message) { | ||
console.log(message.collection); | ||
}); | ||
var subId = ddp.sub("mySubscription"); | ||
ddp.on("ready", function (message) { | ||
if (message.id === subId) { | ||
console.log("mySubscription ready"); | ||
} | ||
}); | ||
ddp.on("added", function (message) { | ||
console.log(message.collection); | ||
}); | ||
var myLoginParams = { | ||
user: { | ||
email: "user@example.com" | ||
}, | ||
password: "hunter2" | ||
}; | ||
var methodId = ddp.method("login", [myLoginParams]); | ||
ddp.on("result", function (message) { | ||
if (message.id === methodId && !message.error) { | ||
console.log("Logged in!"); | ||
} | ||
}); | ||
var myLoginParams = { | ||
user: { | ||
email: "user@example.com" | ||
}, | ||
password: "hunter2" | ||
}; | ||
var methodId = ddp.method("login", [myLoginParams]); | ||
ddp.on("result", function (message) { | ||
if (message.id === methodId && !message.error) { | ||
console.log("Logged in!"); | ||
} | ||
}); | ||
@@ -76,3 +76,4 @@ ``` | ||
`npm test` to run tests, `npm run coverage` to generate the coverage report. | ||
`npm test` to run tests. Coverage reports are generated in the `coverage/` | ||
directory. | ||
@@ -79,0 +80,0 @@ ##Public API |
168
src/ddp.js
@@ -1,79 +0,109 @@ | ||
"use strict"; | ||
import EventEmitter from "wolfy87-eventemitter"; | ||
import Queue from "./queue"; | ||
import Socket from "./socket"; | ||
import {contains, uniqueId} from "./utils"; | ||
var EventEmitter = require("wolfy87-eventemitter"); | ||
const DDP_VERSION = "1"; | ||
const PUBLIC_EVENTS = [ | ||
// Subscription messages | ||
"ready", "nosub", "added", "changed", "removed", | ||
// Method messages | ||
"result", "updated", | ||
// Error messages | ||
"error" | ||
]; | ||
const RECONNECT_INTERVAL = 10000; | ||
var DDP = function (options) { | ||
// Configuration | ||
this._endpoint = options.endpoint; | ||
this._SocketConstructor = options.SocketConstructor; | ||
// Init | ||
this._init(); | ||
}; | ||
DDP.prototype = Object.create(EventEmitter.prototype); | ||
DDP.prototype.constructor = DDP; | ||
export default class DDP extends EventEmitter { | ||
DDP.prototype._init = function () { | ||
require("./socket-proxy.js").call(this); | ||
require("./ddp-connection.js").call(this); | ||
require("./public-events.js").call(this); | ||
require("./ping-pong.js").call(this); | ||
require("./socket-connection.js").call(this); | ||
}; | ||
emit () { | ||
var args = arguments; | ||
setTimeout(() => { | ||
super.emit.apply(this, args); | ||
}, 0); | ||
} | ||
DDP.prototype.connect = function () { | ||
var c = require("./lib/constants.js"); | ||
this._socket.send({ | ||
msg: "connect", | ||
version: c.DDP_VERSION, | ||
support: [c.DDP_VERSION] | ||
}); | ||
}; | ||
constructor (options) { | ||
DDP.prototype.method = function (name, params) { | ||
var id = require("./lib/utils.js").uniqueId(); | ||
this._socket.send({ | ||
msg: "method", | ||
id: id, | ||
method: name, | ||
params: params | ||
}); | ||
return id; | ||
}; | ||
super(); | ||
DDP.prototype.ping = function () { | ||
var id = require("./lib/utils.js").uniqueId(); | ||
this._socket.send({ | ||
msg: "ping", | ||
id: id | ||
}); | ||
return id; | ||
}; | ||
this.status = "disconnected"; | ||
DDP.prototype.pong = function (id) { | ||
this._socket.send({ | ||
msg: "pong", | ||
id: id | ||
}); | ||
return id; | ||
}; | ||
this.messageQueue = new Queue((message) => { | ||
if (this.status === "connected") { | ||
this.socket.send(message); | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}); | ||
DDP.prototype.sub = function (name, params) { | ||
var id = require("./lib/utils.js").uniqueId(); | ||
this._socket.send({ | ||
msg: "sub", | ||
id: id, | ||
name: name, | ||
params: params | ||
}); | ||
return id; | ||
}; | ||
this.socket = new Socket(options.SocketConstructor, options.endpoint); | ||
DDP.prototype.unsub = function (id) { | ||
this._socket.send({ | ||
msg: "unsub", | ||
id: id | ||
}); | ||
return id; | ||
}; | ||
this.socket.on("open", () => { | ||
// When the socket opens, send the `connect` message | ||
// to establish the DDP connection | ||
this.socket.send({ | ||
msg: "connect", | ||
version: DDP_VERSION, | ||
support: [DDP_VERSION] | ||
}); | ||
}); | ||
module.exports = DDP; | ||
this.socket.on("close", () => { | ||
this.status = "disconnected"; | ||
this.messageQueue.empty(); | ||
this.emit("disconnected"); | ||
// Schedule a reconnection | ||
setTimeout(this.socket.connect.bind(this.socket), RECONNECT_INTERVAL); | ||
}); | ||
this.socket.on("message:in", (message) => { | ||
if (message.msg === "connected") { | ||
this.status = "connected"; | ||
this.messageQueue.process(); | ||
this.emit("connected"); | ||
} else if (message.msg === "ping") { | ||
// Reply with a `pong` message to prevent the server from | ||
// closing the connection | ||
this.socket.send({msg: "pong", id: message.id}); | ||
} else if (contains(PUBLIC_EVENTS, message.msg)) { | ||
this.emit(message.msg, message); | ||
} | ||
}); | ||
this.socket.connect(); | ||
} | ||
method (name, params) { | ||
var id = uniqueId(); | ||
this.messageQueue.push({ | ||
msg: "method", | ||
id: id, | ||
method: name, | ||
params: params | ||
}); | ||
return id; | ||
} | ||
sub (name, params) { | ||
var id = uniqueId(); | ||
this.messageQueue.push({ | ||
msg: "sub", | ||
id: id, | ||
name: name, | ||
params: params | ||
}); | ||
return id; | ||
} | ||
unsub (id) { | ||
this.messageQueue.push({ | ||
msg: "unsub", | ||
id: id | ||
}); | ||
return id; | ||
} | ||
} |
398
test/ddp.js
@@ -1,186 +0,262 @@ | ||
"use strict"; | ||
import chai, {expect} from "chai"; | ||
import sinon from "sinon"; | ||
import sinonChai from "sinon-chai"; | ||
import takeTen from "./take-ten"; | ||
require("should"); | ||
var sinon = require("sinon"); | ||
var EventEmitter = require("wolfy87-eventemitter"); | ||
chai.use(sinonChai); | ||
var DDP = require("../src/ddp.js"); | ||
import DDP from "../src/ddp"; | ||
import Socket from "../src/socket"; | ||
describe("The DDP module", function () { | ||
it("should export a constructor", function () { | ||
DDP.should.be.of.type("function"); | ||
DDP.prototype.constructor.should.equal(DDP); | ||
class SocketConstructorMock { | ||
send () {} | ||
} | ||
const options = { | ||
SocketConstructor: SocketConstructorMock | ||
}; | ||
describe("`DDP` class", function () { | ||
describe("`constructor` method", function () { | ||
beforeEach(function () { | ||
sinon.stub(Socket.prototype, "on"); | ||
sinon.stub(Socket.prototype, "connect"); | ||
}); | ||
afterEach(function () { | ||
Socket.prototype.on.restore(); | ||
Socket.prototype.connect.restore(); | ||
}); | ||
it("instantiates a `Socket`", function () { | ||
var ddp = new DDP(options); | ||
expect(ddp.socket).to.be.an.instanceOf(Socket); | ||
}); | ||
it("registers handlers for `socket` events", function () { | ||
var ddp = new DDP(options); | ||
expect(ddp.socket.on).to.have.always.been.calledWithMatch( | ||
sinon.match.string, | ||
sinon.match.func | ||
); | ||
}); | ||
it("calls `socket.connect`", function () { | ||
var ddp = new DDP(options); | ||
expect(ddp.socket.connect).to.have.callCount(1); | ||
}); | ||
}); | ||
}); | ||
describe("The DDP constructor", function () { | ||
beforeEach(function () { | ||
sinon.stub(DDP.prototype, "_init"); | ||
describe("`method` method", function () { | ||
it("sends a DDP `method` message", function () { | ||
var ddp = new DDP(options); | ||
ddp.messageQueue.push = sinon.spy(); | ||
var id = ddp.method("name", ["param"]); | ||
expect(ddp.messageQueue.push).to.have.been.calledWith({ | ||
msg: "method", | ||
id: id, | ||
method: "name", | ||
params: ["param"] | ||
}); | ||
}); | ||
it("returns the method's `id`", function () { | ||
var ddp = new DDP(options); | ||
ddp.messageQueue.push = sinon.spy(); | ||
var id = ddp.method("name", ["param"]); | ||
expect(id).to.be.a("string"); | ||
}); | ||
}); | ||
afterEach(function () { | ||
DDP.prototype._init.restore(); | ||
}); | ||
it("should inherit from EventEmitter", function () { | ||
var ddp = new DDP({}); | ||
ddp.should.be.instanceOf(EventEmitter); | ||
}); | ||
it("should save the endpoint and SocketConstructor passed to it as properties of the instance", function () { | ||
var options = { | ||
endpoint: "endpoint", | ||
SocketConstructor: function () { | ||
// Noop | ||
} | ||
}; | ||
var ddp = new DDP(options); | ||
ddp._endpoint.should.equal(options.endpoint); | ||
ddp._SocketConstructor.should.equal(options.SocketConstructor); | ||
}); | ||
it("should call the _init method", function () { | ||
var ddp = new DDP({}); | ||
ddp._init.called.should.equal(true); | ||
}); | ||
}); | ||
describe("The connect method", function () { | ||
it("should _socket.send a connect message", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
var c = require("../src/lib/constants.js"); | ||
DDP.prototype.connect.call(ctx); | ||
ctx._socket.send.firstCall.args[0].should.eql({ | ||
msg: "connect", | ||
version: c.DDP_VERSION, | ||
support: [c.DDP_VERSION] | ||
describe("`sub` method", function () { | ||
it("sends a DDP `sub` message", function () { | ||
var ddp = new DDP(options); | ||
ddp.messageQueue.push = sinon.spy(); | ||
var id = ddp.sub("name", ["param"]); | ||
expect(ddp.messageQueue.push).to.have.been.calledWith({ | ||
msg: "sub", | ||
id: id, | ||
name: "name", | ||
params: ["param"] | ||
}); | ||
}); | ||
it("returns the sub's `id`", function () { | ||
var ddp = new DDP(options); | ||
ddp.messageQueue.push = sinon.spy(); | ||
var id = ddp.sub("name", ["param"]); | ||
expect(id).to.be.a("string"); | ||
}); | ||
}); | ||
}); | ||
describe("The method method", function () { | ||
it("should _socket.send a method message", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
DDP.prototype.method.call(ctx, "methodName", ["list", "of", "params"]); | ||
var arg = ctx._socket.send.firstCall.args[0]; | ||
arg.should.eql({ | ||
msg: "method", | ||
id: arg.id, | ||
method: "methodName", | ||
params: ["list", "of", "params"] | ||
describe("`unsub` method", function () { | ||
it("sends a DDP `unsub` message", function () { | ||
var ddp = new DDP(options); | ||
ddp.messageQueue.push = sinon.spy(); | ||
var id = ddp.unsub("id"); | ||
expect(ddp.messageQueue.push).to.have.been.calledWith({ | ||
msg: "unsub", | ||
id: id | ||
}); | ||
}); | ||
it("returns the sub's `id`", function () { | ||
var ddp = new DDP(options); | ||
ddp.messageQueue.push = sinon.spy(); | ||
var id = ddp.unsub("id"); | ||
expect(id).to.be.a("string"); | ||
expect(id).to.equal("id"); | ||
}); | ||
}); | ||
it("should return the id of the method call", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
var ret = DDP.prototype.method.call(ctx, "methodName", []); | ||
ret.should.equal(ctx._socket.send.firstCall.args[0].id); | ||
}); | ||
}); | ||
describe("The ping method", function () { | ||
it("should _socket.send a ping message", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
DDP.prototype.ping.call(ctx); | ||
var arg = ctx._socket.send.firstCall.args[0]; | ||
arg.should.eql({ | ||
msg: "ping", | ||
id: arg.id | ||
describe("`socket` `open` handler", function () { | ||
it("sends the `connect` DDP message", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.socket.send = sinon.spy(); | ||
ddp.socket.emit("open"); | ||
takeTen(() => { | ||
expect(ddp.socket.send).to.have.been.calledWith({ | ||
msg: "connect", | ||
version: "1", | ||
support: ["1"] | ||
}); | ||
}, done); | ||
}); | ||
}); | ||
it("should return the id of the ping call", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
var ret = DDP.prototype.ping.call(ctx); | ||
ret.should.equal(ctx._socket.send.firstCall.args[0].id); | ||
}); | ||
}); | ||
describe("The pong method", function () { | ||
it("should _socket.send a pong message", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
DDP.prototype.pong.call(ctx, "0"); | ||
ctx._socket.send.firstCall.args[0].should.eql({ | ||
msg: "pong", | ||
id: "0" | ||
describe("`socket` `close` handler", function () { | ||
before(function () { | ||
sinon.spy(global, "setTimeout"); | ||
}); | ||
after(function () { | ||
global.setTimeout.restore(); | ||
}); | ||
it("emits the `disconnected` event", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.emit = sinon.spy(); | ||
ddp.socket.emit("close"); | ||
takeTen(() => { | ||
expect(ddp.emit).to.have.been.calledWith("disconnected"); | ||
}, done); | ||
}); | ||
it("sets the status to `disconnected`", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.status = "connected"; | ||
ddp.emit = sinon.spy(); | ||
ddp.socket.emit("close"); | ||
takeTen(() => { | ||
expect(ddp.status).to.equal("disconnected"); | ||
}, done); | ||
}); | ||
it("schedules a reconnection", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.socket.emit("close"); | ||
var RECONNECT_INTERVAL = 10000; | ||
takeTen(() => { | ||
expect(global.setTimeout).to.have.been.calledWithMatch( | ||
sinon.match.func, | ||
RECONNECT_INTERVAL | ||
); | ||
}, done); | ||
}); | ||
}); | ||
it("should return the id of the pong call", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
var ret = DDP.prototype.pong.call(ctx, "0"); | ||
ret.should.equal("0"); | ||
}); | ||
}); | ||
describe("The sub method", function () { | ||
it("should _socket.send a sub message", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
DDP.prototype.sub.call(ctx, "subName", ["list", "of", "params"]); | ||
var arg = ctx._socket.send.firstCall.args[0]; | ||
arg.should.eql({ | ||
msg: "sub", | ||
id: arg.id, | ||
name: "subName", | ||
params: ["list", "of", "params"] | ||
describe("`socket` `message:in` handler", function () { | ||
it("responds to `ping` DDP messages", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.socket.send = sinon.spy(); | ||
ddp.socket.emit("message:in", { | ||
id: "id", | ||
msg: "ping" | ||
}); | ||
takeTen(() => { | ||
expect(ddp.socket.send).to.have.been.calledWith({ | ||
id: "id", | ||
msg: "pong" | ||
}); | ||
}, done); | ||
}); | ||
it("triggers `messageQueue` processing upon connection", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.emit = sinon.spy(); | ||
ddp.messageQueue.process = sinon.spy(); | ||
ddp.socket.emit("message:in", {msg: "connected"}); | ||
takeTen(() => { | ||
expect(ddp.messageQueue.process).to.have.callCount(1); | ||
}, done); | ||
}); | ||
it("sets the status to `connected` upon connection", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.emit = sinon.spy(); | ||
ddp.socket.emit("message:in", {msg: "connected"}); | ||
takeTen(() => { | ||
expect(ddp.status).to.equal("connected"); | ||
}, done); | ||
}); | ||
it("emits public DDP messages as events", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.emit = sinon.spy(); | ||
var message = { | ||
id: "id", | ||
msg: "result" | ||
}; | ||
ddp.socket.emit("message:in", message); | ||
takeTen(() => { | ||
expect(ddp.emit).to.have.been.calledWith("result", message); | ||
}, done); | ||
}); | ||
it("ignores unknown (or non public) DDP messages", function (done) { | ||
var ddp = new DDP(options); | ||
ddp.emit = sinon.spy(); | ||
var message = { | ||
id: "id", | ||
msg: "not-a-ddp-message" | ||
}; | ||
ddp.socket.emit("message:in", message); | ||
takeTen(() => { | ||
expect(ddp.emit).to.have.callCount(0); | ||
}, done); | ||
}); | ||
}); | ||
it("should return the id of the sub call", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
var ret = DDP.prototype.sub.call(ctx, "subName", []); | ||
ret.should.equal(ctx._socket.send.firstCall.args[0].id); | ||
}); | ||
}); | ||
describe("The unsub method", function () { | ||
it("should _socket.send a unsub message", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
DDP.prototype.unsub.call(ctx, "0"); | ||
ctx._socket.send.firstCall.args[0].should.eql({ | ||
msg: "unsub", | ||
id: "0" | ||
describe("`messageQueue` consumer", function () { | ||
it("acks if `status` is `connected`", function () { | ||
var ddp = new DDP(options); | ||
ddp.status = "connected"; | ||
var ack = ddp.messageQueue.consumer({}); | ||
expect(ack).to.equal(true); | ||
}); | ||
it("doesn't ack if `status` is `disconnected`", function () { | ||
var ddp = new DDP(options); | ||
ddp.status = "disconnected"; | ||
var ack = ddp.messageQueue.consumer({}); | ||
expect(ack).to.equal(false); | ||
}); | ||
}); | ||
it("should return the id of the unsub call", function () { | ||
var ctx = { | ||
_socket: { | ||
send: sinon.spy() | ||
} | ||
}; | ||
var ret = DDP.prototype.unsub.call(ctx, "0"); | ||
ret.should.equal("0"); | ||
}); | ||
}); |
@@ -1,26 +0,33 @@ | ||
"use strict"; | ||
import {expect} from "chai"; | ||
require("should"); | ||
import {contains, uniqueId} from "../src/utils"; | ||
var u = require("../src/lib/utils.js"); | ||
describe("`utils` object", function () { | ||
describe("The utils.contains function", function () { | ||
it("should return true if the first parameter, an array, contains the second parameter", function () { | ||
var array = ["element"]; | ||
var element = "element"; | ||
u.contains(array, element).should.equal(true); | ||
describe("`contains` function", function () { | ||
it("returns true if the first parameter contains the second parameter", function () { | ||
var array = ["element"]; | ||
var element = "element"; | ||
expect(contains(array, element)).to.equal(true); | ||
}); | ||
it("returns false if the first parameter doesn't contain the second parameter", function () { | ||
var array = ["element"]; | ||
var element = "different-element"; | ||
expect(contains(array, element)).to.equal(false); | ||
}); | ||
}); | ||
it("should return false if the first parameter, an array, doesn't contain the second parameter", function () { | ||
var array = ["element"]; | ||
var element = "different-element"; | ||
u.contains(array, element).should.equal(false); | ||
describe("`uniqueId` function", function () { | ||
it("should return a different string each time it's called", function () { | ||
var ret1 = uniqueId(); | ||
var ret2 = uniqueId(); | ||
expect(ret1).not.to.equal(ret2); | ||
}); | ||
}); | ||
}); | ||
describe("The utils.uniqueId function", function () { | ||
it("should return a different string each time it's called", function () { | ||
var ret1 = u.uniqueId(); | ||
var ret2 = u.uniqueId(); | ||
ret1.should.not.equal(ret2); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
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
55044
183
16
20
1013
1