---
title: "edytor/crdt/edytor"
description: "Every name edytor/crdt/edytor exports, with its declaration and documentation (generated from the published types)."
---

{/* Generated by scripts/api-report.mjs (pnpm api:report) from the published declarations: edit the JSDoc, not this page. */}

Every name `edytor/crdt/edytor` 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. 119 names.

## ActorId

The durable per-block attribution record a replica sees.

```ts
export type ActorId = string;
```

## ActorProfile

Replicated actor profile record payload (`u/` values).

```ts
export type ActorProfile = {
    name?: string;
    color?: string;
};
```

## AnchorAffinity

Selection-endpoint affinity — which side of a position the anchor binds
to (see `T.atomAnchorAt`):

- `'left'` — bound to the atom BEFORE the position; resolves right after
  it, so a concurrent insert exactly at the position lands to the
  anchor's right. Used for carets and range ENDS.
- `'right'` — bound to the atom AT the position; resolves right before
  it, so a concurrent insert lands to the anchor's left — outside a
  range starting here. Used for range STARTS.

```ts
export type AnchorAffinity = 'left' | 'right';
```

## attachDocument

See `DocumentBinding.attachDocument`.

```ts
declare const attachDocument: (doc: YDoc, options?: DocumentOptions) => EdytorDocument;
```

## AttributionActor

The stable application-level author identity (mirrors `DocumentActor`).

```ts
export type AttributionActor = {
    id: string;
    name?: string;
    color?: string;
};
```

## AttributionController

```ts
export type AttributionController = {
    readonly view: DocumentAttribution;
    setProfile(profile: ActorProfile): void;
    destroy(): void;
};
```

- `view`: The public read surface handed to `EdytorDocument.attribution`.
- `setProfile`: Re-publish the local actor's profile record (`u/<actorId>` LWW).
- `destroy`: Release the controller; `setProfile` becomes a no-op.

## Awareness

Simple shared state protocol for non-persistent data (cursor, username,
status…). Semantics are verbatim upstream: clock-ordered LWW per client,
remote `null` states mark peers offline, local state is re-published when
outdated, and remote states expire after `outdatedTimeout`.

```ts
export declare class Awareness extends IsolatedObservable<AwarenessEvents> {
    #private;
    doc: AwarenessDoc;
    clientID: number;
    states: AwarenessStates;
    meta: Map<number, MetaClientState>;
    private _destroyed;
    constructor(doc: AwarenessDoc);
    destroy(): void;
    getLocalState(): Record<string, unknown> | null;
    setLocalState(state: Record<string, unknown> | null): void;
    setLocalStateField(field: string, value: unknown): void;
    getStates(): AwarenessStates;
}
```

- `states`: Maps from client id to client state

## AwarenessDoc

Structural minimum an Awareness host must satisfy. `Y.Doc` (vendored v14)
satisfies this; the structural type keeps Awareness free of any engine
runtime import.

```ts
export interface AwarenessDoc {
    clientID: number;
    on(name: 'destroy', f: (doc: unknown) => void): void;
}
```

## AwarenessStates

```ts
export type AwarenessStates = Map<number, Record<string, unknown>>;
```

## AwarenessUpdate

```ts
export type AwarenessUpdate = {
    added: number[];
    updated: number[];
    removed: number[];
};
```

## bindCrdt

