Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: enisdenjo/graphql-ws
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v1.9.2
Choose a base ref
...
head repository: enisdenjo/graphql-ws
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v1.9.3
Choose a head ref
  • 5 commits
  • 7 files changed
  • 2 contributors

Commits on Oct 31, 2020

  1. Copy the full SHA
    79c2ed2 View commit details
  2. Copy the full SHA
    ef1bc8f View commit details
  3. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    a81e8c1 View commit details
  4. Copy the full SHA
    7f5aeda View commit details
  5. chore(release): 🎉 1.9.3 [skip ci]

    ## [1.9.3](v1.9.2...v1.9.3) (2020-10-31)
    
    ### Bug Fixes
    
    * Drop TypeScript DOM lib dependency ([a81e8c1](a81e8c1))
    * Support more Node versions by not using `globalThis` ([79c2ed2](79c2ed2))
    semantic-release-bot committed Oct 31, 2020
    Copy the full SHA
    8a7ad15 View commit details
Showing with 101 additions and 37 deletions.
  1. +8 −0 CHANGELOG.md
  2. +6 −3 docs/interfaces/_client_.clientoptions.md
  3. +5 −2 docs/interfaces/_types_.sink.md
  4. +30 −1 docs/modules/_client_.md
  5. +1 −4 package.json
  6. +44 −25 src/client.ts
  7. +7 −2 src/types.ts
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
## [1.9.3](https://github.com/enisdenjo/graphql-ws/compare/v1.9.2...v1.9.3) (2020-10-31)


### Bug Fixes

