@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.
@@ -3252,6 +3252,30 @@ var AbracadabraClient = class {
3252
3252
  auth: false
3253
3253
  });
3254
3254
  }
3255
+ /** Request a device session token after successful crypto auth. Requires valid JWT. */
3256
+ async requestDeviceSession(opts) {
3257
+ return this.request("POST", "/auth/device-session", { body: {
3258
+ publicKey: opts.publicKey,
3259
+ deviceName: opts.deviceName
3260
+ } });
3261
+ }
3262
+ /** Exchange a device session token for a fresh JWT. No biometric/passkey needed. */
3263
+ async refreshWithDeviceSession(sessionToken) {
3264
+ const res = await this.request("POST", "/auth/refresh", {
3265
+ body: { sessionToken },
3266
+ auth: false
3267
+ });
3268
+ this.token = res.token;
3269
+ return res.token;
3270
+ }
3271
+ /** List active device sessions for the authenticated user. */
3272
+ async listDeviceSessions() {
3273
+ return (await this.request("GET", "/auth/device-session")).sessions;
3274
+ }
3275
+ /** Revoke a device session by ID. */
3276
+ async revokeDeviceSession(sessionId) {
3277
+ await this.request("DELETE", `/auth/device-session/${encodeURIComponent(sessionId)}`);
3278
+ }
3255
3279
  /**
3256
3280
  * Fetch a short-lived anonymous pairing token for WebRTC signaling.
3257
3281
  * No authentication required. The token only grants access to `__pairing_*` rooms.
@@ -7340,6 +7364,30 @@ var CryptoIdentityKeystore = class {
7340
7364
  db.close();
7341
7365
  }
7342
7366
  }
7367
+ /**
7368
+ * Updates the cached username for a given credential (or the first cached identity).
7369
+ * Call this after the user sets/changes their display name so it persists across devices.
7370
+ */
7371
+ async setUsername(username, credentialIdHint) {
7372
+ const db = await openDb$4();
7373
+ try {
7374
+ if (credentialIdHint) {
7375
+ const stored = await dbGet(db, credentialIdHint);
7376
+ if (stored) await dbPut(db, credentialIdHint, {
7377
+ ...stored,
7378
+ username
7379
+ });
7380
+ } else {
7381
+ const all = await dbGetAll(db);
7382
+ if (all.length > 0) await dbPut(db, all[0].key, {
7383
+ ...all[0].value,
7384
+ username
7385
+ });
7386
+ }
7387
+ } finally {
7388
+ db.close();
7389
+ }
7390
+ }
7343
7391
  /** Returns true if an identity is cached in IndexedDB. */
7344
7392
  async hasIdentity() {
7345
7393
  const db = await openDb$4();
@@ -9984,6 +10032,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
9984
10032
  this.yjsChannels = /* @__PURE__ */ new Map();
9985
10033
  this.fileChannels = /* @__PURE__ */ new Map();
9986
10034
  this.e2eeChannels = /* @__PURE__ */ new Map();
10035
+ this._resolvedE2ee = null;
10036
+ this._resolveE2eePromise = null;
9987
10037
  this.peers = /* @__PURE__ */ new Map();
9988
10038
  this.localPeerId = null;
9989
10039
  this.isConnected = false;
@@ -10124,6 +10174,8 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10124
10174
  this.signaling.destroy();
10125
10175
  this.signaling = null;
10126
10176
  }
10177
+ this._resolvedE2ee = null;
10178
+ this._resolveE2eePromise = null;
10127
10179
  this.removeAllListeners();
10128
10180
  }
10129
10181
  setMuted(muted) {
@@ -10250,25 +10302,69 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10250
10302
  this.attachDataHandlers(peerId, pc);
10251
10303
  return pc;
10252
10304
  }
