1.0.0-beta.5 • Published 4 months ago

@daniel-nagy/transporter-browser v1.0.0-beta.5

Weekly downloads
-
License
Apache-2.0
Repository
github
Last release
4 months ago

Browser

The browser package contains APIs designed to work in the browser.

npm add @daniel-nagy/transporter @daniel-nagy/transporter-browser

Transporter is distributed as ES modules.

Transporter may also be imported directly in the browser from a URL. For example,

<script type="importmap">
  {
    "imports": {
      "@daniel-nagy/transporter/": "https://unpkg.com/@daniel-nagy/transporter@1.0.0-beta.3/build/",
      "@daniel-nagy/transporter-browser/": "https://unpkg.com/@daniel-nagy/transporter-browser@1.0.0-beta.3/build/"
    }
  }
</script>
<script type="module">
  import * as BrowserServer from "@daniel-nagy/transporter-browser/BrowserServer.js";
  import * as Session from "@daniel-nagy/transporter/Session.js";
</script>

API

The browser package contains the following modules.

BroadcastSubject

Module

A BroadcastSubject can be used to synchronize state between same-origin browsing contexts or workers.

Types
Constructors

Type

class BroadcastSubject<T extends StructuredCloneable.t> extends Subject.t<T> {}

A BroadcastSubject is a Subject that broadcasts emitted values over a BroadcastChannel.

FromChannel

Constructor

function fromChannel<T extends StructuredCloneable.t>(
  name: string
): BroadcastSubject<T>;

Creates a BroadcastSubject from a broadcast channel name.

Example
import * as BroadcastSubject from "@daniel-nagy/transporter-browser/BroadcastSubject";
const darkMode = BroadcastSubject.fromChannel("darkMode");
darkMode.subscribe(console.log);

BrowserClient

Module

An interface for making requests to a browsing context or worker.

Types
Constants
Constructors
Methods

Type

class BrowserClient {
  /**
   * The address of the server. An address is like a port number, except an
   * address can be any string instead of a meaningless number.
   */
  public readonly serverAddress: string;
  /**
   * If the window and the origin do not match the connection will fail. The
   * origin is only relevant when connecting to a window since the browser
   * will require worker URLs to be same-origin.
   */
  public readonly origin: string;
  /**
   * The message target. A message target is like a server host.
   */
  public readonly target: Window | Worker | SharedWorker | SW;
}

An object that may be used to make fetch requests to a browsing context or worker.

Options

Type

type Options = {
  /**
   * The address of the server. The default is the empty string.
   */
  address?: string;
  /**
   * When connecting to a `Window` you may specify the allowed origin. If the
   * window and the origin do not match the connection will fail. The origin is
   * passed directly to the `targetOrigin` parameter of `postMessage` when
   * connecting to the window. The default is `"*"`, which allows any origin.
   */
  origin?: string;
};

Options when creating a BrowserClient.

SW

Constant

const SW = Symbol.for("ServiceWorker");

An atom that symbolizes a ServiceWorker. When used as a target the client will make requests to the currently active ServiceWorker.

Example
import * as BrowserClient from "@daniel-nagy/transporter-browser/BrowserClient";

await navigator.serviceWorker.register("./sw.js", {
  type: "module"
});

const client = BrowserClient.from(BrowserClient.SW);

From

Constructor

function from(
  target: Window | Worker | SharedWorker | SW,
  options?: Options
): BrowserClient;

Creates a new BrowserClient.

Example
import * as BrowserClient from "@daniel-nagy/transporter-browser/BrowserClient";

const worker = new Worker("/worker.js", { type: "module" });
const client = BrowserClient.from(worker);

Fetch

Method

fetch(body: StructuredCloneable.t): Promise<StructuredCloneable.t>;

Makes a request to a BrowserServer.

Example
import * as BrowserClient from "@daniel-nagy/transporter-browser/BrowserClient";

const worker = new Worker("/worker.js", { type: "module" });
const client = BrowserClient.from(worker);
const response = await client.fetch("👋");

BrowserRequest

Module

A server receives a Request object when a client makes a request.

Types
Functions

Request

Type

type Request = {
  address: string;
  /**
   * Contains the value sent by the client.
   */
  body: StructuredCloneable.t;
  id: string;
  /**
   * The origin of the client making the request. The origin will be set
   * securely on the server using `MessageEvent.origin`.
   */
  origin: string;
  type: "Request";
};