```ts
declare const bindCrdt: (Y: EngineApi) => {
    createDoc: (opts?: ConstructorParameters<EngineApi["Doc"]>[0]) => YDoc;
    Awareness: typeof _Awareness;
    doc: {
        SCHEMA: {
            readonly version: 6;
            readonly name: "edytor-doc";
            readonly roots: {
                readonly registry: "blocks";
                readonly meta: "meta";
            };
            readonly metaAttrs: {
                readonly version: "v";
                readonly schema: "schema";
            };
        };
        SCHEMA_VERSION: 6;
        SCHEMA_NAME: "edytor-doc";
        META_KEY: "meta";
        init: (doc: import("./engine-api.js").EngineDoc, opts?: {
            content?: import("./index.js").BlockSpec[];
            defaultType?: string;
        }) => void;
        restore: (doc: import("./engine-api.js").EngineDoc, content: import("./index.js").BlockSpec[]) => void;
        seed: (doc: import("./engine-api.js").EngineDoc, value?: import("./index.js").JSONBlock[], defaultType?: string, data?: import("./doc/types.js").JsonObj) => void;
        isInitialized: (doc: import("./engine-api.js").EngineDoc) => boolean;
        schemaVersion: (doc: import("./engine-api.js").EngineDoc) => number | undefined;
        registryEmpty: (doc: import("./engine-api.js").EngineDoc) => boolean;
        checkSchema: (doc: import("./engine-api.js").EngineDoc) => import("./edytor-doc.js").SchemaProblem | null;
        assertSchema: (doc: import("./engine-api.js").EngineDoc, docName?: string) => void;
        SchemaMismatchError: typeof import("./edytor-doc.js").SchemaMismatchError;
        create: (doc: import("./engine-api.js").EngineDoc, config?: import("./edytor-doc.js").EdytorDocConfig) => {
            prepare: {
                insertFlow: (target: import("./flow.js").FlowTarget, flow: import("./flow.js").Flow, view?: import("./flow.js").FlowView) => import("./edytor-doc.js").Prepared;
                deleteRange: (from: import("./rangeDelete.js").DocPosition, to: import("./rangeDelete.js").DocPosition, view?: import("./rangeDelete.js").RangeView) => import("./edytor-doc.js").Prepared;
                replaceRange: (from: import("./rangeDelete.js").DocPosition, to: import("./rangeDelete.js").DocPosition, view?: import("./rangeDelete.js").RangeView) => import("./edytor-doc.js").Prepared;
                insertBlocks: (dest: import("./index.js").Destination, specs: readonly (import("./index.js").BlockSpec | import("./index.js").JSONBlock)[]) => import("./edytor-doc.js").Prepared;
                moveBlocks: (ids: readonly import("./index.js").BlockId[], dest: import("./index.js").Destination) => import("./edytor-doc.js").Prepared;
                nestBlock: (id: import("./index.js").BlockId, parent: import("./index.js").BlockId) => import("./edytor-doc.js").Prepared;
                unNestBlock: (id: import("./index.js").BlockId) => import("./edytor-doc.js").Prepared;
                unNestBlocks: (ids: readonly import("./index.js").BlockId[]) => import("./edytor-doc.js").Prepared;
                liftOut: (id: import("./index.js").BlockId, kind: string, { keep, after }?: {
                    keep?: boolean;
                    after?: readonly import("./index.js").BlockSpec[];
                }) => import("./edytor-doc.js").Prepared;
                placeBeside: (ids: readonly import("./index.js").BlockId[], target: import("./index.js").BlockId, side: "left" | "right", kind?: string) => import("./edytor-doc.js").Prepared;
                wrapInLayout: (ids: readonly import("./index.js").BlockId[], kind?: string, columns?: number) => import("./edytor-doc.js").Prepared;
                splitBlock: (id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail?: import("./placement/model.js").SplitTail) => import("./edytor-doc.js").Prepared;
                mergeBlocks: (fromId: import("./index.js").BlockId, intoId: import("./index.js").BlockId) => import("./edytor-doc.js").Prepared;
                mergeBackward: (id: import("./index.js").BlockId) => import("./edytor-doc.js").Prepared;
                mergeForward: (id: import("./index.js").BlockId) => import("./edytor-doc.js").Prepared;
                deleteBlock: (id: import("./index.js").BlockId, opts?: {
                    keepChildren?: boolean;
                }) => import("./edytor-doc.js").Prepared;
                setBlock: (id: import("./index.js").BlockId, value: {
                    type?: string;
                    data?: Record<string, unknown>;
                    content?: (import("./index.js").ContentItem | import("./index.js").JSONText | import("./index.js").JSONInlineBlock)[];
                    children?: (import("./index.js").BlockSpec | import("./index.js").JSONBlock)[];
                }) => import("./edytor-doc.js").Prepared;
                setBlockType: (id: import("./index.js").BlockId, type: string) => import("./edytor-doc.js").Prepared;
                patchData: (target: import("./edytor-doc.js").DataTarget, patches: readonly import("./data.js").DataPatch[]) => import("./edytor-doc.js").Prepared;
                duplicateBlock: (id: import("./index.js").BlockId, freshId?: (oldId: string, kind: "block" | "inline") => string) => import("./edytor-doc.js").Prepared;
                insertText: (id: import("./index.js").BlockId, offset: number, text: string, marks?: Record<string, unknown>) => import("./edytor-doc.js").Prepared;
                deleteText: (id: import("./index.js").BlockId, offset: number, length: number) => import("./edytor-doc.js").Prepared;
                formatRange: (id: import("./index.js").BlockId, offset: number, length: number, marks: Record<string, unknown>) => import("./edytor-doc.js").Prepared;
                clearMarks: (id: import("./index.js").BlockId, offset: number, length: number) => import("./edytor-doc.js").Prepared;
                insertInline: (id: import("./index.js").BlockId, offset: number, atom: import("./doc/content.js").NewInline) => import("./edytor-doc.js").Prepared;
                removeInline: (id: import("./index.js").BlockId, inlineId: string) => import("./edytor-doc.js").Prepared;
                deleteBlocks: (ids: readonly import("./index.js").BlockId[]) => import("./edytor-doc.js").Prepared;
            };
            apply: (p: import("./edytor-doc.js").Prepared) => import("./edytor-doc.js").OpResult;
            compose: (...parts: import("./edytor-doc.js").Prepared[]) => import("./edytor-doc.js").Prepared;
            transact: <R>(fn: () => R, origin?: unknown) => R;
            model: {
                REGISTRY_KEY: string;
                registryOf: (doc: import("./engine-api.js").EngineDoc) => import("./engine-api.js").EngineNode;
                blockNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
                liveNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
                isLive: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => boolean;
                candidatesOf: (node: import("./engine-api.js").EngineNode) => import("./placement/model.js").PlacementCand[];
                view: (doc: import("./engine-api.js").EngineDoc) => import("./placement/model.js").ModelView;
                resolvePlacements: (blocks: Map<import("./index.js").BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: import("./index.js").BlockId) => import("./text/model.js").Owner) => Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>;
                childrenIndex: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<import("./index.js").BlockId | null, import("./placement/model.js").ChildSlot[]>;
                positionInView: (v: import("./placement/model.js").ModelView, id: import("./index.js").BlockId) => import("./index.js").Destination | null;
                buildInline: (atom: import("./index.js").InlineSpec) => import("./engine-api.js").EngineNode;
                isSelfOrDescendant: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: import("./index.js").BlockId, maybeAncestor: import("./index.js").BlockId) => boolean;
                ranksAt: (siblings: readonly {
                    rank: string;
                }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
                writePlacement: (doc: import("./engine-api.js").EngineDoc, node: import("./engine-api.js").EngineNode, p: import("./index.js").BlockId | null, r: string) => void;
                withdrawOnUndo: (doc: import("./engine-api.js").EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
                    inserts: {
                        has(client: number, clock: number): boolean;
                    };
                }) => boolean;
                materializeSpec: (doc: import("./engine-api.js").EngineDoc, sp: import("./index.js").BlockSpec, parent: import("./index.js").BlockId | null, rank: string) => void;
                collides: (doc: import("./engine-api.js").EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
                writeSplit: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail: import("./placement/model.js").SplitTail, place: import("./placement/model.js").PlacementValue) => void;
                ownText: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => {
                    from: unknown;
                    to: number;
                };
                insertBlocks: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
                insertBlock: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
                restoreBlocks: (doc: import("./engine-api.js").EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
                project: (doc: import("./engine-api.js").EngineDoc) => import("./index.js").ProjectedDoc;
                positionOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./index.js").Destination | null;
                listBlockIds: (doc: import("./engine-api.js").EngineDoc) => import("./index.js").BlockId[];
                blockText: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => string | null;
                crdtId: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => string | null;
                resolveBlock: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
            };
            text: {
                newNode: (name: string) => import("./engine-api.js").EngineNode;
                resolveAnchor: (doc: import("./engine-api.js").EngineDoc, text: import("./engine-api.js").EngineNode, anchor: import("./text/model.js").Anchor) => number | null;
                anchorAt: (text: import("./engine-api.js").EngineNode, index: number, assoc: number) => import("./text/model.js").Anchor;
                openRangeCursor: (text: import("./engine-api.js").EngineNode) => import("./text/model.js").RangeCursor;
                itemsOfRange: (text: import("./engine-api.js").EngineNode, i0: number, i1: number) => import("./text/model.js").RangeItem[];
                readSegs: (segs: readonly import("./text/model.js").Seg[], stats?: import("./text/model.js").RangeReadStats) => import("./text/model.js").RangeItem[];
                computeOwnership: (doc: import("./engine-api.js").EngineDoc, given: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>) => import("./text/model.js").Ownership;
                flatten: (b: import("./text/model.js").BlockId, blocks: unknown, own: import("./text/model.js").Ownership) => import("./text/model.js").Seg[];
                ownedLength: (segs: readonly import("./text/model.js").Seg[]) => number;
                contentItemsOf: (b: import("./text/model.js").BlockId, blocks: unknown, own: import("./text/model.js").Ownership) => import("./text/model.js").RangeItem[];
                blockTextOf: (b: import("./text/model.js").BlockId, blocks: unknown, own: import("./text/model.js").Ownership) => string;
                insertIntoText: (doc: import("./engine-api.js").EngineDoc, blocks: unknown, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, payload: string | import("./engine-api.js").EngineNode, marks?: Record<string, unknown>) => void;
                deleteRange: (doc: import("./engine-api.js").EngineDoc, blocks: unknown, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, length: number) => void;
                formatRangeIn: (doc: import("./engine-api.js").EngineDoc, blocks: unknown, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, length: number, formats: Record<string, unknown>) => void;
                purgeStreams: (streams: readonly import("./text/model.js").Stream[]) => void;
                findAtom: (own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, inlineId: string) => {
                    text: import("./engine-api.js").EngineNode;
                    at: number;
                    node: import("./engine-api.js").EngineNode;
                } | null;
                splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => import("./text/model.js").MergeClaim[];
                claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
                ownText: (doc: import("./engine-api.js").EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
                    from: unknown;
                    to: number;
                };
            };
            runsView: import("./text/runs.js").RunView;
            version: number;
            insertFlow: (target: import("./flow.js").FlowTarget, flow: import("./flow.js").Flow, view?: import("./flow.js").FlowView | undefined) => import("./edytor-doc.js").OpResult;
            deleteRange: (from: import("./rangeDelete.js").DocPosition, to: import("./rangeDelete.js").DocPosition, view?: import("./rangeDelete.js").RangeView | undefined) => import("./edytor-doc.js").OpResult;
            replaceRange: (from: import("./rangeDelete.js").DocPosition, to: import("./rangeDelete.js").DocPosition, view?: import("./rangeDelete.js").RangeView | undefined) => import("./edytor-doc.js").OpResult;
            insertBlocks: (dest: import("./index.js").Destination, specs: readonly (import("./index.js").BlockSpec | import("./index.js").JSONBlock)[]) => import("./edytor-doc.js").OpResult;
            moveBlocks: (ids: readonly string[], dest: import("./index.js").Destination) => import("./edytor-doc.js").OpResult;
            nestBlock: (id: string, parent: string) => import("./edytor-doc.js").OpResult;
            unNestBlock: (id: string) => import("./edytor-doc.js").OpResult;
            unNestBlocks: (ids: readonly string[]) => import("./edytor-doc.js").OpResult;
            liftOut: (id: string, kind: string, args_2?: {
                keep?: boolean;
                after?: readonly import("./index.js").BlockSpec[];
            } | undefined) => import("./edytor-doc.js").OpResult;
            placeBeside: (ids: readonly string[], target: string, side: "right" | "left", kind?: string | undefined) => import("./edytor-doc.js").OpResult;
            wrapInLayout: (ids: readonly string[], kind?: string | undefined, columns?: number | undefined) => import("./edytor-doc.js").OpResult;
            splitBlock: (id: string, offset: number, newId: string, tail?: import("./placement/model.js").SplitTail | undefined) => import("./edytor-doc.js").OpResult;
            mergeBlocks: (fromId: string, intoId: string) => import("./edytor-doc.js").OpResult;
            mergeBackward: (id: string) => import("./edytor-doc.js").OpResult;
            mergeForward: (id: string) => import("./edytor-doc.js").OpResult;
            deleteBlock: (id: string, opts?: {
                keepChildren?: boolean;
            } | undefined) => import("./edytor-doc.js").OpResult;
            setBlock: (id: string, value: {
                type?: string;
                data?: Record<string, unknown>;
                content?: (import("./index.js").ContentItem | import("./index.js").JSONText | import("./index.js").JSONInlineBlock)[];
                children?: (import("./index.js").BlockSpec | import("./index.js").JSONBlock)[];
            }) => import("./edytor-doc.js").OpResult;
            setBlockType: (id: string, type: string) => import("./edytor-doc.js").OpResult;
            patchData: (target: import("./edytor-doc.js").DataTarget, patches: readonly import("./data.js").DataPatch[]) => import("./edytor-doc.js").OpResult;
            duplicateBlock: (id: string, freshId?: ((oldId: string, kind: "block" | "inline") => string) | undefined) => import("./edytor-doc.js").OpResult;
            insertText: (id: string, offset: number, text: string, marks?: Record<string, unknown> | undefined) => import("./edytor-doc.js").OpResult;
            deleteText: (id: string, offset: number, length: number) => import("./edytor-doc.js").OpResult;
            formatRange: (id: string, offset: number, length: number, marks: Record<string, unknown>) => import("./edytor-doc.js").OpResult;
            clearMarks: (id: string, offset: number, length: number) => import("./edytor-doc.js").OpResult;
            insertInline: (id: string, offset: number, atom: import("./doc/content.js").NewInline) => import("./edytor-doc.js").OpResult;
            removeInline: (id: string, inlineId: string) => import("./edytor-doc.js").OpResult;
            deleteBlocks: (ids: readonly string[]) => import("./edytor-doc.js").OpResult;
            init: (opts?: Parameters<(doc: import("./engine-api.js").EngineDoc, opts?: {
                content?: import("./index.js").BlockSpec[];
                defaultType?: string;
            }) => void>[1]) => void;
            seed: (value: import("./index.js").JSONBlock[], data?: import("./doc/types.js").JsonObj) => void;
            isInitialized: () => boolean;
            schemaVersion: () => number | undefined;
            checkSchema: () => import("./edytor-doc.js").SchemaProblem | null;
            assertSchema: () => void;
            dispose: () => void;
            createUndoManager: (options?: import("./doc/history.js").HistoryOptions) => import("./doc/history.js").UndoManager;
            project: () => import("./index.js").ProjectedDoc;
            toJSON: () => import("./index.js").JSONDoc;
            blockJSON: (id: string) => import("./index.js").JSONBlock;
            childrenIds: (id: string | null) => string[];
            childSlots: (parent: import("./index.js").BlockId | null) => import("./placement/model.js").ChildSlot[];
            slotOf: (id: string) => {
                parent: import("./index.js").BlockId | null;
                rank: string;
            } | null;
            positionOf: (id: string) => import("./index.js").Destination | null;
            pathOf: (id: string) => number[] | null;
            parentOf: (id: string) => string | null;
            ancestorsOf: (id: string) => string[];
            listBlockIds: () => string[];
            blockText: (id: string) => string | null;
            blockTypeOf: (id: string) => string | undefined;
            blockDataOf: (id: string) => Record<string, unknown> | undefined;
            docData: () => import("./doc/types.js").JsonObj;
            dataItemIds: (target: import("./edytor-doc.js").DataTarget, path: readonly string[]) => string[];
            blockAttribution: (id: string) => import("./index.js").BlockAttribution | undefined;
            crdtId: (id: string) => string | null;
            resolveBlock: (id: string) => import("./engine-api.js").EngineNode | null;
            displayLength: (id: string) => number;
            contentItems: (id: string) => import("./index.js").ContentItem[];
            hasBlock: (id: string) => boolean;
            isVisibleBlock: (id: string) => boolean;
            order: () => readonly import("./index.js").BlockId[];
            compare: (a: import("./index.js").BlockId, b: import("./index.js").BlockId) => number;
            next: (id: string, policy?: import("./edytor-doc.js").OrderPolicy | undefined) => string | null;
            previous: (id: string, policy?: import("./edytor-doc.js").OrderPolicy | undefined) => string | null;
            anchorAt: (id: string, offset: number, affinity?: import("./edytor-doc.js").AnchorAffinity | undefined) => import("./edytor-doc.js").DocAnchor | null;
            resolveAnchor: (anchor: import("./edytor-doc.js").DocAnchor) => {
                blockId: import("./index.js").BlockId;
                offset: number;
            } | null;
            followUndo: (anchor: import("./edytor-doc.js").DocAnchor) => import("./edytor-doc.js").DocAnchor;
            rolesChanged: () => void;
            isVoid: (id: string) => boolean;
            isIsland: (id: string) => boolean;
            isLines: (id: string) => boolean;
            isLayout: (id: string) => boolean;
            isLayoutItem: (id: string) => boolean;
            besideAt: (id: import("./index.js").BlockId, kind?: string) => string;
            islandOf: (id: string) => string | null;
            isTable: (id: string) => boolean;
            isTableRow: (id: string) => boolean;
            isTableCell: (id: string) => boolean;
            tableOf: (id: string) => string | null;
            tableColumns: (id: string) => {
                id: string;
                width?: number;
            }[] | null;
            tableGrid: (id: string) => {
                columns: string[];
                rows: {
                    id: import("./index.js").BlockId;
                    cells: (import("./index.js").BlockId | null)[];
                }[];
            } | null;
            insideIsland: (id: string) => boolean;
            canPlace: (ids: readonly import("./index.js").BlockId[], parent?: import("./index.js").BlockId | null) => boolean;
            canMerge: (from: import("./index.js").BlockId, into: import("./index.js").BlockId) => boolean;
            fits: (parent: import("./index.js").BlockId | null, kind: string) => boolean;
            nestParent: (ids: readonly import("./index.js").BlockId[], parent: import("./index.js").BlockId) => string;
            landingOf: (id: import("./index.js").BlockId, kind: string, after?: boolean) => {
                parent: string | null;
                levels: string[];
            };
            defaultChild: (id: string | null) => string;
            runs: (id: string) => readonly import("./index.js").ContentRun[];
            contentJSON: (id: string) => ({
                text: string;
                marks?: Record<string, unknown>;
            } | {
                id?: string;
                type: string;
                data?: unknown;
            })[];
            onChange: (cb: (change: import("./edytor-doc.js").DocChange) => void) => (() => void);
        };
        newDoc: (opts?: ConstructorParameters<EngineApi["Doc"]>[0]) => YDoc;
        keepCopies: (doc: import("./engine-api.js").EngineDoc) => void;
        purge: (doc: import("./engine-api.js").EngineDoc, facade: {
            model: import("./placement/model.js").PlacementModel;
            text: import("./text/model.js").TextEngine;
        }, horizon: import("./purge.js").Horizon) => import("./purge.js").PurgeReport;
        horizonOf: (doc: import("./engine-api.js").EngineDoc) => import("./purge.js").Horizon | null;
        restoreTo: <Plan>(doc: import("./engine-api.js").EngineDoc, facade: import("./restore.js").RestoreFacade<Plan>, json: import("./index.js").JSONDoc) => import("./restore.js").RestoreReport;
        model: {
            REGISTRY_KEY: string;
            registryOf: (doc: import("./engine-api.js").EngineDoc) => import("./engine-api.js").EngineNode;
            blockNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
            liveNodeOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
            isLive: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => boolean;
            candidatesOf: (node: import("./engine-api.js").EngineNode) => import("./placement/model.js").PlacementCand[];
            view: (doc: import("./engine-api.js").EngineDoc) => import("./placement/model.js").ModelView;
            resolvePlacements: (blocks: Map<import("./index.js").BlockId, import("./placement/model.js").BlockRec>, ownerOf?: (b: import("./index.js").BlockId) => import("./text/model.js").Owner) => Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>;
            childrenIndex: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./placement/model.js").DisplayOwnership) => Map<import("./index.js").BlockId | null, import("./placement/model.js").ChildSlot[]>;
            positionInView: (v: import("./placement/model.js").ModelView, id: import("./index.js").BlockId) => import("./index.js").Destination | null;
            buildInline: (atom: import("./index.js").InlineSpec) => import("./engine-api.js").EngineNode;
            isSelfOrDescendant: (placements: Map<import("./index.js").BlockId, import("./placement/model.js").ResolvedPlacement>, own: import("./text/model.js").Ownership, id: import("./index.js").BlockId, maybeAncestor: import("./index.js").BlockId) => boolean;
            ranksAt: (siblings: readonly {
                rank: string;
            }[], index: number, count: number, clientId: number, rand?: () => number, run?: boolean) => string[];
            writePlacement: (doc: import("./engine-api.js").EngineDoc, node: import("./engine-api.js").EngineNode, p: import("./index.js").BlockId | null, r: string) => void;
            withdrawOnUndo: (doc: import("./engine-api.js").EngineDoc) => (item: import("./engine-api.js").EngineItemRef, stackItem: {
                inserts: {
                    has(client: number, clock: number): boolean;
                };
            }) => boolean;
            materializeSpec: (doc: import("./engine-api.js").EngineDoc, sp: import("./index.js").BlockSpec, parent: import("./index.js").BlockId | null, rank: string) => void;
            collides: (doc: import("./engine-api.js").EngineDoc, specs: readonly import("./index.js").BlockSpec[]) => boolean;
            writeSplit: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId, offset: number, newId: import("./index.js").BlockId, tail: import("./placement/model.js").SplitTail, place: import("./placement/model.js").PlacementValue) => void;
            ownText: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => {
                from: unknown;
                to: number;
            };
            insertBlocks: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, specs: import("./index.js").BlockSpec[]) => boolean;
            insertBlock: (doc: import("./engine-api.js").EngineDoc, dest: import("./index.js").Destination, spec: import("./index.js").BlockSpec) => boolean;
            restoreBlocks: (doc: import("./engine-api.js").EngineDoc, specs: import("./index.js").BlockSpec[]) => void;
            project: (doc: import("./engine-api.js").EngineDoc) => import("./index.js").ProjectedDoc;
            positionOf: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./index.js").Destination | null;
            listBlockIds: (doc: import("./engine-api.js").EngineDoc) => import("./index.js").BlockId[];
            blockText: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => string | null;
            crdtId: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => string | null;
            resolveBlock: (doc: import("./engine-api.js").EngineDoc, id: import("./index.js").BlockId) => import("./engine-api.js").EngineNode | null;
        };
        text: {
            newNode: (name: string) => import("./engine-api.js").EngineNode;
            resolveAnchor: (doc: import("./engine-api.js").EngineDoc, text: import("./engine-api.js").EngineNode, anchor: import("./text/model.js").Anchor) => number | null;
            anchorAt: (text: import("./engine-api.js").EngineNode, index: number, assoc: number) => import("./text/model.js").Anchor;
            openRangeCursor: (text: import("./engine-api.js").EngineNode) => import("./text/model.js").RangeCursor;
            itemsOfRange: (text: import("./engine-api.js").EngineNode, i0: number, i1: number) => import("./text/model.js").RangeItem[];
            readSegs: (segs: readonly import("./text/model.js").Seg[], stats?: import("./text/model.js").RangeReadStats) => import("./text/model.js").RangeItem[];
            computeOwnership: (doc: import("./engine-api.js").EngineDoc, given: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>) => import("./text/model.js").Ownership;
            flatten: (b: import("./text/model.js").BlockId, blocks: unknown, own: import("./text/model.js").Ownership) => import("./text/model.js").Seg[];
            ownedLength: (segs: readonly import("./text/model.js").Seg[]) => number;
            contentItemsOf: (b: import("./text/model.js").BlockId, blocks: unknown, own: import("./text/model.js").Ownership) => import("./text/model.js").RangeItem[];
            blockTextOf: (b: import("./text/model.js").BlockId, blocks: unknown, own: import("./text/model.js").Ownership) => string;
            insertIntoText: (doc: import("./engine-api.js").EngineDoc, blocks: unknown, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, payload: string | import("./engine-api.js").EngineNode, marks?: Record<string, unknown>) => void;
            deleteRange: (doc: import("./engine-api.js").EngineDoc, blocks: unknown, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, length: number) => void;
            formatRangeIn: (doc: import("./engine-api.js").EngineDoc, blocks: unknown, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, length: number, formats: Record<string, unknown>) => void;
            purgeStreams: (streams: readonly import("./text/model.js").Stream[]) => void;
            findAtom: (own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, inlineId: string) => {
                text: import("./engine-api.js").EngineNode;
                at: number;
                node: import("./engine-api.js").EngineNode;
            } | null;
            splitAt: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: import("./text/model.js").Ownership, b: import("./text/model.js").BlockId, offset: number, newId: import("./text/model.js").BlockId, n: number) => import("./text/model.js").MergeClaim[];
            claimInto: (blocks: ReadonlyMap<import("./text/model.js").BlockId, import("./text/model.js").TextBlockRec>, own: Pick<import("./text/model.js").Ownership, "streamOf">, from: import("./text/model.js").BlockId, into: import("./text/model.js").BlockId) => void;
            ownText: (doc: import("./engine-api.js").EngineDoc, rec: import("./text/model.js").TextBlockRec) => {
                from: unknown;
                to: number;
            };
        };
        runs: {
            attach: (doc: import("./engine-api.js").EngineDoc) => import("./text/runs.js").RunView;
        };
    };
    providers: {
        prefetch: (options: PrefetchOptions) => Promise<import("./index.js").PrefetchResult>;
        lastUpdated: (options: import("./index.js").LastUpdatedOptions) => Promise<number | null>;
        documentSnapshot: (options: import("./index.js").LastUpdatedOptions) => Promise<import("./index.js").JSONDoc | null>;
        IndexeddbPersistence: {
            new (name: string, doc: YDoc, options?: import("./protocol.js").IndexeddbPersistenceOptions): {
                doc: YDoc;
                name: string;
                dbName: string;
                _dbref: number;
                _dbsize: number;
                db: IDBDatabase | null;
                _db: Promise<IDBDatabase>;
                _destroyed: boolean;
                hasSynced: boolean;
                whenSynced: Promise<any>;
                _failedEmitted?: boolean;
                _settleSynced: import("./providers/room.js").LifecycleHost["_settleSynced"];
                _leave: () => void;
                loadError: unknown;
                _storeTimeout: number;
                _storeTimeoutId: ReturnType<typeof setTimeout> | null;
                _storeUpdate: (update: Uint8Array, origin: unknown) => void;
                awareness: _Awareness;
                bcconnected: boolean;
                disableBc: boolean;
                _ownsAwareness: boolean;
                _bcSubscriber: (data: ArrayBuffer | Uint8Array, origin: unknown) => void;
                _awarenessUpdateHandler: (updates: {
                    added: number[];
                    updated: number[];
                    removed: number[];
                }, origin: unknown) => void;
                get synced(): boolean;
                messageHandlers: Record<number, import("./providers/room.js").RoomMessageHandler<any>>;
                readMessage: (buf: Uint8Array, emitSynced: boolean) => import("lib0-v14/encoding").Encoder;
                broadcastMessage(buf: Uint8Array): void;
                connectBc(): void;
                disconnectBc(): void;
                destroy(): Promise<void>;
                emit<N extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: N, args: Parameters<{
                    synced: (provider: any) => void;
                    'protocol-mismatch': (mismatch: import("./protocol.js").ProtocolMismatch, provider: any) => void;
                    'load-error': (error: unknown, provider: any) => void;
                    'message-error': (error: unknown, provider: any) => void;
                    'schema-mismatch': (detail: import("./protocol.js").SchemaMismatchDetail, provider: any) => void;
                    failed: (error: unknown, provider: any) => void;
                }[N]>): void;
                _observers: Map<string, Set<any>>;
                on<NAME extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
                    synced: (provider: any) => void;
                    'protocol-mismatch': (mismatch: import("./protocol.js").ProtocolMismatch, provider: any) => void;
                    'load-error': (error: unknown, provider: any) => void;
                    'message-error': (error: unknown, provider: any) => void;
                    'schema-mismatch': (detail: import("./protocol.js").SchemaMismatchDetail, provider: any) => void;
                    failed: (error: unknown, provider: any) => void;
                }[NAME]): {
                    synced: (provider: any) => void;
                    'protocol-mismatch': (mismatch: import("./protocol.js").ProtocolMismatch, provider: any) => void;
                    'load-error': (error: unknown, provider: any) => void;
                    'message-error': (error: unknown, provider: any) => void;
                    'schema-mismatch': (detail: import("./protocol.js").SchemaMismatchDetail, provider: any) => void;
                    failed: (error: unknown, provider: any) => void;
                }[NAME];
                once<NAME extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
                    synced: (provider: any) => void;
                    'protocol-mismatch': (mismatch: import("./protocol.js").ProtocolMismatch, provider: any) => void;
                    'load-error': (error: unknown, provider: any) => void;
                    'message-error': (error: unknown, provider: any) => void;
                    'schema-mismatch': (detail: import("./protocol.js").SchemaMismatchDetail, provider: any) => void;
                    failed: (error: unknown, provider: any) => void;
                }[NAME]): void;
                off<NAME extends "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "load-error">(name: NAME, f: {
                    synced: (provider: any) => void;
                    'protocol-mismatch': (mismatch: import("./protocol.js").ProtocolMismatch, provider: any) => void;
                    'load-error': (error: unknown, provider: any) => void;
                    'message-error': (error: unknown, provider: any) => void;
                    'schema-mismatch': (detail: import("./protocol.js").SchemaMismatchDetail, provider: any) => void;
                    failed: (error: unknown, provider: any) => void;
                }[NAME]): void;
            };
        };
        WebsocketProvider: {
            new (serverUrl: string, roomname: string, doc: YDoc, { connect, awareness, params, WebSocketPolyfill, resyncInterval, maxBackoffTime, connectTimeout, disableBc, maxFrameBytes }?: import("./protocol.js").WebsocketProviderOptions): {
                serverUrl: string;
                roomname: string;
                doc: YDoc;
                awareness: _Awareness;
                params: Record<string, string>;
                maxBackoffTime: number;
                connectTimeout: number;
                maxFrameBytes: number;
                _WS: import("./protocol.js").WebsocketPolyfill;
                shouldConnect: boolean;
                ws: WebSocket | null;
                wsconnected: boolean;
                bcChannel: string;
                bcconnected: boolean;
                disableBc: boolean;
                _bcSubscriber: (data: ArrayBuffer | Uint8Array, origin: unknown) => void;
                _fromTab: {};
                wsUnsuccessfulReconnects: number;
                wsLastMessageReceived: number;
                wsLastPingSent: number;
                _synced: boolean;
                _chunks: import("./protocol.js").ChunkReader;
                _writes: {
                    pending: {
                        sv: Map<number, number>;
                        deletes: import("./protocols/sync.js").IdSet;
                    }[];
                    acked: Map<number, number>;
                    readOnly: boolean;
                    faults: number;
                    fault(): number;
                    readonly doc: YDoc;
                    readonly changed: (unsaved: number) => void;
                    trackDocument(): void;
                    track(sv: Map<number, number>, deletes: import("./protocols/sync.js").IdSet): void;
                    acknowledge(sv: Uint8Array, deletes?: import("./protocols/sync.js").IdSet | null): void;
                    deny(): void;
                    allow(): void;
                };
                messageHandlers: Record<number, import("./providers/room.js").RoomMessageHandler<any>>;
                hasSynced: boolean;
                whenSynced: Promise<unknown>;
                _destroyed: boolean;
                _failedEmitted?: boolean;
                _settleSynced: import("./providers/room.js").LifecycleHost["_settleSynced"];
                _leave: () => void;
                _resync: ReturnType<typeof setInterval> | undefined;
                _connectTimer: ReturnType<typeof setTimeout> | undefined;
                _checkInterval: ReturnType<typeof setInterval>;
                _updateHandler: (update: Uint8Array, origin: unknown, doc?: unknown, transaction?: {
                    local: boolean;
                }) => void;
                _awarenessUpdateHandler: (updates: {
                    added: number[];
                    updated: number[];
                    removed: number[];
                }, origin: unknown) => void;
                get url(): string;
                get unsaved(): number;
                get saved(): boolean;
                get readOnly(): boolean;
                get synced(): boolean;
                set synced(state: boolean);
                destroy(): void;
                disconnect(): void;
                connect(): void;
                emit<N extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: N, args: Parameters<import("./protocol.js").WebsocketProviderEvents[N]>): void;
                _observers: Map<string, Set<any>>;
                on<NAME extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: import("./protocol.js").WebsocketProviderEvents[NAME]): import("./protocol.js").WebsocketProviderEvents[NAME];
                once<NAME extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: import("./protocol.js").WebsocketProviderEvents[NAME]): void;
                off<NAME extends "refused" | "status" | "failed" | "schema-mismatch" | "message-error" | "protocol-mismatch" | "synced" | "connection-close" | "connection-error" | "saved" | "permission-denied" | "expired" | "unreachable">(name: NAME, f: import("./protocol.js").WebsocketProviderEvents[NAME]): void;
            };
        };
        watchComments: (server: string, roomName: string, listener: (message: import("./protocol.js").CommentMessage) => void) => (() => void);
        storeState: (idbPersistence: {
            db: IDBDatabase | null;
            name: string;
            doc: YDoc;
            _dbref: number;
            _dbsize: number;
            _destroyed: boolean;
        }, forceStore?: boolean) => Promise<undefined>;
        clearDocument: (name: string) => Promise<void>;
        PREFERRED_TRIM_SIZE: number;
        createIndexeddbSync: (name: string) => import("./index.js").EdytorSync;
        createWebsocketSync: (options: import("./index.js").WebsocketSyncOptions) => import("./index.js").WebsocketSync;
    };
    sync: {
        messageYjsSyncStep1: number;
        messageYjsSyncStep2: number;
        messageYjsUpdate: number;
        remoteApplyOrigin: symbol;
        writeSyncStep1: (encoder: import("lib0-v14/encoding").Encoder, doc: YDoc) => void;
        writeSyncStep2: (encoder: import("lib0-v14/encoding").Encoder, doc: YDoc, encodedStateVector?: Uint8Array) => void;
        writeUpdate: (encoder: import("lib0-v14/encoding").Encoder, update: Uint8Array) => void;
        applyRemote: (doc: YDoc, update: Uint8Array, transactionOrigin: unknown, errorHandler?: (error: Error) => unknown) => {
            applied: boolean;
            problem: import("./edytor-doc.js").SchemaProblem | null;
            discarded?: import("./edytor-doc.js").SchemaProblem;
        };
        lacks: (doc: YDoc, sv: Uint8Array) => boolean;
        writeSaved: (encoder: import("lib0-v14/encoding").Encoder, doc: YDoc, deletes?: import("./protocols/sync.js").IdSet, unstored?: import("./protocols/sync.js").IdSet | null) => void;
        readSaved: (decoder: import("lib0-v14/decoding").Decoder) => {
            stateVector: Uint8Array;
            deletes: import("./protocols/sync.js").IdSet | null;
        };
    };
    admission: {
        admitUpdate: (update: Uint8Array | ReadonlyArray<Uint8Array>, docName?: string, options?: {
            prepare?: (doc: YDoc) => void;
        }) => YDoc;
    };
    attribution: {
        attach: (doc: import("./engine-api.js").EngineDoc, opts: {
            actor: import("./index.js").AttributionActor;
        }) => import("./index.js").AttributionController;
    };
};
```

