### Basic Parent-to-Iframe Setup Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Example of setting up communication from a parent window to an iframe. Use this when the parent needs to send messages or call functions within an iframe. ```typescript // Parent window const iframe = document.getElementById('my-iframe') as HTMLIFrameElement; const parentCommutator = new Commutator({ targetWindow: iframe.contentWindow }); parentCommutator.call('iframeFunction'); ``` -------------------------------- ### Basic Iframe-to-Parent Setup Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Example of setting up communication from an iframe to its parent window. Use this when an iframe needs to send messages or call functions in the parent. ```typescript // Iframe window const iframeCommutator = new Commutator(); // Assumes parent is listening iframeCommutator.call('parentFunction'); ``` -------------------------------- ### Basic Initialization Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Demonstrates the basic setup for initializing Commutator with default options. Suitable for simple communication needs. ```typescript const commutator = new Commutator(); ``` -------------------------------- ### Basic Parent-to-iframe Setup Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Example of initializing Commutator in the parent window to communicate with an iframe. It shows how to set up the serviceId and target, and expose a function. ```typescript import { Commutator } from 'commutator'; // In parent window const iframe = document.getElementById('child-frame'); const rpc = new Commutator({ serviceId: 'my-service', target: iframe.contentWindow, }); // Register exposed functions rpc.expose('add', (data) => data.a + data.b); ``` -------------------------------- ### Install Commutator Source: https://github.com/avin/commutator/blob/master/README.md Install the Commutator package using npm. ```bash npm install commutator ``` -------------------------------- ### Basic Iframe-to-Parent Setup Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Example of initializing Commutator within an iframe to communicate with the parent window. It demonstrates making an RPC call to a function exposed by the parent. ```typescript // In iframe (child) import { Commutator } from 'commutator'; const rpc = new Commutator({ serviceId: 'my-service', target: window.parent, }); // Make RPC calls to parent const result = await rpc.call('add', { a: 10, b: 20 }); console.log(result); // 30 ``` -------------------------------- ### Connection State Management Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to monitor and manage the connection state between contexts. Use to react to connection establishment, disconnection, or errors. ```typescript commutator.on('connect', () => console.log('Connected')); commutator.on('disconnect', () => console.log('Disconnected')); ``` -------------------------------- ### Popup Window Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates setting up communication with a popup window opened via `window.open()`. Use when interacting with newly opened browser windows. ```typescript const popup = window.open('popup.html'); const popupCommutator = new Commutator({ targetWindow: popup }); popupCommutator.call('initialize'); ``` -------------------------------- ### Progress Reporting Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Demonstrates how to report progress from a long-running remote task. Use this to provide feedback to the caller during extended operations. ```typescript // Exposing a task that reports progress commutator.expose({ async taskWithProgress(reportProgress) { for (let i = 0; i < 10; i++) { await new Promise(resolve => setTimeout(resolve, 500)); reportProgress(i / 10); } return 'finished'; } }); // Calling and handling progress commutator.call('taskWithProgress', (progress) => { console.log(`Progress: ${progress * 100}%`); }); ``` -------------------------------- ### Middleware/Interceptor Pattern Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates implementing middleware or interceptors for request/response modification or logging. Use to add cross-cutting concerns to communication. ```typescript commutator.use((next, method, args) => { console.log(`Calling ${method} with args:`, args); return next(...args).then(result => { console.log(`Method ${method} returned:`, result); return result; }); }); ``` -------------------------------- ### Multiple Service Instances Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates setting up multiple independent Commutator instances. Useful when you need to manage distinct communication channels or contexts simultaneously. ```typescript const instance1 = new Commutator({ id: "instance1" }); const instance2 = new Commutator({ id: "instance2" }); ``` -------------------------------- ### Timeout Handling Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to implement timeout handling for remote calls. Use this pattern to prevent indefinite waiting for responses. ```typescript try { await commutator.call("longRunningTask", { timeout: 5000 }); } catch (error) { // Handle timeout error } ``` -------------------------------- ### Bidirectional Communication Example Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Demonstrates bidirectional communication where both parent and iframe can expose and call functions using Commutator. ```typescript // Parent const rpc = new Commutator({ target: iframe.contentWindow, serviceId: 'sync' }); rpc.expose('syncData', (data) => { console.log('Parent received:', data); return { acknowledged: true }; }); rpc.call('childReady', {}).then(() => console.log('Child is ready')); // Iframe const rpc = new Commutator({ target: window.parent, serviceId: 'sync' }); rpc.expose('childReady', () => { console.log('Child notifying parent'); return true; }); rpc.call('syncData', { timestamp: Date.now(), userId: 123 }); ``` -------------------------------- ### Commutator Initialization Examples Source: https://github.com/avin/commutator/blob/master/_autodocs/types.md Demonstrates how to initialize a Commutator instance with basic configuration and with an origin restriction for enhanced security. Ensure the target window and origin are correctly specified. ```typescript import { Commutator } from 'commutator'; // Basic configuration const rpc = new Commutator({ serviceId: 'auth-service', target: window.parent, }); // With origin restriction for security const secureRpc = new Commutator({ serviceId: 'secure-api', target: document.getElementById('iframe').contentWindow, origin: 'https://api.example.com', }); ``` -------------------------------- ### Secure Configuration Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to configure Commutator for secure communication, likely involving specific security-related options. Use when security is a primary concern. ```typescript const commutator = new Commutator({ // secure options here }); ``` -------------------------------- ### Service Worker Communication Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to establish communication between a web page and a service worker. Use for background tasks and offline capabilities. ```typescript // In the main page navigator.serviceWorker.ready.then(registration => { const swCommutator = new Commutator({ targetWindow: registration.active?.postMessage ? registration.active : undefined }); swCommutator.call('serviceWorkerTask'); }); // In the service worker self.addEventListener('message', event => { const commutator = new Commutator(); // Assuming message is from the page commutator.expose({ serviceWorkerTask: () => {} }); }); ``` -------------------------------- ### Async Function Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to expose and call asynchronous functions using Commutator. Use when remote operations involve promises or async/await. ```typescript // Exposing an async function commutator.expose({ async asyncRemoteFn() { await new Promise(resolve => setTimeout(resolve, 100)); return 'done'; } }); // Calling an async function const result = await commutator.call('asyncRemoteFn'); ``` -------------------------------- ### Dynamic Target Window Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Demonstrates configuring Commutator to communicate with a dynamically determined target window. Use when the target window is not known at initialization time. ```typescript const targetWindow = window.open("..."); const commutator = new Commutator({ targetWindow }); ``` -------------------------------- ### Simple Function Call Example (Caller Side) Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Demonstrates how to initiate a remote function call using `rpc.call()` and the expected parameters. The call is asynchronous and returns a promise that resolves with the result. ```typescript await rpc.call('add', { a: 5, b: 3 }); ``` -------------------------------- ### Typed RPC Wrapper Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to create type-safe wrappers around Commutator calls. Use for improved developer experience and compile-time safety. ```typescript interface RemoteApi { getUser(id: number): Promise<{ name: string }>; } const typedCommutator = commutator as unknown as RemoteApi; typedCommutator.getUser(123).then(user => console.log(user.name)); ``` -------------------------------- ### Simple Function Call Example (Prefixed and Sent Message) Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Illustrates the final string format of the request message as it is sent via `postMessage`. It includes the service ID prefix followed by the JSON-stringified request object. ```string "my-service::{\"type\":\"req\",\"funcName\":\"add\",\"params\":{\"a\":5,\"b\":3},\"id\":\"K8jQp2xRmN\"}" ``` -------------------------------- ### Parent-to-Iframe Communication Setup Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Set up Commutator to enable communication between a parent window and an iframe. The parent exposes functions for the iframe to call. ```typescript import { Commutator } from 'commutator'; const iframeEl = document.getElementById('myIframe'); const rpc = new Commutator({ target: iframeEl.contentWindow, serviceId: 'app-api', }); // Expose functions that the iframe can call rpc.expose('getConfig', () => ({ apiUrl: 'https://api.example.com', theme: 'dark', })); rpc.expose('log', (message) => { console.log('[iframe]:', message); }); ``` ```typescript import { Commutator } from 'commutator'; const rpc = new Commutator({ target: window.parent, serviceId: 'app-api', }); async function initialize() { const config = await rpc.call('getConfig', {}); console.log('Config:', config); await rpc.call('log', 'Iframe loaded'); } initialize(); ``` -------------------------------- ### Commutator Message Payload Example Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Provides an example of the JSON stringified message object payload used within Commutator messages, showing request details. ```plaintext my-service::{"type":"req","funcName":"getData","params":{"id":123},"id":"aBc1DeF2Ghi"} ``` -------------------------------- ### Secure Commutator Initialization with Origin Restriction Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Demonstrates secure Commutator setup by specifying an explicit origin for enhanced security, recommended for production environments. ```typescript // Recommended for production: explicitly specify origin const rpc = new Commutator({ serviceId: 'secure-api', target: iframe.contentWindow, origin: 'https://api.example.com', }); ``` -------------------------------- ### Iframe Removal Handling Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how to handle scenarios where an iframe might be removed from the DOM while communication is active. Use to gracefully manage communication channels. ```typescript // Add a listener for iframe removal or check its existence before calling const iframe = document.getElementById('my-iframe') as HTMLIFrameElement; if (iframe && iframe.contentWindow) { const commutator = new Commutator({ targetWindow: iframe.contentWindow }); commutator.call('someMethod'); } else { console.warn('Iframe not found or removed.'); } ``` -------------------------------- ### Simple Function Call Example (Request Message) Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Shows the structure of the request message object sent from the caller to the target. It includes the message type, function name, parameters, and a unique ID for tracking. ```json { "type": "req", "funcName": "add", "params": { "a": 5, "b": 3 }, "id": "K8jQp2xRmN" } ``` -------------------------------- ### Retry with Backoff Pattern Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates implementing a retry mechanism with exponential backoff for remote calls. Use this to handle transient network issues or temporary service unavailability. ```typescript async function reliableCall() { try { await commutator.call("remoteOperation"); } catch (error) { // Implement retry logic with backoff setTimeout(reliableCall, 1000); } } ``` -------------------------------- ### Date Handling Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows how Commutator handles JavaScript Date objects during serialization and deserialization. Dates are typically converted to ISO strings and back. ```typescript const date = new Date(); commutator.call('receiveDate', date); commutator.expose({ receiveDate(d) { console.log(d instanceof Date); // true } }); ``` -------------------------------- ### Request Correlation Tracking Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Demonstrates tracking requests using unique IDs. Essential for matching responses to their original requests, especially in asynchronous scenarios. ```typescript const requestId = Commutator.makeId(); commutator.call('process', { id: requestId, data: '...' }); commutator.on('message', (message) => { if (message.id === requestId) { console.log('Received response for request:', message.data); } }); ``` -------------------------------- ### Simple Function Call Example (Response Message) Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Presents the structure of the response message object sent back from the target to the caller. It contains the result of the function execution or an error, along with the original message ID. ```json { "type": "res", "result": 8, "error": null, "id": "K8jQp2xRmN" } ``` -------------------------------- ### Try-Catch Error Handling Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Shows basic error handling using try-catch blocks for remote calls. Essential for robust applications to manage potential failures. ```typescript try { await commutator.call('riskyOperation'); } catch (error) { console.error('Operation failed:', error); } ``` -------------------------------- ### Commutator Message Format Example Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Illustrates the expected format of messages sent by Commutator, which includes the serviceId followed by a JSON stringified message object. ```plaintext {serviceId}::{JSON stringified message object} ``` -------------------------------- ### Long-running Task Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates handling long-running tasks across contexts. Use when a remote operation may take a significant amount of time to complete. ```typescript // Exposing a long-running task commutator.expose({ async longTask() { // Simulate work await new Promise(resolve => setTimeout(resolve, 5000)); return 'task completed'; } }); // Calling the long-running task commutator.call('longTask').then(result => console.log(result)); ``` -------------------------------- ### Conditional Error Handling Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates handling specific types of errors conditionally. Use when different error types require distinct recovery or logging strategies. ```typescript try { await commutator.call('operation'); } catch (error) { if (error.code === 'REMOTE_ERROR') { // Handle remote-specific error } else { // Handle other errors } } ``` -------------------------------- ### CommutatorOptions Type Definition Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Defines the structure and fields for CommutatorOptions, used for configuring Commutator instances, including types, defaults, and usage examples. ```APIDOC ## CommutatorOptions Type Definition ### Type Definition * **`CommutatorOptions`** * Represents the configuration options for a Commutator instance. ### Fields * **`target`** (Window | MessagePort | Worker) - Required - The target window, MessagePort, or Worker to communicate with. * **`origin`** (string) - Required - The expected origin of the target window for security. * **`timeout`** (number) - Optional - Default: `5000` - The timeout in milliseconds for remote calls. * **`retry`** (object) - Optional - Configuration for retrying failed calls. * **`maxAttempts`** (number) - Optional - Maximum number of retry attempts. * **`delay`** (number) - Optional - Delay in milliseconds between retries. * **`onTimeout`** (Function) - Optional - Callback function executed when a call times out. * **`onUnauthorized`** (Function) - Optional - Callback function executed for unauthorized access attempts. * **`onUnexpectedResponse`** (Function) - Optional - Callback function for handling unexpected responses. ### Usage Examples * Basic initialization example. * Secure configuration example. * Multiple service instances example. * Dynamic target window example. ``` -------------------------------- ### Complex Objects Serialization Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Demonstrates passing and receiving complex JavaScript objects. Commutator handles serialization/deserialization, including nested objects and arrays. ```typescript const complexObject = { data: { value: 123 }, items: [1, 2, 3] }; commutator.call('processObject', complexObject); commutator.expose({ processObject(obj) { console.log(obj.data.value); } }); ``` -------------------------------- ### Circular Reference Avoidance Example Source: https://github.com/avin/commutator/blob/master/_autodocs/MANIFEST.txt Illustrates how Commutator handles or avoids issues with circular references in objects. Use when dealing with complex object graphs that might contain cycles. ```typescript const objA: any = {}; const objB: any = {}; objA.ref = objB; objB.ref = objA; try { commutator.call('process', objA); // Commutator should handle this gracefully } catch (e) { console.error('Error processing circular reference:', e); } ``` -------------------------------- ### Commutator Project File Structure Source: https://github.com/avin/commutator/blob/master/_autodocs/DELIVERY_SUMMARY.md Illustrates the directory structure of the Commutator project, highlighting the location of key documentation files. ```bash ``` /workspace/home/output/ ├── README.md ← Start here ├── api-reference/ │ └── commutator.md ← API reference ├── types.md ← Type definitions ├── configuration.md ← Setup and options ├── errors.md ← Error catalog ├── architecture.md ← Internal design ├── usage-patterns.md ← Real-world examples └── DELIVERY_SUMMARY.md ← This file ``` ``` -------------------------------- ### Commutator Constructor and Usage Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Demonstrates how to instantiate the Commutator class for making RPC calls and how to clean it up. ```APIDOC ## Commutator Constructor ### Description Initializes a new Commutator instance to facilitate communication with a target window. ### Parameters #### Request Body - **options** (CommutatorOptions) - Required - The configuration object for the Commutator. - **target** (Window) - Required - The target window to communicate with. - **serviceId** (string) - Required - The identifier for the service. - **origin** (string) - Optional - The expected origin of the target window. ### Example ```typescript const rpc = new Commutator({ target: iframe.contentWindow, serviceId: 'my-service', }); // Use the RPC... await rpc.call('doSomething', {}); // Clean up when done rpc.destroy(); ``` ``` -------------------------------- ### Initialize and Use Commutator Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Demonstrates how to initialize a Commutator instance with a target iframe and service ID, make an RPC call, and then destroy the instance. ```typescript const rpc = new Commutator({ target: iframe.contentWindow, serviceId: 'my-service', }); // Use the RPC... await rpc.call('doSomething', {}); // Clean up when done rpc.destroy(); // Cannot use rpc anymore after this ``` -------------------------------- ### Commutator Initialization with Dynamic Target Window Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Shows how to configure Commutator when the target window is determined at runtime, such as when opening a new popup window. ```typescript // Get target window at runtime function getTargetWindow() { const popup = window.open('https://example.com/popup'); return popup; } const rpc = new Commutator({ serviceId: 'popup-service', target: getTargetWindow(), }); ``` -------------------------------- ### Commutator Class Methods Overview Source: https://github.com/avin/commutator/blob/master/_autodocs/README.md This table outlines the primary methods available on the Commutator class, their signatures, and their intended purposes. Use these methods to initialize connections, make remote calls, expose local functions, and manage the RPC lifecycle. ```typescript constructor (options: CommutatorOptions) call (funcName: string, params: any): Promise expose (funcName: string, cb: (params: any) => any): void unexpose (funcName: string, cb: (params: any) => any): void destroy (): void makeId (length?: number): string ``` -------------------------------- ### Parent Window Initialization and Expose Source: https://github.com/avin/commutator/blob/master/_autodocs/README.md Initialize Commutator in the parent window and expose a function to be called by an iframe. Ensure the target window and serviceId are correctly configured. ```typescript import { Commutator } from 'commutator'; const rpc = new Commutator({ target: document.getElementById('myIframe').contentWindow, serviceId: 'my-service', }); rpc.expose('add', (data) => data.a + data.b); ``` -------------------------------- ### Open and Communicate with a Popup Window Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Open a new popup window and establish communication using Commutator. Ensure the target window is ready before sending messages, potentially using a timeout. ```typescript const popup = window.open('/popup.html', 'my-popup', 'width=800,height=600'); const rpc = new Commutator({ target: popup, serviceId: 'popup-api', origin: window.location.origin, }); // Wait for popup to initialize setTimeout(async () => { const data = await rpc.call('getData', {}); console.log('Data from popup:', data); }, 1000); ``` -------------------------------- ### Expose Method in Parent Window Source: https://github.com/avin/commutator/blob/master/README.md In the parent window, initialize Commutator and expose a method to be called by iframes. Ensure a unique serviceId is used for each producer-consumer pair. ```javascript import { Commutator } from 'commutator'; const rpc = new Commutator({ // The window you want to talk to: target: myIframe.contentWindow, // This should be unique for each of your producer<->consumer pairs: serviceId: 'my-awesome-service', }); rpc.expose('add', (data) => data.a + data.b); ``` -------------------------------- ### Handling Dates in Communication Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Send and receive Date objects by converting them to ISO strings before sending and parsing them back into Date objects upon receiving. ```typescript // Sender const record = { id: 1, createdAt: new Date().toISOString(), // Send as ISO string }; await rpc.call('saveRecord', record); // Receiver rpc.expose('saveRecord', (record) => { const createdAt = new Date(record.createdAt); // Parse back to Date console.log('Created:', createdAt.toLocaleDateString()); return { success: true }; }); ``` -------------------------------- ### expose Method Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Registers a function to be callable remotely via RPC. Multiple handlers can be exposed for the same function name; all will be invoked sequentially. ```APIDOC ## expose ### Description Registers a function to be callable remotely via RPC. Multiple handlers can be exposed for the same function name; all will be invoked sequentially. ### Method `expose` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **funcName** (string) - Required - The name that remote callers will use to invoke this function - **cb** ((params: any) => any) - Required - The handler function; receives parameters from remote caller and can return a value or throw an error ### Request Example ```typescript // Expose simple synchronous function rpc.expose('add', (data) => data.a + data.b); // Expose async function rpc.expose('fetchData', async (data) => { const response = await fetch(`/api/data/${data.id}`); return response.json(); }); // Expose function with error handling rpc.expose('divide', (data) => { if (data.divisor === 0) { throw new Error('Division by zero'); } return data.dividend / data.divisor; }); // Multiple handlers for same function rpc.expose('log', (msg) => console.log('Handler 1:', msg)); rpc.expose('log', (msg) => console.log('Handler 2:', msg)); ``` ### Response None ### Throws None ``` -------------------------------- ### Registering Handlers After Commutator Initialization Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Shows how to dynamically expose functions to be called via RPC after the Commutator instance has already been initialized and potentially used. ```typescript const rpc = new Commutator({ serviceId: 'api', target: window.parent }); // Not exposed yet // await rpc.call('getData', {}); // Will hang // Later, expose it rpc.expose('getData', async (params) => { return await fetchData(params.id); }); // Now the call works await rpc.call('getData', { id: 1 }); ``` -------------------------------- ### Iframe Initialization and Call Source: https://github.com/avin/commutator/blob/master/_autodocs/README.md Initialize Commutator in an iframe to communicate with the parent window. Call an exposed function and handle the asynchronous result. ```typescript import { Commutator } from 'commutator'; const rpc = new Commutator({ target: window.parent, serviceId: 'my-service', }); const result = await rpc.call('add', { a: 3, b: 5 }); console.log(result); // 8 ``` -------------------------------- ### Handling Simultaneous Calls in Commutator Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Illustrates a scenario where both parent and child windows might initiate RPC calls concurrently, highlighting that messages are processed in order but race conditions can occur. ```typescript // Both sides might call each other simultaneously // Parent sends a call to child rpc.call('childMethod', {}); // Child sends a call to parent rpc.call('parentMethod', {}); // Both are processed, both will eventually receive responses ``` -------------------------------- ### Call Method from Iframe Source: https://github.com/avin/commutator/blob/master/README.md In an iframe, initialize Commutator to communicate with the parent window and call an exposed method. Handle the asynchronous result using Promises. ```javascript import { Commutator } from 'commutator'; const rpc = new Commutator({ target: window.parent, serviceId: 'my-awesome-service', }); rpc.call('add', { a: 3, b: 5 }).then(result => console.log('3 + 5 is', result)); ``` -------------------------------- ### Expose Multiple Handlers for the Same Function Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Demonstrates how to register multiple handlers for a single function name. All handlers are executed sequentially, but only the result of the first one is returned. ```typescript rpc.expose('process', handler1); rpc.expose('process', handler2); rpc.expose('process', handler3); // When 'process' is called, handler1, handler2, and handler3 all execute // But only the first handler's result is sent back as the response ``` -------------------------------- ### call Method Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Makes an asynchronous RPC call to a function exposed by the remote endpoint. ```APIDOC ## call ### Description Makes an asynchronous RPC call to a function exposed by the remote endpoint. ### Method `call` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **funcName** (string) - Required - Name of the exposed function to call on the remote side - **params** (any) - Required - Parameters to pass to the remote function (must be JSON-serializable) ### Request Example ```typescript // Calling a remote function try { const result = await rpc.call('add', { a: 3, b: 5 }); console.log('Result:', result); // 8 } catch (err) { console.error('RPC call failed:', err); } // With typed results const typedResult = await rpc.call('calculate', { operation: 'sum' }); ``` ### Response #### Success Response (T) - **T** - The result from the remote function. ### Throws - **Error** - If the remote function throws an error, the error object is reconstructed and rejected in the promise. ``` -------------------------------- ### Initialize Commutator Instance Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Initializes a new Commutator instance. Use this in both the parent window and the iframe, providing the target window and a unique service ID. An optional origin can be specified for security. ```typescript import { Commutator } from 'commutator'; // In parent window const rpc = new Commutator({ target: document.getElementById('myIframe').contentWindow, serviceId: 'my-awesome-service', origin: 'https://trusted-domain.com' }); // In iframe const rpc = new Commutator({ target: window.parent, serviceId: 'my-awesome-service', }); ``` -------------------------------- ### Multiple Commutator Service Instances Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Illustrates how to manage multiple independent Commutator instances on the same page by assigning unique serviceIds to each. ```typescript // Service A: handles authentication const authRpc = new Commutator({ serviceId: 'auth-service', target: authFrame.contentWindow, }); // Service B: handles data processing const dataRpc = new Commutator({ serviceId: 'data-service', target: dataFrame.contentWindow, }); // Messages are automatically routed based on serviceId ``` -------------------------------- ### Commutator Constructor Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Initializes a new Commutator instance with the specified configuration for RPC communication. ```APIDOC ## Constructor Commutator ### Description Initializes a new Commutator instance with the specified configuration. ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **options** (CommutatorOptions) - Required - Configuration object for the RPC connection - **serviceId** (string) - Required - Unique identifier for this producer-consumer pair; used to namespace messages - **target** (Window) - Required - Target window object to communicate with (e.g., `iframe.contentWindow` or `window.parent`) - **origin** (string) - Optional - Origin constraint for postMessage security; defaults to accepting all origins ### Request Example ```typescript import { Commutator } from 'commutator'; // In parent window const rpc = new Commutator({ target: document.getElementById('myIframe').contentWindow, serviceId: 'my-awesome-service', origin: 'https://trusted-domain.com' }); // In iframe const rpc = new Commutator({ target: window.parent, serviceId: 'my-awesome-service', }); ``` ### Response None ### Throws None ``` -------------------------------- ### Maintain Connection State with RemoteService Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Use a class to manage the connection state of a remote service. Ensure the service is connected before sending data and handle connection failures. ```typescript class RemoteService { private rpc: Commutator; private connected = false; constructor(target) { this.rpc = new Commutator({ target, serviceId: 'service-api', }); } async connect() { try { await this.rpc.call('ping', {}); this.connected = true; } catch { this.connected = false; throw new Error('Failed to connect'); } } async sendData(data) { if (!this.connected) { throw new Error('Not connected'); } return await this.rpc.call('sendData', data); } disconnect() { this.rpc.destroy(); this.connected = false; } } const service = new RemoteService(iframe.contentWindow); await service.connect(); await service.sendData({ message: 'hello' }); service.disconnect(); ``` -------------------------------- ### Commutator Source Code Reference Source: https://github.com/avin/commutator/blob/master/_autodocs/README.md Details about the main source file for the Commutator implementation. It highlights the primary export, internal types, and key methods available within the library. ```text - src/index.ts (130 lines) - Exports: Commutator class - Internal types: CommutatorOptions, ReqMessageObject, ResMessageObject - Key methods: constructor, call, expose, unexpose, destroy, static makeId ``` -------------------------------- ### Progress Reporting via Multiple Calls Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Implement progress reporting for long-running tasks by having the background processor make multiple calls to the caller to report progress. ```typescript // Background processor rpc.expose('processFile', async (file) => { let progress = 0; for (let i = 0; i < 100; i++) { // Do work progress = i + 1; await rpc.call('reportProgress', { progress, total: 100 }); } return { status: 'complete' }; }); // Caller rpc.expose('reportProgress', (data) => { console.log(`Progress: ${data.progress}/${data.total}`); }); const result = await rpc.call('processFile', { file: 'document.pdf' }); ``` -------------------------------- ### Commutator Class Reference Source: https://github.com/avin/commutator/blob/master/_autodocs/INDEX.txt Reference for the Commutator class, which facilitates RPC communication between browser windows. It includes details on the constructor, methods for making calls, exposing functions, unexposing functions, destroying the instance, and generating unique IDs. ```APIDOC ## Commutator Class ### Description The Commutator class provides a robust mechanism for establishing Remote Procedure Calls (RPC) between different browser contexts, such as iframes or windows. ### Constructor - **`constructor(options: CommutatorOptions)`** - Initializes a new Commutator instance. - **Parameters**: - `options` (CommutatorOptions) - Configuration object for the Commutator instance. ### Methods #### `call(funcName: string, params: any): Promise` - **Description**: Invokes a remote function exposed by another Commutator instance. - **Method**: RPC Call - **Parameters**: - `funcName` (string) - The name of the function to call on the remote instance. - `params` (any) - The parameters to pass to the remote function. - **Returns**: A Promise that resolves with the return value of the remote function, or rejects if an error occurs. #### `expose(funcName: string, cb: (params: any) => any): void` - **Description**: Exposes a local function to be callable by remote Commutator instances. - **Method**: Function Exposure - **Parameters**: - `funcName` (string) - The name under which to expose the function. - `cb` ((params: any) => any) - The callback function to be executed when the remote function is called. #### `unexpose(funcName: string, cb: (params: any) => any): void` - **Description**: Removes a previously exposed function, making it no longer callable by remote instances. - **Method**: Function Removal - **Parameters**: - `funcName` (string) - The name of the function to unexpose. - `cb` ((params: any) => any) - The specific callback function to remove (must match the one provided during `expose`). #### `destroy(): void` - **Description**: Cleans up the Commutator instance, removing all event listeners and exposed functions. - **Method**: Instance Cleanup #### `static makeId(length?: number): string` - **Description**: Generates a unique identifier string. - **Method**: Utility Function - **Parameters**: - `length` (number, optional) - The desired length of the ID. Defaults to a standard length. - **Returns**: A unique string identifier. ### Properties - **`options: CommutatorOptions`** - The configuration options used to initialize the Commutator instance. ``` -------------------------------- ### Expose and Call a Risky Function Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Demonstrates how to expose a function that might throw an error and then call it. The library handles the serialization of the error on the target side and its reconstruction on the caller side. ```typescript rpc.expose('risky', () => { throw new Error('Something went wrong'); }); await rpc.call('risky', {}); ``` -------------------------------- ### Implementing Middleware with RpcWrapper Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Use RpcWrapper to add pre-call, post-call, and error handling middleware to RPC calls. This allows for cross-cutting concerns like logging or validation. ```typescript class RpcWrapper { constructor(private rpc: Commutator) {} async call(funcName, params, middleware = []) { let finalParams = params; // Pre-call middleware for (const mw of middleware) { finalParams = await mw.before?.(finalParams) ?? finalParams; } try { const result = await this.rpc.call(funcName, finalParams); // Post-call middleware let finalResult = result; for (const mw of middleware) { finalResult = await mw.after?.(finalResult) ?? finalResult; } return finalResult; } catch (err) { for (const mw of middleware) { await mw.error?.(err); } throw err; } } expose = this.rpc.expose; destroy = this.rpc.destroy; } // Usage const wrapped = new RpcWrapper(rpc); const loggingMiddleware = { before: async (params) => (console.log('Sending:', params), params), after: async (result) => (console.log('Received:', result), result), error: async (err) => console.error('Error:', err), }; const result = await wrapped.call('process', { data: '...' }, [loggingMiddleware]); ``` -------------------------------- ### Async Execution Wrapper for Handlers Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Shows how even synchronous exposed handlers are wrapped in an async IIFE (Immediately Invoked Function Expression). This ensures consistent asynchronous behavior and proper error handling. ```typescript void (async () => { try { const result = await cb(dataObj.params); // sync cb wrapped in await // ...send response } catch (err) { // ...send error } })(); ``` -------------------------------- ### Implementing Custom Timeout for RPC Calls Source: https://github.com/avin/commutator/blob/master/_autodocs/configuration.md Demonstrates how to implement a custom timeout mechanism for Commutator's `call()` method using `Promise.race` to prevent promises from hanging indefinitely. ```javascript const timeout = (promise, ms) => Promise.race([ promise, new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), ms)) ]); const result = await timeout(rpc.call('method', {}), 5000); ``` -------------------------------- ### Creating a Typed RPC Client Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Implement an interface to define your API and use TypedRpc to create a type-safe client for making remote procedure calls. This ensures that arguments and return types are correctly handled. ```typescript interface ApiClient { add(a: number, b: number): Promise; getData(id: string): Promise<{ id: string; name: string }>; } class TypedRpc implements ApiClient { constructor(private rpc: Commutator) { // Setup exposed functions on remote this.rpc.expose('add', (params) => params.a + params.b); } add(a: number, b: number): Promise { return this.rpc.call('add', { a, b }); } getData(id: string) { return this.rpc.call('getData', { id }); } destroy() { this.rpc.destroy(); } } const client = new TypedRpc(rpc); const sum = await client.add(5, 3); // Type-safe const data = await client.getData('123'); // Returns { id, name } ``` -------------------------------- ### Handling Origin Mismatch in Commutator Source: https://github.com/avin/commutator/blob/master/_autodocs/errors.md Demonstrates setting the correct origin to avoid silent message ignores. Use `window.location.origin` to ensure the target origin matches the current page's origin. ```typescript // In iframe on https://subdomain.example.com const rpc = new Commutator({ target: window.parent, serviceId: 'api', origin: 'https://example.com', // Different from iframe origin }); // This call will never receive a response await rpc.call('method', {}); // Hangs indefinitely (promise never resolves) ``` ```typescript // Ensure origin matches exactly const rpc = new Commutator({ target: window.parent, serviceId: 'api', origin: window.location.origin, // Use current page origin }); ``` -------------------------------- ### Communicate with Multiple Service Instances Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Instantiate Commutator multiple times with different `serviceId` values to communicate with distinct services running in separate iframes or windows. ```typescript // Service 1: Authentication const authRpc = new Commutator({ serviceId: 'auth-service', target: document.getElementById('auth-iframe').contentWindow, }); // Service 2: Data Processing const dataRpc = new Commutator({ serviceId: 'data-service', target: document.getElementById('data-iframe').contentWindow, }); // Service 3: Logging const logRpc = new Commutator({ serviceId: 'log-service', target: document.getElementById('log-iframe').contentWindow, }); // Each service maintains independent communication const token = await authRpc.call('authenticate', { user: 'alice' }); const result = await dataRpc.call('process', { token, data: [...] }); await logRpc.call('log', { level: 'info', message: 'Processed' }); ``` -------------------------------- ### Commutator Class Constructor Source: https://github.com/avin/commutator/blob/master/_autodocs/INDEX.txt Initializes a new Commutator instance. Options can configure various aspects of the communication, such as timeouts and security. ```typescript new Commutator(options: CommutatorOptions) ``` -------------------------------- ### Commutator Class API Source: https://github.com/avin/commutator/blob/master/_autodocs/DELIVERY_SUMMARY.md Reference for the Commutator class methods and properties. ```APIDOC ## Commutator Class ### Description Provides the core functionality for inter-window communication. ### Methods #### constructor(options: CommutatorOptions) - **Purpose**: Initializes a new Commutator instance. - **Parameters**: - `options` (CommutatorOptions) - Required - Configuration options for the Commutator. #### call(funcName, params): Promise - **Purpose**: Calls a remote function exposed by another window. - **Parameters**: - `funcName` (string) - The name of the function to call. - `params` (any) - The parameters to pass to the remote function. - **Returns**: A Promise that resolves with the return value of the remote function. - **Error Conditions**: Handles remote function errors and serialization issues. #### expose(funcName, cb): void - **Purpose**: Exposes a local function to be called by other windows. - **Parameters**: - `funcName` (string) - The name to expose the function under. - `cb` (function) - The callback function to execute. #### unexpose(funcName, cb): void - **Purpose**: Removes an exposed function. - **Parameters**: - `funcName` (string) - The name of the function to unexpose. - `cb` (function) - The callback function that was originally exposed. #### destroy(): void - **Purpose**: Cleans up and destroys the Commutator instance. ### Static Methods #### makeId(length?): string - **Purpose**: Generates a unique identifier. - **Parameters**: - `length` (number) - Optional - The desired length of the ID. - **Returns**: A unique string identifier. ### Properties #### options: CommutatorOptions - **Description**: Contains the configuration options used to initialize the Commutator. ``` -------------------------------- ### CommutatorOptions Type Definition Source: https://github.com/avin/commutator/blob/master/_autodocs/types.md Defines the configuration object for initializing a Commutator instance. It includes service ID, target window, and an optional origin for security. ```typescript type CommutatorOptions = { serviceId: string; target: Window; origin?: string; }; ``` -------------------------------- ### Error Handling with Try-Catch Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Use a try-catch block to handle potential errors when calling remote functions. This pattern ensures that errors are caught and logged gracefully. ```typescript async function safeCall() { try { const result = await rpc.call('riskyOperation', { data: 'value' }); console.log('Success:', result); } catch (err) { console.error('Operation failed:', err.message); } } safeCall(); ``` -------------------------------- ### Handle RPC Cleanup When Iframes Are Removed Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Use a MutationObserver to detect when an iframe is removed from the DOM. If detected, destroy the associated Commutator instance to clean up resources. ```typescript const observer = new MutationObserver(() => { // If iframe is removed from DOM, destroy RPC if (!document.contains(iframe)) { rpc.destroy(); observer.disconnect(); } }); observer.observe(document.body, { childList: true, subtree: true }); ``` -------------------------------- ### Generate Random IDs with Commutator.makeId Source: https://github.com/avin/commutator/blob/master/_autodocs/api-reference/commutator.md Shows how to use the static makeId method to generate random alphanumeric strings for message IDs. You can specify the desired length. ```typescript const id1 = Commutator.makeId(); // Example: "aBcDeFg1Hi" const id2 = Commutator.makeId(20); // Example: "aBcDeFg1Hi2JkLmN3OpQ" ``` -------------------------------- ### Commutator State Management: exposeListenersMap Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Shows the TypeScript type definition for exposeListenersMap, which stores function names and their associated handlers for managing exposed functions. This is crucial for the unexpose() functionality. ```typescript private exposeListenersMap: Record any,(p:any) => any][]> = {}; ``` -------------------------------- ### Commutator State Management: emitter Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Defines the internal event bus using the 'mitt' library. This emitter is fundamental for handling requests, responses, and broadcasting events throughout the library. ```typescript private emitter!: Emitter; ``` -------------------------------- ### Implement Request-Reply with Correlation IDs Source: https://github.com/avin/commutator/blob/master/_autodocs/usage-patterns.md Use correlation IDs to match requests with their replies. The sender tracks pending requests, and the receiver includes the requestId in its response. ```typescript class Request { id = Commutator.makeId(); timestamp = Date.now(); constructor(public funcName: string, public params: any) {} } // Sender tracks outgoing requests const pendingRequests = new Map(); async function sendRequest(funcName, params) { const req = new Request(funcName, params); pendingRequests.set(req.id, req); try { const result = await rpc.call(funcName, { ...params, requestId: req.id }); return result; } finally { pendingRequests.delete(req.id); } } // Receiver handles request tracking rpc.expose('process', async (params) => { return { requestId: params.requestId, result: 'processed', timestamp: Date.now(), }; }); ``` -------------------------------- ### Commutator Message Routing Event Prefixes Source: https://github.com/avin/commutator/blob/master/_autodocs/architecture.md Defines the event naming conventions used for routing requests and responses within the Commutator library. Use these prefixes when emitting or listening for internal events. ```typescript this.emitter.emit(`req_getData`, dataObj); this.emitter.emit(`res_123abc`, dataObj); ```