A Request is created when a client makes a fetch request.

IsRequest

Function

function isRequest(event: MessageEvent): event is MessageEvent<Request>;

Returns true if the message event contains a Request object.

BrowserResponse

Module

A server sends a Response to a client in response to a request.

Types
Functions

Response

Type

type Response = {
  /**
   * The payload of the response. This is the value the client will receive.
   */
  body: StructuredCloneable.t;
  id: string;
  type: "Response;
};

A Response is created from the value returned by the server's request handler.

IsResponse

Function

function isResponse(event: MessageEvent): event is MessageEvent<Response>;

Returns true if the message event contains a Response object.

BrowserServer

Module

A BrowserServer provides request/response semantics on top of postMessage. It also normalizes the interface for connecting to different types of processes in the browser.

Types
Constructors
Methods

Type

class BrowserServer {
  public readonly address: string;
  public readonly handle: RequestHandler;
  public readonly state: State;
  public readonly stateChange: Observable.t<State>;
  public readonly stopped: Observable.t<State.Stopped>;
}

A BrowserServer listens for incoming requests from clients.

Type

type Options = {
  /**
   * The address of the server. The default is the empty string. All servers
   * must have a globally unique address.
   */
  address?: string;
  /**
   * Called whenever a request is received from a client. The request handler
   * may return anything that is structured cloneable.
   *
   * The request object will contain the origin of the client. The origin can be
   * used to validate the client before fulfilling the request.
   */
  handle: RequestHandler;
};

Options when creating a BrowserServer.

RequestHandler

Type

type RequestHandler = (
  request: Readonly<Request.t>
) => StructuredCloneable.t | Promise<StructuredCloneable.t>;

A RequestHandler receives a Request from a client and returns the body of the Response that will be sent back to the client.

State

Type

enum State {
  Listening = "Listening",
  Stopped = "Stopped"
}

An enumerable of the different server states.

Listen

Constructor

function listen(options: Options): BrowserServer;

Creates a new BrowserServer in the global scope. A UniqueAddressError will be thrown if the address is already taken.

Example

import * as BrowserServer from "@daniel-nagy/transporter/BrowserServer";

const server = BrowserServer.listen({
  handle(request) {
    // Message received from client. Return any response.
    return "👋";
  }
});

Stop

Method

stop(): void;

Stops the server. Once stopped the server will no longer receive requests.

Example

import * as BrowserServer from "@daniel-nagy/transporter/BrowserServer";

const server = BrowserServer.listen({
  handle(request) {}
});

server.stop();

BrowserSocket

Module

Provides a socket API on top of postMessage that is similar to the WebSocket API. A BrowserSocket is connection-oriented, duplex, and unicast. Any data that is structured cloneable can be passed through a browser socket.

Types
Constructors
Methods

Type

class BrowserSocket {
  /**
   * Emits when the socket's state changes to `Closed` and then completes.
   */
  public readonly closed: Observable.t<State.Closed>;
  /**
   * Emits when the socket's state changes to `Closing` and then completes.
   */
  public readonly closing: Observable.t<State.Closing>;
  /**
   * Emits if the socket's state changes to `Connected` and then completes. If
   * the socket errors during connection it will complete without emitting.
   */
  public readonly connected: Observable.t<State.Connected>;
  /**
   * Emits when the socket receives data.
   */
  public readonly receive: Observable.t<StructuredCloneable.t>;
  /**
   * The current state of the socket.
   */
  public readonly state: State;
  /**
   * Emits when the socket's state changes. Completes when the socket state
   * becomes `Closed`.
   */
  public readonly stateChange: Observable.t<State>;
}

A BrowserSocket is used to create a connection between browsing contexts or a browsing context and a worker context.

ConnectionError

Type

type ConnectionError =
  | Observable.BufferOverflowError
  | ConnectTimeoutError
  | HeartbeatTimeoutError;

A variant type for the different reasons a socket may transition to a closing state with an error.

ConnectTimeoutError

Type

class ConnectTimeoutError extends Error {}

Used to indicate that the connection failed because the server did not complete the connection in the allotted time.

DisconnectTimeoutError

Type

class DisconnectTimeoutError extends Error {}

Used to indicate that an acknowledgement was not received when closing the connection in the allotted time.

HeartbeatTimeoutError

Type

class HeartbeatTimeoutError extends Error {}

Used to indicate that a response to a health-check was not received in the allotted time.

Type

interface Options {
  /**
   * The maximum number of messages to buffer before the socket is connected.
   * The default is `Infinity`.
   */
  bufferLimit?: number;
  /**
   * What to do incase there is a buffer overflow. The default is to error.
   */
  bufferOverflowStrategy?: Observable.BufferOverflowStrategy;
  /**
   * The maximum amount of time to wait for a connection in milliseconds. The
   * default is `2000` or 2 seconds.
   */
  connectTimeout?: number;
  /**
   * The maximum amount of time to wait for a disconnection in milliseconds. The
   * default is `2000` or 2 seconds.
   */
  disconnectTimeout?: number;
  /**
   * The frequency at which to request heartbeats in milliseconds. The default
   * is `1000` or 1 second.
   */
  heartbeatInterval?: number;
  /**
   * The maximum amount of time to wait for a heartbeat in milliseconds. The
   * default is `2000` or 2 seconds.
   */
  heartbeatTimeout?: number;
  /**
   * The address of the socket server.
   */
  serverAddress?: string;
}

Options when creating a BrowserSocket.

WindowOptions

Type

interface WindowOptions extends Options {
  /**
   * When connecting to a `Window` you may specify the allowed origin. If the
   * window and the origin do not match the connection will fail. The origin is
   * passed directly to the `targetOrigin` parameter of `postMessage` when
   * connecting to the window. The default is `"*"`, which allows any origin.
   */
  origin?: string;
}

Additional options when connecting to a browsing context.

Connect

Constructor

function connect(
  target: SharedWorker | Window | Worker,
  options?: Options | WindowOptions
): BrowserSocket;

Creates a new BrowserSocket. The socket will start in a Connecting state.

Example
import * as BrowserSocket from "@daniel-nagy/transporter/BrowserSocket";
using socket = BrowserSocket.connect(self.parent);

Close

Method

close(): void;

Closes the socket causing its state to transition to Closing.

Example
import * as BrowserSocket from "@daniel-nagy/transporter/BrowserSocket";
const socket = BrowserSocket.connect(self.parent);
socket.close();

Ping

Method

ping(timeout: number = 2000): Promise<void>;

Sends a ping to a connected socket and waits for a pong to be sent back. Returns a promise that resolves when a pong is received or rejects if a pong is not received in the allotted time.

Example
import * as BrowserSocket from "@daniel-nagy/transporter/BrowserSocket";
using socket = BrowserSocket.connect(self.parent);
await socket.ping();

Send

Method

send(message: StructuredCloneable.t): void;

Sends data through the socket. Data will automatically be buffered until the socket connects.

Example
import * as BrowserSocket from "@daniel-nagy/transporter/BrowserSocket";
const socket = BrowserSocket.connect(self.parent);
socket.send("👋");

BrowserSocket.Message

Module

Internal messages to facilitate the socket API. These messages are filtered from the data received from the socket.

Types
Functions

Type

type Connect = {
  address: string;
  type: Type.Connect;
};

Sent when a connection is initiated. This starts the "handshake".

Connect

Type

type Connected = {
  type: Type.Connected;
};

A message indicating the connection is complete and was successful. This concludes the "handshake".

Disconnect

Type

type Disconnect = {
  type: Type.Disconnect;
};

Sent when a socket is closing so that the other endpoint may preform some cleanup logic or otherwise close the connection gracefully. This starts the "closing handshake".

Disconnected

Type

type Disconnected = {
  type: Type.Disconnected;
};

A message that acknowledges the disconnection. If this message is received then the disconnect was graceful. This concludes the "closing handshake".

Message

Type

export type Message =
  | Connect
  | Connected
  | Disconnect
  | Disconnected
  | Ping
  | Pong;

A variant type for the different types of messages.

Type

type Ping = {
  id: string;
  type: Type.Ping;
};

A ping message may be sent to solicit a response from the other endpoint.

Pong

Type

type Pong = {
  id: string;
  type: Type.Pong;
};

A pong message must always be sent in response to a ping message.

Type

Type

enum Type {
  Connect = "Connect",
  Connected = "Connected",
  Disconnect = "Disconnect",
  Disconnected = "Disconnected",
  Ping = "Ping",
  Pong = "Pong"
}