## BlockAttribution

```ts
export type BlockAttribution = {
    createdBy?: ActorId;
    contributors: ReadonlySet<ActorId>;
    lastChangedBy?: ActorId;
};
```

- `createdBy`: The actor that created the block — assigned at creation, never rewritten.
- `contributors`: Union set of actors that committed an edit addressing this block id, plus the pre-split snapshot a split/merge copies to siblings — block-identity LINEAGE, add-only.
- `lastChangedBy`: The actor whose state change currently wins the LWW resolution.

## BlockId

Logical block identifier — caller-assigned, immutable per block.

```ts
export type BlockId = string;
```

## BlockRole

Structural role of a block type (plugin-side policy, resolved per op).

```ts
export type BlockRole = {
    void?: boolean;
    island?: boolean;
    lines?: boolean;
    layout?: boolean;
    table?: boolean;
    atomic?: readonly (string | readonly string[])[];
};
```

- `void`: Not editable through normal structural flow; no children, no merges.
- `island`: Editable, but its subtree is structurally sealed from outside blocks.
- `lines`: An island of lines (code): each direct child displays as its `defaultChild` kind and holds no children.
- `layout`: A layout (columns): it displays only its items — its `defaultChild` kind, a container that renders no content — side by side, and only while it shows two or more (`layout.*` in the delete contract).
- `table`: A table: it displays only its rows — its `defaultChild` kind, a container whose own `defaultChild` is the cell kind (a text island) — and each row only its cells, in the order of the table's `data.columns` (`table.*` in the delete contract).
- `atomic`: Data paths written as one leaf (`data.atomic`): a top-level key, or an array of keys for a nested one (`['link', ['media', 'source']]`).

