@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.
- package/dist/abracadabra-provider.cjs +302 -1
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +301 -2
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +148 -1
- package/package.json +1 -1
- package/src/AbracadabraProvider.ts +10 -1
- package/src/IdentityDoc.ts +497 -0
- package/src/index.ts +7 -0
|
@@ -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
|
-
|
|
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
|