Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ddp.js

Package Overview
Dependencies
Maintainers
1
Versions
10
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ddp.js - npm Package Compare versions

Comparing version 0.5.0 to 1.0.0

CHANGELOG.md

6

bower.json
{
"name": "ddp.js",
"main": "src/ddp.js",
"version": "0.5.0",
"main": "dist/ddp.js",
"version": "1.0.0-rc1",
"homepage": "https://github.com/mondora/ddp.js",

@@ -9,3 +9,3 @@ "authors": [

],
"description": "A ddp client for the browser.",
"description": "ddp javascript client",
"moduleType": [

@@ -12,0 +12,0 @@ "globals",

{
"name": "ddp.js",
"version": "0.5.0",
"version": "1.0.0",
"description": "ddp javascript client",
"main": "src/ddp.js",
"scripts": {
"test": "./node_modules/mocha/bin/mocha ./test/ddp.unit.js",
"test-node": "./node_modules/mocha/bin/mocha -R nyan ./test/ddp.unit.js",
"test-browser": "./node_modules/karma/bin/karma start test/karma.conf.js"
"build": "./node_modules/.bin/webpack",
"coverage": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha",
"test": "./node_modules/.bin/_mocha"
},

@@ -17,6 +17,6 @@ "repository": {

"ddp",
"meteor",
"asteroid"
"meteor",
"asteroid"
],
"author": "Paolo Scanferla <paolo.scanferla@mondora.com> (https://github.com/pscanf)",
"author": "Paolo Scanferla <paolo.scanferla@mondora.com>",
"license": "MIT",

@@ -28,19 +28,12 @@ "bugs": {

"devDependencies": {
"coveralls": "^2.10.0",
"gulp": "^3.6.2",
"gulp-concat": "^2.2.0",
"istanbul": "^0.2.10",
"karma": "^0.12.14",
"karma-chrome-launcher": "^0.1.3",
"karma-coverage": "^0.2.1",
"karma-firefox-launcher": "^0.1.3",
"karma-mocha": "^0.1.3",
"karma-safari-launcher": "^0.1.1",
"lodash": "^2.4.1",
"mocha": "^1.18.2",
"mocha-lcov-reporter": "0.0.1",
"should": "^3.2.0-beta1",
"sinon": "^1.9.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"
},
"dependencies": {}
"dependencies": {
"wolfy87-eventemitter": "^4.2.11"
}
}
[![Build Status](https://travis-ci.org/mondora/ddp.js.svg?branch=master)](https://travis-ci.org/mondora/ddp.js)
[![Coverage Status](https://coveralls.io/repos/mondora/ddp.js/badge.png)](https://coveralls.io/r/mondora/ddp.js)
[![Code Climate](https://codeclimate.com/github/mondora/ddp.js.png)](https://codeclimate.com/github/mondora/ddp.js)
[![Coverage Status](https://img.shields.io/coveralls/mondora/ddp.js.svg)](https://coveralls.io/r/mondora/ddp.js?branch=master)
[![Dependency Status](https://david-dm.org/mondora/ddp.js.svg)](https://david-dm.org/mondora/ddp.js)
[![devDependency Status](https://david-dm.org/mondora/ddp.js/dev-status.svg)](https://david-dm.org/mondora/ddp.js#info=devDependencies)
#WARNING
Breaking changes from 0.6.x to 1.0.0, [read the
CHANGELOG](https://github.com/mondora/ddp.js/blob/master/CHANGELOG.md) for more
info.
#ddp.js
A javascript ddp client that runs both in the browser and in node.
A javascript isomorphic ddp client.
##Why
##What is it for?
This is the foundation of a project I'm working on to decouple meteor's client and server sides. It allows the to connect through ddp to a meteor server, and use all of the wonderful facilities meteor provides.
The purpose of this library is:
The project was inspired by [ddp-browser-client](https://github.com/bmcmahen/ddp-browser-client), but I decided to re-implement the library from scratch to get a better understanding of the ddp protocol and to adapt it to run on node as well.
- to set up and maintain a ddp connection with a ddp server, freeing the
developer from having to do it on their own
- to give the developer a clear, consistent API to communicate with the ddp
server
##Install
You can install the package for server-side usage via npm:
Via npm
npm install ddp.js
npm install ddp.js
For client-side usage, you can use bower:
Or via bower
bower install ddp.js
bower install ddp.js
or you can just clone the repository and add `ddp.js` to your project.
##Example usage
```javascript
var DDP = require("ddp.js");
var options = {
endpoint: "http://localhost:3000/websocket",
SocketConstructor: WebSocket
endpoint: "http://localhost:3000/websocket",
SocketConstructor: WebSocket
};

@@ -38,14 +45,26 @@ var ddp = new DDP(options);

ddp.on("connected", function () {
console.log("Connected");
console.log("Connected");
ddp.sub("myCollection");
ddp.on("added", function (data) {
console.log(data.collection);
});
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 myLoginParams = { ... };
ddp.method("login", [myLoginParams], function (err, res) {
if (err) throw err;
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!");
}
});
});

@@ -56,164 +75,109 @@ ```

To run tests clone the repository
`npm test` to run tests, `npm run coverage` to generate the coverage report.
git clone https://github.com/mondora/ddp.js
cd ddp.js
##Public API
install dependencies
###new DDP(options)
npm install
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.
and run tests
####Arguments
npm run test-node
npm run test-browser
- `options` **object** *required*
##API
###new DDP(options)
Returns a new DDP instance.
Available options are:
- `endpoint`: the location of the websocket server. Its
- `endpoint` **string** *required*: the location of the websocket server. Its
format depends on the type of socket you are using.
- `SocketConstructor`: the constructor function that will be
used to construct the socket. Meteor (currently the only
DDP server available) supports websockets and SockJS
sockets. So, practically speaking, this means that on the
browser you can use either the browser's native WebSocket
constructor or the SockJS constructor provided by the
SockJS library. On the server you can use whichever
library implements the websocket protocol (e.g.
faye-websocket).
- `SocketConstructor` **function** *required*: the constructor function that
will be used to construct the socket. Meteor (currently the only DDP server
available) supports websockets and SockJS sockets. So, practically speaking,
this means that on the browser you can use either the browser's native
WebSocket constructor or the SockJS constructor provided by the SockJS
library. On the server you can use whichever library implements the
websocket protocol (e.g. faye-websocket).
- `do_not_autoconnect`: pass true if you do not wish to have
the DDP instance to automatically connect itself to the
server upon instantiation. In that case you'll need to
explicitly call the connect method to do so.
####Returns
- `do_not_autoreconnect`: pass true if you do not wish to
have the DDP instance try reconnecting itself.
A new DDP instance, which is also an `EventEmitter` instance.
---
###DDP.method(name, params)
Calls a remote method.
####Arguments
###DDP.connect()
- `name` **string** *required*: name of the method to call.
Tries to connect to the DDP server. To connect to a DDP
server a "connect" message needs to be sent. This function
does not send the message itself. Instead, it opens a
socket connection to the server and delegates sending the
message to the "onopen" event handler of the socket
instance.
- `params` **array** *required*: parameters to pass to the remote method. Pass
an empty array if you do not wish to pass any parameters.
`connect` also sets the readyState property of the DDP instance
to 0 (connecting).
If the user tries to send DDP messages before the connection
is open (readyState equals 1), those messages get queued up
and sent, in order, once the connection is established.
####Returns
The unique `id` (string) corresponding to the method call.
---
###DDP.sub(name, params)
Subscribes to a server publication.
###DDP.method(name, params, onResult, onUpdated)
####Arguments
Calls a remote method and registers callbacks for the
"result" and "updated" responses.
- `name` **string** *required*: name of the server publication.
- `name`: name of the method to call.
- `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`: parameters to pass to the remote method. Pass an
empty array if you do not wish to pass any parameters.
####Returns
- `onResult`: callback for the "result" message
corresponding to the method invocation.
The unique `id` (string) corresponding to the subscription call.
- `onUpdated`: callback for the "updated" message
corresponding to the method invocation.
---
###DDP.sub(name, params, onReady)
Subscribes the current DDP instance to a server publication.
- `name`: name of the server publication.
- `params`: parameters to pass to the server publish
function. Pass an empty array if you do not wish to pass
any parameters.
- `onReady`: callback for the "ready" message corresponding
to this subscription.
###DDP.unsub(id)
Unsubscribes the current DDP instance to a server
publication to which it was subscribed.
Unsubscribes to a previously-subscribed server publication.
- `id`: id of the subscription.
####Arguments
- `id` **string** *required*: id of the subscription.
####Returns
The `id` corresponding to the subscription call (not of much use, but I return
it for consistency).
##Public events
###DDP.on(name, handler)
###Connection events
Registers a callback for the specified event. Built-in
events are: connected, failed, error, added, removed,
changed, socket_close, socket_error.
- `connected`: emitted with no arguments when the DDP connection is
established.
- `name`: name of the event.
- `disconnected`: emitted with no arguments when the DDP connection drops.
- `handler`: handler for the event.
###Subscription events
All the following events are emitted with one argument, the parsed DDP message.
Further details can be found [on the DDP spec
page](https://github.com/meteor/meteor/blob/devel/packages/ddp/DDP.md).
- `ready`
- `nosub`
- `added`
- `changed`
- `removed`
###Method events
All the following events are emitted with one argument, the parsed DDP message.
Further details can be found [on the DDP spec
page](https://github.com/meteor/meteor/blob/devel/packages/ddp/DDP.md).
###DDP.off(name, handler)
Deregisters a previously registered callback for the
specified event.
- `name`: name of the event.
- `handler`: handler for the event.
##DDP events
###"error"
###"connected"
###"failed"
###"socket_close"
###"socket_error"
###"added"
###"changed"
###"removed"
- `result`
- `updated`

@@ -1,331 +0,79 @@

(function (root, factory) {
if (typeof define === "function" && define.amd) {
define(factory);
} else if (typeof exports === "object") {
module.exports = factory();
} else {
root.DDP = factory();
}
}(this, function () {
"use strict";
"use strict";
var EventEmitter = require("wolfy87-eventemitter");
var uniqueId = (function () {
var i = 0;
return function () {
return (i++).toString();
};
})();
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 INIT_DDP_MESSAGE = "{\"server_id\":\"0\"}";
// After hitting the plateau, it'll try to reconnect
// every 16.5 seconds
var RECONNECT_ATTEMPTS_BEFORE_PLATEAU = 10;
var TIMER_INCREMENT = 300;
var DEFAULT_PING_INTERVAL = 10000;
var DDP_SERVER_MESSAGES = [
"added", "changed", "connected", "error", "failed",
"nosub", "ready", "removed", "result", "updated",
"ping", "pong"
];
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);
};
var DDP = function (options) {
// Configuration
this._endpoint = options.endpoint;
this._SocketConstructor = options.SocketConstructor;
this._autoreconnect = !options.do_not_autoreconnect;
this._ping_interval = options._ping_interval || DEFAULT_PING_INTERVAL;
this._socketInterceptFunction = options.socketInterceptFunction;
// Subscriptions callbacks
this._onReadyCallbacks = {};
this._onStopCallbacks = {};
this._onErrorCallbacks = {};
// Methods callbacks
this._onResultCallbacks = {};
this._onUpdatedCallbacks = {};
this._events = {};
this._queue = [];
// Setup
this.readyState = -1;
this._reconnect_count = 0;
this._reconnect_incremental_timer = 0;
// Init
if (!options.do_not_autoconnect) {
this.connect();
}
};
DDP.prototype.constructor = DDP;
DDP.prototype.connect = function () {
var c = require("./lib/constants.js");
this._socket.send({
msg: "connect",
version: c.DDP_VERSION,
support: [c.DDP_VERSION]
});
};
DDP.prototype.connect = function () {
this.readyState = 0;
this._socket = new this._SocketConstructor(this._endpoint);
this._socket.onopen = this._on_socket_open.bind(this);
this._socket.onmessage = this._on_socket_message.bind(this);
this._socket.onerror = this._on_socket_error.bind(this);
this._socket.onclose = this._on_socket_close.bind(this);
};
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;
};
DDP.prototype.method = function (name, params, onResult, onUpdated) {
var id = uniqueId();
this._onResultCallbacks[id] = onResult;
this._onUpdatedCallbacks[id] = onUpdated;
this._send({
msg: "method",
id: id,
method: name,
params: params
});
return id;
};
DDP.prototype.ping = function () {
var id = require("./lib/utils.js").uniqueId();
this._socket.send({
msg: "ping",
id: id
});
return id;
};
DDP.prototype.sub = function (name, params, onReady, onStop, onError) {
var id = uniqueId();
this._onReadyCallbacks[id] = onReady;
this._onStopCallbacks[id] = onStop;
this._onErrorCallbacks[id] = onError;
this._send({
msg: "sub",
id: id,
name: name,
params: params
});
return id;
};
DDP.prototype.pong = function (id) {
this._socket.send({
msg: "pong",
id: id
});
return id;
};
DDP.prototype.unsub = function (id) {
this._send({
msg: "unsub",
id: id
});
return id;
};
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;
};
DDP.prototype.on = function (name, handler) {
this._events[name] = this._events[name] || [];
this._events[name].push(handler);
};
DDP.prototype.unsub = function (id) {
this._socket.send({
msg: "unsub",
id: id
});
return id;
};
DDP.prototype.off = function (name, handler) {
if (!this._events[name]) {
return;
}
var index = this._events[name].indexOf(handler);
if (index !== -1) {
this._events[name].splice(index, 1);
}
};
DDP.prototype._emit = function (name /* , arguments */) {
if (!this._events[name]) {
return;
}
var args = arguments;
var self = this;
this._events[name].forEach(function (handler) {
handler.apply(self, Array.prototype.slice.call(args, 1));
});
};
DDP.prototype._send = function (object) {
if (this.readyState !== 1 && object.msg !== "connect") {
this._queue.push(object);
return;
}
var message;
if (typeof EJSON === "undefined") {
message = JSON.stringify(object);
} else {
message = EJSON.stringify(object);
}
if (this._socketInterceptFunction) {
this._socketInterceptFunction({
type: "socket_message_sent",
message: message,
timestamp: Date.now()
});
}
this._socket.send(message);
};
DDP.prototype._try_reconnect = function () {
if (this._reconnect_count < RECONNECT_ATTEMPTS_BEFORE_PLATEAU) {
setTimeout(this.connect.bind(this), this._reconnect_incremental_timer);
this._reconnect_count += 1;
this._reconnect_incremental_timer += TIMER_INCREMENT * this._reconnect_count;
} else {
setTimeout(this.connect.bind(this), this._reconnect_incremental_timer);
}
};
DDP.prototype._on_result = function (data) {
if (this._onResultCallbacks[data.id]) {
this._onResultCallbacks[data.id](data.error, data.result);
delete this._onResultCallbacks[data.id];
if (data.error) {
delete this._onUpdatedCallbacks[data.id];
}
} else {
if (data.error) {
delete this._onUpdatedCallbacks[data.id];
throw data.error;
}
}
};
DDP.prototype._on_updated = function (data) {
var self = this;
data.methods.forEach(function (id) {
if (self._onUpdatedCallbacks[id]) {
self._onUpdatedCallbacks[id]();
delete self._onUpdatedCallbacks[id];
}
});
};
DDP.prototype._on_nosub = function (data) {
if (data.error) {
if (!this._onErrorCallbacks[data.id]) {
delete this._onReadyCallbacks[data.id];
delete this._onStopCallbacks[data.id];
throw new Error(data.error);
}
this._onErrorCallbacks[data.id](data.error);
delete this._onReadyCallbacks[data.id];
delete this._onStopCallbacks[data.id];
delete this._onErrorCallbacks[data.id];
return;
}
if (this._onStopCallbacks[data.id]) {
this._onStopCallbacks[data.id]();
}
delete this._onReadyCallbacks[data.id];
delete this._onStopCallbacks[data.id];
delete this._onErrorCallbacks[data.id];
};
DDP.prototype._on_ready = function (data) {
var self = this;
data.subs.forEach(function (id) {
if (self._onReadyCallbacks[id]) {
self._onReadyCallbacks[id]();
delete self._onReadyCallbacks[id];
}
});
};
DDP.prototype._on_error = function (data) {
this._emit("error", data);
};
DDP.prototype._on_connected = function (data) {
var self = this;
var firstCon = self._reconnect_count === 0;
var eventName = firstCon ? "connected" : "reconnected";
self.readyState = 1;
self._reconnect_count = 0;
self._reconnect_incremental_timer = 0;
var length = self._queue.length;
for (var i=0; i<length; i++) {
self._send(self._queue.shift());
}
self._emit(eventName, data);
// Set up keepalive ping-s
self._ping_interval_handle = setInterval(function () {
var id = uniqueId();
self._send({
msg: "ping",
id: id
});
}, self._ping_interval);
};
DDP.prototype._on_failed = function (data) {
this.readyState = 4;
this._emit("failed", data);
};
DDP.prototype._on_added = function (data) {
this._emit("added", data);
};
DDP.prototype._on_removed = function (data) {
this._emit("removed", data);
};
DDP.prototype._on_changed = function (data) {
this._emit("changed", data);
};
DDP.prototype._on_ping = function (data) {
this._send({
msg: "pong",
id: data.id
});
};
DDP.prototype._on_pong = function (data) {
// For now, do nothing.
// In the future we might want to log latency or so.
};
DDP.prototype._on_socket_close = function () {
if (this._socketInterceptFunction) {
this._socketInterceptFunction({
type: "socket_close",
timestamp: Date.now()
});
}
clearInterval(this._ping_interval_handle);
this.readyState = 4;
this._emit("socket_close");
if (this._autoreconnect) {
this._try_reconnect();
}
};
DDP.prototype._on_socket_error = function (e) {
if (this._socketInterceptFunction) {
this._socketInterceptFunction({
type: "socket_error",
error: JSON.stringify(e),
timestamp: Date.now()
});
}
clearInterval(this._ping_interval_handle);
this.readyState = 4;
this._emit("socket_error", e);
};
DDP.prototype._on_socket_open = function () {
if (this._socketInterceptFunction) {
this._socketInterceptFunction({
type: "socket_open",
timestamp: Date.now()
});
}
this._send({
msg: "connect",
version: "pre2",
support: ["pre2"]
});
};
DDP.prototype._on_socket_message = function (message) {
if (this._socketInterceptFunction) {
this._socketInterceptFunction({
type: "socket_message_received",
message: message.data,
timestamp: Date.now()
});
}
var data;
if (message.data === INIT_DDP_MESSAGE) {
return;
}
try {
if (typeof EJSON === "undefined") {
data = JSON.parse(message.data);
} else {
data = EJSON.parse(message.data);
}
if (DDP_SERVER_MESSAGES.indexOf(data.msg) === -1) {
throw new Error();
}
} catch (e) {
console.warn("Non DDP message received:");
console.warn(message.data);
return;
}
this["_on_" + data.msg](data);
};
return DDP;
}));
module.exports = DDP;

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc