@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
|
@@ -2822,7 +2822,7 @@ var AbracadabraProvider = class AbracadabraProvider extends AbracadabraBaseProvi
|
|
|
2822
2822
|
this._client = client;
|
|
2823
2823
|
this.abracadabraConfig = configuration;
|
|
2824
2824
|
this.subdocLoading = configuration.subdocLoading ?? "lazy";
|
|
2825
|
-
const serverOrigin = AbracadabraProvider.deriveServerOrigin(configuration, client);
|
|
2825
|
+
const serverOrigin = configuration.serverAgnostic ? void 0 : AbracadabraProvider.deriveServerOrigin(configuration, client);
|
|
2826
2826
|
this.offlineStore = configuration.disableOfflineStore ? null : new OfflineStore(configuration.name, serverOrigin);
|
|
2827
2827
|
this.on("subdocRegistered", configuration.onSubdocRegistered ?? (() => null));
|
|
2828
2828
|
this.on("subdocLoaded", configuration.onSubdocLoaded ?? (() => null));
|
|
@@ -10747,6 +10747,305 @@ var BroadcastChannelSync = class BroadcastChannelSync extends EventEmitter {
|
|
|
10747
10747
|
}
|
|
10748
10748
|
};
|
|
10749
10749
|
|
|
10750
|
+
//#endregion
|
|
10751
|
+
//#region packages/provider/src/IdentityDoc.ts
|
|
10752
|
+
/**
|
|
10753
|
+
* Derives a deterministic UUID from an Ed25519 account-level public key.
|
|
10754
|
+
*
|
|
10755
|
+
* The result is a valid UUID v5-style string that any device sharing the
|
|
10756
|
+
* same identity can independently compute. The `abracadabra:identity:`
|
|
10757
|
+
* prefix prevents collisions with randomly generated doc UUIDs.
|
|
10758
|
+
*
|
|
10759
|
+
* @param publicKeyB64 Base64url-encoded Ed25519 public key (32 bytes).
|
|
10760
|
+
*/
|
|
10761
|
+
function deriveIdentityDocId(publicKeyB64) {
|
|
10762
|
+
const hash = sha256(new TextEncoder().encode(`abracadabra:identity:${publicKeyB64}`));
|
|
10763
|
+
const bytes = new Uint8Array(hash.buffer, hash.byteOffset, 16);
|
|
10764
|
+
bytes[6] = bytes[6] & 15 | 80;
|
|
10765
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
10766
|
+
const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
10767
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20, 32)}`;
|
|
10768
|
+
}
|
|
10769
|
+
/**
|
|
10770
|
+
* Manages a Y.Doc dedicated to user identity that syncs across trusted
|
|
10771
|
+
* targets only: a designated sync server, a local Tauri server, and/or
|
|
10772
|
+
* WebRTC P2P.
|
|
10773
|
+
*
|
|
10774
|
+
* The Y.Doc contains cross-device settings (profile, servers, spaces,
|
|
10775
|
+
* plugins, preferences). Each sync target gets its own
|
|
10776
|
+
* AbracadabraProvider sharing the same Y.Doc, with `serverAgnostic: true`
|
|
10777
|
+
* so they all use one IndexedDB store.
|
|
10778
|
+
*/
|
|
10779
|
+
var IdentityDocProvider = class extends EventEmitter {
|
|
10780
|
+
constructor(configuration) {
|
|
10781
|
+
super();
|
|
10782
|
+
this.providers = /* @__PURE__ */ new Map();
|
|
10783
|
+
this.websockets = /* @__PURE__ */ new Map();
|
|
10784
|
+
this.webrtc = null;
|
|
10785
|
+
this._destroyed = false;
|
|
10786
|
+
this.config = configuration;
|
|
10787
|
+
this.docId = deriveIdentityDocId(configuration.publicKey);
|
|
10788
|
+
this.document = new yjs.Doc({ guid: this.docId });
|
|
10789
|
+
if (configuration.autoConnect !== false) this.connect();
|
|
10790
|
+
}
|
|
10791
|
+
connect() {
|
|
10792
|
+
if (this._destroyed) return;
|
|
10793
|
+
const targets = [];
|
|
10794
|
+
if (this.config.localServerUrl) targets.push({
|
|
10795
|
+
url: this.config.localServerUrl,
|
|
10796
|
+
key: "local"
|
|
10797
|
+
});
|
|
10798
|
+
if (this.config.syncServerUrl) targets.push({
|
|
10799
|
+
url: this.config.syncServerUrl,
|
|
10800
|
+
key: "sync"
|
|
10801
|
+
});
|
|
10802
|
+
for (const { url, key } of targets) {
|
|
10803
|
+
if (this.providers.has(key)) continue;
|
|
10804
|
+
this._connectToServer(key, url);
|
|
10805
|
+
}
|
|
10806
|
+
if (this.config.webrtc && !this.webrtc) this._connectWebRTC();
|
|
10807
|
+
}
|
|
10808
|
+
_connectToServer(key, serverUrl) {
|
|
10809
|
+
const token = this.config.tokens?.[serverUrl] ?? this.config.token ?? "";
|
|
10810
|
+
const ws = new AbracadabraWS({
|
|
10811
|
+
url: serverUrl.replace(/^http/, "ws").replace(/\/$/, "").concat("/ws"),
|
|
10812
|
+
WebSocketPolyfill: void 0
|
|
10813
|
+
});
|
|
10814
|
+
this.websockets.set(key, ws);
|
|
10815
|
+
const provider = new AbracadabraProvider({
|
|
10816
|
+
name: this.docId,
|
|
10817
|
+
document: this.document,
|
|
10818
|
+
websocketProvider: ws,
|
|
10819
|
+
token,
|
|
10820
|
+
serverAgnostic: true,
|
|
10821
|
+
disableOfflineStore: key !== "local" && key !== "sync" ? true : this.config.disableOfflineStore ?? false,
|
|
10822
|
+
...this.config.providerDefaults
|
|
10823
|
+
});
|
|
10824
|
+
provider.on("synced", () => this.emit("synced", { server: key }));
|
|
10825
|
+
provider.on("status", (data) => this.emit("status", {
|
|
10826
|
+
server: key,
|
|
10827
|
+
...data
|
|
10828
|
+
}));
|
|
10829
|
+
this.providers.set(key, provider);
|
|
10830
|
+
}
|
|
10831
|
+
_connectWebRTC() {
|
|
10832
|
+
const rtcConfig = this.config.webrtc;
|
|
10833
|
+
if (!rtcConfig) return;
|
|
10834
|
+
this.webrtc = new AbracadabraWebRTC({
|
|
10835
|
+
docId: this.docId,
|
|
10836
|
+
url: rtcConfig.signalingServerUrl,
|
|
10837
|
+
token: rtcConfig.token,
|
|
10838
|
+
document: this.document,
|
|
10839
|
+
enableDocSync: true,
|
|
10840
|
+
enableAwarenessSync: false,
|
|
10841
|
+
enableFileTransfer: false,
|
|
10842
|
+
e2ee: rtcConfig.e2ee,
|
|
10843
|
+
iceServers: rtcConfig.iceServers
|
|
10844
|
+
});
|
|
10845
|
+
}
|
|
10846
|
+
get profileMap() {
|
|
10847
|
+
return this.document.getMap("profile");
|
|
10848
|
+
}
|
|
10849
|
+
get serversMap() {
|
|
10850
|
+
return this.document.getMap("servers");
|
|
10851
|
+
}
|
|
10852
|
+
get spacesArray() {
|
|
10853
|
+
return this.document.getArray("spaces");
|
|
10854
|
+
}
|
|
10855
|
+
get pluginsMap() {
|
|
10856
|
+
return this.document.getMap("plugins");
|
|
10857
|
+
}
|
|
10858
|
+
get preferencesMap() {
|
|
10859
|
+
return this.document.getMap("preferences");
|
|
10860
|
+
}
|
|
10861
|
+
getProfile() {
|
|
10862
|
+
const m = this.profileMap;
|
|
10863
|
+
return {
|
|
10864
|
+
username: m.get("username"),
|
|
10865
|
+
displayName: m.get("displayName"),
|
|
10866
|
+
colorName: m.get("colorName"),
|
|
10867
|
+
neutralColorName: m.get("neutralColorName"),
|
|
10868
|
+
locale: m.get("locale"),
|
|
10869
|
+
avatarUrl: m.get("avatarUrl")
|
|
10870
|
+
};
|
|
10871
|
+
}
|
|
10872
|
+
setProfile(profile) {
|
|
10873
|
+
const m = this.profileMap;
|
|
10874
|
+
this.document.transact(() => {
|
|
10875
|
+
for (const [key, value] of Object.entries(profile)) if (value !== void 0) m.set(key, value);
|
|
10876
|
+
});
|
|
10877
|
+
}
|
|
10878
|
+
getServer(url) {
|
|
10879
|
+
const entry = this.serversMap.get(url);
|
|
10880
|
+
if (!entry) return void 0;
|
|
10881
|
+
return {
|
|
10882
|
+
label: entry.get("label") ?? url,
|
|
10883
|
+
hubDocId: entry.get("hubDocId"),
|
|
10884
|
+
entryDocId: entry.get("entryDocId"),
|
|
10885
|
+
defaultRole: entry.get("defaultRole"),
|
|
10886
|
+
spacesEnabled: entry.get("spacesEnabled"),
|
|
10887
|
+
addedAt: entry.get("addedAt") ?? 0
|
|
10888
|
+
};
|
|
10889
|
+
}
|
|
10890
|
+
setServer(url, entry) {
|
|
10891
|
+
const m = this.serversMap;
|
|
10892
|
+
this.document.transact(() => {
|
|
10893
|
+
let yEntry = m.get(url);
|
|
10894
|
+
if (!yEntry) {
|
|
10895
|
+
yEntry = new yjs.Map();
|
|
10896
|
+
m.set(url, yEntry);
|
|
10897
|
+
}
|
|
10898
|
+
for (const [key, value] of Object.entries(entry)) if (value !== void 0) yEntry.set(key, value);
|
|
10899
|
+
});
|
|
10900
|
+
}
|
|
10901
|
+
removeServer(url) {
|
|
10902
|
+
this.serversMap.delete(url);
|
|
10903
|
+
}
|
|
10904
|
+
getServers() {
|
|
10905
|
+
const result = /* @__PURE__ */ new Map();
|
|
10906
|
+
this.serversMap.forEach((entry, url) => {
|
|
10907
|
+
result.set(url, {
|
|
10908
|
+
label: entry.get("label") ?? url,
|
|
10909
|
+
hubDocId: entry.get("hubDocId"),
|
|
10910
|
+
entryDocId: entry.get("entryDocId"),
|
|
10911
|
+
defaultRole: entry.get("defaultRole"),
|
|
10912
|
+
spacesEnabled: entry.get("spacesEnabled"),
|
|
10913
|
+
addedAt: entry.get("addedAt") ?? 0
|
|
10914
|
+
});
|
|
10915
|
+
});
|
|
10916
|
+
return result;
|
|
10917
|
+
}
|
|
10918
|
+
getSpaces() {
|
|
10919
|
+
const result = [];
|
|
10920
|
+
this.spacesArray.forEach((yMap) => {
|
|
10921
|
+
result.push({
|
|
10922
|
+
id: yMap.get("id"),
|
|
10923
|
+
name: yMap.get("name"),
|
|
10924
|
+
type: yMap.get("type") ?? "remote",
|
|
10925
|
+
serverUrl: yMap.get("serverUrl") ?? null,
|
|
10926
|
+
docId: yMap.get("docId"),
|
|
10927
|
+
remoteSpaceId: yMap.get("remoteSpaceId"),
|
|
10928
|
+
visibility: yMap.get("visibility") ?? "private",
|
|
10929
|
+
isHub: yMap.get("isHub") ?? false,
|
|
10930
|
+
order: yMap.get("order") ?? 0,
|
|
10931
|
+
lastSyncedAt: yMap.get("lastSyncedAt"),
|
|
10932
|
+
createdAt: yMap.get("createdAt") ?? 0
|
|
10933
|
+
});
|
|
10934
|
+
});
|
|
10935
|
+
return result;
|
|
10936
|
+
}
|
|
10937
|
+
addSpace(space) {
|
|
10938
|
+
const yMap = new yjs.Map();
|
|
10939
|
+
this.document.transact(() => {
|
|
10940
|
+
for (const [key, value] of Object.entries(space)) if (value !== void 0) yMap.set(key, value);
|
|
10941
|
+
this.spacesArray.push([yMap]);
|
|
10942
|
+
});
|
|
10943
|
+
}
|
|
10944
|
+
removeSpace(spaceId) {
|
|
10945
|
+
const arr = this.spacesArray;
|
|
10946
|
+
for (let i = 0; i < arr.length; i++) if (arr.get(i).get("id") === spaceId) {
|
|
10947
|
+
arr.delete(i, 1);
|
|
10948
|
+
return;
|
|
10949
|
+
}
|
|
10950
|
+
}
|
|
10951
|
+
updateSpace(spaceId, updates) {
|
|
10952
|
+
const arr = this.spacesArray;
|
|
10953
|
+
for (let i = 0; i < arr.length; i++) {
|
|
10954
|
+
const yMap = arr.get(i);
|
|
10955
|
+
if (yMap.get("id") === spaceId) {
|
|
10956
|
+
this.document.transact(() => {
|
|
10957
|
+
for (const [key, value] of Object.entries(updates)) if (value !== void 0) yMap.set(key, value);
|
|
10958
|
+
});
|
|
10959
|
+
return;
|
|
10960
|
+
}
|
|
10961
|
+
}
|
|
10962
|
+
}
|
|
10963
|
+
getExternalPlugins() {
|
|
10964
|
+
let arr = this.pluginsMap.get("external");
|
|
10965
|
+
if (!arr) {
|
|
10966
|
+
arr = new yjs.Array();
|
|
10967
|
+
this.pluginsMap.set("external", arr);
|
|
10968
|
+
}
|
|
10969
|
+
return arr;
|
|
10970
|
+
}
|
|
10971
|
+
getDisabledBuiltins() {
|
|
10972
|
+
let arr = this.pluginsMap.get("disabledBuiltins");
|
|
10973
|
+
if (!arr) {
|
|
10974
|
+
arr = new yjs.Array();
|
|
10975
|
+
this.pluginsMap.set("disabledBuiltins", arr);
|
|
10976
|
+
}
|
|
10977
|
+
return arr;
|
|
10978
|
+
}
|
|
10979
|
+
getPreference(key) {
|
|
10980
|
+
return this.preferencesMap.get(key);
|
|
10981
|
+
}
|
|
10982
|
+
setPreference(key, value) {
|
|
10983
|
+
this.preferencesMap.set(key, value);
|
|
10984
|
+
}
|
|
10985
|
+
/**
|
|
10986
|
+
* Observe deep changes on a specific top-level map.
|
|
10987
|
+
* Returns an unsubscribe function.
|
|
10988
|
+
*/
|
|
10989
|
+
observe(mapName, callback) {
|
|
10990
|
+
const map = this.document.getMap(mapName);
|
|
10991
|
+
map.observeDeep(callback);
|
|
10992
|
+
return () => map.unobserveDeep(callback);
|
|
10993
|
+
}
|
|
10994
|
+
/**
|
|
10995
|
+
* Observe changes to the spaces array.
|
|
10996
|
+
* Returns an unsubscribe function.
|
|
10997
|
+
*/
|
|
10998
|
+
observeSpaces(callback) {
|
|
10999
|
+
const arr = this.spacesArray;
|
|
11000
|
+
arr.observe(callback);
|
|
11001
|
+
return () => arr.unobserve(callback);
|
|
11002
|
+
}
|
|
11003
|
+
/**
|
|
11004
|
+
* Returns true if the identity doc has no profile data yet (first use).
|
|
11005
|
+
* Call this to decide whether to run migration from localStorage.
|
|
11006
|
+
*/
|
|
11007
|
+
isEmpty() {
|
|
11008
|
+
return this.profileMap.size === 0 && this.serversMap.size === 0;
|
|
11009
|
+
}
|
|
11010
|
+
/**
|
|
11011
|
+
* Update the sync server URL at runtime (e.g. when user changes their
|
|
11012
|
+
* designated sync server in settings).
|
|
11013
|
+
*/
|
|
11014
|
+
setSyncServer(url) {
|
|
11015
|
+
const existing = this.providers.get("sync");
|
|
11016
|
+
if (existing) {
|
|
11017
|
+
existing.destroy();
|
|
11018
|
+
this.providers.delete("sync");
|
|
11019
|
+
}
|
|
11020
|
+
const existingWs = this.websockets.get("sync");
|
|
11021
|
+
if (existingWs) {
|
|
11022
|
+
existingWs.destroy();
|
|
11023
|
+
this.websockets.delete("sync");
|
|
11024
|
+
}
|
|
11025
|
+
if (url) {
|
|
11026
|
+
this.config = {
|
|
11027
|
+
...this.config,
|
|
11028
|
+
syncServerUrl: url
|
|
11029
|
+
};
|
|
11030
|
+
this._connectToServer("sync", url);
|
|
11031
|
+
}
|
|
11032
|
+
}
|
|
11033
|
+
getProvider(key) {
|
|
11034
|
+
return this.providers.get(key);
|
|
11035
|
+
}
|
|
11036
|
+
destroy() {
|
|
11037
|
+
if (this._destroyed) return;
|
|
11038
|
+
this._destroyed = true;
|
|
11039
|
+
for (const [, provider] of this.providers) provider.destroy();
|
|
11040
|
+
for (const [, ws] of this.websockets) ws.destroy();
|
|
11041
|
+
if (this.webrtc) this.webrtc.disconnect?.();
|
|
11042
|
+
this.providers.clear();
|
|
11043
|
+
this.websockets.clear();
|
|
11044
|
+
this.webrtc = null;
|
|
11045
|
+
this.document.destroy();
|
|
11046
|
+
}
|
|
11047
|
+
};
|
|
11048
|
+
|
|
10750
11049
|
//#endregion
|
|
10751
11050
|
exports.AbracadabraBaseProvider = AbracadabraBaseProvider;
|
|
10752
11051
|
exports.AbracadabraClient = AbracadabraClient;
|
|
@@ -10778,6 +11077,7 @@ exports.FileTransferHandle = FileTransferHandle;
|
|
|
10778
11077
|
exports.Forbidden = Forbidden;
|
|
10779
11078
|
exports.HocuspocusProvider = HocuspocusProvider;
|
|
10780
11079
|
exports.HocuspocusProviderWebsocket = HocuspocusProviderWebsocket;
|
|
11080
|
+
exports.IdentityDocProvider = IdentityDocProvider;
|
|
10781
11081
|
exports.KEY_EXCHANGE_CHANNEL = KEY_EXCHANGE_CHANNEL;
|
|
10782
11082
|
exports.ManualSignaling = ManualSignaling;
|
|
10783
11083
|
exports.MessageTooBig = MessageTooBig;
|
|
@@ -10795,6 +11095,7 @@ exports.YjsDataChannel = YjsDataChannel;
|
|
|
10795
11095
|
exports.attachUpdatedAtObserver = attachUpdatedAtObserver;
|
|
10796
11096
|
exports.awarenessStatesToArray = awarenessStatesToArray;
|
|
10797
11097
|
exports.decryptField = decryptField;
|
|
11098
|
+
exports.deriveIdentityDocId = deriveIdentityDocId;
|
|
10798
11099
|
exports.encryptField = encryptField;
|
|
10799
11100
|
exports.makeEncryptedYMap = makeEncryptedYMap;
|
|
10800
11101
|
exports.makeEncryptedYText = makeEncryptedYText;
|