@abraca/dabra 1.0.24 → 1.0.25

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.
@@ -9935,17 +9935,20 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
9935
9935
  const pc = this.peerConnections.get(peerId);
9936
9936
  if (!pc) return;
9937
9937
  const channelName = "custom";
9938
- let channel = pc.router.getChannel(channelName);
9939
- if (!channel || channel.readyState !== "open") {
9940
- channel = pc.router.createChannel(channelName, { ordered: true });
9941
- channel.onopen = () => {
9942
- const data = new TextEncoder().encode(payload);
9943
- pc.router.send(channelName, data).catch(() => {});
9944
- };
9938
+ const channel = pc.router.getChannel(channelName);
9939
+ if (channel && channel.readyState === "open") {
9940
+ const data = new TextEncoder().encode(payload);
9941
+ pc.router.send(channelName, data).catch(() => {});
9945
9942
  return;
9946
9943
  }
9947
- const data = new TextEncoder().encode(payload);
9948
- pc.router.send(channelName, data);
9944
+ const onOpen = ({ name }) => {
9945
+ if (name === channelName) {
9946
+ pc.router.off("channelOpen", onOpen);
9947
+ const data = new TextEncoder().encode(payload);
9948
+ pc.router.send(channelName, data).catch(() => {});
9949
+ }
9950
+ };
9951
+ pc.router.on("channelOpen", onOpen);
9949
9952
  }
