apollo-client-ws
Advanced tools
Comparing version
@@ -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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
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
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
23
-11.54%0
-100%236154
-28.05%1986
-19.3%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
Updated
Updated
Updated
Updated