Comparing version 1.1.0 to 2.0.0
@@ -1,3 +0,14 @@ | ||
##1.1.0 (July 11, 2015) | ||
## 2.0.0 (February 12, 2016) | ||
Distribute as individual modules in `lib` instead of bundle in `dist`. | ||
Added method to disconnect. | ||
Added options to control auto-connect and auto-reconnect behaviour. As it turns | ||
out they could indeed be useful, for instance when one wants to simulate a | ||
connection scenario (e.g. in stress tests) and needs to have fine-grained | ||
control on the lifecycle of the connection. | ||
## 1.1.0 (July 11, 2015) | ||
Moved the code to use ES6. In the process, I also refactored it a bit to use | ||
@@ -15,5 +26,4 @@ less "exotic" patterns, but there _should be_ no breaking changes to the public | ||
## 1.0.0 (January 11, 2015) | ||
##1.0.0 (January 11, 2015) | ||
The library has been rewritten from scratch and its scope somewhat reduced. The | ||
@@ -20,0 +30,0 @@ purpose of the rewrite, other than simplification, was to implement better |
{ | ||
"name": "ddp.js", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "ddp javascript client", | ||
"main": "dist/ddp.js", | ||
"main": "lib/ddp.js", | ||
"scripts": { | ||
"build": "gulp build", | ||
"build": "babel src --out-dir lib", | ||
"clean": "rimraf lib coverage", | ||
"coverage": "babel-node $(npm bin)/isparta cover $(npm bin)/_mocha -- --recursive test/unit", | ||
"coveralls": "cat ./coverage/lcov.info | coveralls", | ||
"dev": "gulp", | ||
"lint": "eslint src/", | ||
"test": "istanbul cover _mocha --report lcovonly -- -R spec --compilers js:babel/register" | ||
"dev": "npm test -- --watch", | ||
"lint": "eslint src test", | ||
"prepublish": "npm run clean && npm run build", | ||
"test": "mocha --compilers js:babel-core/register --recursive test/unit", | ||
"start-meteor": "cd test/server/ && meteor", | ||
"e2e-test": "mocha --compilers js:babel-core/register --recursive test/e2e", | ||
"e2e-dev": "npm run e2e-test -- --watch" | ||
}, | ||
@@ -29,22 +35,20 @@ "repository": { | ||
"devDependencies": { | ||
"babel": "^5.6.14", | ||
"babel-core": "^5.6.17", | ||
"babel-loader": "^5.3.1", | ||
"chai": "^3.0.0", | ||
"coveralls": "^2.11.2", | ||
"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" | ||
"babel-cli": "^6.5.1", | ||
"babel-core": "^6.5.1", | ||
"babel-eslint": "^4.1.8", | ||
"babel-loader": "^6.2.2", | ||
"babel-preset-es2015": "^6.5.0", | ||
"babel-preset-stage-0": "^6.5.0", | ||
"chai": "^3.5.0", | ||
"coveralls": "^2.11.6", | ||
"eslint": "^1.10.3", | ||
"faye-websocket": "^0.10.0", | ||
"isparta": "^4.0.0", | ||
"mocha": "^2.4.5", | ||
"sinon": "^1.17.3", | ||
"sinon-chai": "^2.8.0" | ||
}, | ||
"dependencies": { | ||
"wolfy87-eventemitter": "^4.2.11" | ||
"wolfy87-eventemitter": "^4.3.0" | ||
} | ||
} |
174
README.md
@@ -0,1 +1,2 @@ | ||
[![npm version](https://badge.fury.io/js/ddp.js.svg)](https://badge.fury.io/js/ddp.js) | ||
[![Build Status](https://travis-ci.org/mondora/ddp.js.svg?branch=master)](https://travis-ci.org/mondora/ddp.js) | ||
@@ -6,13 +7,8 @@ [![Coverage Status](https://img.shields.io/coveralls/mondora/ddp.js.svg)](https://coveralls.io/r/mondora/ddp.js?branch=master) | ||
#WARNING | ||
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 | ||
info. | ||
# ddp.js | ||
#ddp.js | ||
A javascript isomorphic/universal ddp client. | ||
A javascript isomorphic ddp client. | ||
## What is it for? | ||
##What is it for? | ||
The purpose of this library is: | ||
@@ -25,37 +21,31 @@ | ||
##Install | ||
## Install | ||
Via npm | ||
npm install ddp.js | ||
Or via bower | ||
## Example usage | ||
bower install ddp.js | ||
##Example usage | ||
```javascript | ||
var DDP = require("ddp.js"); | ||
var options = { | ||
endpoint: "http://localhost:3000/websocket", | ||
```js | ||
const DDP = require("ddp.js"); | ||
const options = { | ||
endpoint: "ws://localhost:3000/websocket", | ||
SocketConstructor: WebSocket | ||
}; | ||
var ddp = new DDP(options); | ||
const ddp = new DDP(options); | ||
ddp.on("connected", function () { | ||
ddp.on("connected", () => { | ||
console.log("Connected"); | ||
}); | ||
var subId = ddp.sub("mySubscription"); | ||
ddp.on("ready", function (message) { | ||
if (message.id === subId) { | ||
const subId = ddp.sub("mySubscription"); | ||
ddp.on("ready", message => { | ||
if (message.subs.includes(subId)) { | ||
console.log("mySubscription ready"); | ||
} | ||
}); | ||
ddp.on("added", function (message) { | ||
ddp.on("added", message => { | ||
console.log(message.collection); | ||
}); | ||
var myLoginParams = { | ||
const myLoginParams = { | ||
user: { | ||
@@ -66,4 +56,4 @@ email: "user@example.com" | ||
}; | ||
var methodId = ddp.method("login", [myLoginParams]); | ||
ddp.on("result", function (message) { | ||
const methodId = ddp.method("login", [myLoginParams]); | ||
ddp.on("result", message => { | ||
if (message.id === methodId && !message.error) { | ||
@@ -75,15 +65,21 @@ console.log("Logged in!"); | ||
##Tests | ||
## Developing | ||
`npm test` to run tests. Coverage reports are generated in the `coverage/` | ||
directory. | ||
After cloning the repository, install `npm` dependencies with `npm install`. | ||
Run `npm test` to run unit tests, or `npm run dev` to have `mocha` re-run your | ||
tests when source or test files change. | ||
##Public API | ||
To run e2e tests, first [install meteor](https://www.meteor.com/install). Then, | ||
start the meteor server with `npm run start-meteor`. Finally, run | ||
`npm run e2e-test` to run the e2e test suite, or `npm run e2e-dev` to have | ||
`mocha` re-run the suite when source or test files change. | ||
###new DDP(options) | ||
## Public API | ||
### new DDP(options) | ||
Creates a new DDP instance. After being constructed, the instance will | ||
establish a connection with the DDP server and will try to maintain it open. | ||
####Arguments | ||
#### Arguments | ||
@@ -105,4 +101,15 @@ - `options` **object** *required* | ||
####Returns | ||
- `autoConnect` **boolean** *optional* [default: `true`]: whether to establish | ||
the connection to the server upon instantiation. When `false`, one can | ||
manually establish the connection with the `connect` method. | ||
- `autoReconnect` **boolean** *optional* [default: `true`]: whether to try to | ||
reconnect to the server when the socket connection closes, unless the closing | ||
was initiated by a call to the `disconnect` method. | ||
- `reconnectInterval` **number** *optional* [default: 10000]: the interval in ms | ||
between reconnection attempts. | ||
#### Returns | ||
A new DDP instance, which is also an `EventEmitter` instance. | ||
@@ -112,45 +119,74 @@ | ||
###DDP.method(name, params) | ||
### DDP.method(name, params) | ||
Calls a remote method. | ||
####Arguments | ||
#### Arguments | ||
- `name` **string** *required*: name of the method to call. | ||
- `params` **array** *required*: parameters to pass to the remote method. Pass | ||
an empty array if you do not wish to pass any parameters. | ||
- `params` **array** *required*: array of parameters to pass to the remote | ||
method. Pass an empty array if you do not wish to pass any parameters. | ||
####Returns | ||
#### Returns | ||
The unique `id` (string) corresponding to the method call. | ||
#### Example usage | ||
Server code: | ||
```js | ||
Meteor.methods({ | ||
myMethod (param_0, param_1, param_2) { | ||
/* ... */ | ||
} | ||
}); | ||
``` | ||
Client code: | ||
```js | ||
const methodCallId = ddp.method("myMethod", [param_0, param_1, param_2]); | ||
``` | ||
--- | ||
###DDP.sub(name, params) | ||
### DDP.sub(name, params) | ||
Subscribes to a server publication. | ||
####Arguments | ||
#### Arguments | ||
- `name` **string** *required*: name of the server publication. | ||
- `params` **array** *required*: parameters to pass to the server publish | ||
function. Pass an empty array if you do not wish to pass any parameters. | ||
- `params` **array** *required*: array of parameters to pass to the server | ||
publish function. Pass an empty array if you do not wish to pass any | ||
parameters. | ||
####Returns | ||
#### Returns | ||
The unique `id` (string) corresponding to the subscription call. | ||
#### Example usage | ||
Server code: | ||
```js | ||
Meteor.publish("myPublication", (param_0, param_1, param_2) { | ||
/* ... */ | ||
}); | ||
``` | ||
Client code: | ||
```js | ||
const subscriptionId = ddp.sub("myPublication", [param_0, param_1, param_2]); | ||
``` | ||
--- | ||
###DDP.unsub(id) | ||
### DDP.unsub(id) | ||
Unsubscribes to a previously-subscribed server publication. | ||
####Arguments | ||
#### Arguments | ||
- `id` **string** *required*: id of the subscription. | ||
####Returns | ||
#### Returns | ||
@@ -160,6 +196,38 @@ The `id` corresponding to the subscription call (not of much use, but I return | ||
##Public events | ||
--- | ||
###Connection events | ||
### DDP.connect() | ||
Connects to the ddp server. The method is called automatically by the class | ||
constructor if the `autoConnect` option is set to `true` (default behaviour). | ||
So there generally should be no need for the developer to call the method | ||
themselves. | ||
#### Arguments | ||
None | ||
#### Returns | ||
None | ||
--- | ||
### DDP.disconnect() | ||
Disconnects from the ddp server by closing the `WebSocket` connection. You can | ||
listen on the `disconnected` event to be notified of the disconnection. | ||
#### Arguments | ||
None | ||
#### Returns | ||
None | ||
## Public events | ||
### Connection events | ||
- `connected`: emitted with no arguments when the DDP connection is | ||
@@ -170,3 +238,3 @@ established. | ||
###Subscription events | ||
### Subscription events | ||
@@ -183,3 +251,3 @@ All the following events are emitted with one argument, the parsed DDP message. | ||
###Method events | ||
### Method events | ||
@@ -186,0 +254,0 @@ All the following events are emitted with one argument, the parsed DDP message. |
@@ -15,3 +15,3 @@ import EventEmitter from "wolfy87-eventemitter"; | ||
]; | ||
const RECONNECT_INTERVAL = 10000; | ||
const DEFAULT_RECONNECT_INTERVAL = 10000; | ||
@@ -21,6 +21,3 @@ export default class DDP extends EventEmitter { | ||
emit () { | ||
var args = arguments; | ||
setTimeout(() => { | ||
super.emit.apply(this, args); | ||
}, 0); | ||
setTimeout(super.emit.bind(this, ...arguments), 0); | ||
} | ||
@@ -34,3 +31,8 @@ | ||
this.messageQueue = new Queue((message) => { | ||
// Default `autoConnect` and `autoReconnect` to true | ||
this.autoConnect = (options.autoConnect !== false); | ||
this.autoReconnect = (options.autoReconnect !== false); | ||
this.reconnectInterval = options.reconnectInterval || DEFAULT_RECONNECT_INTERVAL; | ||
this.messageQueue = new Queue(message => { | ||
if (this.status === "connected") { | ||
@@ -60,7 +62,12 @@ this.socket.send(message); | ||
this.emit("disconnected"); | ||
// Schedule a reconnection | ||
setTimeout(this.socket.connect.bind(this.socket), RECONNECT_INTERVAL); | ||
if (this.autoReconnect) { | ||
// Schedule a reconnection | ||
setTimeout( | ||
this.socket.open.bind(this.socket), | ||
this.reconnectInterval | ||
); | ||
} | ||
}); | ||
this.socket.on("message:in", (message) => { | ||
this.socket.on("message:in", message => { | ||
if (message.msg === "connected") { | ||
@@ -79,8 +86,24 @@ this.status = "connected"; | ||
this.socket.connect(); | ||
if (this.autoConnect) { | ||
this.connect(); | ||
} | ||
} | ||
connect () { | ||
this.socket.open(); | ||
} | ||
disconnect () { | ||
/* | ||
* If `disconnect` is called, the caller likely doesn't want the | ||
* the instance to try to auto-reconnect. Therefore we set the | ||
* `autoReconnect` flag to false. | ||
*/ | ||
this.autoReconnect = false; | ||
this.socket.close(); | ||
} | ||
method (name, params) { | ||
var id = uniqueId(); | ||
const id = uniqueId(); | ||
this.messageQueue.push({ | ||
@@ -96,3 +119,3 @@ msg: "method", | ||
sub (name, params) { | ||
var id = uniqueId(); | ||
const id = uniqueId(); | ||
this.messageQueue.push({ | ||
@@ -99,0 +122,0 @@ msg: "sub", |
@@ -21,11 +21,9 @@ export default class Queue { | ||
process () { | ||
setTimeout(() => { | ||
if (this.queue.length !== 0) { | ||
var ack = this.consumer(this.queue[0]); | ||
if (ack) { | ||
this.queue.shift(); | ||
this.process(); | ||
} | ||
if (this.queue.length !== 0) { | ||
const ack = this.consumer(this.queue[0]); | ||
if (ack) { | ||
this.queue.shift(); | ||
this.process(); | ||
} | ||
}, 0); | ||
} | ||
} | ||
@@ -32,0 +30,0 @@ |
@@ -6,6 +6,3 @@ import EventEmitter from "wolfy87-eventemitter"; | ||
emit () { | ||
var args = arguments; | ||
setTimeout(() => { | ||
super.emit.apply(this, args); | ||
}, 0); | ||
setTimeout(super.emit.bind(this, ...arguments), 0); | ||
} | ||
@@ -17,6 +14,7 @@ | ||
this.endpoint = endpoint; | ||
this.rawSocket = null; | ||
} | ||
send (object) { | ||
var message = JSON.stringify(object); | ||
const message = JSON.stringify(object); | ||
this.rawSocket.send(message); | ||
@@ -27,16 +25,51 @@ // Emit a copy of the object, as the listener might mutate it. | ||
connect () { | ||
open () { | ||
/* | ||
* Makes `open` a no-op if there's already a `rawSocket`. This avoids | ||
* memory / socket leaks if `open` is called twice (e.g. by a user | ||
* calling `ddp.connect` twice) without properly disposing of the | ||
* socket connection. `rawSocket` gets automatically set to `null` only | ||
* when it goes into a closed or error state. This way `rawSocket` is | ||
* disposed of correctly: the socket connection is closed, and the | ||
* object can be garbage collected. | ||
*/ | ||
if (this.rawSocket) { | ||
return; | ||
} | ||
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 | ||
* Calls to `onopen` and `onclose` directly trigger the `open` and | ||
* `close` events on the `Socket` instance. | ||
*/ | ||
this.rawSocket.onopen = () => this.emit("open"); | ||
this.rawSocket.onerror = (error) => this.emit("error", error); | ||
this.rawSocket.onclose = () => this.emit("close"); | ||
this.rawSocket.onmessage = (message) => { | ||
this.rawSocket.onclose = () => { | ||
this.emit("close"); | ||
this.rawSocket = null; | ||
}; | ||
/* | ||
* Calls to `onerror` trigger the `close` event on the `Socket` | ||
* instance, and cause the `rawSocket` object to be disposed of. | ||
* Since it's not clear what conditions could cause the error and if | ||
* it's possible to recover from it, we prefer to always close the | ||
* connection (if it isn't already) and dispose of the socket object. | ||
*/ | ||
this.rawSocket.onerror = () => { | ||
// It's not clear what the socket lifecycle is when errors occurr. | ||
// Hence, to avoid the `close` event to be emitted twice, before | ||
// manually closing the socket we de-register the `onclose` | ||
// callback. | ||
delete this.rawSocket.onclose; | ||
// Safe to perform even if the socket is already closed | ||
this.rawSocket.close(); | ||
this.emit("close"); | ||
this.rawSocket = null; | ||
}; | ||
/* | ||
* Calls to `onmessage` trigger a `message:in` event on the `Socket` | ||
* instance only once the message (first parameter to `onmessage`) has | ||
* been successfully parsed into a javascript object. | ||
*/ | ||
this.rawSocket.onmessage = message => { | ||
var object; | ||
@@ -50,3 +83,4 @@ try { | ||
// Outside the try-catch block as it must only catch JSON parsing | ||
// errors, not errors that may occur inside a "message:in" event handler | ||
// errors, not errors that may occur inside a "message:in" event | ||
// handler | ||
this.emit("message:in", object); | ||
@@ -57,2 +91,11 @@ }; | ||
close () { | ||
/* | ||
* Avoid throwing an error if `rawSocket === null` | ||
*/ | ||
if (this.rawSocket) { | ||
this.rawSocket.close(); | ||
} | ||
} | ||
} |
var i = 0; | ||
export function uniqueId () { | ||
@@ -4,0 +3,0 @@ return (i++).toString(); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
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
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
58981
14
28
1188
0
251
Updatedwolfy87-eventemitter@^4.3.0