Skip to content
Edytor
Esc
↑↓navigate↵open⌘Jpreview
On this page

edytor/cloudflare

Every name edytor/cloudflare exports, with its declaration and documentation (generated from the published types).

Every name edytor/cloudflare exports, generated from the package’s published type declarations, with the documentation its JSDoc carries. The guides explain how the pieces fit; this page is the exhaustive list. 101 names.

AttachedDocument

One edytor document living in a Durable Object’s storage: the room logic, independent of the class that hosts it. Create it with attachRoom (any Durable Object) or extend DocumentRoom. The socket handlers ignore sockets without SOCKET_TAG and return false for them.

export declare class AttachedDocument {
    readonly ctx: DurableObjectState;
    readonly maxRowBytes: number;
    readonly maxFrameBytes: number;
    readonly compactAfter: number;
    readonly saveAfter: number;
    readonly maxDocumentBytes: number;
    readonly documentWarning: number;
    readonly maxInboundFrameBytes: number;
    readonly maxBufferedBytes: number;
    readonly maxUpdatesPerSecond: number;
    readonly maxPresenceBytes: number;
    readonly maxPresencePerSecond: number;
    readonly maxPresenceFanout: number;
    failure: Error | null;
    presence: Map<number, E.AwarenessEntry>;
    refusals: Refusal[];
    refusalCounts: Partial<Record<Refusal['reason'], number>>;
    origin: {
        kind: 'fresh';
    } | {
        kind: 'restored';
        records: number;
    };
    private readonly room;
    constructor(ctx: DurableObjectState, options?: AttachRoomOptions);
    get doc(): YDoc | null;
    get facade(): DocumentOperations;
    transact<T>(fn: (facade: DocumentOperations) => T): T;
    read(): JSONDoc;
    alarm(): Promise<void>;
    reset(): Promise<void>;
    records(only?: RowKind): StoredRecord[];
    compressed(): Promise<void>;
    compact(): {
        rows: number;
    };
    lastUpdated(): number | null;
    historyWritten(): Promise<void>;
    listHistory(): Promise<HistoryEntry[]>;
    readHistory(key: string): Promise<JSONDoc | null>;
    restoreHistory(key: string, options?: {
        user?: string;
    }): Promise<RestoreResult>;
    undoRestore(options?: {
        user?: string;
    }): Promise<{
        status: 'applied' | 'noop';
    }>;
    listComments(): CommentSnapshot;
    comment(request: CommentRequest, actor: CommentActor): Promise<CommentOutcome>;
    exportBlocks(ids: string[]): ExportedBlocks;
    importBlocks(request: ImportRequest): ImportReceipt;
    commitMove(moveId: string, receipt: {
        to: string;
        ids: Record<string, string>;
    }): CommitResult;
    abortMove(moveId: string): {
        moveId: string;
        status: 'applied' | 'noop';
    };
    lateEdits(): LateEditBatch[];
    ackLateEdits(moveId: string, seq: number): void;
    applyLateEdits(batch: LateEditBatch): {
        applied: number;
        skipped: string[];
    };
    purge(): (PurgeReport & {
        horizon: number;
    }) | null;
    metrics(): RoomMetrics;
    dropWaitingDeletes(): {
        ranges: number;
    };
    fetch(request: Request): Promise<Response>;
    closeUser(userId: string, code?: number, reason?: string): {
        sockets: number;
    };
    setAccess(userId: string, access: 'write' | 'read' | 'none'): {
        sockets: number;
    };
    owns(ws: WebSocket): boolean;
    webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): boolean;
    webSocketClose(ws: WebSocket, code: number, reason: string): boolean;
    webSocketError(ws: WebSocket): boolean;
}
  • failure: Why the stored container was refused (another generation, a torn record, onLoad…).
  • presence: Latest presence entry per replica, for join snapshots (memory: refills after a wake).
  • refusals: The newest MAX_REFUSALS refusals since this instance started (diagnostics; memory only).
  • refusalCounts: Every refusal since this instance started, by reason.
  • origin: How this instance came to be: a fresh container, or a restore of N stored records.
  • doc: The live document; null when the stored container was refused.
  • facade: The facade over the live document.
  • transact: Manipulate: run fn on the facade in one transaction.
  • read: The document as JSON.
  • alarm: The room’s one alarm (room.alarm.tasks): every task due at its time runs — the history slot (room.history.slots), onSave, the purge (room.purge.timing) — each re-arming itself or clearing its due row; then the alarm is set to the earliest due time left.
  • reset: Generation cutover: drop the stored document (both tables) of a room whose storage belongs to another edytor generation, and start again from onLoad — reseed it from JSON.
  • records: Reassembled logical records in write order (of one kind, with only); a torn record throws.
  • compressed: Resolves once the snapshot compression in flight, if any, is stored: a compaction stores its snapshot raw, then compresses it in place.
  • compact: Compaction: the rows become the generation record + one chunked snapshot of the live document’s state (the healed state, deleted content collected but the text a replica may copy again — never a merge of the records, which keeps every keystroke’s struct and every deleted character), atomically, + one pending record of the stored deletes still waiting.
  • lastUpdated: When the room last stored a change (ms since the epoch), or null for a room that stored none since it records the time.
  • historyWritten: Resolves once the versions the room is writing are stored (a slot a write closed is written in the background).
  • listHistory: The room’s versions, newest first: one per half-day slot that changed, for the retention (room.history.slots).
  • readHistory: Version key as JSON (a preview), or null when this room holds no such version.
  • restoreHistory: Restore version key as a forward edit (room.history.restore): one room transaction makes the visible document equal the version, keeping every id the registry holds and writing only what differs; stored, relayed to every socket, one step undoRestore undoes.
  • undoRestore: Undo the last restore (room.history.undo): the history undo of exactly its transaction, edits made since kept.
  • listComments: Every comment thread, oldest first, with the last change’s sequence number (also over RPC).
  • comment: One comments request by actor (also over RPC): add a thread, reply, resolve, reopen or delete, under the rules every store applies.
  • exportBlocks: Export blocks ids for a move (room.move, step 1): their visible subtrees as JSON, the outermost only, in document order.
  • importBlocks: Import a move’s blocks (room.move, step 2) under dest.parent at dest.index, as one room transaction: each keeps its id unless this document holds it already (then <id>~<n>).
  • commitMove: Commit a move (room.move, step 3), with the destination’s acknowledgement: delete the exported blocks with their subtrees, as one room transaction, and watch them for late edits (room.move.late) for the grace period.
  • abortMove: Drop an export that was not committed (room.move): the blocks stay.
  • lateEdits: The late edits waiting to reach their destination (room.move.late), oldest first.
  • ackLateEdits: The destination applied a move’s late edits through seq: they are dropped here.
  • applyLateEdits: Apply a batch of late edits of a move this room imported (room.move.late): each block’s content and data merged three ways into what it holds now (what was written here since stays), as one room transaction.
  • purge: The purge task, also over RPC (room.purge.timing): record an epoch (the state vector, now) when the document changed since the last one, purge what was deleted before the horizon — the newest epoch at least purgeAfterDays old — when it is newer than the last purged, and re-arm for the next epoch to pass it.
  • metrics: The room’s counters, also over RPC — see RoomMetrics.
  • dropWaitingDeletes: Drop every waiting delete, stored or in memory (callable over RPC): the way out when a writer filled MAX_WAITING_DELETES with deletes of registered ids’ items, which compaction keeps.
  • closeUser: Revoke: close every socket of userId (also over RPC), 4403 (access revoked, final for the provider) unless you pass another code (4401 redials with fresh params; 1011 redials too).
  • setAccess: Change userId’s access on its open sockets (also over RPC): 'read' downgrades each write socket in place (the read-only notice, then every write denied, the socket stays); 'write' closes each read-only socket 1012 (access changed), which its provider redials, so authorize grants the new access; 'none' closes every socket 4403 (access revoked), as closeUser.
  • owns: Is ws one of this document’s sockets?

