/**
 * A message that is sent between peers
 */
interface Message {
    /**
     * Message type
     */
    type: string;
    /**
     * Message version
     */
    version: string;
}
/**
 * A functional message wrapper that contains the message payload and the sender and receiver ids.
 * It is used to route messages between peers.
 */
interface RoutedMessage<M extends Message> {
    /**
     * Id of the sender peer
     */
    from: string;
    /**
     * Id of the receiver peer(s). If it is an empty array, the message is broadcasted to all peers.
     */
    to: string[];
    /**
     * Message payload - an actual {@link Message} subtype that is sent between peers
     */
    payload: M;
}
/**
 * A service message that is sent when an error occurs during the message processing.
 * It will be routed back to the sender of the message when parsing/validation exception occurs
 * on the receiver side.
 */
interface ErrorMessage extends Message {
    /**
     * Message type
     */
    type: 'error';
    /**
     * Message version
     */
    version: '1.0';
    /**
     * Error message
     */
    error: string;
    /**
     * The original message that caused the error
     */
    message: RoutedMessage<Message>;
}
/**
 * A service message that is sent when a new {@link MessagePeerType} connects to the network.
 * It contains the list of new peers and messages they can receive.
 */
interface ConnectMessage extends Message {
    /**
     * Message type
     */
    type: 'connect';
    /**
     * Message version
     */
    version: '1.0';
    /**
     * List of new peers and messages they receive
     */
    knownPeers: Map<string, Message[]>;
    /**
     * List of peers ids that have just connected to the network
     */
    connected: string[];
}
/**
 * A service message that is sent when a {@link MessagePeerType} disconnects from the network.
 * It contains the list of peers that are unreachable from the disconnected peer.
 */
interface DisconnectMessage extends Message {
    /**
     * Message type
     */
    type: 'disconnect';
    /**
     * Message version
     */
    version: '1.0';
    /**
     * Id of the disconnected peer
     */
    disconnected: string;
    /**
     * List of peers that are not reachable anymore from the disconnected peer
     */
    unreachable: string[];
}
/**
 * A service message that is sent when an {@link Endpoint} connects to another `Endpoint.
 * This message stays internal and not propagated to the end user.
 */
interface HandshakeMessage extends Message {
    /**
     * Message type
     */
    type: 'handshake';
    /**
     * Message version
     */
    version: '1.0';
    /**
     * Id of the endpoint that receives the message
     */
    endpointId: string;
    /**
     * Id of the endpoint that has sent the message
     */
    remoteId: string;
    /**
     * List of known peers and messages that remote endpoint knows about.
     */
    knownPeers: Map<string, Message[]>;
}
/**
 * A service message that is sent by a {@link MessagePeerType} that starts accepting new message types.
 */
interface DeclareMessages extends Message {
    /**
     * Message type
     */
    type: 'declare_messages';
    /**
     * Message version
     */
    version: '1.0';
    /**
     * List of message types (type, version) that the sender of the messages accepts
     */
    messages: Message[];
}
/**
 * A type that represents a service message that is used by the library to communicate between peers,
 * maintain the network and handle errors.
 */
type ServiceMessage = HandshakeMessage | DeclareMessages | ErrorMessage | DisconnectMessage | ConnectMessage;
/**
 * Checks if a particular message is a {@link ServiceMessage}, like `connect`, `disconnect`, `handshake`, etc.
 *
 * ```ts
 * // Example of usage
 * if (isServiceMessage(message)) {
 *  switch (message.type) {
 *    case 'connect':
 *    // handle connect message with narrowed type
 *    break;
 *  }
 * }
 * ```
 * @param message - Message to check
 */
declare function isServiceMessage(message: Message): message is ServiceMessage;

/**
 * Error class for errors related to message processing
 * @param message - The error message
 * @param messageObject - The message that caused the error
 */
declare class MessageError extends Error {
    messageObject: RoutedMessage<Message>;
    constructor(messageObject: RoutedMessage<Message>, message: string);
}

declare global {
    interface SymbolConstructor {
        readonly observable: symbol;
    }
}
type SubscriberFunction<T> = (value: T) => void;
interface SubscriberObject<T> {
    next?: SubscriberFunction<T>;
}
type Subscriber<T> = SubscriberObject<T> | SubscriberFunction<T> | null | undefined;
interface Subscription {
    unsubscribe(): void;
}
/**
 * An Observable-compatible interface for consuming messages.
 *
 * It can also be used with rxjs, for example:
 *
 * ```ts
 * import { from } from 'rxjs';
 *
 * const peer = new MessagePeer({...});
 * const observable = from(peer.messages);
 * ```
 */
interface Subscribable<T> {
    [Symbol.observable](): Subscribable<T>;
    subscribe(subscriber: Subscriber<T>): Subscription;
}

/**
 * Options to establish peer connection.
 * Useful to connect to a different window or an iframe.
 */
interface PeerConnectionOptions {
    /**
     * Window object peer should connect to or listen connections on.
     * Default is `window` object of the current environment.
     * It can be used to connect to a different window or an iframe.
     */
    window?: Window;
    /**
     * Origin of the window object peer should connect to or listen connections on.
     * Default is `window.origin` of the current environment.
     * Used to verify that the connection is established with the correct window from different origin.
     */
    origin?: string;
}
/**
 * Options to pass when sending a message to the network.
 */
interface PeerSendOptions {
    /**
     * List of peer ids that should receive the message.
     * If not provided or an empty array, the message will be received by all connected peers.
     */
    to?: string | string[];
}
/**
 * Options to pass when creating a new peer.
 * The only required option is the unique `id` of the peer.
 */
