apollo-client-ws
Advanced tools
Comparing version 2.2.1 to 2.3.0
@@ -47,7 +47,9 @@ /* | ||
[ "babelify", { | ||
presets: [ "es2015", "es2016", "es2017" ], | ||
plugins: [ [ "transform-runtime", { | ||
"polyfill": true, | ||
"regenerator": false | ||
} ] ] | ||
presets: [ | ||
[ "@babel/preset-env", { | ||
"targets": { | ||
"browsers": "last 2 versions, not dead" | ||
} | ||
} ] | ||
] | ||
} ], | ||
@@ -74,3 +76,11 @@ "browserify-shim", | ||
[ "envify", { PLATFORM: "node" } ], | ||
[ "babelify", { presets: [ "es2015", "es2016", "es2017" ] } ] | ||
[ "babelify", { | ||
presets: [ | ||
[ "@babel/preset-env", { | ||
"targets": { | ||
"node": "6.0" | ||
} | ||
} ] | ||
] | ||
} ] | ||
], | ||
@@ -77,0 +87,0 @@ plugin: [ |
@@ -28,532 +28,471 @@ /* | ||
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; | ||
var _apolloLink = _dereq_("apollo-link"); | ||
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; }; }(); | ||
var _ws = _interopRequireDefault(_dereq_("ws")); | ||
var _apolloLink = _dereq_("apollo-link"); | ||
var _websocketFramed = _interopRequireDefault(_dereq_("websocket-framed")); | ||
var _ws = _dereq_("ws"); | ||
var _printer = _dereq_("graphql/language/printer"); | ||
var _ws2 = _interopRequireDefault(_ws); | ||
var _graphqlQueryCompress = _interopRequireDefault(_dereq_("graphql-query-compress")); | ||
var _websocketFramed = _dereq_("websocket-framed"); | ||
var _latching = _interopRequireDefault(_dereq_("latching")); | ||
var _websocketFramed2 = _interopRequireDefault(_websocketFramed); | ||
var _eventemitter = _interopRequireDefault(_dereq_("eventemitter3")); | ||
var _printer = _dereq_("graphql/language/printer"); | ||
var _ducky = _interopRequireDefault(_dereq_("ducky")); | ||
var _graphqlQueryCompress = _dereq_("graphql-query-compress"); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var _graphqlQueryCompress2 = _interopRequireDefault(_graphqlQueryCompress); | ||
/* | ||
** Apollo-Client-WS -- GraphQL WebSocket Network Link for Apollo Client | ||
** Copyright (c) 2017-2018 Ralf S. Engelschall <rse@engelschall.com> | ||
** | ||
** Permission is hereby granted, free of charge, to any person obtaining | ||
** a copy of this software and associated documentation files (the | ||
** "Software"), to deal in the Software without restriction, including | ||
** without limitation the rights to use, copy, modify, merge, publish, | ||
** distribute, sublicense, and/or sell copies of the Software, and to | ||
** permit persons to whom the Software is furnished to do so, subject to | ||
** the following conditions: | ||
** | ||
** The above copyright notice and this permission notice shall be included | ||
** in all copies or substantial portions of the Software. | ||
** | ||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
var _latching5 = _dereq_("latching"); | ||
/* external dependencies */ | ||
var _latching6 = _interopRequireDefault(_latching5); | ||
/* Apollo Client Link Interface for WebSocket Communication */ | ||
class ApolloClientWS extends _apolloLink.ApolloLink { | ||
constructor(...args) { | ||
/* initialize ApolloLink base class */ | ||
super(); | ||
/* sanity check constructor arguments */ | ||
var _eventemitter = _dereq_("eventemitter3"); | ||
if (args.length === 2 && typeof args[0] === "string" && typeof args[1] === "object") this._args = { | ||
uri: args[0], | ||
opts: args[1] | ||
};else if (args.length === 1 && typeof args[0] === "object") this._args = args[0];else throw new Error("invalid arguments to ApolloClientWS constructor (invalid number or type of arguments)"); | ||
if (this._args.uri === undefined) throw new Error("invalid arguments to ApolloClientWS constructor (missing URI)"); | ||
/* provide default values for options */ | ||
var _eventemitter2 = _interopRequireDefault(_eventemitter); | ||
this._args.opts = Object.assign({ | ||
debug: 0, | ||
protocols: [], | ||
compress: false, | ||
encoding: "json", | ||
keepalive: 0, | ||
reconnectattempts: -1, | ||
reconnectdelay: 2 * 1000 | ||
}, this._args.opts); | ||
/* validate options */ | ||
var _ducky = _dereq_("ducky"); | ||
let errors = []; | ||
if (!_ducky.default.validate(this._args.opts, `{ | ||
debug: number, | ||
protocols: [ string* ], | ||
compress: boolean, | ||
encoding: string, | ||
keepalive: number, | ||
reconnectattempts: number, | ||
reconnectdelay: number | ||
}`, errors)) throw new Error(`invalid options: ${errors.join("; ")}`); | ||
/* initialize state variables */ | ||
var _ducky2 = _interopRequireDefault(_ducky); | ||
this._ws = null; | ||
this._wsf = null; | ||
this._to = null; | ||
this._tx = {}; | ||
/* provide hook latching sub-system */ | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
this._latching = new _latching.default(); | ||
/* provide event emitter sub-system */ | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } | ||
this._emitter = new _eventemitter.default(); | ||
} | ||
/* ADDON: pass-through methods of latching sub-system */ | ||
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } | ||
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) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* | ||
** Apollo-Client-WS -- GraphQL WebSocket Network Link for Apollo Client | ||
** Copyright (c) 2017-2018 Ralf S. Engelschall <rse@engelschall.com> | ||
** | ||
** Permission is hereby granted, free of charge, to any person obtaining | ||
** a copy of this software and associated documentation files (the | ||
** "Software"), to deal in the Software without restriction, including | ||
** without limitation the rights to use, copy, modify, merge, publish, | ||
** distribute, sublicense, and/or sell copies of the Software, and to | ||
** permit persons to whom the Software is furnished to do so, subject to | ||
** the following conditions: | ||
** | ||
** The above copyright notice and this permission notice shall be included | ||
** in all copies or substantial portions of the Software. | ||
** | ||
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
** SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
*/ | ||
hook(...args) { | ||
return this._latching.hook(...args); | ||
} | ||
/* external dependencies */ | ||
at(...args) { | ||
return this._latching.at(...args); | ||
} | ||
latch(...args) { | ||
return this._latching.latch(...args); | ||
} | ||
/* Apollo Client Link Interface for WebSocket Communication */ | ||
var ApolloClientWS = function (_ApolloLink) { | ||
_inherits(ApolloClientWS, _ApolloLink); | ||
unlatch(...args) { | ||
return this._latching.unlatch(...args); | ||
} | ||
/* ADDON: pass-through methods of emitter sub-system */ | ||
function ApolloClientWS() { | ||
_classCallCheck(this, ApolloClientWS); | ||
/* sanity check constructor arguments */ | ||
var _this = _possibleConstructorReturn(this, (ApolloClientWS.__proto__ || Object.getPrototypeOf(ApolloClientWS)).call(this)); | ||
/* initialize ApolloLink base class */ | ||
emit(...args) { | ||
return this._emitter.emit(...args); | ||
} | ||
once(...args) { | ||
return this._emitter.once(...args); | ||
} | ||
if (arguments.length === 2 && typeof (arguments.length <= 0 ? undefined : arguments[0]) === "string" && _typeof(arguments.length <= 1 ? undefined : arguments[1]) === "object") _this._args = { uri: arguments.length <= 0 ? undefined : arguments[0], opts: arguments.length <= 1 ? undefined : arguments[1] };else if (arguments.length === 1 && _typeof(arguments.length <= 0 ? undefined : arguments[0]) === "object") _this._args = arguments.length <= 0 ? undefined : arguments[0];else throw new Error("invalid arguments to ApolloClientWS constructor (invalid number or type of arguments)"); | ||
if (_this._args.uri === undefined) throw new Error("invalid arguments to ApolloClientWS constructor (missing URI)"); | ||
on(...args) { | ||
return this._emitter.on(...args); | ||
} | ||
/* provide default values for options */ | ||
_this._args.opts = Object.assign({ | ||
debug: 0, | ||
protocols: [], | ||
compress: false, | ||
encoding: "json", | ||
keepalive: 0, | ||
reconnectattempts: -1, | ||
reconnectdelay: 2 * 1000 | ||
}, _this._args.opts); | ||
off(...args) { | ||
return this._emitter.off(...args); | ||
} | ||
/* validate options */ | ||
var errors = []; | ||
if (!_ducky2.default.validate(_this._args.opts, "{\n debug: number,\n protocols: [ string* ],\n compress: boolean,\n encoding: string,\n keepalive: number,\n reconnectattempts: number,\n reconnectdelay: number\n }", errors)) throw new Error("invalid options: " + errors.join("; ")); | ||
addListener(...args) { | ||
return this._emitter.addListener(...args); | ||
} | ||
/* initialize state variables */ | ||
_this._ws = null; | ||
_this._wsf = null; | ||
_this._to = null; | ||
_this._tx = {}; | ||
removeListener(...args) { | ||
return this._emitter.removeListener(...args); | ||
} | ||
/* provide hook latching sub-system */ | ||
_this._latching = new _latching6.default(); | ||
removeAllListeners(...args) { | ||
return this._emitter.removeAllListeners(...args); | ||
} | ||
/* provide event emitter sub-system */ | ||
_this._emitter = new _eventemitter2.default(); | ||
return _this; | ||
} | ||
listenerCount(...args) { | ||
return this._emitter.listenerCount(...args); | ||
} | ||
/* ADDON: pass-through methods of latching sub-system */ | ||
listeners(...args) { | ||
return this._emitter.listeners(...args); | ||
} | ||
eventNames(...args) { | ||
return this._emitter.eventNames(...args); | ||
} | ||
/* ADDON: log a debug message */ | ||
_createClass(ApolloClientWS, [{ | ||
key: "hook", | ||
value: function hook() { | ||
var _latching; | ||
return (_latching = this._latching).hook.apply(_latching, arguments); | ||
} | ||
}, { | ||
key: "at", | ||
value: function at() { | ||
var _latching2; | ||
log(level, msg) { | ||
if (level <= this._args.opts.debug) { | ||
let date = new Date().toISOString(); | ||
let log = `${date} DEBUG [${level}]: ${msg}`; | ||
this.emit("debug", { | ||
date, | ||
level, | ||
msg, | ||
log | ||
}); | ||
} | ||
} | ||
/* ADDON: connect to the peer */ | ||
return (_latching2 = this._latching).at.apply(_latching2, arguments); | ||
} | ||
}, { | ||
key: "latch", | ||
value: function latch() { | ||
var _latching3; | ||
return (_latching3 = this._latching).latch.apply(_latching3, arguments); | ||
} | ||
}, { | ||
key: "unlatch", | ||
value: function unlatch() { | ||
var _latching4; | ||
connect() { | ||
const connectInternal = (attempt = 0) => { | ||
return new Promise((resolve, reject) => { | ||
this.emit("connect"); | ||
this.log(1, "connect: begin"); | ||
/* create a new WebSocket client */ | ||
return (_latching4 = this._latching).unlatch.apply(_latching4, arguments); | ||
let ws; | ||
if ("node" === "browser") ws = new _ws.default(this._args.uri, this._args.opts.protocols);else { | ||
let opts = this.hook("connect:options", "pass", {}); | ||
ws = new _ws.default(this._args.uri, this._args.opts.protocols, opts); | ||
} | ||
/* configure binary transfer */ | ||
/* ADDON: pass-through methods of emitter sub-system */ | ||
ws.binaryType = "node" === "browser" ? "arraybuffer" : "nodebuffer"; | ||
/* create a new WebSocket-Framed wrapper */ | ||
}, { | ||
key: "emit", | ||
value: function emit() { | ||
var _emitter; | ||
let wsf = new _websocketFramed.default(ws, this._args.opts.encoding); | ||
/* react (once) on error messages */ | ||
return (_emitter = this._emitter).emit.apply(_emitter, arguments); | ||
} | ||
}, { | ||
key: "once", | ||
value: function once() { | ||
var _emitter2; | ||
const onError = ev => { | ||
if (this._ws !== null && this._ws._explicitDisconnect) return; | ||
this.log(1, `connect: end (connection error: ${ev.message})`); | ||
ws.removeEventListener("error", onError); | ||
ws._errorOnConnect = true; | ||
return (_emitter2 = this._emitter).once.apply(_emitter2, arguments); | ||
} | ||
}, { | ||
key: "on", | ||
value: function on() { | ||
var _emitter3; | ||
if (attempt < this._args.opts.reconnectattempts || this._args.opts.reconnectattempts === -1) { | ||
this.log(2, "connection error: trigger new connect attempt " + `(in ${Math.trunc(this._args.opts.reconnectdelay / 1000)}s)`); | ||
setTimeout(() => { | ||
/* handle repeated connection attempts (subsequently) */ | ||
connectInternal(attempt + 1).then(() => resolve()).catch(err => reject(err)); | ||
}, this._args.opts.reconnectdelay); | ||
} else reject(ev); | ||
}; | ||
return (_emitter3 = this._emitter).on.apply(_emitter3, arguments); | ||
} | ||
}, { | ||
key: "off", | ||
value: function off() { | ||
var _emitter4; | ||
ws.addEventListener("error", onError); | ||
/* react (once) on the connection opening */ | ||
return (_emitter4 = this._emitter).off.apply(_emitter4, arguments); | ||
} | ||
}, { | ||
key: "addListener", | ||
value: function addListener() { | ||
var _emitter5; | ||
const onOpen = () => { | ||
this.log(1, "connect: end (connection open)"); | ||
ws.removeEventListener("open", onOpen); | ||
this._ws = ws; | ||
this._wsf = wsf; | ||
return (_emitter5 = this._emitter).addListener.apply(_emitter5, arguments); | ||
} | ||
}, { | ||
key: "removeListener", | ||
value: function removeListener() { | ||
var _emitter6; | ||
if (this._args.opts.keepalive > 0) { | ||
this.log(2, "connect: start auto-disconnect timer"); | ||
this._to = setTimeout(() => { | ||
this.disconnect(); | ||
}, this._args.opts.keepalive); | ||
} | ||
return (_emitter6 = this._emitter).removeListener.apply(_emitter6, arguments); | ||
} | ||
}, { | ||
key: "removeAllListeners", | ||
value: function removeAllListeners() { | ||
var _emitter7; | ||
this.emit("open"); | ||
resolve(); | ||
}; | ||
return (_emitter7 = this._emitter).removeAllListeners.apply(_emitter7, arguments); | ||
} | ||
}, { | ||
key: "listenerCount", | ||
value: function listenerCount() { | ||
var _emitter8; | ||
ws.addEventListener("open", onOpen); | ||
/* react (always) on received response messages */ | ||
return (_emitter8 = this._emitter).listenerCount.apply(_emitter8, arguments); | ||
} | ||
}, { | ||
key: "listeners", | ||
value: function listeners() { | ||
var _emitter9; | ||
const onMessage = ev => { | ||
ev = this.hook("receive:message", "pass", ev); | ||
let _ev$frame = ev.frame, | ||
rid = _ev$frame.rid, | ||
type = _ev$frame.type, | ||
data = _ev$frame.data; | ||
return (_emitter9 = this._emitter).listeners.apply(_emitter9, arguments); | ||
} | ||
}, { | ||
key: "eventNames", | ||
value: function eventNames() { | ||
var _emitter10; | ||
if (type === "GRAPHQL-RESPONSE" && typeof data === "object") { | ||
/* is a valid GraphQL response */ | ||
this.log(3, `query: response (framed): ${JSON.stringify(ev.frame)}`); | ||
return (_emitter10 = this._emitter).eventNames.apply(_emitter10, arguments); | ||
} | ||
if (this._tx[rid] !== undefined) { | ||
if (_ducky.default.validate(data, "({ data: Object, errors?: [ Object* ] } | { data?: Object, errors: [ Object* ] })")) this._tx[rid](data);else this._tx[rid](null, "invalid GraphQL response object"); | ||
} | ||
} else { | ||
/* is a non-standard message */ | ||
this.log(2, `message received: ${JSON.stringify(ev.frame)}`); | ||
this.emit("receive", ev.frame); | ||
} | ||
}; | ||
/* ADDON: log a debug message */ | ||
wsf.on("message", onMessage); | ||
}, { | ||
key: "log", | ||
value: function log(level, msg) { | ||
if (level <= this._args.opts.debug) { | ||
var date = new Date().toISOString(); | ||
var log = date + " DEBUG [" + level + "]: " + msg; | ||
this.emit("debug", { date: date, level: level, msg: msg, log: log }); | ||
} | ||
} | ||
const onSocketError = err => { | ||
this.log(2, `WebSocket error: ${err}`); | ||
/* not now, because of auto-reconnects we don't want to mention everything: | ||
this.emit("error", `WebSocket error: ${err}`) */ | ||
}; | ||
/* ADDON: connect to the peer */ | ||
wsf.on("error", onSocketError); | ||
/* react (once) on the connection closing */ | ||
}, { | ||
key: "connect", | ||
value: function connect() { | ||
var _this2 = this; | ||
const onClose = ev => { | ||
if (this._ws !== null && this._ws._explicitDisconnect) return; | ||
this.log(1, `connection closed (code: ${ev.code})`); | ||
ws.removeEventListener("error", onError); | ||
ws.removeEventListener("open", onOpen); | ||
ws.removeEventListener("message", onMessage); | ||
ws.removeEventListener("close", onClose); | ||
if (this._to !== null) clearTimeout(this._to); | ||
this._to = null; | ||
this._ws = null; | ||
this._wsf = null; | ||
let errorOnConnect = ws._errorOnConnect; | ||
delete ws._errorOnConnect; | ||
this.emit("close"); | ||
var connectInternal = function connectInternal() { | ||
var attempt = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0; | ||
if (!errorOnConnect && (ev.code > 1000 || this._args.opts.keepalive === 0)) { | ||
this.log(2, "connection closed: trigger re-connect " + `(in ${this._args.opts.reconnectdelay / 1000}s)`); | ||
setTimeout(() => { | ||
this.connect().catch(err => void err); | ||
}, this._args.opts.reconnectdelay); | ||
} | ||
}; | ||
return new Promise(function (resolve, reject) { | ||
_this2.emit("connect"); | ||
_this2.log(1, "connect: begin"); | ||
ws.addEventListener("close", onClose); | ||
}); | ||
}; | ||
/* handle subsequent connect calls */ | ||
/* create a new WebSocket client */ | ||
var ws = void 0; | ||
if ("node" === "browser") ws = new _ws2.default(_this2._args.uri, _this2._args.opts.protocols);else { | ||
var opts = _this2.hook("connect:options", "pass", {}); | ||
ws = new _ws2.default(_this2._args.uri, _this2._args.opts.protocols, opts); | ||
} | ||
/* configure binary transfer */ | ||
ws.binaryType = "node" === "browser" ? "arraybuffer" : "nodebuffer"; | ||
if (this._connectPromise) return Promise.resolve(this._connectPromise);else { | ||
/* handle repeated connection attempts (initially) */ | ||
return this._connectPromise = connectInternal(0).then(() => { | ||
delete this._connectPromise; | ||
}, err => { | ||
delete this._connectPromise; | ||
throw err; | ||
}); | ||
} | ||
} | ||
/* ADDON: disconnect from the peer */ | ||
/* create a new WebSocket-Framed wrapper */ | ||
var wsf = new _websocketFramed2.default(ws, _this2._args.opts.encoding); | ||
/* react (once) on error messages */ | ||
var onError = function onError(ev) { | ||
if (_this2._ws !== null && _this2._ws._explicitDisconnect) return; | ||
_this2.log(1, "connect: end (connection error: " + ev.message + ")"); | ||
ws.removeEventListener("error", onError); | ||
ws._errorOnConnect = true; | ||
if (attempt < _this2._args.opts.reconnectattempts || _this2._args.opts.reconnectattempts === -1) { | ||
_this2.log(2, "connection error: trigger new connect attempt " + ("(in " + Math.trunc(_this2._args.opts.reconnectdelay / 1000) + "s)")); | ||
setTimeout(function () { | ||
/* handle repeated connection attempts (subsequently) */ | ||
connectInternal(attempt + 1).then(function () { | ||
return resolve(); | ||
}).catch(function (err) { | ||
return reject(err); | ||
}); | ||
}, _this2._args.opts.reconnectdelay); | ||
} else reject(ev); | ||
}; | ||
ws.addEventListener("error", onError); | ||
disconnect() { | ||
/* handle subsequent disconnect calls */ | ||
if (this._disconnectPromise) return Promise.resolve(this._disconnectPromise);else { | ||
return this._disconnectPromise = new Promise((resolve, reject) => { | ||
/* disconnect from the peer */ | ||
this.emit("disconnect"); | ||
this.log(1, "disconnect: begin"); | ||
/* react (once) on the connection opening */ | ||
var onOpen = function onOpen() { | ||
_this2.log(1, "connect: end (connection open)"); | ||
ws.removeEventListener("open", onOpen); | ||
_this2._ws = ws; | ||
_this2._wsf = wsf; | ||
if (_this2._args.opts.keepalive > 0) { | ||
_this2.log(2, "connect: start auto-disconnect timer"); | ||
_this2._to = setTimeout(function () { | ||
_this2.disconnect(); | ||
}, _this2._args.opts.keepalive); | ||
} | ||
_this2.emit("open"); | ||
resolve(); | ||
}; | ||
ws.addEventListener("open", onOpen); | ||
if (this._ws !== null) { | ||
this._ws._explicitDisconnect = true; | ||
/* react (always) on received response messages */ | ||
var onMessage = function onMessage(ev) { | ||
ev = _this2.hook("receive:message", "pass", ev); | ||
var _ev$frame = ev.frame, | ||
rid = _ev$frame.rid, | ||
type = _ev$frame.type, | ||
data = _ev$frame.data; | ||
const onClose = ev => { | ||
if (this._ws === null) return; | ||
if (type === "GRAPHQL-RESPONSE" && (typeof data === "undefined" ? "undefined" : _typeof(data)) === "object") { | ||
/* is a valid GraphQL response */ | ||
_this2.log(3, "query: response (framed): " + JSON.stringify(ev.frame)); | ||
if (_this2._tx[rid] !== undefined) { | ||
if (_ducky2.default.validate(data, "({ data: Object, errors?: [ Object* ] } | { data?: Object, errors: [ Object* ] })")) _this2._tx[rid](data);else _this2._tx[rid](null, "invalid GraphQL response object"); | ||
} | ||
} else { | ||
/* is a non-standard message */ | ||
_this2.log(2, "message received: " + JSON.stringify(ev.frame)); | ||
_this2.emit("receive", ev.frame); | ||
} | ||
}; | ||
wsf.on("message", onMessage); | ||
var onSocketError = function onSocketError(err) { | ||
_this2.log(2, "WebSocket error: " + err); | ||
/* not now, because of auto-reconnects we don't want to mention everything: | ||
this.emit("error", `WebSocket error: ${err}`) */ | ||
}; | ||
wsf.on("error", onSocketError); | ||
this._ws.removeEventListener("close", onClose); | ||
/* react (once) on the connection closing */ | ||
var onClose = function onClose(ev) { | ||
if (_this2._ws !== null && _this2._ws._explicitDisconnect) return; | ||
_this2.log(1, "connection closed (code: " + ev.code + ")"); | ||
ws.removeEventListener("error", onError); | ||
ws.removeEventListener("open", onOpen); | ||
ws.removeEventListener("message", onMessage); | ||
ws.removeEventListener("close", onClose); | ||
if (_this2._to !== null) clearTimeout(_this2._to); | ||
_this2._to = null; | ||
_this2._ws = null; | ||
_this2._wsf = null; | ||
var errorOnConnect = ws._errorOnConnect; | ||
delete ws._errorOnConnect; | ||
_this2.emit("close"); | ||
if (!errorOnConnect && (ev.code > 1000 || _this2._args.opts.keepalive === 0)) { | ||
_this2.log(2, "connection closed: trigger re-connect " + ("(in " + _this2._args.opts.reconnectdelay / 1000 + "s)")); | ||
setTimeout(function () { | ||
_this2.connect().catch(function (err) { | ||
return void err; | ||
}); | ||
}, _this2._args.opts.reconnectdelay); | ||
} | ||
}; | ||
ws.addEventListener("close", onClose); | ||
}); | ||
}; | ||
/* handle subsequent connect calls */ | ||
if (this._connectPromise) return Promise.resolve(this._connectPromise);else { | ||
/* handle repeated connection attempts (initially) */ | ||
return this._connectPromise = connectInternal(0).then(function () { | ||
delete _this2._connectPromise; | ||
}, function (err) { | ||
delete _this2._connectPromise;throw err; | ||
}); | ||
if (this._to !== null) { | ||
clearTimeout(this._to); | ||
this._to = null; | ||
} | ||
} | ||
/* ADDON: disconnect from the peer */ | ||
this._ws = null; | ||
this._wsf = null; | ||
this.log(1, "disconnect: end"); | ||
this.emit("close"); | ||
resolve(); | ||
}; | ||
}, { | ||
key: "disconnect", | ||
value: function disconnect() { | ||
var _this3 = this; | ||
this._ws.addEventListener("close", onClose); | ||
/* handle subsequent disconnect calls */ | ||
if (this._disconnectPromise) return Promise.resolve(this._disconnectPromise);else { | ||
return this._disconnectPromise = new Promise(function (resolve, reject) { | ||
/* disconnect from the peer */ | ||
_this3.emit("disconnect"); | ||
_this3.log(1, "disconnect: begin"); | ||
if (_this3._ws !== null) { | ||
_this3._ws._explicitDisconnect = true; | ||
var onClose = function onClose(ev) { | ||
if (_this3._ws === null) return; | ||
_this3._ws.removeEventListener("close", onClose); | ||
if (_this3._to !== null) { | ||
clearTimeout(_this3._to); | ||
_this3._to = null; | ||
} | ||
_this3._ws = null; | ||
_this3._wsf = null; | ||
_this3.log(1, "disconnect: end"); | ||
_this3.emit("close"); | ||
resolve(); | ||
}; | ||
_this3._ws.addEventListener("close", onClose); | ||
_this3._ws.close(); | ||
} else { | ||
_this3.log(1, "disconnect: end (no-op)"); | ||
resolve(); | ||
} | ||
}).then(function () { | ||
delete _this3._disconnectPromise; | ||
}, function () { | ||
delete _this3._disconnectPromise; | ||
}); | ||
} | ||
this._ws.close(); | ||
} else { | ||
this.log(1, "disconnect: end (no-op)"); | ||
resolve(); | ||
} | ||
}).then(() => { | ||
delete this._disconnectPromise; | ||
}, () => { | ||
delete this._disconnectPromise; | ||
}); | ||
} | ||
} | ||
/* ADDON: send message to the peer */ | ||
/* ADDON: send message to the peer */ | ||
}, { | ||
key: "send", | ||
value: function send(type, data) { | ||
var _this4 = this; | ||
send(type, data) { | ||
this.log(1, "send: begin"); | ||
this.emit("send", { | ||
type, | ||
data | ||
}); | ||
return new Promise((resolve, reject) => { | ||
if (this._ws === null) { | ||
this.log(2, "send: on-the-fly connect"); | ||
this.connect().then(() => resolve()).catch(err => reject(err)); | ||
} else resolve(); | ||
}).then(() => { | ||
/* await WebSocket ready-state OPEN */ | ||
return new Promise((resolve, reject) => { | ||
const check = k => { | ||
if (k <= 0) reject(new Error("failed to await WebSocket ready-state OPEN"));else if (this._ws.readyState === _ws.default.CLOSED) reject(new Error("failed to send to WebSocket, already in ready-state CLOSED"));else if (this._ws.readyState === _ws.default.CLOSING) reject(new Error("failed to send to WebSocket, already in ready-state CLOSING"));else if (this._ws.readyState === _ws.default.CONNECTING) setTimeout(() => check(k - 1), 100);else resolve(); | ||
}; | ||
this.log(1, "send: begin"); | ||
this.emit("send", { type: type, data: data }); | ||
return new Promise(function (resolve, reject) { | ||
if (_this4._ws === null) { | ||
_this4.log(2, "send: on-the-fly connect"); | ||
_this4.connect().then(function () { | ||
return resolve(); | ||
}).catch(function (err) { | ||
return reject(err); | ||
}); | ||
} else resolve(); | ||
}).then(function () { | ||
/* await WebSocket ready-state OPEN */ | ||
return new Promise(function (resolve, reject) { | ||
var check = function check(k) { | ||
if (k <= 0) reject(new Error("failed to await WebSocket ready-state OPEN"));else if (_this4._ws.readyState === _ws2.default.CLOSED) reject(new Error("failed to send to WebSocket, already in ready-state CLOSED"));else if (_this4._ws.readyState === _ws2.default.CLOSING) reject(new Error("failed to send to WebSocket, already in ready-state CLOSING"));else if (_this4._ws.readyState === _ws2.default.CONNECTING) setTimeout(function () { | ||
return check(k - 1); | ||
}, 100);else resolve(); | ||
}; | ||
check(100); | ||
}); | ||
}).then(function () { | ||
/* send the message */ | ||
var _wsf$send = _this4._wsf.send({ type: type, data: data }), | ||
frame = _wsf$send.frame; | ||
check(100); | ||
}); | ||
}).then(() => { | ||
/* send the message */ | ||
let _this$_wsf$send = this._wsf.send({ | ||
type, | ||
data | ||
}), | ||
frame = _this$_wsf$send.frame; | ||
_this4.log(2, "message sent: " + JSON.stringify(frame)); | ||
_this4.log(1, "send: end"); | ||
return frame; | ||
}); | ||
} | ||
this.log(2, `message sent: ${JSON.stringify(frame)}`); | ||
this.log(1, "send: end"); | ||
return frame; | ||
}); | ||
} | ||
/* STANDARD: send request to the peer */ | ||
/* STANDARD: send request to the peer */ | ||
}, { | ||
key: "request", | ||
value: function request(operation) { | ||
var _this5 = this; | ||
request(operation) { | ||
return new _apolloLink.Observable(observer => { | ||
this.emit("request", operation); | ||
this.log(1, "request: begin"); | ||
/* we here would have (but don't use): | ||
const { operationName, extensions, variables, query } = operation */ | ||
return new _apolloLink.Observable(function (observer) { | ||
_this5.emit("request", operation); | ||
_this5.log(1, "request: begin"); | ||
/* we here would have (but don't use): | ||
const { operationName, extensions, variables, query } = operation */ | ||
void new Promise(function (resolve, reject) { | ||
/* optionally perform the deferred connect */ | ||
if (_this5._ws === null) { | ||
_this5.log(2, "request: on-the-fly connect"); | ||
_this5.connect().then(function () { | ||
return resolve(); | ||
}).catch(function (err) { | ||
return reject(err); | ||
}); | ||
} else resolve(); | ||
}).then(function () { | ||
/* await WebSocket ready-state OPEN */ | ||
return new Promise(function (resolve, reject) { | ||
var check = function check(k) { | ||
if (k <= 0) reject(new Error("failed to await WebSocket ready-state OPEN"));else if (_this5._ws.readyState === _ws2.default.CLOSED) reject(new Error("failed to send to WebSocket, already in ready-state CLOSED"));else if (_this5._ws.readyState === _ws2.default.CLOSING) reject(new Error("failed to send to WebSocket, already in ready-state CLOSING"));else if (_this5._ws.readyState === _ws2.default.CONNECTING) setTimeout(function () { | ||
return check(k - 1); | ||
}, 100);else resolve(); | ||
}; | ||
check(100); | ||
}); | ||
}).then(function () { | ||
/* perform the request */ | ||
return new Promise(function (resolve, reject) { | ||
/* prepare the request */ | ||
var request = Object.assign({}, operation); | ||
request.query = (0, _printer.print)(request.query); | ||
if (_this5._args.opts.compress === true) request.query = (0, _graphqlQueryCompress2.default)(request.query); | ||
if (request.operationName === null) delete request.operationName; | ||
if (Object.keys(request.variables).length === 0) delete request.variables; | ||
if (Object.keys(request.extensions).length === 0) delete request.extensions; | ||
request = _this5.hook("query:request", "pass", request); | ||
void new Promise((resolve, reject) => { | ||
/* optionally perform the deferred connect */ | ||
if (this._ws === null) { | ||
this.log(2, "request: on-the-fly connect"); | ||
this.connect().then(() => resolve()).catch(err => reject(err)); | ||
} else resolve(); | ||
}).then(() => { | ||
/* await WebSocket ready-state OPEN */ | ||
return new Promise((resolve, reject) => { | ||
const check = k => { | ||
if (k <= 0) reject(new Error("failed to await WebSocket ready-state OPEN"));else if (this._ws.readyState === _ws.default.CLOSED) reject(new Error("failed to send to WebSocket, already in ready-state CLOSED"));else if (this._ws.readyState === _ws.default.CLOSING) reject(new Error("failed to send to WebSocket, already in ready-state CLOSING"));else if (this._ws.readyState === _ws.default.CONNECTING) setTimeout(() => check(k - 1), 100);else resolve(); | ||
}; | ||
/* send the request */ | ||
_this5.log(2, "request: request: " + JSON.stringify(request)); | ||
check(100); | ||
}); | ||
}).then(() => { | ||
/* perform the request */ | ||
return new Promise((resolve, reject) => { | ||
/* prepare the request */ | ||
let request = Object.assign({}, operation); | ||
request.query = (0, _printer.print)(request.query); | ||
if (this._args.opts.compress === true) request.query = (0, _graphqlQueryCompress.default)(request.query); | ||
if (request.operationName === null) delete request.operationName; | ||
if (Object.keys(request.variables).length === 0) delete request.variables; | ||
if (Object.keys(request.extensions).length === 0) delete request.extensions; | ||
request = this.hook("query:request", "pass", request); | ||
/* send the request */ | ||
var _wsf$send2 = _this5._wsf.send({ type: "GRAPHQL-REQUEST", data: request }), | ||
frame = _wsf$send2.frame; | ||
this.log(2, `request: request: ${JSON.stringify(request)}`); | ||
_this5.log(3, "request: request (framed): " + JSON.stringify(frame)); | ||
let _this$_wsf$send2 = this._wsf.send({ | ||
type: "GRAPHQL-REQUEST", | ||
data: request | ||
}), | ||
frame = _this$_wsf$send2.frame; | ||
/* queue request and await response or error */ | ||
var fid = frame.fid; | ||
_this5._tx[fid] = function (response, error) { | ||
delete _this5._tx[fid]; | ||
if (response) resolve(response);else reject(error); | ||
}; | ||
}); | ||
}).then(function (response) { | ||
/* optionally automatically disconnect connection after request */ | ||
if (_this5._args.opts.keepalive > 0) { | ||
_this5.log(2, "request: (re)start auto-disconnect timer"); | ||
if (_this5._to !== null) clearTimeout(_this5._to); | ||
_this5._to = setTimeout(function () { | ||
_this5.disconnect(); | ||
}, _this5._args.opts.keepalive); | ||
} | ||
_this5.log(2, "request: response: " + JSON.stringify(response)); | ||
_this5.log(1, "request: end"); | ||
return response; | ||
}).then(function (response) { | ||
/* pass response to other Apollo Link instances */ | ||
operation.setContext({ response: response }); | ||
this.log(3, `request: request (framed): ${JSON.stringify(frame)}`); | ||
/* queue request and await response or error */ | ||
/* pass response to caller */ | ||
observer.next(response); | ||
observer.complete(); | ||
}).catch(function (err) { | ||
/* pass error to caller */ | ||
observer.error(err); | ||
}); | ||
return function () { | ||
/* no-op: we cannot cancel the operation */ | ||
}; | ||
}); | ||
let fid = frame.fid; | ||
this._tx[fid] = (response, error) => { | ||
delete this._tx[fid]; | ||
if (response) resolve(response);else reject(error); | ||
}; | ||
}); | ||
}).then(response => { | ||
/* optionally automatically disconnect connection after request */ | ||
if (this._args.opts.keepalive > 0) { | ||
this.log(2, "request: (re)start auto-disconnect timer"); | ||
if (this._to !== null) clearTimeout(this._to); | ||
this._to = setTimeout(() => { | ||
this.disconnect(); | ||
}, this._args.opts.keepalive); | ||
} | ||
}]); | ||
return ApolloClientWS; | ||
}(_apolloLink.ApolloLink); | ||
this.log(2, `request: response: ${JSON.stringify(response)}`); | ||
this.log(1, "request: end"); | ||
return response; | ||
}).then(response => { | ||
/* pass response to other Apollo Link instances */ | ||
operation.setContext({ | ||
response | ||
}); | ||
/* pass response to caller */ | ||
observer.next(response); | ||
observer.complete(); | ||
}).catch(err => { | ||
/* pass error to caller */ | ||
observer.error(err); | ||
}); | ||
return () => { | ||
/* no-op: we cannot cancel the operation */ | ||
}; | ||
}); | ||
} | ||
} | ||
/* API export */ | ||
@@ -563,3 +502,3 @@ | ||
module.exports = { | ||
ApolloClientWS: ApolloClientWS | ||
ApolloClientWS | ||
}; | ||
@@ -566,0 +505,0 @@ |
{ | ||
"name": "apollo-client-ws", | ||
"version": "2.2.1", | ||
"version": "2.3.0", | ||
"description": "GraphQL WebSocket Network Interface for Apollo Client", | ||
@@ -21,49 +21,46 @@ "keywords": [ "graphql", "websocket", "network", "interface", "transport", "apollo", "client" ], | ||
"browserify-shim": { | ||
"ws": "global:WebSocket" | ||
"ws": "global:WebSocket" | ||
}, | ||
"peerDependencies": { | ||
"apollo-client": ">=2.1.0", | ||
"apollo-link": ">=1.0.7" | ||
"apollo-client": ">=2.1.0", | ||
"apollo-link": ">=1.0.7" | ||
}, | ||
"dependencies": { | ||
"graphql": "0.13.2", | ||
"graphql-query-compress": "1.0.11", | ||
"websocket-framed": "1.0.23", | ||
"eventemitter3": "3.1.0", | ||
"latching": "1.0.1", | ||
"es6-promise": "4.2.4", | ||
"ducky": "2.6.11", | ||
"ws": "6.0.0" | ||
"graphql": "0.13.2", | ||
"graphql-query-compress": "1.1.0", | ||
"websocket-framed": "1.1.0", | ||
"eventemitter3": "3.1.0", | ||
"latching": "1.0.3", | ||
"es6-promise": "4.2.4", | ||
"ducky": "2.6.12", | ||
"ws": "6.0.0" | ||
}, | ||
"optionalDependencies": { | ||
"utf-8-validate": "5.0.1", | ||
"bufferutil": "4.0.0" | ||
"utf-8-validate": "5.0.1", | ||
"bufferutil": "4.0.0" | ||
}, | ||
"devDependencies": { | ||
"apollo-client": ">=2.1.0", | ||
"apollo-link": ">=1.0.7", | ||
"grunt": "1.0.3", | ||
"grunt-cli": "1.2.0", | ||
"grunt-contrib-clean": "1.1.0", | ||
"grunt-eslint": "21.0.0", | ||
"grunt-browserify": "5.3.0", | ||
"browserify": "16.2.2", | ||
"envify": "4.1.0", | ||
"babelify": "8.0.0", | ||
"babel-core": "6.26.3", | ||
"babel-preset-es2015": "6.24.1", | ||
"babel-preset-es2016": "6.24.1", | ||
"babel-preset-es2017": "6.24.1", | ||
"babel-plugin-transform-runtime": "6.23.0", | ||
"uglifyify": "5.0.1", | ||
"browserify-header": "0.9.4", | ||
"browserify-derequire": "0.9.6", | ||
"browserify-shim": "3.8.14", | ||
"babel-eslint": "8.2.6", | ||
"eslint": "5.2.0", | ||
"eslint-config-standard": "11.0.0", | ||
"eslint-plugin-standard": "3.1.0", | ||
"eslint-plugin-promise": "3.8.0", | ||
"eslint-plugin-import": "2.13.0", | ||
"eslint-plugin-node": "7.0.1" | ||
"apollo-client": ">=2.1.0", | ||
"apollo-link": ">=1.0.7", | ||
"grunt": "1.0.3", | ||
"grunt-cli": "1.3.1", | ||
"grunt-contrib-clean": "1.1.0", | ||
"grunt-eslint": "21.0.0", | ||
"grunt-browserify": "5.3.0", | ||
"browserify": "16.2.2", | ||
"envify": "4.1.0", | ||
"babelify": "9.0.0", | ||
"@babel/core": "7.0.0", | ||
"@babel/preset-env": "7.0.0", | ||
"uglifyify": "5.0.1", | ||
"browserify-header": "0.9.4", | ||
"browserify-derequire": "0.9.6", | ||
"browserify-shim": "3.8.14", | ||
"babel-eslint": "9.0.0", | ||
"eslint": "5.5.0", | ||
"eslint-config-standard": "12.0.0", | ||
"eslint-plugin-standard": "4.0.0", | ||
"eslint-plugin-promise": "4.0.0", | ||
"eslint-plugin-import": "2.14.0", | ||
"eslint-plugin-node": "7.0.1" | ||
}, | ||
@@ -70,0 +67,0 @@ "scripts": { |
@@ -14,7 +14,9 @@ | ||
[ "babelify", { | ||
presets: [ "es2015", "es2016", "es2017" ], | ||
plugins: [ [ "transform-runtime", { | ||
"polyfill": true, | ||
"regenerator": false | ||
} ] ] | ||
presets: [ | ||
[ "@babel/preset-env", { | ||
"targets": { | ||
"browsers": "last 2 versions, not dead" | ||
} | ||
} ] | ||
] | ||
} ] | ||
@@ -21,0 +23,0 @@ ], |
@@ -8,13 +8,13 @@ { | ||
"graphql-tag": "2.9.2", | ||
"graphql-tools": "3.0.5", | ||
"hapi": "17.5.2", | ||
"hapi-plugin-websocket": "2.0.11", | ||
"graphql-tools": "3.1.1", | ||
"hapi": "17.5.4", | ||
"hapi-plugin-websocket": "2.0.13", | ||
"boom": "7.2.0", | ||
"apollo-client": "2.3.5", | ||
"apollo-client": "2.4.1", | ||
"apollo-client-ws": "file:../", | ||
"apollo-cache-inmemory": "1.2.5" | ||
"apollo-cache-inmemory": "1.2.9" | ||
}, | ||
"devDependencies": { | ||
"grunt": "1.0.3", | ||
"grunt-cli": "1.2.0", | ||
"grunt-cli": "1.3.1", | ||
"grunt-contrib-clean": "1.1.0", | ||
@@ -24,8 +24,5 @@ "grunt-eslint": "21.0.0", | ||
"browserify": "16.2.2", | ||
"babelify": "8.0.0", | ||
"babel-core": "6.26.3", | ||
"babel-preset-es2015": "6.24.1", | ||
"babel-preset-es2016": "6.24.1", | ||
"babel-preset-es2017": "6.24.1", | ||
"babel-plugin-transform-runtime": "6.23.0", | ||
"babelify": "9.0.0", | ||
"@babel/core": "7.0.0", | ||
"@babel/preset-env": "7.0.0", | ||
"rimraf": "2.6.2" | ||
@@ -32,0 +29,0 @@ }, |
Sorry, the diff of this file is too big to display
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
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
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
23
0
236154
1986
+ Added@babel/runtime@7.0.0(transitive)
+ Addedcbor@4.1.1(transitive)
+ Addedducky@2.6.12(transitive)
+ Addedencodr@1.1.0(transitive)
+ Addedgraphql-query-compress@1.1.0(transitive)
+ Addedlatching@1.0.3(transitive)
+ Addedregenerator-runtime@0.12.1(transitive)
+ Addedtokenizr@1.3.10(transitive)
+ Addedwebsocket-framed@1.1.0(transitive)
- Removedbabel-runtime@6.26.0(transitive)
- Removedcbor@4.1.0(transitive)
- Removedcore-js@2.6.12(transitive)
- Removedducky@2.6.11(transitive)
- Removedencodr@1.0.16(transitive)
- Removedgraphql-query-compress@1.0.11(transitive)
- Removedlatching@1.0.1(transitive)
- Removedregenerator-runtime@0.11.1(transitive)
- Removedtokenizr@1.3.9(transitive)
- Removedwebsocket-framed@1.0.23(transitive)
Updatedducky@2.6.12
Updatedgraphql-query-compress@1.1.0
Updatedlatching@1.0.3
Updatedwebsocket-framed@1.1.0