Operational transformation engine
https://ot-engine-rich-text.herokuapp.com/
- typescript full stack
- support local undo/redo
- support presence(cursors)
// @public (undocumented)
export type AgentInfo = {
custom?: any;
docId: string;
collection: string;
};
// @public (undocumented)
export type AgentInfoKeys = keyof AgentInfo;
// @public (undocumented)
export function applyAndInvert<S, P, Pr, I extends boolean>(snapshot: S, op: P, invert: I, otType: OTType<S, P, Pr>): I extends true ? [S, P] : [S, undefined];
// @public (undocumented)
export function assertNever(_: never): void;
// @public (undocumented)
export interface BaseRequest {
// (undocumented)
seq: number;
}
// @public (undocumented)
export interface BaseResponse {
// (undocumented)
error?: any;
// (undocumented)
seq: number;
}
// @public (undocumented)
export type ClientRequest<P, Pr> = RollbackRequest | PresencesRequest | DeleteDocRequest | PresenceRequest<Pr> | GetSnapshotRequest | GetOpsRequest | CommitOpRequest<P>;
// @public (undocumented)
export type ClientResponse<S, P, Pr> = NotifyResponse<P, Pr> | RollbackResponse | DeleteDocResponse | CommitOpResponse<P> | GetOpsResponse<P> | GetSnapshotResponse<S, P, Pr>;
// @public (undocumented)
export interface CommitOpParams<P> extends AgentInfo {
// (undocumented)
op: Op<P>;
}
// @public (undocumented)
export interface CommitOpRequest<P> extends Omit<CommitOpParams<P>, AgentInfoKeys>, BaseRequest {
// (undocumented)
type: 'commitOp';
}
// @public (undocumented)
export interface CommitOpResponse<P> extends BaseResponse {
// (undocumented)
ops?: Op<P>[];
// (undocumented)
type: 'commitOp';
}
// @public (undocumented)
export type DeleteDocNotification = Omit<DeleteDocResponse, 'seq'>;
// @public (undocumented)
export type DeleteDocParams = AgentInfo;
// @public (undocumented)
export interface DeleteDocRequest extends Omit<DeleteDocParams, AgentInfoKeys>, BaseRequest {
// (undocumented)
type: 'deleteDoc';
}
// @public (undocumented)
export interface DeleteDocResponse extends BaseResponse {
// (undocumented)
type: 'deleteDoc';
}
// @public (undocumented)
export interface GetOpsParams extends AgentInfo {
// (undocumented)
fromVersion: number;
// (undocumented)
toVersion?: number;
}
// @public (undocumented)
export interface GetOpsRequest extends Omit<GetOpsParams, AgentInfoKeys>, BaseRequest {
// (undocumented)
type: 'getOps';
}
// @public (undocumented)
export interface GetOpsResponse<P> extends BaseResponse {
// (undocumented)
ops?: Op<P>[];
// (undocumented)
type: 'getOps';
}
// @public (undocumented)
export interface GetSnapshotParams extends AgentInfo {
// (undocumented)
toVersion?: number;
// (undocumented)
version?: number;
}
// @public (undocumented)
export interface GetSnapshotRequest extends Omit<GetSnapshotParams, AgentInfoKeys>, BaseRequest {
// (undocumented)
type: 'getSnapshot';
}
// @public (undocumented)
export interface GetSnapshotResponse<S, P, Pr> extends BaseResponse {
// (undocumented)
presences?: Record<string, Presence<Pr>>;
// (undocumented)
snapshotAndOps?: SnapshotAndOps<S, P>;
// (undocumented)
type: 'getSnapshot';
}
// @public (undocumented)
export function isSameOp<P>(op: Op<P> | undefined, other: Op<P> | undefined): boolean;
// @public (undocumented)
export function last<T>(arr: T[], index?: number): T;
// @public (undocumented)
export interface Logger {
// (undocumented)
log: (...msg: any) => void;
}
// @public (undocumented)
export function noop(): void;
// @public (undocumented)
export type NotifyResponse<P, Pr> = RemoteOpResponse<P> | PresenceResponse<Pr> | PresencesResponse<Pr> | DeleteDocNotification | RollbackNotification;
// @public (undocumented)
export interface Op<P> {
// (undocumented)
clientId: string;
// (undocumented)
content: P;
// (undocumented)
id: string;
// (undocumented)
version: number;
}
// @public (undocumented)
export class OTError extends Error {
constructor(info: {
subType: OTErrorSubType;
detail: any;
});
// (undocumented)
info: {
subType: OTErrorSubType;
detail: any;
type: OTErrorType;
};
// (undocumented)
type: OTErrorType;
}
// @public (undocumented)
export type OTErrorSubType = 'deleted' | 'rollback';
// @public (undocumented)
export type OTErrorType = 'otError';
// @public (undocumented)
export type OTSide = 'left' | 'right';
// @public (undocumented)
export type OTType<S, P, Pr> = {
name: string;
create?(data: any): S;
applyAndInvert?<I extends boolean>(snapshot: S, op: P, invert: I): I extends true ? [S, P] : [S, undefined];
apply?(snapshot: S, op: P): S;
invert?(op: P): P;
invertWithDoc?(op: P, snapshot: S): P;
compose?(op: P, prevOp: P): P | undefined;
transform(op: P, refOp: P, side: OTSide): P;
transformPresence?(presence: Pr, refOp: P, isOwnOp: boolean): Pr;
serialize?(s: S): any;
deserialize?(data: any): S;
};
// @public (undocumented)
export interface Presence<P> {
// (undocumented)
content?: P;
// (undocumented)
version: number;
}
// @public (undocumented)
export interface PresenceRequest<Pr> {
// (undocumented)
clientId: string;
// (undocumented)
presence: Presence<Pr>;
// (undocumented)
type: 'presence';
}
// @public (undocumented)
export type PresenceResponse<Pr> = PresenceRequest<Pr>;
// @public (undocumented)
export interface PresencesRequest {
// (undocumented)
type: 'presences';
}
// @public (undocumented)
export interface PresencesResponse<Pr> {
// (undocumented)
presences: Record<string, Presence<Pr>>;
// (undocumented)
type: 'presences';
}
// @public (undocumented)
export interface RemoteOpResponse<P> {
// (undocumented)
clientId: string;
// (undocumented)
ops: Op<P>[];
// (undocumented)
type: 'remoteOp';
}
// @public (undocumented)
export type RollbackNotification = Omit<RollbackResponse, 'seq'>;
// @public (undocumented)
export interface RollbackParams extends AgentInfo {
// (undocumented)
version: number;
}
// @public (undocumented)
export interface RollbackRequest extends Omit<RollbackParams, AgentInfoKeys>, BaseRequest {
// (undocumented)
type: 'rollback';
}
// @public (undocumented)
export interface RollbackResponse extends BaseResponse {
// (undocumented)
type: 'rollback';
}
// @public (undocumented)
export interface SaveSnapshotParams<S> extends AgentInfo {
// (undocumented)
snapshot: Snapshot<S>;
}
// @public (undocumented)
export interface Snapshot<P> {
// (undocumented)
content: P;
// (undocumented)
rollback?: boolean;
// (undocumented)
version: number;
}
// @public (undocumented)
export type SnapshotAndOps<S, P> = {
snapshot: Snapshot<S>;
ops: Op<P>[];
};
// @public (undocumented)
export function transformPresence<S, P, Pr>(presenceClientId: string, presence_: Pr, refOps: P[], clientIds: string[], otType: OTType<S, P, Pr>): Pr;
// @public (undocumented)
export function transformType<S, P, Pr>(op: P[], refOps: P[], otType: OTType<S, P, Pr>): [any[], any[]];
// (No @packageDocumentation comment for this package)
/// <reference types="node" />
import { ClientRequest } from 'ot-engine-common';
import { ClientResponse } from 'ot-engine-common';
import type { CommitOpParams } from 'ot-engine-common';
import { CommitOpRequest } from 'ot-engine-common';
import type { DeleteDocParams } from 'ot-engine-common';
import { DeleteDocRequest } from 'ot-engine-common';
import type { Duplex } from 'stream';
import type { GetOpsParams } from 'ot-engine-common';
import { GetOpsRequest } from 'ot-engine-common';
import type { GetSnapshotParams } from 'ot-engine-common';
import { GetSnapshotRequest } from 'ot-engine-common';
import { Logger } from 'ot-engine-common';
import type { NotifyResponse } from 'ot-engine-common';
import { Op } from 'ot-engine-common';
import { OTType } from 'ot-engine-common';
import { Presence } from 'ot-engine-common';
import { PresenceRequest } from 'ot-engine-common';
import { RemoteOpResponse } from 'ot-engine-common';
import { RollbackRequest } from 'ot-engine-common';
import type { SaveSnapshotParams } from 'ot-engine-common';
import type { SnapshotAndOps } from 'ot-engine-common';
// @public (undocumented)
export class Agent<S, P, Pr, Custom> {
constructor(server: Server, config: AgentConfig<S, P, Pr, Custom>);
// (undocumented)
get agentInfo(): {
collection: string;
docId: string;
custom: Custom | undefined;
};
// (undocumented)
checkAndSaveSnapshot(op: Op<P>): Promise<void>;
// (undocumented)
clean: () => void;
// (undocumented)
get clientId(): string;
// (undocumented)
close: () => void;
// (undocumented)
closed: boolean;
// (undocumented)
get collection(): string;
// (undocumented)
get custom(): Custom | undefined;
// (undocumented)
get docId(): string;
// (undocumented)
getSnapshotByVersion(opVersion: number): Promise<{
content: unknown;
version: number;
rollback: boolean | undefined;
} | undefined>;
// (undocumented)
handleCommitOpRequest(request: CommitOpRequest<P>): Promise<void>;
// (undocumented)
handleDeleteDocMessage(request: DeleteDocRequest): Promise<void>;
// (undocumented)
handleGetOpsRequest(request: GetOpsRequest): Promise<void>;
// (undocumented)
handleGetSnapshotRequest(request: GetSnapshotRequest): Promise<void>;
// (undocumented)
handleMessage: (request: ClientRequest<P, Pr>) => Promise<void>;
// (undocumented)
handleRollbackMessage(request: RollbackRequest): Promise<void>;
// (undocumented)
log(...msg: any): void;
// (undocumented)
onSubscribe: (e: PubSubData<RemoteOpResponse<P> | PresenceRequest<Pr>>) => void;
// (undocumented)
open(): void;
// (undocumented)
get otType(): OTType<S, P, Pr>;
// (undocumented)
presence?: Presence<Pr>;
// (undocumented)
send(message: ClientResponse<S, P, Pr>): void;
// (undocumented)
sendPresences(): void;
// (undocumented)
server: Server;
// (undocumented)
get stream(): Duplex;
// (undocumented)
get subscribeId(): string;
// (undocumented)
transform(op: any, prevOps: any[]): any;
}
// @public (undocumented)
export interface AgentConfig<S, P, Pr, Custom> {
// (undocumented)
clientId: string;
// (undocumented)
collection: string;
// (undocumented)
custom?: Custom;
// (undocumented)
docId: string;
// (undocumented)
logger?: Logger;
// (undocumented)
otType: OTType<S, P, Pr>;
// (undocumented)
stream: Duplex;
}
// @public (undocumented)
export interface DB {
// (undocumented)
commitOp<P>(params: CommitOpParams<P>): Promise<void>;
// (undocumented)
deleteDoc(params: DeleteDocParams): Promise<void>;
// (undocumented)
getOps<P>(params: GetOpsParams): Promise<Op<P>[]>;
// (undocumented)
getSnapshot<S, P>(params: GetSnapshotParams): Promise<SnapshotAndOps<S, P> | undefined>;
// (undocumented)
saveSnapshot<S>(params: SaveSnapshotParams<S>): Promise<void>;
}
// @public (undocumented)
export interface PresenceMessage {
// (undocumented)
clientId: string;
// (undocumented)
presence: Presence<any>;
// (undocumented)
subscribeId: string;
}
// @public (undocumented)
export interface PubSub<D> {
// (undocumented)
publish(channel: string, data: D): void;
// (undocumented)
subscribe(channel: string, callback: (d: PubSubData<D>) => void): void;
// (undocumented)
unsubscribe(channel: string, callback: (d: PubSubData<D>) => void): void;
}
// @public (undocumented)
export interface PubSubData<D> {
// (undocumented)
data: D | undefined;
}
// @public (undocumented)
export type RequiredServerConfig = Required<ServerConfig>;
// @public (undocumented)
export class Server {
constructor(config_?: ServerConfig);
// (undocumented)
addAgent<S, P, Pr, Custom>(agent: Agent<S, P, Pr, Custom>): void;
// (undocumented)
agentsMap: Map<string, Set<Agent<any, any, any, any>>>;
// (undocumented)
broadcast<S, P, Pr, Custom>(from: Agent<S, P, Pr, Custom>, message: NotifyResponse<P, Pr>): void;
// (undocumented)
config: RequiredServerConfig;
// (undocumented)
get db(): DB;
// (undocumented)
deleteAgent<S, P, Pr, Custom>(agent: Agent<S, P, Pr, Custom>): void;
// (undocumented)
handleStream<S, P, Pr, Custom>(config: AgentConfig<S, P, Pr, Custom>): Agent<S, P, Pr, Custom>;
// (undocumented)
log(...msg: any): void;
// (undocumented)
onPresence: ({ data }: {
data: PresenceMessage;
}) => void;
// (undocumented)
presencesMap: Record<string, Record<string, Presence<any>>>;
// (undocumented)
printAgentSize(): void;
// (undocumented)
get pubSub(): PubSub<any>;
}
// @public (undocumented)
export interface ServerConfig {
// (undocumented)
db?: DB;
// (undocumented)
logger?: Logger;
// (undocumented)
pubSub?: PubSub<any>;
// (undocumented)
saveInterval?: number;
}
// (No @packageDocumentation comment for this package)
import { ClientRequest } from 'ot-engine-common';
import { ClientResponse } from 'ot-engine-common';
import { CommitOpResponse } from 'ot-engine-common';
import { Event as Event_2 } from 'ts-event-target';
import { EventTarget as EventTarget_2 } from 'ts-event-target';
import { Logger } from 'ot-engine-common';
import { Op } from 'ot-engine-common';
import { OTType } from 'ot-engine-common';
import { Presence } from 'ot-engine-common';
import { Snapshot } from 'ot-engine-common';
// @public (undocumented)
export class BeforeOpEvent<P> extends Event_2<'beforeOp'> {
constructor(ops: P[], clientIds: string[], source: boolean, undoRedo: boolean);
// (undocumented)
clientIds: string[];
// (undocumented)
ops: P[];
// (undocumented)
source: boolean;
// (undocumented)
undoRedo: boolean;
}
// @public (undocumented)
export interface ClientRollbackParams {
// (undocumented)
version: number;
}
// @public (undocumented)
export class Doc<S = unknown, P = unknown, Pr = unknown> extends EventTarget_2<[
OpEvent<P>,
BeforeOpEvent<P>,
NoPendingEvent,
RemoteOpEvent<P>,
RemoteDeleteDocEvent,
RemotePresenceEvent<Pr>,
RollbackEvent
]> {
constructor(config: DocConfig<S, P, Pr>);
// (undocumented)
get allPendingOps(): PendingOp<P>[];
// (undocumented)
apply<T extends boolean>(op: P, invert: T): T extends true ? P : undefined;
// (undocumented)
bindToSocket(socket: WebSocket): void;
// (undocumented)
callMap: Map<number, (arg: any) => void>;
// (undocumented)
canRedo(): boolean;
// (undocumented)
canUndo(): boolean;
// (undocumented)
checkSend(): Promise<void>;
// (undocumented)
clear(): void;
// (undocumented)
get clientId(): string;
// (undocumented)
closed: boolean;
// (undocumented)
config: Required<DocConfig<S, P, Pr>>;
// (undocumented)
data: S | undefined;
// (undocumented)
delete(): Promise<void>;
// (undocumented)
destroy(): void;
// (undocumented)
fetch(): Promise<Snapshot<S> | undefined>;
// (undocumented)
fireBeforeOpEvent(ops: P[], clientIds: string[], source?: boolean, undoRedo?: boolean): void;
// (undocumented)
fireOpEvent(ops: P[], clientIds: string[], source?: boolean, undoRedo?: boolean): void;
// (undocumented)
handleCommitOpResponse(res: CommitOpResponse<P>): void;
// (undocumented)
handleResponse(response: ClientResponse<S, P, Pr>): Promise<boolean | undefined>;
// (undocumented)
inflightOp: PendingOp<P> | undefined;
// (undocumented)
localPresenceManager: LocalPresenceManager<S, P, Pr>;
// (undocumented)
log(...msg: any[]): void;
// (undocumented)
get otType(): OTType<S, P, Pr>;
// (undocumented)
pendingOps: PendingOp<P>[];
// (undocumented)
get presence(): Pr | undefined;
// (undocumented)
redo(): void;
// (undocumented)
reloadPresences(presences: Record<string, Presence<Pr>>, fire?: boolean): Map<any, any> | undefined;
// (undocumented)
remotePresenceManager: RemotePresenceManager<S, P, Pr>;
// (undocumented)
get remotePresences(): Map<string, Pr | undefined>;
// (undocumented)
rollback({ version }: ClientRollbackParams): Promise<void>;
// (undocumented)
send(request: ClientRequest<P, Pr>): Promise<ClientResponse<S, P, Pr>> | undefined;
// (undocumented)
sending: boolean;
// (undocumented)
seq: number;
// (undocumented)
socket: WebSocket;
// (undocumented)
submitOp(opContent: P): void;
// (undocumented)
submitPendingOp(op: PendingOp<P>, undoRedo?: boolean): void;
// (undocumented)
submitPresence(presence: Pr): void;
// (undocumented)
undo(): void;
// (undocumented)
undoManager: UndoManager<S, P, Pr>;
// (undocumented)
version: number;
// (undocumented)
waitNoPending(): Promise<void>;
}
// @public (undocumented)
export interface DocConfig<S, P, Pr> {
// (undocumented)
cacheServerOpsLimit?: number;
// (undocumented)
clientId: string;
// (undocumented)
logger?: Logger;
// (undocumented)
otType: OTType<S, P, Pr>;
// (undocumented)
socket: WebSocket;
// (undocumented)
undoStackLimit?: number;
}
// @public (undocumented)
export class LocalPresenceManager<S, P, Pr> {
constructor(doc: Doc<S, P, Pr>);
// (undocumented)
clear(): void;
// (undocumented)
onOp: ({ ops, clientIds }: OpEvent<P>) => void;
// (undocumented)
sending: boolean;
// (undocumented)
submit(value: Pr): Promise<void>;
// (undocumented)
value: Pr | undefined;
}
// @public (undocumented)
export class NoPendingEvent extends Event_2<'noPending'> {
constructor();
}
// @public (undocumented)
export class OpEvent<P> extends Event_2<'op'> {
constructor(ops: P[], clientIds: string[], source: boolean, undoRedo: boolean);
// (undocumented)
clientIds: string[];
// (undocumented)
ops: P[];
// (undocumented)
source: boolean;
// (undocumented)
undoRedo: boolean;
}
// @public (undocumented)
export interface PendingOp<P> {
// (undocumented)
invert: Op<P>;
// (undocumented)
op: Op<P>;
}
// @public (undocumented)
export interface PresenceItem<P> {
// (undocumented)
normal?: Presence<P>;
// (undocumented)
pending?: Presence<P>;
}
// @public (undocumented)
export class RemoteDeleteDocEvent extends Event_2<'remoteDeleteDoc'> {
constructor();
}
// @public (undocumented)
export class RemoteOpEvent<P> extends Event_2<'remoteOp'> {
constructor();
// (undocumented)
afterOps?: Op<P>[];
// (undocumented)
prevOps?: Op<P>[];
// (undocumented)
sourceOp?: Op<P>;
}
// @public (undocumented)
export class RemotePresenceEvent<Pr> extends Event_2<'remotePresence'> {
constructor();
// (undocumented)
changed: Map<string, Pr | undefined>;
}
// @public (undocumented)
export class RemotePresenceManager<S, P, Pr> {
constructor(doc: Doc<S, P, Pr>);
// (undocumented)
clear(): void;
// (undocumented)
getOrCreatePresenceItem(clientId: string): PresenceItem<Pr>;
// (undocumented)
onPresenceResponse(response: {
clientId: string;
presence: Presence<Pr>;
}, changeSet?: Map<string, Pr | undefined>): void;
// (undocumented)
onRemoteOp: ({ prevOps, sourceOp, afterOps }: RemoteOpEvent<P>) => void;
// (undocumented)
reload(presences: Record<string, Presence<Pr>>, fire?: boolean): Map<any, any>;
// (undocumented)
remotePresenceMap: Map<string, PresenceItem<Pr>>;
// (undocumented)
get remotePresences(): Map<string, Pr | undefined>;
// (undocumented)
serverOps: Op<P>[];
// (undocumented)
syncPresence(presenceClientId: string, presence: Presence<Pr>): Presence<Pr> | undefined;
// (undocumented)
syncRemotePresences(ops: P[], clientIds: string[], onlyNormal?: boolean): void;
}
// @public (undocumented)
export class RollbackEvent extends Event_2<'rollback'> {
constructor();
}
// @public (undocumented)
export interface UndoItem<P> {
// (undocumented)
invert: Op<P>;
// (undocumented)
op: Op<P>;
}
// @public (undocumented)
export class UndoManager<S, P, Pr> {
constructor(doc: Doc<S, P, Pr>);
// (undocumented)
canRedo(): boolean;
// (undocumented)
canUndo(): boolean;
// (undocumented)
clear(): void;
// (undocumented)
get lastPendingOp(): UndoRedoItem<P>;
// (undocumented)
onRemoteOp: (e: RemoteOpEvent<P>) => void;
// (undocumented)
redo(): void;
// (undocumented)
redoStack: UndoRedoStack<S, P, Pr>;
// (undocumented)
submitOp(pendingOp: PendingOp<P>): void;
// (undocumented)
undo(): void;
// (undocumented)
undoRedo(popStack: UndoRedoStack<S, P, Pr>, pushStack: UndoRedoStack<S, P, Pr>): void;
// (undocumented)
undoStack: UndoRedoStack<S, P, Pr>;
}
// @public (undocumented)
export interface UndoRedoItem<P> {
// (undocumented)
accepted: boolean;
// (undocumented)
afterOps: any[];
// (undocumented)
invert: Op<P>;
// (undocumented)
op: Op<P>;
}
// @public (undocumented)
export class UndoRedoStack<S, P, Pr> {
constructor(doc: Doc<S, P, Pr>);
// (undocumented)
clear(): void;
// (undocumented)
get length(): number;
// (undocumented)
nextAcceptedIndex: number;
// (undocumented)
onRemoteOp({ prevOps, sourceOp, afterOps }: RemoteOpEvent<P>): void;
// (undocumented)
pop(): PendingOp<P> & {
needInvert: boolean;
};
// (undocumented)
push(item: PendingOp<P>): void;
// (undocumented)
reduceNextAcceptedIndex(): void;
// (undocumented)
stack: UndoRedoItem<P>[];
}
// (No @packageDocumentation comment for this package)
npm i [email protected] -g
pnpm i
pnpm dev
open: http://localhost:3000/
npm run pub
git push heroku main:master --force