## BlockSpec

Declarative block description used for inserts (and by seeders).

```ts
export type BlockSpec = {
    id: BlockId;
    type: string;
    data?: Record<string, unknown>;
    content?: ContentItem[];
    children?: BlockSpec[];
};
```

## codeKinds

`createCodePlugin()`'s structural rows: the code block is an island of `codeLine`s (`lines`).

```ts
declare const codeKinds: {
    code: {
        island: true;
        lines: true;
        rendersContent: false;
        defaultChild: string;
    };
};
```

## codeSemantics

`createCodePlugin()`'s block roles.

```ts
declare const codeSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## commentMarks

The comments plugin's mark edge: a comment's anchor (`comment:<id>`)
never grows at its ends, so text typed or inserted concurrently right
after commented text stays out of the comment (Notion).

```ts
declare const commentMarks: {
    comment: {
        edge: "exclusive";
    };
};
```

## ContentItem

One inline content element in a block spec / projection.

```ts
export type ContentItem = {
    kind: 'text';
    text: string;
    marks?: Record<string, unknown>;
} | {
    kind: 'inline';
    id: string;
    type: string;
    data?: Record<string, unknown>;
};
```

## ContentRun

One visible run of a block — the maintained form of `ContentItem`.
`marks`/`data` are interned: equal payloads share one frozen instance,
so `===` compares formatting without deep walks. A run carries no
authorship: runs never split at author boundaries, and durable
attribution is the compact per-block record (`blockattr`).

```ts
export type ContentRun = {
    kind: 'text';
    text: string;
    marks?: Record<string, unknown>;
} | {
    kind: 'inline';
    id: string;
    type: string;
    data?: Record<string, unknown>;
};
```

## Crdt

```ts
export type Crdt = ReturnType<typeof bindCrdt>;
```

## createDocument

See `DocumentBinding.createDocument`.

```ts
declare const createDocument: (options?: CreateDocumentOptions) => EdytorDocument;
```

## CreateDocumentOptions

```ts
export type CreateDocumentOptions = DocumentOptions & {
    value?: JSONDoc;
};
```

## DataPatch

One data edit at `path` (object keys and array indexes from the data's
root): `splice` takes `Array.prototype.splice`'s arguments and `order`
the array's current indexes in their new order; else it sets `value`, or
deletes the key or removes the item when there is none.

```ts
export type DataPatch = {
    readonly path: readonly string[];
    readonly value?: unknown;
    readonly splice?: readonly [
        start: number,
        deleteCount?: number,
        ...items: unknown[]
    ];
    readonly order?: readonly number[];
};
```

## DataTarget

What a data patch edits: a block, one of its inline atoms, or the document (`null`).

```ts
export type DataTarget = BlockId | null | {
    block: BlockId;
    atom: string;
};
```

## DEFAULT_HISTORY_LIMIT

The undo steps a document's history keeps by default: older ones are released.

```ts
declare const DEFAULT_HISTORY_LIMIT = 200;
```

## DEFAULT_READINESS_BOUND

How long (ms) an empty document waits for a provider that has not
settled before it decides without it (settle-or-bound) — the bound a
provider that cannot report "settled" gets, counted from its attach. A
factory sets its own with `sync.bound`, or arms it from transport state
(`bound: Infinity` + the payload's `armBound`, as the websocket sync
does: from the socket's open, so a slow dial never counts).

```ts
declare const DEFAULT_READINESS_BOUND = 1000;
```

## defaultSemantics

The rich-text, code, image, media, columns, page, table of contents,
table and equation plugins' block roles together (and the rich-text and
comment mark edges) — what the room and a headless
`createDocument`/`loadDocument` adopt by default (`semantics: {}` checks
none). The media, columns, page, table of contents, table and equation
plugins are not default plugins of `<Edytor>`, but their roles are here so their
blocks read the same on every replica.

```ts
declare const defaultSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
} & {
    marks: Record<string, {
        edge: MarkEdge;
    }>;
};
```

## Destination

Where a block goes: `parent` = logical block id or `null` for root.

```ts
export type Destination = {
    parent: BlockId | null;
    index: number;
};
```

## DocAnchor

A selection endpoint: `b` is the home block of the backing text the
position lives in (NOT necessarily the block that displays it — merges and
splits reroute display while the anchor stays on the same items), `a` an
engine relative position whose `a` carries the side (`< 0` left, `>= 0`
right). The containing stream and the side are two facts in two fields.
JSON-serializable — the presence and history wire shape.

```ts
export type DocAnchor = {
    b: BlockId;
    a: Anchor;
};
```

## DocChange

One committed transaction's semantic diff — the payload `EdytorDoc.onChange`
subscribers receive. Every collection names the affected ids; `order`
carries the NEW child-id list per changed parent so a mirror can apply the
diff without re-reading the doc. The report is shared: every subscriber
of the document (each view over it, its cells included) receives the same
object, so it is read-only.

```ts
export type DocChange = {
    origin: unknown;
    local: boolean;
    version: number;
    added: ReadonlyMap<BlockId, ProjectedBlock>;
    removed: ReadonlySet<BlockId>;
    moved: ReadonlySet<BlockId>;
    meta: ReadonlyMap<BlockId, {
        type: string;
        data?: Record<string, unknown>;
    }>;
    content: ReadonlyMap<BlockId, readonly ContentRun[]>;
    order: ReadonlyMap<BlockId | null, readonly BlockId[]>;
    data?: Record<string, unknown>;
};
```

- `origin`: Transaction origin (local origin object, remote marker, …).
- `local`: `transaction.local` — false for remote-applied updates.
- `version`: Monotonic event counter per facade.
- `added`: Newly visible blocks → full projected subtree (incl.
- `removed`: Ids no longer visible (deleted, merged-away, or hidden with subtree).
- `moved`: Ids whose display parent or sibling index changed.
- `meta`: Ids whose `type`/`data` payload changed → new values.
- `content`: Ids whose visible content changed → the new maintained runs.
- `order`: Parents (`null` = root) whose visible child list changed → new order (frozen — shared with the retained snapshot baseline).
- `data`: The document's own data (`docData()`), when this commit changed it.

## DocPosition

A position in one document version: a block and a display offset.

```ts
export type DocPosition = {
    block: BlockId;
    offset: number;
};
```

## DOCUMENT_OPERATIONS

Every name of `DocumentOperations`, for the document's own members (`EdytorDocument`).

```ts
declare const DOCUMENT_OPERATIONS: readonly [
    "insertBlocks",
    "moveBlocks",
    "nestBlock",
    "unNestBlock",
    "unNestBlocks",
    "liftOut",
    "wrapInLayout",
    "placeBeside",
    "splitBlock",
    "mergeBlocks",
    "mergeBackward",
    "mergeForward",
    "deleteBlock",
    "deleteBlocks",
    "setBlock",
    "setBlockType",
    "patchData",
    "duplicateBlock",
    "insertText",
    "deleteText",
    "formatRange",
    "clearMarks",
    "insertInline",
    "removeInline",
    "deleteRange",
    "replaceRange",
    "insertFlow",
    "toJSON",
    "docData",
    "dataItemIds",
    "project",
    "blockJSON",
    "childrenIds",
    "parentOf",
    "ancestorsOf",
    "positionOf",
    "pathOf",
    "blockTypeOf",
    "blockDataOf",
    "blockText",
    "contentItems",
    "displayLength",
    "runs",
    "hasBlock",
    "isVisibleBlock",
    "listBlockIds",
    "blockAttribution",
    "isVoid",
    "isIsland",
    "isLines",
    "islandOf",
    "insideIsland",
    "isLayout",
    "isLayoutItem",
    "besideAt",
    "isTable",
    "isTableRow",
    "isTableCell",
    "tableOf",
    "tableColumns",
    "tableGrid",
    "order",
    "compare",
    "next",
    "previous",
    "canPlace",
    "canMerge",
    "fits",
    "nestParent",
    "landingOf",
    "anchorAt",
    "resolveAnchor",
    "prepare",
    "apply",
    "compose",
    "transact",
    "onChange",
    "version"
];
```

## DOCUMENT_WRITES

Every write of `DocumentWrites`: what a view dispatches under the same names (`edytor.insertBlocks(…)`).

```ts
declare const DOCUMENT_WRITES: readonly [
    "insertBlocks",
    "moveBlocks",
    "nestBlock",
    "unNestBlock",
    "unNestBlocks",
    "liftOut",
    "wrapInLayout",
    "placeBeside",
    "splitBlock",
    "mergeBlocks",
    "mergeBackward",
    "mergeForward",
    "deleteBlock",
    "deleteBlocks",
    "setBlock",
    "setBlockType",
    "patchData",
    "duplicateBlock",
    "insertText",
    "deleteText",
    "formatRange",
    "clearMarks",
    "insertInline",
    "removeInline",
    "deleteRange",
    "replaceRange",
    "insertFlow"
];
```

## DocumentActor

Local actor identity — durable across replicas, independent of presence profiles.

```ts
export type DocumentActor = {
    id: string;
    name?: string;
    color?: string;
};
```

- `id`: Stable author identifier (used by awareness and, later, native attribution).

## DocumentAttribution

The document-level attribution surface. `actors`/`actorOf`/`block`
read replicated doc state directly — they resolve remote-applied
records identically and stay correct as old replicas append records.

```ts
export type DocumentAttribution = {
    readonly actors: ReadonlyMap<string, ActorProfile>;
    actorOf(client: number): string | undefined;
    setProfile(profile: ActorProfile): void;
    block(blockId: string): BlockAttribution | undefined;
};
```

- `actors`: actorId → profile — the replicated actor dictionary (`u/` records).
- `actorOf`: Resolve an item's inserting replica (clientID) to an actor id (`c/` records).
- `setProfile`: Re-publish the local actor's profile record (`u/<actorId>` — LWW).
- `block`: Compact per-block attribution for `blockId` (`{createdBy, contributors, lastChangedBy}` — durable actor ids, never replica clientIDs), or `undefined` for system/foreign blocks that carry no record.

## DocumentDestroyedError

Raised when an operation runs against a torn-down document — every
attach reference has been released and the facade/history/awareness
teardown already ran. Distinct from `DocumentNotReadyError`:
destroyed is terminal, pending may still resolve.

```ts
export declare class DocumentDestroyedError extends Error {
    constructor(service?: string);
}
```

## DocumentNotReadyError

Raised when a readiness-gated service is used on a pending document.

```ts
export declare class DocumentNotReadyError extends Error {
    constructor(service: string);
}
```

## DocumentOperations

The document's public operations: the writes, each also prepared
(`prepare.<op>(…)`, pure, then `apply(plan)`; `compose` joins plans),
the reads, `transact`, `onChange` and `version`. See the document API
reference.

```ts
export interface DocumentOperations extends DocumentWrites, DocumentReads {
    readonly prepare: {
        readonly [K in keyof DocumentWrites]: (...args: Parameters<DocumentWrites[K]>) => Prepared;
    };
    apply(plan: Prepared): OpResult;
    compose(...parts: Prepared[]): Prepared;
    transact<R>(fn: () => R, origin?: unknown): R;
    onChange(listener: (change: DocChange) => void): () => void;
    readonly version: number;
}
```

- `prepare`: Every write, prepared: a plan of named steps and its effect, or `refused`.
- `apply`: Write a prepared plan exactly (a refusal passes through).
- `compose`: One plan of `parts`, applied in order.
- `transact`: Group writes in one transaction and one change event (no rollback on a throw).
- `onChange`: Hear each transaction's change; answers the unsubscribe.
- `version`: Changes on every write that changed the tree.

## DocumentOptions

```ts
export type DocumentOptions = {
    actor?: DocumentActor;
    semantics?: DocumentSemanticsConfig;
    awareness?: Awareness;
    history?: {
        captureTimeout?: number;
        limit?: number;
    };
    requireHydration?: boolean;
};
```

- `actor`: Local actor — anonymous opaque id when absent.
- `semantics`: Up-front semantic configuration (views may also seed via `adoptSemantics`).
- `awareness`: Borrow an existing awareness instance instead of creating one — `destroy()` will NOT destroy it (same owned/borrowed rule as the doc).
- `history`: History tuning — applied when the document's undo manager attaches.
- `requireHydration`: Decide an EMPTY document only from a provider that synced: the room answered, or a local store hydrated.

## DocumentReadiness

How the document's content state was decided — the distinction the
readiness contract requires:
- `pending` — undecided (created without a value or attached before
  `sync()`; a provider may still hydrate — nothing bootstrapped,
  nothing broadcast). Reads/attaches/`sync()` legal, `history`
  refuses;
- `local` — seeded locally (`createDocument({value})`, or `sync()` on a
  still-fresh doc — seed-if-empty bootstrap included). Full surface
  legal;
- `hydrated` — the doc was already initialized when `sync()` ran:
  provider-applied remote state or a `loadDocument` restore (schema
  asserted, content untouched). Full surface legal.

`sync()` is the only transition out of `pending`, and it is the
document-side admission re-check: a doc that arrived in a problem
state (unversioned/unsupported/foreign — e.g. via raw
`applyUpdate` bypass writes) refuses there with a typed error, stays
`pending`, and preserves the state — never silently becoming `local`.

```ts
export type DocumentReadiness = 'pending' | 'local' | 'hydrated';
```

## DocumentReads

The document's reads (transaction-aware where the reference says so).

```ts
export interface DocumentReads {
    toJSON(): JSONDoc;
    docData(): JsonObj;
    dataItemIds(target: DataTarget, path: readonly string[]): string[];
    project(): ProjectedDoc;
    blockJSON(id: BlockId): JSONBlock;
    childrenIds(parent: BlockId | null): BlockId[];
    parentOf(id: BlockId): BlockId | null;
    ancestorsOf(id: BlockId): BlockId[];
    positionOf(id: BlockId): Destination | null;
    pathOf(id: BlockId): number[] | null;
    blockTypeOf(id: BlockId): string | undefined;
    blockDataOf(id: BlockId): Record<string, unknown> | undefined;
    blockText(id: BlockId): string | null;
    contentItems(id: BlockId): ContentItem[];
    displayLength(id: BlockId): number;
    runs(id: BlockId): readonly ContentRun[];
    hasBlock(id: BlockId): boolean;
    isVisibleBlock(id: BlockId): boolean;
    listBlockIds(): BlockId[];
    blockAttribution(id: BlockId): BlockAttribution | undefined;
    isVoid(id: BlockId): boolean;
    isIsland(id: BlockId): boolean;
    isLines(id: BlockId): boolean;
    islandOf(id: BlockId): BlockId | null;
    insideIsland(id: BlockId): boolean;
    isLayout(id: BlockId): boolean;
    isLayoutItem(id: BlockId): boolean;
    besideAt(id: BlockId, kind?: string): BlockId;
    isTable(id: BlockId): boolean;
    isTableRow(id: BlockId): boolean;
    isTableCell(id: BlockId): boolean;
    tableOf(id: BlockId): BlockId | null;
    tableColumns(id: BlockId): {
        id: string;
        width?: number;
    }[] | null;
    tableGrid(id: BlockId): {
        columns: string[];
        rows: {
            id: BlockId;
            cells: (BlockId | null)[];
        }[];
    } | null;
    order(): readonly BlockId[];
    compare(a: BlockId, b: BlockId): number;
    next(id: BlockId, policy?: OrderPolicy): BlockId | null;
    previous(id: BlockId, policy?: OrderPolicy): BlockId | null;
    canPlace(ids: readonly BlockId[], parent?: BlockId | null): boolean;
    canMerge(from: BlockId, into: BlockId): boolean;
    fits(parent: BlockId | null, kind: string): boolean;
    nestParent(ids: readonly BlockId[], parent: BlockId): BlockId;
    landingOf(id: BlockId, kind: string, after?: boolean): {
        parent: BlockId | null;
        levels: BlockId[];
    };
    anchorAt(id: BlockId, offset: number, affinity?: AnchorAffinity): DocAnchor | null;
    resolveAnchor(anchor: DocAnchor): {
        blockId: BlockId;
        offset: number;
    } | null;
}
```

## DocumentSemanticsConfig

Document-level semantic configuration — the structural subset of what
plugin `BlockDefinition`s carry (roles, `rendersContent`, `defaultChild`)
plus the default block type. Snippets/DOM hooks are deliberately NOT
part of this: they are view-side rendering policy.

```ts
export type DocumentSemanticsConfig = {
    roles?: Record<string, BlockRole>;
    rendersContent?: Record<string, boolean>;
    defaultChild?: Record<string, string>;
    defaultType?: string;
    marks?: Record<string, {
        edge?: MarkEdge;
    }>;
};
```

- `roles`: Structural role per block type (`{void?, island?, lines?, layout?}` — absent flags mean false).
- `rendersContent`: Whether a kind renders its own content slot (undeclared kinds do).
- `defaultChild`: Default child type per parent type (undeclared parents take `defaultType`).
- `defaultType`: Default block type — the root's default child and the bootstrap block.
- `marks`: Per mark, its edge (its record's `edge`) — where a concurrent insert at each end of a mark lands.

## DocumentWrites

The document's writes: each one transaction, answering what it did (`OpResult`).

```ts
export interface DocumentWrites {
    insertBlocks(dest: Destination, specs: readonly (JSONBlock | BlockSpec)[]): OpResult;
    moveBlocks(ids: readonly BlockId[], dest: Destination): OpResult;
    nestBlock(id: BlockId, parent: BlockId): OpResult;
    unNestBlock(id: BlockId): OpResult;
    unNestBlocks(ids: readonly BlockId[]): OpResult;
    liftOut(id: BlockId, kind: string, options?: {
        keep?: boolean;
        after?: readonly BlockSpec[];
    }): OpResult;
    wrapInLayout(ids: readonly BlockId[], kind?: string, columns?: number): OpResult;
    placeBeside(ids: readonly BlockId[], target: BlockId, side: 'left' | 'right', kind?: string): OpResult;
    splitBlock(id: BlockId, offset: number, newId: BlockId, tail?: SplitTail): OpResult;
    mergeBlocks(fromId: BlockId, intoId: BlockId): OpResult;
    mergeBackward(id: BlockId): OpResult;
    mergeForward(id: BlockId): OpResult;
    deleteBlock(id: BlockId, options?: {
        keepChildren?: boolean;
    }): OpResult;
    deleteBlocks(ids: readonly BlockId[]): OpResult;
    setBlock(id: BlockId, value: {
        type?: string;
        data?: Record<string, unknown>;
        content?: (ContentItem | JSONText | JSONInlineBlock)[];
        children?: (BlockSpec | JSONBlock)[];
    }): OpResult;
    setBlockType(id: BlockId, type: string): OpResult;
    patchData(target: DataTarget, patches: readonly DataPatch[]): OpResult;
    duplicateBlock(id: BlockId, freshId?: (oldId: string, kind: 'block' | 'inline') => string): OpResult;
    insertText(id: BlockId, offset: number, text: string, marks?: Record<string, unknown>): OpResult;
    deleteText(id: BlockId, offset: number, length: number): OpResult;
    formatRange(id: BlockId, offset: number, length: number, marks: Record<string, unknown>): OpResult;
    clearMarks(id: BlockId, offset: number, length: number): OpResult;
    insertInline(id: BlockId, offset: number, atom: NewInline): OpResult;
    removeInline(id: BlockId, inlineId: string): OpResult;
    deleteRange(from: DocPosition, to: DocPosition, view?: RangeView): OpResult;
    replaceRange(from: DocPosition, to: DocPosition, view?: RangeView): OpResult;
    insertFlow(target: FlowTarget, flow: Flow, view?: FlowView): OpResult;
}
```

- `duplicateBlock`: `freshId` names each copy (fresh ids by default).
- `insertInline`: An atom without an id gets a fresh one.

## EdytorDoc

```ts
export type EdytorDoc = ReturnType<EdytorDocBinding['create']>;
```

## EdytorDocDisposedError

Raised when a mutating facade op runs after `dispose()` — disposal is
terminal for writes (reads stay dead-safe: a stale handle may still be
inspected while its document outlives the facade). The facade-level
counterpart of `DocumentDestroyedError` — kept in this module because a
bare `bindEdytorDoc` facade has no document layer above it.

```ts
export declare class EdytorDocDisposedError extends Error {
    constructor(service?: string);
}
```

## EdytorDocument

The document's operations and reads are its own members
(`document.insertBlocks(…)`, `document.toJSON()`): see `DocumentOperations`.
`transact` is the document's (its local origin by default).

```ts
export interface EdytorDocument extends Omit<DocumentOperations, 'transact'> {
}
export declare class EdytorDocument {
    #private;
    readonly doc: YDoc;
    readonly awareness: Awareness;
    readonly actor: DocumentActor;
    get attribution(): DocumentAttribution;
    readonly transaction: TRANSACTION;
    private _destroyed;
    readonly requireHydration: boolean;
    get clientID(): number;
    get ready(): boolean;
    get readiness(): DocumentReadiness;
    get destroyed(): boolean;
    get writable(): boolean;
    onWritableChange: (listener: (writable: boolean) => void) => (() => void);
    get historyLimit(): number;
    get semantics(): {
        defaultType: string;
        roles: Map<string, BlockRole>;
        rendersContent: Map<string, boolean>;
        defaultChild: Map<string, string>;
        marks: Map<string, MarkEdge>;
    };
    defaultChild: (parentType: string | null) => string;
    rendersContent: (type: string) => boolean;
    adoptSemantics: (config: DocumentSemanticsConfig) => void;
    sync: (value?: JSONDoc) => void;
    get history(): YUndoManager;
    trackOrigin: (origin: unknown) => void;
    untrackOrigin: (origin: unknown) => void;
    clearHistory: () => void;
    transact: <T>(cb: () => T, origin?: unknown) => T;
    onReady: (listener: () => void) => (() => void);
    get syncPending(): boolean;
    get syncRefusal(): SyncRefusedError | undefined;
    onSyncRefused: (listener: (refusal: SyncRefusedError) => void) => (() => void);
    private _decide;
    attachSync: (sync: EdytorSync, opts?: {
        value?: JSONDoc;
    }) => EdytorSyncCleanup | void;
    encode: () => Uint8Array;
    retain: () => (() => void);
    destroy: () => void;
}
```

- `doc`: The underlying engine doc — raw reads/providers borrow it; they never own it.
- `awareness`: One shared awareness instance for every view/provider of this document.
- `actor`: The local actor identity (anonymous opaque id when none was supplied).
- `attribution`: The attribution surface — compact per-block records and the replicated actor dictionary (`u/` profiles, `c/` replica bindings).
- `transaction`: The document's own local-edit transaction origin — headless edits via `transact` carry it so history capture attributes them.
- `requireHydration`: `DocumentOptions.requireHydration`: an empty document waits for a provider's `synced`.
- `clientID`: The replica id (`doc.clientID`) — identifies the replica, not the actor.
- `ready`: `true` once the document's content state is decided — seeded locally or hydrated then synced.
- `readiness`: `pending` | `local` | `hydrated` — distinguishes a locally-seeded ready from a provider-hydrated/restored ready (the contract consumers need to tell "I decided this content" from "sync brought it").
- `writable`: `false` while the document carries a schema stamp this build cannot own (a foreign stamp got in despite the transport's inbound refusal): every write refuses with a `SchemaMismatchError`, and the providers neither persist nor broadcast it.
- `onWritableChange`: Subscribe to `writable` transitions — the visible signal that the document turned read-only (once per transition, not per refused edit).
- `historyLimit`: The undo steps this document's history keeps: the configured `history.limit`, else `DEFAULT_HISTORY_LIMIT`.
- `semantics`: The document-level semantic configuration (snapshot).
- `defaultChild`: The default child type under a parent of `parentType` (`null` = the root) — the one answer split, paragraph insert, merge-unnest, clear and root normalization apply against the new block's actual parent.
- `rendersContent`: Whether kind `type` renders its own content slot (undeclared kinds do).
- `adoptSemantics`: Contribute document-level semantics — the seam views use to seed the structural roles implied by their plugin block definitions.
- `sync`: The readiness transition — decide the document's content state.
- `history`: The default local history (the registry-scoped undo manager — `undo`/`redo`/`stopCapturing`/stacks).
- `trackOrigin`: Track a local-edit transaction origin for history capture — the seam views use to enroll their own `TRANSACTION` instance in this document's history.
- `untrackOrigin`: Release a previously-tracked origin — the view-teardown counterpart of `trackOrigin`.
- `clearHistory`: Drop the undo/redo stacks.
- `transact`: Local document transaction — headless edits, one undo step per call (`origin`: another origin, which the document's history tracks only when it is tracked).
- `onReady`: Subscribe to the readiness transition — fires once, synchronously, inside `sync` when `_readiness` leaves `'pending'` (every decision path goes through `sync()`: provider `synced`, local seed, explicit call).
- `syncPending`: Whether an attached provider has neither settled nor reached its bound.
- `syncRefusal`: The server's refusal of a provider (a `1008`/`4xxx` close — see `SyncRefusedError`), the latest still standing: that provider stopped.
- `onSyncRefused`: Subscribe to provider refusals (`syncRefusal`).
- `attachSync`: Attach a provider sync factory to this document (headless `EdytorSync` path — the same contract views use).
- `encode`: Full replicated state as a v14 update — `loadDocument` restores it on a fresh replica.
- `retain`: Acquire one more attach reference and return its ONE-SHOT release capability.
- `destroy`: Release ONE attach reference.

## EdytorSync

```ts
export type EdytorSync = ((payload: EdytorSyncPayload) => void | EdytorSyncCleanup) & {
    bound?: number;
    target?: string;
};
```

## EdytorSyncCleanup

```ts
export type EdytorSyncCleanup = () => void | Promise<void>;
```

## EdytorSyncPayload

Payload a sync factory receives — the v13 contract plus `failed`:
the terminal half of the sync lifecycle. A provider that can never reach
`synced` (destroyed before syncing, persistence load failure, refused
hydration, denied auth) reports it here exactly once; a transient
disconnect or a provider that already synced never does. A
`SyncRefusedError` is the exception: the server refused this
client for good, reported synced or not, and an empty document does not
seed over it.

```ts
export type EdytorSyncPayload = {
    doc: YDoc;
    awareness: Awareness;
    synced: (provider?: unknown) => void;
    failed?: (error: unknown, provider: unknown) => void;
    armBound?: () => void;
    holdBound?: () => void;
    attach?: (sync: EdytorSync) => EdytorSyncCleanup | void;
};
```

- `armBound`: (Re)start this provider's readiness bound (`DEFAULT_READINESS_BOUND` ms from now) — for a factory whose `bound` is `Infinity` because only its transport knows when waiting starts to count (the websocket sync: from the socket's open, or its first failed dial).
- `holdBound`: Stop this provider's readiness bound: an empty document waits for it to settle (or for the next `armBound()`), as the websocket sync does after expired credentials on a first visit.
- `attach`: Attach another sync to the same document as its own provider (its own target, bound and settle): how a sync brings a companion, e.g.

## EngineApi

The engine surface every `bind*` receives: the object in `engine.js`
(exactly the symbols edytor calls, so consumer bundles tree-shake the
rest). The full namespace `import * as Y from 'edytor/crdt'` is a
superset and satisfies it.

```ts
export type EngineApi = typeof Engine;
```

## EngineDoc

Structural minimum of the vendored `Y.Doc`.

```ts
export interface EngineDoc {
    clientID: number;
    keepReplaced?: ((item: never) => boolean) | null;
    get(key?: string): EngineNode;
    transact<T>(f: (transaction: unknown) => T, origin?: unknown): T;
    on(name: 'update', f: (update: Uint8Array, origin: unknown, doc: EngineDoc, transaction: unknown) => void): void;
    off(name: 'update', f: (update: Uint8Array, origin: unknown, doc: EngineDoc, transaction: unknown) => void): void;
    on(name: 'commit', f: (transaction: never) => void): void;
    off(name: 'commit', f: (transaction: never) => void): void;
    on(name: 'destroy', f: () => void): void;
    off(name: 'destroy', f: () => void): void;
    readonly store?: {
        pendingStructs: null | {
            missing: Map<number, number>;
            update: Uint8Array;
        };
        pendingDs: null | Uint8Array;
        getClock(client: number): number;
    };
    readonly _transaction?: EngineTransaction | null;
}
```

- `keepReplaced`: Which node values a concurrent write of the same key replaced keep their subtree (`null`: none, upstream's semantics).
- `on`: Doc-level `update` channel — fires once per transaction that produced replicated state (local commits AND remote `applyUpdate`s), carrying the transaction's origin and the transaction itself (`transaction.local` distinguishes local commits from remote applies).
- `on`: Doc-level `update` channel — fires once per transaction that produced replicated state (local commits AND remote `applyUpdate`s), carrying the transaction's origin and the transaction itself (`transaction.local` distinguishes local commits from remote applies).
- `on`: Doc-level `update` channel — fires once per transaction that produced replicated state (local commits AND remote `applyUpdate`s), carrying the transaction's origin and the transaction itself (`transaction.local` distinguishes local commits from remote applies).
- `store`: The doc's struct store — present on a real engine doc.
- `_transaction`: The engine's CURRENT transaction (`null` outside one) — vendored `Doc._transaction`.

## EngineItemRef

Engine item identity — used by `crdtId` (`client:clock`).

```ts
export type EngineItemRef = {
    id?: {
        client: number;
        clock: number;
    };
    deleted?: boolean;
    parent?: unknown;
    parentSub?: string | null;
} | null;
```

## EngineNode

Structural minimum of the vendored `Y.Node` (v14) used by the placement
model. A real `YNode` satisfies this at runtime; typed consumers cast once
at the boundary (`doc as unknown as EngineDoc`).

```ts
export interface EngineNode {
    readonly name: string;
    readonly doc: unknown | null;
    readonly length: number;
    readonly _item: EngineItemRef;
    getAttr(key: string): unknown;
    setAttr(key: string, value: unknown): unknown;
    deleteAttr(key: string): void;
    forEachAttr(f: (value: unknown, key: string, node: EngineNode) => void): void;
    attrKeys(): IterableIterator<string>;
    insert(index: number, content: unknown): void;
    delete(index: number, length?: number): void;
}
```

- `name`: Node name ('block' | 'content' | 'at' | 'inline' in our schema).
- `doc`: The doc this node is integrated into; `null` while detached.
- `length`: Sequence length (invalid read on detached nodes — track offsets instead).
- `_item`: The item this node is attached to (identity anchor for `crdtId`).

## EngineTransaction

Structural minimum of the in-flight engine `Transaction` — exposed on the
doc as `doc._transaction` while a transaction is open. `changed` maps each
touched shared type to the set of `parentSub` keys whose items changed
(attr names for map-like children, `null` for sequence edits); the
document index folds it once at commit.

```ts
export interface EngineTransaction {
    changed?: Map<unknown, Set<string | null>>;
}
```

## equationKinds

The equation plugin's structural row: a block equation is a void that
draws its TeX (`data.expression`) and shows no text of its own. Its
inline equation is an atom, which needs no role.

```ts
declare const equationKinds: {
    equation: {
        void: true;
        rendersContent: false;
    };
};
```

## equationSemantics

The equation plugin's block role.

```ts
declare const equationSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## facadeConfigOf

