import { JSONSchema7 } from 'json-schema';

/**
 * Throws an error if multiple copies of a Liveblocks package are being loaded
 * at runtime. This likely indicates a packaging issue with the project.
 */
declare function detectDupes(pkgName: string, pkgVersion: string | false, // false if not built yet
pkgFormat: string | false): void;

/**
 * This helper type is effectively a no-op, but will force TypeScript to
 * "evaluate" any named helper types in its definition. This can sometimes make
 * API signatures clearer in IDEs.
 *
 * For example, in:
 *
 *   type Payload<T> = { data: T };
 *
 *   let r1: Payload<string>;
 *   let r2: Resolve<Payload<string>>;
 *
 * The inferred type of `r1` is going to be `Payload<string>` which shows up in
 * editor hints, and it may be unclear what's inside if you don't know the
 * definition of `Payload`.
 *
 * The inferred type of `r2` is going to be `{ data: string }`, which may be
 * more helpful.
 *
 * This trick comes from:
 * https://effectivetypescript.com/2022/02/25/gentips-4-display/
 */
type Resolve<T> = T extends (...args: unknown[]) => unknown ? T : {
    [K in keyof T]: T[K];
};

/**
 * Relaxes a discriminated union type definition, by explicitly adding
 * properties defined in any other member as 'never'.
 *
 * This makes accessing the members much more relaxed in TypeScript.
 *
 * For example:
 *   type MyUnion = Relax<
 *     | { foo: string }
 *     | { foo: number; bar: string; }
 *     | { qux: boolean }
 *   >;
 *
 *   // With Relax, accessing is much easier:
 *   union.foo;       // string | number | undefined
 *   union.bar;       // string | undefined
 *   union.qux;       // boolean
 *   union.whatever;  // Error: Property 'whatever' does not exist on type 'MyUnion'
 *
 *   // Without Relax, these would all be type errors:
 *   union.foo; // Error: Property 'foo' does not exist on type 'MyUnion'
 *   union.bar; // Error: Property 'bar' does not exist on type 'MyUnion'
 *   union.qux; // Error: Property 'qux' does not exist on type 'MyUnion'
 */
type Relax<T> = DistributiveRelax<T, T extends any ? keyof T : never>;
type DistributiveRelax<T, Ks extends string | number | symbol> = T extends any ? Resolve<{
    [K in keyof T]: T[K];
} & {
    [K in Exclude<Ks, keyof T>]?: never;
}> : never;

declare const Permission: {
    /**
     * Default permission for a room.
     */
    readonly Read: "*:read";
    readonly Write: "*:write";
    /**
     * Legacy aliases for default room permissions.
     */
    readonly RoomWrite: "room:write";
    readonly RoomRead: "room:read";
    /**
     * Storage
     */
    readonly StorageRead: "storage:read";
    readonly StorageWrite: "storage:write";
    readonly StorageNone: "storage:none";
    /**
     * Comments
     */
    readonly CommentsWrite: "comments:write";
    readonly CommentsRead: "comments:read";
    readonly CommentsNone: "comments:none";
    readonly CommentsPublicWrite: "comments:public:write";
    readonly CommentsPublicRead: "comments:public:read";
    readonly CommentsPublicNone: "comments:public:none";
    readonly CommentsPrivateWrite: "comments:private:write";
    readonly CommentsPrivateRead: "comments:private:read";
    readonly CommentsPrivateNone: "comments:private:none";
    /**
     * Feeds
     */
    readonly FeedsRead: "feeds:read";
    readonly FeedsWrite: "feeds:write";
    readonly FeedsNone: "feeds:none";
    /**
     * Legacy
     */
    readonly LegacyRoomPresenceWrite: "room:presence:write";
};
type Permission = (typeof Permission)[keyof typeof Permission];
declare const ACCESS_LEVELS: readonly ["none", "read", "write"];
type AccessLevel = (typeof ACCESS_LEVELS)[number];
type RequiredAccessLevel = "read" | "write";
type PermissionMatrix = {
    room: AccessLevel;
    storage: AccessLevel;
    comments: AccessLevel;
    "comments:public": AccessLevel;
    "comments:private": AccessLevel;
    feeds: AccessLevel;
    personal: AccessLevel;
};
type PermissionResources = keyof PermissionMatrix;
type RoomPermissions = Permission[];
type RoomAccesses = Record<string, RoomPermissions>;
type UpdateRoomAccesses = Record<string, RoomPermissions | null>;
declare function permissionMatrixFromScopes(scopes: RoomPermissions): PermissionMatrix;
declare function hasPermissionAccess(matrix: Partial<PermissionMatrix>, resource: PermissionResources, requiredAccess: RequiredAccessLevel): boolean;
declare function normalizeRoomPermissions(permissions: string[] | readonly string[]): RoomPermissions;
declare function normalizeRoomAccesses(accesses: RoomAccesses | undefined): RoomAccesses | undefined;
declare function normalizeUpdateRoomAccesses(accesses: UpdateRoomAccesses | undefined): UpdateRoomAccesses | undefined;
/**
 * Merges permission scopes from multiple sources, by priority: explicit user
 * accesses override group accesses, which override the room defaults. Groups
 * all share the same priority, so they are first merged together by taking
 * the highest access level per feature (and base).
 */
declare function mergeRoomPermissionScopes({ defaultAccesses, groupsAccesses, userAccesses, }: {
    defaultAccesses: RoomPermissions;
    groupsAccesses: RoomPermissions[];
    userAccesses: RoomPermissions;
}): RoomPermissions;
/**
 * Validates a set of permissions:
 * - every scope must be a known permission scope,
 * - exactly one base permission is required (*:read, *:write, or the legacy
 *   aliases room:read, room:write),
 * - at most one scope per feature (storage, comments, feeds, ...),
 * - room:presence:write is accepted as an extra legacy scope.
 *
 * Returns `true` when the set is valid, or an error message otherwise.
 */
declare function validatePermissionsSet(scopes: readonly string[]): true | string;

type CustomAuthenticationResult = Relax<{
    token: string;
} | {
    error: "forbidden";
    reason: string;
} | {
    error: string;
    reason: string;
}>;

/**
 * Represents an indefinitely deep arbitrary JSON data structure. There are
 * four types that make up the Json family:
 *
 * - Json         any legal JSON value
 * - JsonScalar   any legal JSON leaf value (no lists or objects)
 * - JsonArray    a JSON value whose outer type is an array
 * - JsonObject   a JSON value whose outer type is an object
 *
 */
type Json = JsonScalar | JsonArray | JsonObject;
type JsonScalar = string | number | boolean | null;
type JsonArray = Json[];
/**
 * Any valid JSON object.
 */
type JsonObject = {
    [key: string]: Json | undefined;
};
/**
 * Like Json, but with readonly arrays and objects.
 */
type ReadonlyJson = JsonScalar | readonly ReadonlyJson[] | ReadonlyJsonObject;
/**
 * Like JsonObject, but readonly.
 */
type ReadonlyJsonObject = {
    readonly [key: string]: ReadonlyJson | undefined;
};
declare function isJsonScalar(data: Json): data is JsonScalar;
declare function isJsonArray(data: Json): data is JsonArray;
declare function isJsonObject(data: Json): data is JsonObject;

/**
 * Represents some constraints for user info. Basically read this as: "any JSON
 * object is fine, but _if_ it has a name field, it _must_ be a string."
 * (Ditto for avatar.)
 */
type IUserInfo = {
    [key: string]: Json | undefined;
    name?: string;
    avatar?: string;
};
/**
 * This type is used by clients to define the metadata for a user.
 */
type BaseUserMeta = {
    /**
     * The id of the user that has been set in the authentication endpoint.
     * Useful to get additional information about the connected user.
     */
    id?: string;
    /**
     * Additional user information that has been set in the authentication endpoint.
     */
    info?: IUserInfo;
};

type RenameDataField<T, TFieldName extends string> = T extends any ? {
    [K in keyof T as K extends "data" ? TFieldName : K]: T[K];
} : never;
type AsyncLoading<F extends string = "data"> = RenameDataField<{
    readonly isLoading: true;
    readonly data?: never;
    readonly error?: never;
}, F>;
type AsyncSuccess<T, F extends string = "data"> = RenameDataField<{
    readonly isLoading: false;
    readonly data: T;
    readonly error?: never;
}, F>;
type AsyncError<F extends string = "data"> = RenameDataField<{
    readonly isLoading: false;
    readonly data?: never;
    readonly error: Error;
}, F>;
type AsyncResult<T, F extends string = "data"> = AsyncLoading<F> | AsyncSuccess<T, F> | AsyncError<F>;

type Callback<T> = (event: T) => void;
type UnsubscribeCallback = () => void;
type Observable<T> = {
    /**
     * Register a callback function to be called whenever the event source emits
     * an event.
     */
    subscribe(callback: Callback<T>): UnsubscribeCallback;
    /**
     * Register a one-time callback function to be called whenever the event
     * source emits an event. After the event fires, the callback is
     * auto-unsubscribed.
     */
    subscribeOnce(callback: Callback<T>): UnsubscribeCallback;
    /**
     * Returns a promise that will resolve when an event is emitted by this
     * event source. Optionally, specify a predicate that has to match. The first
     * event matching that predicate will then resolve the promise.
     */
    waitUntil(predicate?: (event: T) => boolean): Promise<T>;
};
type EventSource<T> = Observable<T> & {
    /**
     * Notify all subscribers about the event. Will return `false` if there
     * weren't any subscribers at the time the .notify() was called, or `true` if
     * there was at least one subscriber.
     */
    notify(event: T): boolean;
    /**
     * Returns the number of active subscribers.
     */
    count(): number;
    /**
     * Observable instance, which can be used to subscribe to this event source
     * in a readonly fashion. Safe to publicly expose.
     */
    observable: Observable<T>;
    /**
     * Disposes of this event source.
     *
     * Will clears all registered event listeners. None of the registered
     * functions will ever get called again.
     *
     * WARNING!
     * Be careful when using this API, because the subscribers may not have any
     * idea they won't be notified anymore.
     */
    dispose(): void;
};
/**
 * makeEventSource allows you to generate a subscribe/notify pair of functions
 * to make subscribing easy and to get notified about events.
 *
 * The events are anonymous, so you can use it to define events, like so:
 *
 *   const event1 = makeEventSource();
 *   const event2 = makeEventSource();
 *
 *   event1.subscribe(foo);
 *   event1.subscribe(bar);
 *   event2.subscribe(qux);
 *
 *   // Unsubscription is pretty standard
 *   const unsub = event2.subscribe(foo);
 *   unsub();
 *
 *   event1.notify();  // Now foo and bar will get called
 *   event2.notify();  // Now qux will get called (but foo will not, since it's unsubscribed)
 *
 */
declare function makeEventSource<T>(): EventSource<T>;

type BatchStore<O, I> = {
    subscribe: (callback: Callback<void>) => UnsubscribeCallback;
    enqueue: (input: I) => Promise<void>;
    setData: (entries: [I, O][]) => void;
    getItemState: (input: I) => AsyncResult<O> | undefined;
    getData: (input: I) => O | undefined;
    invalidate: (inputs?: I[]) => void;
};

type ContextualPromptResponse = Relax<{
    type: "insert";
    text: string;
} | {
    type: "replace";
    text: string;
} | {
    type: "other";
    text: string;
}>;
type ContextualPromptContext = {
    beforeSelection: string;
    selection: string;
    afterSelection: string;
};

declare const brand: unique symbol;
type Brand<T, TBrand extends string> = T & {
    [brand]: TBrand;
};
type ISODateString = Brand<string, "ISODateString">;
type DistributiveOmit<T, K extends PropertyKey> = T extends any ? Omit<T, K> : never;
type WithRequired<T, K extends keyof T> = T & {
    [P in K]-?: T[P];
};
type WithOptional<T, K extends keyof T> = Omit<T, K> & {
    [P in K]?: T[P];
};
/**
 * Throw an error, but as an expression instead of a statement.
 */
declare function raise(msg: string): never;
/**
 * Drop-in replacement for Object.entries() that retains better types.
 */
declare function entries<O extends {
    [key: string]: unknown;
}, K extends keyof O>(obj: O): [K, O[K]][];
/**
 * Drop-in replacement for Object.keys() that retains better types.
 */
declare function keys<O extends {
    [key: string]: unknown;
}, K extends keyof O>(obj: O): K[];
/**
 * Creates a new object by mapping a function over all values. Keys remain the
 * same. Think Array.prototype.map(), but for values in an object.
 */
declare function mapValues<V, O extends Record<string, unknown>>(obj: O, mapFn: (value: O[keyof O], key: keyof O) => V): {
    [K in keyof O]: V;
};
/**
 * Alternative to JSON.parse() that will not throw in production. If the passed
 * string cannot be parsed, this will return `undefined`.
 */
declare function tryParseJson(rawMessage: string): Json | undefined;
/**
 * Decode base64 string.
 */
declare function b64decode(b64value: string): string;
type RemoveUndefinedValues<T> = {
    [K in keyof T]-?: Exclude<T[K], undefined>;
};
/**
 * Returns a new object instance where all explictly-undefined values are
 * removed.
 */
declare function compactObject<O extends Record<string, unknown>>(obj: O): RemoveUndefinedValues<O>;
/**
 * Returns a promise that resolves after the given number of milliseconds.
 */
declare function wait(millis: number): Promise<void>;
/**
 * Returns whatever the given promise returns, but will be rejected with
 * a "Timed out" error if the given promise does not return or reject within
 * the given timeout period (in milliseconds).
 */
declare function withTimeout<T>(promise: Promise<T>, millis: number, errmsg: string): Promise<T>;
/**
 * Memoize a promise factory, so that each subsequent call will return the same
 * pending or success promise. If the promise rejects, will retain that failed
 * promise for a small time period, after which the next attempt will reset the
 * memoized value.
 */
declare function memoizeOnSuccess<T>(factoryFn: () => Promise<T>): () => Promise<T>;
/**
 * Polyfill for Array.prototype.findLastIndex()
 */
declare function findLastIndex<T>(arr: T[], predicate: (value: T, index: number, obj: T[]) => boolean): number;

type OpCode = (typeof OpCode)[keyof typeof OpCode];
declare const OpCode: Readonly<{
    INIT: 0;
    SET_PARENT_KEY: 1;
    CREATE_LIST: 2;
    UPDATE_OBJECT: 3;
    CREATE_OBJECT: 4;
    DELETE_CRDT: 5;
    DELETE_OBJECT_KEY: 6;
    CREATE_MAP: 7;
    CREATE_REGISTER: 8;
}>;
declare namespace OpCode {
    type INIT = typeof OpCode.INIT;
    type SET_PARENT_KEY = typeof OpCode.SET_PARENT_KEY;
    type CREATE_LIST = typeof OpCode.CREATE_LIST;
    type UPDATE_OBJECT = typeof OpCode.UPDATE_OBJECT;
    type CREATE_OBJECT = typeof OpCode.CREATE_OBJECT;
    type DELETE_CRDT = typeof OpCode.DELETE_CRDT;
    type DELETE_OBJECT_KEY = typeof OpCode.DELETE_OBJECT_KEY;
    type CREATE_MAP = typeof OpCode.CREATE_MAP;
    type CREATE_REGISTER = typeof OpCode.CREATE_REGISTER;
}
/**
 * These operations are the payload for {@link UpdateStorageServerMsg} messages
 * only.
 */
type Op = CreateOp | UpdateObjectOp | DeleteCrdtOp | SetParentKeyOp | DeleteObjectKeyOp;
type CreateOp = CreateObjectOp | CreateRegisterOp | CreateMapOp | CreateListOp;
type UpdateObjectOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.UPDATE_OBJECT;
    readonly data: Partial<JsonObject>;
};
type CreateObjectOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.CREATE_OBJECT;
    readonly parentId: string;
    readonly parentKey: string;
    readonly data: JsonObject;
    readonly intent?: "set" | "push";
    readonly deletedId?: string;
};
type CreateListOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.CREATE_LIST;
    readonly parentId: string;
    readonly parentKey: string;
    readonly intent?: "set" | "push";
    readonly deletedId?: string;
};
type CreateMapOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.CREATE_MAP;
    readonly parentId: string;
    readonly parentKey: string;
    readonly intent?: "set" | "push";
    readonly deletedId?: string;
};
type CreateRegisterOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.CREATE_REGISTER;
    readonly parentId: string;
    readonly parentKey: string;
    readonly data: Json;
    readonly intent?: "set" | "push";
    readonly deletedId?: string;
};
type DeleteCrdtOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.DELETE_CRDT;
};
type IgnoredOp = {
    readonly type: OpCode.DELETE_CRDT;
    readonly id: "ACK";
    readonly opId: string;
};
type SetParentKeyOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.SET_PARENT_KEY;
    readonly parentKey: string;
};
type DeleteObjectKeyOp = {
    readonly opId?: string;
    readonly id: string;
    readonly type: OpCode.DELETE_OBJECT_KEY;
    readonly key: string;
};
type HasOpId = {
    opId: string;
};
/**
 * Ops sent from client → server. Always includes an opId so the server can
 * acknowledge the receipt.
 */
type ClientWireOp = Op & HasOpId;
type ClientWireCreateOp = CreateOp & HasOpId;
/**
 * ServerWireOp: Ops sent from server → client. Three variants:
 * 1. ClientWireOp — Full echo back of our own op, confirming it was applied
 * 2. IgnoredOp — Our op was seen but intentionally ignored (still counts as ack)
 * 3. Op without opId — Another client's op being forwarded to us
 */
type ServerWireOp = ClientWireOp | IgnoredOp | TheirOp;
type TheirOp = DistributiveOmit<Op, "opId"> & {
    opId?: undefined;
};

type UpdateDelta = {
    type: "update";
} | {
    type: "delete";
    deletedItem: Lson;
};

/**
 * A LiveMap notification that is sent in-client to any subscribers whenever
 * one or more of the values inside the LiveMap instance have changed.
 */
type LiveMapUpdates<TKey extends string, TValue extends Lson> = {
    type: "LiveMap";
    node: LiveMap<TKey, TValue>;
    updates: {
        [key: string]: UpdateDelta;
    };
};
/**
 * The LiveMap class is similar to a JavaScript Map that is synchronized on all clients.
 * Keys should be a string, and values should be serializable to JSON.
 * If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.
 */
declare class LiveMap<TKey extends string, TValue extends Lson> extends AbstractCrdt {
    #private;
    constructor(entries?: readonly (readonly [TKey, TValue])[] | undefined);
    /**
     * Returns a specified element from the LiveMap.
     * @param key The key of the element to return.
     * @returns The element associated with the specified key, or undefined if the key can't be found in the LiveMap.
     */
    get(key: TKey): TValue | undefined;
    /**
     * Adds or updates an element with a specified key and a value.
     * @param key The key of the element to add. Should be a string.
     * @param value The value of the element to add. Should be serializable to JSON.
     */
    set(key: TKey, value: TValue): void;
    /**
     * Returns the number of elements in the LiveMap.
     */
    get size(): number;
    /**
     * Returns a boolean indicating whether an element with the specified key exists or not.
     * @param key The key of the element to test for presence.
     */
    has(key: TKey): boolean;
    /**
     * Removes the specified element by key.
     * @param key The key of the element to remove.
     * @returns true if an element existed and has been removed, or false if the element does not exist.
     */
    delete(key: TKey): boolean;
    /**
     * Returns a new Iterator object that contains the [key, value] pairs for each element.
     */
    entries(): IterableIterator<[TKey, TValue]>;
    /**
     * Same function object as the initial value of the entries method.
     */
    [Symbol.iterator](): IterableIterator<[TKey, TValue]>;
    /**
     * Returns a new Iterator object that contains the keys for each element.
     */
    keys(): IterableIterator<TKey>;
    /**
     * Returns a new Iterator object that contains the values for each element.
     */
    values(): IterableIterator<TValue>;
    /**
     * Executes a provided function once per each key/value pair in the Map object, in insertion order.
     * @param callback Function to execute for each entry in the map.
     */
    forEach(callback: (value: TValue, key: TKey, map: LiveMap<TKey, TValue>) => void): void;
    toJSON(): {
        readonly [P in TKey]: ToJson<TValue>;
    };
    clone(): LiveMap<TKey, TValue>;
}

type CrdtType = (typeof CrdtType)[keyof typeof CrdtType];
declare const CrdtType: Readonly<{
    OBJECT: 0;
    LIST: 1;
    MAP: 2;
    REGISTER: 3;
}>;
declare namespace CrdtType {
    type OBJECT = typeof CrdtType.OBJECT;
    type LIST = typeof CrdtType.LIST;
    type MAP = typeof CrdtType.MAP;
    type REGISTER = typeof CrdtType.REGISTER;
}
type SerializedCrdt = SerializedRootObject | SerializedChild;
type SerializedChild = SerializedObject | SerializedList | SerializedMap | SerializedRegister;
type SerializedRootObject = {
    readonly type: CrdtType.OBJECT;
    readonly data: JsonObject;
    readonly parentId?: never;
    readonly parentKey?: never;
};
type SerializedObject = {
    readonly type: CrdtType.OBJECT;
    readonly parentId: string;
    readonly parentKey: string;
    readonly data: JsonObject;
};
type SerializedList = {
    readonly type: CrdtType.LIST;
    readonly parentId: string;
    readonly parentKey: string;
};
type SerializedMap = {
    readonly type: CrdtType.MAP;
    readonly parentId: string;
    readonly parentKey: string;
};
type SerializedRegister = {
    readonly type: CrdtType.REGISTER;
    readonly parentId: string;
    readonly parentKey: string;
    readonly data: Json;
};
type StorageNode = RootStorageNode | ChildStorageNode;
type ChildStorageNode = ObjectStorageNode | ListStorageNode | MapStorageNode | RegisterStorageNode;
type RootStorageNode = [id: "root", value: SerializedRootObject];
type ObjectStorageNode = [id: string, value: SerializedObject];
type ListStorageNode = [id: string, value: SerializedList];
type MapStorageNode = [id: string, value: SerializedMap];
type RegisterStorageNode = [id: string, value: SerializedRegister];
type NodeMap = Map<string, SerializedCrdt>;
type NodeStream = Iterable<StorageNode>;
declare function isRootStorageNode(node: StorageNode): node is RootStorageNode;
declare function isObjectStorageNode(node: StorageNode): node is RootStorageNode | ObjectStorageNode;
declare function isListStorageNode(node: StorageNode): node is ListStorageNode;
declare function isMapStorageNode(node: StorageNode): node is MapStorageNode;
declare function isRegisterStorageNode(node: StorageNode): node is RegisterStorageNode;
type CompactNode = CompactRootNode | CompactChildNode;
type CompactChildNode = CompactObjectNode | CompactListNode | CompactMapNode | CompactRegisterNode;
type CompactRootNode = readonly [id: "root", data: JsonObject];
type CompactObjectNode = readonly [
    id: string,
    type: CrdtType.OBJECT,
    parentId: string,
    parentKey: string,
    data: JsonObject
];
type CompactListNode = readonly [
    id: string,
    type: CrdtType.LIST,
    parentId: string,
    parentKey: string
];
type CompactMapNode = readonly [
    id: string,
    type: CrdtType.MAP,
    parentId: string,
    parentKey: string
];
type CompactRegisterNode = readonly [
    id: string,
    type: CrdtType.REGISTER,
    parentId: string,
    parentKey: string,
    data: Json
];
declare function compactNodesToNodeStream(compactNodes: CompactNode[]): NodeStream;
declare function nodeStreamToCompactNodes(nodes: NodeStream): Iterable<CompactNode>;

/**
 * Extracts only the explicitly-named string keys of a type, filtering out
 * any index signature (e.g. `[key: string]: ...`).
 */
type KnownKeys<T> = keyof {
    [K in keyof T as {} extends Record<K, 1> ? never : K]: true;
} & string;

/**
 * Per-key sync configuration for node/edge `data` properties.
 *
 * true
 *   Sync this property to Liveblocks Storage. Arrays and objects in the value
 *   will be stored as LiveLists and LiveObjects, enabling fine-grained
 *   conflict-free merging. This is the default for all keys.
 *
 * false
 *   Don't sync this property. It stays local to the current client. This
 *   property will be `undefined` on other clients.
 *
 * "atomic"
 *   Sync this property, but treat it as an indivisible value. The entire value
 *   is replaced as a whole (last-writer-wins) instead of being recursively
 *   converted to LiveObjects/LiveLists. Use this when clients always replace
 *   the value entirely and never need concurrent sub-key merging.
 *
 * { ... }
 *   A nested config object for recursively configuring sub-keys of an object.
 *
 * @example
 * ```ts
 * const sync: SyncConfig = {
 *   label: true,             // sync (default)
 *   createdAt: false,        // local-only
 *   shape: "atomic",         // replaced as a whole, no deep merge
 *   nested: {                // recursive config
 *     deep: false,
 *   },
 * };
 * ```
 */
type SyncMode = boolean | "atomic" | SyncConfig;
type SyncConfig = {
    [key: string]: SyncMode | undefined;
};
/**
 * Deeply converts all nested lists to LiveLists, and all nested objects to
 * LiveObjects.
 *
 * As such, the returned result will not contain any Json arrays or Json
 * objects anymore.
 */
declare function deepLiveify(value: Json, config?: SyncMode): Lson;

/**
 * Optional keys of O whose non-undefined type is plain Json (not a
 * LiveStructure). These are the only keys eligible for setLocal().
 * Uses KnownKeys to only consider explicitly-named keys, not index signatures.
 * Checks optionality inline to avoid index signature pollution of OptionalKeys.
 */
type OptionalJsonKeys<O> = {
    [K in KnownKeys<O>]: undefined extends O[K] ? Exclude<O[K], undefined> extends Json ? K : never : never;
}[KnownKeys<O>];
type LiveObjectUpdateDelta<O extends {
    [key: string]: unknown;
}> = {
    [K in keyof O]?: UpdateDelta | undefined;
};
/**
 * A LiveObject notification that is sent in-client to any subscribers whenever
 * one or more of the entries inside the LiveObject instance have changed.
 */
type LiveObjectUpdates<TData extends LsonObject> = {
    type: "LiveObject";
    node: LiveObject<TData>;
    updates: LiveObjectUpdateDelta<TData>;
};
/**
 * The LiveObject class is similar to a JavaScript object that is synchronized on all clients.
 * Keys should be a string, and values should be serializable to JSON.
 * If multiple clients update the same property simultaneously, the last modification received by the Liveblocks servers is the winner.
 */
