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

snapshot-system.mddocs/

0

# Snapshot System

1

2

Time-travel functionality for document state at specific points with diff capabilities. Yjs snapshots provide immutable views of document state that enable version control, branching, and historical analysis.

3

4

## Capabilities

5

6

### Snapshot Class

7

8

Immutable representation of document state at a specific point in time.

9

10

```typescript { .api }

11

/**

12

* Immutable snapshot of document state

13

*/

14

class Snapshot {

15

/** Delete set at snapshot time */

16

readonly ds: DeleteSet;

17

18

/** State vector at snapshot time */

19

readonly sv: Map<number, number>;

20

}

21

```

22

23

### Creating Snapshots

24

25

Functions for creating snapshots from documents and components.

26

27

```typescript { .api }

28

/**

29

* Create snapshot of current document state

30

* @param doc - Document to snapshot

31

* @returns Snapshot of current state

32

*/

33

function snapshot(doc: Doc): Snapshot;

34

35

/**

36

* Create snapshot from delete set and state vector

37

* @param ds - Delete set at snapshot time

38

* @param sv - State vector at snapshot time

39

* @returns Constructed snapshot

40

*/

41

function createSnapshot(ds: DeleteSet, sv: Map<number, number>): Snapshot;

42

43

/**

44

* Empty snapshot constant representing initial state

45

*/

46

const emptySnapshot: Snapshot;

47

```

48

49

**Usage Examples:**

50

51

```typescript

52

import * as Y from "yjs";

53

54

const doc = new Y.Doc();

55

const yarray = doc.getArray("items");

56

57

// Take initial snapshot

58

const initialSnapshot = Y.snapshot(doc);

59

60

// Make changes

61

yarray.push(["item1", "item2"]);

62

63

// Take snapshot after changes

64

const afterChangesSnapshot = Y.snapshot(doc);

65

66

// Take another snapshot after more changes

67

yarray.delete(0, 1);

68

const finalSnapshot = Y.snapshot(doc);

69

70

console.log("Snapshots are different:",

71

!Y.equalSnapshots(initialSnapshot, afterChangesSnapshot));

72

```

73

74

### Snapshot Comparison

75

76

Functions for comparing snapshots and checking relationships.

77

78

```typescript { .api }

79

/**

80

* Compare two snapshots for equality

81

* @param snap1 - First snapshot

82

* @param snap2 - Second snapshot

83

* @returns True if snapshots represent the same state

84

*/

85

function equalSnapshots(snap1: Snapshot, snap2: Snapshot): boolean;

86

87

/**

88

* Check if snapshot contains a specific update

89

* @param snapshot - Snapshot to check

90

* @param update - Binary update to check for

91

* @returns True if update is contained in snapshot

92

*/

93

function snapshotContainsUpdate(snapshot: Snapshot, update: Uint8Array): boolean;

94

```

95

96

**Usage Examples:**

97

98

```typescript

99

import * as Y from "yjs";

100

101

const doc = new Y.Doc();

102

const yarray = doc.getArray("items");

103

104

// Create snapshots at different states

105

const snap1 = Y.snapshot(doc);

106

107

yarray.push(["item1"]);

108

const snap2 = Y.snapshot(doc);

109

110

yarray.push(["item2"]);

111

const snap3 = Y.snapshot(doc);

112

113

// Compare snapshots

114

console.log("snap1 equals snap2:", Y.equalSnapshots(snap1, snap2)); // false

115

console.log("snap2 equals snap3:", Y.equalSnapshots(snap2, snap3)); // false

116

117

// Check if update is contained in snapshot

118

const update = Y.encodeStateAsUpdate(doc);

119

console.log("Current update in snap3:", Y.snapshotContainsUpdate(snap3, update)); // true

120

console.log("Current update in snap1:", Y.snapshotContainsUpdate(snap1, update)); // false

121

```

122

123

### Document Creation from Snapshots

124

125

Functions for creating new documents from snapshot states.

126

127

```typescript { .api }

128

/**

129

* Create document from snapshot state

130

* @param originDoc - Original document the snapshot was taken from

131

* @param snapshot - Snapshot to recreate state from

132

* @param newDoc - Optional existing document to apply state to

133

* @returns Document in the snapshot state

134

*/

135

function createDocFromSnapshot(originDoc: Doc, snapshot: Snapshot, newDoc?: Doc): Doc;

136

```

