From 0c96603a3f97f28eabe24906e692c31ef0ebca13 Mon Sep 17 00:00:00 2001 From: Jonas Gloning <34194370+jonasgloning@users.noreply.github.com> Date: Wed, 25 May 2022 23:27:51 +0200 Subject: [PATCH] fix(typings): much stronger event typings for `DataConnection`,`MediaConnection` --- lib/baseconnection.ts | 15 +++++++++++++-- lib/dataconnection.ts | 35 ++++++++++++++++++++++------------- lib/enums.ts | 17 ----------------- lib/exports.ts | 2 -- lib/mediaconnection.ts | 21 ++++++++++++--------- lib/negotiator.ts | 29 ++++++++++++++--------------- lib/peer.ts | 18 +++++++++++++----- test/peer.ts | 13 ++++--------- 8 files changed, 78 insertions(+), 72 deletions(-) diff --git a/lib/baseconnection.ts b/lib/baseconnection.ts index b7fa83303..b6c5fb748 100644 --- a/lib/baseconnection.ts +++ b/lib/baseconnection.ts @@ -1,9 +1,20 @@ -import { EventEmitter } from "eventemitter3"; +import { EventEmitter, ValidEventTypes } from "eventemitter3"; import { Peer } from "./peer"; import { ServerMessage } from "./servermessage"; import { ConnectionType } from "./enums"; -export abstract class BaseConnection extends EventEmitter { +export type BaseConnectionEvents = { + /** + * Emitted when either you or the remote peer closes the connection. + */ + close: () => void; + error: (error: Error) => void; + iceStateChanged: (state: RTCIceConnectionState) => void; +}; + +export abstract class BaseConnection< + T extends ValidEventTypes, +> extends EventEmitter { protected _open = false; readonly metadata: any; diff --git a/lib/dataconnection.ts b/lib/dataconnection.ts index 7b727649a..a72dfa445 100644 --- a/lib/dataconnection.ts +++ b/lib/dataconnection.ts @@ -1,26 +1,35 @@ import { util } from "./util"; import logger from "./logger"; import { Negotiator } from "./negotiator"; -import { - ConnectionType, - ConnectionEventType, - SerializationType, - ServerMessageType, -} from "./enums"; +import { ConnectionType, SerializationType, ServerMessageType } from "./enums"; import { Peer } from "./peer"; import { BaseConnection } from "./baseconnection"; import { ServerMessage } from "./servermessage"; import { EncodingQueue } from "./encodingQueue"; import type { DataConnection as IDataConnection } from "./dataconnection"; +type DataConnectionEvents = { + /** + * Emitted when data is received from the remote peer. + */ + data: (data: unknown) => void; + /** + * Emitted when the connection is established and ready-to-use. + */ + open: () => void; +}; + /** * Wraps a DataChannel between two Peers. */ -export class DataConnection extends BaseConnection implements IDataConnection { +export class DataConnection + extends BaseConnection + implements IDataConnection +{ private static readonly ID_PREFIX = "dc_"; private static readonly MAX_BUFFERED_AMOUNT = 8 * 1024 * 1024; - private _negotiator: Negotiator; + private _negotiator: Negotiator; readonly label: string; readonly serialization: SerializationType; readonly reliable: boolean; @@ -98,7 +107,7 @@ export class DataConnection extends BaseConnection implements IDataConnection { this.dataChannel.onopen = () => { logger.log(`DC#${this.connectionId} dc connection success`); this._open = true; - this.emit(ConnectionEventType.Open); + this.emit("open"); }; this.dataChannel.onmessage = (e) => { @@ -131,7 +140,7 @@ export class DataConnection extends BaseConnection implements IDataConnection { // Datatype should never be blob util.blobToArrayBuffer(data as Blob, (ab) => { const unpackedData = util.unpack(ab); - this.emit(ConnectionEventType.Data, unpackedData); + this.emit("data", unpackedData); }); return; } else if (datatype === ArrayBuffer) { @@ -152,7 +161,7 @@ export class DataConnection extends BaseConnection implements IDataConnection { return; } - super.emit(ConnectionEventType.Data, deserializedData); + super.emit("data", deserializedData); } private _handleChunk(data: { @@ -222,14 +231,14 @@ export class DataConnection extends BaseConnection implements IDataConnection { this._open = false; - super.emit(ConnectionEventType.Close); + super.emit("close"); } /** Allows user to send data. */ send(data: any, chunked?: boolean): void { if (!this.open) { super.emit( - ConnectionEventType.Error, + "error", new Error( "Connection is not open. You should listen for the `open` event before sending messages.", ), diff --git a/lib/enums.ts b/lib/enums.ts index 18aa670a5..a309b9b0b 100644 --- a/lib/enums.ts +++ b/lib/enums.ts @@ -1,25 +1,8 @@ -export enum ConnectionEventType { - Open = "open", - Stream = "stream", - Data = "data", - Close = "close", - Error = "error", - IceStateChanged = "iceStateChanged", -} - export enum ConnectionType { Data = "data", Media = "media", } -export type PeerEventType = - | "open" - | "close" - | "connection" - | "call" - | "disconnected" - | "error"; - export enum PeerErrorType { BrowserIncompatible = "browser-incompatible", Disconnected = "disconnected", diff --git a/lib/exports.ts b/lib/exports.ts index 3fe07174d..0fc05b3d4 100644 --- a/lib/exports.ts +++ b/lib/exports.ts @@ -12,9 +12,7 @@ export type { DataConnection } from "./dataconnection"; export type { MediaConnection } from "./mediaconnection"; export type { LogLevel } from "./logger"; export type { - ConnectionEventType, ConnectionType, - PeerEventType, PeerErrorType, SerializationType, SocketEventType, diff --git a/lib/mediaconnection.ts b/lib/mediaconnection.ts index 4ec0323b3..aec9360a4 100644 --- a/lib/mediaconnection.ts +++ b/lib/mediaconnection.ts @@ -1,23 +1,26 @@ import { util } from "./util"; import logger from "./logger"; import { Negotiator } from "./negotiator"; -import { - ConnectionType, - ConnectionEventType, - ServerMessageType, -} from "./enums"; +import { ConnectionType, ServerMessageType } from "./enums"; import { Peer } from "./peer"; import { BaseConnection } from "./baseconnection"; import { ServerMessage } from "./servermessage"; import type { AnswerOption } from "./optionInterfaces"; +type MediaConnectionEvents = { + /** + * Emitted when a connection to the PeerServer is established. + */ + stream: (stream: MediaStream) => void; +}; + /** * Wraps the streaming interface between two Peers. */ -export class MediaConnection extends BaseConnection { +export class MediaConnection extends BaseConnection { private static readonly ID_PREFIX = "mc_"; - private _negotiator: Negotiator; + private _negotiator: Negotiator; private _localStream: MediaStream; private _remoteStream: MediaStream; @@ -54,7 +57,7 @@ export class MediaConnection extends BaseConnection { logger.log("Receiving stream", remoteStream); this._remoteStream = remoteStream; - super.emit(ConnectionEventType.Stream, remoteStream); // Should we call this `open`? + super.emit("stream", remoteStream); // Should we call this `open`? } handleMessage(message: ServerMessage): void { @@ -134,6 +137,6 @@ export class MediaConnection extends BaseConnection { this._open = false; - super.emit(ConnectionEventType.Close); + super.emit("close"); } } diff --git a/lib/negotiator.ts b/lib/negotiator.ts index dcc470297..eccc419fb 100644 --- a/lib/negotiator.ts +++ b/lib/negotiator.ts @@ -2,19 +2,18 @@ import { util } from "./util"; import logger from "./logger"; import { MediaConnection } from "./mediaconnection"; import { DataConnection } from "./dataconnection"; -import { - ConnectionType, - PeerErrorType, - ConnectionEventType, - ServerMessageType, -} from "./enums"; -import { BaseConnection } from "./baseconnection"; +import { ConnectionType, PeerErrorType, ServerMessageType } from "./enums"; +import { BaseConnection, BaseConnectionEvents } from "./baseconnection"; +import { ValidEventTypes } from "eventemitter3"; /** * Manages all negotiations between Peers. */ -export class Negotiator { - constructor(readonly connection: BaseConnection) {} +export class Negotiator< + A extends ValidEventTypes, + T extends BaseConnection, +> { + constructor(readonly connection: T) {} /** Returns a PeerConnection object set up correctly (for data, media). */ startConnection(options: any) { @@ -30,7 +29,7 @@ export class Negotiator { // What do we need to do now? if (options.originator) { if (this.connection.type === ConnectionType.Data) { - const dataConnection = this.connection; + const dataConnection = (this.connection); const config: RTCDataChannelInit = { ordered: !!options.reliable }; @@ -93,7 +92,7 @@ export class Negotiator { "iceConnectionState is failed, closing connections to " + peerId, ); this.connection.emit( - ConnectionEventType.Error, + "error", new Error("Negotiation of connection to " + peerId + " failed."), ); this.connection.close(); @@ -103,7 +102,7 @@ export class Negotiator { "iceConnectionState is closed, closing connections to " + peerId, ); this.connection.emit( - ConnectionEventType.Error, + "error", new Error("Connection to " + peerId + " closed."), ); this.connection.close(); @@ -120,7 +119,7 @@ export class Negotiator { } this.connection.emit( - ConnectionEventType.IceStateChanged, + "iceStateChanged", peerConnection.iceConnectionState, ); }; @@ -179,7 +178,7 @@ export class Negotiator { let dataChannelNotClosed = false; if (this.connection.type === ConnectionType.Data) { - const dataConnection = this.connection; + const dataConnection = (this.connection); const dataChannel = dataConnection.dataChannel; if (dataChannel) { @@ -230,7 +229,7 @@ export class Negotiator { }; if (this.connection.type === ConnectionType.Data) { - const dataConnection = this.connection; + const dataConnection = (this.connection); payload = { ...payload, diff --git a/lib/peer.ts b/lib/peer.ts index 60e49567f..37771bbca 100644 --- a/lib/peer.ts +++ b/lib/peer.ts @@ -10,7 +10,6 @@ import { SocketEventType, ServerMessageType, } from "./enums"; -import { BaseConnection } from "./baseconnection"; import { ServerMessage } from "./servermessage"; import { API } from "./api"; import type { @@ -76,7 +75,10 @@ export class Peer extends EventEmitter { private _destroyed = false; // Connections have been killed private _disconnected = false; // Connection to PeerServer killed but P2P connections still active private _open = false; // Sockets and such are not yet open. - private readonly _connections: Map = new Map(); // All connections for this peer. + private readonly _connections: Map< + string, + (DataConnection | MediaConnection)[] + > = new Map(); // All connections for this peer. private readonly _lostMessages: Map = new Map(); // src => [list of messages] /** * The brokering ID of this peer @@ -471,7 +473,10 @@ export class Peer extends EventEmitter { } /** Add a data/media connection to this peer. */ - private _addConnection(peerId: string, connection: BaseConnection): void { + private _addConnection( + peerId: string, + connection: MediaConnection | DataConnection, + ): void { logger.log( `add connection ${connection.type}:${connection.connectionId} to peerId:${peerId}`, ); @@ -483,7 +488,7 @@ export class Peer extends EventEmitter { } //TODO should be private - _removeConnection(connection: BaseConnection): void { + _removeConnection(connection: DataConnection | MediaConnection): void { const connections = this._connections.get(connection.peer); if (connections) { @@ -499,7 +504,10 @@ export class Peer extends EventEmitter { } /** Retrieve a data/media connection for this peer. */ - getConnection(peerId: string, connectionId: string): null | BaseConnection { + getConnection( + peerId: string, + connectionId: string, + ): null | DataConnection | MediaConnection { const connections = this._connections.get(peerId); if (!connections) { return null; diff --git a/test/peer.ts b/test/peer.ts index 82938d129..86c75aef9 100644 --- a/test/peer.ts +++ b/test/peer.ts @@ -2,12 +2,7 @@ import "./setup"; import { expect } from "chai"; import { Peer } from "../lib/peer"; import { Server } from "mock-socket"; -import { - ConnectionType, - ServerMessageType, - PeerErrorType, - PeerEventType, -} from "../lib/enums"; +import { ConnectionType, ServerMessageType } from "../lib/enums"; const createMockServer = (): Server => { const fakeURL = "ws://localhost:8080/peerjs?key=peerjs&id=1&token=testToken"; @@ -202,10 +197,10 @@ describe("Peer", function () { const peer1 = new Peer({ port: 8080, host: "localhost" }); - peer1.once(PeerEventType.Error, (error) => { - expect(error.type).to.be.eq(PeerErrorType.ServerError); + peer1.once("error", (_error) => { + // expect(error.type).to.be.eq(PeerErrorType.ServerError); - peer1.once(PeerEventType.Close, () => { + peer1.once("close", () => { expect(peer1.disconnected).to.be.true; expect(peer1.destroyed).to.be.true; expect(peer1.open).to.be.false;