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

event-system.mddocs/

0

# Event System

1

2

Comprehensive event handling for observing changes to shared types with detailed change information. The event system provides fine-grained notifications about all modifications to shared data structures.

3

4

## Capabilities

5

6

### Base YEvent Class

7

8

Foundation class for all Yjs events providing common change information.

9

10

```typescript { .api }

11

/**

12

* Base event class for all Yjs type changes

13

*/

14

abstract class YEvent<T> {

15

/** The type that was changed */

16

readonly target: T;

17

18

/** Current event target (may differ from target in event bubbling) */

19

readonly currentTarget: AbstractType<any>;

20

21

/** Transaction that triggered this event */

22

readonly transaction: Transaction;

23

24

/** Path from document root to the changed type */

25

readonly path: Array<string | number>;

26

27

/** Detailed information about all changes in this event */

28

readonly changes: {

29

added: Set<Item>;

30

deleted: Set<Item>;

31

keys: Map<string, { action: 'add' | 'update' | 'delete'; oldValue: any }>;

32

delta: Array<any>;

33

};

34

35

/** Changes in Quill Delta format */

36

readonly delta: Array<any>;

37

38

/** Map of changed keys with their change types and old values */

39

readonly keys: Map<string, { action: 'add' | 'update' | 'delete'; oldValue: any }>;

40

}

41

```

42

43

**Event Methods:**

44

45

```typescript { .api }

46

/**

47

* Check if a struct was added in this event

48

* @param struct - Struct to check

49

* @returns True if struct was added

50

*/

51

adds(struct: AbstractStruct): boolean;

52

53

/**

54

* Check if a struct was deleted in this event

55

* @param struct - Struct to check

56

* @returns True if struct was deleted

57

*/

58

deletes(struct: AbstractStruct): boolean;

59

```

60

61

### AbstractType Observer Methods

62

63

All shared types inherit observer methods for event handling.

64

65

```typescript { .api }

66

/**

67

* Observer methods available on all AbstractType instances

68

*/

69

interface AbstractType<EventType> {

70

/**

71

* Add an observer for direct changes to this type

72

* @param f - Observer function to call on changes

73

*/

74

observe(f: (event: EventType, transaction: Transaction) => void): void;

75

76

/**

77

* Remove an observer for direct changes

78

* @param f - Observer function to remove

79

*/

80

unobserve(f: (event: EventType, transaction: Transaction) => void): void;

81

82

/**

83

* Add an observer for changes to this type or any nested types

84

* @param f - Observer function to call on deep changes

85

*/

86

observeDeep(f: (events: Array<YEvent<any>>, transaction: Transaction) => void): void;

87

88

/**

89

* Remove a deep observer

90

* @param f - Deep observer function to remove

91

*/

92

unobserveDeep(f: (events: Array<YEvent<any>>, transaction: Transaction) => void): void;

93

}

94

```

95

96

### YArrayEvent

97

98

Array-specific event providing detailed information about array changes.

99

100

```typescript { .api }

101

/**

102

* Event fired when YArray changes

103

*/

104

class YArrayEvent extends YEvent<YArray<any>> {

105

/** The YArray that changed */

106

readonly target: YArray<any>;

107

108

/** Changes in Delta format showing insertions and deletions */

109

readonly changes: {

110

added: Set<Item>;

111

deleted: Set<Item>;

112

delta: Array<{ retain?: number; insert?: any[]; delete?: number }>;

113

};

114

}

115

```

116

117

**Usage Examples:**

118

119

```typescript

120

import * as Y from "yjs";

121

122

const doc = new Y.Doc();

123

const yarray = doc.getArray<string>("fruits");

124

125

// Observe array changes

126

yarray.observe((event, transaction) => {

127

console.log("Array changed by:", transaction.origin);

128

129

// Process delta changes

130

event.changes.delta.forEach((change) => {

131

if (change.insert) {

132

console.log("Inserted:", change.insert);

133

}

134

if (change.delete) {

135

console.log("Deleted:", change.delete, "items");

136

}

137

if (change.retain) {

138

console.log("Retained:", change.retain, "items");

139

}

140

});

141

142

// Check specific items

143

event.changes.added.forEach((item) => {

144

console.log("Added item at index:", item.index);

145

});

146

});

147

148

// Trigger changes

149

yarray.push(["apple", "banana"]);

150

yarray.delete(0, 1);

151

```

