sip.js
Version:
A SIP library for JavaScript
160 lines (159 loc) • 6.86 kB
TypeScript
import { Transport as CoreTransport } from "../core/transport.js";
import { Emitter } from "./emitter.js";
import { TransportState } from "./transport-state.js";
/**
* Transport layer interface expected by the `UserAgent`.
*
* @remarks
* The transport behaves in a deterministic manner according to the
* the state defined in {@link TransportState}.
*
* The "Connecting" state is ONLY entered in response to the user calling `connect()`.
* The "Disconnecting" state is ONLY entered in response to the user calling `disconnect()`.
* The `onConnect` callback is ALWAYS called upon transitioning to the "Connected" state.
* The `onDisconnect` callback is ALWAYS called upon transitioning from the "Connected" state.
*
* Adherence to the state machine by the transport implementation is critical as the
* UserAgent depends on this behavior. Furthermore it is critical that the transport
* transition to the "Disconnected" state in all instances where network connectivity
* is lost as the UserAgent, API, and application layer more generally depend on knowing
* network was lost. For example, from a practical standpoint registrations and subscriptions are invalidated
* when network is lost - particularly in the case of connection oriented transport
* protocols such as a secure WebSocket transport.
*
* Proper handling the application level protocol recovery must be left to the application layer,
* thus the transport MUST NOT attempt to "auto-recover" from or otherwise hide loss of network.
* Note that callbacks and emitters such as `onConnect` and `onDisconnect` MUST NOT call methods
* `connect()` and `direct()` synchronously (state change handlers must not loop back). They may
* however do so asynchronously using a Promise resolution, `setTimeout`, or some other method.
* For example...
* ```ts
* transport.onDisconnect = () => {
* Promise.resolve().then(() => transport.connect());
* }
* ```
* @public
*/
export interface Transport extends CoreTransport {
/**
* Transport state.
*
* @remarks
* The initial Transport state MUST be "disconnected" (after calling constructor).
*/
readonly state: TransportState;
/**
* Transport state change emitter.
*/
readonly stateChange: Emitter<TransportState>;
/**
* Callback on state transition to "Connected".
*
* @remarks
* When the `UserAgent` is constructed, this property is set.
* ```txt
* - The `state` MUST be "Connected" when called.
* ```
*/
onConnect: (() => void) | undefined;
/**
* Callback on state transition from "Connected".
*
* @remarks
* When the `UserAgent` is constructed, this property is set.
* ```txt
* - The `state` MUST NOT "Connected" when called.
* - If prior `state` is "Connecting" or "Connected", `error` MUST be defined.
* - If prior `state` is "Disconnecting", `error` MUST NOT be undefined.
* ```
* If the transition from "Connected" occurs because the transport
* user requested it by calling `disconnect`, then `error` will be undefined.
* Otherwise `error` will be defined to provide an indication that the
* transport initiated the transition from "Connected" - for example,
* perhaps network connectivity was lost.
*/
onDisconnect: ((error?: Error) => void) | undefined;
/**
* Callback on receipt of a message.
*
* @remarks
* When the `UserAgent` is constructed, this property is set.
* The `state` MUST be "Connected" when this is called.
*/
onMessage: ((message: string) => void) | undefined;
/**
* Connect to network.
*
* @remarks
* ```txt
* - If `state` is "Connecting", `state` MUST NOT transition before returning.
* - If `state` is "Connected", `state` MUST NOT transition before returning.
* - If `state` is "Disconnecting", `state` MUST transition to "Connecting" before returning.
* - If `state` is "Disconnected" `state` MUST transition to "Connecting" before returning.
* - The `state` MUST transition to "Connected" before resolving (assuming `state` is not already "Connected").
* - The `state` MUST transition to "Disconnecting" or "Disconnected" before rejecting and MUST reject with an Error.
* ```
* Resolves when the transport connects. Rejects if transport fails to connect.
* Rejects with {@link StateTransitionError} if a loop is detected.
* In particular, callbacks and emitters MUST NOT call this method synchronously.
*/
connect(): Promise<void>;
/**
* Disconnect from network.
*
* @remarks
* ```txt
* - If `state` is "Connecting", `state` MUST transition to "Disconnecting" before returning.
* - If `state` is "Connected", `state` MUST transition to "Disconnecting" before returning.
* - If `state` is "Disconnecting", `state` MUST NOT transition before returning.
* - If `state` is "Disconnected", `state` MUST NOT transition before returning.
* - The `state` MUST transition to "Disconnected" before resolving (assuming `state` is not already "Disconnected").
* - The `state` MUST transition to "Connecting" or "Connected" before rejecting and MUST reject with an Error.
* ```
* Resolves when the transport disconnects. Rejects if transport fails to disconnect.
* Rejects with {@link StateTransitionError} if a loop is detected.
* In particular, callbacks and emitters MUST NOT call this method synchronously.
*/
disconnect(): Promise<void>;
/**
* Dispose.
*
* @remarks
* When the `UserAgent` is disposed or stopped, this method is called.
* The `UserAgent` MUST NOT continue to utilize the instance after calling this method.
*/
dispose(): Promise<void>;
/**
* Returns true if the `state` equals "Connected".
*
* @remarks
* This is equivalent to `state === TransportState.Connected`.
* It is convenient. A common paradigm is, for example...
*
* @example
* ```ts
* // Monitor transport connectivity
* userAgent.transport.stateChange.addListener(() => {
* if (userAgent.transport.isConnected()) {
* // handle transport connect
* } else {
* // handle transport disconnect
* }
* });
* ```
*/
isConnected(): boolean;
/**
* Send a message.
*
* @remarks
* ```txt
* - If `state` is "Connecting", rejects with an Error.
* - If `state` is "Connected", resolves when the message is sent otherwise rejects with an Error.
* - If `state` is "Disconnecting", rejects with an Error.
* - If `state` is "Disconnected", rejects with an Error.
* ```
* @param message - Message to send.
*/
send(message: string): Promise<void>;
}