declare class LiveObject<O extends LsonObject> extends AbstractCrdt {
    #private;
    /**
     * Enable or disable detection of too large LiveObjects.
     * When enabled, throws an error if LiveObject static data exceeds 128KB, which
     * is the maximum value the server will be able to accept.
     * By default, this behavior is disabled to avoid the runtime performance
     * overhead on every LiveObject.set() or LiveObject.update() call.
     *
     * @experimental
     */
    static detectLargeObjects: boolean;
    /** @private Do not use this API directly */
    static _fromItems<O extends LsonObject>(nodes: NodeStream, pool: ManagedPool): LiveObject<O>;
    constructor(obj?: O);
    /** @private */
    keys(): Set<string>;
    /**
     * Adds or updates a property with a specified key and a value.
     * @param key The key of the property to add
     * @param value The value of the property to add
     */
    set<TKey extends keyof O>(key: TKey, value: O[TKey]): void;
    /**
     * @experimental
     *
     * Sets a local-only property that is not synchronized over the wire. The
     * value will be visible via get(), and toJSON() on this client only. Other
     * clients and the server will see `undefined` for this key.
     *
     * Caveat: this method will not add changes to the undo/redo stack.
     */
    setLocal<TKey extends OptionalJsonKeys<O>>(key: TKey, value: Extract<Exclude<O[TKey], undefined>, Json>): void;
    /**
     * Returns a specified property from the LiveObject.
     * @param key The key of the property to get
     */
    get<TKey extends keyof O>(key: TKey): O[TKey];
    /**
     * Deletes a key from the LiveObject
     * @param key The key of the property to delete
     */
    delete(key: keyof O): void;
    /**
     * Adds or updates multiple properties at once with an object.
     * @param patch The object used to overrides properties
     */
    update(patch: Partial<O>): void;
    /**
     * Creates a new LiveObject from a plain JSON object, recursively converting
     * nested objects to LiveObjects and arrays to LiveLists.
     */
    static from(obj: JsonObject): LiveObject<LsonObject>;
    /** @private */
    static from(obj: JsonObject, config?: SyncConfig): LiveObject<LsonObject>;
    /**
     * Reconciles this LiveObject tree to match the given JSON object. Only
     * mutates keys that actually changed. Keys present on this LiveObject but
     * absent from `jsonObj` will be deleted. Nested structures are recursively
     * reconciled.
     */
    reconcile(jsonObj: JsonObject): void;
    /** @private */
    reconcile(jsonObj: JsonObject, config?: SyncConfig): void;
    /**
     * Like reconcile(), but only touches the top-level keys present in
     * `partialObj`. Keys on this LiveObject that are absent from `partialObj`
     * are left untouched. Typically called on the storage root when
     * reconciling a subset of keys without affecting other keys on the root.
     *
     * Note: the partial behavior only applies to the top-level keys of this
     * object. Nested structures are always fully reconciled.
     *
     * @private
     */
    reconcilePartially(partialObj: JsonObject, config?: SyncConfig): void;
    toJSON(): ToJson<O>;
    clone(): LiveObject<O>;
}

type StorageCallback = (updates: StorageUpdate[]) => void;
type LiveMapUpdate = LiveMapUpdates<string, Lson>;
type LiveObjectUpdate = LiveObjectUpdates<LsonObject>;
type LiveListUpdate = LiveListUpdates<Lson>;
/**
 * The payload of notifications sent (in-client) when LiveStructures change.
 * Messages of this kind are not originating from the network, but are 100%
 * in-client.
 */
type StorageUpdate = LiveMapUpdate | LiveObjectUpdate | LiveListUpdate;

/**
 * Read-only query surface over {@link UnacknowledgedOps}, handed to CRDTs so
 * they can look up their own still-pending Create ops without being able to
 * mutate the set (only the room adds/acks).
 */
interface ReadonlyUnacknowledgedOps {
    /** Still-unacknowledged Create ops whose `parentId` is the given one. */
    getByParentId(parentId: string): Iterable<ClientWireCreateOp>;
    /**
     * Still-unacknowledged Create ops whose `parentId` and `parentKey` are both
     * the given ones (i.e. targeting one exact position).
     */
    getByParentIdAndKey(parentId: string, parentKey: string): Iterable<ClientWireCreateOp>;
    /**
     * Whether the given pending op may already have been processed by the
     * server. True for ops that were in flight when a connection died: the
     * server may have stored them with the ack getting lost in the disconnect,
     * or may never have received them. Until the (re-sent) op's ack arrives,
     * the client cannot know which, so optimistic predictions that assume the
     * op has not been processed yet are unsound for these ops.
     */
    isPossiblyStored(opId: string): boolean;
}

/**
 * The managed pool is a namespace registry (i.e. a context) that "owns" all
 * the individual live nodes, ensuring each one has a unique ID, and holding on
 * to live nodes before and after they are inter-connected.
 */
interface ManagedPool {
    readonly nodes: ReadonlyMap<string, LiveNode>;
    readonly generateId: () => string;
    readonly generateOpId: () => string;
    readonly getNode: (id: string) => LiveNode | undefined;
    readonly addNode: (id: string, node: LiveNode) => void;
    readonly deleteNode: (id: string) => void;
    /**
     * Dispatching has three responsibilities:
     * - Sends serialized ops to the WebSocket servers
     * - Add reverse operations to the undo/redo stack
     * - Notify room subscribers with updates (in-client, no networking)
     */
    dispatch: (ops: ClientWireOp[], reverseOps: Op[], storageUpdates: Map<string, StorageUpdate>) => void;
    /**
     * Ensures storage can be written to else throws an error.
     * This is used to prevent writing to storage when the user does not have
     * permission to do so.
     * @throws {Error} if storage is not writable
     * @returns {void}
     */
    assertStorageIsWritable: () => void;
    /**
     * Read-only view of the client's still-unacknowledged ops (sent or
     * pending-send, not yet confirmed by the server).
     */
    readonly unacknowledgedOps: ReadonlyUnacknowledgedOps;
}
type CreateManagedPoolOptions = {
    /**
     * Returns the current connection ID. This is used to generate unique
     * prefixes for nodes created by this client. This number is allowed to
     * change over time (for example, when the client reconnects).
     */
    getCurrentConnectionId(): number;
    /**
     * Will get invoked when any Live structure calls .dispatch() on the pool.
     */
    onDispatch?: (ops: ClientWireOp[], reverse: Op[], storageUpdates: Map<string, StorageUpdate>) => void;
    /**
     * Will get invoked when any Live structure calls .assertStorageIsWritable()
     * on the pool. Defaults to true when not provided. Return false if you want
     * to prevent writes to the pool locally early, because you know they won't
     * have an effect upstream.
     */
    isStorageWritable?: () => boolean;
    /**
     * Read-only view of the client's still-unacknowledged ops. Used by CRDTs
     * (e.g. LiveList) to know which of their optimistic mutations the server
     * hasn't confirmed yet. Defaults to an empty view (e.g. server-side pools
     * that dispatch-and-flush have no optimistic state to track).
     */
    unacknowledgedOps?: ReadonlyUnacknowledgedOps;
};
/**
 * @private Private API, never use this API directly.
 */
declare function createManagedPool(options: CreateManagedPoolOptions): ManagedPool;
declare abstract class AbstractCrdt {
    #private;
    /**
     * @private
     * Returns true if the cached JSON snapshot exists and is reference-equal
     * to the given value. Does not trigger a recompute.
     */
    hasCache(value: unknown): boolean;
    /**
     * Return a JSON-compatible snapshot of this Live node and its children.
     * LiveObject values become plain objects, LiveList values become arrays,
     * and LiveMap values also become plain objects (not Map instances).
     * The result is cached and only recomputed when the contents change.
     */
    toJSON(): ReadonlyJson;
    /**
     * Returns a deep clone of the current LiveStructure, suitable for insertion
     * in the tree elsewhere.
     */
    abstract clone(): Lson;
}

type LiveListUpdateDelta = {
    type: "insert";
    index: number;
    item: Lson;
} | {
    type: "delete";
    index: number;
    deletedItem: Lson;
} | {
    type: "move";
    index: number;
    previousIndex: number;
    item: Lson;
} | {
    type: "set";
    index: number;
    item: Lson;
};
/**
 * A LiveList notification that is sent in-client to any subscribers whenever
 * one or more of the items inside the LiveList instance have changed.
 */
type LiveListUpdates<TItem extends Lson> = {
    type: "LiveList";
    node: LiveList<TItem>;
    updates: LiveListUpdateDelta[];
};
/**
 * The LiveList class represents an ordered collection of items that is synchronized across clients.
 */
declare class LiveList<TItem extends Lson> extends AbstractCrdt {
    #private;
    constructor(items: TItem[]);
    /**
     * Returns the number of elements.
     */
    get length(): number;
    /**
     * Adds one element to the end of the LiveList.
     * @param element The element to add to the end of the LiveList.
     */
    push(element: TItem): void;
    /**
     * Inserts one element at a specified index.
     * @param element The element to insert.
     * @param index The index at which you want to insert the element.
     */
    insert(element: TItem, index: number): void;
    /**
     * Move one element from one index to another.
     * @param index The index of the element to move
     * @param targetIndex The index where the element should be after moving.
     */
    move(index: number, targetIndex: number): void;
    /**
     * Deletes an element at the specified index
     * @param index The index of the element to delete
     */
    delete(index: number): void;
    clear(): void;
    set(index: number, item: TItem): void;
    /**
     * Tests whether all elements pass the test implemented by the provided function.
     * @param predicate Function to test for each element, taking two arguments (the element and its index).
     * @returns true if the predicate function returns a truthy value for every element. Otherwise, false.
     */
    every(predicate: (value: TItem, index: number) => unknown): boolean;
    /**
     * Creates an array with all elements that pass the test implemented by the provided function.
     * @param predicate Function to test each element of the LiveList. Return a value that coerces to true to keep the element, or to false otherwise.
     * @returns An array with the elements that pass the test.
     */
    filter(predicate: (value: TItem, index: number) => unknown): TItem[];
    /**
     * Returns the first element that satisfies the provided testing function.
     * @param predicate Function to execute on each value.
     * @returns The value of the first element in the LiveList that satisfies the provided testing function. Otherwise, undefined is returned.
     */
    find(predicate: (value: TItem, index: number) => unknown): TItem | undefined;
    /**
     * Returns the index of the first element in the LiveList that satisfies the provided testing function.
     * @param predicate Function to execute on each value until the function returns true, indicating that the satisfying element was found.
     * @returns The index of the first element in the LiveList that passes the test. Otherwise, -1.
     */
    findIndex(predicate: (value: TItem, index: number) => unknown): number;
    /**
     * Executes a provided function once for each element.
     * @param callbackfn Function to execute on each element.
     */
    forEach(callbackfn: (value: TItem, index: number) => void): void;
    /**
     * Get the element at the specified index.
     * @param index The index on the element to get.
     * @returns The element at the specified index or undefined.
     */
    get(index: number): TItem | undefined;
    /**
     * Returns the first index at which a given element can be found in the LiveList, or -1 if it is not present.
     * @param searchElement Element to locate.
     * @param fromIndex The index to start the search at.
     * @returns The first index of the element in the LiveList; -1 if not found.
     */
    indexOf(searchElement: TItem, fromIndex?: number): number;
    /**
     * Returns the last index at which a given element can be found in the LiveList, or -1 if it is not present. The LiveList is searched backwards, starting at fromIndex.
     * @param searchElement Element to locate.
     * @param fromIndex The index at which to start searching backwards.
     * @returns The last index of the element in the LiveList; -1 if not found.
     */
    lastIndexOf(searchElement: TItem, fromIndex?: number): number;
    /**
     * Creates an array populated with the results of calling a provided function on every element.
     * @param callback Function that is called for every element.
     * @returns An array with each element being the result of the callback function.
     */
    map<U>(callback: (value: TItem, index: number) => U): U[];
    /**
     * Tests whether at least one element in the LiveList passes the test implemented by the provided function.
     * @param predicate Function to test for each element.
     * @returns true if the callback function returns a truthy value for at least one element. Otherwise, false.
     */
    some(predicate: (value: TItem, index: number) => unknown): boolean;
    [Symbol.iterator](): IterableIterator<TItem>;
    toJSON(): readonly ToJson<TItem>[];
    clone(): LiveList<TItem>;
}

/**
 * INTERNAL
 */
declare class LiveRegister<TValue extends Json> extends AbstractCrdt {
    #private;
    constructor(data: TValue);
    get data(): TValue;
    clone(): TValue;
}

type LiveStructure = LiveObject<LsonObject> | LiveList<Lson> | LiveMap<string, Lson>;
/**
 * Think of Lson as a sibling of the Json data tree, except that the nested
 * data structure can contain a mix of Json values and LiveStructure instances.
 */
type Lson = Json | LiveStructure;
/**
 * LiveNode is the internal tree for managing Live data structures. The key
 * difference with Lson is that all the Json values get represented in
 * a LiveRegister node.
 */
type LiveNode = LiveStructure | LiveRegister<Json>;
/**
 * A mapping of keys to Lson values. A Lson value is any valid JSON
 * value or a Live storage data structure (LiveMap, LiveList, etc.)
 */
type LsonObject = Record<string, Lson | undefined>;
/**
 * Helper type to convert any valid Lson type to the equivalent Json type.
 *
 * Examples:
 *
 *   ToJson<42>                         // 42
 *   ToJson<'hi'>                       // 'hi'
 *   ToJson<number>                     // number
 *   ToJson<string>                     // string
 *   ToJson<string | LiveList<number>>  // string | readonly number[]
 *   ToJson<LiveMap<string, LiveList<number>>>
 *                                      // { readonly [key: string]: readonly number[] }
 *   ToJson<LiveObject<{ a: number, b: LiveList<string>, c?: number }>>
 *                                      // { readonly a: null, readonly b: readonly string[], readonly c?: number }
 */
type ToJson<L extends Lson | LsonObject> = L extends LiveList<infer I extends Lson> ? Lson extends I ? readonly ReadonlyJson[] : readonly ToJson<I>[] : L extends LiveObject<infer O extends LsonObject> ? LsonObject extends O ? ReadonlyJsonObject : {
    readonly [K in keyof O]: ToJson<Exclude<O[K], undefined>> | (undefined extends O[K] ? undefined : never);
} : L extends LiveMap<infer KS extends string, infer V extends Lson> ? Lson extends V ? ReadonlyJsonObject : {
    readonly [K in KS]: ToJson<V>;
} : L extends LsonObject ? string extends keyof L ? ReadonlyJsonObject : {
    readonly [K in keyof L]: ToJson<Exclude<L[K], undefined>> | (undefined extends L[K] ? undefined : never);
} : L extends Json ? L : never;

type DateToString<T> = {
    [P in keyof T]: T[P] extends Date ? string : T[P] extends Date | null ? string | null : T[P] extends Date | undefined ? string | undefined : T[P];
};

type MentionData = Relax<UserMentionData | GroupMentionData>;
type UserMentionData = {
    kind: "user";
    id: string;
};
type GroupMentionData = {
    kind: "group";
    id: string;
    userIds?: string[];
};

type InboxNotificationThreadData = {
    kind: "thread";
    id: string;
    roomId: string;
    threadId: string;
    notifiedAt: Date;
    readAt: Date | null;
};
type InboxNotificationTextMentionData = {
    kind: "textMention";
    id: string;
    roomId: string;
    notifiedAt: Date;
    readAt: Date | null;
    createdBy: string;
    mentionId: string;
    mention: MentionData;
};
type InboxNotificationTextMentionDataPlain = DateToString<InboxNotificationTextMentionData>;
type ActivityData = Record<string, string | boolean | number | undefined>;
type InboxNotificationActivity<K extends keyof DAD = keyof DAD> = {
    id: string;
    createdAt: Date;
    data: DAD[K];
};
type InboxNotificationCustomData<K extends keyof DAD = keyof DAD> = {
    kind: K;
    id: string;
    roomId?: string;
    subjectId: string;
    notifiedAt: Date;
    readAt: Date | null;
    activities: InboxNotificationActivity<K>[];
};
type InboxNotificationData = InboxNotificationThreadData | InboxNotificationCustomData | InboxNotificationTextMentionData;
type InboxNotificationThreadDataPlain = DateToString<InboxNotificationThreadData>;
type InboxNotificationCustomDataPlain = Omit<DateToString<InboxNotificationCustomData>, "activities"> & {
    activities: DateToString<InboxNotificationActivity>[];
};
type InboxNotificationDataPlain = InboxNotificationThreadDataPlain | InboxNotificationCustomDataPlain | InboxNotificationTextMentionDataPlain;
type InboxNotificationDeleteInfo = {
    type: "deletedInboxNotification";
    id: string;
    roomId: string;
    deletedAt: Date;
};

type BaseActivitiesData = {
    [key: `$${string}`]: ActivityData;
};

type BaseGroupInfo = {
    [key: string]: Json | undefined;
    /**
     * The name of the group.
     */
    name?: string;
    /**
     * The avatar of the group.
     */
    avatar?: string;
    /**
     * The description of the group.
     */
    description?: string;
};

type BaseRoomInfo = {
    [key: string]: Json | undefined;
    /**
     * The name of the room.
     */
    name?: string;
    /**
     * The URL of the room.
     */
    url?: string;
};

declare global {
    /**
     * Namespace for user-defined Liveblocks types.
     */
    export interface Liveblocks {
        [key: string]: unknown;
    }
}
type ExtendableTypes = "Presence" | "Storage" | "UserMeta" | "RoomEvent" | "ThreadMetadata" | "CommentMetadata" | "FeedMetadata" | "FeedMessageData" | "RoomInfo" | "GroupInfo" | "ActivitiesData";
type MakeErrorString<K extends ExtendableTypes, Reason extends string = "does not match its requirements"> = `The type you provided for '${K}' ${Reason}. To learn how to fix this, see https://liveblocks.io/docs/errors/${K}`;
type GetOverride<K extends ExtendableTypes, B, Reason extends string = "does not match its requirements"> = GetOverrideOrErrorValue<K, B, MakeErrorString<K, Reason>>;
type GetOverrideOrErrorValue<K extends ExtendableTypes, B, ErrorType> = unknown extends Liveblocks[K] ? B : Liveblocks[K] extends B ? Liveblocks[K] : ErrorType;
type DP = GetOverride<"Presence", JsonObject, "is not a valid JSON object">;
type DS = GetOverride<"Storage", LsonObject, "is not a valid LSON value">;
type DU = GetOverrideOrErrorValue<"UserMeta", BaseUserMeta, Record<"id" | "info", MakeErrorString<"UserMeta">>>;
type DE = GetOverride<"RoomEvent", Json, "is not a valid JSON value">;
type DTM = GetOverride<"ThreadMetadata", BaseMetadata>;
type DCM = GetOverride<"CommentMetadata", BaseMetadata>;
type DFM = GetOverride<"FeedMetadata", Json, "is not a valid JSON value">;
type DFMD = GetOverride<"FeedMessageData", Json, "is not a valid JSON value">;
type DRI = GetOverride<"RoomInfo", BaseRoomInfo>;
type DGI = GetOverride<"GroupInfo", BaseGroupInfo>;
type DAD = GetOverrideOrErrorValue<"ActivitiesData", BaseActivitiesData, {
    [K in keyof Liveblocks["ActivitiesData"]]: "At least one of the custom notification kinds you provided for 'ActivitiesData' does not match its requirements. To learn how to fix this, see https://liveblocks.io/docs/errors/ActivitiesData";
}>;
type KDAD = keyof DAD extends `$${string}` ? keyof DAD : "Custom notification kinds must start with '$' but your custom 'ActivitiesData' type contains at least one kind which doesn't. To learn how to fix this, see https://liveblocks.io/docs/errors/ActivitiesData";

type BaseMetadata = Record<string, string | boolean | number | undefined>;
type CommentReaction = {
    emoji: string;
    createdAt: Date;
    users: {
        id: string;
    }[];
};
type CommentAttachment = {
    type: "attachment";
    id: string;
    name: string;
    size: number;
    mimeType: string;
};
type CommentLocalAttachmentIdle = {
    type: "localAttachment";
    status: "idle";
    id: string;
    name: string;
    size: number;
    mimeType: string;
    file: File;
};
type CommentLocalAttachmentUploading = {
    type: "localAttachment";
    status: "uploading";
    id: string;
    name: string;
    size: number;
    mimeType: string;
    file: File;
};
type CommentLocalAttachmentUploaded = {
    type: "localAttachment";
    status: "uploaded";
    id: string;
    name: string;
    size: number;
    mimeType: string;
    file: File;
};
type CommentLocalAttachmentError = {
    type: "localAttachment";
    status: "error";
    id: string;
    name: string;
    size: number;
    mimeType: string;
    file: File;
    error: Error;
};
type CommentLocalAttachment = CommentLocalAttachmentIdle | CommentLocalAttachmentUploading | CommentLocalAttachmentUploaded | CommentLocalAttachmentError;
type CommentMixedAttachment = CommentAttachment | CommentLocalAttachment;
/**
 * Represents a comment.
 */
type CommentData<CM extends BaseMetadata = DCM> = {
    type: "comment";
    id: string;
    threadId: string;
    roomId: string;
    userId: string;
    createdAt: Date;
    editedAt?: Date;
    reactions: CommentReaction[];
    attachments: CommentAttachment[];
    metadata: CM;
} & Relax<{
    body: CommentBody;
} | {
    deletedAt: Date;
}>;
type CommentDataPlain<CM extends BaseMetadata = DCM> = Omit<DateToString<CommentData<CM>>, "reactions" | "body" | "metadata"> & {
    reactions: DateToString<CommentReaction>[];
    metadata: CM;
} & Relax<{
    body: CommentBody;
} | {
    deletedAt: string;
}>;
type CommentBodyBlockElement = CommentBodyParagraph;
type CommentBodyInlineElement = CommentBodyText | CommentBodyMention | CommentBodyLink;
type CommentBodyElement = CommentBodyBlockElement | CommentBodyInlineElement;
type CommentBodyParagraph = {
    type: "paragraph";
    children: CommentBodyInlineElement[];
};
type CommentBodyMention = Relax<CommentBodyUserMention | CommentBodyGroupMention>;
type CommentBodyUserMention = {
    type: "mention";
    kind: "user";
    id: string;
};
type CommentBodyGroupMention = {
    type: "mention";
    kind: "group";
    id: string;
    userIds?: string[];
};
type CommentBodyLink = {
    type: "link";
    url: string;
    text?: string;
};
type CommentBodyText = Resolve<{
    bold?: boolean;
    italic?: boolean;
    strikethrough?: boolean;
    code?: boolean;
    text: string;
} & {
    [K in Exclude<keyof Relax<CommentBodyMention | CommentBodyLink>, "text">]?: never;
}>;
type CommentBody = {
    version: 1;
    content: CommentBodyBlockElement[];
};
type CommentUserReaction = {
    emoji: string;
    createdAt: Date;
    userId: string;
};
type CommentUserReactionPlain = DateToString<CommentUserReaction>;
type SearchCommentsResult = {
    threadId: string;
    commentId: string;
    content: string;
};
type ThreadVisibility = "public" | "private";
/**
 * Represents a thread of comments.
 */
type ThreadData<TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM> = {
    type: "thread";
    id: string;
    roomId: string;
    createdAt: Date;
    updatedAt: Date;
    comments: CommentData<CM>[];
    metadata: TM;
    resolved: boolean;
    visibility: ThreadVisibility;
};
interface ThreadDataWithDeleteInfo<TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM> extends ThreadData<TM, CM> {
    deletedAt?: Date;
}
type ThreadDataPlain<TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM> = Omit<DateToString<ThreadData<TM, CM>>, "comments" | "metadata"> & {
    comments: CommentDataPlain<CM>[];
    metadata: TM;
};
type ThreadDeleteInfo = {
    type: "deletedThread";
    id: string;
    roomId: string;
    deletedAt: Date;
};
type StringOperators<T> = T | {
    startsWith: string;
};
type NumberOperators<T> = T | {
    lt?: number;
    gt?: number;
    lte?: number;
    gte?: number;
};
/**
 * This type can be used to build a metadata query string (compatible
 * with `@liveblocks/query-parser`) through a type-safe API.
 *
 * In addition to exact values (`:` in query string), it adds:
 * - to strings:
 *  - `startsWith` (`^` in query string)
 */
type QueryMetadata<M extends BaseMetadata> = {
    [K in keyof M]: (string extends M[K] ? StringOperators<M[K]> : M[K]) | (number extends M[K] ? NumberOperators<M[K]> : M[K]) | null;
};

type GroupMemberData = {
    id: string;
    addedAt: Date;
};
type GroupScopes = Partial<{
    mention: true;
}>;
type GroupData = {
    type: "group";
    id: string;
    /**
     * @deprecated Use `organizationId` instead.
     */
    tenantId: string;
    organizationId: string;
    createdAt: Date;
    updatedAt: Date;
    scopes: GroupScopes;
    members: GroupMemberData[];
};
type GroupDataPlain = Omit<DateToString<GroupData>, "members"> & {
    members: DateToString<GroupMemberData>[];
};

/**
 * Pre-defined notification channels support list.
 */
type NotificationChannel = "email" | "slack" | "teams" | "webPush";
/**
 * `K` represents custom notification kinds
 * defined in the augmentation `ActivitiesData` (e.g `liveblocks.config.ts`).
 * It means the type `NotificationKind` will be shaped like:
 * thread | textMention | $customKind1 | $customKind2 | ...
 */
type NotificationKind<K extends keyof DAD = keyof DAD> = "thread" | "textMention" | K;
/**
 * A notification channel settings is a set of notification kinds.
 * One setting can have multiple kinds (+ augmentation)
 */
type NotificationChannelSettings = {
    [K in NotificationKind]: boolean;
};
/**
 * @private
 *
 * Base definition of notification settings.
 * Plain means it's a simple object coming from the remote backend.
 *
 * It's the raw settings object where somme channels cannot exists
 * because there are no notification kinds enabled on the dashboard.
 * And this object isn't yet proxied by the creator factory `createNotificationSettings`.
 */
type NotificationSettingsPlain = {
    [C in NotificationChannel]?: NotificationChannelSettings;
};
/**
 * Notification settings.
 * One channel for one set of settings.
 */
type NotificationSettings = {
    [C in NotificationChannel]: NotificationChannelSettings | null;
};
/**
 * It creates a deep partial specific for `NotificationSettings`
 * to offer a nice DX when updating the settings (e.g not being forced to define every keys)
 * and at the same the some preserver the augmentation for custom kinds (e.g `liveblocks.config.ts`).
 */
type DeepPartialWithAugmentation<T> = T extends object ? {
    [P in keyof T]?: T[P] extends {
        [K in NotificationKind]: boolean;
    } ? Partial<T[P]> & {
        [K in keyof DAD]?: boolean;
    } : DeepPartialWithAugmentation<T[P]>;
} : T;
/**
 * Partial notification settings with augmentation preserved gracefully.
 * It means you can update the settings without being forced to define every keys.
 * Useful when implementing update functions.
 */
type PartialNotificationSettings = DeepPartialWithAugmentation<NotificationSettingsPlain>;
/**
 * @private
 *
 * Creates a `NotificationSettings` object with the given initial plain settings.
 * It defines a getter for each channel to access the settings and returns `null` with an error log
 * in case the required channel isn't enabled in the dashboard.
 *
 * You can see this function as `Proxy` like around `NotificationSettingsPlain` type.
 * We can't predict what will be enabled on the dashboard or not, so it's important
 * provide a good DX to developers by returning `null` completed by an error log
 * when they try to access a channel that isn't enabled in the dashboard.
 */
