@edryslabs/genericprovider 1.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/LICENSE +24 -0
- package/README.md +660 -0
- package/dist/index.d.ts +323 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1001 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +36 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +37 -0
- package/dist/lib.js.map +1 -0
- package/dist/providers/dummy/index.d.ts +226 -0
- package/dist/providers/dummy/index.d.ts.map +1 -0
- package/dist/providers/dummy/index.js +326 -0
- package/dist/providers/dummy/index.js.map +1 -0
- package/dist/providers/gun/index.d.ts +269 -0
- package/dist/providers/gun/index.d.ts.map +1 -0
- package/dist/providers/gun/index.js +683 -0
- package/dist/providers/gun/index.js.map +1 -0
- package/dist/providers/indexeddb/index.d.ts +161 -0
- package/dist/providers/indexeddb/index.d.ts.map +1 -0
- package/dist/providers/indexeddb/index.js +369 -0
- package/dist/providers/indexeddb/index.js.map +1 -0
- package/dist/providers/matrix/index.d.ts +109 -0
- package/dist/providers/matrix/index.d.ts.map +1 -0
- package/dist/providers/matrix/index.js +329 -0
- package/dist/providers/matrix/index.js.map +1 -0
- package/dist/providers/nostr/index.d.ts +172 -0
- package/dist/providers/nostr/index.d.ts.map +1 -0
- package/dist/providers/nostr/index.js +280 -0
- package/dist/providers/nostr/index.js.map +1 -0
- package/dist/providers/peerjs/index.d.ts +231 -0
- package/dist/providers/peerjs/index.d.ts.map +1 -0
- package/dist/providers/peerjs/index.js +1038 -0
- package/dist/providers/peerjs/index.js.map +1 -0
- package/dist/providers/pubnub/index.d.ts +106 -0
- package/dist/providers/pubnub/index.d.ts.map +1 -0
- package/dist/providers/pubnub/index.js +357 -0
- package/dist/providers/pubnub/index.js.map +1 -0
- package/dist/providers/simple-peer/index.d.ts +253 -0
- package/dist/providers/simple-peer/index.d.ts.map +1 -0
- package/dist/providers/simple-peer/index.js +783 -0
- package/dist/providers/simple-peer/index.js.map +1 -0
- package/dist/providers/supabase/index.d.ts +80 -0
- package/dist/providers/supabase/index.d.ts.map +1 -0
- package/dist/providers/supabase/index.js +202 -0
- package/dist/providers/supabase/index.js.map +1 -0
- package/dist/providers/trystero/index.d.ts +181 -0
- package/dist/providers/trystero/index.d.ts.map +1 -0
- package/dist/providers/trystero/index.js +187 -0
- package/dist/providers/trystero/index.js.map +1 -0
- package/dist/providers/websocket/index.d.ts +92 -0
- package/dist/providers/websocket/index.d.ts.map +1 -0
- package/dist/providers/websocket/index.js +272 -0
- package/dist/providers/websocket/index.js.map +1 -0
- package/dist/sync-monitor.d.ts +90 -0
- package/dist/sync-monitor.d.ts.map +1 -0
- package/dist/sync-monitor.js +149 -0
- package/dist/sync-monitor.js.map +1 -0
- package/dist/transport.d.ts +116 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +2 -0
- package/dist/transport.js.map +1 -0
- package/package.json +137 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trystero Transport Provider
|
|
3
|
+
*
|
|
4
|
+
* Serverless peer-to-peer transport using Trystero with multiple strategies.
|
|
5
|
+
* Trystero uses decentralized infrastructure for peer discovery while keeping
|
|
6
|
+
* all data transmission direct and end-to-end encrypted.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Zero server setup required
|
|
10
|
+
* - Multiple strategies: Nostr, BitTorrent, MQTT, Supabase, Firebase, IPFS
|
|
11
|
+
* - End-to-end encrypted P2P connections
|
|
12
|
+
* - Automatic chunking and serialization
|
|
13
|
+
* - Session encryption via AES-GCM
|
|
14
|
+
* - Optional TURN server support
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { GenericProvider } from 'y-generic'
|
|
19
|
+
* import { TrysteroTransport } from 'y-generic/providers/trystero'
|
|
20
|
+
* import { joinRoom } from 'trystero/nostr' // or other strategy
|
|
21
|
+
*
|
|
22
|
+
* const doc = new Y.Doc()
|
|
23
|
+
* const transport = new TrysteroTransport({
|
|
24
|
+
* joinRoom,
|
|
25
|
+
* appId: 'my-unique-app-id'
|
|
26
|
+
* })
|
|
27
|
+
* const provider = new GenericProvider(doc, transport)
|
|
28
|
+
* await provider.connect({ room: 'my-room' })
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
/**
|
|
32
|
+
* Trystero transport implementation.
|
|
33
|
+
* Creates serverless P2P connections using Trystero library.
|
|
34
|
+
*/
|
|
35
|
+
export class TrysteroTransport {
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this._connected = false;
|
|
38
|
+
this._room = '';
|
|
39
|
+
this.room = null;
|
|
40
|
+
this.sendUpdate = null;
|
|
41
|
+
this.peers = new Set();
|
|
42
|
+
this.options = {
|
|
43
|
+
debug: false,
|
|
44
|
+
...options,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
log(message, level = 'info') {
|
|
48
|
+
if (this.options.debug) {
|
|
49
|
+
const prefix = '[TrysteroTransport]';
|
|
50
|
+
switch (level) {
|
|
51
|
+
case 'error':
|
|
52
|
+
console.error(prefix, message);
|
|
53
|
+
break;
|
|
54
|
+
case 'warn':
|
|
55
|
+
console.warn(prefix, message);
|
|
56
|
+
break;
|
|
57
|
+
default:
|
|
58
|
+
console.log(prefix, message);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
get isConnected() {
|
|
63
|
+
return this._connected;
|
|
64
|
+
}
|
|
65
|
+
async connect(config) {
|
|
66
|
+
if (this._connected) {
|
|
67
|
+
this.log('Already connected, disconnecting first...');
|
|
68
|
+
this.disconnect();
|
|
69
|
+
}
|
|
70
|
+
const room = config.room;
|
|
71
|
+
if (!room) {
|
|
72
|
+
throw new Error('Room ID is required');
|
|
73
|
+
}
|
|
74
|
+
this._room = room;
|
|
75
|
+
this.log(`Connecting to room: ${room}`);
|
|
76
|
+
// Build Trystero config
|
|
77
|
+
const trysteroConfig = {
|
|
78
|
+
appId: this.options.appId,
|
|
79
|
+
};
|
|
80
|
+
// Add optional config
|
|
81
|
+
if (this.options.password)
|
|
82
|
+
trysteroConfig.password = this.options.password;
|
|
83
|
+
if (this.options.relayUrls)
|
|
84
|
+
trysteroConfig.relayUrls = this.options.relayUrls;
|
|
85
|
+
if (this.options.relayRedundancy)
|
|
86
|
+
trysteroConfig.relayRedundancy = this.options.relayRedundancy;
|
|
87
|
+
if (this.options.rtcConfig)
|
|
88
|
+
trysteroConfig.rtcConfig = this.options.rtcConfig;
|
|
89
|
+
if (this.options.turnConfig)
|
|
90
|
+
trysteroConfig.turnConfig = this.options.turnConfig;
|
|
91
|
+
if (this.options.rtcPolyfill)
|
|
92
|
+
trysteroConfig.rtcPolyfill = this.options.rtcPolyfill;
|
|
93
|
+
if (this.options.supabaseKey)
|
|
94
|
+
trysteroConfig.supabaseKey = this.options.supabaseKey;
|
|
95
|
+
if (this.options.firebaseApp)
|
|
96
|
+
trysteroConfig.firebaseApp = this.options.firebaseApp;
|
|
97
|
+
if (this.options.rootPath)
|
|
98
|
+
trysteroConfig.rootPath = this.options.rootPath;
|
|
99
|
+
if (this.options.manualRelayReconnection !== undefined) {
|
|
100
|
+
trysteroConfig.manualRelayReconnection =
|
|
101
|
+
this.options.manualRelayReconnection;
|
|
102
|
+
}
|
|
103
|
+
// Join room with error handler
|
|
104
|
+
this.room = this.options.joinRoom(trysteroConfig, room, (details) => {
|
|
105
|
+
this.log(`Join error: ${details.error}`, 'error');
|
|
106
|
+
if (this.onJoinErrorCallback) {
|
|
107
|
+
this.onJoinErrorCallback(details);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
// Create action for Yjs updates
|
|
111
|
+
const [send, receive] = this.room.makeAction('yjs-update');
|
|
112
|
+
this.sendUpdate = send;
|
|
113
|
+
// Listen for incoming updates
|
|
114
|
+
receive((data, peerId) => {
|
|
115
|
+
this.log(`Received update from ${peerId} (${data.byteLength} bytes)`);
|
|
116
|
+
if (this._callback) {
|
|
117
|
+
// Convert ArrayBuffer to Uint8Array
|
|
118
|
+
this._callback(new Uint8Array(data));
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
// Track peers
|
|
122
|
+
this.room.onPeerJoin((peerId) => {
|
|
123
|
+
this.peers.add(peerId);
|
|
124
|
+
this.log(`Peer joined: ${peerId} (${this.peers.size} total)`);
|
|
125
|
+
});
|
|
126
|
+
this.room.onPeerLeave((peerId) => {
|
|
127
|
+
this.peers.delete(peerId);
|
|
128
|
+
this.log(`Peer left: ${peerId} (${this.peers.size} remaining)`);
|
|
129
|
+
});
|
|
130
|
+
this._connected = true;
|
|
131
|
+
this.log(`✅ Connected to room: ${room}`);
|
|
132
|
+
}
|
|
133
|
+
disconnect() {
|
|
134
|
+
if (!this._connected) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
this.log('Disconnecting...');
|
|
138
|
+
if (this.room) {
|
|
139
|
+
this.room.leave();
|
|
140
|
+
this.room = null;
|
|
141
|
+
}
|
|
142
|
+
this.sendUpdate = null;
|
|
143
|
+
this._callback = undefined;
|
|
144
|
+
this._connected = false;
|
|
145
|
+
this.peers.clear();
|
|
146
|
+
this.log('✅ Disconnected');
|
|
147
|
+
}
|
|
148
|
+
async send(data) {
|
|
149
|
+
if (!this._connected || !this.sendUpdate) {
|
|
150
|
+
this.log('⚠️ Not connected, cannot send', 'warn');
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// Send to all peers (null = broadcast)
|
|
154
|
+
this.log(`Sending update (${data.byteLength} bytes) to ${this.peers.size} peers`);
|
|
155
|
+
await this.sendUpdate(data, null);
|
|
156
|
+
}
|
|
157
|
+
onMessage(callback) {
|
|
158
|
+
this._callback = callback;
|
|
159
|
+
this.log('Message callback registered');
|
|
160
|
+
return () => {
|
|
161
|
+
this._callback = undefined;
|
|
162
|
+
this.log('Message callback unregistered');
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Set a callback for join errors (optional).
|
|
167
|
+
*/
|
|
168
|
+
onJoinError(callback) {
|
|
169
|
+
this.onJoinErrorCallback = callback;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Get the set of connected peer IDs.
|
|
173
|
+
*/
|
|
174
|
+
getPeers() {
|
|
175
|
+
return new Set(this.peers);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Ping a peer and get round-trip time in ms.
|
|
179
|
+
*/
|
|
180
|
+
async ping(peerId) {
|
|
181
|
+
if (!this.room) {
|
|
182
|
+
throw new Error('Not connected to a room');
|
|
183
|
+
}
|
|
184
|
+
return await this.room.ping(peerId);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/trystero/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAqJH;;;GAGG;AACH,MAAM,OAAO,iBAAiB;IAY5B,YAAY,OAAiC;QAVrC,eAAU,GAAY,KAAK,CAAA;QAC3B,UAAK,GAAW,EAAE,CAAA;QAElB,SAAI,GAAwB,IAAI,CAAA;QAChC,eAAU,GAEP,IAAI,CAAA;QACP,UAAK,GAAgB,IAAI,GAAG,EAAE,CAAA;QAIpC,IAAI,CAAC,OAAO,GAAG;YACb,KAAK,EAAE,KAAK;YACZ,GAAG,OAAO;SACX,CAAA;IACH,CAAC;IAEO,GAAG,CACT,OAAe,EACf,QAAmC,MAAM;QAEzC,IAAI,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,qBAAqB,CAAA;YACpC,QAAQ,KAAK,EAAE,CAAC;gBACd,KAAK,OAAO;oBACV,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;oBAC9B,MAAK;gBACP,KAAK,MAAM;oBACT,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;oBAC7B,MAAK;gBACP;oBACE,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,WAAW;QACb,OAAO,IAAI,CAAC,UAAU,CAAA;IACxB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,MAAwB;QACpC,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACpB,IAAI,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAA;YACrD,IAAI,CAAC,UAAU,EAAE,CAAA;QACnB,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAA;QACxB,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAA;QACxC,CAAC;QAED,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QACjB,IAAI,CAAC,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAA;QAEvC,wBAAwB;QACxB,MAAM,cAAc,GAAmB;YACrC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,KAAK;SAC1B,CAAA;QAED,sBAAsB;QACtB,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,cAAc,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAA;QAC1E,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS;YACxB,cAAc,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAA;QACnD,IAAI,IAAI,CAAC,OAAO,CAAC,eAAe;YAC9B,cAAc,CAAC,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,eAAe,CAAA;QAC/D,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS;YACxB,cAAc,CAAC,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAA;QACnD,IAAI,IAAI,CAAC,OAAO,CAAC,UAAU;YACzB,cAAc,CAAC,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAA;QACrD,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW;YAC1B,cAAc,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAA;QACvD,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW;YAC1B,cAAc,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAA;QACvD,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW;YAC1B,cAAc,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAA;QACvD,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ;YAAE,cAAc,CAAC,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAA;QAC1E,IAAI,IAAI,CAAC,OAAO,CAAC,uBAAuB,KAAK,SAAS,EAAE,CAAC;YACvD,cAAc,CAAC,uBAAuB;gBACpC,IAAI,CAAC,OAAO,CAAC,uBAAuB,CAAA;QACxC,CAAC;QAED,+BAA+B;QAC/B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,EAAE;YAClE,IAAI,CAAC,GAAG,CAAC,eAAe,OAAO,CAAC,KAAK,EAAE,EAAE,OAAO,CAAC,CAAA;YACjD,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC7B,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAA;YACnC,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,gCAAgC;QAChC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,CAAA;QAC1D,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QAEtB,8BAA8B;QAC9B,OAAO,CAAC,CAAC,IAAiB,EAAE,MAAc,EAAE,EAAE;YAC5C,IAAI,CAAC,GAAG,CAAC,wBAAwB,MAAM,KAAK,IAAI,CAAC,UAAU,SAAS,CAAC,CAAA;YACrE,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,oCAAoC;gBACpC,IAAI,CAAC,SAAS,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAA;YACtC,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,cAAc;QACd,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,EAAE;YAC9B,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YACtB,IAAI,CAAC,GAAG,CAAC,gBAAgB,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,SAAS,CAAC,CAAA;QAC/D,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,MAAM,EAAE,EAAE;YAC/B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;YACzB,IAAI,CAAC,GAAG,CAAC,cAAc,MAAM,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,aAAa,CAAC,CAAA;QACjE,CAAC,CAAC,CAAA;QAEF,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QACtB,IAAI,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC,CAAA;IAC1C,CAAC;IAED,UAAU;QACR,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,OAAM;QACR,CAAC;QAED,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAA;QAE5B,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACd,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAA;YACjB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAClB,CAAC;QAED,IAAI,CAAC,UAAU,GAAG,IAAI,CAAA;QACtB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAA;QACvB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;QAElB,IAAI,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAA;IAC5B,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,IAAgB;QACzB,IAAI,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACzC,IAAI,CAAC,GAAG,CAAC,+BAA+B,EAAE,MAAM,CAAC,CAAA;YACjD,OAAM;QACR,CAAC;QAED,uCAAuC;QACvC,IAAI,CAAC,GAAG,CACN,mBAAmB,IAAI,CAAC,UAAU,cAAc,IAAI,CAAC,KAAK,CAAC,IAAI,QAAQ,CACxE,CAAA;QACD,MAAM,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;IACnC,CAAC;IAED,SAAS,CAAC,QAAoC;QAC5C,IAAI,CAAC,SAAS,GAAG,QAAQ,CAAA;QACzB,IAAI,CAAC,GAAG,CAAC,6BAA6B,CAAC,CAAA;QAEvC,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;YAC1B,IAAI,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;QAC3C,CAAC,CAAA;IACH,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,QAAgC;QAC1C,IAAI,CAAC,mBAAmB,GAAG,QAAQ,CAAA;IACrC,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,OAAO,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;IAC5B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,IAAI,CAAC,MAAc;QACvB,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAA;QAC5C,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACrC,CAAC;CACF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import type { Transport, ConnectionConfig } from '../../transport';
|
|
2
|
+
/**
|
|
3
|
+
* WebSocket transport configuration
|
|
4
|
+
*/
|
|
5
|
+
export interface WebSocketConfig extends ConnectionConfig {
|
|
6
|
+
/** WebSocket server URL (required) e.g., 'ws://localhost:1234' or 'wss://example.com' */
|
|
7
|
+
serverUrl: string;
|
|
8
|
+
/** Enable automatic reconnection on disconnect (default: true) */
|
|
9
|
+
autoReconnect?: boolean;
|
|
10
|
+
/** Reconnection delay in milliseconds (default: 2000) */
|
|
11
|
+
reconnectDelay?: number;
|
|
12
|
+
/** Maximum reconnection attempts (0 = infinite, default: 0) */
|
|
13
|
+
maxReconnectAttempts?: number;
|
|
14
|
+
/** WebSocket protocols (optional) */
|
|
15
|
+
protocols?: string | string[];
|
|
16
|
+
/** Enable debug logging */
|
|
17
|
+
debug?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* WebSocket Transport for Yjs
|
|
21
|
+
*
|
|
22
|
+
* Provides real-time synchronization using WebSocket connections.
|
|
23
|
+
*
|
|
24
|
+
* Features:
|
|
25
|
+
* - Direct WebSocket connection to server
|
|
26
|
+
* - Room-based collaboration
|
|
27
|
+
* - Automatic reconnection
|
|
28
|
+
* - Binary message support
|
|
29
|
+
* - Low latency real-time sync
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```ts
|
|
33
|
+
* import * as Y from 'yjs'
|
|
34
|
+
* import { GenericProvider } from 'y-generic'
|
|
35
|
+
* import { WebSocketTransport } from 'y-generic/providers/websocket'
|
|
36
|
+
*
|
|
37
|
+
* const doc = new Y.Doc()
|
|
38
|
+
* const transport = new WebSocketTransport()
|
|
39
|
+
*
|
|
40
|
+
* const provider = new GenericProvider(doc, transport)
|
|
41
|
+
* await provider.connect({
|
|
42
|
+
* serverUrl: 'ws://localhost:1234',
|
|
43
|
+
* room: 'my-room'
|
|
44
|
+
* })
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
export declare class WebSocketTransport implements Transport {
|
|
48
|
+
private ws;
|
|
49
|
+
private config;
|
|
50
|
+
private messageCallback?;
|
|
51
|
+
private _isConnected;
|
|
52
|
+
private debug;
|
|
53
|
+
private reconnectAttempts;
|
|
54
|
+
private reconnectTimer?;
|
|
55
|
+
private intentionalDisconnect;
|
|
56
|
+
private messageQueue;
|
|
57
|
+
private receivedBuffer;
|
|
58
|
+
get isConnected(): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Connect to WebSocket server
|
|
61
|
+
*/
|
|
62
|
+
connect(config: WebSocketConfig): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Disconnect from WebSocket server
|
|
65
|
+
*/
|
|
66
|
+
disconnect(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Send data to server
|
|
69
|
+
*/
|
|
70
|
+
send(data: Uint8Array): void;
|
|
71
|
+
/**
|
|
72
|
+
* Register message callback
|
|
73
|
+
*/
|
|
74
|
+
onMessage(callback: (data: Uint8Array) => void): () => void;
|
|
75
|
+
/**
|
|
76
|
+
* Handle incoming WebSocket message
|
|
77
|
+
*/
|
|
78
|
+
private handleMessage;
|
|
79
|
+
/**
|
|
80
|
+
* Flush queued messages
|
|
81
|
+
*/
|
|
82
|
+
private flushMessageQueue;
|
|
83
|
+
/**
|
|
84
|
+
* Attempt to reconnect
|
|
85
|
+
*/
|
|
86
|
+
private attemptReconnect;
|
|
87
|
+
/**
|
|
88
|
+
* Debug logging
|
|
89
|
+
*/
|
|
90
|
+
private log;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/websocket/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AA6ClE;;GAEG;AACH,MAAM,WAAW,eAAgB,SAAQ,gBAAgB;IACvD,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAA;IACjB,kEAAkE;IAClE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,yDAAyD;IACzD,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,+DAA+D;IAC/D,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAA;IAC7B,2BAA2B;IAC3B,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,qBAAa,kBAAmB,YAAW,SAAS;IAClD,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,MAAM,CAA+B;IAC7C,OAAO,CAAC,eAAe,CAAC,CAA4B;IACpD,OAAO,CAAC,YAAY,CAAiB;IACrC,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,iBAAiB,CAAY;IACrC,OAAO,CAAC,cAAc,CAAC,CAA+B;IACtD,OAAO,CAAC,qBAAqB,CAAiB;IAC9C,OAAO,CAAC,YAAY,CAAmB;IACvC,OAAO,CAAC,cAAc,CAAmB;IAEzC,IAAI,WAAW,IAAI,OAAO,CAEzB;IAED;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC;IAgFrD;;OAEG;IACH,UAAU,IAAI,IAAI;IAiBlB;;OAEG;IACH,IAAI,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAiB5B;;OAEG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAmB3D;;OAEG;IACH,OAAO,CAAC,aAAa;IAwBrB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAYzB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IA0BxB;;OAEG;IACH,OAAO,CAAC,GAAG;CAKZ"}
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// CRC32 translation helpers
|
|
3
|
+
//
|
|
4
|
+
// GenericProvider wraps every message as [CRC32 (4 bytes)][payload] before
|
|
5
|
+
// calling transport.send(), and expects the same format from onMessage().
|
|
6
|
+
// The y-websocket server however speaks plain Yjs wire format (no header).
|
|
7
|
+
// These helpers translate between the two worlds at the transport boundary.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
const _CRC32_TABLE = (() => {
|
|
10
|
+
const table = new Uint32Array(256);
|
|
11
|
+
for (let i = 0; i < 256; i++) {
|
|
12
|
+
let c = i;
|
|
13
|
+
for (let j = 0; j < 8; j++)
|
|
14
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
15
|
+
table[i] = c;
|
|
16
|
+
}
|
|
17
|
+
return table;
|
|
18
|
+
})();
|
|
19
|
+
function _crc32(data) {
|
|
20
|
+
let crc = 0xffffffff;
|
|
21
|
+
for (let i = 0; i < data.length; i++)
|
|
22
|
+
crc = (crc >>> 8) ^ _CRC32_TABLE[(crc ^ data[i]) & 0xff];
|
|
23
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
24
|
+
}
|
|
25
|
+
/** Strip the 4-byte CRC32 header that GenericProvider prepends. */
|
|
26
|
+
function stripCRC32Header(data) {
|
|
27
|
+
return data.length >= 4 ? data.subarray(4) : data;
|
|
28
|
+
}
|
|
29
|
+
/** Add a valid CRC32 header so GenericProvider accepts the message. */
|
|
30
|
+
function addCRC32Header(data) {
|
|
31
|
+
const crc = _crc32(data);
|
|
32
|
+
const wrapped = new Uint8Array(4 + data.length);
|
|
33
|
+
wrapped[0] = (crc >>> 24) & 0xff;
|
|
34
|
+
wrapped[1] = (crc >>> 16) & 0xff;
|
|
35
|
+
wrapped[2] = (crc >>> 8) & 0xff;
|
|
36
|
+
wrapped[3] = crc & 0xff;
|
|
37
|
+
wrapped.set(data, 4);
|
|
38
|
+
return wrapped;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* WebSocket Transport for Yjs
|
|
42
|
+
*
|
|
43
|
+
* Provides real-time synchronization using WebSocket connections.
|
|
44
|
+
*
|
|
45
|
+
* Features:
|
|
46
|
+
* - Direct WebSocket connection to server
|
|
47
|
+
* - Room-based collaboration
|
|
48
|
+
* - Automatic reconnection
|
|
49
|
+
* - Binary message support
|
|
50
|
+
* - Low latency real-time sync
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* import * as Y from 'yjs'
|
|
55
|
+
* import { GenericProvider } from 'y-generic'
|
|
56
|
+
* import { WebSocketTransport } from 'y-generic/providers/websocket'
|
|
57
|
+
*
|
|
58
|
+
* const doc = new Y.Doc()
|
|
59
|
+
* const transport = new WebSocketTransport()
|
|
60
|
+
*
|
|
61
|
+
* const provider = new GenericProvider(doc, transport)
|
|
62
|
+
* await provider.connect({
|
|
63
|
+
* serverUrl: 'ws://localhost:1234',
|
|
64
|
+
* room: 'my-room'
|
|
65
|
+
* })
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export class WebSocketTransport {
|
|
69
|
+
constructor() {
|
|
70
|
+
this.ws = null;
|
|
71
|
+
this.config = null;
|
|
72
|
+
this._isConnected = false;
|
|
73
|
+
this.debug = false;
|
|
74
|
+
this.reconnectAttempts = 0;
|
|
75
|
+
this.intentionalDisconnect = false;
|
|
76
|
+
this.messageQueue = []; // Queue messages until connected
|
|
77
|
+
this.receivedBuffer = []; // Buffer messages received before callback registered
|
|
78
|
+
}
|
|
79
|
+
get isConnected() {
|
|
80
|
+
return this._isConnected;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Connect to WebSocket server
|
|
84
|
+
*/
|
|
85
|
+
async connect(config) {
|
|
86
|
+
this.config = config;
|
|
87
|
+
this.debug = config.debug ?? false;
|
|
88
|
+
this.intentionalDisconnect = false;
|
|
89
|
+
if (!config.serverUrl) {
|
|
90
|
+
throw new Error('WebSocket serverUrl is required');
|
|
91
|
+
}
|
|
92
|
+
if (!config.room) {
|
|
93
|
+
throw new Error('Room name is required');
|
|
94
|
+
}
|
|
95
|
+
// Build URL with room (y-websocket compatible)
|
|
96
|
+
let wsUrl = config.serverUrl;
|
|
97
|
+
// Remove trailing slash if present
|
|
98
|
+
if (wsUrl.endsWith('/')) {
|
|
99
|
+
wsUrl = wsUrl.slice(0, -1);
|
|
100
|
+
}
|
|
101
|
+
// Append room name to URL path
|
|
102
|
+
wsUrl = `${wsUrl}/${encodeURIComponent(config.room)}`;
|
|
103
|
+
this.log(`Connecting to WebSocket server: ${wsUrl}`);
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
try {
|
|
106
|
+
// Create WebSocket connection with room in URL (y-websocket style)
|
|
107
|
+
this.ws = new WebSocket(wsUrl, config.protocols);
|
|
108
|
+
this.ws.binaryType = 'arraybuffer';
|
|
109
|
+
const timeout = setTimeout(() => {
|
|
110
|
+
if (this.ws && this.ws.readyState !== WebSocket.OPEN) {
|
|
111
|
+
this.ws.close();
|
|
112
|
+
reject(new Error('WebSocket connection timeout'));
|
|
113
|
+
}
|
|
114
|
+
}, 10000);
|
|
115
|
+
this.ws.onopen = () => {
|
|
116
|
+
clearTimeout(timeout);
|
|
117
|
+
this._isConnected = true;
|
|
118
|
+
this.reconnectAttempts = 0;
|
|
119
|
+
this.log(`✅ WebSocket connected to room: ${config.room}`);
|
|
120
|
+
// Flush queued messages
|
|
121
|
+
this.flushMessageQueue();
|
|
122
|
+
resolve();
|
|
123
|
+
};
|
|
124
|
+
this.ws.onmessage = (event) => {
|
|
125
|
+
this.handleMessage(event.data);
|
|
126
|
+
};
|
|
127
|
+
this.ws.onerror = (error) => {
|
|
128
|
+
clearTimeout(timeout);
|
|
129
|
+
this.log('❌ WebSocket error:', error);
|
|
130
|
+
if (!this._isConnected) {
|
|
131
|
+
reject(new Error('WebSocket connection failed'));
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
this.ws.onclose = (event) => {
|
|
135
|
+
clearTimeout(timeout);
|
|
136
|
+
this._isConnected = false;
|
|
137
|
+
this.log(`WebSocket closed: code=${event.code}, reason=${event.reason || 'none'}`);
|
|
138
|
+
// Attempt reconnection if not intentional
|
|
139
|
+
if (!this.intentionalDisconnect && (config.autoReconnect ?? true)) {
|
|
140
|
+
this.attemptReconnect();
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
reject(error);
|
|
146
|
+
}
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Disconnect from WebSocket server
|
|
151
|
+
*/
|
|
152
|
+
disconnect() {
|
|
153
|
+
this.intentionalDisconnect = true;
|
|
154
|
+
if (this.reconnectTimer) {
|
|
155
|
+
clearTimeout(this.reconnectTimer);
|
|
156
|
+
this.reconnectTimer = undefined;
|
|
157
|
+
}
|
|
158
|
+
if (this.ws) {
|
|
159
|
+
this.log('Disconnecting from WebSocket...');
|
|
160
|
+
this.ws.close(1000, 'Client disconnect');
|
|
161
|
+
this.ws = null;
|
|
162
|
+
}
|
|
163
|
+
this._isConnected = false;
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Send data to server
|
|
167
|
+
*/
|
|
168
|
+
send(data) {
|
|
169
|
+
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
|
|
170
|
+
this.log('⚠️ WebSocket not ready, queueing message');
|
|
171
|
+
this.messageQueue.push(data);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
// Strip CRC32 header added by GenericProvider before sending to server
|
|
176
|
+
const raw = stripCRC32Header(data);
|
|
177
|
+
this.ws.send(raw);
|
|
178
|
+
this.log(`📤 Sent ${raw.length} bytes (${data.length} with header)`);
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
this.log('❌ Send error:', error);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Register message callback
|
|
186
|
+
*/
|
|
187
|
+
onMessage(callback) {
|
|
188
|
+
this.messageCallback = callback;
|
|
189
|
+
// Flush any buffered messages that arrived before callback was registered
|
|
190
|
+
if (this.receivedBuffer.length > 0) {
|
|
191
|
+
this.log(`📦 Flushing ${this.receivedBuffer.length} buffered received messages`);
|
|
192
|
+
for (const data of this.receivedBuffer) {
|
|
193
|
+
callback(data);
|
|
194
|
+
}
|
|
195
|
+
this.receivedBuffer = [];
|
|
196
|
+
}
|
|
197
|
+
return () => {
|
|
198
|
+
this.messageCallback = undefined;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Handle incoming WebSocket message
|
|
203
|
+
*/
|
|
204
|
+
handleMessage(data) {
|
|
205
|
+
try {
|
|
206
|
+
if (typeof data === 'string') {
|
|
207
|
+
// Handle text messages (control messages)
|
|
208
|
+
this.log('📨 Received text message:', data);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
// Wrap plain y-websocket message with CRC32 header expected by GenericProvider
|
|
212
|
+
const uint8Data = addCRC32Header(new Uint8Array(data));
|
|
213
|
+
this.log(`📨 Received ${uint8Data.length - 4} bytes`);
|
|
214
|
+
if (this.messageCallback) {
|
|
215
|
+
this.messageCallback(uint8Data);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
// Buffer message if callback not registered yet (race condition)
|
|
219
|
+
this.log('⏳ Buffering message (callback not yet registered)');
|
|
220
|
+
this.receivedBuffer.push(uint8Data);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
this.log('❌ Error handling message:', error);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Flush queued messages
|
|
229
|
+
*/
|
|
230
|
+
flushMessageQueue() {
|
|
231
|
+
if (this.messageQueue.length === 0)
|
|
232
|
+
return;
|
|
233
|
+
this.log(`📦 Flushing ${this.messageQueue.length} queued messages`);
|
|
234
|
+
for (const data of this.messageQueue) {
|
|
235
|
+
this.send(data);
|
|
236
|
+
}
|
|
237
|
+
this.messageQueue = [];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Attempt to reconnect
|
|
241
|
+
*/
|
|
242
|
+
attemptReconnect() {
|
|
243
|
+
if (!this.config)
|
|
244
|
+
return;
|
|
245
|
+
const maxAttempts = this.config.maxReconnectAttempts ?? 0;
|
|
246
|
+
if (maxAttempts > 0 && this.reconnectAttempts >= maxAttempts) {
|
|
247
|
+
this.log(`❌ Max reconnection attempts (${maxAttempts}) reached`);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
this.reconnectAttempts++;
|
|
251
|
+
const delay = this.config.reconnectDelay ?? 2000;
|
|
252
|
+
this.log(`🔄 Attempting reconnection #${this.reconnectAttempts} in ${delay}ms...`);
|
|
253
|
+
this.reconnectTimer = setTimeout(async () => {
|
|
254
|
+
try {
|
|
255
|
+
await this.connect(this.config);
|
|
256
|
+
this.log('✅ Reconnected successfully');
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
this.log('❌ Reconnection failed:', error);
|
|
260
|
+
}
|
|
261
|
+
}, delay);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Debug logging
|
|
265
|
+
*/
|
|
266
|
+
log(message, ...args) {
|
|
267
|
+
if (this.debug) {
|
|
268
|
+
console.log(`[WebSocketTransport] ${message}`, ...args);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/websocket/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional sync monitoring utilities using hash-based detection.
|
|
3
|
+
*
|
|
4
|
+
* ⚠️ NOTE: For most use cases, use the BUILT-IN verification instead:
|
|
5
|
+
* GenericProvider with verifyUpdates: true (default)
|
|
6
|
+
*
|
|
7
|
+
* When to use SyncHealthMonitor:
|
|
8
|
+
* - You need to monitor sync health across ALL peers simultaneously
|
|
9
|
+
* - You want periodic pub/sub-based hash broadcasting for diagnostics
|
|
10
|
+
* - You need alerting/monitoring separate from the core sync mechanism
|
|
11
|
+
*
|
|
12
|
+
* When to use built-in verification (recommended):
|
|
13
|
+
* - Fast desync detection on every update (no waiting)
|
|
14
|
+
* - Exponential backoff and rate limiting
|
|
15
|
+
* - Sequence numbers for duplicate detection
|
|
16
|
+
* - Zero configuration (enabled by default)
|
|
17
|
+
*
|
|
18
|
+
* This utility is complementary to Yjs's built-in state vector sync.
|
|
19
|
+
* Use for monitoring/alerting, not as primary sync mechanism.
|
|
20
|
+
*/
|
|
21
|
+
import * as Y from 'yjs';
|
|
22
|
+
import type { GenericProvider } from '../src/index';
|
|
23
|
+
/**
|
|
24
|
+
* Compute hash of entire Yjs document state.
|
|
25
|
+
*/
|
|
26
|
+
export declare function computeDocumentHash(doc: Y.Doc): string;
|
|
27
|
+
/**
|
|
28
|
+
* Sync health monitor that detects divergence using periodic hashing.
|
|
29
|
+
*/
|
|
30
|
+
export declare class SyncHealthMonitor {
|
|
31
|
+
private options;
|
|
32
|
+
private provider;
|
|
33
|
+
private intervalId?;
|
|
34
|
+
private localHash;
|
|
35
|
+
private peerHashes;
|
|
36
|
+
constructor(provider: GenericProvider, options?: {
|
|
37
|
+
/** How often to check sync health (ms) */
|
|
38
|
+
checkInterval?: number;
|
|
39
|
+
/** Callback when desync detected */
|
|
40
|
+
onDesync?: (details: DesyncDetails) => void;
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Start monitoring sync health.
|
|
44
|
+
*/
|
|
45
|
+
start(): void;
|
|
46
|
+
/**
|
|
47
|
+
* Stop monitoring.
|
|
48
|
+
*/
|
|
49
|
+
stop(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Perform a health check.
|
|
52
|
+
*/
|
|
53
|
+
private checkHealth;
|
|
54
|
+
/**
|
|
55
|
+
* Handle hash from peer.
|
|
56
|
+
*/
|
|
57
|
+
private handlePeerHash;
|
|
58
|
+
/**
|
|
59
|
+
* Detect if any peer has different hash.
|
|
60
|
+
*/
|
|
61
|
+
private detectDivergence;
|
|
62
|
+
}
|
|
63
|
+
export interface DesyncDetails {
|
|
64
|
+
localHash: string;
|
|
65
|
+
peerHashes: Map<number, string>;
|
|
66
|
+
divergentClients: number[];
|
|
67
|
+
timestamp: number;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Example usage:
|
|
71
|
+
*
|
|
72
|
+
* ```typescript
|
|
73
|
+
* const provider = new GenericProvider(doc, transport)
|
|
74
|
+
* await provider.connect({ room: 'my-room' })
|
|
75
|
+
*
|
|
76
|
+
* // Start monitoring
|
|
77
|
+
* const monitor = new SyncHealthMonitor(provider, {
|
|
78
|
+
* checkInterval: 5000, // Check every 5 seconds
|
|
79
|
+
* onDesync: (details) => {
|
|
80
|
+
* console.error('Documents out of sync!', details)
|
|
81
|
+
* // Send to error tracking, show warning to user, etc.
|
|
82
|
+
* }
|
|
83
|
+
* })
|
|
84
|
+
* monitor.start()
|
|
85
|
+
*
|
|
86
|
+
* // Stop when done
|
|
87
|
+
* monitor.stop()
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
//# sourceMappingURL=sync-monitor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-monitor.d.ts","sourceRoot":"","sources":["../src/sync-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AACxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAcnD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,CAAC,CAAC,GAAG,GAAG,MAAM,CAGtD;AAED;;GAEG;AACH,qBAAa,iBAAiB;IAQ1B,OAAO,CAAC,OAAO;IAPjB,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,UAAU,CAAC,CAAK;IACxB,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,UAAU,CAAiC;gBAGjD,QAAQ,EAAE,eAAe,EACjB,OAAO,GAAE;QACf,0CAA0C;QAC1C,aAAa,CAAC,EAAE,MAAM,CAAA;QACtB,oCAAoC;QACpC,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;KACvC;IAMR;;OAEG;IACH,KAAK,IAAI,IAAI;IAiBb;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACH,OAAO,CAAC,WAAW;IAiBnB;;OAEG;IACH,OAAO,CAAC,cAAc;IAItB;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAiCzB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAA;IACjB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,gBAAgB,EAAE,MAAM,EAAE,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG"}
|