@open-ot/core
v0.3.0
Published
The foundational package for OpenOT, providing the core interfaces and built-in OT types.
Readme
@open-ot/core
The foundational package for OpenOT, providing the core interfaces and built-in OT types.
Overview
@open-ot/core defines the OTType interface, which is the contract for implementing Operational Transformation on any data structure. It also includes production-ready implementations for Text and JSON types.
Installation
npm install @open-ot/coreWhat's Included
Core Interfaces
OTType<Snapshot, Op>
The fundamental interface that defines how to synchronize a data structure:
interface OTType<Snapshot, Op> {
name: string;
create(): Snapshot;
apply(snapshot: Snapshot, op: Op): Snapshot;
transform(opA: Op, opB: Op, side: 'left' | 'right'): Op;
compose(opA: Op, opB: Op): Op;
invert?(op: Op): Op; // Optional
}name: Unique identifier for the type (e.g.,"text","json").create(): Returns the initial empty state.apply(snapshot, op): Applies an operation to a snapshot, producing a new snapshot.transform(opA, opB, side): TransformsopAto apply afteropB. Thesideparameter handles tie-breaking for concurrent inserts.compose(opA, opB): Merges two consecutive operations into a single efficient operation.invert(op): (Optional) Generates an operation that undoes the given operation.
TransportAdapter<TMessage>
Defines the interface for network communication:
interface TransportAdapter<TMessage = unknown> {
connect(onReceive: (msg: TMessage) => void): Promise<void>;
send(msg: TMessage): Promise<void>;
disconnect(): Promise<void>;
}Built-in Types
TextType
A production-ready OT implementation for plain text using Retain/Insert/Delete operations.
Operation Format:
type TextOperation = Array<
| { r: number } // Retain n characters
| { i: string } // Insert string
| { d: number } // Delete n characters
>;Example:
import { TextType } from '@open-ot/core';
const snapshot = "Hello World";
const op = [
{ r: 6 }, // Retain "Hello "
{ i: "Beautiful " }, // Insert "Beautiful "
{ r: 5 } // Retain "World"
];
const result = TextType.apply(snapshot, op);
// => "Hello Beautiful World"Features:
- Efficient composition and transformation
- Handles concurrent edits with tie-breaking
- Normalizes operations to minimize size
- Validates operations for correctness
JsonType
A wrapper around the battle-tested ot-json1 library for collaborative JSON editing.
Example:
import { JsonType } from '@open-ot/core';
const snapshot = { users: ["Alice"] };
const op = [
{ p: ["users", 1], li: "Bob" } // Insert "Bob" at index 1
];
const result = JsonType.apply(snapshot, op);
// => { users: ["Alice", "Bob"] }Advanced: Custom JSON Type with Hooks
You can create a custom JSON type with semantic validation:
import { createJsonType } from '@open-ot/core';
const CustomJsonType = createJsonType({
validate: (op, doc) => {
// Reject operations that violate your schema
return isValidSchema(doc);
},
normalize: (doc) => {
// Post-process the document after applying an operation
return sortKeys(doc);
}
});Creating Your Own Type
To synchronize a custom data structure, implement the OTType interface:
import { OTType } from '@open-ot/core';
interface MySnapshot {
// Your data structure
}
type MyOp = {
// Your operation format
};
export const MyCustomType: OTType<MySnapshot, MyOp> = {
name: 'my-custom-type',
create() {
return { /* initial state */ };
},
apply(snapshot, op) {
// Apply the operation to the snapshot
return newSnapshot;
},
transform(opA, opB, side) {
// Transform opA to apply after opB
return transformedOpA;
},
compose(opA, opB) {
// Merge two consecutive operations
return composedOp;
}
};Use Cases
- Text Editors: Use
TextTypefor collaborative plain text editing. - Rich Text Editors: Serialize ProseMirror/Lexical state to JSON and use
JsonType. - Configuration Tools: Sync JSON configuration files in real-time.
- Custom Data Structures: Implement your own
OTTypefor domain-specific collaboration (e.g., whiteboards, spreadsheets).
API Reference
TextType
TextType.create()→""TextType.apply(snapshot, op)→stringTextType.transform(opA, opB, side)→TextOperationTextType.compose(opA, opB)→TextOperation
Helpers:
isInsert(op),isRetain(op),isDelete(op)— Type guardsgetLength(op)— Get the length of an operation componentnormalize(op)— Merge consecutive operations of the same typecheckOp(op)— Validate operation structure
JsonType
JsonType.create()→nullJsonType.apply(snapshot, op)→json1.DocJsonType.transform(opA, opB, side)→JsonOpJsonType.compose(opA, opB)→JsonOp
For detailed JSON operation syntax, see the ot-json1 documentation.
License
MIT
