import * as A from "@automerge/automerge/slim/next";
import { EventEmitter } from "eventemitter3";
import type { AutomergeUrl, DocumentId, PeerId } from "./types.js";
import { StorageId } from "./storage/types.js";
/**
 * A DocHandle is a wrapper around a single Automerge document that lets us listen for changes and
 * notify the network and storage of new changes.
 *
 * @remarks
 * A `DocHandle` represents a document which is being managed by a {@link Repo}. You shouldn't ever
 * instantiate this yourself. To obtain `DocHandle` use {@link Repo.find} or {@link Repo.create}.
 *
 * To modify the underlying document use either {@link DocHandle.change} or
 * {@link DocHandle.changeAt}. These methods will notify the `Repo` that some change has occured and
 * the `Repo` will save any new changes to the attached {@link StorageAdapter} and send sync
 * messages to connected peers.
 */
export declare class DocHandle<T> extends EventEmitter<DocHandleEvents<T>> {
    #private;
    documentId: DocumentId;
    /** @hidden */
    constructor(documentId: DocumentId, options?: DocHandleOptions<T>);
    /** Our documentId in Automerge URL form.
     */
    get url(): AutomergeUrl;
    /**
     * @returns true if the document is ready for accessing or changes.
     *
     * Note that for documents already stored locally this occurs before synchronization with any
     * peers. We do not currently have an equivalent `whenSynced()`.
     */
    isReady: () => boolean;
    /**
     * @returns true if the document has been marked as deleted.
     *
     * Deleted documents are removed from local storage and the sync process. It's not currently
     * possible at runtime to undelete a document.
     */
    isDeleted: () => boolean;
    /**
     * @returns true if the document is currently unavailable.
     *
     * This will be the case if the document is not found in storage and no peers have shared it with us.
     */
    isUnavailable: () => boolean;
    /**
     * @returns true if the handle is in one of the given states.
     */
    inState: (states: HandleState[]) => boolean;
    /** @hidden */
    get state(): "idle" | "ready" | "loading" | "requesting" | "awaitingNetwork" | "unavailable" | "deleted";
    /**
     * @returns a promise that resolves when the document is in one of the given states (if no states
     * are passed, when the document is ready)
     *
     * Use this to block until the document handle has finished loading. The async equivalent to
     * checking `inState()`.
     */
    whenReady(awaitStates?: HandleState[]): Promise<void>;
    /**
     * @returns the current state of this handle's Automerge document.
     *
     * This is the recommended way to access a handle's document. Note that this waits for the handle
     * to be ready if necessary. If loading (or synchronization) fails, this will never resolve.
     */
    doc(
    /** states to wait for, such as "LOADING". mostly for internal use. */
    awaitStates?: HandleState[]): Promise<A.Doc<T> | undefined>;
    /**
     * Synchronously returns the current state of the Automerge document this handle manages, or
     * undefined. Consider using `await handle.doc()` instead. Check `isReady()`, or use `whenReady()`
     * if you want to make sure loading is complete first.
     *
     * Not to be confused with the SyncState of the document, which describes the state of the
     * synchronization process.
     *
     * Note that `undefined` is not a valid Automerge document, so the return from this function is
     * unambigous.
     *
     * @returns the current document, or undefined if the document is not ready.
     */
    docSync(): A.Doc<T> | undefined;
    /**
     * Returns the current "heads" of the document, akin to a git commit.
     * This precisely defines the state of a document.
     * @returns the current document's heads, or undefined if the document is not ready
     */
    heads(): A.Heads | undefined;
    /**
     * `update` is called by the repo when we receive changes from the network
     * Called by the repo when we receive changes from the network.
     * @hidden
     */
    update(callback: (doc: A.Doc<T>) => A.Doc<T>): void;
    /**
     * Called by the repo either when a doc handle changes or we receive new remote heads.
     * @hidden
     */
    setRemoteHeads(storageId: StorageId, heads: A.Heads): void;
    /** Returns the heads of the storageId. */
    getRemoteHeads(storageId: StorageId): A.Heads | undefined;
    /**
     * All changes to an Automerge document should be made through this method.
     * Inside the callback, the document should be treated as mutable: all edits will be recorded
     * using a Proxy and translated into operations as part of a single recorded "change".
     *
     * Note that assignment via ES6 spread operators will result in *replacing* the object
     * instead of mutating it which will prevent clean merges. This may be what you want, but
     * `doc.foo = { ...doc.foo, bar: "baz" }` is not equivalent to `doc.foo.bar = "baz"`.
     *
     * Local changes will be stored (by the StorageSubsystem) and synchronized (by the
     * DocSynchronizer) to any peers you are sharing it with.
     *
     * @param callback - A function that takes the current document and mutates it.
     *
     */
    change(callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): void;
    /**
     * Makes a change as if the document were at `heads`.
     *
     * @returns A set of heads representing the concurrent change that was made.
     */
    changeAt(heads: A.Heads, callback: A.ChangeFn<T>, options?: A.ChangeOptions<T>): string[] | undefined;
    /**
     * Merges another document into this document. Any peers we are sharing changes with will be
     * notified of the changes resulting from the merge.
     *
     * @returns the merged document.
     *
     * @throws if either document is not ready or if `otherHandle` is unavailable.
     */
    merge(
    /** the handle of the document to merge into this one */
    otherHandle: DocHandle<T>): void;
    /**
     * Used in testing to mark this document as unavailable.
     * @hidden
     */
    unavailable(): void;
    /** Called by the repo when the document is not found in storage.
     * @hidden
     * */
    request(): void;
    /** @hidden */
    awaitNetwork(): void;
    /** @hidden */
    networkReady(): void;
    /** Called by the repo when the document is deleted. */
    delete(): void;
    /**
     * Sends an arbitrary ephemeral message out to all reachable peers who would receive sync messages
     * from you. It has no guarantee of delivery, and is not persisted to the underlying automerge doc
     * in any way. Messages will have a sending PeerId but this is *not* a useful user identifier (a
     * user could have multiple tabs open and would appear as multiple PeerIds). Every message source
     * must have a unique PeerId.
     */
    broadcast(message: unknown): void;
}
/** @hidden */
export type DocHandleOptions<T> = {
    /** If we know this is a new document (because we're creating it) this should be set to true. */
    isNew: true;
    /** The initial value of the document. */
    initialValue?: T;
} | {
    isNew?: false;
    /** The number of milliseconds before we mark this document as unavailable if we don't have it and nobody shares it with us. */
    timeoutDelay?: number;
};
/** These are the events that this DocHandle emits to external listeners */
export interface DocHandleEvents<T> {
    "heads-changed": (payload: DocHandleEncodedChangePayload<T>) => void;
    change: (payload: DocHandleChangePayload<T>) => void;
    delete: (payload: DocHandleDeletePayload<T>) => void;
    unavailable: (payload: DocHandleUnavailablePayload<T>) => void;
    "ephemeral-message": (payload: DocHandleEphemeralMessagePayload<T>) => void;
    "ephemeral-message-outbound": (payload: DocHandleOutboundEphemeralMessagePayload<T>) => void;
    "remote-heads": (payload: DocHandleRemoteHeadsPayload) => void;
}
/** Emitted when this document's heads have changed */
export interface DocHandleEncodedChangePayload<T> {
    handle: DocHandle<T>;
    doc: A.Doc<T>;
}
/** Emitted when this document has changed */
export interface DocHandleChangePayload<T> {
    /** The handle that changed */
    handle: DocHandle<T>;
    /** The value of the document after the change */
    doc: A.Doc<T>;
    /** The patches representing the change that occurred */
    patches: A.Patch[];
    /** Information about the change */
    patchInfo: A.PatchInfo<T>;
}
/** Emitted when this document is deleted */
export interface DocHandleDeletePayload<T> {
    handle: DocHandle<T>;
}
/** Emitted when this document has been marked unavailable */
export interface DocHandleUnavailablePayload<T> {
    handle: DocHandle<T>;
}
/** Emitted when an ephemeral message is received for the document */
export interface DocHandleEphemeralMessagePayload<T> {
    handle: DocHandle<T>;
    senderId: PeerId;
    message: unknown;
}
/** Emitted when an ephemeral message is sent for this document */
export interface DocHandleOutboundEphemeralMessagePayload<T> {
    handle: DocHandle<T>;
    data: Uint8Array;
}
/** Emitted when we have new remote heads for this document */
export interface DocHandleRemoteHeadsPayload {
    storageId: StorageId;
    heads: A.Heads;
}
/**
 * Possible internal states for a DocHandle
 */
export declare const HandleState: {
    /** The handle has been created but not yet loaded or requested */
    readonly IDLE: "idle";
    /** We are waiting for storage to finish loading */
    readonly LOADING: "loading";
    /** We are waiting for the network to be come ready */
    readonly AWAITING_NETWORK: "awaitingNetwork";
    /** We are waiting for someone in the network to respond to a sync request */
    readonly REQUESTING: "requesting";
    /** The document is available */
    readonly READY: "ready";
    /** The document has been deleted from the repo */
    readonly DELETED: "deleted";
    /** The document was not available in storage or from any connected peers */
    readonly UNAVAILABLE: "unavailable";
};
export type HandleState = (typeof HandleState)[keyof typeof HandleState];
export declare const IDLE: "idle", LOADING: "loading", AWAITING_NETWORK: "awaitingNetwork", REQUESTING: "requesting", READY: "ready", DELETED: "deleted", UNAVAILABLE: "unavailable";
//# sourceMappingURL=DocHandle.d.ts.map