137

138

**Usage Examples:**

139

140

```typescript

141

import * as Y from "yjs";

142

143

const doc = new Y.Doc();

144

const yarray = doc.getArray("items");

145

146

// Build up document state

147

yarray.push(["item1", "item2", "item3"]);

148

const snapshot1 = Y.snapshot(doc);

149

150

yarray.delete(1, 1); // Remove "item2"

151

const snapshot2 = Y.snapshot(doc);

152

153

yarray.push(["item4"]);

154

const finalSnapshot = Y.snapshot(doc);

155

156

// Create documents from different snapshots

157

const docFromSnap1 = Y.createDocFromSnapshot(doc, snapshot1);

158

const docFromSnap2 = Y.createDocFromSnapshot(doc, snapshot2);

159

160

console.log("Snap1 array:", docFromSnap1.getArray("items").toArray()); // ["item1", "item2", "item3"]

161

console.log("Snap2 array:", docFromSnap2.getArray("items").toArray()); // ["item1", "item3"]

162

console.log("Current array:", doc.getArray("items").toArray()); // ["item1", "item3", "item4"]

163

164

// Create document with specific client ID

165

const newDoc = new Y.Doc({ clientID: 999 });

166

const restoredDoc = Y.createDocFromSnapshot(doc, snapshot1, newDoc);

167

console.log("Restored doc client ID:", restoredDoc.clientID); // 999

168

```

169

170

### Snapshot Serialization

171

172

Functions for encoding and decoding snapshots for storage or transmission.

173

174

```typescript { .api }

175

/**

176

* Encode snapshot to binary format (V1)

177

* @param snapshot - Snapshot to encode

178

* @returns Binary representation

179

*/

180

function encodeSnapshot(snapshot: Snapshot): Uint8Array;

181

182

/**

183

* Decode snapshot from binary format (V1)

184

* @param buf - Binary snapshot data

185

* @returns Decoded snapshot

186

*/

187

function decodeSnapshot(buf: Uint8Array): Snapshot;

188

189

/**

190

* Encode snapshot to binary format (V2, optimized)

191

* @param snapshot - Snapshot to encode

192

* @returns Binary representation in V2 format

193

*/

194

function encodeSnapshotV2(snapshot: Snapshot): Uint8Array;

195

196

/**

197

* Decode snapshot from binary format (V2)

198

* @param buf - Binary snapshot data in V2 format

199

* @returns Decoded snapshot

200

*/

201

function decodeSnapshotV2(buf: Uint8Array): Snapshot;

202

```

203

204

**Usage Examples:**

205

206

```typescript

207

import * as Y from "yjs";

208

209

const doc = new Y.Doc();

210

const yarray = doc.getArray("items");

211

yarray.push(["item1", "item2", "item3"]);

212

213

const snapshot = Y.snapshot(doc);

214

215

// Encode snapshot (V1)

216

const encodedV1 = Y.encodeSnapshot(snapshot);

217

console.log("V1 encoded size:", encodedV1.length);

218

219

// Encode snapshot (V2, typically smaller)

220

const encodedV2 = Y.encodeSnapshotV2(snapshot);

221

console.log("V2 encoded size:", encodedV2.length);

222

223

// Decode snapshots

224

const decodedV1 = Y.decodeSnapshot(encodedV1);

225

const decodedV2 = Y.decodeSnapshotV2(encodedV2);

226

227

// Verify equality

228

console.log("V1 decoded equals original:", Y.equalSnapshots(snapshot, decodedV1));

229

console.log("V2 decoded equals original:", Y.equalSnapshots(snapshot, decodedV2));

230

231

// Store/retrieve from storage

232

localStorage.setItem("document-snapshot", JSON.stringify(Array.from(encodedV2)));

233

const stored = new Uint8Array(JSON.parse(localStorage.getItem("document-snapshot")!));

234

const restoredSnapshot = Y.decodeSnapshotV2(stored);

235

```

236

237

### Snapshot-based Type Operations

238

239

Functions for getting type values at specific snapshot states.

240

241