10305
+ /** Resolve the E2EE identity, supporting both pre-resolved objects and lazy factories. */
10306
+ async resolveE2ee() {
10307
+ if (this._resolvedE2ee) return this._resolvedE2ee;
10308
+ if (!this.config.e2ee) return null;
10309
+ if (typeof this.config.e2ee === "function") {
10310
+ if (!this._resolveE2eePromise) this._resolveE2eePromise = this.config.e2ee().then((id) => {
10311
+ this._resolvedE2ee = id;
10312
+ return id;
10313
+ });
10314
+ return this._resolveE2eePromise;
10315
+ }
10316
+ this._resolvedE2ee = this.config.e2ee;
10317
+ return this._resolvedE2ee;
10318
+ }
10253
10319
  attachDataHandlers(peerId, pc) {
10254
- if (this.config.e2ee) {
10255
- const e2ee = new E2EEChannel(this.config.e2ee, this.config.docId);
10320
+ if (!this.config.e2ee) {
10321
+ this.startDataSync(peerId, pc);
10322
+ return;
10323
+ }
10324
+ let e2ee = null;
10325
+ const pendingMessages = [];
10326
+ let pendingKeyExchangeChannel = null;
10327
+ pc.router.on("channelMessage", async ({ name, data }) => {
10328
+ if (name !== KEY_EXCHANGE_CHANNEL) return;
10329
+ const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
10330
+ if (!e2ee) {
10331
+ pendingMessages.push(buf);
10332
+ return;
10333
+ }
10334
+ try {
10335
+ await e2ee.handleKeyExchange(buf);
10336
+ } catch (err) {
10337
+ this.emit("e2eeFailed", {
10338
+ peerId,
10339
+ error: err
10340
+ });
10341
+ }
10342
+ });
10343
+ pc.router.on("channelOpen", ({ name, channel }) => {
10344
+ if (name !== KEY_EXCHANGE_CHANNEL) return;
10345
+ if (!e2ee) {
10346
+ pendingKeyExchangeChannel = channel;
10347
+ return;
10348
+ }
10349
+ channel.send(e2ee.getKeyExchangeMessage());
10350
+ });
10351
+ this.resolveE2ee().then(async (identity) => {
10352
+ if (!identity) {
10353
+ this.startDataSync(peerId, pc);
10354
+ return;
10355
+ }
10356
+ e2ee = new E2EEChannel(identity, this.config.docId);
10256
10357
  this.e2eeChannels.set(peerId, e2ee);
10257
10358
  pc.router.setEncryptor(e2ee);
10258
- pc.router.on("channelMessage", async ({ name, data }) => {
10259
- if (name === KEY_EXCHANGE_CHANNEL) try {
10260
- const buf = data instanceof ArrayBuffer ? new Uint8Array(data) : data;
10261
- await e2ee.handleKeyExchange(buf);
10262
- } catch (err) {
10263
- this.emit("e2eeFailed", {
10264
- peerId,
10265
- error: err
10266
- });
10267
- }
10268
- });
10269
- pc.router.on("channelOpen", ({ name, channel }) => {
10270
- if (name === KEY_EXCHANGE_CHANNEL) channel.send(e2ee.getKeyExchangeMessage());
10271
- });
10359
+ if (pendingKeyExchangeChannel) pendingKeyExchangeChannel.send(e2ee.getKeyExchangeMessage());
10360
+ for (const msg of pendingMessages) try {
10361
+ await e2ee.handleKeyExchange(msg);
10362
+ } catch (err) {
10363
+ this.emit("e2eeFailed", {
10364
+ peerId,
10365
+ error: err
10366
+ });
10367
+ }
10272
10368
  e2ee.on("established", () => {
10273
10369
  this.emit("e2eeEstablished", { peerId });
10274
10370
  this.startDataSync(peerId, pc);
@@ -10279,7 +10375,13 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10279
10375
  error: err
10280
10376
  });
10281
10377
  });
10282
- } else this.startDataSync(peerId, pc);
10378
+ }).catch((err) => {
10379
+ this.emit("e2eeFailed", {
10380
+ peerId,
10381
+ error: err
10382
+ });
10383
+ this.startDataSync(peerId, pc);
10384
+ });
10283
10385
  }
10284
10386
  startDataSync(peerId, pc) {
10285
10387
  if (this.config.document && this.config.enableDocSync) {