9950
9953
  /**
9951
9954
  * Send a custom string message to all connected peers.
@@ -10101,6 +10104,7 @@ var AbracadabraWebRTC = class AbracadabraWebRTC extends EventEmitter {
10101
10104
  enableAwareness: this.config.enableAwarenessSync,
10102
10105
  enableFileTransfer: this.config.enableFileTransfer
10103
10106
  });
10107
+ pc.router.createChannel("custom", { ordered: true });
10104
10108
  try {
10105
10109
  const sdp = await pc.createOffer();
10106
10110
  this.signaling?.sendOffer(peerId, sdp);
@@ -10897,6 +10901,9 @@ var IdentityDocProvider = class extends EventEmitter {
10897
10901
  get pluginsMap() {
10898
10902
  return this.document.getMap("plugins");
10899
10903
  }
10904
+ get devicesMap() {
10905
+ return this.document.getMap("devices");
10906
+ }
10900
10907
  get preferencesMap() {
10901
10908
  return this.document.getMap("preferences");
10902
10909
  }
@@ -11024,6 +11031,97 @@ var IdentityDocProvider = class extends EventEmitter {
11024
11031
  setPreference(key, value) {
11025
11032
  this.preferencesMap.set(key, value);
11026
11033
  }
11034
+ getDevice(publicKey) {
11035
+ const entry = this.devicesMap.get(publicKey);
11036
+ if (!entry) return void 0;
11037
+ const servers = /* @__PURE__ */ new Map();
11038
+ const serversYMap = entry.get("servers");
11039
+ if (serversYMap) serversYMap.forEach((sEntry, url) => {
11040
+ servers.set(url, {
11041
+ registered: sEntry.get("registered") ?? false,
11042
+ registeredAt: sEntry.get("registeredAt") ?? null,
11043
+ keyId: sEntry.get("keyId") ?? null,
11044
+ lastVerifiedAt: sEntry.get("lastVerifiedAt") ?? null,
11045
+ error: sEntry.get("error") ?? null
11046
+ });
11047
+ });
11048
+ return {
11049
+ deviceName: entry.get("deviceName") ?? "Unknown",
11050
+ tier: entry.get("tier") ?? "paired",
11051
+ x25519Key: entry.get("x25519Key") ?? null,
11052
+ addedAt: entry.get("addedAt") ?? 0,
11053
+ revokedAt: entry.get("revokedAt") ?? null,
11054
+ revokedBy: entry.get("revokedBy") ?? null,
11055
+ servers
11056
+ };
11057
+ }
11058
+ getDevices() {
11059
+ const result = /* @__PURE__ */ new Map();
11060
+ this.devicesMap.forEach((_entry, pubKey) => {
11061
+ const device = this.getDevice(pubKey);
11062
+ if (device) result.set(pubKey, device);
11063
+ });
11064
+ return result;
11065
+ }
11066
+ addDevice(publicKey, opts) {
11067
+ this.document.transact(() => {
11068
+ let entry = this.devicesMap.get(publicKey);
11069
+ if (!entry) {
11070
+ entry = new Y.Map();
11071
+ this.devicesMap.set(publicKey, entry);
11072
+ }
11073
+ entry.set("deviceName", opts.deviceName);
11074
+ entry.set("tier", opts.tier);
11075
+ if (opts.x25519Key) entry.set("x25519Key", opts.x25519Key);
11076
+ entry.set("addedAt", Date.now());
11077
+ entry.set("revokedAt", null);
11078
+ entry.set("revokedBy", null);
11079
+ if (opts.serverUrl) {
11080
+ let servers = entry.get("servers");
11081
+ if (!servers) {
11082
+ servers = new Y.Map();
11083
+ entry.set("servers", servers);
11084
+ }
11085
+ const sEntry = new Y.Map();
11086
+ sEntry.set("registered", true);
11087
+ sEntry.set("registeredAt", Date.now());
11088
+ sEntry.set("keyId", opts.keyId ?? null);
11089
+ sEntry.set("lastVerifiedAt", Date.now());
11090
+ sEntry.set("error", null);
11091
+ servers.set(opts.serverUrl, sEntry);
11092
+ }
11093
+ });
11094
+ }
11095
+ revokeDevice(publicKey, revokedBy) {
11096
+ const entry = this.devicesMap.get(publicKey);
11097
+ if (!entry) return;
11098
+ this.document.transact(() => {
11099
+ entry.set("revokedAt", Date.now());
11100
+ entry.set("revokedBy", revokedBy);
11101
+ });
11102
+ }
11103
+ setDeviceServerStatus(devicePubKey, serverUrl, status) {
11104
+ const entry = this.devicesMap.get(devicePubKey);
11105
+ if (!entry) return;
11106
+ this.document.transact(() => {
11107
+ let servers = entry.get("servers");
11108
+ if (!servers) {
11109
+ servers = new Y.Map();
11110
+ entry.set("servers", servers);
11111
+ }
11112
+ let sEntry = servers.get(serverUrl);
11113
+ if (!sEntry) {
11114
+ sEntry = new Y.Map();
11115
+ sEntry.set("registered", false);
11116
+ sEntry.set("registeredAt", null);
11117
+ sEntry.set("keyId", null);
11118
+ sEntry.set("lastVerifiedAt", null);
11119
+ sEntry.set("error", null);
11120
+ servers.set(serverUrl, sEntry);
11121
+ }
11122
+ for (const [key, value] of Object.entries(status)) if (value !== void 0) sEntry.set(key, value);
11123
+ });
11124
+ }
11027
11125
  /**
11028
11126
  * Observe deep changes on a specific top-level map.
11029
11127
  * Returns an unsubscribe function.
@@ -11089,5 +11187,153 @@ var IdentityDocProvider = class extends EventEmitter {
11089
11187
  };
11090
11188
 
11091
11189
  //#endregion
11092
- 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 };
11190
+ //#region packages/provider/src/DeviceRegistrationService.ts
11191
+ /**
11192
+ * Handles cross-server device key registration and revocation propagation.
11193
+ *
11194
+ * All methods operate on the identity Y.Doc's `devicesMap` and authenticate
11195
+ * independently on each server using the master key's `signChallenge`.
11196
+ */
11197
+ var DeviceRegistrationService = class {
11198
+ /**
11199
+ * Fan out a device key to all servers in `serversMap` where it isn't registered.
11200
+ *
11201
+ * Must be called from a master device that can sign challenges with the
11202
+ * account-level private key.
11203
+ */
11204
+ static async fanOutDeviceKey(opts) {
11205
+ const { devicePubKey, x25519Key, deviceName, identityDoc, masterPubKey, signChallenge, skipServerUrl } = opts;
11206
+ const servers = identityDoc.getServers();
11207
+ const results = [];
11208
+ if (!identityDoc.getDevice(devicePubKey)) identityDoc.addDevice(devicePubKey, {
11209
+ deviceName: deviceName ?? "Unknown",
11210
+ tier: "paired",
11211
+ x25519Key
11212
+ });
11213
+ for (const [serverUrl] of servers) {
11214
+ if (skipServerUrl && serverUrl === skipServerUrl) continue;
11215
+ if ((identityDoc.getDevice(devicePubKey)?.servers.get(serverUrl))?.registered) continue;
11216
+ try {
11217
+ const client = new AbracadabraClient({ url: serverUrl });
11218
+ await client.loginWithKey(masterPubKey, signChallenge);
11219
+ await client.addKey({
11220
+ publicKey: devicePubKey,
11221
+ x25519Key,
11222
+ deviceName
11223
+ });
11224
+ const match = (await client.listKeys()).find((k) => k.publicKey === devicePubKey && !k.revoked);
11225
+ identityDoc.setDeviceServerStatus(devicePubKey, serverUrl, {
11226
+ registered: true,
11227
+ registeredAt: Date.now(),
11228
+ keyId: match?.id ?? null,
11229
+ lastVerifiedAt: Date.now(),
11230
+ error: null
11231
+ });
11232
+ results.push({
11233
+ serverUrl,
11234
+ success: true
11235
+ });
11236
+ } catch (e) {
11237
+ identityDoc.setDeviceServerStatus(devicePubKey, serverUrl, { error: e?.message ?? "Registration failed" });
11238
+ results.push({
11239
+ serverUrl,
11240
+ success: false,
11241
+ error: e?.message ?? "Registration failed"
11242
+ });
11243
+ }
11244
+ }
11245
+ return results;
11246
+ }
11247
+ /**
11248
+ * Propagate pending revocations from the identity Y.Doc to all servers.
11249
+ *
11250
+ * Scans `devicesMap` for entries with `revokedAt` set and revokes them
11251
+ * on every server where they're still registered. Must be called from
11252
+ * a master device.
11253
+ */
11254
+ static async propagateRevocations(opts) {
11255
+ const { identityDoc, masterPubKey, signChallenge } = opts;
11256
+ const devices = identityDoc.getDevices();
11257
+ const results = [];
11258
+ for (const [devicePubKey, device] of devices) {
11259
+ if (!device.revokedAt) continue;
11260
+ for (const [serverUrl, serverStatus] of device.servers) {
11261
+ if (!serverStatus.registered || !serverStatus.keyId) continue;
11262
+ try {
11263
+ const client = new AbracadabraClient({ url: serverUrl });
11264
+ await client.loginWithKey(masterPubKey, signChallenge);
11265
+ await client.revokeKey(serverStatus.keyId);
11266
+ identityDoc.setDeviceServerStatus(devicePubKey, serverUrl, {
11267
+ registered: false,
11268
+ error: null
11269
+ });
11270
+ results.push({
11271
+ serverUrl,
11272
+ success: true
11273
+ });
11274
+ } catch (e) {
11275
+ results.push({
11276
+ serverUrl,
11277
+ success: false,
11278
+ error: e?.message ?? "Revocation failed"
11279
+ });
11280
+ }
11281
+ }
11282
+ }
11283
+ return results;
11284
+ }
11285
+ /**
11286
+ * Reconcile the identity Y.Doc's `devicesMap` with a server's actual
11287
+ * device key list. Call this on every server connection.
11288
+ *
11289
+ * - Updates registration status in Y.Doc to match server reality
11290
+ * - Imports unknown server keys as legacy entries
11291
+ * - Executes pending revocations if the caller is a master device
11292
+ */
11293
+ static async reconcile(opts) {
11294
+ const { identityDoc, serverUrl, client, isMaster } = opts;
11295
+ let serverKeys;
11296
+ try {
11297
+ serverKeys = await client.listKeys();
11298
+ } catch {
11299
+ return;
11300
+ }
11301
+ const devicesMap = identityDoc.devicesMap;
11302
+ devicesMap.forEach((yEntry, devicePub) => {
11303
+ const serverEntry = yEntry.get("servers")?.get(serverUrl);
11304
+ const serverKey = serverKeys.find((k) => k.publicKey === devicePub);
11305
+ const revokedAt = yEntry.get("revokedAt");
11306
+ if (serverEntry?.get("registered") && !serverKey) identityDoc.setDeviceServerStatus(devicePub, serverUrl, {
11307
+ registered: false,
11308
+ keyId: null
11309
+ });
11310
+ if (serverKey && !serverKey.revoked) {
11311
+ if (!serverEntry?.get("registered")) identityDoc.setDeviceServerStatus(devicePub, serverUrl, {
11312
+ registered: true,
11313
+ keyId: serverKey.id,
11314
+ lastVerifiedAt: Date.now()
11315
+ });
11316
+ else identityDoc.setDeviceServerStatus(devicePub, serverUrl, {
11317
+ lastVerifiedAt: Date.now(),
11318
+ keyId: serverKey.id
11319
+ });
11320
+ if (revokedAt && isMaster) client.revokeKey(serverKey.id).then(() => {
11321
+ identityDoc.setDeviceServerStatus(devicePub, serverUrl, { registered: false });
11322
+ }).catch(() => {});
11323
+ }
11324
+ });
11325
+ for (const key of serverKeys) {
11326
+ if (key.revoked) continue;
11327
+ if (!devicesMap.has(key.publicKey)) identityDoc.addDevice(key.publicKey, {
11328
+ deviceName: key.deviceName ?? "Unknown",
11329
+ tier: "paired",
11330
+ serverUrl,
11331
+ keyId: key.id
11332
+ });
11333
+ }
11334
+ }
11335
+ };
11336
+
11337
+ //#endregion
11338
+ export { AbracadabraBaseProvider, AbracadabraClient, AbracadabraProvider, AbracadabraWS, AbracadabraWebRTC, AuthMessageType, AwarenessError, BackgroundSyncManager, BackgroundSyncPersistence, BroadcastChannelSync, CHANNEL_NAMES, ConnectionTimeout, CryptoIdentityKeystore, DEFAULT_FILE_CHUNK_SIZE, DEFAULT_ICE_SERVERS, DataChannelRouter, DevicePairingChannel, DeviceRegistrationService, 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 };
11093
11339
  //# sourceMappingURL=abracadabra-provider.esm.js.map