Initial Sample.
This commit is contained in:
9
graphql-subscription-express/node_modules/graphql-ws/LICENSE.md
generated
vendored
Normal file
9
graphql-subscription-express/node_modules/graphql-ws/LICENSE.md
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2021 Denis Badurina
|
||||
|
||||
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.
|
||||
251
graphql-subscription-express/node_modules/graphql-ws/PROTOCOL.md
generated
vendored
Normal file
251
graphql-subscription-express/node_modules/graphql-ws/PROTOCOL.md
generated
vendored
Normal file
@@ -0,0 +1,251 @@
|
||||
# GraphQL over WebSocket Protocol
|
||||
|
||||
## Nomenclature
|
||||
|
||||
- **Socket** is the main WebSocket communication channel between the _server_ and the _client_
|
||||
- **Connection** is a connection **within the established socket** describing a "connection" through which the operation requests will be communicated
|
||||
|
||||
## Communication
|
||||
|
||||
The WebSocket sub-protocol for this specification is: `graphql-transport-ws`.
|
||||
|
||||
Messages are represented through the JSON structure and are stringified before being sent over the network. They are bidirectional, meaning both the server and the client must conform to the specified message structure.
|
||||
|
||||
**All** messages contain the `type` field outlining the action this message describes.
|
||||
|
||||
Messages corresponding to operations must contain the `id` field used for uniquely identifying server responses and connecting them with the client's requests.
|
||||
|
||||
Multiple operations identified with separate IDs can be active at any time and their messages can be interleaved on the connection.
|
||||
|
||||
The server can close the socket (kick the client off) at any time. The close event dispatched by the server is used to describe the fatal error to the client.
|
||||
|
||||
The client closes the socket and the connection by dispatching a `1000: Normal Closure` close event to the server indicating a normal closure.
|
||||
|
||||
## Message types
|
||||
|
||||
### `ConnectionInit`
|
||||
|
||||
Direction: **Client -> Server**
|
||||
|
||||
Indicates that the client wants to establish a connection within the existing socket. This connection is **not** the actual WebSocket communication channel, but is rather a frame within it asking the server to allow future operation requests.
|
||||
|
||||
The server must receive the connection initialisation message within the allowed waiting time specified in the `connectionInitWaitTimeout` parameter during the server setup. If the client does not request a connection within the allowed timeout, the server will close the socket with the event: `4408: Connection initialisation timeout`.
|
||||
|
||||
If the server receives more than one `ConnectionInit` message at any given time, the server will close the socket with the event `4429: Too many initialisation requests`.
|
||||
|
||||
If the server wishes to reject the connection, for example during authentication, it is recommended to close the socket with `4403: Forbidden`.
|
||||
|
||||
```typescript
|
||||
interface ConnectionInitMessage {
|
||||
type: 'connection_init';
|
||||
payload?: Record<string, unknown> | null;
|
||||
}
|
||||
```
|
||||
|
||||
### `ConnectionAck`
|
||||
|
||||
Direction: **Server -> Client**
|
||||
|
||||
Expected response to the `ConnectionInit` message from the client acknowledging a successful connection with the server.
|
||||
|
||||
The server can use the optional `payload` field to transfer additional details about the connection.
|
||||
|
||||
```typescript
|
||||
interface ConnectionAckMessage {
|
||||
type: 'connection_ack';
|
||||
payload?: Record<string, unknown> | null;
|
||||
}
|
||||
```
|
||||
|
||||
The client is now **ready** to request subscription operations.
|
||||
|
||||
### `Ping`
|
||||
|
||||
Direction: **bidirectional**
|
||||
|
||||
Useful for detecting failed connections, displaying latency metrics or other types of network probing.
|
||||
|
||||
A `Pong` must be sent in response from the receiving party as soon as possible.
|
||||
|
||||
The `Ping` message can be sent at any time within the established socket.
|
||||
|
||||
The optional `payload` field can be used to transfer additional details about the ping.
|
||||
|
||||
```typescript
|
||||
interface PingMessage {
|
||||
type: 'ping';
|
||||
payload?: Record<string, unknown> | null;
|
||||
}
|
||||
```
|
||||
|
||||
### `Pong`
|
||||
|
||||
Direction: **bidirectional**
|
||||
|
||||
The response to the `Ping` message. Must be sent as soon as the `Ping` message is received.
|
||||
|
||||
The `Pong` message can be sent at any time within the established socket. Furthermore, the `Pong` message may even be sent unsolicited as an unidirectional heartbeat.
|
||||
|
||||
The optional `payload` field can be used to transfer additional details about the pong.
|
||||
|
||||
```typescript
|
||||
interface PongMessage {
|
||||
type: 'pong';
|
||||
payload?: Record<string, unknown> | null;
|
||||
}
|
||||
```
|
||||
|
||||
### `Subscribe`
|
||||
|
||||
Direction: **Client -> Server**
|
||||
|
||||
Requests an operation specified in the message `payload`. This message provides a unique ID field to connect published messages to the operation requested by this message.
|
||||
|
||||
If there is already an active subscriber for an operation matching the provided ID, regardless of the operation type, the server **must** close the socket immediately with the event `4409: Subscriber for <unique-operation-id> already exists`.
|
||||
|
||||
The server needs only keep track of IDs for as long as the subscription is active. Once a client completes an operation, it is free to re-use that ID.
|
||||
|
||||
```typescript
|
||||
interface SubscribeMessage {
|
||||
id: '<unique-operation-id>';
|
||||
type: 'subscribe';
|
||||
payload: {
|
||||
operationName?: string | null;
|
||||
query: string;
|
||||
variables?: Record<string, unknown> | null;
|
||||
extensions?: Record<string, unknown> | null;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
Executing operations is allowed **only** after the server has acknowledged the connection through the `ConnectionAck` message, if the connection is not acknowledged, the socket will be closed immediately with the event `4401: Unauthorized`.
|
||||
|
||||
### `Next`
|
||||
|
||||
Direction: **Server -> Client**
|
||||
|
||||
Operation execution result(s) from the source stream created by the binding `Subscribe` message. After all results have been emitted, the `Complete` message will follow indicating stream completion.
|
||||
|
||||
```typescript
|
||||
import { ExecutionResult } from 'graphql';
|
||||
|
||||
interface NextMessage {
|
||||
id: '<unique-operation-id>';
|
||||
type: 'next';
|
||||
payload: ExecutionResult;
|
||||
}
|
||||
```
|
||||
|
||||
### `Error`
|
||||
|
||||
Direction: **Server -> Client**
|
||||
|
||||
Operation execution error(s) in response to the `Subscribe` message. This can occur _before_ execution starts, usually due to validation errors, or _during_ the execution of the request. This message terminates the operation and no further messages will be sent.
|
||||
|
||||
```typescript
|
||||
import { GraphQLError } from 'graphql';
|
||||
|
||||
interface ErrorMessage {
|
||||
id: '<unique-operation-id>';
|
||||
type: 'error';
|
||||
payload: GraphQLError[];
|
||||
}
|
||||
```
|
||||
|
||||
### `Complete`
|
||||
|
||||
Direction: **bidirectional**
|
||||
|
||||
- **Server -> Client** indicates that the requested operation execution has completed. If the server dispatched the `Error` message relative to the original `Subscribe` message, no `Complete` message will be emitted.
|
||||
|
||||
- **Client -> Server** indicates that the client has stopped listening and wants to complete the subscription. No further events, relevant to the original subscription, should be sent through. Even if the client sent a `Complete` message for a _single-result-operation_ before it resolved, the result should not be sent through once it does.
|
||||
|
||||
Note: The asynchronous nature of the full-duplex connection means that a client can send a `Complete` message to the server even when messages are in-flight to the client, or when the server has itself completed the operation (via a `Error` or `Complete` message). Both client and server must therefore be prepared to receive (and ignore) messages for operations that they consider already completed.
|
||||
|
||||
```typescript
|
||||
interface CompleteMessage {
|
||||
id: '<unique-operation-id>';
|
||||
type: 'complete';
|
||||
}
|
||||
```
|
||||
|
||||
### Invalid message
|
||||
|
||||
Direction: **bidirectional**
|
||||
|
||||
Receiving a message of a type or format which is not specified in this document will result in an **immediate** socket closure with the event `4400: <error-message>`. The `<error-message>` can be vaguely descriptive on why the received message is invalid.
|
||||
|
||||
Receiving a message (other than `Subscribe`) with an ID that belongs to an operation that has been previously completed does not constitute an error. It is permissable to simply ignore all _unknown_ IDs without closing the connection.
|
||||
|
||||
## Examples
|
||||
|
||||
For the sake of clarity, the following examples demonstrate the communication protocol.
|
||||
|
||||
<h3 id="successful-connection-initialisation">Successful connection initialisation</h3>
|
||||
|
||||
1. _Client_ sends a WebSocket handshake request with the sub-protocol: `graphql-transport-ws`
|
||||
1. _Server_ accepts the handshake and establishes a WebSocket communication channel (which we call "socket")
|
||||
1. _Client_ immediately dispatches a `ConnectionInit` message optionally providing a payload as agreed with the server
|
||||
1. _Server_ validates the connection initialisation request and dispatches a `ConnectionAck` message to the client on successful connection
|
||||
1. _Client_ has received the acknowledgement message and is now ready to request operation executions
|
||||
|
||||
### Connection initialisation timeout
|
||||
|
||||
1. _Client_ sends a WebSocket handshake request with the sub-protocol: `graphql-transport-ws`
|
||||
1. _Server_ accepts the handshake and establishes a WebSocket communication channel (which we call "socket")
|
||||
1. _Client_ does not dispatch a `ConnectionInit` message
|
||||
1. _Server_ waits for the `ConnectionInit` message for the duration specified in the `connectionInitWaitTimeout` parameter
|
||||
1. _Server_ waiting time has passed
|
||||
1. _Server_ closes the socket by dispatching the event `4408: Connection initialisation timeout`
|
||||
|
||||
### Streaming operation
|
||||
|
||||
#### `subscription` operation and queries with streaming directives
|
||||
|
||||
_The client and the server has already gone through [successful connection initialisation](#successful-connection-initialisation)._
|
||||
|
||||
1. _Client_ generates a unique ID for the following operation
|
||||
1. _Client_ dispatches the `Subscribe` message with the generated ID through the `id` field and the requested operation passed through the `payload` field
|
||||
<br>_All future communication is linked through this unique ID_
|
||||
1. _Server_ executes the streaming GraphQL operation
|
||||
1. _Server_ checks if the generated ID is unique across active streaming subscriptions
|
||||
|
||||
- If **not** unique, the _server_ will close the socket with the event `4409: Subscriber for <generated-id> already exists`
|
||||
- If unique, continue...
|
||||
|
||||
1. _Server_ _optionally_ checks if the operation is valid before starting executing it, e.g. checking permissions
|
||||
|
||||
- If **not** valid, the _server_ sends an `Error` message and deems the operation complete.
|
||||
- If valid, continue...
|
||||
|
||||
1. _Server_ dispatches results over time with the `Next` message
|
||||
1. - _Server_ dispatches the `Complete` message indicating that the source stream has completed
|
||||
- _Client_ completes the stream observer
|
||||
<br>**or**
|
||||
- _Client_ stops the subscription by dispatching a `Complete` message
|
||||
- _Server_ receives `Complete` message and completes the source stream
|
||||
- _Client_ ignores all further messages that it receives with this ID
|
||||
<br>**or**
|
||||
- _Server_ dispatches the `Complete` message indicating that the source stream has completed
|
||||
- **Simultaneously** _client_ stops the subscription by dispatching a `Complete` message
|
||||
- _Client_ ignores all further messages that it receives with this ID
|
||||
- _Server_ ignores the `Complete` message from the client
|
||||
|
||||
### Single result operation
|
||||
|
||||
#### `query` and `mutation` operations without streaming directives
|
||||
|
||||
A single result operation is identical to a streaming operation except that _at most one_ `Next` message is sent.
|
||||
|
||||
It shares the same name-space for IDs as streaming operations and can be multiplexed with other operations on the connection.
|
||||
|
||||
_The client and the server has already gone through [successful connection initialisation](#successful-connection-initialisation)._
|
||||
|
||||
1. _Client_ generates a unique ID for the following operation
|
||||
1. _Client_ dispatches the `Subscribe` message with the generated ID through the `id` field and the requested operation passed through the `payload` field
|
||||
<br>_All future communication is linked through this unique ID_
|
||||
1. _Server_ executes the single result GraphQL operation
|
||||
1. _Server_ dispatches the result with the `Next` message
|
||||
1. _Server_ dispatches the `Complete` message indicating that the execution has completed
|
||||
|
||||
The _client_ may dispatch a `Complete` message at any time, just as shown in the streaming operations examples above, and the same interactions ensue.
|
||||
39
graphql-subscription-express/node_modules/graphql-ws/README.md
generated
vendored
Normal file
39
graphql-subscription-express/node_modules/graphql-ws/README.md
generated
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
<div align="center">
|
||||
<br />
|
||||
|
||||

|
||||
|
||||
<h6>Coherent, zero-dependency, lazy, simple, <a href="PROTOCOL.md">GraphQL over WebSocket Protocol</a> compliant server and client.</h6>
|
||||
|
||||
[](https://github.com/enisdenjo/graphql-ws/actions?query=workflow%3A%22Continuous+integration%22) [](https://www.npmjs.com/package/graphql-ws)
|
||||
|
||||
<i>Use [Server-Sent Events (SSE)](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events) instead? Check out <b>[graphql-sse](https://github.com/enisdenjo/graphql-sse)</b>!</i>
|
||||
|
||||
<br />
|
||||
</div>
|
||||
|
||||
## [Get started](https://the-guild.dev/graphql/ws/get-started)
|
||||
|
||||
Swiftly start with the [get started guide on the website](https://the-guild.dev/graphql/ws/get-started).
|
||||
|
||||
## [Recipes](https://the-guild.dev/graphql/ws/recipes)
|
||||
|
||||
Short and concise code snippets for starting with common use-cases. [Available on the website.](https://the-guild.dev/graphql/ws/recipes)
|
||||
|
||||
## [Documentation](https://the-guild.dev/graphql/ws/docs)
|
||||
|
||||
Auto-generated by [TypeDoc](https://typedoc.org) and then [rendered on the website](https://the-guild.dev/graphql/ws/docs).
|
||||
|
||||
## [How does it work?](PROTOCOL.md)
|
||||
|
||||
Read about the exact transport intricacies used by the library in the [GraphQL over WebSocket Protocol document](PROTOCOL.md).
|
||||
|
||||
## [Want to help?](CONTRIBUTING.md)
|
||||
|
||||
File a bug, contribute with code, or improve documentation? Read up on our guidelines for [contributing](CONTRIBUTING.md) and drive development with `yarn test --watch` away!
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This library and the [GraphQL over WebSocket Protocol](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) are **not** cross-compatible with the [deprecated `subscriptions-transport-ws`](https://github.com/apollographql/subscriptions-transport-ws) and its [accompanying Protocol](https://github.com/apollographql/subscriptions-transport-ws/blob/master/PROTOCOL.md).
|
||||
|
||||
You must use `graphql-ws` coherently and implement the [GraphQL over WebSocket Protocol](https://github.com/enisdenjo/graphql-ws/blob/master/PROTOCOL.md) on both sides, server and the client.
|
||||
407
graphql-subscription-express/node_modules/graphql-ws/lib/client.d.mts
generated
vendored
Normal file
407
graphql-subscription-express/node_modules/graphql-ws/lib/client.d.mts
generated
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
/**
|
||||
*
|
||||
* client
|
||||
*
|
||||
*/
|
||||
import { ExecutionResult } from 'graphql';
|
||||
import { Sink, ID, Disposable, Message, ConnectionInitMessage, ConnectionAckMessage, PingMessage, PongMessage, SubscribePayload, JSONMessageReviver, JSONMessageReplacer } from './common.mjs';
|
||||
/** This file is the entry point for browsers, re-export common elements. */
|
||||
export * from './common.mjs';
|
||||
/**
|
||||
* WebSocket started connecting.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventConnecting = 'connecting';
|
||||
/**
|
||||
* WebSocket has opened.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventOpened = 'opened';
|
||||
/**
|
||||
* Open WebSocket connection has been acknowledged.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventConnected = 'connected';
|
||||
/**
|
||||
* `PingMessage` has been received or sent.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPing = 'ping';
|
||||
/**
|
||||
* `PongMessage` has been received or sent.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPong = 'pong';
|
||||
/**
|
||||
* A message has been received.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventMessage = 'message';
|
||||
/**
|
||||
* WebSocket connection has closed.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventClosed = 'closed';
|
||||
/**
|
||||
* WebSocket connection had an error or client had an internal error.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventError = 'error';
|
||||
/**
|
||||
* All events that could occur.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type Event = EventConnecting | EventOpened | EventConnected | EventPing | EventPong | EventMessage | EventClosed | EventError;
|
||||
/** @category Client */
|
||||
export type EventConnectingListener = (isRetry: boolean) => void;
|
||||
/**
|
||||
* The first argument is actually the `WebSocket`, but to avoid
|
||||
* bundling DOM typings because the client can run in Node env too,
|
||||
* you should assert the websocket type during implementation.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventOpenedListener = (socket: unknown) => void;
|
||||
/**
|
||||
* The first argument is actually the `WebSocket`, but to avoid
|
||||
* bundling DOM typings because the client can run in Node env too,
|
||||
* you should assert the websocket type during implementation.
|
||||
*
|
||||
* Also, the second argument is the optional payload that the server may
|
||||
* send through the `ConnectionAck` message.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventConnectedListener = (socket: unknown, payload: ConnectionAckMessage['payload'], wasRetry: boolean) => void;
|
||||
/**
|
||||
* The first argument communicates whether the ping was received from the server.
|
||||
* If `false`, the ping was sent by the client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPingListener = (received: boolean, payload: PingMessage['payload']) => void;
|
||||
/**
|
||||
* The first argument communicates whether the pong was received from the server.
|
||||
* If `false`, the pong was sent by the client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPongListener = (received: boolean, payload: PongMessage['payload']) => void;
|
||||
/**
|
||||
* Called for all **valid** messages received by the client. Mainly useful for
|
||||
* debugging and logging received messages.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventMessageListener = (message: Message) => void;
|
||||
/**
|
||||
* The argument is actually the websocket `CloseEvent`, but to avoid
|
||||
* bundling DOM typings because the client can run in Node env too,
|
||||
* you should assert the websocket type during implementation.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventClosedListener = (event: unknown) => void;
|
||||
/**
|
||||
* Events dispatched from the WebSocket `onerror` are handled in this listener,
|
||||
* as well as all internal client errors that could throw.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventErrorListener = (error: unknown) => void;
|
||||
/** @category Client */
|
||||
export type EventListener<E extends Event> = E extends EventConnecting ? EventConnectingListener : E extends EventOpened ? EventOpenedListener : E extends EventConnected ? EventConnectedListener : E extends EventPing ? EventPingListener : E extends EventPong ? EventPongListener : E extends EventMessage ? EventMessageListener : E extends EventClosed ? EventClosedListener : E extends EventError ? EventErrorListener : never;
|
||||
/**
|
||||
* Configuration used for the GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export interface ClientOptions<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload']> {
|
||||
/**
|
||||
* URL of the GraphQL over WebSocket Protocol compliant server to connect.
|
||||
*
|
||||
* If the option is a function, it will be called on every WebSocket connection attempt.
|
||||
* Returning a promise is supported too and the connecting phase will stall until it
|
||||
* resolves with the URL.
|
||||
*
|
||||
* A good use-case for having a function is when using the URL for authentication,
|
||||
* where subsequent reconnects (due to auth) may have a refreshed identity token in
|
||||
* the URL.
|
||||
*/
|
||||
url: string | (() => Promise<string> | string);
|
||||
/**
|
||||
* Optional parameters, passed through the `payload` field with the `ConnectionInit` message,
|
||||
* that the client specifies when establishing a connection with the server. You can use this
|
||||
* for securely passing arguments for authentication.
|
||||
*
|
||||
* If you decide to return a promise, keep in mind that the server might kick you off if it
|
||||
* takes too long to resolve! Check the `connectionInitWaitTimeout` on the server for more info.
|
||||
*
|
||||
* Throwing an error from within this function will close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
connectionParams?: P | (() => Promise<P> | P);
|
||||
/**
|
||||
* Controls when should the connection be established.
|
||||
*
|
||||
* - `false`: Establish a connection immediately. Use `onNonLazyError` to handle errors.
|
||||
* - `true`: Establish a connection on first subscribe and close on last unsubscribe. Use
|
||||
* the subscription sink's `error` to handle errors.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
lazy?: boolean;
|
||||
/**
|
||||
* Used ONLY when the client is in non-lazy mode (`lazy = false`). When
|
||||
* using this mode, the errors might have no sinks to report to; however,
|
||||
* to avoid swallowing errors, consider using `onNonLazyError`, which will
|
||||
* be called when either:
|
||||
* - An unrecoverable error/close event occurs
|
||||
* - Silent retry attempts have been exceeded
|
||||
*
|
||||
* After a client has errored out, it will NOT perform any automatic actions.
|
||||
*
|
||||
* The argument can be a websocket `CloseEvent` or an `Error`. To avoid bundling
|
||||
* DOM types, you should derive and assert the correct type. When receiving:
|
||||
* - A `CloseEvent`: retry attempts have been exceeded or the specific
|
||||
* close event is labeled as fatal (read more in `retryAttempts`).
|
||||
* - An `Error`: some internal issue has occured, all internal errors are
|
||||
* fatal by nature.
|
||||
*
|
||||
* @default console.error
|
||||
*/
|
||||
onNonLazyError?: (errorOrCloseEvent: unknown) => void;
|
||||
/**
|
||||
* How long should the client wait before closing the socket after the last oparation has
|
||||
* completed. This is meant to be used in combination with `lazy`. You might want to have
|
||||
* a calmdown time before actually closing the connection. Kinda' like a lazy close "debounce".
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
lazyCloseTimeout?: number;
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages, naimly server pings. Internally
|
||||
* dispatches the `PingMessage` type to the server and expects a `PongMessage` in response.
|
||||
* This helps with making sure that the connection with the server is alive and working.
|
||||
*
|
||||
* Timeout countdown starts from the moment the socket was opened and subsequently
|
||||
* after every received `PongMessage`.
|
||||
*
|
||||
* Note that NOTHING will happen automatically with the client if the server never
|
||||
* responds to a `PingMessage` with a `PongMessage`. If you want the connection to close,
|
||||
* you should implement your own logic on top of the client. A simple example looks like this:
|
||||
*
|
||||
* ```js
|
||||
* import { createClient } from 'graphql-ws';
|
||||
*
|
||||
* let activeSocket, timedOut;
|
||||
* createClient({
|
||||
* url: 'ws://i.time.out:4000/after-5/seconds',
|
||||
* keepAlive: 10_000, // ping server every 10 seconds
|
||||
* on: {
|
||||
* connected: (socket) => (activeSocket = socket),
|
||||
* ping: (received) => {
|
||||
* if (!received) // sent
|
||||
* timedOut = setTimeout(() => {
|
||||
* if (activeSocket.readyState === WebSocket.OPEN)
|
||||
* activeSocket.close(4408, 'Request Timeout');
|
||||
* }, 5_000); // wait 5 seconds for the pong and then close the connection
|
||||
* },
|
||||
* pong: (received) => {
|
||||
* if (received) clearTimeout(timedOut); // pong is received, clear connection close timeout
|
||||
* },
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
keepAlive?: number;
|
||||
/**
|
||||
* The amount of time for which the client will wait
|
||||
* for `ConnectionAck` message.
|
||||
*
|
||||
* Set the value to `Infinity`, `''`, `0`, `null` or `undefined` to skip waiting.
|
||||
*
|
||||
* If the wait timeout has passed and the server
|
||||
* has not responded with `ConnectionAck` message,
|
||||
* the client will terminate the socket by
|
||||
* dispatching a close event `4418: Connection acknowledgement timeout`
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
connectionAckWaitTimeout?: number;
|
||||
/**
|
||||
* Disable sending the `PongMessage` automatically.
|
||||
*
|
||||
* Useful for when integrating your own custom client pinger that performs
|
||||
* custom actions before responding to a ping, or to pass along the optional pong
|
||||
* message payload. Please check the readme recipes for a concrete example.
|
||||
*/
|
||||
disablePong?: boolean;
|
||||
/**
|
||||
* How many times should the client try to reconnect on abnormal socket closure before it errors out?
|
||||
*
|
||||
* The library classifies the following close events as fatal:
|
||||
* - _All internal WebSocket fatal close codes (check `isFatalInternalCloseCode` in `src/client.ts` for exact list)_
|
||||
* - `4500: Internal server error`
|
||||
* - `4005: Internal client error`
|
||||
* - `4400: Bad request`
|
||||
* - `4004: Bad response`
|
||||
* - `4401: Unauthorized` _tried subscribing before connect ack_
|
||||
* - `4406: Subprotocol not acceptable`
|
||||
* - `4409: Subscriber for <id> already exists` _distinction is very important_
|
||||
* - `4429: Too many initialisation requests`
|
||||
*
|
||||
* In addition to the aforementioned close events, any _non-CloseEvent_ connection problem
|
||||
* is considered fatal by default. However, this specific behaviour can be altered by using
|
||||
* the `shouldRetry` option.
|
||||
*
|
||||
* These events are reported immediately and the client will not reconnect.
|
||||
*
|
||||
* @default 5
|
||||
*/
|
||||
retryAttempts?: number;
|
||||
/**
|
||||
* Control the wait time between retries. You may implement your own strategy
|
||||
* by timing the resolution of the returned promise with the retries count.
|
||||
* `retries` argument counts actual connection attempts, so it will begin with
|
||||
* 0 after the first retryable disconnect.
|
||||
*
|
||||
* @default 'Randomised exponential backoff'
|
||||
*/
|
||||
retryWait?: (retries: number) => Promise<void>;
|
||||
/**
|
||||
* Check if the close event or connection error is fatal. If you return `false`,
|
||||
* the client will fail immediately without additional retries; however, if you
|
||||
* return `true`, the client will keep retrying until the `retryAttempts` have
|
||||
* been exceeded.
|
||||
*
|
||||
* The argument is whatever has been thrown during the connection phase.
|
||||
*
|
||||
* Beware, the library classifies a few close events as fatal regardless of
|
||||
* what is returned here. They are listed in the documentation of the `retryAttempts`
|
||||
* option.
|
||||
*
|
||||
* @default 'Only `CloseEvent`s'
|
||||
*/
|
||||
shouldRetry?: (errOrCloseEvent: unknown) => boolean;
|
||||
/**
|
||||
* @deprecated Use `shouldRetry` instead.
|
||||
*
|
||||
* Check if the close event or connection error is fatal. If you return `true`,
|
||||
* the client will fail immediately without additional retries; however, if you
|
||||
* return `false`, the client will keep retrying until the `retryAttempts` have
|
||||
* been exceeded.
|
||||
*
|
||||
* The argument is either a WebSocket `CloseEvent` or an error thrown during
|
||||
* the connection phase.
|
||||
*
|
||||
* Beware, the library classifies a few close events as fatal regardless of
|
||||
* what is returned. They are listed in the documentation of the `retryAttempts`
|
||||
* option.
|
||||
*
|
||||
* @default 'Any non-`CloseEvent`'
|
||||
*/
|
||||
isFatalConnectionProblem?: (errOrCloseEvent: unknown) => boolean;
|
||||
/**
|
||||
* Register listeners before initialising the client. This way
|
||||
* you can ensure to catch all client relevant emitted events.
|
||||
*
|
||||
* The listeners passed in will **always** be the first ones
|
||||
* to get the emitted event before other registered listeners.
|
||||
*/
|
||||
on?: Partial<{
|
||||
[event in Event]: EventListener<event>;
|
||||
}>;
|
||||
/**
|
||||
* A custom WebSocket implementation to use instead of the
|
||||
* one provided by the global scope. Mostly useful for when
|
||||
* using the client outside of the browser environment.
|
||||
*/
|
||||
webSocketImpl?: unknown;
|
||||
/**
|
||||
* A custom ID generator for identifying subscriptions.
|
||||
*
|
||||
* The default generates a v4 UUID to be used as the ID using `Math`
|
||||
* as the random number generator. Supply your own generator
|
||||
* in case you need more uniqueness.
|
||||
*
|
||||
* Reference: https://gist.github.com/jed/982883
|
||||
*/
|
||||
generateID?: (payload: SubscribePayload) => ID;
|
||||
/**
|
||||
* An optional override for the JSON.parse function used to hydrate
|
||||
* incoming messages to this client. Useful for parsing custom datatypes
|
||||
* out of the incoming JSON.
|
||||
*/
|
||||
jsonMessageReviver?: JSONMessageReviver;
|
||||
/**
|
||||
* An optional override for the JSON.stringify function used to serialize
|
||||
* outgoing messages from this client. Useful for serializing custom
|
||||
* datatypes out to the client.
|
||||
*/
|
||||
jsonMessageReplacer?: JSONMessageReplacer;
|
||||
}
|
||||
/** @category Client */
|
||||
export interface Client extends Disposable {
|
||||
/**
|
||||
* Listens on the client which dispatches events about the socket state.
|
||||
*/
|
||||
on<E extends Event>(event: E, listener: EventListener<E>): () => void;
|
||||
/**
|
||||
* Subscribes through the WebSocket following the config parameters. It
|
||||
* uses the `sink` to emit received data or errors. Returns a _cleanup_
|
||||
* function used for dropping the subscription and cleaning stuff up.
|
||||
*/
|
||||
subscribe<Data = Record<string, unknown>, Extensions = unknown>(payload: SubscribePayload, sink: Sink<ExecutionResult<Data, Extensions>>): () => void;
|
||||
/**
|
||||
* Subscribes and iterates over emitted results from the WebSocket
|
||||
* through the returned async iterator.
|
||||
*/
|
||||
iterate<Data = Record<string, unknown>, Extensions = unknown>(payload: SubscribePayload): AsyncIterableIterator<ExecutionResult<Data, Extensions>>;
|
||||
/**
|
||||
* Terminates the WebSocket abruptly and immediately.
|
||||
*
|
||||
* A close event `4499: Terminated` is issued to the current WebSocket and a
|
||||
* syntetic {@link TerminatedCloseEvent} is immediately emitted without waiting for
|
||||
* the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
terminate(): void;
|
||||
}
|
||||
/**
|
||||
* Creates a disposable GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export declare function createClient<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload']>(options: ClientOptions<P>): Client;
|
||||
/**
|
||||
* A syntetic close event `4499: Terminated` is issued to the current to immediately
|
||||
* close the connection without waiting for the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
export declare class TerminatedCloseEvent extends Error {
|
||||
name: string;
|
||||
message: string;
|
||||
code: number;
|
||||
reason: string;
|
||||
wasClean: boolean;
|
||||
}
|
||||
407
graphql-subscription-express/node_modules/graphql-ws/lib/client.d.ts
generated
vendored
Normal file
407
graphql-subscription-express/node_modules/graphql-ws/lib/client.d.ts
generated
vendored
Normal file
@@ -0,0 +1,407 @@
|
||||
/**
|
||||
*
|
||||
* client
|
||||
*
|
||||
*/
|
||||
import { ExecutionResult } from 'graphql';
|
||||
import { Sink, ID, Disposable, Message, ConnectionInitMessage, ConnectionAckMessage, PingMessage, PongMessage, SubscribePayload, JSONMessageReviver, JSONMessageReplacer } from './common';
|
||||
/** This file is the entry point for browsers, re-export common elements. */
|
||||
export * from './common';
|
||||
/**
|
||||
* WebSocket started connecting.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventConnecting = 'connecting';
|
||||
/**
|
||||
* WebSocket has opened.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventOpened = 'opened';
|
||||
/**
|
||||
* Open WebSocket connection has been acknowledged.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventConnected = 'connected';
|
||||
/**
|
||||
* `PingMessage` has been received or sent.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPing = 'ping';
|
||||
/**
|
||||
* `PongMessage` has been received or sent.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPong = 'pong';
|
||||
/**
|
||||
* A message has been received.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventMessage = 'message';
|
||||
/**
|
||||
* WebSocket connection has closed.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventClosed = 'closed';
|
||||
/**
|
||||
* WebSocket connection had an error or client had an internal error.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventError = 'error';
|
||||
/**
|
||||
* All events that could occur.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type Event = EventConnecting | EventOpened | EventConnected | EventPing | EventPong | EventMessage | EventClosed | EventError;
|
||||
/** @category Client */
|
||||
export type EventConnectingListener = (isRetry: boolean) => void;
|
||||
/**
|
||||
* The first argument is actually the `WebSocket`, but to avoid
|
||||
* bundling DOM typings because the client can run in Node env too,
|
||||
* you should assert the websocket type during implementation.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventOpenedListener = (socket: unknown) => void;
|
||||
/**
|
||||
* The first argument is actually the `WebSocket`, but to avoid
|
||||
* bundling DOM typings because the client can run in Node env too,
|
||||
* you should assert the websocket type during implementation.
|
||||
*
|
||||
* Also, the second argument is the optional payload that the server may
|
||||
* send through the `ConnectionAck` message.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventConnectedListener = (socket: unknown, payload: ConnectionAckMessage['payload'], wasRetry: boolean) => void;
|
||||
/**
|
||||
* The first argument communicates whether the ping was received from the server.
|
||||
* If `false`, the ping was sent by the client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPingListener = (received: boolean, payload: PingMessage['payload']) => void;
|
||||
/**
|
||||
* The first argument communicates whether the pong was received from the server.
|
||||
* If `false`, the pong was sent by the client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventPongListener = (received: boolean, payload: PongMessage['payload']) => void;
|
||||
/**
|
||||
* Called for all **valid** messages received by the client. Mainly useful for
|
||||
* debugging and logging received messages.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventMessageListener = (message: Message) => void;
|
||||
/**
|
||||
* The argument is actually the websocket `CloseEvent`, but to avoid
|
||||
* bundling DOM typings because the client can run in Node env too,
|
||||
* you should assert the websocket type during implementation.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventClosedListener = (event: unknown) => void;
|
||||
/**
|
||||
* Events dispatched from the WebSocket `onerror` are handled in this listener,
|
||||
* as well as all internal client errors that could throw.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export type EventErrorListener = (error: unknown) => void;
|
||||
/** @category Client */
|
||||
export type EventListener<E extends Event> = E extends EventConnecting ? EventConnectingListener : E extends EventOpened ? EventOpenedListener : E extends EventConnected ? EventConnectedListener : E extends EventPing ? EventPingListener : E extends EventPong ? EventPongListener : E extends EventMessage ? EventMessageListener : E extends EventClosed ? EventClosedListener : E extends EventError ? EventErrorListener : never;
|
||||
/**
|
||||
* Configuration used for the GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export interface ClientOptions<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload']> {
|
||||
/**
|
||||
* URL of the GraphQL over WebSocket Protocol compliant server to connect.
|
||||
*
|
||||
* If the option is a function, it will be called on every WebSocket connection attempt.
|
||||
* Returning a promise is supported too and the connecting phase will stall until it
|
||||
* resolves with the URL.
|
||||
*
|
||||
* A good use-case for having a function is when using the URL for authentication,
|
||||
* where subsequent reconnects (due to auth) may have a refreshed identity token in
|
||||
* the URL.
|
||||
*/
|
||||
url: string | (() => Promise<string> | string);
|
||||
/**
|
||||
* Optional parameters, passed through the `payload` field with the `ConnectionInit` message,
|
||||
* that the client specifies when establishing a connection with the server. You can use this
|
||||
* for securely passing arguments for authentication.
|
||||
*
|
||||
* If you decide to return a promise, keep in mind that the server might kick you off if it
|
||||
* takes too long to resolve! Check the `connectionInitWaitTimeout` on the server for more info.
|
||||
*
|
||||
* Throwing an error from within this function will close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
connectionParams?: P | (() => Promise<P> | P);
|
||||
/**
|
||||
* Controls when should the connection be established.
|
||||
*
|
||||
* - `false`: Establish a connection immediately. Use `onNonLazyError` to handle errors.
|
||||
* - `true`: Establish a connection on first subscribe and close on last unsubscribe. Use
|
||||
* the subscription sink's `error` to handle errors.
|
||||
*
|
||||
* @default true
|
||||
*/
|
||||
lazy?: boolean;
|
||||
/**
|
||||
* Used ONLY when the client is in non-lazy mode (`lazy = false`). When
|
||||
* using this mode, the errors might have no sinks to report to; however,
|
||||
* to avoid swallowing errors, consider using `onNonLazyError`, which will
|
||||
* be called when either:
|
||||
* - An unrecoverable error/close event occurs
|
||||
* - Silent retry attempts have been exceeded
|
||||
*
|
||||
* After a client has errored out, it will NOT perform any automatic actions.
|
||||
*
|
||||
* The argument can be a websocket `CloseEvent` or an `Error`. To avoid bundling
|
||||
* DOM types, you should derive and assert the correct type. When receiving:
|
||||
* - A `CloseEvent`: retry attempts have been exceeded or the specific
|
||||
* close event is labeled as fatal (read more in `retryAttempts`).
|
||||
* - An `Error`: some internal issue has occured, all internal errors are
|
||||
* fatal by nature.
|
||||
*
|
||||
* @default console.error
|
||||
*/
|
||||
onNonLazyError?: (errorOrCloseEvent: unknown) => void;
|
||||
/**
|
||||
* How long should the client wait before closing the socket after the last oparation has
|
||||
* completed. This is meant to be used in combination with `lazy`. You might want to have
|
||||
* a calmdown time before actually closing the connection. Kinda' like a lazy close "debounce".
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
lazyCloseTimeout?: number;
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages, naimly server pings. Internally
|
||||
* dispatches the `PingMessage` type to the server and expects a `PongMessage` in response.
|
||||
* This helps with making sure that the connection with the server is alive and working.
|
||||
*
|
||||
* Timeout countdown starts from the moment the socket was opened and subsequently
|
||||
* after every received `PongMessage`.
|
||||
*
|
||||
* Note that NOTHING will happen automatically with the client if the server never
|
||||
* responds to a `PingMessage` with a `PongMessage`. If you want the connection to close,
|
||||
* you should implement your own logic on top of the client. A simple example looks like this:
|
||||
*
|
||||
* ```js
|
||||
* import { createClient } from 'graphql-ws';
|
||||
*
|
||||
* let activeSocket, timedOut;
|
||||
* createClient({
|
||||
* url: 'ws://i.time.out:4000/after-5/seconds',
|
||||
* keepAlive: 10_000, // ping server every 10 seconds
|
||||
* on: {
|
||||
* connected: (socket) => (activeSocket = socket),
|
||||
* ping: (received) => {
|
||||
* if (!received) // sent
|
||||
* timedOut = setTimeout(() => {
|
||||
* if (activeSocket.readyState === WebSocket.OPEN)
|
||||
* activeSocket.close(4408, 'Request Timeout');
|
||||
* }, 5_000); // wait 5 seconds for the pong and then close the connection
|
||||
* },
|
||||
* pong: (received) => {
|
||||
* if (received) clearTimeout(timedOut); // pong is received, clear connection close timeout
|
||||
* },
|
||||
* },
|
||||
* });
|
||||
* ```
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
keepAlive?: number;
|
||||
/**
|
||||
* The amount of time for which the client will wait
|
||||
* for `ConnectionAck` message.
|
||||
*
|
||||
* Set the value to `Infinity`, `''`, `0`, `null` or `undefined` to skip waiting.
|
||||
*
|
||||
* If the wait timeout has passed and the server
|
||||
* has not responded with `ConnectionAck` message,
|
||||
* the client will terminate the socket by
|
||||
* dispatching a close event `4418: Connection acknowledgement timeout`
|
||||
*
|
||||
* @default 0
|
||||
*/
|
||||
connectionAckWaitTimeout?: number;
|
||||
/**
|
||||
* Disable sending the `PongMessage` automatically.
|
||||
*
|
||||
* Useful for when integrating your own custom client pinger that performs
|
||||
* custom actions before responding to a ping, or to pass along the optional pong
|
||||
* message payload. Please check the readme recipes for a concrete example.
|
||||
*/
|
||||
disablePong?: boolean;
|
||||
/**
|
||||
* How many times should the client try to reconnect on abnormal socket closure before it errors out?
|
||||
*
|
||||
* The library classifies the following close events as fatal:
|
||||
* - _All internal WebSocket fatal close codes (check `isFatalInternalCloseCode` in `src/client.ts` for exact list)_
|
||||
* - `4500: Internal server error`
|
||||
* - `4005: Internal client error`
|
||||
* - `4400: Bad request`
|
||||
* - `4004: Bad response`
|
||||
* - `4401: Unauthorized` _tried subscribing before connect ack_
|
||||
* - `4406: Subprotocol not acceptable`
|
||||
* - `4409: Subscriber for <id> already exists` _distinction is very important_
|
||||
* - `4429: Too many initialisation requests`
|
||||
*
|
||||
* In addition to the aforementioned close events, any _non-CloseEvent_ connection problem
|
||||
* is considered fatal by default. However, this specific behaviour can be altered by using
|
||||
* the `shouldRetry` option.
|
||||
*
|
||||
* These events are reported immediately and the client will not reconnect.
|
||||
*
|
||||
* @default 5
|
||||
*/
|
||||
retryAttempts?: number;
|
||||
/**
|
||||
* Control the wait time between retries. You may implement your own strategy
|
||||
* by timing the resolution of the returned promise with the retries count.
|
||||
* `retries` argument counts actual connection attempts, so it will begin with
|
||||
* 0 after the first retryable disconnect.
|
||||
*
|
||||
* @default 'Randomised exponential backoff'
|
||||
*/
|
||||
retryWait?: (retries: number) => Promise<void>;
|
||||
/**
|
||||
* Check if the close event or connection error is fatal. If you return `false`,
|
||||
* the client will fail immediately without additional retries; however, if you
|
||||
* return `true`, the client will keep retrying until the `retryAttempts` have
|
||||
* been exceeded.
|
||||
*
|
||||
* The argument is whatever has been thrown during the connection phase.
|
||||
*
|
||||
* Beware, the library classifies a few close events as fatal regardless of
|
||||
* what is returned here. They are listed in the documentation of the `retryAttempts`
|
||||
* option.
|
||||
*
|
||||
* @default 'Only `CloseEvent`s'
|
||||
*/
|
||||
shouldRetry?: (errOrCloseEvent: unknown) => boolean;
|
||||
/**
|
||||
* @deprecated Use `shouldRetry` instead.
|
||||
*
|
||||
* Check if the close event or connection error is fatal. If you return `true`,
|
||||
* the client will fail immediately without additional retries; however, if you
|
||||
* return `false`, the client will keep retrying until the `retryAttempts` have
|
||||
* been exceeded.
|
||||
*
|
||||
* The argument is either a WebSocket `CloseEvent` or an error thrown during
|
||||
* the connection phase.
|
||||
*
|
||||
* Beware, the library classifies a few close events as fatal regardless of
|
||||
* what is returned. They are listed in the documentation of the `retryAttempts`
|
||||
* option.
|
||||
*
|
||||
* @default 'Any non-`CloseEvent`'
|
||||
*/
|
||||
isFatalConnectionProblem?: (errOrCloseEvent: unknown) => boolean;
|
||||
/**
|
||||
* Register listeners before initialising the client. This way
|
||||
* you can ensure to catch all client relevant emitted events.
|
||||
*
|
||||
* The listeners passed in will **always** be the first ones
|
||||
* to get the emitted event before other registered listeners.
|
||||
*/
|
||||
on?: Partial<{
|
||||
[event in Event]: EventListener<event>;
|
||||
}>;
|
||||
/**
|
||||
* A custom WebSocket implementation to use instead of the
|
||||
* one provided by the global scope. Mostly useful for when
|
||||
* using the client outside of the browser environment.
|
||||
*/
|
||||
webSocketImpl?: unknown;
|
||||
/**
|
||||
* A custom ID generator for identifying subscriptions.
|
||||
*
|
||||
* The default generates a v4 UUID to be used as the ID using `Math`
|
||||
* as the random number generator. Supply your own generator
|
||||
* in case you need more uniqueness.
|
||||
*
|
||||
* Reference: https://gist.github.com/jed/982883
|
||||
*/
|
||||
generateID?: (payload: SubscribePayload) => ID;
|
||||
/**
|
||||
* An optional override for the JSON.parse function used to hydrate
|
||||
* incoming messages to this client. Useful for parsing custom datatypes
|
||||
* out of the incoming JSON.
|
||||
*/
|
||||
jsonMessageReviver?: JSONMessageReviver;
|
||||
/**
|
||||
* An optional override for the JSON.stringify function used to serialize
|
||||
* outgoing messages from this client. Useful for serializing custom
|
||||
* datatypes out to the client.
|
||||
*/
|
||||
jsonMessageReplacer?: JSONMessageReplacer;
|
||||
}
|
||||
/** @category Client */
|
||||
export interface Client extends Disposable {
|
||||
/**
|
||||
* Listens on the client which dispatches events about the socket state.
|
||||
*/
|
||||
on<E extends Event>(event: E, listener: EventListener<E>): () => void;
|
||||
/**
|
||||
* Subscribes through the WebSocket following the config parameters. It
|
||||
* uses the `sink` to emit received data or errors. Returns a _cleanup_
|
||||
* function used for dropping the subscription and cleaning stuff up.
|
||||
*/
|
||||
subscribe<Data = Record<string, unknown>, Extensions = unknown>(payload: SubscribePayload, sink: Sink<ExecutionResult<Data, Extensions>>): () => void;
|
||||
/**
|
||||
* Subscribes and iterates over emitted results from the WebSocket
|
||||
* through the returned async iterator.
|
||||
*/
|
||||
iterate<Data = Record<string, unknown>, Extensions = unknown>(payload: SubscribePayload): AsyncIterableIterator<ExecutionResult<Data, Extensions>>;
|
||||
/**
|
||||
* Terminates the WebSocket abruptly and immediately.
|
||||
*
|
||||
* A close event `4499: Terminated` is issued to the current WebSocket and a
|
||||
* syntetic {@link TerminatedCloseEvent} is immediately emitted without waiting for
|
||||
* the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
terminate(): void;
|
||||
}
|
||||
/**
|
||||
* Creates a disposable GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export declare function createClient<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload']>(options: ClientOptions<P>): Client;
|
||||
/**
|
||||
* A syntetic close event `4499: Terminated` is issued to the current to immediately
|
||||
* close the connection without waiting for the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
export declare class TerminatedCloseEvent extends Error {
|
||||
name: string;
|
||||
message: string;
|
||||
code: number;
|
||||
reason: string;
|
||||
wasClean: boolean;
|
||||
}
|
||||
575
graphql-subscription-express/node_modules/graphql-ws/lib/client.js
generated
vendored
Normal file
575
graphql-subscription-express/node_modules/graphql-ws/lib/client.js
generated
vendored
Normal file
@@ -0,0 +1,575 @@
|
||||
"use strict";
|
||||
/**
|
||||
*
|
||||
* client
|
||||
*
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
||||
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
||||
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
||||
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
||||
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
||||
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
||||
function fulfill(value) { resume("next", value); }
|
||||
function reject(value) { resume("throw", value); }
|
||||
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TerminatedCloseEvent = exports.createClient = void 0;
|
||||
const common_1 = require("./common");
|
||||
const utils_1 = require("./utils");
|
||||
/** This file is the entry point for browsers, re-export common elements. */
|
||||
__exportStar(require("./common"), exports);
|
||||
/**
|
||||
* Creates a disposable GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
function createClient(options) {
|
||||
const { url, connectionParams, lazy = true, onNonLazyError = console.error, lazyCloseTimeout: lazyCloseTimeoutMs = 0, keepAlive = 0, disablePong, connectionAckWaitTimeout = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
|
||||
let retryDelay = 1000; // start with 1s delay
|
||||
for (let i = 0; i < retries; i++) {
|
||||
retryDelay *= 2;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelay +
|
||||
// add random timeout from 300ms to 3s
|
||||
Math.floor(Math.random() * (3000 - 300) + 300)));
|
||||
}, shouldRetry = isLikeCloseEvent, isFatalConnectionProblem, on, webSocketImpl,
|
||||
/**
|
||||
* Generates a v4 UUID to be used as the ID using `Math`
|
||||
* as the random number generator. Supply your own generator
|
||||
* in case you need more uniqueness.
|
||||
*
|
||||
* Reference: https://gist.github.com/jed/982883
|
||||
*/
|
||||
generateID = function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
|
||||
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 ||
|
||||
// @ts-expect-error: Support more browsers
|
||||
global.MozWebSocket;
|
||||
}
|
||||
else if (typeof window !== 'undefined') {
|
||||
ws =
|
||||
window.WebSocket ||
|
||||
// @ts-expect-error: Support more browsers
|
||||
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;
|
||||
// websocket status emitter, subscriptions are handled differently
|
||||
const emitter = (() => {
|
||||
const message = (() => {
|
||||
const listeners = {};
|
||||
return {
|
||||
on(id, listener) {
|
||||
listeners[id] = listener;
|
||||
return () => {
|
||||
delete listeners[id];
|
||||
};
|
||||
},
|
||||
emit(message) {
|
||||
var _a;
|
||||
if ('id' in message)
|
||||
(_a = listeners[message.id]) === null || _a === void 0 ? void 0 : _a.call(listeners, message);
|
||||
},
|
||||
};
|
||||
})();
|
||||
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) {
|
||||
// we copy the listeners so that unlistens dont "pull the rug under our feet"
|
||||
for (const listener of [...listeners[event]]) {
|
||||
// @ts-expect-error: The args should fit
|
||||
listener(...args);
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
// invokes the callback either when an error or closed event is emitted,
|
||||
// first one that gets called prevails, other emissions are ignored
|
||||
function errorOrClosed(cb) {
|
||||
const listening = [
|
||||
// errors are fatal and more critical than close events, throw them first
|
||||
emitter.on('error', (err) => {
|
||||
listening.forEach((unlisten) => unlisten());
|
||||
cb(err);
|
||||
}),
|
||||
// closes can be graceful and not fatal, throw them second (if error didnt throw)
|
||||
emitter.on('closed', (event) => {
|
||||
listening.forEach((unlisten) => unlisten());
|
||||
cb(event);
|
||||
}),
|
||||
];
|
||||
}
|
||||
let connecting, locks = 0, lazyCloseTimeout, retrying = false, retries = 0, disposed = false;
|
||||
async function connect() {
|
||||
// clear the lazy close timeout immediatelly so that close gets debounced
|
||||
// see: https://github.com/enisdenjo/graphql-ws/issues/388
|
||||
clearTimeout(lazyCloseTimeout);
|
||||
const [socket, throwOnClose] = await (connecting !== null && connecting !== void 0 ? connecting : (connecting = new Promise((connected, denied) => (async () => {
|
||||
if (retrying) {
|
||||
await retryWait(retries);
|
||||
// subscriptions might complete while waiting for retry
|
||||
if (!locks) {
|
||||
connecting = undefined;
|
||||
return denied({ code: 1000, reason: 'All Subscriptions Gone' });
|
||||
}
|
||||
retries++;
|
||||
}
|
||||
emitter.emit('connecting', retrying);
|
||||
const socket = new WebSocketImpl(typeof url === 'function' ? await url() : url, common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL);
|
||||
let connectionAckTimeout, queuedPing;
|
||||
function enqueuePing() {
|
||||
if (isFinite(keepAlive) && keepAlive > 0) {
|
||||
clearTimeout(queuedPing); // in case where a pong was received before a ping (this is valid behaviour)
|
||||
queuedPing = setTimeout(() => {
|
||||
if (socket.readyState === WebSocketImpl.OPEN) {
|
||||
socket.send((0, common_1.stringifyMessage)({ type: common_1.MessageType.Ping }));
|
||||
emitter.emit('ping', false, undefined);
|
||||
}
|
||||
}, keepAlive);
|
||||
}
|
||||
}
|
||||
errorOrClosed((errOrEvent) => {
|
||||
connecting = undefined;
|
||||
clearTimeout(connectionAckTimeout);
|
||||
clearTimeout(queuedPing);
|
||||
denied(errOrEvent);
|
||||
if (errOrEvent instanceof TerminatedCloseEvent) {
|
||||
socket.close(4499, 'Terminated'); // close event is artificial and emitted manually, see `Client.terminate()` below
|
||||
socket.onerror = null;
|
||||
socket.onclose = null;
|
||||
}
|
||||
});
|
||||
socket.onerror = (err) => emitter.emit('error', err);
|
||||
socket.onclose = (event) => emitter.emit('closed', event);
|
||||
socket.onopen = async () => {
|
||||
try {
|
||||
emitter.emit('opened', socket);
|
||||
const payload = typeof connectionParams === 'function'
|
||||
? await connectionParams()
|
||||
: connectionParams;
|
||||
// connectionParams might take too long causing the server to kick off the client
|
||||
// the necessary error/close event is already reported - simply stop execution
|
||||
if (socket.readyState !== WebSocketImpl.OPEN)
|
||||
return;
|
||||
socket.send((0, common_1.stringifyMessage)(payload
|
||||
? {
|
||||
type: common_1.MessageType.ConnectionInit,
|
||||
payload,
|
||||
}
|
||||
: {
|
||||
type: common_1.MessageType.ConnectionInit,
|
||||
// payload is completely absent if not provided
|
||||
}, replacer));
|
||||
if (isFinite(connectionAckWaitTimeout) &&
|
||||
connectionAckWaitTimeout > 0) {
|
||||
connectionAckTimeout = setTimeout(() => {
|
||||
socket.close(common_1.CloseCode.ConnectionAcknowledgementTimeout, 'Connection acknowledgement timeout');
|
||||
}, connectionAckWaitTimeout);
|
||||
}
|
||||
enqueuePing(); // enqueue ping (noop if disabled)
|
||||
}
|
||||
catch (err) {
|
||||
emitter.emit('error', err);
|
||||
socket.close(common_1.CloseCode.InternalClientError, (0, utils_1.limitCloseReason)(err instanceof Error ? err.message : new Error(err).message, 'Internal client error'));
|
||||
}
|
||||
};
|
||||
let acknowledged = false;
|
||||
socket.onmessage = ({ data }) => {
|
||||
try {
|
||||
const message = (0, common_1.parseMessage)(data, reviver);
|
||||
emitter.emit('message', message);
|
||||
if (message.type === 'ping' || message.type === 'pong') {
|
||||
emitter.emit(message.type, true, message.payload); // received
|
||||
if (message.type === 'pong') {
|
||||
enqueuePing(); // enqueue next ping (noop if disabled)
|
||||
}
|
||||
else if (!disablePong) {
|
||||
// respond with pong on ping
|
||||
socket.send((0, common_1.stringifyMessage)(message.payload
|
||||
? {
|
||||
type: common_1.MessageType.Pong,
|
||||
payload: message.payload,
|
||||
}
|
||||
: {
|
||||
type: common_1.MessageType.Pong,
|
||||
// payload is completely absent if not provided
|
||||
}));
|
||||
emitter.emit('pong', false, message.payload);
|
||||
}
|
||||
return; // ping and pongs can be received whenever
|
||||
}
|
||||
if (acknowledged)
|
||||
return; // already connected and acknowledged
|
||||
if (message.type !== common_1.MessageType.ConnectionAck)
|
||||
throw new Error(`First message cannot be of type ${message.type}`);
|
||||
clearTimeout(connectionAckTimeout);
|
||||
acknowledged = true;
|
||||
emitter.emit('connected', socket, message.payload, retrying); // connected = socket opened + acknowledged
|
||||
retrying = false; // future lazy connects are not retries
|
||||
retries = 0; // reset the retries on connect
|
||||
connected([
|
||||
socket,
|
||||
new Promise((_, reject) => errorOrClosed(reject)),
|
||||
]);
|
||||
}
|
||||
catch (err) {
|
||||
socket.onmessage = null; // stop reading messages as soon as reading breaks once
|
||||
emitter.emit('error', err);
|
||||
socket.close(common_1.CloseCode.BadResponse, (0, utils_1.limitCloseReason)(err instanceof Error ? err.message : new Error(err).message, 'Bad response'));
|
||||
}
|
||||
};
|
||||
})())));
|
||||
// if the provided socket is in a closing state, wait for the throw on close
|
||||
if (socket.readyState === WebSocketImpl.CLOSING)
|
||||
await throwOnClose;
|
||||
let release = () => {
|
||||
// releases this connection
|
||||
};
|
||||
const released = new Promise((resolve) => (release = resolve));
|
||||
return [
|
||||
socket,
|
||||
release,
|
||||
Promise.race([
|
||||
// wait for
|
||||
released.then(() => {
|
||||
if (!locks) {
|
||||
// and if no more locks are present, complete the connection
|
||||
const complete = () => socket.close(1000, 'Normal Closure');
|
||||
if (isFinite(lazyCloseTimeoutMs) && lazyCloseTimeoutMs > 0) {
|
||||
// if the keepalive is set, allow for the specified calmdown time and
|
||||
// then complete if the socket is still open.
|
||||
lazyCloseTimeout = setTimeout(() => {
|
||||
if (socket.readyState === WebSocketImpl.OPEN)
|
||||
complete();
|
||||
}, lazyCloseTimeoutMs);
|
||||
}
|
||||
else {
|
||||
// otherwise complete immediately
|
||||
complete();
|
||||
}
|
||||
}
|
||||
}),
|
||||
// or
|
||||
throwOnClose,
|
||||
]),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Checks the `connect` problem and evaluates if the client should retry.
|
||||
*/
|
||||
function shouldRetryConnectOrThrow(errOrCloseEvent) {
|
||||
// some close codes are worth reporting immediately
|
||||
if (isLikeCloseEvent(errOrCloseEvent) &&
|
||||
(isFatalInternalCloseCode(errOrCloseEvent.code) ||
|
||||
[
|
||||
common_1.CloseCode.InternalServerError,
|
||||
common_1.CloseCode.InternalClientError,
|
||||
common_1.CloseCode.BadRequest,
|
||||
common_1.CloseCode.BadResponse,
|
||||
common_1.CloseCode.Unauthorized,
|
||||
// CloseCode.Forbidden, might grant access out after retry
|
||||
common_1.CloseCode.SubprotocolNotAcceptable,
|
||||
// CloseCode.ConnectionInitialisationTimeout, might not time out after retry
|
||||
// CloseCode.ConnectionAcknowledgementTimeout, might not time out after retry
|
||||
common_1.CloseCode.SubscriberAlreadyExists,
|
||||
common_1.CloseCode.TooManyInitialisationRequests,
|
||||
// 4499, // Terminated, probably because the socket froze, we want to retry
|
||||
].includes(errOrCloseEvent.code)))
|
||||
throw errOrCloseEvent;
|
||||
// client was disposed, no retries should proceed regardless
|
||||
if (disposed)
|
||||
return false;
|
||||
// normal closure (possibly all subscriptions have completed)
|
||||
// if no locks were acquired in the meantime, shouldnt try again
|
||||
if (isLikeCloseEvent(errOrCloseEvent) && errOrCloseEvent.code === 1000)
|
||||
return locks > 0;
|
||||
// retries are not allowed or we tried to many times, report error
|
||||
if (!retryAttempts || retries >= retryAttempts)
|
||||
throw errOrCloseEvent;
|
||||
// throw non-retryable connection problems
|
||||
if (!shouldRetry(errOrCloseEvent))
|
||||
throw errOrCloseEvent;
|
||||
// @deprecated throw fatal connection problems immediately
|
||||
if (isFatalConnectionProblem === null || isFatalConnectionProblem === void 0 ? void 0 : isFatalConnectionProblem(errOrCloseEvent))
|
||||
throw errOrCloseEvent;
|
||||
// looks good, start retrying
|
||||
return (retrying = true);
|
||||
}
|
||||
// in non-lazy (hot?) mode always hold one connection lock to persist the socket
|
||||
if (!lazy) {
|
||||
(async () => {
|
||||
locks++;
|
||||
for (;;) {
|
||||
try {
|
||||
const [, , throwOnClose] = await connect();
|
||||
await throwOnClose; // will always throw because releaser is not used
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
try {
|
||||
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
|
||||
return;
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
// report thrown error, no further retries
|
||||
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
function subscribe(payload, sink) {
|
||||
const id = generateID(payload);
|
||||
let done = false, errored = false, releaser = () => {
|
||||
// for handling completions before connect
|
||||
locks--;
|
||||
done = true;
|
||||
};
|
||||
(async () => {
|
||||
locks++;
|
||||
for (;;) {
|
||||
try {
|
||||
const [socket, release, waitForReleaseOrThrowOnClose] = await connect();
|
||||
// if done while waiting for connect, release the connection lock right away
|
||||
if (done)
|
||||
return release();
|
||||
const unlisten = emitter.onMessage(id, (message) => {
|
||||
switch (message.type) {
|
||||
case common_1.MessageType.Next: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- payload will fit type
|
||||
sink.next(message.payload);
|
||||
return;
|
||||
}
|
||||
case common_1.MessageType.Error: {
|
||||
(errored = true), (done = true);
|
||||
sink.error(message.payload);
|
||||
releaser();
|
||||
return;
|
||||
}
|
||||
case common_1.MessageType.Complete: {
|
||||
done = true;
|
||||
releaser(); // release completes the sink
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.send((0, common_1.stringifyMessage)({
|
||||
id,
|
||||
type: common_1.MessageType.Subscribe,
|
||||
payload,
|
||||
}, replacer));
|
||||
releaser = () => {
|
||||
if (!done && socket.readyState === WebSocketImpl.OPEN)
|
||||
// if not completed already and socket is open, send complete message to server on release
|
||||
socket.send((0, common_1.stringifyMessage)({
|
||||
id,
|
||||
type: common_1.MessageType.Complete,
|
||||
}, replacer));
|
||||
locks--;
|
||||
done = true;
|
||||
release();
|
||||
};
|
||||
// either the releaser will be called, connection completed and
|
||||
// the promise resolved or the socket closed and the promise rejected.
|
||||
// whatever happens though, we want to stop listening for messages
|
||||
await waitForReleaseOrThrowOnClose.finally(unlisten);
|
||||
return; // completed, shouldnt try again
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
|
||||
return;
|
||||
}
|
||||
}
|
||||
})()
|
||||
.then(() => {
|
||||
// delivering either an error or a complete terminates the sequence
|
||||
if (!errored)
|
||||
sink.complete();
|
||||
}) // resolves on release or normal closure
|
||||
.catch((err) => {
|
||||
sink.error(err);
|
||||
}); // rejects on close events and errors
|
||||
return () => {
|
||||
// dispose only of active subscriptions
|
||||
if (!done)
|
||||
releaser();
|
||||
};
|
||||
}
|
||||
return {
|
||||
on: emitter.on,
|
||||
subscribe,
|
||||
iterate(request) {
|
||||
const pending = [];
|
||||
const deferred = {
|
||||
done: false,
|
||||
error: null,
|
||||
resolve: () => {
|
||||
// noop
|
||||
},
|
||||
};
|
||||
const dispose = subscribe(request, {
|
||||
next(val) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
pending.push(val);
|
||||
deferred.resolve();
|
||||
},
|
||||
error(err) {
|
||||
deferred.done = true;
|
||||
deferred.error = err;
|
||||
deferred.resolve();
|
||||
},
|
||||
complete() {
|
||||
deferred.done = true;
|
||||
deferred.resolve();
|
||||
},
|
||||
});
|
||||
const iterator = (function iterator() {
|
||||
return __asyncGenerator(this, arguments, function* iterator_1() {
|
||||
for (;;) {
|
||||
if (!pending.length) {
|
||||
// only wait if there are no pending messages available
|
||||
yield __await(new Promise((resolve) => (deferred.resolve = resolve)));
|
||||
}
|
||||
// first flush
|
||||
while (pending.length) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
yield yield __await(pending.shift());
|
||||
}
|
||||
// then error
|
||||
if (deferred.error) {
|
||||
throw deferred.error;
|
||||
}
|
||||
// or complete
|
||||
if (deferred.done) {
|
||||
return yield __await(void 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
iterator.throw = async (err) => {
|
||||
if (!deferred.done) {
|
||||
deferred.done = true;
|
||||
deferred.error = err;
|
||||
deferred.resolve();
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
};
|
||||
iterator.return = async () => {
|
||||
dispose();
|
||||
return { done: true, value: undefined };
|
||||
};
|
||||
return iterator;
|
||||
},
|
||||
async dispose() {
|
||||
disposed = true;
|
||||
if (connecting) {
|
||||
// if there is a connection, close it
|
||||
const [socket] = await connecting;
|
||||
socket.close(1000, 'Normal Closure');
|
||||
}
|
||||
},
|
||||
terminate() {
|
||||
if (connecting) {
|
||||
// only if there is a connection
|
||||
emitter.emit('closed', new TerminatedCloseEvent());
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
exports.createClient = createClient;
|
||||
/**
|
||||
* A syntetic close event `4499: Terminated` is issued to the current to immediately
|
||||
* close the connection without waiting for the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
class TerminatedCloseEvent extends Error {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.name = 'TerminatedCloseEvent';
|
||||
this.message = '4499: Terminated';
|
||||
this.code = 4499;
|
||||
this.reason = 'Terminated';
|
||||
this.wasClean = false;
|
||||
}
|
||||
}
|
||||
exports.TerminatedCloseEvent = TerminatedCloseEvent;
|
||||
function isLikeCloseEvent(val) {
|
||||
return (0, utils_1.isObject)(val) && 'code' in val && 'reason' in val;
|
||||
}
|
||||
function isFatalInternalCloseCode(code) {
|
||||
if ([
|
||||
1000,
|
||||
1001,
|
||||
1006,
|
||||
1005,
|
||||
1012,
|
||||
1013,
|
||||
1014, // Bad Gateway
|
||||
].includes(code))
|
||||
return false;
|
||||
// all other internal errors are fatal
|
||||
return code >= 1000 && code <= 1999;
|
||||
}
|
||||
function isWebSocket(val) {
|
||||
return (typeof val === 'function' &&
|
||||
'constructor' in val &&
|
||||
'CLOSED' in val &&
|
||||
'CLOSING' in val &&
|
||||
'CONNECTING' in val &&
|
||||
'OPEN' in val);
|
||||
}
|
||||
556
graphql-subscription-express/node_modules/graphql-ws/lib/client.mjs
generated
vendored
Normal file
556
graphql-subscription-express/node_modules/graphql-ws/lib/client.mjs
generated
vendored
Normal file
@@ -0,0 +1,556 @@
|
||||
/**
|
||||
*
|
||||
* client
|
||||
*
|
||||
*/
|
||||
var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
||||
var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
||||
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
||||
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
||||
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
||||
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
||||
function fulfill(value) { resume("next", value); }
|
||||
function reject(value) { resume("throw", value); }
|
||||
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
||||
};
|
||||
import { GRAPHQL_TRANSPORT_WS_PROTOCOL, CloseCode, MessageType, parseMessage, stringifyMessage, } from './common.mjs';
|
||||
import { isObject, limitCloseReason } from './utils.mjs';
|
||||
/** This file is the entry point for browsers, re-export common elements. */
|
||||
export * from './common.mjs';
|
||||
/**
|
||||
* Creates a disposable GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
export function createClient(options) {
|
||||
const { url, connectionParams, lazy = true, onNonLazyError = console.error, lazyCloseTimeout: lazyCloseTimeoutMs = 0, keepAlive = 0, disablePong, connectionAckWaitTimeout = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
|
||||
let retryDelay = 1000; // start with 1s delay
|
||||
for (let i = 0; i < retries; i++) {
|
||||
retryDelay *= 2;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelay +
|
||||
// add random timeout from 300ms to 3s
|
||||
Math.floor(Math.random() * (3000 - 300) + 300)));
|
||||
}, shouldRetry = isLikeCloseEvent, isFatalConnectionProblem, on, webSocketImpl,
|
||||
/**
|
||||
* Generates a v4 UUID to be used as the ID using `Math`
|
||||
* as the random number generator. Supply your own generator
|
||||
* in case you need more uniqueness.
|
||||
*
|
||||
* Reference: https://gist.github.com/jed/982883
|
||||
*/
|
||||
generateID = function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
|
||||
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 ||
|
||||
// @ts-expect-error: Support more browsers
|
||||
global.MozWebSocket;
|
||||
}
|
||||
else if (typeof window !== 'undefined') {
|
||||
ws =
|
||||
window.WebSocket ||
|
||||
// @ts-expect-error: Support more browsers
|
||||
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;
|
||||
// websocket status emitter, subscriptions are handled differently
|
||||
const emitter = (() => {
|
||||
const message = (() => {
|
||||
const listeners = {};
|
||||
return {
|
||||
on(id, listener) {
|
||||
listeners[id] = listener;
|
||||
return () => {
|
||||
delete listeners[id];
|
||||
};
|
||||
},
|
||||
emit(message) {
|
||||
var _a;
|
||||
if ('id' in message)
|
||||
(_a = listeners[message.id]) === null || _a === void 0 ? void 0 : _a.call(listeners, message);
|
||||
},
|
||||
};
|
||||
})();
|
||||
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) {
|
||||
// we copy the listeners so that unlistens dont "pull the rug under our feet"
|
||||
for (const listener of [...listeners[event]]) {
|
||||
// @ts-expect-error: The args should fit
|
||||
listener(...args);
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
// invokes the callback either when an error or closed event is emitted,
|
||||
// first one that gets called prevails, other emissions are ignored
|
||||
function errorOrClosed(cb) {
|
||||
const listening = [
|
||||
// errors are fatal and more critical than close events, throw them first
|
||||
emitter.on('error', (err) => {
|
||||
listening.forEach((unlisten) => unlisten());
|
||||
cb(err);
|
||||
}),
|
||||
// closes can be graceful and not fatal, throw them second (if error didnt throw)
|
||||
emitter.on('closed', (event) => {
|
||||
listening.forEach((unlisten) => unlisten());
|
||||
cb(event);
|
||||
}),
|
||||
];
|
||||
}
|
||||
let connecting, locks = 0, lazyCloseTimeout, retrying = false, retries = 0, disposed = false;
|
||||
async function connect() {
|
||||
// clear the lazy close timeout immediatelly so that close gets debounced
|
||||
// see: https://github.com/enisdenjo/graphql-ws/issues/388
|
||||
clearTimeout(lazyCloseTimeout);
|
||||
const [socket, throwOnClose] = await (connecting !== null && connecting !== void 0 ? connecting : (connecting = new Promise((connected, denied) => (async () => {
|
||||
if (retrying) {
|
||||
await retryWait(retries);
|
||||
// subscriptions might complete while waiting for retry
|
||||
if (!locks) {
|
||||
connecting = undefined;
|
||||
return denied({ code: 1000, reason: 'All Subscriptions Gone' });
|
||||
}
|
||||
retries++;
|
||||
}
|
||||
emitter.emit('connecting', retrying);
|
||||
const socket = new WebSocketImpl(typeof url === 'function' ? await url() : url, GRAPHQL_TRANSPORT_WS_PROTOCOL);
|
||||
let connectionAckTimeout, queuedPing;
|
||||
function enqueuePing() {
|
||||
if (isFinite(keepAlive) && keepAlive > 0) {
|
||||
clearTimeout(queuedPing); // in case where a pong was received before a ping (this is valid behaviour)
|
||||
queuedPing = setTimeout(() => {
|
||||
if (socket.readyState === WebSocketImpl.OPEN) {
|
||||
socket.send(stringifyMessage({ type: MessageType.Ping }));
|
||||
emitter.emit('ping', false, undefined);
|
||||
}
|
||||
}, keepAlive);
|
||||
}
|
||||
}
|
||||
errorOrClosed((errOrEvent) => {
|
||||
connecting = undefined;
|
||||
clearTimeout(connectionAckTimeout);
|
||||
clearTimeout(queuedPing);
|
||||
denied(errOrEvent);
|
||||
if (errOrEvent instanceof TerminatedCloseEvent) {
|
||||
socket.close(4499, 'Terminated'); // close event is artificial and emitted manually, see `Client.terminate()` below
|
||||
socket.onerror = null;
|
||||
socket.onclose = null;
|
||||
}
|
||||
});
|
||||
socket.onerror = (err) => emitter.emit('error', err);
|
||||
socket.onclose = (event) => emitter.emit('closed', event);
|
||||
socket.onopen = async () => {
|
||||
try {
|
||||
emitter.emit('opened', socket);
|
||||
const payload = typeof connectionParams === 'function'
|
||||
? await connectionParams()
|
||||
: connectionParams;
|
||||
// connectionParams might take too long causing the server to kick off the client
|
||||
// the necessary error/close event is already reported - simply stop execution
|
||||
if (socket.readyState !== WebSocketImpl.OPEN)
|
||||
return;
|
||||
socket.send(stringifyMessage(payload
|
||||
? {
|
||||
type: MessageType.ConnectionInit,
|
||||
payload,
|
||||
}
|
||||
: {
|
||||
type: MessageType.ConnectionInit,
|
||||
// payload is completely absent if not provided
|
||||
}, replacer));
|
||||
if (isFinite(connectionAckWaitTimeout) &&
|
||||
connectionAckWaitTimeout > 0) {
|
||||
connectionAckTimeout = setTimeout(() => {
|
||||
socket.close(CloseCode.ConnectionAcknowledgementTimeout, 'Connection acknowledgement timeout');
|
||||
}, connectionAckWaitTimeout);
|
||||
}
|
||||
enqueuePing(); // enqueue ping (noop if disabled)
|
||||
}
|
||||
catch (err) {
|
||||
emitter.emit('error', err);
|
||||
socket.close(CloseCode.InternalClientError, limitCloseReason(err instanceof Error ? err.message : new Error(err).message, 'Internal client error'));
|
||||
}
|
||||
};
|
||||
let acknowledged = false;
|
||||
socket.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); // received
|
||||
if (message.type === 'pong') {
|
||||
enqueuePing(); // enqueue next ping (noop if disabled)
|
||||
}
|
||||
else if (!disablePong) {
|
||||
// respond with pong on ping
|
||||
socket.send(stringifyMessage(message.payload
|
||||
? {
|
||||
type: MessageType.Pong,
|
||||
payload: message.payload,
|
||||
}
|
||||
: {
|
||||
type: MessageType.Pong,
|
||||
// payload is completely absent if not provided
|
||||
}));
|
||||
emitter.emit('pong', false, message.payload);
|
||||
}
|
||||
return; // ping and pongs can be received whenever
|
||||
}
|
||||
if (acknowledged)
|
||||
return; // already connected and acknowledged
|
||||
if (message.type !== MessageType.ConnectionAck)
|
||||
throw new Error(`First message cannot be of type ${message.type}`);
|
||||
clearTimeout(connectionAckTimeout);
|
||||
acknowledged = true;
|
||||
emitter.emit('connected', socket, message.payload, retrying); // connected = socket opened + acknowledged
|
||||
retrying = false; // future lazy connects are not retries
|
||||
retries = 0; // reset the retries on connect
|
||||
connected([
|
||||
socket,
|
||||
new Promise((_, reject) => errorOrClosed(reject)),
|
||||
]);
|
||||
}
|
||||
catch (err) {
|
||||
socket.onmessage = null; // stop reading messages as soon as reading breaks once
|
||||
emitter.emit('error', err);
|
||||
socket.close(CloseCode.BadResponse, limitCloseReason(err instanceof Error ? err.message : new Error(err).message, 'Bad response'));
|
||||
}
|
||||
};
|
||||
})())));
|
||||
// if the provided socket is in a closing state, wait for the throw on close
|
||||
if (socket.readyState === WebSocketImpl.CLOSING)
|
||||
await throwOnClose;
|
||||
let release = () => {
|
||||
// releases this connection
|
||||
};
|
||||
const released = new Promise((resolve) => (release = resolve));
|
||||
return [
|
||||
socket,
|
||||
release,
|
||||
Promise.race([
|
||||
// wait for
|
||||
released.then(() => {
|
||||
if (!locks) {
|
||||
// and if no more locks are present, complete the connection
|
||||
const complete = () => socket.close(1000, 'Normal Closure');
|
||||
if (isFinite(lazyCloseTimeoutMs) && lazyCloseTimeoutMs > 0) {
|
||||
// if the keepalive is set, allow for the specified calmdown time and
|
||||
// then complete if the socket is still open.
|
||||
lazyCloseTimeout = setTimeout(() => {
|
||||
if (socket.readyState === WebSocketImpl.OPEN)
|
||||
complete();
|
||||
}, lazyCloseTimeoutMs);
|
||||
}
|
||||
else {
|
||||
// otherwise complete immediately
|
||||
complete();
|
||||
}
|
||||
}
|
||||
}),
|
||||
// or
|
||||
throwOnClose,
|
||||
]),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Checks the `connect` problem and evaluates if the client should retry.
|
||||
*/
|
||||
function shouldRetryConnectOrThrow(errOrCloseEvent) {
|
||||
// some close codes are worth reporting immediately
|
||||
if (isLikeCloseEvent(errOrCloseEvent) &&
|
||||
(isFatalInternalCloseCode(errOrCloseEvent.code) ||
|
||||
[
|
||||
CloseCode.InternalServerError,
|
||||
CloseCode.InternalClientError,
|
||||
CloseCode.BadRequest,
|
||||
CloseCode.BadResponse,
|
||||
CloseCode.Unauthorized,
|
||||
// CloseCode.Forbidden, might grant access out after retry
|
||||
CloseCode.SubprotocolNotAcceptable,
|
||||
// CloseCode.ConnectionInitialisationTimeout, might not time out after retry
|
||||
// CloseCode.ConnectionAcknowledgementTimeout, might not time out after retry
|
||||
CloseCode.SubscriberAlreadyExists,
|
||||
CloseCode.TooManyInitialisationRequests,
|
||||
// 4499, // Terminated, probably because the socket froze, we want to retry
|
||||
].includes(errOrCloseEvent.code)))
|
||||
throw errOrCloseEvent;
|
||||
// client was disposed, no retries should proceed regardless
|
||||
if (disposed)
|
||||
return false;
|
||||
// normal closure (possibly all subscriptions have completed)
|
||||
// if no locks were acquired in the meantime, shouldnt try again
|
||||
if (isLikeCloseEvent(errOrCloseEvent) && errOrCloseEvent.code === 1000)
|
||||
return locks > 0;
|
||||
// retries are not allowed or we tried to many times, report error
|
||||
if (!retryAttempts || retries >= retryAttempts)
|
||||
throw errOrCloseEvent;
|
||||
// throw non-retryable connection problems
|
||||
if (!shouldRetry(errOrCloseEvent))
|
||||
throw errOrCloseEvent;
|
||||
// @deprecated throw fatal connection problems immediately
|
||||
if (isFatalConnectionProblem === null || isFatalConnectionProblem === void 0 ? void 0 : isFatalConnectionProblem(errOrCloseEvent))
|
||||
throw errOrCloseEvent;
|
||||
// looks good, start retrying
|
||||
return (retrying = true);
|
||||
}
|
||||
// in non-lazy (hot?) mode always hold one connection lock to persist the socket
|
||||
if (!lazy) {
|
||||
(async () => {
|
||||
locks++;
|
||||
for (;;) {
|
||||
try {
|
||||
const [, , throwOnClose] = await connect();
|
||||
await throwOnClose; // will always throw because releaser is not used
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
try {
|
||||
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
|
||||
return;
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
// report thrown error, no further retries
|
||||
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
function subscribe(payload, sink) {
|
||||
const id = generateID(payload);
|
||||
let done = false, errored = false, releaser = () => {
|
||||
// for handling completions before connect
|
||||
locks--;
|
||||
done = true;
|
||||
};
|
||||
(async () => {
|
||||
locks++;
|
||||
for (;;) {
|
||||
try {
|
||||
const [socket, release, waitForReleaseOrThrowOnClose] = await connect();
|
||||
// if done while waiting for connect, release the connection lock right away
|
||||
if (done)
|
||||
return release();
|
||||
const unlisten = emitter.onMessage(id, (message) => {
|
||||
switch (message.type) {
|
||||
case MessageType.Next: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- payload will fit type
|
||||
sink.next(message.payload);
|
||||
return;
|
||||
}
|
||||
case MessageType.Error: {
|
||||
(errored = true), (done = true);
|
||||
sink.error(message.payload);
|
||||
releaser();
|
||||
return;
|
||||
}
|
||||
case MessageType.Complete: {
|
||||
done = true;
|
||||
releaser(); // release completes the sink
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.send(stringifyMessage({
|
||||
id,
|
||||
type: MessageType.Subscribe,
|
||||
payload,
|
||||
}, replacer));
|
||||
releaser = () => {
|
||||
if (!done && socket.readyState === WebSocketImpl.OPEN)
|
||||
// if not completed already and socket is open, send complete message to server on release
|
||||
socket.send(stringifyMessage({
|
||||
id,
|
||||
type: MessageType.Complete,
|
||||
}, replacer));
|
||||
locks--;
|
||||
done = true;
|
||||
release();
|
||||
};
|
||||
// either the releaser will be called, connection completed and
|
||||
// the promise resolved or the socket closed and the promise rejected.
|
||||
// whatever happens though, we want to stop listening for messages
|
||||
await waitForReleaseOrThrowOnClose.finally(unlisten);
|
||||
return; // completed, shouldnt try again
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
|
||||
return;
|
||||
}
|
||||
}
|
||||
})()
|
||||
.then(() => {
|
||||
// delivering either an error or a complete terminates the sequence
|
||||
if (!errored)
|
||||
sink.complete();
|
||||
}) // resolves on release or normal closure
|
||||
.catch((err) => {
|
||||
sink.error(err);
|
||||
}); // rejects on close events and errors
|
||||
return () => {
|
||||
// dispose only of active subscriptions
|
||||
if (!done)
|
||||
releaser();
|
||||
};
|
||||
}
|
||||
return {
|
||||
on: emitter.on,
|
||||
subscribe,
|
||||
iterate(request) {
|
||||
const pending = [];
|
||||
const deferred = {
|
||||
done: false,
|
||||
error: null,
|
||||
resolve: () => {
|
||||
// noop
|
||||
},
|
||||
};
|
||||
const dispose = subscribe(request, {
|
||||
next(val) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
pending.push(val);
|
||||
deferred.resolve();
|
||||
},
|
||||
error(err) {
|
||||
deferred.done = true;
|
||||
deferred.error = err;
|
||||
deferred.resolve();
|
||||
},
|
||||
complete() {
|
||||
deferred.done = true;
|
||||
deferred.resolve();
|
||||
},
|
||||
});
|
||||
const iterator = (function iterator() {
|
||||
return __asyncGenerator(this, arguments, function* iterator_1() {
|
||||
for (;;) {
|
||||
if (!pending.length) {
|
||||
// only wait if there are no pending messages available
|
||||
yield __await(new Promise((resolve) => (deferred.resolve = resolve)));
|
||||
}
|
||||
// first flush
|
||||
while (pending.length) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
yield yield __await(pending.shift());
|
||||
}
|
||||
// then error
|
||||
if (deferred.error) {
|
||||
throw deferred.error;
|
||||
}
|
||||
// or complete
|
||||
if (deferred.done) {
|
||||
return yield __await(void 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
iterator.throw = async (err) => {
|
||||
if (!deferred.done) {
|
||||
deferred.done = true;
|
||||
deferred.error = err;
|
||||
deferred.resolve();
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
};
|
||||
iterator.return = async () => {
|
||||
dispose();
|
||||
return { done: true, value: undefined };
|
||||
};
|
||||
return iterator;
|
||||
},
|
||||
async dispose() {
|
||||
disposed = true;
|
||||
if (connecting) {
|
||||
// if there is a connection, close it
|
||||
const [socket] = await connecting;
|
||||
socket.close(1000, 'Normal Closure');
|
||||
}
|
||||
},
|
||||
terminate() {
|
||||
if (connecting) {
|
||||
// only if there is a connection
|
||||
emitter.emit('closed', new TerminatedCloseEvent());
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* A syntetic close event `4499: Terminated` is issued to the current to immediately
|
||||
* close the connection without waiting for the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
export class TerminatedCloseEvent extends Error {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.name = 'TerminatedCloseEvent';
|
||||
this.message = '4499: Terminated';
|
||||
this.code = 4499;
|
||||
this.reason = 'Terminated';
|
||||
this.wasClean = false;
|
||||
}
|
||||
}
|
||||
function isLikeCloseEvent(val) {
|
||||
return isObject(val) && 'code' in val && 'reason' in val;
|
||||
}
|
||||
function isFatalInternalCloseCode(code) {
|
||||
if ([
|
||||
1000,
|
||||
1001,
|
||||
1006,
|
||||
1005,
|
||||
1012,
|
||||
1013,
|
||||
1014, // Bad Gateway
|
||||
].includes(code))
|
||||
return false;
|
||||
// all other internal errors are fatal
|
||||
return code >= 1000 && code <= 1999;
|
||||
}
|
||||
function isWebSocket(val) {
|
||||
return (typeof val === 'function' &&
|
||||
'constructor' in val &&
|
||||
'CLOSED' in val &&
|
||||
'CLOSING' in val &&
|
||||
'CONNECTING' in val &&
|
||||
'OPEN' in val);
|
||||
}
|
||||
201
graphql-subscription-express/node_modules/graphql-ws/lib/common.d.mts
generated
vendored
Normal file
201
graphql-subscription-express/node_modules/graphql-ws/lib/common.d.mts
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
*
|
||||
* common
|
||||
*
|
||||
*/
|
||||
import { GraphQLError } from 'graphql';
|
||||
/**
|
||||
* The WebSocket sub-protocol used for the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare const GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws";
|
||||
/**
|
||||
* The deprecated subprotocol used by [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export declare const DEPRECATED_GRAPHQL_WS_PROTOCOL = "graphql-ws";
|
||||
/**
|
||||
* `graphql-ws` expected and standard close codes of the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare enum CloseCode {
|
||||
InternalServerError = 4500,
|
||||
InternalClientError = 4005,
|
||||
BadRequest = 4400,
|
||||
BadResponse = 4004,
|
||||
/** Tried subscribing before connect ack */
|
||||
Unauthorized = 4401,
|
||||
Forbidden = 4403,
|
||||
SubprotocolNotAcceptable = 4406,
|
||||
ConnectionInitialisationTimeout = 4408,
|
||||
ConnectionAcknowledgementTimeout = 4504,
|
||||
/** Subscriber distinction is very important */
|
||||
SubscriberAlreadyExists = 4409,
|
||||
TooManyInitialisationRequests = 4429
|
||||
}
|
||||
/**
|
||||
* ID is a string type alias representing
|
||||
* the globally unique ID used for identifying
|
||||
* subscriptions established by the client.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export type ID = string;
|
||||
/** @category Common */
|
||||
export interface Disposable {
|
||||
/** Dispose of the instance and clear up resources. */
|
||||
dispose: () => void | Promise<void>;
|
||||
}
|
||||
/**
|
||||
* A representation of any set of values over any amount of time.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export interface Sink<T = unknown> {
|
||||
/** Next value arriving. */
|
||||
next(value: T): void;
|
||||
/**
|
||||
* An error that has occured. Calling this function "closes" the sink.
|
||||
* Besides the errors being `Error` and `readonly GraphQLError[]`, it
|
||||
* can also be a `CloseEvent`, but to avoid bundling DOM typings because
|
||||
* the client can run in Node env too, you should assert the close event
|
||||
* type during implementation.
|
||||
*/
|
||||
error(error: unknown): void;
|
||||
/** The sink has completed. This function "closes" the sink. */
|
||||
complete(): void;
|
||||
}
|
||||
/**
|
||||
* Types of messages allowed to be sent by the client/server over the WS protocol.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare enum MessageType {
|
||||
ConnectionInit = "connection_init",
|
||||
ConnectionAck = "connection_ack",
|
||||
Ping = "ping",
|
||||
Pong = "pong",
|
||||
Subscribe = "subscribe",
|
||||
Next = "next",
|
||||
Error = "error",
|
||||
Complete = "complete"
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ConnectionInitMessage {
|
||||
readonly type: MessageType.ConnectionInit;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ConnectionAckMessage {
|
||||
readonly type: MessageType.ConnectionAck;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface PingMessage {
|
||||
readonly type: MessageType.Ping;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface PongMessage {
|
||||
readonly type: MessageType.Pong;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface SubscribeMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Subscribe;
|
||||
readonly payload: SubscribePayload;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface SubscribePayload {
|
||||
readonly operationName?: string | null;
|
||||
readonly query: string;
|
||||
readonly variables?: Record<string, unknown> | null;
|
||||
readonly extensions?: Record<string, unknown> | null;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ExecutionResult<Data = Record<string, unknown>, Extensions = Record<string, unknown>> {
|
||||
errors?: ReadonlyArray<GraphQLError>;
|
||||
data?: Data | null;
|
||||
hasNext?: boolean;
|
||||
extensions?: Extensions;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ExecutionPatchResult<Data = unknown, Extensions = Record<string, unknown>> {
|
||||
errors?: ReadonlyArray<GraphQLError>;
|
||||
data?: Data | null;
|
||||
path?: ReadonlyArray<string | number>;
|
||||
label?: string;
|
||||
hasNext: boolean;
|
||||
extensions?: Extensions;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface NextMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Next;
|
||||
readonly payload: ExecutionResult | ExecutionPatchResult;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ErrorMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Error;
|
||||
readonly payload: readonly GraphQLError[];
|
||||
}
|
||||
/** @category Common */
|
||||
export interface CompleteMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Complete;
|
||||
}
|
||||
/** @category Common */
|
||||
export type Message<T extends MessageType = MessageType> = T extends MessageType.ConnectionAck ? ConnectionAckMessage : T extends MessageType.ConnectionInit ? ConnectionInitMessage : T extends MessageType.Ping ? PingMessage : T extends MessageType.Pong ? PongMessage : T extends MessageType.Subscribe ? SubscribeMessage : T extends MessageType.Next ? NextMessage : T extends MessageType.Error ? ErrorMessage : T extends MessageType.Complete ? CompleteMessage : never;
|
||||
/**
|
||||
* Validates the message against the GraphQL over WebSocket Protocol.
|
||||
*
|
||||
* Invalid messages will throw descriptive errors.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function validateMessage(val: unknown): Message;
|
||||
/**
|
||||
* Checks if the provided value is a valid GraphQL over WebSocket message.
|
||||
*
|
||||
* @deprecated Use `validateMessage` instead.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function isMessage(val: unknown): val is Message;
|
||||
/**
|
||||
* Function for transforming values within a message during JSON parsing
|
||||
* The values are produced by parsing the incoming raw JSON.
|
||||
*
|
||||
* Read more about using it:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#using_the_reviver_parameter
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export type JSONMessageReviver = (this: any, key: string, value: any) => any;
|
||||
/**
|
||||
* Parses the raw websocket message data to a valid message.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function parseMessage(data: unknown, reviver?: JSONMessageReviver): Message;
|
||||
/**
|
||||
* Function that allows customization of the produced JSON string
|
||||
* for the elements of an outgoing `Message` object.
|
||||
*
|
||||
* Read more about using it:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export type JSONMessageReplacer = (this: any, key: string, value: any) => any;
|
||||
/**
|
||||
* Stringifies a valid message ready to be sent through the socket.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function stringifyMessage<T extends MessageType>(msg: Message<T>, replacer?: JSONMessageReplacer): string;
|
||||
201
graphql-subscription-express/node_modules/graphql-ws/lib/common.d.ts
generated
vendored
Normal file
201
graphql-subscription-express/node_modules/graphql-ws/lib/common.d.ts
generated
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
*
|
||||
* common
|
||||
*
|
||||
*/
|
||||
import { GraphQLError } from 'graphql';
|
||||
/**
|
||||
* The WebSocket sub-protocol used for the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare const GRAPHQL_TRANSPORT_WS_PROTOCOL = "graphql-transport-ws";
|
||||
/**
|
||||
* The deprecated subprotocol used by [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export declare const DEPRECATED_GRAPHQL_WS_PROTOCOL = "graphql-ws";
|
||||
/**
|
||||
* `graphql-ws` expected and standard close codes of the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare enum CloseCode {
|
||||
InternalServerError = 4500,
|
||||
InternalClientError = 4005,
|
||||
BadRequest = 4400,
|
||||
BadResponse = 4004,
|
||||
/** Tried subscribing before connect ack */
|
||||
Unauthorized = 4401,
|
||||
Forbidden = 4403,
|
||||
SubprotocolNotAcceptable = 4406,
|
||||
ConnectionInitialisationTimeout = 4408,
|
||||
ConnectionAcknowledgementTimeout = 4504,
|
||||
/** Subscriber distinction is very important */
|
||||
SubscriberAlreadyExists = 4409,
|
||||
TooManyInitialisationRequests = 4429
|
||||
}
|
||||
/**
|
||||
* ID is a string type alias representing
|
||||
* the globally unique ID used for identifying
|
||||
* subscriptions established by the client.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export type ID = string;
|
||||
/** @category Common */
|
||||
export interface Disposable {
|
||||
/** Dispose of the instance and clear up resources. */
|
||||
dispose: () => void | Promise<void>;
|
||||
}
|
||||
/**
|
||||
* A representation of any set of values over any amount of time.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export interface Sink<T = unknown> {
|
||||
/** Next value arriving. */
|
||||
next(value: T): void;
|
||||
/**
|
||||
* An error that has occured. Calling this function "closes" the sink.
|
||||
* Besides the errors being `Error` and `readonly GraphQLError[]`, it
|
||||
* can also be a `CloseEvent`, but to avoid bundling DOM typings because
|
||||
* the client can run in Node env too, you should assert the close event
|
||||
* type during implementation.
|
||||
*/
|
||||
error(error: unknown): void;
|
||||
/** The sink has completed. This function "closes" the sink. */
|
||||
complete(): void;
|
||||
}
|
||||
/**
|
||||
* Types of messages allowed to be sent by the client/server over the WS protocol.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare enum MessageType {
|
||||
ConnectionInit = "connection_init",
|
||||
ConnectionAck = "connection_ack",
|
||||
Ping = "ping",
|
||||
Pong = "pong",
|
||||
Subscribe = "subscribe",
|
||||
Next = "next",
|
||||
Error = "error",
|
||||
Complete = "complete"
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ConnectionInitMessage {
|
||||
readonly type: MessageType.ConnectionInit;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ConnectionAckMessage {
|
||||
readonly type: MessageType.ConnectionAck;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface PingMessage {
|
||||
readonly type: MessageType.Ping;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface PongMessage {
|
||||
readonly type: MessageType.Pong;
|
||||
readonly payload?: Record<string, unknown>;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface SubscribeMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Subscribe;
|
||||
readonly payload: SubscribePayload;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface SubscribePayload {
|
||||
readonly operationName?: string | null;
|
||||
readonly query: string;
|
||||
readonly variables?: Record<string, unknown> | null;
|
||||
readonly extensions?: Record<string, unknown> | null;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ExecutionResult<Data = Record<string, unknown>, Extensions = Record<string, unknown>> {
|
||||
errors?: ReadonlyArray<GraphQLError>;
|
||||
data?: Data | null;
|
||||
hasNext?: boolean;
|
||||
extensions?: Extensions;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ExecutionPatchResult<Data = unknown, Extensions = Record<string, unknown>> {
|
||||
errors?: ReadonlyArray<GraphQLError>;
|
||||
data?: Data | null;
|
||||
path?: ReadonlyArray<string | number>;
|
||||
label?: string;
|
||||
hasNext: boolean;
|
||||
extensions?: Extensions;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface NextMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Next;
|
||||
readonly payload: ExecutionResult | ExecutionPatchResult;
|
||||
}
|
||||
/** @category Common */
|
||||
export interface ErrorMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Error;
|
||||
readonly payload: readonly GraphQLError[];
|
||||
}
|
||||
/** @category Common */
|
||||
export interface CompleteMessage {
|
||||
readonly id: ID;
|
||||
readonly type: MessageType.Complete;
|
||||
}
|
||||
/** @category Common */
|
||||
export type Message<T extends MessageType = MessageType> = T extends MessageType.ConnectionAck ? ConnectionAckMessage : T extends MessageType.ConnectionInit ? ConnectionInitMessage : T extends MessageType.Ping ? PingMessage : T extends MessageType.Pong ? PongMessage : T extends MessageType.Subscribe ? SubscribeMessage : T extends MessageType.Next ? NextMessage : T extends MessageType.Error ? ErrorMessage : T extends MessageType.Complete ? CompleteMessage : never;
|
||||
/**
|
||||
* Validates the message against the GraphQL over WebSocket Protocol.
|
||||
*
|
||||
* Invalid messages will throw descriptive errors.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function validateMessage(val: unknown): Message;
|
||||
/**
|
||||
* Checks if the provided value is a valid GraphQL over WebSocket message.
|
||||
*
|
||||
* @deprecated Use `validateMessage` instead.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function isMessage(val: unknown): val is Message;
|
||||
/**
|
||||
* Function for transforming values within a message during JSON parsing
|
||||
* The values are produced by parsing the incoming raw JSON.
|
||||
*
|
||||
* Read more about using it:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#using_the_reviver_parameter
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export type JSONMessageReviver = (this: any, key: string, value: any) => any;
|
||||
/**
|
||||
* Parses the raw websocket message data to a valid message.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function parseMessage(data: unknown, reviver?: JSONMessageReviver): Message;
|
||||
/**
|
||||
* Function that allows customization of the produced JSON string
|
||||
* for the elements of an outgoing `Message` object.
|
||||
*
|
||||
* Read more about using it:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#the_replacer_parameter
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export type JSONMessageReplacer = (this: any, key: string, value: any) => any;
|
||||
/**
|
||||
* Stringifies a valid message ready to be sent through the socket.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export declare function stringifyMessage<T extends MessageType>(msg: Message<T>, replacer?: JSONMessageReplacer): string;
|
||||
185
graphql-subscription-express/node_modules/graphql-ws/lib/common.js
generated
vendored
Normal file
185
graphql-subscription-express/node_modules/graphql-ws/lib/common.js
generated
vendored
Normal file
@@ -0,0 +1,185 @@
|
||||
"use strict";
|
||||
/**
|
||||
*
|
||||
* common
|
||||
*
|
||||
*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.stringifyMessage = exports.parseMessage = exports.isMessage = exports.validateMessage = exports.MessageType = exports.CloseCode = exports.DEPRECATED_GRAPHQL_WS_PROTOCOL = exports.GRAPHQL_TRANSPORT_WS_PROTOCOL = void 0;
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* The WebSocket sub-protocol used for the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
exports.GRAPHQL_TRANSPORT_WS_PROTOCOL = 'graphql-transport-ws';
|
||||
/**
|
||||
* The deprecated subprotocol used by [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
exports.DEPRECATED_GRAPHQL_WS_PROTOCOL = 'graphql-ws';
|
||||
/**
|
||||
* `graphql-ws` expected and standard close codes of the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
var CloseCode;
|
||||
(function (CloseCode) {
|
||||
CloseCode[CloseCode["InternalServerError"] = 4500] = "InternalServerError";
|
||||
CloseCode[CloseCode["InternalClientError"] = 4005] = "InternalClientError";
|
||||
CloseCode[CloseCode["BadRequest"] = 4400] = "BadRequest";
|
||||
CloseCode[CloseCode["BadResponse"] = 4004] = "BadResponse";
|
||||
/** Tried subscribing before connect ack */
|
||||
CloseCode[CloseCode["Unauthorized"] = 4401] = "Unauthorized";
|
||||
CloseCode[CloseCode["Forbidden"] = 4403] = "Forbidden";
|
||||
CloseCode[CloseCode["SubprotocolNotAcceptable"] = 4406] = "SubprotocolNotAcceptable";
|
||||
CloseCode[CloseCode["ConnectionInitialisationTimeout"] = 4408] = "ConnectionInitialisationTimeout";
|
||||
CloseCode[CloseCode["ConnectionAcknowledgementTimeout"] = 4504] = "ConnectionAcknowledgementTimeout";
|
||||
/** Subscriber distinction is very important */
|
||||
CloseCode[CloseCode["SubscriberAlreadyExists"] = 4409] = "SubscriberAlreadyExists";
|
||||
CloseCode[CloseCode["TooManyInitialisationRequests"] = 4429] = "TooManyInitialisationRequests";
|
||||
})(CloseCode || (exports.CloseCode = CloseCode = {}));
|
||||
/**
|
||||
* Types of messages allowed to be sent by the client/server over the WS protocol.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
var MessageType;
|
||||
(function (MessageType) {
|
||||
MessageType["ConnectionInit"] = "connection_init";
|
||||
MessageType["ConnectionAck"] = "connection_ack";
|
||||
MessageType["Ping"] = "ping";
|
||||
MessageType["Pong"] = "pong";
|
||||
MessageType["Subscribe"] = "subscribe";
|
||||
MessageType["Next"] = "next";
|
||||
MessageType["Error"] = "error";
|
||||
MessageType["Complete"] = "complete";
|
||||
})(MessageType || (exports.MessageType = MessageType = {}));
|
||||
/**
|
||||
* Validates the message against the GraphQL over WebSocket Protocol.
|
||||
*
|
||||
* Invalid messages will throw descriptive errors.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function validateMessage(val) {
|
||||
if (!(0, utils_1.isObject)(val)) {
|
||||
throw new Error(`Message is expected to be an object, but got ${(0, utils_1.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 ${(0, utils_1.extendedTypeof)(val.type)}`);
|
||||
}
|
||||
switch (val.type) {
|
||||
case MessageType.ConnectionInit:
|
||||
case MessageType.ConnectionAck:
|
||||
case MessageType.Ping:
|
||||
case MessageType.Pong: {
|
||||
if (val.payload != null && !(0, utils_1.isObject)(val.payload)) {
|
||||
throw new Error(`"${val.type}" message expects the 'payload' property to be an object or nullish 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 ${(0, utils_1.extendedTypeof)(val.id)}`);
|
||||
}
|
||||
if (!val.id) {
|
||||
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
|
||||
}
|
||||
if (!(0, utils_1.isObject)(val.payload)) {
|
||||
throw new Error(`"${val.type}" message expects the 'payload' property to be an object, but got ${(0, utils_1.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 ${(0, utils_1.extendedTypeof)(val.payload.query)}`);
|
||||
}
|
||||
if (val.payload.variables != null && !(0, utils_1.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 ${(0, utils_1.extendedTypeof)(val.payload.variables)}`);
|
||||
}
|
||||
if (val.payload.operationName != null &&
|
||||
(0, utils_1.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 ${(0, utils_1.extendedTypeof)(val.payload.operationName)}`);
|
||||
}
|
||||
if (val.payload.extensions != null && !(0, utils_1.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 ${(0, utils_1.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 ${(0, utils_1.extendedTypeof)(val.id)}`);
|
||||
}
|
||||
if (!val.id) {
|
||||
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
|
||||
}
|
||||
if (!(0, utils_1.isObject)(val.payload)) {
|
||||
throw new Error(`"${val.type}" message expects the 'payload' property to be an object, but got ${(0, utils_1.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 ${(0, utils_1.extendedTypeof)(val.id)}`);
|
||||
}
|
||||
if (!val.id) {
|
||||
throw new Error(`"${val.type}" message requires a non-empty 'id' property`);
|
||||
}
|
||||
if (!(0, utils_1.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 ${(0, utils_1.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;
|
||||
}
|
||||
exports.validateMessage = validateMessage;
|
||||
/**
|
||||
* Checks if the provided value is a valid GraphQL over WebSocket message.
|
||||
*
|
||||
* @deprecated Use `validateMessage` instead.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function isMessage(val) {
|
||||
try {
|
||||
validateMessage(val);
|
||||
return true;
|
||||
}
|
||||
catch (_a) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.isMessage = isMessage;
|
||||
/**
|
||||
* Parses the raw websocket message data to a valid message.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function parseMessage(data, reviver) {
|
||||
return validateMessage(typeof data === 'string' ? JSON.parse(data, reviver) : data);
|
||||
}
|
||||
exports.parseMessage = parseMessage;
|
||||
/**
|
||||
* Stringifies a valid message ready to be sent through the socket.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function stringifyMessage(msg, replacer) {
|
||||
validateMessage(msg);
|
||||
return JSON.stringify(msg, replacer);
|
||||
}
|
||||
exports.stringifyMessage = stringifyMessage;
|
||||
178
graphql-subscription-express/node_modules/graphql-ws/lib/common.mjs
generated
vendored
Normal file
178
graphql-subscription-express/node_modules/graphql-ws/lib/common.mjs
generated
vendored
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
*
|
||||
* common
|
||||
*
|
||||
*/
|
||||
import { areGraphQLErrors, extendedTypeof, isObject } from './utils.mjs';
|
||||
/**
|
||||
* The WebSocket sub-protocol used for the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export const GRAPHQL_TRANSPORT_WS_PROTOCOL = 'graphql-transport-ws';
|
||||
/**
|
||||
* The deprecated subprotocol used by [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export const DEPRECATED_GRAPHQL_WS_PROTOCOL = 'graphql-ws';
|
||||
/**
|
||||
* `graphql-ws` expected and standard close codes of the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export var CloseCode;
|
||||
(function (CloseCode) {
|
||||
CloseCode[CloseCode["InternalServerError"] = 4500] = "InternalServerError";
|
||||
CloseCode[CloseCode["InternalClientError"] = 4005] = "InternalClientError";
|
||||
CloseCode[CloseCode["BadRequest"] = 4400] = "BadRequest";
|
||||
CloseCode[CloseCode["BadResponse"] = 4004] = "BadResponse";
|
||||
/** Tried subscribing before connect ack */
|
||||
CloseCode[CloseCode["Unauthorized"] = 4401] = "Unauthorized";
|
||||
CloseCode[CloseCode["Forbidden"] = 4403] = "Forbidden";
|
||||
CloseCode[CloseCode["SubprotocolNotAcceptable"] = 4406] = "SubprotocolNotAcceptable";
|
||||
CloseCode[CloseCode["ConnectionInitialisationTimeout"] = 4408] = "ConnectionInitialisationTimeout";
|
||||
CloseCode[CloseCode["ConnectionAcknowledgementTimeout"] = 4504] = "ConnectionAcknowledgementTimeout";
|
||||
/** Subscriber distinction is very important */
|
||||
CloseCode[CloseCode["SubscriberAlreadyExists"] = 4409] = "SubscriberAlreadyExists";
|
||||
CloseCode[CloseCode["TooManyInitialisationRequests"] = 4429] = "TooManyInitialisationRequests";
|
||||
})(CloseCode || (CloseCode = {}));
|
||||
/**
|
||||
* Types of messages allowed to be sent by the client/server over the WS protocol.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export var MessageType;
|
||||
(function (MessageType) {
|
||||
MessageType["ConnectionInit"] = "connection_init";
|
||||
MessageType["ConnectionAck"] = "connection_ack";
|
||||
MessageType["Ping"] = "ping";
|
||||
MessageType["Pong"] = "pong";
|
||||
MessageType["Subscribe"] = "subscribe";
|
||||
MessageType["Next"] = "next";
|
||||
MessageType["Error"] = "error";
|
||||
MessageType["Complete"] = "complete";
|
||||
})(MessageType || (MessageType = {}));
|
||||
/**
|
||||
* Validates the message against the GraphQL over WebSocket Protocol.
|
||||
*
|
||||
* Invalid messages will throw descriptive errors.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export 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 (val.payload != null && !isObject(val.payload)) {
|
||||
throw new Error(`"${val.type}" message expects the 'payload' property to be an object or nullish 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;
|
||||
}
|
||||
/**
|
||||
* Checks if the provided value is a valid GraphQL over WebSocket message.
|
||||
*
|
||||
* @deprecated Use `validateMessage` instead.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export function isMessage(val) {
|
||||
try {
|
||||
validateMessage(val);
|
||||
return true;
|
||||
}
|
||||
catch (_a) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parses the raw websocket message data to a valid message.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export function parseMessage(data, reviver) {
|
||||
return validateMessage(typeof data === 'string' ? JSON.parse(data, reviver) : data);
|
||||
}
|
||||
/**
|
||||
* Stringifies a valid message ready to be sent through the socket.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
export function stringifyMessage(msg, replacer) {
|
||||
validateMessage(msg);
|
||||
return JSON.stringify(msg, replacer);
|
||||
}
|
||||
3
graphql-subscription-express/node_modules/graphql-ws/lib/index.d.mts
generated
vendored
Normal file
3
graphql-subscription-express/node_modules/graphql-ws/lib/index.d.mts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './client.mjs';
|
||||
export * from './server.mjs';
|
||||
export * from './common.mjs';
|
||||
3
graphql-subscription-express/node_modules/graphql-ws/lib/index.d.ts
generated
vendored
Normal file
3
graphql-subscription-express/node_modules/graphql-ws/lib/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './client';
|
||||
export * from './server';
|
||||
export * from './common';
|
||||
19
graphql-subscription-express/node_modules/graphql-ws/lib/index.js
generated
vendored
Normal file
19
graphql-subscription-express/node_modules/graphql-ws/lib/index.js
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
||||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
__exportStar(require("./client"), exports);
|
||||
__exportStar(require("./server"), exports);
|
||||
__exportStar(require("./common"), exports);
|
||||
3
graphql-subscription-express/node_modules/graphql-ws/lib/index.mjs
generated
vendored
Normal file
3
graphql-subscription-express/node_modules/graphql-ws/lib/index.mjs
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './client.mjs';
|
||||
export * from './server.mjs';
|
||||
export * from './common.mjs';
|
||||
417
graphql-subscription-express/node_modules/graphql-ws/lib/server.d.mts
generated
vendored
Normal file
417
graphql-subscription-express/node_modules/graphql-ws/lib/server.d.mts
generated
vendored
Normal file
@@ -0,0 +1,417 @@
|
||||
/**
|
||||
*
|
||||
* server
|
||||
*
|
||||
*/
|
||||
import { OperationTypeNode, GraphQLSchema, ExecutionArgs, validate as graphqlValidate, GraphQLError, SubscriptionArgs } from 'graphql';
|
||||
import { GRAPHQL_TRANSPORT_WS_PROTOCOL, ID, ConnectionInitMessage, SubscribeMessage, NextMessage, ErrorMessage, CompleteMessage, JSONMessageReplacer, JSONMessageReviver, PingMessage, PongMessage, ExecutionResult, ExecutionPatchResult } from './common.mjs';
|
||||
/** @category Server */
|
||||
export type OperationResult = Promise<AsyncGenerator<ExecutionResult | ExecutionPatchResult> | AsyncIterable<ExecutionResult | ExecutionPatchResult> | ExecutionResult> | AsyncGenerator<ExecutionResult | ExecutionPatchResult> | AsyncIterable<ExecutionResult | ExecutionPatchResult> | ExecutionResult;
|
||||
/**
|
||||
* A concrete GraphQL execution context value type.
|
||||
*
|
||||
* Mainly used because TypeScript collapses unions
|
||||
* with `any` or `unknown` to `any` or `unknown`. So,
|
||||
* we use a custom type to allow definitions such as
|
||||
* the `context` server option.
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export type GraphQLExecutionContextValue = object | symbol | number | string | boolean | undefined | null;
|
||||
/** @category Server */
|
||||
export interface ServerOptions<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E = unknown> {
|
||||
/**
|
||||
* The GraphQL schema on which the operations
|
||||
* will be executed and validated against.
|
||||
*
|
||||
* If a function is provided, it will be called on
|
||||
* every subscription request allowing you to manipulate
|
||||
* schema dynamically.
|
||||
*
|
||||
* If the schema is left undefined, you're trusted to
|
||||
* provide one in the returned `ExecutionArgs` from the
|
||||
* `onSubscribe` callback.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
schema?: GraphQLSchema | ((ctx: Context<P, E>, message: SubscribeMessage, args: Omit<ExecutionArgs, 'schema'>) => Promise<GraphQLSchema> | GraphQLSchema);
|
||||
/**
|
||||
* A value which is provided to every resolver and holds
|
||||
* important contextual information like the currently
|
||||
* logged in user, or access to a database.
|
||||
*
|
||||
* If you return from `onSubscribe`, and the returned value is
|
||||
* missing the `contextValue` field, this context will be used
|
||||
* instead.
|
||||
*
|
||||
* If you use the function signature, the final execution arguments
|
||||
* will be passed in (also the returned value from `onSubscribe`).
|
||||
* Since the context is injected on every subscribe, the `SubscribeMessage`
|
||||
* with the regular `Context` will be passed in through the arguments too.
|
||||
*
|
||||
* Note that the context function is invoked on each operation only once.
|
||||
* Meaning, for subscriptions, only at the point of initialising the subscription;
|
||||
* not on every subscription event emission. Read more about the context lifecycle
|
||||
* in subscriptions here: https://github.com/graphql/graphql-js/issues/894.
|
||||
*/
|
||||
context?: GraphQLExecutionContextValue | ((ctx: Context<P, E>, message: SubscribeMessage, args: ExecutionArgs) => Promise<GraphQLExecutionContextValue> | GraphQLExecutionContextValue);
|
||||
/**
|
||||
* The GraphQL root fields or resolvers to go
|
||||
* alongside the schema. Learn more about them
|
||||
* here: https://graphql.org/learn/execution/#root-fields-resolvers.
|
||||
*
|
||||
* If you return from `onSubscribe`, and the returned value is
|
||||
* missing the `rootValue` field, the relevant operation root
|
||||
* will be used instead.
|
||||
*/
|
||||
roots?: {
|
||||
[operation in OperationTypeNode]?: Record<string, NonNullable<SubscriptionArgs['rootValue']>>;
|
||||
};
|
||||
/**
|
||||
* A custom GraphQL validate function allowing you to apply your
|
||||
* own validation rules.
|
||||
*
|
||||
* Returned, non-empty, array of `GraphQLError`s will be communicated
|
||||
* to the client through the `ErrorMessage`. Use an empty array if the
|
||||
* document is valid and no errors have been encountered.
|
||||
*
|
||||
* Will not be used when implementing a custom `onSubscribe`.
|
||||
*
|
||||
* Throwing an error from within this function will close the socket
|
||||
* with the `Error` message in the close event reason.
|
||||
*/
|
||||
validate?: typeof graphqlValidate;
|
||||
/**
|
||||
* Is the `execute` function from GraphQL which is
|
||||
* used to execute the query and mutation operations.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
execute?: (args: ExecutionArgs) => OperationResult;
|
||||
/**
|
||||
* Is the `subscribe` function from GraphQL which is
|
||||
* used to execute the subscription operation.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
subscribe?: (args: ExecutionArgs) => OperationResult;
|
||||
/**
|
||||
* The amount of time for which the server will wait
|
||||
* for `ConnectionInit` message.
|
||||
*
|
||||
* Set the value to `Infinity`, `''`, `0`, `null` or `undefined` to skip waiting.
|
||||
*
|
||||
* If the wait timeout has passed and the client
|
||||
* has not sent the `ConnectionInit` message,
|
||||
* the server will terminate the socket by
|
||||
* dispatching a close event `4408: Connection initialisation timeout`
|
||||
*
|
||||
* @default 3_000 // 3 seconds
|
||||
*/
|
||||
connectionInitWaitTimeout?: number;
|
||||
/**
|
||||
* Is the connection callback called when the
|
||||
* client requests the connection initialisation
|
||||
* through the message `ConnectionInit`.
|
||||
*
|
||||
* The message payload (`connectionParams` from the
|
||||
* client) is present in the `Context.connectionParams`.
|
||||
*
|
||||
* - Returning `true` or nothing from the callback will
|
||||
* allow the client to connect.
|
||||
*
|
||||
* - Returning `false` from the callback will
|
||||
* terminate the socket by dispatching the
|
||||
* close event `4403: Forbidden`.
|
||||
*
|
||||
* - Returning a `Record` from the callback will
|
||||
* allow the client to connect and pass the returned
|
||||
* value to the client through the optional `payload`
|
||||
* field in the `ConnectionAck` message.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onConnect?: (ctx: Context<P, E>) => Promise<Record<string, unknown> | boolean | void> | Record<string, unknown> | boolean | void;
|
||||
/**
|
||||
* Called when the client disconnects for whatever reason after
|
||||
* he successfully went through the connection initialisation phase.
|
||||
* Provides the close event too. Beware that this callback happens
|
||||
* AFTER all subscriptions have been gracefully completed and BEFORE
|
||||
* the `onClose` callback.
|
||||
*
|
||||
* If you are interested in tracking the subscriptions completions,
|
||||
* consider using the `onComplete` callback.
|
||||
*
|
||||
* This callback will be called EXCLUSIVELY if the client connection
|
||||
* is acknowledged. Meaning, `onConnect` will be called before the `onDisconnect`.
|
||||
*
|
||||
* For tracking socket closures at any point in time, regardless
|
||||
* of the connection state - consider using the `onClose` callback.
|
||||
*/
|
||||
onDisconnect?: (ctx: Context<P, E>, code?: number, reason?: string) => Promise<void> | void;
|
||||
/**
|
||||
* Called when the socket closes for whatever reason, at any
|
||||
* point in time. Provides the close event too. Beware
|
||||
* that this callback happens AFTER all subscriptions have
|
||||
* been gracefully completed and AFTER the `onDisconnect` callback.
|
||||
*
|
||||
* If you are interested in tracking the subscriptions completions,
|
||||
* consider using the `onComplete` callback.
|
||||
*
|
||||
* In comparison to `onDisconnect`, this callback will ALWAYS
|
||||
* be called, regardless if the user successfully went through
|
||||
* the connection initialisation or not. `onConnect` might not
|
||||
* called before the `onClose`.
|
||||
*/
|
||||
onClose?: (ctx: Context<P, E>, code?: number, reason?: string) => Promise<void> | void;
|
||||
/**
|
||||
* The subscribe callback executed right after
|
||||
* acknowledging the request before any payload
|
||||
* processing has been performed.
|
||||
*
|
||||
* If you return `ExecutionArgs` from the callback,
|
||||
* it will be used instead of trying to build one
|
||||
* internally. In this case, you are responsible
|
||||
* for providing a ready set of arguments which will
|
||||
* be directly plugged in the operation execution.
|
||||
*
|
||||
* Omitting the fields `contextValue` or `rootValue`
|
||||
* from the returned value will have the provided server
|
||||
* options fill in the gaps.
|
||||
*
|
||||
* To report GraphQL errors simply return an array
|
||||
* of them from the callback, they will be reported
|
||||
* to the client through the error message.
|
||||
*
|
||||
* Useful for preparing the execution arguments
|
||||
* following a custom logic. A typical use case are
|
||||
* persisted queries, you can identify the query from
|
||||
* the subscribe message and create the GraphQL operation
|
||||
* execution args which are then returned by the function.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onSubscribe?: (ctx: Context<P, E>, message: SubscribeMessage) => Promise<ExecutionArgs | readonly GraphQLError[] | void> | ExecutionArgs | readonly GraphQLError[] | void;
|
||||
/**
|
||||
* Executed after the operation call resolves. For streaming
|
||||
* operations, triggering this callback does not necessarily
|
||||
* mean that there is already a result available - it means
|
||||
* that the subscription process for the stream has resolved
|
||||
* and that the client is now subscribed.
|
||||
*
|
||||
* The `OperationResult` argument is the result of operation
|
||||
* execution. It can be an iterator or already a value.
|
||||
*
|
||||
* If you want the single result and the events from a streaming
|
||||
* operation, use the `onNext` callback.
|
||||
*
|
||||
* Use this callback to listen for subscribe operation and
|
||||
* execution result manipulation.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onOperation?: (ctx: Context<P, E>, message: SubscribeMessage, args: ExecutionArgs, result: OperationResult) => Promise<OperationResult | void> | OperationResult | void;
|
||||
/**
|
||||
* Executed after an error occurred right before it
|
||||
* has been dispatched to the client.
|
||||
*
|
||||
* Use this callback to format the outgoing GraphQL
|
||||
* errors before they reach the client.
|
||||
*
|
||||
* Returned result will be injected in the error message payload.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onError?: (ctx: Context<P, E>, message: ErrorMessage, errors: readonly GraphQLError[]) => Promise<readonly GraphQLError[] | void> | readonly GraphQLError[] | void;
|
||||
/**
|
||||
* Executed after an operation has emitted a result right before
|
||||
* that result has been sent to the client. Results from both
|
||||
* single value and streaming operations will appear in this callback.
|
||||
*
|
||||
* Use this callback if you want to format the execution result
|
||||
* before it reaches the client.
|
||||
*
|
||||
* Returned result will be injected in the next message payload.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onNext?: (ctx: Context<P, E>, message: NextMessage, args: ExecutionArgs, result: ExecutionResult | ExecutionPatchResult) => Promise<ExecutionResult | ExecutionPatchResult | void> | ExecutionResult | ExecutionPatchResult | void;
|
||||
/**
|
||||
* The complete callback is executed after the
|
||||
* operation has completed right before sending
|
||||
* the complete message to the client.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*
|
||||
* Since the library makes sure to complete streaming
|
||||
* operations even after an abrupt closure, this callback
|
||||
* will still be called.
|
||||
*/
|
||||
onComplete?: (ctx: Context<P, E>, message: CompleteMessage) => Promise<void> | void;
|
||||
/**
|
||||
* An optional override for the JSON.parse function used to hydrate
|
||||
* incoming messages to this server. Useful for parsing custom datatypes
|
||||
* out of the incoming JSON.
|
||||
*/
|
||||
jsonMessageReviver?: JSONMessageReviver;
|
||||
/**
|
||||
* An optional override for the JSON.stringify function used to serialize
|
||||
* outgoing messages to from server. Useful for serializing custom
|
||||
* datatypes out to the client.
|
||||
*/
|
||||
jsonMessageReplacer?: JSONMessageReplacer;
|
||||
}
|
||||
/** @category Server */
|
||||
export interface Server<E = undefined> {
|
||||
/**
|
||||
* New socket has been established. The lib will validate
|
||||
* the protocol and use the socket accordingly. Returned promise
|
||||
* will resolve after the socket closes.
|
||||
*
|
||||
* The second argument will be passed in the `extra` field
|
||||
* of the `Context`. You may pass the initial request or the
|
||||
* original WebSocket, if you need it down the road.
|
||||
*
|
||||
* Returns a function that should be called when the same socket
|
||||
* has been closed, for whatever reason. The close code and reason
|
||||
* must be passed for reporting to the `onDisconnect` callback. Returned
|
||||
* promise will resolve once the internal cleanup is complete.
|
||||
*/
|
||||
opened(socket: WebSocket, ctxExtra: E): (code?: number, reason?: string) => Promise<void>;
|
||||
}
|
||||
/** @category Server */
|
||||
export interface WebSocket {
|
||||
/**
|
||||
* The subprotocol of the WebSocket. Will be used
|
||||
* to validate against the supported ones.
|
||||
*/
|
||||
readonly protocol: string;
|
||||
/**
|
||||
* Sends a message through the socket. Will always
|
||||
* provide a `string` message.
|
||||
*
|
||||
* Please take care that the send is ready. Meaning,
|
||||
* only provide a truly OPEN socket through the `opened`
|
||||
* method of the `Server`.
|
||||
*
|
||||
* The returned promise is used to control the flow of data
|
||||
* (like handling backpressure).
|
||||
*/
|
||||
send(data: string): Promise<void> | void;
|
||||
/**
|
||||
* Closes the socket gracefully. Will always provide
|
||||
* the appropriate code and close reason. `onDisconnect`
|
||||
* callback will be called.
|
||||
*
|
||||
* The returned promise is used to control the graceful
|
||||
* closure.
|
||||
*/
|
||||
close(code: number, reason: string): Promise<void> | void;
|
||||
/**
|
||||
* Called when message is received. The library requires the data
|
||||
* to be a `string`.
|
||||
*
|
||||
* All operations requested from the client will block the promise until
|
||||
* completed, this means that the callback will not resolve until all
|
||||
* subscription events have been emitted (or until the client has completed
|
||||
* the stream), or until the query/mutation resolves.
|
||||
*
|
||||
* Exceptions raised during any phase of operation processing will
|
||||
* reject the callback's promise, catch them and communicate them
|
||||
* to your clients however you wish.
|
||||
*/
|
||||
onMessage(cb: (data: string) => Promise<void>): void;
|
||||
/**
|
||||
* Implement a listener for the `PingMessage` sent from the client to the server.
|
||||
* If the client sent the ping with a payload, it will be passed through the
|
||||
* first argument.
|
||||
*
|
||||
* If this listener is implemented, the server will NOT automatically reply
|
||||
* to any pings from the client. Implementing it makes it your responsibility
|
||||
* to decide how and when to respond.
|
||||
*/
|
||||
onPing?(payload: PingMessage['payload']): Promise<void> | void;
|
||||
/**
|
||||
* Implement a listener for the `PongMessage` sent from the client to the server.
|
||||
* If the client sent the pong with a payload, it will be passed through the
|
||||
* first argument.
|
||||
*/
|
||||
onPong?(payload: PongMessage['payload']): Promise<void> | void;
|
||||
}
|
||||
/** @category Server */
|
||||
export interface Context<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E = unknown> {
|
||||
/**
|
||||
* Indicates that the `ConnectionInit` message
|
||||
* has been received by the server. If this is
|
||||
* `true`, the client wont be kicked off after
|
||||
* the wait timeout has passed.
|
||||
*/
|
||||
readonly connectionInitReceived: boolean;
|
||||
/**
|
||||
* Indicates that the connection was acknowledged
|
||||
* by having dispatched the `ConnectionAck` message
|
||||
* to the related client.
|
||||
*/
|
||||
readonly acknowledged: boolean;
|
||||
/** The parameters passed during the connection initialisation. */
|
||||
readonly connectionParams?: Readonly<P>;
|
||||
/**
|
||||
* Holds the active subscriptions for this context. **All operations**
|
||||
* that are taking place are aggregated here. The user is _subscribed_
|
||||
* to an operation when waiting for result(s).
|
||||
*
|
||||
* If the subscription behind an ID is an `AsyncIterator` - the operation
|
||||
* is streaming; on the contrary, if the subscription is `null` - it is simply
|
||||
* a reservation, meaning - the operation resolves to a single result or is still
|
||||
* pending/being prepared.
|
||||
*/
|
||||
readonly subscriptions: Record<ID, AsyncGenerator<unknown> | AsyncIterable<unknown> | null>;
|
||||
/**
|
||||
* An extra field where you can store your own context values
|
||||
* to pass between callbacks.
|
||||
*/
|
||||
extra: E;
|
||||
}
|
||||
/**
|
||||
* Makes a Protocol compliant WebSocket GraphQL server. The server
|
||||
* is actually an API which is to be used with your favourite WebSocket
|
||||
* server library!
|
||||
*
|
||||
* Read more about the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export declare function makeServer<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E = unknown>(options: ServerOptions<P, E>): Server<E>;
|
||||
/**
|
||||
* Helper utility for choosing the "graphql-transport-ws" subprotocol from
|
||||
* a set of WebSocket subprotocols.
|
||||
*
|
||||
* Accepts a set of already extracted WebSocket subprotocols or the raw
|
||||
* Sec-WebSocket-Protocol header value. In either case, if the right
|
||||
* protocol appears, it will be returned.
|
||||
*
|
||||
* By specification, the server should not provide a value with Sec-WebSocket-Protocol
|
||||
* if it does not agree with client's subprotocols. The client has a responsibility
|
||||
* to handle the connection afterwards.
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export declare function handleProtocols(protocols: Set<string> | string[] | string): typeof GRAPHQL_TRANSPORT_WS_PROTOCOL | false;
|
||||
417
graphql-subscription-express/node_modules/graphql-ws/lib/server.d.ts
generated
vendored
Normal file
417
graphql-subscription-express/node_modules/graphql-ws/lib/server.d.ts
generated
vendored
Normal file
@@ -0,0 +1,417 @@
|
||||
/**
|
||||
*
|
||||
* server
|
||||
*
|
||||
*/
|
||||
import { OperationTypeNode, GraphQLSchema, ExecutionArgs, validate as graphqlValidate, GraphQLError, SubscriptionArgs } from 'graphql';
|
||||
import { GRAPHQL_TRANSPORT_WS_PROTOCOL, ID, ConnectionInitMessage, SubscribeMessage, NextMessage, ErrorMessage, CompleteMessage, JSONMessageReplacer, JSONMessageReviver, PingMessage, PongMessage, ExecutionResult, ExecutionPatchResult } from './common';
|
||||
/** @category Server */
|
||||
export type OperationResult = Promise<AsyncGenerator<ExecutionResult | ExecutionPatchResult> | AsyncIterable<ExecutionResult | ExecutionPatchResult> | ExecutionResult> | AsyncGenerator<ExecutionResult | ExecutionPatchResult> | AsyncIterable<ExecutionResult | ExecutionPatchResult> | ExecutionResult;
|
||||
/**
|
||||
* A concrete GraphQL execution context value type.
|
||||
*
|
||||
* Mainly used because TypeScript collapses unions
|
||||
* with `any` or `unknown` to `any` or `unknown`. So,
|
||||
* we use a custom type to allow definitions such as
|
||||
* the `context` server option.
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export type GraphQLExecutionContextValue = object | symbol | number | string | boolean | undefined | null;
|
||||
/** @category Server */
|
||||
export interface ServerOptions<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E = unknown> {
|
||||
/**
|
||||
* The GraphQL schema on which the operations
|
||||
* will be executed and validated against.
|
||||
*
|
||||
* If a function is provided, it will be called on
|
||||
* every subscription request allowing you to manipulate
|
||||
* schema dynamically.
|
||||
*
|
||||
* If the schema is left undefined, you're trusted to
|
||||
* provide one in the returned `ExecutionArgs` from the
|
||||
* `onSubscribe` callback.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
schema?: GraphQLSchema | ((ctx: Context<P, E>, message: SubscribeMessage, args: Omit<ExecutionArgs, 'schema'>) => Promise<GraphQLSchema> | GraphQLSchema);
|
||||
/**
|
||||
* A value which is provided to every resolver and holds
|
||||
* important contextual information like the currently
|
||||
* logged in user, or access to a database.
|
||||
*
|
||||
* If you return from `onSubscribe`, and the returned value is
|
||||
* missing the `contextValue` field, this context will be used
|
||||
* instead.
|
||||
*
|
||||
* If you use the function signature, the final execution arguments
|
||||
* will be passed in (also the returned value from `onSubscribe`).
|
||||
* Since the context is injected on every subscribe, the `SubscribeMessage`
|
||||
* with the regular `Context` will be passed in through the arguments too.
|
||||
*
|
||||
* Note that the context function is invoked on each operation only once.
|
||||
* Meaning, for subscriptions, only at the point of initialising the subscription;
|
||||
* not on every subscription event emission. Read more about the context lifecycle
|
||||
* in subscriptions here: https://github.com/graphql/graphql-js/issues/894.
|
||||
*/
|
||||
context?: GraphQLExecutionContextValue | ((ctx: Context<P, E>, message: SubscribeMessage, args: ExecutionArgs) => Promise<GraphQLExecutionContextValue> | GraphQLExecutionContextValue);
|
||||
/**
|
||||
* The GraphQL root fields or resolvers to go
|
||||
* alongside the schema. Learn more about them
|
||||
* here: https://graphql.org/learn/execution/#root-fields-resolvers.
|
||||
*
|
||||
* If you return from `onSubscribe`, and the returned value is
|
||||
* missing the `rootValue` field, the relevant operation root
|
||||
* will be used instead.
|
||||
*/
|
||||
roots?: {
|
||||
[operation in OperationTypeNode]?: Record<string, NonNullable<SubscriptionArgs['rootValue']>>;
|
||||
};
|
||||
/**
|
||||
* A custom GraphQL validate function allowing you to apply your
|
||||
* own validation rules.
|
||||
*
|
||||
* Returned, non-empty, array of `GraphQLError`s will be communicated
|
||||
* to the client through the `ErrorMessage`. Use an empty array if the
|
||||
* document is valid and no errors have been encountered.
|
||||
*
|
||||
* Will not be used when implementing a custom `onSubscribe`.
|
||||
*
|
||||
* Throwing an error from within this function will close the socket
|
||||
* with the `Error` message in the close event reason.
|
||||
*/
|
||||
validate?: typeof graphqlValidate;
|
||||
/**
|
||||
* Is the `execute` function from GraphQL which is
|
||||
* used to execute the query and mutation operations.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
execute?: (args: ExecutionArgs) => OperationResult;
|
||||
/**
|
||||
* Is the `subscribe` function from GraphQL which is
|
||||
* used to execute the subscription operation.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
subscribe?: (args: ExecutionArgs) => OperationResult;
|
||||
/**
|
||||
* The amount of time for which the server will wait
|
||||
* for `ConnectionInit` message.
|
||||
*
|
||||
* Set the value to `Infinity`, `''`, `0`, `null` or `undefined` to skip waiting.
|
||||
*
|
||||
* If the wait timeout has passed and the client
|
||||
* has not sent the `ConnectionInit` message,
|
||||
* the server will terminate the socket by
|
||||
* dispatching a close event `4408: Connection initialisation timeout`
|
||||
*
|
||||
* @default 3_000 // 3 seconds
|
||||
*/
|
||||
connectionInitWaitTimeout?: number;
|
||||
/**
|
||||
* Is the connection callback called when the
|
||||
* client requests the connection initialisation
|
||||
* through the message `ConnectionInit`.
|
||||
*
|
||||
* The message payload (`connectionParams` from the
|
||||
* client) is present in the `Context.connectionParams`.
|
||||
*
|
||||
* - Returning `true` or nothing from the callback will
|
||||
* allow the client to connect.
|
||||
*
|
||||
* - Returning `false` from the callback will
|
||||
* terminate the socket by dispatching the
|
||||
* close event `4403: Forbidden`.
|
||||
*
|
||||
* - Returning a `Record` from the callback will
|
||||
* allow the client to connect and pass the returned
|
||||
* value to the client through the optional `payload`
|
||||
* field in the `ConnectionAck` message.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onConnect?: (ctx: Context<P, E>) => Promise<Record<string, unknown> | boolean | void> | Record<string, unknown> | boolean | void;
|
||||
/**
|
||||
* Called when the client disconnects for whatever reason after
|
||||
* he successfully went through the connection initialisation phase.
|
||||
* Provides the close event too. Beware that this callback happens
|
||||
* AFTER all subscriptions have been gracefully completed and BEFORE
|
||||
* the `onClose` callback.
|
||||
*
|
||||
* If you are interested in tracking the subscriptions completions,
|
||||
* consider using the `onComplete` callback.
|
||||
*
|
||||
* This callback will be called EXCLUSIVELY if the client connection
|
||||
* is acknowledged. Meaning, `onConnect` will be called before the `onDisconnect`.
|
||||
*
|
||||
* For tracking socket closures at any point in time, regardless
|
||||
* of the connection state - consider using the `onClose` callback.
|
||||
*/
|
||||
onDisconnect?: (ctx: Context<P, E>, code?: number, reason?: string) => Promise<void> | void;
|
||||
/**
|
||||
* Called when the socket closes for whatever reason, at any
|
||||
* point in time. Provides the close event too. Beware
|
||||
* that this callback happens AFTER all subscriptions have
|
||||
* been gracefully completed and AFTER the `onDisconnect` callback.
|
||||
*
|
||||
* If you are interested in tracking the subscriptions completions,
|
||||
* consider using the `onComplete` callback.
|
||||
*
|
||||
* In comparison to `onDisconnect`, this callback will ALWAYS
|
||||
* be called, regardless if the user successfully went through
|
||||
* the connection initialisation or not. `onConnect` might not
|
||||
* called before the `onClose`.
|
||||
*/
|
||||
onClose?: (ctx: Context<P, E>, code?: number, reason?: string) => Promise<void> | void;
|
||||
/**
|
||||
* The subscribe callback executed right after
|
||||
* acknowledging the request before any payload
|
||||
* processing has been performed.
|
||||
*
|
||||
* If you return `ExecutionArgs` from the callback,
|
||||
* it will be used instead of trying to build one
|
||||
* internally. In this case, you are responsible
|
||||
* for providing a ready set of arguments which will
|
||||
* be directly plugged in the operation execution.
|
||||
*
|
||||
* Omitting the fields `contextValue` or `rootValue`
|
||||
* from the returned value will have the provided server
|
||||
* options fill in the gaps.
|
||||
*
|
||||
* To report GraphQL errors simply return an array
|
||||
* of them from the callback, they will be reported
|
||||
* to the client through the error message.
|
||||
*
|
||||
* Useful for preparing the execution arguments
|
||||
* following a custom logic. A typical use case are
|
||||
* persisted queries, you can identify the query from
|
||||
* the subscribe message and create the GraphQL operation
|
||||
* execution args which are then returned by the function.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onSubscribe?: (ctx: Context<P, E>, message: SubscribeMessage) => Promise<ExecutionArgs | readonly GraphQLError[] | void> | ExecutionArgs | readonly GraphQLError[] | void;
|
||||
/**
|
||||
* Executed after the operation call resolves. For streaming
|
||||
* operations, triggering this callback does not necessarily
|
||||
* mean that there is already a result available - it means
|
||||
* that the subscription process for the stream has resolved
|
||||
* and that the client is now subscribed.
|
||||
*
|
||||
* The `OperationResult` argument is the result of operation
|
||||
* execution. It can be an iterator or already a value.
|
||||
*
|
||||
* If you want the single result and the events from a streaming
|
||||
* operation, use the `onNext` callback.
|
||||
*
|
||||
* Use this callback to listen for subscribe operation and
|
||||
* execution result manipulation.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onOperation?: (ctx: Context<P, E>, message: SubscribeMessage, args: ExecutionArgs, result: OperationResult) => Promise<OperationResult | void> | OperationResult | void;
|
||||
/**
|
||||
* Executed after an error occurred right before it
|
||||
* has been dispatched to the client.
|
||||
*
|
||||
* Use this callback to format the outgoing GraphQL
|
||||
* errors before they reach the client.
|
||||
*
|
||||
* Returned result will be injected in the error message payload.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onError?: (ctx: Context<P, E>, message: ErrorMessage, errors: readonly GraphQLError[]) => Promise<readonly GraphQLError[] | void> | readonly GraphQLError[] | void;
|
||||
/**
|
||||
* Executed after an operation has emitted a result right before
|
||||
* that result has been sent to the client. Results from both
|
||||
* single value and streaming operations will appear in this callback.
|
||||
*
|
||||
* Use this callback if you want to format the execution result
|
||||
* before it reaches the client.
|
||||
*
|
||||
* Returned result will be injected in the next message payload.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*/
|
||||
onNext?: (ctx: Context<P, E>, message: NextMessage, args: ExecutionArgs, result: ExecutionResult | ExecutionPatchResult) => Promise<ExecutionResult | ExecutionPatchResult | void> | ExecutionResult | ExecutionPatchResult | void;
|
||||
/**
|
||||
* The complete callback is executed after the
|
||||
* operation has completed right before sending
|
||||
* the complete message to the client.
|
||||
*
|
||||
* Throwing an error from within this function will
|
||||
* close the socket with the `Error` message
|
||||
* in the close event reason.
|
||||
*
|
||||
* Since the library makes sure to complete streaming
|
||||
* operations even after an abrupt closure, this callback
|
||||
* will still be called.
|
||||
*/
|
||||
onComplete?: (ctx: Context<P, E>, message: CompleteMessage) => Promise<void> | void;
|
||||
/**
|
||||
* An optional override for the JSON.parse function used to hydrate
|
||||
* incoming messages to this server. Useful for parsing custom datatypes
|
||||
* out of the incoming JSON.
|
||||
*/
|
||||
jsonMessageReviver?: JSONMessageReviver;
|
||||
/**
|
||||
* An optional override for the JSON.stringify function used to serialize
|
||||
* outgoing messages to from server. Useful for serializing custom
|
||||
* datatypes out to the client.
|
||||
*/
|
||||
jsonMessageReplacer?: JSONMessageReplacer;
|
||||
}
|
||||
/** @category Server */
|
||||
export interface Server<E = undefined> {
|
||||
/**
|
||||
* New socket has been established. The lib will validate
|
||||
* the protocol and use the socket accordingly. Returned promise
|
||||
* will resolve after the socket closes.
|
||||
*
|
||||
* The second argument will be passed in the `extra` field
|
||||
* of the `Context`. You may pass the initial request or the
|
||||
* original WebSocket, if you need it down the road.
|
||||
*
|
||||
* Returns a function that should be called when the same socket
|
||||
* has been closed, for whatever reason. The close code and reason
|
||||
* must be passed for reporting to the `onDisconnect` callback. Returned
|
||||
* promise will resolve once the internal cleanup is complete.
|
||||
*/
|
||||
opened(socket: WebSocket, ctxExtra: E): (code?: number, reason?: string) => Promise<void>;
|
||||
}
|
||||
/** @category Server */
|
||||
export interface WebSocket {
|
||||
/**
|
||||
* The subprotocol of the WebSocket. Will be used
|
||||
* to validate against the supported ones.
|
||||
*/
|
||||
readonly protocol: string;
|
||||
/**
|
||||
* Sends a message through the socket. Will always
|
||||
* provide a `string` message.
|
||||
*
|
||||
* Please take care that the send is ready. Meaning,
|
||||
* only provide a truly OPEN socket through the `opened`
|
||||
* method of the `Server`.
|
||||
*
|
||||
* The returned promise is used to control the flow of data
|
||||
* (like handling backpressure).
|
||||
*/
|
||||
send(data: string): Promise<void> | void;
|
||||
/**
|
||||
* Closes the socket gracefully. Will always provide
|
||||
* the appropriate code and close reason. `onDisconnect`
|
||||
* callback will be called.
|
||||
*
|
||||
* The returned promise is used to control the graceful
|
||||
* closure.
|
||||
*/
|
||||
close(code: number, reason: string): Promise<void> | void;
|
||||
/**
|
||||
* Called when message is received. The library requires the data
|
||||
* to be a `string`.
|
||||
*
|
||||
* All operations requested from the client will block the promise until
|
||||
* completed, this means that the callback will not resolve until all
|
||||
* subscription events have been emitted (or until the client has completed
|
||||
* the stream), or until the query/mutation resolves.
|
||||
*
|
||||
* Exceptions raised during any phase of operation processing will
|
||||
* reject the callback's promise, catch them and communicate them
|
||||
* to your clients however you wish.
|
||||
*/
|
||||
onMessage(cb: (data: string) => Promise<void>): void;
|
||||
/**
|
||||
* Implement a listener for the `PingMessage` sent from the client to the server.
|
||||
* If the client sent the ping with a payload, it will be passed through the
|
||||
* first argument.
|
||||
*
|
||||
* If this listener is implemented, the server will NOT automatically reply
|
||||
* to any pings from the client. Implementing it makes it your responsibility
|
||||
* to decide how and when to respond.
|
||||
*/
|
||||
onPing?(payload: PingMessage['payload']): Promise<void> | void;
|
||||
/**
|
||||
* Implement a listener for the `PongMessage` sent from the client to the server.
|
||||
* If the client sent the pong with a payload, it will be passed through the
|
||||
* first argument.
|
||||
*/
|
||||
onPong?(payload: PongMessage['payload']): Promise<void> | void;
|
||||
}
|
||||
/** @category Server */
|
||||
export interface Context<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E = unknown> {
|
||||
/**
|
||||
* Indicates that the `ConnectionInit` message
|
||||
* has been received by the server. If this is
|
||||
* `true`, the client wont be kicked off after
|
||||
* the wait timeout has passed.
|
||||
*/
|
||||
readonly connectionInitReceived: boolean;
|
||||
/**
|
||||
* Indicates that the connection was acknowledged
|
||||
* by having dispatched the `ConnectionAck` message
|
||||
* to the related client.
|
||||
*/
|
||||
readonly acknowledged: boolean;
|
||||
/** The parameters passed during the connection initialisation. */
|
||||
readonly connectionParams?: Readonly<P>;
|
||||
/**
|
||||
* Holds the active subscriptions for this context. **All operations**
|
||||
* that are taking place are aggregated here. The user is _subscribed_
|
||||
* to an operation when waiting for result(s).
|
||||
*
|
||||
* If the subscription behind an ID is an `AsyncIterator` - the operation
|
||||
* is streaming; on the contrary, if the subscription is `null` - it is simply
|
||||
* a reservation, meaning - the operation resolves to a single result or is still
|
||||
* pending/being prepared.
|
||||
*/
|
||||
readonly subscriptions: Record<ID, AsyncGenerator<unknown> | AsyncIterable<unknown> | null>;
|
||||
/**
|
||||
* An extra field where you can store your own context values
|
||||
* to pass between callbacks.
|
||||
*/
|
||||
extra: E;
|
||||
}
|
||||
/**
|
||||
* Makes a Protocol compliant WebSocket GraphQL server. The server
|
||||
* is actually an API which is to be used with your favourite WebSocket
|
||||
* server library!
|
||||
*
|
||||
* Read more about the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export declare function makeServer<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E = unknown>(options: ServerOptions<P, E>): Server<E>;
|
||||
/**
|
||||
* Helper utility for choosing the "graphql-transport-ws" subprotocol from
|
||||
* a set of WebSocket subprotocols.
|
||||
*
|
||||
* Accepts a set of already extracted WebSocket subprotocols or the raw
|
||||
* Sec-WebSocket-Protocol header value. In either case, if the right
|
||||
* protocol appears, it will be returned.
|
||||
*
|
||||
* By specification, the server should not provide a value with Sec-WebSocket-Protocol
|
||||
* if it does not agree with client's subprotocols. The client has a responsibility
|
||||
* to handle the connection afterwards.
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export declare function handleProtocols(protocols: Set<string> | string[] | string): typeof GRAPHQL_TRANSPORT_WS_PROTOCOL | false;
|
||||
309
graphql-subscription-express/node_modules/graphql-ws/lib/server.js
generated
vendored
Normal file
309
graphql-subscription-express/node_modules/graphql-ws/lib/server.js
generated
vendored
Normal file
@@ -0,0 +1,309 @@
|
||||
"use strict";
|
||||
/**
|
||||
*
|
||||
* server
|
||||
*
|
||||
*/
|
||||
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var m = o[Symbol.asyncIterator], i;
|
||||
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
||||
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
||||
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.handleProtocols = exports.makeServer = void 0;
|
||||
const graphql_1 = require("graphql");
|
||||
const common_1 = require("./common");
|
||||
const utils_1 = require("./utils");
|
||||
/**
|
||||
* Makes a Protocol compliant WebSocket GraphQL server. The server
|
||||
* is actually an API which is to be used with your favourite WebSocket
|
||||
* server library!
|
||||
*
|
||||
* Read more about the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
function makeServer(options) {
|
||||
const { schema, context, roots, validate, execute, subscribe, connectionInitWaitTimeout = 3000, // 3 seconds
|
||||
onConnect, onDisconnect, onClose, onSubscribe, onOperation, onNext, onError, onComplete, jsonMessageReviver: reviver, jsonMessageReplacer: replacer, } = options;
|
||||
return {
|
||||
opened(socket, extra) {
|
||||
const ctx = {
|
||||
connectionInitReceived: false,
|
||||
acknowledged: false,
|
||||
subscriptions: {},
|
||||
extra,
|
||||
};
|
||||
if (socket.protocol !== common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL) {
|
||||
socket.close(common_1.CloseCode.SubprotocolNotAcceptable, 'Subprotocol not acceptable');
|
||||
return async (code, reason) => {
|
||||
/* nothing was set up, just notify the closure */
|
||||
await (onClose === null || onClose === void 0 ? void 0 : onClose(ctx, code, reason));
|
||||
};
|
||||
}
|
||||
// kick the client off (close socket) if the connection has
|
||||
// not been initialised after the specified wait timeout
|
||||
const connectionInitWait = connectionInitWaitTimeout > 0 && isFinite(connectionInitWaitTimeout)
|
||||
? setTimeout(() => {
|
||||
if (!ctx.connectionInitReceived)
|
||||
socket.close(common_1.CloseCode.ConnectionInitialisationTimeout, 'Connection initialisation timeout');
|
||||
}, connectionInitWaitTimeout)
|
||||
: null;
|
||||
socket.onMessage(async function onMessage(data) {
|
||||
var _a, e_1, _b, _c;
|
||||
var _d;
|
||||
let message;
|
||||
try {
|
||||
message = (0, common_1.parseMessage)(data, reviver);
|
||||
}
|
||||
catch (err) {
|
||||
return socket.close(common_1.CloseCode.BadRequest, 'Invalid message received');
|
||||
}
|
||||
switch (message.type) {
|
||||
case common_1.MessageType.ConnectionInit: {
|
||||
if (ctx.connectionInitReceived)
|
||||
return socket.close(common_1.CloseCode.TooManyInitialisationRequests, 'Too many initialisation requests');
|
||||
// @ts-expect-error: I can write
|
||||
ctx.connectionInitReceived = true;
|
||||
if ((0, utils_1.isObject)(message.payload))
|
||||
// @ts-expect-error: I can write
|
||||
ctx.connectionParams = message.payload;
|
||||
const permittedOrPayload = await (onConnect === null || onConnect === void 0 ? void 0 : onConnect(ctx));
|
||||
if (permittedOrPayload === false)
|
||||
return socket.close(common_1.CloseCode.Forbidden, 'Forbidden');
|
||||
// we should acknowledge before send to avoid race conditions (like those exampled in https://github.com/enisdenjo/graphql-ws/issues/501)
|
||||
// even if the send fails/throws, the connection should be closed because its malfunctioning
|
||||
// @ts-expect-error: I can write
|
||||
ctx.acknowledged = true;
|
||||
await socket.send((0, common_1.stringifyMessage)((0, utils_1.isObject)(permittedOrPayload)
|
||||
? {
|
||||
type: common_1.MessageType.ConnectionAck,
|
||||
payload: permittedOrPayload,
|
||||
}
|
||||
: {
|
||||
type: common_1.MessageType.ConnectionAck,
|
||||
// payload is completely absent if not provided
|
||||
}, replacer));
|
||||
return;
|
||||
}
|
||||
case common_1.MessageType.Ping: {
|
||||
if (socket.onPing)
|
||||
// if the onPing listener is registered, automatic pong is disabled
|
||||
return await socket.onPing(message.payload);
|
||||
await socket.send((0, common_1.stringifyMessage)(message.payload
|
||||
? { type: common_1.MessageType.Pong, payload: message.payload }
|
||||
: {
|
||||
type: common_1.MessageType.Pong,
|
||||
// payload is completely absent if not provided
|
||||
}));
|
||||
return;
|
||||
}
|
||||
case common_1.MessageType.Pong:
|
||||
return await ((_d = socket.onPong) === null || _d === void 0 ? void 0 : _d.call(socket, message.payload));
|
||||
case common_1.MessageType.Subscribe: {
|
||||
if (!ctx.acknowledged)
|
||||
return socket.close(common_1.CloseCode.Unauthorized, 'Unauthorized');
|
||||
const { id, payload } = message;
|
||||
if (id in ctx.subscriptions)
|
||||
return socket.close(common_1.CloseCode.SubscriberAlreadyExists, `Subscriber for ${id} already exists`);
|
||||
// if this turns out to be a streaming operation, the subscription value
|
||||
// will change to an `AsyncIterable`, otherwise it will stay as is
|
||||
ctx.subscriptions[id] = null;
|
||||
const emit = {
|
||||
next: async (result, args) => {
|
||||
let nextMessage = {
|
||||
id,
|
||||
type: common_1.MessageType.Next,
|
||||
payload: result,
|
||||
};
|
||||
const maybeResult = await (onNext === null || onNext === void 0 ? void 0 : onNext(ctx, nextMessage, args, result));
|
||||
if (maybeResult)
|
||||
nextMessage = Object.assign(Object.assign({}, nextMessage), { payload: maybeResult });
|
||||
await socket.send((0, common_1.stringifyMessage)(nextMessage, replacer));
|
||||
},
|
||||
error: async (errors) => {
|
||||
let errorMessage = {
|
||||
id,
|
||||
type: common_1.MessageType.Error,
|
||||
payload: errors,
|
||||
};
|
||||
const maybeErrors = await (onError === null || onError === void 0 ? void 0 : onError(ctx, errorMessage, errors));
|
||||
if (maybeErrors)
|
||||
errorMessage = Object.assign(Object.assign({}, errorMessage), { payload: maybeErrors });
|
||||
await socket.send((0, common_1.stringifyMessage)(errorMessage, replacer));
|
||||
},
|
||||
complete: async (notifyClient) => {
|
||||
const completeMessage = {
|
||||
id,
|
||||
type: common_1.MessageType.Complete,
|
||||
};
|
||||
await (onComplete === null || onComplete === void 0 ? void 0 : onComplete(ctx, completeMessage));
|
||||
if (notifyClient)
|
||||
await socket.send((0, common_1.stringifyMessage)(completeMessage, replacer));
|
||||
},
|
||||
};
|
||||
try {
|
||||
let execArgs;
|
||||
const maybeExecArgsOrErrors = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(ctx, message));
|
||||
if (maybeExecArgsOrErrors) {
|
||||
if ((0, utils_1.areGraphQLErrors)(maybeExecArgsOrErrors))
|
||||
return id in ctx.subscriptions
|
||||
? await emit.error(maybeExecArgsOrErrors)
|
||||
: void 0;
|
||||
else if (Array.isArray(maybeExecArgsOrErrors))
|
||||
throw new Error('Invalid return value from onSubscribe hook, expected an array of GraphQLError objects');
|
||||
// not errors, is exec args
|
||||
execArgs = maybeExecArgsOrErrors;
|
||||
}
|
||||
else {
|
||||
// you either provide a schema dynamically through
|
||||
// `onSubscribe` or you set one up during the server setup
|
||||
if (!schema)
|
||||
throw new Error('The GraphQL schema is not provided');
|
||||
const args = {
|
||||
operationName: payload.operationName,
|
||||
document: (0, graphql_1.parse)(payload.query),
|
||||
variableValues: payload.variables,
|
||||
};
|
||||
execArgs = Object.assign(Object.assign({}, args), { schema: typeof schema === 'function'
|
||||
? await schema(ctx, message, args)
|
||||
: schema });
|
||||
const validationErrors = (validate !== null && validate !== void 0 ? validate : graphql_1.validate)(execArgs.schema, execArgs.document);
|
||||
if (validationErrors.length > 0)
|
||||
return id in ctx.subscriptions
|
||||
? await emit.error(validationErrors)
|
||||
: void 0;
|
||||
}
|
||||
const operationAST = (0, graphql_1.getOperationAST)(execArgs.document, execArgs.operationName);
|
||||
if (!operationAST)
|
||||
return id in ctx.subscriptions
|
||||
? await emit.error([
|
||||
new graphql_1.GraphQLError('Unable to identify operation'),
|
||||
])
|
||||
: void 0;
|
||||
// if `onSubscribe` didn't specify a rootValue, inject one
|
||||
if (!('rootValue' in execArgs))
|
||||
execArgs.rootValue = roots === null || roots === void 0 ? void 0 : roots[operationAST.operation];
|
||||
// if `onSubscribe` didn't specify a context, inject one
|
||||
if (!('contextValue' in execArgs))
|
||||
execArgs.contextValue =
|
||||
typeof context === 'function'
|
||||
? await context(ctx, message, execArgs)
|
||||
: context;
|
||||
// the execution arguments have been prepared
|
||||
// perform the operation and act accordingly
|
||||
let operationResult;
|
||||
if (operationAST.operation === 'subscription')
|
||||
operationResult = await (subscribe !== null && subscribe !== void 0 ? subscribe : graphql_1.subscribe)(execArgs);
|
||||
// operation === 'query' || 'mutation'
|
||||
else
|
||||
operationResult = await (execute !== null && execute !== void 0 ? execute : graphql_1.execute)(execArgs);
|
||||
const maybeResult = await (onOperation === null || onOperation === void 0 ? void 0 : onOperation(ctx, message, execArgs, operationResult));
|
||||
if (maybeResult)
|
||||
operationResult = maybeResult;
|
||||
if ((0, utils_1.isAsyncIterable)(operationResult)) {
|
||||
/** multiple emitted results */
|
||||
if (!(id in ctx.subscriptions)) {
|
||||
// subscription was completed/canceled before the operation settled
|
||||
if ((0, utils_1.isAsyncGenerator)(operationResult))
|
||||
operationResult.return(undefined);
|
||||
}
|
||||
else {
|
||||
ctx.subscriptions[id] = operationResult;
|
||||
try {
|
||||
for (var _e = true, operationResult_1 = __asyncValues(operationResult), operationResult_1_1; operationResult_1_1 = await operationResult_1.next(), _a = operationResult_1_1.done, !_a; _e = true) {
|
||||
_c = operationResult_1_1.value;
|
||||
_e = false;
|
||||
const result = _c;
|
||||
await emit.next(result, execArgs);
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (!_e && !_a && (_b = operationResult_1.return)) await _b.call(operationResult_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/** single emitted result */
|
||||
// if the client completed the subscription before the single result
|
||||
// became available, he effectively canceled it and no data should be sent
|
||||
if (id in ctx.subscriptions)
|
||||
await emit.next(operationResult, execArgs);
|
||||
}
|
||||
// lack of subscription at this point indicates that the client
|
||||
// completed the subscription, he doesn't need to be reminded
|
||||
await emit.complete(id in ctx.subscriptions);
|
||||
}
|
||||
finally {
|
||||
// whatever happens to the subscription, we finally want to get rid of the reservation
|
||||
delete ctx.subscriptions[id];
|
||||
}
|
||||
return;
|
||||
}
|
||||
case common_1.MessageType.Complete: {
|
||||
const subscription = ctx.subscriptions[message.id];
|
||||
delete ctx.subscriptions[message.id]; // deleting the subscription means no further activity should take place
|
||||
if ((0, utils_1.isAsyncGenerator)(subscription))
|
||||
await subscription.return(undefined);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unexpected message of type ${message.type} received`);
|
||||
}
|
||||
});
|
||||
// wait for close, cleanup and the disconnect callback
|
||||
return async (code, reason) => {
|
||||
if (connectionInitWait)
|
||||
clearTimeout(connectionInitWait);
|
||||
const subs = Object.assign({}, ctx.subscriptions);
|
||||
// @ts-expect-error: I can write
|
||||
ctx.subscriptions = {}; // deleting the subscription means no further activity should take place
|
||||
// we return all iterable subscriptions immediatelly, independant of the order
|
||||
await Promise.all(Object.values(subs)
|
||||
.filter(utils_1.isAsyncGenerator)
|
||||
.map((sub) => sub.return(undefined)));
|
||||
if (ctx.acknowledged)
|
||||
await (onDisconnect === null || onDisconnect === void 0 ? void 0 : onDisconnect(ctx, code, reason));
|
||||
await (onClose === null || onClose === void 0 ? void 0 : onClose(ctx, code, reason));
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
exports.makeServer = makeServer;
|
||||
/**
|
||||
* Helper utility for choosing the "graphql-transport-ws" subprotocol from
|
||||
* a set of WebSocket subprotocols.
|
||||
*
|
||||
* Accepts a set of already extracted WebSocket subprotocols or the raw
|
||||
* Sec-WebSocket-Protocol header value. In either case, if the right
|
||||
* protocol appears, it will be returned.
|
||||
*
|
||||
* By specification, the server should not provide a value with Sec-WebSocket-Protocol
|
||||
* if it does not agree with client's subprotocols. The client has a responsibility
|
||||
* to handle the connection afterwards.
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
function handleProtocols(protocols) {
|
||||
switch (true) {
|
||||
case protocols instanceof Set &&
|
||||
protocols.has(common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL):
|
||||
case Array.isArray(protocols) &&
|
||||
protocols.includes(common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL):
|
||||
case typeof protocols === 'string' &&
|
||||
protocols
|
||||
.split(',')
|
||||
.map((p) => p.trim())
|
||||
.includes(common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL):
|
||||
return common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.handleProtocols = handleProtocols;
|
||||
304
graphql-subscription-express/node_modules/graphql-ws/lib/server.mjs
generated
vendored
Normal file
304
graphql-subscription-express/node_modules/graphql-ws/lib/server.mjs
generated
vendored
Normal file
@@ -0,0 +1,304 @@
|
||||
/**
|
||||
*
|
||||
* server
|
||||
*
|
||||
*/
|
||||
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var m = o[Symbol.asyncIterator], i;
|
||||
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
||||
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
||||
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
||||
};
|
||||
import { parse, validate as graphqlValidate, execute as graphqlExecute, subscribe as graphqlSubscribe, getOperationAST, GraphQLError, } from 'graphql';
|
||||
import { GRAPHQL_TRANSPORT_WS_PROTOCOL, CloseCode, MessageType, stringifyMessage, parseMessage, } from './common.mjs';
|
||||
import { isObject, isAsyncGenerator, isAsyncIterable, areGraphQLErrors, } from './utils.mjs';
|
||||
/**
|
||||
* Makes a Protocol compliant WebSocket GraphQL server. The server
|
||||
* is actually an API which is to be used with your favourite WebSocket
|
||||
* server library!
|
||||
*
|
||||
* Read more about the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export function makeServer(options) {
|
||||
const { schema, context, roots, validate, execute, subscribe, connectionInitWaitTimeout = 3000, // 3 seconds
|
||||
onConnect, onDisconnect, onClose, onSubscribe, onOperation, onNext, onError, onComplete, jsonMessageReviver: reviver, jsonMessageReplacer: replacer, } = options;
|
||||
return {
|
||||
opened(socket, extra) {
|
||||
const ctx = {
|
||||
connectionInitReceived: false,
|
||||
acknowledged: false,
|
||||
subscriptions: {},
|
||||
extra,
|
||||
};
|
||||
if (socket.protocol !== GRAPHQL_TRANSPORT_WS_PROTOCOL) {
|
||||
socket.close(CloseCode.SubprotocolNotAcceptable, 'Subprotocol not acceptable');
|
||||
return async (code, reason) => {
|
||||
/* nothing was set up, just notify the closure */
|
||||
await (onClose === null || onClose === void 0 ? void 0 : onClose(ctx, code, reason));
|
||||
};
|
||||
}
|
||||
// kick the client off (close socket) if the connection has
|
||||
// not been initialised after the specified wait timeout
|
||||
const connectionInitWait = connectionInitWaitTimeout > 0 && isFinite(connectionInitWaitTimeout)
|
||||
? setTimeout(() => {
|
||||
if (!ctx.connectionInitReceived)
|
||||
socket.close(CloseCode.ConnectionInitialisationTimeout, 'Connection initialisation timeout');
|
||||
}, connectionInitWaitTimeout)
|
||||
: null;
|
||||
socket.onMessage(async function onMessage(data) {
|
||||
var _a, e_1, _b, _c;
|
||||
var _d;
|
||||
let message;
|
||||
try {
|
||||
message = parseMessage(data, reviver);
|
||||
}
|
||||
catch (err) {
|
||||
return socket.close(CloseCode.BadRequest, 'Invalid message received');
|
||||
}
|
||||
switch (message.type) {
|
||||
case MessageType.ConnectionInit: {
|
||||
if (ctx.connectionInitReceived)
|
||||
return socket.close(CloseCode.TooManyInitialisationRequests, 'Too many initialisation requests');
|
||||
// @ts-expect-error: I can write
|
||||
ctx.connectionInitReceived = true;
|
||||
if (isObject(message.payload))
|
||||
// @ts-expect-error: I can write
|
||||
ctx.connectionParams = message.payload;
|
||||
const permittedOrPayload = await (onConnect === null || onConnect === void 0 ? void 0 : onConnect(ctx));
|
||||
if (permittedOrPayload === false)
|
||||
return socket.close(CloseCode.Forbidden, 'Forbidden');
|
||||
// we should acknowledge before send to avoid race conditions (like those exampled in https://github.com/enisdenjo/graphql-ws/issues/501)
|
||||
// even if the send fails/throws, the connection should be closed because its malfunctioning
|
||||
// @ts-expect-error: I can write
|
||||
ctx.acknowledged = true;
|
||||
await socket.send(stringifyMessage(isObject(permittedOrPayload)
|
||||
? {
|
||||
type: MessageType.ConnectionAck,
|
||||
payload: permittedOrPayload,
|
||||
}
|
||||
: {
|
||||
type: MessageType.ConnectionAck,
|
||||
// payload is completely absent if not provided
|
||||
}, replacer));
|
||||
return;
|
||||
}
|
||||
case MessageType.Ping: {
|
||||
if (socket.onPing)
|
||||
// if the onPing listener is registered, automatic pong is disabled
|
||||
return await socket.onPing(message.payload);
|
||||
await socket.send(stringifyMessage(message.payload
|
||||
? { type: MessageType.Pong, payload: message.payload }
|
||||
: {
|
||||
type: MessageType.Pong,
|
||||
// payload is completely absent if not provided
|
||||
}));
|
||||
return;
|
||||
}
|
||||
case MessageType.Pong:
|
||||
return await ((_d = socket.onPong) === null || _d === void 0 ? void 0 : _d.call(socket, message.payload));
|
||||
case MessageType.Subscribe: {
|
||||
if (!ctx.acknowledged)
|
||||
return socket.close(CloseCode.Unauthorized, 'Unauthorized');
|
||||
const { id, payload } = message;
|
||||
if (id in ctx.subscriptions)
|
||||
return socket.close(CloseCode.SubscriberAlreadyExists, `Subscriber for ${id} already exists`);
|
||||
// if this turns out to be a streaming operation, the subscription value
|
||||
// will change to an `AsyncIterable`, otherwise it will stay as is
|
||||
ctx.subscriptions[id] = null;
|
||||
const emit = {
|
||||
next: async (result, args) => {
|
||||
let nextMessage = {
|
||||
id,
|
||||
type: MessageType.Next,
|
||||
payload: result,
|
||||
};
|
||||
const maybeResult = await (onNext === null || onNext === void 0 ? void 0 : onNext(ctx, nextMessage, args, result));
|
||||
if (maybeResult)
|
||||
nextMessage = Object.assign(Object.assign({}, nextMessage), { payload: maybeResult });
|
||||
await socket.send(stringifyMessage(nextMessage, replacer));
|
||||
},
|
||||
error: async (errors) => {
|
||||
let errorMessage = {
|
||||
id,
|
||||
type: MessageType.Error,
|
||||
payload: errors,
|
||||
};
|
||||
const maybeErrors = await (onError === null || onError === void 0 ? void 0 : onError(ctx, errorMessage, errors));
|
||||
if (maybeErrors)
|
||||
errorMessage = Object.assign(Object.assign({}, errorMessage), { payload: maybeErrors });
|
||||
await socket.send(stringifyMessage(errorMessage, replacer));
|
||||
},
|
||||
complete: async (notifyClient) => {
|
||||
const completeMessage = {
|
||||
id,
|
||||
type: MessageType.Complete,
|
||||
};
|
||||
await (onComplete === null || onComplete === void 0 ? void 0 : onComplete(ctx, completeMessage));
|
||||
if (notifyClient)
|
||||
await socket.send(stringifyMessage(completeMessage, replacer));
|
||||
},
|
||||
};
|
||||
try {
|
||||
let execArgs;
|
||||
const maybeExecArgsOrErrors = await (onSubscribe === null || onSubscribe === void 0 ? void 0 : onSubscribe(ctx, message));
|
||||
if (maybeExecArgsOrErrors) {
|
||||
if (areGraphQLErrors(maybeExecArgsOrErrors))
|
||||
return id in ctx.subscriptions
|
||||
? await emit.error(maybeExecArgsOrErrors)
|
||||
: void 0;
|
||||
else if (Array.isArray(maybeExecArgsOrErrors))
|
||||
throw new Error('Invalid return value from onSubscribe hook, expected an array of GraphQLError objects');
|
||||
// not errors, is exec args
|
||||
execArgs = maybeExecArgsOrErrors;
|
||||
}
|
||||
else {
|
||||
// you either provide a schema dynamically through
|
||||
// `onSubscribe` or you set one up during the server setup
|
||||
if (!schema)
|
||||
throw new Error('The GraphQL schema is not provided');
|
||||
const args = {
|
||||
operationName: payload.operationName,
|
||||
document: parse(payload.query),
|
||||
variableValues: payload.variables,
|
||||
};
|
||||
execArgs = Object.assign(Object.assign({}, args), { schema: typeof schema === 'function'
|
||||
? await schema(ctx, message, args)
|
||||
: schema });
|
||||
const validationErrors = (validate !== null && validate !== void 0 ? validate : graphqlValidate)(execArgs.schema, execArgs.document);
|
||||
if (validationErrors.length > 0)
|
||||
return id in ctx.subscriptions
|
||||
? await emit.error(validationErrors)
|
||||
: void 0;
|
||||
}
|
||||
const operationAST = getOperationAST(execArgs.document, execArgs.operationName);
|
||||
if (!operationAST)
|
||||
return id in ctx.subscriptions
|
||||
? await emit.error([
|
||||
new GraphQLError('Unable to identify operation'),
|
||||
])
|
||||
: void 0;
|
||||
// if `onSubscribe` didn't specify a rootValue, inject one
|
||||
if (!('rootValue' in execArgs))
|
||||
execArgs.rootValue = roots === null || roots === void 0 ? void 0 : roots[operationAST.operation];
|
||||
// if `onSubscribe` didn't specify a context, inject one
|
||||
if (!('contextValue' in execArgs))
|
||||
execArgs.contextValue =
|
||||
typeof context === 'function'
|
||||
? await context(ctx, message, execArgs)
|
||||
: context;
|
||||
// the execution arguments have been prepared
|
||||
// perform the operation and act accordingly
|
||||
let operationResult;
|
||||
if (operationAST.operation === 'subscription')
|
||||
operationResult = await (subscribe !== null && subscribe !== void 0 ? subscribe : graphqlSubscribe)(execArgs);
|
||||
// operation === 'query' || 'mutation'
|
||||
else
|
||||
operationResult = await (execute !== null && execute !== void 0 ? execute : graphqlExecute)(execArgs);
|
||||
const maybeResult = await (onOperation === null || onOperation === void 0 ? void 0 : onOperation(ctx, message, execArgs, operationResult));
|
||||
if (maybeResult)
|
||||
operationResult = maybeResult;
|
||||
if (isAsyncIterable(operationResult)) {
|
||||
/** multiple emitted results */
|
||||
if (!(id in ctx.subscriptions)) {
|
||||
// subscription was completed/canceled before the operation settled
|
||||
if (isAsyncGenerator(operationResult))
|
||||
operationResult.return(undefined);
|
||||
}
|
||||
else {
|
||||
ctx.subscriptions[id] = operationResult;
|
||||
try {
|
||||
for (var _e = true, operationResult_1 = __asyncValues(operationResult), operationResult_1_1; operationResult_1_1 = await operationResult_1.next(), _a = operationResult_1_1.done, !_a; _e = true) {
|
||||
_c = operationResult_1_1.value;
|
||||
_e = false;
|
||||
const result = _c;
|
||||
await emit.next(result, execArgs);
|
||||
}
|
||||
}
|
||||
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
||||
finally {
|
||||
try {
|
||||
if (!_e && !_a && (_b = operationResult_1.return)) await _b.call(operationResult_1);
|
||||
}
|
||||
finally { if (e_1) throw e_1.error; }
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
/** single emitted result */
|
||||
// if the client completed the subscription before the single result
|
||||
// became available, he effectively canceled it and no data should be sent
|
||||
if (id in ctx.subscriptions)
|
||||
await emit.next(operationResult, execArgs);
|
||||
}
|
||||
// lack of subscription at this point indicates that the client
|
||||
// completed the subscription, he doesn't need to be reminded
|
||||
await emit.complete(id in ctx.subscriptions);
|
||||
}
|
||||
finally {
|
||||
// whatever happens to the subscription, we finally want to get rid of the reservation
|
||||
delete ctx.subscriptions[id];
|
||||
}
|
||||
return;
|
||||
}
|
||||
case MessageType.Complete: {
|
||||
const subscription = ctx.subscriptions[message.id];
|
||||
delete ctx.subscriptions[message.id]; // deleting the subscription means no further activity should take place
|
||||
if (isAsyncGenerator(subscription))
|
||||
await subscription.return(undefined);
|
||||
return;
|
||||
}
|
||||
default:
|
||||
throw new Error(`Unexpected message of type ${message.type} received`);
|
||||
}
|
||||
});
|
||||
// wait for close, cleanup and the disconnect callback
|
||||
return async (code, reason) => {
|
||||
if (connectionInitWait)
|
||||
clearTimeout(connectionInitWait);
|
||||
const subs = Object.assign({}, ctx.subscriptions);
|
||||
// @ts-expect-error: I can write
|
||||
ctx.subscriptions = {}; // deleting the subscription means no further activity should take place
|
||||
// we return all iterable subscriptions immediatelly, independant of the order
|
||||
await Promise.all(Object.values(subs)
|
||||
.filter(isAsyncGenerator)
|
||||
.map((sub) => sub.return(undefined)));
|
||||
if (ctx.acknowledged)
|
||||
await (onDisconnect === null || onDisconnect === void 0 ? void 0 : onDisconnect(ctx, code, reason));
|
||||
await (onClose === null || onClose === void 0 ? void 0 : onClose(ctx, code, reason));
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Helper utility for choosing the "graphql-transport-ws" subprotocol from
|
||||
* a set of WebSocket subprotocols.
|
||||
*
|
||||
* Accepts a set of already extracted WebSocket subprotocols or the raw
|
||||
* Sec-WebSocket-Protocol header value. In either case, if the right
|
||||
* protocol appears, it will be returned.
|
||||
*
|
||||
* By specification, the server should not provide a value with Sec-WebSocket-Protocol
|
||||
* if it does not agree with client's subprotocols. The client has a responsibility
|
||||
* to handle the connection afterwards.
|
||||
*
|
||||
* @category Server
|
||||
*/
|
||||
export function handleProtocols(protocols) {
|
||||
switch (true) {
|
||||
case protocols instanceof Set &&
|
||||
protocols.has(GRAPHQL_TRANSPORT_WS_PROTOCOL):
|
||||
case Array.isArray(protocols) &&
|
||||
protocols.includes(GRAPHQL_TRANSPORT_WS_PROTOCOL):
|
||||
case typeof protocols === 'string' &&
|
||||
protocols
|
||||
.split(',')
|
||||
.map((p) => p.trim())
|
||||
.includes(GRAPHQL_TRANSPORT_WS_PROTOCOL):
|
||||
return GRAPHQL_TRANSPORT_WS_PROTOCOL;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
36
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.d.mts
generated
vendored
Normal file
36
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.d.mts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import type * as fastifyWebsocket from '@fastify/websocket';
|
||||
import { ServerOptions } from '../../server.mjs';
|
||||
import { ConnectionInitMessage } from '../../common.mjs';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/@fastify/websocket
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The underlying socket connection between the server and the client.
|
||||
* The WebSocket socket is located under the `socket` parameter.
|
||||
*/
|
||||
readonly connection: fastifyWebsocket.SocketStream;
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*/
|
||||
readonly request: FastifyRequest;
|
||||
}
|
||||
/**
|
||||
* Make a handler to use on a [@fastify/websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/@fastify/websocket
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): fastifyWebsocket.WebsocketHandler;
|
||||
36
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.d.ts
generated
vendored
Normal file
36
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.d.ts
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import type * as fastifyWebsocket from '@fastify/websocket';
|
||||
import { ServerOptions } from '../../server';
|
||||
import { ConnectionInitMessage } from '../../common';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/@fastify/websocket
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The underlying socket connection between the server and the client.
|
||||
* The WebSocket socket is located under the `socket` parameter.
|
||||
*/
|
||||
readonly connection: fastifyWebsocket.SocketStream;
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*/
|
||||
readonly request: FastifyRequest;
|
||||
}
|
||||
/**
|
||||
* Make a handler to use on a [@fastify/websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/@fastify/websocket
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): fastifyWebsocket.WebsocketHandler;
|
||||
126
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.js
generated
vendored
Normal file
126
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.js
generated
vendored
Normal file
@@ -0,0 +1,126 @@
|
||||
"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");
|
||||
/**
|
||||
* Make a handler to use on a [@fastify/websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/@fastify/websocket
|
||||
*/
|
||||
function makeHandler(options,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
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 instanceof Error ? err.message : String(err), '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 instanceof Error ? err.message : String(err), '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 instanceof Error ? err.message : String(err), '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));
|
||||
});
|
||||
};
|
||||
}
|
||||
exports.makeHandler = makeHandler;
|
||||
122
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.mjs
generated
vendored
Normal file
122
graphql-subscription-express/node_modules/graphql-ws/lib/use/@fastify/websocket.mjs
generated
vendored
Normal file
@@ -0,0 +1,122 @@
|
||||
import { handleProtocols, makeServer } from '../../server.mjs';
|
||||
import { DEPRECATED_GRAPHQL_WS_PROTOCOL, CloseCode, } from '../../common.mjs';
|
||||
import { limitCloseReason } from '../../utils.mjs';
|
||||
/**
|
||||
* Make a handler to use on a [@fastify/websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/@fastify/websocket
|
||||
*/
|
||||
export function makeHandler(options,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const server = 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 = 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(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), '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(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), '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(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
}),
|
||||
}, { connection, request });
|
||||
socket.once('close', (code, reason) => {
|
||||
if (pongWait)
|
||||
clearTimeout(pongWait);
|
||||
if (pingInterval)
|
||||
clearInterval(pingInterval);
|
||||
if (!isProd &&
|
||||
code === CloseCode.SubprotocolNotAcceptable &&
|
||||
socket.protocol === 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));
|
||||
});
|
||||
};
|
||||
}
|
||||
60
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.d.mts
generated
vendored
Normal file
60
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.d.mts
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/// <reference types="bun-types" />
|
||||
import type { WebSocketHandler, ServerWebSocket } from 'bun';
|
||||
import { ConnectionInitMessage } from '../common.mjs';
|
||||
import { ServerOptions } from '../server.mjs';
|
||||
/**
|
||||
* Convenience export for checking the WebSocket protocol on the request in `Bun.serve`.
|
||||
*/
|
||||
export { handleProtocols } from '../server.mjs';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/bun
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The actual socket connection between the server and the client.
|
||||
*/
|
||||
readonly socket: ServerWebSocket;
|
||||
}
|
||||
/**
|
||||
* Use the server with [Bun](https://bun.sh/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* The WebSocket subprotocol is not available on the established socket and therefore
|
||||
* needs to be checked during the request handling.
|
||||
*
|
||||
* Additionally, the keep-alive logic _seems_ to be handled by Bun seeing that
|
||||
* they default [`sendPingsAutomatically` to `true`](https://github.com/oven-sh/bun/blob/6a163cf933542506354dc836bd92693bcae5939b/src/deps/uws.zig#L893).
|
||||
*
|
||||
* ```ts
|
||||
* import { makeHandler, handleProtocols } from 'graphql-ws/lib/use/lib/bun';
|
||||
* import { schema } from './my-schema/index.mjs';
|
||||
*
|
||||
* Bun.serve({
|
||||
* fetch(req, server) {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* if (!handleProtocols(req.headers.get('sec-websocket-protocol') || '')) {
|
||||
* return new Response('Bad Request', { status: 404 });
|
||||
* }
|
||||
* if (!server.upgrade(req)) {
|
||||
* return new Response('Internal Server Error', { status: 500 });
|
||||
* }
|
||||
* return new Response();
|
||||
* },
|
||||
* websocket: makeHandler({ schema }),
|
||||
* port: 4000,
|
||||
* });
|
||||
*
|
||||
* console.log('Listening to port 4000');
|
||||
* ```
|
||||
*
|
||||
* @category Server/bun
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>): WebSocketHandler;
|
||||
60
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.d.ts
generated
vendored
Normal file
60
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.d.ts
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
/// <reference types="bun-types" />
|
||||
import type { WebSocketHandler, ServerWebSocket } from 'bun';
|
||||
import { ConnectionInitMessage } from '../common';
|
||||
import { ServerOptions } from '../server';
|
||||
/**
|
||||
* Convenience export for checking the WebSocket protocol on the request in `Bun.serve`.
|
||||
*/
|
||||
export { handleProtocols } from '../server';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/bun
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The actual socket connection between the server and the client.
|
||||
*/
|
||||
readonly socket: ServerWebSocket;
|
||||
}
|
||||
/**
|
||||
* Use the server with [Bun](https://bun.sh/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* The WebSocket subprotocol is not available on the established socket and therefore
|
||||
* needs to be checked during the request handling.
|
||||
*
|
||||
* Additionally, the keep-alive logic _seems_ to be handled by Bun seeing that
|
||||
* they default [`sendPingsAutomatically` to `true`](https://github.com/oven-sh/bun/blob/6a163cf933542506354dc836bd92693bcae5939b/src/deps/uws.zig#L893).
|
||||
*
|
||||
* ```ts
|
||||
* import { makeHandler, handleProtocols } from 'graphql-ws/lib/use/lib/bun';
|
||||
* import { schema } from './my-schema';
|
||||
*
|
||||
* Bun.serve({
|
||||
* fetch(req, server) {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* if (!handleProtocols(req.headers.get('sec-websocket-protocol') || '')) {
|
||||
* return new Response('Bad Request', { status: 404 });
|
||||
* }
|
||||
* if (!server.upgrade(req)) {
|
||||
* return new Response('Internal Server Error', { status: 500 });
|
||||
* }
|
||||
* return new Response();
|
||||
* },
|
||||
* websocket: makeHandler({ schema }),
|
||||
* port: 4000,
|
||||
* });
|
||||
*
|
||||
* console.log('Listening to port 4000');
|
||||
* ```
|
||||
*
|
||||
* @category Server/bun
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>): WebSocketHandler;
|
||||
97
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.js
generated
vendored
Normal file
97
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.js
generated
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
/// <reference types="bun-types" />
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.makeHandler = exports.handleProtocols = void 0;
|
||||
const common_1 = require("../common");
|
||||
const server_1 = require("../server");
|
||||
/**
|
||||
* Convenience export for checking the WebSocket protocol on the request in `Bun.serve`.
|
||||
*/
|
||||
var server_2 = require("../server");
|
||||
Object.defineProperty(exports, "handleProtocols", { enumerable: true, get: function () { return server_2.handleProtocols; } });
|
||||
/**
|
||||
* Use the server with [Bun](https://bun.sh/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* The WebSocket subprotocol is not available on the established socket and therefore
|
||||
* needs to be checked during the request handling.
|
||||
*
|
||||
* Additionally, the keep-alive logic _seems_ to be handled by Bun seeing that
|
||||
* they default [`sendPingsAutomatically` to `true`](https://github.com/oven-sh/bun/blob/6a163cf933542506354dc836bd92693bcae5939b/src/deps/uws.zig#L893).
|
||||
*
|
||||
* ```ts
|
||||
* import { makeHandler, handleProtocols } from 'graphql-ws/lib/use/lib/bun';
|
||||
* import { schema } from './my-schema';
|
||||
*
|
||||
* Bun.serve({
|
||||
* fetch(req, server) {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* if (!handleProtocols(req.headers.get('sec-websocket-protocol') || '')) {
|
||||
* return new Response('Bad Request', { status: 404 });
|
||||
* }
|
||||
* if (!server.upgrade(req)) {
|
||||
* return new Response('Internal Server Error', { status: 500 });
|
||||
* }
|
||||
* return new Response();
|
||||
* },
|
||||
* websocket: makeHandler({ schema }),
|
||||
* port: 4000,
|
||||
* });
|
||||
*
|
||||
* console.log('Listening to port 4000');
|
||||
* ```
|
||||
*
|
||||
* @category Server/bun
|
||||
*/
|
||||
function makeHandler(options) {
|
||||
const server = (0, server_1.makeServer)(options);
|
||||
const clients = new WeakMap();
|
||||
return {
|
||||
open(ws) {
|
||||
const client = {
|
||||
handleMessage: () => {
|
||||
throw new Error('Message received before handler was registered');
|
||||
},
|
||||
closed: () => {
|
||||
throw new Error('Closed before handler was registered');
|
||||
},
|
||||
};
|
||||
client.closed = server.opened({
|
||||
// TODO: use protocol on socket once Bun exposes it
|
||||
protocol: common_1.GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
send: async (message) => {
|
||||
// ws might have been destroyed in the meantime, send only if exists
|
||||
if (clients.has(ws)) {
|
||||
ws.sendText(message);
|
||||
}
|
||||
},
|
||||
close: (code, reason) => {
|
||||
if (clients.has(ws)) {
|
||||
ws.close(code, reason);
|
||||
}
|
||||
},
|
||||
onMessage: (cb) => (client.handleMessage = cb),
|
||||
}, { socket: ws });
|
||||
clients.set(ws, client);
|
||||
},
|
||||
message(ws, message) {
|
||||
const client = clients.get(ws);
|
||||
if (!client)
|
||||
throw new Error('Message received for a missing client');
|
||||
return client.handleMessage(String(message));
|
||||
},
|
||||
close(ws, code, message) {
|
||||
const client = clients.get(ws);
|
||||
if (!client)
|
||||
throw new Error('Closing a missing client');
|
||||
return client.closed(code, message);
|
||||
},
|
||||
};
|
||||
}
|
||||
exports.makeHandler = makeHandler;
|
||||
92
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.mjs
generated
vendored
Normal file
92
graphql-subscription-express/node_modules/graphql-ws/lib/use/bun.mjs
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
/// <reference types="bun-types" />
|
||||
import { GRAPHQL_TRANSPORT_WS_PROTOCOL, } from '../common.mjs';
|
||||
import { makeServer } from '../server.mjs';
|
||||
/**
|
||||
* Convenience export for checking the WebSocket protocol on the request in `Bun.serve`.
|
||||
*/
|
||||
export { handleProtocols } from '../server.mjs';
|
||||
/**
|
||||
* Use the server with [Bun](https://bun.sh/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* The WebSocket subprotocol is not available on the established socket and therefore
|
||||
* needs to be checked during the request handling.
|
||||
*
|
||||
* Additionally, the keep-alive logic _seems_ to be handled by Bun seeing that
|
||||
* they default [`sendPingsAutomatically` to `true`](https://github.com/oven-sh/bun/blob/6a163cf933542506354dc836bd92693bcae5939b/src/deps/uws.zig#L893).
|
||||
*
|
||||
* ```ts
|
||||
* import { makeHandler, handleProtocols } from 'graphql-ws/lib/use/lib/bun';
|
||||
* import { schema } from './my-schema/index.mjs';
|
||||
*
|
||||
* Bun.serve({
|
||||
* fetch(req, server) {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* if (!handleProtocols(req.headers.get('sec-websocket-protocol') || '')) {
|
||||
* return new Response('Bad Request', { status: 404 });
|
||||
* }
|
||||
* if (!server.upgrade(req)) {
|
||||
* return new Response('Internal Server Error', { status: 500 });
|
||||
* }
|
||||
* return new Response();
|
||||
* },
|
||||
* websocket: makeHandler({ schema }),
|
||||
* port: 4000,
|
||||
* });
|
||||
*
|
||||
* console.log('Listening to port 4000');
|
||||
* ```
|
||||
*
|
||||
* @category Server/bun
|
||||
*/
|
||||
export function makeHandler(options) {
|
||||
const server = makeServer(options);
|
||||
const clients = new WeakMap();
|
||||
return {
|
||||
open(ws) {
|
||||
const client = {
|
||||
handleMessage: () => {
|
||||
throw new Error('Message received before handler was registered');
|
||||
},
|
||||
closed: () => {
|
||||
throw new Error('Closed before handler was registered');
|
||||
},
|
||||
};
|
||||
client.closed = server.opened({
|
||||
// TODO: use protocol on socket once Bun exposes it
|
||||
protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
send: async (message) => {
|
||||
// ws might have been destroyed in the meantime, send only if exists
|
||||
if (clients.has(ws)) {
|
||||
ws.sendText(message);
|
||||
}
|
||||
},
|
||||
close: (code, reason) => {
|
||||
if (clients.has(ws)) {
|
||||
ws.close(code, reason);
|
||||
}
|
||||
},
|
||||
onMessage: (cb) => (client.handleMessage = cb),
|
||||
}, { socket: ws });
|
||||
clients.set(ws, client);
|
||||
},
|
||||
message(ws, message) {
|
||||
const client = clients.get(ws);
|
||||
if (!client)
|
||||
throw new Error('Message received for a missing client');
|
||||
return client.handleMessage(String(message));
|
||||
},
|
||||
close(ws, code, message) {
|
||||
const client = clients.get(ws);
|
||||
if (!client)
|
||||
throw new Error('Closing a missing client');
|
||||
return client.closed(code, message);
|
||||
},
|
||||
};
|
||||
}
|
||||
56
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.d.mts
generated
vendored
Normal file
56
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.d.mts
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import { ServerOptions } from '../server.mjs';
|
||||
import { ConnectionInitMessage } from '../common.mjs';
|
||||
export { GRAPHQL_TRANSPORT_WS_PROTOCOL } from '../common.mjs';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/deno
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The actual socket connection between the server and the client.
|
||||
*/
|
||||
readonly socket: WebSocket;
|
||||
}
|
||||
/**
|
||||
* Use the server with [Deno](https://deno.com/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs.
|
||||
*
|
||||
* The keep-alive is set in `Deno.upgradeWebSocket` during the upgrade.
|
||||
*
|
||||
* Additionally, the required WebSocket protocol is also defined during the upgrade,
|
||||
* the correct example being:
|
||||
*
|
||||
* ```ts
|
||||
* import { serve } from 'https://deno.land/std/http/mod.ts';
|
||||
* import {
|
||||
* makeHandler,
|
||||
* GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* } from 'https://esm.sh/graphql-ws/lib/use/deno';
|
||||
* import { schema } from './my-schema.ts/index.mjs';
|
||||
*
|
||||
* const handler = makeHandler({ schema });
|
||||
*
|
||||
* serve(
|
||||
* (req: Request) => {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* const { socket, response } = Deno.upgradeWebSocket(req, {
|
||||
* protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* idleTimeout: 12_000,
|
||||
* });
|
||||
* handler(socket);
|
||||
* return response;
|
||||
* },
|
||||
* { port: 4000 },
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @category Server/deno
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>): (socket: WebSocket) => void;
|
||||
56
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.d.ts
generated
vendored
Normal file
56
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.d.ts
generated
vendored
Normal file
@@ -0,0 +1,56 @@
|
||||
import { ServerOptions } from '../server';
|
||||
import { ConnectionInitMessage } from '../common';
|
||||
export { GRAPHQL_TRANSPORT_WS_PROTOCOL } from '../common';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/deno
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The actual socket connection between the server and the client.
|
||||
*/
|
||||
readonly socket: WebSocket;
|
||||
}
|
||||
/**
|
||||
* Use the server with [Deno](https://deno.com/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs.
|
||||
*
|
||||
* The keep-alive is set in `Deno.upgradeWebSocket` during the upgrade.
|
||||
*
|
||||
* Additionally, the required WebSocket protocol is also defined during the upgrade,
|
||||
* the correct example being:
|
||||
*
|
||||
* ```ts
|
||||
* import { serve } from 'https://deno.land/std/http/mod.ts';
|
||||
* import {
|
||||
* makeHandler,
|
||||
* GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* } from 'https://esm.sh/graphql-ws/lib/use/deno';
|
||||
* import { schema } from './my-schema.ts';
|
||||
*
|
||||
* const handler = makeHandler({ schema });
|
||||
*
|
||||
* serve(
|
||||
* (req: Request) => {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* const { socket, response } = Deno.upgradeWebSocket(req, {
|
||||
* protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* idleTimeout: 12_000,
|
||||
* });
|
||||
* handler(socket);
|
||||
* return response;
|
||||
* },
|
||||
* { port: 4000 },
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @category Server/deno
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>): (socket: WebSocket) => void;
|
||||
88
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.js
generated
vendored
Normal file
88
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.js
generated
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.makeHandler = exports.GRAPHQL_TRANSPORT_WS_PROTOCOL = void 0;
|
||||
const server_1 = require("../server");
|
||||
const common_1 = require("../common");
|
||||
var common_2 = require("../common");
|
||||
Object.defineProperty(exports, "GRAPHQL_TRANSPORT_WS_PROTOCOL", { enumerable: true, get: function () { return common_2.GRAPHQL_TRANSPORT_WS_PROTOCOL; } });
|
||||
/**
|
||||
* Use the server with [Deno](https://deno.com/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs.
|
||||
*
|
||||
* The keep-alive is set in `Deno.upgradeWebSocket` during the upgrade.
|
||||
*
|
||||
* Additionally, the required WebSocket protocol is also defined during the upgrade,
|
||||
* the correct example being:
|
||||
*
|
||||
* ```ts
|
||||
* import { serve } from 'https://deno.land/std/http/mod.ts';
|
||||
* import {
|
||||
* makeHandler,
|
||||
* GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* } from 'https://esm.sh/graphql-ws/lib/use/deno';
|
||||
* import { schema } from './my-schema.ts';
|
||||
*
|
||||
* const handler = makeHandler({ schema });
|
||||
*
|
||||
* serve(
|
||||
* (req: Request) => {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* const { socket, response } = Deno.upgradeWebSocket(req, {
|
||||
* protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* idleTimeout: 12_000,
|
||||
* });
|
||||
* handler(socket);
|
||||
* return response;
|
||||
* },
|
||||
* { port: 4000 },
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @category Server/deno
|
||||
*/
|
||||
function makeHandler(options) {
|
||||
const server = (0, server_1.makeServer)(options);
|
||||
return function handle(socket) {
|
||||
socket.onerror = (err) => {
|
||||
console.error('Internal error emitted on the WebSocket socket. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.close(common_1.CloseCode.InternalServerError, 'Internal server error');
|
||||
};
|
||||
let closed = () => {
|
||||
// noop
|
||||
};
|
||||
socket.onopen = () => {
|
||||
closed = server.opened({
|
||||
protocol: socket.protocol,
|
||||
send: (msg) => socket.send(msg),
|
||||
close: (code, reason) => socket.close(code, reason),
|
||||
onMessage: (cb) => {
|
||||
socket.onmessage = async (event) => {
|
||||
try {
|
||||
await cb(String(event.data));
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Internal error occurred during message handling. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.close(common_1.CloseCode.InternalServerError, 'Internal server error');
|
||||
}
|
||||
};
|
||||
},
|
||||
}, { socket });
|
||||
};
|
||||
socket.onclose = (event) => {
|
||||
if (event.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(event.code, event.reason);
|
||||
};
|
||||
};
|
||||
}
|
||||
exports.makeHandler = makeHandler;
|
||||
83
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.mjs
generated
vendored
Normal file
83
graphql-subscription-express/node_modules/graphql-ws/lib/use/deno.mjs
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
import { makeServer } from '../server.mjs';
|
||||
import { DEPRECATED_GRAPHQL_WS_PROTOCOL, CloseCode, } from '../common.mjs';
|
||||
export { GRAPHQL_TRANSPORT_WS_PROTOCOL } from '../common.mjs';
|
||||
/**
|
||||
* Use the server with [Deno](https://deno.com/).
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs.
|
||||
*
|
||||
* The keep-alive is set in `Deno.upgradeWebSocket` during the upgrade.
|
||||
*
|
||||
* Additionally, the required WebSocket protocol is also defined during the upgrade,
|
||||
* the correct example being:
|
||||
*
|
||||
* ```ts
|
||||
* import { serve } from 'https://deno.land/std/http/mod.ts';
|
||||
* import {
|
||||
* makeHandler,
|
||||
* GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* } from 'https://esm.sh/graphql-ws/lib/use/deno';
|
||||
* import { schema } from './my-schema.ts/index.mjs';
|
||||
*
|
||||
* const handler = makeHandler({ schema });
|
||||
*
|
||||
* serve(
|
||||
* (req: Request) => {
|
||||
* const [path, _search] = req.url.split('?');
|
||||
* if (!path.endsWith('/graphql')) {
|
||||
* return new Response('Not Found', { status: 404 });
|
||||
* }
|
||||
* if (req.headers.get('upgrade') != 'websocket') {
|
||||
* return new Response('Upgrade Required', { status: 426 });
|
||||
* }
|
||||
* const { socket, response } = Deno.upgradeWebSocket(req, {
|
||||
* protocol: GRAPHQL_TRANSPORT_WS_PROTOCOL,
|
||||
* idleTimeout: 12_000,
|
||||
* });
|
||||
* handler(socket);
|
||||
* return response;
|
||||
* },
|
||||
* { port: 4000 },
|
||||
* );
|
||||
* ```
|
||||
*
|
||||
* @category Server/deno
|
||||
*/
|
||||
export function makeHandler(options) {
|
||||
const server = makeServer(options);
|
||||
return function handle(socket) {
|
||||
socket.onerror = (err) => {
|
||||
console.error('Internal error emitted on the WebSocket socket. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.close(CloseCode.InternalServerError, 'Internal server error');
|
||||
};
|
||||
let closed = () => {
|
||||
// noop
|
||||
};
|
||||
socket.onopen = () => {
|
||||
closed = server.opened({
|
||||
protocol: socket.protocol,
|
||||
send: (msg) => socket.send(msg),
|
||||
close: (code, reason) => socket.close(code, reason),
|
||||
onMessage: (cb) => {
|
||||
socket.onmessage = async (event) => {
|
||||
try {
|
||||
await cb(String(event.data));
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Internal error occurred during message handling. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.close(CloseCode.InternalServerError, 'Internal server error');
|
||||
}
|
||||
};
|
||||
},
|
||||
}, { socket });
|
||||
};
|
||||
socket.onclose = (event) => {
|
||||
if (event.code === CloseCode.SubprotocolNotAcceptable &&
|
||||
socket.protocol === 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(event.code, event.reason);
|
||||
};
|
||||
};
|
||||
}
|
||||
40
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.d.mts
generated
vendored
Normal file
40
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.d.mts
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import type * as fastifyWebsocket from 'fastify-websocket';
|
||||
import { ServerOptions } from '../server.mjs';
|
||||
import { ConnectionInitMessage } from '../common.mjs';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @deprecated Use `@fastify/websocket` instead.
|
||||
*
|
||||
* @category Server/fastify-websocket
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The underlying socket connection between the server and the client.
|
||||
* The WebSocket socket is located under the `socket` parameter.
|
||||
*/
|
||||
readonly connection: fastifyWebsocket.SocketStream;
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*/
|
||||
readonly request: FastifyRequest;
|
||||
}
|
||||
/**
|
||||
* Make a handler to use on a [fastify-websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @deprecated Use `@fastify/websocket` instead.
|
||||
*
|
||||
* @category Server/fastify-websocket
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): fastifyWebsocket.WebsocketHandler;
|
||||
40
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.d.ts
generated
vendored
Normal file
40
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.d.ts
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { FastifyRequest } from 'fastify';
|
||||
import type * as fastifyWebsocket from 'fastify-websocket';
|
||||
import { ServerOptions } from '../server';
|
||||
import { ConnectionInitMessage } from '../common';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @deprecated Use `@fastify/websocket` instead.
|
||||
*
|
||||
* @category Server/fastify-websocket
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The underlying socket connection between the server and the client.
|
||||
* The WebSocket socket is located under the `socket` parameter.
|
||||
*/
|
||||
readonly connection: fastifyWebsocket.SocketStream;
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*/
|
||||
readonly request: FastifyRequest;
|
||||
}
|
||||
/**
|
||||
* Make a handler to use on a [fastify-websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @deprecated Use `@fastify/websocket` instead.
|
||||
*
|
||||
* @category Server/fastify-websocket
|
||||
*/
|
||||
export declare function makeHandler<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): fastifyWebsocket.WebsocketHandler;
|
||||
25
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.js
generated
vendored
Normal file
25
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.makeHandler = void 0;
|
||||
const websocket_1 = require("./@fastify/websocket");
|
||||
/**
|
||||
* Make a handler to use on a [fastify-websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @deprecated Use `@fastify/websocket` instead.
|
||||
*
|
||||
* @category Server/fastify-websocket
|
||||
*/
|
||||
function makeHandler(options,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
// new handler can be reused, the semantics stayed the same
|
||||
return (0, websocket_1.makeHandler)(options, keepAlive);
|
||||
}
|
||||
exports.makeHandler = makeHandler;
|
||||
21
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.mjs
generated
vendored
Normal file
21
graphql-subscription-express/node_modules/graphql-ws/lib/use/fastify-websocket.mjs
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
import { makeHandler as makeHandlerCurrent } from './@fastify/websocket.mjs';
|
||||
/**
|
||||
* Make a handler to use on a [fastify-websocket](https://github.com/fastify/fastify-websocket) route.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @deprecated Use `@fastify/websocket` instead.
|
||||
*
|
||||
* @category Server/fastify-websocket
|
||||
*/
|
||||
export function makeHandler(options,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
// new handler can be reused, the semantics stayed the same
|
||||
return makeHandlerCurrent(options, keepAlive);
|
||||
}
|
||||
67
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.d.mts
generated
vendored
Normal file
67
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.d.mts
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/// <reference types="node" />
|
||||
import type * as uWS from 'uWebSockets.js';
|
||||
import type http from 'http';
|
||||
import { ServerOptions } from '../server.mjs';
|
||||
import { ConnectionInitMessage } from '../common.mjs';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export interface Extra extends UpgradeData {
|
||||
/**
|
||||
* The actual socket connection between the server and the client
|
||||
* with the upgrade data.
|
||||
*/
|
||||
readonly socket: uWS.WebSocket<unknown> & UpgradeData;
|
||||
}
|
||||
/**
|
||||
* Data acquired during the HTTP upgrade callback from uWS.
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export interface UpgradeData {
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*
|
||||
* uWS's request is stack allocated and cannot be accessed
|
||||
* from outside of the internal upgrade; therefore, the persisted
|
||||
* request holds the relevant values extracted from the uWS's request
|
||||
* while it is accessible.
|
||||
*/
|
||||
readonly persistedRequest: PersistedRequest;
|
||||
}
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*
|
||||
* uWS's request is stack allocated and cannot be accessed
|
||||
* from outside of the internal upgrade; therefore, the persisted
|
||||
* request holds relevant values extracted from the uWS's request
|
||||
* while it is accessible.
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export interface PersistedRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
/** The raw query string (after the `?` sign) or empty string. */
|
||||
query: string;
|
||||
headers: http.IncomingHttpHeaders;
|
||||
}
|
||||
/**
|
||||
* Make the behaviour for using a [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) WebSocket server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export declare function makeBehavior<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>, behavior?: uWS.WebSocketBehavior<unknown>,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): uWS.WebSocketBehavior<unknown>;
|
||||
67
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.d.ts
generated
vendored
Normal file
67
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.d.ts
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
/// <reference types="node" />
|
||||
import type * as uWS from 'uWebSockets.js';
|
||||
import type http from 'http';
|
||||
import { ServerOptions } from '../server';
|
||||
import { ConnectionInitMessage } from '../common';
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export interface Extra extends UpgradeData {
|
||||
/**
|
||||
* The actual socket connection between the server and the client
|
||||
* with the upgrade data.
|
||||
*/
|
||||
readonly socket: uWS.WebSocket<unknown> & UpgradeData;
|
||||
}
|
||||
/**
|
||||
* Data acquired during the HTTP upgrade callback from uWS.
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export interface UpgradeData {
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*
|
||||
* uWS's request is stack allocated and cannot be accessed
|
||||
* from outside of the internal upgrade; therefore, the persisted
|
||||
* request holds the relevant values extracted from the uWS's request
|
||||
* while it is accessible.
|
||||
*/
|
||||
readonly persistedRequest: PersistedRequest;
|
||||
}
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*
|
||||
* uWS's request is stack allocated and cannot be accessed
|
||||
* from outside of the internal upgrade; therefore, the persisted
|
||||
* request holds relevant values extracted from the uWS's request
|
||||
* while it is accessible.
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export interface PersistedRequest {
|
||||
method: string;
|
||||
url: string;
|
||||
/** The raw query string (after the `?` sign) or empty string. */
|
||||
query: string;
|
||||
headers: http.IncomingHttpHeaders;
|
||||
}
|
||||
/**
|
||||
* Make the behaviour for using a [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) WebSocket server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export declare function makeBehavior<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>, behavior?: uWS.WebSocketBehavior<unknown>,
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): uWS.WebSocketBehavior<unknown>;
|
||||
141
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.js
generated
vendored
Normal file
141
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.js
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.makeBehavior = void 0;
|
||||
const server_1 = require("../server");
|
||||
const common_1 = require("../common");
|
||||
const utils_1 = require("../utils");
|
||||
/**
|
||||
* Make the behaviour for using a [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) WebSocket server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
function makeBehavior(options, behavior = {},
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const server = (0, server_1.makeServer)(options);
|
||||
const clients = new Map();
|
||||
let onDrain = () => {
|
||||
// gets called when backpressure drains
|
||||
};
|
||||
return Object.assign(Object.assign({}, behavior), { pong(...args) {
|
||||
var _a;
|
||||
(_a = behavior.pong) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [socket] = args;
|
||||
const client = clients.get(socket);
|
||||
if (!client)
|
||||
throw new Error('Pong received for a missing client');
|
||||
if (client.pongWaitTimeout) {
|
||||
clearTimeout(client.pongWaitTimeout);
|
||||
client.pongWaitTimeout = null;
|
||||
}
|
||||
},
|
||||
upgrade(...args) {
|
||||
var _a;
|
||||
(_a = behavior.upgrade) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [res, req, context] = args;
|
||||
const headers = {};
|
||||
req.forEach((key, value) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
res.upgrade({
|
||||
persistedRequest: {
|
||||
method: req.getMethod(),
|
||||
url: req.getUrl(),
|
||||
query: req.getQuery(),
|
||||
headers,
|
||||
},
|
||||
}, req.getHeader('sec-websocket-key'), (0, server_1.handleProtocols)(req.getHeader('sec-websocket-protocol')) ||
|
||||
new Uint8Array(), req.getHeader('sec-websocket-extensions'), context);
|
||||
},
|
||||
open(...args) {
|
||||
var _a;
|
||||
(_a = behavior.open) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const socket = args[0];
|
||||
const persistedRequest = socket.persistedRequest;
|
||||
// prepare client object
|
||||
const client = {
|
||||
pingInterval: null,
|
||||
pongWaitTimeout: null,
|
||||
handleMessage: () => {
|
||||
throw new Error('Message received before handler was registered');
|
||||
},
|
||||
closed: () => {
|
||||
throw new Error('Closed before handler was registered');
|
||||
},
|
||||
};
|
||||
client.closed = server.opened({
|
||||
protocol: (0, server_1.handleProtocols)(persistedRequest.headers['sec-websocket-protocol'] || '') || '',
|
||||
send: async (message) => {
|
||||
// the socket might have been destroyed in the meantime
|
||||
if (!clients.has(socket))
|
||||
return;
|
||||
if (!socket.send(message))
|
||||
// if backpressure is built up wait for drain
|
||||
await new Promise((resolve) => (onDrain = resolve));
|
||||
},
|
||||
close: (code, reason) => {
|
||||
// end socket in next tick making sure the client is registered
|
||||
setImmediate(() => {
|
||||
// the socket might have been destroyed before issuing a close
|
||||
if (clients.has(socket))
|
||||
socket.end(code, reason);
|
||||
});
|
||||
},
|
||||
onMessage: (cb) => (client.handleMessage = cb),
|
||||
}, { socket, persistedRequest });
|
||||
if (keepAlive > 0 && isFinite(keepAlive)) {
|
||||
client.pingInterval = setInterval(() => {
|
||||
// terminate the connection after pong wait has passed because the client is idle
|
||||
client.pongWaitTimeout = setTimeout(() => socket.close(), keepAlive);
|
||||
socket.ping();
|
||||
}, keepAlive);
|
||||
}
|
||||
clients.set(socket, client);
|
||||
},
|
||||
drain(...args) {
|
||||
var _a;
|
||||
(_a = behavior.drain) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
onDrain();
|
||||
},
|
||||
async message(...args) {
|
||||
var _a;
|
||||
(_a = behavior.message) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [socket, message] = args;
|
||||
const client = clients.get(socket);
|
||||
if (!client)
|
||||
throw new Error('Message received for a missing client');
|
||||
try {
|
||||
await client.handleMessage(Buffer.from(message).toString());
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Internal error occurred during message handling. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.end(common_1.CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: (0, utils_1.limitCloseReason)(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
},
|
||||
close(...args) {
|
||||
var _a;
|
||||
(_a = behavior.close) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [socket, code, message] = args;
|
||||
const client = clients.get(socket);
|
||||
if (!client)
|
||||
throw new Error('Closing a missing client');
|
||||
if (client.pongWaitTimeout)
|
||||
clearTimeout(client.pongWaitTimeout);
|
||||
if (client.pingInterval)
|
||||
clearTimeout(client.pingInterval);
|
||||
client.closed(code, Buffer.from(message).toString());
|
||||
clients.delete(socket);
|
||||
} });
|
||||
}
|
||||
exports.makeBehavior = makeBehavior;
|
||||
137
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.mjs
generated
vendored
Normal file
137
graphql-subscription-express/node_modules/graphql-ws/lib/use/uWebSockets.mjs
generated
vendored
Normal file
@@ -0,0 +1,137 @@
|
||||
import { handleProtocols, makeServer } from '../server.mjs';
|
||||
import { CloseCode } from '../common.mjs';
|
||||
import { limitCloseReason } from '../utils.mjs';
|
||||
/**
|
||||
* Make the behaviour for using a [uWebSockets.js](https://github.com/uNetworking/uWebSockets.js) WebSocket server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/uWebSockets
|
||||
*/
|
||||
export function makeBehavior(options, behavior = {},
|
||||
/**
|
||||
* The timout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const server = makeServer(options);
|
||||
const clients = new Map();
|
||||
let onDrain = () => {
|
||||
// gets called when backpressure drains
|
||||
};
|
||||
return Object.assign(Object.assign({}, behavior), { pong(...args) {
|
||||
var _a;
|
||||
(_a = behavior.pong) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [socket] = args;
|
||||
const client = clients.get(socket);
|
||||
if (!client)
|
||||
throw new Error('Pong received for a missing client');
|
||||
if (client.pongWaitTimeout) {
|
||||
clearTimeout(client.pongWaitTimeout);
|
||||
client.pongWaitTimeout = null;
|
||||
}
|
||||
},
|
||||
upgrade(...args) {
|
||||
var _a;
|
||||
(_a = behavior.upgrade) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [res, req, context] = args;
|
||||
const headers = {};
|
||||
req.forEach((key, value) => {
|
||||
headers[key] = value;
|
||||
});
|
||||
res.upgrade({
|
||||
persistedRequest: {
|
||||
method: req.getMethod(),
|
||||
url: req.getUrl(),
|
||||
query: req.getQuery(),
|
||||
headers,
|
||||
},
|
||||
}, req.getHeader('sec-websocket-key'), handleProtocols(req.getHeader('sec-websocket-protocol')) ||
|
||||
new Uint8Array(), req.getHeader('sec-websocket-extensions'), context);
|
||||
},
|
||||
open(...args) {
|
||||
var _a;
|
||||
(_a = behavior.open) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const socket = args[0];
|
||||
const persistedRequest = socket.persistedRequest;
|
||||
// prepare client object
|
||||
const client = {
|
||||
pingInterval: null,
|
||||
pongWaitTimeout: null,
|
||||
handleMessage: () => {
|
||||
throw new Error('Message received before handler was registered');
|
||||
},
|
||||
closed: () => {
|
||||
throw new Error('Closed before handler was registered');
|
||||
},
|
||||
};
|
||||
client.closed = server.opened({
|
||||
protocol: handleProtocols(persistedRequest.headers['sec-websocket-protocol'] || '') || '',
|
||||
send: async (message) => {
|
||||
// the socket might have been destroyed in the meantime
|
||||
if (!clients.has(socket))
|
||||
return;
|
||||
if (!socket.send(message))
|
||||
// if backpressure is built up wait for drain
|
||||
await new Promise((resolve) => (onDrain = resolve));
|
||||
},
|
||||
close: (code, reason) => {
|
||||
// end socket in next tick making sure the client is registered
|
||||
setImmediate(() => {
|
||||
// the socket might have been destroyed before issuing a close
|
||||
if (clients.has(socket))
|
||||
socket.end(code, reason);
|
||||
});
|
||||
},
|
||||
onMessage: (cb) => (client.handleMessage = cb),
|
||||
}, { socket, persistedRequest });
|
||||
if (keepAlive > 0 && isFinite(keepAlive)) {
|
||||
client.pingInterval = setInterval(() => {
|
||||
// terminate the connection after pong wait has passed because the client is idle
|
||||
client.pongWaitTimeout = setTimeout(() => socket.close(), keepAlive);
|
||||
socket.ping();
|
||||
}, keepAlive);
|
||||
}
|
||||
clients.set(socket, client);
|
||||
},
|
||||
drain(...args) {
|
||||
var _a;
|
||||
(_a = behavior.drain) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
onDrain();
|
||||
},
|
||||
async message(...args) {
|
||||
var _a;
|
||||
(_a = behavior.message) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [socket, message] = args;
|
||||
const client = clients.get(socket);
|
||||
if (!client)
|
||||
throw new Error('Message received for a missing client');
|
||||
try {
|
||||
await client.handleMessage(Buffer.from(message).toString());
|
||||
}
|
||||
catch (err) {
|
||||
console.error('Internal error occurred during message handling. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.end(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
},
|
||||
close(...args) {
|
||||
var _a;
|
||||
(_a = behavior.close) === null || _a === void 0 ? void 0 : _a.call(behavior, ...args);
|
||||
const [socket, code, message] = args;
|
||||
const client = clients.get(socket);
|
||||
if (!client)
|
||||
throw new Error('Closing a missing client');
|
||||
if (client.pongWaitTimeout)
|
||||
clearTimeout(client.pongWaitTimeout);
|
||||
if (client.pingInterval)
|
||||
clearTimeout(client.pingInterval);
|
||||
client.closed(code, Buffer.from(message).toString());
|
||||
clients.delete(socket);
|
||||
} });
|
||||
}
|
||||
38
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.d.mts
generated
vendored
Normal file
38
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.d.mts
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import type * as http from 'http';
|
||||
import type * as ws from 'ws';
|
||||
import { ServerOptions } from '../server.mjs';
|
||||
import { ConnectionInitMessage, Disposable } from '../common.mjs';
|
||||
type WebSocket = typeof ws.prototype;
|
||||
type WebSocketServer = ws.Server;
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/ws
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The actual socket connection between the server and the client.
|
||||
*/
|
||||
readonly socket: WebSocket;
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*/
|
||||
readonly request: http.IncomingMessage;
|
||||
}
|
||||
/**
|
||||
* Use the server on a [ws](https://github.com/websockets/ws) ws server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/ws
|
||||
*/
|
||||
export declare function useServer<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>, ws: WebSocketServer,
|
||||
/**
|
||||
* The timeout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): Disposable;
|
||||
export {};
|
||||
38
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.d.ts
generated
vendored
Normal file
38
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.d.ts
generated
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
import type * as http from 'http';
|
||||
import type * as ws from 'ws';
|
||||
import { ServerOptions } from '../server';
|
||||
import { ConnectionInitMessage, Disposable } from '../common';
|
||||
type WebSocket = typeof ws.prototype;
|
||||
type WebSocketServer = ws.Server;
|
||||
/**
|
||||
* The extra that will be put in the `Context`.
|
||||
*
|
||||
* @category Server/ws
|
||||
*/
|
||||
export interface Extra {
|
||||
/**
|
||||
* The actual socket connection between the server and the client.
|
||||
*/
|
||||
readonly socket: WebSocket;
|
||||
/**
|
||||
* The initial HTTP upgrade request before the actual
|
||||
* socket and connection is established.
|
||||
*/
|
||||
readonly request: http.IncomingMessage;
|
||||
}
|
||||
/**
|
||||
* Use the server on a [ws](https://github.com/websockets/ws) ws server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/ws
|
||||
*/
|
||||
export declare function useServer<P extends ConnectionInitMessage['payload'] = ConnectionInitMessage['payload'], E extends Record<PropertyKey, unknown> = Record<PropertyKey, never>>(options: ServerOptions<P, Extra & Partial<E>>, ws: WebSocketServer,
|
||||
/**
|
||||
* The timeout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive?: number): Disposable;
|
||||
export {};
|
||||
119
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.js
generated
vendored
Normal file
119
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.js
generated
vendored
Normal file
@@ -0,0 +1,119 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.useServer = void 0;
|
||||
const server_1 = require("../server");
|
||||
const common_1 = require("../common");
|
||||
const utils_1 = require("../utils");
|
||||
/**
|
||||
* Use the server on a [ws](https://github.com/websockets/ws) ws server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/ws
|
||||
*/
|
||||
function useServer(options, ws,
|
||||
/**
|
||||
* The timeout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const server = (0, server_1.makeServer)(options);
|
||||
ws.options.handleProtocols = server_1.handleProtocols;
|
||||
ws.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 ws.clients) {
|
||||
try {
|
||||
client.close(common_1.CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: (0, utils_1.limitCloseReason)(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
catch (err) {
|
||||
firstErr = firstErr !== null && firstErr !== void 0 ? firstErr : err;
|
||||
}
|
||||
}
|
||||
if (firstErr)
|
||||
throw firstErr;
|
||||
});
|
||||
ws.on('connection', (socket, request) => {
|
||||
socket.once('error', (err) => {
|
||||
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 instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
});
|
||||
// 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 instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
}),
|
||||
}, { socket, 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));
|
||||
});
|
||||
});
|
||||
return {
|
||||
dispose: async () => {
|
||||
for (const client of ws.clients) {
|
||||
client.close(1001, 'Going away');
|
||||
}
|
||||
ws.removeAllListeners();
|
||||
await new Promise((resolve, reject) => {
|
||||
ws.close((err) => (err ? reject(err) : resolve()));
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
exports.useServer = useServer;
|
||||
115
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.mjs
generated
vendored
Normal file
115
graphql-subscription-express/node_modules/graphql-ws/lib/use/ws.mjs
generated
vendored
Normal file
@@ -0,0 +1,115 @@
|
||||
import { handleProtocols, makeServer } from '../server.mjs';
|
||||
import { DEPRECATED_GRAPHQL_WS_PROTOCOL, CloseCode, } from '../common.mjs';
|
||||
import { limitCloseReason } from '../utils.mjs';
|
||||
/**
|
||||
* Use the server on a [ws](https://github.com/websockets/ws) ws server.
|
||||
* This is a basic starter, feel free to copy the code over and adjust it to your needs
|
||||
*
|
||||
* @category Server/ws
|
||||
*/
|
||||
export function useServer(options, ws,
|
||||
/**
|
||||
* The timeout between dispatched keep-alive messages. Internally uses the [ws Ping and Pongs]((https://developer.mozilla.org/en-US/docs/Web/API/wss_API/Writing_ws_servers#Pings_and_Pongs_The_Heartbeat_of_wss))
|
||||
* to check that the link between the clients and the server is operating and to prevent the link
|
||||
* from being broken due to idling.
|
||||
*
|
||||
* @default 12_000 // 12 seconds
|
||||
*/
|
||||
keepAlive = 12000) {
|
||||
const isProd = process.env.NODE_ENV === 'production';
|
||||
const server = makeServer(options);
|
||||
ws.options.handleProtocols = handleProtocols;
|
||||
ws.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 ws.clients) {
|
||||
try {
|
||||
client.close(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
catch (err) {
|
||||
firstErr = firstErr !== null && firstErr !== void 0 ? firstErr : err;
|
||||
}
|
||||
}
|
||||
if (firstErr)
|
||||
throw firstErr;
|
||||
});
|
||||
ws.on('connection', (socket, request) => {
|
||||
socket.once('error', (err) => {
|
||||
console.error('Internal error emitted on a WebSocket socket. ' +
|
||||
'Please check your implementation.', err);
|
||||
socket.close(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
});
|
||||
// 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(CloseCode.InternalServerError, isProd
|
||||
? 'Internal server error'
|
||||
: limitCloseReason(err instanceof Error ? err.message : String(err), 'Internal server error'));
|
||||
}
|
||||
}),
|
||||
}, { socket, request });
|
||||
socket.once('close', (code, reason) => {
|
||||
if (pongWait)
|
||||
clearTimeout(pongWait);
|
||||
if (pingInterval)
|
||||
clearInterval(pingInterval);
|
||||
if (!isProd &&
|
||||
code === CloseCode.SubprotocolNotAcceptable &&
|
||||
socket.protocol === 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));
|
||||
});
|
||||
});
|
||||
return {
|
||||
dispose: async () => {
|
||||
for (const client of ws.clients) {
|
||||
client.close(1001, 'Going away');
|
||||
}
|
||||
ws.removeAllListeners();
|
||||
await new Promise((resolve, reject) => {
|
||||
ws.close((err) => (err ? reject(err) : resolve()));
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
23
graphql-subscription-express/node_modules/graphql-ws/lib/utils.d.mts
generated
vendored
Normal file
23
graphql-subscription-express/node_modules/graphql-ws/lib/utils.d.mts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
*
|
||||
* utils
|
||||
*
|
||||
*/
|
||||
import { GraphQLError } from 'graphql';
|
||||
/** @private */
|
||||
export declare function extendedTypeof(val: unknown): 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'array' | 'null';
|
||||
/** @private */
|
||||
export declare function isObject(val: unknown): val is Record<PropertyKey, unknown>;
|
||||
/** @private */
|
||||
export declare function isAsyncIterable<T = unknown>(val: unknown): val is AsyncIterable<T>;
|
||||
/** @private */
|
||||
export declare function isAsyncGenerator<T = unknown>(val: unknown): val is AsyncGenerator<T>;
|
||||
/** @private */
|
||||
export declare function areGraphQLErrors(obj: unknown): obj is readonly GraphQLError[];
|
||||
/**
|
||||
* Limits the WebSocket close event reason to not exceed a length of one frame.
|
||||
* Reference: https://datatracker.ietf.org/doc/html/rfc6455#section-5.2.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export declare function limitCloseReason(reason: string, whenTooLong: string): string;
|
||||
23
graphql-subscription-express/node_modules/graphql-ws/lib/utils.d.ts
generated
vendored
Normal file
23
graphql-subscription-express/node_modules/graphql-ws/lib/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
*
|
||||
* utils
|
||||
*
|
||||
*/
|
||||
import { GraphQLError } from 'graphql';
|
||||
/** @private */
|
||||
export declare function extendedTypeof(val: unknown): 'string' | 'number' | 'bigint' | 'boolean' | 'symbol' | 'undefined' | 'object' | 'function' | 'array' | 'null';
|
||||
/** @private */
|
||||
export declare function isObject(val: unknown): val is Record<PropertyKey, unknown>;
|
||||
/** @private */
|
||||
export declare function isAsyncIterable<T = unknown>(val: unknown): val is AsyncIterable<T>;
|
||||
/** @private */
|
||||
export declare function isAsyncGenerator<T = unknown>(val: unknown): val is AsyncGenerator<T>;
|
||||
/** @private */
|
||||
export declare function areGraphQLErrors(obj: unknown): obj is readonly GraphQLError[];
|
||||
/**
|
||||
* Limits the WebSocket close event reason to not exceed a length of one frame.
|
||||
* Reference: https://datatracker.ietf.org/doc/html/rfc6455#section-5.2.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export declare function limitCloseReason(reason: string, whenTooLong: string): string;
|
||||
54
graphql-subscription-express/node_modules/graphql-ws/lib/utils.js
generated
vendored
Normal file
54
graphql-subscription-express/node_modules/graphql-ws/lib/utils.js
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.limitCloseReason = exports.areGraphQLErrors = exports.isAsyncGenerator = exports.isAsyncIterable = exports.isObject = exports.extendedTypeof = void 0;
|
||||
/** @private */
|
||||
function extendedTypeof(val) {
|
||||
if (val === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
return 'array';
|
||||
}
|
||||
return typeof val;
|
||||
}
|
||||
exports.extendedTypeof = extendedTypeof;
|
||||
/** @private */
|
||||
function isObject(val) {
|
||||
return extendedTypeof(val) === 'object';
|
||||
}
|
||||
exports.isObject = isObject;
|
||||
/** @private */
|
||||
function isAsyncIterable(val) {
|
||||
return typeof Object(val)[Symbol.asyncIterator] === 'function';
|
||||
}
|
||||
exports.isAsyncIterable = isAsyncIterable;
|
||||
/** @private */
|
||||
function isAsyncGenerator(val) {
|
||||
return (isObject(val) &&
|
||||
typeof Object(val)[Symbol.asyncIterator] === 'function' &&
|
||||
typeof val.return === 'function'
|
||||
// for lazy ones, we only need the return anyway
|
||||
// typeof val.throw === 'function' &&
|
||||
// typeof val.next === 'function'
|
||||
);
|
||||
}
|
||||
exports.isAsyncGenerator = isAsyncGenerator;
|
||||
/** @private */
|
||||
function areGraphQLErrors(obj) {
|
||||
return (Array.isArray(obj) &&
|
||||
// must be at least one error
|
||||
obj.length > 0 &&
|
||||
// error has at least a message
|
||||
obj.every((ob) => 'message' in ob));
|
||||
}
|
||||
exports.areGraphQLErrors = areGraphQLErrors;
|
||||
/**
|
||||
* Limits the WebSocket close event reason to not exceed a length of one frame.
|
||||
* Reference: https://datatracker.ietf.org/doc/html/rfc6455#section-5.2.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function limitCloseReason(reason, whenTooLong) {
|
||||
return reason.length < 124 ? reason : whenTooLong;
|
||||
}
|
||||
exports.limitCloseReason = limitCloseReason;
|
||||
45
graphql-subscription-express/node_modules/graphql-ws/lib/utils.mjs
generated
vendored
Normal file
45
graphql-subscription-express/node_modules/graphql-ws/lib/utils.mjs
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
/** @private */
|
||||
export function extendedTypeof(val) {
|
||||
if (val === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
return 'array';
|
||||
}
|
||||
return typeof val;
|
||||
}
|
||||
/** @private */
|
||||
export function isObject(val) {
|
||||
return extendedTypeof(val) === 'object';
|
||||
}
|
||||
/** @private */
|
||||
export function isAsyncIterable(val) {
|
||||
return typeof Object(val)[Symbol.asyncIterator] === 'function';
|
||||
}
|
||||
/** @private */
|
||||
export function isAsyncGenerator(val) {
|
||||
return (isObject(val) &&
|
||||
typeof Object(val)[Symbol.asyncIterator] === 'function' &&
|
||||
typeof val.return === 'function'
|
||||
// for lazy ones, we only need the return anyway
|
||||
// typeof val.throw === 'function' &&
|
||||
// typeof val.next === 'function'
|
||||
);
|
||||
}
|
||||
/** @private */
|
||||
export function areGraphQLErrors(obj) {
|
||||
return (Array.isArray(obj) &&
|
||||
// must be at least one error
|
||||
obj.length > 0 &&
|
||||
// error has at least a message
|
||||
obj.every((ob) => 'message' in ob));
|
||||
}
|
||||
/**
|
||||
* Limits the WebSocket close event reason to not exceed a length of one frame.
|
||||
* Reference: https://datatracker.ietf.org/doc/html/rfc6455#section-5.2.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
export function limitCloseReason(reason, whenTooLong) {
|
||||
return reason.length < 124 ? reason : whenTooLong;
|
||||
}
|
||||
152
graphql-subscription-express/node_modules/graphql-ws/package.json
generated
vendored
Normal file
152
graphql-subscription-express/node_modules/graphql-ws/package.json
generated
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
{
|
||||
"name": "graphql-ws",
|
||||
"version": "5.16.0",
|
||||
"description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client",
|
||||
"keywords": [
|
||||
"protocol",
|
||||
"graphql",
|
||||
"transport",
|
||||
"subscriptions",
|
||||
"websockets",
|
||||
"server",
|
||||
"client",
|
||||
"observables",
|
||||
"express",
|
||||
"relay",
|
||||
"apollo",
|
||||
"fastify",
|
||||
"uwebsockets"
|
||||
],
|
||||
"author": "Denis Badurina <badurinadenis@gmail.com>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/enisdenjo/graphql-ws#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/enisdenjo/graphql-ws.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"packageManager": "yarn@3.6.0",
|
||||
"types": "lib/index.d.ts",
|
||||
"main": "lib/index.js",
|
||||
"module": "lib/index.mjs",
|
||||
"browser": "umd/graphql-ws.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/index.d.ts",
|
||||
"require": "./lib/index.js",
|
||||
"import": "./lib/index.mjs",
|
||||
"browser": "./umd/graphql-ws.js"
|
||||
},
|
||||
"./lib/use/ws": {
|
||||
"types": "./lib/use/ws.d.ts",
|
||||
"require": "./lib/use/ws.js",
|
||||
"import": "./lib/use/ws.mjs"
|
||||
},
|
||||
"./lib/use/uWebSockets": {
|
||||
"types": "./lib/use/uWebSockets.d.ts",
|
||||
"require": "./lib/use/uWebSockets.js",
|
||||
"import": "./lib/use/uWebSockets.mjs"
|
||||
},
|
||||
"./lib/use/@fastify/websocket": {
|
||||
"types": "./lib/use/@fastify/websocket.d.ts",
|
||||
"require": "./lib/use/@fastify/websocket.js",
|
||||
"import": "./lib/use/@fastify/websocket.mjs"
|
||||
},
|
||||
"./lib/use/fastify-websocket": {
|
||||
"types": "./lib/use/fastify-websocket.d.ts",
|
||||
"require": "./lib/use/fastify-websocket.js",
|
||||
"import": "./lib/use/fastify-websocket.mjs"
|
||||
},
|
||||
"./lib/use/bun": {
|
||||
"bun": "./lib/use/bun.mjs",
|
||||
"types": "./lib/use/bun.d.ts",
|
||||
"require": "./lib/use/bun.js",
|
||||
"import": "./lib/use/bun.mjs"
|
||||
},
|
||||
"./lib/use/deno": {
|
||||
"types": "./lib/use/deno.d.ts",
|
||||
"require": "./lib/use/deno.js",
|
||||
"import": "./lib/use/deno.mjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"lib",
|
||||
"umd",
|
||||
"README.md",
|
||||
"LICENSE.md",
|
||||
"PROTOCOL.md"
|
||||
],
|
||||
"sideEffects": [
|
||||
"umd/*"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"scripts": {
|
||||
"gendocs": "typedoc --options typedoc.js src/ && node scripts/post-gendocs.mjs",
|
||||
"lint:eslint": "eslint 'src'",
|
||||
"lint:prettier": "prettier -c .",
|
||||
"lint": "yarn lint:eslint && yarn lint:prettier",
|
||||
"type-check": "tsc --noEmit",
|
||||
"test": "jest",
|
||||
"bench:start-servers": "NODE_ENV=production node benchmark/servers/index.mjs",
|
||||
"bench": "k6 run benchmark/k6.mjs",
|
||||
"build:esm": "tsc -b tsconfig.esm.json && node scripts/esm-post-process.mjs",
|
||||
"build:cjs": "tsc -b tsconfig.cjs.json",
|
||||
"build:umd": "rollup --bundleConfigAsCjs --config rollup.config.ts --configPlugin typescript && gzip umd/graphql-ws.min.js -c > umd/graphql-ws.min.js.gz",
|
||||
"build": "yarn build:esm && yarn build:cjs && yarn build:umd && yarn postbuild",
|
||||
"postbuild": "node scripts/fix-declaration-directives.mjs",
|
||||
"prepack": "npm pkg delete workspaces",
|
||||
"postpack": "npm pkg set 'workspaces[]=website'",
|
||||
"release": "semantic-release"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"graphql": ">=0.11 <=16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.22.5",
|
||||
"@babel/plugin-proposal-class-properties": "^7.18.6",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.20.7",
|
||||
"@babel/plugin-proposal-optional-chaining": "^7.21.0",
|
||||
"@babel/preset-env": "^7.22.5",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@fastify/websocket": "^7.2.0",
|
||||
"@rollup/plugin-terser": "^0.4.3",
|
||||
"@rollup/plugin-typescript": "^11.1.1",
|
||||
"@semantic-release/changelog": "^6.0.3",
|
||||
"@semantic-release/git": "^10.0.1",
|
||||
"@types/eslint": "^8.40.2",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/jest": "^29.5.2",
|
||||
"@types/ws": "^8.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.60.0",
|
||||
"@typescript-eslint/parser": "^5.60.0",
|
||||
"babel-jest": "^29.5.0",
|
||||
"bun-types": "^0.6.9",
|
||||
"eslint": "^8.43.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"fastify": "^4.18.0",
|
||||
"fastify-websocket": "4.2.2",
|
||||
"glob": "^10.3.0",
|
||||
"graphql": "^16.7.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest-environment-jsdom": "^29.5.0",
|
||||
"jest-jasmine2": "^29.5.0",
|
||||
"prettier": "^2.8.8",
|
||||
"replacestream": "^4.0.3",
|
||||
"rollup": "^3.25.1",
|
||||
"semantic-release": "^21.0.5",
|
||||
"subscriptions-transport-ws": "^0.11.0",
|
||||
"tslib": "^2.5.3",
|
||||
"typedoc": "^0.24.8",
|
||||
"typedoc-plugin-markdown": "^3.15.3",
|
||||
"typescript": "^5.1.3",
|
||||
"uWebSockets.js": "uNetworking/uWebSockets.js#v20.30.0",
|
||||
"ws": "8.12.0",
|
||||
"ws7": "npm:ws@^7.5.9"
|
||||
}
|
||||
}
|
||||
800
graphql-subscription-express/node_modules/graphql-ws/umd/graphql-ws.js
generated
vendored
Normal file
800
graphql-subscription-express/node_modules/graphql-ws/umd/graphql-ws.js
generated
vendored
Normal file
@@ -0,0 +1,800 @@
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.graphqlWs = {}));
|
||||
})(this, (function (exports) { 'use strict';
|
||||
|
||||
/******************************************************************************
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||
PERFORMANCE OF THIS SOFTWARE.
|
||||
***************************************************************************** */
|
||||
/* global Reflect, Promise */
|
||||
|
||||
|
||||
function __await(v) {
|
||||
return this instanceof __await ? (this.v = v, this) : new __await(v);
|
||||
}
|
||||
|
||||
function __asyncGenerator(thisArg, _arguments, generator) {
|
||||
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
||||
var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
||||
return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
||||
function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
||||
function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
||||
function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
||||
function fulfill(value) { resume("next", value); }
|
||||
function reject(value) { resume("throw", value); }
|
||||
function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
||||
}
|
||||
|
||||
/** @private */
|
||||
function extendedTypeof(val) {
|
||||
if (val === null) {
|
||||
return 'null';
|
||||
}
|
||||
if (Array.isArray(val)) {
|
||||
return 'array';
|
||||
}
|
||||
return typeof val;
|
||||
}
|
||||
/** @private */
|
||||
function isObject(val) {
|
||||
return extendedTypeof(val) === 'object';
|
||||
}
|
||||
/** @private */
|
||||
function areGraphQLErrors(obj) {
|
||||
return (Array.isArray(obj) &&
|
||||
// must be at least one error
|
||||
obj.length > 0 &&
|
||||
// error has at least a message
|
||||
obj.every((ob) => 'message' in ob));
|
||||
}
|
||||
/**
|
||||
* Limits the WebSocket close event reason to not exceed a length of one frame.
|
||||
* Reference: https://datatracker.ietf.org/doc/html/rfc6455#section-5.2.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function limitCloseReason(reason, whenTooLong) {
|
||||
return reason.length < 124 ? reason : whenTooLong;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* common
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* The WebSocket sub-protocol used for the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
const GRAPHQL_TRANSPORT_WS_PROTOCOL = 'graphql-transport-ws';
|
||||
/**
|
||||
* The deprecated subprotocol used by [subscriptions-transport-ws](https://github.com/apollographql/subscriptions-transport-ws).
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
const DEPRECATED_GRAPHQL_WS_PROTOCOL = 'graphql-ws';
|
||||
/**
|
||||
* `graphql-ws` expected and standard close codes of the [GraphQL over WebSocket Protocol](https://github.com/graphql/graphql-over-http/blob/main/rfcs/GraphQLOverWebSocket.md).
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
exports.CloseCode = void 0;
|
||||
(function (CloseCode) {
|
||||
CloseCode[CloseCode["InternalServerError"] = 4500] = "InternalServerError";
|
||||
CloseCode[CloseCode["InternalClientError"] = 4005] = "InternalClientError";
|
||||
CloseCode[CloseCode["BadRequest"] = 4400] = "BadRequest";
|
||||
CloseCode[CloseCode["BadResponse"] = 4004] = "BadResponse";
|
||||
/** Tried subscribing before connect ack */
|
||||
CloseCode[CloseCode["Unauthorized"] = 4401] = "Unauthorized";
|
||||
CloseCode[CloseCode["Forbidden"] = 4403] = "Forbidden";
|
||||
CloseCode[CloseCode["SubprotocolNotAcceptable"] = 4406] = "SubprotocolNotAcceptable";
|
||||
CloseCode[CloseCode["ConnectionInitialisationTimeout"] = 4408] = "ConnectionInitialisationTimeout";
|
||||
CloseCode[CloseCode["ConnectionAcknowledgementTimeout"] = 4504] = "ConnectionAcknowledgementTimeout";
|
||||
/** Subscriber distinction is very important */
|
||||
CloseCode[CloseCode["SubscriberAlreadyExists"] = 4409] = "SubscriberAlreadyExists";
|
||||
CloseCode[CloseCode["TooManyInitialisationRequests"] = 4429] = "TooManyInitialisationRequests";
|
||||
})(exports.CloseCode || (exports.CloseCode = {}));
|
||||
/**
|
||||
* Types of messages allowed to be sent by the client/server over the WS protocol.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
exports.MessageType = void 0;
|
||||
(function (MessageType) {
|
||||
MessageType["ConnectionInit"] = "connection_init";
|
||||
MessageType["ConnectionAck"] = "connection_ack";
|
||||
MessageType["Ping"] = "ping";
|
||||
MessageType["Pong"] = "pong";
|
||||
MessageType["Subscribe"] = "subscribe";
|
||||
MessageType["Next"] = "next";
|
||||
MessageType["Error"] = "error";
|
||||
MessageType["Complete"] = "complete";
|
||||
})(exports.MessageType || (exports.MessageType = {}));
|
||||
/**
|
||||
* Validates the message against the GraphQL over WebSocket Protocol.
|
||||
*
|
||||
* Invalid messages will throw descriptive errors.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
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 exports.MessageType.ConnectionInit:
|
||||
case exports.MessageType.ConnectionAck:
|
||||
case exports.MessageType.Ping:
|
||||
case exports.MessageType.Pong: {
|
||||
if (val.payload != null && !isObject(val.payload)) {
|
||||
throw new Error(`"${val.type}" message expects the 'payload' property to be an object or nullish or missing, but got "${val.payload}"`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case exports.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 exports.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 exports.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 exports.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;
|
||||
}
|
||||
/**
|
||||
* Checks if the provided value is a valid GraphQL over WebSocket message.
|
||||
*
|
||||
* @deprecated Use `validateMessage` instead.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function isMessage(val) {
|
||||
try {
|
||||
validateMessage(val);
|
||||
return true;
|
||||
}
|
||||
catch (_a) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parses the raw websocket message data to a valid message.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function parseMessage(data, reviver) {
|
||||
return validateMessage(typeof data === 'string' ? JSON.parse(data, reviver) : data);
|
||||
}
|
||||
/**
|
||||
* Stringifies a valid message ready to be sent through the socket.
|
||||
*
|
||||
* @category Common
|
||||
*/
|
||||
function stringifyMessage(msg, replacer) {
|
||||
validateMessage(msg);
|
||||
return JSON.stringify(msg, replacer);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* client
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* Creates a disposable GraphQL over WebSocket client.
|
||||
*
|
||||
* @category Client
|
||||
*/
|
||||
function createClient(options) {
|
||||
const { url, connectionParams, lazy = true, onNonLazyError = console.error, lazyCloseTimeout: lazyCloseTimeoutMs = 0, keepAlive = 0, disablePong, connectionAckWaitTimeout = 0, retryAttempts = 5, retryWait = async function randomisedExponentialBackoff(retries) {
|
||||
let retryDelay = 1000; // start with 1s delay
|
||||
for (let i = 0; i < retries; i++) {
|
||||
retryDelay *= 2;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, retryDelay +
|
||||
// add random timeout from 300ms to 3s
|
||||
Math.floor(Math.random() * (3000 - 300) + 300)));
|
||||
}, shouldRetry = isLikeCloseEvent, isFatalConnectionProblem, on, webSocketImpl,
|
||||
/**
|
||||
* Generates a v4 UUID to be used as the ID using `Math`
|
||||
* as the random number generator. Supply your own generator
|
||||
* in case you need more uniqueness.
|
||||
*
|
||||
* Reference: https://gist.github.com/jed/982883
|
||||
*/
|
||||
generateID = function generateUUID() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
||||
const r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
|
||||
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 ||
|
||||
// @ts-expect-error: Support more browsers
|
||||
global.MozWebSocket;
|
||||
}
|
||||
else if (typeof window !== 'undefined') {
|
||||
ws =
|
||||
window.WebSocket ||
|
||||
// @ts-expect-error: Support more browsers
|
||||
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;
|
||||
// websocket status emitter, subscriptions are handled differently
|
||||
const emitter = (() => {
|
||||
const message = (() => {
|
||||
const listeners = {};
|
||||
return {
|
||||
on(id, listener) {
|
||||
listeners[id] = listener;
|
||||
return () => {
|
||||
delete listeners[id];
|
||||
};
|
||||
},
|
||||
emit(message) {
|
||||
var _a;
|
||||
if ('id' in message)
|
||||
(_a = listeners[message.id]) === null || _a === void 0 ? void 0 : _a.call(listeners, message);
|
||||
},
|
||||
};
|
||||
})();
|
||||
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) {
|
||||
// we copy the listeners so that unlistens dont "pull the rug under our feet"
|
||||
for (const listener of [...listeners[event]]) {
|
||||
// @ts-expect-error: The args should fit
|
||||
listener(...args);
|
||||
}
|
||||
},
|
||||
};
|
||||
})();
|
||||
// invokes the callback either when an error or closed event is emitted,
|
||||
// first one that gets called prevails, other emissions are ignored
|
||||
function errorOrClosed(cb) {
|
||||
const listening = [
|
||||
// errors are fatal and more critical than close events, throw them first
|
||||
emitter.on('error', (err) => {
|
||||
listening.forEach((unlisten) => unlisten());
|
||||
cb(err);
|
||||
}),
|
||||
// closes can be graceful and not fatal, throw them second (if error didnt throw)
|
||||
emitter.on('closed', (event) => {
|
||||
listening.forEach((unlisten) => unlisten());
|
||||
cb(event);
|
||||
}),
|
||||
];
|
||||
}
|
||||
let connecting, locks = 0, lazyCloseTimeout, retrying = false, retries = 0, disposed = false;
|
||||
async function connect() {
|
||||
// clear the lazy close timeout immediatelly so that close gets debounced
|
||||
// see: https://github.com/enisdenjo/graphql-ws/issues/388
|
||||
clearTimeout(lazyCloseTimeout);
|
||||
const [socket, throwOnClose] = await (connecting !== null && connecting !== void 0 ? connecting : (connecting = new Promise((connected, denied) => (async () => {
|
||||
if (retrying) {
|
||||
await retryWait(retries);
|
||||
// subscriptions might complete while waiting for retry
|
||||
if (!locks) {
|
||||
connecting = undefined;
|
||||
return denied({ code: 1000, reason: 'All Subscriptions Gone' });
|
||||
}
|
||||
retries++;
|
||||
}
|
||||
emitter.emit('connecting', retrying);
|
||||
const socket = new WebSocketImpl(typeof url === 'function' ? await url() : url, GRAPHQL_TRANSPORT_WS_PROTOCOL);
|
||||
let connectionAckTimeout, queuedPing;
|
||||
function enqueuePing() {
|
||||
if (isFinite(keepAlive) && keepAlive > 0) {
|
||||
clearTimeout(queuedPing); // in case where a pong was received before a ping (this is valid behaviour)
|
||||
queuedPing = setTimeout(() => {
|
||||
if (socket.readyState === WebSocketImpl.OPEN) {
|
||||
socket.send(stringifyMessage({ type: exports.MessageType.Ping }));
|
||||
emitter.emit('ping', false, undefined);
|
||||
}
|
||||
}, keepAlive);
|
||||
}
|
||||
}
|
||||
errorOrClosed((errOrEvent) => {
|
||||
connecting = undefined;
|
||||
clearTimeout(connectionAckTimeout);
|
||||
clearTimeout(queuedPing);
|
||||
denied(errOrEvent);
|
||||
if (errOrEvent instanceof TerminatedCloseEvent) {
|
||||
socket.close(4499, 'Terminated'); // close event is artificial and emitted manually, see `Client.terminate()` below
|
||||
socket.onerror = null;
|
||||
socket.onclose = null;
|
||||
}
|
||||
});
|
||||
socket.onerror = (err) => emitter.emit('error', err);
|
||||
socket.onclose = (event) => emitter.emit('closed', event);
|
||||
socket.onopen = async () => {
|
||||
try {
|
||||
emitter.emit('opened', socket);
|
||||
const payload = typeof connectionParams === 'function'
|
||||
? await connectionParams()
|
||||
: connectionParams;
|
||||
// connectionParams might take too long causing the server to kick off the client
|
||||
// the necessary error/close event is already reported - simply stop execution
|
||||
if (socket.readyState !== WebSocketImpl.OPEN)
|
||||
return;
|
||||
socket.send(stringifyMessage(payload
|
||||
? {
|
||||
type: exports.MessageType.ConnectionInit,
|
||||
payload,
|
||||
}
|
||||
: {
|
||||
type: exports.MessageType.ConnectionInit,
|
||||
// payload is completely absent if not provided
|
||||
}, replacer));
|
||||
if (isFinite(connectionAckWaitTimeout) &&
|
||||
connectionAckWaitTimeout > 0) {
|
||||
connectionAckTimeout = setTimeout(() => {
|
||||
socket.close(exports.CloseCode.ConnectionAcknowledgementTimeout, 'Connection acknowledgement timeout');
|
||||
}, connectionAckWaitTimeout);
|
||||
}
|
||||
enqueuePing(); // enqueue ping (noop if disabled)
|
||||
}
|
||||
catch (err) {
|
||||
emitter.emit('error', err);
|
||||
socket.close(exports.CloseCode.InternalClientError, limitCloseReason(err instanceof Error ? err.message : new Error(err).message, 'Internal client error'));
|
||||
}
|
||||
};
|
||||
let acknowledged = false;
|
||||
socket.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); // received
|
||||
if (message.type === 'pong') {
|
||||
enqueuePing(); // enqueue next ping (noop if disabled)
|
||||
}
|
||||
else if (!disablePong) {
|
||||
// respond with pong on ping
|
||||
socket.send(stringifyMessage(message.payload
|
||||
? {
|
||||
type: exports.MessageType.Pong,
|
||||
payload: message.payload,
|
||||
}
|
||||
: {
|
||||
type: exports.MessageType.Pong,
|
||||
// payload is completely absent if not provided
|
||||
}));
|
||||
emitter.emit('pong', false, message.payload);
|
||||
}
|
||||
return; // ping and pongs can be received whenever
|
||||
}
|
||||
if (acknowledged)
|
||||
return; // already connected and acknowledged
|
||||
if (message.type !== exports.MessageType.ConnectionAck)
|
||||
throw new Error(`First message cannot be of type ${message.type}`);
|
||||
clearTimeout(connectionAckTimeout);
|
||||
acknowledged = true;
|
||||
emitter.emit('connected', socket, message.payload, retrying); // connected = socket opened + acknowledged
|
||||
retrying = false; // future lazy connects are not retries
|
||||
retries = 0; // reset the retries on connect
|
||||
connected([
|
||||
socket,
|
||||
new Promise((_, reject) => errorOrClosed(reject)),
|
||||
]);
|
||||
}
|
||||
catch (err) {
|
||||
socket.onmessage = null; // stop reading messages as soon as reading breaks once
|
||||
emitter.emit('error', err);
|
||||
socket.close(exports.CloseCode.BadResponse, limitCloseReason(err instanceof Error ? err.message : new Error(err).message, 'Bad response'));
|
||||
}
|
||||
};
|
||||
})())));
|
||||
// if the provided socket is in a closing state, wait for the throw on close
|
||||
if (socket.readyState === WebSocketImpl.CLOSING)
|
||||
await throwOnClose;
|
||||
let release = () => {
|
||||
// releases this connection
|
||||
};
|
||||
const released = new Promise((resolve) => (release = resolve));
|
||||
return [
|
||||
socket,
|
||||
release,
|
||||
Promise.race([
|
||||
// wait for
|
||||
released.then(() => {
|
||||
if (!locks) {
|
||||
// and if no more locks are present, complete the connection
|
||||
const complete = () => socket.close(1000, 'Normal Closure');
|
||||
if (isFinite(lazyCloseTimeoutMs) && lazyCloseTimeoutMs > 0) {
|
||||
// if the keepalive is set, allow for the specified calmdown time and
|
||||
// then complete if the socket is still open.
|
||||
lazyCloseTimeout = setTimeout(() => {
|
||||
if (socket.readyState === WebSocketImpl.OPEN)
|
||||
complete();
|
||||
}, lazyCloseTimeoutMs);
|
||||
}
|
||||
else {
|
||||
// otherwise complete immediately
|
||||
complete();
|
||||
}
|
||||
}
|
||||
}),
|
||||
// or
|
||||
throwOnClose,
|
||||
]),
|
||||
];
|
||||
}
|
||||
/**
|
||||
* Checks the `connect` problem and evaluates if the client should retry.
|
||||
*/
|
||||
function shouldRetryConnectOrThrow(errOrCloseEvent) {
|
||||
// some close codes are worth reporting immediately
|
||||
if (isLikeCloseEvent(errOrCloseEvent) &&
|
||||
(isFatalInternalCloseCode(errOrCloseEvent.code) ||
|
||||
[
|
||||
exports.CloseCode.InternalServerError,
|
||||
exports.CloseCode.InternalClientError,
|
||||
exports.CloseCode.BadRequest,
|
||||
exports.CloseCode.BadResponse,
|
||||
exports.CloseCode.Unauthorized,
|
||||
// CloseCode.Forbidden, might grant access out after retry
|
||||
exports.CloseCode.SubprotocolNotAcceptable,
|
||||
// CloseCode.ConnectionInitialisationTimeout, might not time out after retry
|
||||
// CloseCode.ConnectionAcknowledgementTimeout, might not time out after retry
|
||||
exports.CloseCode.SubscriberAlreadyExists,
|
||||
exports.CloseCode.TooManyInitialisationRequests,
|
||||
// 4499, // Terminated, probably because the socket froze, we want to retry
|
||||
].includes(errOrCloseEvent.code)))
|
||||
throw errOrCloseEvent;
|
||||
// client was disposed, no retries should proceed regardless
|
||||
if (disposed)
|
||||
return false;
|
||||
// normal closure (possibly all subscriptions have completed)
|
||||
// if no locks were acquired in the meantime, shouldnt try again
|
||||
if (isLikeCloseEvent(errOrCloseEvent) && errOrCloseEvent.code === 1000)
|
||||
return locks > 0;
|
||||
// retries are not allowed or we tried to many times, report error
|
||||
if (!retryAttempts || retries >= retryAttempts)
|
||||
throw errOrCloseEvent;
|
||||
// throw non-retryable connection problems
|
||||
if (!shouldRetry(errOrCloseEvent))
|
||||
throw errOrCloseEvent;
|
||||
// @deprecated throw fatal connection problems immediately
|
||||
if (isFatalConnectionProblem === null || isFatalConnectionProblem === void 0 ? void 0 : isFatalConnectionProblem(errOrCloseEvent))
|
||||
throw errOrCloseEvent;
|
||||
// looks good, start retrying
|
||||
return (retrying = true);
|
||||
}
|
||||
// in non-lazy (hot?) mode always hold one connection lock to persist the socket
|
||||
if (!lazy) {
|
||||
(async () => {
|
||||
locks++;
|
||||
for (;;) {
|
||||
try {
|
||||
const [, , throwOnClose] = await connect();
|
||||
await throwOnClose; // will always throw because releaser is not used
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
try {
|
||||
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
|
||||
return;
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
// report thrown error, no further retries
|
||||
return onNonLazyError === null || onNonLazyError === void 0 ? void 0 : onNonLazyError(errOrCloseEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
}
|
||||
function subscribe(payload, sink) {
|
||||
const id = generateID(payload);
|
||||
let done = false, errored = false, releaser = () => {
|
||||
// for handling completions before connect
|
||||
locks--;
|
||||
done = true;
|
||||
};
|
||||
(async () => {
|
||||
locks++;
|
||||
for (;;) {
|
||||
try {
|
||||
const [socket, release, waitForReleaseOrThrowOnClose] = await connect();
|
||||
// if done while waiting for connect, release the connection lock right away
|
||||
if (done)
|
||||
return release();
|
||||
const unlisten = emitter.onMessage(id, (message) => {
|
||||
switch (message.type) {
|
||||
case exports.MessageType.Next: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- payload will fit type
|
||||
sink.next(message.payload);
|
||||
return;
|
||||
}
|
||||
case exports.MessageType.Error: {
|
||||
(errored = true), (done = true);
|
||||
sink.error(message.payload);
|
||||
releaser();
|
||||
return;
|
||||
}
|
||||
case exports.MessageType.Complete: {
|
||||
done = true;
|
||||
releaser(); // release completes the sink
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
socket.send(stringifyMessage({
|
||||
id,
|
||||
type: exports.MessageType.Subscribe,
|
||||
payload,
|
||||
}, replacer));
|
||||
releaser = () => {
|
||||
if (!done && socket.readyState === WebSocketImpl.OPEN)
|
||||
// if not completed already and socket is open, send complete message to server on release
|
||||
socket.send(stringifyMessage({
|
||||
id,
|
||||
type: exports.MessageType.Complete,
|
||||
}, replacer));
|
||||
locks--;
|
||||
done = true;
|
||||
release();
|
||||
};
|
||||
// either the releaser will be called, connection completed and
|
||||
// the promise resolved or the socket closed and the promise rejected.
|
||||
// whatever happens though, we want to stop listening for messages
|
||||
await waitForReleaseOrThrowOnClose.finally(unlisten);
|
||||
return; // completed, shouldnt try again
|
||||
}
|
||||
catch (errOrCloseEvent) {
|
||||
if (!shouldRetryConnectOrThrow(errOrCloseEvent))
|
||||
return;
|
||||
}
|
||||
}
|
||||
})()
|
||||
.then(() => {
|
||||
// delivering either an error or a complete terminates the sequence
|
||||
if (!errored)
|
||||
sink.complete();
|
||||
}) // resolves on release or normal closure
|
||||
.catch((err) => {
|
||||
sink.error(err);
|
||||
}); // rejects on close events and errors
|
||||
return () => {
|
||||
// dispose only of active subscriptions
|
||||
if (!done)
|
||||
releaser();
|
||||
};
|
||||
}
|
||||
return {
|
||||
on: emitter.on,
|
||||
subscribe,
|
||||
iterate(request) {
|
||||
const pending = [];
|
||||
const deferred = {
|
||||
done: false,
|
||||
error: null,
|
||||
resolve: () => {
|
||||
// noop
|
||||
},
|
||||
};
|
||||
const dispose = subscribe(request, {
|
||||
next(val) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
pending.push(val);
|
||||
deferred.resolve();
|
||||
},
|
||||
error(err) {
|
||||
deferred.done = true;
|
||||
deferred.error = err;
|
||||
deferred.resolve();
|
||||
},
|
||||
complete() {
|
||||
deferred.done = true;
|
||||
deferred.resolve();
|
||||
},
|
||||
});
|
||||
const iterator = (function iterator() {
|
||||
return __asyncGenerator(this, arguments, function* iterator_1() {
|
||||
for (;;) {
|
||||
if (!pending.length) {
|
||||
// only wait if there are no pending messages available
|
||||
yield __await(new Promise((resolve) => (deferred.resolve = resolve)));
|
||||
}
|
||||
// first flush
|
||||
while (pending.length) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
yield yield __await(pending.shift());
|
||||
}
|
||||
// then error
|
||||
if (deferred.error) {
|
||||
throw deferred.error;
|
||||
}
|
||||
// or complete
|
||||
if (deferred.done) {
|
||||
return yield __await(void 0);
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
||||
iterator.throw = async (err) => {
|
||||
if (!deferred.done) {
|
||||
deferred.done = true;
|
||||
deferred.error = err;
|
||||
deferred.resolve();
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
};
|
||||
iterator.return = async () => {
|
||||
dispose();
|
||||
return { done: true, value: undefined };
|
||||
};
|
||||
return iterator;
|
||||
},
|
||||
async dispose() {
|
||||
disposed = true;
|
||||
if (connecting) {
|
||||
// if there is a connection, close it
|
||||
const [socket] = await connecting;
|
||||
socket.close(1000, 'Normal Closure');
|
||||
}
|
||||
},
|
||||
terminate() {
|
||||
if (connecting) {
|
||||
// only if there is a connection
|
||||
emitter.emit('closed', new TerminatedCloseEvent());
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
/**
|
||||
* A syntetic close event `4499: Terminated` is issued to the current to immediately
|
||||
* close the connection without waiting for the one coming from `WebSocket.onclose`.
|
||||
*
|
||||
* Terminating is not considered fatal and a connection retry will occur as expected.
|
||||
*
|
||||
* Useful in cases where the WebSocket is stuck and not emitting any events;
|
||||
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
|
||||
*/
|
||||
class TerminatedCloseEvent extends Error {
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.name = 'TerminatedCloseEvent';
|
||||
this.message = '4499: Terminated';
|
||||
this.code = 4499;
|
||||
this.reason = 'Terminated';
|
||||
this.wasClean = false;
|
||||
}
|
||||
}
|
||||
function isLikeCloseEvent(val) {
|
||||
return isObject(val) && 'code' in val && 'reason' in val;
|
||||
}
|
||||
function isFatalInternalCloseCode(code) {
|
||||
if ([
|
||||
1000,
|
||||
1001,
|
||||
1006,
|
||||
1005,
|
||||
1012,
|
||||
1013,
|
||||
1014, // Bad Gateway
|
||||
].includes(code))
|
||||
return false;
|
||||
// all other internal errors are fatal
|
||||
return code >= 1000 && code <= 1999;
|
||||
}
|
||||
function isWebSocket(val) {
|
||||
return (typeof val === 'function' &&
|
||||
'constructor' in val &&
|
||||
'CLOSED' in val &&
|
||||
'CLOSING' in val &&
|
||||
'CONNECTING' in val &&
|
||||
'OPEN' in val);
|
||||
}
|
||||
|
||||
exports.DEPRECATED_GRAPHQL_WS_PROTOCOL = DEPRECATED_GRAPHQL_WS_PROTOCOL;
|
||||
exports.GRAPHQL_TRANSPORT_WS_PROTOCOL = GRAPHQL_TRANSPORT_WS_PROTOCOL;
|
||||
exports.TerminatedCloseEvent = TerminatedCloseEvent;
|
||||
exports.createClient = createClient;
|
||||
exports.isMessage = isMessage;
|
||||
exports.parseMessage = parseMessage;
|
||||
exports.stringifyMessage = stringifyMessage;
|
||||
exports.validateMessage = validateMessage;
|
||||
|
||||
}));
|
||||
1
graphql-subscription-express/node_modules/graphql-ws/umd/graphql-ws.min.js
generated
vendored
Normal file
1
graphql-subscription-express/node_modules/graphql-ws/umd/graphql-ws.min.js
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
BIN
graphql-subscription-express/node_modules/graphql-ws/umd/graphql-ws.min.js.gz
generated
vendored
Normal file
BIN
graphql-subscription-express/node_modules/graphql-ws/umd/graphql-ws.min.js.gz
generated
vendored
Normal file
Binary file not shown.
Reference in New Issue
Block a user