@edryslabs/genericprovider 1.0.4 → 1.5.2
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/README.md +129 -5
- package/dist/index.d.ts +903 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2723 -428
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +1 -0
- package/dist/lib.d.ts.map +1 -1
- package/dist/lib.js +3 -0
- package/dist/lib.js.map +1 -1
- package/dist/providers/ably/index.d.ts +203 -0
- package/dist/providers/ably/index.d.ts.map +1 -0
- package/dist/providers/ably/index.js +561 -0
- package/dist/providers/ably/index.js.map +1 -0
- package/dist/providers/chunking.d.ts +26 -0
- package/dist/providers/chunking.d.ts.map +1 -0
- package/dist/providers/chunking.js +52 -0
- package/dist/providers/chunking.js.map +1 -0
- package/dist/providers/dummy/index.d.ts +138 -2
- package/dist/providers/dummy/index.d.ts.map +1 -1
- package/dist/providers/dummy/index.js +266 -4
- package/dist/providers/dummy/index.js.map +1 -1
- package/dist/providers/gun/index.d.ts +11 -4
- package/dist/providers/gun/index.d.ts.map +1 -1
- package/dist/providers/gun/index.js +104 -22
- package/dist/providers/gun/index.js.map +1 -1
- package/dist/providers/indexeddb/index.d.ts +44 -20
- package/dist/providers/indexeddb/index.d.ts.map +1 -1
- package/dist/providers/indexeddb/index.js +85 -71
- package/dist/providers/indexeddb/index.js.map +1 -1
- package/dist/providers/matrix/index.d.ts +6 -1
- package/dist/providers/matrix/index.d.ts.map +1 -1
- package/dist/providers/matrix/index.js +44 -6
- package/dist/providers/matrix/index.js.map +1 -1
- package/dist/providers/nostr/index.d.ts +69 -5
- package/dist/providers/nostr/index.d.ts.map +1 -1
- package/dist/providers/nostr/index.js +204 -36
- package/dist/providers/nostr/index.js.map +1 -1
- package/dist/providers/peerjs/index.d.ts +11 -1
- package/dist/providers/peerjs/index.d.ts.map +1 -1
- package/dist/providers/peerjs/index.js +32 -2
- package/dist/providers/peerjs/index.js.map +1 -1
- package/dist/providers/pubnub/index.d.ts +35 -1
- package/dist/providers/pubnub/index.d.ts.map +1 -1
- package/dist/providers/pubnub/index.js +56 -35
- package/dist/providers/pubnub/index.js.map +1 -1
- package/dist/providers/simple-peer/index.d.ts +10 -15
- package/dist/providers/simple-peer/index.d.ts.map +1 -1
- package/dist/providers/simple-peer/index.js +81 -109
- package/dist/providers/simple-peer/index.js.map +1 -1
- package/dist/providers/supabase/index.d.ts +43 -1
- package/dist/providers/supabase/index.d.ts.map +1 -1
- package/dist/providers/supabase/index.js +211 -18
- package/dist/providers/supabase/index.js.map +1 -1
- package/dist/providers/trystero/index.d.ts +19 -1
- package/dist/providers/trystero/index.d.ts.map +1 -1
- package/dist/providers/trystero/index.js +37 -2
- package/dist/providers/trystero/index.js.map +1 -1
- package/dist/providers/websocket/index.d.ts +9 -1
- package/dist/providers/websocket/index.d.ts.map +1 -1
- package/dist/providers/websocket/index.js +7 -1
- package/dist/providers/websocket/index.js.map +1 -1
- package/dist/transport.d.ts +69 -16
- package/dist/transport.d.ts.map +1 -1
- package/package.json +35 -24
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ably Transport Provider
|
|
3
|
+
*
|
|
4
|
+
* Real-time synchronization using Ably's managed pub/sub messaging platform.
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Global edge network with low latency
|
|
8
|
+
* - Built-in presence tracking
|
|
9
|
+
* - API-key or token-based authentication
|
|
10
|
+
* - Optional password-protected rooms (channel name obfuscation)
|
|
11
|
+
* - Automatic chunking for messages above Ably's size limit
|
|
12
|
+
*
|
|
13
|
+
* The Ably SDK class is injected via the constructor (not imported directly)
|
|
14
|
+
* so this file compiles without the `ably` package installed, and consumers
|
|
15
|
+
* only pull in Ably if they actually use this provider.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import * as Y from 'yjs'
|
|
20
|
+
* import { GenericProvider } from 'y-generic'
|
|
21
|
+
* import { AblyTransport } from 'y-generic/providers/ably'
|
|
22
|
+
* import * as Ably from 'ably'
|
|
23
|
+
*
|
|
24
|
+
* const doc = new Y.Doc()
|
|
25
|
+
* const transport = new AblyTransport({ Realtime: Ably.Realtime })
|
|
26
|
+
* const provider = new GenericProvider(doc, transport)
|
|
27
|
+
*
|
|
28
|
+
* await provider.connect({
|
|
29
|
+
* apiKey: 'your-ably-api-key',
|
|
30
|
+
* room: 'my-collab-room',
|
|
31
|
+
* })
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @example Token auth (recommended for browser clients)
|
|
35
|
+
* ```typescript
|
|
36
|
+
* await provider.connect({
|
|
37
|
+
* authUrl: '/api/ably-token',
|
|
38
|
+
* room: 'my-collab-room',
|
|
39
|
+
* })
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
import * as encoding from 'lib0/encoding';
|
|
43
|
+
import * as syncProtocol from 'y-protocols/sync';
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
// CRC32 translation helpers
|
|
46
|
+
//
|
|
47
|
+
// GenericProvider wraps every outgoing message as [CRC32 (4 bytes)][payload].
|
|
48
|
+
// Ably message data is JSON-friendly, so we strip the CRC32 header before
|
|
49
|
+
// base64-encoding and re-add it after decoding so GenericProvider accepts
|
|
50
|
+
// the incoming message.
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
const _CRC32_TABLE = (() => {
|
|
53
|
+
const table = new Uint32Array(256);
|
|
54
|
+
for (let i = 0; i < 256; i++) {
|
|
55
|
+
let c = i;
|
|
56
|
+
for (let j = 0; j < 8; j++)
|
|
57
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
58
|
+
table[i] = c;
|
|
59
|
+
}
|
|
60
|
+
return table;
|
|
61
|
+
})();
|
|
62
|
+
function _crc32(data) {
|
|
63
|
+
let crc = 0xffffffff;
|
|
64
|
+
for (let i = 0; i < data.length; i++)
|
|
65
|
+
crc = (crc >>> 8) ^ _CRC32_TABLE[(crc ^ data[i]) & 0xff];
|
|
66
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
67
|
+
}
|
|
68
|
+
/** Strip the 4-byte CRC32 header that GenericProvider prepends. */
|
|
69
|
+
function stripCRC32Header(data) {
|
|
70
|
+
return data.length >= 4 ? data.subarray(4) : data;
|
|
71
|
+
}
|
|
72
|
+
/** Add a valid CRC32 header so GenericProvider accepts the message. */
|
|
73
|
+
function addCRC32Header(data) {
|
|
74
|
+
const crc = _crc32(data);
|
|
75
|
+
const wrapped = new Uint8Array(4 + data.length);
|
|
76
|
+
wrapped[0] = (crc >>> 24) & 0xff;
|
|
77
|
+
wrapped[1] = (crc >>> 16) & 0xff;
|
|
78
|
+
wrapped[2] = (crc >>> 8) & 0xff;
|
|
79
|
+
wrapped[3] = crc & 0xff;
|
|
80
|
+
wrapped.set(data, 4);
|
|
81
|
+
return wrapped;
|
|
82
|
+
}
|
|
83
|
+
// ---------------------------------------------------------------------------
|
|
84
|
+
// Base64 helpers (no Buffer/Node dependency)
|
|
85
|
+
// ---------------------------------------------------------------------------
|
|
86
|
+
function uint8ToBase64(data) {
|
|
87
|
+
const chunkSize = 8192; // Process 8KB at a time to avoid arg-count overflow
|
|
88
|
+
let binary = '';
|
|
89
|
+
for (let i = 0; i < data.length; i += chunkSize) {
|
|
90
|
+
const chunk = data.subarray(i, Math.min(i + chunkSize, data.length));
|
|
91
|
+
binary += String.fromCharCode(...chunk);
|
|
92
|
+
}
|
|
93
|
+
return btoa(binary);
|
|
94
|
+
}
|
|
95
|
+
function base64ToUint8(str) {
|
|
96
|
+
const binary = atob(str);
|
|
97
|
+
const bytes = new Uint8Array(binary.length);
|
|
98
|
+
for (let i = 0; i < binary.length; i++) {
|
|
99
|
+
bytes[i] = binary.charCodeAt(i);
|
|
100
|
+
}
|
|
101
|
+
return bytes;
|
|
102
|
+
}
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Password hashing (obfuscates channel name — not encryption)
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
async function hashPassword(password) {
|
|
107
|
+
const encoded = new TextEncoder().encode(password);
|
|
108
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', encoded);
|
|
109
|
+
return Array.from(new Uint8Array(hashBuffer))
|
|
110
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
111
|
+
.join('')
|
|
112
|
+
.substring(0, 16);
|
|
113
|
+
}
|
|
114
|
+
function generateUUID() {
|
|
115
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
116
|
+
return crypto.randomUUID();
|
|
117
|
+
}
|
|
118
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
119
|
+
const r = (Math.random() * 16) | 0;
|
|
120
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
121
|
+
return v.toString(16);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
const EVENT_NAME = 'yjs-update';
|
|
125
|
+
// Ably's default max message size is ~64 KiB; stay well under it. Used both
|
|
126
|
+
// as a base64-string cap for send()'s pub/sub chunking, and as a raw-byte
|
|
127
|
+
// cap for the persistence snapshot's LiveMap chunking (LiveMap values are
|
|
128
|
+
// raw ArrayBuffer/Buffer, no base64 inflation, so the same conservative
|
|
129
|
+
// number is a safe threshold for both).
|
|
130
|
+
const MAX_MESSAGE_SIZE = 55000;
|
|
131
|
+
// Message type identifiers (must match GenericProvider's wire format).
|
|
132
|
+
const MESSAGE_AWARENESS = 1;
|
|
133
|
+
/**
|
|
134
|
+
* Ably transport for y-generic.
|
|
135
|
+
*
|
|
136
|
+
* Publishes Yjs binary updates as base64-encoded messages on an Ably channel
|
|
137
|
+
* and subscribes to matching messages from peers.
|
|
138
|
+
*/
|
|
139
|
+
export class AblyTransport {
|
|
140
|
+
constructor(options) {
|
|
141
|
+
this.client = null;
|
|
142
|
+
this.channel = null;
|
|
143
|
+
this.clientId = '';
|
|
144
|
+
this.channelName = '';
|
|
145
|
+
this._isConnected = false;
|
|
146
|
+
this.debug = false;
|
|
147
|
+
this.messageBuffer = [];
|
|
148
|
+
this.chunkBuffer = new Map();
|
|
149
|
+
// No preferredCompressMinBytes: this transport strips the CRC32 header
|
|
150
|
+
// and synthesizes frames from persisted snapshots, both of which assume
|
|
151
|
+
// the uncompressed frame layout (see compressionThresholdBytes).
|
|
152
|
+
// Persistence
|
|
153
|
+
this.persistentMode = false;
|
|
154
|
+
this.persistDoc = null;
|
|
155
|
+
this.persistDebounceMs = 2000;
|
|
156
|
+
this.isWritingSnapshot = false;
|
|
157
|
+
this.savePending = false;
|
|
158
|
+
/** True once loadSnapshot()'s initial read has completed */
|
|
159
|
+
this.snapshotLoaded = false;
|
|
160
|
+
/** Cached LiveObjects root, resolved once per connect() */
|
|
161
|
+
this.liveRoot = null;
|
|
162
|
+
this.opts = options;
|
|
163
|
+
}
|
|
164
|
+
get isConnected() {
|
|
165
|
+
return this._isConnected;
|
|
166
|
+
}
|
|
167
|
+
async connect(config) {
|
|
168
|
+
this.debug = config.debug ?? this.opts.debug ?? false;
|
|
169
|
+
if (!config.apiKey && !config.authUrl) {
|
|
170
|
+
throw new Error('AblyTransport: apiKey or authUrl is required');
|
|
171
|
+
}
|
|
172
|
+
if (!config.room) {
|
|
173
|
+
throw new Error('AblyTransport: room name is required');
|
|
174
|
+
}
|
|
175
|
+
this.persistentMode = config.persistent ?? false;
|
|
176
|
+
this.persistDoc = config.doc ?? null;
|
|
177
|
+
this.persistDebounceMs = config.persistDebounceMs ?? 2000;
|
|
178
|
+
if (this.persistentMode && !this.persistDoc) {
|
|
179
|
+
throw new Error('AblyTransport: a Y.Doc must be provided via config.doc when persistent is true');
|
|
180
|
+
}
|
|
181
|
+
if (this.persistentMode && !this.opts.LiveObjects) {
|
|
182
|
+
throw new Error('AblyTransport: the "LiveObjects" plugin class must be provided via constructor options when persistent is true. ' +
|
|
183
|
+
'import { LiveObjects } from "ably/liveobjects"; new AblyTransport({ Realtime, LiveObjects })');
|
|
184
|
+
}
|
|
185
|
+
this.clientId = generateUUID();
|
|
186
|
+
this.channelName = config.password
|
|
187
|
+
? `${config.room}-${await hashPassword(config.password)}`
|
|
188
|
+
: config.room;
|
|
189
|
+
// Don't deliver our own published messages back to ourselves — except
|
|
190
|
+
// in persistent mode, where Ably's LiveObjects requires echoMessages to
|
|
191
|
+
// be enabled for write operations (root.set() throws otherwise). Any
|
|
192
|
+
// resulting self-echo of our own pub/sub messages is harmless:
|
|
193
|
+
// GenericProvider applies updates idempotently under its own origin
|
|
194
|
+
// sentinel, which also prevents re-broadcasting them.
|
|
195
|
+
const clientOptions = {
|
|
196
|
+
clientId: this.clientId,
|
|
197
|
+
echoMessages: this.persistentMode,
|
|
198
|
+
};
|
|
199
|
+
if (config.apiKey)
|
|
200
|
+
clientOptions.key = config.apiKey;
|
|
201
|
+
if (config.authUrl)
|
|
202
|
+
clientOptions.authUrl = config.authUrl;
|
|
203
|
+
if (config.authMethod)
|
|
204
|
+
clientOptions.authMethod = config.authMethod;
|
|
205
|
+
if (this.persistentMode) {
|
|
206
|
+
clientOptions.plugins = { LiveObjects: this.opts.LiveObjects };
|
|
207
|
+
}
|
|
208
|
+
this.log('Connecting as', this.clientId, 'to channel', this.channelName);
|
|
209
|
+
this.client = new this.opts.Realtime(clientOptions);
|
|
210
|
+
// Passing `modes` replaces the channel's default (unrestricted) mode set,
|
|
211
|
+
// so the persistent path must explicitly list everything this transport
|
|
212
|
+
// needs, not just the OBJECT_* modes LiveObjects requires. The
|
|
213
|
+
// non-persistent path is left untouched (no modes option, as before).
|
|
214
|
+
this.channel = this.persistentMode
|
|
215
|
+
? this.client.channels.get(this.channelName, {
|
|
216
|
+
modes: [
|
|
217
|
+
'PUBLISH',
|
|
218
|
+
'SUBSCRIBE',
|
|
219
|
+
'PRESENCE',
|
|
220
|
+
'PRESENCE_SUBSCRIBE',
|
|
221
|
+
'OBJECT_PUBLISH',
|
|
222
|
+
'OBJECT_SUBSCRIBE',
|
|
223
|
+
],
|
|
224
|
+
})
|
|
225
|
+
: this.client.channels.get(this.channelName);
|
|
226
|
+
await new Promise((resolve, reject) => {
|
|
227
|
+
const timeout = setTimeout(() => {
|
|
228
|
+
reject(new Error('Ably connection timeout'));
|
|
229
|
+
}, 10000);
|
|
230
|
+
this.client.connection.once('connected', () => {
|
|
231
|
+
clearTimeout(timeout);
|
|
232
|
+
this._isConnected = true;
|
|
233
|
+
this.log('Connected to Ably');
|
|
234
|
+
resolve();
|
|
235
|
+
});
|
|
236
|
+
this.client.connection.on('failed', (stateChange) => {
|
|
237
|
+
clearTimeout(timeout);
|
|
238
|
+
reject(new Error(`Ably connection failed: ${stateChange?.reason?.message ?? 'unknown error'}`));
|
|
239
|
+
});
|
|
240
|
+
this.client.connection.on('suspended', () => {
|
|
241
|
+
this.log('Connection suspended');
|
|
242
|
+
this._isConnected = false;
|
|
243
|
+
});
|
|
244
|
+
this.client.connection.on('disconnected', () => {
|
|
245
|
+
this.log('Connection disconnected');
|
|
246
|
+
this._isConnected = false;
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
await this.channel.subscribe((message) => {
|
|
250
|
+
// In persistent mode, echoMessages is enabled (LiveObjects requires it
|
|
251
|
+
// for writes) — filter out our own echoed pub/sub messages here so
|
|
252
|
+
// they never reach GenericProvider, same as Gun's transport dedupes
|
|
253
|
+
// its own writes before delivery. Otherwise a stale self-echo (hash
|
|
254
|
+
// computed a moment before our own newer local edits) trips the
|
|
255
|
+
// hash-mismatch resync check for no reason.
|
|
256
|
+
if (message.clientId === this.clientId)
|
|
257
|
+
return;
|
|
258
|
+
// The publisher's clientId is the peer id GenericProvider learns as
|
|
259
|
+
// `from` - the same id a presence leave reports (onPeerDisconnect).
|
|
260
|
+
this.handleMessage(message.data, message.clientId);
|
|
261
|
+
});
|
|
262
|
+
await this.channel.presence.enter();
|
|
263
|
+
// Presence leave: a clean leave, or Ably's own removal after a dropped
|
|
264
|
+
// connection's TTL. Delivered to every subscriber, so GenericProvider
|
|
265
|
+
// drops the member's awareness without a broadcast burst and lets the
|
|
266
|
+
// awareness lease default to 5 min (round 5, item 2). The client was
|
|
267
|
+
// already a presence member (enter/leave above); this subscribes to the
|
|
268
|
+
// events it ignored before.
|
|
269
|
+
await this.channel.presence.subscribe('leave', (member) => {
|
|
270
|
+
const id = member?.clientId;
|
|
271
|
+
if (typeof id === 'string' && id !== this.clientId) {
|
|
272
|
+
this.log('Peer left:', id);
|
|
273
|
+
this._peerDisconnectCallback?.(id);
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
// Persistence: load any existing snapshot. Unlike Gun, there's no
|
|
277
|
+
// "clear stale snapshot on non-persistent connect" step here — a plain
|
|
278
|
+
// channel (no OBJECT_* modes) never attaches LiveObjects at all, so a
|
|
279
|
+
// non-persistent connect has nothing to clear.
|
|
280
|
+
this.snapshotLoaded = false;
|
|
281
|
+
if (this.persistentMode) {
|
|
282
|
+
this.liveRoot = await this.channel.object.get();
|
|
283
|
+
this.loadSnapshot();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
async disconnect() {
|
|
287
|
+
this.log('Disconnecting...');
|
|
288
|
+
if (this.persistTimer) {
|
|
289
|
+
clearTimeout(this.persistTimer);
|
|
290
|
+
this.persistTimer = undefined;
|
|
291
|
+
}
|
|
292
|
+
if (this.persistentMode && this.persistDoc) {
|
|
293
|
+
try {
|
|
294
|
+
await this.saveSnapshot();
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
this.log('Error flushing snapshot on disconnect:', error);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (this.channel) {
|
|
301
|
+
try {
|
|
302
|
+
await this.channel.presence.leave();
|
|
303
|
+
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
this.log('Error leaving presence:', error);
|
|
306
|
+
}
|
|
307
|
+
this.channel.unsubscribe();
|
|
308
|
+
this.channel = null;
|
|
309
|
+
}
|
|
310
|
+
if (this.client) {
|
|
311
|
+
this.client.close();
|
|
312
|
+
this.client = null;
|
|
313
|
+
}
|
|
314
|
+
this._isConnected = false;
|
|
315
|
+
this.messageCallback = undefined;
|
|
316
|
+
this.messageBuffer = [];
|
|
317
|
+
this.chunkBuffer.clear();
|
|
318
|
+
this.persistentMode = false;
|
|
319
|
+
this.persistDoc = null;
|
|
320
|
+
this.snapshotLoaded = false;
|
|
321
|
+
this.liveRoot = null;
|
|
322
|
+
}
|
|
323
|
+
send(data) {
|
|
324
|
+
if (!this.channel || !this._isConnected) {
|
|
325
|
+
this.log('Cannot send: not connected');
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
const payload = stripCRC32Header(data);
|
|
329
|
+
const base64Data = uint8ToBase64(payload);
|
|
330
|
+
if (base64Data.length > MAX_MESSAGE_SIZE) {
|
|
331
|
+
this.sendChunked(base64Data, payload.length);
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
this.channel.publish(EVENT_NAME, base64Data).catch((error) => {
|
|
335
|
+
this.log('Publish error:', error);
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// Only real doc updates trigger a snapshot save, not every awareness
|
|
339
|
+
// heartbeat (peekMessageType() reads the type byte right after the
|
|
340
|
+
// 4-byte CRC header GenericProvider prepends to every message).
|
|
341
|
+
if (this.persistentMode && this.peekMessageType(data) !== MESSAGE_AWARENESS) {
|
|
342
|
+
this.queuePersist();
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
/** Peek the message type byte from CRC32-wrapped data (byte 4, after the 4-byte CRC32 header). */
|
|
346
|
+
peekMessageType(data) {
|
|
347
|
+
return data.length < 5 ? -1 : data[4];
|
|
348
|
+
}
|
|
349
|
+
onMessage(callback) {
|
|
350
|
+
this.messageCallback = callback;
|
|
351
|
+
if (this.messageBuffer.length > 0) {
|
|
352
|
+
for (const { data, from } of this.messageBuffer) {
|
|
353
|
+
callback(data, from);
|
|
354
|
+
}
|
|
355
|
+
this.messageBuffer = [];
|
|
356
|
+
}
|
|
357
|
+
return () => {
|
|
358
|
+
this.messageCallback = undefined;
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/** Get the clientIds of other peers currently present on the channel. */
|
|
362
|
+
async getPresence() {
|
|
363
|
+
if (!this.channel || !this._isConnected) {
|
|
364
|
+
return [];
|
|
365
|
+
}
|
|
366
|
+
try {
|
|
367
|
+
const members = await this.channel.presence.get();
|
|
368
|
+
return members
|
|
369
|
+
.map((m) => m.clientId)
|
|
370
|
+
.filter((id) => id !== this.clientId);
|
|
371
|
+
}
|
|
372
|
+
catch (error) {
|
|
373
|
+
this.log('Error getting presence:', error);
|
|
374
|
+
return [];
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
// ---------------------------------------------------------------------------
|
|
378
|
+
// Persistence helpers (LiveObjects)
|
|
379
|
+
// ---------------------------------------------------------------------------
|
|
380
|
+
/** Schedule a debounced snapshot write. Called on every non-awareness send(). */
|
|
381
|
+
queuePersist() {
|
|
382
|
+
if (this.persistTimer)
|
|
383
|
+
clearTimeout(this.persistTimer);
|
|
384
|
+
this.persistTimer = setTimeout(() => this.saveSnapshot(), this.persistDebounceMs);
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Encode the full Y.Doc state as a SYNC_STEP_2 message and write it across
|
|
388
|
+
* one or more LiveMap keys (each `set()` is capped at Ably's 64 KiB message
|
|
389
|
+
* size, so a real snapshot needs chunking — see `snapshot-count`/`snapshot-N`
|
|
390
|
+
* below). Chunk keys are written first, `snapshot-count` last, so a reader
|
|
391
|
+
* can treat its presence as "this snapshot is complete."
|
|
392
|
+
*/
|
|
393
|
+
async saveSnapshot() {
|
|
394
|
+
if (!this.persistDoc || !this.persistentMode || !this.liveRoot)
|
|
395
|
+
return;
|
|
396
|
+
if (!this.snapshotLoaded) {
|
|
397
|
+
// The initial LiveObjects read hasn't resolved yet — saving now could
|
|
398
|
+
// clobber the real persisted state with our still-unmerged local doc.
|
|
399
|
+
this.persistTimer = setTimeout(() => this.saveSnapshot(), 100);
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (this.isWritingSnapshot) {
|
|
403
|
+
this.savePending = true;
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
this.isWritingSnapshot = true;
|
|
407
|
+
this.savePending = false;
|
|
408
|
+
try {
|
|
409
|
+
const enc = encoding.createEncoder();
|
|
410
|
+
encoding.writeVarUint(enc, 0); // MESSAGE_SYNC
|
|
411
|
+
syncProtocol.writeSyncStep2(enc, this.persistDoc);
|
|
412
|
+
const snapshotBytes = encoding.toUint8Array(enc);
|
|
413
|
+
const chunks = [];
|
|
414
|
+
for (let i = 0; i < snapshotBytes.length; i += MAX_MESSAGE_SIZE) {
|
|
415
|
+
const end = Math.min(i + MAX_MESSAGE_SIZE, snapshotBytes.length);
|
|
416
|
+
chunks.push(snapshotBytes.buffer.slice(snapshotBytes.byteOffset + i, snapshotBytes.byteOffset + end));
|
|
417
|
+
}
|
|
418
|
+
await Promise.all(chunks.map((chunk, i) => this.liveRoot.set(`snapshot-${i}`, chunk)));
|
|
419
|
+
await this.liveRoot.set('snapshot-count', chunks.length);
|
|
420
|
+
this.log('Snapshot saved', snapshotBytes.length, 'bytes in', chunks.length, 'chunk(s)');
|
|
421
|
+
}
|
|
422
|
+
catch (error) {
|
|
423
|
+
this.log('Error saving snapshot:', error.message);
|
|
424
|
+
console.warn('AblyTransport: Failed to save snapshot. Will retry later.', error);
|
|
425
|
+
this.savePending = true;
|
|
426
|
+
}
|
|
427
|
+
finally {
|
|
428
|
+
this.isWritingSnapshot = false;
|
|
429
|
+
if (this.savePending) {
|
|
430
|
+
setTimeout(() => this.saveSnapshot(), 1000);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Load the snapshot from LiveObjects and deliver it to the message
|
|
436
|
+
* callback. Aborts without delivering anything if a chunk is missing —
|
|
437
|
+
* partial data is worse than none.
|
|
438
|
+
*/
|
|
439
|
+
async loadSnapshot() {
|
|
440
|
+
if (!this.liveRoot)
|
|
441
|
+
return;
|
|
442
|
+
try {
|
|
443
|
+
const count = this.liveRoot.get('snapshot-count').value();
|
|
444
|
+
if (!count) {
|
|
445
|
+
this.log('No snapshot found in LiveObjects');
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
const chunks = [];
|
|
449
|
+
for (let i = 0; i < count; i++) {
|
|
450
|
+
const chunk = this.liveRoot.get(`snapshot-${i}`).value();
|
|
451
|
+
if (!chunk) {
|
|
452
|
+
this.log(`Missing chunk ${i}, cannot reassemble snapshot`);
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
chunks.push(new Uint8Array(chunk));
|
|
456
|
+
}
|
|
457
|
+
const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
|
|
458
|
+
const snapshotBytes = new Uint8Array(totalLength);
|
|
459
|
+
let offset = 0;
|
|
460
|
+
for (const chunk of chunks) {
|
|
461
|
+
snapshotBytes.set(chunk, offset);
|
|
462
|
+
offset += chunk.length;
|
|
463
|
+
}
|
|
464
|
+
if (snapshotBytes.length > 0) {
|
|
465
|
+
this.deliver(addCRC32Header(snapshotBytes));
|
|
466
|
+
this.log('Loaded snapshot:', snapshotBytes.length, 'bytes');
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
catch (error) {
|
|
470
|
+
this.log('Error loading snapshot:', error);
|
|
471
|
+
console.warn('AblyTransport: Failed to load snapshot:', error);
|
|
472
|
+
}
|
|
473
|
+
finally {
|
|
474
|
+
this.snapshotLoaded = true;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
// ---------------------------------------------------------------------------
|
|
478
|
+
// Private helpers
|
|
479
|
+
// ---------------------------------------------------------------------------
|
|
480
|
+
sendChunked(base64Data, originalSize) {
|
|
481
|
+
const chunkId = generateUUID();
|
|
482
|
+
const chunks = [];
|
|
483
|
+
for (let i = 0; i < base64Data.length; i += MAX_MESSAGE_SIZE) {
|
|
484
|
+
chunks.push(base64Data.slice(i, i + MAX_MESSAGE_SIZE));
|
|
485
|
+
}
|
|
486
|
+
this.log(`Splitting ${originalSize} bytes into ${chunks.length} chunks (id: ${chunkId.slice(0, 8)}...)`);
|
|
487
|
+
chunks.forEach((chunk, index) => {
|
|
488
|
+
const message = { chunked: true, id: chunkId, index, total: chunks.length, data: chunk };
|
|
489
|
+
this.channel.publish(EVENT_NAME, message).catch((error) => {
|
|
490
|
+
this.log(`Failed to send chunk ${index + 1}:`, error);
|
|
491
|
+
});
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
handleChunkedMessage(message, from) {
|
|
495
|
+
const { id, index, total, data } = message;
|
|
496
|
+
if (!this.chunkBuffer.has(id)) {
|
|
497
|
+
this.chunkBuffer.set(id, new Map());
|
|
498
|
+
}
|
|
499
|
+
const chunks = this.chunkBuffer.get(id);
|
|
500
|
+
chunks.set(index, data);
|
|
501
|
+
if (chunks.size !== total)
|
|
502
|
+
return;
|
|
503
|
+
let base64Data = '';
|
|
504
|
+
for (let i = 0; i < total; i++) {
|
|
505
|
+
const chunk = chunks.get(i);
|
|
506
|
+
if (!chunk) {
|
|
507
|
+
this.log(`Missing chunk ${i}, cannot reassemble message ${id}`);
|
|
508
|
+
this.chunkBuffer.delete(id);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
base64Data += chunk;
|
|
512
|
+
}
|
|
513
|
+
this.chunkBuffer.delete(id);
|
|
514
|
+
try {
|
|
515
|
+
const raw = base64ToUint8(base64Data);
|
|
516
|
+
this.deliver(addCRC32Header(raw), from);
|
|
517
|
+
}
|
|
518
|
+
catch (error) {
|
|
519
|
+
this.log('Error reassembling chunked message:', error);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
handleMessage(data, from) {
|
|
523
|
+
try {
|
|
524
|
+
if (data && typeof data === 'object' && data.chunked) {
|
|
525
|
+
this.handleChunkedMessage(data, from);
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
if (typeof data !== 'string')
|
|
529
|
+
return;
|
|
530
|
+
const raw = base64ToUint8(data);
|
|
531
|
+
this.deliver(addCRC32Header(raw), from);
|
|
532
|
+
}
|
|
533
|
+
catch (error) {
|
|
534
|
+
this.log('Error handling message:', error);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
deliver(data, from) {
|
|
538
|
+
if (this.messageCallback) {
|
|
539
|
+
this.messageCallback(data, from);
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
this.messageBuffer.push({ data, from });
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Transport.onPeerDisconnect: Ably presence 'leave' on the channel. Peer
|
|
547
|
+
* ids are Ably clientIds, the same `from` onMessage passes.
|
|
548
|
+
*/
|
|
549
|
+
onPeerDisconnect(callback) {
|
|
550
|
+
this._peerDisconnectCallback = callback;
|
|
551
|
+
return () => {
|
|
552
|
+
this._peerDisconnectCallback = undefined;
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
log(...args) {
|
|
556
|
+
if (this.debug) {
|
|
557
|
+
console.log('[AblyTransport]', ...args);
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/providers/ably/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Chunk envelope shared by the providers whose backend caps a single
|
|
3
|
+
* message (Matrix 65,536 B canonical JSON per event, Nostr 64 KiB per
|
|
4
|
+
* event, Supabase 256 KB per broadcast). Same shape PubNub and Ably use
|
|
5
|
+
* for theirs. Payloads are the provider's base64 string; the provider
|
|
6
|
+
* decides the limit after its own encoding.
|
|
7
|
+
*/
|
|
8
|
+
export interface Chunk {
|
|
9
|
+
chunked: true;
|
|
10
|
+
id: string;
|
|
11
|
+
index: number;
|
|
12
|
+
total: number;
|
|
13
|
+
data: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function isChunk(x: unknown): x is Chunk;
|
|
16
|
+
/** Split `data` into chunks of at most `maxChars`; one chunk when it fits. */
|
|
17
|
+
export declare function splitChunks(data: string, maxChars: number): Chunk[];
|
|
18
|
+
/** Reassembles chunks per id; returns the whole payload once complete. */
|
|
19
|
+
export declare class ChunkAssembler {
|
|
20
|
+
private readonly maxPending;
|
|
21
|
+
private pending;
|
|
22
|
+
constructor(maxPending?: number);
|
|
23
|
+
push(chunk: Chunk): string | null;
|
|
24
|
+
clear(): void;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=chunking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunking.d.ts","sourceRoot":"","sources":["../../src/providers/chunking.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,KAAK;IACpB,OAAO,EAAE,IAAI,CAAA;IACb,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,KAAK,CAQ9C;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,EAAE,CAQnE;AAED,0EAA0E;AAC1E,qBAAa,cAAc;IAEb,OAAO,CAAC,QAAQ,CAAC,UAAU;IADvC,OAAO,CAAC,OAAO,CAAyC;gBAC3B,UAAU,SAAK;IAE5C,IAAI,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,GAAG,IAAI;IAuBjC,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export function isChunk(x) {
|
|
2
|
+
return (typeof x === 'object' &&
|
|
3
|
+
x !== null &&
|
|
4
|
+
x.chunked === true &&
|
|
5
|
+
typeof x.id === 'string' &&
|
|
6
|
+
typeof x.data === 'string');
|
|
7
|
+
}
|
|
8
|
+
/** Split `data` into chunks of at most `maxChars`; one chunk when it fits. */
|
|
9
|
+
export function splitChunks(data, maxChars) {
|
|
10
|
+
const id = Math.random().toString(36).slice(2) + Date.now().toString(36);
|
|
11
|
+
const total = Math.max(1, Math.ceil(data.length / maxChars));
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for (let i = 0; i < total; i++) {
|
|
14
|
+
chunks.push({ chunked: true, id, index: i, total, data: data.slice(i * maxChars, (i + 1) * maxChars) });
|
|
15
|
+
}
|
|
16
|
+
return chunks;
|
|
17
|
+
}
|
|
18
|
+
/** Reassembles chunks per id; returns the whole payload once complete. */
|
|
19
|
+
export class ChunkAssembler {
|
|
20
|
+
constructor(maxPending = 32) {
|
|
21
|
+
this.maxPending = maxPending;
|
|
22
|
+
this.pending = new Map();
|
|
23
|
+
}
|
|
24
|
+
push(chunk) {
|
|
25
|
+
let parts = this.pending.get(chunk.id);
|
|
26
|
+
if (!parts) {
|
|
27
|
+
// ponytail: oldest-first eviction bounds memory when a sender's
|
|
28
|
+
// chunks never complete (lost event); no per-id timers.
|
|
29
|
+
if (this.pending.size >= this.maxPending) {
|
|
30
|
+
this.pending.delete(this.pending.keys().next().value);
|
|
31
|
+
}
|
|
32
|
+
parts = new Map();
|
|
33
|
+
this.pending.set(chunk.id, parts);
|
|
34
|
+
}
|
|
35
|
+
parts.set(chunk.index, chunk.data);
|
|
36
|
+
if (parts.size < chunk.total)
|
|
37
|
+
return null;
|
|
38
|
+
this.pending.delete(chunk.id);
|
|
39
|
+
let out = '';
|
|
40
|
+
for (let i = 0; i < chunk.total; i++) {
|
|
41
|
+
const part = parts.get(i);
|
|
42
|
+
if (part === undefined)
|
|
43
|
+
return null;
|
|
44
|
+
out += part;
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
clear() {
|
|
49
|
+
this.pending.clear();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=chunking.js.map
|