Răsfoiți Sursa

feat: `PeerError` from connections

Jonas Gloning 1 an în urmă
părinte
comite
ad3a0cbe8c

+ 18 - 6
lib/baseconnection.ts

@@ -1,9 +1,17 @@
-import { EventEmitter, type ValidEventTypes } from "eventemitter3";
 import type { Peer } from "./peer";
 import type { ServerMessage } from "./servermessage";
 import type { ConnectionType } from "./enums";
+import { BaseConnectionErrorType } from "./enums";
+import {
+	EventEmitterWithError,
+	type EventsWithError,
+	PeerError,
+} from "./peerError";
+import type { ValidEventTypes } from "eventemitter3";
 
-export type BaseConnectionEvents = {
+export interface BaseConnectionEvents<
+	ErrorType extends string = BaseConnectionErrorType,
+> extends EventsWithError<ErrorType> {
 	/**
 	 * Emitted when either you or the remote peer closes the connection.
 	 *
@@ -17,13 +25,17 @@ export type BaseConnectionEvents = {
 	 * connection.on('error', (error) => { ... });
 	 * ```
 	 */
-	error: (error: Error) => void;
+	error: (error: PeerError<`${ErrorType}`>) => void;
 	iceStateChanged: (state: RTCIceConnectionState) => void;
-};
+}
 
 export abstract class BaseConnection<
