@appsurify-testmap/rrweb-plugin-canvas-webrtc-record 2.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1154 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ /*! simple-peer. MIT License. Feross Aboukhadijeh <https://feross.org/opensource> */
5
+ const MAX_BUFFERED_AMOUNT = 64 * 1024;
6
+ const ICECOMPLETE_TIMEOUT = 5 * 1e3;
7
+ const CHANNEL_CLOSING_TIMEOUT = 5 * 1e3;
8
+ function randombytes(size) {
9
+ const array = new Uint8Array(size);
10
+ for (let i = 0; i < size; i++) {
11
+ array[i] = Math.random() * 256 | 0;
12
+ }
13
+ return array;
14
+ }
15
+ function getBrowserRTC() {
16
+ if (typeof globalThis === "undefined") return null;
17
+ const wrtc = {
18
+ RTCPeerConnection: globalThis.RTCPeerConnection || globalThis.mozRTCPeerConnection || globalThis.webkitRTCPeerConnection,
19
+ RTCSessionDescription: globalThis.RTCSessionDescription || globalThis.mozRTCSessionDescription || globalThis.webkitRTCSessionDescription,
20
+ RTCIceCandidate: globalThis.RTCIceCandidate || globalThis.mozRTCIceCandidate || globalThis.webkitRTCIceCandidate
21
+ };
22
+ if (!wrtc.RTCPeerConnection) return null;
23
+ return wrtc;
24
+ }
25
+ function errCode(err, code) {
26
+ Object.defineProperty(err, "code", {
27
+ value: code,
28
+ enumerable: true,
29
+ configurable: true
30
+ });
31
+ return err;
32
+ }
33
+ function filterTrickle(sdp) {
34
+ return sdp.replace(/a=ice-options:trickle\s\n/g, "");
35
+ }
36
+ function warn(message) {
37
+ console.warn(message);
38
+ }
39
+ class Peer {
40
+ constructor(opts = {}) {
41
+ this._map = /* @__PURE__ */ new Map();
42
+ this._id = randombytes(4).toString("hex").slice(0, 7);
43
+ this._doDebug = opts.debug;
44
+ this._debug("new peer %o", opts);
45
+ this.channelName = opts.initiator ? opts.channelName || randombytes(20).toString("hex") : null;
46
+ this.initiator = opts.initiator || false;
47
+ this.channelConfig = opts.channelConfig || Peer.channelConfig;
48
+ this.channelNegotiated = this.channelConfig.negotiated;
49
+ this.config = Object.assign({}, Peer.config, opts.config);
50
+ this.offerOptions = opts.offerOptions || {};
51
+ this.answerOptions = opts.answerOptions || {};
52
+ this.sdpTransform = opts.sdpTransform || ((sdp) => sdp);
53
+ this.streams = opts.streams || (opts.stream ? [opts.stream] : []);
54
+ this.trickle = opts.trickle !== void 0 ? opts.trickle : true;
55
+ this.allowHalfTrickle = opts.allowHalfTrickle !== void 0 ? opts.allowHalfTrickle : false;
56
+ this.iceCompleteTimeout = opts.iceCompleteTimeout || ICECOMPLETE_TIMEOUT;
57
+ this.destroyed = false;
58
+ this.destroying = false;
59
+ this._connected = false;
60
+ this.remoteAddress = void 0;
61
+ this.remoteFamily = void 0;
62
+ this.remotePort = void 0;
63
+ this.localAddress = void 0;
64
+ this.localFamily = void 0;
65
+ this.localPort = void 0;
66
+ this._wrtc = opts.wrtc && typeof opts.wrtc === "object" ? opts.wrtc : getBrowserRTC();
67
+ if (!this._wrtc) {
68
+ if (typeof window === "undefined") {
69
+ throw errCode(
70
+ new Error(
71
+ "No WebRTC support: Specify `opts.wrtc` option in this environment"
72
+ ),
73
+ "ERR_WEBRTC_SUPPORT"
74
+ );
75
+ } else {
76
+ throw errCode(
77
+ new Error("No WebRTC support: Not a supported browser"),
78
+ "ERR_WEBRTC_SUPPORT"
79
+ );
80
+ }
81
+ }
82
+ this._pcReady = false;
83
+ this._channelReady = false;
84
+ this._iceComplete = false;
85
+ this._iceCompleteTimer = null;
86
+ this._channel = null;
87
+ this._pendingCandidates = [];
88
+ this._isNegotiating = false;
89
+ this._firstNegotiation = true;
90
+ this._batchedNegotiation = false;
91
+ this._queuedNegotiation = false;
92
+ this._sendersAwaitingStable = [];
93
+ this._senderMap = /* @__PURE__ */ new Map();
94
+ this._closingInterval = null;
95
+ this._remoteTracks = [];
96
+ this._remoteStreams = [];
97
+ this._chunk = null;
98
+ this._cb = null;
99
+ this._interval = null;
100
+ try {
101
+ this._pc = new this._wrtc.RTCPeerConnection(this.config);
102
+ } catch (err) {
103
+ this.destroy(errCode(err, "ERR_PC_CONSTRUCTOR"));
104
+ return;
105
+ }
106
+ this._isReactNativeWebrtc = typeof this._pc._peerConnectionId === "number";
107
+ this._pc.oniceconnectionstatechange = () => {
108
+ this._onIceStateChange();
109
+ };
110
+ this._pc.onicegatheringstatechange = () => {
111
+ this._onIceStateChange();
112
+ };
113
+ this._pc.onconnectionstatechange = () => {
114
+ this._onConnectionStateChange();
115
+ };
116
+ this._pc.onsignalingstatechange = () => {
117
+ this._onSignalingStateChange();
118
+ };
119
+ this._pc.onicecandidate = (event) => {
120
+ this._onIceCandidate(event);
121
+ };
122
+ if (typeof this._pc.peerIdentity === "object") {
123
+ this._pc.peerIdentity.catch((err) => {
124
+ this.destroy(errCode(err, "ERR_PC_PEER_IDENTITY"));
125
+ });
126
+ }
127
+ if (this.initiator || this.channelNegotiated) {
128
+ this._setupData({
129
+ channel: this._pc.createDataChannel(
130
+ this.channelName,
131
+ this.channelConfig
132
+ )
133
+ });
134
+ } else {
135
+ this._pc.ondatachannel = (event) => {
136
+ this._setupData(event);
137
+ };
138
+ }
139
+ if (this.streams) {
140
+ this.streams.forEach((stream) => {
141
+ this.addStream(stream);
142
+ });
143
+ }
144
+ this._pc.ontrack = (event) => {
145
+ this._onTrack(event);
146
+ };
147
+ this._debug("initial negotiation");
148
+ this._needsNegotiation();
149
+ }
150
+ get bufferSize() {
151
+ return this._channel && this._channel.bufferedAmount || 0;
152
+ }
153
+ // HACK: it's possible channel.readyState is "closing" before peer.destroy() fires
154
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=882743
155
+ get connected() {
156
+ return this._connected && this._channel.readyState === "open";
157
+ }
158
+ address() {
159
+ return {
160
+ port: this.localPort,
161
+ family: this.localFamily,
162
+ address: this.localAddress
163
+ };
164
+ }
165
+ signal(data) {
166
+ if (this.destroying) return;
167
+ if (this.destroyed) throw errCode(new Error("cannot signal after peer is destroyed"), "ERR_DESTROYED");
168
+ if (typeof data === "string") {
169
+ try {
170
+ data = JSON.parse(data);
171
+ } catch (err) {
172
+ data = {};
173
+ }
174
+ }
175
+ this._debug("signal()");
176
+ if (data.renegotiate && this.initiator) {
177
+ this._debug("got request to renegotiate");
178
+ this._needsNegotiation();
179
+ }
180
+ if (data.transceiverRequest && this.initiator) {
181
+ this._debug("got request for transceiver");
182
+ this.addTransceiver(
183
+ data.transceiverRequest.kind,
184
+ data.transceiverRequest.init
185
+ );
186
+ }
187
+ if (data.candidate) {
188
+ if (this._pc.remoteDescription && this._pc.remoteDescription.type) {
189
+ this._addIceCandidate(data.candidate);
190
+ } else {
191
+ this._pendingCandidates.push(data.candidate);
192
+ }
193
+ }
194
+ if (data.sdp) {
195
+ this._pc.setRemoteDescription(new this._wrtc.RTCSessionDescription(data)).then(() => {
196
+ if (this.destroyed) return;
197
+ this._pendingCandidates.forEach((candidate) => {
198
+ this._addIceCandidate(candidate);
199
+ });
200
+ this._pendingCandidates = [];
201
+ if (this._pc.remoteDescription.type === "offer") this._createAnswer();
202
+ }).catch((err) => {
203
+ this.destroy(errCode(err, "ERR_SET_REMOTE_DESCRIPTION"));
204
+ });
205
+ }
206
+ if (!data.sdp && !data.candidate && !data.renegotiate && !data.transceiverRequest) {
207
+ this.destroy(
208
+ errCode(
209
+ new Error("signal() called with invalid signal data"),
210
+ "ERR_SIGNALING"
211
+ )
212
+ );
213
+ }
214
+ }
215
+ _addIceCandidate(candidate) {
216
+ const iceCandidateObj = new this._wrtc.RTCIceCandidate(candidate);
217
+ this._pc.addIceCandidate(iceCandidateObj).catch((err) => {
218
+ if (!iceCandidateObj.address || iceCandidateObj.address.endsWith(".local")) {
219
+ warn("Ignoring unsupported ICE candidate.");
220
+ } else {
221
+ this.destroy(errCode(err, "ERR_ADD_ICE_CANDIDATE"));
222
+ }
223
+ });
224
+ }
225
+ /**
226
+ * Send text/binary data to the remote peer.
227
+ * @param {ArrayBufferView|ArrayBuffer|string|Blob} chunk
228
+ */
229
+ send(chunk) {
230
+ if (this.destroying) return;
231
+ if (this.destroyed) throw errCode(new Error("cannot send after peer is destroyed"), "ERR_DESTROYED");
232
+ this._channel.send(chunk);
233
+ }
234
+ /**
235
+ * Add a Transceiver to the connection.
236
+ * @param {String} kind
237
+ * @param {Object} init
238
+ */
239
+ addTransceiver(kind, init) {
240
+ if (this.destroying) return;
241
+ if (this.destroyed) throw errCode(new Error("cannot addTransceiver after peer is destroyed"), "ERR_DESTROYED");
242
+ this._debug("addTransceiver()");
243
+ if (this.initiator) {
244
+ try {
245
+ this._pc.addTransceiver(kind, init);
246
+ this._needsNegotiation();
247
+ } catch (err) {
248
+ this.destroy(errCode(err, "ERR_ADD_TRANSCEIVER"));
249
+ }
250
+ } else {
251
+ this.emit("signal", {
252
+ // request initiator to renegotiate
253
+ type: "transceiverRequest",
254
+ transceiverRequest: { kind, init }
255
+ });
256
+ }
257
+ }
258
+ /**
259
+ * Add a MediaStream to the connection.
260
+ * @param {MediaStream} stream
261
+ */
262
+ addStream(stream) {
263
+ if (this.destroying) return;
264
+ if (this.destroyed) throw errCode(new Error("cannot addStream after peer is destroyed"), "ERR_DESTROYED");
265
+ this._debug("addStream()");
266
+ stream.getTracks().forEach((track) => {
267
+ this.addTrack(track, stream);
268
+ });
269
+ }
270
+ /**
271
+ * Add a MediaStreamTrack to the connection.
272
+ * @param {MediaStreamTrack} track
273
+ * @param {MediaStream} stream
274
+ */
275
+ addTrack(track, stream) {
276
+ if (this.destroying) return;
277
+ if (this.destroyed) throw errCode(new Error("cannot addTrack after peer is destroyed"), "ERR_DESTROYED");
278
+ this._debug("addTrack()");
279
+ const submap = this._senderMap.get(track) || /* @__PURE__ */ new Map();
280
+ let sender = submap.get(stream);
281
+ if (!sender) {
282
+ sender = this._pc.addTrack(track, stream);
283
+ submap.set(stream, sender);
284
+ this._senderMap.set(track, submap);
285
+ this._needsNegotiation();
286
+ } else if (sender.removed) {
287
+ throw errCode(
288
+ new Error(
289
+ "Track has been removed. You should enable/disable tracks that you want to re-add."
290
+ ),
291
+ "ERR_SENDER_REMOVED"
292
+ );
293
+ } else {
294
+ throw errCode(
295
+ new Error("Track has already been added to that stream."),
296
+ "ERR_SENDER_ALREADY_ADDED"
297
+ );
298
+ }
299
+ }
300
+ /**
301
+ * Replace a MediaStreamTrack by another in the connection.
302
+ * @param {MediaStreamTrack} oldTrack
303
+ * @param {MediaStreamTrack} newTrack
304
+ * @param {MediaStream} stream
305
+ */
306
+ replaceTrack(oldTrack, newTrack, stream) {
307
+ if (this.destroying) return;
308
+ if (this.destroyed) throw errCode(new Error("cannot replaceTrack after peer is destroyed"), "ERR_DESTROYED");
309
+ this._debug("replaceTrack()");
310
+ const submap = this._senderMap.get(oldTrack);
311
+ const sender = submap ? submap.get(stream) : null;
312
+ if (!sender) {
313
+ throw errCode(
314
+ new Error("Cannot replace track that was never added."),
315
+ "ERR_TRACK_NOT_ADDED"
316
+ );
317
+ }
318
+ if (newTrack) this._senderMap.set(newTrack, submap);
319
+ if (sender.replaceTrack != null) {
320
+ sender.replaceTrack(newTrack);
321
+ } else {
322
+ this.destroy(
323
+ errCode(
324
+ new Error("replaceTrack is not supported in this browser"),
325
+ "ERR_UNSUPPORTED_REPLACETRACK"
326
+ )
327
+ );
328
+ }
329
+ }
330
+ /**
331
+ * Remove a MediaStreamTrack from the connection.
332
+ * @param {MediaStreamTrack} track
333
+ * @param {MediaStream} stream
334
+ */
335
+ removeTrack(track, stream) {
336
+ if (this.destroying) return;
337
+ if (this.destroyed) throw errCode(new Error("cannot removeTrack after peer is destroyed"), "ERR_DESTROYED");
338
+ this._debug("removeSender()");
339
+ const submap = this._senderMap.get(track);
340
+ const sender = submap ? submap.get(stream) : null;
341
+ if (!sender) {
342
+ throw errCode(
343
+ new Error("Cannot remove track that was never added."),
344
+ "ERR_TRACK_NOT_ADDED"
345
+ );
346
+ }
347
+ try {
348
+ sender.removed = true;
349
+ this._pc.removeTrack(sender);
350
+ } catch (err) {
351
+ if (err.name === "NS_ERROR_UNEXPECTED") {
352
+ this._sendersAwaitingStable.push(sender);
353
+ } else {
354
+ this.destroy(errCode(err, "ERR_REMOVE_TRACK"));
355
+ }
356
+ }
357
+ this._needsNegotiation();
358
+ }
359
+ /**
360
+ * Remove a MediaStream from the connection.
361
+ * @param {MediaStream} stream
362
+ */
363
+ removeStream(stream) {
364
+ if (this.destroying) return;
365
+ if (this.destroyed) throw errCode(new Error("cannot removeStream after peer is destroyed"), "ERR_DESTROYED");
366
+ this._debug("removeSenders()");
367
+ stream.getTracks().forEach((track) => {
368
+ this.removeTrack(track, stream);
369
+ });
370
+ }
371
+ _needsNegotiation() {
372
+ this._debug("_needsNegotiation");
373
+ if (this._batchedNegotiation) return;
374
+ this._batchedNegotiation = true;
375
+ queueMicrotask(() => {
376
+ this._batchedNegotiation = false;
377
+ if (this.initiator || !this._firstNegotiation) {
378
+ this._debug("starting batched negotiation");
379
+ this.negotiate();
380
+ } else {
381
+ this._debug("non-initiator initial negotiation request discarded");
382
+ }
383
+ this._firstNegotiation = false;
384
+ });
385
+ }
386
+ negotiate() {
387
+ if (this.destroying) return;
388
+ if (this.destroyed) throw errCode(new Error("cannot negotiate after peer is destroyed"), "ERR_DESTROYED");
389
+ if (this.initiator) {
390
+ if (this._isNegotiating) {
391
+ this._queuedNegotiation = true;
392
+ this._debug("already negotiating, queueing");
393
+ } else {
394
+ this._debug("start negotiation");
395
+ setTimeout(() => {
396
+ this._createOffer();
397
+ }, 0);
398
+ }
399
+ } else {
400
+ if (this._isNegotiating) {
401
+ this._queuedNegotiation = true;
402
+ this._debug("already negotiating, queueing");
403
+ } else {
404
+ this._debug("requesting negotiation from initiator");
405
+ this.emit("signal", {
406
+ // request initiator to renegotiate
407
+ type: "renegotiate",
408
+ renegotiate: true
409
+ });
410
+ }
411
+ }
412
+ this._isNegotiating = true;
413
+ }
414
+ destroy(err) {
415
+ if (this.destroyed || this.destroying) return;
416
+ this.destroying = true;
417
+ this._debug("destroying (error: %s)", err && (err.message || err));
418
+ queueMicrotask(() => {
419
+ this.destroyed = true;
420
+ this.destroying = false;
421
+ this._debug("destroy (error: %s)", err && (err.message || err));
422
+ this._connected = false;
423
+ this._pcReady = false;
424
+ this._channelReady = false;
425
+ this._remoteTracks = null;
426
+ this._remoteStreams = null;
427
+ this._senderMap = null;
428
+ clearInterval(this._closingInterval);
429
+ this._closingInterval = null;
430
+ clearInterval(this._interval);
431
+ this._interval = null;
432
+ this._chunk = null;
433
+ this._cb = null;
434
+ if (this._channel) {
435
+ try {
436
+ this._channel.close();
437
+ } catch (err2) {
438
+ }
439
+ this._channel.onmessage = null;
440
+ this._channel.onopen = null;
441
+ this._channel.onclose = null;
442
+ this._channel.onerror = null;
443
+ }
444
+ if (this._pc) {
445
+ try {
446
+ this._pc.close();
447
+ } catch (err2) {
448
+ }
449
+ this._pc.oniceconnectionstatechange = null;
450
+ this._pc.onicegatheringstatechange = null;
451
+ this._pc.onsignalingstatechange = null;
452
+ this._pc.onicecandidate = null;
453
+ this._pc.ontrack = null;
454
+ this._pc.ondatachannel = null;
455
+ }
456
+ this._pc = null;
457
+ this._channel = null;
458
+ if (err) this.emit("error", err);
459
+ this.emit("close");
460
+ });
461
+ }
462
+ _setupData(event) {
463
+ if (!event.channel) {
464
+ return this.destroy(
465
+ errCode(
466
+ new Error("Data channel event is missing `channel` property"),
467
+ "ERR_DATA_CHANNEL"
468
+ )
469
+ );
470
+ }
471
+ this._channel = event.channel;
472
+ this._channel.binaryType = "arraybuffer";
473
+ if (typeof this._channel.bufferedAmountLowThreshold === "number") {
474
+ this._channel.bufferedAmountLowThreshold = MAX_BUFFERED_AMOUNT;
475
+ }
476
+ this.channelName = this._channel.label;
477
+ this._channel.onmessage = (event2) => {
478
+ this._onChannelMessage(event2);
479
+ };
480
+ this._channel.onbufferedamountlow = () => {
481
+ this._onChannelBufferedAmountLow();
482
+ };
483
+ this._channel.onopen = () => {
484
+ this._onChannelOpen();
485
+ };
486
+ this._channel.onclose = () => {
487
+ this._onChannelClose();
488
+ };
489
+ this._channel.onerror = (err) => {
490
+ this.destroy(errCode(err, "ERR_DATA_CHANNEL"));
491
+ };
492
+ let isClosing = false;
493
+ this._closingInterval = setInterval(() => {
494
+ if (this._channel && this._channel.readyState === "closing") {
495
+ if (isClosing) this._onChannelClose();
496
+ isClosing = true;
497
+ } else {
498
+ isClosing = false;
499
+ }
500
+ }, CHANNEL_CLOSING_TIMEOUT);
501
+ }
502
+ _startIceCompleteTimeout() {
503
+ if (this.destroyed) return;
504
+ if (this._iceCompleteTimer) return;
505
+ this._debug("started iceComplete timeout");
506
+ this._iceCompleteTimer = setTimeout(() => {
507
+ if (!this._iceComplete) {
508
+ this._iceComplete = true;
509
+ this._debug("iceComplete timeout completed");
510
+ this.emit("iceTimeout");
511
+ this.emit("_iceComplete");
512
+ }
513
+ }, this.iceCompleteTimeout);
514
+ }
515
+ _createOffer() {
516
+ if (this.destroyed) return;
517
+ this._pc.createOffer(this.offerOptions).then((offer) => {
518
+ if (this.destroyed) return;
519
+ if (!this.trickle && !this.allowHalfTrickle) {
520
+ offer.sdp = filterTrickle(offer.sdp);
521
+ }
522
+ offer.sdp = this.sdpTransform(offer.sdp);
523
+ const sendOffer = () => {
524
+ if (this.destroyed) return;
525
+ const signal = this._pc.localDescription || offer;
526
+ this._debug("signal");
527
+ this.emit("signal", {
528
+ type: signal.type,
529
+ sdp: signal.sdp
530
+ });
531
+ };
532
+ const onSuccess = () => {
533
+ this._debug("createOffer success");
534
+ if (this.destroyed) return;
535
+ if (this.trickle || this._iceComplete) sendOffer();
536
+ else this.once("_iceComplete", sendOffer);
537
+ };
538
+ const onError = (err) => {
539
+ this.destroy(errCode(err, "ERR_SET_LOCAL_DESCRIPTION"));
540
+ };
541
+ this._pc.setLocalDescription(offer).then(onSuccess).catch(onError);
542
+ }).catch((err) => {
543
+ this.destroy(errCode(err, "ERR_CREATE_OFFER"));
544
+ });
545
+ }
546
+ _requestMissingTransceivers() {
547
+ if (this._pc.getTransceivers) {
548
+ this._pc.getTransceivers().forEach((transceiver) => {
549
+ if (!transceiver.mid && transceiver.sender.track && !transceiver.requested) {
550
+ transceiver.requested = true;
551
+ this.addTransceiver(transceiver.sender.track.kind);
552
+ }
553
+ });
554
+ }
555
+ }
556
+ _createAnswer() {
557
+ if (this.destroyed) return;
558
+ this._pc.createAnswer(this.answerOptions).then((answer) => {
559
+ if (this.destroyed) return;
560
+ if (!this.trickle && !this.allowHalfTrickle) {
561
+ answer.sdp = filterTrickle(answer.sdp);
562
+ }
563
+ answer.sdp = this.sdpTransform(answer.sdp);
564
+ const sendAnswer = () => {
565
+ if (this.destroyed) return;
566
+ const signal = this._pc.localDescription || answer;
567
+ this._debug("signal");
568
+ this.emit("signal", {
569
+ type: signal.type,
570
+ sdp: signal.sdp
571
+ });
572
+ if (!this.initiator) this._requestMissingTransceivers();
573
+ };
574
+ const onSuccess = () => {
575
+ if (this.destroyed) return;
576
+ if (this.trickle || this._iceComplete) sendAnswer();
577
+ else this.once("_iceComplete", sendAnswer);
578
+ };
579
+ const onError = (err) => {
580
+ this.destroy(errCode(err, "ERR_SET_LOCAL_DESCRIPTION"));
581
+ };
582
+ this._pc.setLocalDescription(answer).then(onSuccess).catch(onError);
583
+ }).catch((err) => {
584
+ this.destroy(errCode(err, "ERR_CREATE_ANSWER"));
585
+ });
586
+ }
587
+ _onConnectionStateChange() {
588
+ if (this.destroyed) return;
589
+ if (this._pc.connectionState === "failed") {
590
+ this.destroy(
591
+ errCode(new Error("Connection failed."), "ERR_CONNECTION_FAILURE")
592
+ );
593
+ }
594
+ }
595
+ _onIceStateChange() {
596
+ if (this.destroyed) return;
597
+ const iceConnectionState = this._pc.iceConnectionState;
598
+ const iceGatheringState = this._pc.iceGatheringState;
599
+ this._debug(
600
+ "iceStateChange (connection: %s) (gathering: %s)",
601
+ iceConnectionState,
602
+ iceGatheringState
603
+ );
604
+ this.emit("iceStateChange", iceConnectionState, iceGatheringState);
605
+ if (iceConnectionState === "connected" || iceConnectionState === "completed") {
606
+ this._pcReady = true;
607
+ this._maybeReady();
608
+ }
609
+ if (iceConnectionState === "failed") {
610
+ this.destroy(
611
+ errCode(
612
+ new Error("Ice connection failed."),
613
+ "ERR_ICE_CONNECTION_FAILURE"
614
+ )
615
+ );
616
+ }
617
+ if (iceConnectionState === "closed") {
618
+ this.destroy(
619
+ errCode(
620
+ new Error("Ice connection closed."),
621
+ "ERR_ICE_CONNECTION_CLOSED"
622
+ )
623
+ );
624
+ }
625
+ }
626
+ getStats(cb) {
627
+ const flattenValues = (report) => {
628
+ if (Object.prototype.toString.call(report.values) === "[object Array]") {
629
+ report.values.forEach((value) => {
630
+ Object.assign(report, value);
631
+ });
632
+ }
633
+ return report;
634
+ };
635
+ if (this._pc.getStats.length === 0 || this._isReactNativeWebrtc) {
636
+ this._pc.getStats().then(
637
+ (res) => {
638
+ const reports = [];
639
+ res.forEach((report) => {
640
+ reports.push(flattenValues(report));
641
+ });
642
+ cb(null, reports);
643
+ },
644
+ (err) => cb(err)
645
+ );
646
+ } else if (this._pc.getStats.length > 0) {
647
+ this._pc.getStats(
648
+ (res) => {
649
+ if (this.destroyed) return;
650
+ const reports = [];
651
+ res.result().forEach((result) => {
652
+ const report = {};
653
+ result.names().forEach((name) => {
654
+ report[name] = result.stat(name);
655
+ });
656
+ report.id = result.id;
657
+ report.type = result.type;
658
+ report.timestamp = result.timestamp;
659
+ reports.push(flattenValues(report));
660
+ });
661
+ cb(null, reports);
662
+ },
663
+ (err) => cb(err)
664
+ );
665
+ } else {
666
+ cb(null, []);
667
+ }
668
+ }
669
+ _maybeReady() {
670
+ this._debug(
671
+ "maybeReady pc %s channel %s",
672
+ this._pcReady,
673
+ this._channelReady
674
+ );
675
+ if (this._connected || this._connecting || !this._pcReady || !this._channelReady) {
676
+ return;
677
+ }
678
+ this._connecting = true;
679
+ const findCandidatePair = () => {
680
+ if (this.destroyed) return;
681
+ this.getStats((err, items) => {
682
+ if (this.destroyed) return;
683
+ if (err) items = [];
684
+ const remoteCandidates = {};
685
+ const localCandidates = {};
686
+ const candidatePairs = {};
687
+ let foundSelectedCandidatePair = false;
688
+ items.forEach((item) => {
689
+ if (item.type === "remotecandidate" || item.type === "remote-candidate") {
690
+ remoteCandidates[item.id] = item;
691
+ }
692
+ if (item.type === "localcandidate" || item.type === "local-candidate") {
693
+ localCandidates[item.id] = item;
694
+ }
695
+ if (item.type === "candidatepair" || item.type === "candidate-pair") {
696
+ candidatePairs[item.id] = item;
697
+ }
698
+ });
699
+ const setSelectedCandidatePair = (selectedCandidatePair) => {
700
+ foundSelectedCandidatePair = true;
701
+ let local = localCandidates[selectedCandidatePair.localCandidateId];
702
+ if (local && (local.ip || local.address)) {
703
+ this.localAddress = local.ip || local.address;
704
+ this.localPort = Number(local.port);
705
+ } else if (local && local.ipAddress) {
706
+ this.localAddress = local.ipAddress;
707
+ this.localPort = Number(local.portNumber);
708
+ } else if (typeof selectedCandidatePair.googLocalAddress === "string") {
709
+ local = selectedCandidatePair.googLocalAddress.split(":");
710
+ this.localAddress = local[0];
711
+ this.localPort = Number(local[1]);
712
+ }
713
+ if (this.localAddress) {
714
+ this.localFamily = this.localAddress.includes(":") ? "IPv6" : "IPv4";
715
+ }
716
+ let remote = remoteCandidates[selectedCandidatePair.remoteCandidateId];
717
+ if (remote && (remote.ip || remote.address)) {
718
+ this.remoteAddress = remote.ip || remote.address;
719
+ this.remotePort = Number(remote.port);
720
+ } else if (remote && remote.ipAddress) {
721
+ this.remoteAddress = remote.ipAddress;
722
+ this.remotePort = Number(remote.portNumber);
723
+ } else if (typeof selectedCandidatePair.googRemoteAddress === "string") {
724
+ remote = selectedCandidatePair.googRemoteAddress.split(":");
725
+ this.remoteAddress = remote[0];
726
+ this.remotePort = Number(remote[1]);
727
+ }
728
+ if (this.remoteAddress) {
729
+ this.remoteFamily = this.remoteAddress.includes(":") ? "IPv6" : "IPv4";
730
+ }
731
+ this._debug(
732
+ "connect local: %s:%s remote: %s:%s",
733
+ this.localAddress,
734
+ this.localPort,
735
+ this.remoteAddress,
736
+ this.remotePort
737
+ );
738
+ };
739
+ items.forEach((item) => {
740
+ if (item.type === "transport" && item.selectedCandidatePairId) {
741
+ setSelectedCandidatePair(
742
+ candidatePairs[item.selectedCandidatePairId]
743
+ );
744
+ }
745
+ if (item.type === "googCandidatePair" && item.googActiveConnection === "true" || (item.type === "candidatepair" || item.type === "candidate-pair") && item.selected) {
746
+ setSelectedCandidatePair(item);
747
+ }
748
+ });
749
+ if (!foundSelectedCandidatePair && (!Object.keys(candidatePairs).length || Object.keys(localCandidates).length)) {
750
+ setTimeout(findCandidatePair, 100);
751
+ return;
752
+ } else {
753
+ this._connecting = false;
754
+ this._connected = true;
755
+ }
756
+ if (this._chunk) {
757
+ try {
758
+ this.send(this._chunk);
759
+ } catch (err2) {
760
+ return this.destroy(errCode(err2, "ERR_DATA_CHANNEL"));
761
+ }
762
+ this._chunk = null;
763
+ this._debug('sent chunk from "write before connect"');
764
+ const cb = this._cb;
765
+ this._cb = null;
766
+ cb(null);
767
+ }
768
+ if (typeof this._channel.bufferedAmountLowThreshold !== "number") {
769
+ this._interval = setInterval(() => this._onInterval(), 150);
770
+ if (this._interval.unref) this._interval.unref();
771
+ }
772
+ this._debug("connect");
773
+ this.emit("connect");
774
+ });
775
+ };
776
+ findCandidatePair();
777
+ }
778
+ _onInterval() {
779
+ if (!this._cb || !this._channel || this._channel.bufferedAmount > MAX_BUFFERED_AMOUNT) {
780
+ return;
781
+ }
782
+ this._onChannelBufferedAmountLow();
783
+ }
784
+ _onSignalingStateChange() {
785
+ if (this.destroyed) return;
786
+ if (this._pc.signalingState === "stable") {
787
+ this._isNegotiating = false;
788
+ this._debug("flushing sender queue", this._sendersAwaitingStable);
789
+ this._sendersAwaitingStable.forEach((sender) => {
790
+ this._pc.removeTrack(sender);
791
+ this._queuedNegotiation = true;
792
+ });
793
+ this._sendersAwaitingStable = [];
794
+ if (this._queuedNegotiation) {
795
+ this._debug("flushing negotiation queue");
796
+ this._queuedNegotiation = false;
797
+ this._needsNegotiation();
798
+ } else {
799
+ this._debug("negotiated");
800
+ this.emit("negotiated");
801
+ }
802
+ }
803
+ this._debug("signalingStateChange %s", this._pc.signalingState);
804
+ this.emit("signalingStateChange", this._pc.signalingState);
805
+ }
806
+ _onIceCandidate(event) {
807
+ if (this.destroyed) return;
808
+ if (event.candidate && this.trickle) {
809
+ this.emit("signal", {
810
+ type: "candidate",
811
+ candidate: {
812
+ candidate: event.candidate.candidate,
813
+ sdpMLineIndex: event.candidate.sdpMLineIndex,
814
+ sdpMid: event.candidate.sdpMid
815
+ }
816
+ });
817
+ } else if (!event.candidate && !this._iceComplete) {
818
+ this._iceComplete = true;
819
+ this.emit("_iceComplete");
820
+ }
821
+ if (event.candidate) {
822
+ this._startIceCompleteTimeout();
823
+ }
824
+ }
825
+ _onChannelMessage(event) {
826
+ if (this.destroyed) return;
827
+ let data = event.data;
828
+ if (data instanceof ArrayBuffer) data = new Uint8Array(data);
829
+ this.emit("data", data);
830
+ }
831
+ _onChannelBufferedAmountLow() {
832
+ if (this.destroyed || !this._cb) return;
833
+ this._debug(
834
+ "ending backpressure: bufferedAmount %d",
835
+ this._channel.bufferedAmount
836
+ );
837
+ const cb = this._cb;
838
+ this._cb = null;
839
+ cb(null);
840
+ }
841
+ _onChannelOpen() {
842
+ if (this._connected || this.destroyed) return;
843
+ this._debug("on channel open");
844
+ this._channelReady = true;
845
+ this._maybeReady();
846
+ }
847
+ _onChannelClose() {
848
+ if (this.destroyed) return;
849
+ this._debug("on channel close");
850
+ this.destroy();
851
+ }
852
+ _onTrack(event) {
853
+ if (this.destroyed) return;
854
+ event.streams.forEach((eventStream) => {
855
+ this._debug("on track");
856
+ this.emit("track", event.track, eventStream);
857
+ this._remoteTracks.push({
858
+ track: event.track,
859
+ stream: eventStream
860
+ });
861
+ if (this._remoteStreams.some((remoteStream) => {
862
+ return remoteStream.id === eventStream.id;
863
+ })) {
864
+ return;
865
+ }
866
+ this._remoteStreams.push(eventStream);
867
+ queueMicrotask(() => {
868
+ this._debug("on stream");
869
+ this.emit("stream", eventStream);
870
+ });
871
+ });
872
+ }
873
+ _debug(...args) {
874
+ if (!this._doDebug) return;
875
+ args[0] = "[" + this._id + "] " + args[0];
876
+ console.log(...args);
877
+ }
878
+ // event emitter
879
+ on(key, listener) {
880
+ const map = this._map;
881
+ if (!map.has(key)) map.set(key, /* @__PURE__ */ new Set());
882
+ map.get(key).add(listener);
883
+ }
884
+ off(key, listener) {
885
+ const map = this._map;
886
+ const listeners = map.get(key);
887
+ if (!listeners) return;
888
+ listeners.delete(listener);
889
+ if (listeners.size === 0) map.delete(key);
890
+ }
891
+ once(key, listener) {
892
+ const listener_ = (...args) => {
893
+ this.off(key, listener_);
894
+ listener(...args);
895
+ };
896
+ this.on(key, listener_);
897
+ }
898
+ emit(key, ...args) {
899
+ const map = this._map;
900
+ if (!map.has(key)) return;
901
+ for (const listener of map.get(key)) {
902
+ try {
903
+ listener(...args);
904
+ } catch (err) {
905
+ console.error(err);
906
+ }
907
+ }
908
+ }
909
+ }
910
+ Peer.WEBRTC_SUPPORT = !!getBrowserRTC();
911
+ Peer.config = {
912
+ iceServers: [
913
+ {
914
+ urls: [
915
+ "stun:stun.l.google.com:19302",
916
+ "stun:global.stun.twilio.com:3478"
917
+ ]
918
+ }
919
+ ],
920
+ sdpSemantics: "unified-plan"
921
+ };
922
+ Peer.channelConfig = {};
923
+ const PLUGIN_NAME = "rrweb/canvas-webrtc@1";
924
+ class RRWebPluginCanvasWebRTCRecord {
925
+ constructor({
926
+ signalSendCallback,
927
+ peer
928
+ }) {
929
+ __publicField(this, "peer", null);
930
+ __publicField(this, "mirror");
931
+ __publicField(this, "crossOriginIframeMirror");
932
+ __publicField(this, "streamMap", /* @__PURE__ */ new Map());
933
+ __publicField(this, "incomingStreams", /* @__PURE__ */ new Set());
934
+ __publicField(this, "outgoingStreams", /* @__PURE__ */ new Set());
935
+ __publicField(this, "streamNodeMap", /* @__PURE__ */ new Map());
936
+ __publicField(this, "canvasWindowMap", /* @__PURE__ */ new Map());
937
+ __publicField(this, "windowPeerMap", /* @__PURE__ */ new WeakMap());
938
+ __publicField(this, "peerWindowMap", /* @__PURE__ */ new WeakMap());
939
+ __publicField(this, "signalSendCallback");
940
+ this.signalSendCallback = signalSendCallback;
941
+ window.addEventListener(
942
+ "message",
943
+ (event) => this.windowPostMessageHandler(event)
944
+ );
945
+ if (peer) this.peer = peer;
946
+ }
947
+ initPlugin() {
948
+ return {
949
+ name: PLUGIN_NAME,
950
+ getMirror: ({ nodeMirror, crossOriginIframeMirror }) => {
951
+ this.mirror = nodeMirror;
952
+ this.crossOriginIframeMirror = crossOriginIframeMirror;
953
+ },
954
+ options: {}
955
+ };
956
+ }
957
+ signalReceive(signal) {
958
+ var _a;
959
+ if (!this.peer) this.setupPeer();
960
+ (_a = this.peer) == null ? void 0 : _a.signal(signal);
961
+ }
962
+ signalReceiveFromCrossOriginIframe(signal, source) {
963
+ const peer = this.setupPeer(source);
964
+ peer.signal(signal);
965
+ }
966
+ startStream(id, stream) {
967
+ var _a, _b;
968
+ if (!this.peer) this.setupPeer();
969
+ const data = {
970
+ nodeId: id,
971
+ streamId: stream.id
972
+ };
973
+ (_a = this.peer) == null ? void 0 : _a.send(JSON.stringify(data));
974
+ if (!this.outgoingStreams.has(stream)) (_b = this.peer) == null ? void 0 : _b.addStream(stream);
975
+ this.outgoingStreams.add(stream);
976
+ }
977
+ setupPeer(source) {
978
+ let peer;
979
+ if (!source) {
980
+ if (this.peer) return this.peer;
981
+ peer = this.peer = new Peer({
982
+ initiator: true
983
+ // trickle: false, // only create one WebRTC offer per session
984
+ });
985
+ } else {
986
+ const peerFromMap = this.windowPeerMap.get(source);
987
+ if (peerFromMap) return peerFromMap;
988
+ peer = new Peer({
989
+ initiator: false
990
+ // trickle: false, // only create one WebRTC offer per session
991
+ });
992
+ this.windowPeerMap.set(source, peer);
993
+ this.peerWindowMap.set(peer, source);
994
+ }
995
+ const resetPeer = (source2) => {
996
+ if (!source2) return this.peer = null;
997
+ this.windowPeerMap.delete(source2);
998
+ this.peerWindowMap.delete(peer);
999
+ };
1000
+ peer.on("error", (err) => {
1001
+ resetPeer(source);
1002
+ console.log("error", err);
1003
+ });
1004
+ peer.on("close", () => {
1005
+ resetPeer(source);
1006
+ console.log("closing");
1007
+ });
1008
+ peer.on("signal", (data) => {
1009
+ var _a, _b;
1010
+ if (this.inRootFrame()) {
1011
+ if (peer === this.peer) {
1012
+ this.signalSendCallback(data);
1013
+ } else {
1014
+ (_a = this.peerWindowMap.get(peer)) == null ? void 0 : _a.postMessage(
1015
+ {
1016
+ type: "rrweb-canvas-webrtc",
1017
+ data: {
1018
+ type: "signal",
1019
+ signal: data
1020
+ }
1021
+ },
1022
+ "*"
1023
+ );
1024
+ }
1025
+ } else {
1026
+ (_b = window.top) == null ? void 0 : _b.postMessage(
1027
+ {
1028
+ type: "rrweb-canvas-webrtc",
1029
+ data: {
1030
+ type: "signal",
1031
+ signal: data
1032
+ }
1033
+ },
1034
+ "*"
1035
+ );
1036
+ }
1037
+ });
1038
+ peer.on("connect", () => {
1039
+ if (this.inRootFrame() && peer !== this.peer) return;
1040
+ for (const [id, stream] of this.streamMap) {
1041
+ this.startStream(id, stream);
1042
+ }
1043
+ });
1044
+ if (!this.inRootFrame()) return peer;
1045
+ peer.on("data", (data) => {
1046
+ try {
1047
+ const json = JSON.parse(data);
1048
+ this.streamNodeMap.set(json.streamId, json.nodeId);
1049
+ } catch (error) {
1050
+ console.error("Could not parse data", error);
1051
+ }
1052
+ this.flushStreams();
1053
+ });
1054
+ peer.on("stream", (stream) => {
1055
+ this.incomingStreams.add(stream);
1056
+ this.flushStreams();
1057
+ });
1058
+ return peer;
1059
+ }
1060
+ setupStream(id, rootId) {
1061
+ var _a;
1062
+ if (id === -1 || !this.mirror) return false;
1063
+ let stream = this.streamMap.get(rootId || id);
1064
+ if (stream) return stream;
1065
+ const el = this.mirror.getNode(id);
1066
+ if (!el || !("captureStream" in el))
1067
+ return this.setupStreamInCrossOriginIframe(id, rootId || id);
1068
+ if (!this.inRootFrame()) {
1069
+ (_a = window.top) == null ? void 0 : _a.postMessage(
1070
+ {
1071
+ type: "rrweb-canvas-webrtc",
1072
+ data: {
1073
+ type: "i-have-canvas",
1074
+ rootId: rootId || id
1075
+ }
1076
+ },
1077
+ "*"
1078
+ );
1079
+ }
1080
+ stream = el.captureStream();
1081
+ this.streamMap.set(rootId || id, stream);
1082
+ this.setupPeer();
1083
+ return stream;
1084
+ }
1085
+ flushStreams() {
1086
+ this.incomingStreams.forEach((stream) => {
1087
+ const nodeId = this.streamNodeMap.get(stream.id);
1088
+ if (!nodeId) return;
1089
+ this.startStream(nodeId, stream);
1090
+ });
1091
+ }
1092
+ inRootFrame() {
1093
+ return Boolean(window.top && window.top === window);
1094
+ }
1095
+ setupStreamInCrossOriginIframe(id, rootId) {
1096
+ let found = false;
1097
+ document.querySelectorAll("iframe").forEach((iframe) => {
1098
+ var _a;
1099
+ if (found) return;
1100
+ if (!this.crossOriginIframeMirror) return;
1101
+ const remoteId = this.crossOriginIframeMirror.getRemoteId(iframe, id);
1102
+ if (remoteId === -1) return;
1103
+ found = true;
1104
+ (_a = iframe.contentWindow) == null ? void 0 : _a.postMessage(
1105
+ {
1106
+ type: "rrweb-canvas-webrtc",
1107
+ data: {
1108
+ type: "who-has-canvas",
1109
+ id: remoteId,
1110
+ rootId
1111
+ }
1112
+ },
1113
+ "*"
1114
+ );
1115
+ });
1116
+ return found;
1117
+ }
1118
+ isCrossOriginIframeMessageEventContent(event) {
1119
+ return Boolean(
1120
+ event.data && typeof event.data === "object" && "type" in event.data && "data" in event.data && event.data.type === "rrweb-canvas-webrtc" && event.data.data
1121
+ );
1122
+ }
1123
+ /**
1124
+ * All messages being sent to the (root or sub) frame are received through `windowPostMessageHandler`.
1125
+ * @param event - The message event
1126
+ */
1127
+ windowPostMessageHandler(event) {
1128
+ if (!this.isCrossOriginIframeMessageEventContent(event)) return;
1129
+ const { type } = event.data.data;
1130
+ if (type === "who-has-canvas") {
1131
+ const { id, rootId } = event.data.data;
1132
+ this.setupStream(id, rootId);
1133
+ } else if (type === "signal") {
1134
+ const { signal } = event.data.data;
1135
+ const { source } = event;
1136
+ if (!source || !("self" in source)) return;
1137
+ if (this.inRootFrame()) {
1138
+ this.signalReceiveFromCrossOriginIframe(signal, source);
1139
+ } else {
1140
+ this.signalReceive(signal);
1141
+ }
1142
+ } else if (type === "i-have-canvas") {
1143
+ const { rootId } = event.data.data;
1144
+ const { source } = event;
1145
+ if (!source || !("self" in source)) return;
1146
+ this.canvasWindowMap.set(rootId, source);
1147
+ }
1148
+ }
1149
+ }
1150
+ export {
1151
+ PLUGIN_NAME,
1152
+ RRWebPluginCanvasWebRTCRecord
1153
+ };
1154
+ //# sourceMappingURL=rrweb-plugin-canvas-webrtc-record.js.map