@abraca/dabra 1.0.21 → 1.0.22

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.
@@ -2792,7 +2792,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
2792
2792
  this._client = client;
2793
2793
  this.abracadabraConfig = configuration;
2794
2794
  this.subdocLoading = configuration.subdocLoading ?? "lazy";
2795
- const serverOrigin = AbracadabraProvider.deriveServerOrigin(configuration, client);
2795
+ const serverOrigin = configuration.serverAgnostic ? void 0 : AbracadabraProvider.deriveServerOrigin(configuration, client);
2796
2796
  this.offlineStore = configuration.disableOfflineStore ? null : new OfflineStore(configuration.name, serverOrigin);
2797
2797
  this.on("subdocRegistered", configuration.onSubdocRegistered ?? (() => null));
2798
2798
  this.on("subdocLoaded", configuration.onSubdocLoaded ?? (() => null));
@@ -10696,5 +10696,304 @@ var BroadcastChannelSync = class BroadcastChannelSync extends EventEmitter {
10696
10696
  };
10697
10697
 
10698
10698
  //#endregion
10699
- export { AbracadabraBaseProvider, AbracadabraClient, AbracadabraProvider, AbracadabraWS, AbracadabraWebRTC, AuthMessageType, AwarenessError, BackgroundSyncManager, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ConnectionTimeout, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, DevicePairingChannel, DocKeyManager, DocumentCache, E2EAbracadabraProvider, E2EEChannel, E2EOfflineStore, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, Forbidden, HocuspocusProvider, HocuspocusProviderWebsocket, KEY_EXCHANGE_CHANNEL, ManualSignaling, MessageTooBig, MessageType, OfflineStore, PeerConnection, ResetConnection, SearchIndex, SignalingSocket, SubdocMessage, Unauthorized, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, decryptField, encryptField, makeEncryptedYMap, makeEncryptedYText, readAuthMessage, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
10699
+ //#region packages/provider/src/IdentityDoc.ts
10700
+ /**
10701
+ * Derives a deterministic UUID from an Ed25519 account-level public key.
10702
+ *
10703
+ * The result is a valid UUID v5-style string that any device sharing the
10704
+ * same identity can independently compute. The `abracadabra:identity:`
10705
+ * prefix prevents collisions with randomly generated doc UUIDs.
10706
+ *
10707
+ * @param publicKeyB64 Base64url-encoded Ed25519 public key (32 bytes).
10708
+ */
10709
+ function deriveIdentityDocId(publicKeyB64) {
10710
+ const hash = sha256(new TextEncoder().encode(`abracadabra:identity:${publicKeyB64}`));
10711
+ const bytes = new Uint8Array(hash.buffer, hash.byteOffset, 16);
10712
+ bytes[6] = bytes[6] & 15 | 80;
10713
+ bytes[8] = bytes[8] & 63 | 128;
10714
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
10715
+ return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
10716
+ }
10717
+ /**
10718
+ * Manages a Y.Doc dedicated to user identity that syncs across trusted
10719
+ * targets only: a designated sync server, a local Tauri server, and/or
10720
+ * WebRTC P2P.
10721
+ *
10722
+ * The Y.Doc contains cross-device settings (profile, servers, spaces,
10723
+ * plugins, preferences). Each sync target gets its own
10724
+ * AbracadabraProvider sharing the same Y.Doc, with `serverAgnostic: true`
10725
+ * so they all use one IndexedDB store.
10726
+ */
10727
+ var IdentityDocProvider = class extends EventEmitter {
10728
+ constructor(configuration) {
10729
+ super();
10730
+ this.providers = /* @__PURE__ */ new Map();
10731
+ this.websockets = /* @__PURE__ */ new Map();
10732
+ this.webrtc = null;
10733
+ this._destroyed = false;
10734
+ this.config = configuration;
10735
+ this.docId = deriveIdentityDocId(configuration.publicKey);
10736
+ this.document = new Y.Doc({ guid: this.docId });
10737
+ if (configuration.autoConnect !== false) this.connect();
10738
+ }
10739
+ connect() {
10740
+ if (this._destroyed) return;
10741
+ const targets = [];
10742
+ if (this.config.localServerUrl) targets.push({
10743
+ url: this.config.localServerUrl,
10744
+ key: "local"
10745
+ });
10746
+ if (this.config.syncServerUrl) targets.push({
10747
+ url: this.config.syncServerUrl,
10748
+ key: "sync"
10749
+ });
10750
+ for (const { url, key } of targets) {
10751
+ if (this.providers.has(key)) continue;
10752
+ this._connectToServer(key, url);
10753
+ }
10754
+ if (this.config.webrtc && !this.webrtc) this._connectWebRTC();
10755
+ }
10756
+ _connectToServer(key, serverUrl) {
10757
+ const token = this.config.tokens?.[serverUrl] ?? this.config.token ?? "";
10758
+ const ws = new AbracadabraWS({
10759
+ url: serverUrl.replace(/^http/, "ws").replace(/\/$/, "").concat("/ws"),
10760
+ WebSocketPolyfill: void 0
10761
+ });
10762
+ this.websockets.set(key, ws);
10763
+ const provider = new AbracadabraProvider({
10764
+ name: this.docId,
10765
+ document: this.document,
10766
+ websocketProvider: ws,
10767
+ token,
10768
+ serverAgnostic: true,
10769
+ disableOfflineStore: key !== "local" && key !== "sync" ? true : this.config.disableOfflineStore ?? false,
10770
+ ...this.config.providerDefaults
10771
+ });
10772
+ provider.on("synced", () => this.emit("synced", { server: key }));
10773
+ provider.on("status", (data) => this.emit("status", {
10774
+ server: key,
10775
+ ...data
10776
+ }));
10777
+ this.providers.set(key, provider);
10778
+ }
10779
+ _connectWebRTC() {
10780
+ const rtcConfig = this.config.webrtc;
10781
+ if (!rtcConfig) return;
10782
+ this.webrtc = new AbracadabraWebRTC({
10783
+ docId: this.docId,
10784
+ url: rtcConfig.signalingServerUrl,
10785
+ token: rtcConfig.token,
10786
+ document: this.document,
10787
+ enableDocSync: true,
10788
+ enableAwarenessSync: false,
10789
+ enableFileTransfer: false,
10790
+ e2ee: rtcConfig.e2ee,
10791
+ iceServers: rtcConfig.iceServers
10792
+ });
10793
+ }
10794
+ get profileMap() {
10795
+ return this.document.getMap("profile");
10796
+ }
10797
+ get serversMap() {
10798
+ return this.document.getMap("servers");
10799
+ }
10800
+ get spacesArray() {
10801
+ return this.document.getArray("spaces");
10802
+ }
10803
+ get pluginsMap() {
10804
+ return this.document.getMap("plugins");
10805
+ }
10806
+ get preferencesMap() {
10807
+ return this.document.getMap("preferences");
10808
+ }
10809
+ getProfile() {
10810
+ const m = this.profileMap;
10811
+ return {
10812
+ username: m.get("username"),
10813
+ displayName: m.get("displayName"),
10814
+ colorName: m.get("colorName"),
10815
+ neutralColorName: m.get("neutralColorName"),
10816
+ locale: m.get("locale"),
10817
+ avatarUrl: m.get("avatarUrl")
10818
+ };
10819
+ }
10820
+ setProfile(profile) {
10821
+ const m = this.profileMap;
10822
+ this.document.transact(() => {
10823
+ for (const [key, value] of Object.entries(profile)) if (value !== void 0) m.set(key, value);
10824
+ });
10825
+ }
10826
+ getServer(url) {
10827
+ const entry = this.serversMap.get(url);
10828
+ if (!entry) return void 0;
10829
+ return {
10830
+ label: entry.get("label") ?? url,
10831
+ hubDocId: entry.get("hubDocId"),
10832
+ entryDocId: entry.get("entryDocId"),
10833
+ defaultRole: entry.get("defaultRole"),
10834
+ spacesEnabled: entry.get("spacesEnabled"),
10835
+ addedAt: entry.get("addedAt") ?? 0
10836
+ };
10837
+ }
10838
+ setServer(url, entry) {
10839
+ const m = this.serversMap;
10840
+ this.document.transact(() => {
10841
+ let yEntry = m.get(url);
10842
+ if (!yEntry) {
10843
+ yEntry = new Y.Map();
10844
+ m.set(url, yEntry);
10845
+ }
10846
+ for (const [key, value] of Object.entries(entry)) if (value !== void 0) yEntry.set(key, value);
10847
+ });
10848
+ }
10849
+ removeServer(url) {
10850
+ this.serversMap.delete(url);
10851
+ }
10852
+ getServers() {
10853
+ const result = /* @__PURE__ */ new Map();
10854
+ this.serversMap.forEach((entry, url) => {
10855
+ result.set(url, {
10856
+ label: entry.get("label") ?? url,
10857
+ hubDocId: entry.get("hubDocId"),
10858
+ entryDocId: entry.get("entryDocId"),
10859
+ defaultRole: entry.get("defaultRole"),
10860
+ spacesEnabled: entry.get("spacesEnabled"),
10861
+ addedAt: entry.get("addedAt") ?? 0
10862
+ });
10863
+ });
10864
+ return result;
10865
+ }
10866
+ getSpaces() {
10867
+ const result = [];
10868
+ this.spacesArray.forEach((yMap) => {
10869
+ result.push({
10870
+ id: yMap.get("id"),
10871
+ name: yMap.get("name"),
10872
+ type: yMap.get("type") ?? "remote",
10873
+ serverUrl: yMap.get("serverUrl") ?? null,
10874
+ docId: yMap.get("docId"),
10875
+ remoteSpaceId: yMap.get("remoteSpaceId"),
10876
+ visibility: yMap.get("visibility") ?? "private",
10877
+ isHub: yMap.get("isHub") ?? false,
10878
+ order: yMap.get("order") ?? 0,
10879
+ lastSyncedAt: yMap.get("lastSyncedAt"),
10880
+ createdAt: yMap.get("createdAt") ?? 0
10881
+ });
10882
+ });
10883
+ return result;
10884
+ }
10885
+ addSpace(space) {
10886
+ const yMap = new Y.Map();
10887
+ this.document.transact(() => {
10888
+ for (const [key, value] of Object.entries(space)) if (value !== void 0) yMap.set(key, value);
10889
+ this.spacesArray.push([yMap]);
10890
+ });
10891
+ }
10892
+ removeSpace(spaceId) {
10893
+ const arr = this.spacesArray;
10894
+ for (let i = 0; i < arr.length; i++) if (arr.get(i).get("id") === spaceId) {
10895
+ arr.delete(i, 1);
10896
+ return;
10897
+ }
10898
+ }
10899
+ updateSpace(spaceId, updates) {
10900
+ const arr = this.spacesArray;
10901
+ for (let i = 0; i < arr.length; i++) {
10902
+ const yMap = arr.get(i);
10903
+ if (yMap.get("id") === spaceId) {
10904
+ this.document.transact(() => {
10905
+ for (const [key, value] of Object.entries(updates)) if (value !== void 0) yMap.set(key, value);
10906
+ });
10907
+ return;
10908
+ }
10909
+ }
10910
+ }
10911
+ getExternalPlugins() {
10912
+ let arr = this.pluginsMap.get("external");
10913
+ if (!arr) {
10914
+ arr = new Y.Array();
10915
+ this.pluginsMap.set("external", arr);
10916
+ }
10917
+ return arr;
10918
+ }
10919
+ getDisabledBuiltins() {
10920
+ let arr = this.pluginsMap.get("disabledBuiltins");
10921
+ if (!arr) {
10922
+ arr = new Y.Array();
10923
+ this.pluginsMap.set("disabledBuiltins", arr);
10924
+ }
10925
+ return arr;
10926
+ }
10927
+ getPreference(key) {
10928
+ return this.preferencesMap.get(key);
10929
+ }
10930
+ setPreference(key, value) {
10931
+ this.preferencesMap.set(key, value);
10932
+ }
10933
+ /**
10934
+ * Observe deep changes on a specific top-level map.
10935
+ * Returns an unsubscribe function.
10936
+ */
10937
+ observe(mapName, callback) {
10938
+ const map = this.document.getMap(mapName);
10939
+ map.observeDeep(callback);
10940
+ return () => map.unobserveDeep(callback);
10941
+ }
10942
+ /**
10943
+ * Observe changes to the spaces array.
10944
+ * Returns an unsubscribe function.
10945
+ */
10946
+ observeSpaces(callback) {
10947
+ const arr = this.spacesArray;
10948
+ arr.observe(callback);
10949
+ return () => arr.unobserve(callback);
10950
+ }
10951
+ /**
10952
+ * Returns true if the identity doc has no profile data yet (first use).
10953
+ * Call this to decide whether to run migration from localStorage.
10954
+ */
10955
+ isEmpty() {
10956
+ return this.profileMap.size === 0 && this.serversMap.size === 0;
10957
+ }
10958
+ /**
10959
+ * Update the sync server URL at runtime (e.g. when user changes their
10960
+ * designated sync server in settings).
10961
+ */
10962
+ setSyncServer(url) {
10963
+ const existing = this.providers.get("sync");
10964
+ if (existing) {
10965
+ existing.destroy();
10966
+ this.providers.delete("sync");
10967
+ }
10968
+ const existingWs = this.websockets.get("sync");
10969
+ if (existingWs) {
10970
+ existingWs.destroy();
10971
+ this.websockets.delete("sync");
10972
+ }
10973
+ if (url) {
10974
+ this.config = {
10975
+ ...this.config,
10976
+ syncServerUrl: url
10977
+ };
10978
+ this._connectToServer("sync", url);
10979
+ }
10980
+ }
10981
+ getProvider(key) {
10982
+ return this.providers.get(key);
10983
+ }
10984
+ destroy() {
10985
+ if (this._destroyed) return;
10986
+ this._destroyed = true;
10987
+ for (const [, provider] of this.providers) provider.destroy();
10988
+ for (const [, ws] of this.websockets) ws.destroy();
10989
+ if (this.webrtc) this.webrtc.disconnect?.();
10990
+ this.providers.clear();
10991
+ this.websockets.clear();
10992
+ this.webrtc = null;
10993
+ this.document.destroy();
10994
+ }
10995
+ };
10996
+
10997
+ //#endregion
10998
+ export { AbracadabraBaseProvider, AbracadabraClient, AbracadabraProvider, AbracadabraWS, AbracadabraWebRTC, AuthMessageType, AwarenessError, BackgroundSyncManager, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ConnectionTimeout, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, DevicePairingChannel, DocKeyManager, DocumentCache, E2EAbracadabraProvider, E2EEChannel, E2EOfflineStore, EncryptedYMap, EncryptedYText, FileBlobStore, FileTransferChannel, FileTransferHandle, Forbidden, HocuspocusProvider, HocuspocusProviderWebsocket, IdentityDocProvider, KEY_EXCHANGE_CHANNEL, ManualSignaling, MessageTooBig, MessageType, OfflineStore, PeerConnection, ResetConnection, SearchIndex, SignalingSocket, SubdocMessage, Unauthorized, WebSocketStatus, WsReadyStates, YjsDataChannel, attachUpdatedAtObserver, awarenessStatesToArray, decryptField, deriveIdentityDocId, encryptField, makeEncryptedYMap, makeEncryptedYText, readAuthMessage, writeAuthenticated, writeAuthentication, writePermissionDenied, writeTokenSyncRequest };
10700
10999
  //# sourceMappingURL=abracadabra-provider.esm.js.map