@abraca/dabra 0.6.0 → 0.8.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.
@@ -1,8 +1,18 @@
1
- import { WsReadyStates, awarenessStatesToArray, readAuthMessage, writeAuthentication } from "@abraca/dabra-common";
2
1
  import * as Y from "yjs";
3
2
  import { retry } from "@lifeomic/attempt";
4
3
  import * as ed from "@noble/ed25519";
5
4
 
5
+ //#region packages/provider/src/awarenessStatesToArray.ts
6
+ const awarenessStatesToArray = (states) => {
7
+ return Array.from(states.entries()).map(([key, value]) => {
8
+ return {
9
+ clientId: key,
10
+ ...value
11
+ };
12
+ });
13
+ };
14
+
15
+ //#endregion
6
16
  //#region node_modules/lib0/math.js
7
17
  /**
8
18
  * Common Math expressions.
@@ -1024,6 +1034,37 @@ var EventEmitter = class {
1024
1034
  }
1025
1035
  };
1026
1036
 
1037
+ //#endregion
1038
+ //#region packages/provider/src/types.ts
1039
+ /**
1040
+ * State of the WebSocket connection.
1041
+ * https://developer.mozilla.org/de/docs/Web/API/WebSocket/readyState
1042
+ */
1043
+ let WsReadyStates = /* @__PURE__ */ function(WsReadyStates) {
1044
+ WsReadyStates[WsReadyStates["Connecting"] = 0] = "Connecting";
1045
+ WsReadyStates[WsReadyStates["Open"] = 1] = "Open";
1046
+ WsReadyStates[WsReadyStates["Closing"] = 2] = "Closing";
1047
+ WsReadyStates[WsReadyStates["Closed"] = 3] = "Closed";
1048
+ return WsReadyStates;
1049
+ }({});
1050
+ let MessageType = /* @__PURE__ */ function(MessageType) {
1051
+ MessageType[MessageType["Sync"] = 0] = "Sync";
1052
+ MessageType[MessageType["Awareness"] = 1] = "Awareness";
1053
+ MessageType[MessageType["Auth"] = 2] = "Auth";
1054
+ MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
1055
+ MessageType[MessageType["Subdoc"] = 4] = "Subdoc";
1056
+ MessageType[MessageType["Stateless"] = 5] = "Stateless";
1057
+ MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
1058
+ MessageType[MessageType["SyncStatus"] = 8] = "SyncStatus";
1059
+ return MessageType;
1060
+ }({});
1061
+ let WebSocketStatus = /* @__PURE__ */ function(WebSocketStatus) {
1062
+ WebSocketStatus["Connecting"] = "connecting";
1063
+ WebSocketStatus["Connected"] = "connected";
1064
+ WebSocketStatus["Disconnected"] = "disconnected";
1065
+ return WebSocketStatus;
1066
+ }({});
1067
+
1027
1068
  //#endregion
1028
1069
  //#region packages/provider/src/IncomingMessage.ts
1029
1070
  var IncomingMessage = class {
@@ -1058,26 +1099,6 @@ var IncomingMessage = class {
1058
1099
  }
1059
1100
  };
1060
1101
 
1061
- //#endregion
1062
- //#region packages/provider/src/types.ts
1063
- let MessageType = /* @__PURE__ */ function(MessageType) {
1064
- MessageType[MessageType["Sync"] = 0] = "Sync";
1065
- MessageType[MessageType["Awareness"] = 1] = "Awareness";
1066
- MessageType[MessageType["Auth"] = 2] = "Auth";
1067
- MessageType[MessageType["QueryAwareness"] = 3] = "QueryAwareness";
1068
- MessageType[MessageType["Subdoc"] = 4] = "Subdoc";
1069
- MessageType[MessageType["Stateless"] = 5] = "Stateless";
1070
- MessageType[MessageType["CLOSE"] = 7] = "CLOSE";
1071
- MessageType[MessageType["SyncStatus"] = 8] = "SyncStatus";
1072
- return MessageType;
1073
- }({});
1074
- let WebSocketStatus = /* @__PURE__ */ function(WebSocketStatus) {
1075
- WebSocketStatus["Connecting"] = "connecting";
1076
- WebSocketStatus["Connected"] = "connected";
1077
- WebSocketStatus["Disconnected"] = "disconnected";
1078
- return WebSocketStatus;
1079
- }({});
1080
-
1081
1102
  //#endregion
1082
1103
  //#region packages/provider/src/OutgoingMessage.ts
