or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

document-management.mdevent-system.mdindex.mdposition-tracking.mdshared-data-types.mdsnapshot-system.mdsynchronization.mdtransaction-system.mdundo-redo-system.mdxml-types.md

synchronization.mddocs/

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

```