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

graphql-subscriptions-client

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-subscriptions-client - npm Package Compare versions

Comparing version 0.15.0 to 0.16.0

53

dist/esm/index.js

@@ -5,7 +5,7 @@ // src/index.ts

import $$observable from "symbol-observable";
const WS_MINTIMEOUT = 1e3;
const WS_TIMEOUT = 3e4;
const isString = (value) => typeof value === "string";
const isObject = (value) => value !== null && typeof value === "object";
class SubscriptionClient {
var WS_MIN_TIMEOUT = 1e3;
var WS_TIMEOUT = 3e4;
var isString = (value) => typeof value === "string";
var isObject = (value) => value !== null && typeof value === "object";
var SubscriptionClient = class {
constructor(url, options) {

@@ -15,3 +15,3 @@ const {

connectionParams = {},
minTimeout = WS_MINTIMEOUT,
minTimeout = WS_MIN_TIMEOUT,
timeout = WS_TIMEOUT,

@@ -355,22 +355,27 @@ reconnect = false,

this.client.addEventListener("message", ({data}) => {
this.processReceivedData(data);
let parsedMessage;
try {
parsedMessage = JSON.parse(data);
} catch (error) {
throw new Error(`Message must be JSON-parseable. Got: ${data}`);
}
if (Array.isArray(parsedMessage)) {
for (const message of parsedMessage) {
this.processReceivedMessage(message);
}
} else {
this.processReceivedMessage(parsedMessage);
}
});
}
processReceivedData(receivedData) {
let parsedMessage;
let opId;
try {
parsedMessage = JSON.parse(receivedData);
opId = parsedMessage.id;
} catch (error) {
throw new Error(`Message must be JSON-parseable. Got: ${receivedData}`);
}
if (["data", "complete", "error"].includes(parsedMessage.type) && !this.operations[opId]) {
processReceivedMessage(message) {
const opId = message.id;
if (["data", "complete", "error"].includes(message.type) && !this.operations[opId]) {
this.unsubscribe(opId);
return;
}
switch (parsedMessage.type) {
switch (message.type) {
case "connection_error":
if (this.connectionCallback) {
this.connectionCallback(parsedMessage.payload);
this.connectionCallback(message.payload);
}

@@ -392,9 +397,9 @@ break;

case "error":
this.operations[opId].handler(this.formatErrors(parsedMessage.payload), null);
this.operations[opId].handler(this.formatErrors(message.payload), null);
delete this.operations[opId];
break;
case "data":
const parsedPayload = !parsedMessage.payload.errors ? parsedMessage.payload : {
...parsedMessage.payload,
errors: this.formatErrors(parsedMessage.payload.errors)
const parsedPayload = !message.payload.errors ? message.payload : {
...message.payload,
errors: this.formatErrors(message.payload.errors)
};

@@ -426,5 +431,5 @@ this.operations[opId].handler(null, parsedPayload);

}
}
};
export {
SubscriptionClient
};
(() => {
var __defineProperty = Object.defineProperty;
var __hasOwnProperty = Object.prototype.hasOwnProperty;
var __commonJS = (callback, module) => () => {
if (!module) {
module = {exports: {}};
callback(module.exports, module);
}
return module.exports;
};
var __markAsModule = (target) => {
return __defineProperty(target, "__esModule", {value: true});
};
var __export = (target, all) => {
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __markAsModule = (target) => __defProp(target, "__esModule", {value: true});
var __exportStar = (target, module, desc) => {
__markAsModule(target);
for (var name in all)
__defineProperty(target, name, {get: all[name], enumerable: true});
};
var __exportStar = (target, module) => {
__markAsModule(target);
if (typeof module === "object" || typeof module === "function") {
for (let key in module)
if (!__hasOwnProperty.call(target, key) && key !== "default")
__defineProperty(target, key, {get: () => module[key], enumerable: true});
if (module && typeof module === "object" || typeof module === "function") {
for (let key of __getOwnPropNames(module))
if (!__hasOwnProp.call(target, key) && key !== "default")
__defProp(target, key, {get: () => module[key], enumerable: !(desc = __getOwnPropDesc(module, key)) || desc.enumerable});
}

@@ -31,433 +21,432 @@ return target;

return module;
return __exportStar(__defineProperty({}, "default", {value: module, enumerable: true}), module);
return __exportStar(__defProp(module != null ? __create(__getProtoOf(module)) : {}, "default", {value: module, enumerable: true}), module);
};
// src/index.ts
var require_src = __commonJS((exports) => {
__export(exports, {
SubscriptionClient: () => SubscriptionClient
});
const backo2 = __toModule(require("backo2"));
const eventemitter3 = __toModule(require("eventemitter3"));
const symbol_observable = __toModule(require("symbol-observable"));
const WS_MINTIMEOUT = 1e3;
const WS_TIMEOUT = 3e4;
const isString = (value) => typeof value === "string";
const isObject = (value) => value !== null && typeof value === "object";
class SubscriptionClient {
constructor(url, options) {
const {
connectionCallback = void 0,
connectionParams = {},
minTimeout = WS_MINTIMEOUT,
timeout = WS_TIMEOUT,
reconnect = false,
reconnectionAttempts = Infinity,
lazy = false,
inactivityTimeout = 0
} = options || {};
this.wsImpl = WebSocket;
this.connectionCallback = connectionCallback;
this.url = url;
this.operations = {};
this.nextOperationId = 0;
this.wsMinTimeout = minTimeout;
this.wsTimeout = timeout;
this.unsentMessagesQueue = [];
this.reconnect = reconnect;
this.reconnecting = false;
this.reconnectionAttempts = reconnectionAttempts;
this.lazy = !!lazy;
this.inactivityTimeout = inactivityTimeout;
this.closedByUser = false;
this.backoff = new backo2.default({jitter: 0.5});
this.eventEmitter = new eventemitter3.default();
this.client = null;
this.maxConnectTimeGenerator = this.createMaxConnectTimeGenerator();
this.connectionParams = this.getConnectionParams(connectionParams);
if (!this.lazy) {
this.connect();
}
var import_backo2 = __toModule(require("backo2"));
var import_eventemitter3 = __toModule(require("eventemitter3"));
var import_symbol_observable = __toModule(require("symbol-observable"));
var WS_MIN_TIMEOUT = 1e3;
var WS_TIMEOUT = 3e4;
var isString = (value) => typeof value === "string";
var isObject = (value) => value !== null && typeof value === "object";
var SubscriptionClient = class {
constructor(url, options) {
const {
connectionCallback = void 0,
connectionParams = {},
minTimeout = WS_MIN_TIMEOUT,
timeout = WS_TIMEOUT,
reconnect = false,
reconnectionAttempts = Infinity,
lazy = false,
inactivityTimeout = 0
} = options || {};
this.wsImpl = WebSocket;
this.connectionCallback = connectionCallback;
this.url = url;
this.operations = {};
this.nextOperationId = 0;
this.wsMinTimeout = minTimeout;
this.wsTimeout = timeout;
this.unsentMessagesQueue = [];
this.reconnect = reconnect;
this.reconnecting = false;
this.reconnectionAttempts = reconnectionAttempts;
this.lazy = !!lazy;
this.inactivityTimeout = inactivityTimeout;
this.closedByUser = false;
this.backoff = new import_backo2.default({jitter: 0.5});
this.eventEmitter = new import_eventemitter3.default();
this.client = null;
this.maxConnectTimeGenerator = this.createMaxConnectTimeGenerator();
this.connectionParams = this.getConnectionParams(connectionParams);
if (!this.lazy) {
this.connect();
}
get status() {
if (this.client === null) {
return this.wsImpl.CLOSED;
}
return this.client.readyState;
}
get status() {
if (this.client === null) {
return this.wsImpl.CLOSED;
}
close(isForced = true, closedByUser = true) {
this.clearInactivityTimeout();
if (this.client !== null) {
this.closedByUser = closedByUser;
if (isForced) {
this.clearCheckConnectionInterval();
this.clearMaxConnectTimeout();
this.clearTryReconnectTimeout();
this.unsubscribeAll();
this.sendMessage(void 0, "connection_terminate", null);
}
this.client.close();
this.client = null;
this.eventEmitter.emit("disconnected");
if (!isForced) {
this.tryReconnect();
}
return this.client.readyState;
}
close(isForced = true, closedByUser = true) {
this.clearInactivityTimeout();
if (this.client !== null) {
this.closedByUser = closedByUser;
if (isForced) {
this.clearCheckConnectionInterval();
this.clearMaxConnectTimeout();
this.clearTryReconnectTimeout();
this.unsubscribeAll();
this.sendMessage(void 0, "connection_terminate", null);
}
this.client.close();
this.client = null;
this.eventEmitter.emit("disconnected");
if (!isForced) {
this.tryReconnect();
}
}
request(request) {
const getObserver = this.getObserver.bind(this);
const executeOperation = this.executeOperation.bind(this);
const unsubscribe = this.unsubscribe.bind(this);
let opId;
this.clearInactivityTimeout();
return {
[symbol_observable.default]() {
return this;
},
subscribe(observerOrNext, onError, onComplete) {
const observer = getObserver(observerOrNext, onError, onComplete);
opId = executeOperation(request, (error, result) => {
if (error === null && result === null) {
if (observer.complete) {
observer.complete();
}
} else if (error) {
if (observer.error) {
observer.error(error[0]);
}
} else {
if (observer.next) {
observer.next(result);
}
}
request(request) {
const getObserver = this.getObserver.bind(this);
const executeOperation = this.executeOperation.bind(this);
const unsubscribe = this.unsubscribe.bind(this);
let opId;
this.clearInactivityTimeout();
return {
[import_symbol_observable.default]() {
return this;
},
subscribe(observerOrNext, onError, onComplete) {
const observer = getObserver(observerOrNext, onError, onComplete);
opId = executeOperation(request, (error, result) => {
if (error === null && result === null) {
if (observer.complete) {
observer.complete();
}
});
return {
unsubscribe: () => {
if (opId) {
unsubscribe(opId);
opId = null;
}
} else if (error) {
if (observer.error) {
observer.error(error[0]);
}
};
}
};
}
on(eventName, callback, context) {
const handler = this.eventEmitter.on(eventName, callback, context);
return () => {
handler.off(eventName, callback, context);
};
}
onConnected(callback, context) {
return this.on("connected", callback, context);
}
onConnecting(callback, context) {
return this.on("connecting", callback, context);
}
onDisconnected(callback, context) {
return this.on("disconnected", callback, context);
}
onReconnected(callback, context) {
return this.on("reconnected", callback, context);
}
onReconnecting(callback, context) {
return this.on("reconnecting", callback, context);
}
onError(callback, context) {
return this.on("error", callback, context);
}
unsubscribeAll() {
Object.keys(this.operations).forEach((subId) => {
this.unsubscribe(subId);
});
}
getConnectionParams(connectionParams) {
return () => {
return new Promise((resolve, reject) => {
if (typeof connectionParams === "function") {
try {
return resolve(connectionParams());
} catch (error) {
return reject(error);
} else {
if (observer.next) {
observer.next(result);
}
}
resolve(connectionParams);
});
};
}
executeOperation(options, handler) {
if (this.client === null) {
this.connect();
}
const opId = this.generateOperationId();
this.operations[opId] = {options, handler};
try {
this.checkOperationOptions(options, handler);
if (this.operations[opId]) {
this.operations[opId] = {options, handler};
this.sendMessage(opId, "start", options);
}
} catch (error) {
this.unsubscribe(opId);
handler(this.formatErrors(error));
}
return opId;
}
getObserver(observerOrNext, error, complete) {
if (typeof observerOrNext === "function") {
return {
next: (value) => observerOrNext(value),
error: (e) => error && error(e),
complete: () => complete && complete()
unsubscribe: () => {
if (opId) {
unsubscribe(opId);
opId = null;
}
}
};
}
return observerOrNext;
}
createMaxConnectTimeGenerator() {
const minValue = this.wsMinTimeout;
const maxValue = this.wsTimeout;
return new backo2.default({
min: minValue,
max: maxValue,
factor: 1.2
};
}
on(eventName, callback, context) {
const handler = this.eventEmitter.on(eventName, callback, context);
return () => {
handler.off(eventName, callback, context);
};
}
onConnected(callback, context) {
return this.on("connected", callback, context);
}
onConnecting(callback, context) {
return this.on("connecting", callback, context);
}
onDisconnected(callback, context) {
return this.on("disconnected", callback, context);
}
onReconnected(callback, context) {
return this.on("reconnected", callback, context);
}
onReconnecting(callback, context) {
return this.on("reconnecting", callback, context);
}
onError(callback, context) {
return this.on("error", callback, context);
}
unsubscribeAll() {
Object.keys(this.operations).forEach((subId) => {
this.unsubscribe(subId);
});
}
getConnectionParams(connectionParams) {
return () => {
return new Promise((resolve, reject) => {
if (typeof connectionParams === "function") {
try {
return resolve(connectionParams());
} catch (error) {
return reject(error);
}
}
resolve(connectionParams);
});
};
}
executeOperation(options, handler) {
if (this.client === null) {
this.connect();
}
clearCheckConnectionInterval() {
if (this.checkConnectionIntervalId) {
clearInterval(this.checkConnectionIntervalId);
this.checkConnectionIntervalId = null;
const opId = this.generateOperationId();
this.operations[opId] = {options, handler};
try {
this.checkOperationOptions(options, handler);
if (this.operations[opId]) {
this.operations[opId] = {options, handler};
this.sendMessage(opId, "start", options);
}
} catch (error) {
this.unsubscribe(opId);
handler(this.formatErrors(error));
}
clearMaxConnectTimeout() {
if (this.maxConnectTimeoutId) {
clearTimeout(this.maxConnectTimeoutId);
this.maxConnectTimeoutId = null;
}
return opId;
}
getObserver(observerOrNext, error, complete) {
if (typeof observerOrNext === "function") {
return {
next: (value) => observerOrNext(value),
error: (e) => error && error(e),
complete: () => complete && complete()
};
}
clearTryReconnectTimeout() {
if (this.tryReconnectTimeoutId) {
clearTimeout(this.tryReconnectTimeoutId);
this.tryReconnectTimeoutId = null;
}
return observerOrNext;
}
createMaxConnectTimeGenerator() {
const minValue = this.wsMinTimeout;
const maxValue = this.wsTimeout;
return new import_backo2.default({
min: minValue,
max: maxValue,
factor: 1.2
});
}
clearCheckConnectionInterval() {
if (this.checkConnectionIntervalId) {
clearInterval(this.checkConnectionIntervalId);
this.checkConnectionIntervalId = null;
}
clearInactivityTimeout() {
if (this.inactivityTimeoutId) {
clearTimeout(this.inactivityTimeoutId);
this.inactivityTimeoutId = null;
}
}
clearMaxConnectTimeout() {
if (this.maxConnectTimeoutId) {
clearTimeout(this.maxConnectTimeoutId);
this.maxConnectTimeoutId = null;
}
setInactivityTimeout() {
if (this.inactivityTimeout > 0 && Object.keys(this.operations).length === 0) {
this.inactivityTimeoutId = setTimeout(() => {
if (Object.keys(this.operations).length === 0) {
this.close();
}
}, this.inactivityTimeout);
}
}
clearTryReconnectTimeout() {
if (this.tryReconnectTimeoutId) {
clearTimeout(this.tryReconnectTimeoutId);
this.tryReconnectTimeoutId = null;
}
checkOperationOptions(options, handler) {
const {query, variables, operationName} = options;
if (!query) {
throw new Error("Must provide a query.");
}
if (!handler) {
throw new Error("Must provide an handler.");
}
if (!isString(query) || operationName && !isString(operationName) || variables && !isObject(variables)) {
throw new Error("Incorrect option types. query must be a string,`operationName` must be a string, and `variables` must be an object.");
}
}
clearInactivityTimeout() {
if (this.inactivityTimeoutId) {
clearTimeout(this.inactivityTimeoutId);
this.inactivityTimeoutId = null;
}
buildMessage(id, type, payload) {
const payloadToReturn = payload && payload.query ? Object.assign({}, payload, {
query: payload.query
}) : payload;
return {
id,
type,
payload: payloadToReturn
};
}
formatErrors(errors) {
if (Array.isArray(errors)) {
return errors;
}
if (errors && errors.errors) {
return this.formatErrors(errors.errors);
}
if (errors && errors.message) {
return [errors];
}
return [
{
name: "FormatedError",
message: "Unknown error",
originalError: errors
}
setInactivityTimeout() {
if (this.inactivityTimeout > 0 && Object.keys(this.operations).length === 0) {
this.inactivityTimeoutId = setTimeout(() => {
if (Object.keys(this.operations).length === 0) {
this.close();
}
];
}, this.inactivityTimeout);
}
sendMessage(id, type, payload) {
this.sendMessageRaw(this.buildMessage(id, type, payload));
}
checkOperationOptions(options, handler) {
const {query, variables, operationName} = options;
if (!query) {
throw new Error("Must provide a query.");
}
sendMessageRaw(message) {
switch (this.status) {
case this.wsImpl.OPEN:
const serializedMessage = JSON.stringify(message);
try {
JSON.parse(serializedMessage);
} catch (error) {
this.eventEmitter.emit("error", new Error(`Message must be JSON-serializable. Got: ${message}`));
}
this.client.send(serializedMessage);
break;
case this.wsImpl.CONNECTING:
this.unsentMessagesQueue.push(message);
break;
default:
if (!this.reconnecting) {
this.eventEmitter.emit("error", new Error("A message was not sent because socket is not connected, is closing or is already closed. Message was: " + JSON.stringify(message)));
}
}
if (!handler) {
throw new Error("Must provide an handler.");
}
generateOperationId() {
return String(++this.nextOperationId);
if (!isString(query) || operationName && !isString(operationName) || variables && !isObject(variables)) {
throw new Error("Incorrect option types. query must be a string,`operationName` must be a string, and `variables` must be an object.");
}
tryReconnect() {
if (!this.reconnect || this.backoff.attempts >= this.reconnectionAttempts) {
return;
}
buildMessage(id, type, payload) {
const payloadToReturn = payload && payload.query ? Object.assign({}, payload, {
query: payload.query
}) : payload;
return {
id,
type,
payload: payloadToReturn
};
}
formatErrors(errors) {
if (Array.isArray(errors)) {
return errors;
}
if (errors && errors.errors) {
return this.formatErrors(errors.errors);
}
if (errors && errors.message) {
return [errors];
}
return [
{
name: "FormatedError",
message: "Unknown error",
originalError: errors
}
if (!this.reconnecting) {
Object.keys(this.operations).forEach((key) => {
this.unsentMessagesQueue.push(this.buildMessage(key, "start", this.operations[key].options));
});
this.reconnecting = true;
}
this.clearTryReconnectTimeout();
const delay = this.backoff.duration();
this.tryReconnectTimeoutId = setTimeout(() => {
this.connect();
}, delay);
];
}
sendMessage(id, type, payload) {
this.sendMessageRaw(this.buildMessage(id, type, payload));
}
sendMessageRaw(message) {
switch (this.status) {
case this.wsImpl.OPEN:
const serializedMessage = JSON.stringify(message);
try {
JSON.parse(serializedMessage);
} catch (error) {
this.eventEmitter.emit("error", new Error(`Message must be JSON-serializable. Got: ${message}`));
}
this.client.send(serializedMessage);
break;
case this.wsImpl.CONNECTING:
this.unsentMessagesQueue.push(message);
break;
default:
if (!this.reconnecting) {
this.eventEmitter.emit("error", new Error("A message was not sent because socket is not connected, is closing or is already closed. Message was: " + JSON.stringify(message)));
}
}
flushUnsentMessagesQueue() {
this.unsentMessagesQueue.forEach((message) => {
this.sendMessageRaw(message);
}
generateOperationId() {
return String(++this.nextOperationId);
}
tryReconnect() {
if (!this.reconnect || this.backoff.attempts >= this.reconnectionAttempts) {
return;
}
if (!this.reconnecting) {
Object.keys(this.operations).forEach((key) => {
this.unsentMessagesQueue.push(this.buildMessage(key, "start", this.operations[key].options));
});
this.unsentMessagesQueue = [];
this.reconnecting = true;
}
checkConnection() {
if (this.wasKeepAliveReceived) {
this.wasKeepAliveReceived = false;
return;
}
if (!this.reconnecting) {
this.clearTryReconnectTimeout();
const delay = this.backoff.duration();
this.tryReconnectTimeoutId = setTimeout(() => {
this.connect();
}, delay);
}
flushUnsentMessagesQueue() {
this.unsentMessagesQueue.forEach((message) => {
this.sendMessageRaw(message);
});
this.unsentMessagesQueue = [];
}
checkConnection() {
if (this.wasKeepAliveReceived) {
this.wasKeepAliveReceived = false;
return;
}
if (!this.reconnecting) {
this.close(false, true);
}
}
checkMaxConnectTimeout() {
this.clearMaxConnectTimeout();
this.maxConnectTimeoutId = setTimeout(() => {
if (this.status !== this.wsImpl.OPEN) {
this.reconnecting = true;
this.close(false, true);
}
}
checkMaxConnectTimeout() {
this.clearMaxConnectTimeout();
this.maxConnectTimeoutId = setTimeout(() => {
if (this.status !== this.wsImpl.OPEN) {
this.reconnecting = true;
this.close(false, true);
}, this.maxConnectTimeGenerator.duration());
}
connect() {
this.client = new WebSocket(this.url, "graphql-ws");
this.checkMaxConnectTimeout();
this.client.addEventListener("open", async () => {
if (this.status === this.wsImpl.OPEN) {
this.clearMaxConnectTimeout();
this.closedByUser = false;
this.eventEmitter.emit(this.reconnecting ? "reconnecting" : "connecting");
try {
const connectionParams = await this.connectionParams();
this.sendMessage(void 0, "connection_init", connectionParams);
this.flushUnsentMessagesQueue();
} catch (error) {
this.sendMessage(void 0, "connection_error", error);
this.flushUnsentMessagesQueue();
}
}, this.maxConnectTimeGenerator.duration());
}
connect() {
this.client = new WebSocket(this.url, "graphql-ws");
this.checkMaxConnectTimeout();
this.client.addEventListener("open", async () => {
if (this.status === this.wsImpl.OPEN) {
this.clearMaxConnectTimeout();
this.closedByUser = false;
this.eventEmitter.emit(this.reconnecting ? "reconnecting" : "connecting");
try {
const connectionParams = await this.connectionParams();
this.sendMessage(void 0, "connection_init", connectionParams);
this.flushUnsentMessagesQueue();
} catch (error) {
this.sendMessage(void 0, "connection_error", error);
this.flushUnsentMessagesQueue();
}
}
});
this.client.onclose = () => {
if (!this.closedByUser) {
this.close(false, false);
}
};
this.client.addEventListener("error", (error) => {
this.eventEmitter.emit("error", error);
});
this.client.addEventListener("message", ({data}) => {
this.processReceivedData(data);
});
}
processReceivedData(receivedData) {
}
});
this.client.onclose = () => {
if (!this.closedByUser) {
this.close(false, false);
}
};
this.client.addEventListener("error", (error) => {
this.eventEmitter.emit("error", error);
});
this.client.addEventListener("message", ({data}) => {
let parsedMessage;
let opId;
try {
parsedMessage = JSON.parse(receivedData);
opId = parsedMessage.id;
parsedMessage = JSON.parse(data);
} catch (error) {
throw new Error(`Message must be JSON-parseable. Got: ${receivedData}`);
throw new Error(`Message must be JSON-parseable. Got: ${data}`);
}
if (["data", "complete", "error"].includes(parsedMessage.type) && !this.operations[opId]) {
this.unsubscribe(opId);
return;
if (Array.isArray(parsedMessage)) {
for (const message of parsedMessage) {
this.processReceivedMessage(message);
}
} else {
this.processReceivedMessage(parsedMessage);
}
switch (parsedMessage.type) {
case "connection_error":
if (this.connectionCallback) {
this.connectionCallback(parsedMessage.payload);
}
break;
case "connection_ack":
this.eventEmitter.emit(this.reconnecting ? "reconnected" : "connected");
this.reconnecting = false;
this.backoff.reset();
this.maxConnectTimeGenerator.reset();
if (this.connectionCallback) {
this.connectionCallback();
}
break;
case "complete":
this.operations[opId].handler(null, null);
delete this.operations[opId];
break;
case "error":
this.operations[opId].handler(this.formatErrors(parsedMessage.payload), null);
delete this.operations[opId];
break;
case "data":
const parsedPayload = !parsedMessage.payload.errors ? parsedMessage.payload : {
...parsedMessage.payload,
errors: this.formatErrors(parsedMessage.payload.errors)
};
this.operations[opId].handler(null, parsedPayload);
break;
case "ka":
const firstKA = typeof this.wasKeepAliveReceived === "undefined";
this.wasKeepAliveReceived = true;
if (firstKA) {
this.checkConnection();
}
if (this.checkConnectionIntervalId) {
clearInterval(this.checkConnectionIntervalId);
this.checkConnection();
}
this.checkConnectionIntervalId = setInterval(this.checkConnection.bind(this), this.wsTimeout);
break;
default:
throw new Error("Invalid message type!");
}
});
}
processReceivedMessage(message) {
const opId = message.id;
if (["data", "complete", "error"].includes(message.type) && !this.operations[opId]) {
this.unsubscribe(opId);
return;
}
unsubscribe(opId) {
if (this.operations[opId]) {
switch (message.type) {
case "connection_error":
if (this.connectionCallback) {
this.connectionCallback(message.payload);
}
break;
case "connection_ack":
this.eventEmitter.emit(this.reconnecting ? "reconnected" : "connected");
this.reconnecting = false;
this.backoff.reset();
this.maxConnectTimeGenerator.reset();
if (this.connectionCallback) {
this.connectionCallback();
}
break;
case "complete":
this.operations[opId].handler(null, null);
delete this.operations[opId];
this.setInactivityTimeout();
this.sendMessage(opId, "stop", void 0);
}
break;
case "error":
this.operations[opId].handler(this.formatErrors(message.payload), null);
delete this.operations[opId];
break;
case "data":
const parsedPayload = !message.payload.errors ? message.payload : {
...message.payload,
errors: this.formatErrors(message.payload.errors)
};
this.operations[opId].handler(null, parsedPayload);
break;
case "ka":
const firstKA = typeof this.wasKeepAliveReceived === "undefined";
this.wasKeepAliveReceived = true;
if (firstKA) {
this.checkConnection();
}
if (this.checkConnectionIntervalId) {
clearInterval(this.checkConnectionIntervalId);
this.checkConnection();
}
this.checkConnectionIntervalId = setInterval(this.checkConnection.bind(this), this.wsTimeout);
break;
default:
throw new Error("Invalid message type!");
}
}
});
require_src();
unsubscribe(opId) {
if (this.operations[opId]) {
delete this.operations[opId];
this.setInactivityTimeout();
this.sendMessage(opId, "stop", void 0);
}
}
};
})();

@@ -38,14 +38,14 @@ import Backoff from 'backo2';

private wsImpl;
private connectionCallback;
private url;
private operations;
private readonly connectionCallback;
private readonly url;
private readonly operations;
private nextOperationId;
private wsMinTimeout;
private wsTimeout;
private readonly wsMinTimeout;
private readonly wsTimeout;
private unsentMessagesQueue;
private reconnect;
private readonly reconnect;
private reconnecting;
private reconnectionAttempts;
private lazy;
private inactivityTimeout;
private readonly reconnectionAttempts;
private readonly lazy;
private readonly inactivityTimeout;
private closedByUser;

@@ -56,3 +56,3 @@ private backoff;

private maxConnectTimeGenerator;
private connectionParams;
private readonly connectionParams;
private checkConnectionIntervalId;

@@ -63,3 +63,3 @@ private maxConnectTimeoutId;

private wasKeepAliveReceived;
constructor(url: string, options: ClientOptions);
constructor(url: string, options?: ClientOptions);
get status(): number;

@@ -109,3 +109,3 @@ close(isForced?: boolean, closedByUser?: boolean): void;

connect(): void;
processReceivedData(receivedData: string): void;
processReceivedMessage(message: any): void;
unsubscribe(opId: string): void;

@@ -112,0 +112,0 @@ }

@@ -5,7 +5,7 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/index.ts

var _symbolobservable = require('symbol-observable'); var _symbolobservable2 = _interopRequireDefault(_symbolobservable);
const WS_MINTIMEOUT = 1e3;
const WS_TIMEOUT = 3e4;
const isString = (value) => typeof value === "string";
const isObject = (value) => value !== null && typeof value === "object";
class SubscriptionClient {
var WS_MIN_TIMEOUT = 1e3;
var WS_TIMEOUT = 3e4;
var isString = (value) => typeof value === "string";
var isObject = (value) => value !== null && typeof value === "object";
var SubscriptionClient = class {
constructor(url, options) {

@@ -15,3 +15,3 @@ const {

connectionParams = {},
minTimeout = WS_MINTIMEOUT,
minTimeout = WS_MIN_TIMEOUT,
timeout = WS_TIMEOUT,

@@ -355,22 +355,27 @@ reconnect = false,

this.client.addEventListener("message", ({data}) => {
this.processReceivedData(data);
let parsedMessage;
try {
parsedMessage = JSON.parse(data);
} catch (error) {
throw new Error(`Message must be JSON-parseable. Got: ${data}`);
}
if (Array.isArray(parsedMessage)) {
for (const message of parsedMessage) {
this.processReceivedMessage(message);
}
} else {
this.processReceivedMessage(parsedMessage);
}
});
}
processReceivedData(receivedData) {
let parsedMessage;
let opId;
try {
parsedMessage = JSON.parse(receivedData);
opId = parsedMessage.id;
} catch (error) {
throw new Error(`Message must be JSON-parseable. Got: ${receivedData}`);
}
if (["data", "complete", "error"].includes(parsedMessage.type) && !this.operations[opId]) {
processReceivedMessage(message) {
const opId = message.id;
if (["data", "complete", "error"].includes(message.type) && !this.operations[opId]) {
this.unsubscribe(opId);
return;
}
switch (parsedMessage.type) {
switch (message.type) {
case "connection_error":
if (this.connectionCallback) {
this.connectionCallback(parsedMessage.payload);
this.connectionCallback(message.payload);
}

@@ -392,9 +397,9 @@ break;

case "error":
this.operations[opId].handler(this.formatErrors(parsedMessage.payload), null);
this.operations[opId].handler(this.formatErrors(message.payload), null);
delete this.operations[opId];
break;
case "data":
const parsedPayload = !parsedMessage.payload.errors ? parsedMessage.payload : {
...parsedMessage.payload,
errors: this.formatErrors(parsedMessage.payload.errors)
const parsedPayload = !message.payload.errors ? message.payload : {
...message.payload,
errors: this.formatErrors(message.payload.errors)
};

@@ -426,5 +431,5 @@ this.operations[opId].handler(null, parsedPayload);

}
}
};
exports.SubscriptionClient = SubscriptionClient;
{
"name": "graphql-subscriptions-client",
"version": "0.15.0",
"version": "0.16.0",
"description": "A simpler client for graphql subscriptions based on apollographql/subscriptions-transport-ws",

@@ -13,3 +13,3 @@ "module": "dist/esm/index.js",

"prepack": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1"
"test": "jest --runInBand"
},

@@ -31,11 +31,15 @@ "keywords": [

"eventemitter3": "^3.1.2",
"symbol-observable": "^1.2.0",
"ws": "^7.3.1"
"symbol-observable": "^1.2.0"
},
"devDependencies": {
"@types/backo2": "^1.0.1",
"@types/ws": "^7.2.7",
"tsup": "^3.7.0",
"typescript": "^4.0.3"
"@types/jest": "^26.0.20",
"@types/ws": "^7.4.0",
"jest": "^26.6.3",
"jest-websocket-mock": "^2.2.0",
"mock-socket": "^9.0.3",
"ts-jest": "^26.4.4",
"tsup": "^3.11.0",
"typescript": "^4.1.3"
}
}

@@ -19,4 +19,11 @@ # graphql-subscriptions-client

If you have a apollo-server instance you can use this for subscriptions only, pass all requests over the websocket. The API is similar to what's described at [subscriptions-transport-ws docs](https://github.com/apollographql/subscriptions-transport-ws#api-docs) except that it doesn't support middleware and requires queries to be strings.
If you have a apollo-server instance you can use this for subscriptions only, pass all requests over the websocket.
The API is similar to what's described at [subscriptions-transport-ws docs](https://github.com/apollographql/subscriptions-transport-ws#api-docs) except that it doesn't support middleware and requires queries to be strings.
Also, this client supports batch messages as arrays from the server, and they will be processed as if they were received one after another, for example:
```
[{ id: "1", type: "data", ... }, { id: "1", type: "complete" }]
```
```js

@@ -23,0 +30,0 @@ import { SubscriptionClient } from 'graphql-subscriptions-client';

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