Attachment

A socket’s attachment (survives hibernation): its identity, its presence clock, and whether it subscribed to the comment changes.

export type Attachment = SocketIdentity & {
    clock: number | null;
    comments?: boolean;
};

attachRoom

Attach an edytor room to any Durable Object (attachRoom(this, opts), in the constructor or a field). Its tables (edytor_rows, edytor_replicas) live beside yours and its sockets carry SOCKET_TAG. Each handler your class does not define — fetch, webSocketMessage, webSocketClose, webSocketError and alarm (a socket’s credential may expire) — is installed on the object; a class that defines one delegates to the returned document’s method (which returns false for a socket that is not the document’s).

declare const attachRoom: (host: DurableObject<any>, options?: AttachRoomOptions) => AttachedDocument;

AttachRoomOptions

What attachRoom (and DocumentRoom) takes.

export type AttachRoomOptions = {
    onLoad?: () => Promise<LoadedDocument | null | undefined> | LoadedDocument | null | undefined;
    onSave?: (document: SavedDocument) => Promise<void> | void;
    saveAfter?: number;
    compactAfter?: number;
    maxRowBytes?: number;
    maxFrameBytes?: number;
    tablePrefix?: string;
    maxDocumentBytes?: number;
    documentWarning?: number;
    maxInboundFrameBytes?: number;
    maxBufferedBytes?: number;
    maxUpdatesPerSecond?: number;
    maxPresenceBytes?: number;
    maxPresencePerSecond?: number;
    maxPresenceFanout?: number;
    validate?: (frame: FrameValidation) => boolean | void;
    locks?: LockOptions;
    rooms?: () => MoveNamespace | undefined;
    log?: ((entry: RoomLogEntry) => void) | false;
    semantics?: DocumentSemanticsConfig;
    history?: HistoryOptions;
    purgeAfterDays?: number | false;
    now?: () => number;
    comments?: CommentOptions | false;
};
  • onLoad: Retrieve: the document for a room that stores nothing yet.
  • onSave: Save: mirror the document to your own store, saveAfter ms after the first unsaved change, on the object’s alarm (a throw is retried by the platform).
  • saveAfter: ms between the first unsaved change and onSave (default DEFAULT_SAVE_AFTER).
  • compactAfter: Update records before the rows are merged into one snapshot (default DEFAULT_COMPACT_AFTER).
  • maxRowBytes: Largest stored row; can only be lowered (default DEFAULT_MAX_ROW_BYTES).
  • maxFrameBytes: Largest frame sent whole; can only be lowered (default 32 MiB).
  • tablePrefix: Prefix of the document’s SQL tables, beside your own (default 'edytor_').
  • maxDocumentBytes: Largest document, in stored bytes (default DEFAULT_MAX_DOCUMENT_BYTES).
  • documentWarning: The share of maxDocumentBytes past which the room logs the document’s size (size, once per crossing), above 0 and at most 1 (default DEFAULT_DOCUMENT_WARNING, 0.8).
  • maxInboundFrameBytes: Largest frame a socket may send, reassembled (default DEFAULT_MAX_INBOUND_FRAME_BYTES).
  • maxBufferedBytes: The chunk sequences in flight on every socket together (default twice maxInboundFrameBytes, never less than it): a sequence that would pass it closes its socket 1011 (room busy), and its provider redials.
  • maxUpdatesPerSecond: Sync messages per second a socket may send (default DEFAULT_MAX_UPDATES_PER_SECOND).
  • maxPresenceBytes: Largest presence state a socket may publish, as JSON (default DEFAULT_MAX_PRESENCE_BYTES).
  • maxPresencePerSecond: Presence messages per second a socket may send (default DEFAULT_MAX_PRESENCE_PER_SECOND).
  • maxPresenceFanout: Presence frames per second the room sends over all its sockets (default DEFAULT_MAX_PRESENCE_FANOUT).
  • validate: Accept, then compensate: after the room applied and stored a client frame that changed blocks, it is asked whether to keep it.
  • locks: Per-block locks (room.locks): a block whose data names a user under key (default lockedBy) is that user’s alone — a frame from anyone else touching it is denied and compensated, as validate’s denials are (lockedBlocks).
  • rooms: The rooms blocks move to (room.move): with it, the room forwards the late edits of blocks it moved out to their destination itself, on its alarm; without it, they wait for the host’s forwardLateEdits.
  • log: The room’s log: one entry per compaction, quota hit, denial or fault.
  • semantics: The block roles transact edits obey — the document semantics your clients’ plugins declare (defaultType also names the block an empty room is seeded with).
  • history: Version history, in the store HistoryOptions.store names (a KV namespace, an R2 bucket or the room’s own storage): the room writes its state twice a day — the morning’s at local noon, the evening’s at midnight, in timeZone — when it changed, for retentionDays; read and restore them with listHistory, readHistory, restoreHistory and undoRestore (or routeDocumentHistory).
  • purgeAfterDays: Days after which deleted content is purged from the room and every replica (room.purge.*): default the history’s retentionDays, else 30; false never purges.
  • now: The room’s clock, ms since the epoch (default Date.now): its slots, epochs and alarm read it.
  • comments: Comment threads (room.comments): kept in the room’s threads and comments tables, read and written through routeDocumentComments (or listComments and comment over RPC), each change sent to the sockets that subscribed.

AuthorizeDocumentSocket

The host’s decision: an identity, null to refuse (close 4403, terminal), or { expired: true } for an expired credential (close 4401, retried).

export type AuthorizeDocumentSocket = (request: Request, documentId: string) => DocumentIdentity | ExpiredCredential | null | Promise<DocumentIdentity | ExpiredCredential | null>;

closedSocket

An upgrade accepted and closed at once, so the client reads code: a browser sees an HTTP error at the upgrade only as 1006, which a provider cannot tell from a network failure. Refuse a dial your Worker turns away itself with it: the provider stops at 1008 or 4xxx (except 4401, redialed), and redials after anything else (CLOSE, isRefusal in crdt/providers/room.ts).

declare const closedSocket: (code: number, reason: string) => Response;

COMMENT_ROW_BYTES

What one stored comment counts against a store’s byte quota beyond its text (its ids, author, times; a thread’s row is counted with its first comment).

declare const COMMENT_ROW_BYTES = 128;

COMMENT_STATUS

The HTTP status of each refusal.

declare const COMMENT_STATUS: Readonly<Record<CommentRefusal, number>>;

CommentActor

