@amadeus-it-group/microfrontends
Version:
Amadeus Micro Frontend Toolkit
462 lines (455 loc) • 14.6 kB
TypeScript
/**
* A message that is sent between peers
*/
interface Message {
/**
* Message type
*/
type: string;
/**
* Message version
*/
version?: string;
}
/**
* A versioned message
*/
interface VersionedMessage extends Message {
/**
* @inheritDoc
*/
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);
}
/**
* A strategy that a message peer uses to check messages upon reception.
* - `default` - checks that the message structure is correct (from, to, type, version and payload are present)
* - `type` - checks that the message type is known for the peer
* - `version` - checks that the message version is known for the peer
*/
type MessageCheckStrategy = 'default' | 'type' | 'version';
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.
* 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.
* 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;
}
/**
* Function that filters incoming connections based on the message and its source.
* @param message - the message received from the peer
* @param source - the source of the message
* @param origin - the origin of the message
*/
type PeerConnectionFilterFn = (message: RoutedMessage<HandshakeMessage>, source: MessageEventSource | null, origin: string) => boolean;
/**
* Filter for incoming connections.
*/
interface PeerConnectionFilter {
/**
* Id of a peer allowed to connect.
*/
id?: string;
/**
* Window object for allowed incoming connections.
* Default is `window` object of the current environment.
* It can be used to connect to a different window or an iframe.
*/
source?: MessageEventSource;
/**
* Origin of allowed incoming connections.
* 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[];
/**
* Defines how peer will validate messages upon reception.
* By 'default' it will check only that the message structure is correct.
*/
messageCheckStrategy?: MessageCheckStrategy;
}
/**
* 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[]>;
/**
* List of peers reachable from connected peers.
*/
get peerConnections(): Map<string, Set<string>>;
/**
* 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 remoteId - a remote peer id to connect 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(remoteId: string, options?: PeerConnectionOptions): Promise<() => void>;
/**
* Listens for incoming connections from any peers that match the provided filters.
* @param filters - optional filters to use to accept incoming connections. If not provided,
* accepts all incoming connections, otherwise tries to match at least one of the filters. Can be
* a string with peer id, a {@link PeerConnectionFilter} object or an array of such objects.
* @returns a function that can be called to disconnect this peer from the network
*/
listen(filters?: string | PeerConnectionFilter | PeerConnectionFilterFn | readonly (string | PeerConnectionFilter)[]): () => 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' });
* const two = new MessagePeer({ id: 'two' });
*
* // connecting two peers
* one.listen();
* 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
*
* // receiving messages
* one.messages.subscribe((message) => {}); // receives all messages sent to 'one'
* one.serviceMessages.subscribe((message) => {}); // receives service messages like 'connect', 'disconnect', etc.
*
* // 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 peerConnections(): Map<string, Set<string>>;
/**
* @inheritDoc
*/
get messages(): Subscribable<RoutedMessage<M>>;
/**
* @inheritDoc
*/
get serviceMessages(): Subscribable<RoutedMessage<ServiceMessage>>;
/**
* @inheritDoc
*/
get errors(): Subscribable<MessageError>;
/**
* @inheritDoc
*/
connect(remoteId: string, options?: PeerConnectionOptions): Promise<() => void>;
/**
* @inheritDoc
*/
send(message: M, options?: PeerSendOptions): void;
/**
* @inheritDoc
*/
listen(filters?: string | PeerConnectionFilter | PeerConnectionFilterFn | readonly (string | PeerConnectionFilter)[]): () => 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 { MessageError, MessagePeer, enableLogging, isServiceMessage };
export type { ConnectMessage, DeclareMessages, DisconnectMessage, Message, MessageCheckStrategy, MessagePeerType, PeerConnectionFilter, PeerConnectionFilterFn, PeerConnectionOptions, PeerOptions, PeerSendOptions, RoutedMessage, ServiceMessage, Subscribable, VersionedMessage };