### Install Dependencies
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Install project dependencies using npm.
```bash
npm install
```
--------------------------------
### Install Penpal via npm
Source: https://github.com/aaronius/penpal/blob/main/README.md
Install Penpal using npm. This is the recommended method for most projects.
```bash
npm install penpal
```
--------------------------------
### Install Git Hooks
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Installs Husky git hooks for the project.
```bash
npm run prepare
```
--------------------------------
### Parent Window Communication Setup
Source: https://github.com/aaronius/penpal/blob/main/README.md
Set up communication from the parent window to an iframe. This involves creating a `WindowMessenger` and connecting to the iframe.
```javascript
import { WindowMessenger, connect } from 'penpal';
const iframe = document.createElement('iframe');
iframe.src = 'https://childorigin.example.com/path/to/iframe.html';
document.body.appendChild(iframe);
const messenger = new WindowMessenger({
remoteWindow: iframe.contentWindow,
// Defaults to the current origin.
allowedOrigins: ['https://childorigin.example.com'],
// Alternatively,
// allowedOrigins: [new Url(iframe.src).origin]
});
const connection = connect({
messenger,
// Methods the parent window is exposing to the iframe window.
methods: {
add(num1, num2) {
return num1 + num2;
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const multiplicationResult = await remote.multiply(2, 6);
console.log(multiplicationResult); // 12
const divisionResult = await remote.divide(12, 4);
console.log(divisionResult); // 3
```
--------------------------------
### Opened Window Communication Setup
Source: https://github.com/aaronius/penpal/blob/main/README.md
Use this snippet in the opened child window to connect to the parent window (opener). It exposes methods like 'multiply' and 'divide' to the parent and calls methods exposed by the parent.
```javascript
import { WindowMessenger, connect } from 'penpal';
const messenger = new WindowMessenger({
remoteWindow: window.opener,
// Defaults to the current origin.
allowedOrigins: ['https://parentorigin.example.com'],
});
const connection = connect({
messenger,
// Methods the child window is exposing to the parent (opener) window.
methods: {
multiply(num1, num2) {
return num1 * num2;
},
divide(num1, num2) {
// Return a promise if asynchronous processing is needed.
return new Promise((resolve) => {
setTimeout(() => {
resolve(num1 / num2);
}, 1000);
});
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const additionResult = await remote.add(2, 6);
console.log(additionResult); // 8
```
--------------------------------
### Parent Window Communication Setup
Source: https://github.com/aaronius/penpal/blob/main/README.md
Use this snippet in the parent window to open a child window and establish a messenger connection. It exposes methods like 'add' to the child window and calls methods exposed by the child.
```javascript
import { WindowMessenger, connect } from 'penpal';
const windowUrl = 'https://childorigin.example.com/path/to/window.html';
const childWindow = window.open(windowUrl);
const messenger = new WindowMessenger({
remoteWindow: childWindow,
// Defaults to the current origin.
allowedOrigins: ['https://childorigin.example.com'],
// Alternatively,
// allowedOrigins: [new Url(windowUrl).origin]
});
const connection = connect({
messenger,
// Methods the parent window is exposing to the child window.
methods: {
add(num1, num2) {
return num1 + num2;
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const multiplicationResult = await remote.multiply(2, 6);
console.log(multiplicationResult); // 12
const divisionResult = await remote.divide(12, 4);
console.log(divisionResult); // 3
```
--------------------------------
### Create General Methods for Parent Communication
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/general.html
This snippet demonstrates how to create a set of general methods that can be exposed to a parent window. It includes functions for getting the parent API, setting and getting return values, handling unclonable values, creating replies, and controlling window navigation and reloads.
```javascript
let parentReturnValue;
let parentApiPromise;
const methods = PenpalGeneralFixtureMethods.createGeneralMethods({
getParentApi: () => parentApiPromise,
setParentReturnValue: (value) => {
parentReturnValue = value;
},
getParentReturnValue: () => {
return parentReturnValue;
},
getUnclonableValue: () => {
return window;
},
createReply: (value, options) => {
return new Penpal.Reply(value, options);
},
reload: () => {
window.location.reload(true);
},
navigate: (to) => {
window.location.href = to;
},
});
parentApiPromise = PenpalFixture.connect({
methods,
allowedOrigins: ['*'],
}).promise;
```
--------------------------------
### Child Window Setup with Penpal
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fileProtocol/child.html
Sets up a Penpal WindowMessenger in the child window to communicate with the parent. It exposes a 'multiply' method and enables debug logging.
```javascript
const messenger = new Penpal.WindowMessenger({
remoteWindow: window.parent,
allowedOrigins: ['*'],
});
Penpal.connect({
messenger,
methods: {
multiply: function (num1, num2) {
return num1 * num2;
},
},
log: Penpal.debug('Child'),
});
```
--------------------------------
### Iframe Window Communication Setup
Source: https://github.com/aaronius/penpal/blob/main/README.md
Set up communication from an iframe window to its parent. This involves creating a `WindowMessenger` and connecting to the parent.
```javascript
import { WindowMessenger, connect } from 'penpal';
const messenger = new WindowMessenger({
remoteWindow: window.parent,
// Defaults to the current origin.
allowedOrigins: ['https://parentorigin.example.com'],
});
const connection = connect({
messenger,
// Methods the iframe window is exposing to the parent window.
methods: {
multiply(num1, num2) {
return num1 * num2;
},
divide(num1, num2) {
// Return a promise if asynchronous processing is needed.
return new Promise((resolve) => {
setTimeout(() => {
resolve(num1 / num2);
}, 1000);
});
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const additionResult = await remote.add(2, 6);
console.log(additionResult); // 8
```
--------------------------------
### Transferring Data in a Method Reply
Source: https://github.com/aaronius/penpal/blob/main/README.md
When responding to a method call, return an instance of Reply with the `transferables` option set to specify objects to be transferred. This example shows transferring a result array back to the caller.
```javascript
import { connect, Reply } from 'penpal';
...
const connection = connect({
messenger,
methods: {
double(numbersArray) {
// numbersArray and resultArray are both Int32Arrays
const resultArray = numbersArray.map(num => num * 2);
return new Reply(resultArray, {
transferables: [resultArray.buffer],
});
}
},
});
```
--------------------------------
### Transferring Data to a Remote Method
Source: https://github.com/aaronius/penpal/blob/main/README.md
When calling a remote method, pass an instance of CallOptions as the last argument with the `transferables` option set to specify objects to be transferred instead of cloned. This example demonstrates transferring an Int32Array.
```javascript
import { connect, CallOptions } from 'penpal';
...
const connection = connect({
messenger
});
const remote = await connection.promise;
const numbersArray = new Int32Array(new ArrayBuffer(8));
numbersArray[0] = 4;
numbersArray[1] = 5;
const multiplicationResultArray = await remote.double(
numbersArray,
new CallOptions({ transferables: [numbersArray.buffer] })
);
console.log(multiplicationResultArray[0]); // 8
console.log(multiplicationResultArray[1]); // 10
```
--------------------------------
### Connecting with Mismatched Parent Origin Regex
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/backwardCompatibility/mismatchedParentOriginRegex.html
This snippet shows the Penpal connection setup where the `parentOrigin` is defined as a regular expression that will not match the actual parent origin. This scenario is used to test backward compatibility and ensure Penpal correctly rejects such connections.
```javascript
PenpalLegacyFixture.connectToParent({
parentOrigin: /example\.com/,
methods: {
multiply: function (num1, num2) {
return num1 * num2;
},
},
});
```
--------------------------------
### Run Main Browser Test Suite (WebKit)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes the main browser test suite using WebKit.
```bash
npm run test:webkit:browser
```
--------------------------------
### Build Project
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Builds ESM, CJS, and IIFE bundles, including a minified IIFE bundle.
```bash
npm run build
```
--------------------------------
### Launch Changeset Prompt
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Launches the interactive prompt to create a .changeset/*.md entry.
```bash
npm run changeset
```
--------------------------------
### Download and Vendor Legacy Build
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Downloads and vendors a Penpal 6 build, used for backward-compatibility fixtures.
```bash
npm run test:legacy:vendor -- <6.x.x-version>
```
--------------------------------
### Run Main Browser Test Suite (Chromium)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes the main browser test suite using Chromium.
```bash
npm run test:chromium:browser
```
--------------------------------
### Run Prepublish Checks
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs formatting, linting, Chromium tests, type tests, and build before publishing.
```bash
npm run prepublishOnly
```
--------------------------------
### Analyze Build Size
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Prints the minified bundle size analysis for dist/penpal.min.js.
```bash
npm run build:analysis
```
--------------------------------
### Run Main Browser Test Suite (Firefox)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes the main browser test suite using Firefox.
```bash
npm run test:firefox:browser
```
--------------------------------
### Add a Changeset
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Create a changeset file for changes that should trigger a release.
```bash
npx changeset
```
--------------------------------
### Run Tests
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Execute the test suite, which includes unit, browser, and file-protocol tests.
```bash
npm test
```
--------------------------------
### Initialize Penpal in the Window Context
Source: https://github.com/aaronius/penpal/blob/main/README.md
This snippet shows how to initialize Penpal in the main browser window to communicate with a Service Worker. It sets up a MessageChannel, posts the worker port to the Service Worker, and defines methods exposed to the worker. It also demonstrates calling methods exposed by the Service Worker.
```javascript
import { PortMessenger, connect } from 'penpal';
const initPenpal = async () => {
const { port1, port2 } = new MessageChannel();
navigator.serviceWorker.controller?.postMessage(
{
type: 'INIT_PENPAL',
port: port2,
},
{
transfer: [port2],
}
);
const messenger = new PortMessenger({
port: port1,
});
const connection = connect({
messenger,
// Methods the window is exposing to the worker.
methods: {
add(num1, num2) {
return num1 + num2;
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const multiplicationResult = await remote.multiply(2, 6);
console.log(multiplicationResult); // 12
const divisionResult = await remote.divide(12, 4);
console.log(divisionResult); // 3
};
if (navigator.serviceWorker.controller) {
initPenpal();
}
navigator.serviceWorker.addEventListener('controllerchange', initPenpal);
navigator.serviceWorker.register('service-worker.js');
```
--------------------------------
### Run File-Protocol Tests (WebKit)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes file-protocol tests specifically in WebKit.
```bash
npm run test:file:webkit
```
--------------------------------
### Run Browser and File-Protocol Tests (WebKit)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs the WebKit browser suite along with WebKit file-protocol tests.
```bash
npm run test:webkit
```
--------------------------------
### Run Main Browser Test Suite (Edge)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes the main browser test suite using the Edge channel via Playwright.
```bash
npm run test:edge:browser
```
--------------------------------
### Run Browser and File-Protocol Tests (Edge)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs the Edge browser suite along with Edge file-protocol tests.
```bash
npm run test:edge
```
--------------------------------
### Run File-Protocol Tests (Edge)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes file-protocol tests specifically in Edge.
```bash
npm run test:file:edge
```
--------------------------------
### Run File-Protocol Tests (Chromium)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes file-protocol tests specifically in Chromium.
```bash
npm run test:file:chromium
```
--------------------------------
### Run All Browsers Full Test Matrix
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes the full test commands for Chromium, Firefox, Edge, and WebKit.
```bash
npm run test:all-browsers
```
--------------------------------
### Run Browser and File-Protocol Tests (Chromium)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs the Chromium browser suite along with Chromium file-protocol tests.
```bash
npm run test:chromium
```
--------------------------------
### Window to Worker Communication with Penpal
Source: https://github.com/aaronius/penpal/blob/main/README.md
Set up communication from a window to a worker. Expose methods from the window to the worker and call methods on the worker.
```javascript
import { WorkerMessenger, connect } from 'penpal';
const worker = new Worker('worker.js');
const messenger = new WorkerMessenger({
worker,
});
const connection = connect({
messenger,
// Methods the window is exposing to the worker.
methods: {
add(num1, num2) {
return num1 + num2;
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const multiplicationResult = await remote.multiply(2, 6);
console.log(multiplicationResult); // 12
const divisionResult = await remote.divide(12, 4);
console.log(divisionResult); // 3
```
--------------------------------
### connect(options: Object)
Source: https://github.com/aaronius/penpal/blob/main/README.md
Establishes a connection for cross-origin or cross-process communication. It allows exposing local methods to a remote context and provides a proxy to call remote methods.
```APIDOC
## connect(options: Object) => Object
### Description
Establishes a connection for cross-origin or cross-process communication. It allows exposing local methods to a remote context and provides a proxy to call remote methods.
### Parameters
#### Options
- **messenger** (Messenger) - Required - A messenger instance that handles message transmission. Available messengers include `WindowMessenger`, `WorkerMessenger`, and `PortMessenger`.
- **methods** (Object) - Optional - An object containing methods to be exposed to the remote. Keys are method names, and values are functions. Nested objects with functions are supported. Asynchronous methods should return a promise.
- **timeout** (number) - Optional - The time in milliseconds to wait for a connection to establish before rejecting. Defaults to no timeout.
- **channel** (string) - Optional - A string identifier to disambiguate communication for parallel connections.
- **log** ((...args: unknown[]) => void) - Optional - A function that Penpal will call to log debugging information. Logging only occurs if this is defined.
### Return value
The `connect` function returns a `Connection` object with the following properties:
- **promise** (Promise) - A promise that resolves with a proxy object for remote methods once communication is established. Method calls on this proxy return promises.
- **destroy** (() => void) - A method to disconnect messaging channels and event listeners. Can be called before a connection is established.
```
--------------------------------
### Run File-Protocol Tests
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs file-protocol tests using the browser selected by the BROWSER environment variable.
```bash
npm run test:file
```
--------------------------------
### Run Browser and File-Protocol Tests (Firefox)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs the Firefox browser suite along with Firefox file-protocol tests.
```bash
npm run test:firefox
```
--------------------------------
### Worker to Window Communication with Penpal
Source: https://github.com/aaronius/penpal/blob/main/README.md
Set up communication from a worker to a window. Expose methods from the worker to the window and call methods on the window. Supports asynchronous operations via Promises.
```javascript
import { WorkerMessenger, connect } from 'penpal';
const messenger = new WorkerMessenger({
worker: self,
});
const connection = connect({
messenger,
// Methods the worker is exposing to the window.
methods: {
multiply(num1, num2) {
return num1 * num2;
},
divide(num1, num2) {
// Return a promise if asynchronous processing is needed.
return new Promise((resolve) => {
setTimeout(() => {
resolve(num1 / num2);
}, 1000);
});
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const additionResult = await remote.add(2, 6);
console.log(additionResult); // 8
```
--------------------------------
### Initialize Penpal in the Service Worker Context
Source: https://github.com/aaronius/penpal/blob/main/README.md
This snippet demonstrates how to set up Penpal within a Service Worker to communicate with the main browser window. It listens for initialization messages, sets up a MessageChannel, and defines methods that the Service Worker will expose to the window. It also shows how to call methods exposed by the window.
```javascript
import { PortMessenger, connect } from 'penpal';
self.addEventListener('install', () => self.skipWaiting());
self.addEventListener('activate', () => self.clients.claim());
self.addEventListener('message', async (event) => {
if (event.data?.type !== 'INIT_PENPAL') {
return;
}
const { port } = event.data;
const messenger = new PortMessenger({
port,
});
const connection = connect({
messenger,
// Methods worker is exposing to window.
methods: {
multiply(num1, num2) {
return num1 * num2;
},
divide(num1, num2) {
// Return a promise if asynchronous processing is needed.
return new Promise((resolve) => {
setTimeout(() => {
resolve(num1 / num2);
}, 1000);
});
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const additionResult = await remote.add(2, 6);
console.log(additionResult); // 8
});
```
--------------------------------
### Run File-Protocol Tests (Firefox)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes file-protocol tests specifically in Firefox.
```bash
npm run test:file:firefox
```
--------------------------------
### Run ESLint with Autofix
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs ESLint with autofix and cache enabled for code linting.
```bash
npm run lint
```
--------------------------------
### Run Prettier Formatting
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs Prettier on JSON, TS, JS, CJS, Markdown, and HTML files.
```bash
npm run format
```
--------------------------------
### Shared Worker to Window Communication with Penpal
Source: https://github.com/aaronius/penpal/blob/main/README.md
Set up communication from a Shared Worker to a window. Expose methods from the Shared Worker to the window and call methods on the window. Handles asynchronous operations using Promises.
```javascript
import { PortMessenger, connect } from 'penpal';
self.addEventListener('connect', async (event) => {
const [port] = event.ports;
const messenger = new PortMessenger({
port,
});
const connection = connect({
messenger,
// Methods the worker is exposing to the window.
methods: {
multiply(num1, num2) {
return num1 * num2;
},
divide(num1, num2) {
// Return a promise if asynchronous processing is needed.
return new Promise((resolve) => {
setTimeout(() => {
resolve(num1 / num2);
}, 1000);
});
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const additionResult = await remote.add(2, 6);
console.log(additionResult); // 8
});
```
--------------------------------
### Run Unit Tests
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Executes fast Node-based unit tests located in the test/unit directory.
```bash
npm run test:unit
```
--------------------------------
### Parent Window: Establish Parallel Connections
Source: https://github.com/aaronius/penpal/blob/main/README.md
This snippet shows how to establish two distinct parallel connections from a parent window to an iframe using separate messenger instances and unique channel identifiers ('A' and 'B'). Ensure each connection uses its own messenger.
```javascript
import { WindowMessenger, connect } from 'penpal';
const iframe = document.createElement('iframe');
iframe.src = 'https://childorigin.example.com/iframe.html';
document.body.appendChild(iframe);
const messengerA = new WindowMessenger({
remoteWindow: iframe.contentWindow,
allowedOrigins: ['https://childorigin.example.com'],
});
const connectionA = connect({
messenger: messengerA,
channel: 'A',
methods: {
add(num1, num2) {
return num1 + num2;
},
},
});
// Note that each call to connect() needs a separate messenger instance.
const messengerB = new WindowMessenger({
remoteWindow: iframe.contentWindow,
allowedOrigins: ['https://childorigin.example.com'],
});
const connectionB = connect({
messenger: messengerB,
channel: 'B',
methods: {
subtract(num1, num2) {
return num1 - num2;
},
},
});
```
--------------------------------
### Load Penpal via CDN
Source: https://github.com/aaronius/penpal/blob/main/README.md
Load Penpal from a CDN for direct use in HTML. Penpal will be available as a global variable `window.Penpal`.
```html
```
--------------------------------
### Iframe Window: Establish Parallel Connections
Source: https://github.com/aaronius/penpal/blob/main/README.md
This snippet demonstrates how an iframe window can establish two distinct parallel connections back to its parent window, utilizing separate messenger instances and unique channel identifiers ('A' and 'B'). Each connection requires its own messenger.
```javascript
import { WindowMessenger, connect } from 'penpal';
const messengerA = new WindowMessenger({
remoteWindow: window.parent,
allowedOrigins: ['https://parentorigin.example.com'],
});
const connectionA = connect({
messenger: messengerA,
channel: 'A',
methods: {
multiply(num1, num2) {
return num1 * num2;
},
},
});
// Note that each call to connect() needs a separate messenger instance.
const messengerB = new WindowMessenger({
remoteWindow: iframe.contentWindow,
allowedOrigins: ['https://parentorigin.example.com'],
});
const connectionB = connect({
messenger: messengerB,
channel: 'B',
methods: {
divide(num1, num2) {
return num1 / num2;
},
},
});
```
--------------------------------
### Connect Child Frame B to Parent
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/channels.html
Establishes a Penpal connection for child frame 'B', defining methods for communication and specifying allowed origins and a log label.
```javascript
const channelBParentPromise = PenpalFixture.connect({
channel: 'B',
methods: {
getChannel() {
return 'B';
},
getChannelFromParent() {
return channelBParentPromise.then((parent) => parent.getChannel());
},
},
allowedOrigins: ['*'],
logLabel: 'Child Connection B',
}).promise;
```
--------------------------------
### Connect Window to Worker with TypeScript Types
Source: https://github.com/aaronius/penpal/blob/main/README.md
Demonstrates connecting a window to a worker using Penpal with TypeScript generics for type safety. The generic type argument ensures the `remote` object has correctly typed methods.
```typescript
import { WorkerMessenger, connect } from 'penpal';
// This interace could be in a module imported by both the window and worker.
interface WorkerApi {
multiply(...args: number[]): number;
}
const worker = new Worker('worker.js');
const messenger = new WorkerMessenger({
worker,
});
// Note we're passing in WorkerApi as a generic type argument.
const connection = connect({
messenger,
});
// This `remote` object will contain properly typed methods.
const remote = await connection.promise;
// This `multiplicationResult` constant will be properly typed as a number.
const multiplicationResult = await remote.multiply(2, 6);
```
--------------------------------
### Parent Window Communication with Iframe
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fileProtocol/parent.html
This code snippet initializes Penpal communication from a parent window to an iframe. It sets up the messenger, connects to the child, and calls a method on the child to perform a calculation, displaying the result.
```html
window.addEventListener('load', function () {
const iframe = document.createElement('iframe');
iframe.src = 'child.html';
document.body.appendChild(iframe);
const messenger = new Penpal.WindowMessenger({
remoteWindow: iframe.contentWindow,
allowedOrigins: ['*'],
});
Penpal.connect({
messenger,
log: Penpal.debug('Parent'),
}).promise.then(function (child) {
child.multiply(3, 2).then((result) => {
const div = document.createElement('div');
div.textContent = '3 X 2 = ' + result;
document.body.appendChild(div);
});
});
});
```
--------------------------------
### Publish Release with Changesets
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Publishes the release using Changesets in a CI environment with npm trusted publishing.
```bash
npm run release:publish
```
--------------------------------
### Setting Connection Timeouts
Source: https://github.com/aaronius/penpal/blob/main/README.md
Configure a connection timeout in milliseconds to prevent prolonged connection attempts. The promise rejects if the timeout is exceeded.
```javascript
import { ErrorCode } from 'penpal';
...
const connection = connect({
messenger,
timeout: 5000 // 5 seconds
});
try {
const remote = await connection.promise;
} catch (error) {
if (error.code === ErrorCode.ConnectionTimeout) {
// Connection failed due to timeout.
}
}
```
--------------------------------
### Add an Empty Changeset
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Create an empty changeset file for changes that should not trigger a release.
```bash
npx changeset --empty
```
--------------------------------
### Connect to Parent with Matching Origin
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/backwardCompatibility/matchingParentOrigin.html
Connect to the parent frame by first retrieving its origin and then using it in the connection configuration. This method ensures that the connection is established only if the origins match.
```javascript
const parentOrigin = PenpalLegacyFixture.getParentOrigin();
PenpalLegacyFixture.connectToParent({
parentOrigin,
methods: {
multiply: function (num1, num2) {
return num1 * num2;
},
},
});
```
--------------------------------
### Run Vitest in Watch Mode (Chromium)
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs Vitest Browser Mode in watch mode using Chromium.
```bash
npm run test:watch:chromium
```
--------------------------------
### Connect Child Frame A to Parent
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/channels.html
Establishes a Penpal connection for child frame 'A', defining methods for communication and specifying allowed origins and a log label.
```javascript
const channelAParentPromise = PenpalFixture.connect({
channel: 'A',
methods: {
getChannel() {
return 'A';
},
getChannelFromParent() {
return channelAParentPromise.then((parent) => parent.getChannel());
},
},
allowedOrigins: ['*'],
logLabel: 'Child Connection A',
}).promise;
```
--------------------------------
### Debugging with Penpal's built-in logger
Source: https://github.com/aaronius/penpal/blob/main/README.md
Use Penpal's `debug` function for logging during development. Pass a prefix to distinguish log origins.
```javascript
import { connect, debug } from 'penpal';
...
const connection = connect({
messenger,
log: debug('parent')
});
```
--------------------------------
### Check Prettier Formatting
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs Prettier in check mode (no writes), suitable for CI environments.
```bash
npm run format:check
```
--------------------------------
### Window to Shared Worker Communication with Penpal
Source: https://github.com/aaronius/penpal/blob/main/README.md
Establish communication from a window to a Shared Worker. Expose methods from the window to the Shared Worker and invoke methods on the Shared Worker.
```javascript
import { PortMessenger, connect } from 'penpal';
const worker = new SharedWorker('shared-worker.js');
const messenger = new PortMessenger({
port: worker.port,
});
const connection = connect({
messenger,
// Methods the window is exposing to the worker.
methods: {
add(num1, num2) {
return num1 + num2;
},
},
});
const remote = await connection.promise;
// Calling a remote method will always return a promise.
const multiplicationResult = await remote.multiply(2, 6);
console.log(multiplicationResult); // 12
const divisionResult = await remote.divide(12, 4);
console.log(divisionResult); // 3
```
--------------------------------
### Advanced Debugging with the 'debug' package
Source: https://github.com/aaronius/penpal/blob/main/README.md
Integrate the 'debug' npm package for more sophisticated logging. This allows for fine-grained control over log output.
```javascript
import debug from 'debug';
import { connect } from 'penpal';
...
const connection = connect({
messenger,
log: debug('penpal:parent')
});
```
--------------------------------
### Check ESLint without Autofix
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs ESLint in check mode (no autofix), suitable for CI environments.
```bash
npm run lint:check
```
--------------------------------
### Connect with Regex Allowed Origin
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/matchingParentOriginRegex.html
Connects to the parent window, allowing communication only from origins that match the generated regular expression. This is useful for flexible origin matching.
```javascript
const parentOrigin = PenpalFixture.getParentOrigin();
const parentOriginRegex = new RegExp( PenpalFixture.escapeRegExp(parentOrigin) );
PenpalFixture.connect({
methods: {
multiply(num1, num2) {
return num1 * num2;
},
},
allowedOrigins: [parentOriginRegex],
});
```
--------------------------------
### Connect to Parent with Origin Regex
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/backwardCompatibility/matchingParentOriginRegex.html
Connects to the parent frame using a regular expression for the parent's origin. This is useful for flexible origin matching, ensuring compatibility with various parent configurations.
```javascript
const parentOrigin = PenpalLegacyFixture.getParentOrigin();
const parentOriginRegex = new RegExp( PenpalLegacyFixture.escapeRegExp(parentOrigin) );
PenpalLegacyFixture.connectToParent({
parentOrigin: parentOriginRegex,
methods: {
multiply: function (num1, num2) {
return num1 * num2;
},
},
});
```
--------------------------------
### Redirect based on URL parameter
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/backwardCompatibility/redirect.html
Extracts the 'to' parameter from the URL and redirects. Defaults to 'general.html' if the parameter is not present.
```javascript
const params = new URL(document.location).searchParams;
document.location.href = params.get('to') || 'general.html';
```
--------------------------------
### Version Packages with Changesets
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Applies pending changesets by updating package versions and changelogs.
```bash
npm run changeset:version
```
--------------------------------
### Handle Connection Destroyed Probe Messages
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/connectionDestroyedProbe.html
Listens for 'REQUEST_ADD_USING_PARENT' messages, calls a parent API method, and posts the result or an error back to the parent window. Ensure the Penpal library is loaded and the fixture is correctly configured.
```javascript
let parentApiPromise;
parentApiPromise = PenpalFixture.connect({
methods: {},
allowedOrigins: ['*'],
}).promise;
window.addEventListener('message', async (event) => {
const message = event.data;
if (
!message ||
typeof message !== 'object' ||
message.fixture !== 'connectionDestroyedProbe' ||
message.type !== 'REQUEST_ADD_USING_PARENT'
) {
return;
}
try {
const parentApi = await parentApiPromise;
const result = await parentApi.add(3, 6);
window.parent.postMessage(
{
fixture: 'connectionDestroyedProbe',
type: 'RESULT',
result,
},
'*'
);
} catch (error) {
window.parent.postMessage(
{
fixture: 'connectionDestroyedProbe',
type: 'RESULT_ERROR',
code: error?.code,
},
'*'
);
}
});
```
--------------------------------
### Check Changesets
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Validates that at least one changeset exists for changed packages since origin/main.
```bash
npm run changeset:check
```
--------------------------------
### Run Tests in Watch Mode
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Alias for running Vitest Browser Mode in watch mode using Chromium.
```bash
npm run test:watch
```
--------------------------------
### Connect with a Method Not in General Page
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/backwardCompatibility/methodNotInGeneralPage.html
Connects to the parent frame and defines a method 'methodNotInGeneralPage' directly in the connection configuration. This is useful for backward compatibility or for methods specific to this connection.
```javascript
PenpalLegacyFixture.connectToParent({
parentOrigin: "*",
methods: {
methodNotInGeneralPage() {
return 'success';
},
},
});
```
--------------------------------
### Import Penpal Exported Types
Source: https://github.com/aaronius/penpal/blob/main/README.md
Shows how to import core types provided by Penpal for use in your TypeScript projects. These types help define the structure of connections and remote objects.
```typescript
import { Connection, Methods, RemoteProxy } from 'penpal';
```
--------------------------------
### Run TypeScript Type Tests
Source: https://github.com/aaronius/penpal/blob/main/CONTRIBUTING.md
Runs TypeScript type tests using the tsconfig.typesTest.json configuration.
```bash
npm run test:types
```
--------------------------------
### Connect with Allowed Origins Regex
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/mismatchedParentOriginRegex.html
Use this to establish a Penpal connection where the parent's origin must match a specific regular expression. This is useful for testing scenarios with flexible origin validation.
```javascript
PenpalFixture.connect({
methods: {
multiply(num1, num2) {
return num1 * num2;
},
},
allowedOrigins: [/example\.com/],
});
```
--------------------------------
### Setting Method Call Timeouts
Source: https://github.com/aaronius/penpal/blob/main/README.md
Specify a timeout for remote method calls using `CallOptions`. The method call promise will reject if the response is not received within the specified time.
```javascript
import { CallOptions, ErrorCode } from 'penpal';
...
const remote = await connection.promise;
try {
const multiplicationResult =
await remote.multiply(2, 6, new CallOptions({ timeout: 1000 }));
} catch (error) {
if (error.code === ErrorCode.MethodCallTimeout) {
// Method call failed due to timeout.
}
}
```
--------------------------------
### WindowMessenger
Source: https://github.com/aaronius/penpal/blob/main/README.md
Facilitates communication between two browser windows, such as a parent window and an iframe, or a window and its opener.
```APIDOC
### WindowMessenger
Supports communication between two windows.
#### Constructor Options
- **remoteWindow** (Window) - Required - A reference to the remote window for communication.
- **allowedOrigins** ((string | RegExp)[]) - Optional - An array of allowed origins for communication. Defaults to the current document's origin. Can be set to `*` to allow any origin, but this is discouraged due to security risks.
```
--------------------------------
### Connect to Parent Origin with Allowed Origins
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/matchingParentOrigin.html
Establish a connection to the parent origin, specifying allowed origins for secure communication. This is useful when an iframe needs to communicate with its parent window.
```javascript
const parentOrigin = PenpalFixture.getParentOrigin(); PenpalFixture.connect({
methods: {
multiply(num1, num2) {
return num1 * num2;
},
},
allowedOrigins: [parentOrigin],
});
```
--------------------------------
### Define Methods for Parent Communication
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/backwardCompatibility/general.html
Define an object containing methods that can be called by the parent frame. This includes synchronous, asynchronous, and promise-based methods.
```javascript
let parentAPI;
let parentReturnValue;
const methods = {
multiply: function (num1, num2) {
return num1 * num2;
},
multiplyAsync: function (num1, num2) {
return new Promise(function (resolve) {
resolve(num1 * num2);
});
},
addUsingParent: function () {
return parentAPI.add(3, 6).then(function (value) {
parentReturnValue = value;
});
},
getParentReturnValue: function () {
return parentReturnValue;
},
getPromiseRejectedWithString() {
return Promise.reject('test error string');
},
getPromiseRejectedWithObject() {
return Promise.reject({ a: 'b' });
},
getPromiseRejectedWithUndefined() {
return Promise.reject();
},
getPromiseRejectedWithError() {
return Promise.reject(new TypeError('test error object'));
},
throwError: function () {
throw new Error('Oh nos!');
},
getUnclonableValue: function () {
return window;
},
reload: function () {
window.location.reload(true);
},
navigate: function (to) {
window.location.href = to;
},
nested: {
oneLevel: function (input) {
return input;
},
by: {
twoLevels: function (input) {
return input;
},
},
},
neverResolve() {
return new Promise(() => {});
},
};
PenpalLegacyFixture.connectToParent({
methods,
}).promise.then(function (parent) {
parentAPI = parent;
});
```
--------------------------------
### Define Custom Method in Penpal Connection
Source: https://github.com/aaronius/penpal/blob/main/test/browser/fixtures/pages/methodNotInGeneralPage.html
This snippet shows how to define a custom method `methodNotInGeneralPage` within the `methods` object when establishing a Penpal connection. This is useful for creating specific functionalities not covered by the general API.
```javascript
PenpalFixture.connect({
methods: {
methodNotInGeneralPage() {
return 'success';
},
},
allowedOrigins: ['*'],
});
```
--------------------------------
### Import and Reference Penpal Error Codes
Source: https://github.com/aaronius/penpal/blob/main/README.md
Import the `ErrorCode` enum from 'penpal' to reference specific error codes programmatically. This allows for conditional logic based on the type of error encountered.
```javascript
import { ErrorCode } from 'penpal';
// ErrorCode.ConnectionDestroyed
// ErrorCode.ConnectionTimeout
// ErrorCode.InvalidArgument
// ErrorCode.MethodCallTimeout
// ErrorCode.MethodNotFound
// ErrorCode.TransmissionFailed
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.