Skip to content
48 changes: 48 additions & 0 deletions e2e/peer/id-taken.await.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="../style.css" />
</head>
<body>
<h1>ID-TAKEN</h1>
<div id="messages"></div>
<div id="error-message"></div>
<script src="/dist/peerjs.js"></script>
<script type="application/javascript">
(async () => {
/**
* @type {typeof import("../../lib/exports.ts").Peer}
*/
const Peer = window.peerjs.Peer;

const messages = document.getElementById("messages");
const errorMessage = document.getElementById("error-message");

// Peer A should be created without an error
const peerA = await new Peer().catch(
(error) => (errorMessage.textContent += JSON.stringify(error)),
);

// Create 10 new `Peer`s that will try to steel A's id
// Wait for all peers to finish
const steeling_peers = await Promise.allSettled(
Array.from({ length: 10 }, () => new Peer(peerA.id)),
);

if (
steeling_peers.every(
({ reason, status }) =>
status === "rejected" && reason.type === "unavailable-id",
)
) {
messages.textContent = "No ID takeover";
} else {
errorMessage.textContent += JSON.stringify(steeling_peers);
}
})();
</script>
</body>
</html>
4 changes: 2 additions & 2 deletions e2e/peer/id-taken.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ <h1>ID-TAKEN</h1>
)
.once("open", (id) => {
// Create 10 new `Peer`s that will try to steel A's id
let peers_try_to_take = Array.from(
const steeling_peers = Array.from(
{ length: 10 },
(_, i) =>
new Promise((resolve, reject) =>
Expand All @@ -45,7 +45,7 @@ <h1>ID-TAKEN</h1>
}),
),
);
Promise.all(peers_try_to_take)
Promise.all(steeling_peers)
.then(() => (messages.textContent = "No ID takeover"))
.catch(
(error) => (errorMessage.textContent += JSON.stringify(error)),
Expand Down
41 changes: 41 additions & 0 deletions e2e/peer/peer-unavailable.async.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="../style.css" />
</head>
<body>
<h1>PEER-UNAVAILABLE</h1>
<div id="messages"></div>
<div id="error-message"></div>
<script src="/dist/peerjs.js"></script>
<script type="application/javascript">
(async () => {
/**
* @type {typeof import("../../lib/exports.ts").Peer}
*/
const Peer = window.peerjs.Peer;

const messages = document.getElementById("messages");
const errors = document.getElementById("error-message");

const not_existing_peer = crypto
.getRandomValues(new Uint8Array(16))
.join("");

try {
const peer = await new Peer();
await peer.connect(not_existing_peer);
} catch (error) {
if (error.type === "peer-unavailable") {
messages.textContent = "Success: Peer unavailable";
} else {
errors.textContent += JSON.stringify(error);
}
}
})();
</script>
</body>
</html>
43 changes: 43 additions & 0 deletions e2e/peer/peer-unavailable.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<link rel="stylesheet" href="../style.css" />
</head>
<body>
<h1>PEER-UNAVAILABLE</h1>
<div id="messages"></div>
<div id="error-message"></div>
<script src="/dist/peerjs.js"></script>
<script type="application/javascript">
/**
* @type {typeof import("../..").Peer}
*/
const Peer = window.peerjs.Peer;

const connectionErrors = document.getElementById("messages");
const peerErrors = document.getElementById("error-message");

const not_existing_peer = crypto
.getRandomValues(new Uint8Array(16))
.join("");

const peer = new Peer();
peer
.once(
"error",
(error) => void (peerErrors.textContent += JSON.stringify(error)),
)
.once("open", (id) => {
const connection = peer.connect(not_existing_peer);
connection.once(
"error",
(error) =>
void (connectionErrors.textContent += JSON.stringify(error)),
);
});
</script>
</body>
</html>
17 changes: 17 additions & 0 deletions e2e/peer/peer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,21 @@ describe("Peer", () => {
await P.waitForMessage('{"type":"disconnected"}');
expect(await P.errorMessage.getText()).toBe("");
});
it("should emit an error, when the remote peer is unavailable", async () => {
await P.open("peer-unavailable");
await P.waitForMessage('{"type":"peer-unavailable"}');
expect(await P.errorMessage.getText()).toBe('{"type":"peer-unavailable"}');
});
});
describe("Peer:async", () => {
it("should emit an error, when the ID is already taken", async () => {
await P.open("id-taken.await");
await P.waitForMessage("No ID takeover");
expect(await P.errorMessage.getText()).toBe("");
});
it("should emit an error, when the remote peer is unavailable", async () => {
await P.open("peer-unavailable.async");
await P.waitForMessage("Success: Peer unavailable");
expect(await P.errorMessage.getText()).toBe("");
});
});
59 changes: 39 additions & 20 deletions lib/baseconnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,13 @@ 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";
import { PeerError, type PromiseEvents } from "./peerError";
import EventEmitter from "eventemitter3";
import { EventEmitterWithPromise } from "./eventEmitterWithPromise";