Who asks: a verified user, read-only or not; a moderator may delete anyone’s comment.

export type CommentActor = {
    user: string;
    readOnly?: boolean;
    moderator?: boolean;
};

commentAnchors

Every thread’s anchor in doc: the runs of text its comment:<id> mark covers, in document order (adjacent runs of one block joined). A split or a merge moves the marked text, never copies it, so a thread keeps its runs through both; a thread whose marked text was deleted has none.

declare const commentAnchors: (doc: CommentAnchorSource) => Map<string, CommentRun[]>;

commentBytes

What a stored comment counts against a byte quota: its body’s and its thread’s quote’s UTF-8 bytes (the quote with the first comment only), plus COMMENT_ROW_BYTES.

declare const commentBytes: (body: string, quote?: string) => number;

CommentChange

What one request changed (the room broadcasts it, onComment receives it).

export type CommentChange = {
    type: 'added' | 'replied' | 'resolved' | 'reopened' | 'deleted' | 'removed';
    thread: CommentThread;
    comment: ThreadComment | null;
    user: string;
    at: number;
    seq: number;
};
  • type: added a thread, replied to one, resolved or reopened one, deleted a reply, or removed a whole thread (its anchor marks go with it).
  • thread: The thread after the change; for removed, as it was.
  • comment: The comment added (added, replied) or deleted (deleted), else null.
  • user: Who made the change.
  • seq: The change’s sequence number in its document (the thread’s new rev).

CommentOptions

The room’s comment threads (comments of AttachRoomOptions).

export type CommentOptions = {
    onComment?: (change: CommentChange) => void | Promise<void>;
    maxLength?: number;
    maxBytes?: number;
    maxRequestsPerSecond?: number;
};
  • onComment: Notifications: called after each change is stored and sent to the sockets (a thread added, a reply, a resolve or reopen, a delete), awaited before the request is answered.
  • maxLength: The longest comment body (default and ceiling 10,000 characters).
  • maxBytes: The bytes the room’s comments may hold (default DEFAULT_MAX_COMMENT_BYTES, 4 MiB): each body and quote in UTF-8 plus 128 a comment.
  • maxRequestsPerSecond: Comment requests per second (default DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND, a ten-second burst): over HTTP per verified user (past it, 429), and a socket’s comment messages per socket (past it, dropped).

CommentOutcome

What a request did: noop when it changed nothing (resolving a resolved thread).

export type CommentOutcome = {
    status: 'applied';
    change: CommentChange;
} | {
    status: 'noop';
    thread: CommentThread;
} | {
    status: 'refused';
    reason: CommentRefusal;
    message: string;
};

CommentRefusal

Why a request was refused (each an HTTP status: COMMENT_STATUS).

export type CommentRefusal = 'invalid' | 'read-only' | 'forbidden' | 'missing' | 'exists' | 'full';

CommentRequest

A request to a document’s comments, by the user a store verified.

export type CommentRequest = {
    op: 'add';
    thread: string;
    body: string;
    quote?: string;
    block?: string | null;
    comment?: string;
} | {
    op: 'reply';
    thread: string;
    body: string;
    comment?: string;
} | {
    op: 'resolve';
    thread: string;
} | {
    op: 'reopen';
    thread: string;
} | {
    op: 'delete';
    thread: string;
    comment?: string;
};

CommentRun

One run of text a thread’s anchor mark covers: length units from offset of block.

export type CommentRun = {
    block: string;
    offset: number;
    length: number;
};

CommentSnapshot

The document’s threads at sequence number seq.

export type CommentSnapshot = {
    seq: number;
    threads: CommentThread[];
};

CommentThread

A comment thread: its first comment starts it, the others reply.

export type CommentThread = {
    id: string;
    block: string | null;
    quote: string;
    createdBy: string;
    createdAt: number;
    resolved: {
        by: string;
        at: number;
    } | null;
    comments: ThreadComment[];
    rev: number;
};
  • id: The thread’s id, which its anchor mark names (comment:<id>).
  • block: The block it was started in (null: unknown).
  • quote: The text it was started on, as it read then (at most MAX_QUOTE_LENGTH).
  • resolved: Who resolved it and when; null while it is open.
  • comments: Oldest first; never empty.
  • rev: The sequence number of the last change to it (a later change has a greater one).

CommitResult

What commitMove returns.

export type CommitResult = {
    moveId: string;
    status: 'applied' | 'refused';
    reason?: string;
};

decideComment

Decide request by actor: the one rule of every comment store.

  • A read-only actor changes nothing (read-only).
  • add starts a thread under an id no thread holds (exists), with a first comment; reply adds one to a thread (missing without it).
  • resolve and reopen are anyone’s who may write (noop when the thread already is so).
  • delete removes one comment, its author’s or a moderator’s (forbidden); the first comment, or no comment, removes the whole thread, its creator’s or a moderator’s.

Writes nothing: the store applies the change it returns.

declare const decideComment: (request: CommentRequest, actor: CommentActor, context: CommentContext) => CommentOutcome;

DEFAULT_COMPACT_AFTER

Update records before the rows are merged into one snapshot.

declare const DEFAULT_COMPACT_AFTER = 500;

DEFAULT_DOCUMENT_WARNING

The share of the document quota past which the room logs its size, by default.

declare const DEFAULT_DOCUMENT_WARNING = 0.8;

DEFAULT_MAX_BUFFERED_BYTES

The chunk sequences in flight on all of a room’s sockets together, by default twice the frame quota (never less than one frame): each is one buffer of its announced size, allocated at its start.

declare const DEFAULT_MAX_BUFFERED_BYTES: number;

DEFAULT_MAX_COMMENT_BYTES

The bytes a room’s comments hold at most, by default (4 MiB).

declare const DEFAULT_MAX_COMMENT_BYTES: number;

DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND

Comment requests per second and user (or socket), by default.

declare const DEFAULT_MAX_COMMENT_REQUESTS_PER_SECOND = 2;

DEFAULT_MAX_DOCUMENT_BYTES

Room quotas, each refused with 4413 (quota: <name>). The document’s size: what its records hold (uncompressed) and the updates the engine holds waiting. An isolate has 128 MB, and the live document takes 18 to 46 bytes of heap per stored byte (prose to one short line per block, its index included; bench/room-memory.mjs): 2 MiB is about 100 MB at worst. Raise it for documents of long text.

declare const DEFAULT_MAX_DOCUMENT_BYTES: number;

DEFAULT_MAX_INBOUND_FRAME_BYTES

The largest frame a socket may send, reassembled from chunks: room for a whole document at the default quota (a client seeding an empty room). Raise it with the document quota.

declare const DEFAULT_MAX_INBOUND_FRAME_BYTES: number;

DEFAULT_MAX_PRESENCE_BYTES

The largest presence state a socket may publish, as JSON (a caret, a name, a color: a few hundred bytes). A larger entry is ignored.

declare const DEFAULT_MAX_PRESENCE_BYTES: number;

DEFAULT_MAX_PRESENCE_FANOUT

Presence frames the room sends a second, over all its sockets together (a one-second burst). Within it each entry goes out at once; past it the entries wait per recipient, the newest of each replica, and go out together in one frame per recipient (room.presence.fanout). Ten editors each moving a caret at the client’s 20 Hz stay within it; fifty would send 50,000 frames a second.