The facade configuration (`EdytorDocConfig`) a document with no view
reads `semantics` with — the room's: roles, default children, content-less kinds
and mark edges, each answered only for a kind the tables name.

```ts
declare const facadeConfigOf: (semantics: DocumentSemanticsConfig) => {
    roleOf: (type: string) => BlockRole | undefined;
    kinds: () => string[];
    defaultChildOf: (type: string) => string | undefined;
    rendersContent: (type: string) => boolean;
    markEdge: (mark: string) => MarkEdge | undefined;
    defaultType: string | undefined;
};
```

## FlowView

What the view tells flow placement beyond range deletion's view: a
container's flat item kind (`numbered-list-item` for an `ordered-list`).

```ts
export type FlowView = RangeView & {
    itemKind?: (parent: BlockId) => string | undefined;
    header?: (id: BlockId) => boolean | 'closed';
};
```

## imageKinds

`createImagePlugin()`'s structural rows: void, its only text is the caption.

```ts
declare const imageKinds: {
    image: {
        void: true;
    };
};
```

## imageSemantics

`createImagePlugin()`'s block roles.

```ts
declare const imageSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## IndexeddbSyncOptions

```ts
export type IndexeddbSyncOptions = {
    awareness?: Awareness;
};
```

## InlineSpec

```ts
export type InlineSpec = {
    id: string;
    type: string;
    data?: Record<string, unknown>;
};
```

## JSONBlock

```ts
export type JSONBlock = {
    type: string;
    id?: string;
    data?: Record<string, SerializableContent>;
    children?: JSONBlock[];
    content?: (JSONText | JSONInlineBlock)[];
};
```

## JSONDoc

```ts
export type JSONDoc = {
    data?: Record<string, SerializableContent>;
    children: JSONBlock[];
} & Partial<Record<string, unknown>>;
```

## JSONInlineBlock

```ts
export type JSONInlineBlock = {
    id?: string;
    data?: any;
    type: string;
};
```

## JSONText

```ts
export type JSONText = {
    text: string;
    marks?: Record<string, SerializableContent>;
};
```

## KindRecord

A kind record as a plugin declares it (`plugin.blocks[type]`): its
structural row with whatever else it holds (a snippet, an element,
presets), or a bare snippet, a plain kind.

```ts
export type KindRecord = KindSemantics | ((...args: never[]) => unknown);
```

## KindSemantics

One kind's structural row — the fields of its `BlockDefinition` a document adopts.

```ts
export type KindSemantics = BlockRole & {
    rendersContent?: boolean;
    defaultChild?: string;
};
```

## LastUpdatedOptions

What `lastUpdated` takes: the room it asks, and the `params` (a token) its dial would carry.

```ts
export type LastUpdatedOptions = WebsocketTarget & {
    params?: Record<string, string>;
    fetch?: typeof fetch;
};
```

## layoutKinds

The columns plugin's structural rows: `columns` is a layout of `column`s
(`layout.*` in the delete contract), a column a container that holds any
block. In `defaultSemantics`: a document holding no `columns` block
is unchanged by them, and every replica of one that does (a view with the
plugin, a headless document, the room) reads the same layout rules.

```ts
declare const layoutKinds: {
    columns: {
        layout: true;
        rendersContent: false;
        defaultChild: string;
    };
    column: {
        rendersContent: false;
    };
};
```

## layoutSemantics

The columns plugin's block roles (a layout of columns).

```ts
declare const layoutSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## loadDocument