-	T extends ValidEventTypes,
-> extends EventEmitter<T & BaseConnectionEvents> {
+	SubClassEvents extends ValidEventTypes,
+	ErrorType extends string = never,
+> extends EventEmitterWithError<
+	ErrorType | BaseConnectionErrorType,
+	SubClassEvents & BaseConnectionEvents<BaseConnectionErrorType | ErrorType>
+> {
 	protected _open = false;
 
 	/**

+ 5 - 2
lib/dataconnection/BufferedConnection/Json.ts

@@ -1,5 +1,5 @@
 import { BufferedConnection } from "./BufferedConnection";
-import { SerializationType } from "../../enums";
+import { DataConnectionErrorType, SerializationType } from "../../enums";
 import { util } from "../../util";
 
 export class Json extends BufferedConnection {
@@ -27,7 +27,10 @@ export class Json extends BufferedConnection {
 	override _send(data, _chunked) {
 		const encodedData = this.encoder.encode(this.stringify(data));
 		if (encodedData.byteLength >= util.chunkedMTU) {
-			this.emit("error", new Error("Message too big for JSON channel"));
+			this.emitError(
+				DataConnectionErrorType.MessageToBig,
+				"Message too big for JSON channel",
+			);
 			return;
 		}
 		this._bufferedSend(encodedData);

+ 19 - 15
lib/dataconnection/DataConnection.ts

@@ -1,13 +1,20 @@
 import logger from "../logger";
 import { Negotiator } from "../negotiator";
-import { ConnectionType, ServerMessageType } from "../enums";
+import {
+	BaseConnectionErrorType,
+	ConnectionType,
+	DataConnectionErrorType,
+	ServerMessageType,
+} from "../enums";
 import type { Peer } from "../peer";
-import { BaseConnection } from "../baseconnection";
+import { BaseConnection, type BaseConnectionEvents } from "../baseconnection";
 import type { ServerMessage } from "../servermessage";
-import type { DataConnection as IDataConnection } from "./DataConnection";
+import type { EventsWithError } from "../peerError";
 import { randomToken } from "../utils/randomToken";
 
-type DataConnectionEvents = {
+export interface DataConnectionEvents
+	extends EventsWithError<DataConnectionErrorType | BaseConnectionErrorType>,
+		BaseConnectionEvents<DataConnectionErrorType | BaseConnectionErrorType> {
 	/**
 	 * Emitted when data is received from the remote peer.
 	 */
@@ -16,15 +23,15 @@ type DataConnectionEvents = {
 	 * Emitted when the connection is established and ready-to-use.
 	 */
 	open: () => void;
-};
+}
 
 /**
  * Wraps a DataChannel between two Peers.
  */
-export abstract class DataConnection
-	extends BaseConnection<DataConnectionEvents>
-	implements IDataConnection
-{
+export abstract class DataConnection extends BaseConnection<
+	DataConnectionEvents,
+	DataConnectionErrorType
+> {
 	protected static readonly ID_PREFIX = "dc_";
 	protected static readonly MAX_BUFFERED_AMOUNT = 8 * 1024 * 1024;
 
@@ -32,7 +39,6 @@ export abstract class DataConnection
 	abstract readonly serialization: string;
 	readonly reliable: boolean;
 
-	// public type: ConnectionType.Data;
 	public get type() {
 		return ConnectionType.Data;
 	}
@@ -123,11 +129,9 @@ export abstract class DataConnection
 	/** Allows user to send data. */
 	public send(data: any, chunked = false) {
 		if (!this.open) {
-			super.emit(
-				"error",
-				new Error(
-					"Connection is not open. You should listen for the `open` event before sending messages.",
-				),
+			this.emitError(
+				DataConnectionErrorType.NotOpenYet,
+				"Connection is not open. You should listen for the `open` event before sending messages.",
 			);
 			return;
 		}

+ 10 - 0
lib/enums.ts

@@ -60,6 +60,16 @@ export enum PeerErrorType {
 	WebRTC = "webrtc",
 }
 
+export enum BaseConnectionErrorType {
+	NegotiationFailed = "negotiation-failed",
+	ConnectionClosed = "connection-closed",
+}
+
+export enum DataConnectionErrorType {
+	NotOpenYet = "not-open-yet",
+	MessageToBig = "message-too-big",
+}
+
 export enum SerializationType {
 	Binary = "binary",
 	BinaryUTF8 = "binary-utf8",

+ 3 - 8
lib/exports.ts

@@ -3,7 +3,7 @@ import { Peer } from "./peer";
 import { CborPeer } from "./cborPeer";
 import { MsgPackPeer } from "./msgPackPeer";
 
-export type { PeerEvents, PeerError, PeerOptions } from "./peer";
+export type { PeerEvents, PeerOptions } from "./peer";
 
 export type {
 	PeerJSOption,
@@ -15,13 +15,7 @@ export type { UtilSupportsObj } from "./util";
 export type { DataConnection } from "./dataconnection/DataConnection";
 export type { MediaConnection } from "./mediaconnection";
 export type { LogLevel } from "./logger";
-export type {
-	ConnectionType,
-	PeerErrorType,
-	SerializationType,
-	SocketEventType,
-	ServerMessageType,
-} from "./enums";
+export * from "./enums";
 
 export { BufferedConnection } from "./dataconnection/BufferedConnection/BufferedConnection";
 export { StreamConnection } from "./dataconnection/StreamConnection/StreamConnection";
@@ -31,4 +25,5 @@ export type { SerializerMapping } from "./peer";
 
 export { Peer, MsgPackPeer, CborPeer };
 
+export { PeerError } from "./peerError";
 export default Peer;

+ 4 - 4
lib/mediaconnection.ts

@@ -3,11 +3,11 @@ import logger from "./logger";
 import { Negotiator } from "./negotiator";
 import { ConnectionType, ServerMessageType } from "./enums";
 import type { Peer } from "./peer";
-import { BaseConnection } from "./baseconnection";
+import { BaseConnection, type BaseConnectionEvents } from "./baseconnection";
 import type { ServerMessage } from "./servermessage";
 import type { AnswerOption } from "./optionInterfaces";
 
-export type MediaConnectionEvents = {
+export interface MediaConnectionEvents extends BaseConnectionEvents<never> {
 	/**
 	 * Emitted when a connection to the PeerServer is established.
 	 *
@@ -22,7 +22,7 @@ export type MediaConnectionEvents = {
 	 * @beta
 	 */
 	willCloseOnRemote: () => void;
-};
+}
 
 /**
  * Wraps WebRTC's media streams.
@@ -32,7 +32,7 @@ export class MediaConnection extends BaseConnection<MediaConnectionEvents> {
 	private static readonly ID_PREFIX = "mc_";
 	readonly label: string;
 
-	private _negotiator: Negotiator<MediaConnectionEvents, MediaConnection>;
+	private _negotiator: Negotiator<MediaConnectionEvents, this>;
 	private _localStream: MediaStream;
 	private _remoteStream: MediaStream;
 

+ 15 - 10
lib/negotiator.ts

@@ -1,7 +1,12 @@
 import logger from "./logger";
 import type { MediaConnection } from "./mediaconnection";
 import type { DataConnection } from "./dataconnection/DataConnection";
-import { ConnectionType, PeerErrorType, ServerMessageType } from "./enums";
+import {
+	BaseConnectionErrorType,
+	ConnectionType,
+	PeerErrorType,
+	ServerMessageType,
+} from "./enums";
 import type { BaseConnection, BaseConnectionEvents } from "./baseconnection";
 import type { ValidEventTypes } from "eventemitter3";
 
@@ -9,10 +14,10 @@ import type { ValidEventTypes } from "eventemitter3";
  * Manages all negotiations between Peers.
  */
 export class Negotiator<
-	A extends ValidEventTypes,
-	T extends BaseConnection<A | BaseConnectionEvents>,
+	Events extends ValidEventTypes,
+	ConnectionType extends BaseConnection<Events | BaseConnectionEvents>,
 > {
-	constructor(readonly connection: T) {}
+	constructor(readonly connection: ConnectionType) {}
 
 	/** Returns a PeerConnection object set up correctly (for data, media). */
 	startConnection(options: any) {
@@ -88,9 +93,9 @@ export class Negotiator<
 					logger.log(
 						"iceConnectionState is failed, closing connections to " + peerId,
 					);
-					this.connection.emit(
-						"error",
-						new Error("Negotiation of connection to " + peerId + " failed."),
+					this.connection.emitError(
+						BaseConnectionErrorType.NegotiationFailed,
+						"Negotiation of connection to " + peerId + " failed.",
 					);
 					this.connection.close();
 					break;
@@ -98,9 +103,9 @@ export class Negotiator<
 					logger.log(
 						"iceConnectionState is closed, closing connections to " + peerId,
 					);
-					this.connection.emit(
-						"error",
-						new Error("Connection to " + peerId + " closed."),
+					this.connection.emitError(
+						BaseConnectionErrorType.ConnectionClosed,
+						"Connection to " + peerId + " closed.",
 					);
 					this.connection.close();
 					break;

+ 8 - 28
lib/peer.ts

@@ -1,4 +1,3 @@
-import { EventEmitter } from "eventemitter3";
 import { util } from "./util";
 import logger, { LogLevel } from "./logger";
 import { Socket } from "./socket";
@@ -21,6 +20,8 @@ import { BinaryPack } from "./dataconnection/BufferedConnection/BinaryPack";
 import { Raw } from "./dataconnection/BufferedConnection/Raw";
 import { Json } from "./dataconnection/BufferedConnection/Json";
 
+import { EventEmitterWithError, PeerError } from "./peerError";
+
 class PeerOptions implements PeerJSOption {
 	/**
 	 * Prints log messages depending on the debug level passed in.
@@ -66,21 +67,7 @@ class PeerOptions implements PeerJSOption {
 	serializers?: SerializerMapping;
 }
 
-class PeerError extends Error {
-	constructor(type: PeerErrorType, err: Error | string) {
-		if (typeof err === "string") {
-			super(err);
-		} else {
-			super();
-			Object.assign(this, err);
-		}
-
-		this.type = type;
-	}
-
-	type: PeerErrorType;
-}
-export type { PeerError, PeerOptions };
+export { type PeerOptions };
 
 export type SerializerMapping = {
 	[key: string]: new (
@@ -90,7 +77,7 @@ export type SerializerMapping = {
 	) => DataConnection;
 };
 
-export type PeerEvents = {
+export interface PeerEvents {
 	/**
 	 * Emitted when a connection to the PeerServer is established.
 	 *
@@ -118,12 +105,12 @@ export type PeerEvents = {
 	 *
 	 * Errors from the underlying socket and PeerConnections are forwarded here.
 	 */
-	error: (error: PeerError) => void;
-};
+	error: (error: PeerError<`${PeerErrorType}`>) => void;
+}
 /**
  * A peer who can initiate connections with other peers.
  */
-export class Peer extends EventEmitter<PeerEvents> {
+export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
 	private static readonly DEFAULT_KEY = "peerjs";
 
 	protected readonly _serializers: SerializerMapping = {
@@ -500,7 +487,7 @@ export class Peer extends EventEmitter<PeerEvents> {
 	 * @param peer The brokering ID of the remote peer (their {@apilink Peer.id}).
 	 * @param options for specifying details about Peer Connection
 	 */
-	connect(peer: string, options: PeerConnectOption): DataConnection {
+	connect(peer: string, options: PeerConnectOption = {}): DataConnection {
 		options = {
 			serialization: "default",
 			...options,
@@ -640,13 +627,6 @@ export class Peer extends EventEmitter<PeerEvents> {
 		}
 	}
 
-	/** Emits a typed error message. */
-	emitError(type: PeerErrorType, err: string | Error): void {
-		logger.error("Error:", err);
-
-		this.emit("error", new PeerError(type, err));
-	}
-
 	/**
 	 * Destroys the Peer: closes all active connections as well as the connection
 	 * to the server.

+ 44 - 0
lib/peerError.ts

@@ -0,0 +1,44 @@
+import { EventEmitter } from "eventemitter3";
+import logger from "./logger";
+
+export interface EventsWithError<ErrorType extends string> {
+	error: (error: PeerError<`${ErrorType}`>) => void;
+}
+
+export class EventEmitterWithError<
+	ErrorType extends string,
+	Events extends EventsWithError<ErrorType>,
+> extends EventEmitter<Events, never> {
+	/**
+	 * Emits a typed error message.
+	 *
+	 * @internal
+	 */
+	emitError(type: ErrorType, err: string | Error): void {
+		logger.error("Error:", err);
+
+		// @ts-ignore
+		this.emit("error", new PeerError<`${ErrorType}`>(`${type}`, err));
+	}
+}
+/**
+ * A PeerError is emitted whenever an error occurs.
+ * It always has a `.type`, which can be used to identify the error.
+ */
+export class PeerError<T extends string> extends Error {
+	/**
+	 * @internal
+	 */
+	constructor(type: T, err: Error | string) {
+		if (typeof err === "string") {
+			super(err);
+		} else {
+			super();
+			Object.assign(this, err);
+		}
+
+		this.type = type;
+	}
+
+	public type: T;
+}