declare const DEFAULT_MAX_PRESENCE_FANOUT = 2000;

DEFAULT_MAX_PRESENCE_PER_SECOND

Presence messages (entries and queries) a socket may send per second, sustained, on a bucket of its own (a ten-second burst): a view publishes at most every 50 ms (presence.throttle), and renews every 15 s. Past it, entries are coalesced and queries dropped.

declare const DEFAULT_MAX_PRESENCE_PER_SECOND = 50;

DEFAULT_MAX_ROW_BYTES

Under SQLite-backed Durable Objects’ 2 MB row cap, with room for the other columns.

declare const DEFAULT_MAX_ROW_BYTES: number;

DEFAULT_MAX_UPDATES_PER_SECOND

Sync messages a socket may send per second, sustained; a burst of ten seconds’ worth is allowed (a reconnect, a paste). Far above typing speed: a script should batch its edits into transactions.

declare const DEFAULT_MAX_UPDATES_PER_SECOND = 50;

DEFAULT_MOVE_GRACE_DAYS

The default grace period: late edits are forwarded this long after the commit.

declare const DEFAULT_MOVE_GRACE_DAYS = 30;

DEFAULT_RETENTION_DAYS

The default retention, in days (also the default purge horizon).

declare const DEFAULT_RETENTION_DAYS = 30;

DEFAULT_SAVE_AFTER

ms between the first unsaved change and onSave.

declare const DEFAULT_SAVE_AFTER = 2000;

DocumentIdentity

What the host verified. userId owns every Yjs client id the socket writes under; replica is the client’s doc.clientID (read it from a query parameter with requestedReplica; omitted, the socket’s first presence entry binds it); readOnly refuses every write.

export type DocumentIdentity = {
    userId: string;
    replica?: number | null;
    readOnly?: boolean;
    expiresAt?: number | null;
};
  • expiresAt: When the credential expires, ms since the epoch (a token’s exp × 1000): the room closes the socket 4401 then, and the provider redials with its params read again.

DocumentNamespace

A namespace whose objects host a document (DocumentRoom, or any object with attachRoom).

export type DocumentNamespace = {
    getByName(name: string): {
        fetch(request: Request): Promise<Response>;
    };
};

DocumentRoom

The ready-made room: a Durable Object hosting one document, configured by vars (DocumentRoomEnv). Subclass it to override onLoad and onSave, and add your own RPC methods over transact/read. Its tables are rows and replicas (no prefix).

export declare class DocumentRoom<Env = DocumentRoomEnv> extends DurableObject<Env> {
    readonly room: AttachedDocument;
    constructor(ctx: DurableObjectState, env: Env);
    protected onLoad(): Promise<LoadedDocument | null | undefined>;
    protected onSave(_document: SavedDocument): Promise<void>;
    protected validate(_frame: FrameValidation): boolean | void;
    protected onComment(_change: CommentChange): void | Promise<void>;
    protected log(entry: RoomLogEntry): void;
    protected semantics(): DocumentSemanticsConfig;
    protected history(): HistoryOptions | undefined;
    protected locks(): LockOptions | undefined;
    protected rooms(): MoveNamespace | undefined;
    protected now(): number;
    fetch(request: Request): Promise<Response>;
    webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): void;
    webSocketClose(ws: WebSocket, code: number, reason: string): void;
    webSocketError(ws: WebSocket): void;
    alarm(): Promise<void>;
    listHistory(): Promise<HistoryEntry[]>;
    readHistory(key: string): Promise<JSONDoc | null>;
    restoreHistory(key: string, options?: {
        user?: string;
    }): Promise<RestoreResult>;
    undoRestore(options?: {
        user?: string;
    }): Promise<{
        status: 'applied' | 'noop';
    }>;
    historyWritten(): Promise<void>;
    exportBlocks(ids: string[]): ExportedBlocks;
    importBlocks(request: ImportRequest): ImportReceipt;
    commitMove(moveId: string, receipt: {
        to: string;
        ids: Record<string, string>;
    }): CommitResult;
    abortMove(moveId: string): {
        moveId: string;
        status: 'applied' | 'noop';
    };
    lateEdits(): LateEditBatch[];
    ackLateEdits(moveId: string, seq: number): void;
    applyLateEdits(batch: LateEditBatch): {
        applied: number;
        skipped: string[];
    };
    purge(): (PurgeReport & {
        horizon: number;
    }) | null;
    listComments(): CommentSnapshot;
    comment(request: CommentRequest, actor: CommentActor): Promise<CommentOutcome>;
    closeUser(userId: string, code?: number, reason?: string): {
        sockets: number;
    };
    setAccess(userId: string, access: 'write' | 'read' | 'none'): {
        sockets: number;
    };
    compact(): {
        rows: number;
    };
    metrics(): RoomMetrics;
    lastUpdated(): number | null;
    compressed(): Promise<void>;
    dropWaitingDeletes(): {
        ranges: number;
    };
    reset(): Promise<void>;
    transact<T>(fn: (facade: DocumentOperations) => T): T;
    read(): JSONDoc;
    get facade(): DocumentOperations;
    get doc(): YDoc | null;
    get failure(): Error | null;
    get refusals(): Refusal[];
    get refusalCounts(): AttachedDocument['refusalCounts'];
    get presence(): Map<number, AwarenessEntry>;
    get origin(): AttachedDocument['origin'];
    records(): ReturnType<AttachedDocument['records']>;
}
  • onLoad: Retrieve — see AttachRoomOptions.onLoad.
  • onSave: Save — see AttachRoomOptions.onSave.
  • validate: Validate a client frame — see AttachRoomOptions.validate: false denies it, and the room writes its inverse.
  • onComment: Notifications — see CommentOptions.onComment: each comment change, once stored.
  • log: One log entry: console.log of it as JSON, unless the EDYTOR_LOG var is off.
  • semantics: Block roles — see AttachRoomOptions.semantics.
  • history: Version history — see AttachRoomOptions.history.
  • locks: Per-block locks — see AttachRoomOptions.locks.
  • rooms: The rooms blocks move to — see AttachRoomOptions.rooms.
  • now: The room’s clock — see AttachRoomOptions.now.
  • alarm: Runs the room’s tasks: onSave, the history slots, the purge.
  • listHistory: The versions, newest first, also over RPC — see AttachedDocument.listHistory.
  • readHistory: A version as JSON, also over RPC — see AttachedDocument.readHistory.
  • restoreHistory: Restore a version, also over RPC — see AttachedDocument.restoreHistory.
  • undoRestore: Undo the last restore, also over RPC — see AttachedDocument.undoRestore.
  • historyWritten: The version writes in flight, also over RPC — see AttachedDocument.historyWritten.
  • exportBlocks: Export blocks for a move, also over RPC — see AttachedDocument.exportBlocks.
  • importBlocks: Import a move’s blocks, also over RPC — see AttachedDocument.importBlocks.
  • commitMove: Commit a move, also over RPC — see AttachedDocument.commitMove.
  • abortMove: Abort an export, also over RPC — see AttachedDocument.abortMove.
  • lateEdits: The late edits waiting, also over RPC — see AttachedDocument.lateEdits.
  • ackLateEdits: Drop forwarded late edits, also over RPC — see AttachedDocument.ackLateEdits.
  • applyLateEdits: Apply late edits, also over RPC — see AttachedDocument.applyLateEdits.
  • purge: Run the purge task now, also over RPC — see AttachedDocument.purge.
  • listComments: Every comment thread, also over RPC — see AttachedDocument.listComments.
  • comment: One comments request, also over RPC — see AttachedDocument.comment.
  • closeUser: Close every socket of a user, also over RPC — see AttachedDocument.closeUser.
  • setAccess: Change a user’s access on its sockets, also over RPC — see AttachedDocument.setAccess.
  • compact: Compaction, also over RPC.
  • metrics: The room’s counters, also over RPC — see RoomMetrics.
  • lastUpdated: When the room last stored a change, also over RPC — see AttachedDocument.lastUpdated.
  • compressed: The snapshot compression in flight, also over RPC — see AttachedDocument.compressed.
  • dropWaitingDeletes: Drop the stored waiting deletes, also over RPC — see AttachedDocument.dropWaitingDeletes.
  • reset: Generation cutover, also over RPC — see AttachedDocument.reset.
  • transact: Server-side edit — see AttachedDocument.transact.
  • read: The document as JSON.

