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

graphql-ws

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

graphql-ws - npm Package Compare versions

Comparing version 5.9.1 to 5.10.0

lib/use/@fastify/websocket.d.ts

4

lib/use/fastify-websocket.d.ts

@@ -8,2 +8,4 @@ import type { FastifyRequest } from 'fastify';

*
* @deprecated Use `@fastify/websocket` instead.
*
* @category Server/fastify-websocket

@@ -27,2 +29,4 @@ */

*
* @deprecated Use `@fastify/websocket` instead.
*
* @category Server/fastify-websocket

@@ -29,0 +33,0 @@ */

111

lib/use/fastify-websocket.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeHandler = void 0;
const server_1 = require("../server");
const common_1 = require("../common");
const utils_1 = require("../utils");
const websocket_1 = require("./@fastify/websocket");
/**

@@ -11,2 +9,4 @@ * Make a handler to use on a [fastify-websocket](https://github.com/fastify/fastify-websocket) route.

*
* @deprecated Use `@fastify/websocket` instead.
*
* @category Server/fastify-websocket

@@ -23,106 +23,5 @@ */

keepAlive = 12000) {
const isProd = process.env.NODE_ENV === 'production';
const server = (0, server_1.makeServer)(options);
// we dont have access to the fastify-websocket server instance yet,
// register an error handler on first connection ONCE only
let handlingServerEmittedErrors = false;
return function handler(connection, request) {
const { socket } = connection;
// might be too late, but meh
this.websocketServer.options.handleProtocols = server_1.handleProtocols;
// handle server emitted errors only if not already handling
if (!handlingServerEmittedErrors) {
handlingServerEmittedErrors = true;
this.websocketServer.once('error', (err) => {
console.error('Internal error emitted on the WebSocket server. ' +
'Please check your implementation.', err);
// catch the first thrown error and re-throw it once all clients have been notified
let firstErr = null;
// report server errors by erroring out all clients with the same error
for (const client of this.websocketServer.clients) {
try {
client.close(common_1.CloseCode.InternalServerError, isProd
? 'Internal server error'
: (0, utils_1.limitCloseReason)(err.message, 'Internal server error'));
}
catch (err) {
firstErr = firstErr !== null && firstErr !== void 0 ? firstErr : err;
}
}
if (firstErr)
throw firstErr;
});
}
// used as listener on two streams, prevent superfluous calls on close
let emittedErrorHandled = false;
function handleEmittedError(err) {
if (emittedErrorHandled)
return;
emittedErrorHandled = true;
console.error('Internal error emitted on a WebSocket socket. ' +
'Please check your implementation.', err);
socket.close(common_1.CloseCode.InternalServerError, isProd
? 'Internal server error'
: (0, utils_1.limitCloseReason)(err.message, 'Internal server error'));
}
// fastify-websocket uses the WebSocket.createWebSocketStream,
// therefore errors get emitted on both the connection and the socket
connection.once('error', handleEmittedError);
socket.once('error', handleEmittedError);
// keep alive through ping-pong messages
let pongWait = null;
const pingInterval = keepAlive > 0 && isFinite(keepAlive)
? setInterval(() => {
// ping pong on open sockets only
if (socket.readyState === socket.OPEN) {
// terminate the connection after pong wait has passed because the client is idle
pongWait = setTimeout(() => {
socket.terminate();
}, keepAlive);
// listen for client's pong and stop socket termination
socket.once('pong', () => {
if (pongWait) {
clearTimeout(pongWait);
pongWait = null;
}
});
socket.ping();
}
}, keepAlive)
: null;
const closed = server.opened({
protocol: socket.protocol,
send: (data) => new Promise((resolve, reject) => {
if (socket.readyState !== socket.OPEN)
return resolve();
socket.send(data, (err) => (err ? reject(err) : resolve()));
}),
close: (code, reason) => socket.close(code, reason),
onMessage: (cb) => socket.on('message', async (event) => {
try {
await cb(String(event));
}
catch (err) {
console.error('Internal error occurred during message handling. ' +
'Please check your implementation.', err);
socket.close(common_1.CloseCode.InternalServerError, isProd
? 'Internal server error'
: (0, utils_1.limitCloseReason)(err.message, 'Internal server error'));
}
}),
}, { connection, request });
socket.once('close', (code, reason) => {
if (pongWait)
clearTimeout(pongWait);
if (pingInterval)
clearInterval(pingInterval);
if (!isProd &&
code === common_1.CloseCode.SubprotocolNotAcceptable &&
socket.protocol === common_1.DEPRECATED_GRAPHQL_WS_PROTOCOL)
console.warn(`Client provided the unsupported and deprecated subprotocol "${socket.protocol}" used by subscriptions-transport-ws.` +
'Please see https://www.apollographql.com/docs/apollo-server/data/subscriptions/#switching-from-subscriptions-transport-ws.');
closed(code, String(reason));
});
};
// new handler can be reused, the semantics stayed the same
return (0, websocket_1.makeHandler)(options, keepAlive);
}
exports.makeHandler = makeHandler;
{
"name": "graphql-ws",
"version": "5.9.1",
"version": "5.10.0",
"description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client",

@@ -51,2 +51,7 @@ "keywords": [

},
"./lib/use/@fastify/websocket": {
"require": "./lib/use/@fastify/websocket.js",
"import": "./lib/use/@fastify/websocket.mjs",
"types": "./lib/use/@fastify/websocket.d.ts"
},
"./lib/use/fastify-websocket": {

@@ -90,30 +95,31 @@ "require": "./lib/use/fastify-websocket.js",

"devDependencies": {
"@babel/core": "^7.18.6",
"@babel/core": "^7.18.10",
"@babel/plugin-proposal-class-properties": "^7.18.6",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
"@babel/plugin-proposal-object-rest-spread": "^7.18.6",
"@babel/plugin-proposal-optional-chaining": "^7.18.6",
"@babel/preset-env": "^7.18.6",
"@babel/plugin-proposal-object-rest-spread": "^7.18.9",
"@babel/plugin-proposal-optional-chaining": "^7.18.9",
"@babel/preset-env": "^7.18.10",
"@babel/preset-typescript": "^7.18.6",
"@rollup/plugin-typescript": "^8.3.3",
"@fastify/websocket": "^7.0.0",
"@rollup/plugin-typescript": "^8.3.4",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/git": "^10.0.1",
"@types/jest": "^28.1.4",
"@types/jest": "^28.1.6",
"@types/ws": "^8.5.3",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "^5.30.0",
"babel-jest": "^28.1.2",
"eslint": "^8.18.0",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"babel-jest": "^28.1.3",
"eslint": "^8.21.0",
"eslint-config-prettier": "^8.5.0",
"eslint-plugin-prettier": "^4.2.1",
"fastify": "^3.29.0",
"fastify": "^4.4.0",
"fastify-websocket": "4.2.2",
"glob": "^8.0.3",
"graphql": "^16.5.0",
"jest": "^28.1.2",
"jest-environment-jsdom": "^28.1.2",
"jest-jasmine2": "^28.1.2",
"jest": "^28.1.3",
"jest-environment-jsdom": "^28.1.3",
"jest-jasmine2": "^28.1.3",
"prettier": "^2.7.1",
"replacestream": "^4.0.3",
"rollup": "^2.75.7",
"rollup": "^2.77.2",
"rollup-plugin-terser": "^7.0.2",

@@ -123,8 +129,8 @@ "semantic-release": "^19.0.3",

"tslib": "^2.4.0",
"typedoc": "^0.23.2",
"typedoc-plugin-markdown": "^3.13.2",
"typedoc": "^0.23.10",
"typedoc-plugin-markdown": "^3.13.4",
"typescript": "^4.7.4",
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.10.0",
"ws": "^8.8.0",
"ws7": "npm:ws@^7.5.8"
"ws": "^8.8.1",
"ws7": "npm:ws@^7.5.9"
},

@@ -131,0 +137,0 @@ "resolutions": {

@@ -102,8 +102,8 @@ <div align="center">

##### With [fastify-websocket](https://github.com/fastify/fastify-websocket)
##### With [@fastify/websocket](https://github.com/fastify/fastify-websocket)
```ts
import Fastify from 'fastify'; // yarn add fastify
import fastifyWebsocket from 'fastify-websocket'; // yarn add fastify-websocket
import { makeHandler } from 'graphql-ws/lib/use/fastify-websocket';
import fastifyWebsocket from '@fastify/websocket'; // yarn add @fastify/websocket
import { makeHandler } from 'graphql-ws/lib/use/@fastify/websocket';
import { schema } from './previous-step';

@@ -114,3 +114,5 @@

fastify.get('/graphql', { websocket: true }, makeHandler({ schema }));
fastify.register(async (fastify) => {
fastify.get('/graphql', { websocket: true }, makeHandler({ schema }));
});

@@ -1173,2 +1175,74 @@ fastify.listen(4000, (err) => {

<details id="yoga">
<summary><a href="#yoga">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server usage with <a href="https://www.graphql-yoga.com">GraphQL Yoga</a></summary>
```typescript
import { ExecutionArgs, execute, subscribe } from 'graphql';
import { createServer } from '@graphql-yoga/node';
import { WebSocketServer } from 'ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { schema } from './my-graphql-schema';
async function main() {
const yogaApp = createServer({
schema,
graphiql: {
subscriptionsProtocol: 'WS', // use WebSockets instead of SSE
},
});
const httpServer = await yogaApp.start();
const wsServer = new WebSocketServer({
server: httpServer,
path: yogaApp.getAddressInfo().endpoint,
});
// yoga's envelop may augment the `execute` and `subscribe` operations
// so we need to make sure we always use the freshest instance
type EnvelopedExecutionArgs = ExecutionArgs & {
rootValue: {
execute: typeof execute;
subscribe: typeof subscribe;
};
};
useServer(
{
execute: (args) =>
(args as EnvelopedExecutionArgs).rootValue.execute(args),
subscribe: (args) =>
(args as EnvelopedExecutionArgs).rootValue.subscribe(args),
onSubscribe: async (ctx, msg) => {
const { schema, execute, subscribe, contextFactory, parse, validate } =
yogaApp.getEnveloped(ctx);
const args: EnvelopedExecutionArgs = {
schema,
operationName: msg.payload.operationName,
document: parse(msg.payload.query),
variableValues: msg.payload.variables,
contextValue: await contextFactory(),
rootValue: {
execute,
subscribe,
},
};
const errors = validate(args.schema, args.document);
if (errors.length) return errors;
return args;
},
},
wsServer,
);
}
main().catch((e) => {
console.error(e);
process.exit(1);
});
```
</details>
<details id="express">

@@ -1256,2 +1330,27 @@ <summary><a href="#express">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server usage with <a href="https://github.com/graphql/express-graphql">Express GraphQL</a></summary>

<details id="deprecated-fastify-websocket">
<summary><a href="#deprecated-fastify-websocket">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server usage with <a href="https://www.npmjs.com/package/fastify-websocket">deprecated fastify-websocket</a></summary>
```typescript
import Fastify from 'fastify'; // yarn add fastify@^3
import fastifyWebsocket from 'fastify-websocket'; // yarn add fastify-websocket@4.2.2
import { makeHandler } from 'graphql-ws/lib/use/fastify-websocket';
import { schema } from './previous-step';
const fastify = Fastify();
fastify.register(fastifyWebsocket);
fastify.get('/graphql', { websocket: true }, makeHandler({ schema }));
fastify.listen(4000, (err) => {
if (err) {
fastify.log.error(err);
return process.exit(1);
}
console.log('Listening to port 4000');
});
```
</details>
<details id="ws-backwards-compat">

@@ -1794,2 +1893,153 @@ <summary><a href="#ws-backwards-compat">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server usage with <a href="https://github.com/apollographql/subscriptions-transport-ws">subscriptions-transport-ws</a> backwards compatibility</summary>

<details id="subscribe-ack">
<summary><a href="#subscribe-ack">🔗</a> <a href="https://github.com/websockets/ws">ws</a> server and client usage with subscription acknowledgment</summary>
```ts
// 🛸 server
import { WebSocketServer } from 'ws';
// import ws from 'ws'; yarn add ws@7
// const WebSocketServer = ws.Server;
import { MessageType, stringifyMessage } from 'graphql-ws';
import { useServer } from 'graphql-ws/lib/use/ws';
import { schema } from './my-graphql-schema';
const wsServer = new WebSocketServer({
port: 4000,
path: '/graphql',
});
useServer<undefined, { ackWaiters: Record<string, () => void> }>(
{
schema,
onConnect: (ctx) => {
// listeners waiting for operation acknowledgment. if subscription, this means the graphql.subscribe was successful
// intentionally in context extra to avoid memory leaks when clients disconnect
ctx.extra.ackWaiters = {};
},
onSubscribe: (ctx, msg) => {
const ackId = msg.payload.extensions?.ackId;
if (typeof ackId === 'string') {
// if acknowledgment ID is present, create an acknowledger that will be executed when operation succeeds
ctx.extra.ackWaiters![msg.id] = () => {
ctx.extra.socket.send(
stringifyMessage({
type: MessageType.Ping,
payload: {
ackId,
},
}),
);
};
}
},
onOperation: (ctx, msg) => {
// acknowledge operation success and remove waiter
ctx.extra.ackWaiters![msg.id]?.();
delete ctx.extra.ackWaiters![msg.id];
},
},
wsServer,
);
console.log('Listening to port 4000');
```
```ts
// 📺 client
import {
Client,
ClientOptions,
createClient,
ExecutionResult,
Sink,
SubscribePayload,
} from 'graphql-ws';
// client with augmented subscribe method accepting the `onAck` callback for operation acknowledgement
type ClientWithSubscribeAck = Omit<Client, 'subscribe'> & {
subscribe<Data = Record<string, unknown>, Extensions = unknown>(
payload: SubscribePayload,
sink: Sink<ExecutionResult<Data, Extensions>>,
onAck: () => void,
): () => void;
};
function createClientWithSubscribeAck(
options: ClientOptions,
): ClientWithSubscribeAck {
const client = createClient(options);
const ackListeners: Record<string, () => void> = {};
client.on('ping', (_received, payload) => {
const ackId = payload?.ackId;
if (typeof ackId === 'string') {
ackListeners[ackId]?.();
delete ackListeners[ackId];
}
});
return {
...client,
subscribe: (payload, sink, onAck) => {
const ackId = Math.random().toString(); // be wary of uniqueness
ackListeners[ackId] = onAck;
return client.subscribe(
{
...payload,
extensions: {
...payload.extensions,
ackId,
},
},
sink,
);
},
};
}
```
Using the augmented client would be as simple as:
```ts
const client = createClientWithSubscribeAck({
url: 'ws://i.want.ack:4000/graphql',
});
(async () => {
const onNext = () => {
/* handle incoming values */
};
let unsubscribe = () => {
/* complete the subscription */
};
let subscriptionAcknowledged = () => {
/* server successfully subscribed */
};
await new Promise((resolve, reject) => {
unsubscribe = client.subscribe(
{
query: 'subscription { greetings }',
},
{
next: onNext,
error: reject,
complete: resolve,
},
subscriptionAcknowledged,
);
});
expect(subscriptionAcknowledged).toBeCalledFirst();
expect(onNext).then.toBeCalledTimes(5); // we say "Hi" in 5 languages
})();
```
</details>
## [Documentation](docs/)

@@ -1796,0 +2046,0 @@

Sorry, the diff of this file is not supported yet

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