@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.
@@ -3222,6 +3222,18 @@ var AbracadabraClient = class {
3222
3222
  auth: false
3223
3223
  });
3224
3224
  }
3225
+ /**
3226
+ * Fetch a short-lived anonymous pairing token for WebRTC signaling.
3227
+ * No authentication required. The token only grants access to `__pairing_*` rooms.
3228
+ */
3229
+ static async getPairingToken(serverUrl) {
3230
+ let base = serverUrl;
3231
+ while (base.endsWith("/")) base = base.slice(0, -1);
3232
+ const resp = await fetch(`${base}/auth/pairing-token`, { method: "POST" });
3233
+ if (!resp.ok) throw new Error(`Failed to fetch pairing token: ${resp.status}`);
3234
+ const { token } = await resp.json();
3235
+ return token;
3236
+ }
3225
3237
  /** Get encryption info for a document. */
3226
3238
  async getDocEncryption(docId) {
3227
3239
  return this.request("GET", `/docs/${encodeURIComponent(docId)}/encryption`);
@@ -8858,8 +8870,8 @@ var SignalingSocket = class extends EventEmitter {
8858
8870
  break;
8859
8871
  case "joined":
8860
8872
  this.emit("joined", {
8861
- peerId: msg.peer_id,
8862
- userId: msg.user_id,
8873
+ peer_id: msg.peer_id,
8874
+ user_id: msg.user_id,
8863
8875
  muted: msg.muted,
8864
8876
  video: msg.video,
8865
8877
  screen: msg.screen,
@@ -9928,7 +9940,7 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
9928
9940
  channel = pc.router.createChannel(channelName, { ordered: true });
9929
9941
  channel.onopen = () => {
9930
9942
  const data = new TextEncoder().encode(payload);
9931
- pc.router.send(channelName, data);
9943
+ pc.router.send(channelName, data).catch(() => {});
9932
9944
  };
9933
9945
  return;
9934
9946
  }
@@ -10014,9 +10026,14 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10014
10026
  this.e2eeChannels.set(peerId, e2ee);
10015
10027
  pc.router.setEncryptor(e2ee);
10016
10028
  pc.router.on("channelMessage", async ({ name, data }) => {
10017
- if (name === KEY_EXCHANGE_CHANNEL) {
10029
+ if (name === KEY_EXCHANGE_CHANNEL) try {
10018
10030
  const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
10019
10031
  await e2ee.handleKeyExchange(buf);
10032
+ } catch (err) {
10033
+ this.emit("e2eeFailed", {
10034
+ peerId,
10035
+ error: err
10036
+ });
10020
10037
  }
10021
10038
  });
10022
10039
  pc.router.on("channelOpen", ({ name, channel }) => {
@@ -10087,7 +10104,12 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10087
10104
  try {
10088
10105
  const sdp = await pc.createOffer();
10089
10106
  this.signaling?.sendOffer(peerId, sdp);
10090
- } catch {
10107
+ } catch (err) {
10108
+ this.emit("error", {
10109
+ type: "offer-failed",
10110
+ peerId,
10111
+ error: err
10112
+ });
10091
10113
  this.removePeer(peerId);
10092
10114
  }
10093
10115
  }
@@ -10105,7 +10127,12 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10105
10127
  try {
10106
10128
  const answerSdp = await pc.setRemoteOffer(sdp);
10107
10129
  this.signaling?.sendAnswer(from, answerSdp);
10108
- } catch {
10130
+ } catch (err) {
10131
+ this.emit("error", {
10132
+ type: "answer-failed",
10133
+ peerId: from,
10134
+ error: err
10135
+ });
10109
10136
  this.removePeer(from);
10110
10137
  }
10111
10138
  }
@@ -10297,6 +10324,7 @@ var ManualSignaling = class extends EventEmitter {
10297
10324
  const CODE_CHARSET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
10298
10325
  const CODE_LENGTH = 6;
10299
10326
  const PAIRING_TIMEOUT_MS = 300 * 1e3;
10327
+ const SIGNALING_CONNECT_TIMEOUT_MS = 5e3;
10300
10328
  function generatePairingCode() {
10301
10329
  const bytes = crypto.getRandomValues(new Uint8Array(CODE_LENGTH));
10302
10330
  return Array.from(bytes).map((b) => CODE_CHARSET[b % 32]).join("");
@@ -10314,6 +10342,7 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10314
10342
  this._destroyed = false;
10315
10343
  this._pendingRequest = null;
10316
10344
  this._connectedPeerId = null;
10345
+ this._usingFallback = false;
10317
10346
  this.role = role;
10318
10347
  this.pairingCode = pairingCode;
10319
10348
  }
@@ -10468,12 +10497,41 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10468
10497
  }
10469
10498
  this.removeAllListeners();
10470
10499
  }
10500
+ async resolveToken(serverUrl) {
10501
+ if (this.config.token && serverUrl === this.config.serverUrl) return this.config.token;
10502
+ let base = serverUrl ?? this.config.serverUrl;
10503
+ while (base.endsWith("/")) base = base.slice(0, -1);
10504
+ const resp = await fetch(`${base}/auth/pairing-token`, { method: "POST" });
10505
+ if (!resp.ok) throw new Error(`Failed to fetch pairing token: ${resp.status} ${resp.statusText}`);
10506
+ const { token } = await resp.json();
10507
+ return token;
10508
+ }
10471
10509
  start() {
10510
+ this.connectToServer(this.config.serverUrl);
10511
+ this.timeoutHandle = setTimeout(() => {
10512
+ if (!this._destroyed) {
10513
+ this.emit("error", /* @__PURE__ */ new Error("Pairing timed out"));
10514
+ this.destroy();
10515
+ }
10516
+ }, PAIRING_TIMEOUT_MS);
10517
+ }
10518
+ connectToServer(serverUrl, signalingUrl) {
10519
+ const roomId = codeToRoomId(this.pairingCode);
10520
+ const tokenPromise = this.resolveToken(serverUrl);
10521
+ const tokenFactory = async () => {
10522
+ const t = await tokenPromise;
10523
+ if (typeof t === "function") return await t();
10524
+ return t;
10525
+ };
10526
+ if (!this.config.iceServers) new AbracadabraClient({ url: serverUrl }).getIceServers().then((servers) => {
10527
+ if (servers.length > 0) this._resolvedIceServers = servers;
10528
+ });
10472
10529
  this.webrtc = new AbracadabraWebRTC({
10473
- docId: codeToRoomId(this.pairingCode),
10474
- url: this.config.serverUrl,
10475
- token: this.config.token,
10476
- iceServers: this.config.iceServers,
10530
+ docId: roomId,
10531
+ url: serverUrl,
10532
+ signalingUrl: signalingUrl ?? void 0,
10533
+ token: tokenFactory,
10534
+ iceServers: this.config.iceServers ?? this._resolvedIceServers,
10477
10535
  e2ee: this.config.e2ee,
10478
10536
  enableDocSync: false,
10479
10537
  enableAwarenessSync: false,
@@ -10481,6 +10539,10 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10481
10539
  autoConnect: false,
10482
10540
  WebSocketPolyfill: this.config.WebSocketPolyfill
10483
10541
  });
10542
+ let connected = false;
10543
+ this.webrtc.on("connected", () => {
10544
+ connected = true;
10545
+ });
10484
10546
  this.webrtc.on("e2eeEstablished", ({ peerId }) => {
10485
10547
  this._connectedPeerId = peerId;
10486
10548
  this.emit("connected");
@@ -10488,20 +10550,37 @@ var DevicePairingChannel = class DevicePairingChannel extends EventEmitter {
10488
10550
  this.webrtc.on("customMessage", ({ peerId, payload }) => {
10489
10551
  this.handleMessage(peerId, payload);
10490
10552
  });
10553
+ this.webrtc.on("e2eeFailed", ({ peerId, error }) => {
10554
+ if (!this._destroyed) this.emit("error", /* @__PURE__ */ new Error(`E2EE failed: ${error?.message ?? error}`));
10555
+ });
10556
+ this.webrtc.on("error", (err) => {
10557
+ if (!this._destroyed) this.emit("error", /* @__PURE__ */ new Error(`WebRTC: ${err?.type ?? err?.message ?? "unknown"}`));
10558
+ });
10491
10559
  this.webrtc.on("peerLeft", () => {
10492
10560
  if (!this._destroyed) this.emit("error", /* @__PURE__ */ new Error("Peer disconnected"));
10493
10561
  });
10494
10562
  this.webrtc.on("signalingError", (err) => {
10495
10563
  this.emit("error", /* @__PURE__ */ new Error(`Signaling: ${err.message}`));
10496
10564
  });
10497
- this.timeoutHandle = setTimeout(() => {
10498
- if (!this._destroyed) {
10499
- this.emit("error", /* @__PURE__ */ new Error("Pairing timed out"));
10500
- this.destroy();
10501
- }
10502
- }, PAIRING_TIMEOUT_MS);
10565
+ if (this.config.fallbackSignalingUrl && !signalingUrl) {
10566
+ const fallbackTimer = setTimeout(() => {
10567
+ if (this._destroyed || connected) return;
10568
+ if (this.webrtc) {
10569
+ this.webrtc.destroy();
10570
+ this.webrtc = null;
10571
+ }
10572
+ this._usingFallback = true;
10573
+ this.emit("fallback", { url: this.config.fallbackSignalingUrl });
10574
+ this.connectToServer(this.config.fallbackSignalingUrl);
10575
+ }, SIGNALING_CONNECT_TIMEOUT_MS);
10576
+ this.webrtc.on("connected", () => clearTimeout(fallbackTimer));
10577
+ }
10503
10578
  this.webrtc.connect();
10504
10579
  }
10580
+ /** Whether the connection fell back to the fallback signaling server. */
10581
+ get usingFallback() {
10582
+ return this._usingFallback;
10583
+ }
10505
10584
  sendMessage(msg) {
10506
10585
  if (!this.webrtc || !this._connectedPeerId) return;
10507
10586
  this.webrtc.sendCustomMessage(this._connectedPeerId, JSON.stringify(msg));
@@ -10749,26 +10828,41 @@ var IdentityDocProvider = class extends EventEmitter {
10749
10828
  });
10750
10829
  for (const { url, key } of targets) {
10751
10830
  if (this.providers.has(key)) continue;
10752
- this._connectToServer(key, url);
10831
+ this.connectToServer(key, url);
10753
10832
  }
10754
10833
  if (this.config.webrtc && !this.webrtc) this._connectWebRTC();
10755
10834
  }
10756
- _connectToServer(key, serverUrl) {
10835
+ connectToServer(key, serverUrl) {
10836
+ if (this._destroyed) return;
10837
+ const existingProvider = this.providers.get(key);
10838
+ if (existingProvider) {
10839
+ existingProvider.destroy();
10840
+ this.providers.delete(key);
10841
+ }
10842
+ const existingWs = this.websockets.get(key);
10843
+ if (existingWs) {
10844
+ existingWs.destroy();
10845
+ this.websockets.delete(key);
10846
+ }
10757
10847
  const token = this.config.tokens?.[serverUrl] ?? this.config.token ?? "";
10758
10848
  const ws = new AbracadabraWS({
10759
10849
  url: serverUrl.replace(/^http/, "ws").replace(/\/$/, "").concat("/ws"),
10760
10850
  WebSocketPolyfill: void 0
10761
10851
  });
10762
10852
  this.websockets.set(key, ws);
10763
- const provider = new AbracadabraProvider({
10853
+ const providerConfig = {
10764
10854
  name: this.docId,
10765
10855
  document: this.document,
10766
10856
  websocketProvider: ws,
10767
- token,
10768
10857
  serverAgnostic: true,
10769
10858
  disableOfflineStore: key !== "local" && key !== "sync" ? true : this.config.disableOfflineStore ?? false,
10770
10859
  ...this.config.providerDefaults
10771
- });
10860
+ };
10861
+ if (this.config.cryptoIdentity && this.config.signChallenge) {
10862
+ providerConfig.cryptoIdentity = this.config.cryptoIdentity;
10863
+ providerConfig.signChallenge = this.config.signChallenge;
10864
+ } else providerConfig.token = token;
10865
+ const provider = new AbracadabraProvider(providerConfig);
10772
10866
  provider.on("synced", () => this.emit("synced", { server: key }));
10773
10867
  provider.on("status", (data) => this.emit("status", {
10774
10868
  server: key,
@@ -10975,7 +11069,7 @@ var IdentityDocProvider = class extends EventEmitter {
10975
11069
  ...this.config,
10976
11070
  syncServerUrl: url
10977
11071
  };
10978
- this._connectToServer("sync", url);
11072
+ this.connectToServer("sync", url);
10979
11073
  }
10980
11074
  }
10981
11075
  getProvider(key) {