DocumentRoomEnv

Optional knobs (strings, as vars arrive): row size, outgoing frame limit and compaction threshold. The defaults are the platform limits.

export type DocumentRoomEnv = {
    EDYTOR_MAX_ROW_BYTES?: string | number;
    EDYTOR_MAX_FRAME_BYTES?: string | number;
    EDYTOR_COMPACT_AFTER?: string | number;
    EDYTOR_SAVE_AFTER?: string | number;
    EDYTOR_MAX_DOCUMENT_BYTES?: string | number;
    EDYTOR_DOCUMENT_WARNING?: string | number;
    EDYTOR_MAX_INBOUND_FRAME_BYTES?: string | number;
    EDYTOR_MAX_BUFFERED_BYTES?: string | number;
    EDYTOR_MAX_UPDATES_PER_SECOND?: string | number;
    EDYTOR_MAX_PRESENCE_BYTES?: string | number;
    EDYTOR_MAX_PRESENCE_PER_SECOND?: string | number;
    EDYTOR_MAX_PRESENCE_FANOUT?: string | number;
    EDYTOR_LOG?: string;
    EDYTOR_PURGE_AFTER_DAYS?: string | number;
    EDYTOR_HISTORY?: KVLike | R2BucketLike | 'room';
    EDYTOR_HISTORY_RETENTION_DAYS?: string | number;
    EDYTOR_HISTORY_TIME_ZONE?: string;
    EDYTOR_LOCKS?: string;
    EDYTOR_ROOMS?: MoveNamespace;
    EDYTOR_COMMENTS?: string;
    EDYTOR_MAX_COMMENT_BYTES?: string | number;
    EDYTOR_MAX_COMMENT_REQUESTS_PER_SECOND?: string | number;
};
  • EDYTOR_DOCUMENT_WARNING: The share of the document quota past which the room logs its size (documentWarning).
  • EDYTOR_LOG: off: the room logs nothing (default: one JSON line per compaction, quota hit, denial, fault).
  • EDYTOR_PURGE_AFTER_DAYS: Days after which deleted content is purged (default: the history retention, else 30); off never purges.
  • EDYTOR_HISTORY: Where the room keeps its version history (history()): a KV namespace or an R2 bucket binding, or the string room (the room’s own storage, roomHistory).
  • EDYTOR_HISTORY_RETENTION_DAYS: Days a version is kept (default 30).
  • EDYTOR_HISTORY_TIME_ZONE: The IANA time zone of the history’s half-day slots (default UTC).
  • EDYTOR_LOCKS: Per-block locks (locks()): the data key naming a block’s owner, e.g.
  • EDYTOR_ROOMS: The namespace of the rooms blocks move to (rooms()): the room forwards late edits of moved blocks there itself.
  • EDYTOR_COMMENTS: off: the room keeps no comment threads (its comments requests answer 404).
  • EDYTOR_MAX_COMMENT_BYTES: The bytes the room’s comments may hold (CommentOptions.maxBytes).
  • EDYTOR_MAX_COMMENT_REQUESTS_PER_SECOND: Comment requests per second (CommentOptions.maxRequestsPerSecond).

ExpiredCredential

A credential that was valid but has expired: the dial is closed 4401, which the provider retries with its params read again.

export type ExpiredCredential = {
    expired: true;
};

ExportedBlocks

What exportBlocks returns.

export type ExportedBlocks = {
    moveId: string;
    blocks: JSONBlock[];
};

forwardLateEdits

Forward the late edits document from holds (room.move.late) to their destinations and acknowledge them — for a room without rooms(), which cannot reach the destination itself. Returns how many batches went.

declare const forwardLateEdits: (namespace: MoveNamespace, from: string) => Promise<number>;

FrameValidation

One client frame the room applied, as validate reads it.

export type FrameValidation = {
    user: string;
    replica: number | null;
    touched: string[];
    dataChanged: boolean;
    before: (id: string) => ValidatedBlock | null;
    after: (id: string) => ValidatedBlock | null;
    facade: DocumentOperations;
};
  • user: The sender’s verified user.
  • replica: The sender’s replica (client id), when bound.
  • touched: The blocks the frame changed, from the facade’s change report: shown or hidden (with their subtrees), given another parent or moved among their siblings (not shifted by a sibling’s move), retyped, given other data, or other content.
  • dataChanged: The frame changed the document’s own data.
  • before: A block as it was before the frame (null: it did not show).
  • after: A block as it is now (null: it does not show).
  • facade: The live document.

HISTORY_MAX_VALUE_BYTES

KV’s largest value: a version above it is skipped, never split.

declare const HISTORY_MAX_VALUE_BYTES: number;

HistoryEntry

One stored version, as listHistory returns it.

export type HistoryEntry = {
    key: string;
    date: string;
    slot: Slot;
    bytes: number;
    blocks: number;
    editors: string[];
    more: number;
    at: number;
    expiresAt: number | null;
};
  • key: The store key (history/<room>/<YYYY-MM-DD>-am|pm).
  • date: The local date, YYYY-MM-DD, in the room’s time zone.
  • bytes: Stored (compressed) bytes.
  • blocks: Visible blocks.
  • editors: The verified users whose edits the room stored during the slot.
  • more: Editors left out to keep the metadata under KV’s limit.
  • at: When the version was written (ms since the epoch).
  • expiresAt: When it expires (ms since the epoch), null when its store does not say.

HistoryMetadata

A version’s metadata, as the room writes it to the store.

export type HistoryMetadata = {
    bytes: number;
    blocks: number;
    editors: string[];
    more?: number;
    at: number;
};

HistoryOptions

The history option: where and how long the room keeps its versions.

export type HistoryOptions = {
    store: HistoryStore | HistoryStoreFactory;
    retentionDays?: number;
    timeZone?: string;
    maxValueBytes?: number;
};
  • store: The store: a HistoryStore (kvHistory, r2History or your own) or roomHistory``().
  • retentionDays: How long a version is kept, in days (default 30).
  • timeZone: The IANA time zone whose noon and midnight end the slots (default 'UTC').
  • maxValueBytes: The largest value written, in bytes; can only lower the store’s own maxValueBytes.

