jsonrpc-websocket-client
Version:
JSON-RPC 2 over WebSocket
201 lines (155 loc) • 4.59 kB
JavaScript
"use strict";
exports.__esModule = true;
exports.default = exports.OPEN = exports.MESSAGE = exports.ConnectionError = exports.CONNECTING = exports.CLOSED = exports.AbortedConnection = void 0;
var _fromEvent = _interopRequireDefault(require("promise-toolbox/fromEvent"));
var _fromEvents = _interopRequireDefault(require("promise-toolbox/fromEvents"));
var _try = _interopRequireDefault(require("promise-toolbox/try"));
var _isomorphicWs = _interopRequireDefault(require("isomorphic-ws"));
var _makeError = require("make-error");
var _events = require("events");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
class ConnectionError extends _makeError.BaseError {}
exports.ConnectionError = ConnectionError;
class AbortedConnection extends ConnectionError {
constructor() {
super("connection aborted");
}
}
exports.AbortedConnection = AbortedConnection;
const CLOSED = "closed";
exports.CLOSED = CLOSED;
const CONNECTING = "connecting";
exports.CONNECTING = CONNECTING;
const MESSAGE = "message";
exports.MESSAGE = MESSAGE;
const OPEN = "open";
exports.OPEN = OPEN;
class WebSocketClient extends _events.EventEmitter {
constructor(url, protocols, opts) {
super();
if (opts && !url.startsWith("wss")) {
delete opts.rejectUnauthorized;
}
this._opts = opts;
this._protocols = protocols;
this._url = url;
this._protocol = null;
this._socket = null;
this._status = CLOSED;
this._onClose = this._onClose.bind(this);
}
get protocol() {
return this._protocol;
}
get status() {
return this._status;
}
close() {
return (0, _try.default)(() => {
const status = this._status;
if (status === CLOSED) {
return;
}
const socket = this._socket;
if (status === CONNECTING) {
socket.abort = true;
socket.close();
return;
}
const promise = (0, _fromEvent.default)(socket, "close");
socket.close();
return promise;
});
}
open(backoff) {
if (!backoff) {
return this._open();
}
const iterator = backoff[Symbol.iterator]();
let cancelled = false;
const cancel = () => {
cancelled = true;
};
let error_;
const attempt = () => {
if (cancelled) {
throw error_;
}
return this._open().catch(error => {
let current;
if (error instanceof AbortedConnection || (current = iterator.next()).done) {
throw error;
}
const {
value
} = current;
this.emit("scheduledAttempt", {
cancel,
delay: value
});
error_ = error;
return delay(current.value).then(attempt);
});
};
const promise = attempt();
promise.cancel = cancel;
return promise;
}
send(data) {
this._assertStatus(OPEN);
this._socket.send(data);
}
_assertNotStatus(notExpected) {
if (this._status === notExpected) {
throw new ConnectionError(`invalid status ${this._status}`);
}
}
_assertStatus(expected) {
if (this._status !== expected) {
throw new ConnectionError(`invalid status ${this._status}, expected ${expected}`);
}
}
_onClose() {
const previous = this._status;
this._socket = null;
this._status = CLOSED;
if (previous === OPEN) {
this.emit(CLOSED);
}
}
_open() {
return (0, _try.default)(() => {
this._assertStatus(CLOSED);
this._status = CONNECTING;
return (0, _try.default)(() => {
const socket = this._socket = new _isomorphicWs.default(this._url, this._protocols, this._opts);
return (0, _fromEvents.default)(socket, ["open"], ["close", "error"]).then(() => {
socket.addEventListener("close", this._onClose);
socket.addEventListener("error", error => {
this.emit("error", error);
});
socket.addEventListener("message", _ref => {
let {
data
} = _ref;
this.emit(MESSAGE, data);
});
this._status = OPEN;
this.emit(OPEN);
}, _ref2 => {
let [error] = _ref2;
if (socket.abort) {
throw new AbortedConnection();
}
throw error;
});
}).catch(error => {
this._onClose();
throw error;
});
});
}
}
exports.default = WebSocketClient;
//# sourceMappingURL=websocket-client.js.map