export interface BaseConnectionEvents<
ErrorType extends string = BaseConnectionErrorType,
> extends EventsWithError<ErrorType> {
> extends PromiseEvents<never, ErrorType> {
/**
* Emitted when either you or the remote peer closes the connection.
*
Expand All @@ -29,13 +26,42 @@ export interface BaseConnectionEvents<
iceStateChanged: (state: RTCIceConnectionState) => void;
}

export abstract class BaseConnection<
SubClassEvents extends ValidEventTypes,
export interface IBaseConnection<
SubClassEvents extends BaseConnectionEvents<
BaseConnectionErrorType | ErrorType
>,
ErrorType extends string = never,
> extends EventEmitterWithError<
ErrorType | BaseConnectionErrorType,
SubClassEvents & BaseConnectionEvents<BaseConnectionErrorType | ErrorType>
> {
> extends EventEmitter<SubClassEvents> {
readonly metadata: any;
readonly connectionId: string;
get type(): ConnectionType;
/**
* The optional label passed in or assigned by PeerJS when the connection was initiated.
*/
label: string;
/**
* Whether the media connection is active (e.g. your call has been answered).
* You can check this if you want to set a maximum wait time for a one-sided call.
*/
get open(): boolean;
close(): void;
}

export abstract class BaseConnection<
AwaitType extends EventEmitter<SubClassEvents>,
SubClassEvents extends BaseConnectionEvents<
BaseConnectionErrorType | ErrorType
>,
ErrorType extends string = never,
>
extends EventEmitterWithPromise<
AwaitType,
never,
ErrorType | BaseConnectionErrorType,
SubClassEvents
>
implements IBaseConnection<SubClassEvents, ErrorType>
{
protected _open = false;

/**
Expand All @@ -50,15 +76,8 @@ export abstract class BaseConnection<

abstract get type(): ConnectionType;

/**
* The optional label passed in or assigned by PeerJS when the connection was initiated.
*/
label: string;

/**
* Whether the media connection is active (e.g. your call has been answered).
* You can check this if you want to set a maximum wait time for a one-sided call.
*/
get open() {
return this._open;
}
Expand Down
32 changes: 24 additions & 8 deletions lib/dataconnection/DataConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import {
ServerMessageType,
} from "../enums";
import type { Peer } from "../peer";
import { BaseConnection, type BaseConnectionEvents } from "../baseconnection";
import {
BaseConnection,
type BaseConnectionEvents,
IBaseConnection,
} from "../baseconnection";
import type { ServerMessage } from "../servermessage";
import type { EventsWithError } from "../peerError";
import { randomToken } from "../utils/randomToken";

export interface DataConnectionEvents
extends EventsWithError<DataConnectionErrorType | BaseConnectionErrorType>,
BaseConnectionEvents<DataConnectionErrorType | BaseConnectionErrorType> {
extends BaseConnectionEvents<
DataConnectionErrorType | BaseConnectionErrorType
> {
/**
* Emitted when data is received from the remote peer.
*/
Expand All @@ -25,21 +29,35 @@ export interface DataConnectionEvents
open: () => void;
}

export interface IDataConnection
extends IBaseConnection<DataConnectionEvents, DataConnectionErrorType> {
get type(): ConnectionType.Data;
/** Allows user to close connection. */
close(options?: { flush?: boolean }): void;
/** Allows user to send data. */
send(data: any, chunked?: boolean): void;
}

/**
* Wraps a DataChannel between two Peers.
*/
export abstract class DataConnection extends BaseConnection<
IDataConnection,
DataConnectionEvents,
DataConnectionErrorType
> {
protected static readonly ID_PREFIX = "dc_";
protected static readonly MAX_BUFFERED_AMOUNT = 8 * 1024 * 1024;

private _negotiator: Negotiator<DataConnectionEvents, this>;
private _negotiator: Negotiator<
DataConnectionEvents,
DataConnectionErrorType,
this
>;
abstract readonly serialization: string;
readonly reliable: boolean;

public get type() {
public get type(): ConnectionType.Data {
return ConnectionType.Data;
}

Expand Down Expand Up @@ -87,7 +105,6 @@ export abstract class DataConnection extends BaseConnection<
* Exposed functionality for users.
*/

/** Allows user to close connection. */
close(options?: { flush?: boolean }): void {
if (options?.flush) {
this.send({
Expand Down Expand Up @@ -126,7 +143,6 @@ export abstract class DataConnection extends BaseConnection<

protected abstract _send(data: any, chunked: boolean): void;

/** Allows user to send data. */
public send(data: any, chunked = false) {
if (!this.open) {
this.emitError(
Expand Down
1 change: 1 addition & 0 deletions lib/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ export enum PeerErrorType {
}

export enum BaseConnectionErrorType {
PeerUnavailable = "peer-unavailable",
NegotiationFailed = "negotiation-failed",
ConnectionClosed = "connection-closed",
}
Expand Down
Loading