interface PeerOptions {
    /**
     * Unique identifier of the peer on the network it will be connected to
     */
    id: string;
    /**
     * List of known messages that the peer can receive, it can be amended later with {@link MessagePeerType#registerMessage()} method
     */
    knownMessages?: Message[];
}
/**
 * A type that represents a peer in the network that can send and receive messages.
 */
interface MessagePeerType<M extends Message> {
    /**
     * Unique identifier of the peer on the network
     */
    get id(): string;
    /**
     * List of other known peers on the network and types of messages they can receive
     */
    get knownPeers(): Map<string, Message[]>;
    /**
     * A {@link Subscribable} that emits a message received by the peer
     * To handle {@link ServiceMessage} like `connect` or `disconnect`, use the {@link MessagePeer#serviceMessages} stream.
     */
    get messages(): Subscribable<RoutedMessage<M>>;
    /**
     * A {@link Subscribable} that emits a {@link ServiceMessage} is received by the peer.
     */
    get serviceMessages(): Subscribable<RoutedMessage<ServiceMessage>>;
    /**
     * A {@link Subscribable} that emits an error occurs during received message processing locally.
     * The {@link ErrorMessage} will be sent back to the sender of the message automatically
     */
    get errors(): Subscribable<MessageError>;
    /**
     * Connects to the peer with the given id
     * @param peerId - id of the peer we're connecting to
     * @param options - additional {@link PeerConnectionOptions} options for the connection
     * @returns a function that can be called to disconnect this peer from the network
     */
    connect(peerId: string, options?: PeerConnectionOptions): Promise<() => void>;
    /**
     * Listens for incoming connections from the peer with the given id
     * @param peerId - id of the peer we're listening for
     * @param options - additional {@link PeerConnectionOptions} for the connection
     * @returns a function that can be called to disconnect this peer from the network*
     */
    listen(peerId: string, options?: PeerConnectionOptions): Promise<() => void>;
    /**
     * Sends a message to the network.
     *
     * Message must be a serializable object supported by `structuredClone()` algorithm.
     * Message send can happen synchronously or asynchronously depending on how the peer is connected.
     * By default, the message is broadcast to all connected peers.
     *
     * If you need to send a message to a specific peer only, use {@link PeerSendOptions#to} option.
     *
     * If there are no connected peers, the message will be queued until
     * the first connection is established.
     *
     * @param message - message to send
     * @param options - additional {@link PeerSendOptions} for the message delivery
     */
    send(message: M, options?: PeerSendOptions): void;
    /**
     * Registers a new message that this peer can receive.
     * @param message - message type and version to register
     */
    registerMessage(message: Message): void;
    /**
     * Disconnects this peer from the network completely or from a particular peer.
     * If no `peerId` is provided, disconnects completely from the network.
     * @param peerId - a specific peer id to disconnect from
     */
    disconnect(peerId?: string): void;
}
/**
 * Default message peer that can send and receive messages to/from other peers in the same document
 * or across different windows or iframes.
 *
 * Messages will be sent in a synchronous way if both peers are in the same window.
 * Otherwise, a `MessageChannel` will be established to send messages between different windows.
 *
 * ```ts
 * // Simple example of creating two peers and sending messages between them
 * const one = new MessagePeer({ id: 'one', onMessage: (message) => {} });
 * const two = new MessagePeer({ id: 'two', onMessage: (message) => {} });
 *
 * // connecting two peers
 * one.listen('two');
 * two.connect('one');
 *
 * // sending messages
 * one.send({ type: 'ping', version: '1.0' }); // broadcast
 * two.send({ type: 'pong', version: '1.0' }, { to: 'one' }); // send to a specific peer
 *
 * // learning about the network
 * one.knownPeers; // lists all known peers and messages they can receive
 *
 * // disconnecting
 * one.disconnect(); // disconnects from all peers
 * one.disconnect('two'); // disconnects from a specific peer
 * ```
 */
declare class MessagePeer<M extends Message> implements MessagePeerType<M> {
    #private;
    constructor(options: PeerOptions);
    /**
     * @inheritDoc
     */
    get id(): string;
    /**
     * @inheritDoc
     */
    get knownPeers(): Map<string, Message[]>;
    /**
     * @inheritDoc
     */
    get messages(): Subscribable<RoutedMessage<M>>;
    /**
     * @inheritDoc
     */
    get serviceMessages(): Subscribable<RoutedMessage<ServiceMessage>>;
    /**
     * @inheritDoc
     */
    get errors(): Subscribable<MessageError>;
    /**
     * @inheritDoc
     */
    connect(peerId: string, options?: PeerConnectionOptions): Promise<() => void>;
    /**
     * @inheritDoc
     */
    send(message: M, options?: PeerSendOptions): void;
    /**
     * @inheritDoc
     */
    listen(peerId: string, options?: PeerConnectionOptions): Promise<() => void>;
    /**
     * @inheritDoc
     */
    registerMessage(message: Message): void;
    /**
     * Logs the current state of the peer to the console
     */
    log(): void;
    /**
     * @inheritDoc
     */
    disconnect(peerId?: string): void;
}

/**
 * If true, tracing information to help debugging will be logged in the console
 * @param enabled
 */
declare function enableLogging(enabled: boolean): void;

export { type ConnectMessage, type DeclareMessages, type DisconnectMessage, type Message, MessageError, MessagePeer, type MessagePeerType, type PeerConnectionOptions, type PeerOptions, type PeerSendOptions, type RoutedMessage, type ServiceMessage, type Subscribable, enableLogging, isServiceMessage };