See `DocumentBinding.loadDocument`.

```ts
declare const loadDocument: (update: Uint8Array, options?: LoadDocumentOptions) => EdytorDocument;
```

## LoadDocumentOptions

```ts
export type LoadDocumentOptions = DocumentOptions;
```

## MarkEdge

Whether a mark grows at its edges — always, never, or at its start only.

```ts
export type MarkEdge = 'inclusive' | 'exclusive' | 'side-dependent';
```

## markName

The record a mark key names: `comment:c1` → `comment` (a key of its own otherwise).

```ts
declare const markName: (key: string) => string;
```

## mediaKinds

The media plugins' structural rows (`createEmbedPlugin()`, `createBookmarkPlugin()`,
`createFilePlugin()`, `createVideoPlugin()`, `createAudioPlugin()`): void like the image, their
only text is the caption.

```ts
declare const mediaKinds: {
    embed: {
        void: true;
    };
    bookmark: {
        void: true;
    };
    file: {
        void: true;
    };
    video: {
        void: true;
    };
    audio: {
        void: true;
    };
};
```

## mediaSemantics

The media plugins' block roles (embed, bookmark, file, video, audio).

```ts
declare const mediaSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## MergedSemantics

The merged config's fields, each present (empty when no config named one).

```ts
export type MergedSemantics = {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
    marks: Record<string, {
        edge?: MarkEdge;
    }>;
    defaultType?: string;
};
```

## mergeSemantics

Several configs as one, deeply frozen, merged field by field (`roles`,
`rendersContent`, `defaultChild`, `marks`, `defaultType`): the room's or
a headless document's roles for an app's own kinds beside the bundled
ones, `mergeSemantics(defaultSemantics, semanticsOf(myKinds))`. A
top-level spread of two configs would keep only the last one's fields.
A kind (or mark, or the default type) two configs declare otherwise
throws `SemanticConflictError`, as a view's conflicting plugin does
on a document; an equal declaration merges.

```ts
declare const mergeSemantics: (...configs: DocumentSemanticsConfig[]) => MergedSemantics;
```

## MetaClientState

```ts
export type MetaClientState = {
    clock: number;
    lastUpdated: number;
};
```

- `lastUpdated`: unix timestamp

## OpResult

The observed outcome of one document operation — the one result
shape every op returns, empty inputs included. `refused`: the op did not
apply and wrote nothing; `noop`: it applied and changed nothing; `applied`:
the transaction wrote. Read from the transaction's effects, never predicted.

```ts
export type OpResult = {
    readonly status: 'refused' | 'noop' | 'applied';
    readonly ids: readonly BlockId[];
    readonly reason?: string;
};
```

- `ids`: What the op is about — created, moved, merge target, or its target; empty unless applied.
- `reason`: Why a refused op refused, when it names a reason (`'id-collision'`).

## OrderPolicy

Island-sealing policy for a walk in document order (see `next`).

```ts
export type OrderPolicy = {
    sealed?: boolean;
};
```

## pageKinds

The page plugin's structural row: a link to another document (a subpage),
void and showing no text of its own, as a divider.

```ts
declare const pageKinds: {
    page: {
        void: true;
        rendersContent: false;
    };
};
```

## pageSemantics

The page plugin's block role (a link to another document).

```ts
declare const pageSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## Plan

