Initial Sample.
This commit is contained in:
407
graphql-subscription/node_modules/graphql-ws/lib/client.d.mts
generated
vendored
Normal file
407
graphql-subscription/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/node_modules/graphql-ws/lib/client.d.ts
generated
vendored
Normal file
407
graphql-subscription/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/node_modules/graphql-ws/lib/client.js
generated
vendored
Normal file
575
graphql-subscription/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/node_modules/graphql-ws/lib/client.mjs
generated
vendored
Normal file
556
graphql-subscription/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/node_modules/graphql-ws/lib/common.d.mts
generated
vendored
Normal file
201
graphql-subscription/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/node_modules/graphql-ws/lib/common.d.ts
generated
vendored
Normal file
201
graphql-subscription/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/node_modules/graphql-ws/lib/common.js
generated
vendored
Normal file
185
graphql-subscription/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/node_modules/graphql-ws/lib/common.mjs
generated
vendored
Normal file
178
graphql-subscription/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/node_modules/graphql-ws/lib/index.d.mts
generated
vendored
Normal file
3
graphql-subscription/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/node_modules/graphql-ws/lib/index.d.ts
generated
vendored
Normal file
3
graphql-subscription/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/node_modules/graphql-ws/lib/index.js
generated
vendored
Normal file
19
graphql-subscription/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/node_modules/graphql-ws/lib/index.mjs
generated
vendored
Normal file
3
graphql-subscription/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/node_modules/graphql-ws/lib/server.d.mts
generated
vendored
Normal file
417
graphql-subscription/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/node_modules/graphql-ws/lib/server.d.ts
generated
vendored
Normal file
417
graphql-subscription/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;
|
||||
301
graphql-subscription/node_modules/graphql-ws/lib/server.js
generated
vendored
Normal file
301
graphql-subscription/node_modules/graphql-ws/lib/server.js
generated
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
"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 await emit.error(maybeExecArgsOrErrors);
|
||||
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 await emit.error(validationErrors);
|
||||
}
|
||||
const operationAST = (0, graphql_1.getOperationAST)(execArgs.document, execArgs.operationName);
|
||||
if (!operationAST)
|
||||
return await emit.error([
|
||||
new graphql_1.GraphQLError('Unable to identify operation'),
|
||||
]);
|
||||
// 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);
|
||||
for (const [id, sub] of Object.entries(ctx.subscriptions)) {
|
||||
if ((0, utils_1.isAsyncGenerator)(sub))
|
||||
await sub.return(undefined);
|
||||
delete ctx.subscriptions[id]; // deleting the subscription means no further activity should take place
|
||||
}
|
||||
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;
|
||||
296
graphql-subscription/node_modules/graphql-ws/lib/server.mjs
generated
vendored
Normal file
296
graphql-subscription/node_modules/graphql-ws/lib/server.mjs
generated
vendored
Normal file
@@ -0,0 +1,296 @@
|
||||
/**
|
||||
*
|
||||
* 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 await emit.error(maybeExecArgsOrErrors);
|
||||
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 await emit.error(validationErrors);
|
||||
}
|
||||
const operationAST = getOperationAST(execArgs.document, execArgs.operationName);
|
||||
if (!operationAST)
|
||||
return await emit.error([
|
||||
new GraphQLError('Unable to identify operation'),
|
||||
]);
|
||||
// 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);
|
||||
for (const [id, sub] of Object.entries(ctx.subscriptions)) {
|
||||
if (isAsyncGenerator(sub))
|
||||
await sub.return(undefined);
|
||||
delete ctx.subscriptions[id]; // deleting the subscription means no further activity should take place
|
||||
}
|
||||
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/node_modules/graphql-ws/lib/use/@fastify/websocket.d.mts
generated
vendored
Normal file
36
graphql-subscription/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/node_modules/graphql-ws/lib/use/@fastify/websocket.d.ts
generated
vendored
Normal file
36
graphql-subscription/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/node_modules/graphql-ws/lib/use/@fastify/websocket.js
generated
vendored
Normal file
126
graphql-subscription/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/node_modules/graphql-ws/lib/use/@fastify/websocket.mjs
generated
vendored
Normal file
122
graphql-subscription/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/node_modules/graphql-ws/lib/use/bun.d.mts
generated
vendored
Normal file
60
graphql-subscription/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/node_modules/graphql-ws/lib/use/bun.d.ts
generated
vendored
Normal file
60
graphql-subscription/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/node_modules/graphql-ws/lib/use/bun.js
generated
vendored
Normal file
97
graphql-subscription/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/node_modules/graphql-ws/lib/use/bun.mjs
generated
vendored
Normal file
92
graphql-subscription/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/node_modules/graphql-ws/lib/use/deno.d.mts
generated
vendored
Normal file
56
graphql-subscription/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/node_modules/graphql-ws/lib/use/deno.d.ts
generated
vendored
Normal file
56
graphql-subscription/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/node_modules/graphql-ws/lib/use/deno.js
generated
vendored
Normal file
88
graphql-subscription/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/node_modules/graphql-ws/lib/use/deno.mjs
generated
vendored
Normal file
83
graphql-subscription/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/node_modules/graphql-ws/lib/use/fastify-websocket.d.mts
generated
vendored
Normal file
40
graphql-subscription/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/node_modules/graphql-ws/lib/use/fastify-websocket.d.ts
generated
vendored
Normal file
40
graphql-subscription/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/node_modules/graphql-ws/lib/use/fastify-websocket.js
generated
vendored
Normal file
25
graphql-subscription/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/node_modules/graphql-ws/lib/use/fastify-websocket.mjs
generated
vendored
Normal file
21
graphql-subscription/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/node_modules/graphql-ws/lib/use/uWebSockets.d.mts
generated
vendored
Normal file
67
graphql-subscription/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/node_modules/graphql-ws/lib/use/uWebSockets.d.ts
generated
vendored
Normal file
67
graphql-subscription/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/node_modules/graphql-ws/lib/use/uWebSockets.js
generated
vendored
Normal file
141
graphql-subscription/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/node_modules/graphql-ws/lib/use/uWebSockets.mjs
generated
vendored
Normal file
137
graphql-subscription/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/node_modules/graphql-ws/lib/use/ws.d.mts
generated
vendored
Normal file
38
graphql-subscription/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/node_modules/graphql-ws/lib/use/ws.d.ts
generated
vendored
Normal file
38
graphql-subscription/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/node_modules/graphql-ws/lib/use/ws.js
generated
vendored
Normal file
119
graphql-subscription/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/node_modules/graphql-ws/lib/use/ws.mjs
generated
vendored
Normal file
115
graphql-subscription/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/node_modules/graphql-ws/lib/utils.d.mts
generated
vendored
Normal file
23
graphql-subscription/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/node_modules/graphql-ws/lib/utils.d.ts
generated
vendored
Normal file
23
graphql-subscription/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/node_modules/graphql-ws/lib/utils.js
generated
vendored
Normal file
54
graphql-subscription/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/node_modules/graphql-ws/lib/utils.mjs
generated
vendored
Normal file
45
graphql-subscription/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;
|
||||
}
|
||||
Reference in New Issue
Block a user