1083
1104
  var OutgoingMessage = class {
@@ -1108,8 +1129,8 @@ var CloseMessage = class extends OutgoingMessage {
1108
1129
  };
1109
1130
 
1110
1131
  //#endregion
1111
- //#region packages/provider/src/HocuspocusProviderWebsocket.ts
1112
- var HocuspocusProviderWebsocket = class extends EventEmitter {
1132
+ //#region packages/provider/src/AbracadabraWS.ts
1133
+ var AbracadabraWS = class extends EventEmitter {
1113
1134
  constructor(configuration) {
1114
1135
  super();
1115
1136
  this.messageQueue = [];
@@ -1370,6 +1391,46 @@ var HocuspocusProviderWebsocket = class extends EventEmitter {
1370
1391
  this.cleanupWebSocket();
1371
1392
  }
1372
1393
  };
1394
+ /** @deprecated Use AbracadabraWS */
1395
+ const HocuspocusProviderWebsocket = AbracadabraWS;
1396
+
1397
+ //#endregion
1398
+ //#region packages/provider/src/auth.ts
1399
+ let AuthMessageType = /* @__PURE__ */ function(AuthMessageType) {
1400
+ AuthMessageType[AuthMessageType["Token"] = 0] = "Token";
1401
+ AuthMessageType[AuthMessageType["PermissionDenied"] = 1] = "PermissionDenied";
1402
+ AuthMessageType[AuthMessageType["Authenticated"] = 2] = "Authenticated";
1403
+ return AuthMessageType;
1404
+ }({});
1405
+ const writeAuthentication = (encoder, auth) => {
1406
+ writeVarUint(encoder, AuthMessageType.Token);
1407
+ writeVarString(encoder, auth);
1408
+ };
1409
+ const writePermissionDenied = (encoder, reason) => {
1410
+ writeVarUint(encoder, AuthMessageType.PermissionDenied);
1411
+ writeVarString(encoder, reason);
1412
+ };
1413
+ const writeAuthenticated = (encoder, scope) => {
1414
+ writeVarUint(encoder, AuthMessageType.Authenticated);
1415
+ writeVarString(encoder, scope);
1416
+ };
1417
+ const writeTokenSyncRequest = (encoder) => {
1418
+ writeVarUint(encoder, AuthMessageType.Token);
1419
+ };
1420
+ const readAuthMessage = (decoder, sendToken, permissionDeniedHandler, authenticatedHandler) => {
1421
+ switch (readVarUint(decoder)) {
1422
+ case AuthMessageType.Token:
1423
+ sendToken();
1424
+ break;
1425
+ case AuthMessageType.PermissionDenied:
1426
+ permissionDeniedHandler(readVarString(decoder));
1427
+ break;
1428
+ case AuthMessageType.Authenticated:
1429
+ authenticatedHandler(readVarString(decoder));
1430
+ break;
1431
+ default:
1432
+ }
1433
+ };
1373
1434
 
1374
1435
  //#endregion
1375
1436
  //#region node_modules/y-protocols/sync.js
@@ -1664,14 +1725,14 @@ var UpdateMessage = class extends OutgoingMessage {
1664
1725
  };
1665
1726
 
1666
1727
  //#endregion
1667
- //#region packages/provider/src/HocuspocusProvider.ts
1728
+ //#region packages/provider/src/AbracadabraBaseProvider.ts
1668
1729
  var AwarenessError = class extends Error {
1669
1730
  constructor(..._args) {
1670
1731
  super(..._args);
1671
1732
  this.code = 1001;
1672
1733
  }
1673
1734
  };
1674
- var HocuspocusProvider = class extends EventEmitter {
1735
+ var AbracadabraBaseProvider = class extends EventEmitter {
1675
1736
  constructor(configuration) {
1676
1737
  super();
1677
1738
  this.configuration = {
@@ -1745,7 +1806,7 @@ var HocuspocusProvider = class extends EventEmitter {
1745
1806
  setConfiguration(configuration = {}) {
1746
1807
  if (!configuration.websocketProvider) {
1747
1808
  this.manageSocket = true;
1748
- this.configuration.websocketProvider = new HocuspocusProviderWebsocket(configuration);
1809
+ this.configuration.websocketProvider = new AbracadabraWS(configuration);
1749
1810
  }
1750
1811
  this.configuration = {
1751
1812
  ...this.configuration,
@@ -1845,11 +1906,11 @@ var HocuspocusProvider = class extends EventEmitter {
1845
1906
  }
1846
1907
  async connect() {
1847
1908
  if (this.manageSocket) return this.configuration.websocketProvider.connect();
1848
- console.warn("HocuspocusProvider::connect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.");
1909
+ console.warn("AbracadabraBaseProvider::connect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.");
1849
1910
  }
1850
1911
  disconnect() {
1851
1912
  if (this.manageSocket) return this.configuration.websocketProvider.disconnect();
1852
- console.warn("HocuspocusProvider::disconnect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.");
1913
+ console.warn("AbracadabraBaseProvider::disconnect() is deprecated and does not do anything. Please connect/disconnect on the websocketProvider, or attach/deattach providers.");
1853
1914
  }
1854
1915
  async onOpen(event) {
1855
1916
  this.isAuthenticated = false;
@@ -1958,16 +2019,18 @@ var HocuspocusProvider = class extends EventEmitter {
1958
2019
  this.awareness.setLocalStateField(key, value);
1959
2020
  }
1960
2021
  };
2022
+ /** @deprecated Use AbracadabraBaseProvider */
2023
+ const HocuspocusProvider = AbracadabraBaseProvider;
1961
2024
 
1962
2025
  //#endregion
1963
2026
  //#region packages/provider/src/OfflineStore.ts
1964
- const DB_VERSION = 2;
1965
- function idbAvailable() {
2027
+ const DB_VERSION$3 = 2;
2028
+ function idbAvailable$3() {
1966
2029
  return typeof globalThis !== "undefined" && "indexedDB" in globalThis;
1967
2030
  }
1968
- function openDb$1(storeKey) {
2031
+ function openDb$4(storeKey) {
1969
2032
  return new Promise((resolve, reject) => {
1970
- const req = globalThis.indexedDB.open(`abracadabra:${storeKey}`, DB_VERSION);
2033
+ const req = globalThis.indexedDB.open(`abracadabra:${storeKey}`, DB_VERSION$3);
1971
2034
  req.onupgradeneeded = (event) => {
1972
2035
  const db = event.target.result;
1973
2036
  if (!db.objectStoreNames.contains("updates")) db.createObjectStore("updates", { autoIncrement: true });
@@ -1979,7 +2042,7 @@ function openDb$1(storeKey) {
1979
2042
  req.onerror = () => reject(req.error);
1980
2043
  });
1981
2044
  }
1982
- function txPromise(store, request) {
2045
+ function txPromise$2(store, request) {
1983
2046
  return new Promise((resolve, reject) => {
1984
2047
  request.onsuccess = () => resolve(request.result);
1985
2048
  request.onerror = () => reject(request.error);
@@ -1998,8 +2061,8 @@ var OfflineStore = class {
1998
2061
  this.storeKey = serverOrigin ? `${serverOrigin}/${docId}` : docId;
1999
2062
  }
2000
2063
  getDb() {
2001
- if (!idbAvailable()) return Promise.resolve(null);
2002
- if (!this.dbPromise) this.dbPromise = openDb$1(this.storeKey).catch(() => null).then((db) => {
2064
+ if (!idbAvailable$3()) return Promise.resolve(null);
2065
+ if (!this.dbPromise) this.dbPromise = openDb$4(this.storeKey).catch(() => null).then((db) => {
2003
2066
  this.db = db;
2004
2067
  return db;
2005
2068
  });
@@ -2009,7 +2072,7 @@ var OfflineStore = class {
2009
2072
  const db = await this.getDb();
2010
2073
  if (!db) return;
2011
2074
  const store = db.transaction("updates", "readwrite").objectStore("updates");
2012
- await txPromise(store, store.add(update));
2075
+ await txPromise$2(store, store.add(update));
2013
2076
  }
2014
2077
  async getPendingUpdates() {
2015
2078
  const db = await this.getDb();
@@ -2024,7 +2087,7 @@ var OfflineStore = class {
2024
2087
  const db = await this.getDb();
2025
2088
  if (!db) return;
2026
2089
  const tx = db.transaction("updates", "readwrite");
2027
- await txPromise(tx.objectStore("updates"), tx.objectStore("updates").clear());
2090
+ await txPromise$2(tx.objectStore("updates"), tx.objectStore("updates").clear());
2028
2091
  }
2029
2092
  /**
2030
2093
  * Persist a full Y.js state snapshot (Y.encodeStateAsUpdate output).
@@ -2034,7 +2097,7 @@ var OfflineStore = class {
2034
2097
  const db = await this.getDb();
2035
2098
  if (!db) return;
2036
2099
  const tx = db.transaction("doc_state", "readwrite");
2037
- await txPromise(tx.objectStore("doc_state"), tx.objectStore("doc_state").put(snapshot, "snapshot"));
2100
+ await txPromise$2(tx.objectStore("doc_state"), tx.objectStore("doc_state").put(snapshot, "snapshot"));
2038
2101
  }
2039
2102
  /**
2040
2103
  * Retrieve the stored full document snapshot, or null if none exists.
@@ -2043,37 +2106,37 @@ var OfflineStore = class {
2043
2106
  const db = await this.getDb();
2044
2107
  if (!db) return null;
2045
2108
  const tx = db.transaction("doc_state", "readonly");
2046
- return await txPromise(tx.objectStore("doc_state"), tx.objectStore("doc_state").get("snapshot")) ?? null;
2109
+ return await txPromise$2(tx.objectStore("doc_state"), tx.objectStore("doc_state").get("snapshot")) ?? null;
2047
2110
  }
2048
2111
  async getStateVector() {
2049
2112
  const db = await this.getDb();
2050
2113
  if (!db) return null;
2051
2114
  const tx = db.transaction("meta", "readonly");
2052
- return await txPromise(tx.objectStore("meta"), tx.objectStore("meta").get("sv")) ?? null;
2115
+ return await txPromise$2(tx.objectStore("meta"), tx.objectStore("meta").get("sv")) ?? null;
2053
2116
  }
2054
2117
  async saveStateVector(sv) {
2055
2118
  const db = await this.getDb();
2056
2119
  if (!db) return;
2057
2120
  const tx = db.transaction("meta", "readwrite");
2058
- await txPromise(tx.objectStore("meta"), tx.objectStore("meta").put(sv, "sv"));
2121
+ await txPromise$2(tx.objectStore("meta"), tx.objectStore("meta").put(sv, "sv"));
2059
2122
  }
2060
2123
  async getPermissionSnapshot() {
2061
2124
  const db = await this.getDb();
2062
2125
  if (!db) return null;
2063
2126
  const tx = db.transaction("meta", "readonly");
2064
- return await txPromise(tx.objectStore("meta"), tx.objectStore("meta").get("role")) ?? null;
2127
+ return await txPromise$2(tx.objectStore("meta"), tx.objectStore("meta").get("role")) ?? null;
2065
2128
  }
2066
2129
  async savePermissionSnapshot(role) {
2067
2130
  const db = await this.getDb();
2068
2131
  if (!db) return;
2069
2132
  const tx = db.transaction("meta", "readwrite");
2070
- await txPromise(tx.objectStore("meta"), tx.objectStore("meta").put(role, "role"));
2133
+ await txPromise$2(tx.objectStore("meta"), tx.objectStore("meta").put(role, "role"));
2071
2134
  }
2072
2135
  async queueSubdoc(entry) {
2073
2136
  const db = await this.getDb();
2074
2137
  if (!db) return;
2075
2138
  const tx = db.transaction("subdoc_queue", "readwrite");
2076
- await txPromise(tx.objectStore("subdoc_queue"), tx.objectStore("subdoc_queue").put(entry));
2139
+ await txPromise$2(tx.objectStore("subdoc_queue"), tx.objectStore("subdoc_queue").put(entry));
2077
2140
  }
2078
2141
  async getPendingSubdocs() {
2079
2142
  const db = await this.getDb();
@@ -2088,7 +2151,7 @@ var OfflineStore = class {
2088
2151
  const db = await this.getDb();
2089
2152
  if (!db) return;
2090
2153
  const tx = db.transaction("subdoc_queue", "readwrite");
2091
- await txPromise(tx.objectStore("subdoc_queue"), tx.objectStore("subdoc_queue").delete(childId));
2154
+ await txPromise$2(tx.objectStore("subdoc_queue"), tx.objectStore("subdoc_queue").delete(childId));
2092
2155
  }
2093
2156
  destroy() {
2094
2157
  this.db?.close();
@@ -2131,7 +2194,7 @@ function isValidDocId(id) {
2131
2194
  return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);
2132
2195
  }
2133
2196
  /**
2134
- * AbracadabraProvider extends HocuspocusProvider with:
2197
+ * AbracadabraProvider extends AbracadabraBaseProvider with:
2135
2198
  *
2136
2199
  * 1. Subdocument lifecycle – intercepts Y.Doc subdoc events and syncs them
2137
2200
  * with the server via MSG_SUBDOC (4) frames. Child documents get their
@@ -2151,7 +2214,7 @@ function isValidDocId(id) {
2151
2214
  * can gate write operations without a network round-trip. Role is
2152
2215
  * refreshed from the server on every reconnect.
2153
2216
  */
2154
- var AbracadabraProvider = class AbracadabraProvider extends HocuspocusProvider {
2217
+ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvider {
2155
2218
  constructor(configuration) {
2156
2219
  const resolved = { ...configuration };
2157
2220
  const client = configuration.client ?? null;
@@ -2432,6 +2495,7 @@ var AbracadabraClient = class {
2432
2495
  this.persistAuth = config.persistAuth ?? typeof localStorage !== "undefined";
2433
2496
  this.storageKey = config.storageKey ?? "abracadabra:auth";
2434
2497
  this._fetch = config.fetch ?? globalThis.fetch.bind(globalThis);
2498
+ this.cache = config.cache ?? null;
2435
2499
  this._token = config.token ?? this.loadPersistedToken() ?? null;
2436
2500
  }
2437
2501
  get token() {
@@ -2468,7 +2532,8 @@ var AbracadabraClient = class {
2468
2532
  identityPublicKey: opts.publicKey,
2469
2533
  deviceName: opts.deviceName,
2470
2534
  displayName: opts.displayName,
2471
- email: opts.email
2535
+ email: opts.email,
2536
+ inviteCode: opts.inviteCode
2472
2537
  },
2473
2538
  auth: false
2474
2539
  });
@@ -2529,7 +2594,13 @@ var AbracadabraClient = class {
2529
2594
  }
2530
2595
  /** Get the current user's profile. */
2531
2596
  async getMe() {
2532
- return this.request("GET", "/users/me");
2597
+ if (this.cache) {
2598
+ const cached = await this.cache.getCurrentProfile();
2599
+ if (cached) return cached;
2600
+ }
2601
+ const profile = await this.request("GET", "/users/me");
2602
+ if (this.cache) await this.cache.setCurrentProfile(profile).catch(() => null);
2603
+ return profile;
2533
2604
  }
2534
2605
  /** Update the current user's display name. */
2535
2606
  async updateMe(opts) {
@@ -2541,15 +2612,28 @@ var AbracadabraClient = class {
2541
2612
  }
2542
2613
  /** Get document metadata. */
2543
2614
  async getDoc(docId) {
2544
- return this.request("GET", `/docs/${encodeURIComponent(docId)}`);
2615
+ if (this.cache) {
2616
+ const cached = await this.cache.getDoc(docId);
2617
+ if (cached) return cached;
2618
+ }
2619
+ const meta = await this.request("GET", `/docs/${encodeURIComponent(docId)}`);
2620
+ if (this.cache) await this.cache.setDoc(meta).catch(() => null);
2621
+ return meta;
2545
2622
  }
2546
2623
  /** Delete a document (requires Owner role). Cascades to children and uploads. */
2547
2624
  async deleteDoc(docId) {
2548
2625
  await this.request("DELETE", `/docs/${encodeURIComponent(docId)}`);
2626
+ if (this.cache) await this.cache.invalidateDoc(docId).catch(() => null);
2549
2627
  }
2550
2628
  /** List immediate child documents. */
2551
2629
  async listChildren(docId) {
2552
- return (await this.request("GET", `/docs/${encodeURIComponent(docId)}/children`)).children;
2630
+ if (this.cache) {
2631
+ const cached = await this.cache.getChildren(docId);
2632
+ if (cached) return cached;
2633
+ }
2634
+ const res = await this.request("GET", `/docs/${encodeURIComponent(docId)}/children`);
2635
+ if (this.cache) await this.cache.setChildren(docId, res.children).catch(() => null);
2636
+ return res.children;
2553
2637
  }
2554
2638
  /** Create a child document under a parent (requires write permission). */
2555
2639
  async createChild(docId, opts) {
@@ -2557,7 +2641,13 @@ var AbracadabraClient = class {
2557
2641
  }
2558
2642
  /** List all permissions for a document (requires read access). */
2559
2643
  async listPermissions(docId) {
2560
- return (await this.request("GET", `/docs/${encodeURIComponent(docId)}/permissions`)).permissions;
2644
+ if (this.cache) {
2645
+ const cached = await this.cache.getPermissions(docId);
2646
+ if (cached) return cached;
2647
+ }
2648
+ const res = await this.request("GET", `/docs/${encodeURIComponent(docId)}/permissions`);
2649
+ if (this.cache) await this.cache.setPermissions(docId, res.permissions).catch(() => null);
2650
+ return res.permissions;
2561
2651
  }
2562
2652
  /** Grant or change a user's role on a document (requires Owner). */
2563
2653
  async setPermission(docId, opts) {
@@ -2579,11 +2669,19 @@ var AbracadabraClient = class {
2579
2669
  body: formData
2580
2670
  });
2581
2671
  if (!res.ok) throw await this.toError(res);
2582
- return res.json();
2672
+ const meta = await res.json();
2673
+ if (this.cache) await this.cache.invalidateUploads(docId).catch(() => null);
2674
+ return meta;
2583
2675
  }
2584
2676
  /** List all uploads for a document. */
2585
2677
  async listUploads(docId) {
2586
- return (await this.request("GET", `/docs/${encodeURIComponent(docId)}/uploads`)).uploads;
2678
+ if (this.cache) {
2679
+ const cached = await this.cache.getUploads(docId);
2680
+ if (cached) return cached;
2681
+ }
2682
+ const res = await this.request("GET", `/docs/${encodeURIComponent(docId)}/uploads`);
2683
+ if (this.cache) await this.cache.setUploads(docId, res.uploads).catch(() => null);
2684
+ return res.uploads;
2587
2685
  }
2588
2686
  /** Download an upload as a Blob. */
2589
2687
  async getUpload(docId, uploadId) {
@@ -2599,11 +2697,35 @@ var AbracadabraClient = class {
2599
2697
  /** Delete an upload (requires uploader or document Owner). */
2600
2698
  async deleteUpload(docId, uploadId) {
2601
2699
  await this.request("DELETE", `/docs/${encodeURIComponent(docId)}/uploads/${encodeURIComponent(uploadId)}`);
2700
+ if (this.cache) await this.cache.invalidateUploads(docId).catch(() => null);
2701
+ }
2702
+ /** Create an invite code (requires permission per server config). */
2703
+ async createInvite(opts) {
2704
+ return this.request("POST", "/invites", { body: opts ?? {} });
2705
+ }
2706
+ /** List invite codes visible to the current user. */
2707
+ async listInvites() {
2708
+ return (await this.request("GET", "/invites")).invites;
2709
+ }
2710
+ /** Revoke an invite by its code. */
2711
+ async revokeInvite(code) {
2712
+ await this.request("DELETE", `/invites/${encodeURIComponent(code)}`);
2713
+ }
2714
+ /** Redeem an invite code for the currently authenticated user. */
2715
+ async redeemInvite(code) {
2716
+ await this.request("POST", "/invites/redeem", { body: { code } });
2602
2717
  }
2603
2718
  /** Health check — no auth required. */
2604
2719
  async health() {
2605
2720
  return this.request("GET", "/health", { auth: false });
2606
2721
  }
2722
+ /**
2723
+ * Fetch server metadata including the optional `index_doc_id` entry point.
2724
+ * No auth required.
2725
+ */
2726
+ async serverInfo() {
2727
+ return this.request("GET", "/info", { auth: false });
2728
+ }
2607
2729
  async request(method, path, opts) {
2608
2730
  const auth = opts?.auth ?? true;
2609
2731
  const headers = {};
@@ -2651,6 +2773,49 @@ var AbracadabraClient = class {
2651
2773
  }
2652
2774
  };
2653
2775
 
2776
+ //#endregion
2777
+ //#region packages/provider/src/CloseEvents.ts
2778
+ /**
2779
+ * The server is terminating the connection because a data frame was received
2780
+ * that is too large.
2781
+ * See: https://developer.mozilla.org/en-US/docs/Web/API/CloseEvent/code
2782
+ */
2783
+ const MessageTooBig = {
2784
+ code: 1009,
2785
+ reason: "Message Too Big"
2786
+ };
2787
+ /**
2788
+ * The server successfully processed the request, asks that the requester reset
2789
+ * its document view, and is not returning any content.
2790
+ */
2791
+ const ResetConnection = {
2792
+ code: 4205,
2793
+ reason: "Reset Connection"
2794
+ };
2795
+ /**
2796
+ * Similar to Forbidden, but specifically for use when authentication is required and has
2797
+ * failed or has not yet been provided.
2798
+ */
2799
+ const Unauthorized = {
2800
+ code: 4401,
2801
+ reason: "Unauthorized"
2802
+ };
2803
+ /**
2804
+ * The request contained valid data and was understood by the server, but the server
2805
+ * is refusing action.
2806
+ */
2807
+ const Forbidden = {
2808
+ code: 4403,
2809
+ reason: "Forbidden"
2810
+ };
2811
+ /**
2812
+ * The server timed out waiting for the request.
2813
+ */
2814
+ const ConnectionTimeout = {
2815
+ code: 4408,
2816
+ reason: "Connection Timeout"
2817
+ };
2818
+
2654
2819
  //#endregion
2655
2820
  //#region node_modules/@noble/hashes/esm/utils.js
2656
2821
  /** Checks if something is Uint8Array. Be careful: nodejs Buffer will return true. */
@@ -3328,7 +3493,7 @@ const DB_NAME = "abracadabra:identity";
3328
3493
  const STORE_NAME = "identity";
3329
3494
  const RECORD_KEY = "current";
3330
3495
  const HKDF_INFO = new TextEncoder().encode("abracadabra-identity-v1");
3331
- function openDb() {
3496
+ function openDb$3() {
3332
3497
  return new Promise((resolve, reject) => {
3333
3498
  const req = indexedDB.open(DB_NAME, 1);
3334
3499
  req.onupgradeneeded = () => {
@@ -3411,7 +3576,7 @@ var CryptoIdentityKeystore = class {
3411
3576
  name: "AES-GCM",
3412
3577
  iv
3413
3578
  }, aesKey, privateKey);
3414
- const db = await openDb();
3579
+ const db = await openDb$3();
3415
3580
  await dbPut(db, {
3416
3581
  username,
3417
3582
  publicKey: toBase64url(publicKey),
@@ -3434,7 +3599,7 @@ var CryptoIdentityKeystore = class {
3434
3599
  * @returns base64url-encoded Ed25519 signature (64 bytes).
3435
3600
  */
3436
3601
  async sign(challengeB64) {
3437
- const db = await openDb();
3602
+ const db = await openDb$3();
3438
3603
  const stored = await dbGet(db);
3439
3604
  db.close();
3440
3605
  if (!stored) throw new Error("No identity stored. Call register() first.");
@@ -3463,7 +3628,7 @@ var CryptoIdentityKeystore = class {
3463
3628
  }
3464
3629
  /** Returns the stored base64url public key, or null if no identity exists. */
3465
3630
  async getPublicKey() {
3466
- const db = await openDb();
3631
+ const db = await openDb$3();
3467
3632
  const stored = await dbGet(db);
3468
3633
  db.close();
3469
3634
  return stored?.publicKey ?? null;
@@ -3476,26 +3641,524 @@ var CryptoIdentityKeystore = class {
3476
3641
  * a real display name via PATCH /users/me.
3477
3642
  */
3478
3643
  async getUsername() {
3479
- const db = await openDb();
3644
+ const db = await openDb$3();
3480
3645
  const stored = await dbGet(db);
3481
3646
  db.close();
3482
3647
  return stored?.username ?? null;
3483
3648
  }
3484
3649
  /** Returns true if an identity is stored in IndexedDB. */
3485
3650
  async hasIdentity() {
3486
- const db = await openDb();
3651
+ const db = await openDb$3();
3487
3652
  const stored = await dbGet(db);
3488
3653
  db.close();
3489
3654
  return stored !== void 0;
3490
3655
  }
3491
3656
  /** Remove the stored identity from IndexedDB. */
3492
3657
  async clear() {
3493
- const db = await openDb();
3658
+ const db = await openDb$3();
3494
3659
  await dbDelete(db);
3495
3660
  db.close();
3496
3661
  }
3497
3662
  };
3498
3663
 
3499
3664
  //#endregion
3500
- export { AbracadabraClient, AbracadabraProvider, AwarenessError, CryptoIdentityKeystore, HocuspocusProvider, HocuspocusProviderWebsocket, MessageType, OfflineStore, SubdocMessage, WebSocketStatus };
3665
+ //#region packages/provider/src/DocumentCache.ts
3666
+ const DB_VERSION$2 = 1;
3667
+ const DEFAULT_TTL_MS = 300 * 1e3;
3668
+ function idbAvailable$2() {
3669
+ return typeof globalThis !== "undefined" && "indexedDB" in globalThis;
3670
+ }
3671
+ function openDb$2(origin) {
3672
+ return new Promise((resolve, reject) => {
3673
+ const req = globalThis.indexedDB.open(`abracadabra:meta-cache:${origin}`, DB_VERSION$2);
3674
+ req.onupgradeneeded = (event) => {
3675
+ const db = event.target.result;
3676
+ for (const name of [
3677
+ "doc_meta",
3678
+ "children",
3679
+ "user_profile",
3680
+ "permissions",
3681
+ "uploads"
3682
+ ]) if (!db.objectStoreNames.contains(name)) db.createObjectStore(name);
3683
+ };
3684
+ req.onsuccess = () => resolve(req.result);
3685
+ req.onerror = () => reject(req.error);
3686
+ });
3687
+ }
3688
+ function txPromise$1(store, request) {
3689
+ return new Promise((resolve, reject) => {
3690
+ request.onsuccess = () => resolve(request.result);
3691
+ request.onerror = () => reject(request.error);
3692
+ });
3693
+ }
3694
+ var DocumentCache = class {
3695
+ constructor(serverOrigin, opts) {
3696
+ this.dbPromise = null;
3697
+ this.db = null;
3698
+ this.origin = serverOrigin;
3699
+ this.ttlMs = opts?.ttlMs ?? DEFAULT_TTL_MS;
3700
+ }
3701
+ getDb() {
3702
+ if (!idbAvailable$2()) return Promise.resolve(null);
3703
+ if (!this.dbPromise) this.dbPromise = openDb$2(this.origin).catch(() => null).then((db) => {
3704
+ this.db = db;
3705
+ return db;
3706
+ });
3707
+ return this.dbPromise;
3708
+ }
3709
+ isExpired(cachedAt) {
3710
+ return Date.now() - cachedAt > this.ttlMs;
3711
+ }
3712
+ async getWithTtl(storeName, key) {
3713
+ const db = await this.getDb();
3714
+ if (!db) return null;
3715
+ const tx = db.transaction(storeName, "readonly");
3716
+ const result = await txPromise$1(tx.objectStore(storeName), tx.objectStore(storeName).get(key));
3717
+ if (!result) return null;
3718
+ if (this.isExpired(result.cachedAt)) {
3719
+ this.deleteKey(storeName, key).catch(() => null);
3720
+ return null;
3721
+ }
3722
+ return result.value;
3723
+ }
3724
+ async setWithTtl(storeName, key, value) {
3725
+ const db = await this.getDb();
3726
+ if (!db) return;
3727
+ const entry = {
3728
+ value,
3729
+ cachedAt: Date.now()
3730
+ };
3731
+ const tx = db.transaction(storeName, "readwrite");
3732
+ await txPromise$1(tx.objectStore(storeName), tx.objectStore(storeName).put(entry, key));
3733
+ }
3734
+ async deleteKey(storeName, key) {
3735
+ const db = await this.getDb();
3736
+ if (!db) return;
3737
+ const tx = db.transaction(storeName, "readwrite");
3738
+ await txPromise$1(tx.objectStore(storeName), tx.objectStore(storeName).delete(key));
3739
+ }
3740
+ async getDoc(docId) {
3741
+ return this.getWithTtl("doc_meta", docId);
3742
+ }
3743
+ async setDoc(meta) {
3744
+ return this.setWithTtl("doc_meta", meta.id, meta);
3745
+ }
3746
+ async invalidateDoc(docId) {
3747
+ await this.deleteKey("doc_meta", docId).catch(() => null);
3748
+ }
3749
+ async getChildren(parentId) {
3750
+ return this.getWithTtl("children", parentId);
3751
+ }
3752
+ async setChildren(parentId, items) {
3753
+ return this.setWithTtl("children", parentId, items);
3754
+ }
3755
+ async invalidateChildren(parentId) {
3756
+ await this.deleteKey("children", parentId).catch(() => null);
3757
+ }
3758
+ async getProfile(userId) {
3759
+ return this.getWithTtl("user_profile", userId);
3760
+ }
3761
+ async setProfile(profile) {
3762
+ return this.setWithTtl("user_profile", profile.id, profile);
3763
+ }
3764
+ /** Get the cached profile for the currently authenticated user. */
3765
+ async getCurrentProfile() {
3766
+ return this.getWithTtl("user_profile", "__current__");
3767
+ }
3768
+ /** Cache a profile both by its ID and as the current user. */
3769
+ async setCurrentProfile(profile) {
3770
+ await Promise.all([this.setWithTtl("user_profile", profile.id, profile), this.setWithTtl("user_profile", "__current__", profile)]);
3771
+ }
3772
+ async getPermissions(docId) {
3773
+ return this.getWithTtl("permissions", docId);
3774
+ }
3775
+ async setPermissions(docId, items) {
3776
+ return this.setWithTtl("permissions", docId, items);
3777
+ }
3778
+ async getUploads(docId) {
3779
+ return this.getWithTtl("uploads", docId);
3780
+ }
3781
+ async setUploads(docId, items) {
3782
+ return this.setWithTtl("uploads", docId, items);
3783
+ }
3784
+ async invalidateUploads(docId) {
3785
+ await this.deleteKey("uploads", docId).catch(() => null);
3786
+ }
3787
+ destroy() {
3788
+ this.db?.close();
3789
+ this.db = null;
3790
+ }
3791
+ };
3792
+
3793
+ //#endregion
3794
+ //#region packages/provider/src/SearchIndex.ts
3795
+ const DB_VERSION$1 = 1;
3796
+ function idbAvailable$1() {
3797
+ return typeof globalThis !== "undefined" && "indexedDB" in globalThis;
3798
+ }
3799
+ function openDb$1(origin) {
3800
+ return new Promise((resolve, reject) => {
3801
+ const req = globalThis.indexedDB.open(`abracadabra:search:${origin}`, DB_VERSION$1);
3802
+ req.onupgradeneeded = (event) => {
3803
+ const db = event.target.result;
3804
+ if (!db.objectStoreNames.contains("postings")) db.createObjectStore("postings");
3805
+ if (!db.objectStoreNames.contains("doc_trigrams")) db.createObjectStore("doc_trigrams");
3806
+ };
3807
+ req.onsuccess = () => resolve(req.result);
3808
+ req.onerror = () => reject(req.error);
3809
+ });
3810
+ }
3811
+ /** Extract the set of trigrams for a piece of text. */
3812
+ function extractTrigrams(text) {
3813
+ const trigrams = /* @__PURE__ */ new Set();
3814
+ const padded = ` ${text.toLowerCase()} `;
3815
+ for (let i = 0; i <= padded.length - 3; i++) trigrams.add(padded.slice(i, i + 3));
3816
+ return trigrams;
3817
+ }
3818
+ /** Merge trigrams from multiple texts into a single set. */
3819
+ function extractAllTrigrams(texts) {
3820
+ const result = /* @__PURE__ */ new Set();
3821
+ for (const t of texts) for (const trigram of extractTrigrams(t)) result.add(trigram);
3822
+ return result;
3823
+ }
3824
+ var SearchIndex = class {
3825
+ constructor(serverOrigin) {
3826
+ this.dbPromise = null;
3827
+ this.db = null;
3828
+ this.origin = serverOrigin;
3829
+ }
3830
+ getDb() {
3831
+ if (!idbAvailable$1()) return Promise.resolve(null);
3832
+ if (!this.dbPromise) this.dbPromise = openDb$1(this.origin).catch(() => null).then((db) => {
3833
+ this.db = db;
3834
+ return db;
3835
+ });
3836
+ return this.dbPromise;
3837
+ }
3838
+ /**
3839
+ * Replace the index for docId with the given texts.
3840
+ * Old trigram associations are removed before new ones are added.
3841
+ */
3842
+ async index(docId, texts) {
3843
+ const db = await this.getDb();
3844
+ if (!db) return;
3845
+ const newTrigrams = extractAllTrigrams(texts);
3846
+ return new Promise((resolve, reject) => {
3847
+ const tx = db.transaction(["postings", "doc_trigrams"], "readwrite");
3848
+ tx.oncomplete = () => resolve();
3849
+ tx.onerror = () => reject(tx.error);
3850
+ const postings = tx.objectStore("postings");
3851
+ const docTrigramsStore = tx.objectStore("doc_trigrams");
3852
+ const oldReq = docTrigramsStore.get(docId);
3853
+ oldReq.onsuccess = () => {
3854
+ const oldTrigrams = oldReq.result ?? [];
3855
+ let pending = oldTrigrams.length + newTrigrams.size + 1;
3856
+ function done() {
3857
+ pending--;
3858
+ }
3859
+ for (const trigram of oldTrigrams) {
3860
+ const req = postings.get(trigram);
3861
+ req.onsuccess = () => {
3862
+ const updated = (req.result ?? []).filter((id) => id !== docId);
3863
+ if (updated.length === 0) postings.delete(trigram);
3864
+ else postings.put(updated, trigram);
3865
+ done();
3866
+ };
3867
+ req.onerror = done;
3868
+ }
3869
+ for (const trigram of newTrigrams) {
3870
+ const req = postings.get(trigram);
3871
+ req.onsuccess = () => {
3872
+ const list = req.result ?? [];
3873
+ if (!list.includes(docId)) list.push(docId);
3874
+ postings.put(list, trigram);
3875
+ done();
3876
+ };
3877
+ req.onerror = done;
3878
+ }
3879
+ const writeReq = docTrigramsStore.put([...newTrigrams], docId);
3880
+ writeReq.onsuccess = done;
3881
+ writeReq.onerror = done;
3882
+ };
3883
+ oldReq.onerror = () => reject(oldReq.error);
3884
+ });
3885
+ }
3886
+ /** Remove all indexed content for a document. */
3887
+ async remove(docId) {
3888
+ const db = await this.getDb();
3889
+ if (!db) return;
3890
+ return new Promise((resolve, reject) => {
3891
+ const tx = db.transaction(["postings", "doc_trigrams"], "readwrite");
3892
+ tx.oncomplete = () => resolve();
3893
+ tx.onerror = () => reject(tx.error);
3894
+ const postings = tx.objectStore("postings");
3895
+ const docTrigramsStore = tx.objectStore("doc_trigrams");
3896
+ const oldReq = docTrigramsStore.get(docId);
3897
+ oldReq.onsuccess = () => {
3898
+ const oldTrigrams = oldReq.result ?? [];
3899
+ for (const trigram of oldTrigrams) {
3900
+ const req = postings.get(trigram);
3901
+ req.onsuccess = () => {
3902
+ const updated = (req.result ?? []).filter((id) => id !== docId);
3903
+ if (updated.length === 0) postings.delete(trigram);
3904
+ else postings.put(updated, trigram);
3905
+ };
3906
+ }
3907
+ docTrigramsStore.delete(docId);
3908
+ };
3909
+ oldReq.onerror = () => reject(oldReq.error);
3910
+ });
3911
+ }
3912
+ /**
3913
+ * Search for documents matching the query.
3914
+ * Returns results sorted by score (matching trigram count) descending.
3915
+ */
3916
+ async search(query, limit = 20) {
3917
+ const db = await this.getDb();
3918
+ if (!db) return [];
3919
+ const queryTrigrams = [...extractTrigrams(query)];
3920
+ if (queryTrigrams.length === 0) return [];
3921
+ return new Promise((resolve, reject) => {
3922
+ const tx = db.transaction("postings", "readonly");
3923
+ const postings = tx.objectStore("postings");
3924
+ const scores = /* @__PURE__ */ new Map();
3925
+ let remaining = queryTrigrams.length;
3926
+ for (const trigram of queryTrigrams) {
3927
+ const req = postings.get(trigram);
3928
+ req.onsuccess = () => {
3929
+ const docIds = req.result ?? [];
3930
+ for (const docId of docIds) scores.set(docId, (scores.get(docId) ?? 0) + 1);
3931
+ remaining--;
3932
+ if (remaining === 0) resolve([...scores.entries()].map(([docId, score]) => ({
3933
+ docId,
3934
+ score
3935
+ })).sort((a, b) => b.score - a.score).slice(0, limit));
3936
+ };
3937
+ req.onerror = () => {
3938
+ remaining--;
3939
+ if (remaining === 0) resolve([...scores.entries()].map(([docId, score]) => ({
3940
+ docId,
3941
+ score
3942
+ })).sort((a, b) => b.score - a.score).slice(0, limit));
3943
+ };
3944
+ }
3945
+ tx.onerror = () => reject(tx.error);
3946
+ });
3947
+ }
3948
+ destroy() {
3949
+ this.db?.close();
3950
+ this.db = null;
3951
+ }
3952
+ };
3953
+
3954
+ //#endregion
3955
+ //#region packages/provider/src/FileBlobStore.ts
3956
+ const DB_VERSION = 1;
3957
+ function idbAvailable() {
3958
+ return typeof globalThis !== "undefined" && "indexedDB" in globalThis;
3959
+ }
3960
+ function openDb(origin) {
3961
+ return new Promise((resolve, reject) => {
3962
+ const req = globalThis.indexedDB.open(`abracadabra:files:${origin}`, DB_VERSION);
3963
+ req.onupgradeneeded = (event) => {
3964
+ const db = event.target.result;
3965
+ if (!db.objectStoreNames.contains("blobs")) db.createObjectStore("blobs");
3966
+ if (!db.objectStoreNames.contains("upload_queue")) db.createObjectStore("upload_queue", { keyPath: "id" });
3967
+ };
3968
+ req.onsuccess = () => resolve(req.result);
3969
+ req.onerror = () => reject(req.error);
3970
+ });
3971
+ }
3972
+ function txPromise(store, request) {
3973
+ return new Promise((resolve, reject) => {
3974
+ request.onsuccess = () => resolve(request.result);
3975
+ request.onerror = () => reject(request.error);
3976
+ });
3977
+ }
3978
+ var FileBlobStore = class extends EventEmitter {
3979
+ constructor(serverOrigin, client) {
3980
+ super();
3981
+ this.dbPromise = null;
3982
+ this.db = null;
3983
+ this.objectUrls = /* @__PURE__ */ new Map();
3984
+ this._flushing = false;
3985
+ this.origin = serverOrigin;
3986
+ this.client = client;
3987
+ this._onlineHandler = () => {
3988
+ this.flushQueue().catch(() => null);
3989
+ };
3990
+ if (typeof window !== "undefined") window.addEventListener("online", this._onlineHandler);
3991
+ }
3992
+ getDb() {
3993
+ if (!idbAvailable()) return Promise.resolve(null);
3994
+ if (!this.dbPromise) this.dbPromise = openDb(this.origin).catch(() => null).then((db) => {
3995
+ this.db = db;
3996
+ return db;
3997
+ });
3998
+ return this.dbPromise;
3999
+ }
4000
+ blobKey(docId, uploadId) {
4001
+ return `${docId}/${uploadId}`;
4002
+ }
4003
+ /**
4004
+ * Return a local object URL for a file.
4005
+ * On first call the blob is downloaded from the server and cached in IDB.
4006
+ * Returns null when offline and the blob is not yet cached, or when
4007
+ * URL.createObjectURL is unavailable (e.g. Node.js / SSR).
4008
+ */
4009
+ async getBlobUrl(docId, uploadId) {
4010
+ if (typeof window === "undefined") return null;
4011
+ const key = this.blobKey(docId, uploadId);
4012
+ const existing = this.objectUrls.get(key);
4013
+ if (existing) return existing;
4014
+ const db = await this.getDb();
4015
+ if (db) {
4016
+ const tx = db.transaction("blobs", "readonly");
4017
+ const entry = await txPromise(tx.objectStore("blobs"), tx.objectStore("blobs").get(key));
4018
+ if (entry) {
4019
+ const url = URL.createObjectURL(entry.blob);
4020
+ this.objectUrls.set(key, url);
4021
+ return url;
4022
+ }
4023
+ }
4024
+ let blob;
4025
+ try {
4026
+ blob = await this.client.getUpload(docId, uploadId);
4027
+ } catch {
4028
+ return null;
4029
+ }
4030
+ if (db) {
4031
+ const entry = {
4032
+ blob,
4033
+ mime_type: blob.type,
4034
+ filename: uploadId,
4035
+ cachedAt: Date.now()
4036
+ };
4037
+ db.transaction("blobs", "readwrite").objectStore("blobs").put(entry, key);
4038
+ }
4039
+ const url = URL.createObjectURL(blob);
4040
+ this.objectUrls.set(key, url);
4041
+ return url;
4042
+ }
4043
+ /** Revoke the object URL and remove the blob from cache. */
4044
+ async evictBlob(docId, uploadId) {
4045
+ const key = this.blobKey(docId, uploadId);
4046
+ const url = this.objectUrls.get(key);
4047
+ if (url) {
4048
+ URL.revokeObjectURL(url);
4049
+ this.objectUrls.delete(key);
4050
+ }
4051
+ const db = await this.getDb();
4052
+ if (!db) return;
4053
+ const tx = db.transaction("blobs", "readwrite");
4054
+ await txPromise(tx.objectStore("blobs"), tx.objectStore("blobs").delete(key));
4055
+ }
4056
+ /**
4057
+ * Queue a file for upload. Works offline — the entry is persisted to IDB
4058
+ * and flushed the next time the queue is flushed.
4059
+ * Returns the generated queue entry id.
4060
+ */
4061
+ async queueUpload(docId, file, filename) {
4062
+ const id = crypto.randomUUID();
4063
+ const entry = {
4064
+ id,
4065
+ docId,
4066
+ file,
4067
+ filename: file instanceof File ? file.name : filename ?? "file",
4068
+ status: "pending",
4069
+ createdAt: Date.now()
4070
+ };
4071
+ const db = await this.getDb();
4072
+ if (db) {
4073
+ const tx = db.transaction("upload_queue", "readwrite");
4074
+ await txPromise(tx.objectStore("upload_queue"), tx.objectStore("upload_queue").put(entry));
4075
+ }
4076
+ this.emit("upload:queued", entry);
4077
+ return id;
4078
+ }
4079
+ /** Return all upload queue entries. */
4080
+ async getQueue() {
4081
+ const db = await this.getDb();
4082
+ if (!db) return [];
4083
+ return new Promise((resolve, reject) => {
4084
+ const req = db.transaction("upload_queue", "readonly").objectStore("upload_queue").getAll();
4085
+ req.onsuccess = () => resolve(req.result);
4086
+ req.onerror = () => reject(req.error);
4087
+ });
4088
+ }
4089
+ /**
4090
+ * Upload all pending queue entries via AbracadabraClient.
4091
+ * Safe to call repeatedly — a concurrent call is a no-op.
4092
+ * Entries that fail are marked with status "error" and left in the queue.
4093
+ */
4094
+ async flushQueue() {
4095
+ if (this._flushing) return;
4096
+ this._flushing = true;
4097
+ try {
4098
+ const pending = (await this.getQueue()).filter((e) => e.status === "pending");
4099
+ for (const entry of pending) {
4100
+ await this._updateQueueEntry(entry.id, { status: "uploading" });
4101
+ this.emit("upload:started", {
4102
+ ...entry,
4103
+ status: "uploading"
4104
+ });
4105
+ try {
4106
+ await this.client.upload(entry.docId, entry.file, entry.filename);
4107
+ await this._updateQueueEntry(entry.id, { status: "done" });
4108
+ this.emit("upload:done", {
4109
+ ...entry,
4110
+ status: "done"
4111
+ });
4112
+ } catch (err) {
4113
+ const message = err instanceof Error ? err.message : String(err);
4114
+ await this._updateQueueEntry(entry.id, {
4115
+ status: "error",
4116
+ error: message
4117
+ });
4118
+ this.emit("upload:error", {
4119
+ ...entry,
4120
+ status: "error",
4121
+ error: message
4122
+ });
4123
+ }
4124
+ }
4125
+ } finally {
4126
+ this._flushing = false;
4127
+ }
4128
+ }
4129
+ async _updateQueueEntry(id, patch) {
4130
+ const db = await this.getDb();
4131
+ if (!db) return;
4132
+ return new Promise((resolve, reject) => {
4133
+ const tx = db.transaction("upload_queue", "readwrite");
4134
+ const store = tx.objectStore("upload_queue");
4135
+ const req = store.get(id);
4136
+ req.onsuccess = () => {
4137
+ if (!req.result) {
4138
+ resolve();
4139
+ return;
4140
+ }
4141
+ const updated = {
4142
+ ...req.result,
4143
+ ...patch
4144
+ };
4145
+ store.put(updated);
4146
+ tx.oncomplete = () => resolve();
4147
+ tx.onerror = () => reject(tx.error);
4148
+ };
4149
+ req.onerror = () => reject(req.error);
4150
+ });
4151
+ }
4152
+ destroy() {
4153
+ if (typeof window !== "undefined") window.removeEventListener("online", this._onlineHandler);
4154
+ for (const url of this.objectUrls.values()) URL.revokeObjectURL(url);
4155
+ this.objectUrls.clear();
4156
+ this.db?.close();
4157
+ this.db = null;
4158
+ this.removeAllListeners();
4159
+ }
4160
+ };
4161
+
4162
+ //#endregion
4163
+ export { AbracadabraBaseProvider, AbracadabraClient, AbracadabraProvider, AbracadabraWS, AuthMessageType, AwarenessError, ConnectionTimeout, CryptoIdentityKeystore, DocumentCache, FileBlobStore, Forbidden, HocuspocusProvider, HocuspocusProviderWebsocket, MessageTooBig, MessageType, OfflineStore, ResetConnection, SearchIndex, SubdocMessage, Unauthorized, WebSocketStatus, WsReadyStates, awarenessStatesToArray, readAuthMessage, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
3501
4164
  //# sourceMappingURL=abracadabra-provider.esm.js.map