A prepared operation: its steps, their effect, the ids the op
is about, and the document version it was prepared against — valid only
there, applied in the same synchronous turn.

```ts
export type Plan = {
    readonly ids: readonly BlockId[];
    readonly writes: readonly PlanStep[];
    readonly effect: PlanEffect;
    readonly version: number;
    readonly at?: DocPosition;
};
```

- `at`: Where the op leaves the caret, when it decides one (`deleteRange`).

## PrefetchOptions

What `bindProviders`' `prefetch` takes: a websocket sync's target and options.

```ts
export type PrefetchOptions = (WebsocketSyncOptions extends infer O ? O extends unknown ? Omit<O, 'onExpired' | 'persist' | 'disableBc'> : never : never) & {
    timeout?: number;
};
```

## PrefetchResult

What `prefetch` found: whether the room sent the store anything it lacked.

```ts
export type PrefetchResult = {
    updated: boolean;
};
```

## Prepared

`prepare`'s answer: a plan, or the op's refusal.

```ts
export type Prepared = Plan | OpResult;
```

## ProjectedBlock

Canonical projected block — the comparison surface for convergence.

```ts
export type ProjectedBlock = {
    id: BlockId;
    type: string;
    data?: Record<string, unknown>;
    content: ContentItem[];
    children: ProjectedBlock[];
    malformed?: true;
};
```