* Drop TypeScript DOM lib dependency ([a81e8c1](https://github.com/enisdenjo/graphql-ws/commit/a81e8c1ea080984ddd6d5e58c154b866ee44c14c))
* Support more Node versions by not using `globalThis` ([79c2ed2](https://github.com/enisdenjo/graphql-ws/commit/79c2ed2056b69bd9b56984947d78897e36594b80))

## [1.9.2](https://github.com/enisdenjo/graphql-ws/compare/v1.9.1...v1.9.2) (2020-10-31)


9 changes: 6 additions & 3 deletions docs/interfaces/_client_.clientoptions.md
Original file line number Diff line number Diff line change
@@ -40,9 +40,12 @@ ___
`Optional` **generateID**: undefined \| () => [ID](../modules/_types_.md#id)

A custom ID generator for identifying subscriptions.
The default uses the `crypto` module in the global scope
which is present for modern browsers. However, if
it can't be found, `Math.random` would be used instead.

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://stackoverflow.com/a/2117523/709884

___

7 changes: 5 additions & 2 deletions docs/interfaces/_types_.sink.md
Original file line number Diff line number Diff line change
@@ -38,15 +38,18 @@ ___

### error

**error**(`error`: Error \| CloseEvent \| readonly GraphQLError[]): void
**error**(`error`: Error \| readonly GraphQLError[] \| unknown): void

An error that has occured. Calling this function "closes" the sink.
The error can be also `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.

#### Parameters:

Name | Type |
------ | ------ |
`error` | Error \| CloseEvent \| readonly GraphQLError[] |
`error` | Error \| readonly GraphQLError[] \| unknown |

**Returns:** void

31 changes: 30 additions & 1 deletion docs/modules/_client_.md
Original file line number Diff line number Diff line change
@@ -15,8 +15,11 @@

* [Event](_client_.md#event)
* [EventClosed](_client_.md#eventclosed)
* [EventClosedListener](_client_.md#eventclosedlistener)
* [EventConnected](_client_.md#eventconnected)
* [EventConnectedListener](_client_.md#eventconnectedlistener)
* [EventConnecting](_client_.md#eventconnecting)
* [EventConnectingListener](_client_.md#eventconnectinglistener)
* [EventListener](_client_.md#eventlistener)

### Functions
@@ -37,21 +40,47 @@ ___

___

### EventClosedListener

Ƭ **EventClosedListener**: (event: unknown) => 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.

___

### EventConnected

Ƭ **EventConnected**: \"connected\"

___

### EventConnectedListener

Ƭ **EventConnectedListener**: (socket: unknown) => void

The 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.

___

### EventConnecting

Ƭ **EventConnecting**: \"connecting\"

___

### EventConnectingListener

Ƭ **EventConnectingListener**: () => void

___

### EventListener

Ƭ **EventListener**\<E>: E *extends* EventConnecting ? () => void : E *extends* EventConnected ? (socket: WebSocket) => void : E *extends* EventClosed ? (event: CloseEvent) => void : never
Ƭ **EventListener**\<E>: E *extends* EventConnecting ? EventConnectingListener : E *extends* EventConnected ? EventConnectedListener : E *extends* EventClosed ? EventClosedListener : never

#### Type parameters:

5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "graphql-ws",
"version": "1.9.2",
"version": "1.9.3",
"description": "Coherent, zero-dependency, lazy, simple, GraphQL over WebSocket Protocol compliant server and client",
"keywords": [
"protocol",
@@ -33,9 +33,6 @@
"publishConfig": {
"access": "public"
},
"engines": {
"node": ">=12"
},
"scripts": {
"gendocs": "typedoc --options typedoc.js src/",
"lint": "eslint 'src'",
69 changes: 44 additions & 25 deletions src/client.ts
Original file line number Diff line number Diff line change
@@ -20,12 +20,28 @@ export type EventConnected = 'connected'; // connected = socket opened + acknowl
export type EventClosed = 'closed';
export type Event = EventConnecting | EventConnected | EventClosed;

/**
* The 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.
*/
export type EventConnectedListener = (socket: unknown) => void;

export type EventConnectingListener = () => 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.
*/
export type EventClosedListener = (event: unknown) => void;

export type EventListener<E extends Event> = E extends EventConnecting
? () => void
? EventConnectingListener
: E extends EventConnected
? (socket: WebSocket) => void
? EventConnectedListener
: E extends EventClosed
? (event: CloseEvent) => void
? EventClosedListener
: never;

type CancellerRef = { current: (() => void) | null };
@@ -75,9 +91,12 @@ export interface ClientOptions {
webSocketImpl?: unknown;
/**
* A custom ID generator for identifying subscriptions.
* The default uses the `crypto` module in the global scope
* which is present for modern browsers. However, if
* it can't be found, `Math.random` would be used instead.
*
* 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://stackoverflow.com/a/2117523/709884
*/
generateID?: () => ID;
}
@@ -106,22 +125,13 @@ export function createClient(options: ClientOptions): Client {
on,
webSocketImpl,
/**
* Generates a v4 UUID to be used as the ID.
* 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://stackoverflow.com/a/2117523/709884
*/
generateID = function generateUUID() {
if (globalThis.crypto) {
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (s) => {
const c = Number.parseInt(s, 10);
return (
c ^
(globalThis.crypto.getRandomValues(new Uint8Array(1))[0] &
(15 >> (c / 4)))
).toString(16);
});
}

// use Math.random when crypto is not available
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8;
@@ -130,16 +140,25 @@ export function createClient(options: ClientOptions): Client {
},
} = options;

let WebSocketImpl = globalThis.WebSocket;
let ws;
if (webSocketImpl) {
if (!isWebSocket(webSocketImpl)) {
throw new Error('Invalid WebSocket implementation provided');
}
WebSocketImpl = webSocketImpl;
ws = webSocketImpl;
} else if (typeof WebSocket !== 'undefined') {
ws = WebSocket;
} else if (typeof global !== 'undefined') {
// @ts-expect-error: Support more browsers
ws = global.WebSocket || global.MozWebSocket;
} else if (typeof window !== 'undefined') {
// @ts-expect-error: Support more browsers
ws = window.WebSocket || window.MozWebSocket;
}
if (!WebSocketImpl) {
if (!ws) {
throw new Error('WebSocket implementation missing');
}
const WebSocketImpl = ws;

// websocket status emitter, subscriptions are handled differently
const emitter = (() => {
@@ -410,7 +429,7 @@ export function createClient(options: ClientOptions): Client {
const id = generateID();
const cancellerRef: CancellerRef = { current: null };

const messageHandler = ({ data }: MessageEvent) => {
const messageListener = ({ data }: MessageEvent) => {
const message = memoParseMessage(data);
switch (message.type) {
case MessageType.Next: {
@@ -454,7 +473,7 @@ export function createClient(options: ClientOptions): Client {
const [socket, throwOnCloseOrWaitForCancel] = await connect(
cancellerRef,
);
socket.addEventListener('message', messageHandler);
socket.addEventListener('message', messageListener);

socket.send(
stringifyMessage<MessageType.Subscribe>({
@@ -476,7 +495,7 @@ export function createClient(options: ClientOptions): Client {
);
});

socket.removeEventListener('message', messageHandler);
socket.removeEventListener('message', messageListener);

// cancelled, shouldnt try again
return;
9 changes: 7 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -24,8 +24,13 @@ export interface Disposable {
export interface Sink<T = unknown> {
/** Next value arriving. */
next(value: T): void;
/** An error that has occured. Calling this function "closes" the sink. */
error(error: Error | CloseEvent | readonly GraphQLError[]): void;
/**
* An error that has occured. Calling this function "closes" the sink.
* The error can be also `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: Error | readonly GraphQLError[] | unknown): void;
/** The sink has completed. This function "closes" the sink. */
complete(): void;
}