@decentnetwork/beagle 0.1.121 → 0.1.122

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,67 @@
1
+ /** Custom packet id for the components channel.
2
+ *
3
+ * 160-191 is toxcore's LOSSLESS range — a form must not be silently dropped.
4
+ * decentlan owns 161 (session), 162 (dora), 163 (IP), 164 (rate); 165 is the
5
+ * first free slot. Clients that don't implement this id never see the packet,
6
+ * which is exactly the backward-compatibility story we want. */
7
+ export declare const PACKET_ID_CHAT_COMPONENTS = 165;
8
+ /** Payload version. Receivers ignore payloads they don't know how to read
9
+ * rather than best-effort parsing a future shape. */
10
+ export declare const COMPONENTS_WIRE_VERSION = 1;
11
+ export declare const MAX_COMPONENTS = 12;
12
+ export declare const MAX_OPTIONS = 40;
13
+ export declare const MAX_LABEL_LEN = 120;
14
+ export declare const MAX_ID_LEN = 64;
15
+ export declare const MAX_VALUE_LEN = 512;
16
+ export declare const MAX_CUSTOM_ID_LEN = 128;
17
+ /** Hard ceiling on a decoded payload before we even try to parse it. */
18
+ export declare const MAX_PAYLOAD_BYTES: number;
19
+ export type ComponentType = "select" | "submit" | "text" | "button" | "attachment";
20
+ export interface SelectOption {
21
+ label: string;
22
+ value: string;
23
+ }
24
+ export interface ChatComponent {
25
+ type: ComponentType;
26
+ id: string;
27
+ label?: string;
28
+ required?: boolean;
29
+ default?: string;
30
+ placeholder?: string;
31
+ options?: SelectOption[];
32
+ }
33
+ export interface ComponentsMessage {
34
+ v: number;
35
+ type: "components";
36
+ /** Chosen by the agent; echoed back so it can route the reply. */
37
+ custom_id: string;
38
+ components: ChatComponent[];
39
+ }
40
+ export interface InteractionMessage {
41
+ v: number;
42
+ type: "interaction";
43
+ custom_id: string;
44
+ /** id of the submit/button that fired. */
45
+ component: string;
46
+ values: Record<string, string | {
47
+ fileId: string;
48
+ name: string;
49
+ size: number;
50
+ }>;
51
+ }
52
+ /**
53
+ * Validate a remote components payload. Returns null when nothing renderable
54
+ * survives — the caller then shows only the text message, which is the correct
55
+ * degraded state, never an error.
56
+ */
57
+ export declare function parseComponentsMessage(raw: unknown): ComponentsMessage | null;
58
+ /** Build the interaction reply. Values are the user's own input, but they are
59
+ * bounded here too so a pathological local state can't emit an oversized
60
+ * packet at the peer. */
61
+ export declare function buildInteraction(customId: string, component: string, values: Record<string, string>): InteractionMessage;
62
+ /** Decode a received packet. Returns null for anything malformed or oversized —
63
+ * a peer must not be able to make us throw on the receive path. */
64
+ export declare function decodePacket(data: Uint8Array): ComponentsMessage | InteractionMessage | null;
65
+ /** Validate an inbound interaction (the agent side of the channel). */
66
+ export declare function parseInteractionMessage(raw: unknown): InteractionMessage | null;
67
+ export declare function encodePacket(msg: ComponentsMessage | InteractionMessage): Uint8Array;
@@ -0,0 +1,208 @@
1
+ // Brief 30 — interactive components in chat.
2
+ //
3
+ // An agent declares a form as DATA; this client renders it with its own trusted
4
+ // widgets. No HTML crosses the wire, no URL is fetched, no script runs. The UI's
5
+ // escape-everything policy (dkHtmlEscape) is untouched — components are a
6
+ // separate, whitelisted channel, not an escape hatch from it.
7
+ //
8
+ // Wire shape: a sibling packet, not a field on the chat message. Answering the
9
+ // brief's first open question that way makes "old clients still see the
10
+ // message" a property of the protocol rather than a rule senders must remember:
11
+ // the human-readable text goes out as an ordinary chat message that every
12
+ // existing client already renders, and the component payload rides a custom
13
+ // packet id that any client not built for it never even receives.
14
+ //
15
+ // EVERY value here arrives from a remote peer. The validator is the trust
16
+ // boundary: it whitelists types, caps every count and length, and drops what it
17
+ // does not understand instead of guessing.
18
+ /** Custom packet id for the components channel.
19
+ *
20
+ * 160-191 is toxcore's LOSSLESS range — a form must not be silently dropped.
21
+ * decentlan owns 161 (session), 162 (dora), 163 (IP), 164 (rate); 165 is the
22
+ * first free slot. Clients that don't implement this id never see the packet,
23
+ * which is exactly the backward-compatibility story we want. */
24
+ export const PACKET_ID_CHAT_COMPONENTS = 165;
25
+ /** Payload version. Receivers ignore payloads they don't know how to read
26
+ * rather than best-effort parsing a future shape. */
27
+ export const COMPONENTS_WIRE_VERSION = 1;
28
+ // --- caps -------------------------------------------------------------------
29
+ // "The client caps sizes/counts itself — a peer must not be able to freeze
30
+ // another peer's UI." (Brief 30, Security). These are deliberately small: a
31
+ // legitimate form is a handful of fields, and anything larger is either a bug
32
+ // or an attempt to hang the renderer.
33
+ export const MAX_COMPONENTS = 12;
34
+ export const MAX_OPTIONS = 40;
35
+ export const MAX_LABEL_LEN = 120;
36
+ export const MAX_ID_LEN = 64;
37
+ export const MAX_VALUE_LEN = 512;
38
+ export const MAX_CUSTOM_ID_LEN = 128;
39
+ /** Hard ceiling on a decoded payload before we even try to parse it. */
40
+ export const MAX_PAYLOAD_BYTES = 64 * 1024;
41
+ /** Types this build actually renders. Anything else is dropped — forward
42
+ * compatibility per the brief: an older client must ignore, not break. */
43
+ const RENDERABLE = new Set(["select", "submit", "text"]);
44
+ const str = (v, max) => {
45
+ if (typeof v !== "string")
46
+ return undefined;
47
+ const s = v.trim();
48
+ if (!s || s.length > max)
49
+ return undefined;
50
+ return s;
51
+ };
52
+ /**
53
+ * Validate a remote components payload. Returns null when nothing renderable
54
+ * survives — the caller then shows only the text message, which is the correct
55
+ * degraded state, never an error.
56
+ */
57
+ export function parseComponentsMessage(raw) {
58
+ if (!raw || typeof raw !== "object")
59
+ return null;
60
+ const o = raw;
61
+ if (o.type !== "components")
62
+ return null;
63
+ if (o.v !== COMPONENTS_WIRE_VERSION)
64
+ return null;
65
+ const customId = str(o.custom_id, MAX_CUSTOM_ID_LEN);
66
+ if (!customId)
67
+ return null;
68
+ if (!Array.isArray(o.components))
69
+ return null;
70
+ const seen = new Set();
71
+ const out = [];
72
+ for (const entry of o.components.slice(0, MAX_COMPONENTS)) {
73
+ if (!entry || typeof entry !== "object")
74
+ continue;
75
+ const c = entry;
76
+ const type = typeof c.type === "string" ? c.type : "";
77
+ if (!RENDERABLE.has(type))
78
+ continue; // unknown/unsupported: drop, don't guess
79
+ const id = str(c.id, MAX_ID_LEN);
80
+ if (!id || seen.has(id))
81
+ continue; // duplicate ids would alias each other's values
82
+ seen.add(id);
83
+ const component = { type: type, id };
84
+ const label = str(c.label, MAX_LABEL_LEN);
85
+ if (label)
86
+ component.label = label;
87
+ if (c.required === true)
88
+ component.required = true;
89
+ const placeholder = str(c.placeholder, MAX_LABEL_LEN);
90
+ if (placeholder)
91
+ component.placeholder = placeholder;
92
+ if (type === "select") {
93
+ if (!Array.isArray(c.options))
94
+ continue; // a select with no options is unusable
95
+ const options = [];
96
+ const seenValues = new Set();
97
+ for (const raw of c.options.slice(0, MAX_OPTIONS)) {
98
+ if (!raw || typeof raw !== "object")
99
+ continue;
100
+ const opt = raw;
101
+ const value = str(opt.value, MAX_VALUE_LEN);
102
+ if (!value || seenValues.has(value))
103
+ continue;
104
+ seenValues.add(value);
105
+ options.push({ label: str(opt.label, MAX_LABEL_LEN) ?? value, value });
106
+ }
107
+ if (!options.length)
108
+ continue;
109
+ component.options = options;
110
+ // A default that isn't one of the options would submit a value the agent
111
+ // never offered; ignore it rather than pass it through.
112
+ const def = str(c.default, MAX_VALUE_LEN);
113
+ if (def && options.some((o2) => o2.value === def))
114
+ component.default = def;
115
+ }
116
+ if (type === "text") {
117
+ const def = str(c.default, MAX_VALUE_LEN);
118
+ if (def)
119
+ component.default = def;
120
+ }
121
+ out.push(component);
122
+ }
123
+ // A form with no way to submit can never be completed — treat it as noise.
124
+ if (!out.some((c) => c.type === "submit"))
125
+ return null;
126
+ // ...and a submit button with nothing to fill in is equally pointless.
127
+ if (!out.some((c) => c.type === "select" || c.type === "text"))
128
+ return null;
129
+ return { v: COMPONENTS_WIRE_VERSION, type: "components", custom_id: customId, components: out };
130
+ }
131
+ /** Build the interaction reply. Values are the user's own input, but they are
132
+ * bounded here too so a pathological local state can't emit an oversized
133
+ * packet at the peer. */
134
+ export function buildInteraction(customId, component, values) {
135
+ const clean = {};
136
+ for (const [k, v] of Object.entries(values)) {
137
+ const key = str(k, MAX_ID_LEN);
138
+ if (!key)
139
+ continue;
140
+ clean[key] = typeof v === "string" ? v.slice(0, MAX_VALUE_LEN) : "";
141
+ }
142
+ return {
143
+ v: COMPONENTS_WIRE_VERSION,
144
+ type: "interaction",
145
+ custom_id: customId.slice(0, MAX_CUSTOM_ID_LEN),
146
+ component: component.slice(0, MAX_ID_LEN),
147
+ values: clean,
148
+ };
149
+ }
150
+ /** Decode a received packet. Returns null for anything malformed or oversized —
151
+ * a peer must not be able to make us throw on the receive path. */
152
+ export function decodePacket(data) {
153
+ if (!data?.length || data.length > MAX_PAYLOAD_BYTES)
154
+ return null;
155
+ let parsed;
156
+ try {
157
+ parsed = JSON.parse(Buffer.from(data).toString("utf-8"));
158
+ }
159
+ catch {
160
+ return null;
161
+ }
162
+ const o = parsed;
163
+ if (!o || typeof o !== "object")
164
+ return null;
165
+ if (o.type === "components")
166
+ return parseComponentsMessage(o);
167
+ if (o.type === "interaction")
168
+ return parseInteractionMessage(o);
169
+ return null;
170
+ }
171
+ /** Validate an inbound interaction (the agent side of the channel). */
172
+ export function parseInteractionMessage(raw) {
173
+ if (!raw || typeof raw !== "object")
174
+ return null;
175
+ const o = raw;
176
+ if (o.type !== "interaction" || o.v !== COMPONENTS_WIRE_VERSION)
177
+ return null;
178
+ const customId = str(o.custom_id, MAX_CUSTOM_ID_LEN);
179
+ const component = str(o.component, MAX_ID_LEN);
180
+ if (!customId || !component)
181
+ return null;
182
+ const values = {};
183
+ if (o.values && typeof o.values === "object") {
184
+ for (const [k, v] of Object.entries(o.values).slice(0, MAX_COMPONENTS)) {
185
+ const key = str(k, MAX_ID_LEN);
186
+ if (!key)
187
+ continue;
188
+ if (typeof v === "string") {
189
+ values[key] = v.slice(0, MAX_VALUE_LEN);
190
+ continue;
191
+ }
192
+ // File values are {fileId, name, size} — fileId is the content sha256,
193
+ // which is what makes the reference exact (Brief 30 §3).
194
+ if (v && typeof v === "object") {
195
+ const f = v;
196
+ const fileId = str(f.fileId, 128);
197
+ const name = str(f.name, MAX_LABEL_LEN);
198
+ const size = typeof f.size === "number" && Number.isFinite(f.size) ? f.size : undefined;
199
+ if (fileId && name && size !== undefined)
200
+ values[key] = { fileId, name, size };
201
+ }
202
+ }
203
+ }
204
+ return { v: COMPONENTS_WIRE_VERSION, type: "interaction", custom_id: customId, component, values };
205
+ }
206
+ export function encodePacket(msg) {
207
+ return new Uint8Array(Buffer.from(JSON.stringify(msg), "utf-8"));
208
+ }
@@ -1,4 +1,4 @@
1
- window.__DK_UI_VERSION="0.1.121";
1
+ window.__DK_UI_VERSION="0.1.122";
2
2
  "use strict";