```typescript { .api }

242

/**

243

* Convert YArray to JavaScript array at snapshot state

244

* @param type - YArray to convert

245

* @param snapshot - Snapshot state to use

246

* @returns Array content at snapshot time

247

*/

248

function typeListToArraySnapshot(type: YArray<any>, snapshot: Snapshot): Array<any>;

249

250

/**

251

* Get YMap value at snapshot state

252

* @param type - YMap to query

253

* @param key - Key to get value for

254

* @param snapshot - Snapshot state to use

255

* @returns Value at snapshot time or undefined

256

*/

257

function typeMapGetSnapshot(type: YMap<any>, key: string, snapshot: Snapshot): any;

258

259

/**

260

* Get all YMap entries at snapshot state

261

* @param type - YMap to query

262

* @param snapshot - Snapshot state to use

263

* @returns Object with all key-value pairs at snapshot time

264

*/

265

function typeMapGetAllSnapshot(type: YMap<any>, snapshot: Snapshot): { [key: string]: any };

266

```

267

268

**Usage Examples:**

269

270

```typescript

271

import * as Y from "yjs";

272

273

const doc = new Y.Doc();

274

const yarray = doc.getArray("items");

275

const ymap = doc.getMap("metadata");

276

277

// Build initial state

278

yarray.push(["item1", "item2"]);

279

ymap.set("count", 2);

280

ymap.set("created", "2023-01-01");

281

const snapshot1 = Y.snapshot(doc);

282

283

// Make changes

284

yarray.push(["item3"]);

285

ymap.set("count", 3);

286

ymap.set("modified", "2023-01-02");

287

ymap.delete("created");

288

const snapshot2 = Y.snapshot(doc);

289

290

// Query historical values

291

const arrayAtSnap1 = Y.typeListToArraySnapshot(yarray, snapshot1);

292

const arrayAtSnap2 = Y.typeListToArraySnapshot(yarray, snapshot2);

293

294

console.log("Array at snapshot1:", arrayAtSnap1); // ["item1", "item2"]

295

console.log("Array at snapshot2:", arrayAtSnap2); // ["item1", "item2", "item3"]

296

297

const countAtSnap1 = Y.typeMapGetSnapshot(ymap, "count", snapshot1);

298

const countAtSnap2 = Y.typeMapGetSnapshot(ymap, "count", snapshot2);

299

300

console.log("Count at snapshot1:", countAtSnap1); // 2

301

console.log("Count at snapshot2:", countAtSnap2); // 3

302

303

const allMetaAtSnap1 = Y.typeMapGetAllSnapshot(ymap, snapshot1);

304

const allMetaAtSnap2 = Y.typeMapGetAllSnapshot(ymap, snapshot2);

305

306

console.log("Metadata at snapshot1:", allMetaAtSnap1); // {count: 2, created: "2023-01-01"}

307

console.log("Metadata at snapshot2:", allMetaAtSnap2); // {count: 3, modified: "2023-01-02"}

308

```

309

310

### Advanced Snapshot Patterns

311

312

**Version Control System:**

313

314

```typescript

315

import * as Y from "yjs";

316

317

interface Version {

318

id: string;

319

timestamp: number;

320

message: string;

321

snapshot: Y.Snapshot;

322

}

323

324

class VersionControl {

325

private doc: Y.Doc;

326

private versions: Version[] = [];

327

328

constructor(doc: Y.Doc) {

329

this.doc = doc;

330

// Save initial version

331

this.saveVersion("Initial version");

332

}

333

334

saveVersion(message: string): string {

335

const versionId = `v${Date.now()}`;

336

const version: Version = {

337

id: versionId,

338

timestamp: Date.now(),

339

message,

340

snapshot: Y.snapshot(this.doc)

341

};

342

343

this.versions.push(version);

344

return versionId;

345

}

346

347

getVersions(): Version[] {

348

return [...this.versions];

349

}

350

351

restoreVersion(versionId: string): Y.Doc | null {

352

const version = this.versions.find(v => v.id === versionId);

353

if (!version) return null;

354

355

return Y.createDocFromSnapshot(this.doc, version.snapshot);

356

}

357

358

compareVersions(versionId1: string, versionId2: string): boolean {

359

const v1 = this.versions.find(v => v.id === versionId1);

360

const v2 = this.versions.find(v => v.id === versionId2);

361

362

if (!v1 || !v2) return false;

363

return Y.equalSnapshots(v1.snapshot, v2.snapshot);

364

}

365

}

366

367

// Usage

368

const doc = new Y.Doc();

369

const vc = new VersionControl(doc);

370

371

const yarray = doc.getArray("items");

372

yarray.push(["item1"]);

373

const v1 = vc.saveVersion("Added item1");

374

375

yarray.push(["item2"]);

376

const v2 = vc.saveVersion("Added item2");

377

378

// Restore to previous version

379

const restoredDoc = vc.restoreVersion(v1);

380

console.log("Restored array:", restoredDoc?.getArray("items").toArray()); // ["item1"]

381

```

