0
# Synchronization
1
2
Binary update system for efficient network synchronization and state management. Yjs provides comprehensive tools for encoding, decoding, merging, and applying document changes across distributed systems.
3
4
## Capabilities
5
6
### Basic Update Operations
7
8
Core functions for applying and generating document updates.
9
10
```typescript { .api }
11
/**
12
* Apply a binary update to a document (V1 format)
13
* @param doc - Document to apply update to
14
* @param update - Binary update data
15
* @param transactionOrigin - Optional origin for the transaction
16
*/
17
function applyUpdate(doc: Doc, update: Uint8Array, transactionOrigin?: any): void;
18
19
/**
20
* Apply a binary update to a document (V2 format, optimized)
21
* @param doc - Document to apply update to
22
* @param update - Binary update data in V2 format
23
* @param transactionOrigin - Optional origin for the transaction
24
*/
25
function applyUpdateV2(doc: Doc, update: Uint8Array, transactionOrigin?: any): void;
26
27
/**
28
* Encode document state as update (V1 format)
29
* @param doc - Document to encode
30
* @param encodedTargetStateVector - Optional target state to encode diff against
31
* @returns Binary update data
32
*/
33
function encodeStateAsUpdate(doc: Doc, encodedTargetStateVector?: Uint8Array): Uint8Array;
34
35
/**
36
* Encode document state as update (V2 format, optimized)
37
* @param doc - Document to encode
38
* @param encodedTargetStateVector - Optional target state to encode diff against
39
* @returns Binary update data in V2 format
40
*/
41
function encodeStateAsUpdateV2(doc: Doc, encodedTargetStateVector?: Uint8Array): Uint8Array;
42
```
43
44
**Usage Examples:**
45
46
```typescript
47
import * as Y from "yjs";
48
49
// Create two documents
50
const doc1 = new Y.Doc();
51
const doc2 = new Y.Doc();
52
53
// Make changes to doc1
54
const yarray1 = doc1.getArray("items");
55
yarray1.push(["apple", "banana"]);
56
57
// Generate update from doc1
58
const update = Y.encodeStateAsUpdate(doc1);
59
60
// Apply update to doc2
61
Y.applyUpdate(doc2, update);
62
63
// doc2 now has same content as doc1
64
const yarray2 = doc2.getArray("items");
65
console.log(yarray2.toArray()); // ["apple", "banana"]
66
67
// Use V2 format for better compression
68
const updateV2 = Y.encodeStateAsUpdateV2(doc1);
69
const doc3 = new Y.Doc();
70
Y.applyUpdateV2(doc3, updateV2);
71
```
72
73
### Update Merging
74
75
Functions for combining multiple updates into single updates for efficient transmission.
76
77
```typescript { .api }
78
/**
79
* Merge multiple updates into a single update (V1 format)
80
* @param updates - Array of binary updates to merge
81
* @returns Single merged update
82
*/
83
function mergeUpdates(updates: Array<Uint8Array>): Uint8Array;
84
85
/**
86
* Merge multiple updates into a single update (V2 format)
87
* @param updates - Array of binary updates to merge
88
* @returns Single merged update in V2 format
89
*/
90
function mergeUpdatesV2(updates: Array<Uint8Array>): Uint8Array;
91
```
92
93
**Usage Examples:**
94
95
```typescript
96
import * as Y from "yjs";
97
98
const doc = new Y.Doc();
99
const updates: Uint8Array[] = [];
100
101
// Collect multiple updates
102
const yarray = doc.getArray("items");
103
yarray.push(["item1"]);
104
updates.push(Y.encodeStateAsUpdate(doc));
105
106
yarray.push(["item2"]);
107
updates.push(Y.encodeStateAsUpdate(doc));
108
109
yarray.push(["item3"]);
110
updates.push(Y.encodeStateAsUpdate(doc));
111
112
// Merge into single update
113
const mergedUpdate = Y.mergeUpdates(updates);
114
115
// Apply merged update to new document
116
const newDoc = new Y.Doc();
117
Y.applyUpdate(newDoc, mergedUpdate);
118
119
console.log(newDoc.getArray("items").toArray()); // ["item1", "item2", "item3"]
120
121
// V2 merging for better compression
122
const mergedUpdateV2 = Y.mergeUpdatesV2(updates.map(u => Y.convertUpdateFormatV1ToV2(u)));
123
```
124
125
### State Vectors
126
127
Functions for working with document state vectors that represent document synchronization state.
128
129
```typescript { .api }
130
/**
131
* Encode state vector to binary format
132
* @param doc - Document or state vector map to encode
133
* @returns Binary state vector
134
*/
135
function encodeStateVector(doc: Doc | Map<number, number>): Uint8Array;
136
137
/**
138
* Decode state vector from binary format
139
* @param encodedState - Binary state vector data
140
* @returns State vector as Map
141
*/
142
function decodeStateVector(encodedState: Uint8Array): Map<number, number>;
143
144
/**
145
* Get current state vector from document
146
* @param doc - Document to get state from
147
* @returns State vector as Map
148
*/
149
function getState(doc: Doc): Map<number, number>;
150
151
/**
152
* Extract state vector from update (V1 format)
153
* @param update - Binary update to extract from
154
* @returns Binary state vector
155
*/
156
function encodeStateVectorFromUpdate(update: Uint8Array): Uint8Array;
157
158
/**
159
* Extract state vector from update (V2 format)
160
* @param update - Binary update to extract from
161
* @returns Binary state vector
162
*/
163
function encodeStateVectorFromUpdateV2(update: Uint8Array): Uint8Array;
164
```
165
166
**Usage Examples:**
167
168
```typescript
169
import * as Y from "yjs";
170
171
const doc1 = new Y.Doc();
172
const doc2 = new Y.Doc();
173
174
// Make changes to doc1
175
doc1.getArray("items").push(["item1", "item2"]);
176
177
// Get state vectors
178
const state1 = Y.getState(doc1);
179
const state2 = Y.getState(doc2);
180
181
console.log("Doc1 state:", state1); // Map with client states
182
console.log("Doc2 state:", state2); // Empty map
183
184
// Encode/decode state vectors
185
const encodedState = Y.encodeStateVector(doc1);
186
const decodedState = Y.decodeStateVector(encodedState);
187
188
console.log("Decoded state equals original:",
189
JSON.stringify([...state1]) === JSON.stringify([...decodedState]));
190
191
// Use state vector for targeted sync
192
const targetStateVector = Y.encodeStateVector(doc2);
193
const diffUpdate = Y.encodeStateAsUpdate(doc1, targetStateVector);
194
Y.applyUpdate(doc2, diffUpdate);
195
```
196
197
### Update Diffing
198
199
Functions for computing differences between updates and state vectors.
200
201
```typescript { .api }
202
/**
203
* Compute diff between update and state vector (V1 format)
204
* @param update - Binary update data
205
* @param stateVector - Binary state vector to diff against
206
* @returns Diff update containing only new changes
207
*/
208
function diffUpdate(update: Uint8Array, stateVector: Uint8Array): Uint8Array;
209
210
/**
211
* Compute diff between update and state vector (V2 format)
212
* @param update - Binary update data in V2 format
213
* @param stateVector - Binary state vector to diff against
214
* @returns Diff update containing only new changes
215
*/
216
function diffUpdateV2(update: Uint8Array, stateVector: Uint8Array): Uint8Array;
217
```
218
219
**Usage Examples:**
220
221
```typescript
222
import * as Y from "yjs";
223
224
const doc1 = new Y.Doc();
225
const doc2 = new Y.Doc();
226
227
// Make changes to doc1
228
doc1.getArray("items").push(["item1", "item2"]);
229
230
// Get full update from doc1
231
const fullUpdate = Y.encodeStateAsUpdate(doc1);
232
233
// Simulate doc2 already having some of the changes
234
doc2.getArray("items").push(["item1"]); // Partial state
235
236
// Get state vector from doc2
237
const doc2State = Y.encodeStateVector(doc2);
238
239
// Compute diff - only changes doc2 doesn't have
240
const diffUpdate = Y.diffUpdate(fullUpdate, doc2State);
241
242
// Apply diff to doc2
243
Y.applyUpdate(doc2, diffUpdate);
244
245
console.log(doc2.getArray("items").toArray()); // ["item1", "item2"]
246
```
247
248
### Format Conversion
249
250
Functions for converting between V1 and V2 update formats.
251
252
```typescript { .api }
253
/**
254
* Convert update from V1 to V2 format
255
* @param update - Update in V1 format
256
* @returns Update in V2 format
257
*/
258
function convertUpdateFormatV1ToV2(update: Uint8Array): Uint8Array;
259
260
/**
261
* Convert update from V2 to V1 format
262
* @param update - Update in V2 format
263
* @returns Update in V1 format
264
*/
265
function convertUpdateFormatV2ToV1(update: Uint8Array): Uint8Array;
266
```
267
268
**Usage Examples:**
269
270
```typescript
271
import * as Y from "yjs";
272
273
const doc = new Y.Doc();
274
doc.getArray("items").push(["item1", "item2"]);
275
276
// Generate V1 update
277
const updateV1 = Y.encodeStateAsUpdate(doc);
278
console.log("V1 size:", updateV1.length);
279
280
// Convert to V2 (typically smaller)
281
const updateV2 = Y.convertUpdateFormatV1ToV2(updateV1);
282
console.log("V2 size:", updateV2.length);
283
284
// Convert back to V1
285
const backToV1 = Y.convertUpdateFormatV2ToV1(updateV2);
286
287
// Verify they're functionally equivalent
288
const testDoc1 = new Y.Doc();
289
const testDoc2 = new Y.Doc();
290
291
Y.applyUpdate(testDoc1, updateV1);
292
Y.applyUpdate(testDoc2, backToV1);
293
294
console.log("Results equal:",
295
JSON.stringify(testDoc1.getArray("items").toArray()) ===
296
JSON.stringify(testDoc2.getArray("items").toArray()));
297
```
298
299
### Update Inspection
300
301
Functions for reading and analyzing update contents without applying them.
302
303
```typescript { .api }
304
/**
305
* Read update metadata (V1 format)
306
* @param update - Binary update to analyze
307
* @returns Object with from/to state vectors
308
*/
309
function parseUpdateMeta(update: Uint8Array): {
310
from: Map<number, number>;
311
to: Map<number, number>;
312
};
313
314
/**
315
* Read update metadata (V2 format)
316
* @param update - Binary update to analyze
317
* @returns Object with from/to state vectors
318
*/
319
function parseUpdateMetaV2(update: Uint8Array): {
320
from: Map<number, number>;
321
to: Map<number, number>;
322
};
323
324
/**
325
* Read update without applying it (V1 format)
326
* @param update - Binary update data
327
* @param YDoc - Doc constructor for validation
328
* @returns Update content information
329
*/
330
function readUpdate(update: Uint8Array, YDoc: typeof Doc): any;
331
332
/**
333
* Read update without applying it (V2 format)
334
* @param update - Binary update data
335
* @param YDoc - Doc constructor for validation
336
* @returns Update content information
337
*/
338
function readUpdateV2(update: Uint8Array, YDoc: typeof Doc): any;
339
```
340
341
**Usage Examples:**
342
343
```typescript
344
import * as Y from "yjs";
345
346
const doc = new Y.Doc();
347
doc.getArray("items").push(["item1", "item2"]);
348
349
const update = Y.encodeStateAsUpdate(doc);
350
351
// Analyze update metadata
352
const meta = Y.parseUpdateMeta(update);
353
console.log("From state:", meta.from);
354
console.log("To state:", meta.to);
355
356
// Read update contents
357
const updateContents = Y.readUpdate(update, Y.Doc);
358
console.log("Update contents:", updateContents);
359
360
// Check what changes an update would make
361
function analyzeUpdate(update: Uint8Array) {
362
const meta = Y.parseUpdateMeta(update);
363
364
console.log("Update affects clients:", Array.from(meta.to.keys()));
365
366
const totalOperations = Array.from(meta.to.values()).reduce((sum, clock) => {
367
const fromClock = meta.from.get(Array.from(meta.to.keys())[0]) || 0;
368
return sum + (clock - fromClock);
369
}, 0);
370
371
console.log("Total operations:", totalOperations);
372
}
373
374
analyzeUpdate(update);
375
```
376
377
### Advanced Synchronization Patterns
378
379
**Incremental Sync:**
380
381
```typescript
382
import * as Y from "yjs";
383
384
class IncrementalSyncer {
385
private doc: Y.Doc;
386
private lastSyncState: Map<number, number>;
387
388
constructor(doc: Y.Doc) {
389
this.doc = doc;
390
this.lastSyncState = new Map();
391
}
392
393
getIncrementalUpdate(): Uint8Array | null {
394
const currentState = Y.getState(this.doc);
395
396
// Check if there are changes since last sync
397
const hasChanges = Array.from(currentState.entries()).some(([client, clock]) => {
398
return (this.lastSyncState.get(client) || 0) < clock;
399
});
400
401
if (!hasChanges) return null;
402
403
// Encode only changes since last sync
404
const lastSyncVector = Y.encodeStateVector(this.lastSyncState);
405
const incrementalUpdate = Y.encodeStateAsUpdate(this.doc, lastSyncVector);
406
407
// Update sync state
408
this.lastSyncState = new Map(currentState);
409
410
return incrementalUpdate;
411
}
412
413
reset() {
414
this.lastSyncState.clear();
415
}
416
}
417
418
// Usage
419
const doc = new Y.Doc();
420
const syncer = new IncrementalSyncer(doc);
421
422
doc.getArray("items").push(["item1"]);
423
const update1 = syncer.getIncrementalUpdate(); // Contains item1
424
425
doc.getArray("items").push(["item2"]);
426
const update2 = syncer.getIncrementalUpdate(); // Contains only item2
427
428
const noUpdate = syncer.getIncrementalUpdate(); // null - no changes
429
```
430
431
**Conflict-Free Updates:**
432
433
```typescript
434
import * as Y from "yjs";
435
436
// Ensure updates can be applied in any order
437
function ensureConflictFree(updates: Uint8Array[]): boolean {
438
const permutations = getPermutations(updates);
439
440
return permutations.every(updateOrder => {
441
const doc1 = new Y.Doc();
442
const doc2 = new Y.Doc();
443
444
// Apply in one order
445
updateOrder.forEach(update => Y.applyUpdate(doc1, update));
446
447
// Apply merged update
448
const merged = Y.mergeUpdates(updates);
449
Y.applyUpdate(doc2, merged);
450
451
// Results should be identical
452
return Y.encodeStateAsUpdate(doc1).every((byte, i) =>
453
byte === Y.encodeStateAsUpdate(doc2)[i]);
454
});
455
}
456
457
function getPermutations<T>(arr: T[]): T[][] {
458
if (arr.length <= 1) return [arr];
459
460
const result: T[][] = [];
461
for (let i = 0; i < arr.length; i++) {
462
const rest = [...arr.slice(0, i), ...arr.slice(i + 1)];
463
const perms = getPermutations(rest);
464
perms.forEach(perm => result.push([arr[i], ...perm]));
465
}
466
return result;
467
}
468
```
469
470
**Update Validation:**
471
472
```typescript
473
import * as Y from "yjs";
474
475
function validateUpdate(update: Uint8Array): { valid: boolean; errors: string[] } {
476
const errors: string[] = [];
477
478
try {
479
// Check if update can be parsed
480
const meta = Y.parseUpdateMeta(update);
481
482
// Validate state vector consistency
483
if (meta.from.size === 0 && meta.to.size === 0) {
484
errors.push("Empty update");
485
}
486
487
// Check for negative clocks
488
for (const [client, clock] of meta.to) {
489
if (clock < 0) {
490
errors.push(`Negative clock for client ${client}: ${clock}`);
491
}
492
493
const fromClock = meta.from.get(client) || 0;
494
if (fromClock > clock) {
495
errors.push(`Invalid clock sequence for client ${client}: ${fromClock} -> ${clock}`);
496
}
497
}
498
499
// Try to read update structure
500
Y.readUpdate(update, Y.Doc);
501
502
} catch (error) {
503
errors.push(`Parse error: ${error.message}`);
504
}
505
506
return { valid: errors.length === 0, errors };
507
}
508
509
// Usage
510
const doc = new Y.Doc();
511
doc.getArray("items").push(["item1"]);
512
const update = Y.encodeStateAsUpdate(doc);
513
514
const validation = validateUpdate(update);
515
console.log("Update valid:", validation.valid);
516
if (!validation.valid) {
517
console.log("Errors:", validation.errors);
518
}
519
```