Skip to content

@repo/datadata/events

The wire protocol between client and server: the event types, their parsers, and the transport constants.

Classes

InvalidRequestError

export declare class InvalidRequestError extends Error

A request the server cannot act on as sent: an id outside its grammar, a frame that fails its shape, a write missing a part it needs. The wire answers it doc:error with category invalidRequest. The client runs the same checks before it sends, so a datadata client meets it as a throw from the call that built the request.

constructor(message: string);

Constructs a new instance of the InvalidRequestError class

WireFrameError

export declare class WireFrameError extends Error

A binary frame that is malformed — truncated, over-long, or wrongly framed.

constructor(message: string);

Constructs a new instance of the WireFrameError class

Functions

assertValidDocIdForType

export declare function assertValidDocIdForType(docId: string, type: string): void;

Enforce the docId ↔ type contract shared by the optimistic client and the authoritative server, so a write either side accepts is one the other accepts too (no optimistic create that the server then rejects). Throws on violation.

- A user docId is an allow-listed id (assertValidUserId / USER_ID_PATTERN): 1–128 characters of letters, digits, - and _. The : is NOT allowed — it is reserved for the system sys: namespace. - The sys: namespace is reserved for system-maintained documents (sys:index, sys:client-docs-status), with one carve-out: schemas live at sys:schema:<docType> and are first-class user-writable documents (that's how custom docTypes get defined). The <docType> after the prefix must satisfy the schema-docType rule: the same colon-free allow-list (which rules out shadow-defining a system docType like sys:schema:sys:index) but a tighter length cap than a user id — a docType is a short identifier. - docId-prefix ↔ type symmetry: docs at sys:schema:* must declare type sys:schema, and sys:schema-typed docs must live at sys:schema:*. Either direction broken means a schema lookup against the doc would crash or silently mis-validate.

Assumes docId and type are strings (callers handle the typeof guard); the charset/length/sys: rules are enforced here, the single shared authority.

clientEventKind

export declare function clientEventKind(event: {
    type: ClientSentEvent["type"];
}): ClientSentEventKind;

Classify a client-sent event into its ClientSentEventKind. Lets a consumer gate by class (e.g. require auth for "write") without re-enumerating the ClientSentEvent union — and stay exhaustive: a new variant that isn't classified fails to compile here rather than falling through a consumer's switch to a runtime "unknown event type" rejection.

decodeWireFrame

export declare function decodeWireFrame(frame: ArrayBufferView | ArrayBuffer): {
    header: unknown;
    blobs: Uint8Array[];
};

Unpack a frame into its parsed header and blob table.

Every length is checked against what remains, so a truncated or hostile frame throws WireFrameError rather than reading past the buffer or allocating on a bogus length. Transports answer that the same way they answer unparseable JSON.

encodeClientSentEvent

export declare function encodeClientSentEvent(event: {
    type: string;
    data: unknown;
    id?: string;
}): ArrayBuffer;

Frame a client-sent event for the wire — the one way a client event crosses a socket. CALLED BY THE TRANSPORT, not the client: the engine and the client core only ever handle Uint8Array lanes, and the framing is nobody else's business. An in-process client hands events over by reference and never comes here.

encodeServerSentEvent

export declare function encodeServerSentEvent(event: {
    type: string;
    data: unknown;
}): ArrayBuffer;

Frame a server-sent event for the wire. See encodeClientSentEvent.

encodeWireFrame

export declare function encodeWireFrame(header: unknown, blobs: readonly Uint8Array[]): ArrayBuffer;

Pack a header and its blobs into a frame. Prefer encodeClientSentEvent or encodeServerSentEvent, which build the index from the event's schema; this is the layer below them.

isDatadataClientSentEvent

export declare function isDatadataClientSentEvent(event: {
    type: string;
}): event is ClientSentEvent;

Whether event.type is one of the ClientSentEvent types. Checks the type only, not the payload.

isPresenceDocId

export declare function isPresenceDocId(docId: string): boolean;

Whether docId is a presence channel document id (starts with PRESENCE_DOC_ID_PREFIX).

isPresenceViewDocId

export declare function isPresenceViewDocId(docId: string): boolean;

Whether docId is a presence view document id (starts with PRESENCE_VIEW_DOC_ID_PREFIX).

isWireFrame

export declare function isWireFrame(frame: ArrayBufferView | ArrayBuffer): boolean;

Whether frame is one of ours. A binary message that is not a container is a protocol error, not a dialect to fall back on.

parseClientSentEvent

export declare function parseClientSentEvent(data: unknown): ClientSentEventEnvelope;

Throwing envelope check, implemented in terms of safeParseClientSentEvent so the two share one validator and never disagree on what an envelope is. Throws on a malformed envelope; callers that prefer to branch should use the safe twin.

parseClockPingEventData

parseClockPingEventData: (data: unknown) => {}

Parse and validate a clock:ping payload, which must be an empty object.

parseClockPongEventData

parseClockPongEventData: (data: unknown) => {
    clientEventId: string;
    serverTime: number;
}

Parse and validate a clock:pong payload.

parseDocumentCreateEventData

parseDocumentCreateEventData: (data: unknown) => {
    data: Record<string, import("../schema/schema.js").JsonValue>;
    docId: string;
    generation?: string | undefined;
    name?: string | undefined;
    schemaSequence?: number | undefined;
    type: string;
    yjsDocs: Record<string, Uint8Array<ArrayBufferLike>>;
}

Parse and validate a doc:create payload. The document data is checked against its type's schema later, not here.

parseDocumentDeletedEventData

parseDocumentDeletedEventData: (data: unknown) => {
    clientEventId: string | null;
    deletedAt: number;
    docId: string;
    generation: string;
    type: string;
}

Parse and validate a doc:deleted payload.

parseDocumentDeleteEventData

parseDocumentDeleteEventData: (data: unknown) => {
    baseSequence?: number | undefined;
    docId: string;
    generation?: string | undefined;
    guard?: "sequence" | undefined;
}

Parse and validate a doc:delete payload.

parseDocumentErrorEventData

parseDocumentErrorEventData: (data: unknown) => {
    category: "alreadyExists" | "commandRefused" | "deleted" | "discarded" | "invalidRequest" | "notFound" | "preconditionFailed" | "queueFull" | "schemaValidation" | "sizeLimitExceeded" | "storageError" | "timeout" | "unauthorized" | "unconfirmed" | "unknown" | "unknownType" | "yjsCorruption";
    clientEventId: string | null;
    code?: string | undefined;
    docId: string;
    message: string;
}

Parse and validate a doc:error payload.

parseDocumentEventsEventData

parseDocumentEventsEventData: (data: unknown) => {
    clientEventId: string | null;
    docId: string;
    events: {
        actor: string | null;
        createdAt: number;
        patch: string;
        schemaSequence: number;
        sequence: number;
        subject: string | null;
    }[];
    generation: string;
    hasMore: boolean;
    yjsEvents: {
        action: "create" | "delete" | "update";
        actor: string | null;
        blob: Uint8Array<ArrayBufferLike> | null;
        createdAt: number;
        subject: string | null;
        yjsId: string;
        yjsSequence: number;
    }[];
}

Parse and validate a doc:events payload.

parseDocumentGetEventsEventData

parseDocumentGetEventsEventData: (data: unknown) => {
    afterSequence?: number | undefined;
    afterYjsSequence?: number | undefined;
    docId: string;
    limit?: number | undefined;
}

Parse and validate a doc:get-events payload.

parseDocumentGetProcessedWritesEventData

parseDocumentGetProcessedWritesEventData: (data: unknown) => {
    docId: string;
    eventIds: string[];
}

Parse and validate a doc:get-processed-writes payload.

parseDocumentInitEventData

parseDocumentInitEventData: (data: unknown) => {
    clientEventId: string | null;
    conformedSchemaSequence?: number | undefined;
    data: Record<string, import("../schema/schema.js").JsonValue>;
    docId: string;
    epoch?: string | undefined;
    generation?: string | undefined;
    invalid?: {
        issues: {
            kind: "reference" | "schema";
            message: string;
            path: (string | number)[];
            reference?: {
                rule: string;
                targetDocId: string;
            } | undefined;
        }[];
        message: string;
    } | undefined;
    listing?: {
        name: string | null;
    } | undefined;
    sequence: number;
    type: string;
    yjsDeleted?: string[] | undefined;
    yjsDiffs?: Record<string, Uint8Array<ArrayBufferLike>> | undefined;
    yjsDocs: Record<string, Uint8Array<ArrayBufferLike>>;
    yjsServerStateVectors?: Record<string, Uint8Array<ArrayBufferLike>> | undefined;
}

Parse and validate a doc:init payload.

parseDocumentNotFoundEventData

parseDocumentNotFoundEventData: (data: unknown) => {
    clientEventId: string | null;
    docId: string;
}

Parse and validate a doc:notfound payload.

parseDocumentPatchEventData

parseDocumentPatchEventData: (data: unknown) => {
    clientEventId: string | null;
    conformedSchemaSequence?: number | undefined;
    docId: string;
    epoch?: string | undefined;
    patch: ({
        op: "add";
        path: string;
        value: import("../schema/schema.js").JsonValue;
    } | {
        from: string;
        op: "copy";
        path: string;
    } | {
        from: string;
        op: "move";
        path: string;
    } | {
        op: "remove";
        path: string;
    } | {
        op: "replace";
        path: string;
        value: import("../schema/schema.js").JsonValue;
    } | {
        op: "test";
        path: string;
        value: import("../schema/schema.js").JsonValue;
    })[];
    sequence: number;
    yjsDeleted?: string[] | undefined;
    yjsUpdates?: {
        action: "create" | "delete" | "update";
        update?: Uint8Array<ArrayBufferLike> | undefined;
        yjsId: string;
    }[] | undefined;
}

Parse and validate a doc:patch payload.

parseDocumentProcessedWritesEventData

parseDocumentProcessedWritesEventData: (data: unknown) => {
    clientEventId: string | null;
    docId: string;
    processed: {
        eventId: string;
        sequence: number;
    }[];
}

Parse and validate a doc:processed-writes payload.

parseDocumentPurgeEventData

parseDocumentPurgeEventData: (data: unknown) => {
    docIds: string[];
}

Parse and validate a doc:purge payload. The batch size limit (PURGE_BATCH_LIMIT) is enforced by the server, not here.

parseDocumentRenameEventData

parseDocumentRenameEventData: (data: unknown) => {
    docId: string;
    generation?: string | undefined;
    name: string | null;
}

Parse and validate a doc:rename payload.

parseDocumentRestoreEventData

parseDocumentRestoreEventData: (data: unknown) => {
    docId: string;
    generation?: string | undefined;
}

Parse and validate a doc:restore payload.

parseDocumentResumeEventData

parseDocumentResumeEventData: (data: unknown) => {
    clientEventId: string | null;
    conformedSchemaSequence?: number | undefined;
    docId: string;
    sequence: number;
    yjsDeleted?: string[] | undefined;
    yjsDiffs?: Record<string, Uint8Array<ArrayBufferLike>> | undefined;
    yjsServerStateVectors?: Record<string, Uint8Array<ArrayBufferLike>> | undefined;
}

Parse and validate a doc:resume payload.

parseDocumentResyncEventData

parseDocumentResyncEventData: (data: unknown) => {
    clientEventId: string | null;
    docId: string;
}

Parse and validate a doc:resync payload.

parseDocumentSubscribeEventData

parseDocumentSubscribeEventData: (data: unknown) => {
    docId: string;
    generation: string | null;
    sequence: number | null;
    yjsStateVectors?: Record<string, Uint8Array<ArrayBufferLike>> | undefined;
}

Parse and validate a doc:subscribe payload, filling sequence and generation with null when absent.

parseDocumentUnsubscribeEventData

parseDocumentUnsubscribeEventData: (data: unknown) => {
    docId: string;
}

Parse and validate a doc:unsubscribe payload.

parseDocumentUpdateEventData

parseDocumentUpdateEventData: (data: unknown) => {
    baseSequence?: number | undefined;
    docId: string;
    generation?: string | undefined;
    guard?: ("patch" | "sequence") | undefined;
    patch: ({
        op: "add";
        path: string;
        value: import("../schema/schema.js").JsonValue;
    } | {
        from: string;
        op: "copy";
        path: string;
    } | {
        from: string;
        op: "move";
        path: string;
    } | {
        op: "remove";
        path: string;
    } | {
        op: "replace";
        path: string;
        value: import("../schema/schema.js").JsonValue;
    } | {
        op: "test";
        path: string;
        value: import("../schema/schema.js").JsonValue;
    })[];
    schemaSequence?: number | undefined;
    yjsUpdates?: {
        action: "create" | "delete" | "update";
        update?: Uint8Array<ArrayBufferLike> | undefined;
        yjsId: string;
    }[] | undefined;
}

Parse and validate a doc:update payload.

parsePresenceDocId

export declare function parsePresenceDocId(docId: string): {
    presenceType: string;
    docId: string;
} | null;

Split a presence or presence-view document id into its presenceType and target docId. Returns null for any other id, and for a malformed one: a missing segment, a presenceType no presence schema could be declared under, or a target id over the length limit.

parseServerSentEvent

export declare function parseServerSentEvent(data: unknown): ServerSentEventEnvelope;

Throwing envelope check, implemented in terms of safeParseServerSentEvent so the two share one validator and never disagree on what an envelope is. Throws on a malformed envelope; callers that prefer to branch should use the safe twin.

presenceDocIdForView

export declare function presenceDocIdForView(viewDocId: string): string;

The presence channel id a presence view id projects. Expects an id isPresenceViewDocId accepts.

presenceSchemaDocId

export declare function presenceSchemaDocId(presenceType: string): string;

Id of the schema document that declares the presence channel type presenceType: sys:schema:presence:<presenceType>. A channel of that type exists only while this schema is declared.

presenceSchemaType

export declare function presenceSchemaType(presenceType: string): string;

The schema document type a presence channel type is declared under: presence:<presenceType>.

presenceSchemaTypeOfDocId

export declare function presenceSchemaTypeOfDocId(docId: string): string | null;

The schema document type (presence:<presenceType>) governing a presence or presence view document, read from the id alone. null when the id is not a well-formed presence id.

principalDocumentData

export declare function principalDocumentData(principal: Principal): PrincipalDocument;

Serialize a Principal as sys:principal document data. Canonicalizes the two nullish-vs-present distinctions: grants/scopes are OMITTED when absent (and scopes: null — the other unattenuated spelling — is omitted too), while an EMPTY scopes array survives verbatim (deny-all is a real value, not absence). The result is structurally a Principal, so the adopting client uses it as-is.

Every AccessScope axis must be copied here (and declared in the schema above): the client predicts from THIS document, so an axis missing from the projection is enforced by the server but invisible to the client — which would predict a WIDER permission set than it holds. Adding an axis to AccessScope without touching this function is the one way to break prediction silently.

replayData

export declare function replayData(events: readonly ReplayableEvent[], from?: Record<string, unknown>): Record<string, unknown>;

Fold a document's JSON-patch log over the genesis state ({}) in doc_sequence order, reproducing the document's data. from resumes an earlier fold of the log's prefix.

This is the production form of the fold previously inlined in the test-only assertDocumentConsistency: the executable form of the spec's DataIsReplayOfLog / PerDocumentReplay. A document's data is reproducible from its own log alone — migration-driven changes are themselves logged events (MigrationsAreLogged), so the schema document is not needed to reconstruct the current state.

Throws

if any patch fails to apply (a corrupt or out-of-order log).

replayYjs

export declare function replayYjs(yjsEvents: readonly ReplayableYjsEvent[]): Record<string, Uint8Array>;

Reconstruct each Yjs sub-document by folding the Yjs lane's updates in yjs_sequence order, returning the encoded state per yjsId — mirroring the stored yjs_docs full states. create/update apply the update; delete removes the yjsId (ServerCleansUpRemovedYjsDoc).

Equality of the result is **logical** (compare via yDocToJSON), not byte-for-byte: Yjs is a CRDT, so the encoded state vector is not guaranteed identical across nodes or apply-orders even when the documents are logically equal.

resolveClientSentEventBlobs

export declare function resolveClientSentEventBlobs(type: string, data: unknown, blobs: readonly Uint8Array[]): void;

Resolve a received frame's blob indices back into the Yjs lanes, in place. The receiving twin of encodeClientSentEvent; runs before the variant parse, so an unrecognized type or a payload whose shape disagrees with the schema is left alone for that parse to reject with its own error and path.

resolveServerSentEventBlobs

export declare function resolveServerSentEventBlobs(type: string, data: unknown, blobs: readonly Uint8Array[]): void;

Resolve a received frame's blob indices into the Yjs lanes, in place.

safeParseClientSentEvent

export declare function safeParseClientSentEvent(value: unknown): ValidatorResult<ClientSentEventEnvelope>;

Non-throwing envelope check: returns { ok: true, value } for an envelope-shaped value, { ok: false, error } otherwise. The server-side twin of safeParseServerSentEvent, letting a handler branch on the result instead of wrapping the call in try/catch.

WHY HAND-WRITTEN, not a DocumentSchema parser like every other wire parse. The declarative json value kind does not merely check a payload, it WALKS and COPIES it — so an envelope that declared data (it was record<json>) paid a full O(payload) recursion on every inbound event, ahead of every wire bound in server/wire-bounds.ts and duplicated immediately afterwards by the variant parser that walks the same payload again, with the shape knowledge to actually judge it. Measured at ~1.1 s for a 10 MB Yjs delta or a 1M-op patch, all of it before the caps that exist to stop exactly that.

The envelope's job is only "which variant is this, and is there a payload to hand it?", so it reads three fields and stops: O(1) in the payload's size. That is safe ONLY because the payload is JSON-checked downstream WITHOUT exception — every variant, doc:subscribe and doc:unsubscribe included, now has a parser. Do not reintroduce a case that reads parsedEvent.data directly; there is no longer an upstream walk backing it up.

Extra top-level keys are rejected rather than ignored, matching the extras: "reject" the schema-based envelope enforced (and every variant parser still does) — the envelope is a closed shape, and the cost is Object.keys over three entries.

Failures are built with the validator's own fail, so an envelope rejection carries the same error/issues shape and the same wording a schema rejection would — being hand-written changes what is checked, not how a caller reads the answer.

safeParseServerSentEvent

export declare function safeParseServerSentEvent(value: unknown): ValidatorResult<ServerSentEventEnvelope>;

Non-throwing envelope check: returns { ok: true, value } for an envelope-shaped value, { ok: false, error } otherwise. Lets a per-frame WebSocket handler branch on the result instead of wrapping every call in try/catch.

WHY HAND-WRITTEN — the same two reasons as its client-sent twin (safeParseClientSentEvent), which this now mirrors.

1. COST. It declared data as record<json>, and the declarative json kind does not merely check a payload, it WALKS and COPIES it — an O(payload) recursion on every inbound frame, ahead of the variant parser that walks the same payload again with the shape knowledge to actually judge it. The client-sent envelope on the server had already shed exactly that cost; the same cost sat here, unnoticed, on the client.

2. CORRECTNESS, once the Yjs lanes became bytes. A Uint8Array is not a JsonValue, so record<json> REJECTED every doc:init carrying Yjs content — and because an envelope rejection is a silent drop by design (malformed wire data is filtered, not thrown), the document simply never arrived. A dispatch envelope must not have an opinion about a payload it exists only to route.

The envelope's job is "which variant is this, and is there a payload to hand it?", so it reads two fields and stops: O(1) in the payload's size. Safe because every variant has its own parser downstream. Extra top-level keys are rejected rather than ignored, matching the extras: "reject" the schema-based envelope enforced. Failures are built with the validator's own fail, so a rejection carries the same error/issues shape a schema rejection would.

yDocEntries

export declare function yDocEntries(doc: Y.Doc): YjsEntry[];

The top-level shared types of a Y.Doc, each tagged with its YjsEntryKind and projected to JSON. This is the typed form behind yDocToJSON: a viewer needs the kind (to render a text note differently from a map), while a plain JSON snapshot only needs the values.

yDocToJSON

export declare function yDocToJSON(doc: Y.Doc): Record<string, unknown>;

Project a Y.Doc to a plain JSON object, one property per top-level shared type: a string for text and XML, an object for a map, an array for an array. Throws on a shared type it cannot classify.

Interfaces

ClientSentEventEnvelope

export interface ClientSentEventEnvelope

The dispatch envelope: the routing fields (id, type) plus an unexamined data payload. data is typed Record<string, unknown> — a payload, not yet a shape — and every variant re-parses it against its own schema downstream once type selects the variant.

data: Record<string, unknown>;

The event payload, not yet checked against the variant's shape.

id: string;

The client-minted event id; the server echoes it as clientEventId on its answer.

type: string;

The event type, e.g. "doc:update"; selects the variant parser for data.

DeletedDocumentEntry

export interface DeletedDocumentEntry

One soft-deleted document as a storage adapter lists it: a summary, not the document's data. The trash listing is built from these.

deletedAt: number;

When the document was deleted, in milliseconds since the Unix epoch.

docId: string;

The deleted document's id.

generation: string;

The deleted document's generation; a restore naming another generation is refused.

name: string | null;

The document's index name when it was deleted; null when unnamed.

type: string;

The deleted document's type.

DocumentErrorCategoryTraits

export interface DocumentErrorCategoryTraits

How to handle a DocumentErrorCategory, independent of what an app shows for it. Every category is in DOCUMENT_ERROR_CATEGORY_TRAITS, so a category added later arrives with its traits and an app that branches on them needs no change.

readonly appFault: boolean;

A bug or a misconfiguration on the app's side, which neither retrying nor the user can fix (schemaValidation, invalidRequest, unknownType, yjsCorruption): the data or the request breaks a rule the app is expected to follow, or the client and server disagree on the schema. commandRefused is not marked, since most refusals are the mutator's domain outcome; its reserved COMMAND_REFUSAL_CODES (an unknown command, invalid args, a mutator that threw) are app faults.

readonly outcomeUnknown: boolean;

Whether the write took effect is not known (timeout, unconfirmed): it may have committed. Read the document again before retrying or telling the user it failed. Every other category reports a write the server refused or failed. A re-send carries the write's client event id, which the server de-duplicates, so retrying a storageError or unknown never applies a write twice.

readonly retryable: boolean;

The failure is transient: the same request can succeed later with nothing else changed (storageError, unknown, queueFull, timeout, unconfirmed). Every other category fails the same way again until the document, the data or the setup changes. When DocumentErrorCategoryTraits.outcomeUnknown is also set, refetch before retrying.

ServerSentEventEnvelope

export interface ServerSentEventEnvelope

The dispatch envelope: { type, data }.

data: Record<string, unknown>;

The event payload, not yet checked against the variant's shape.

type: string;

The event type, e.g. "doc:patch"; selects the variant parser for data.

YjsEntry

export interface YjsEntry

One top-level shared type of a Y.Doc, tagged with its kind and projected to JSON.

key: string;

The shared type's top-level key (the name passed to getText/getMap/…).

kind: YjsEntryKind;

Which kind of shared type it is, and so what shape value has.

value: unknown;

The JSON projection of this shared type: a string for text and xml (XML serialises to its markup), a plain object for map, an array for array.

Types

BytesPath

export type BytesPath = readonly string[];

A position a schema places bytes at, as path segments from the value root. The segment "*" stands for "every key of this record" or "every element of this array" — which is what makes one path cover yjsDocs.<any yjsId> and yjsUpdates[<any index>].update alike.

ClientSentEvent

export type ClientSentEvent = DocumentSubscribeEvent | DocumentUnsubscribeEvent | DocumentCreateEvent | DocumentUpdateEvent | DocumentCommandEvent | DocumentDeleteEvent | DocumentRestoreEvent | DocumentPurgeEvent | DocumentRenameEvent | DocumentGetEventsEvent | DocumentGetProcessedWritesEvent | ClockPingEvent;

Every event a client sends to the server, discriminated by type.

ClientSentEventKind

export type ClientSentEventKind = "read" | "write";

The coarse class of a client-sent event: "read" — subscribe/unsubscribe/get-events/get-processed-writes and the clock:ping: no document mutation. "write" — create/update/command/delete/restore/rename: mutates the folder or a document; the class a consumer typically gates behind auth.

Presence is no longer a class of its own: publishing presence is a doc:update on a sys:presence:<presenceType>:<docId> document (a "write" by type), which the server routes to the ephemeral cell registry by docId rather than the stored-document write path. A consumer gating writes behind auth that wants to exempt presence keys off the docId (isPresenceDocId), not the event class.

ClockPingEvent

export type ClockPingEvent = ClientSentEventBase<"clock:ping", Record<string, never>>;

Ask for the server's clock, answered with clock:pong to this connection only. Carries no data: the envelope id pairs the answer with the ping. Sent only by a client that started its clock.

ClockPongEvent

export type ClockPongEvent = ServerSentEventBase<"clock:pong", InferJsonSchema<typeof ClockPongEventDataSchema>>;

The answer to clock:ping: serverTime is the server's clock in milliseconds since the Unix epoch, and clientEventId is the ping's id. Sent to the asking connection only.

CommandGuardMode

export type CommandGuardMode = (typeof COMMAND_GUARD_MODES)[number];

Opt-in concurrency control on a domain command: sequence runs the command only if the document is still at its base sequence, refusing it with preconditionFailed otherwise.

DeleteGuardMode

export type DeleteGuardMode = (typeof DELETE_GUARD_MODES)[number];

Opt-in concurrency control on a delete: only sequence, which deletes only if the document is still at its base sequence. A delete has no patch, so there is no patch mode.

DiscardedWriteReason

export type DiscardedWriteReason = (typeof DISCARDED_WRITE_REASONS)[number];

Why a queued offline write was discarded instead of re-sent: retentionExpired, it outlived ClientConfig.persistedWriteMaxAgeMs; or replayHorizon, it was sent but unconfirmed for longer than MAX_REPLAY_AGE_MS, so its outcome is unknown.

DocumentCommandEvent

export type DocumentCommandEvent = ClientSentEventBase<"doc:command", {
    docId: string;
    name: string;
    args: unknown;
    baseSequence?: number;
    generation?: string;
    guard?: CommandGuardMode;
}>;

Execute the domain command name with args against a document (defineCommands). Without a guard the command runs once against the document as it then is; guard: "sequence" refuses it with preconditionFailed unless the document is still at baseSequence. A command the server does not know for the document's type, args that fail its shape, or a mutator refusal are answered with a doc:error of category commandRefused carrying the refusal code.

DocumentCreateEvent

export type DocumentCreateEvent = ClientSentEventBase<"doc:create", DocumentCreateEventData>;

Create a document. Answered with a doc:init, or a doc:error (e.g. alreadyExists).

DocumentCreateEventData

export type DocumentCreateEventData = InferJsSchema<typeof DocumentCreateEventDataSchema>;

Payload of doc:create: the new document's id, type, initial data and initial Yjs sub-document states. name seeds its index name; generation, when given, is the generation the client minted for its optimistic copy, which the server adopts; schemaSequence is the schema sequence data was written against, so the server can migrate it forward.

DocumentDeletedEvent

export type DocumentDeletedEvent = ServerSentEventBase<"doc:deleted", InferJsonSchema<typeof DocumentDeletedEventDataSchema>>;

The document is soft-deleted: pushed to subscribers when a delete commits, and the answer to a subscribe to a deleted document. Carries the deleted document's type and generation, which a later restore is aimed at. The subscription stays registered, so a restore pushes a fresh doc:init.

DocumentDeleteEvent

export type DocumentDeleteEvent = ClientSentEventBase<"doc:delete", {
    docId: string;
    baseSequence?: number;
    generation?: string;
    guard?: DeleteGuardMode;
}>;

Soft-delete a document: it leaves the index and the read path but stays restorable. baseSequence, generation and the "sequence" guard work as on DocumentUpdateEvent.

DocumentError

export type DocumentError = InferValue<typeof DocumentErrorValue>;

An error on a document: its DocumentErrorCategory and a human-readable message.

DocumentErrorCategory

export type DocumentErrorCategory = (typeof DOCUMENT_ERROR_CATEGORIES)[number];

Why an operation on a document failed. Sent by the server: schemaValidation, invalidRequest (the request is malformed: an invalid id, a frame that fails its shape), unknownType (no schema declares the type), yjsCorruption, storageError, sizeLimitExceeded, alreadyExists, preconditionFailed (a guarded write's precondition no longer holds; refetch and rebase), commandRefused (a domain command was refused), deleted (the document is soft-deleted), unauthorized, notFound and unknown. Raised by the client itself, never sent: queueFull (the offline write queue is at its cap), timeout and unconfirmed (outcome unknown; refetch before retrying), and discarded (local content was dropped before it could be sent). notFound is also raised by the client when losing read access settles a write it never sent; a sent one settles unconfirmed. DOCUMENT_ERROR_CATEGORY_TRAITS says how each one should be handled.

DocumentErrorEvent

export type DocumentErrorEvent = ServerSentEventBase<"doc:error", InferJsonSchema<typeof DocumentErrorEventDataSchema>>;

A client event on the document was rejected; category is a DocumentErrorCategory.

DocumentEventsEvent

export type DocumentEventsEvent = ServerSentEventBase<"doc:events", InferJsSchema<typeof DocumentEventsEventDataSchema>>;

One page of a document's event history, the answer to doc:get-events: the JSON-lane events and the Yjs-lane yjsEvents, each in its own sequence order. hasMore says whether rows remain. generation is the generation the page was read from; a cursor from another generation means nothing in this one.

DocumentGetEventsEvent

export type DocumentGetEventsEvent = ClientSentEventBase<"doc:get-events", {
    docId: string;
    afterSequence?: number;
    afterYjsSequence?: number;
    limit?: number;
}>;

Read one page of a document's event history, answered with doc:events. afterSequence and afterYjsSequence are exclusive cursors into the JSON and Yjs lanes (absent: from the start); a page fills from the JSON lane first. limit caps the rows across both lanes and defaults to, and is clamped at, GET_EVENTS_PAGE_LIMIT.

DocumentGetProcessedWritesEvent

export type DocumentGetProcessedWritesEvent = ClientSentEventBase<"doc:get-processed-writes", {
    docId: string;
    eventIds: string[];
}>;

Ask which of the client's own writes on a document, by event id, the server has already processed. Answered with doc:processed-writes. The client sends it before re-sending pending writes, so it can tell which ones landed and keep their order.

DocumentInitEvent

export type DocumentInitEvent<TData extends Record<string, unknown> = Record<string, unknown>> = ServerSentEventBase<"doc:init", Omit<DocumentInitEventData, "data"> & {
    data: TData;
}>;

A document's full state: the answer to a subscribe or create, and the push after a restore.

DocumentInitEventData

export type DocumentInitEventData = InferJsSchema<typeof DocumentInitEventDataSchema>;

Payload of doc:init: a document's full JSON state at sequence, plus its Yjs sub-documents — as full states in yjsDocs, or as diffs against the subscriber's state vectors in yjsDiffs. clientEventId is the id of the client event this answers, or null when it answers none. invalid is present when the data does not conform to its current schema (it is still delivered). generation and conformedSchemaSequence are absent on synthesized documents.

DocumentNotFoundEvent

export type DocumentNotFoundEvent = ServerSentEventBase<"doc:notfound", InferJsonSchema<typeof DocumentNotFoundEventDataSchema>>;

The document does not exist, or is not readable by this connection. Distinct from DocumentDeletedEvent.

DocumentPatchEvent

export type DocumentPatchEvent = ServerSentEventBase<"doc:patch", Omit<DocumentPatchEventData, "patch"> & {
    patch: Operation[];
}>;

One committed write, broadcast to every subscriber; to the writer it is also the acknowledgement.

DocumentPatchEventData

export type DocumentPatchEventData = InferJsSchema<typeof DocumentPatchEventDataSchema>;

Payload of doc:patch: the JSON Patch and Yjs updates of one write, and the document's sequence after it. A Yjs-only patch (empty patch) repeats the unchanged sequence. yjsDeleted lists Yjs sub-documents the write removed. clientEventId is the id of the client event that made the write (every subscriber sees the same id, so the writer recognises its acknowledgement), or null for a write no client event made.

DocumentProcessedWritesEvent

export type DocumentProcessedWritesEvent = ServerSentEventBase<"doc:processed-writes", InferJsSchema<typeof DocumentProcessedWritesEventDataSchema>>;

The answer to doc:get-processed-writes: of the event ids asked about, those the server has processed for the document, each with the sequence its write was answered at. An id not listed was not processed within PROCESSED_WRITE_RETENTION_MS. Sent to the asking connection only.

DocumentPurgeEvent

export type DocumentPurgeEvent = ClientSentEventBase<"doc:purge", {
    docIds: string[];
}>;

Permanently delete a batch of soft-deleted documents and release their ids. All or nothing: if any id is not purgeable by this principal, the whole batch is rejected with one doc:error. Success shows as one patch removing the entries from the trash listing.

DocumentRenameEvent

export type DocumentRenameEvent = ClientSentEventBase<"doc:rename", {
    docId: string;
    name: string | null;
    generation?: string;
}>;

Set a document's index name; name: null clears it. The name is index metadata, so a rename does not touch the document's data or sequence. Last-writer-wins; a generation the document no longer has is refused as not found.

DocumentRestoreEvent

export type DocumentRestoreEvent = ClientSentEventBase<"doc:restore", {
    docId: string;
    generation?: string;
}>;

Undo a soft delete: the document re-enters the index and subscribers receive a fresh doc:init. Restoring a live document is acknowledged with its current state; an id that never existed, or a generation the document no longer has, is answered with doc:notfound.

DocumentResumeEvent

export type DocumentResumeEvent = ServerSentEventBase<"doc:resume", InferJsSchema<typeof DocumentResumeEventDataSchema>>;

Answers a subscribe whose copy is already at the latest JSON sequence, confirming it without re-sending the document. Carries the Yjs diffs the client's state vectors were missing, and the server's own state vectors so the client can send back what the server lacks.

DocumentResyncEvent

export type DocumentResyncEvent = ServerSentEventBase<"doc:resync", InferJsonSchema<typeof DocumentResyncEventDataSchema>>;

Sent only for presence documents, when the server lost its in-memory presence (a restart or wake from hibernation): subscribers republish their own cells. Unlike an empty doc:init it does not clear the peers a client is showing.

DocumentStatus

export type DocumentStatus = (typeof DOCUMENT_STATUSES)[number];

A document's load state in the client's status document: pending until the server answers, then available, notFound, deleted (soft-deleted), or error.

DocumentSubscribeEvent

export type DocumentSubscribeEvent = ClientSentEventBase<"doc:subscribe", DocumentSubscribeEventData>;

Subscribe to a document. The server answers with doc:init, doc:resume, doc:notfound or doc:deleted, then streams doc:patch events.

DocumentSubscribeEventData

export type DocumentSubscribeEventData = InferJsSchema<typeof DocumentSubscribeEventDataSchema>;

Payload of doc:subscribe: the document to subscribe to plus the cursors of the copy the client already holds. sequence and generation are null when it holds none. yjsStateVectors (one state vector per Yjs sub-document) lets the server answer the Yjs lanes with diffs; without it the server sends full states.

DocumentUnsubscribeEvent

export type DocumentUnsubscribeEvent = ClientSentEventBase<"doc:unsubscribe", DocumentUnsubscribeEventData>;

End a subscription made with DocumentSubscribeEvent.

DocumentUnsubscribeEventData

export type DocumentUnsubscribeEventData = InferJsonSchema<typeof DocumentUnsubscribeEventDataSchema>;

Payload of doc:unsubscribe: the document to stop receiving events for.

DocumentUpdateEvent

export type DocumentUpdateEvent = ClientSentEventBase<"doc:update", {
    docId: string;
    baseSequence?: number;
    generation?: string;
    guard?: GuardMode;
    schemaSequence?: number;
    patch: Operation[];
    yjsUpdates?: YjsUpdate[];
}>;

Apply a JSON Patch and/or Yjs updates to a document. Without a guard the write is last-writer-wins. guard: "sequence" applies it only if the document is still at baseSequence; guard: "patch" keeps the patch's test operations and rejects the write if one fails. A rejected guard is answered with a doc:error of category preconditionFailed. A generation the document no longer has is refused as not found.

ExportedYjsEvent

export type ExportedYjsEvent = Omit<StoredYjsEvent, "yjsSequence">;

A Yjs-lane log entry as a document export carries it: a StoredYjsEvent without yjsSequence, since position in the array carries the order. Serialize an export with serializeDocumentExport rather than JSON.stringify, which mangles the bytes.

GuardMode

export type GuardMode = (typeof GUARD_MODES)[number];

Opt-in concurrency control on a document update. sequence applies the write only if the document is still at its base sequence; patch keeps the patch's test operations and applies the write only if they all still hold, tolerating unrelated changes.

Operation

export type Operation = {
    op: "add";
    path: string;
    value: unknown;
} | {
    op: "remove";
    path: string;
} | {
    op: "replace";
    path: string;
    value: unknown;
} | {
    op: "move";
    path: string;
    from: string;
} | {
    op: "copy";
    path: string;
    from: string;
} | {
    op: "test";
    path: string;
    value: unknown;
};

A JSON Patch operation as datadata holds one: the wire's shape (RFC 6902, no extensions). Its value is unknown until something reads it — on the wire it is JSON, and the apply refuses anything else. @dossierhq/json-patch's own Operation (readonly, its value JsonValue) is assignable to it: a diff's patch is one.

PresenceCell

export type PresenceCell = InferValue<typeof PresenceCellValue>;

One client's cell in a presence channel, stored under its presence id. subject is stamped by the server from the connection (null when it has none), so a client cannot claim another's identity; state is application-defined JSON the server relays without interpreting.

PresenceEntry

export type PresenceEntry = InferValue<typeof PresenceEntryValue>;

A PresenceCell together with its presenceId: the shape of each peer in a session's presence view. The id lets a client recognise its own cell.

ReplayableEvent

export type ReplayableEvent = Pick<StoredDocumentEvent, "patch">;

The minimal JSON-lane event shape replay needs: the patch string. This is the persisted/wire shape — exactly what the storage adapter's getDocumentEvents returns and what per-document export/import ships — so every real consumer already holds it.

Events MUST be supplied in doc_sequence order (the order getDocumentEvents returns them). Replay reads one document's events only; it needs nothing from any other document (PerDocumentReplay).

ReplayableYjsEvent

export type ReplayableYjsEvent = Pick<StoredYjsEvent, "yjsId" | "action" | "blob">;

The minimal Yjs-lane event shape replay needs. Supplied in yjs_sequence order (the order getYjsEvents returns them) — the order only carries meaning across a delete/recreate boundary; within a yjsId's life the CRDT merge is order-insensitive.

ServerSentEvent

export type ServerSentEvent = DocumentInitEvent | DocumentResumeEvent | DocumentPatchEvent | DocumentNotFoundEvent | DocumentDeletedEvent | DocumentErrorEvent | DocumentEventsEvent | DocumentProcessedWritesEvent | DocumentResyncEvent | ClockPongEvent;

Every event the server sends to a client, discriminated by type.

StoredDocumentEvent

export type StoredDocumentEvent = InferValue<typeof StoredDocumentEventValue>;

One entry of a document's JSON-lane log: the JSON Patch (as a JSON string) that took the document to sequence, who wrote it (subject, actor; null when unknown), when (createdAt, milliseconds since the Unix epoch), and the schemaSequence of the schema it was written against.

StoredYjsEvent

export type StoredYjsEvent = InferValue<typeof StoredYjsEventValue>;

One entry of a document's Yjs-lane log, ordered by yjsSequence (1, 2, … per document, independent of the JSON lane's sequence). blob holds the Yjs update bytes, null only for a delete. subject, actor and createdAt attribute it as on StoredDocumentEvent.

SubscriptionState

export type SubscriptionState = (typeof SUBSCRIPTION_STATES)[number];

A document's subscription state in the client's status document: subscribing until the server answers, then subscribed. preloaded means the document is held without a subscription of its own, for example loaded from the persisted cache.

SystemValidatorRegistry

export type SystemValidatorRegistry = typeof SYSTEM_VALIDATOR_REGISTRY;

The type of SYSTEM_VALIDATOR_REGISTRY.

YjsEntryKind

export type YjsEntryKind = "text" | "map" | "array" | "xml";

The kind of a top-level shared type in a Y.Doc, as far as JSON projection cares.

YjsStatus

export type YjsStatus = (typeof YJS_STATUSES)[number];

The state of one Yjs sub-document in the client's status document. Only available today.

YjsUpdate

export type YjsUpdate = InferValue<typeof YjsUpdateValue>;

One change to a Yjs sub-document: create or update carries the Yjs update bytes in update; delete removes the sub-document and carries none.

Variables

ClientDocumentsStatusDocumentSchema

ClientDocumentsStatusDocumentSchema: {
    readonly root: {
        readonly type: "object";
        readonly fields: {
            readonly cacheHydratedDocuments: {
                readonly type: "number";
                readonly optional: true;
            };
            readonly discardedWrites: {
                readonly type: "record";
                readonly optional: true;
                readonly values: {
                    readonly type: "object";
                    readonly fields: {
                        readonly count: {
                            readonly type: "number";
                        };
                        readonly reason: {
                            readonly type: "enum";
                            readonly values: readonly ["retentionExpired", "replayHorizon"];
                        };
                        readonly oldestSentAt: {
                            readonly type: "number";
                            readonly optional: true;
                        };
                        readonly oldestStagedAt: {
                            readonly type: "number";
                            readonly optional: true;
                        };
                    };
                };
            };
            readonly evictedCachedDocuments: {
                readonly type: "number";
                readonly optional: true;
            };
            readonly documents: {
                readonly type: "record";
                readonly values: {
                    readonly type: "object";
                    readonly fields: {
                        readonly subscribed: {
                            readonly type: "enum";
                            readonly values: readonly ["subscribing", "subscribed", "preloaded"];
                        };
                        readonly optimisticUpdates: {
                            readonly type: "number";
                        };
                        readonly status: {
                            readonly type: "enum";
                            readonly values: readonly ["pending", "notFound", "available", "error", "deleted"];
                        };
                        readonly cacheHydrated: {
                            readonly type: "boolean";
                            readonly optional: true;
                        };
                        readonly cachedAt: {
                            readonly type: "number";
                            readonly optional: true;
                        };
                        readonly oldestStagedAt: {
                            readonly type: "number";
                            readonly optional: true;
                        };
                        readonly yjsDocs: {
                            readonly type: "record";
                            readonly values: {
                                readonly type: "enum";
                                readonly values: readonly ["available"];
                            };
                        };
                        readonly error: {
                            readonly type: "object";
                            readonly optional: true;
                            readonly fields: {
                                readonly category: {
                                    readonly type: "enum";
                                    readonly values: readonly ["schemaValidation", "invalidRequest", "unknownType", "yjsCorruption", "storageError", "sizeLimitExceeded", "alreadyExists", "preconditionFailed", "commandRefused", "deleted", "unknown", "unauthorized", "queueFull", "notFound", "timeout", "unconfirmed", "discarded"];
                                };
                                readonly message: {
                                    readonly type: "string";
                                };
                                readonly issues: {
                                    readonly type: "array";
                                    readonly items: {
                                        readonly type: "object";
                                        readonly fields: {
                                            readonly path: {
                                                readonly type: "array";
                                                readonly items: {
                                                    readonly type: "oneOf";
                                                    readonly options: readonly [{
                                                        readonly type: "string";
                                                    }, {
                                                        readonly type: "number";
                                                    }];
                                                };
                                            };
                                            readonly message: {
                                                readonly type: "string";
                                            };
                                            readonly kind: {
                                                readonly type: "enum";
                                                readonly values: readonly ["schema", "reference"];
                                            };
                                            readonly reference: {
                                                readonly type: "object";
                                                readonly optional: true;
                                                readonly fields: {
                                                    readonly rule: {
                                                        readonly type: "string";
                                                    };
                                                    readonly targetDocId: {
                                                        readonly type: "string";
                                                    };
                                                };
                                            };
                                        };
                                    };
                                    readonly optional: true;
                                };
                            };
                        };
                    };
                };
            };
        };
    };
}

The schema of the client's status document; see ClientDocumentsStatusDocument.

COMMAND_GUARD_MODES

COMMAND_GUARD_MODES: readonly ["sequence"]

Every CommandGuardMode, as a tuple.

DELETE_GUARD_MODES

DELETE_GUARD_MODES: readonly ["sequence"]

Every DeleteGuardMode, as a tuple.

DISCARDED_WRITE_REASONS

DISCARDED_WRITE_REASONS: readonly ["retentionExpired", "replayHorizon"]

Every DiscardedWriteReason, as a tuple.

DOCUMENT_ERROR_CATEGORIES

DOCUMENT_ERROR_CATEGORIES: readonly ["schemaValidation", "invalidRequest", "unknownType", "yjsCorruption", "storageError", "sizeLimitExceeded", "alreadyExists", "preconditionFailed", "commandRefused", "deleted", "unknown", "unauthorized", "queueFull", "notFound", "timeout", "unconfirmed", "discarded"]

Every DocumentErrorCategory, as a tuple.

DOCUMENT_ERROR_CATEGORY_TRAITS

DOCUMENT_ERROR_CATEGORY_TRAITS: Readonly<Record<DocumentErrorCategory, DocumentErrorCategoryTraits>>

The DocumentErrorCategoryTraits of every DocumentErrorCategory: branch on these for behavior (offer a retry, refetch first, report a bug) and keep only what the app shows keyed by category.

Example

const { retryable, outcomeUnknown } = DOCUMENT_ERROR_CATEGORY_TRAITS[error.category];

DOCUMENT_STATUSES

DOCUMENT_STATUSES: readonly ["pending", "notFound", "available", "error", "deleted"]

Every DocumentStatus, as a tuple.

DocumentErrorValue

DocumentErrorValue: {
    readonly type: "object";
    readonly fields: {
        readonly category: {
            readonly type: "enum";
            readonly values: readonly ["schemaValidation", "invalidRequest", "unknownType", "yjsCorruption", "storageError", "sizeLimitExceeded", "alreadyExists", "preconditionFailed", "commandRefused", "deleted", "unknown", "unauthorized", "queueFull", "notFound", "timeout", "unconfirmed", "discarded"];
        };
        readonly message: {
            readonly type: "string";
        };
    };
}

Schema value of a DocumentError.

GET_EVENTS_PAGE_LIMIT

GET_EVENTS_PAGE_LIMIT = 500

The most event rows (both lanes together) one doc:events page carries; a larger doc:get-events limit is clamped to it, and it is the default. The server also cuts a page early at a byte budget, so a page may hold fewer.

GUARD_MODES

GUARD_MODES: readonly ["sequence", "patch"]

Every GuardMode, as a tuple.

HEARTBEAT_PING_MESSAGE

HEARTBEAT_PING_MESSAGE = "{\"type\":\"ping\"}"

The WebSocket heartbeat frame a client sends on an interval. A transport-level frame, not a datadata event: it is matched byte-for-byte, so both ends must send this exact string.

HEARTBEAT_PONG_MESSAGE

HEARTBEAT_PONG_MESSAGE = "{\"type\":\"pong\"}"

The reply to HEARTBEAT_PING_MESSAGE. A client that stops receiving it treats the socket as dead even while it still reports open.

JsonPatchOperationValue

JsonPatchOperationValue: {
    readonly type: "union";
    readonly discriminator: "op";
    readonly variants: {
        readonly add: {
            readonly path: {
                readonly type: "string";
            };
            readonly value: {
                readonly type: "json";
            };
        };
        readonly remove: {
            readonly path: {
                readonly type: "string";
            };
        };
        readonly replace: {
            readonly path: {
                readonly type: "string";
            };
            readonly value: {
                readonly type: "json";
            };
        };
        readonly move: {
            readonly path: {
                readonly type: "string";
            };
            readonly from: {
                readonly type: "string";
            };
        };
        readonly copy: {
            readonly path: {
                readonly type: "string";
            };
            readonly from: {
                readonly type: "string";
            };
        };
        readonly test: {
            readonly path: {
                readonly type: "string";
            };
            readonly value: {
                readonly type: "json";
            };
        };
    };
}

Schema value of one RFC 6902 JSON Patch operation, discriminated by op.

MAX_DOCUMENT_NAME_LENGTH

MAX_DOCUMENT_NAME_LENGTH = 1024

The longest a document's index name may be, in UTF-16 code units (JavaScript string length). A longer name is rejected by the client and the server alike; a rename UI can show it as the limit.

MAX_PRESENCE_STATE_BYTES

MAX_PRESENCE_STATE_BYTES = 4096

The largest one presence state may be: the UTF-8 byte length of its JSON. The client throws on a larger state and the server drops it. Document-sized data belongs in documents.

MAX_REPLAY_AGE_MS

MAX_REPLAY_AGE_MS: number

The oldest a sent-but-unconfirmed write may be for the client to re-send it on reconnect, in milliseconds: half of PROCESSED_WRITE_RETENTION_MS. An older write is discarded with its outcome unknown rather than risk being applied twice.

MAX_WIRE_MESSAGE_BYTES

MAX_WIRE_MESSAGE_BYTES = 16777216

The default cap on one received WebSocket message, in bytes (16 MiB). A transport enforces it before decoding the frame; a deployment whose storage limits are tighter should lower it to match.

PRESENCE_DOC_ID_PREFIX

PRESENCE_DOC_ID_PREFIX = "sys:presence:"

Prefix of every presence channel document id: sys:presence:.

PRESENCE_DOCUMENT_TYPE

PRESENCE_DOCUMENT_TYPE = "sys:presence"

Document type of a presence channel document (see presenceDocId).

PRESENCE_VIEW_DOC_ID_PREFIX

PRESENCE_VIEW_DOC_ID_PREFIX = "sys:presence-view:"

Prefix of every presence view document id: sys:presence-view:.

PRESENCE_VIEW_DOCUMENT_TYPE

PRESENCE_VIEW_DOCUMENT_TYPE = "sys:presence-view"

Document type of a session's presence view document (see presenceViewDocId).

PresenceCellValue

PresenceCellValue: {
    readonly type: "object";
    readonly fields: {
        readonly subject: {
            readonly type: "string";
            readonly nullable: true;
        };
        readonly state: {
            readonly type: "record";
            readonly values: {
                readonly type: "json";
            };
        };
    };
}

Schema value of a PresenceCell.

PresenceEntryValue

PresenceEntryValue: {
    readonly type: "object";
    readonly fields: {
        readonly presenceId: {
            readonly type: "string";
        };
        readonly subject: {
            readonly type: "string";
            readonly nullable: true;
        };
        readonly state: {
            readonly type: "record";
            readonly values: {
                readonly type: "json";
            };
        };
    };
}

Schema value of a PresenceEntry.

PROCESSED_WRITE_RETENTION_MS

PROCESSED_WRITE_RETENTION_MS: number

How long the server remembers a write's client event id, in milliseconds (one hour), so a write re-sent on reconnect is acknowledged again instead of applied twice.

PURGE_BATCH_LIMIT

PURGE_BATCH_LIMIT = 100

The most documents one doc:purge event may name — bounds a single event's server work and patch size; an "empty trash" over more loops in pages.

SCHEMA_DOC_ID_PREFIX

SCHEMA_DOC_ID_PREFIX = "sys:schema:"

Prefix of every schema document id: sys:schema:.

StoredDocumentEventValue

StoredDocumentEventValue: {
    readonly type: "object";
    readonly fields: {
        readonly sequence: {
            readonly type: "number";
        };
        readonly patch: {
            readonly type: "string";
        };
        readonly subject: {
            readonly type: "string";
            readonly nullable: true;
        };
        readonly actor: {
            readonly type: "string";
            readonly nullable: true;
        };
        readonly createdAt: {
            readonly type: "number";
        };
        readonly schemaSequence: {
            readonly type: "number";
            readonly int: true;
            readonly min: 1;
        };
    };
}

Schema value of a StoredDocumentEvent.

StoredYjsEventValue

StoredYjsEventValue: {
    readonly type: "object";
    readonly fields: {
        readonly yjsSequence: {
            readonly type: "number";
            readonly int: true;
            readonly min: 1;
        };
        readonly yjsId: {
            readonly type: "string";
        };
        readonly action: {
            readonly type: "enum";
            readonly values: readonly ["create", "update", "delete"];
        };
        readonly blob: {
            readonly type: "bytes";
            readonly nullable: true;
        };
        readonly subject: {
            readonly type: "string";
            readonly nullable: true;
        };
        readonly actor: {
            readonly type: "string";
            readonly nullable: true;
        };
        readonly createdAt: {
            readonly type: "number";
        };
    };
}

Schema value of a StoredYjsEvent.

SYSTEM_SCHEMA_DOCUMENTS

SYSTEM_SCHEMA_DOCUMENTS: ReadonlyMap<string, DocumentSchema>

The schemas of the built-in sys: document types, keyed by schema document id (sys:schema:<type>). The server serves these as read-only schema documents; they change only with the datadata version and are not listed in the index.

SYSTEM_VALIDATOR_REGISTRY

SYSTEM_VALIDATOR_REGISTRY: {
    "sys:index": JsonValidator<{
        documents: Record<string, {
            name: string | null;
            type: string;
        }>;
    }, string, {
        documents: Record<string, {
            name: string | null;
            type: string;
        }>;
    }, {
        documents: Record<string, {
            name: string | null;
            type: string;
        }>;
    }>;
    "sys:trash": JsonValidator<{
        documents: Record<string, {
            deletedAt: number;
            generation: string;
            name: string | null;
            type: string;
        }>;
    }, string, {
        documents: Record<string, {
            deletedAt: number;
            generation: string;
            name: string | null;
            type: string;
        }>;
    }, {
        documents: Record<string, {
            deletedAt: number;
            generation: string;
            name: string | null;
            type: string;
        }>;
    }>;
    "sys:client-docs-status": JsonValidator<{
        cacheHydratedDocuments?: number | undefined;
        discardedWrites?: Record<string, {
            count: number;
            oldestSentAt?: number | undefined;
            oldestStagedAt?: number | undefined;
            reason: "replayHorizon" | "retentionExpired";
        }> | undefined;
        documents: Record<string, {
            cacheHydrated?: boolean | undefined;
            cachedAt?: number | undefined;
            error?: {
                category: "alreadyExists" | "commandRefused" | "deleted" | "discarded" | "invalidRequest" | "notFound" | "preconditionFailed" | "queueFull" | "schemaValidation" | "sizeLimitExceeded" | "storageError" | "timeout" | "unauthorized" | "unconfirmed" | "unknown" | "unknownType" | "yjsCorruption";
                issues?: {
                    kind: "reference" | "schema";
                    message: string;
                    path: (string | number)[];
                    reference?: {
                        rule: string;
                        targetDocId: string;
                    } | undefined;
                }[] | undefined;
                message: string;
            } | undefined;
            oldestStagedAt?: number | undefined;
            optimisticUpdates: number;
            status: "available" | "deleted" | "error" | "notFound" | "pending";
            subscribed: "preloaded" | "subscribed" | "subscribing";
            yjsDocs: Record<string, "available">;
        }>;
        evictedCachedDocuments?: number | undefined;
    }, string, {
        cacheHydratedDocuments?: number | undefined;
        discardedWrites?: Record<string, {
            count: number;
            oldestSentAt?: number | undefined;
            oldestStagedAt?: number | undefined;
            reason: "replayHorizon" | "retentionExpired";
        }> | undefined;
        documents: Record<string, {
            cacheHydrated?: boolean | undefined;
            cachedAt?: number | undefined;
            error?: {
                category: "alreadyExists" | "commandRefused" | "deleted" | "discarded" | "invalidRequest" | "notFound" | "preconditionFailed" | "queueFull" | "schemaValidation" | "sizeLimitExceeded" | "storageError" | "timeout" | "unauthorized" | "unconfirmed" | "unknown" | "unknownType" | "yjsCorruption";
                issues?: {
                    kind: "reference" | "schema";
                    message: string;
                    path: (string | number)[];
                    reference?: {
                        rule: string;
                        targetDocId: string;
                    } | undefined;
                }[] | undefined;
                message: string;
            } | undefined;
            oldestStagedAt?: number | undefined;
            optimisticUpdates: number;
            status: "available" | "deleted" | "error" | "notFound" | "pending";
            subscribed: "preloaded" | "subscribed" | "subscribing";
            yjsDocs: Record<string, "available">;
        }>;
        evictedCachedDocuments?: number | undefined;
    }, {
        cacheHydratedDocuments?: number | undefined;
        discardedWrites?: Record<string, {
            count: number;
            oldestSentAt?: number | undefined;
            oldestStagedAt?: number | undefined;
            reason: "replayHorizon" | "retentionExpired";
        }> | undefined;
        documents: Record<string, {
            cacheHydrated?: boolean | undefined;
            cachedAt?: number | undefined;
            error?: {
                category: "alreadyExists" | "commandRefused" | "deleted" | "discarded" | "invalidRequest" | "notFound" | "preconditionFailed" | "queueFull" | "schemaValidation" | "sizeLimitExceeded" | "storageError" | "timeout" | "unauthorized" | "unconfirmed" | "unknown" | "unknownType" | "yjsCorruption";
                issues?: {
                    kind: "reference" | "schema";
                    message: string;
                    path: (string | number)[];
                    reference?: {
                        rule: string;
                        targetDocId: string;
                    } | undefined;
                }[] | undefined;
                message: string;
            } | undefined;
            oldestStagedAt?: number | undefined;
            optimisticUpdates: number;
            status: "available" | "deleted" | "error" | "notFound" | "pending";
            subscribed: "preloaded" | "subscribed" | "subscribing";
            yjsDocs: Record<string, "available">;
        }>;
        evictedCachedDocuments?: number | undefined;
    }>;
    "sys:schema": JsonValidator<DocumentSchema, string, DocumentSchema, DocumentSchema>;
    "sys:access": JsonValidator<{
        anonymousDefault?: ("admin" | "editor" | "viewer") | undefined;
        authenticatedDefault?: ("admin" | "editor" | "viewer") | undefined;
        documents?: Record<string, {
            commands?: Record<string, {
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })> | undefined;
            create?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            delete?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            purge?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            read?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            rename?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            restore?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            update?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            write?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
        }> | undefined;
        roles: Record<string, "admin" | "editor" | "viewer">;
    }, string, {
        anonymousDefault?: ("admin" | "editor" | "viewer") | undefined;
        authenticatedDefault?: ("admin" | "editor" | "viewer") | undefined;
        documents?: Record<string, {
            commands?: Record<string, {
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })> | undefined;
            create?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            delete?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            purge?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            read?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            rename?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            restore?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            update?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            write?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
        }> | undefined;
        roles: Record<string, "admin" | "editor" | "viewer">;
    }, {
        anonymousDefault?: ("admin" | "editor" | "viewer") | undefined;
        authenticatedDefault?: ("admin" | "editor" | "viewer") | undefined;
        documents?: Record<string, {
            commands?: Record<string, {
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })> | undefined;
            create?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            delete?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            purge?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            read?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            rename?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            restore?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            update?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
            write?: ({
                anyOf: ("anyone" | "authenticated" | "nobody" | {
                    role: "admin" | "editor" | "viewer";
                } | {
                    grant: string;
                } | {
                    subject: string;
                })[];
            } | ("anyone" | "authenticated" | "nobody" | {
                role: "admin" | "editor" | "viewer";
            } | {
                grant: string;
            } | {
                subject: string;
            })) | undefined;
        }> | undefined;
        roles: Record<string, "admin" | "editor" | "viewer">;
    }>;
    "sys:principal": JsonValidator<PrincipalDocument, string, PrincipalDocument, PrincipalDocument>;
}

The validators for the built-in sys: document types, keyed by document type. The client merges them into an app's registry, so an app registers only its own types.

YjsUpdateValue

YjsUpdateValue: {
    readonly type: "object";
    readonly fields: {
        readonly yjsId: {
            readonly type: "string";
        };
        readonly action: {
            readonly type: "enum";
            readonly values: readonly ["create", "update", "delete"];
        };
        readonly update: {
            readonly type: "bytes";
            readonly optional: true;
        };
    };
}

Schema value of one Yjs-lane change on the wire: the sub-document's yjsId, the action, and the update bytes (absent for delete). See YjsUpdate.