382

383

**Branching System:**

384

385

```typescript

386

import * as Y from "yjs";

387

388

interface Branch {

389

name: string;

390

baseSnapshot: Y.Snapshot;

391

doc: Y.Doc;

392

}

393

394

class BranchManager {

395

private mainDoc: Y.Doc;

396

private branches: Map<string, Branch> = new Map();

397

398

constructor(doc: Y.Doc) {

399

this.mainDoc = doc;

400

}

401

402

createBranch(name: string, fromSnapshot?: Y.Snapshot): Y.Doc {

403

const baseSnapshot = fromSnapshot || Y.snapshot(this.mainDoc);

404

const branchDoc = Y.createDocFromSnapshot(this.mainDoc, baseSnapshot);

405

406

this.branches.set(name, {

407

name,

408

baseSnapshot,

409

doc: branchDoc

410

});

411

412

return branchDoc;

413

}

414

415

getBranch(name: string): Y.Doc | null {

416

return this.branches.get(name)?.doc || null;

417

}

418

419

mergeBranch(branchName: string): boolean {

420

const branch = this.branches.get(branchName);

421

if (!branch) return false;

422

423

// Get changes made in branch

424

const branchStateVector = Y.encodeStateVector(branch.baseSnapshot.sv);

425

const branchChanges = Y.encodeStateAsUpdate(branch.doc, branchStateVector);

426

427

// Apply to main document

428

Y.applyUpdate(this.mainDoc, branchChanges);

429

430

return true;

431

}

432

433

deleteBranch(name: string): boolean {

434

return this.branches.delete(name);

435

}

436

}

437

438

// Usage

439

const mainDoc = new Y.Doc();

440

const bm = new BranchManager(mainDoc);

441

442

// Work on main

443

mainDoc.getArray("items").push(["main-item"]);

444

445

// Create feature branch

446

const featureBranch = bm.createBranch("feature-xyz");

447

featureBranch.getArray("items").push(["feature-item"]);

448

449

// Merge back to main

450

bm.mergeBranch("feature-xyz");

451

452

console.log("Main after merge:", mainDoc.getArray("items").toArray());

453

// ["main-item", "feature-item"]

454

```

455

456

**Snapshot-based Diff:**

457

458

```typescript

459

import * as Y from "yjs";

460

461

function diffSnapshots(doc: Y.Doc, snap1: Y.Snapshot, snap2: Y.Snapshot): any {

462

const doc1 = Y.createDocFromSnapshot(doc, snap1);

463

const doc2 = Y.createDocFromSnapshot(doc, snap2);

464

465

const diff: any = {};

466

467

// Compare shared types

468

doc1.share.forEach((type1, name) => {

469

const type2 = doc2.share.get(name);

470

471

if (type1 instanceof Y.YArray && type2 instanceof Y.YArray) {

472

const arr1 = type1.toArray();

473

const arr2 = type2.toArray();

474

if (JSON.stringify(arr1) !== JSON.stringify(arr2)) {

475

diff[name] = { before: arr1, after: arr2 };

476

}

477

} else if (type1 instanceof Y.YMap && type2 instanceof Y.YMap) {

478

const obj1 = type1.toJSON();

479

const obj2 = type2.toJSON();

480

if (JSON.stringify(obj1) !== JSON.stringify(obj2)) {

481

diff[name] = { before: obj1, after: obj2 };

482

}

483

}

484

});

485

486

return diff;

487

}

488

489

// Usage

490

const doc = new Y.Doc();

491

const yarray = doc.getArray("items");

492

493

yarray.push(["item1", "item2"]);

494

const snap1 = Y.snapshot(doc);

495

496

yarray.delete(0, 1);

497

yarray.push(["item3"]);

498

const snap2 = Y.snapshot(doc);

499

500

const diff = diffSnapshots(doc, snap1, snap2);

501

console.log("Diff:", diff);

502

// { items: { before: ["item1", "item2"], after: ["item2", "item3"] } }

503

```