Using Streams
Relevant source files
The following files were used as context for generating this wiki page:The stream feature in webext-bridge provides a mechanism for continuous, end-to-end, and namespaced communication between different parts of a web extension. It is built on top of the core messaging bridge and is designed to simplify scenarios requiring ongoing data exchange. By creating a scoped channel, streams reduce the complexity and potential for message ID conflicts that can arise with global message listeners.
Streams are available in all runtime contexts, including background, content-script, devtools, popup, options, and window. The API provides a consistent way to open a communication channel, send and receive data, and handle the stream's closure.
Sources: src/internal/stream.ts:13-18, README.md:14-17
Architecture
The stream system is orchestrated by a few key components that work together to establish and maintain communication channels. At its core, it leverages the existing EndpointRuntime for message routing.
Sources: src/internal/stream.ts, src/internal/endpoint-runtime.ts
Core Components
| Component | Description | File |
|---|---|---|
Stream | A class representing one end of a communication stream. It manages the connection state, provides methods for sending data, and exposes event listeners for incoming messages and closure events. | src/internal/stream.ts:19-135 |
createStreamWirings | A factory function that initializes the stream handling logic for a specific EndpointRuntime. It sets up listeners for stream-opening requests and returns the openStream and onOpenStreamChannel functions. | src/internal/stream.ts:137-183 |
EndpointRuntime | The underlying message routing engine used by streams to send control messages (like open and close) and data transfers between endpoints. | src/internal/endpoint-runtime.ts |
StreamInfo | An interface defining the metadata for a stream, including its unique ID, the channel name, and the destination endpoint. | src/types.ts:28-32 |
Internal Messaging Protocol
Streams use a dedicated set of internal message IDs to manage their lifecycle. These messages are handled internally by the webext-bridge library and are not typically interacted with directly.
__crx_bridge_stream_open__: Sent byopenStreamto request the establishment of a new stream channel with a destination endpoint.__crx_bridge_stream_transfer__: Used for both transferring data over an established stream and signaling its closure.
This diagram illustrates the relationship between the main components of the stream system.
Sources: src/internal/stream.ts:28-44,141-160
Stream Lifecycle
A stream's lifecycle consists of three main phases: opening the channel, transferring data, and closing the channel.
1. Opening a Stream
A stream is initiated by one context (the "caller") and accepted by another (the "listener").
- The listener context calls
onOpenStreamChannel(channelName, callback)to register a handler for a specific channel. - The caller context calls
openStream(channelName, destination)to initiate the connection. openStreamgenerates a uniquestreamId, creates a newStreaminstance, and sends a__crx_bridge_stream_open__message to the destination via theEndpointRuntime.- The listener's
EndpointRuntimereceives this message and invokes the callback registered in step 1. - The callback receives a new
Streaminstance, completing the connection. Both contexts now hold aStreamobject representing their end of the channel.
The following sequence diagram shows the process of opening a stream between a popup script and the background script.
Sources: src/internal/stream.ts:137-183
2. Data Transfer
Once a stream is open, both endpoints can send and receive data.
- An endpoint calls
stream.send(data)to transmit aJsonValuepayload. - This action sends a
__crx_bridge_stream_transfer__message withaction: 'transfer'. - The receiving endpoint's
Streaminstance gets the message and emits amessageevent. - Listeners registered with
stream.onMessage(callback)are executed with the received data.
Sources: src/internal/stream.ts:28-34,65-74
3. Closing a Stream
Either endpoint can close the stream, which terminates the connection for both parties.
- An endpoint calls
stream.close(). - A
__crx_bridge_stream_transfer__message withaction: 'close'is sent to the other endpoint. - Both local and remote
Streaminstances are marked as closed, and theironCloseevent listeners are invoked. - The stream instances are removed from the internal
openStreamsmap and cannot be reused.
Sources: src/internal/stream.ts:36-42,79-91,100-106
API Reference
The stream functionality is exposed through the openStream and onOpenStreamChannel functions, which are exported from each context-specific module (e.g., webext-bridge/background).
Sources: src/background.ts:352-353, src/content-script.ts:39-41, src/window.ts:24-25
Factory Functions
These functions are used to establish a stream.
| Function | Description |
|---|---|
openStream(channel, destination) | Asynchronously opens a new stream to a specified destination on a given channel. Throws an error if a stream is already open on that channel. Returns a Promise<Stream>. |
onOpenStreamChannel(channel, callback) | Registers a callback to handle incoming stream requests on a specific channel. Throws an error if the channel is already claimed. |
Sources: src/internal/stream.ts:162-183
The Stream Class
The Stream object is the primary interface for interacting with an open communication channel.
| Method / Property | Description |
|---|---|
get info(): StreamInfo | Returns an object containing the stream's metadata: streamId, channel, and remote endpoint. |
send(msg?: JsonValue): void | Sends a JSON-serializable message to the other endpoint. Throws an error if the stream is closed. |
close(msg?: JsonValue): void | Closes the stream for both endpoints. An optional final message can be sent before closing. |
onMessage(callback): HybridUnsubscriber | Registers a callback to be fired when a message is received from the other endpoint. Returns an unsubscriber function. |
onClose(callback): HybridUnsubscriber | Registers a callback to be fired when the stream is closed by either endpoint. Returns an unsubscriber function. |
Sources: src/internal/stream.ts:49-120
Data Structures
The StreamInfo object provides metadata about an active stream.
| Field | Type | Description |
|---|---|---|
streamId | string | A unique identifier for the stream instance, generated with tiny-uid. |
channel | string | The user-defined channel name for the stream. |
endpoint | Endpoint | An object representing the remote endpoint, containing its context and tabId. |
Sources: src/types.ts:28-32, src/internal/stream.ts:167
Conclusion
The stream API in webext-bridge offers a powerful abstraction for managing persistent, two-way communication channels. By handling the complexities of connection setup, data transfer, and teardown, it allows developers to focus on application logic rather than the low-level details of messaging protocols. This is particularly useful for features that require a continuous exchange of information, such as synchronizing state between a devtools panel and a content script.