API: Stream
Relevant source files
The following files were used as context for generating this wiki page:
The Stream API provides a mechanism for continuous, stateful, and namespaced two-way communication between different parts of a web extension. It is built on top of the core webext-bridge messaging system and is designed to simplify interactions that require a persistent connection, such as long-running tasks or data synchronization. By creating a scoped channel, it reduces the chance of message ID conflicts that might occur in a global messaging scope.
The Stream API is available in all major extension contexts, including background, content-script, devtools, popup, options, and window. It exposes a consistent interface for both initiating a stream (openStream) and listening for incoming stream requests (onOpenStreamChannel).
Sources: src/internal/stream.ts:13-18, README.md:12-15, src/background.ts:299-300, src/content-script.ts:44-45
Core Components
The Stream API is primarily composed of the Stream class, which represents an active communication channel, and a set of "wiring" functions that manage the creation and lifecycle of these streams.
Sources: src/internal/stream.ts
The Stream Class
The Stream class is the central component of the API, representing a single, active communication channel between two endpoints. It encapsulates the logic for sending data, handling incoming messages, and managing the stream's lifecycle (creation and closure).
Sources: src/internal/stream.ts:19-123
The diagram below illustrates the structure of the Stream class.
This diagram shows the private fields and public methods of the Stream class.
Sources: src/internal/stream.ts:19-123
Properties
| Property | Type | Description |
|---|---|---|
info | StreamInfo | Returns metadata about the stream, including its streamId, channel, and the remote endpoint. |
Sources: src/internal/stream.ts:44-46
Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
send | msg?: JsonValue | void | Sends a message to the other endpoint. Throws an error if the stream is closed. |
close | msg?: JsonValue | void | Closes the stream. An optional final message can be sent before closing. Triggers onClose on both ends. |
onMessage | callback: (msg?: T) => void | HybridUnsubscriber | Registers a callback to handle incoming messages from the other endpoint. |
onClose | callback: (msg?: T) => void | HybridUnsubscriber | Registers a callback that fires when the stream is closed by either endpoint. |
Sources: src/internal/stream.ts:54-113
StreamInfo Data Structure
The StreamInfo interface defines the metadata for a stream. This object is created when a stream is opened and is accessible via the stream.info property.
| Field | Type | Description |
|---|---|---|
streamId | string | A unique identifier for the stream instance (UUID). |
channel | string | The user-defined channel name for the stream. |
endpoint | Endpoint | The destination endpoint the stream is connected to. |
Sources: src/types.ts:34-38
Stream Lifecycle and Communication
The lifecycle of a stream involves three main phases: opening the connection, transferring data, and closing the connection. This process is managed by internal messages and a set of factory functions.
Sources: src/internal/stream.ts
Creating and Listening for Streams
Streams are managed through two primary functions generated by createStreamWirings: openStream and onOpenStreamChannel.
openStream(channel, destination): Asynchronously initiates a new stream on a specificchannelto a givendestination. It returns aPromise<Stream>. A channel can only have one open stream at a time.onOpenStreamChannel(channel, callback): Registers a listener on a specificchannel. When a remote endpoint callsopenStreamfor that channel, thecallbackis invoked with the newly createdStreaminstance.
Sources: src/internal/stream.ts:125-184
The following sequence diagram illustrates the process of opening a stream from a popup to the background script.
This diagram shows how openStream and onOpenStreamChannel work together to establish a stream.
Sources: src/internal/stream.ts:151-184
Data Transfer and Closure
Once a stream is established, data is transferred using the stream.send() method. This triggers the onMessage handler on the receiving end. The entire process is managed by a single internal message ID, __crx_bridge_stream_transfer__, which carries a payload indicating the action (transfer or close).
Sources: src/internal/stream.ts:25-39, src/internal/stream.ts:62-69
The sequence diagram below shows a data exchange and the subsequent closing of the stream.
This diagram illustrates the flow of data and the closing handshake between two endpoints in a stream.
Sources: src/internal/stream.ts:25-39, src/internal/stream.ts:62-92
Integration in Extension Contexts
The Stream API is made available to each extension context by calling createStreamWirings(endpointRuntime). This factory function takes the context-specific endpointRuntime and returns the openStream and onOpenStreamChannel functions, which are then exported for use in the extension's code.
This pattern is consistently applied across all contexts, such as:
background.tscontent-script.tsdevtools.tspopup.tsoptions.tswindow.ts
Sources: src/internal/stream.ts:125-126, src/background.ts:300, src/content-script.ts:45, src/devtools.ts:14, src/window.ts:22
The following code snippet from window.ts demonstrates this integration:
// src/window.ts:18-20
export const { sendMessage, onMessage } = endpointRuntime
export const { openStream, onOpenStreamChannel }
= createStreamWirings(endpointRuntime)
Sources: src/window.ts:18-20
The flowchart below visualizes the logic within createStreamWirings for handling an incoming stream request.
This flowchart shows how an incoming stream request is handled, either by immediately invoking a pre-registered callback or by waiting for one to be registered.
Sources: src/internal/stream.ts:131-149
Conclusion
The Stream API in webext-bridge is a powerful abstraction for managing persistent, namespaced communication channels. By providing a simple and consistent interface (openStream, onOpenStreamChannel, and the Stream class), it simplifies complex, stateful interactions between different parts of a web extension, making the code more robust and easier to maintain. Its design, centered around the createStreamWirings factory, ensures seamless integration into every extension context.
Sources: src/internal/stream.ts, README.md:12-15