Skip to content

Commit 74b4ceb

Browse files
committed
fix(client): Use TerminatedCloseEvent class extending an Error for rejecting promises when terminating
Closes #531
1 parent f76bb54 commit 74b4ceb

File tree

2 files changed

+38
-13
lines changed

2 files changed

+38
-13
lines changed

src/__tests__/client.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,12 @@
44

55
import WebSocket from 'ws';
66
import { EventEmitter } from 'events';
7-
import { createClient, Client, EventListener } from '../client';
7+
import {
8+
createClient,
9+
Client,
10+
EventListener,
11+
TerminatedCloseEvent,
12+
} from '../client';
813
import {
914
CloseCode,
1015
MessageType,
@@ -815,9 +820,16 @@ it('should terminate socket immediately on terminate', async (done) => {
815820
on: {
816821
closed: (event) => {
817822
expect(event).not.toBeInstanceOf(CloseEvent); // because its an artificial close-event-like object
818-
expect((event as any).code).toBe(4499);
819-
expect((event as any).reason).toBe('Terminated');
820-
expect((event as any).wasClean).toBeFalsy();
823+
expect(event).toBeInstanceOf(TerminatedCloseEvent);
824+
expect((event as TerminatedCloseEvent).name).toBe(
825+
'TerminatedCloseEvent',
826+
);
827+
expect((event as TerminatedCloseEvent).message).toBe(
828+
'4499: Terminated',
829+
);
830+
expect((event as TerminatedCloseEvent).code).toBe(4499);
831+
expect((event as TerminatedCloseEvent).reason).toBe('Terminated');
832+
expect((event as TerminatedCloseEvent).wasClean).toBeFalsy();
821833
done();
822834
},
823835
},

src/client.ts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,9 @@ export interface Client extends Disposable {
451451
/**
452452
* Terminates the WebSocket abruptly and immediately.
453453
*
454-
* A close event `4499: Terminated` is issued to the current WebSocket and an
455-
* artificial `{ code: 4499, reason: 'Terminated', wasClean: false }` close-event-like
456-
* object is immediately emitted without waiting for the one coming from `WebSocket.onclose`.
454+
* A close event `4499: Terminated` is issued to the current WebSocket and a
455+
* syntetic {@link TerminatedCloseEvent} is immediately emitted without waiting for
456+
* the one coming from `WebSocket.onclose`.
457457
*
458458
* Terminating is not considered fatal and a connection retry will occur as expected.
459459
*
@@ -664,7 +664,7 @@ export function createClient<
664664
clearTimeout(queuedPing);
665665
denied(errOrEvent);
666666

667-
if (isLikeCloseEvent(errOrEvent) && errOrEvent.code === 4499) {
667+
if (errOrEvent instanceof TerminatedCloseEvent) {
668668
socket.close(4499, 'Terminated'); // close event is artificial and emitted manually, see `Client.terminate()` below
669669
socket.onerror = null;
670670
socket.onclose = null;
@@ -1061,16 +1061,29 @@ export function createClient<
10611061
terminate() {
10621062
if (connecting) {
10631063
// only if there is a connection
1064-
emitter.emit('closed', {
1065-
code: 4499,
1066-
reason: 'Terminated',
1067-
wasClean: false,
1068-
});
1064+
emitter.emit('closed', new TerminatedCloseEvent());
10691065
}
10701066
},
10711067
};
10721068
}
10731069

1070+
/**
1071+
* A syntetic close event `4499: Terminated` is issued to the current to immediately
1072+
* close the connection without waiting for the one coming from `WebSocket.onclose`.
1073+
*
1074+
* Terminating is not considered fatal and a connection retry will occur as expected.
1075+
*
1076+
* Useful in cases where the WebSocket is stuck and not emitting any events;
1077+
* can happen on iOS Safari, see: https://github.com/enisdenjo/graphql-ws/discussions/290.
1078+
*/
1079+
export class TerminatedCloseEvent extends Error {
1080+
public name = 'TerminatedCloseEvent';
1081+
public message = '4499: Terminated';
1082+
public code = 4499;
1083+
public reason = 'Terminated';
1084+
public wasClean = false;
1085+
}
1086+
10741087
/** Minimal close event interface required by the lib for error and socket close handling. */
10751088
interface LikeCloseEvent {
10761089
/** Returns the WebSocket connection close code provided by the server. */

0 commit comments

Comments
 (0)