Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ Communication framework for browser extensions, with wings!

## Supports

* **Runtime contexts:** window (injected script), popup, devtools, content script, background, options, sidepanel (_planned_)
* **Runtime contexts:** window (injected script), popup, sidepanel, devtools, content script, background, options
* **Browsers:** Chrome, Firefox, Safari, Opera, Edge + others supported by [webextension-polyfill](https://github.com/mozilla/webextension-polyfill)
1 change: 1 addition & 0 deletions packages/rpc/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type RuntimeContext =
| 'devtools'
| 'background'
| 'popup'
| 'sidepanel'
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RuntimeContext now includes 'sidepanel', but packages/rpc/README.md still lists sidepanel as "planned". Either update the RPC README to reflect current support, or (if RPC isn’t intended to support sidepanel yet) avoid advertising it via the public RuntimeContext type until the rest of the RPC surface is ready.

Suggested change
| 'sidepanel'

Copilot uses AI. Check for mistakes.
| 'options'
| 'content-script'
| 'window';
Expand Down
7 changes: 4 additions & 3 deletions packages/transport/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This library provides two communication patterns:

## Supports

* **Runtime contexts:** window (injected script), popup, devtools, content script, background, options, sidepanel (_planned_)
* **Runtime contexts:** window (injected script), popup, sidepanel, devtools, content script, background, options
* **Browsers:** Chrome, Firefox, Safari, Opera, Edge + others supported by [webextension-polyfill](https://github.com/mozilla/webextension-polyfill)

## Comparison to other libraries
Expand Down Expand Up @@ -88,6 +88,7 @@ eventBus.emitBroadcastEvent(
- `@webext-pegasus/transport/devtools`
- `@webext-pegasus/transport/options`
- `@webext-pegasus/transport/popup`
- `@webext-pegasus/transport/sidepanel`
- `@webext-pegasus/transport/window` (for injected scripts)


Expand Down Expand Up @@ -125,7 +126,7 @@ initPegasusTransport();

## Security risks while communicating with injected script

The following note only applies if and only if, you will be sending/receiving messages to/from `window` contexts. There's no security concern if you will be only working with `content-script`, `background`, `popup`, `options`, or `devtools` scope, which is the default setting.
The following note only applies if and only if, you will be sending/receiving messages to/from `window` contexts. There's no security concern if you will be only working with `content-script`, `background`, `popup`, `sidepanel`, `options`, or `devtools` scope, which is the default setting.

`window` context(s) in tab `A` get unlocked the moment you call `initPegasusTransport({allowWindowMessagingForNamespace: 'TEST'})` in your extension's content script AND `initPegasusTransport({namespace: 'TEST'})` in your injected script.

Expand All @@ -142,7 +143,7 @@ import { onMessage } from '@webext-pegasus/transport/background';

onMessage("getUserBrowsingHistory", (message) => {
const { data, sender } = message;
// Respond only if request is from 'devtools', 'content-script', 'popup', 'options', or 'background' endpoint
// Respond only if request is from 'devtools', 'content-script', 'popup', 'sidepanel', 'options', or 'background' endpoint
});
```

Expand Down
9 changes: 8 additions & 1 deletion packages/transport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
],
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"tsup-build": "tsup index.ts background.ts content-script.ts devtools.ts popup.ts window.ts options.ts --format cjs,esm --dts",
"tsup-build": "tsup index.ts background.ts content-script.ts devtools.ts popup.ts sidepanel.ts window.ts options.ts --format cjs,esm --dts",
"build": "npm run tsup-build --dts-resolve",
"prepublishOnly": "npm run build"
},
Expand Down Expand Up @@ -47,6 +47,10 @@
"import": "./dist/popup.js",
"require": "./dist/popup.cjs"
},
"./sidepanel": {
"import": "./dist/sidepanel.js",
"require": "./dist/sidepanel.cjs"
},
"./window": {
"import": "./dist/window.js",
"require": "./dist/window.cjs"
Expand All @@ -72,6 +76,9 @@
"popup": [
"dist/popup.d.ts"
],
"sidepanel": [
"dist/sidepanel.d.ts"
],
"window": [
"dist/window.d.ts"
]
Expand Down
33 changes: 33 additions & 0 deletions packages/transport/sidepanel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import browser from 'webextension-polyfill';

import {createBroadcastEventRuntime} from './src/BroadcastEventRuntime';
import {createMessageRuntime} from './src/MessageRuntime';
import {createPersistentPort} from './src/PersistentPort';
import {initTransportAPI} from './src/TransportAPI';
import {internalPacketTypeRouter} from './src/utils/internalPacketTypeRouter';

export function initPegasusTransport(): void {
const port = createPersistentPort('sidepanel');
const messageRuntime = createMessageRuntime('sidepanel', async (message) =>
port.postMessage(message),
);

port.onMessage((packet) =>
internalPacketTypeRouter(packet, {eventRuntime, messageRuntime}),
);

const eventRuntime = createBroadcastEventRuntime(
'sidepanel',
async (event) => {
port.postMessage(event);
},
);

initTransportAPI({
browser: browser,
emitBroadcastEvent: eventRuntime.emitBroadcastEvent,
onBroadcastEvent: eventRuntime.onBroadcastEvent,
onMessage: messageRuntime.onMessage,
sendMessage: messageRuntime.sendMessage,
});
}
1 change: 1 addition & 0 deletions packages/transport/src/isInternalEndpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const internalEndpoints: RuntimeContext[] = [
'content-script',
'options',
'popup',
'sidepanel',
];

export const isInternalEndpoint = ({context: ctx}: Endpoint): boolean =>
Expand Down
1 change: 1 addition & 0 deletions packages/transport/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export type RuntimeContext =
| 'devtools'
| 'background'
| 'popup'
| 'sidepanel'
| 'options'
| 'content-script'
| 'window';
Expand Down
4 changes: 2 additions & 2 deletions packages/transport/src/utils/endpoint-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {Endpoint, RuntimeContext} from '../types';

const ENDPOINT_RE =
/^((?:background$)|devtools|popup|options|content-script|window)(?:@(\d+)(?:\.(\d+))?)?$/;
/^((?:background$)|devtools|popup|sidepanel|options|content-script|window)(?:@(\d+)(?:\.(\d+))?)?$/;

Comment on lines 3 to 5
Copy link

Copilot AI Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENDPOINT_RE allows endpoints without an @tabId suffix (e.g. sidepanel, popup, background). In deserializeEndpoint, those cases currently produce tabId: NaN (because +tabId where tabId is undefined), which violates Endpoint.tabId: number | null and can leak into routing logic (e.g. deserializeEndpoint(connArgs.endpointName) in background.ts). Consider setting tabId to null when the capture group is missing (and optionally rejecting missing tabId for contexts that require it).

Copilot uses AI. Check for mistakes.
export const deserializeEndpoint = (endpoint: string): Endpoint => {
const [, context, tabId, frameId] = endpoint.match(ENDPOINT_RE) || [];
Expand All @@ -18,7 +18,7 @@ export const serializeEndpoint = ({
tabId,
frameId,
}: Endpoint): string => {
if (['background', 'popup', 'options'].includes(context)) {
if (['background', 'popup', 'sidepanel', 'options'].includes(context)) {
return context;
}

Expand Down
Loading