152

153

### YMapEvent

154

155

Map-specific event providing information about key-value changes.

156

157

```typescript { .api }

158

/**

159

* Event fired when YMap changes

160

*/

161

class YMapEvent extends YEvent<YMap<any>> {

162

/** The YMap that changed */

163

readonly target: YMap<any>;

164

165

/** Set of keys that changed in this event */

166

readonly keysChanged: Set<string>;

167

168

/** Detailed key changes with actions and old values */

169

readonly keys: Map<string, { action: 'add' | 'update' | 'delete'; oldValue: any }>;

170

}

171

```

172

173

**Usage Examples:**

174

175

```typescript

176

import * as Y from "yjs";

177

178

const doc = new Y.Doc();

179

const ymap = doc.getMap<any>("settings");

180

181

// Observe map changes

182

ymap.observe((event, transaction) => {

183

console.log("Map changed, keys affected:", Array.from(event.keysChanged));

184

185

// Process each changed key

186

event.keys.forEach((change, key) => {

187

switch (change.action) {

188

case 'add':

189

console.log(`Added key "${key}" with value:`, ymap.get(key));

190

break;

191

case 'update':

192

console.log(`Updated key "${key}" from:`, change.oldValue, "to:", ymap.get(key));

193

break;

194

case 'delete':

195

console.log(`Deleted key "${key}" with old value:`, change.oldValue);

196

break;

197

}

198

});

199

});

200

201

// Trigger changes

202

ymap.set("theme", "dark");

203

ymap.set("theme", "light"); // Update

204

ymap.delete("theme"); // Delete

205

```

206

207

### YTextEvent

208

209

Text-specific event providing information about text and formatting changes.

210

211

```typescript { .api }

212

/**

213

* Event fired when YText changes

214

*/

215

class YTextEvent extends YEvent<YText> {

216

/** The YText that changed */

217

readonly target: YText;

218

219

/** Whether child list (content) changed */

220

readonly childListChanged: boolean;

221

222

/** Set of attribute keys that changed */

223

readonly keysChanged: Set<string>;

224

225

/** Changes in Delta format for text operations */

226

readonly changes: {

227

added: Set<Item>;

228

deleted: Set<Item>;

229

keys: Map<string, { action: 'add' | 'update' | 'delete'; oldValue: any }>;

230

delta: Array<any>;

231

};

232

}

233

```

234

235

**Usage Examples:**

236

237

```typescript

238

import * as Y from "yjs";

239

240

const doc = new Y.Doc();

241

const ytext = doc.getText("document");

242

243

// Observe text changes

244

ytext.observe((event, transaction) => {

245

if (event.childListChanged) {

246

console.log("Text content changed");

247

248

// Process delta for text changes

249

event.changes.delta.forEach((op) => {

250

if (op.retain) {

251

console.log(`Retained ${op.retain} characters`);

252

}

253

if (op.insert) {

254

console.log(`Inserted: "${op.insert}"`);

255

if (op.attributes) {

256

console.log("With attributes:", op.attributes);

257

}

258

}

259

if (op.delete) {

260

console.log(`Deleted ${op.delete} characters`);

261

}

262

});

263

}

264

265

if (event.keysChanged.size > 0) {

266

console.log("Text attributes changed:", Array.from(event.keysChanged));

267

}

268

});

269

270

// Trigger changes

271

ytext.insert(0, "Hello");

272

ytext.insert(5, " World", { bold: true });

273

ytext.format(0, 5, { italic: true });

274

```

275

276

### YXmlEvent

277

278

XML-specific event providing information about XML structure and attribute changes.

279

280

```typescript { .api }

281

/**

282

* Event fired when XML types change

283

*/

284

class YXmlEvent extends YEvent<YXmlElement | YXmlText | YXmlFragment> {

285

/** The XML type that changed */

286

readonly target: YXmlElement | YXmlText | YXmlFragment;

287

288

/** Whether child elements changed */

289

readonly childListChanged: boolean;

290

291

/** Set of attributes that changed (for YXmlElement) */

292

readonly attributesChanged: Set<string>;

293

}

294

```