HistoryRoomStorage

What a HistoryStoreFactory gets: the room’s own storage.

export type HistoryRoomStorage = {
    readonly sql: SqlStorage;
    transactionSync<T>(closure: () => T): T;
    readonly tablePrefix: string;
};
  • tablePrefix: The room’s table prefix ('' for DocumentRoom, 'edytor_' by default for attachRoom).

HistoryStore

Where a room keeps its versions (room.history.store). Any object with these members works; the room owns everything else: the keys (one room’s share a prefix), the values (compressed bytes, at most maxValueBytes), the metadata, and the retention — it never lists or reads a version past its expiresAt, and, unless the store expires values itself (nativeTtl), its alarm deletes each one when it expires.

export type HistoryStore = {
    readonly maxValueBytes: number;
    readonly nativeTtl?: boolean;
    put(key: string, value: Uint8Array, options: {
        expiresAt: number;
        metadata: HistoryMetadata;
    }): Promise<void>;
    get(key: string): Promise<{
        value: Uint8Array;
        expiresAt: number | null;
    } | null>;
    list(prefix: string, cursor?: string): Promise<{
        entries: HistoryStoreRecord[];
        cursor?: string;
    }>;
    delete(key: string): Promise<void>;
};
  • maxValueBytes: The largest value the store takes: a larger version is skipped (logged history), never split.
  • nativeTtl: The store deletes a value at its expiresAt itself (KV’s TTL): the room’s alarm never deletes.
  • put: Store value at key (replacing any value there) with its expiry and metadata.
  • get: The value at key, with its expiry when the store knows it, or null.
  • list: One page of the keys starting with prefix; cursor continues a listing, and is absent on its last page.
  • delete: Delete key (a missing key is no error).

HistoryStoreFactory

A store built from the room’s own storage at first use (roomHistory).

export type HistoryStoreFactory = (room: HistoryRoomStorage) => HistoryStore;

HistoryStoreRecord

One stored version as a HistoryStore lists it.

export type HistoryStoreRecord = {
    key: string;
    expiresAt: number | null;
    metadata: unknown;
};
  • expiresAt: When it expires (ms since the epoch); null when the store does not know (it never expires by the room).
  • metadata: The metadata the room wrote with it (HistoryMetadata).

IDENTITY_HEADERS

Headers carrying the identity routeDocumentSocket verified — never the client’s. The user id is percent-encoded (encodeURIComponent): a header value is trimmed and cannot carry every character, and a user id is any string.

declare const IDENTITY_HEADERS: {
    readonly user: "X-Edytor-User";
    readonly replica: "X-Edytor-Replica";
    readonly access: "X-Edytor-Access";
    readonly expires: "X-Edytor-Expires";
};

ImportReceipt

What importBlocks returns: the acknowledgement commitMove needs.

export type ImportReceipt = {
    moveId: string;
    status: 'applied' | 'refused';
    ids: Record<string, string>;
    reason?: string;
};
  • ids: Each moved block’s id in the destination (a source id it already held is renamed).

ImportRequest

What importBlocks takes.

export type ImportRequest = {
    moveId: string;
    from: string;
    blocks: JSONBlock[];
    dest: MoveDestination;
};
  • from: The source document’s id.

kvHistory

Versions in a Workers KV namespace (the room history’s first store): KV’s TTL expires them (nativeTtl, expirationTtl = the retention, at least KV’s 60 s), its listing’s expiration is each one’s expiresAt, its metadata the room’s. Values up to KV’s 25 MiB. Versions written by the pre-releases that took a bare namespace as the store read as they are.

declare const kvHistory: (namespace: KVLike) => HistoryStore;

KVLike

The structural subset of a KV namespace kvHistory uses — a Workers KVNamespace binding satisfies it, and so does an in-memory fake. As history.store it goes through kvHistory.

export type KVLike = {
    put(key: string, value: ArrayBuffer | ArrayBufferView | string, options?: {
        expirationTtl?: number;
        metadata?: unknown;
    }): Promise<unknown>;
    get(key: string, type: 'arrayBuffer'): Promise<ArrayBuffer | null>;
    list(options?: {
        prefix?: string;
        cursor?: string;
        limit?: number;
    }): Promise<{
        keys: Array<{
            name: string;
            expiration?: number;
            metadata?: unknown;
        }>;
        list_complete: boolean;
        cursor?: string;
    }>;
    delete?(key: string): Promise<unknown>;
};

LateEdit

One block’s late edit: the state last sent (base) and the source’s now (src).

export type LateEdit = {
    block: string;
    base: MovedState;
    src: MovedState;
};

LateEditBatch

A batch of late edits of one move, numbered from 1 (to: its destination, as the source lists it).

export type LateEditBatch = {
    moveId: string;
    from: string;
    to?: string;
    seq: number;
    edits: LateEdit[];
};

LoadedDocument

What onLoad may return: JSON, a bare v14 update, or { update, replicas } (a SavedDocument).

export type LoadedDocument = JSONDoc | Uint8Array | Pick<SavedDocument, 'update' | 'replicas'>;

lockedBlocks

The validate hook of per-block locks: pass it as attachRoom’s validate, return it from DocumentRoom.locks(), or call it from your own validate (lockedBlocks(options)(frame) && …).

declare const lockedBlocks: (options?: LockOptions) => (frame: FrameValidation) => boolean;

LockOptions

What lockedBlocks reads.

export type LockOptions = {
    key?: string;
    subtree?: boolean;
    bypass?: (user: string) => boolean;
};
  • key: The data key naming a block’s owner (default 'lockedBy'): a string user id locks it.
  • subtree: A lock also holds the blocks displayed under the locked one (default false).
  • bypass: Users who edit locked blocks anyway (an admin): true admits the frame’s user.

MAX_COMMENT_LENGTH

The longest comment body, in UTF-16 code units.

declare const MAX_COMMENT_LENGTH = 10000;

MAX_COMMENTS_PER_THREAD

Comments one thread holds at most (its first included).

declare const MAX_COMMENTS_PER_THREAD = 500;

MAX_REFUSALS

Refusals kept in refusals (the newest); refusalCounts counts every one.

declare const MAX_REFUSALS = 100;

MAX_THREADS

Threads one document holds at most, resolved ones included.

declare const MAX_THREADS = 5000;

MAX_WAITING_DELETES

Delete ranges the room keeps waiting for items it does not hold (a relayer’s delete of an author’s edit a restore lost). Every update the engine applies re-reads them, so they are capped.

declare const MAX_WAITING_DELETES = 1024;

moveBlocksBetweenRooms

Move blocks ids of document from to document to, under dest.parent at dest.index (room.move): export at the source, import at the destination, then — with its acknowledgement — delete at the source. A failure before the import leaves both documents as they were (the export is aborted); a failure of the commit leaves the blocks in both and throws: call it again with the same moveId (commitMove is idempotent), or abortMove. Returns the destination ids.

declare const moveBlocksBetweenRooms: (namespace: MoveNamespace, move: {
    from: string;
    to: string;
    ids: string[];
    dest: MoveDestination;
}) => Promise<ImportReceipt>;