declare function createNotificationSettings(plain: NotificationSettingsPlain): NotificationSettings;
/**
 * @private
 *
 * Patch a `NotificationSettings` object by applying notification kind updates
 * coming from a `PartialNotificationSettings` object.
 */
declare function patchNotificationSettings(existing: NotificationSettings, patch: PartialNotificationSettings): NotificationSettings;
/**
 *
 * Utility to check if a notification channel settings
 * is enabled for every notification kinds.
 *
 * Usage:
 * ```ts
 * const isEmailChannelEnabled = isNotificationChannelEnabled(settings.email);
 * ```
 */
declare function isNotificationChannelEnabled(settings: NotificationChannelSettings | null): boolean;

type RoomThreadsSubscriptionSettings = "all" | "replies_and_mentions" | "none";
type RoomTextMentionsSubscriptionSettings = "mine" | "none";
type RoomSubscriptionSettings = {
    threads: RoomThreadsSubscriptionSettings;
    textMentions: RoomTextMentionsSubscriptionSettings;
};
type UserRoomSubscriptionSettings = {
    roomId: string;
} & RoomSubscriptionSettings;

type SubscriptionData<K extends keyof DAD = keyof DAD> = {
    kind: NotificationKind<K>;
    subjectId: string;
    createdAt: Date;
};
type SubscriptionDataPlain = DateToString<SubscriptionData>;
type UserSubscriptionData<K extends keyof DAD = keyof DAD> = SubscriptionData<K> & {
    userId: string;
};
type UserSubscriptionDataPlain = DateToString<UserSubscriptionData>;
type SubscriptionDeleteInfo = {
    type: "deletedSubscription";
    kind: NotificationKind;
    subjectId: string;
    deletedAt: Date;
};
type SubscriptionDeleteInfoPlain = DateToString<SubscriptionDeleteInfo>;
type SubscriptionKey = `${NotificationKind}:${string}`;
declare function getSubscriptionKey(subscription: SubscriptionData | SubscriptionDeleteInfo): SubscriptionKey;
declare function getSubscriptionKey(kind: NotificationKind, subjectId: string): SubscriptionKey;

type UrlMetadata = {
    title?: string;
    description?: string;
    image?: string;
    icon?: string;
};

type HistoryVersion = {
    id: `vh_${string}`;
    createdAt: Date;
    authors: {
        id: string;
    }[];
};

/**
 * Represents a user connected in a room. Treated as immutable.
 */
type User<P extends JsonObject = DP, U extends BaseUserMeta = DU> = {
    /**
     * The connection ID of the User. It is unique and increment at every new connection.
     */
    readonly connectionId: number;
    /**
     * The ID of the User that has been set in the authentication endpoint.
     * Useful to get additional information about the connected user.
     */
    readonly id: U["id"];
    /**
     * Additional user information that has been set in the authentication endpoint.
     */
    readonly info: U["info"];
    /**
     * The user’s presence data.
     */
    readonly presence: P;
    /**
     * True if the user can mutate the Room’s Storage and/or YDoc, false if they
     * can only read but not mutate it.
     */
    readonly canWrite: boolean;
    /**
     * True if the user can comment on a thread
     */
    readonly canComment: boolean;
};

type InternalOthersEvent<P extends JsonObject, U extends BaseUserMeta> = Relax<{
    type: "leave";
    user: User<P, U>;
} | {
    type: "enter";
    user: User<P, U>;
} | {
    type: "update";
    user: User<P, U>;
    updates: Partial<P>;
} | {
    type: "reset";
}>;
type OthersEvent<P extends JsonObject = DP, U extends BaseUserMeta = DU> = Resolve<InternalOthersEvent<P, U> & {
    others: readonly User<P, U>[];
}>;
declare enum TextEditorType {
    Lexical = "lexical",
    TipTap = "tiptap",
    BlockNote = "blocknote"
}
type BadgeLocation = "top-right" | "bottom-right" | "bottom-left" | "top-left";

/**
 * Extracts the optional keys (whose values are allowed to be `undefined`).
 */
