@abraca/dabra 2.20.0 → 2.22.0

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.
@@ -2801,8 +2801,8 @@ var AbracadabraBaseProvider = class extends EventEmitter {
2801
2801
  this.configuration.websocketProvider.on("destroy", this.configuration.onDestroy);
2802
2802
  this.configuration.websocketProvider.on("destroy", this.forwardDestroy);
2803
2803
  this.configuration.websocketProvider.on("rateLimited", this.forwardRateLimited);
2804
- this.configuration.websocketProvider.attach(this);
2805
2804
  this._isAttached = true;
2805
+ this.configuration.websocketProvider.attach(this);
2806
2806
  }
2807
2807
  permissionDeniedHandler(reason) {
2808
2808
  this.emit("authenticationFailed", { reason });
@@ -2854,9 +2854,12 @@ function txPromise$2(store, request) {
2854
2854
  var OfflineStore = class {
2855
2855
  /**
2856
2856
  * @param docId The document UUID.
2857
- * @param serverOrigin Hostname of the server (e.g. "abra.cou.sh").
2858
- * When provided the IndexedDB database is namespaced
2859
- * per-server, preventing cross-server data contamination.
2857
+ * @param serverOrigin Host of the server, including a non-default port
2858
+ * (e.g. "abra.cou.sh", "localhost:3001"). When provided
2859
+ * the IndexedDB database is namespaced per-server,
2860
+ * preventing cross-server data contamination — the port
2861
+ * matters because two same-host servers sharing the
2862
+ * default root_doc_id would otherwise collide.
2860
2863
  */
2861
2864
  constructor(docId, serverOrigin) {
2862
2865
  this.db = null;
@@ -3094,7 +3097,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
3094
3097
  static deriveServerOrigin(config, client) {
3095
3098
  try {
3096
3099
  const url = config.url ?? config.websocketProvider?.url ?? client?.wsUrl;
3097
- if (url) return new URL(url).hostname;
3100
+ if (url) return new URL(url).host;
3098
3101
  } catch {}
3099
3102
  }
3100
3103
  /**
@@ -10512,6 +10515,20 @@ var IdentityDocProvider = class extends EventEmitter {
10512
10515
  function fromBase64$3(b64) {
10513
10516
  return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
10514
10517
  }
10518
+ /**
10519
+ * Decode a base64url-encoded string (e.g. a JWT header/payload segment) to
10520
+ * UTF-8 text. `atob` only accepts standard base64, so we translate the
10521
+ * url-safe alphabet (`-`/`_`) and re-pad before decoding — otherwise a
10522
+ * perfectly valid token whose payload happens to contain `-` or `_` throws
10523
+ * and gets misclassified as invalid/expired.
10524
+ */
10525
+ function decodeBase64UrlToString(b64url) {
10526
+ let b64 = b64url.replace(/-/g, "+").replace(/_/g, "/");
10527
+ const pad = b64.length % 4;
10528
+ if (pad) b64 += "=".repeat(4 - pad);
10529
+ const bytes = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
10530
+ return new TextDecoder().decode(bytes);
10531
+ }
10515
10532
  var AbracadabraClient = class {
10516
10533
  constructor(config) {
10517
10534
  this.rootListInflight = /* @__PURE__ */ new Map();
@@ -10539,7 +10556,7 @@ var AbracadabraClient = class {
10539
10556
  if (!this._token) return false;
10540
10557
  try {
10541
10558
  const [, payload] = this._token.split(".");
10542
- const { exp } = JSON.parse(atob(payload));
10559
+ const { exp } = JSON.parse(decodeBase64UrlToString(payload));
10543
10560
  return typeof exp === "number" && exp * 1e3 > Date.now();
10544
10561
  } catch {
10545
10562
  return false;
@@ -10629,9 +10646,17 @@ var AbracadabraClient = class {
10629
10646
  async renameKey(keyId, deviceName) {
10630
10647
  await this.request("PATCH", `/auth/keys/${encodeURIComponent(keyId)}`, { body: { deviceName } });
10631
10648
  }
10632
- /** Revoke a public key by its ID. */
10649
+ /**
10650
+ * Revoke a public key by its ID. The server treats this as a panic
10651
+ * action: every outstanding JWT for the account is invalidated (a stolen
10652
+ * device's token must die immediately, and tokens carry no per-key
10653
+ * claim), and a fresh token for THIS session is returned and adopted
10654
+ * automatically — so the device performing the revocation stays
10655
+ * authenticated while every other device silently re-auths.
10656
+ */
10633
10657
  async revokeKey(keyId) {
10634
- await this.request("DELETE", `/auth/keys/${encodeURIComponent(keyId)}`);
10658
+ const res = await this.request("DELETE", `/auth/keys/${encodeURIComponent(keyId)}`);
10659
+ if (res && typeof res === "object" && typeof res.token === "string") this.token = res.token;
10635
10660
  }
10636
10661
  /** Create a single-use device invite code for pairing a new device to this account. */
10637
10662
  async createDeviceInvite(opts) {
@@ -11761,7 +11786,11 @@ var TokenManager = class extends EventEmitter {
11761
11786
  try {
11762
11787
  const [, payload] = jwt.split(".");
11763
11788
  if (!payload) return null;
11764
- const { exp } = JSON.parse(atob(payload));
11789
+ let b64 = payload.replace(/-/g, "+").replace(/_/g, "/");
11790
+ const pad = b64.length % 4;
11791
+ if (pad) b64 += "=".repeat(4 - pad);
11792
+ const json = new TextDecoder().decode(Uint8Array.from(atob(b64), (c) => c.charCodeAt(0)));
11793
+ const { exp } = JSON.parse(json);
11765
11794
  return typeof exp === "number" ? exp : null;
11766
11795
  } catch {
11767
11796
  return null;
@@ -15241,7 +15270,10 @@ var FileBlobStore = class FileBlobStore extends EventEmitter {
15241
15270
  */
15242
15271
  const HKDF_INFO = new TextEncoder().encode("abracadabra-dockey-v1");
15243
15272
  function fromBase64$2(b64) {
15244
- return Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
15273
+ let normalized = b64.replace(/-/g, "+").replace(/_/g, "/");
15274
+ const pad = normalized.length % 4;
15275
+ if (pad) normalized += "=".repeat(4 - pad);
15276
+ return Uint8Array.from(atob(normalized), (c) => c.charCodeAt(0));
15245
15277
  }
15246
15278
  var DocKeyManager = class DocKeyManager {
15247
15279
  constructor() {
@@ -15298,7 +15330,7 @@ var DocKeyManager = class DocKeyManager {
15298
15330
  if (me.publicKey) {
15299
15331
  const primaryKey = (await client.listUserKeys(me.id))[0];
15300
15332
  if (primaryKey?.x25519Key) {
15301
- const x25519Pub = fromBase64$2(primaryKey.x25519Key.replace(/-/g, "+").replace(/_/g, "/"));
15333
+ const x25519Pub = fromBase64$2(primaryKey.x25519Key);
15302
15334
  const rewrapped = await this.wrapKeyForRecipient(docKey, x25519Pub, docId);
15303
15335
  const b64 = (() => {
15304
15336
  let s = "";
@@ -16146,7 +16178,7 @@ var BackgroundSyncManager = class extends EventEmitter {
16146
16178
  };
16147
16179
  let serverOrigin = "default";
16148
16180
  try {
16149
- serverOrigin = new URL(client.baseUrl ?? "").hostname;
16181
+ serverOrigin = new URL(client.baseUrl ?? "").host;
16150
16182
  } catch {}
16151
16183
  this.persistence = new BackgroundSyncPersistence(serverOrigin);
16152
16184
  this.semaphore = new Semaphore(this.opts.concurrency);
@@ -16293,7 +16325,7 @@ var BackgroundSyncManager = class extends EventEmitter {
16293
16325
  for (const docId of treeMap.keys()) docIds.add(docId);
16294
16326
  let serverOrigin;
16295
16327
  try {
16296
- serverOrigin = new URL(this.client.baseUrl ?? "").hostname;
16328
+ serverOrigin = new URL(this.client.baseUrl ?? "").host;
16297
16329
  } catch {}
16298
16330
  const clearPromises = Array.from(docIds).map(async (docId) => {
16299
16331
  try {