- `malformed`: Set when the registry node could not be read completely.

## ProjectedDoc

```ts
export type ProjectedDoc = {
    children: ProjectedBlock[];
};
```

## ProviderStack

```ts
export type ProviderStack = ReturnType<typeof bindProviders>;
```

## RangeView

What the view knows of a range (view state the document does not hold):
whether it hides a block, and, given `removed`, whether the block stays
hidden once those go (a removed closed toggle's children take its place, shown).

```ts
export type RangeView = {
    hidden?: (id: BlockId, removed?: ReadonlySet<BlockId>) => boolean;
};
```

## richTextKinds

`createRichTextPlugin()`'s structural rows (its other kinds are plain).

```ts
declare const richTextKinds: {
    divider: {
        void: true;
        rendersContent: false;
    };
    'ordered-list': {
        rendersContent: false;
        defaultChild: string;
    };
    'unordered-list': {
        rendersContent: false;
        defaultChild: string;
    };
};
```

## richTextMarks

`createRichTextPlugin()`'s mark edges: where a concurrent insert at each end
of a mark lands. A link grows at its start only (typing after it
stays out of it unless from inside the anchor); the others are inclusive.

```ts
declare const richTextMarks: {
    link: {
        edge: "side-dependent";
    };
};
```

## richTextSemantics

`createRichTextPlugin()`'s block roles and mark edges, for `createDocument({ semantics })` and the room.

```ts
declare const richTextSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
} & {
    marks: Record<string, {
        edge: MarkEdge;
    }>;
};
```

## SchemaMismatchError

Error raised by `assertSchema` — carries the detected problem.

```ts
export declare class SchemaMismatchError extends Error {
    readonly docName: string;
    readonly problem: SchemaProblem;
    constructor(docName: string, problem: SchemaProblem);
}
```

## SemanticConflictError

Raised when two declarations of one kind's structural facts disagree —
a view's plugin and the document (`adoptSemantics`), or two configs
`mergeSemantics` is given: no replica may silently read another
rule than its peers.

```ts
export declare class SemanticConflictError extends Error {
    readonly subject: string;
    readonly existing: unknown;
    readonly incoming: unknown;
    constructor(subject: string, existing: unknown, incoming: unknown);
}
```

- `subject`: What conflicted (e.g.

## semanticsDigest

A config's facts as one short hash per kind, per mark (`mark:<name>`) and
for the default type (`@defaultType`): what a document advertises in its
presence in development builds, so the room can name the kinds it reads
otherwise than a client (`semanticsMismatch`). A plain kind (no
role, content, no default child) hashes as an undeclared one.

```ts
declare const semanticsDigest: (config: DocumentSemanticsConfig) => Record<string, string>;
```

## semanticsMismatch

The keys of a client's advertised digest that `ours` (the room's
`semanticsDigest`) hashes otherwise: kinds, marks or the default
type the two read differently. A kind the client does not name is not
its mismatch (a view without that plugin); a kind only the client
names counts when it has facts. Anything but a digest compares nothing.

```ts
declare const semanticsMismatch: (ours: Record<string, string>, theirs: unknown) => string[];
```

## semanticsOf

Kind tables (`type → row`) as one `DocumentSemanticsConfig`, deeply
frozen: `semanticsOf({ embed: { void: true, rendersContent: false } })`.
A row may be a plugin's kind record (`plugin.blocks`): only its
structural fields are read (`void`, `island`, `lines`, `layout`,
`table`, `atomic`, `rendersContent`, `defaultChild`), and a bare snippet is a
plain kind. To add kinds to a bundled config, use `mergeSemantics`.

```ts
declare const semanticsOf: (...tables: Record<string, KindRecord>[]) => {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## SerializableContent

```ts
export type SerializableContent = {
    [key: string]: SerializableContent;
} | SerializableContent[] | string | boolean | number | null;
```

## SyncRefusedError

A terminal refusal: the server closed the socket with a policy code
(`isRefusal`), so the next dial would be refused the same way (a
stale generation, another user's replica, a foreign stamp). The provider
stops dialing and the document does not seed.

```ts
export declare class SyncRefusedError extends Error {
    code: number;
    reason: string;
    constructor(code: number, reason?: string);
}
```

- `code`: The close code.
- `reason`: The close reason the server gave, e.g.

## tableBlock

A table block in the document JSON: `rows × columns` cells, the table's
`columns` and each cell's `column` set. It names no block id, so a value
holding it seeds the same table every time.

```ts
createDocument({
	value: { children: [tableBlock({ cells: [['Name', 'Role'], ['Ada', 'Eng']], headerRow: true })] }
});
```

```ts
declare const tableBlock: (options?: TableBlockOptions) => JSONBlock;
```

## TableBlockOptions

What `tableBlock` builds.

```ts
export type TableBlockOptions = {
    rows?: number;
    columns?: number;
    cells?: readonly (readonly (string | readonly JSONText[])[])[];
    headerRow?: boolean;
    headerColumn?: boolean;
    widths?: readonly (number | undefined)[];
};
```

- `rows`: Rows (default: the rows of `cells`, else 2).
- `columns`: Columns (default: the widest row of `cells`, else 2).
- `cells`: Each cell's text, by row then column (a string, or rich text).
- `headerRow`: The first row is a header row.
- `headerColumn`: The first column is a header column.
- `widths`: Column widths in pixels, by position (absent: the default width).

## tableKinds

The table plugin's structural rows: `table` displays only its
`tableRow`s, a row only its `tableCell`s, in the order of the table's
`data.columns` (`table.*` in the delete contract); a cell is a text
island (its text holds its lines, as soft breaks). In
`defaultSemantics`, so every replica of a document holding a table
reads the same rules.

```ts
declare const tableKinds: {
    table: {
        table: true;
        rendersContent: false;
        defaultChild: string;
    };
    tableRow: {
        rendersContent: false;
        defaultChild: string;
    };
    tableCell: {
        island: true;
    };
};
```

## tableSemantics

The table plugin's block roles (a table of rows of cells).

```ts
declare const tableSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## toBlockSpec

A canonical `JSONBlock` (from `facade.toJSON()`/`blockJSON`, a template, an
import) as the `BlockSpec` `facade.insertBlocks` takes: ids are kept, and
minted where missing; `freshIds` mints every id (a duplicate).

```ts
declare const toBlockSpec: (block: JSONBlock, { freshIds }?: {
    freshIds?: boolean | undefined;
}) => BlockSpec;
```

## tocKinds

The table of contents plugin's structural row: a void that lists the
document's headings, showing no text of its own.

```ts
declare const tocKinds: {
    toc: {
        void: true;
        rendersContent: false;
    };
};
```

## tocSemantics

The table of contents plugin's block role.

```ts
declare const tocSemantics: {
    roles: Record<string, BlockRole>;
    rendersContent: Record<string, boolean>;
    defaultChild: Record<string, string>;
};
```

## UndecodableUpdateError

Raised when a candidate update cannot even be decoded or integrated —
refused before the schema question is ever asked. Distinct from the
schema refusals: this payload was never document-shaped.

```ts
export declare class UndecodableUpdateError extends Error {
    readonly docName: string;
    readonly cause: unknown;
    constructor(docName: string, cause: unknown);
}
```

- `docName`: What the update was offered as (e.g.

## UnsupportedDocError

Error raised by `assertUsableDoc` — the doc cannot host a v14 facade:
a foreign engine object (a v13 `yjs` Doc — a different CRDT implementation
entirely).

```ts
export declare class UnsupportedDocError extends Error {
    readonly kind: 'foreign';
    constructor(kind: 'foreign');
}
```

## WebsocketSync

A websocket sync; `persistName` names its local store (for `clearDocument`), unset without one.

```ts
export type WebsocketSync = EdytorSync & {
    persistName?: string;
};
```

## WebsocketSyncOptions

```ts
export type WebsocketSyncOptions = WebsocketTarget & {
    params?: Record<string, string>;
    WebSocketPolyfill?: import('./websocket.js').WebsocketPolyfill;
    maxBackoffTime?: number;
    connectTimeout?: number;
    onExpired?: (state: Parameters<WebsocketProviderEvents['expired']>[0]) => void;
    disableBc?: boolean;
    maxFrameBytes?: number;
    persist?: boolean;
    persistName?: string;
};
```

## YDoc

```ts
export type YDoc = InstanceType<typeof Y.Doc>;
```

## YItem

```ts
export type YItem = Y.Item;
```

## YNode

```ts
export type YNode = InstanceType<typeof Y.Node>;
```

## YTransaction

```ts
export type YTransaction = Y.Transaction;
```

## YUndoManager

The document's undo manager (`doc/history.ts`).

```ts
export type YUndoManager = import('./doc/history.js').UndoManager;
```