MoveDestination

Where imported blocks land in the destination: under parent at index.

export type MoveDestination = {
    parent: string | null;
    index: number;
};

MovedState

One block’s state as a late edit carries it: its content and data.

export type MovedState = {
    content: NonNullable<JSONBlock['content']>;
    data: Record<string, unknown>;
};

MoveNamespace

The rooms a move talks to: a Durable Object namespace of DocumentRooms (or a subclass).

export type MoveNamespace = {
    getByName(name: string): MoveRoom;
};

MoveRoom

The RPC surface of a room a move talks to (a DocumentRoom stub satisfies it).

export type MoveRoom = {
    exportBlocks(ids: string[]): Promise<ExportedBlocks> | ExportedBlocks;
    importBlocks(request: ImportRequest): Promise<ImportReceipt> | ImportReceipt;
    commitMove(moveId: string, receipt: {
        to: string;
        ids: Record<string, string>;
    }): Promise<CommitResult> | CommitResult;
    abortMove(moveId: string): Promise<unknown> | unknown;
    applyLateEdits(batch: LateEditBatch): Promise<{
        applied: number;
        skipped: string[];
    }> | {
        applied: number;
        skipped: string[];
    };
    lateEdits(): Promise<LateEditBatch[]> | LateEditBatch[];
    ackLateEdits(moveId: string, seq: number): Promise<unknown> | unknown;
};

noTimers

Run an entry point with timers forbidden — a pending timer keeps a Durable Object from hibernating.

declare const noTimers: <T>(fn: () => T) => T;

PROBE_HEADER

The header routeDocumentSocket sets on an authorized probe: lastUpdated or snapshot.

declare const PROBE_HEADER = "X-Edytor-Probe";

PURGE_ORIGIN

The transaction origin of the room’s purge: tracked by no history.

declare const PURGE_ORIGIN: unique symbol;

PurgeReport

What one purge wrote.

export type PurgeReport = {
    removed: number;
    emptied: number;
    marks: number;
    records: number;
    candidates: number;
    claims: number;
    merged: number;
};
  • removed: Blocks removed: registry entry deleted, node collected.
  • emptied: Deleted blocks kept (they delimit a live text, or a block is placed under them) whose stream, data and claims were deleted.
  • marks: Text delete mark records deleted.
  • records: Restoration records deleted (their copies collected).
  • candidates: Placement candidates dropped (runner-ups of an old, accepted winner).
  • claims: Claims naming a removed block, deleted from the blocks that hold them.
  • merged: Attribution records of blocks merged away before the horizon (their text shows in the block that claimed them, which unioned their contributors at the merge): deleted, their registry entries kept.

R2_HISTORY_MAX_VALUE_BYTES

The largest value r2History writes (R2 takes more; the room holds a version in memory).

declare const R2_HISTORY_MAX_VALUE_BYTES: number;

R2BucketLike

The structural subset of an R2 bucket r2History uses (a Workers R2Bucket binding satisfies it).

export type R2BucketLike = {
    put(key: string, value: ArrayBuffer | ArrayBufferView, options?: {
        customMetadata?: Record<string, string>;
    }): Promise<unknown>;
    get(key: string): Promise<{
        arrayBuffer(): Promise<ArrayBuffer>;
        customMetadata?: Record<string, string>;
    } | null>;
    list(options?: {
        prefix?: string;
        cursor?: string;
        limit?: number;
        include?: Array<'httpMetadata' | 'customMetadata'>;
    }): Promise<{
        objects: Array<{
            key: string;
            customMetadata?: Record<string, string>;
        }>;
        truncated: boolean;
        cursor?: string;
    }>;
    delete(keys: string | string[]): Promise<unknown>;
};

r2History

Versions in an R2 bucket: one object per version, the room’s metadata and the expiry in its custom metadata (edytor, edytor-expires), a listing by prefix. R2 expires nothing by itself here, so the room’s alarm deletes each version at its expiry (an R2 lifecycle rule on the history/ prefix may also do it, a day late at most). Values up to R2_HISTORY_MAX_VALUE_BYTES.

declare const r2History: (bucket: R2BucketLike) => HistoryStore;

Refusal

export type Refusal = {
    reason: 'generation' | 'schema' | 'malformed' | 'container' | 'identity' | 'replica' | 'read-only' | 'waiting' | 'storage' | 'internal' | 'quota' | 'access' | 'presence' | 'comments' | 'mark' | 'forged' | 'denied' | 'orphan' | 'relayed' | 'history';
    detail: unknown;
};

ReplicaOwner

A client id and the user who owns it (the room’s replica registry). An empty user marks an id a restore without a registry left unowned: its first authenticated writer claims it.

export type ReplicaOwner = {
    replica: number;
    user: string;
};

requestedReplica

The replica a client asked for in ?replica=<doc.clientID> (or param), or null.

declare const requestedReplica: (request: Request, param?: string) => number | null;

RESTORE_ORIGIN

The transaction origin of a history restore: the room’s restore history tracks it.

declare const RESTORE_ORIGIN: unique symbol;

RestoreResult

What restoreHistory did (room.history.restore): refused when the room holds no such version.

export type RestoreResult = {
    status: 'applied' | 'noop' | 'refused';
    key: string;
    revived?: number;
    moved?: number;
    rewritten?: number;
    created?: number;
    deleted?: number;
};

ROOM_HISTORY_MAX_BYTES

declare const ROOM_HISTORY_MAX_BYTES: number;

ROOM_HISTORY_MAX_VALUE_BYTES

roomHistory’s defaults: the largest version, and the most its table holds.

declare const ROOM_HISTORY_MAX_VALUE_BYTES: number;

ROOM_ORIGIN

The transaction origin of the room’s own edits (transact, onLoad seeds).

declare const ROOM_ORIGIN: unique symbol;

roomHistory

Versions in the room’s own SQLite storage, in its history table (edytor_history with attachRoom’s default prefix): no binding. A version is stored atomically (one transactionSync, split into rows of 1 MiB under the platform’s 2 MB). It is counted apart from the document quota (maxDocumentBytes measures what clients send; history is the room’s own), against its own cap, maxBytes (default ROOM_HISTORY_MAX_BYTES): a version that would pass it deletes the oldest versions first. The room’s alarm deletes each version at its expiry. maxValueBytes (default ROOM_HISTORY_MAX_VALUE_BYTES) caps one version. A generation cutover (reset()) keeps the table.

declare const roomHistory: (options?: {
    maxBytes?: number;
    maxValueBytes?: number;
}) => HistoryStoreFactory;

RoomLogEntry

One line of the room’s log: a compaction, a quota hit, a denial, a fault.