type OptionalKeys<T> = Extract<{
    [K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T], string>;
type MakeOptionalFieldsNullable<T> = {
    [K in keyof T]: K extends OptionalKeys<T> ? T[K] | null : T[K];
};
/**
 * Like Partial<T>, but also allows `null` for optional fields. Useful for
 * representing patches where `null` means "remove this field" and `undefined`
 * means "leave this field unchanged".
 */
type Patchable<T> = Partial<MakeOptionalFieldsNullable<T>>;

interface RoomHttpApi<TM extends BaseMetadata, CM extends BaseMetadata> {
    getThreads(options: {
        roomId: string;
        cursor?: string;
        query?: {
            resolved?: boolean;
            visibility?: ThreadVisibility;
            subscribed?: boolean;
            metadata?: Partial<QueryMetadata<TM>>;
        };
    }): Promise<{
        threads: ThreadData<TM, CM>[];
        inboxNotifications: InboxNotificationData[];
        subscriptions: SubscriptionData[];
        requestedAt: Date;
        nextCursor: string | null;
        permissionHints: Record<string, RoomPermissions>;
    }>;
    getThreadsSince(options: {
        roomId: string;
        since: Date;
        signal?: AbortSignal;
    }): Promise<{
        threads: {
            updated: ThreadData<TM, CM>[];
            deleted: ThreadDeleteInfo[];
        };
        inboxNotifications: {
            updated: InboxNotificationData[];
            deleted: InboxNotificationDeleteInfo[];
        };
        subscriptions: {
            updated: SubscriptionData[];
            deleted: SubscriptionDeleteInfo[];
        };
        requestedAt: Date;
        permissionHints: Record<string, RoomPermissions>;
    }>;
    searchComments(options: {
        roomId: string;
        query: {
            threadMetadata?: Partial<QueryMetadata<TM>>;
            threadResolved?: boolean;
            hasAttachments?: boolean;
            hasMentions?: boolean;
            text: string;
        };
    }, requestOptions?: {
        signal?: AbortSignal;
    }): Promise<{
        data: Array<SearchCommentsResult>;
    }>;
    createThread({ roomId, metadata, body, commentId, threadId, commentMetadata, attachmentIds, }: {
        roomId: string;
        threadId?: string;
        commentId?: string;
        visibility?: ThreadVisibility;
        metadata: TM | undefined;
        commentMetadata: CM | undefined;
        body: CommentBody;
        attachmentIds?: string[];
    }): Promise<ThreadData<TM, CM>>;
    getThread(options: {
        roomId: string;
        threadId: string;
    }): Promise<{
        thread?: ThreadData<TM, CM>;
        inboxNotification?: InboxNotificationData;
        subscription?: SubscriptionData;
    }>;
    deleteThread({ roomId, threadId, }: {
        roomId: string;
        threadId: string;
        visibility?: ThreadVisibility;
    }): Promise<void>;
    editThreadMetadata({ roomId, metadata, threadId, }: {
        roomId: string;
        metadata: Patchable<TM>;
        threadId: string;
        visibility?: ThreadVisibility;
    }): Promise<TM>;
    editCommentMetadata({ roomId, threadId, commentId, metadata, }: {
        roomId: string;
        threadId: string;
        commentId: string;
        metadata: Patchable<CM>;
        visibility?: ThreadVisibility;
    }): Promise<CM>;
    createComment({ roomId, threadId, commentId, body, metadata, attachmentIds, }: {
        roomId: string;
        threadId: string;
        commentId?: string;
        body: CommentBody;
        metadata?: CM;
        attachmentIds?: string[];
        visibility?: ThreadVisibility;
    }): Promise<CommentData<CM>>;
    editComment({ roomId, threadId, commentId, body, attachmentIds, metadata, }: {
        roomId: string;
        threadId: string;
        commentId: string;
        body: CommentBody;
        attachmentIds?: string[];
        metadata?: Patchable<CM>;
        visibility?: ThreadVisibility;
    }): Promise<CommentData<CM>>;
    deleteComment({ roomId, threadId, commentId, }: {
        roomId: string;
        threadId: string;
        commentId: string;
        visibility?: ThreadVisibility;
    }): Promise<void>;
    addReaction({ roomId, threadId, commentId, emoji, }: {
        roomId: string;
        threadId: string;
        commentId: string;
        emoji: string;
        visibility?: ThreadVisibility;
    }): Promise<CommentUserReaction>;
    removeReaction({ roomId, threadId, commentId, emoji, }: {
        roomId: string;
        threadId: string;
        commentId: string;
        emoji: string;
        visibility?: ThreadVisibility;
    }): Promise<void>;
    markThreadAsResolved({ roomId, threadId, }: {
        roomId: string;
        threadId: string;
        visibility?: ThreadVisibility;
    }): Promise<void>;
    markThreadAsUnresolved({ roomId, threadId, }: {
        roomId: string;
        threadId: string;
        visibility?: ThreadVisibility;
    }): Promise<void>;
    subscribeToThread({ roomId, threadId, }: {
        roomId: string;
        threadId: string;
    }): Promise<SubscriptionData>;
    unsubscribeFromThread({ roomId, threadId, }: {
        roomId: string;
        threadId: string;
    }): Promise<void>;
    markRoomInboxNotificationAsRead({ roomId, inboxNotificationId, }: {
        roomId: string;
        inboxNotificationId: string;
    }): Promise<string>;
    getSubscriptionSettings({ roomId, signal, }: {
        roomId: string;
        signal?: AbortSignal;
    }): Promise<RoomSubscriptionSettings>;
    updateSubscriptionSettings({ roomId, settings, }: {
        roomId: string;
        settings: Partial<RoomSubscriptionSettings>;
    }): Promise<RoomSubscriptionSettings>;
    getAttachmentUrl(options: {
        roomId: string;
        attachmentId: string;
    }): Promise<string>;
    uploadAttachment({ roomId, attachment, signal, }: {
        roomId: string;
        attachment: CommentLocalAttachment;
        signal?: AbortSignal;
    }): Promise<CommentAttachment>;
    getOrCreateAttachmentUrlsStore(roomId: string): BatchStore<string, string>;
    createTextMention({ roomId, mentionId, mention, }: {
        roomId: string;
        mentionId: string;
        mention: MentionData;
    }): Promise<void>;
    deleteTextMention({ roomId, mentionId, }: {
        roomId: string;
        mentionId: string;
    }): Promise<void>;
    fetchStorageHistoryVersion({ roomId, versionId, }: {
        roomId: string;
        versionId: string;
    }): Promise<Response>;
    fetchYjsHistoryVersion({ roomId, versionId, }: {
        roomId: string;
        versionId: string;
    }): Promise<Response>;
    createVersionHistorySnapshot({ roomId }: {
        roomId: string;
    }): Promise<void>;
    deleteHistoryVersion({ roomId, versionId, }: {
        roomId: string;
        versionId: string;
    }): Promise<void>;
    reportTextEditor({ roomId, type, rootKey, }: {
        roomId: string;
        type: TextEditorType;
        rootKey: string;
    }): Promise<void>;
    listHistoryVersions({ roomId }: {
        roomId: string;
    }): Promise<{
        versions: HistoryVersion[];
        requestedAt: Date;
    }>;
    listHistoryVersionsSince({ roomId, since, signal, }: {
        roomId: string;
        since: Date;
        signal?: AbortSignal;
    }): Promise<{
        versions: HistoryVersion[];
        requestedAt: Date;
    }>;
    streamStorage(options: {
        roomId: string;
    }): Promise<StorageNode[]>;
    executeContextualPrompt({ roomId, prompt, context, signal, }: {
        roomId: string;
        prompt: string;
        context: ContextualPromptContext;
        previous?: {
            prompt: string;
            response: ContextualPromptResponse;
        };
        signal: AbortSignal;
    }): Promise<string>;
}
interface NotificationHttpApi<TM extends BaseMetadata, CM extends BaseMetadata> {
    getInboxNotifications(options?: {
        cursor?: string;
        query?: {
            roomId?: string;
            kind?: string;
        };
    }): Promise<{
        inboxNotifications: InboxNotificationData[];
        threads: ThreadData<TM, CM>[];
        subscriptions: SubscriptionData[];
        nextCursor: string | null;
        requestedAt: Date;
    }>;
    getInboxNotificationsSince(options: {
        since: Date;
        query?: {
            roomId?: string;
            kind?: string;
        };
        signal?: AbortSignal;
    }): Promise<{
        inboxNotifications: {
            updated: InboxNotificationData[];
            deleted: InboxNotificationDeleteInfo[];
        };
        threads: {
            updated: ThreadData<TM, CM>[];
            deleted: ThreadDeleteInfo[];
        };
        subscriptions: {
            updated: SubscriptionData[];
            deleted: SubscriptionDeleteInfo[];
        };
        requestedAt: Date;
    }>;
    getUnreadInboxNotificationsCount(options?: {
        query?: {
            roomId?: string;
            kind?: string;
        };
        signal?: AbortSignal;
    }): Promise<number>;
    markAllInboxNotificationsAsRead(): Promise<void>;
    markInboxNotificationAsRead(inboxNotificationId: string): Promise<void>;
    deleteAllInboxNotifications(): Promise<void>;
    deleteInboxNotification(inboxNotificationId: string): Promise<void>;
    getNotificationSettings(options?: {
        signal?: AbortSignal;
    }): Promise<NotificationSettingsPlain>;
    updateNotificationSettings(settings: PartialNotificationSettings): Promise<NotificationSettingsPlain>;
}
interface LiveblocksHttpApi<TM extends BaseMetadata, CM extends BaseMetadata> extends RoomHttpApi<TM, CM>, NotificationHttpApi<TM, CM> {
    getUrlMetadata(url: string): Promise<UrlMetadata>;
    getUserThreads_experimental(options?: {
        cursor?: string;
        query?: {
            resolved?: boolean;
            visibility?: ThreadVisibility;
            metadata?: Partial<QueryMetadata<TM>>;
        };
    }): Promise<{
        threads: ThreadData<TM, CM>[];
        inboxNotifications: InboxNotificationData[];
        subscriptions: SubscriptionData[];
        nextCursor: string | null;
        requestedAt: Date;
        permissionHints: Record<string, RoomPermissions>;
    }>;
    getUserThreadsSince_experimental(options: {
        since: Date;
        signal?: AbortSignal;
    }): Promise<{
        inboxNotifications: {
            updated: InboxNotificationData[];
            deleted: InboxNotificationDeleteInfo[];
        };
        threads: {
            updated: ThreadData<TM, CM>[];
            deleted: ThreadDeleteInfo[];
        };
        subscriptions: {
            updated: SubscriptionData[];
            deleted: SubscriptionDeleteInfo[];
        };
        requestedAt: Date;
        permissionHints: Record<string, RoomPermissions>;
    }>;
    groupsStore: BatchStore<GroupData | undefined, string>;
    getGroup(groupId: string): Promise<GroupData | undefined>;
}

/**
 * Use this symbol to brand an object property as internal.
 *
 * @example
 * Object.defineProperty(
 *   {
 *     public,
 *     [kInternal]: {
 *       private
 *     },
 *   },
 *   kInternal,
 *   {
 *     enumerable: false,
 *   }
 * );
 */
declare const kInternal: unique symbol;

/**
 * Back-port of TypeScript 5.4's built-in NoInfer utility type.
 * See https://stackoverflow.com/a/56688073/148872
 */
type NoInfr<A> = [A][A extends any ? 0 : never];

declare const kTrigger: unique symbol;
/**
 * Runs a callback function that is allowed to change multiple signals. At the
 * end of the batch, all changed signals will be notified (at most once).
 *
 * Nesting batches is supported.
 */
declare function batch(callback: Callback<void>): void;
type SignalType<S extends ISignal<any>> = S extends ISignal<infer T> ? T : never;
interface ISignal<T> {
    get(): T;
    subscribe(callback: Callback<void>): UnsubscribeCallback;
    addSink(sink: DerivedSignal<unknown>): void;
    removeSink(sink: DerivedSignal<unknown>): void;
}
/**
 * Base functionality every Signal implementation needs.
 */
declare abstract class AbstractSignal<T> implements ISignal<T>, Observable<void> {
    #private;
    constructor(equals?: (a: T, b: T) => boolean);
    dispose(): void;
    abstract get(): T;
    get hasWatchers(): boolean;
    [kTrigger](): void;
    subscribe(callback: Callback<void>): UnsubscribeCallback;
    subscribeOnce(callback: Callback<void>): UnsubscribeCallback;
    waitUntil(): never;
    markSinksDirty(): void;
    addSink(sink: DerivedSignal<unknown>): void;
    removeSink(sink: DerivedSignal<unknown>): void;
    asReadonly(): ISignal<T>;
}
declare class Signal<T> extends AbstractSignal<T> {
    #private;
    constructor(value: T, equals?: (a: T, b: T) => boolean);
    dispose(): void;
    get(): T;
    set(newValue: T | ((oldValue: T) => T)): void;
}
declare class DerivedSignal<T> extends AbstractSignal<T> {
    #private;
    static from<Ts extends unknown[], V>(...args: [...signals: {
        [K in keyof Ts]: ISignal<Ts[K]>;
    }, transform: (...values: Ts) => V]): DerivedSignal<V>;
    static from<Ts extends unknown[], V>(...args: [...signals: {
        [K in keyof Ts]: ISignal<Ts[K]>;
    }, transform: (...values: Ts) => V, equals: (a: V, b: V) => boolean]): DerivedSignal<V>;
    private constructor();
    dispose(): void;
    get isDirty(): boolean;
    markDirty(): void;
    get(): T;
    /**
     * Called by the Signal system if one or more of the dependent signals have
     * changed. In the case of a DerivedSignal, we'll only want to re-evaluate
     * the actual value if it's being watched, or any of their sinks are being
     * watched actively.
     */
    [kTrigger](): void;
}
/**
 * A MutableSignal is a bit like Signal, except its state is managed by
 * a single value whose reference does not change but is mutated.
 *
 * Similar to how useSyncExternalState() works in React, there is a way to read
 * the current state at any point in time synchronously, and a way to update
 * its reference.
 */
declare class MutableSignal<T extends object> extends AbstractSignal<T> {
    #private;
    constructor(initialState: T);
    dispose(): void;
    get(): T;
    /**
     * Invokes a callback function that is allowed to mutate the given state
     * value. Do not change the value outside of the callback.
     *
     * If the callback explicitly returns `false`, it's assumed that the state
     * was not changed.
     */
    mutate(callback?: (state: T) => void | boolean): void;
}

type Awaitable<T> = T | PromiseLike<T>;

type AiConnectionErrorContext = {
    type: "AI_CONNECTION_ERROR";
    code: -1 | 4001 | (number & {});
};
type RoomConnectionErrorContext = {
    type: "ROOM_CONNECTION_ERROR";
    code: -1 | 4001 | 4005 | 4006 | (number & {});
    roomId: string;
};
type LargeMessageErrorContext = {
    type: "LARGE_MESSAGE_ERROR";
};
type FeedRequestErrorContext = {
    type: "FEED_REQUEST_ERROR";
    roomId: string;
    requestId: string;
    code: string;
    reason?: string;
};
type CommentsOrNotificationsErrorContext = {
    type: "CREATE_THREAD_ERROR";
    roomId: string;
    threadId: string;
    commentId: string;
    body: CommentBody;
    visibility: ThreadVisibility;
    metadata: BaseMetadata;
    commentMetadata: BaseMetadata;
} | {
    type: "DELETE_THREAD_ERROR";
    roomId: string;
    threadId: string;
} | {
    type: "EDIT_THREAD_METADATA_ERROR";
    roomId: string;
    threadId: string;
    metadata: Patchable<BaseMetadata>;
} | {
    type: "EDIT_COMMENT_METADATA_ERROR";
    roomId: string;
    threadId: string;
    commentId: string;
    metadata: Patchable<BaseMetadata>;
} | {
    type: "MARK_THREAD_AS_RESOLVED_ERROR" | "MARK_THREAD_AS_UNRESOLVED_ERROR" | "SUBSCRIBE_TO_THREAD_ERROR" | "UNSUBSCRIBE_FROM_THREAD_ERROR";
    roomId: string;
    threadId: string;
} | {
    type: "CREATE_COMMENT_ERROR" | "EDIT_COMMENT_ERROR";
    roomId: string;
    threadId: string;
    commentId: string;
    body: CommentBody;
    metadata: BaseMetadata;
} | {
    type: "DELETE_COMMENT_ERROR";
    roomId: string;
    threadId: string;
    commentId: string;
} | {
    type: "ADD_REACTION_ERROR" | "REMOVE_REACTION_ERROR";
    roomId: string;
    threadId: string;
    commentId: string;
    emoji: string;
} | {
    type: "MARK_INBOX_NOTIFICATION_AS_READ_ERROR";
    inboxNotificationId: string;
    roomId?: string;
} | {
    type: "DELETE_INBOX_NOTIFICATION_ERROR";
    inboxNotificationId: string;
} | {
    type: "MARK_ALL_INBOX_NOTIFICATIONS_AS_READ_ERROR" | "DELETE_ALL_INBOX_NOTIFICATIONS_ERROR";
} | {
    type: "UPDATE_ROOM_SUBSCRIPTION_SETTINGS_ERROR";
    roomId: string;
} | {
    type: "UPDATE_NOTIFICATION_SETTINGS_ERROR";
};
type LiveblocksErrorContext = Relax<RoomConnectionErrorContext | CommentsOrNotificationsErrorContext | AiConnectionErrorContext | LargeMessageErrorContext | FeedRequestErrorContext>;
declare class LiveblocksError extends Error {
    readonly context: LiveblocksErrorContext;
    constructor(message: string, context: LiveblocksErrorContext, cause?: Error);
    /** Convenience accessor for error.context.roomId (if available) */
    get roomId(): LiveblocksErrorContext["roomId"];
    /**
     * Creates a LiveblocksError from a generic error, by attaching Liveblocks
     * contextual information like room ID, thread ID, etc.
     */
    static from(context: LiveblocksErrorContext, cause?: Error): LiveblocksError;
}

type ResolveMentionSuggestionsArgs = {
    /**
     * The ID of the current room.
     */
    roomId: string;
    /**
     * The text to search for.
     */
    text: string;
};
type ResolveUsersArgs = {
    /**
     * The IDs of the users to resolve.
     */
    userIds: string[];
};
type ResolveRoomsInfoArgs = {
    /**
     * The IDs of the rooms to resolve.
     */
    roomIds: string[];
};
type ResolveGroupsInfoArgs = {
    /**
     * The IDs of the groups to resolve.
     */
    groupIds: string[];
};
type EnterOptions<P extends JsonObject = DP, S extends LsonObject = DS> = Resolve<{
    /**
     * Whether or not the room automatically connects to Liveblock servers.
     * Default is true.
     *
     * Usually set to false when the client is used from the server to not call
     * the authentication endpoint or connect via WebSocket.
     */
    autoConnect?: boolean;
} & PartialUnless<P, {
    /**
     * The initial Presence to use and announce when you enter the Room. The
     * Presence is available on all users in the Room (me & others).
     */
    initialPresence: P | ((roomId: string) => P);
}> & PartialUnless<S, {
    /**
     * The initial Storage to use when entering a new Room.
     */
    initialStorage: S | LiveObject<S> | ((roomId: string) => S | LiveObject<S>);
}>>;
type SyncStatus = "synchronizing" | "synchronized";
/**
 * "synchronizing"     - Liveblocks is in the process of writing changes
 * "synchronized"      - Liveblocks has persisted all pending changes
 * "has-local-changes" - There is local pending state inputted by the user, but
 *                       we're not yet "synchronizing" it until a user
 *                       interaction, like the draft text in a comment box.
 */
type InternalSyncStatus = SyncStatus | "has-local-changes";
/**
 * @private
 *
 * Private methods and variables used in the core internals, but as a user
 * of Liveblocks, NEVER USE ANY OF THESE DIRECTLY, because bad things
 * will probably happen if you do.
 */
type PrivateClientApi<U extends BaseUserMeta, TM extends BaseMetadata, CM extends BaseMetadata, FM extends Json = DFM, FMD extends Json = DFMD> = {
    readonly currentUserId: Signal<string | undefined>;
    readonly mentionSuggestionsCache: Map<string, MentionData[]>;
    readonly resolveMentionSuggestions: ClientOptions<U>["resolveMentionSuggestions"];
    readonly usersStore: BatchStore<U["info"] | undefined, string>;
    readonly roomsInfoStore: BatchStore<DRI | undefined, string>;
    readonly groupsInfoStore: BatchStore<DGI | undefined, string>;
    readonly getRoomIds: () => string[];
    readonly httpClient: LiveblocksHttpApi<TM, CM>;
    as<TM2 extends BaseMetadata, CM2 extends BaseMetadata, FM2 extends Json = FM, FMD2 extends Json = FMD>(): Client<U, TM2, CM2, FM2, FMD2>;
    createSyncSource(): SyncSource;
    emitError(context: LiveblocksErrorContext, cause?: Error): void;
    ai: Ai;
};
type NotificationsApi<TM extends BaseMetadata, CM extends BaseMetadata> = {
    /**
     * Gets a page (or the initial page) for user inbox notifications and their
     * associated threads and thread subscriptions.
     *
     * This function should NOT be used for delta updates, only for pagination
     * (including the first page fetch). For delta updates (done during the
     * periodic polling), use the `getInboxNotificationsSince` function.
     *
     * @example
     * const {
     *   inboxNotifications,
     *   threads,
     *   subscriptions,
     *   nextCursor,
     * } = await client.getInboxNotifications();
     * const data = await client.getInboxNotifications();  // Fetch initial page (of 20 inbox notifications)
     * const data = await client.getInboxNotifications({ cursor: nextCursor });  // Fetch next page (= next 20 inbox notifications)
     */
    getInboxNotifications(options?: {
        cursor?: string;
        query?: {
            roomId?: string;
            kind?: string;
        };
    }): Promise<{
        inboxNotifications: InboxNotificationData[];
        threads: ThreadData<TM, CM>[];
        subscriptions: SubscriptionData[];
        nextCursor: string | null;
        requestedAt: Date;
    }>;
    /**
     * Fetches a "delta update" since the last time we updated.
     *
     * This function should NOT be used for pagination, for that, see the
     * `getInboxNotifications` function.
     *
     * @example
     * const {
     *   inboxNotifications: {
     *     updated,
     *     deleted,
     *   },
     *   threads: {
     *     updated,
     *     deleted,
     *   },
     *   subscriptions: {
     *     updated,
     *     deleted,
     *   },
     *   requestedAt,
     * } = await client.getInboxNotificationsSince({ since: result.requestedAt }});
     */
    getInboxNotificationsSince(options: {
        since: Date;
        query?: {
            roomId?: string;
            kind?: string;
        };
        signal?: AbortSignal;
    }): Promise<{
        inboxNotifications: {
            updated: InboxNotificationData[];
            deleted: InboxNotificationDeleteInfo[];
        };
        threads: {
            updated: ThreadData<TM, CM>[];
            deleted: ThreadDeleteInfo[];
        };
        subscriptions: {
            updated: SubscriptionData[];
            deleted: SubscriptionDeleteInfo[];
        };
        requestedAt: Date;
    }>;
    /**
     * Gets the number of unread inbox notifications for the current user.
     *
     * @example
     * const count = await client.getUnreadInboxNotificationsCount();
     */
    getUnreadInboxNotificationsCount(options?: {
        query?: {
            roomId?: string;
            kind?: string;
        };
        signal?: AbortSignal;
    }): Promise<number>;
    /**
     * Marks all inbox notifications as read.
     *
     * @example
     * await client.markAllInboxNotificationsAsRead();
     */
    markAllInboxNotificationsAsRead(): Promise<void>;
    /**
     * Marks an inbox notification as read.
     *
     * @example
     * await client.markInboxNotificationAsRead("in_xxx");
     */
    markInboxNotificationAsRead(inboxNotificationId: string): Promise<void>;
    /**
     * Deletes all inbox notifications for the current user.
     *
     * @example
     * await client.deleteAllInboxNotifications();
     */
    deleteAllInboxNotifications(): Promise<void>;
    /**
     * Deletes an inbox notification for the current user.
     *
     * @example
     * await client.deleteInboxNotification("in_xxx");
     */
    deleteInboxNotification(inboxNotificationId: string): Promise<void>;
    /**
     * Gets notifications settings for a user for a project.
     *
     * @example
     * const notificationSettings = await client.getNotificationSettings();
     */
    getNotificationSettings(options?: {
        signal?: AbortSignal;
    }): Promise<NotificationSettings>;
    /**
     * Update notifications settings for a user for a project.
     *
     * @example
     * await client.updateNotificationSettings({
     *  email: {
     *    thread: true,
     *    textMention: false,
     *    $customKind1: true,
     *  }
     * })
     */
    updateNotificationSettings(settings: PartialNotificationSettings): Promise<NotificationSettings>;
};
/**
 * @private Widest-possible Client type, matching _any_ Client instance. Note
 * that this type is different from `Client`-without-type-arguments. That
 * represents a Client instance using globally augmented types only, which is
 * narrower.
 */
type OpaqueClient = Client<BaseUserMeta>;
type Client<U extends BaseUserMeta = DU, TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM, FM extends Json = DFM, FMD extends Json = DFMD> = {
    /**
     * Gets a room. Returns null if {@link Client.enter} has not been called previously.
     *
     * @param roomId The id of the room
     */
    getRoom<P extends JsonObject = DP, S extends LsonObject = DS, E extends Json = DE, TM2 extends BaseMetadata = TM, CM2 extends BaseMetadata = CM, FM2 extends Json = FM, FMD2 extends Json = FMD>(roomId: string): Room<P, S, U, E, TM2, CM2, FM2, FMD2> | null;
    /**
     * Enter a room.
     * @param roomId The id of the room
     * @param options Optional. You can provide initializers for the Presence or Storage when entering the Room.
     * @returns The room and a leave function. Call the returned leave() function when you no longer need the room.
     */
    enterRoom<P extends JsonObject = DP, S extends LsonObject = DS, E extends Json = DE, TM2 extends BaseMetadata = TM, CM2 extends BaseMetadata = CM, FM2 extends Json = FM, FMD2 extends Json = FMD>(roomId: string, ...args: OptionalTupleUnless<P & S, [
        options: EnterOptions<NoInfr<P>, NoInfr<S>>
    ]>): {
        room: Room<P, S, U, E, TM2, CM2, FM2, FMD2>;
        leave: () => void;
    };
    /**
     * Purges all cached auth tokens and reconnects all rooms that are still
     * connected, if any.
     *
     * Call this whenever you log out a user in your application.
     */
    logout(): void;
    /**
     * Advanced APIs related to the resolvers.
     */
    resolvers: {
        /**
         * Invalidate some or all users that were previously cached by `resolveUsers`.
         *
         * @example
         * // Invalidate all users
         * client.resolvers.invalidateUsers();
         *
         * @example
         * // Invalidate specific users
         * client.resolvers.invalidateUsers(["user-1", "user-2"]);
         */
        invalidateUsers(userIds?: string[]): void;
        /**
         * Invalidate some or all rooms info that were previously cached by `resolveRoomsInfo`.
         *
         * @example
         * // Invalidate all rooms
         * client.resolvers.invalidateRoomsInfo();
         *
         * @example
         * // Invalidate specific rooms
         * client.resolvers.invalidateRoomsInfo(["room-1", "room-2"]);
         */
        invalidateRoomsInfo(roomIds?: string[]): void;
        /**
         * Invalidate some or all groups info that were previously cached by `resolveGroupsInfo`.
         *
         * @example
         * // Invalidate all groups
         * client.resolvers.invalidateGroupsInfo();
         *
         * @example
         * // Invalidate specific groups
         * client.resolvers.invalidateGroupsInfo(["group-1", "group-2"]);
         */
        invalidateGroupsInfo(groupIds?: string[]): void;
        /**
         * Invalidate all mention suggestions cached by `resolveMentionSuggestions`.
         *
         * @example
         * // Invalidate all mention suggestions
         * client.resolvers.invalidateMentionSuggestions();
         */
        invalidateMentionSuggestions(): void;
    };
    /**
     * @private
     *
     * Private methods and variables used in the core internals, but as a user
     * of Liveblocks, NEVER USE ANY OF THESE DIRECTLY, because bad things
     * will probably happen if you do.
     */
    readonly [kInternal]: PrivateClientApi<U, TM, CM, FM, FMD>;
    /**
     * Returns the current global sync status of the Liveblocks client. If any
     * part of Liveblocks has any local pending changes that haven't been
     * confirmed by or persisted by the server yet, this will be "synchronizing",
     * otherwise "synchronized".
     *
     * This is a combined status for all of the below parts of Liveblocks:
     * - Storage (realtime APIs)
     * - Text Editors
     * - Comments
     * - Notifications
     *
     * @example
     * const status = client.getSyncStatus();  // "synchronizing" | "synchronized"
     */
    getSyncStatus(): SyncStatus;
    /**
     * All possible client events, subscribable from a single place.
     */
    readonly events: {
        readonly error: Observable<LiveblocksError>;
        readonly syncStatus: Observable<void>;
    };
} & NotificationsApi<TM, CM>;
type AuthEndpoint = string | ((room?: string) => Promise<CustomAuthenticationResult>);
/**
 * The authentication endpoint that is called to ensure that the current user has access to a room.
 * Can be an url or a callback if you need to add additional headers.
 */
type ClientOptions<U extends BaseUserMeta = DU> = {
    throttle?: number;
    lostConnectionTimeout?: number;
    backgroundKeepAliveTimeout?: number;
    polyfills?: Polyfills;
    /**
     * A function that returns a list of mention suggestions matching a string.
     */
    resolveMentionSuggestions?: (args: ResolveMentionSuggestionsArgs) => Awaitable<string[] | MentionData[]>;
    /**
     * A function that returns user info from user IDs.
     * You should return a list of user objects of the same size, in the same order.
     */
    resolveUsers?: (args: ResolveUsersArgs) => Awaitable<(U["info"] | undefined)[] | undefined>;
    /**
     * A function that returns room info from room IDs.
     * You should return a list of room info objects of the same size, in the same order.
     */
    resolveRoomsInfo?: (args: ResolveRoomsInfoArgs) => Awaitable<(DRI | undefined)[] | undefined>;
    /**
     * A function that returns group info from group IDs.
     * You should return a list of group info objects of the same size, in the same order.
     */
    resolveGroupsInfo?: (args: ResolveGroupsInfoArgs) => Awaitable<(DGI | undefined)[] | undefined>;
    /**
     * Prevent the current browser tab from being closed if there are any locally
     * pending Liveblocks changes that haven't been submitted to or confirmed by
     * the server yet.
     */
    preventUnsavedChanges?: boolean;
    /**
     * The location where the brand badge should be displayed when using a free plan.
     * Default is "bottom-right".
     */
    badgeLocation?: BadgeLocation;
    /** Point the client to an alternative Liveblocks server. */
    baseUrl?: string;
} & Relax<{
    publicApiKey: string;
} | {
    authEndpoint: AuthEndpoint;
}>;
/**
 * Create a client that will be responsible to communicate with liveblocks servers.
 *
 * @example
 * const client = createClient({
 *   authEndpoint: "/api/auth"
 * });
 *
 * // It's also possible to use a function to call your authentication endpoint.
 * // Useful to add additional headers or use an API wrapper (like Firebase functions)
 * const client = createClient({
 *   authEndpoint: async (room?) => {
 *     const response = await fetch("/api/auth", {
 *       method: "POST",
 *       headers: {
 *          Authentication: "token",
 *          "Content-Type": "application/json"
 *       },
 *       body: JSON.stringify({ room })
 *     });
 *
 *     return await response.json(); // should be: { token: "..." }
 *   }
 * });
 */
declare function createClient<U extends BaseUserMeta = DU>(options: ClientOptions<U>): Client<U>;
/**
 * @private Private API, don't use this directly.
 */
declare function checkBounds(option: string, value: unknown, min: number, max?: number, recommendedMin?: number): number;

interface IWebSocketEvent {
    type: string;
}
interface IWebSocketCloseEvent extends IWebSocketEvent {
    readonly code: WebsocketCloseCodes;
    readonly wasClean: boolean;
    readonly reason: string;
}
interface IWebSocketMessageEvent extends IWebSocketEvent {
    readonly data: string | Buffer | ArrayBuffer | readonly Buffer[];
}
interface IWebSocketInstance {
    readonly CONNECTING: number;
    readonly OPEN: number;
    readonly CLOSING: number;
    readonly CLOSED: number;
    readonly readyState: number;
    addEventListener(type: "close", listener: (this: IWebSocketInstance, ev: IWebSocketCloseEvent) => unknown): void;
    addEventListener(type: "message", listener: (this: IWebSocketInstance, ev: IWebSocketMessageEvent) => unknown): void;
    addEventListener(type: "open" | "error", listener: (this: IWebSocketInstance, ev: IWebSocketEvent) => unknown): void;
    removeEventListener(type: "close", listener: (this: IWebSocketInstance, ev: IWebSocketCloseEvent) => unknown): void;
    removeEventListener(type: "message", listener: (this: IWebSocketInstance, ev: IWebSocketMessageEvent) => unknown): void;
    removeEventListener(type: "open" | "error", listener: (this: IWebSocketInstance, ev: IWebSocketEvent) => unknown): void;
    close(): void;
    send(data: string): void;
}
/**
 * Either the browser-based WebSocket API or Node.js' WebSocket API (from the
 * 'ws' package).
 *
 * This type defines the minimal WebSocket API that Liveblocks needs from
 * a WebSocket implementation, and is a minimal subset of the browser-based
 * WebSocket APIs and Node.js' WebSocket API so that both implementations are
 * assignable to this type.
 */
interface IWebSocket {
    new (address: string): IWebSocketInstance;
}
/**
 * The following ranges will be respected by the client:
 *
 *   10xx: client will reauthorize (just like 41xx)
 *   40xx: client will disconnect
 *   41xx: client will reauthorize
 *   42xx: client will retry without reauthorizing (currently not used)
 *
 */
declare enum WebsocketCloseCodes {
    /** Normal close of connection, the connection fulfilled its purpose. */
    CLOSE_NORMAL = 1000,
    /** Unexpected error happened with the network/infra level. In spirit akin to HTTP 503 */
    CLOSE_ABNORMAL = 1006,
    /** Unexpected error happened. In spirit akin to HTTP 500 */
    UNEXPECTED_CONDITION = 1011,
    /** Please back off for now, but try again in a few moments */
    TRY_AGAIN_LATER = 1013,
    /** Message wasn't understood, disconnect */
    INVALID_MESSAGE_FORMAT = 4000,
    /** Server refused to allow connection. Re-authorizing won't help. Disconnect. In spirit akin to HTTP 403 */
    NOT_ALLOWED = 4001,
    /** Unused */
    MAX_NUMBER_OF_MESSAGES_PER_SECONDS = 4002,
    /** Unused */
    MAX_NUMBER_OF_CONCURRENT_CONNECTIONS = 4003,
    /** Unused */
    MAX_NUMBER_OF_MESSAGES_PER_DAY_PER_APP = 4004,
    /** Room is full, disconnect */
    MAX_NUMBER_OF_CONCURRENT_CONNECTIONS_PER_ROOM = 4005,
    /** The room's ID was updated, disconnect */
    ROOM_ID_UPDATED = 4006,
    /** The server kicked the connection from the room. */
    KICKED = 4100,
    /** The auth token is expired, reauthorize to get a fresh one. In spirit akin to HTTP 401 */
    TOKEN_EXPIRED = 4109,
    /** Disconnect immediately */
    CLOSE_WITHOUT_RETRY = 4999
}

/**
 * Returns a human-readable status indicating the current connection status of
 * a Room, as returned by `room.getStatus()`. Can be used to implement
 * a connection status badge.
 */
type Status = "initial" | "connecting" | "connected" | "reconnecting" | "disconnected";
/**
 * Used to report about app-level reconnection issues.
 *
 * Normal (quick) reconnects won't be reported as a "lost connection". Instead,
 * the application will only get an event if the reconnection attempts by the
 * client are taking (much) longer than usual. Definitely a situation you want
 * to inform your users about, for example, by throwing a toast message on
 * screen, or show a "trying to reconnect" banner.
 */
type LostConnectionEvent = "lost" | "restored" | "failed";
/**
 * Arbitrary record that will be used as the authentication "authValue". It's the
 * value that is returned by calling the authentication delegate, and will get
 * passed to the connection factory delegate. This value will be remembered by
 * the connection manager, but its value will not be interpreted, so it can be
 * any value (except null).
 */
type BaseAuthResult = NonNullable<Json>;
type Delegates<T extends BaseAuthResult> = {
    authenticate: () => Promise<T>;
    createSocket: (authValue: T) => IWebSocketInstance;
    canZombie: () => boolean;
};

type ClientMsgCode = (typeof ClientMsgCode)[keyof typeof ClientMsgCode];
declare const ClientMsgCode: Readonly<{
    UPDATE_PRESENCE: 100;
    BROADCAST_EVENT: 103;
    FETCH_STORAGE: 200;
    UPDATE_STORAGE: 201;
    FETCH_YDOC: 300;
    UPDATE_YDOC: 301;
    FETCH_FEEDS: 510;
    FETCH_FEED_MESSAGES: 511;
    ADD_FEED: 512;
    UPDATE_FEED: 513;
    DELETE_FEED: 514;
    ADD_FEED_MESSAGE: 515;
    UPDATE_FEED_MESSAGE: 516;
    DELETE_FEED_MESSAGE: 517;
}>;
declare namespace ClientMsgCode {
    type UPDATE_PRESENCE = typeof ClientMsgCode.UPDATE_PRESENCE;
    type BROADCAST_EVENT = typeof ClientMsgCode.BROADCAST_EVENT;
    type FETCH_STORAGE = typeof ClientMsgCode.FETCH_STORAGE;
    type UPDATE_STORAGE = typeof ClientMsgCode.UPDATE_STORAGE;
    type FETCH_YDOC = typeof ClientMsgCode.FETCH_YDOC;
    type UPDATE_YDOC = typeof ClientMsgCode.UPDATE_YDOC;
    type FETCH_FEEDS = typeof ClientMsgCode.FETCH_FEEDS;
    type FETCH_FEED_MESSAGES = typeof ClientMsgCode.FETCH_FEED_MESSAGES;
    type ADD_FEED = typeof ClientMsgCode.ADD_FEED;
    type UPDATE_FEED = typeof ClientMsgCode.UPDATE_FEED;
    type DELETE_FEED = typeof ClientMsgCode.DELETE_FEED;
    type ADD_FEED_MESSAGE = typeof ClientMsgCode.ADD_FEED_MESSAGE;
    type UPDATE_FEED_MESSAGE = typeof ClientMsgCode.UPDATE_FEED_MESSAGE;
    type DELETE_FEED_MESSAGE = typeof ClientMsgCode.DELETE_FEED_MESSAGE;
}
/**
 * Messages that can be sent from the client to the server.
 */
type ClientMsg<P extends JsonObject, E extends Json> = BroadcastEventClientMsg<E> | UpdatePresenceClientMsg<P> | UpdateStorageClientMsg | FetchStorageClientMsg | FetchYDocClientMsg | UpdateYDocClientMsg | FetchFeedsClientMsg | FetchFeedMessagesClientMsg | AddFeedClientMsg | UpdateFeedClientMsg | DeleteFeedClientMsg | AddFeedMessageClientMsg | UpdateFeedMessageClientMsg | DeleteFeedMessageClientMsg;
type BroadcastEventClientMsg<E extends Json> = {
    type: ClientMsgCode.BROADCAST_EVENT;
    event: E;
};
type UpdatePresenceClientMsg<P extends JsonObject> = {
    readonly type: ClientMsgCode.UPDATE_PRESENCE;
    /**
     * Set this to any number to signify that this is a Full Presence™
     * update, not a patch.
     *
     * The numeric value itself no longer has specific meaning. Historically,
     * this field was intended so that clients could ignore these broadcasted
     * full presence messages, but it turned out that getting a full presence
     * "keyframe" from time to time was useful.
     *
     * So nowadays, the presence (pun intended) of this `targetActor` field
     * is a backward-compatible way of expressing that the `data` contains
     * all presence fields, and isn't a partial "patch".
     */
    readonly targetActor: number;
    readonly data: P;
} | {
    readonly type: ClientMsgCode.UPDATE_PRESENCE;
    /**
     * Absence of the `targetActor` field signifies that this is a Partial
     * Presence™ "patch".
     */
    readonly targetActor?: undefined;
    readonly data: Partial<P>;
};
type UpdateStorageClientMsg = {
    readonly type: ClientMsgCode.UPDATE_STORAGE;
    readonly ops: ClientWireOp[];
};
type FetchStorageClientMsg = {
    readonly type: ClientMsgCode.FETCH_STORAGE;
};
type FetchYDocClientMsg = {
    readonly type: ClientMsgCode.FETCH_YDOC;
    readonly vector: string;
    readonly guid?: string;
    readonly v2?: boolean;
};
type UpdateYDocClientMsg = {
    readonly type: ClientMsgCode.UPDATE_YDOC;
    readonly update: string;
    readonly guid?: string;
    readonly v2?: boolean;
};
/** Metadata filter for {@link FetchFeedsClientMsg}. Values are matched as strings. */
type FeedFetchMetadataFilter = Record<string, string>;
/** Metadata for {@link AddFeedClientMsg}. */
type FeedCreateMetadata = Record<string, string | string[]>;
/** Metadata for {@link UpdateFeedClientMsg}. Use `null` to remove a key. */
type FeedUpdateMetadata = Record<string, string | string[] | null>;
type FetchFeedsClientMsg = {
    readonly type: ClientMsgCode.FETCH_FEEDS;
    readonly requestId: string;
    readonly cursor?: string;
    readonly since?: number;
    readonly limit?: number;
    readonly metadata?: FeedFetchMetadataFilter;
};
type FetchFeedMessagesClientMsg = {
    readonly type: ClientMsgCode.FETCH_FEED_MESSAGES;
    readonly requestId: string;
    readonly feedId: string;
    readonly cursor?: string;
    readonly since?: number;
    readonly limit?: number;
};
type AddFeedClientMsg = {
    readonly type: ClientMsgCode.ADD_FEED;
    readonly requestId: string;
    readonly feedId: string;
    readonly metadata?: FeedCreateMetadata;
    readonly createdAt?: number;
};
type UpdateFeedClientMsg = {
    readonly type: ClientMsgCode.UPDATE_FEED;
    readonly requestId: string;
    readonly feedId: string;
    readonly metadata: FeedUpdateMetadata;
};
type DeleteFeedClientMsg = {
    readonly type: ClientMsgCode.DELETE_FEED;
    readonly requestId: string;
    readonly feedId: string;
};
type AddFeedMessageClientMsg = {
    readonly type: ClientMsgCode.ADD_FEED_MESSAGE;
    readonly requestId: string;
    readonly feedId: string;
    readonly data: JsonObject;
    readonly id?: string;
    readonly createdAt?: number;
};
type UpdateFeedMessageClientMsg = {
    readonly type: ClientMsgCode.UPDATE_FEED_MESSAGE;
    readonly requestId: string;
    readonly feedId: string;
    readonly messageId: string;
    readonly data: JsonObject;
    readonly updatedAt?: number;
};
type DeleteFeedMessageClientMsg = {
    readonly type: ClientMsgCode.DELETE_FEED_MESSAGE;
    readonly requestId: string;
    readonly feedId: string;
    readonly messageId: string;
};

type Feed<FM extends Json = Json> = {
    feedId: string;
    metadata: FM;
    createdAt: number;
    updatedAt: number;
};
type FeedMessage<FMD extends Json = Json> = {
    id: string;
    createdAt: number;
    updatedAt: number;
    data: FMD;
};

type ServerMsgCode = (typeof ServerMsgCode)[keyof typeof ServerMsgCode];
declare const ServerMsgCode: Readonly<{
    UPDATE_PRESENCE: 100;
    USER_JOINED: 101;
    USER_LEFT: 102;
    BROADCASTED_EVENT: 103;
    ROOM_STATE: 104;
    STORAGE_STATE_V7: 200;
    STORAGE_CHUNK: 210;
    STORAGE_STREAM_END: 211;
    UPDATE_STORAGE: 201;
    UPDATE_YDOC: 300;
    THREAD_CREATED: 400;
    THREAD_DELETED: 407;
    THREAD_METADATA_UPDATED: 401;
    THREAD_UPDATED: 408;
    COMMENT_CREATED: 402;
    COMMENT_EDITED: 403;
    COMMENT_DELETED: 404;
    COMMENT_REACTION_ADDED: 405;
    COMMENT_REACTION_REMOVED: 406;
    COMMENT_METADATA_UPDATED: 409;
    FEEDS_LIST: 500;
    FEEDS_ADDED: 501;
    FEEDS_UPDATED: 502;
    FEED_DELETED: 503;
    FEED_MESSAGES_LIST: 504;
    FEED_MESSAGES_ADDED: 505;
    FEED_MESSAGES_UPDATED: 506;
    FEED_MESSAGES_DELETED: 507;
    FEED_REQUEST_FAILED: 508;
    REJECT_STORAGE_OP: 299;
}>;
declare namespace ServerMsgCode {
    type UPDATE_PRESENCE = typeof ServerMsgCode.UPDATE_PRESENCE;
    type USER_JOINED = typeof ServerMsgCode.USER_JOINED;
    type USER_LEFT = typeof ServerMsgCode.USER_LEFT;
    type BROADCASTED_EVENT = typeof ServerMsgCode.BROADCASTED_EVENT;
    type ROOM_STATE = typeof ServerMsgCode.ROOM_STATE;
    type STORAGE_STATE_V7 = typeof ServerMsgCode.STORAGE_STATE_V7;
    type STORAGE_CHUNK = typeof ServerMsgCode.STORAGE_CHUNK;
    type STORAGE_STREAM_END = typeof ServerMsgCode.STORAGE_STREAM_END;
    type UPDATE_STORAGE = typeof ServerMsgCode.UPDATE_STORAGE;
    type UPDATE_YDOC = typeof ServerMsgCode.UPDATE_YDOC;
    type THREAD_CREATED = typeof ServerMsgCode.THREAD_CREATED;
    type THREAD_DELETED = typeof ServerMsgCode.THREAD_DELETED;
    type THREAD_METADATA_UPDATED = typeof ServerMsgCode.THREAD_METADATA_UPDATED;
    type THREAD_UPDATED = typeof ServerMsgCode.THREAD_UPDATED;
    type COMMENT_CREATED = typeof ServerMsgCode.COMMENT_CREATED;
    type COMMENT_EDITED = typeof ServerMsgCode.COMMENT_EDITED;
    type COMMENT_DELETED = typeof ServerMsgCode.COMMENT_DELETED;
    type COMMENT_REACTION_ADDED = typeof ServerMsgCode.COMMENT_REACTION_ADDED;
    type COMMENT_REACTION_REMOVED = typeof ServerMsgCode.COMMENT_REACTION_REMOVED;
    type FEEDS_LIST = typeof ServerMsgCode.FEEDS_LIST;
    type FEEDS_ADDED = typeof ServerMsgCode.FEEDS_ADDED;
    type FEEDS_UPDATED = typeof ServerMsgCode.FEEDS_UPDATED;
    type FEED_DELETED = typeof ServerMsgCode.FEED_DELETED;
    type FEED_MESSAGES_LIST = typeof ServerMsgCode.FEED_MESSAGES_LIST;
    type FEED_MESSAGES_ADDED = typeof ServerMsgCode.FEED_MESSAGES_ADDED;
    type FEED_MESSAGES_UPDATED = typeof ServerMsgCode.FEED_MESSAGES_UPDATED;
    type FEED_MESSAGES_DELETED = typeof ServerMsgCode.FEED_MESSAGES_DELETED;
    type FEED_REQUEST_FAILED = typeof ServerMsgCode.FEED_REQUEST_FAILED;
    type COMMENT_METADATA_UPDATED = typeof ServerMsgCode.COMMENT_METADATA_UPDATED;
    type REJECT_STORAGE_OP = typeof ServerMsgCode.REJECT_STORAGE_OP;
}
/**
 * Messages that can be sent from the server to the client.
 */
type ServerMsg<P extends JsonObject, U extends BaseUserMeta, E extends Json> = UpdatePresenceServerMsg<P> | UserJoinServerMsg<U> | UserLeftServerMsg | BroadcastedEventServerMsg<E> | RoomStateServerMsg<U> | StorageStateServerMsg_V7 | StorageChunkServerMsg | StorageEndServerMsg | UpdateStorageServerMsg | YDocUpdateServerMsg | RejectedStorageOpServerMsg | CommentsEventServerMsg | FeedsEventServerMsg;
type CommentsEventServerMsg = ThreadCreatedEvent | ThreadDeletedEvent | ThreadMetadataUpdatedEvent | ThreadUpdatedEvent | CommentCreatedEvent | CommentEditedEvent | CommentDeletedEvent | CommentReactionAdded | CommentReactionRemoved | CommentMetadataUpdatedEvent;
type ThreadCreatedEvent = {
    type: ServerMsgCode.THREAD_CREATED;
    threadId: string;
};
type ThreadDeletedEvent = {
    type: ServerMsgCode.THREAD_DELETED;
    threadId: string;
};
type ThreadMetadataUpdatedEvent = {
    type: ServerMsgCode.THREAD_METADATA_UPDATED;
    threadId: string;
};
type ThreadUpdatedEvent = {
    type: ServerMsgCode.THREAD_UPDATED;
    threadId: string;
};
type CommentCreatedEvent = {
    type: ServerMsgCode.COMMENT_CREATED;
    threadId: string;
    commentId: string;
};
type CommentEditedEvent = {
    type: ServerMsgCode.COMMENT_EDITED;
    threadId: string;
    commentId: string;
};
type CommentDeletedEvent = {
    type: ServerMsgCode.COMMENT_DELETED;
    threadId: string;
    commentId: string;
};
type CommentReactionAdded = {
    type: ServerMsgCode.COMMENT_REACTION_ADDED;
    threadId: string;
    commentId: string;
    emoji: string;
};
type CommentReactionRemoved = {
    type: ServerMsgCode.COMMENT_REACTION_REMOVED;
    threadId: string;
    commentId: string;
    emoji: string;
};
type CommentMetadataUpdatedEvent = {
    type: ServerMsgCode.COMMENT_METADATA_UPDATED;
    threadId: string;
    commentId: string;
};
/**
 * Sent by the WebSocket server and broadcasted to all clients to announce that
 * a User updated their presence. For example, when a user moves their cursor.
 *
 * In most cases, the data payload will only include the fields from the
 * Presence that have been changed since the last announcement. However, after
 * a new user joins a room, a "full presence" will be announced so the newly
 * connected user will get each other's user full presence at least once. In
 * those cases, the `targetActor` field indicates the newly connected client,
 * so all other existing clients can ignore this broadcasted message.
 */
type UpdatePresenceServerMsg<P extends JsonObject> = {
    readonly type: ServerMsgCode.UPDATE_PRESENCE;
    /**
     * The User whose Presence has changed.
     */
    readonly actor: number;
    /**
     * When set, signifies that this is a Full Presence™ update, not a patch.
     *
     * The numeric value itself no longer has specific meaning. Historically,
     * this field was intended so that clients could ignore these broadcasted
     * full presence messages, but it turned out that getting a full presence
     * "keyframe" from time to time was useful.
     *
     * So nowadays, the presence (pun intended) of this `targetActor` field
     * is a backward-compatible way of expressing that the `data` contains
     * all presence fields, and isn't a partial "patch".
     */
    readonly targetActor: number;
    /**
     * The partial or full Presence of a User. If the `targetActor` field is set,
     * this will be the full Presence, otherwise it only contain the fields that
     * have changed since the last broadcast.
     */
    readonly data: P;
} | {
    readonly type: ServerMsgCode.UPDATE_PRESENCE;
    /**
     * The User whose Presence has changed.
     */
    readonly actor: number;
    /**
     * Not set for partial presence updates.
     */
    readonly targetActor?: undefined;
    /**
     * A partial Presence patch to apply to the User. It will only contain the
     * fields that have changed since the last broadcast.
     */
    readonly data: Partial<P>;
};
/**
 * Sent by the WebSocket server and broadcasted to all clients to announce that
 * a new User has joined the Room.
 */
type UserJoinServerMsg<U extends BaseUserMeta> = {
    readonly type: ServerMsgCode.USER_JOINED;
    readonly actor: number;
    /**
     * The id of the User that has been set in the authentication endpoint.
     * Useful to get additional information about the connected user.
     */
    readonly id: U["id"];
    /**
     * Additional user information that has been set in the authentication
     * endpoint.
     */
    readonly info: U["info"];
    /**
     * Informs the client what (public) permissions this (other) User has.
     */
    readonly scopes: string[];
};
/**
 * Sent by the WebSocket server and broadcasted to all clients to announce that
 * a new User has left the Room.
 */
type UserLeftServerMsg = {
    readonly type: ServerMsgCode.USER_LEFT;
    readonly actor: number;
};
/**
 * Sent by the WebSocket server when the ydoc is updated or when requested based on stateVector passed.
 * Contains a base64 encoded update
 */
type YDocUpdateServerMsg = {
    readonly type: ServerMsgCode.UPDATE_YDOC;
    readonly update: string;
    readonly isSync: boolean;
    readonly stateVector: string | null;
    readonly guid?: string;
    readonly v2?: boolean;
    readonly remoteSnapshotHash: string;
};
/**
 * Sent by the WebSocket server and broadcasted to all clients to announce that
 * a User broadcasted an Event to everyone in the Room.
 */
type BroadcastedEventServerMsg<E extends Json> = {
    readonly type: ServerMsgCode.BROADCASTED_EVENT;
    /**
     * The User who broadcast the Event. Absent when this event is broadcast from
     * the REST API in the backend.
     */
    readonly actor: number;
    /**
     * The arbitrary payload of the Event. This can be any JSON value. Clients
     * will have to manually verify/decode this event.
     */
    readonly event: E;
};
/**
 * Sent by the WebSocket server to a single client in response to the client
 * joining the Room, to provide the initial state of the Room. The payload
 * includes a list of all other Users that already are in the Room.
 */
type RoomStateServerMsg<U extends BaseUserMeta> = {
    readonly type: ServerMsgCode.ROOM_STATE;
    /** Informs the client what their actor ID is going to be. */
    readonly actor: number;
    /** Secure nonce for the current session. */
    readonly nonce: string;
    /** Informs the client what permissions the current User (self) has. */
    readonly scopes: string[];
    readonly users: {
        readonly [otherActor: number]: U & {
            scopes: string[];
        };
    };
    /** Metadata sent from the server to the client. */
    readonly meta: JsonObject;
};
/**
 * No longer used as of WS API v8.
 */
type StorageStateServerMsg_V7 = {
    readonly type: ServerMsgCode.STORAGE_STATE_V7;
    readonly items: StorageNode[];
};
/**
 * Sent by the WebSocket server to a single client in response to the client
 * sending a FetchStorageClientMsg message, to provide one chunk of the initial
 * Storage state of the Room.
 *
 * The server will respond with 1+ STORAGE_CHUNK messages, followed by exactly
 * one STORAGE_STREAM_END message to mark the end of the transmission.
 *
 * If the room is using the new storage engine that supports streaming, then
 * potentially multiple chunks might get sent. If the room is using the old
 * storage engine, then all nodes will be sent in a single/large chunk
 * (non-streaming).
 */
type StorageChunkServerMsg = {
    readonly type: ServerMsgCode.STORAGE_CHUNK;
    readonly nodes: CompactNode[];
};
type StorageEndServerMsg = {
    readonly type: ServerMsgCode.STORAGE_STREAM_END;
};
/**
 * Sent by the WebSocket server and broadcasted to all clients to announce that
 * a change occurred in the Storage document.
 *
 * The payload of this message contains a list of Ops (aka incremental
 * mutations to make to the initially loaded document).
 */
type UpdateStorageServerMsg = {
    readonly type: ServerMsgCode.UPDATE_STORAGE;
    readonly ops: ServerWireOp[];
};
/**
 * Sent by the WebSocket server to the client to indicate that certain opIds
 * have been rejected, possibly due to lack of permissions or exceeding
 * a limit.
 */
type RejectedStorageOpServerMsg = {
    readonly type: ServerMsgCode.REJECT_STORAGE_OP;
    readonly opIds: string[];
    readonly reason: string;
};
type FeedsEventServerMsg<FM extends Json = Json, FMD extends Json = Json> = FeedsListServerMsg<FM> | FeedsAddedServerMsg<FM> | FeedsUpdatedServerMsg<FM> | FeedDeletedServerMsg | FeedMessagesListServerMsg<FMD> | FeedMessagesAddedServerMsg<FMD> | FeedMessagesUpdatedServerMsg<FMD> | FeedMessagesDeletedServerMsg | FeedRequestFailedServerMsg;
/** Error codes for {@link FeedRequestFailedServerMsg}. */
declare const FeedRequestErrorCode: {
    readonly INTERNAL: "INTERNAL";
    readonly FEED_ALREADY_EXISTS: "FEED_ALREADY_EXISTS";
    readonly FEED_NOT_FOUND: "FEED_NOT_FOUND";
    readonly FEED_MESSAGE_NOT_FOUND: "FEED_MESSAGE_NOT_FOUND";
};
/** String literals accepted in {@link FeedRequestFailedServerMsg}.code */
type FeedRequestError = (typeof FeedRequestErrorCode)[keyof typeof FeedRequestErrorCode];
/**
 * Sent to the client when a feed mutation referenced by `requestId` failed
 * (e.g. validation or permission error).
 */
type FeedRequestFailedServerMsg = {
    readonly type: ServerMsgCode.FEED_REQUEST_FAILED;
    readonly requestId: string;
    readonly code: string;
    readonly reason?: string;
};
type FeedsListServerMsg<FM extends Json = Json> = {
    readonly type: ServerMsgCode.FEEDS_LIST;
    readonly requestId: string;
    readonly feeds: Feed<FM>[];
    readonly nextCursor?: string;
};
type FeedsAddedServerMsg<FM extends Json = Json> = {
    readonly type: ServerMsgCode.FEEDS_ADDED;
    readonly feeds: Feed<FM>[];
};
type FeedsUpdatedServerMsg<FM extends Json = Json> = {
    readonly type: ServerMsgCode.FEEDS_UPDATED;
    readonly feeds: Feed<FM>[];
};
type FeedDeletedServerMsg = {
    readonly type: ServerMsgCode.FEED_DELETED;
    readonly feedId: string;
};
type FeedMessagesListServerMsg<FMD extends Json = Json> = {
    readonly type: ServerMsgCode.FEED_MESSAGES_LIST;
    readonly requestId: string;
    readonly feedId: string;
    readonly messages: FeedMessage<FMD>[];
    readonly nextCursor?: string;
};
type FeedMessagesAddedServerMsg<FMD extends Json = Json> = {
    readonly type: ServerMsgCode.FEED_MESSAGES_ADDED;
    readonly feedId: string;
    readonly messages: FeedMessage<FMD>[];
};
type FeedMessagesUpdatedServerMsg<FMD extends Json = Json> = {
    readonly type: ServerMsgCode.FEED_MESSAGES_UPDATED;
    readonly feedId: string;
    readonly messages: FeedMessage<FMD>[];
};
type FeedMessagesDeletedServerMsg = {
    readonly type: ServerMsgCode.FEED_MESSAGES_DELETED;
    readonly feedId: string;
    readonly messageIds: readonly string[];
};

type JsonTreeNode = {
    readonly type: "Json";
    readonly id: string;
    readonly key: string;
    readonly payload: Json;
};
type LiveTreeNode<TName extends `Live${string}` = `Live${string}`> = {
    readonly type: TName;
    readonly id: string;
    readonly key: string;
    readonly payload: LsonTreeNode[];
};
type LsonTreeNode = LiveTreeNode | JsonTreeNode;
type UserTreeNode = {
    readonly type: "User";
    readonly id: string;
    readonly key: string;
    readonly payload: {
        readonly connectionId: number;
        readonly id?: string;
        readonly info?: Json;
        readonly presence: JsonObject;
        readonly isReadOnly: boolean;
    };
};
type CustomEventTreeNode = {
    readonly type: "CustomEvent";
    readonly id: string;
    readonly key: string;
    readonly connectionId: number;
    readonly payload: Json;
};
type TreeNode = LsonTreeNode | UserTreeNode | CustomEventTreeNode;

type DevToolsTreeNode_CustomEventTreeNode = CustomEventTreeNode;
type DevToolsTreeNode_JsonTreeNode = JsonTreeNode;
type DevToolsTreeNode_LiveTreeNode<TName extends `Live${string}` = `Live${string}`> = LiveTreeNode<TName>;
type DevToolsTreeNode_LsonTreeNode = LsonTreeNode;
type DevToolsTreeNode_TreeNode = TreeNode;
type DevToolsTreeNode_UserTreeNode = UserTreeNode;
declare namespace DevToolsTreeNode {
  export type { DevToolsTreeNode_CustomEventTreeNode as CustomEventTreeNode, DevToolsTreeNode_JsonTreeNode as JsonTreeNode, DevToolsTreeNode_LiveTreeNode as LiveTreeNode, DevToolsTreeNode_LsonTreeNode as LsonTreeNode, DevToolsTreeNode_TreeNode as TreeNode, DevToolsTreeNode_UserTreeNode as UserTreeNode };
}

type LegacyOthersEvent<P extends JsonObject, U extends BaseUserMeta> = {
    type: "leave";
    user: User<P, U>;
} | {
    type: "enter";
    user: User<P, U>;
} | {
    type: "update";
    user: User<P, U>;
    updates: Partial<P>;
} | {
    type: "reset";
};
type LegacyOthersEventCallback<P extends JsonObject, U extends BaseUserMeta> = (others: readonly User<P, U>[], event: LegacyOthersEvent<P, U>) => void;
type RoomEventMessage<P extends JsonObject, U extends BaseUserMeta, E extends Json> = {
    /**
     * The connection ID of the client that sent the event.
     * If this message was broadcast from the server (via the REST API), then
     * this value will be -1.
     */
    connectionId: number;
    /**
     * The User (from the others list) that sent the event.
     * If this message was broadcast from the server (via the REST API), then
     * this value will be null.
     */
    user: User<P, U> | null;
    event: E;
};
type StorageStatus = "not-loaded" | "loading" | "synchronizing" | "synchronized";
interface History {
    /**
     * Undoes the last operation executed by the current client.
     * It does not impact operations made by other clients.
     *
     * @example
     * room.updatePresence({ selectedId: "xx" }, { addToHistory: true });
     * room.updatePresence({ selectedId: "yy" }, { addToHistory: true });
     * room.history.undo();
     * // room.getPresence() equals { selectedId: "xx" }
     */
    undo: () => void;
    /**
     * Redoes the last operation executed by the current client.
     * It does not impact operations made by other clients.
     *
     * @example
     * room.updatePresence({ selectedId: "xx" }, { addToHistory: true });
     * room.updatePresence({ selectedId: "yy" }, { addToHistory: true });
     * room.history.undo();
     * // room.getPresence() equals { selectedId: "xx" }
     * room.history.redo();
     * // room.getPresence() equals { selectedId: "yy" }
     */
    redo: () => void;
    /**
     * Returns whether there are any operations to undo.
     *
     * @example
     * room.updatePresence({ selectedId: "xx" }, { addToHistory: true });
     * // room.history.canUndo() is true
     * room.history.undo();
     * // room.history.canUndo() is false
     */
    canUndo: () => boolean;
    /**
     * Returns whether there are any operations to redo.
     *
     * @example
     * room.updatePresence({ selectedId: "xx" }, { addToHistory: true });
     * room.history.undo();
     * // room.history.canRedo() is true
     * room.history.redo();
     * // room.history.canRedo() is false
     */
    canRedo: () => boolean;
    /**
     * Clears the undo and redo stacks. This operation cannot be undone ;)
     */
    clear: () => void;
    /**
     * All future modifications made on the Room will be merged together to create a single history item until resume is called.
     *
     * @example
     * room.updatePresence({ cursor: { x: 0, y: 0 } }, { addToHistory: true });
     * room.history.pause();
     * room.updatePresence({ cursor: { x: 1, y: 1 } }, { addToHistory: true });
     * room.updatePresence({ cursor: { x: 2, y: 2 } }, { addToHistory: true });
     * room.history.resume();
     * room.history.undo();
     * // room.getPresence() equals { cursor: { x: 0, y: 0 } }
     */
    pause: () => void;
    /**
     * Resumes history. Modifications made on the Room are not merged into a single history item anymore.
     *
     * @example
     * room.updatePresence({ cursor: { x: 0, y: 0 } }, { addToHistory: true });
     * room.history.pause();
     * room.updatePresence({ cursor: { x: 1, y: 1 } }, { addToHistory: true });
     * room.updatePresence({ cursor: { x: 2, y: 2 } }, { addToHistory: true });
     * room.history.resume();
     * room.history.undo();
     * // room.getPresence() equals { cursor: { x: 0, y: 0 } }
     */
    resume: () => void;
    /**
     * Executes a callback with history tracking temporarily disabled. Any
     * storage mutations made inside the callback will be applied normally
     * but will not appear on the undo/redo stacks.
     *
     * This is useful for background or async writes that should not be
     * undoable, such as writing back results from an AI generation task
     * or reconciling state from an external source.
     *
     * Returns the callback's return value. If the callback throws, the
     * undo/redo stacks are left unchanged (as if the callback never ran).
     *
     * @example
     * room.history.disable(() => {
     *   root.set("generatedText", result);
     * });
     *
     */
    disable: <T>(fn: () => T) => T;
}
type HistoryEvent = {
    canUndo: boolean;
    canRedo: boolean;
};
type BroadcastOptions = {
    /**
     * Whether or not event is queued if the connection is currently closed.
     *
     * ❗ We are not sure if we want to support this option in the future so it might be deprecated to be replaced by something else
     */
    shouldQueueEventIfNotReady: boolean;
};
type SubscribeFn<P extends JsonObject, _TStorage extends LsonObject, U extends BaseUserMeta, E extends Json> = {
    /**
     * Subscribe to the current user presence updates.
     *
     * @param listener the callback that is called every time the current user presence is updated with {@link Room.updatePresence}.
     *
     * @returns Unsubscribe function.
     *
     * @example
     * room.subscribe("my-presence", (presence) => {
     *   // Do something
     * });
     */
    (type: "my-presence", listener: Callback<P>): () => void;
    /**
     * Subscribe to the other users updates.
     *
     * @param listener the callback that is called when a user enters or leaves the room or when a user update its presence.
     *
     * @returns Unsubscribe function.
     *
     * @example
     * room.subscribe("others", (others) => {
     *   // Do something
     * });
     *
     */
    (type: "others", listener: LegacyOthersEventCallback<P, U>): () => void;
    /**
     * Subscribe to events broadcasted by {@link Room.broadcastEvent}
     *
     * @param listener the callback that is called when a user calls {@link Room.broadcastEvent}
     *
     * @returns Unsubscribe function.
     *
     * @example
     * room.subscribe("event", ({ event, connectionId }) => {
     *   // Do something
     * });
     *
     */
    (type: "event", listener: Callback<RoomEventMessage<P, U, E>>): () => void;
    /**
     * Subscribe to errors thrown in the room.
     *
     * @returns Unsubscribe function.
     *
     */
    (type: "error", listener: Callback<LiveblocksError>): () => void;
    /**
     * Subscribe to connection status updates. The callback will be called any
     * time the status changes.
     *
     * @returns Unsubscribe function.
     *
     */
    (type: "status", listener: Callback<Status>): () => void;
    /**
     * Subscribe to the exceptional event where reconnecting to the Liveblocks
     * servers is taking longer than usual. This typically is a sign of a client
     * that has lost internet connectivity.
     *
     * This isn't problematic (because the Liveblocks client is still trying to
     * reconnect), but it's typically a good idea to inform users about it if
     * the connection takes too long to recover.
     */
    (type: "lost-connection", listener: Callback<LostConnectionEvent>): () => void;
    /**
     * Subscribes to changes made on a Live structure. Returns an unsubscribe function.
     *
     * @param callback The callback this called when the Live structure changes.
     *
     * @returns Unsubscribe function.
     *
     * @example
     * const liveMap = new LiveMap();  // Could also be LiveList or LiveObject
     * const unsubscribe = room.subscribe(liveMap, (liveMap) => { });
     * unsubscribe();
     */
    <L extends LiveStructure>(liveStructure: L, callback: (node: L) => void): () => void;
    /**
     * Subscribes to changes made on a Live structure and all the nested data
     * structures. Returns an unsubscribe function. In a future version, we
     * will also expose what exactly changed in the Live structure.
     *
     * @param callback The callback this called when the Live structure, or any
     * of its nested values, changes.
     *
     * @returns Unsubscribe function.
     *
     * @example
     * const liveMap = new LiveMap();  // Could also be LiveList or LiveObject
     * const unsubscribe = room.subscribe(liveMap, (updates) => { }, { isDeep: true });
     * unsubscribe();
     */
    <L extends LiveStructure>(liveStructure: L, callback: StorageCallback, options: {
        isDeep: true;
    }): () => void;
    /**
     * Subscribe to the current user's history changes.
     *
     * @returns Unsubscribe function.
     *
     * @example
     * room.subscribe("history", ({ canUndo, canRedo }) => {
     *   // Do something
     * });
     */
    (type: "history", listener: Callback<HistoryEvent>): () => void;
    /**
     * Subscribe to storage status changes.
     *
     * @returns Unsubscribe function.
     *
     * @example
     * room.subscribe("storage-status", (status) => {
     *   switch(status) {
     *      case "not-loaded":
     *        break;
     *      case "loading":
     *        break;
     *      case "synchronizing":
     *        break;
     *      case "synchronized":
     *        break;
     *      default:
     *        break;
     *   }
     * });
     */
    (type: "storage-status", listener: Callback<StorageStatus>): () => void;
    (type: "comments", listener: Callback<CommentsEventServerMsg>): () => void;
};
type GetThreadsOptions<TM extends BaseMetadata> = {
    cursor?: string;
    query?: {
        resolved?: boolean;
        visibility?: ThreadVisibility;
        subscribed?: boolean;
        metadata?: Partial<QueryMetadata<TM>>;
    };
};
type GetThreadsSinceOptions = {
    since: Date;
    signal?: AbortSignal;
};
type UploadAttachmentOptions = {
    signal?: AbortSignal;
};
type ListTextVersionsSinceOptions = {
    since: Date;
    signal?: AbortSignal;
};
type GetSubscriptionSettingsOptions = {
    signal?: AbortSignal;
};
/**
 * @private Widest-possible Room type, matching _any_ Room instance. Note that
 * this type is different from `Room`-without-type-arguments. That represents
 * a Room instance using globally augmented types only, which is narrower.
 */
type OpaqueRoom = Room<JsonObject, LsonObject, BaseUserMeta, Json, BaseMetadata, BaseMetadata, Json, Json>;
type Room<P extends JsonObject = DP, S extends LsonObject = DS, U extends BaseUserMeta = DU, E extends Json = DE, TM extends BaseMetadata = DTM, CM extends BaseMetadata = DCM, FM extends Json = DFM, FMD extends Json = DFMD> = {
    /**
     * @private
     *
     * Private methods and variables used in the core internals, but as a user
     * of Liveblocks, NEVER USE ANY OF THESE DIRECTLY, because bad things
     * will probably happen if you do.
     */
    readonly [kInternal]: PrivateRoomApi;
    /**
     * The id of the room.
     */
    readonly id: string;
    /**
     * Return the current connection status for this room. Can be used to display
     * a status badge for your Liveblocks connection.
     */
    getStatus(): Status;
    readonly subscribe: SubscribeFn<P, S, U, E>;
    /**
     * Room's history contains functions that let you undo and redo operation made on by the current client on the presence and storage.
     */
    readonly history: History;
    /**
     * Gets the current user.
     * Returns null if not it is not yet connected to the room.
     *
     * @example
     * const user = room.getSelf();
     */
    getSelf(): User<P, U> | null;
    /**
     * Gets the presence of the current user.
     *
     * @example
     * const presence = room.getPresence();
     */
    getPresence(): P;
    /**
     * Gets all the other users in the room.
     *
     * @example
     * const others = room.getOthers();
     */
    getOthers(): readonly User<P, U>[];
    /**
     * Updates the presence of the current user. Only pass the properties you want to update. No need to send the full presence.
     * @param patch A partial object that contains the properties you want to update.
     * @param options Optional object to configure the behavior of updatePresence.
     *
     * @example
     * room.updatePresence({ x: 0 });
     * room.updatePresence({ y: 0 });
     *
     * const presence = room.getPresence();
     * // presence is equivalent to { x: 0, y: 0 }
     */
    updatePresence(patch: Partial<P>, options?: {
        /**
         * Whether or not the presence should have an impact on the undo/redo history.
         */
        addToHistory: boolean;
    }): void;
    /**
     * Sends Yjs document updates to Liveblocks server.
     *
     * @param {string} data the doc update to send to the server, base64 encoded uint8array
     */
    updateYDoc(data: string, guid?: string, isV2?: boolean): void;
    /**
     * Sends a request for the current document from liveblocks server
     */
    fetchYDoc(stateVector: string, guid?: string, isV2?: boolean): void;
    /**
     * Fetches feeds for the room.
     */
    fetchFeeds(options?: {
        cursor?: string;
        since?: number;
        limit?: number;
        metadata?: FeedFetchMetadataFilter;
    }): Promise<{
        feeds: Feed<FM>[];
        nextCursor?: string;
    }>;
    /**
     * Fetches messages for a specific feed.
     */
    fetchFeedMessages(feedId: string, options?: {
        cursor?: string;
        since?: number;
        limit?: number;
    }): Promise<{
        messages: FeedMessage<FMD>[];
        nextCursor?: string;
    }>;
    /**
     * Adds a new feed to the room via WebSocket.
     * Resolves when the server broadcasts the new feed, or rejects on
     * FEED_REQUEST_FAILED (508) or timeout.
     */
    addFeed(feedId: string, options?: {
        metadata?: FeedCreateMetadata;
        createdAt?: number;
    }): Promise<void>;
    /**
     * Updates metadata for an existing feed via WebSocket.
     */
    updateFeed(feedId: string, metadata: FeedUpdateMetadata): Promise<void>;
    /**
     * Deletes a feed via WebSocket.
     */
    deleteFeed(feedId: string): Promise<void>;
    /**
     * Adds a new message to a feed via WebSocket.
     */
    addFeedMessage(feedId: string, data: JsonObject, options?: {
        id?: string;
        createdAt?: number;
    }): Promise<void>;
    /**
     * Updates an existing feed message via WebSocket.
     */
    updateFeedMessage(feedId: string, messageId: string, data: JsonObject, options?: {
        updatedAt?: number;
    }): Promise<void>;
    /**
     * Deletes a feed message via WebSocket.
     */
    deleteFeedMessage(feedId: string, messageId: string): Promise<void>;
    /**
     * Broadcasts an event to other users in the room. Event broadcasted to the room can be listened with {@link Room.subscribe}("event").
     * @param {any} event the event to broadcast. Should be serializable to JSON
     *
     * @example
     * // On client A
     * room.broadcastEvent({ type: "EMOJI", emoji: "🔥" });
     *
     * // On client B
     * room.subscribe("event", ({ event }) => {
     *   if(event.type === "EMOJI") {
     *     // Do something
     *   }
     * });
     */
    broadcastEvent(event: E, options?: BroadcastOptions): void;
    /**
     * Get the room's storage asynchronously.
     * The storage's root is a {@link LiveObject}.
     *
     * @example
     * const { root } = await room.getStorage();
     */
    getStorage(): Promise<{
        root: LiveObject<S>;
    }>;
    /**
     * Get the room's storage synchronously.
     * The storage's root is a LiveObject.
     *
     * @example
     * const root = room.getStorageOrNull();
     */
    getStorageOrNull(): LiveObject<S> | null;
    /**
     * @deprecated Renamed to `Room.getStorageOrNull`. This alias will be
     * removed in the future.
     */
    getStorageSnapshot(): LiveObject<S> | null;
    /**
     * All possible room events, subscribable from a single place.
     *
     * @private These event sources are private for now, but will become public
     * once they're stable.
     */
    readonly events: {
        readonly status: Observable<Status>;
        readonly lostConnection: Observable<LostConnectionEvent>;
        readonly customEvent: Observable<RoomEventMessage<P, U, E>>;
        readonly self: Observable<User<P, U>>;
        readonly myPresence: Observable<P>;
        readonly others: Observable<OthersEvent<P, U>>;
        readonly storageBatch: Observable<StorageUpdate[]>;
        readonly history: Observable<HistoryEvent>;
        /**
         * Subscribe to the storage loaded event. Will fire any time a full Storage
         * copy is downloaded. (This happens after the initial connect, and on
         * every reconnect.)
         */
        readonly storageDidLoad: Observable<void>;
        readonly storageStatus: Observable<StorageStatus>;
        readonly ydoc: Observable<YDocUpdateServerMsg | UpdateYDocClientMsg>;
        readonly comments: Observable<CommentsEventServerMsg>;
        readonly feeds: Observable<FeedsEventServerMsg<FM, FMD>>;
        /**
         * Called right before the room is destroyed. The event cannot be used to
         * prevent the room from being destroyed, only to be informed that this is
         * imminent.
         */
        readonly roomWillDestroy: Observable<void>;
    };
    /**
     * Batches modifications made during the given function.
     * All the modifications are sent to other clients in a single message.
     * All the subscribers are called only after the batch is over.
     * All the modifications are merged in a single history item (undo/redo).
     *
     * @example
     * const { root } = await room.getStorage();
     * room.batch(() => {
     *   root.set("x", 0);
     *   room.updatePresence({ cursor: { x: 100, y: 100 }});
     * });
     */
    batch<T>(fn: () => T): T;
    /**
     * Get the storage status.
     *
     * - `not-loaded`: Initial state when entering the room.
     * - `loading`: Once the storage has been requested via room.getStorage().
     * - `synchronizing`: When some local updates have not been acknowledged by Liveblocks servers.
     * - `synchronized`: Storage is in sync with Liveblocks servers.
     */
    getStorageStatus(): StorageStatus;
    isPresenceReady(): boolean;
    isStorageReady(): boolean;
    /**
     * Returns a Promise that resolves as soon as Presence is available, which
     * happens shortly after the WebSocket connection has been established. Once
     * this happens, `self` and `others` are known and available to use. After
     * awaiting this promise, `.isPresenceReady()` will be guaranteed to be true.
     * Even when calling this function multiple times, it's guaranteed to return
     * the same Promise instance.
     */
    waitUntilPresenceReady(): Promise<void>;
    /**
     * Returns a Promise that resolves as soon as Storage has been loaded and
     * available. After awaiting this promise, `.isStorageReady()` will be
     * guaranteed to be true. Even when calling this function multiple times,
     * it's guaranteed to return the same Promise instance.
     */
    waitUntilStorageReady(): Promise<void>;
    /**
     * Start an attempt to connect the room (aka "enter" it). Calling
     * `.connect()` only has an effect if the room is still in its idle initial
     * state, or the room was explicitly disconnected, or reconnection attempts
     * were stopped (for example, because the user isn't authorized to enter the
     * room). Will be a no-op otherwise.
     */
    connect(): void;
    /**
     * Disconnect the room's connection to the Liveblocks server, if any. Puts
     * the room back into an idle state. It will not do anything until either
     * `.connect()` or `.reconnect()` is called.
     *
     * Only use this API if you wish to connect the room again at a later time.
     * If you want to disconnect the room because you no longer need it, call
     * `.destroy()` instead.
     */
    disconnect(): void;
    /**
     * Reconnect the room to the Liveblocks server by re-establishing a fresh
     * connection. If the room is not connected yet, initiate it.
     */
    reconnect(): void;
    /**
     * Returns the threads within the current room and their associated inbox notifications.
     * It also returns the request date that can be used for subsequent polling.
     *
     * @example
     * const {
     *   threads,
     *   inboxNotifications,
     *   subscriptions,
     *   requestedAt
     * } = await room.getThreads({ query: { resolved: false }});
     */
    getThreads(options?: GetThreadsOptions<TM>): Promise<{
        threads: ThreadData<TM, CM>[];
        inboxNotifications: InboxNotificationData[];
        subscriptions: SubscriptionData[];
        requestedAt: Date;
        nextCursor: string | null;
        permissionHints: Record<string, RoomPermissions>;
    }>;
    /**
     * Returns the updated and deleted threads and their associated inbox notifications and subscriptions since the requested date.
     *
     * @example
     * const result = await room.getThreads();
     * // ... //
     * await room.getThreadsSince({ since: result.requestedAt });
     */
    getThreadsSince(options: GetThreadsSinceOptions): Promise<{
        threads: {
            updated: ThreadData<TM, CM>[];
            deleted: ThreadDeleteInfo[];
        };
        inboxNotifications: {
            updated: InboxNotificationData[];
            deleted: InboxNotificationDeleteInfo[];
        };
        subscriptions: {
            updated: SubscriptionData[];
            deleted: SubscriptionDeleteInfo[];
        };
        requestedAt: Date;
        permissionHints: Record<string, RoomPermissions>;
    }>;
    /**
     * Returns a thread and the associated inbox notification and subscription if it exists.
     *
     * @example
     * const { thread, inboxNotification, subscription } = await room.getThread("th_xxx");
     */
    getThread(threadId: string): Promise<{
        thread?: ThreadData<TM, CM>;
        inboxNotification?: InboxNotificationData;
        subscription?: SubscriptionData;
    }>;
    /**
     * Creates a thread.
     *
     * @example
     * const thread = await room.createThread({
     *   body: {
     *     version: 1,
     *     content: [{ type: "paragraph", children: [{ text: "Hello" }] }],
     *   },
     * })
     */
    createThread(options: {
        threadId?: string;
        commentId?: string;
        visibility?: ThreadVisibility;
        metadata: TM | undefined;
        body: CommentBody;
        commentMetadata?: CM;
        attachmentIds?: string[];
    }): Promise<ThreadData<TM, CM>>;
    /**
     * Deletes a thread.
     *
     * @example
     * await room.deleteThread("th_xxx");
     */
    deleteThread(threadId: string): Promise<void>;
    /**
     * Edits a thread's metadata.
     * To delete an existing metadata property, set its value to `null`.
     *
     * @example
     * await room.editThreadMetadata({ threadId: "th_xxx", metadata: { x: 100, y: 100 } })
     */
    editThreadMetadata(options: {
        metadata: Patchable<TM>;
        threadId: string;
    }): Promise<TM>;
    /**
     * Edits a comment's metadata.
     * To delete an existing metadata property, set its value to `null`.
     *
     * @example
     * await room.editCommentMetadata({ threadId: "th_xxx", commentId: "cm_xxx", metadata: { tag: "important", externalId: 1234 } })
     */
    editCommentMetadata(options: {
        threadId: string;
        commentId: string;
        metadata: Patchable<CM>;
    }): Promise<CM>;
    /**
     * Marks a thread as resolved.
     *
     * @example
     * await room.markThreadAsResolved("th_xxx");
     */
    markThreadAsResolved(threadId: string): Promise<void>;
    /**
     * Marks a thread as unresolved.
     *
     * @example
     * await room.markThreadAsUnresolved("th_xxx");
     */
    markThreadAsUnresolved(threadId: string): Promise<void>;
    /**
     * Subscribes the user to a thread.
     *
     * @example
     * await room.subscribeToThread("th_xxx");
     */
    subscribeToThread(threadId: string): Promise<SubscriptionData>;
    /**
     * Unsubscribes the user from a thread.
     *
     * @example
     * await room.unsubscribeFromThread("th_xxx");
     */
    unsubscribeFromThread(threadId: string): Promise<void>;
    /**
     * Creates a comment.
     *
     * @example
     * await room.createComment({
     *   threadId: "th_xxx",
     *   body: {
     *     version: 1,
     *     content: [{ type: "paragraph", children: [{ text: "Hello" }] }],
     *   },
     * });
     */
    createComment(options: {
        threadId: string;
        commentId?: string;
        body: CommentBody;
        metadata?: CM;
        attachmentIds?: string[];
    }): Promise<CommentData<CM>>;
    /**
     * Edits a comment.
     *
     * @example
     * await room.editComment({
     *   threadId: "th_xxx",
     *   commentId: "cm_xxx"
     *   body: {
     *     version: 1,
     *     content: [{ type: "paragraph", children: [{ text: "Hello" }] }],
     *   },
     * });
     */
    editComment(options: {
        threadId: string;
        commentId: string;
        body: CommentBody;
        metadata?: Patchable<CM>;
        attachmentIds?: string[];
    }): Promise<CommentData<CM>>;
    /**
     * Deletes a comment.
     * If it is the last non-deleted comment, the thread also gets deleted.
     *
     * @example
     * await room.deleteComment({
     *   threadId: "th_xxx",
     *   commentId: "cm_xxx"
     * });
     */
    deleteComment(options: {
        threadId: string;
        commentId: string;
    }): Promise<void>;
    /**
     * Adds a reaction from a comment for the current user.
     *
     * @example
     * await room.addReaction({ threadId: "th_xxx", commentId: "cm_xxx", emoji: "👍" })
     */
    addReaction(options: {
        threadId: string;
        commentId: string;
        emoji: string;
    }): Promise<CommentUserReaction>;
    /**
     * Removes a reaction from a comment.
     *
     * @example
     * await room.removeReaction({ threadId: "th_xxx", commentId: "cm_xxx", emoji: "👍" })
     */
    removeReaction(options: {
        threadId: string;
        commentId: string;
        emoji: string;
    }): Promise<void>;
    /**
     * Creates a local attachment from a file.
     *
     * @example
     * room.prepareAttachment(file);
     */
    prepareAttachment(file: File): CommentLocalAttachment;
    /**
     * Uploads a local attachment.
     *
     * @example
     * const attachment = room.prepareAttachment(file);
     * await room.uploadAttachment(attachment);
     */
    uploadAttachment(attachment: CommentLocalAttachment, options?: UploadAttachmentOptions): Promise<CommentAttachment>;
    /**
     * Returns a presigned URL for an attachment by its ID.
     *
     * @example
     * await room.getAttachmentUrl("at_xxx");
     */
    getAttachmentUrl(attachmentId: string): Promise<string>;
    /**
     * Gets the user's subscription settings for the current room.
     *
     * @example
     * const settings = await room.getSubscriptionSettings();
     */
    getSubscriptionSettings(options?: GetSubscriptionSettingsOptions): Promise<RoomSubscriptionSettings>;
    /**
     * Updates the user's subscription settings for the current room.
     *
     * @example
     * await room.updateSubscriptionSettings({ threads: "replies_and_mentions" });
     */
    updateSubscriptionSettings(settings: Partial<RoomSubscriptionSettings>): Promise<RoomSubscriptionSettings>;
    /**
     * @private
     *
     * Internal use only. Signature might change in the future.
     */
    markInboxNotificationAsRead(notificationId: string): Promise<void>;
};
type YjsSyncStatus = "loading" | "synchronizing" | "synchronized";
/**
 * Interface that @liveblocks/yjs must respect.
 * This interface type is declare in @liveblocks/core, so we don't have to
 * depend on `yjs`. It's only used to determine the API contract between
 * @liveblocks/core and @liveblocks/yjs.
 */
interface IYjsProvider {
    synced: boolean;
    getStatus: () => YjsSyncStatus;
    on(event: "sync", listener: (synced: boolean) => void): void;
    on(event: "status", listener: (status: YjsSyncStatus) => void): void;
    off(event: "sync", listener: (synced: boolean) => void): void;
    off(event: "status", listener: (status: YjsSyncStatus) => void): void;
}
/**
 * A "Sync Source" can be a Storage document, a Yjs document, Comments,
 * Notifications, etc.
 * The Client keeps a registry of all active sync sources, and will use it to
 * determine the global "sync status" for Liveblocks.
 */
interface SyncSource {
    setSyncStatus(status: InternalSyncStatus): void;
    getStatus(): InternalSyncStatus;
    destroy(): void;
}
/**
 * @private
 *
 * Private methods to directly control the underlying state machine for this
 * room. Used in the core internals and for unit testing, but as a user of
 * Liveblocks, NEVER USE ANY OF THESE METHODS DIRECTLY, because bad things
 * will probably happen if you do.
 */
type PrivateRoomApi = {
    presenceBuffer: Json | undefined;
    undoStack: readonly (readonly Readonly<Stackframe<JsonObject>>[])[];
    nodeCount: number;
    getYjsProvider(): IYjsProvider | undefined;
    setYjsProvider(provider: IYjsProvider | undefined): void;
    yjsProviderDidChange: Observable<void>;
    getSelf_forDevTools(): UserTreeNode | null;
    getOthers_forDevTools(): readonly UserTreeNode[];
    reportTextEditor(editor: TextEditorType, rootKey: string): Promise<void>;
    getPermissionMatrix(): PermissionMatrix | undefined;
    createTextMention(mentionId: string, mention: MentionData): Promise<void>;
    deleteTextMention(mentionId: string): Promise<void>;
    listHistoryVersions(): Promise<{
        versions: HistoryVersion[];
        requestedAt: Date;
    }>;
    listHistoryVersionsSince(options: ListTextVersionsSinceOptions): Promise<{
        versions: HistoryVersion[];
        requestedAt: Date;
    }>;
    fetchStorageHistoryVersion(versionId: string): Promise<Response>;
    fetchYjsHistoryVersion(versionId: string): Promise<Response>;
    liveObjectFromNodeStream(nodes: NodeStream): LiveObject<LsonObject>;
    reconcileStorageWithNodes(nodes: NodeStream): void;
    createVersionHistorySnapshot(): Promise<void>;
    deleteHistoryVersion(versionId: string): Promise<void>;
    executeContextualPrompt(options: {
        prompt: string;
        context: ContextualPromptContext;
        previous?: {
            prompt: string;
            response: ContextualPromptResponse;
        };
        signal: AbortSignal;
    }): Promise<string>;
    simulate: {
        explicitClose(event: IWebSocketCloseEvent): void;
        rawSend(data: string): void;
        incomingMessage(data: string): void;
    };
    attachmentUrlsStore: BatchStore<string, string>;
};
type Stackframe<P extends JsonObject> = Op | PresenceStackframe<P>;
type PresenceStackframe<P extends JsonObject> = {
    readonly type: "presence";
    readonly data: P;
};
type StaticSessionInfo = {
    readonly userId?: string;
    readonly userInfo?: IUserInfo;
};
type DynamicSessionInfo = {
    readonly actor: number;
    readonly nonce: string;
    readonly permissionMatrix: PermissionMatrix;
    readonly meta: JsonObject;
};
type Polyfills = {
    atob?: (data: string) => string;
    fetch?: typeof fetch;
    WebSocket?: IWebSocket;
};
/**
 * Makes all tuple positions optional.
 * Example, turns:
 *   [foo: string; bar: number]
 * into:
 *   [foo?: string; bar?: number]
 */
type OptionalTuple<T extends any[]> = {
    [K in keyof T]?: T[K];
};
/**
 * Returns Partial<T> if all fields on C are optional, T otherwise.
 */
type PartialUnless<C, T> = Record<string, never> extends C ? Partial<T> : [
    C
] extends [never] ? Partial<T> : T;
/**
 * Returns OptionalTupleUnless<T> if all fields on C are optional, T otherwise.
 */
type OptionalTupleUnless<C, T extends any[]> = Record<string, never> extends C ? OptionalTuple<T> : [
    C
] extends [never] ? OptionalTuple<T> : T;

type Cursor = Brand<string, "Cursor">;
type ChatId = string;
type MessageId = Brand<`ms_${string}`, "MessageId">;
type CmdId = Brand<string, "CmdId">;
type CopilotId = Brand<`co_${string}`, "CopilotId">;
type ServerAiMsg = ServerCmdResponse | ServerEvent;
type DefineCmd<CmdName extends string, TRequest, TResponse> = [
    Resolve<{
        cmd: CmdName;
        cmdId: CmdId;
    } & TRequest>,
    Resolve<{
        cmd: CmdName;
        cmdId: CmdId;
    } & TResponse>
];
type CommandPair = GetChatsPair | GetOrCreateChatPair | DeleteChatPair | GetMessageTreePair | DeleteMessagePair | ClearChatPair | AskInChatPair | AbortAiPair | SetToolResultPair;
type ServerCmdResponse<T extends CommandPair = CommandPair> = T[1];
type GetChatsResponse = ServerCmdResponse<GetChatsPair>;
type GetOrCreateChatResponse = ServerCmdResponse<GetOrCreateChatPair>;
type DeleteChatResponse = ServerCmdResponse<DeleteChatPair>;
type GetMessageTreeResponse = ServerCmdResponse<GetMessageTreePair>;
type DeleteMessageResponse = ServerCmdResponse<DeleteMessagePair>;
type ClearChatResponse = ServerCmdResponse<ClearChatPair>;
type AskInChatResponse = ServerCmdResponse<AskInChatPair>;
type AbortAiResponse = ServerCmdResponse<AbortAiPair>;
type GetChatsPair = DefineCmd<"get-chats", {
    cursor?: Cursor;
    pageSize?: number;
    query?: {
        metadata?: Record<string, string | string[] | null>;
    };
}, {
    chats: AiChat[];
    nextCursor: Cursor | null;
}>;
type CreateChatOptions = {
    /** A human-friendly title for the chat. If not set, it will get auto-generated after the first response. */
    title?: string;
    /** Arbitrary metadata to record for this chat. This can be later used to filter the list of chats by metadata. */
    metadata?: Record<string, string | string[]>;
};
type AiChatsQuery = {
    metadata?: Record<string, string | string[] | null>;
};
type GetChatsOptions = {
    /**
     * The cursor to use for pagination.
     */
    cursor?: Cursor;
    /**
     * The query (including metadata) to filter chats by. If provided, only chats
     * that match the query will be returned. If not provided, all chats will be returned.
     * @example
     * ```
     * // Filter by presence of metadata values
     * { metadata: { tag: ["urgent"] } }
     *
     * // Filter by absence of metadata key (key must not exist)
     * { metadata: { archived: null } }
     * ```
     */
    query?: AiChatsQuery;
};
type GetOrCreateChatPair = DefineCmd<"get-or-create-chat", {
    id: ChatId;
    options?: CreateChatOptions;
}, {
    chat: AiChat;
}>;
type DeleteChatPair = DefineCmd<"delete-chat", {
    chatId: ChatId;
}, {
    chatId: ChatId;
}>;
type GetMessageTreePair = DefineCmd<"get-message-tree", {
    chatId: ChatId;
}, {
    chat: AiChat;
    messages: AiChatMessage[];
}>;
type DeleteMessagePair = DefineCmd<"delete-message", {
    chatId: ChatId;
    messageId: MessageId;
}, {
    chatId: ChatId;
    messageId: MessageId;
}>;
type ClearChatPair = DefineCmd<"clear-chat", {
    chatId: ChatId;
}, {
    chatId: ChatId;
}>;
type AiGenerationOptions = {
    /**
     * The copilot ID to use for this request. If not provided, a built-in
     * default Copilot will be used instead of one that you configured via the
     * dashboard.
     */
    copilotId?: CopilotId;
    stream?: boolean;
    tools?: AiToolDescription[];
    knowledge?: AiKnowledgeSource[];
    timeout?: number;
};
type AskInChatPair = DefineCmd<"ask-in-chat", {
    chatId: ChatId;
    /** The chat message to use as the source to create the assistant response. */
    sourceMessage: // An existing message ID to reply to
    MessageId | {
        id: MessageId;
        parentMessageId: MessageId | null;
        content: AiUserContentPart[];
    };
    /**
     * The new (!) message ID to output the assistant response into. This ID
     * should be a non-existing message ID, optimistically assigned by the
     * client. The output message will be created as a child to the source
     * message ID.
     */
    targetMessageId: MessageId;
    generationOptions: AiGenerationOptions;
}, {
    sourceMessage?: AiChatMessage;
    targetMessage: AiChatMessage;
}>;
type AbortAiPair = DefineCmd<"abort-ai", {
    messageId: MessageId;
}, {
    ok: true;
}>;
type ToolResultResponse<R extends JsonObject = JsonObject> = Relax<({
    data: R;
    description?: string;
} | {
    error: string;
} | {
    cancel: true | /* reason */ string;
})>;
type NonEmptyString<T extends string> = T & {
    __nonEmpty: true;
};
type RenderableToolResultResponse<R extends JsonObject = JsonObject> = Relax<{
    type: "success";
    data: R;
} | {
    type: "error";
    error: NonEmptyString<string>;
} | {
    type: "cancelled";
    cancelled: true;
    reason?: string;
}>;
type SetToolResultPair = DefineCmd<"set-tool-result", {
    chatId: ChatId;
    messageId: MessageId;
    invocationId: string;
    result: ToolResultResponse;
    generationOptions: AiGenerationOptions;
}, {
    ok: true;
    message: AiChatMessage;
} | {
    ok: false;
}>;
type ServerEvent = RebootedEvent | CmdFailedEvent | WarningServerEvent | ErrorServerEvent | SyncServerEvent | DeltaServerEvent | SettleServerEvent;
type RebootedEvent = {
    event: "rebooted";
};
type CmdFailedEvent = {
    event: "cmd-failed";
    failedCmd: CommandPair[0]["cmd"];
    failedCmdId: CmdId;
    error: string;
};
type WarningServerEvent = {
    event: "warning";
    message: string;
};
type ErrorServerEvent = {
    event: "error";
    error: string;
};
type SyncServerEvent = {
    event: "sync";
    chats?: AiChat[];
    messages?: AiChatMessage[];
    clear?: ChatId[];
    "-chats"?: ChatId[];
    "-messages"?: Pick<AiChatMessage, "id" | "chatId">[];
};
/**
 * A "delta" event is sent to append an incoming delta chunk to an assistant
 * message.
 */
type DeltaServerEvent = {
    event: "delta";
    id: MessageId;
    delta: AiAssistantDeltaUpdate;
};
/**
 * A "settle" event happens after 0 or more "delta" messages, and signifies the
 * end of a stream of updates to a pending assistant message. This event turns
 * a pending message into either a completed or failed assistant message.
 */
type SettleServerEvent = {
    event: "settle";
    message: AiAwaitingToolAssistantMessage | AiCompletedAssistantMessage | AiFailedAssistantMessage;
};
type AiChat = {
    id: ChatId;
    title: string;
    metadata: Record<string, string | string[]>;
    createdAt: ISODateString;
    lastMessageAt?: ISODateString;
    deletedAt?: ISODateString;
};
type AiToolDescription = {
    name: string;
    description?: string;
    parameters: JSONSchema7;
};
type AiToolInvocationPart<A extends JsonObject = JsonObject, R extends JsonObject = JsonObject> = Relax<AiReceivingToolInvocationPart | AiExecutingToolInvocationPart<A> | AiExecutedToolInvocationPart<A, R>>;
type AiReceivingToolInvocationPart = {
    type: "tool-invocation";
    stage: "receiving";
    invocationId: string;
    name: string;
    partialArgs: JsonObject;
};
type AiExecutingToolInvocationPart<A extends JsonObject = JsonObject> = {
    type: "tool-invocation";
    stage: "executing";
    invocationId: string;
    name: string;
    args: A;
};
type AiExecutedToolInvocationPart<A extends JsonObject = JsonObject, R extends JsonObject = JsonObject> = {
    type: "tool-invocation";
    stage: "executed";
    invocationId: string;
    name: string;
    args: A;
    result: RenderableToolResultResponse<R>;
};
type AiTextPart = {
    type: "text";
    text: string;
};
type AiTextDelta = {
    type: "text-delta";
    textDelta: string;
};
type AiReasoningDelta = {
    type: "reasoning-delta";
    textDelta: string;
};
type AiToolInvocationStreamStart = {
    type: "tool-stream";
    invocationId: string;
    name: string;
};
type AiToolInvocationDelta = {
    type: "tool-delta";
    /**
     * The textual delta to be appended to the last tool invocation stream's
     * partial JSON buffer that will eventually become the full `args` value when
     * JSON.parse()'ed.
     */
    delta: string;
};
type AiReasoningPart = {
    type: "reasoning";
    text: string;
    startedAt: ISODateString;
    endedAt?: ISODateString;
};
type AiUploadedImagePart = {
    type: "image";
    id: string;
    name: string;
    size: number;
    mimeType: string;
};
/**
 * Represents a pending or completed knowledge retrieval operation.
 * Since protocol V6.
 */
type AiRetrievalPart = AiKnowledgeRetrievalPart | AiWebRetrievalPart;
type AiKnowledgeRetrievalPart = {
    type: "retrieval";
    id: string;
    startedAt: ISODateString;
    endedAt?: ISODateString;
    kind: "knowledge";
    query: string;
};
type AiWebRetrievalPart = {
    type: "retrieval";
    id: string;
    startedAt: ISODateString;
    endedAt?: ISODateString;
    kind: "web";
    query?: string;
    sources?: Array<{
        type: "url";
        title?: string;
        url: string;
    }>;
};
type AiSourcesPart = {
    type: "sources";
    sources: Array<AiUrlSource>;
};
type AiUrlSource = {
    type: "source";
    kind: "url";
    title: string;
    url: string;
};
type AiUserContentPart = AiTextPart | AiUploadedImagePart;
type AiAssistantContentPart = AiReasoningPart | AiTextPart | AiToolInvocationPart | AiRetrievalPart | AiSourcesPart;
type AiAssistantDeltaUpdate = AiTextDelta | AiReasoningDelta | AiExecutingToolInvocationPart | AiToolInvocationStreamStart | AiToolInvocationDelta | AiRetrievalPart | AiUrlSource;
type AiUserMessage = {
    id: MessageId;
    chatId: ChatId;
    parentId: MessageId | null;
    role: "user";
    content: AiUserContentPart[];
    createdAt: ISODateString;
    deletedAt?: ISODateString;
};
type AiAssistantMessage = Relax<AiGeneratingAssistantMessage | AiAwaitingToolAssistantMessage | AiCompletedAssistantMessage | AiFailedAssistantMessage>;
type AiGeneratingAssistantMessage = {
    id: MessageId;
    chatId: ChatId;
    parentId: MessageId | null;
    role: "assistant";
    createdAt: ISODateString;
    deletedAt?: ISODateString;
    copilotId?: CopilotId;
    status: "generating";
    contentSoFar: AiAssistantContentPart[];
};
type AiAwaitingToolAssistantMessage = {
    id: MessageId;
    chatId: ChatId;
    parentId: MessageId | null;
    role: "assistant";
    createdAt: ISODateString;
    deletedAt?: ISODateString;
    copilotId?: CopilotId;
    status: "awaiting-tool";
    contentSoFar: AiAssistantContentPart[];
};
type AiCompletedAssistantMessage = {
    id: MessageId;
    chatId: ChatId;
    parentId: MessageId | null;
    role: "assistant";
    content: AiAssistantContentPart[];
    createdAt: ISODateString;
    deletedAt?: ISODateString;
    copilotId?: CopilotId;
    status: "completed";
};
type AiFailedAssistantMessage = {
    id: MessageId;
    chatId: ChatId;
    parentId: MessageId | null;
    role: "assistant";
    createdAt: ISODateString;
    deletedAt?: ISODateString;
    copilotId?: CopilotId;
    status: "failed";
    contentSoFar: AiAssistantContentPart[];
    errorReason: string;
};
type AiChatMessage = Relax<AiUserMessage | AiAssistantMessage>;
type AiKnowledgeSource = {
    description: string;
    value: Json;
};

type JSONObjectSchema7 = JSONSchema7 & {
    type: "object";
};
type NoFields = {};
type Infer<T> = T extends JSONSchema7 ? InferFromSchema<T> : never;
type InferAllOf<T extends readonly JSONSchema7[]> = T extends readonly [
    infer U,
    ...infer R
] ? R extends readonly JSONSchema7[] ? Infer<U> & InferAllOf<R> : Infer<U> : unknown;
type InferRequireds<P, R extends readonly string[]> = {
    -readonly [K in keyof P as K extends string ? K extends Extract<K, R[number]> ? K : never : never]: Infer<P[K]>;
};
type InferOptionals<P, R extends readonly string[]> = {
    -readonly [K in keyof P as K extends string ? K extends Extract<K, R[number]> ? never : K : never]?: Infer<P[K]>;
};
type InferBaseObject<T extends JSONObjectSchema7> = T extends {
    properties?: infer P extends Record<string, JSONSchema7>;
    required?: infer R;
} ? InferRequireds<P, R extends readonly string[] ? R : []> & InferOptionals<P, R extends readonly string[] ? R : []> : NoFields;
type InferAdditionals<T extends JSONObjectSchema7> = T extends {
    additionalProperties: false;
} ? NoFields : T extends {
    additionalProperties?: true;
} ? JsonObject : T extends {
    additionalProperties: infer A;
} ? {
    [extra: string]: Infer<A> | undefined;
} : JsonObject;
type InferFromObjectSchema<T extends JSONObjectSchema7> = Resolve<InferBaseObject<T> & InferAdditionals<T>>;
type InferFromArraySchema<T extends JSONSchema7> = T extends {
    items: infer U;
} ? Infer<U>[] : Json[];
type InferFromSchema<T extends JSONSchema7> = JSONSchema7 extends T ? JsonObject : T extends {
    type: "object";
} ? InferFromObjectSchema<T> : T extends {
    type: "array";
} ? InferFromArraySchema<T> : T extends {
    oneOf: readonly (infer U)[];
} ? Infer<U> : T extends {
    anyOf: readonly (infer U)[];
} ? Infer<U> : T extends {
    allOf: readonly JSONSchema7[];
} ? InferAllOf<T["allOf"]> : T extends {
    not: JSONSchema7;
} ? Json : T extends {
    enum: readonly (infer U)[];
} ? U : T extends {
    const: infer C;
} ? C : T extends {
    type: "string";
} ? string : T extends {
    type: "number";
} ? number : T extends {
    type: "integer";
} ? number : T extends {
    type: "boolean";
} ? boolean : T extends {
    type: "null";
} ? null : Json;

type AiToolTypePack<A extends JsonObject = JsonObject, R extends JsonObject = JsonObject> = {
    A: A;
    R: R;
};
type AskUserMessageInChatOptions = Omit<AiGenerationOptions, "tools">;
type SetToolResultOptions = Omit<AiGenerationOptions, "tools" | "knowledge">;
type AiToolInvocationProps<A extends JsonObject, R extends JsonObject> = Resolve<DistributiveOmit<AiToolInvocationPart<A, R>, "type"> & {
    respond: (...args: OptionalTupleUnless<R, [result: ToolResultResponse<R>]>) => void;
    /**
     * These are the inferred types for your tool call which you can pass down
     * to UI components, like so:
     *
     *     <AiTool.Confirmation
     *       types={types}
     *       confirm={
     *         // Now fully type-safe!
     *         (args) => result
     *       } />
     *
     * This will make your AiTool.Confirmation component aware of the types for
     * `args` and `result`.
     */
    types: AiToolTypePack<A, R>;
    [kInternal]: {
        execute: AiToolExecuteCallback<A, R> | undefined;
        messageStatus: AiAssistantMessage["status"];
    };
}>;
type AiOpaqueToolInvocationProps = AiToolInvocationProps<JsonObject, JsonObject>;
type AiToolExecuteContext = {
    name: string;
    invocationId: string;
};
type AiToolExecuteCallback<A extends JsonObject, R extends JsonObject> = (args: A, context: AiToolExecuteContext) => Record<string, never> extends R ? Awaitable<ToolResultResponse<R> | undefined | void> : Awaitable<ToolResultResponse<R>>;
type AiToolDefinition<S extends JSONObjectSchema7, A extends JsonObject, R extends JsonObject> = {
    description?: string;
    parameters: S;
    execute?: AiToolExecuteCallback<A, R>;
    render?: (props: AiToolInvocationProps<A, R>) => unknown;
    enabled?: boolean;
};
type AiOpaqueToolDefinition = AiToolDefinition<JSONObjectSchema7, JsonObject, JsonObject>;
/**
 * Helper function to help infer the types of `args`, `render`, and `result`.
 * This function has no runtime implementation and is only needed to make it
 * possible for TypeScript to infer types.
 */
declare function defineAiTool<R extends JsonObject>(): <const S extends JSONObjectSchema7>(def: AiToolDefinition<S, InferFromSchema<S> extends JsonObject ? InferFromSchema<S> : JsonObject, R>) => AiOpaqueToolDefinition;
type NavigationInfo = {
    /**
     * The message ID of the parent message, or null if there is no parent.
     */
    parent: MessageId | null;
    /**
     * The message ID of the left sibling message, or null if there is no left sibling.
     */
    prev: MessageId | null;
    /**
     * The message ID of the right sibling message, or null if there is no right sibling.
     */
    next: MessageId | null;
};
type WithNavigation<T> = T & {
    navigation: NavigationInfo;
};
type UiChatMessage = WithNavigation<AiChatMessage>;
type AiContext = {
    staticSessionInfoSig: Signal<StaticSessionInfo | null>;
    dynamicSessionInfoSig: Signal<DynamicSessionInfo | null>;
    pendingCmds: Map<CmdId, {
        resolve: (value: ServerAiMsg) => void;
        reject: (reason: unknown) => void;
    }>;
    chatsStore: ReturnType<typeof createStore_forUserAiChats>;
    toolsStore: ReturnType<typeof createStore_forTools>;
    messagesStore: ReturnType<typeof createStore_forChatMessages>;
    knowledgeStore: ReturnType<typeof createStore_forKnowledge>;
};
type LayerKey = Brand<string, "LayerKey">;
declare class KnowledgeStack {
    #private;
    constructor();
    registerLayer(uniqueLayerId: string): LayerKey;
    deregisterLayer(layerKey: LayerKey): void;
    get(): AiKnowledgeSource[];
    invalidate(): void;
    updateKnowledge(layerKey: LayerKey, key: string, data: AiKnowledgeSource | null): void;
}
declare function createStore_forKnowledge(): {
    getKnowledgeStack: (chatId?: string) => KnowledgeStack;
    getKnowledgeForChat: (chatId: string) => AiKnowledgeSource[];
};
declare function createStore_forTools(): {
    getToolDescriptions: (chatId: string) => AiToolDescription[];
    getToolΣ: (name: string, chatId?: string) => DerivedSignal<AiOpaqueToolDefinition | undefined>;
    registerTool: (name: string, tool: AiOpaqueToolDefinition, chatId?: string) => () => void;
};
declare function createStore_forChatMessages(toolsStore: ReturnType<typeof createStore_forTools>, setToolResultFn: (chatId: string, messageId: MessageId, invocationId: string, result: ToolResultResponse, options?: SetToolResultOptions) => Promise<void>): {
    getMessageById: (messageId: MessageId) => AiChatMessage | undefined;
    getChatMessagesForBranchΣ: (chatId: string, branch?: MessageId) => DerivedSignal<UiChatMessage[]>;
    getLastUsedCopilotId: (chatId: string) => CopilotId | undefined;
    createOptimistically: {
        (chatId: string, role: "user", parentId: MessageId | null, content: AiUserContentPart[]): MessageId;
        (chatId: string, role: "assistant", parentId: MessageId | null, copilotId?: CopilotId): MessageId;
    };
    upsert: (message: AiChatMessage) => void;
    upsertMany: (messages: AiChatMessage[]) => void;
    remove: (chatId: string, messageId: MessageId) => void;
    removeByChatId: (chatId: string) => void;
    addDelta: (messageId: MessageId, delta: AiAssistantDeltaUpdate) => void;
    failAllPending: () => void;
    markMine(messageId: MessageId): void;
    /**
     * Iterates over all my auto-executing messages.
     *
     * These are messages that match all these conditions:
     * - The message is an assistant message
     * - The message is owned by this client ("mine")
     * - The message is currently in "awaiting-tool" status
     * - The message has at least one tool invocation in "executing" stage
     * - The tool invocation has an execute() function defined
     */
    getAutoExecutingMessageIds(): Iterable<MessageId>;
};
declare function createStore_forUserAiChats(): {
    getChatById: (chatId: string) => AiChat | undefined;
    findMany: (query: AiChatsQuery) => AiChat[];
    upsert: (chat: AiChat) => void;
    upsertMany: (chats: AiChat[]) => void;
    markDeleted: (chatId: string) => void;
};
/** @private This API will change, and is not considered stable. DO NOT RELY on it. */
type Ai = {
    [kInternal]: {
        context: AiContext;
    };
    connectInitially: () => void;
    disconnect: () => void;
    getStatus: () => Status;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    getChats: (options?: GetChatsOptions) => Promise<GetChatsResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    getOrCreateChat: (
    /** A unique identifier for the chat. */
    chatId: string, options?: CreateChatOptions) => Promise<GetOrCreateChatResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    deleteChat: (chatId: string) => Promise<DeleteChatResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    getMessageTree: (chatId: string) => Promise<GetMessageTreeResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    deleteMessage: (chatId: string, messageId: MessageId) => Promise<DeleteMessageResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    clearChat: (chatId: string) => Promise<ClearChatResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    askUserMessageInChat: (chatId: string, userMessage: MessageId | {
        id: MessageId;
        parentMessageId: MessageId | null;
        content: AiUserContentPart[];
    }, targetMessageId: MessageId, options?: AskUserMessageInChatOptions) => Promise<AskInChatResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    abort: (messageId: MessageId) => Promise<AbortAiResponse>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    setToolResult: (chatId: string, messageId: MessageId, invocationId: string, result: ToolResultResponse, options?: SetToolResultOptions) => Promise<void>;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    signals: {
        getChatMessagesForBranchΣ(chatId: string, branch?: MessageId): DerivedSignal<UiChatMessage[]>;
        getToolΣ(name: string, chatId?: string): DerivedSignal<AiOpaqueToolDefinition | undefined>;
        statusΣ: Signal<Status>;
    };
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    getChatById: (chatId: string) => AiChat | undefined;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    queryChats: (query: AiChatsQuery) => AiChat[];
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    getLastUsedCopilotId: (chatId: string) => CopilotId | undefined;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    registerKnowledgeLayer: (uniqueLayerId: string, chatId?: string) => {
        layerKey: LayerKey;
        deregister: () => void;
    };
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    updateKnowledge: (layerKey: LayerKey, data: AiKnowledgeSource, key?: string, chatId?: string) => void;
    /** @private This API will change, and is not considered stable. DO NOT RELY on it. */
    registerTool: (name: string, tool: AiOpaqueToolDefinition, chatId?: string) => () => void;
};

type CommentBodyParagraphElementArgs = {
    /**
     * The paragraph element.
     */
    element: CommentBodyParagraph;
    /**
     * The text content of the paragraph.
     */
    children: string;
};
type CommentBodyTextElementArgs = {
    /**
     * The text element.
     */
    element: CommentBodyText;
};
type CommentBodyLinkElementArgs = {
    /**
     * The link element.
     */
    element: CommentBodyLink;
    /**
     * The absolute URL of the link.
     */
    href: string;
};
type CommentBodyMentionElementArgs<U extends BaseUserMeta = DU> = {
    /**
     * The mention element.
     */
    element: CommentBodyMention;
    /**
     * The mention's user info, if the mention is a user mention and the `resolveUsers` option was provided.
     */
    user?: U["info"];
    /**
     * The mention's group info, if the mention is a group mention and the `resolveGroupsInfo` option was provided.
     */
    group?: DGI;
};
type StringifyCommentBodyElements<U extends BaseUserMeta = DU> = {
    /**
     * The element used to display paragraphs.
     */
    paragraph: (args: CommentBodyParagraphElementArgs, index: number) => string;
    /**
     * The element used to display text elements.
     */
    text: (args: CommentBodyTextElementArgs, index: number) => string;
    /**
     * The element used to display links.
     */
    link: (args: CommentBodyLinkElementArgs, index: number) => string;
    /**
     * The element used to display mentions.
     */
    mention: (args: CommentBodyMentionElementArgs<U>, index: number) => string;
};
type StringifyCommentBodyOptions<U extends BaseUserMeta = DU> = {
    /**
     * Which format to convert the comment to.
     */
    format?: "plain" | "html" | "markdown";
    /**
     * The elements used to customize the resulting string. Each element has
     * priority over the defaults inherited from the `format` option.
     */
    elements?: Partial<StringifyCommentBodyElements<U>>;
    /**
     * The separator used between paragraphs.
     */
    separator?: string;
    /**
     * A function that returns user info from user IDs.
     * You should return a list of user objects of the same size, in the same order.
     */
    resolveUsers?: (args: ResolveUsersArgs) => Awaitable<(U["info"] | undefined)[] | undefined>;
    /**
     * A function that returns group info from group IDs.
     * You should return a list of group info objects of the same size, in the same order.
     */
    resolveGroupsInfo?: (args: ResolveGroupsInfoArgs) => Awaitable<(DGI | undefined)[] | undefined>;
};
declare function isCommentBodyText(element: CommentBodyElement): element is CommentBodyText;
declare function isCommentBodyMention(element: CommentBodyElement): element is CommentBodyMention;
declare function isCommentBodyLink(element: CommentBodyElement): element is CommentBodyLink;
/**
 * Get an array of all mentions in a `CommentBody`.
 *
 * Narrow results with an optional predicate, e.g.
 * `(mention) => mention.kind === "user"` to only get user mentions.
 */
declare function getMentionsFromCommentBody(body: CommentBody, predicate?: (mention: CommentBodyMention) => boolean): CommentBodyMention[];
declare function resolveMentionsInCommentBody<U extends BaseUserMeta>(body: CommentBody, resolveUsers?: (args: ResolveUsersArgs) => Awaitable<(U["info"] | undefined)[] | undefined>, resolveGroupsInfo?: (args: ResolveGroupsInfoArgs) => Awaitable<(DGI | undefined)[] | undefined>): Promise<{
    users: Map<string, U["info"]>;
    groups: Map<string, DGI>;
}>;
declare function htmlSafe(value: string): HtmlSafeString;
declare class HtmlSafeString {
    #private;
    constructor(strings: readonly string[], values: readonly (string | string[] | HtmlSafeString | HtmlSafeString[])[]);
    toString(): string;
}
/**
 * Build an HTML string from a template literal where the values are escaped.
 * Nested calls are supported and won't be escaped.
 */
declare function html(strings: TemplateStringsArray, ...values: (string | string[] | HtmlSafeString | HtmlSafeString[])[]): string;
/**
 * Convert a `CommentBody` into either a plain string,
 * Markdown, HTML, or a custom format.
 */
declare function stringifyCommentBody(body: CommentBody, options?: StringifyCommentBodyOptions<BaseUserMeta>): Promise<string>;

declare const MENTION_CHARACTER = "@";

/**
 * Converts a plain comment data object (usually returned by the API) to a comment data object that can be used by the client.
 * This is necessary because the plain data object stores dates as ISO strings, but the client expects them as Date objects.
 * @param data The plain comment data object (usually returned by the API)
 * @returns The rich comment data object that can be used by the client.
 */
declare function convertToCommentData<CM extends BaseMetadata>(data: CommentDataPlain<CM>): CommentData<CM>;
/**
 * Converts a plain thread data object (usually returned by the API) to a thread data object that can be used by the client.
 * This is necessary because the plain data object stores dates as ISO strings, but the client expects them as Date objects.
 * @param data The plain thread data object (usually returned by the API)
 * @returns The rich thread data object that can be used by the client.
 */
declare function convertToThreadData<TM extends BaseMetadata, CM extends BaseMetadata>(data: ThreadDataPlain<TM, CM>): ThreadData<TM, CM>;
/**
 * Converts a plain comment reaction object (usually returned by the API) to a comment reaction object that can be used by the client.
 * This is necessary because the plain data object stores dates as ISO strings, but the client expects them as Date objects.
 * @param data The plain comment reaction object (usually returned by the API)
 * @returns The rich comment reaction object that can be used by the client.
 */
declare function convertToCommentUserReaction(data: CommentUserReactionPlain): CommentUserReaction;
/**
 * Converts a plain inbox notification data object (usually returned by the API) to an inbox notification data object that can be used by the client.
 * This is necessary because the plain data object stores dates as ISO strings, but the client expects them as Date objects.
 * @param data The plain inbox notification data object (usually returned by the API)
 * @returns The rich inbox notification data object that can be used by the client.
 */
declare function convertToInboxNotificationData(data: InboxNotificationDataPlain): InboxNotificationData;
/**
 * Converts a plain subscription data object (usually returned by the API) to a subscription data object that can be used by the client.
 * This is necessary because the plain data object stores dates as ISO strings, but the client expects them as Date objects.
 * @param data The plain subscription data object (usually returned by the API)
 * @returns The rich subscription data object that can be used by the client.
 */
declare function convertToSubscriptionData(data: SubscriptionDataPlain): SubscriptionData;
/**
 * Converts a plain user subscription data object (usually returned by the API) to a user subscription data object that can be used by the client.
 * This is necessary because the plain data object stores dates as ISO strings, but the client expects them as Date objects.
 * @param data The plain user subscription data object (usually returned by the API)
 * @returns The rich user subscription data object that can be used by the client.
 */
declare function convertToUserSubscriptionData(data: UserSubscriptionDataPlain): UserSubscriptionData;
declare function convertToGroupData(data: GroupDataPlain): GroupData;

/**
 * Reverse lookup table for all child nodes (= list of SerializedCrdt values)
 * by their parent node's IDs.
 */
type ParentToChildNodeMap = Map<string, // Parent's node ID
ChildStorageNode[]>;

declare function isLiveNode(value: unknown): value is LiveNode;
declare function cloneLson<L extends Lson | undefined>(value: L): L;

/**
 * "Plain LSON" is a JSON-based format that's used when serializing Live structures
 * to send them over HTTP (e.g. in the API endpoint to let users upload their initial
 * Room storage, in the API endpoint to fetch a Room's storage, ...).
 *
 * In the client, you would typically create LSON values using:
 *
 *    new LiveObject({ x: 0, y: 0 })
 *
 * But over HTTP, this has to be serialized somehow. The "Plain LSON" format
 * is what's used in the POST /init-storage-new endpoint, to allow users to
 * control which parts of their data structure should be considered "Live"
 * objects, and which parts are "normal" objects.
 *
 * So if they have a structure like:
 *
 *    { x: 0, y: 0 }
 *
 * And want to make it a Live object, they can serialize it by wrapping it in
 * a special "annotation":
 *
 *    {
 *      "liveblocksType": "LiveObject",
 *      "data": { x: 0, y: 0 },
 *    }
 *
 * This "Plain LSON" data format defines exactly those wrappings.
 *
 * To summarize:
 *
 *   LSON value            |  Plain LSON equivalent
 *   ----------------------+----------------------------------------------
 *   42                    |  42
 *   [1, 2, 3]             |  [1, 2, 3]
 *   { x: 0, y: 0 }        |  { x: 0, y: 0 }
 *   ----------------------+----------------------------------------------
 *   new LiveList(...)     |  { liveblocksType: "LiveList",   data: ... }
 *   new LiveMap(...)      |  { liveblocksType: "LiveMap",    data: ... }
 *   new LiveObject(...)   |  { liveblocksType: "LiveObject", data: ... }
 *
 */

type PlainLsonFields = Record<string, PlainLson>;
type PlainLsonObject = {
    liveblocksType: "LiveObject";
    data: PlainLsonFields;
};
type PlainLsonMap = {
    liveblocksType: "LiveMap";
    data: PlainLsonFields;
};
type PlainLsonList = {
    liveblocksType: "LiveList";
    data: PlainLson[];
};
type PlainLson = PlainLsonObject | PlainLsonMap | PlainLsonList | Json;

/**
 * Returns PlainLson for a given Json or LiveStructure, suitable for calling the storage init api
 */
declare function toPlainLson(lson: Lson): PlainLson;

/**
 * Like `new AbortController()`, but where the result can be unpacked
 * safely, i.e. `const { signal, abort } = makeAbortController()`.
 *
 * This unpacking is unsafe to do with a regular `AbortController` because
 * the `abort` method is not bound to the controller instance.
 *
 * In addition to this, you can also pass an optional (external)
 * AbortSignal to "wrap", in which case the returned signal will be in
 * aborted state when either the signal is aborted externally or
 * internally.
 */
declare function makeAbortController(externalSignal?: AbortSignal): {
    signal: AbortSignal;
    abort: (reason?: unknown) => void;
};

/**
 * Helper function that can be used to implement exhaustive switch statements
 * with TypeScript. Example usage:
 *
 *    type Fruit = "🍎" | "🍌";
 *
 *    switch (fruit) {
 *      case "🍎":
 *      case "🍌":
 *        return doSomething();
 *
 *      default:
 *        return assertNever(fruit, "Unknown fruit");
 *    }
 *
 * If now the Fruit union is extended (i.e. add "🍒"), TypeScript will catch
 * this *statically*, rather than at runtime, and force you to handle the
 * 🍒 case.
 */
declare function assertNever(_value: never, errmsg: string): never;
/**
 * Asserts that a certain condition holds. If it does not hold, will throw
 * a runtime error in dev mode.
 *
 * In production, nothing is asserted and this acts as a no-op.
 */
declare function assert(condition: boolean, errmsg: string): asserts condition;
/**
 * Asserts that a given value is non-nullable. This is similar to TypeScript's
 * `!` operator, but will throw an error at runtime (dev-mode only) indicating
 * an incorrect assumption.
 *
 * Instead of:
 *
 *     foo!.bar
 *
 * Use:
 *
 *     nn(foo).bar
 *
 */
declare function nn<T>(value: T, errmsg?: string): NonNullable<T>;

declare class HttpError extends Error {
    response: Response;
    details?: JsonObject;
    private constructor();
    static fromResponse(response: Response): Promise<HttpError>;
    /**
     * Convenience accessor for response.status.
     */
    get status(): number;
}
/**
 * Wraps a promise factory. Will create promises until one succeeds. If
 * a promise rejects, it will retry calling the factory for at most `maxTries`
 * times. Between each attempt, it will inject a a backoff delay (in millis)
 * from the given array. If the array contains fewer items then `maxTries`,
 * then the last backoff number will be used indefinitely.
 *
 * If the last attempt is rejected too, the returned promise will fail too.
 *
 * @param promiseFn The promise factory to execute
 * @param maxTries The number of total tries (must be >=1)
 * @param backoff An array of timings to inject between each promise attempt
 * @param shouldStopRetrying An optional function to not auto-retry on certain errors
 */
declare function autoRetry<T>(promiseFn: () => Promise<T>, maxTries: number, backoff: number[], shouldStopRetrying?: (err: any) => boolean): Promise<T>;

declare function chunk<T>(array: T[], size: number): T[][];

type ControlledPromise<T> = {
    promise: Promise<T>;
    resolve: (value: T) => void;
    reject: (reason: unknown) => void;
};
/**
 * Drop-in replacement for the ES2024 Promise.withResolvers() API.
 */
declare function Promise_withResolvers<T>(): ControlledPromise<T>;

declare function createThreadId(): string;
declare function createCommentId(): string;
declare function createCommentAttachmentId(): string;
declare function createInboxNotificationId(): string;

/**
 * Like ES6 map, but takes a default (factory) function which will be used
 * to create entries for missing keys on the fly.
 *
 * Useful for code like:
 *
 *   const map = new DefaultMap(() => []);
 *   map.getOrCreate('foo').push('hello');
 *   map.getOrCreate('foo').push('world');
 *   map.getOrCreate('foo')
 *   // ['hello', 'world']
 *
 */
declare class DefaultMap<K, V> extends Map<K, V> {
    #private;
    /**
     * If the default function is not provided to the constructor, it has to be
     * provided in each .getOrCreate() call individually.
     */
    constructor(defaultFn?: (key: K) => V, entries?: readonly (readonly [K, V])[] | null);
    /**
     * Gets the value at the given key, or creates it.
     *
     * Difference from normal Map: if the key does not exist, it will be created
     * on the fly using the factory function, and that value will get returned
     * instead of `undefined`.
     */
    getOrCreate(key: K, defaultFn?: (key: K) => V): V;
}

/**
 * Displays a deprecation warning in the dev console. Only in dev mode, and
 * only once per message/key. In production, this is a no-op.
 */
declare function deprecate(message: string, key?: string): void;
/**
 * Conditionally displays a deprecation warning in the dev
 * console if the first argument is truthy. Only in dev mode, and
 * only once per message/key. In production, this is a no-op.
 */
declare function deprecateIf(condition: unknown, message: string, key?: string): void;
/**
 * Throws a deprecation error in the dev console.
 *
 * Only triggers in dev mode. In production, this is a no-op.
 */
declare function throwUsageError(message: string): void;
/**
 * Conditionally throws a usage error in the dev console if the first argument
 * is truthy. Use this to "escalate" usage patterns that in previous versions
 * we already warned about with deprecation warnings.
 *
 * Only has effect in dev mode. In production, this is a no-op.
 */
declare function errorIf(condition: unknown, message: string): void;

/**
 * A Deque (= Double Ended Queue) is like a stack, but where elements can be
 * efficiently pushed or popped from either side.
 *
 * The following calls are equivalent with arrays (but insertions are O(n)
 * instead of O(n^2)):
 *
 * - deque.push(1)              ⇔  array.push(1)
 * - deque.push([1, 2, 3])      ⇔  array.push(1, 2, 3)
 * - deque.push(many)           ⇔  array.push(...many)
 * - deque.pop()                ⇔  array.pop()
 *
 * - deque.pushLeft(1)          ⇔  array.unshift(1)
 * - deque.pushLeft([1, 2, 3])  ⇔  array.unshift(1, 2, 3)
 * - deque.pushLeft(many)       ⇔  array.unshift(...many)
 * - deque.popLeft()            ⇔  array.shift()
 *
 */
declare class Deque<T> {
    #private;
    constructor();
    get length(): number;
    [Symbol.iterator](): IterableIterator<T>;
    push(value: T | readonly T[]): void;
    pop(): T | undefined;
    pushLeft(value: T | readonly T[]): void;
    popLeft(): T | undefined;
}

declare const warn: (message: string, ...args: readonly unknown[]) => void;
declare const error: (message: string, ...args: readonly unknown[]) => void;
declare const warnWithTitle: (title: string, message: string, ...args: readonly unknown[]) => void;
declare const errorWithTitle: (title: string, message: string, ...args: readonly unknown[]) => void;

declare const fancyConsole_error: typeof error;
declare const fancyConsole_errorWithTitle: typeof errorWithTitle;
declare const fancyConsole_warn: typeof warn;
declare const fancyConsole_warnWithTitle: typeof warnWithTitle;
declare namespace fancyConsole {
  export { fancyConsole_error as error, fancyConsole_errorWithTitle as errorWithTitle, fancyConsole_warn as warn, fancyConsole_warnWithTitle as warnWithTitle };
}

/**
 * Freezes the given argument, but only in development builds. In production
 * builds, this is a no-op for performance reasons.
 */
declare const freeze: typeof Object.freeze;

declare function isPlainObject(blob: unknown): blob is {
    [key: string]: unknown;
};
/**
 * Check if value is of shape { startsWith: string }
 */
declare function isStartsWithOperator(blob: unknown): blob is {
    startsWith: string;
};
declare function isNumberOperator(blob: unknown): blob is {
    lt?: number;
    gt?: number;
    lte?: number;
    gte?: number;
};

declare const nanoid: (t?: number) => string;

/**
 * Converts an object to a query string
 * Example:
 * ```ts
 * const query = objectToQuery({
 *   resolved: true,
 *   subscribed: true,
 *   metadata: {
 *     status: "open",
 *     priority: 3,
 *     org: {
 *       startsWith: "liveblocks:",
 *     },
 *     posX: {
 *       gt: 100,
 *       lt: 200,
 *     },
 *     posY: {
 *       gte: 50,
 *       lte: 300,
 *     },
 *   },
 * });
 *
 * console.log(query);
 * // resolved:true AND subscribed:true AND metadata["status"]:open AND metadata["priority"]:3 AND metadata["org"]^"liveblocks:" AND metadata["posX"]>100 AND metadata["posX"]<200 AND metadata["posY"]>=50 AND metadata["posY"]<=300
 * ```
 */
type SimpleFilterValue = string | number | boolean | null;
type OperatorFilterValue = {
    startsWith: string;
    gt?: never;
    lt?: never;
    gte?: never;
    lte?: never;
} | {
    lt?: number;
    gt?: number;
    lte?: number;
    gte?: number;
    startsWith?: never;
};
type FilterValue = SimpleFilterValue | OperatorFilterValue;
declare function objectToQuery(obj: {
    [key: string]: FilterValue | {
        [key: string]: FilterValue | undefined;
    } | undefined;
}): string;

type Poller = {
    /**
     * Increments the subscriber count for this poller. If it becomes > 0, the
     * poller will be enabled.
     */
    inc(): void;
    /**
     * Decrements the subscriber count for this poller. If it becomes = 0, the
     * poller will be disabled.
     */
    dec(): void;
    /**
     * Polls immediately only if it has been more than `maxStaleTimeMs` milliseconds since
     * the last poll and no poll is currently in progress. After polling, schedules
     * the next poll at the regular interval.
     */
    pollNowIfStale(): void;
    /**
     * Marks the poller as stale. This can be used to force the next call
     * to `.pollNowIfStale()` to poll immediately.
     */
    markAsStale(): void;
};
/**
 * Makes a poller that will call `await callback()` at the desired interval (in
 * millis).
 *
 * The poller has only three public APIs, all side effects:
 * - .inc(): void
 * - .dec(): void
 * - .pollNowIfStale(): void
 *
 * It has the following behaviors/guarantees:
 * - Performing a "poll" literally means calling the provided callback (and
 *   awaiting it)
 * - It will only ever start polling if .inc() was called (more often than .dec())
 * - It will not _immediately_ poll if .inc() is called. The first poll
 *   can be expected no earlier than the specified interval.
 * - If .dec() is called as many times as .inc(), it stops the poller. This
 *   means that any next poll will get unscheduled. If .dev() is called while
 *   a poll is ongoing, it will still finish that poll, but after that stop
 *   further polling.
 * - If the document's visibility state changes to hidden (tab is moved to the
 *   background), polling will be paused until the document's made visible again
 * - If the document becomes visible again, the poller will:
 *   - Still do nothing if the poller isn't enabled
 *   - Still do nothing if the poller is enabled, but the last time a poll
 *     happened recently enough (= less than the maxStaleTimeMs, which defaults
 *     to infinity)
 *   - Trigger a poll right away otherwise. If an existing poll was already
 *     scheduled, think of it as if this future poll is "earlied" and just
 *     happening right now instead
 */
declare function makePoller(callback: (signal: AbortSignal) => Promise<void> | void, intervalMs: number, options?: {
    maxStaleTimeMs?: number;
}): Poller;

/**
 * Positions, aka the Pos type, are efficient encodings of "positions" in
 * a list, using the following printable subset of the ASCII alphabet:
 *
 *    !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
 *   ^                                                                                             ^
 *   Lowest digit                                                                      Highest digit
 *
 * Each Pos is a sequence of characters from the above alphabet, conceptually
 * codifying a floating point number 0 < n < 1. For example, the string "31007"
 * would be used to represent the number 0.31007, except that this
 * representation uses base 96.
 *
 *   0 ≃ ' '  (lowest digit)
 *   1 ≃ '!'
 *   2 ≃ '"'
 *   ...
 *   9 ≃ '~'  (highest digit)
 *
 * So think:
 *   '!'    ≃ 0.1
 *   '"'    ≃ 0.2
 *   '!"~'  ≃ 0.129
 *
 * Three rules:
 * - All "characters" in the string should be valid digits (from the above
 *   alphabet)
 * - The value 0.0 is not a valid Pos value
 * - A Pos cannot have trailing "zeroes"
 *
 * This representation has the following benefits:
 *
 * 1. It's always possible to get a number that lies before, after, or between
 *    two arbitrary Pos values.
 * 2. Pos values can be compared using normal string comparison.
 *
 * Some examples:
 * - '!'  < '"'   (like how .1  < .2)
 * - '!'  < '~'   (like how .1  < .9)
 * - '!!' < '!~'  (like how .11 < .19)
 * - '~!' < '~~'  (like how .91 < .99)
 * - '~'  < '~!'  (like how .9  < .91)
 * - '!!' < '!O'  (like how .1  < .5)
 * - '!O' < '!~'  (like how .5  < .9)
 *
 */

/**
 * A valid/verified "position" string. These values are used as "parentKey"s by
 * LiveList children, and define their relative ordering.
 */
type Pos = Brand<string, "Pos">;
/**
 * Given two positions, returns the position value that lies in the middle.
 * When given only a high bound, computes the canonical position "before" it.
 * When given only a low bound, computes the canonical position "after" it.
 * When given no bounds at all, returns the "first" canonical position.
 */
declare function makePosition(x?: Pos, y?: Pos): Pos;
/**
 * Checks that a str is a valid Pos, and converts it to the nearest valid one
 * if not.
 */
declare function asPos(str: string): Pos;

/**
 * Shallowly compares two given values.
 *
 * - Two simple values are considered equal if they're strictly equal
 * - Two arrays are considered equal if their members are strictly equal
 * - Two objects are considered equal if their values are strictly equal
 *
 * Testing goes one level deep.
 */
declare function shallow(a: unknown, b: unknown): boolean;
/**
 * Two-level deep shallow check.
 * Useful for checking equality of { isLoading: false, myData: [ ... ] } like
 * data structures, where you want to do a shallow comparison on the "data"
 * key.
 *
 * NOTE: Works on objects only, not on arrays!
 */
declare function shallow2(a: unknown, b: unknown): boolean;

/**
 * A datastructure to keep elements in ascending order, as defined by the "less
 * than" function you provide. The elements will be ordered according to
 * whatever you define as the "less than" for this element type, so that every
 * element is less than its successor in the list.
 *
 * const sorted = SortedList.from(
 *   [{ id: 4 }, { id: 1 }, { id: 9 }, { id: 4 }],
 *   (a, b) => a.id < b.id)
 * )
 * sorted.add({ id: 5 })
 * sorted.remove({ id: 4 })  // Assuming it's the same obj ref!
 *
 * Array.from(sorted)
 * [{ id: 1 }, { id: 4 }, { id: 5 }, { id: 9 }])
 */
declare class SortedList<T> {
    #private;
    private constructor();
    /**
     * Creates an empty SortedList with the given "less than" function.
     */
    static with<T>(lt: (a: T, b: T) => boolean): SortedList<T>;
    static from<T>(arr: readonly T[], lt: (a: T, b: T) => boolean): SortedList<T>;
    static fromAlreadySorted<T>(alreadySorted: T[], lt: (a: T, b: T) => boolean): SortedList<T>;
    /**
     * Clones the sorted list to a new instance.
     */
    clone(): SortedList<T>;
    /**
     * Adds a new item to the sorted list, such that it remains sorted.
     * Returns the index where the item was inserted.
     */
    add(value: T): number;
    /**
     * Removes all values from the sorted list, making it empty again.
     * Returns whether the list was mutated or not.
     */
    clear(): boolean;
    /**
     * Removes the first value matching the predicate.
     * Returns whether the list was mutated or not.
     */
    removeBy(predicate: (item: T) => boolean, limit?: number): boolean;
    /**
     * Removes the given value from the sorted list, if it exists. The given
     * value must be `===` to one of the list items. Only the first entry will be
     * removed if the element exists in the sorted list multiple times.
     *
     * Returns whether the list was mutated or not.
     */
    remove(value: T): boolean;
    /**
     * Removes the item at the given index.
     * Returns the removed item, or undefined if index is out of bounds.
     */
    removeAt(index: number): T | undefined;
    /**
     * Repositions an item to maintain sorted order after its sort key has
     * been mutated in-place. For example:
     *
     *   const item = sorted.at(3);
     *   item.updatedAt = new Date();  // mutate the item's sort key in-place
     *   sorted.reposition(item);      // restore sorted order
     *
     * Returns the new index of the item. Throws if the item is not in the list.
     *
     * Semantically equivalent to remove(value) + add(value), but optimized
     * to avoid array shifting when the item only moves a short distance.
     */
    reposition(value: T): number;
    at(index: number): T | undefined;
    get length(): number;
    /**
     * Whether the given value is present, by identity. O(log n) plus the length
     * of any run of items that share its sort key (normally 1). Bisects on the
     * value's own key, so it only finds values sitting at their sorted position,
     * which is true for any item currently in the list.
     */
    includes(value: T): boolean;
    filter(predicate: (value: T) => boolean): IterableIterator<T>;
    findAllRight(predicate: (value: T, index: number) => unknown): IterableIterator<T>;
    [Symbol.iterator](): IterableIterator<T>;
    iterReversed(): IterableIterator<T>;
    /** Finds the leftmost item that matches the predicate. */
    find(predicate: (value: T, index: number) => unknown, start?: number): T | undefined;
    /** Finds the leftmost index that matches the predicate. */
    findIndex(predicate: (value: T, index: number) => unknown, start?: number): number;
    /** Finds the rightmost item that matches the predicate. */
    findRight(predicate: (value: T, index: number) => unknown, start?: number): T | undefined;
    /** Finds the rightmost index that matches the predicate. */
    findIndexRight(predicate: (value: T, index: number) => unknown, start?: number): number;
    get rawArray(): readonly T[];
}

/**
 * Like JSON.stringify(), but using stable (sorted) object key order, so that
 * it returns the same value for the same keys, no matter their order.
 */
declare function stableStringify(value: unknown): string;

type QueryParams = Record<string, string | number | null | undefined> | URLSearchParams;
/**
 * Concatenates a path to an existing URL.
 */
declare function urljoin(baseUrl: string | URL, path: string, params?: QueryParams): string;
/**
 * A string that is guaranteed to be URL safe (where all arguments are properly
 * encoded), only obtainable as the result of using `url` template strings.
 */
type URLSafeString = Brand<string, "URLSafeString">;
/**
 * Builds a URL where each "hole" in the template string will automatically be
 * encodeURIComponent()-escaped, so it's impossible to build invalid URLs.
 */
declare function url(strings: TemplateStringsArray, ...values: string[]): URLSafeString;
/**
 * Sanitize a URL (normalize www URLs, handle relative URLs, prevent XSS attacks, etc.)
 *
 * Accepted URLs:
 * - Absolute URLs with an http or https protocol (e.g. https://liveblocks.io)
 * - Absolute URLs with a `www` prefix (e.g. www.liveblocks.io)
 * - Relative URLs (e.g. /path/to/page)
 * - Hash-only URLs (e.g. #hash)
 *
 * The presence/absence of trailing slashes is preserved.
 * Rejected URLs are returned as `null`.
 */
declare function sanitizeUrl(url: string): string | null;
/**
 * Construct a URL with optional parameters and hash.
 */
declare function generateUrl(url: string, params?: Record<string, string | number | undefined>, hash?: string): string;
declare function isUrl(string: string): boolean;

/**
 * Emit a warning only once.
 *
 * Only has effect in dev mode. In production, this is a no-op.
 */
declare function warnOnce(message: string, key?: string): void;
/**
 * Emit a warning only once if a condition is met.
 *
 * Only has effect in dev mode. In production, this is a no-op.
 */
declare function warnOnceIf(condition: boolean | (() => boolean), message: string, key?: string): void;

/**
 * Represents an indefinitely deep arbitrary immutable data
 * structure.
 */
type Immutable = Scalar | ImmutableList | ImmutableObject | ImmutableMap;
type Scalar = string | number | boolean | null;
type ImmutableList = readonly Immutable[];
type ImmutableObject = {
    readonly [key: string]: Immutable | undefined;
};
type ImmutableMap = ReadonlyMap<string, Immutable>;

/**
 * Definition of all messages the Panel can send to the Client.
 */
type PanelToClientMessage = 
/**
 * Initial message from the panel to the client, used for two purposes.
 * 1. First, it’s eavesdropped by the background script, which uses this
 *    message to register a "port", which sets up a channel for two-way
 *    communication between panel and client for the remainder of the time.
 * 2. It signifies to the client that the devpanel is listening.
 */
{
    msg: "connect";
}
/**
 * Expresses to the client that the devtool is interested in
 * receiving the "sync stream" for the room. The sync stream
 * that follows is an initial "full sync", followed by many
 * "partial" syncs, happening for every update.
 */
 | {
    msg: "room::subscribe";
    roomId: string;
}
/**
 * Expresses to the client that the devtool no longer is
 * interested in the "sync stream" for a room, for example,
 * because the devtools panel is closed, or if it switched to
 * a different room.
 */
 | {
    msg: "room::unsubscribe";
    roomId: string;
};
/**
 * Definition of all messages the Client can send to the Panel.
 */
type ClientToPanelMessage = 
/**
 * Initial message sent by the client to test if a dev panel is listening.
 * This is necessary in cases where the dev panel is already opened and
 * listened, before the client is loaded. If the panel receives this message,
 * it will replay its initial "connect" message, which triggers the loading
 * of the two-way connection.
 */
{
    msg: "wake-up-devtools";
}
/**
 * Sent when a new room is available for the dev panel to track and watch.
 * Sent by the client as soon as the room is attempted to be entered. This
 * happens _before_ the actual connection to the room server is established,
 * meaning the room is visible to the devtools even while it is connecting.
 */
 | {
    msg: "room::available";
    roomId: string;
    clientVersion: string;
}
/**
 * Sent when a room is left and the client loses track of the room instance.
 */
 | {
    msg: "room::unavailable";
    roomId: string;
}
/**
 * Sent initially, to synchronize the entire current state of the room.
 */
 | {
    msg: "room::sync::full";
    roomId: string;
    status: Status;
    storage: readonly LsonTreeNode[] | null;
    me: UserTreeNode | null;
    others: readonly UserTreeNode[];
}
/**
 * Sent whenever something about the internals of a room changes.
 */
 | {
    msg: "room::sync::partial";
    roomId: string;
    status?: Status;
    storage?: readonly LsonTreeNode[];
    me?: UserTreeNode;
    others?: readonly UserTreeNode[];
}
/**
 * Sent whenever an user room event is emitted in the room.
 */
 | {
    msg: "room::events::custom-event";
    roomId: string;
    event: CustomEventTreeNode;
}
/**
 * Sent whenever the ydoc is updated
 */
 | {
    msg: "room::sync::ydoc";
    roomId: string;
    update: YDocUpdateServerMsg | UpdateYDocClientMsg;
};
type FullPanelToClientMessage = PanelToClientMessage & {
    source: "liveblocks-devtools-panel";
    tabId: number;
};
type FullClientToPanelMessage = ClientToPanelMessage & {
    source: "liveblocks-devtools-client";
};

type protocol_ClientToPanelMessage = ClientToPanelMessage;
type protocol_FullClientToPanelMessage = FullClientToPanelMessage;
type protocol_FullPanelToClientMessage = FullPanelToClientMessage;
type protocol_PanelToClientMessage = PanelToClientMessage;
declare namespace protocol {
  export type { protocol_ClientToPanelMessage as ClientToPanelMessage, protocol_FullClientToPanelMessage as FullClientToPanelMessage, protocol_FullPanelToClientMessage as FullPanelToClientMessage, protocol_PanelToClientMessage as PanelToClientMessage };
}

/**
 * Helper type to help users adopt to Lson types from interface definitions.
 * You should only use this to wrap interfaces you don't control. For more
 * information, see
 * https://liveblocks.io/docs/guides/limits#lson-constraint-and-interfaces
 */
type EnsureJson<T> = T extends Json ? T : T extends Array<infer I> ? (EnsureJson<I>)[] : [
    unknown
] extends [T] ? Json | undefined : T extends Date ? string : T extends (...args: any[]) => any ? never : {
    [K in keyof T as EnsureJson<T[K]> extends never ? never : K]: EnsureJson<T[K]>;
};

export { type AccessLevel, type ActivityData, type AiAssistantContentPart, type AiAssistantMessage, type AiChat, type AiChatMessage, type AiChatsQuery, type AiKnowledgeRetrievalPart, type AiKnowledgeSource, type AiOpaqueToolDefinition, type AiOpaqueToolInvocationProps, type AiReasoningPart, type AiRetrievalPart, type AiSourcesPart, type AiTextPart, type AiToolDefinition, type AiToolExecuteCallback, type AiToolExecuteContext, type AiToolInvocationPart, type AiToolInvocationProps, type AiToolTypePack, type AiUrlSource, type AiUserMessage, type AiWebRetrievalPart, type AsyncError, type AsyncLoading, type AsyncResult, type AsyncSuccess, type Awaitable, type BaseActivitiesData, type BaseAuthResult, type BaseGroupInfo, type BaseMetadata, type BaseRoomInfo, type BaseUserMeta, type Brand, type BroadcastEventClientMsg, type BroadcastOptions, type BroadcastedEventServerMsg, type ChildStorageNode, type Client, type ClientMsg, ClientMsgCode, type ClientOptions, type ClientWireOp, type CommentAttachment, type CommentBody, type CommentBodyBlockElement, type CommentBodyElement, type CommentBodyInlineElement, type CommentBodyLink, type CommentBodyLinkElementArgs, type CommentBodyMention, type CommentBodyMentionElementArgs, type CommentBodyParagraph, type CommentBodyParagraphElementArgs, type CommentBodyText, type CommentBodyTextElementArgs, type CommentData, type CommentDataPlain, type CommentLocalAttachment, type CommentMixedAttachment, type CommentReaction, type CommentUserReaction, type CommentUserReactionPlain, type CommentsEventServerMsg, type CompactChildNode, type CompactListNode, type CompactMapNode, type CompactNode, type CompactObjectNode, type CompactRegisterNode, type CompactRootNode, type ContextualPromptContext, type ContextualPromptResponse, type CopilotId, CrdtType, type CreateListOp, type CreateManagedPoolOptions, type CreateMapOp, type CreateObjectOp, type CreateOp, type CreateRegisterOp, type Cursor, type CustomAuthenticationResult, type DAD, type DCM, type DE, type DFM, type DFMD, type DGI, type DP, type DRI, type DS, type DTM, type DU, DefaultMap, type Delegates, type DeleteCrdtOp, type DeleteObjectKeyOp, Deque, DerivedSignal, DevToolsTreeNode as DevTools, protocol as DevToolsMsg, type DistributiveOmit, type EnsureJson, type EnterOptions, type EventSource, type Feed, type FeedCreateMetadata, type FeedDeletedServerMsg, type FeedFetchMetadataFilter, type FeedMessage, type FeedMessagesAddedServerMsg, type FeedMessagesDeletedServerMsg, type FeedMessagesListServerMsg, type FeedMessagesUpdatedServerMsg, type FeedRequestError, FeedRequestErrorCode, type FeedRequestFailedServerMsg, type FeedUpdateMetadata, type FeedsAddedServerMsg, type FeedsEventServerMsg, type FeedsListServerMsg, type FeedsUpdatedServerMsg, type FetchStorageClientMsg, type FetchYDocClientMsg, type GetThreadsOptions, type GroupData, type GroupDataPlain, type GroupMemberData, type GroupMentionData, type GroupScopes, type HasOpId, type History, type HistoryVersion, HttpError, type ISODateString, type ISignal, type IUserInfo, type IWebSocket, type IWebSocketCloseEvent, type IWebSocketEvent, type IWebSocketInstance, type IWebSocketMessageEvent, type IYjsProvider, type IgnoredOp, type Immutable, type InboxNotificationCustomData, type InboxNotificationCustomDataPlain, type InboxNotificationData, type InboxNotificationDataPlain, type InboxNotificationDeleteInfo, type InboxNotificationTextMentionData, type InboxNotificationTextMentionDataPlain, type InboxNotificationThreadData, type InboxNotificationThreadDataPlain, type InferFromSchema, type Json, type JsonArray, type JsonObject, type JsonScalar, type KDAD, type LayerKey, type ListStorageNode, LiveList, type LiveListUpdate, LiveMap, type LiveMapUpdate, type LiveNode, LiveObject, type LiveObjectUpdate, type LiveStructure, LiveblocksError, type LiveblocksErrorContext, type LostConnectionEvent, type Lson, type LsonObject, MENTION_CHARACTER, type ManagedPool, type MapStorageNode, type MentionData, type MessageId, MutableSignal, type NoInfr, type NodeMap, type NodeStream, type NotificationChannel, type NotificationChannelSettings, type NotificationKind, type NotificationSettings, type NotificationSettingsPlain, type ObjectStorageNode, type Observable, type Op, OpCode, type OpaqueClient, type OpaqueRoom, type OptionalTupleUnless, type OthersEvent, type ParentToChildNodeMap, type PartialNotificationSettings, type PartialUnless, type Patchable, Permission, type PermissionMatrix, type PermissionResources, type PlainLson, type PlainLsonFields, type PlainLsonList, type PlainLsonMap, type PlainLsonObject, type Poller, type PrivateClientApi, type PrivateRoomApi, Promise_withResolvers, type QueryMetadata, type QueryParams, type ReadonlyJson, type ReadonlyJsonObject, type RegisterStorageNode, type RejectedStorageOpServerMsg, type Relax, type RenderableToolResultResponse, type RequiredAccessLevel, type Resolve, type ResolveGroupsInfoArgs, type ResolveMentionSuggestionsArgs, type ResolveRoomsInfoArgs, type ResolveUsersArgs, type Room, type RoomAccesses, type RoomEventMessage, type RoomPermissions, type RoomStateServerMsg, type RoomSubscriptionSettings, type RootStorageNode, type SearchCommentsResult, type SerializedChild, type SerializedCrdt, type SerializedList, type SerializedMap, type SerializedObject, type SerializedRegister, type SerializedRootObject, type ServerMsg, ServerMsgCode, type ServerWireOp, type SetParentKeyOp, Signal, type SignalType, SortedList, type Status, type StorageChunkServerMsg, type StorageNode, type StorageStatus, type StorageUpdate, type StringifyCommentBodyElements, type StringifyCommentBodyOptions, type SubscriptionData, type SubscriptionDataPlain, type SubscriptionDeleteInfo, type SubscriptionDeleteInfoPlain, type SubscriptionKey, type SyncConfig, type SyncMode, type SyncSource, type SyncStatus, TextEditorType, type ThreadData, type ThreadDataPlain, type ThreadDataWithDeleteInfo, type ThreadDeleteInfo, type ThreadVisibility, type ToJson, type ToolResultResponse, type URLSafeString, type UnsubscribeCallback, type UpdateObjectOp, type UpdatePresenceClientMsg, type UpdatePresenceServerMsg, type UpdateRoomAccesses, type UpdateStorageClientMsg, type UpdateStorageServerMsg, type UpdateYDocClientMsg, type UploadAttachmentOptions, type UrlMetadata, type User, type UserJoinServerMsg, type UserLeftServerMsg, type UserMentionData, type UserRoomSubscriptionSettings, type UserSubscriptionData, type UserSubscriptionDataPlain, WebsocketCloseCodes, type WithNavigation, type WithOptional, type WithRequired, type YDocUpdateServerMsg, type YjsSyncStatus, asPos, assert, assertNever, autoRetry, b64decode, batch, checkBounds, chunk, cloneLson, compactNodesToNodeStream, compactObject, fancyConsole as console, convertToCommentData, convertToCommentUserReaction, convertToGroupData, convertToInboxNotificationData, convertToSubscriptionData, convertToThreadData, convertToUserSubscriptionData, createClient, createCommentAttachmentId, createCommentId, createInboxNotificationId, createManagedPool, createNotificationSettings, createThreadId, deepLiveify, defineAiTool, deprecate, deprecateIf, detectDupes, entries, errorIf, findLastIndex, freeze, generateUrl, getMentionsFromCommentBody, getSubscriptionKey, hasPermissionAccess, html, htmlSafe, isCommentBodyLink, isCommentBodyMention, isCommentBodyText, isJsonArray, isJsonObject, isJsonScalar, isListStorageNode, isLiveNode, isMapStorageNode, isNotificationChannelEnabled, isNumberOperator, isObjectStorageNode, isPlainObject, isRegisterStorageNode, isRootStorageNode, isStartsWithOperator, isUrl, kInternal, keys, makeAbortController, makeEventSource, makePoller, makePosition, mapValues, memoizeOnSuccess, mergeRoomPermissionScopes, nanoid, nn, nodeStreamToCompactNodes, normalizeRoomAccesses, normalizeRoomPermissions, normalizeUpdateRoomAccesses, objectToQuery, patchNotificationSettings, permissionMatrixFromScopes, raise, resolveMentionsInCommentBody, sanitizeUrl, shallow, shallow2, stableStringify, stringifyCommentBody, throwUsageError, toPlainLson, tryParseJson, url, urljoin, validatePermissionsSet, wait, warnOnce, warnOnceIf, withTimeout };
