@abraca/dabra 1.9.1 → 2.0.1
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 +12728 -9142
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +12746 -9210
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +1510 -118
- package/package.json +1 -1
- package/src/AbracadabraBaseProvider.ts +70 -2
- package/src/AbracadabraClient.ts +572 -66
- package/src/AbracadabraProvider.ts +22 -7
- package/src/AbracadabraWS.ts +1 -1
- package/src/ChatClient.ts +193 -113
- package/src/ContentManager.ts +80 -12
- package/src/CryptoIdentityKeystore.ts +3 -3
- package/src/DocConverters.ts +161 -6
- package/src/DocKeyManager.ts +60 -12
- package/src/DocTypes.ts +10 -0
- package/src/DocumentManager.ts +62 -85
- package/src/EncryptedChatClient.ts +173 -0
- package/src/EncryptedY.ts +2 -2
- package/src/IdentityDoc.ts +25 -0
- package/src/MnemonicKeyDerivation.ts +4 -4
- package/src/NotificationsClient.ts +120 -98
- package/src/OutgoingMessages/SubdocMessage.ts +2 -2
- package/src/RpcClient.ts +659 -0
- package/src/TreeManager.ts +61 -17
- package/src/TreeTimestamps.ts +28 -25
- package/src/index.ts +71 -1
- package/src/messageRecord.ts +121 -0
- package/src/types.ts +235 -16
- package/src/webrtc/AbracadabraWebRTC.ts +2 -2
- package/src/webrtc/DataChannelRouter.ts +2 -2
- package/src/webrtc/E2EEChannel.ts +3 -3
- package/src/webrtc/FileTransferChannel.ts +9 -2
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import { MessageSender } from "./MessageSender.ts";
|
|
|
11
11
|
import { AuthenticationMessage } from "./OutgoingMessages/AuthenticationMessage.ts";
|
|
12
12
|
import { AwarenessMessage } from "./OutgoingMessages/AwarenessMessage.ts";
|
|
13
13
|
import { StatelessMessage } from "./OutgoingMessages/StatelessMessage.ts";
|
|
14
|
+
import { RpcClient } from "./RpcClient.ts";
|
|
14
15
|
import { SyncStepOneMessage } from "./OutgoingMessages/SyncStepOneMessage.ts";
|
|
15
16
|
import { UpdateMessage } from "./OutgoingMessages/UpdateMessage.ts";
|
|
16
17
|
import type {
|
|
@@ -157,6 +158,19 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
157
158
|
forceSync: null,
|
|
158
159
|
};
|
|
159
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Lazily-constructed RPC v1 client. See `RpcClient`/`docs/rpc-v1.md`.
|
|
163
|
+
* Bound to this provider's stateless transport, so calls and handlers
|
|
164
|
+
* ride the doc this provider is subscribed to.
|
|
165
|
+
*/
|
|
166
|
+
private _rpc: RpcClient | undefined;
|
|
167
|
+
get rpc(): RpcClient {
|
|
168
|
+
if (!this._rpc) {
|
|
169
|
+
this._rpc = new RpcClient(this);
|
|
170
|
+
}
|
|
171
|
+
return this._rpc;
|
|
172
|
+
}
|
|
173
|
+
|
|
160
174
|
constructor(configuration: AbracadabraBaseProviderConfiguration) {
|
|
161
175
|
super();
|
|
162
176
|
this.setConfiguration(configuration);
|
|
@@ -167,6 +181,15 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
167
181
|
? configuration.awareness
|
|
168
182
|
: new Awareness(this.document);
|
|
169
183
|
|
|
184
|
+
// y-protocols' setLocalStateField silently no-ops when getLocalState() is null.
|
|
185
|
+
// Seeding with {} once ensures the *first* setLocalStateField call from any
|
|
186
|
+
// consumer (TipTap CollaborationCaret, DocumentManager, app code) actually
|
|
187
|
+
// publishes — instead of vanishing without a trace. Idempotent for callers
|
|
188
|
+
// that pass their own awareness instance with state already set.
|
|
189
|
+
if (this.awareness && this.awareness.getLocalState() === null) {
|
|
190
|
+
this.awareness.setLocalState({});
|
|
191
|
+
}
|
|
192
|
+
|
|
170
193
|
this.on("open", this.configuration.onOpen);
|
|
171
194
|
this.on("message", this.configuration.onMessage);
|
|
172
195
|
this.on("outgoingMessage", this.configuration.onOutgoingMessage);
|
|
@@ -264,7 +287,12 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
264
287
|
}
|
|
265
288
|
|
|
266
289
|
private resetUnsyncedChanges() {
|
|
267
|
-
|
|
290
|
+
// The unsynced counter tracks pending UpdateMessage acks (decremented by
|
|
291
|
+
// SyncStatusMessage). The handshake itself is a connection-level concern,
|
|
292
|
+
// not a doc update — reflect that by resetting to 0. The synced flag and
|
|
293
|
+
// status: 'connecting' already surface in-flight sync to the UI; bumping
|
|
294
|
+
// this counter to 1 would leave it stuck (SyncStep2 doesn't decrement).
|
|
295
|
+
this.unsyncedChanges = 0;
|
|
268
296
|
this.emit("unsyncedChanges", { number: this.unsyncedChanges });
|
|
269
297
|
}
|
|
270
298
|
|
|
@@ -367,7 +395,18 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
367
395
|
this.isSynced = state;
|
|
368
396
|
|
|
369
397
|
if (state) {
|
|
370
|
-
|
|
398
|
+
// Defer the emit one microtask. Y.applyUpdate dispatches Y.Map /
|
|
399
|
+
// Y.Array observer notifications via microtasks; emitting `synced`
|
|
400
|
+
// synchronously would let listeners read `getArray("messages")` (or
|
|
401
|
+
// any sub-type populated by the just-applied state) and see length
|
|
402
|
+
// 0 even though the wire delivered the full state. Deferring one
|
|
403
|
+
// microtask lets the observers run first so everyone reading after
|
|
404
|
+
// the synced event sees the populated state.
|
|
405
|
+
queueMicrotask(() => {
|
|
406
|
+
// Re-check in case `synced` flipped back to false between the
|
|
407
|
+
// mutation and this microtask (e.g. a reconnect aborted sync).
|
|
408
|
+
if (this.isSynced) this.emit("synced", { state });
|
|
409
|
+
});
|
|
371
410
|
}
|
|
372
411
|
}
|
|
373
412
|
|
|
@@ -481,6 +520,16 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
481
520
|
destroy() {
|
|
482
521
|
this.emit("destroy");
|
|
483
522
|
|
|
523
|
+
if (this._rpc) {
|
|
524
|
+
// Keep the (now-destroyed) RpcClient bound to `_rpc` rather than
|
|
525
|
+
// setting it back to `undefined`. The lazy getter would otherwise
|
|
526
|
+
// construct a fresh client wired against a provider whose listeners
|
|
527
|
+
// are gone, leaving any subsequent `rpc.call(...)` to hang forever.
|
|
528
|
+
// The client's own `destroyed` flag now turns `call()` into an
|
|
529
|
+
// immediate `CANCELLED` rejection — the right terminal outcome.
|
|
530
|
+
this._rpc.destroy();
|
|
531
|
+
}
|
|
532
|
+
|
|
484
533
|
if (this.intervals.forceSync) {
|
|
485
534
|
clearInterval(this.intervals.forceSync);
|
|
486
535
|
}
|
|
@@ -562,6 +611,25 @@ export class AbracadabraBaseProvider extends EventEmitter {
|
|
|
562
611
|
permissionDeniedHandler(reason: string) {
|
|
563
612
|
this.emit("authenticationFailed", { reason });
|
|
564
613
|
this.isAuthenticated = false;
|
|
614
|
+
|
|
615
|
+
// Stop the reconnect loop on a permanent denial. Without this the WS
|
|
616
|
+
// closes after every server-side rejection, AbracadabraWS schedules a
|
|
617
|
+
// retry, the new connection sends the same Subscribe → server denies
|
|
618
|
+
// again, ad infinitum (CPU/log/network spam, page looks "broken" to
|
|
619
|
+
// the user). The dominant cases:
|
|
620
|
+
// * "document not found" — entry-doc-id pinned to something that
|
|
621
|
+
// doesn't exist on this server
|
|
622
|
+
// * "permission denied" — operator never granted the user access
|
|
623
|
+
// Both are config issues; retrying won't fix them. We only stop the
|
|
624
|
+
// socket when this provider OWNS it (`manageSocket`); shared sockets
|
|
625
|
+
// (e.g. multiplexed child providers) leave the parent's lifecycle
|
|
626
|
+
// alone and just give up on this doc.
|
|
627
|
+
if (this.manageSocket) {
|
|
628
|
+
try {
|
|
629
|
+
this.configuration.websocketProvider.disconnect();
|
|
630
|
+
}
|
|
631
|
+
catch { /* WS may already be torn down — best effort */ }
|
|
632
|
+
}
|
|
565
633
|
}
|
|
566
634
|
|
|
567
635
|
authenticatedHandler(scope: string) {
|