Conflict-free Replicated Data Type (CRDT) framework for real-time collaborative applications
npx @tessl/cli install tessl/npm-yjs@13.6.00
# Yjs
1
2
Yjs is a Conflict-free Replicated Data Type (CRDT) framework that enables real-time collaborative editing without conflicts. It provides shared data types that multiple users can edit simultaneously, with automatic synchronization and conflict resolution. Yjs is network-agnostic and supports peer-to-peer synchronization, offline editing, undo/redo functionality, and efficient binary serialization.
3
4
## Package Information
5
6
- **Package Name**: yjs
7
- **Package Type**: npm
8
- **Language**: JavaScript/TypeScript
9
- **Installation**: `npm install yjs`
10
11
## Core Imports
12
13
```typescript
14
import * as Y from "yjs";
15
```
16
17
For individual imports:
18
19
```typescript
20
import { Doc, Array as YArray, Map as YMap, Text as YText } from "yjs";
21
```
22
23
CommonJS:
24
25
```javascript
26
const Y = require("yjs");
27
const { Doc, Array: YArray, Map: YMap, Text: YText } = require("yjs");
28
```
29
30
## Basic Usage
31
32
```typescript
33
import * as Y from "yjs";
34
35
// Create a new document
36
const doc = new Y.Doc();
37
38
// Get shared types
39
const yarray = doc.getArray("my-array");
40
const ymap = doc.getMap("my-map");
41
const ytext = doc.getText("my-text");
42
43
// Use shared types
44
yarray.push(["hello", "world"]);
45
ymap.set("key", "value");
46
ytext.insert(0, "Hello collaborative world!");
47
48
// Observe changes
49
yarray.observe((event) => {
50
console.log("Array changed:", event.changes);
51
});
52
53
// Create updates for synchronization
54
const update = Y.encodeStateAsUpdate(doc);
55
56
// Apply updates from other clients
57
const otherDoc = new Y.Doc();
58
Y.applyUpdate(otherDoc, update);
59
```
60
61
## Architecture
62
63
Yjs is built around several key components:
64
65
- **Document (Doc)**: Central container managing shared types and synchronization state
66
- **Shared Data Types**: Observable CRDT types (YArray, YMap, YText, YXmlElement) for collaborative editing
67
- **Transaction System**: Atomic change batching for consistent updates and event handling
68
- **Position System**: Relative positions that survive concurrent edits and structural changes
69
- **Update System**: Binary serialization and merging for efficient network synchronization
70
- **Snapshot System**: Time-travel functionality for document state at specific points
71
- **Undo/Redo System**: Multi-level undo/redo with scope management and origin tracking
72
73
## Capabilities
74
75
### Document Management
76
77
Core document functionality for creating, managing, and synchronizing collaborative documents.
78
79
```typescript { .api }
80
class Doc {
81
constructor(options?: DocOpts);
82
readonly clientID: number;
83
readonly guid: string;
84
readonly isLoaded: boolean;
85
readonly isSynced: boolean;
86
readonly isDestroyed: boolean;
87
readonly whenLoaded: Promise<Doc>;
88
readonly whenSynced: Promise<void>;
89
readonly subdocs: Set<Doc>;
90
get<Type>(name: string, TypeConstructor?: Type): InstanceType<Type>;
91
getArray<T>(name?: string): YArray<T>;
92
getText(name?: string): YText;
93
getMap<T>(name?: string): YMap<T>;
94
getXmlElement(name?: string): YXmlElement;
95
getXmlFragment(name?: string): YXmlFragment;
96
transact<T>(f: (transaction: Transaction) => T, origin?: any): T;
97
load(): void;
98
getSubdocs(): Set<Doc>;
99
getSubdocGuids(): Set<string>;
100
destroy(): void;
101
toJSON(): any;
102
}
103
104
interface DocOpts {
105
clientID?: number;
106
guid?: string;
107
collectionid?: string | null;
108
gc?: boolean;
109
gcFilter?: (item: Item) => boolean;
110
meta?: any;
111
autoLoad?: boolean;
112
shouldLoad?: boolean;
113
}
114
```
115
116
[Document Management](./document-management.md)
117
118
### Shared Data Types
119
120
Observable CRDT types for different collaborative editing scenarios with automatic conflict resolution.
121
122
```typescript { .api }
123
class YArray<T> {
124
constructor();
125
static from<T>(items: Array<T>): YArray<T>;
126
readonly length: number;
127
insert(index: number, content: Array<T>): void;
128
push(content: Array<T>): void;
129
unshift(content: Array<T>): void;
130
delete(index: number, length?: number): void;
131
get(index: number): T;
132
toArray(): Array<T>;
133
}
134
135
class YMap<T> {
136
constructor(entries?: Iterable<readonly [string, any]>);
137
readonly size: number;
138
set<VAL>(key: string, value: VAL): VAL;
139
get(key: string): T | undefined;
140
has(key: string): boolean;
141
delete(key: string): void;
142
clear(): void;
143
}
144
145
class YText {
146
constructor(string?: string);
147
readonly length: number;
148
insert(index: number, text: string, attributes?: TextAttributes): void;
149
insertEmbed(index: number, embed: Object | AbstractType<any>, attributes?: TextAttributes): void;
150
delete(index: number, length: number): void;
151
format(index: number, length: number, attributes: TextAttributes): void;
152
removeAttribute(attributeName: string): void;
153
setAttribute(attributeName: string, attributeValue: any): void;
154
getAttribute(attributeName: string): any;
155
getAttributes(): {[key: string]: any};
156
toString(): string;
157
}
158
```
159
160
[Shared Data Types](./shared-data-types.md)
161
162
### XML Types
163
164
XML-aware collaborative types for structured document editing with DOM integration.
165
166
```typescript { .api }
167
class YXmlElement<KV = { [key: string]: any }> {
168
constructor(nodeName?: string);
169
readonly nodeName: string;
170
setAttribute<KEY>(attributeName: KEY, attributeValue: KV[KEY]): void;
171
getAttribute<KEY>(attributeName: KEY): KV[KEY] | undefined;
172
hasAttribute(attributeName: string): boolean;
173
toDOM(document?: Document): Element;
174
}
175
176
class YXmlFragment {
177
constructor();
178
readonly length: number;
179
insert(index: number, content: Array<YXmlElement | YXmlText>): void;
180
insertAfter(ref: null | Item | YXmlElement | YXmlText, content: Array<YXmlElement | YXmlText>): void;
181
querySelector(query: string): YXmlElement | YXmlText | YXmlHook | null;
182
querySelectorAll(query: string): Array<YXmlElement | YXmlText | YXmlHook>;
183
createTreeWalker(filter?: (node: AbstractType<any>) => boolean): YXmlTreeWalker;
184
}
185
```
186
187
[XML Types](./xml-types.md)
188
189
### Transaction System
190
191
Atomic change batching for consistent updates and coordinated event handling.
192
193
```typescript { .api }
194
class Transaction {
195
readonly doc: Doc;
196
readonly origin: any;
197
readonly local: boolean;
198
readonly beforeState: Map<number, number>;
199
readonly afterState: Map<number, number>;
200
}
201
202
function transact<T>(doc: Doc, f: (transaction: Transaction) => T, origin?: any): T;
203
```
204
205
[Transaction System](./transaction-system.md)
206
207
### Event System
208
209
Comprehensive event handling for observing changes to shared types with detailed change information.
210
211
```typescript { .api }
212
abstract class YEvent<T> {
213
readonly target: T;
214
readonly transaction: Transaction;
215
readonly path: Array<string | number>;
216
readonly changes: {
217
added: Set<Item>;
218
deleted: Set<Item>;
219
keys: Map<string, any>;
220
delta: Array<any>;
221
};
222
}
223
224
interface AbstractType<EventType> {
225
observe(f: (event: EventType, transaction: Transaction) => void): void;
226
unobserve(f: (event: EventType, transaction: Transaction) => void): void;
227
observeDeep(f: (events: Array<YEvent<any>>, transaction: Transaction) => void): void;
228
}
229
```
230
231
[Event System](./event-system.md)
232
233
### Position Tracking
234
235
Position system that maintains references across concurrent edits and structural changes.
236
237
```typescript { .api }
238
class RelativePosition {
239
readonly type: ID | null;
240
readonly tname: string | null;
241
readonly item: ID | null;
242
readonly assoc: number;
243
}
244
245
class AbsolutePosition {
246
readonly type: AbstractType<any>;
247
readonly index: number;
248
readonly assoc: number;
249
}
250
251
function createRelativePositionFromTypeIndex(
252
type: AbstractType<any>,
253
index: number,
254
assoc?: number
255
): RelativePosition;
256
257
function createAbsolutePositionFromRelativePosition(
258
rpos: RelativePosition,
259
doc: Doc
260
): AbsolutePosition | null;
261
262
function createRelativePositionFromJSON(json: any): RelativePosition;
263
function encodeRelativePosition(rpos: RelativePosition): Uint8Array;
264
function decodeRelativePosition(encoded: Uint8Array): RelativePosition;
265
function relativePositionToJSON(rpos: RelativePosition): any;
266
function compareRelativePositions(a: RelativePosition, b: RelativePosition): number;
267
```
268
269
[Position Tracking](./position-tracking.md)
270
271
### Synchronization
272
273
Binary update system for efficient network synchronization and state management.
274
275
```typescript { .api }
276
function encodeStateAsUpdate(doc: Doc, encodedTargetStateVector?: Uint8Array): Uint8Array;
277
function encodeStateAsUpdateV2(doc: Doc, encodedTargetStateVector?: Uint8Array): Uint8Array;
278
function applyUpdate(doc: Doc, update: Uint8Array, transactionOrigin?: any): void;
279
function applyUpdateV2(doc: Doc, update: Uint8Array, transactionOrigin?: any): void;
280
function mergeUpdates(updates: Array<Uint8Array>): Uint8Array;
281
function mergeUpdatesV2(updates: Array<Uint8Array>): Uint8Array;
282
function readUpdate(decoder: UpdateDecoder, doc: Doc, transactionOrigin?: any): void;
283
function readUpdateV2(decoder: UpdateDecoder, doc: Doc, transactionOrigin?: any): void;
284
function encodeStateVector(doc: Doc): Uint8Array;
285
function decodeStateVector(stateVector: Uint8Array): Map<number, number>;
286
function parseUpdateMeta(update: Uint8Array): { from: Map<number, number>; to: Map<number, number> };
287
function parseUpdateMetaV2(update: Uint8Array): { from: Map<number, number>; to: Map<number, number> };
288
function encodeStateVectorFromUpdate(update: Uint8Array): Uint8Array;
289
function encodeStateVectorFromUpdateV2(update: Uint8Array): Uint8Array;
290
function diffUpdate(update: Uint8Array, stateVector: Uint8Array): Uint8Array;
291
function diffUpdateV2(update: Uint8Array, stateVector: Uint8Array): Uint8Array;
292
function convertUpdateFormatV1ToV2(update: Uint8Array): Uint8Array;
293
function convertUpdateFormatV2ToV1(update: Uint8Array): Uint8Array;
294
function logUpdate(update: Uint8Array): void;
295
function logUpdateV2(update: Uint8Array): void;
296
```
297
298
[Synchronization](./synchronization.md)
299
300
### Snapshot System
301
302
Time-travel functionality for document state at specific points with diff capabilities.
303
304
```typescript { .api }
305
class Snapshot {
306
readonly ds: DeleteSet;
307
readonly sv: Map<number, number>;
308
}
309
310
function snapshot(doc: Doc): Snapshot;
311
function createSnapshot(doc: Doc): Snapshot;
312
function createDocFromSnapshot(originDoc: Doc, snapshot: Snapshot, newDoc?: Doc): Doc;
313
function equalSnapshots(snap1: Snapshot, snap2: Snapshot): boolean;
314
function encodeSnapshot(snapshot: Snapshot): Uint8Array;
315
function decodeSnapshot(encoded: Uint8Array): Snapshot;
316
function encodeSnapshotV2(snapshot: Snapshot): Uint8Array;
317
function decodeSnapshotV2(encoded: Uint8Array): Snapshot;
318
function snapshotContainsUpdate(snapshot: Snapshot, update: Uint8Array): boolean;
319
```
320
321
[Snapshot System](./snapshot-system.md)
322
323
### Undo/Redo System
324
325
Multi-level undo/redo functionality with scope management and origin tracking.
326
327
```typescript { .api }
328
class UndoManager {
329
constructor(
330
typeScope: Doc | AbstractType<any> | Array<AbstractType<any>>,
331
options?: UndoManagerOptions
332
);
333
readonly undoStack: Array<StackItem>;
334
readonly redoStack: Array<StackItem>;
335
undo(): StackItem | null;
336
redo(): StackItem | null;
337
canUndo(): boolean;
338
canRedo(): boolean;
339
clear(clearUndoStack?: boolean, clearRedoStack?: boolean): void;
340
}
341
342
interface UndoManagerOptions {
343
captureTimeout?: number;
344
captureTransaction?: (transaction: Transaction) => boolean;
345
deleteFilter?: (item: Item) => boolean;
346
trackedOrigins?: Set<any>;
347
}
348
```
349
350
[Undo/Redo System](./undo-redo-system.md)
351
352
### Utility Functions
353
354
Essential utility functions for working with Yjs internals and advanced operations.
355
356
```typescript { .api }
357
// Item and structure utilities
358
function getItem(store: StructStore, id: ID): Item;
359
function getItemCleanStart(transaction: Transaction, id: ID): Item;
360
function getItemCleanEnd(transaction: Transaction, id: ID): Item;
361
function isDeleted(deleteSet: DeleteSet, id: ID): boolean;
362
function isParentOf(parent: AbstractType<any>, child: Item): boolean;
363
364
// ID utilities
365
function createID(client: number, clock: number): ID;
366
function compareIDs(a: ID | null, b: ID | null): boolean;
367
function getState(store: StructStore, client: number): number;
368
369
// Delete set utilities
370
function createDeleteSet(): DeleteSet;
371
function createDeleteSetFromStructStore(store: StructStore): DeleteSet;
372
function equalDeleteSets(ds1: DeleteSet, ds2: DeleteSet): boolean;
373
function mergeDeleteSets(deleteSets: Array<DeleteSet>): DeleteSet;
374
375
// Advanced operations
376
function transact<T>(doc: Doc, f: (transaction: Transaction) => T, origin?: any): T;
377
function tryGc(deleteSet: DeleteSet, store: StructStore, gcFilter: Function): void;
378
function cleanupYTextFormatting(type: YText): number;
379
function findRootTypeKey(type: AbstractType<any>): string;
380
function getTypeChildren(type: AbstractType<any>): Array<AbstractType<any>>;
381
```
382
383
## Types
384
385
```typescript { .api }
386
interface ID {
387
readonly client: number;
388
readonly clock: number;
389
}
390
391
interface DeleteSet {
392
readonly clients: Map<number, Array<DeleteItem>>;
393
}
394
395
interface TextAttributes {
396
[key: string]: any;
397
}
398
399
interface UpdateDecoder {
400
readUint8(): number;
401
readUint32(): number;
402
readVarUint(): number;
403
readVarString(): string;
404
}
405
406
interface UpdateEncoder {
407
writeUint8(value: number): void;
408
writeUint32(value: number): void;
409
writeVarUint(value: number): void;
410
writeVarString(value: string): void;
411
toUint8Array(): Uint8Array;
412
}
413
414
interface DeleteItem {
415
clock: number;
416
len: number;
417
}
418
419
interface StructStore {
420
clients: Map<number, Array<AbstractStruct>>;
421
pendingStructs: null | { missing: Map<number, number>; update: Uint8Array };
422
}
423
424
interface Item {
425
readonly id: ID;
426
readonly left: Item | null;
427
readonly right: Item | null;
428
readonly origin: ID | null;
429
readonly rightOrigin: ID | null;
430
readonly parent: AbstractType<any> | ID | null;
431
readonly parentSub: string | null;
432
readonly content: AbstractContent;
433
}
434
435
// Abstract base classes
436
abstract class AbstractType<EventType> {
437
observe(f: (event: EventType, transaction: Transaction) => void): void;
438
unobserve(f: (event: EventType, transaction: Transaction) => void): void;
439
observeDeep(f: (events: Array<YEvent<any>>, transaction: Transaction) => void): void;
440
clone(): AbstractType<EventType>;
441
toJSON(): any;
442
}
443
444
abstract class AbstractStruct {
445
readonly id: ID;
446
readonly length: number;
447
}
448
449
class YXmlTreeWalker {
450
filter: (node: AbstractType<any>) => boolean;
451
nextNode(): AbstractType<any> | null;
452
}
453
```