Socket
Socket
Sign inDemoInstall

graphql-http-ws-client

Package Overview
Dependencies
Maintainers
1
Versions
20
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-http-ws-client - npm Package Compare versions

Comparing version 2.1.0 to 3.0.0

83

dist/exports.d.ts

@@ -11,4 +11,7 @@ import { InMemoryCache } from '@apollo/client/cache/cache.cjs';

import { HttpLink } from '@apollo/client/link/http/http.cjs';
import { setContext } from '@apollo/client/link/context/context.cjs';
import { WebSocketLink } from '@apollo/client/link/ws/ws.cjs';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions/subscriptions.cjs';
import { SubscriptionClient } from 'subscriptions-transport-ws';
import { createClient } from 'graphql-ws';

@@ -67,7 +70,7 @@ class ApolloClientWithGQL extends ApolloClient {

var createGraphQLLinks = (graphQLURL, passedOptions) => {
const options = {
var createGraphQLLinks = (graphQLURL, options) => {
const mergedOptions = {
createHTTPLink: true,
createWSLink: true,
...passedOptions
...options
};

@@ -80,6 +83,11 @@

let httpLink = null;
if(options.createHTTPLink) {
if(mergedOptions.createHTTPLink) {
const authLink = mergedOptions.httpLinkOptions?.setContext
? setContext(mergedOptions.httpLinkOptions?.setContext)
: undefined;
const httpLinkOptions = {
uri: graphQLURL,
...options.httpLinkOptions
...mergedOptions.httpLinkOptions
};

@@ -90,3 +98,5 @@

httpLink = new HttpLink(httpLinkOptions);
httpLink = authLink
? authLink.concat(new HttpLink(httpLinkOptions)) // add auth link before http link
: new HttpLink(httpLinkOptions); // no auth link
}

@@ -96,6 +106,35 @@

let subscriptionClient = null;
if(options.createWSLink) {
if(mergedOptions.createWSLink) {
const wsSubprotocol = mergedOptions.wsSubprotocol
? mergedOptions.wsSubprotocol.toLowerCase()
: 'graphql-ws';
const wsLinkOptions = {
reconnect: true,
...options.wsLinkOptions
...mergedOptions.wsLinkOptions,
// override connectionParams to add subprotocol header
connectionParams: async (...args) => { // add subprotocol
console.log("CP");
const params = mergedOptions.wsLinkOptions?.connectionParams
? await mergedOptions.wsLinkOptions?.connectionParams(...args)
: {};
// merge in subprotocol headers
console.log("SENDING", {
...params,
headers: {
"sec-websocket-protocol": wsSubprotocol,
...params.headers
}
});
return {
...params,
headers: {
"sec-websocket-protocol": wsSubprotocol,
...params.headers
}
}
}
};

@@ -106,7 +145,20 @@

const websocketImplementation = (typeof options.websocket === 'function') ? options.websocket : window.WebSocket;
const websocketImplementation = typeof options.websocket === 'function'
? options.websocket
: window.WebSocket;
subscriptionClient = new SubscriptionClient(httpURLToWS(graphQLURL), wsLinkOptions, websocketImplementation);
wsLink = new WebSocketLink(subscriptionClient);
if(wsSubprotocol === 'graphql-transport-ws') { // graphql-transport-ws, from graphql-ws
subscriptionClient = createClient({
webSocketImpl: websocketImplementation,
url: httpURLToWS(graphQLURL),
...wsLinkOptions
});
wsLink = new GraphQLWsLink(subscriptionClient);
}
else if(wsSubprotocol === 'graphql-ws') { // graphql-ws, from subscriptions-transport-ws
subscriptionClient = new SubscriptionClient(httpURLToWS(graphQLURL), wsLinkOptions, websocketImplementation);
wsLink = new WebSocketLink(subscriptionClient);
}
else
throw new Error(`Unknown wsSubprotocol`);
}

@@ -121,3 +173,4 @@

wsLink,
httpLink);
httpLink
);
}

@@ -138,3 +191,5 @@ else if(wsLink !== null) // only websocketLink exists

return {
link: options.hasOwnProperty('middleware') ? concat(options.middleware, link) : link,
link: options.hasOwnProperty('middleware')
? concat(options.middleware, link)
: link,
httpLink: httpLink,

@@ -141,0 +196,0 @@ wsLink: wsLink,

@@ -683,9 +683,543 @@ var __create = Object.create;

import { HttpLink } from "@apollo/client/link/http/http.cjs";
import { setContext } from "@apollo/client/link/context/context.cjs";
import { WebSocketLink } from "@apollo/client/link/ws/ws.cjs";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions/subscriptions.cjs";
import { SubscriptionClient } from "subscriptions-transport-ws";
var createGraphQLLinks_default = (graphQLURL, passedOptions) => {
const options = __spreadValues({
// node_modules/graphql-ws/lib/client.mjs
init_esm_shims();
// node_modules/graphql-ws/lib/common.mjs
init_esm_shims();
// node_modules/graphql-ws/lib/utils.mjs
init_esm_shims();
function extendedTypeof(val) {
if (val === null) {
return "null";
}
if (Array.isArray(val)) {
return "array";
}
return typeof val;
}
function isObject(val) {
return extendedTypeof(val) === "object";
}
function areGraphQLErrors(obj) {
return Array.isArray(obj) && obj.length > 0 && obj.every((ob) => "message" in ob);
}
function limitCloseReason(reason, whenTooLong) {
return reason.length < 124 ? reason : whenTooLong;
}
// node_modules/graphql-ws/lib/common.mjs
var GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws";
var CloseCode;
(function(CloseCode2) {
CloseCode2[CloseCode2["InternalServerError"] = 4500] = "InternalServerError";
CloseCode2[CloseCode2["InternalClientError"] = 4005] = "InternalClientError";
CloseCode2[CloseCode2["BadRequest"] = 4400] = "BadRequest";
CloseCode2[CloseCode2["BadResponse"] = 4004] = "BadResponse";
CloseCode2[CloseCode2["Unauthorized"] = 4401] = "Unauthorized";
CloseCode2[CloseCode2["Forbidden"] = 4403] = "Forbidden";
CloseCode2[CloseCode2["SubprotocolNotAcceptable"] = 4406] = "SubprotocolNotAcceptable";
CloseCode2[CloseCode2["ConnectionInitialisationTimeout"] = 4408] = "ConnectionInitialisationTimeout";
CloseCode2[CloseCode2["ConnectionAcknowledgementTimeout"] = 4504] = "ConnectionAcknowledgementTimeout";
CloseCode2[CloseCode2["SubscriberAlreadyExists"] = 4409] = "SubscriberAlreadyExists";
CloseCode2[CloseCode2["TooManyInitialisationRequests"] = 4429] = "TooManyInitialisationRequests";
})(CloseCode || (CloseCode = {}));
var MessageType;
(function(MessageType2) {
MessageType2["ConnectionInit"] = "connection_init";
MessageType2["ConnectionAck"] = "connection_ack";
MessageType2["Ping"] = "ping";
MessageType2["Pong"] = "pong";
MessageType2["Subscribe"] = "subscribe";
MessageType2["Next"] = "next";
MessageType2["Error"] = "error";
MessageType2["Complete"] = "complete";
})(MessageType || (MessageType = {}));
function validateMessage(val) {
if (!isObject(val)) {
throw new Error(`Message is expected to be an object, but got ${extendedTypeof(val)}`);
}
if (!val.type) {
throw new Error(`Message is missing the 'type' property`);
}
if (typeof val.type !== "string") {
throw new Error(`Message is expects the 'type' property to be a string, but got ${extendedTypeof(val.type)}`);
}
switch (val.type) {
case MessageType.ConnectionInit:
case MessageType.ConnectionAck:
case MessageType.Ping:
case MessageType.Pong: {
if ("payload" in val && !isObject(val.payload)) {
throw new Error(`"${val.type}" message expects the 'payload' property to be an object or missing, but got "${val.payload}"`);
}
break;
}
case MessageType.Subscribe: {
if (typeof val.id !== "string") {
throw new Error(`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(val.id)}`);
}
if (!val.id) {
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
}
if (!isObject(val.payload)) {
throw new Error(`"${val.type}" message expects the 'payload' property to be an object, but got ${extendedTypeof(val.payload)}`);
}
if (typeof val.payload.query !== "string") {
throw new Error(`"${val.type}" message payload expects the 'query' property to be a string, but got ${extendedTypeof(val.payload.query)}`);
}
if (val.payload.variables != null && !isObject(val.payload.variables)) {
throw new Error(`"${val.type}" message payload expects the 'variables' property to be a an object or nullish or missing, but got ${extendedTypeof(val.payload.variables)}`);
}
if (val.payload.operationName != null && extendedTypeof(val.payload.operationName) !== "string") {
throw new Error(`"${val.type}" message payload expects the 'operationName' property to be a string or nullish or missing, but got ${extendedTypeof(val.payload.operationName)}`);
}
if (val.payload.extensions != null && !isObject(val.payload.extensions)) {
throw new Error(`"${val.type}" message payload expects the 'extensions' property to be a an object or nullish or missing, but got ${extendedTypeof(val.payload.extensions)}`);
}
break;
}
case MessageType.Next: {
if (typeof val.id !== "string") {
throw new Error(`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(val.id)}`);
}
if (!val.id) {
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
}
if (!isObject(val.payload)) {
throw new Error(`"${val.type}" message expects the 'payload' property to be an object, but got ${extendedTypeof(val.payload)}`);
}
break;
}
case MessageType.Error: {
if (typeof val.id !== "string") {
throw new Error(`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(val.id)}`);
}
if (!val.id) {
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
}
if (!areGraphQLErrors(val.payload)) {
throw new Error(`"${val.type}" message expects the 'payload' property to be an array of GraphQL errors, but got ${JSON.stringify(val.payload)}`);
}
break;
}
case MessageType.Complete: {
if (typeof val.id !== "string") {
throw new Error(`"${val.type}" message expects the 'id' property to be a string, but got ${extendedTypeof(val.id)}`);
}
if (!val.id) {
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
}
break;
}
default:
throw new Error(`Invalid message 'type' property "${val.type}"`);
}
return val;
}
function parseMessage(data, reviver) {
try {
return validateMessage(data);
} catch (_a) {
if (typeof data !== "string") {
throw new Error("Only strings are parsable messages");
}
const message = JSON.parse(data, reviver);
return validateMessage(message);
}
}
function stringifyMessage(msg, replacer) {
validateMessage(msg);
return JSON.stringify(msg, replacer);
}
// node_modules/graphql-ws/lib/client.mjs
function createClient(options) {
const {
url,
connectionParams,
lazy = true,
onNonLazyError = console.error,
lazyCloseTimeout = 0,
keepAlive = 0,
disablePong,
connectionAckWaitTimeout = 0,
retryAttempts = 5,
retryWait = async function randomisedExponentialBackoff(retries2) {
let retryDelay = 1e3;
for (let i = 0; i < retries2; i++) {
retryDelay *= 2;
}
await new Promise((resolve) => setTimeout(resolve, retryDelay + Math.floor(Math.random() * (3e3 - 300) + 300)));
},
shouldRetry = isLikeCloseEvent,
isFatalConnectionProblem,
on,
webSocketImpl,
generateID = function generateUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0, v = c == "x" ? r : r & 3 | 8;
return v.toString(16);
});
},
jsonMessageReplacer: replacer,
jsonMessageReviver: reviver
} = options;
let ws;
if (webSocketImpl) {
if (!isWebSocket(webSocketImpl)) {
throw new Error("Invalid WebSocket implementation provided");
}
ws = webSocketImpl;
} else if (typeof WebSocket !== "undefined") {
ws = WebSocket;
} else if (typeof global !== "undefined") {
ws = global.WebSocket || global.MozWebSocket;
} else if (typeof window !== "undefined") {
ws = window.WebSocket || window.MozWebSocket;
}
if (!ws)
throw new Error("WebSocket implementation missing; on Node you can `import WebSocket from 'ws';` and pass `webSocketImpl: WebSocket` to `createClient`");
const WebSocketImpl = ws;
const emitter = (() => {
const message = (() => {
const listeners2 = {};
return {
on(id, listener) {
listeners2[id] = listener;
return () => {
delete listeners2[id];
};
},
emit(message2) {
var _a;
if ("id" in message2)
(_a = listeners2[message2.id]) === null || _a === void 0 ? void 0 : _a.call(listeners2, message2);
}
};
})();
const listeners = {
connecting: (on === null || on === void 0 ? void 0 : on.connecting) ? [on.connecting] : [],
opened: (on === null || on === void 0 ? void 0 : on.opened) ? [on.opened] : [],
connected: (on === null || on === void 0 ? void 0 : on.connected) ? [on.connected] : [],
ping: (on === null || on === void 0 ? void 0 : on.ping) ? [on.ping] : [],
pong: (on === null || on === void 0 ? void 0 : on.pong) ? [on.pong] : [],
message: (on === null || on === void 0 ? void 0 : on.message) ? [message.emit, on.message] : [message.emit],
closed: (on === null || on === void 0 ? void 0 : on.closed) ? [on.closed] : [],
error: (on === null || on === void 0 ? void 0 : on.error) ? [on.error] : []
};
return {
onMessage: message.on,
on(event, listener) {
const l = listeners[event];
l.push(listener);
return () => {
l.splice(l.indexOf(listener), 1);
};
},
emit(event, ...args) {
for (const listener of [...listeners[event]]) {
listener(...args);
}
}
};
})();
function errorOrClosed(cb) {
const listening = [
emitter.on("error", (err) => {
listening.forEach((unlisten) => unlisten());
cb(err);
}),
emitter.on("closed", (event) => {
listening.forEach((unlisten) => unlisten());
cb(event);
})
];
}
let connecting, locks = 0, retrying = false, retries = 0, disposed = false;
async function connect() {
const [socket, throwOnClose] = await (connecting !== null && connecting !== void 0 ? connecting : connecting = new Promise((connected, denied) => (async () => {
if (retrying) {
await retryWait(retries);
if (!locks) {
connecting = void 0;
return denied({ code: 1e3, reason: "All Subscriptions Gone" });
}
retries++;
}
emitter.emit("connecting");
const socket2 = new WebSocketImpl(typeof url === "function" ? await url() : url, GRAPHQL_TRANSPORT_WS_PROTOCOL);
let connectionAckTimeout, queuedPing;
function enqueuePing() {
if (isFinite(keepAlive) && keepAlive > 0) {
clearTimeout(queuedPing);
queuedPing = setTimeout(() => {
if (socket2.readyState === WebSocketImpl.OPEN) {
socket2.send(stringifyMessage({ type: MessageType.Ping }));
emitter.emit("ping", false, void 0);
}
}, keepAlive);
}
}
errorOrClosed((errOrEvent) => {
connecting = void 0;
clearTimeout(connectionAckTimeout);
clearTimeout(queuedPing);
denied(errOrEvent);
if (isLikeCloseEvent(errOrEvent) && errOrEvent.code === 4499) {
socket2.close(4499, "Terminated");
socket2.onerror = null;
socket2.onclose = null;
}
});
socket2.onerror = (err) => emitter.emit("error", err);
socket2.onclose = (event) => emitter.emit("closed", event);
socket2.onopen = async () => {
try {
emitter.emit("opened", socket2);
const payload = typeof connectionParams === "function" ? await connectionParams() : connectionParams;
if (socket2.readyState !== WebSocketImpl.OPEN)
return;
socket2.send(stringifyMessage(payload ? {
type: MessageType.ConnectionInit,
payload
} : {
type: MessageType.ConnectionInit
}, replacer));
if (isFinite(connectionAckWaitTimeout) && connectionAckWaitTimeout > 0) {
connectionAckTimeout = setTimeout(() => {
socket2.close(CloseCode.ConnectionAcknowledgementTimeout, "Connection acknowledgement timeout");
}, connectionAckWaitTimeout);
}
enqueuePing();
} catch (err) {
emitter.emit("error", err);
socket2.close(CloseCode.InternalClientError, limitCloseReason(err instanceof Error ? err.message : new Error(err).message, "Internal client error"));
}
};
let acknowledged = false;
socket2.onmessage = ({ data }) => {
try {
const message = parseMessage(data, reviver);
emitter.emit("message", message);
if (message.type === "ping" || message.type === "pong") {
emitter.emit(message.type, true, message.payload);
if (message.type === "pong") {
enqueuePing();
} else if (!disablePong) {
socket2.send(stringifyMessage(message.payload ? {
type: MessageType.Pong,
payload: message.payload
} : {
type: MessageType.Pong
}));
emitter.emit("pong", false, message.payload);
}
return;
}
if (acknowledged)
return;
if (message.type !== MessageType.ConnectionAck)
throw new Error(`First message cannot be of type ${message.type}`);
clearTimeout(connectionAckTimeout);
acknowledged = true;
emitter.emit("connected", socket2, message.payload);
retrying = false;
retries = 0;
connected([
socket2,
new Promise((_, reject) => errorOrClosed(reject))
]);
} catch (err) {
socket2.onmessage = null;
emitter.emit("error", err);
socket2.close(CloseCode.BadResponse, limitCloseReason(err instanceof Error ? err.message : new Error(err).message, "Bad response"));
}
};
})()));
if (socket.readyState === WebSocketImpl.CLOSING)
await throwOnClose;
let release = () => {
};
const released = new Promise((resolve) => release = resolve);
return [
socket,
release,
Promise.race([
released.then(() => {
if (!locks) {
const complete = () => socket.close(1e3, "Normal Closure");
if (isFinite(lazyCloseTimeout) && lazyCloseTimeout > 0) {
setTimeout(() => {
if (!locks && socket.readyState === WebSocketImpl.OPEN)
complete();
}, lazyCloseTimeout);
} else {
complete();
}
}
}),
throwOnClose
])
];
}
function shouldRetryConnectOrThrow(errOrCloseEvent) {
if (isLikeCloseEvent(errOrCloseEvent) && (isFatalInternalCloseCode(errOrCloseEvent.code) || [
CloseCode.InternalServerError,
CloseCode.InternalClientError,
CloseCode.BadRequest,
CloseCode.BadResponse,
CloseCode.Unauthorized,
CloseCode.SubprotocolNotAcceptable,
CloseCode.SubscriberAlreadyExists,
CloseCode.TooManyInitialisationRequests
].includes(errOrCloseEvent.code)))
throw errOrCloseEvent;
if (disposed)
return false;
if (isLikeCloseEvent(errOrCloseEvent) && errOrCloseEvent.code === 1e3)
return locks > 0;
if (!retryAttempts || retries >= retryAttempts)
throw errOrCloseEvent;
if (!shouldRetry(errOrCloseEvent))
throw errOrCloseEvent;
if (isFatalConnectionProblem === null || isFatalConnectionProblem === void 0 ? void 0 : isFatalConnectionProblem(errOrCloseEvent))
throw errOrCloseEvent;
return retrying = true;
}
if (!lazy) {
(async () => {
locks++;
for (; ; ) {
try {
const [, , throwOnClose] = await connect();
await throwOnClose;
} catch (errOrCloseEvent) {
try {
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
return;
} catch (errOrCloseEvent2) {
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent2);
}
}
}
})();
}
return {
on: emitter.on,
subscribe(payload, sink) {
const id = generateID();
let done = false, errored = false, releaser = () => {
locks--;
done = true;
};
(async () => {
locks++;
for (; ; ) {
try {
const [socket, release, waitForReleaseOrThrowOnClose] = await connect();
if (done)
return release();
const unlisten = emitter.onMessage(id, (message) => {
switch (message.type) {
case MessageType.Next: {
sink.next(message.payload);
return;
}
case MessageType.Error: {
errored = true, done = true;
sink.error(message.payload);
releaser();
return;
}
case MessageType.Complete: {
done = true;
releaser();
return;
}
}
});
socket.send(stringifyMessage({
id,
type: MessageType.Subscribe,
payload
}, replacer));
releaser = () => {
if (!done && socket.readyState === WebSocketImpl.OPEN)
socket.send(stringifyMessage({
id,
type: MessageType.Complete
}, replacer));
locks--;
done = true;
release();
};
await waitForReleaseOrThrowOnClose.finally(unlisten);
return;
} catch (errOrCloseEvent) {
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
return;
}
}
})().then(() => {
if (!errored)
sink.complete();
}).catch((err) => {
sink.error(err);
});
return () => {
if (!done)
releaser();
};
},
async dispose() {
disposed = true;
if (connecting) {
const [socket] = await connecting;
socket.close(1e3, "Normal Closure");
}
},
terminate() {
if (connecting) {
emitter.emit("closed", {
code: 4499,
reason: "Terminated",
wasClean: false
});
}
}
};
}
function isLikeCloseEvent(val) {
return isObject(val) && "code" in val && "reason" in val;
}
function isFatalInternalCloseCode(code) {
if ([
1e3,
1001,
1006,
1005,
1012,
1013,
1013
].includes(code))
return false;
return code >= 1e3 && code <= 1999;
}
function isWebSocket(val) {
return typeof val === "function" && "constructor" in val && "CLOSED" in val && "CLOSING" in val && "CONNECTING" in val && "OPEN" in val;
}
// src/createGraphQLLinks.js
var createGraphQLLinks_default = (graphQLURL, options) => {
var _a, _b;
const mergedOptions = __spreadValues({
createHTTPLink: true,
createWSLink: true
}, passedOptions);
}, options);
const httpURLToWS = (url) => {

@@ -695,21 +1229,48 @@ return url.replace(/(http)(s)?:\/\//, "ws$2://");

let httpLink = null;
if (options.createHTTPLink) {
if (mergedOptions.createHTTPLink) {
const authLink = ((_a = mergedOptions.httpLinkOptions) == null ? void 0 : _a.setContext) ? setContext((_b = mergedOptions.httpLinkOptions) == null ? void 0 : _b.setContext) : void 0;
const httpLinkOptions = __spreadValues({
uri: graphQLURL
}, options.httpLinkOptions);
}, mergedOptions.httpLinkOptions);
if (typeof httpLinkOptions.fetch !== "function" && (typeof window !== "object" || typeof window.fetch !== "function"))
throw new Error(`Missing fetch implementation on window.fetch or options.httpLinkOptions.fetch`);
httpLink = new HttpLink(httpLinkOptions);
httpLink = authLink ? authLink.concat(new HttpLink(httpLinkOptions)) : new HttpLink(httpLinkOptions);
}
let wsLink = null;
let subscriptionClient = null;
if (options.createWSLink) {
const wsLinkOptions = __spreadValues({
if (mergedOptions.createWSLink) {
const wsSubprotocol = mergedOptions.wsSubprotocol ? mergedOptions.wsSubprotocol.toLowerCase() : "graphql-ws";
const wsLinkOptions = __spreadProps(__spreadValues({
reconnect: true
}, options.wsLinkOptions);
}, mergedOptions.wsLinkOptions), {
connectionParams: async (...args) => {
var _a2, _b2;
console.log("CP");
const params = ((_a2 = mergedOptions.wsLinkOptions) == null ? void 0 : _a2.connectionParams) ? await ((_b2 = mergedOptions.wsLinkOptions) == null ? void 0 : _b2.connectionParams(...args)) : {};
console.log("SENDING", __spreadProps(__spreadValues({}, params), {
headers: __spreadValues({
"sec-websocket-protocol": wsSubprotocol
}, params.headers)
}));
return __spreadProps(__spreadValues({}, params), {
headers: __spreadValues({
"sec-websocket-protocol": wsSubprotocol
}, params.headers)
});
}
});
if (typeof options.websocket !== "function" && (typeof window !== "object" || typeof window.WebSocket !== "function"))
throw new Error(`Missing websocket implementation on window.WebSocket or options.websocket`);
const websocketImplementation = typeof options.websocket === "function" ? options.websocket : window.WebSocket;
subscriptionClient = new SubscriptionClient(httpURLToWS(graphQLURL), wsLinkOptions, websocketImplementation);
wsLink = new WebSocketLink(subscriptionClient);
if (wsSubprotocol === "graphql-transport-ws") {
subscriptionClient = createClient(__spreadValues({
webSocketImpl: websocketImplementation,
url: httpURLToWS(graphQLURL)
}, wsLinkOptions));
wsLink = new GraphQLWsLink(subscriptionClient);
} else if (wsSubprotocol === "graphql-ws") {
subscriptionClient = new SubscriptionClient(httpURLToWS(graphQLURL), wsLinkOptions, websocketImplementation);
wsLink = new WebSocketLink(subscriptionClient);
} else
throw new Error(`Unknown wsSubprotocol`);
}

@@ -716,0 +1277,0 @@ let transportLink = null;

5

package.json
{
"name": "graphql-http-ws-client",
"version": "2.1.0",
"version": "3.0.0",
"private": false,

@@ -34,3 +34,4 @@ "license": "MIT",

"typescript": "^4.6.4",
"ws": "^8.6.0"
"ws": "^8.6.0",
"graphql-ws": "^5.9.0"
},

@@ -37,0 +38,0 @@ "peerDependencies": {

## GraphQL client over HTTP/WS
### Node.js with HTTP and WS links
### Node.js with HTTP and WS links, and context

@@ -8,7 +8,21 @@ import { createGraphQLClient } from "graphql-http-ws-client";

import fetch from "node-fetch";
let addContext = async () => {
return {
headers: {
Authorization: `Bearer MYAUTHTOKEN`
}
}
};
const { client } = createGraphQLClient("MY_GRAPHQL_URL", {
websocket: WebSocket,
httpLinkOptions: {
fetch: fetch
createWSLink: true,
createHTTPLink: true,
websocket: WebSocket,
httpLinkOptions: {
fetch: fetch,
setContext: addContext
},
wsLinkOptions: {
connectionParams: addContext
}

@@ -39,3 +53,27 @@ });

});
### Node.js with WS link and [graphql-transport-ws subprotocol](https://github.com/enisdenjo/graphql-ws/issues/154)
import { createGraphQLClient } from "graphql-http-ws-client";
import WebSocket from "ws";
import fetch from "node-fetch";
const { client } = createGraphQLClient("MY_GRAPHQL_URL", {
websocket: WebSocket,
wsSubprotocol: "graphql-transport-ws",
createHTTPLink: false
});
### Node.js with WS link and [graphql-ws subprotocol](https://github.com/enisdenjo/graphql-ws/issues/154) (default)
import { createGraphQLClient } from "graphql-ws";
import WebSocket from "ws";
import fetch from "node-fetch";
const { client } = createGraphQLClient("MY_GRAPHQL_URL", {
websocket: WebSocket,
wsSubprotocol: "graphql-ws",
createHTTPLink: false
});
### React with HTTP and WS links

@@ -42,0 +80,0 @@

@@ -5,12 +5,15 @@ import { getMainDefinition } from "@apollo/client/utilities/utilities.cjs";

import { HttpLink } from "@apollo/client/link/http/http.cjs";
import { setContext } from '@apollo/client/link/context/context.cjs';
import { WebSocketLink } from "@apollo/client/link/ws/ws.cjs";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions/subscriptions.cjs";
import { SubscriptionClient } from "subscriptions-transport-ws";
import { createClient } from "graphql-ws";
// TODO allow override of fetch options as in https://www.apollographql.com/docs/link/links/http/
export default (graphQLURL, passedOptions) => {
const options = {
export default (graphQLURL, options) => {
const mergedOptions = {
createHTTPLink: true,
createWSLink: true,
...passedOptions
...options
};

@@ -23,6 +26,11 @@

let httpLink = null;
if(options.createHTTPLink) {
if(mergedOptions.createHTTPLink) {
const authLink = mergedOptions.httpLinkOptions?.setContext
? setContext(mergedOptions.httpLinkOptions?.setContext)
: undefined;
const httpLinkOptions = {
uri: graphQLURL,
...options.httpLinkOptions
...mergedOptions.httpLinkOptions
};

@@ -33,3 +41,5 @@

httpLink = new HttpLink(httpLinkOptions);
httpLink = authLink
? authLink.concat(new HttpLink(httpLinkOptions)) // add auth link before http link
: new HttpLink(httpLinkOptions); // no auth link
}

@@ -39,6 +49,35 @@

let subscriptionClient = null;
if(options.createWSLink) {
if(mergedOptions.createWSLink) {
const wsSubprotocol = mergedOptions.wsSubprotocol
? mergedOptions.wsSubprotocol.toLowerCase()
: 'graphql-ws';
const wsLinkOptions = {
reconnect: true,
...options.wsLinkOptions
...mergedOptions.wsLinkOptions,
// override connectionParams to add subprotocol header
connectionParams: async (...args) => { // add subprotocol
console.log("CP");
const params = mergedOptions.wsLinkOptions?.connectionParams
? await mergedOptions.wsLinkOptions?.connectionParams(...args)
: {};
// merge in subprotocol headers
console.log("SENDING", {
...params,
headers: {
"sec-websocket-protocol": wsSubprotocol,
...params.headers
}
});
return {
...params,
headers: {
"sec-websocket-protocol": wsSubprotocol,
...params.headers
}
}
}
};

@@ -49,7 +88,20 @@

const websocketImplementation = (typeof options.websocket === 'function') ? options.websocket : window.WebSocket;
const websocketImplementation = typeof options.websocket === 'function'
? options.websocket
: window.WebSocket;
subscriptionClient = new SubscriptionClient(httpURLToWS(graphQLURL), wsLinkOptions, websocketImplementation);
wsLink = new WebSocketLink(subscriptionClient);
if(wsSubprotocol === 'graphql-transport-ws') { // graphql-transport-ws, from graphql-ws
subscriptionClient = createClient({
webSocketImpl: websocketImplementation,
url: httpURLToWS(graphQLURL),
...wsLinkOptions
});
wsLink = new GraphQLWsLink(subscriptionClient);
}
else if(wsSubprotocol === 'graphql-ws') { // graphql-ws, from subscriptions-transport-ws
subscriptionClient = new SubscriptionClient(httpURLToWS(graphQLURL), wsLinkOptions, websocketImplementation);
wsLink = new WebSocketLink(subscriptionClient);
}
else
throw new Error(`Unknown wsSubprotocol`);
}

@@ -64,3 +116,4 @@

wsLink,
httpLink);
httpLink
);
}

@@ -81,3 +134,5 @@ else if(wsLink !== null) // only websocketLink exists

return {
link: options.hasOwnProperty('middleware') ? concat(options.middleware, link) : link,
link: options.hasOwnProperty('middleware')
? concat(options.middleware, link)
: link,
httpLink: httpLink,

@@ -89,2 +144,2 @@ wsLink: wsLink,

};
}
}

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc