@abraca/dabra 1.2.0 → 1.3.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.
@@ -3222,6 +3222,30 @@ var AbracadabraClient = class {
3222
3222
  auth: false
3223
3223
  });
3224
3224
  }
3225
+ /** Request a device session token after successful crypto auth. Requires valid JWT. */
3226
+ async requestDeviceSession(opts) {
3227
+ return this.request("POST", "/auth/device-session", { body: {
3228
+ publicKey: opts.publicKey,
3229
+ deviceName: opts.deviceName
3230
+ } });
3231
+ }
3232
+ /** Exchange a device session token for a fresh JWT. No biometric/passkey needed. */
3233
+ async refreshWithDeviceSession(sessionToken) {
3234
+ const res = await this.request("POST", "/auth/refresh", {
3235
+ body: { sessionToken },
3236
+ auth: false
3237
+ });
3238
+ this.token = res.token;
3239
+ return res.token;
3240
+ }
3241
+ /** List active device sessions for the authenticated user. */
3242
+ async listDeviceSessions() {
3243
+ return (await this.request("GET", "/auth/device-session")).sessions;
3244
+ }
3245
+ /** Revoke a device session by ID. */
3246
+ async revokeDeviceSession(sessionId) {
3247
+ await this.request("DELETE", `/auth/device-session/${encodeURIComponent(sessionId)}`);
3248
+ }
3225
3249
  /**
3226
3250
  * Fetch a short-lived anonymous pairing token for WebRTC signaling.
3227
3251
  * No authentication required. The token only grants access to `__pairing_*` rooms.
@@ -7310,6 +7334,30 @@ var CryptoIdentityKeystore = class {
7310
7334
  db.close();
7311
7335
  }
7312
7336
  }
7337
+ /**
7338
+ * Updates the cached username for a given credential (or the first cached identity).
7339
+ * Call this after the user sets/changes their display name so it persists across devices.
7340
+ */
7341
+ async setUsername(username, credentialIdHint) {
7342
+ const db = await openDb$4();
7343
+ try {
7344
+ if (credentialIdHint) {
7345
+ const stored = await dbGet(db, credentialIdHint);
7346
+ if (stored) await dbPut(db, credentialIdHint, {
7347
+ ...stored,
7348
+ username
7349
+ });
7350
+ } else {
7351
+ const all = await dbGetAll(db);
7352
+ if (all.length > 0) await dbPut(db, all[0].key, {
7353
+ ...all[0].value,
7354
+ username
7355
+ });
7356
+ }
7357
+ } finally {
7358
+ db.close();
7359
+ }
7360
+ }
7313
7361
  /** Returns true if an identity is cached in IndexedDB. */
7314
7362
  async hasIdentity() {
7315
7363
  const db = await openDb$4();
@@ -9932,6 +9980,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
9932
9980
  this.yjsChannels = /* @__PURE__ */ new Map();
9933
9981
  this.fileChannels = /* @__PURE__ */ new Map();
9934
9982
  this.e2eeChannels = /* @__PURE__ */ new Map();
9983
+ this._resolvedE2ee = null;
9984
+ this._resolveE2eePromise = null;
9935
9985
  this.peers = /* @__PURE__ */ new Map();
9936
9986
  this.localPeerId = null;
9937
9987
  this.isConnected = false;
@@ -10072,6 +10122,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10072
10122
  this.signaling.destroy();
10073
10123
  this.signaling = null;
10074
10124
  }
10125
+ this._resolvedE2ee = null;
10126
+ this._resolveE2eePromise = null;
10075
10127
  this.removeAllListeners();
10076
10128
  }
10077
10129
  setMuted(muted) {
@@ -10198,25 +10250,69 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10198
10250
  this.attachDataHandlers(peerId, pc);
10199
10251
  return pc;
10200
10252
  }
10253
+ /** Resolve the E2EE identity, supporting both pre-resolved objects and lazy factories. */
10254
+ async resolveE2ee() {
10255
+ if (this._resolvedE2ee) return this._resolvedE2ee;
10256
+ if (!this.config.e2ee) return null;
10257
+ if (typeof this.config.e2ee === "function") {
10258
+ if (!this._resolveE2eePromise) this._resolveE2eePromise = this.config.e2ee().then((id) => {
10259
+ this._resolvedE2ee = id;
10260
+ return id;
10261
+ });
10262
+ return this._resolveE2eePromise;
10263
+ }
10264
+ this._resolvedE2ee = this.config.e2ee;
10265
+ return this._resolvedE2ee;
10266
+ }
10201
10267
  attachDataHandlers(peerId, pc) {
10202
- if (this.config.e2ee) {
10203
- const e2ee = new E2EEChannel(this.config.e2ee, this.config.docId);
10268
+ if (!this.config.e2ee) {
10269
+ this.startDataSync(peerId, pc);
10270
+ return;
10271
+ }
10272
+ let e2ee = null;
10273
+ const pendingMessages = [];
10274
+ let pendingKeyExchangeChannel = null;
10275
+ pc.router.on("channelMessage", async ({ name, data }) => {
10276
+ if (name !== KEY_EXCHANGE_CHANNEL) return;
10277
+ const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
10278
+ if (!e2ee) {
10279
+ pendingMessages.push(buf);
10280
+ return;
10281
+ }
10282
+ try {
10283
+ await e2ee.handleKeyExchange(buf);
10284
+ } catch (err) {
10285
+ this.emit("e2eeFailed", {
10286
+ peerId,
10287
+ error: err
10288
+ });
10289
+ }
10290
+ });
10291
+ pc.router.on("channelOpen", ({ name, channel }) => {
10292
+ if (name !== KEY_EXCHANGE_CHANNEL) return;
10293
+ if (!e2ee) {
10294
+ pendingKeyExchangeChannel = channel;
10295
+ return;
10296
+ }
10297
+ channel.send(e2ee.getKeyExchangeMessage());
10298
+ });
10299
+ this.resolveE2ee().then(async (identity) => {
10300
+ if (!identity) {
10301
+ this.startDataSync(peerId, pc);
10302
+ return;
10303
+ }
10304
+ e2ee = new E2EEChannel(identity, this.config.docId);
10204
10305
  this.e2eeChannels.set(peerId, e2ee);
10205
10306
  pc.router.setEncryptor(e2ee);
10206
- pc.router.on("channelMessage", async ({ name, data }) => {
10207
- if (name === KEY_EXCHANGE_CHANNEL) try {
10208
- const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
10209
- await e2ee.handleKeyExchange(buf);
10210
- } catch (err) {
10211
- this.emit("e2eeFailed", {
10212
- peerId,
10213
- error: err
10214
- });
10215
- }
10216
- });
10217
- pc.router.on("channelOpen", ({ name, channel }) => {
10218
- if (name === KEY_EXCHANGE_CHANNEL) channel.send(e2ee.getKeyExchangeMessage());
10219
- });
10307
+ if (pendingKeyExchangeChannel) pendingKeyExchangeChannel.send(e2ee.getKeyExchangeMessage());
10308
+ for (const msg of pendingMessages) try {
10309
+ await e2ee.handleKeyExchange(msg);
10310
+ } catch (err) {
10311
+ this.emit("e2eeFailed", {
10312
+ peerId,
10313
+ error: err
10314
+ });
10315
+ }
10220
10316
  e2ee.on("established", () => {
10221
10317
  this.emit("e2eeEstablished", { peerId });
10222
10318
  this.startDataSync(peerId, pc);
@@ -10227,7 +10323,13 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10227
10323
  error: err
10228
10324
  });
10229
10325
  });
10230
- } else this.startDataSync(peerId, pc);
10326
+ }).catch((err) => {
10327
+ this.emit("e2eeFailed", {
10328
+ peerId,
10329
+ error: err
10330
+ });
10331
+ this.startDataSync(peerId, pc);
10332
+ });
10231
10333
  }
10232
10334
  startDataSync(peerId, pc) {
10233
10335
  if (this.config.document && this.config.enableDocSync) {