Skip to content

Commit 5a16d82

Browse files
committed
feat: await new Peer()
1 parent 59cea73 commit 5a16d82

File tree

3 files changed

+144
-53
lines changed

3 files changed

+144
-53
lines changed

e2e/peer/id-taken.await.html

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title></title>
7+
<link rel="stylesheet" href="../style.css" />
8+
</head>
9+
<body>
10+
<h1>ID-TAKEN</h1>
11+
<div id="messages"></div>
12+
<div id="error-message"></div>
13+
<script src="/dist/peerjs.js"></script>
14+
<script type="application/javascript">
15+
(async () => {
16+
/**
17+
* @type {typeof import("../..").Peer}
18+
*/
19+
const Peer = window.peerjs.Peer;
20+
21+
const messages = document.getElementById("messages");
22+
const errorMessage = document.getElementById("error-message");
23+
24+
// Peer A should be created without an error
25+
try {
26+
const peerA = await new Peer();
27+
// Create 10 new `Peer`s that will try to steel A's id
28+
let peers_try_to_take = Array.from({ length: 10 }, async (_, i) => {
29+
try {
30+
await new Peer(peerA.id);
31+
throw `Peer ${i} failed! Connection got established.`;
32+
} catch (error) {
33+
if (error.type === "unavailable-id") {
34+
return `ID already taken. (${i})`;
35+
} else {
36+
throw error;
37+
}
38+
}
39+
});
40+
await Promise.all(peers_try_to_take);
41+
messages.textContent = "No ID takeover";
42+
} catch (error) {
43+
errorMessage.textContent += JSON.stringify(error);
44+
}
45+
})();
46+
</script>
47+
</body>
48+
</html>

e2e/peer/peer.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,10 @@ describe("Peer", () => {
1818
expect(await P.errorMessage.getText()).toBe("");
1919
});
2020
});
21+
describe("Peer:async", () => {
22+
it("should emit an error, when the ID is already taken", async () => {
23+
await P.open("id-taken.await");
24+
await P.waitForMessage("No ID takeover");
25+
expect(await P.errorMessage.getText()).toBe("");
26+
});
27+
});

lib/peer.ts

