### 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.