export type RoomLogEntry = {
    edytor: 'compaction';
    ms: number;
    bytes: number;
    records: number;
    rows: number;
} | {
    edytor: 'history';
    key: string;
    bytes: number;
    editors: number;
} | {
    edytor: 'restore';
    key: string;
    user: string | null;
    undo: boolean;
} | {
    edytor: 'comment';
    type: CommentChange['type'];
    thread: string;
    user: string;
} | ({
    edytor: 'purge';
    horizon: number;
    bytes: number;
} & PurgeReport) | {
    edytor: 'quota';
    user: string;
    quota: string;
} | {
    edytor: 'size';
    bytes: number;
    limit: number;
    share: number;
} | {
    edytor: 'denied';
    user: string;
    touched: number;
} | {
    edytor: 'semantics';
    user: string;
    kinds: string[];
} | {
    edytor: 'fault';
    reason: 'storage' | 'internal';
    detail: string;
} | {
    edytor: 'move';
    moveId: string;
    role: 'out' | 'in';
    peer: string;
    blocks: number;
} | {
    edytor: 'late';
    moveId: string;
    seq: number;
    edits: number;
    structural: number;
    forwarded: boolean;
};

RoomMetrics

The room’s counters, since this instance started (memory: a wake starts them again), read with metrics() (also over RPC). Times are what the runtime’s clock reports: Workers advance it only across I/O, so in production synchronous work (a compaction, a fold) often reads 0; sizes and counts are exact.

export type RoomMetrics = {
    documentBytes: number;
    documentLimit: number;
    storedBytes: number;
    records: number;
    rows: number;
    updateRecords: number;
    waitingBytes: number;
    sockets: number;
    buffered: {
        sequences: number;
        bytes: number;
    };
    compaction: Timing & {
        lastBytes: number;
    };
    fold: Timing;
    fanOut: {
        messages: number;
        bytes: number;
    };
    quotaHits: number;
    validationDenials: number;
    refusals: Partial<Record<Refusal['reason'], number>>;
    history: {
        written: number;
        skipped: number;
        lastKey: string | null;
    };
    purge: PurgeReport & {
        runs: number;
        horizon: number | null;
    };
    since: number;
};
  • documentBytes: The document’s size as the document quota measures it: its records, uncompressed.
  • documentLimit: The document quota (maxDocumentBytes): writes past it are refused (4413).
  • storedBytes: What its rows take in storage (a snapshot compressed).
  • records: Logical records stored (the generation record included), and their rows.
  • updateRecords: Update records since the last compaction (compaction runs at compactAfter).
  • waitingBytes: Bytes of updates held waiting for a dependency (in memory, never stored).
  • sockets: The document’s open sockets.
  • buffered: Chunk sequences in flight on every socket, and the bytes buffered for them (maxBufferedBytes).
  • compaction: Compactions run, with their time.
  • fold: Client sync frames folded (applied, indexed, stored and relayed), with their time.
  • fanOut: Frames sent to other sockets (relays, presence, acknowledgements to others), and their bytes.
  • quotaHits: Writes refused by a quota (quota refusals) and frames validate denied.
  • refusals: Every refusal, by reason (as refusalCounts).
  • history: Versions written to the history, skipped (history refusals), and the last key written.
  • purge: Purges run and what they wrote, summed; the last horizon purged (ms since the epoch).
  • since: When this instance started (ms since the epoch).

routeDocumentComments

The host Worker’s door to a document’s comment threads, authorized as routeDocumentSocket authorizes a dial, forwarding only the verified identity:

  • GET <url> → every thread and the last change’s sequence number ({ seq, threads }), as JSON;
  • POST <url> with a JSON body ({ op: 'add' | 'reply' | 'resolve' | 'reopen' | 'delete', thread, … }) → what it did ({ status: 'applied', change } or { status: 'noop', thread }), as the verified user.

A refusal is an HTTP status: 400 (an invalid document id or request), 401 ({ expired: true }), 403 (denied, a read-only identity’s write, another user’s comment, an Origin that options.allowedOrigins does not list), 404 (no such thread or comment, or the room keeps no comments), 405 (another method), 409 (a thread or comment id taken), 413 (a body over 64 KiB, too many threads or comments, or the room’s comment bytes full), 429 (the user past the room’s comment rate).

export declare function routeDocumentComments(request: Request, rooms: DocumentNamespace, documentId: string, authorize: AuthorizeDocumentSocket, options?: RouteDocumentOptions): Promise<Response>;

routeDocumentHistory

The host Worker’s door to a document’s version history, authorized as routeDocumentSocket authorizes a dial, forwarding only the verified identity:

  • GET <url> → the versions, newest first (listHistory), as JSON;
  • GET <url>?key=<key> → that version as JSON (readHistory), 404 when the room holds none;
  • POST <url>?restore=<key> → restore it as a forward edit (restoreHistory, as the verified user), 404 when the room holds none;
  • POST <url>?undo → undo the last restore (undoRestore).

A refusal is an HTTP status: 400 (an invalid document id), 401 ({ expired: true }), 403 (denied, or a read-only identity’s restore, or an Origin that options.allowedOrigins does not list), 404 (the room keeps no history), 405 (another method).

export declare function routeDocumentHistory(request: Request, rooms: DocumentNamespace, documentId: string, authorize: AuthorizeDocumentSocket, options?: RouteDocumentOptions): Promise<Response>;

RouteDocumentOptions

Options of routeDocumentSocket and routeDocumentHistory.

export type RouteDocumentOptions = {
    allowedOrigins?: readonly string[] | ((origin: string) => boolean);
};
  • allowedOrigins: The page origins (https://app.example.com, scheme, host and port) whose browser requests may reach the document, or a predicate over the Origin header.

routeDocumentSocket

export declare function routeDocumentSocket(request: Request, rooms: DocumentNamespace, documentId: string, authorize: AuthorizeDocumentSocket, options?: RouteDocumentOptions): Promise<Response>;

SavedDocument

The document as onSave receives it.

export type SavedDocument = {
    value: JSONDoc;
    update: Uint8Array;
    replicas: ReplicaOwner[];
};
  • value: The document as JSON.
  • update: The full v14 state (onLoad takes it back).
  • replicas: Who owns each client id, read with update.

SOCKET_TAG

The tag of the document’s sockets: other sockets of the object are left to you.

declare const SOCKET_TAG = "edytor";

SocketIdentity

What a socket is bound to: its verified user, its replica (Yjs client id), its access.

export type SocketIdentity = {
    user: string;
    replica: number | null;
    readOnly: boolean;
    expiresAt?: number | null;
};
  • replica: null until bound — by authorize, or by the socket’s first presence entry.
  • expiresAt: When its credential expires (ms since the epoch, the room’s clock): the room closes it 4401 then (room.access).

StoredRecord

A stored record, reassembled: its kind, record number and bytes.

export type StoredRecord = {
    kind: RowKind;
    record: number;
    bytes: Uint8Array<ArrayBuffer>;
};

ThreadComment

One comment of a thread.

export type ThreadComment = {
    id: string;
    author: string;
    body: string;
    createdAt: number;
};
  • author: The verified user who wrote it.
  • body: Plain text (line breaks kept).
  • createdAt: ms since the epoch.

Timing

A duration series: how many, their total and longest (ms; see RoomMetrics).

export type Timing = {
    count: number;
    totalMs: number;
    maxMs: number;
    lastMs: number;
};

ValidatedBlock

A block as validate reads it, before or after a frame.

export type ValidatedBlock = {
    id: string;
    type: string;
    data: Record<string, unknown>;
    content: NonNullable<JSONBlock['content']>;
    parent: string | null;
};
  • parent: Its display parent (null: the document’s root).

Was this page helpful?