295

296

**Usage Examples:**

297

298

```typescript

299

import * as Y from "yjs";

300

301

const doc = new Y.Doc();

302

const xmlFragment = doc.getXmlFragment("document");

303

304

// Observe XML changes

305

xmlFragment.observeDeep((events, transaction) => {

306

events.forEach((event) => {

307

if (event instanceof Y.YXmlEvent) {

308

console.log("XML changed:", event.target.constructor.name);

309

310

if (event.childListChanged) {

311

console.log("Child elements changed");

312

}

313

314

if (event.attributesChanged.size > 0) {

315

console.log("Attributes changed:", Array.from(event.attributesChanged));

316

}

317

}

318

});

319

});

320

321

// Create XML structure

322

const element = new Y.XmlElement("div");

323

element.setAttribute("class", "container");

324

xmlFragment.push([element]);

325

```

326

327

### Deep Observation

328

329

Deep observers receive events from nested types and provide comprehensive change tracking.

330

331

```typescript { .api }

332

/**

333

* Deep observer function signature

334

*/

335

type DeepObserver = (events: Array<YEvent<any>>, transaction: Transaction) => void;

336

```

337

338

**Usage Examples:**

339

340

```typescript

341

import * as Y from "yjs";

342

343

const doc = new Y.Doc();

344

const rootMap = doc.getMap("root");

345

346

// Set up nested structure

347

const nestedArray = new Y.Array();

348

const nestedMap = new Y.Map();

349

rootMap.set("array", nestedArray);

350

rootMap.set("map", nestedMap);

351

352

// Deep observer catches all nested changes

353

rootMap.observeDeep((events, transaction) => {

354

console.log(`Received ${events.length} events in transaction`);

355

356

events.forEach((event, index) => {

357

console.log(`Event ${index}:`);

358

console.log(" Type:", event.target.constructor.name);

359

console.log(" Path:", event.path);

360

361

if (event instanceof Y.YArrayEvent) {

362

console.log(" Array delta:", event.changes.delta);

363

} else if (event instanceof Y.YMapEvent) {

364

console.log(" Map keys changed:", Array.from(event.keysChanged));

365

}

366

});

367

});

368

369

// Changes to nested types trigger deep observer

370

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

371

nestedMap.set("key", "value");

372

```

373

374

### Event Performance Patterns

375

376

**Efficient Event Handling:**

377

378

```typescript

379

import * as Y from "yjs";

380

381

const doc = new Y.Doc();

382

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

383

384

// Efficient: Process changes in batches

385

yarray.observe((event) => {

386

// Single event handler processes all changes at once

387

const insertedItems = [];

388

const deletedCount = event.changes.delta.reduce((count, op) => {

389

if (op.insert) insertedItems.push(...op.insert);

390

if (op.delete) count += op.delete;

391

return count;

392

}, 0);

393

394

console.log(`Batch: +${insertedItems.length}, -${deletedCount}`);

395

});

396

397

// Multiple operations in single transaction = single event

398

doc.transact(() => {

399

yarray.push(["a", "b", "c"]);

400

yarray.delete(0, 1);

401

yarray.push(["d"]);

402

});

403

```

404

405

**Event Filtering:**

406

407

```typescript

408

import * as Y from "yjs";

409

410

const doc = new Y.Doc();

411

const ytext = doc.getText("document");

412

413

// Filter events by origin

414

ytext.observe((event, transaction) => {

415

// Only process user-initiated changes

416

if (transaction.origin === "user-input") {

417

console.log("Processing user input:", event.changes.delta);

418

}

419

});

420

421

// Filter by change type

422

ytext.observe((event) => {

423

const hasTextChanges = event.changes.delta.some(op => op.insert || op.delete);

424

const hasFormatChanges = event.changes.delta.some(op => op.attributes);

425

426

if (hasTextChanges) {

427

console.log("Text content changed");

428

}

429

if (hasFormatChanges) {

430

console.log("Formatting changed");

431

}

432

});

433

```