@abraca/dabra 1.0.22 → 1.0.24

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.
@@ -3252,6 +3252,18 @@ var AbracadabraClient = class {
3252
3252
  auth: false
3253
3253
  });
3254
3254
  }
3255
+ /**
3256
+ * Fetch a short-lived anonymous pairing token for WebRTC signaling.
3257
+ * No authentication required. The token only grants access to `__pairing_*` rooms.
3258
+ */
3259
+ static async getPairingToken(serverUrl) {
3260
+ let base = serverUrl;
3261
+ while (base.endsWith("/")) base = base.slice(0, -1);
3262
+ const resp = await fetch(`${base}/auth/pairing-token`, { method: "POST" });
3263
+ if (!resp.ok) throw new Error(`Failed to fetch pairing token: ${resp.status}`);
3264
+ const { token } = await resp.json();
3265
+ return token;
3266
+ }
3255
3267
  /** Get encryption info for a document. */
3256
3268
  async getDocEncryption(docId) {
3257
3269
  return this.request("GET", `/docs/${encodeURIComponent(docId)}/encryption`);
@@ -8910,8 +8922,8 @@ var SignalingSocket = class extends EventEmitter {
8910
8922
  break;
8911
8923
  case "joined":
8912
8924
  this.emit("joined", {
8913
- peerId: msg.peer_id,
8914
- userId: msg.user_id,
8925
+ peer_id: msg.peer_id,
8926
+ user_id: msg.user_id,
8915
8927
  muted: msg.muted,
8916
8928
  video: msg.video,
8917
8929
  screen: msg.screen,
@@ -9980,7 +9992,7 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
9980
9992
  channel = pc.router.createChannel(channelName, { ordered: true });
9981
9993
  channel.onopen = () => {
9982
9994
  const data = new TextEncoder().encode(payload);
9983
- pc.router.send(channelName, data);
9995
+ pc.router.send(channelName, data).catch(() => {});
9984
9996
  };
9985
9997
  return;
9986
9998
  }
@@ -10066,9 +10078,14 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10066
10078
  this.e2eeChannels.set(peerId, e2ee);
10067
10079
  pc.router.setEncryptor(e2ee);
10068
10080
  pc.router.on("channelMessage", async ({ name, data }) => {
10069
- if (name === KEY_EXCHANGE_CHANNEL) {
10081
+ if (name === KEY_EXCHANGE_CHANNEL) try {
10070
10082
  const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
10071
10083
  await e2ee.handleKeyExchange(buf);
10084
+ } catch (err) {
10085
+ this.emit("e2eeFailed", {
10086
+ peerId,
10087
+ error: err
10088
+ });
10072
10089
  }
10073
10090
  });
10074
10091
  pc.router.on("channelOpen", ({ name, channel }) => {
@@ -10139,7 +10156,12 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10139
10156
  try {
10140
10157
  const sdp = await pc.createOffer();
10141
10158
  this.signaling?.sendOffer(peerId, sdp);
10142
- } catch {
10159
+ } catch (err) {
10160
+ this.emit("error", {
10161
+ type: "offer-failed",
10162
+ peerId,
10163
+ error: err
10164
+ });
10143
10165
  this.removePeer(peerId);
10144
10166
  }
10145
10167
  }
@@ -10157,7 +10179,12 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10157
10179
  try {
10158
10180
  const answerSdp = await pc.setRemoteOffer(sdp);
10159
10181
  this.signaling?.sendAnswer(from, answerSdp);
10160
- } catch {
10182
+ } catch (err) {
10183
+ this.emit("error", {
10184
+ type: "answer-failed",
10185
+ peerId: from,
10186
+ error: err
10187
+ });
10161
10188
  this.removePeer(from);
10162
10189
  }
10163
10190
  }
@@ -10349,6 +10376,7 @@ var ManualSignaling = class extends EventEmitter {
10349
10376
  const CODE_CHARSET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
10350
10377
  const CODE_LENGTH = 6;
10351
10378
  const PAIRING_TIMEOUT_MS = 300 * 1e3;
10379
+ const SIGNALING_CONNECT_TIMEOUT_MS = 5e3;
10352
10380
  function generatePairingCode() {
10353
10381
  const bytes = crypto.getRandomValues(new Uint8Array(CODE_LENGTH));
10354
10382
  return Array.from(bytes).map((b) => CODE_CHARSET[b % 32]).join("");
@@ -10366,6 +10394,7 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10366
10394
  this._destroyed = false;
10367
10395
  this._pendingRequest = null;
10368
10396
  this._connectedPeerId = null;
10397
+ this._usingFallback = false;
10369
10398
  this.role = role;
10370
10399
  this.pairingCode = pairingCode;
10371
10400
  }
@@ -10520,12 +10549,41 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10520
10549
  }
10521
10550
  this.removeAllListeners();
10522
10551
  }
10552
+ async resolveToken(serverUrl) {
10553
+ if (this.config.token && serverUrl === this.config.serverUrl) return this.config.token;
10554
+ let base = serverUrl ?? this.config.serverUrl;
10555
+ while (base.endsWith("/")) base = base.slice(0, -1);
10556
+ const resp = await fetch(`${base}/auth/pairing-token`, { method: "POST" });
10557
+ if (!resp.ok) throw new Error(`Failed to fetch pairing token: ${resp.status} ${resp.statusText}`);
10558
+ const { token } = await resp.json();
10559
+ return token;
10560
+ }
10523
10561
  start() {
10562
+ this.connectToServer(this.config.serverUrl);
10563
+ this.timeoutHandle = setTimeout(() => {
10564
+ if (!this._destroyed) {
10565
+ this.emit("error", /* @__PURE__ */ new Error("Pairing timed out"));
10566
+ this.destroy();
10567
+ }
10568
+ }, PAIRING_TIMEOUT_MS);
10569
+ }
10570
+ connectToServer(serverUrl, signalingUrl) {
10571
+ const roomId = codeToRoomId(this.pairingCode);
10572
+ const tokenPromise = this.resolveToken(serverUrl);
10573
+ const tokenFactory = async () => {
10574
+ const t = await tokenPromise;
10575
+ if (typeof t === "function") return await t();
10576
+ return t;
10577
+ };
10578
+ if (!this.config.iceServers) new AbracadabraClient({ url: serverUrl }).getIceServers().then((servers) => {
10579
+ if (servers.length > 0) this._resolvedIceServers = servers;
10580
+ });
10524
10581
  this.webrtc = new AbracadabraWebRTC({
10525
- docId: codeToRoomId(this.pairingCode),
10526
- url: this.config.serverUrl,
10527
- token: this.config.token,
10528
- iceServers: this.config.iceServers,
10582
+ docId: roomId,
10583
+ url: serverUrl,
10584
+ signalingUrl: signalingUrl ?? void 0,
10585
+ token: tokenFactory,
10586
+ iceServers: this.config.iceServers ?? this._resolvedIceServers,
10529
10587
  e2ee: this.config.e2ee,
10530
10588
  enableDocSync: false,
10531
10589
  enableAwarenessSync: false,
@@ -10533,6 +10591,10 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10533
10591
  autoConnect: false,
10534
10592
  WebSocketPolyfill: this.config.WebSocketPolyfill
10535
10593
  });
10594
+ let connected = false;
10595
+ this.webrtc.on("connected", () => {
10596
+ connected = true;
10597
+ });
10536
10598
  this.webrtc.on("e2eeEstablished", ({ peerId }) => {
10537
10599
  this._connectedPeerId = peerId;
10538
10600
  this.emit("connected");
@@ -10540,20 +10602,37 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10540
10602
  this.webrtc.on("customMessage", ({ peerId, payload }) => {
10541
10603
  this.handleMessage(peerId, payload);
10542
10604
  });
10605
+ this.webrtc.on("e2eeFailed", ({ peerId, error }) => {
10606
+ if (!this._destroyed) this.emit("error", /* @__PURE__ */ new Error(`E2EE failed: ${error?.message ?? error}`));
10607
+ });
10608
+ this.webrtc.on("error", (err) => {
10609
+ if (!this._destroyed) this.emit("error", /* @__PURE__ */ new Error(`WebRTC: ${err?.type ?? err?.message ?? "unknown"}`));
10610
+ });
10543
10611
  this.webrtc.on("peerLeft", () => {
10544
10612
  if (!this._destroyed) this.emit("error", /* @__PURE__ */ new Error("Peer disconnected"));
10545
10613
  });
10546
10614
  this.webrtc.on("signalingError", (err) => {
10547
10615
  this.emit("error", /* @__PURE__ */ new Error(`Signaling: ${err.message}`));
10548
10616
  });
10549
- this.timeoutHandle = setTimeout(() => {
10550
- if (!this._destroyed) {
10551
- this.emit("error", /* @__PURE__ */ new Error("Pairing timed out"));
10552
- this.destroy();
10553
- }
10554
- }, PAIRING_TIMEOUT_MS);
10617
+ if (this.config.fallbackSignalingUrl && !signalingUrl) {
10618
+ const fallbackTimer = setTimeout(() => {
10619
+ if (this._destroyed || connected) return;
10620
+ if (this.webrtc) {
10621
+ this.webrtc.destroy();
10622
+ this.webrtc = null;
10623
+ }
10624
+ this._usingFallback = true;
10625
+ this.emit("fallback", { url: this.config.fallbackSignalingUrl });
10626
+ this.connectToServer(this.config.fallbackSignalingUrl);
10627
+ }, SIGNALING_CONNECT_TIMEOUT_MS);
10628
+ this.webrtc.on("connected", () => clearTimeout(fallbackTimer));
10629
+ }
10555
10630
  this.webrtc.connect();
10556
10631
  }
10632
+ /** Whether the connection fell back to the fallback signaling server. */
10633
+ get usingFallback() {
10634
+ return this._usingFallback;
10635
+ }
10557
10636
  sendMessage(msg) {
10558
10637
  if (!this.webrtc || !this._connectedPeerId) return;
10559
10638
  this.webrtc.sendCustomMessage(this._connectedPeerId, JSON.stringify(msg));
@@ -10801,26 +10880,41 @@ var IdentityDocProvider = class extends EventEmitter {
10801
10880
  });
10802
10881
  for (const { url, key } of targets) {
10803
10882
  if (this.providers.has(key)) continue;
10804
- this._connectToServer(key, url);
10883
+ this.connectToServer(key, url);
10805
10884
  }
10806
10885
  if (this.config.webrtc && !this.webrtc) this._connectWebRTC();
10807
10886
  }
10808
- _connectToServer(key, serverUrl) {
10887
+ connectToServer(key, serverUrl) {
10888
+ if (this._destroyed) return;
10889
+ const existingProvider = this.providers.get(key);
10890
+ if (existingProvider) {
10891
+ existingProvider.destroy();
10892
+ this.providers.delete(key);
10893
+ }
10894
+ const existingWs = this.websockets.get(key);
10895
+ if (existingWs) {
10896
+ existingWs.destroy();
10897
+ this.websockets.delete(key);
10898
+ }
10809
10899
  const token = this.config.tokens?.[serverUrl] ?? this.config.token ?? "";
10810
10900
  const ws = new AbracadabraWS({
10811
10901
  url: serverUrl.replace(/^http/, "ws").replace(/\/$/, "").concat("/ws"),
10812
10902
  WebSocketPolyfill: void 0
10813
10903
  });
10814
10904
  this.websockets.set(key, ws);
10815
- const provider = new AbracadabraProvider({
10905
+ const providerConfig = {
10816
10906
  name: this.docId,
10817
10907
  document: this.document,
10818
10908
  websocketProvider: ws,
10819
- token,
10820
10909
  serverAgnostic: true,
10821
10910
  disableOfflineStore: key !== "local" && key !== "sync" ? true : this.config.disableOfflineStore ?? false,
10822
10911
  ...this.config.providerDefaults
10823
- });
10912
+ };
10913
+ if (this.config.cryptoIdentity && this.config.signChallenge) {
10914
+ providerConfig.cryptoIdentity = this.config.cryptoIdentity;
10915
+ providerConfig.signChallenge = this.config.signChallenge;
10916
+ } else providerConfig.token = token;
10917
+ const provider = new AbracadabraProvider(providerConfig);
10824
10918
  provider.on("synced", () => this.emit("synced", { server: key }));
10825
10919
  provider.on("status", (data) => this.emit("status", {
10826
10920
  server: key,
@@ -11027,7 +11121,7 @@ var IdentityDocProvider = class extends EventEmitter {
11027
11121
  ...this.config,
11028
11122
  syncServerUrl: url
11029
11123
  };
11030
- this._connectToServer("sync", url);
11124
+ this.connectToServer("sync", url);
11031
11125
  }
11032
11126
  }
11033
11127
  getProvider(key) {