3
3
  (() => {
4
4
  // packages/beagle-ui/dist/beagle-ui.js
@@ -57,6 +57,8 @@ var PeerWebRTC = (() => {
57
57
  fitSignalForInvite: () => fitSignalForInvite,
58
58
  looksLikeSignal: () => looksLikeSignal,
59
59
  mediaDirection: () => mediaDirection,
60
+ orphanPayloadTypeLines: () => orphanPayloadTypeLines,
61
+ sanitizeSdp: () => sanitizeSdp,
60
62
  shrinkSdpForInvite: () => shrinkSdpForInvite,
61
63
  videoIsSending: () => videoIsSending
62
64
  });
@@ -198,6 +200,29 @@ var PeerWebRTC = (() => {
198
200
  }
199
201
  return join(lines);
200
202
  }
203
+ function orphanPayloadTypeLines(sdp) {
204
+ const out = [];
205
+ let pts;
206
+ for (const line of sdp.replace(/\r\n/g, "\n").split("\n")) {
207
+ if (line.startsWith("m=")) {
208
+ pts = new Set(line.split(" ").slice(3));
209
+ continue;
210
+ }
211
+ const m = pts && /^a=(?:rtpmap|rtcp-fb|fmtp):(\d+)\b/.exec(line);
212
+ if (m && !pts.has(m[1]))
213
+ out.push(line);
214
+ }
215
+ return out;
216
+ }
217
+ function sanitizeSdp(sdp) {
218
+ const orphans = orphanPayloadTypeLines(sdp);
219
+ if (orphans.length === 0)
220
+ return sdp;
221
+ const drop = new Set(orphans);
222
+ const crlf = sdp.includes("\r\n");
223
+ const kept = sdp.replace(/\r\n/g, "\n").split("\n").filter((l) => !drop.has(l));
224
+ return kept.join(crlf ? "\r\n" : "\n");
225
+ }
201
226
 
202
227
  // node_modules/@decentnetwork/peer-webrtc/dist/signal.js
203
228
  var SDP_TYPES = /* @__PURE__ */ new Set([
@@ -297,7 +322,7 @@ var PeerWebRTC = (() => {
297
322
  var ICE_RESTART_MAX = 3;
298
323
  var ICE_DISCONNECT_GRACE_MS = 4e3;
299
324
  var ICE_FAILED_GRACE_MS = 12e4;
300
- var _opts, _iceServers, _sessions, _restartOffersEnabled, _setOutgoingVideoTrack, setOutgoingVideoTrack_fn, _renegotiate, renegotiate_fn, _createOffer, createOffer_fn, _createAnswer, createAnswer_fn, _preferLeanVideoCodecs, preferLeanVideoCodecs_fn, _restartIce, restartIce_fn, _handleSignal, handleSignal_fn, _onOffer, onOffer_fn, _answerRenegotiation, answerRenegotiation_fn, _onAnswer, onAnswer_fn, _onCandidate, onCandidate_fn, _onBye, onBye_fn, _onAuxiliary, onAuxiliary_fn, _createSession, createSession_fn, _wirePeerConnection, wirePeerConnection_fn, _ensureRemoteStream, ensureRemoteStream_fn, _attachLocalMedia, attachLocalMedia_fn, _flushLocalCandidates, flushLocalCandidates_fn, _drainRemoteCandidates, drainRemoteCandidates_fn, _send, send_fn, _sendByeSafe, sendByeSafe_fn, _cleanup, cleanup_fn, _setState, setState_fn, _normId, normId_fn, _toInfo, _newCallId, newCallId_fn, _log, log_fn;
325
+ var _opts, _iceServers, _sessions, _restartOffersEnabled, _setOutgoingVideoTrack, setOutgoingVideoTrack_fn, _renegotiate, renegotiate_fn, _createOffer, createOffer_fn, _createAnswer, createAnswer_fn, _cleanOutbound, cleanOutbound_fn, _cleanInbound, cleanInbound_fn, _preferLeanVideoCodecs, preferLeanVideoCodecs_fn, _restartIce, restartIce_fn, _handleSignal, handleSignal_fn, _onOffer, onOffer_fn, _answerRenegotiation, answerRenegotiation_fn, _negotiationFailed, negotiationFailed_fn, _onAnswer, onAnswer_fn, _onCandidate, onCandidate_fn, _onBye, onBye_fn, _onAuxiliary, onAuxiliary_fn, _createSession, createSession_fn, _wirePeerConnection, wirePeerConnection_fn, _ensureRemoteStream, ensureRemoteStream_fn, _attachLocalMedia, attachLocalMedia_fn, _flushLocalCandidates, flushLocalCandidates_fn, _drainRemoteCandidates, drainRemoteCandidates_fn, _send, send_fn, _sendByeSafe, sendByeSafe_fn, _cleanup, cleanup_fn, _setState, setState_fn, _normId, normId_fn, _toInfo, _newCallId, newCallId_fn, _log, log_fn;
301
326
  var CallEngine = class extends Emitter {
302
327
  constructor(opts) {
303
328
  var _a, _b;
@@ -309,10 +334,31 @@ var PeerWebRTC = (() => {
309
334
  /** Create + send a fresh offer to renegotiate media (e.g. after adding a
310
335
  * screen-share track). The peer applies it via #onOffer's renegotiation path. */
311
336
  __privateAdd(this, _renegotiate);
312
- /** createOffer with the lean codec set applied (see #preferLeanVideoCodecs). */
337
+ /** createOffer with the lean codec set applied (see #preferLeanVideoCodecs)
338
+ * and the result sanitized (see #cleanOutbound). Every offer — initial,
339
+ * screen-share re-offer, ICE restart — goes through here. */
313
340
  __privateAdd(this, _createOffer);
314
- /** createAnswer with the lean codec set applied. */
341
+ /** createAnswer with the lean codec set applied and the result sanitized. */
315
342
  __privateAdd(this, _createAnswer);
343
+ /**
344
+ * Strip orphan codec lines from a description WE produced, before it is
345
+ * applied locally and before it is sent.
346
+ *
347
+ * Firefox's setCodecPreferences drops VP9/AV1 from the m=video line but
348
+ * leaves their `a=fmtp:` / `a=rtcp-fb:` lines in place; Chrome's parser then
349
+ * rejects the whole re-offer ("Failed to parse codecs correctly") and the
350
+ * Firefox peer's screen share never shows up. Measured 2026-10-31 between
351
+ * the Firefox extension and a Chrome desktop, both on 0.2.16. The sanitizer
352
+ * is a no-op on a clean description (Chrome, Safari), so the same object
353
+ * comes back untouched there.
354
+ */
355
+ __privateAdd(this, _cleanOutbound);
356
+ /**
357
+ * The same cleanup for a description the PEER sent, before setRemoteDescription:
358
+ * Firefox extensions already in the field (AMO 1.4.14 ships 0.2.16) still
359
+ * produce the orphan lines, and a receiver on this engine must accept them.
360
+ */
361
+ __privateAdd(this, _cleanInbound);
316
362
  /**
317
363
  * Restrict video to the codecs the peers actually negotiate, so the SDP fits
318
364
  * the 8192-byte Carrier invite channel. Chrome otherwise advertises VP9 and
@@ -335,6 +381,7 @@ var PeerWebRTC = (() => {
335
381
  __privateAdd(this, _onOffer);
336
382
  /** Apply a mid-call re-offer (renegotiation) and send the answer. */
337
383
  __privateAdd(this, _answerRenegotiation);
384
+ __privateAdd(this, _negotiationFailed);
338
385
  __privateAdd(this, _onAnswer);
339
386
  __privateAdd(this, _onCandidate);
340
387
  __privateAdd(this, _onBye);
@@ -450,7 +497,7 @@ var PeerWebRTC = (() => {
450
497
  throw new Error(`accept: no pending incoming call ${callId}`);
451
498
  }
452
499
  await __privateMethod(this, _attachLocalMedia, attachLocalMedia_fn).call(this, session);
453
- await session.pc.setRemoteDescription({ type: "offer", sdp: session.pendingOffer });
500
+ await session.pc.setRemoteDescription({ type: "offer", sdp: __privateMethod(this, _cleanInbound, cleanInbound_fn).call(this, session.pendingOffer, "offer") });
454
501
  session.hasReceivedSdp = true;
455
502
  session.pendingOffer = void 0;
456
503
  await __privateMethod(this, _drainRemoteCandidates, drainRemoteCandidates_fn).call(this, session);
@@ -638,12 +685,30 @@ var PeerWebRTC = (() => {
638
685
  _createOffer = new WeakSet();
639
686
  createOffer_fn = async function(session, opts) {
640
687
  __privateMethod(this, _preferLeanVideoCodecs, preferLeanVideoCodecs_fn).call(this, session.pc);
641
- return session.pc.createOffer(opts);
688
+ return __privateMethod(this, _cleanOutbound, cleanOutbound_fn).call(this, await session.pc.createOffer(opts));
642
689
  };
643
690
  _createAnswer = new WeakSet();
644
691
  createAnswer_fn = async function(session) {
645
692
  __privateMethod(this, _preferLeanVideoCodecs, preferLeanVideoCodecs_fn).call(this, session.pc);
646
- return session.pc.createAnswer();
693
+ return __privateMethod(this, _cleanOutbound, cleanOutbound_fn).call(this, await session.pc.createAnswer());
694
+ };
695
+ _cleanOutbound = new WeakSet();
696
+ cleanOutbound_fn = function(desc) {
697
+ var _a, _b;
698
+ const raw = (_a = desc.sdp) != null ? _a : "";
699
+ const sdp = sanitizeSdp(raw);
700
+ if (sdp === raw)
701
+ return desc;
702
+ __privateMethod(this, _log, log_fn).call(this, `${(_b = desc.type) != null ? _b : "sdp"}: removed ${orphanPayloadTypeLines(raw).length} orphan codec line(s) from our own description`);
703
+ return { type: desc.type, sdp };
704
+ };
705
+ _cleanInbound = new WeakSet();
706
+ cleanInbound_fn = function(raw, what) {
707
+ const sdp = sanitizeSdp(raw);
708
+ if (sdp !== raw) {
709
+ __privateMethod(this, _log, log_fn).call(this, `${what}: removed ${orphanPayloadTypeLines(raw).length} orphan codec line(s) from the peer's description`);
710
+ }
711
+ return sdp;
647
712
  };
648
713
  _preferLeanVideoCodecs = new WeakSet();
649
714
  preferLeanVideoCodecs_fn = function(pc) {
@@ -733,14 +798,21 @@ var PeerWebRTC = (() => {
733
798
  _answerRenegotiation = new WeakSet();
734
799
  answerRenegotiation_fn = async function(session, sdp) {
735
800
  try {
736
- await session.pc.setRemoteDescription({ type: "offer", sdp });
801
+ await session.pc.setRemoteDescription({ type: "offer", sdp: __privateMethod(this, _cleanInbound, cleanInbound_fn).call(this, sdp, "offer") });
737
802
  const answer = await __privateMethod(this, _createAnswer, createAnswer_fn).call(this, session);
738
803
  await session.pc.setLocalDescription(answer);
739
804
  await __privateMethod(this, _send, send_fn).call(this, session.peerId, { type: "answer", sdp: answer.sdp, callId: session.callId });
740
805
  } catch (err) {
741
- __privateMethod(this, _log, log_fn).call(this, `renegotiation answer failed: ${err.message}`);
806
+ __privateMethod(this, _negotiationFailed, negotiationFailed_fn).call(this, session, "re-offer", err);
742
807
  }
743
808
  };
809
+ _negotiationFailed = new WeakSet();
810
+ negotiationFailed_fn = function(session, what, err) {
811
+ const error = err instanceof Error ? err : new Error(String(err));
812
+ __privateMethod(this, _log, log_fn).call(this, `renegotiation failed (${what}) callId=${session.callId}: ${error.message}`);
813
+ console.warn(`[peer-webrtc] could not apply the peer's ${what} for call ${session.callId}: ${error.message}`);
814
+ this.emit("negotiationFailed", session.callId, error);
815
+ };
744
816
  _onAnswer = new WeakSet();
745
817
  onAnswer_fn = function(peerId, signal) {
746
818
  const session = __privateGet(this, _sessions).get(__privateMethod(this, _normId, normId_fn).call(this, signal.callId));
@@ -749,14 +821,14 @@ var PeerWebRTC = (() => {
749
821
  session.peerId = peerId;
750
822
  void (async () => {
751
823
  try {
752
- await session.pc.setRemoteDescription({ type: "answer", sdp: signal.sdp });
824
+ await session.pc.setRemoteDescription({ type: "answer", sdp: __privateMethod(this, _cleanInbound, cleanInbound_fn).call(this, signal.sdp, "answer") });
753
825
  session.hasReceivedSdp = true;
754
826
  if (session.state !== "connected")
755
827
  __privateMethod(this, _setState, setState_fn).call(this, session, "connecting");
756
828
  await __privateMethod(this, _drainRemoteCandidates, drainRemoteCandidates_fn).call(this, session);
757
829
  await __privateMethod(this, _flushLocalCandidates, flushLocalCandidates_fn).call(this, session);
758
830
  } catch (err) {
759
- __privateMethod(this, _log, log_fn).call(this, `setRemoteDescription(answer) failed: ${err.message}`);
831
+ __privateMethod(this, _negotiationFailed, negotiationFailed_fn).call(this, session, "answer", err);
760
832
  }
761
833
  })();
762
834
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decentnetwork/beagle",
3
- "version": "0.1.121",
3
+ "version": "0.1.122",
4
4
  "description": "Beagle — P2P chat, file transfer and calls for regular users, on the Decent Network. No admin privilege required.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -30,7 +30,7 @@
30
30
  "@decentnetwork/chat-components": "^0.1.3",
31
31
  "@decentnetwork/lan": "^0.1.283",
32
32
  "@decentnetwork/peer": "^0.1.168",
33
- "@decentnetwork/peer-webrtc": "^0.2.16",
33
+ "@decentnetwork/peer-webrtc": "^0.2.17",
34
34
  "js-yaml": "^4.1.0",
35
35
  "ws": "^8.21.1",
36
36
  "yargs": "^17.7.2"