Lines changed: 89 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,79 @@ export interface PeerEvents {
107107
*/
108108
error: (error: PeerError<`${PeerErrorType}`>) => void;
109109
}
110+
111+
export interface IPeer {
112+
/**
113+
* The brokering ID of this peer
114+
*
115+
* If no ID was specified in {@apilink Peer | the constructor},
116+
* this will be `undefined` until the {@apilink PeerEvents | `open`} event is emitted.
117+
*/
118+
get id(): string;
119+
get open(): boolean;
120+
/**
121+
* A hash of all connections associated with this peer, keyed by the remote peer's ID.
122+
* @deprecated
123+
* Return type will change from Object to Map<string,[]>
124+
*/
125+
get connections(): Object;
126+
/**
127+
* true if this peer and all of its connections can no longer be used.
128+
*/
129+
get destroyed(): boolean;
130+
/**
131+
* Connects to the remote peer specified by id and returns a data connection.
132+
* @param peer The brokering ID of the remote peer (their {@apilink Peer.id}).
133+
* @param options for specifying details about Peer Connection
134+
*/
135+
connect(peer: string, options: PeerConnectOption): DataConnection;
136+
/**
137+
* Calls the remote peer specified by id and returns a media connection.
138+
* @param peer The brokering ID of the remote peer (their peer.id).
139+
* @param stream The caller's media stream
140+
* @param options Metadata associated with the connection, passed in by whoever initiated the connection.
141+
*/
142+
call(peer: string, stream: MediaStream, options: CallOption): MediaConnection;
143+
/** Retrieve a data/media connection for this peer. */
144+
getConnection(
145+
peerId: string,
146+
connectionId: string,
147+
): null | DataConnection | MediaConnection;
148+
/**
149+
* Destroys the Peer: closes all active connections as well as the connection
150+
* to the server.
151+
*
152+
* :::caution
153+
* This cannot be undone; the respective peer object will no longer be able
154+
* to create or receive any connections, its ID will be forfeited on the server,
155+
* and all of its data and media connections will be closed.
156+
* :::
157+
*/
158+
destroy(): void;
159+
/**
160+
* Disconnects the Peer's connection to the PeerServer. Does not close any
161+
* active connections.
162+
* Warning: The peer can no longer create or accept connections after being
163+
* disconnected. It also cannot reconnect to the server.
164+
*/
165+
disconnect(): void;
166+
/** Attempts to reconnect with the same ID.
167+
*
168+
* Only {@apilink Peer.disconnect | disconnected peers} can be reconnected.
169+
* Destroyed peers cannot be reconnected.
170+
* If the connection fails (as an example, if the peer's old ID is now taken),
171+
* the peer's existing connections will not close, but any associated errors events will fire.
172+
*/
173+
reconnect(): void;
174+
}
175+
110176
/**
111177
* A peer who can initiate connections with other peers.
112178
*/
113-
export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
179+
export class Peer
180+
extends EventEmitterWithError<PeerErrorType, PeerEvents>
181+
implements IPeer
182+
{
114183
private static readonly DEFAULT_KEY = "peerjs";
115184

116185
protected readonly _serializers: SerializerMapping = {
@@ -137,12 +206,11 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
137206
(DataConnection | MediaConnection)[]
138207
> = new Map(); // All connections for this peer.
139208
private readonly _lostMessages: Map<string, ServerMessage[]> = new Map(); // src => [list of messages]
140-
/**
141-
* The brokering ID of this peer
142-
*
143-
* If no ID was specified in {@apilink Peer | the constructor},
144-
* this will be `undefined` until the {@apilink PeerEvents | `open`} event is emitted.
145-
*/
209+
private then: (
210+
onfulfilled?: (value: IPeer) => any,
211+
onrejected?: (reason: PeerError<PeerErrorType>) => any,
212+
) => void;
213+
146214
get id() {
147215
return this._id;
148216
}
@@ -162,11 +230,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
162230
return this._socket;
163231
}
164232

165-
/**
166-
* A hash of all connections associated with this peer, keyed by the remote peer's ID.
167-
* @deprecated
168-
* Return type will change from Object to Map<string,[]>
169-
*/
170233
get connections(): Object {
171234
const plainConnections = Object.create(null);
172235

@@ -177,15 +240,9 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
177240
return plainConnections;
178241
}
179242

180-
/**
181-
* true if this peer and all of its connections can no longer be used.
182-
*/
183243
get destroyed() {
184244
return this._destroyed;
185245
}
186-
/**
187-
* false if there is an active connection to the PeerServer.
188-
*/
189246
get disconnected() {
190247
return this._disconnected;
191248
}
@@ -213,6 +270,20 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
213270
constructor(id?: string | PeerOptions, options?: PeerOptions) {
214271
super();
215272

273+
this.then = (
274+
onfulfilled?: (value: IPeer) => any,
275+
onrejected?: (reason: PeerError<PeerErrorType>) => any,
276+
) => {
277+
// Remove 'then' to prevent potential recursion issues
278+
// `await` will wait for a Promise-like to resolve recursively
279+
delete this.then;
280+
281+
// We don’t need to worry about cleaning up listeners here
282+
// `await`ing a Promise will make sure only one of the paths executes
283+
this.once("open", () => onfulfilled(this));
284+
this.once("error", onrejected);
285+
};
286+
216287
let userId: string | undefined;
217288

218289
// Deal with overloading
@@ -482,11 +553,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
482553
return [];
483554
}
484555

485-
/**
486-
* Connects to the remote peer specified by id and returns a data connection.
487-
* @param peer The brokering ID of the remote peer (their {@apilink Peer.id}).
488-
* @param options for specifying details about Peer Connection
489-
*/
490556
connect(peer: string, options: PeerConnectOption = {}): DataConnection {
491557
options = {
492558
serialization: "default",
@@ -515,12 +581,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
515581
return dataConnection;
516582
}
517583

518-
/**
519-
* Calls the remote peer specified by id and returns a media connection.
520-
* @param peer The brokering ID of the remote peer (their peer.id).
521-
* @param stream The caller's media stream
522-
* @param options Metadata associated with the connection, passed in by whoever initiated the connection.
523-
*/
524584
call(
525585
peer: string,
526586
stream: MediaStream,
@@ -585,7 +645,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
585645
this._lostMessages.delete(connection.connectionId);
586646
}
587647

588-
/** Retrieve a data/media connection for this peer. */
589648
getConnection(
590649
peerId: string,
591650
connectionId: string,
@@ -627,16 +686,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
627686
}
628687
}
629688

630-
/**
631-
* Destroys the Peer: closes all active connections as well as the connection
632-
* to the server.
633-
*
634-
* :::caution
635-
* This cannot be undone; the respective peer object will no longer be able
636-
* to create or receive any connections, its ID will be forfeited on the server,
637-
* and all of its data and media connections will be closed.
638-
* :::
639-
*/
640689
destroy(): void {
641690
if (this.destroyed) {
642691
return;
@@ -673,12 +722,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
673722
}
674723
}
675724

676-
/**
677-
* Disconnects the Peer's connection to the PeerServer. Does not close any
678-
* active connections.
679-
* Warning: The peer can no longer create or accept connections after being
680-
* disconnected. It also cannot reconnect to the server.
681-
*/
682725
disconnect(): void {
683726
if (this.disconnected) {
684727
return;
@@ -699,13 +742,6 @@ export class Peer extends EventEmitterWithError<PeerErrorType, PeerEvents> {
699742
this.emit("disconnected", currentId);
700743
}
701744

702-
/** Attempts to reconnect with the same ID.
703-
*
704-
* Only {@apilink Peer.disconnect | disconnected peers} can be reconnected.
705-
* Destroyed peers cannot be reconnected.
706-
* If the connection fails (as an example, if the peer's old ID is now taken),
707-
* the peer's existing connections will not close, but any associated errors events will fire.
708-
*/
709745
reconnect(): void {
710746
if (this.disconnected && !this.destroyed) {
711747
logger.log(

0 commit comments

Comments
 (0)