From 9053224863ac3ca07dbe990f1332e3f7f79da01d Mon Sep 17 00:00:00 2001 From: Denis Badurina Date: Wed, 23 Jun 2021 15:47:05 +0200 Subject: [PATCH] feat(client): Add `opened` event for when a WebSocket opens --- README.md | 12 ++++++------ docs/modules/client.md | 36 ++++++++++++++++++++++++++++++++++-- src/client.ts | 23 ++++++++++++++++++++--- src/tests/client.ts | 9 +++++++++ 4 files changed, 69 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index afa781e5..864a6579 100644 --- a/README.md +++ b/README.md @@ -547,8 +547,8 @@ function createRestartableClient(options: ClientOptions): RestartableClient { ...options, on: { ...options.on, - connected: (socket) => { - options.on?.connected?.(socket); + opened: (socket) => { + options.on?.opened?.(socket); restart = () => { if (socket.readyState === WebSocket.OPEN) { @@ -556,7 +556,7 @@ function createRestartableClient(options: ClientOptions): RestartableClient { socket.close(4205, 'Client Restart'); } else { // otherwise the socket might've closed, indicate that you want - // a restart on the next connected event + // a restart on the next opened event restartRequested = true; } }; @@ -603,7 +603,7 @@ createClient({ url: 'ws://i.time.out:4000/and-measure/latency', keepAlive: 10_000, // ping server every 10 seconds on: { - connected: (socket) => (activeSocket = socket), + opened: (socket) => (activeSocket = socket), ping: (received) => { if (!received /* sent */) { pingSentAt = Date.now(); @@ -651,8 +651,8 @@ function createPingerClient(options: ClientOptions): PingerClient { disablePong: true, ...options, on: { - connected: (socket) => { - options.on?.connected?.(socket); + opened: (socket) => { + options.on?.opened?.(socket); activeSocket = socket; }, }, diff --git a/docs/modules/client.md b/docs/modules/client.md index e7669ddf..6fb2a623 100644 --- a/docs/modules/client.md +++ b/docs/modules/client.md @@ -46,6 +46,8 @@ - [EventListener](client.md#eventlistener) - [EventMessage](client.md#eventmessage) - [EventMessageListener](client.md#eventmessagelistener) +- [EventOpened](client.md#eventopened) +- [EventOpenedListener](client.md#eventopenedlistener) - [EventPing](client.md#eventping) - [EventPingListener](client.md#eventpinglistener) - [EventPong](client.md#eventpong) @@ -59,7 +61,7 @@ ### Event -Ƭ **Event**: [EventConnecting](client.md#eventconnecting) \| [EventConnected](client.md#eventconnected) \| [EventPing](client.md#eventping) \| [EventPong](client.md#eventpong) \| [EventMessage](client.md#eventmessage) \| [EventClosed](client.md#eventclosed) \| [EventError](client.md#eventerror) +Ƭ **Event**: [EventConnecting](client.md#eventconnecting) \| [EventOpened](client.md#eventopened) \| [EventConnected](client.md#eventconnected) \| [EventPing](client.md#eventping) \| [EventPong](client.md#eventpong) \| [EventMessage](client.md#eventmessage) \| [EventClosed](client.md#eventclosed) \| [EventError](client.md#eventerror) ___ @@ -180,7 +182,7 @@ ___ ### EventListener -Ƭ **EventListener**: `E` extends [EventConnecting](client.md#eventconnecting) ? [EventConnectingListener](client.md#eventconnectinglistener) : `E` extends [EventConnected](client.md#eventconnected) ? [EventConnectedListener](client.md#eventconnectedlistener) : `E` extends [EventPing](client.md#eventping) ? [EventPingListener](client.md#eventpinglistener) : `E` extends [EventPong](client.md#eventpong) ? [EventPongListener](client.md#eventponglistener) : `E` extends [EventMessage](client.md#eventmessage) ? [EventMessageListener](client.md#eventmessagelistener) : `E` extends [EventClosed](client.md#eventclosed) ? [EventClosedListener](client.md#eventclosedlistener) : `E` extends [EventError](client.md#eventerror) ? [EventErrorListener](client.md#eventerrorlistener) : `never` +Ƭ **EventListener**: `E` extends [EventConnecting](client.md#eventconnecting) ? [EventConnectingListener](client.md#eventconnectinglistener) : `E` extends [EventOpened](client.md#eventopened) ? [EventOpenedListener](client.md#eventopenedlistener) : `E` extends [EventConnected](client.md#eventconnected) ? [EventConnectedListener](client.md#eventconnectedlistener) : `E` extends [EventPing](client.md#eventping) ? [EventPingListener](client.md#eventpinglistener) : `E` extends [EventPong](client.md#eventpong) ? [EventPongListener](client.md#eventponglistener) : `E` extends [EventMessage](client.md#eventmessage) ? [EventMessageListener](client.md#eventmessagelistener) : `E` extends [EventClosed](client.md#eventclosed) ? [EventClosedListener](client.md#eventclosedlistener) : `E` extends [EventError](client.md#eventerror) ? [EventErrorListener](client.md#eventerrorlistener) : `never` #### Type parameters @@ -219,6 +221,36 @@ debugging and logging received messages. ___ +### EventOpened + +Ƭ **EventOpened**: ``"opened"`` + +___ + +### EventOpenedListener + +Ƭ **EventOpenedListener**: (`socket`: `unknown`) => `void` + +#### Type declaration + +▸ (`socket`): `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. + +##### Parameters + +| Name | Type | +| :------ | :------ | +| `socket` | `unknown` | + +##### Returns + +`void` + +___ + ### EventPing Ƭ **EventPing**: ``"ping"`` diff --git a/src/client.ts b/src/client.ts index c3fc60ca..bb6dffeb 100644 --- a/src/client.ts +++ b/src/client.ts @@ -29,6 +29,9 @@ export * from './common'; /** @category Client */ export type EventConnecting = 'connecting'; +/** @category Client */ +export type EventOpened = 'opened'; // socket opened + /** @category Client */ export type EventConnected = 'connected'; // connected = socket opened + acknowledged @@ -50,6 +53,7 @@ export type EventError = 'error'; /** @category Client */ export type Event = | EventConnecting + | EventOpened | EventConnected | EventPing | EventPong @@ -57,6 +61,18 @@ export type Event = | EventClosed | EventError; +/** @category Client */ +export type EventConnectingListener = () => 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, @@ -72,9 +88,6 @@ export type EventConnectedListener = ( payload: ConnectionAckMessage['payload'], ) => void; -/** @category Client */ -export type EventConnectingListener = () => void; - /** * The first argument communicates whether the ping was received from the server. * If `false`, the ping was sent by the client. @@ -127,6 +140,8 @@ export type EventErrorListener = (error: unknown) => void; /** @category Client */ export type EventListener = E extends EventConnecting ? EventConnectingListener + : E extends EventOpened + ? EventOpenedListener : E extends EventConnected ? EventConnectedListener : E extends EventPing @@ -444,6 +459,7 @@ export function createClient(options: ClientOptions): Client { })(); const listeners: { [event in Event]: EventListener[] } = { connecting: on?.connecting ? [on.connecting] : [], + opened: on?.opened ? [on.opened] : [], connected: on?.connected ? [on.connected] : [], ping: on?.ping ? [on.ping] : [], pong: on?.pong ? [on.pong] : [], @@ -531,6 +547,7 @@ export function createClient(options: ClientOptions): Client { socket.onopen = async () => { try { + emitter.emit('opened', socket); const payload = typeof connectionParams === 'function' ? await connectionParams() diff --git a/src/tests/client.ts b/src/tests/client.ts index 1a75c33c..fd22209b 100644 --- a/src/tests/client.ts +++ b/src/tests/client.ts @@ -1485,6 +1485,7 @@ describe('events', () => { const { url, ...server } = await startTServer(); const connectingFn = jest.fn(noop as EventListener<'connecting'>); + const openedFn = jest.fn(noop as EventListener<'opened'>); const connectedFn = jest.fn(noop as EventListener<'connected'>); const messageFn = jest.fn(noop as EventListener<'message'>); const closedFn = jest.fn(noop as EventListener<'closed'>); @@ -1498,12 +1499,14 @@ describe('events', () => { onNonLazyError: noop, on: { connecting: connectingFn, + opened: openedFn, connected: connectedFn, message: messageFn, closed: closedFn, }, }); client.on('connecting', connectingFn); + client.on('opened', openedFn); client.on('connected', connectedFn); client.on('message', messageFn); client.on('closed', closedFn); @@ -1519,6 +1522,11 @@ describe('events', () => { expect(connectingFn).toBeCalledTimes(2); expect(connectingFn.mock.calls[0].length).toBe(0); + expect(openedFn).toBeCalledTimes(2); // initial and registered listener + openedFn.mock.calls.forEach((cal) => { + expect(cal[0]).toBeInstanceOf(WebSocket); + }); + expect(connectedFn).toBeCalledTimes(2); // initial and registered listener connectedFn.mock.calls.forEach((cal) => { expect(cal[0]).toBeInstanceOf(WebSocket); @@ -1546,6 +1554,7 @@ describe('events', () => { // retrying is disabled expect(connectingFn).toBeCalledTimes(2); + expect(openedFn).toBeCalledTimes(2); expect(connectedFn).toBeCalledTimes(2); expect(closedFn).toBeCalledTimes(2); // initial and registered listener