Endpoints Explained
Relevant source files
The following files were used as context for generating this wiki page:An "Endpoint" is the fundamental concept in webext-bridge that represents a distinct, addressable location within a web extension. Each part of an extension, such as the background script, a content script, a popup, or the devtools panel, is treated as an endpoint. This architecture allows for a consistent and simplified messaging system where any endpoint can communicate with any other endpoint without needing to manage the underlying browser.runtime connection details manually.
The system is designed around a central routing mechanism, typically managed by the background script, which maintains a map of all active connections. When a message is sent to a specific destination, the endpoint runtime forwards it to the background script, which then relays it to the correct recipient based on its unique endpoint identifier. This hub-and-spoke model simplifies communication and state management across the entire extension.
What is an Endpoint?
An endpoint is defined by its context, and where applicable, its tab and frame ID. This information is encapsulated in the Endpoint object.
Sources: src/types.ts:9-14
The Endpoint Interface
The structure of an endpoint is defined by the Endpoint interface.
| Field | Type | Description |
|---|---|---|
context | RuntimeContext | The execution context of the endpoint (e.g., 'background', 'content-script'). |
tabId | number | The ID of the tab the endpoint is associated with. Not always required for global contexts like 'background'. |
frameId | number | (Optional) The ID of the frame within a tab, used to target specific iframes. |
Sources: src/types.ts:9-14
Runtime Contexts
The RuntimeContext type enumerates all possible locations for an endpoint.
| Context | Description |
|---|---|
background | The extension's background script (or service worker). |
content-script | A script injected into a web page. |
popup | The script running in the browser action popup. |
options | The script running in the extension's options page. |
devtools | The script running in the developer tools panel. |
window | A script running in the web page's main window context, communicating via a content script. |
Sources: src/types.ts:3-10
Endpoint Identification and Formatting
To facilitate routing, Endpoint objects are serialized into and parsed from a standardized string format. This string identifier is used as a key for connection mapping and as a destination address in messages.
Sources: src/internal/endpoint.ts
Endpoint String Format
The formatEndpoint function creates the string representation, while parseEndpoint reverses the process. The format is context@tabId.frameId. Contexts that are singletons (like background, popup, options) do not require a tabId or frameId.
The diagram below illustrates how an Endpoint object is converted to a string and back.
This logic is primarily handled by the formatEndpoint and parseEndpoint functions, which use a regular expression to validate and deconstruct the endpoint string.
Sources: src/internal/endpoint.ts:5-18
The Endpoint Runtime
The core of every endpoint is its "runtime," created by the createEndpointRuntime factory function. This function equips each context with the necessary tools to send and receive messages, manage asynchronous transactions, and route messages to their destinations.
Sources: src/internal/endpoint-runtime.ts
Core Components
The createEndpointRuntime function returns an object with several key methods:
| Method | Description |
|---|---|
sendMessage | Sends a message to a destination and returns a Promise that resolves with the reply. |
onMessage | Registers a listener to handle incoming messages for a specific message ID. |
handleMessage | The internal handler that processes all incoming messages, routing them to local listeners or forwarding them. |
endTransaction | Terminates an open transaction, causing the corresponding sendMessage promise to reject. |
Sources: src/internal/endpoint-runtime.ts:20-32
Message Handling Flow
When a message arrives at an endpoint, the handleMessage function orchestrates its processing. The function first checks if the current endpoint is the final destination.
- If it is, the message is processed locally. It could be a reply to a message sent earlier, in which case it resolves a pending
Promise. Or, it could be a new message, which triggers the appropriateonMessagelistener. - If it is not the destination, the message is forwarded via the
routeMessagefunction provided during the runtime's creation.
The following diagram shows the internal logic of handleMessage:
Centralized Routing via Background Script
The background script acts as the central message broker. All other endpoints establish a persistent connection to it. The background script maintains a map of all connected endpoints and is responsible for routing messages between them.
Sources: src/background.ts
Connection Lifecycle
- An endpoint (e.g.,
content-script,popup) creates a port to the background script. The port's name contains connection arguments, including a uniquefingerprint. - The background script's
runtime.onConnectlistener fires. - It decodes the connection arguments and stores the port, its endpoint identifier (e.g.,
content-script@123), and its fingerprint in a globalconnMap. - When the port disconnects (e.g., tab is closed), the background script removes the entry from
connMapand notifies any senders waiting for a reply from the disconnected endpoint.
This sequence diagram illustrates a content script connecting to the background script.
Sources: src/background.ts:211-239
Message Routing Flow
When an endpoint calls sendMessage, the message is routed through the background script to its destination.
- Popup calls
sendMessage('get-data', ..., 'content-script@123'). - The Popup's endpoint runtime sends an
InternalMessageto the Background script via its port. - The Background script's
handleMessagefunction receives the message. It looks up'content-script@123'in itsconnMap. - If found, it forwards the message through the corresponding port to the Content Script.
- The Content Script receives the message, processes it with its
onMessagehandler, and sends a reply back. - The reply follows the reverse path back to the Popup, where the original
sendMessagepromise is resolved.
Conclusion
The endpoint system in webext-bridge provides a powerful abstraction over the native WebExtension messaging APIs. By defining every part of an extension as an addressable endpoint and using a centralized router, it creates a robust and easy-to-use communication fabric. This design decouples the different components of an extension, allowing developers to send messages and stream data between contexts with a simple, promise-based API, regardless of the underlying complexity.