An enumerable of the different types of socket messages.

IsMessage

Function

function isMessage(message: StructuredCloneable.t): message is Message;

Returns true if the message is a socket message.

IsType

Function

function isType<T extends Type>(
  message: StructuredCloneable.t,
  type: T
): message is {
  [Type.Connect]: Connect;
  [Type.Connected]: Connected;
  [Type.Disconnect]: Disconnect;
  [Type.Disconnected]: Disconnected;
  [Type.Ping]: Ping;
  [Type.Pong]: Pong;
}[T];

Returns true if the message is of the specified type.

TypeOf

Function

function typeOf(message: StructuredCloneable.t): Type | null;

Returns the message Type if the message is a socket message. Returns null otherwise.

BrowserSocket.State

Module

A socket's state.

Types

Closed

Type

type Closed<E> = {
  error?: E;
  type: Type.Closed;
};

The socket is closed, possibly with an error.

Closing

Type

type Closing<E> = {
  error?: E;
  type: Type.Closing;
};

The socket is closing, possibly with an error.

Type

type Connected = {
  type: Type.Connected;
};

The socket is connected.

Connecting

Type

type Connecting = {
  type: Type.Connecting;
};

The socket is connecting.

Type

type State =
  | Connecting
  | Connected
  | Closing<Error.ConnectionError>
  | Closed<Error.DisconnectTimeoutError>;

A variant type for the different socket states.

Type

enum Type {
  Connecting = "Connecting",
  Connected = "Connected",
  Closing = "Closing",
  Closed = "Closed"
}

An enumerable of the different socket states.

BrowserSocketServer

Module

A BrowserSocketServer listens for socket connect requests. When a request is received it will create a corresponding socket server side and complete the handshake.

Types
Constructors
Methods

Type

class BrowserSocketServer {
  public readonly address: string;
  public readonly connect: Observable.t<BrowserSocket.t>;
  public readonly state: State;
  public readonly stateChange: Observable.t<State>;
  public readonly stopped: Observable.t<State.Stopped>;
}

Creates socket connections as requests come in.

Type

type Options = {
  /**
   * The address of the server. The default is an empty string.
   */
  address?: string;
  /**
   * Allows intercepting connection requests and denying the request if
   * necessary.
   */
  connectFilter?(message: MessageEvent<Message.Connect>): boolean;
  /**
   * Forwarded to the socket that is created on connection.
   */
  socketOptions?: SocketOptions;
};

Options when creating a BrowserSocketServer.

SocketOptions

Type

type SocketOptions = {
  disconnectTimeout?: number;
  heartbeatInterval?: number;
  heartbeatTimeout?: number;
};

Options forwarded to the BrowserSocket when it is created.

Type

enum State {
  Listening = "Listening",
  Stopped = "Stopped"
}

An enumerable of the different server states.

Constructor

function listen(options?: Options): BrowserSocketServer;

Creates a new BrowserSocketServer. Throws a UniqueAddressError if the address is already taken.

Example

import * as BrowserSocketServer from "@daniel-nagy/transporter/BrowserSocketServer";

const server = BrowserSocketServer.listen();
server.connect.subscribe((socket) => socket.send("👋"));

Method

function stop(): void;

Stops the server. A disconnect message will be sent to all connected clients.

Example

import * as BrowserSocketServer from "@daniel-nagy/transporter/BrowserSocketServer";

const server = BrowserSocketServer.listen();
server.stop();

StructuredCloneable

Module

A StructuredCloneable type can be passed between processes in the browser.

Types

Type

type StructuredCloneable =
  | void
  | null
  | undefined
  | boolean
  | number
  | bigint
  | string
  | Date
  | ArrayBuffer
  | RegExp
  | TypedArray
  | Array<StructuredCloneable>
  | Map<StructuredCloneable, StructuredCloneable>
  | Set<StructuredCloneable>
  | { [key: string]: StructuredCloneable };

A value that can be cloned using the structured clone algorithm.

TypedArray

Type

type TypedArray =
  | BigInt64Array
  | BigUint64Array
  | Float32Array
  | Float64Array
  | Int8Array
  | Int16Array
  | Int32Array
  | Uint8Array
  | Uint8ClampedArray
  | Uint16Array
  | Uint32Array;

A TypedArray object describes an array-like view of an underlying binary data buffer.