@vgerbot/web-rpc
Version:
A TypeScript library that provides type-safe Remote Procedure Call (RPC) communication between different JavaScript contexts using various transport mechanisms
1 lines • 54.9 kB
Source Map (JSON)
{"version":3,"sources":["../src/common/isFunction.ts","../src/common/isPlainObject.ts","../src/protocol/Message.ts","../src/core/SendFunctionTransport.ts","../src/common/Defer.ts","../src/protocol/Callback.ts","../src/protocol/Getter.ts","../src/common/deserializeRequestData.ts","../src/common/isTransferable.ts","../src/common/isTypedArray.ts","../src/common/uid.ts","../src/common/serializeRequestData.ts","../src/common/isPromiseLike.ts","../src/core/WebRPCPort.ts","../src/core/WebRPC.ts","../src/transports/index.ts","../src/transports/BrowserExtensionTransport.ts","../src/transports/PostMessageTransport.ts","../src/transports/WindowPostMessageTransport.ts","../src/index.ts"],"sourcesContent":["export function isFunction(value: unknown): value is Function {\n return typeof value === 'function';\n}\n","export function isPlainObject(obj: unknown): obj is Record<string, unknown> {\n if (typeof obj !== 'object' || obj === null) {\n return false;\n }\n\n const proto = Object.getPrototypeOf(obj);\n\n return proto === null || proto === Object.prototype;\n}\n","import { isPlainObject } from '../common/isPlainObject';\nimport type { ErrorInfo } from './ErrorInfo';\nimport type { InvocationId } from './InvocationId';\nimport type { SerializableData } from './SerializableData';\n\nexport interface RPCMessage {\n invocationId: InvocationId;\n action: 'method-call' | 'method-return';\n timestamp: number;\n}\nexport interface CallMethodMessage extends RPCMessage {\n action: 'method-call';\n method: string;\n args: SerializableData[];\n}\nexport interface ReturnMethodMessage extends RPCMessage {\n action: 'method-return';\n status: 'success' | 'error';\n result?: SerializableData;\n error?: ErrorInfo;\n}\nexport function isRPCMessage(data: unknown): data is RPCMessage {\n return (\n isPlainObject(data) &&\n typeof data.invocationId === 'object' &&\n typeof data.action === 'string' &&\n typeof data.timestamp === 'number' &&\n (data.action === 'method-call' || data.action === 'method-return')\n );\n}\n","import type { SerializableData } from '../protocol/SerializableData';\nimport type { Transport } from './Transport';\n\nexport class SendFunctionTransport implements Transport {\n constructor(readonly send: (data: SerializableData, transferables: Transferable[]) => void) {}\n\n onMessage(callback: (data: SerializableData) => void): () => void {\n return () => {\n //\n };\n }\n close(): void {\n //\n }\n}\n","export class Defer<T> {\n private _resolve!: (value: T) => void;\n private _reject!: (reason: unknown) => void;\n private _promise: Promise<T>;\n constructor() {\n this._promise = new Promise((resolve, reject) => {\n this._resolve = resolve;\n this._reject = reject;\n });\n }\n public get promise() {\n return this._promise;\n }\n public resolve(value: T) {\n this._resolve(value);\n }\n public reject(reason: unknown) {\n this._reject(reason);\n }\n}\n","export const CALLBACK_FLAG = '$$WEB-RPC-CALLBACK' as const;\n\nexport interface Callback {\n flag: typeof CALLBACK_FLAG;\n contextId: string;\n id: string;\n}\n\nexport function isCallback(data: unknown): data is Callback {\n return typeof data === 'object' && data !== null && 'flag' in data && data.flag === CALLBACK_FLAG;\n}\n","export const GETTER_FLAG = '$$WEB-RPC-GETTER' as const;\n\nexport interface Getter {\n flag: typeof GETTER_FLAG;\n contextId: string;\n id: string;\n}\n\nexport function isGetter(data: unknown): data is Getter {\n return typeof data === 'object' && data !== null && 'flag' in data && data.flag === GETTER_FLAG;\n}\n","import { isCallback } from '../protocol/Callback';\nimport { isGetter } from '../protocol/Getter';\nimport { isPlainObject } from './isPlainObject';\n\nexport function deserializeRequestData(\n data: unknown[],\n invokeCallback: (callbackId: string, args: unknown[]) => Promise<unknown>\n) {\n // Tracks objects that have already been transformed to handle circular references.\n const handled = new Map<unknown, unknown>();\n\n const transform = (value: unknown) => {\n if (handled.has(value)) {\n return handled.get(value);\n }\n if (isCallback(value)) {\n const callback = (...args: unknown[]) => {\n return invokeCallback(value.id, args);\n };\n handled.set(value, callback);\n return callback;\n }\n if (Array.isArray(value)) {\n const result: unknown[] = [];\n // Immediately cache the array to handle circular references from within its elements.\n handled.set(value, result);\n const arr = value.map(it => {\n if (handled.has(it)) {\n return handled.get(it);\n }\n const result = transform(it);\n handled.set(it, result);\n return result;\n });\n result.push(...arr);\n return result;\n } else if (isPlainObject(value)) {\n const object: Record<string, unknown> = {};\n // Immediately cache the object to handle circular references from within its properties.\n handled.set(value, object);\n\n const descriptors = Object.getOwnPropertyDescriptors(value);\n for (const key in descriptors) {\n const originalValue = value[key];\n if (isGetter(originalValue)) {\n const descriptor = {\n get: () => {\n return invokeCallback(originalValue.id, []);\n },\n };\n Object.defineProperty(object, key, descriptor);\n } else {\n object[key] = transform(originalValue);\n }\n }\n return object;\n }\n return value;\n };\n return data.map(transform);\n}\n","export function isTransferable(value: unknown): value is Transferable {\n return (\n value instanceof ArrayBuffer ||\n value instanceof MessagePort ||\n (globalThis.OffscreenCanvas && value instanceof globalThis.OffscreenCanvas) ||\n (globalThis.ReadableStream && value instanceof globalThis.ReadableStream) ||\n (globalThis.WritableStream && value instanceof globalThis.WritableStream) ||\n (globalThis.TransformStream && value instanceof globalThis.TransformStream) ||\n (globalThis.VideoFrame && value instanceof globalThis.VideoFrame) ||\n (globalThis.ImageBitmap && value instanceof globalThis.ImageBitmap)\n );\n}\n","import type { TypedArray } from './TypedArray';\n\nexport function isTypedArray(value: unknown): value is TypedArray {\n return (\n value instanceof Uint8Array ||\n value instanceof Uint16Array ||\n value instanceof Uint32Array ||\n value instanceof Int8Array ||\n value instanceof Int16Array ||\n value instanceof Int32Array ||\n value instanceof Uint8ClampedArray\n );\n}\n","export default function uid(template = '********') {\n return template.replace(/\\*/g, () => {\n const character = Math.floor(random() * 16).toString(16);\n if (random() >= 0.5) {\n return character;\n } else {\n return character.toUpperCase();\n }\n });\n}\nconst cryptoArray = new Uint32Array(1);\nfunction random() {\n if (typeof crypto === 'undefined') {\n return Math.random();\n }\n const value = crypto.getRandomValues(cryptoArray)[0];\n if (typeof value === 'undefined') {\n return Math.random();\n }\n return value / 0xffffffff;\n}\n","import { CALLBACK_FLAG } from '../protocol/Callback';\nimport { GETTER_FLAG, type Getter } from '../protocol/Getter';\nimport { isPlainObject } from './isPlainObject';\nimport { isTransferable } from './isTransferable';\nimport { isTypedArray } from './isTypedArray';\nimport type { Method } from './Method';\nimport uid from './uid';\n\nexport interface ProcessedRequestDataResult {\n functions: Map<string, Method>;\n transferables: Set<Transferable>;\n data: unknown[];\n}\n\n/**\n * Serializes request data by converting functions and getters to serializable objects\n */\nexport function serializeRequestData(contextId: string, data: unknown[]): ProcessedRequestDataResult {\n const functions = new Map<string, Method>();\n const processedItems = new Set<unknown>();\n const transferables = new Set<Transferable>();\n\n const createFunctionCallback = (func: Method) => {\n const functionId = uid('func-******');\n functions.set(functionId, func);\n return {\n flag: CALLBACK_FLAG,\n contextId,\n id: functionId,\n };\n };\n\n const createGetterObject = (getter: Method): Getter => {\n const functionId = uid('func-******');\n functions.set(functionId, getter);\n return {\n flag: GETTER_FLAG,\n contextId,\n id: functionId,\n };\n };\n\n const processPropertyDescriptor = (\n targetObject: Record<string, unknown>,\n key: string,\n descriptor: PropertyDescriptor,\n originalValue: unknown\n ) => {\n if (descriptor.get) {\n const getterObject = createGetterObject(descriptor.get);\n const newDescriptor: PropertyDescriptor = {\n configurable: descriptor.configurable,\n enumerable: descriptor.enumerable,\n writable: descriptor.writable,\n value: getterObject,\n };\n Object.defineProperty(targetObject, key, newDescriptor);\n } else if (typeof originalValue === 'function') {\n const newDescriptor: PropertyDescriptor = {\n configurable: descriptor.configurable,\n enumerable: descriptor.enumerable,\n writable: descriptor.writable,\n value: processValue(originalValue),\n };\n Object.defineProperty(targetObject, key, newDescriptor);\n } else {\n Object.defineProperty(targetObject, key, descriptor);\n }\n };\n\n const processPlainObject = (obj: Record<string, unknown>) => {\n const descriptors = Object.getOwnPropertyDescriptors(obj);\n const transformedObject = {};\n\n for (const key in descriptors) {\n const descriptor = descriptors[key];\n processPropertyDescriptor(transformedObject, key, descriptor, obj[key]);\n }\n\n return transformedObject;\n };\n\n const collectTransferables = (value: unknown) => {\n if (isTypedArray(value)) {\n transferables.add(value.buffer);\n } else if (isTransferable(value)) {\n transferables.add(value);\n }\n };\n\n const processValue = (value: unknown): unknown => {\n if (processedItems.has(value)) {\n return value;\n }\n processedItems.add(value);\n\n if (Array.isArray(value)) {\n return value.map(item => processValue(item));\n }\n\n if (isPlainObject(value)) {\n return processPlainObject(value);\n }\n\n if (typeof value === 'function') {\n return createFunctionCallback(value as Method);\n }\n\n collectTransferables(value);\n\n return value;\n };\n\n const processedData = data.map(item => processValue(item));\n\n return {\n functions,\n transferables,\n data: processedData,\n };\n}\n","export function isPromiseLike(value: unknown): value is PromiseLike<unknown> {\n return typeof value === 'object' && value !== null && 'then' in value;\n}\n","import { Defer } from '../common/Defer';\nimport { deserializeRequestData } from '../common/deserializeRequestData';\nimport { serializeRequestData } from '../common/serializeRequestData';\nimport { isFunction } from '../common/isFunction';\nimport { isPromiseLike } from '../common/isPromiseLike';\nimport type { Method } from '../common/Method';\nimport uid from '../common/uid';\nimport type { InvocationId } from '../protocol/InvocationId';\nimport type { CallMethodMessage, RPCMessage, ReturnMethodMessage } from '../protocol/Message';\nimport type { SerializableData } from '../protocol/SerializableData';\nimport type { Transport } from './Transport';\n\nexport class WebRPCPort {\n private readonly callbacks: Map<string, Method> = new Map();\n private readonly invocationDefers: Map<string, Defer<unknown>> = new Map();\n\n public readonly remoteImplementation = new Proxy(\n {},\n {\n has(property) {\n switch (property) {\n case 'then':\n return false;\n }\n return typeof property === 'string';\n },\n get: (target, property) => {\n if (typeof property === 'symbol') {\n return undefined;\n }\n if (property === 'then') {\n return undefined;\n }\n return (...args: unknown[]) => {\n return this.invokeRemoteMethod(property, args);\n };\n },\n }\n );\n\n constructor(\n private readonly clientId: string,\n private readonly portId: string,\n private readonly localInstance: Record<string, Method>,\n private readonly transport: Transport\n ) {\n //\n }\n\n receive(message: RPCMessage) {\n switch (message.action) {\n case 'method-call':\n this.handleMethodCall(message as CallMethodMessage);\n break;\n case 'method-return':\n this.handleMethodReturn(message as ReturnMethodMessage);\n break;\n }\n }\n private async invokeRemoteMethod(methodName: string, args: unknown[]): Promise<unknown> {\n const invocationId = uid('invocation-********');\n const { data, transferables, functions } = serializeRequestData(this.portId, args);\n const defer = new Defer<unknown>();\n\n functions.forEach((func, functionId) => {\n this.callbacks.set(functionId, func as Method);\n });\n\n const req: CallMethodMessage = {\n invocationId: {\n clientId: this.clientId,\n portId: this.portId,\n method: methodName,\n id: invocationId,\n },\n action: 'method-call',\n method: methodName,\n args: data as SerializableData[],\n timestamp: Date.now(),\n };\n this.transport.send(req as unknown as SerializableData, Array.from(transferables));\n\n this.invocationDefers.set(invocationId, defer);\n\n return defer.promise;\n }\n\n private handleMethodCall(message: CallMethodMessage) {\n const invocationId = message.invocationId;\n const args = deserializeRequestData(message.args, (callbackId, args) => {\n return this.invokeRemoteMethod(callbackId, args);\n });\n let method: Method | undefined;\n if (Object.prototype.hasOwnProperty.call(this.localInstance, message.method)) {\n method = (this.localInstance[message.method] as Method).bind(this.localInstance);\n } else {\n method = this.callbacks.get(message.method);\n }\n if (!isFunction(method)) {\n throw new Error(`Method not found: ${message.method}`);\n }\n this.invokeLocalMethod(invocationId, method, args);\n }\n private invokeLocalMethod(invocationId: InvocationId, method: Method, args: unknown[]) {\n const handleSuccess = (result: unknown) => {\n const { data, transferables, functions } = serializeRequestData(this.portId, [result]);\n functions.forEach((func, functionId) => {\n this.callbacks.set(functionId, func);\n });\n const response: ReturnMethodMessage = {\n invocationId,\n action: 'method-return',\n status: 'success',\n result: data[0] as SerializableData,\n timestamp: Date.now(),\n };\n this.transport.send(response as unknown as SerializableData, Array.from(transferables));\n };\n const handleError = (reason: unknown) => {\n const ret: ReturnMethodMessage = {\n invocationId,\n action: 'method-return',\n status: 'error',\n error: {\n message: reason instanceof Error ? reason.message : String(reason),\n stack: reason instanceof Error ? reason.stack : undefined,\n },\n timestamp: Date.now(),\n };\n this.transport.send(ret as unknown as SerializableData, []);\n };\n try {\n const result = method(...args);\n if (isPromiseLike(result)) {\n result.then(handleSuccess, handleError);\n } else {\n handleSuccess(result);\n }\n } catch (e) {\n handleError(e);\n }\n }\n\n private handleMethodReturn(message: ReturnMethodMessage) {\n const defer = this.invocationDefers.get(message.invocationId.id);\n if (defer) {\n if (message.status === 'success') {\n const result = deserializeRequestData([message.result], (callbackId, callbackArgs) => {\n return this.invokeRemoteMethod(callbackId, callbackArgs);\n })[0];\n defer.resolve(result);\n } else {\n defer.reject(message.error);\n }\n }\n this.invocationDefers.delete(message.invocationId.id);\n }\n}\n","import { isFunction } from '../common/isFunction';\nimport type { Method } from '../common/Method';\nimport type { PromisifyObject } from '../common/PromisifyType';\nimport { isRPCMessage } from '../protocol/Message';\nimport type { SerializableData } from '../protocol/SerializableData';\nimport { SendFunctionTransport } from './SendFunctionTransport';\nimport type { Transport } from './Transport';\nimport { WebRPCPort } from './WebRPCPort';\n\n/**\n * WebRPC is a remote procedure call (RPC) system that enables seamless communication\n * between different execution contexts (e.g., web workers, iframes, main thread).\n *\n * It provides a simple interface to register services and call remote methods as if\n * they were local, handling serialization, transport, and promise-based responses\n * automatically.\n *\n * @example\n * ```typescript\n * // Create WebRPC instance with a transport function\n * const worker = new Worker('worker.js');\n * const webRPC = new WebRPC('client-1', new PostMessageTransport(worker));\n *\n * // Register a service\n * webRPC.register('math', {\n * add: (a: number, b: number) => a + b,\n * multiply: (a: number, b: number) => a * b\n * });\n *\n * // Get remote service proxy\n * const remoteMath = webRPC.get<{\n * add: (a: number, b: number) => Promise<number>;\n * multiply: (a: number, b: number) => Promise<number>;\n * }>('math');\n *\n * // Call remote methods\n * const result = await remoteMath.add(5, 3); // Returns 8\n * ```\n */\nexport class WebRPC {\n private readonly ports: Map<string, WebRPCPort> = new Map();\n private readonly transport: Transport;\n\n /**\n * Creates a new WebRPC instance.\n *\n * @param clientId - A unique identifier for this client instance\n * @param transport - Either a Transport object or a function that sends data to the remote endpoint\n */\n constructor(\n private readonly clientId: string,\n transport: Transport | ((data: SerializableData, transferables: Transferable[]) => void)\n ) {\n if (isFunction(transport)) {\n this.transport = new SendFunctionTransport(transport);\n } else {\n this.transport = transport;\n }\n this.transport.onMessage(data => {\n this.receive(data);\n });\n }\n\n receive(data: unknown) {\n if (!isRPCMessage(data)) {\n return;\n }\n if (data.invocationId.clientId !== this.clientId) {\n return;\n }\n const portId = data.invocationId.portId;\n const port = this.ports.get(portId);\n if (port) {\n port.receive(data);\n }\n }\n\n /**\n * Registers a service instance that can be called remotely.\n *\n * @param id - Unique identifier for the service\n * @param instance - Object containing methods to be exposed remotely\n *\n * @example\n * ```typescript\n * webRPC.register('calculator', {\n * add: (a: number, b: number) => a + b,\n * subtract: (a: number, b: number) => a - b\n * });\n * ```\n */\n register(id: string, instance: unknown) {\n const port = new WebRPCPort(this.clientId, id, instance as Record<string, Method>, this.transport);\n this.ports.set(id, port);\n }\n\n /**\n * Gets a proxy object for calling remote methods on a registered service.\n *\n * @param id - Identifier of the remote service\n * @returns A proxy object that allows calling remote methods as if they were local\n *\n * @example\n * ```typescript\n * const remoteCalc = webRPC.get<{\n * add: (a: number, b: number) => Promise<number>;\n * subtract: (a: number, b: number) => Promise<number>;\n * }>('calculator');\n *\n * const result = await remoteCalc.add(10, 5); // Returns 15\n * ```\n */\n get<T>(id: string): PromisifyObject<T> {\n if (!this.ports.has(id)) {\n const port = new WebRPCPort(this.clientId, id, {}, this.transport);\n this.ports.set(id, port);\n }\n const port = this.ports.get(id)!;\n return port.remoteImplementation as PromisifyObject<T>;\n }\n\n /**\n * Closes the WebRPC connection and cleans up resources.\n *\n * @example\n * ```typescript\n * webRPC.close();\n * ```\n */\n close() {\n this.transport.close();\n }\n}\n","export * from './BrowserExtensionTransport';\nexport * from './PostMessageTransport';\nexport * from './WindowPostMessageTransport';\n","import type browser from 'webextension-polyfill';\n\nimport type { Transport } from '../core/Transport';\nimport type { SerializableData } from '../protocol/SerializableData';\n\nexport type BrowserExtensionTransportOptions = {\n port: browser.Runtime.Port;\n};\n\n/**\n * BrowserExtensionTransport provides a Transport implementation for browser extension communication.\n *\n * This transport enables communication between different components of a browser extension such as\n * background scripts, content scripts, popup scripts, options pages, and DevTools pages. It uses\n * the browser extension's runtime.Port API for reliable bidirectional communication.\n *\n * **Limitations**:\n * - Does not support transferable objects\n * - Requires webextension-polyfill or native browser.runtime API\n * - Port must be established before creating the transport\n *\n * @example\n * ```typescript\n * // Background script (service provider)\n * // manifest.json: \"background\": { \"service_worker\": \"background.js\" }\n * chrome.runtime.onConnect.addListener((port) => {\n * if (port.name === 'webRPC') {\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('background', transport);\n *\n * // Register background services\n * webRPC.register('storage', {\n * getData: async (key: string) => {\n * const result = await chrome.storage.sync.get(key);\n * return result[key];\n * },\n * setData: async (key: string, value: any) => {\n * await chrome.storage.sync.set({ [key]: value });\n * return true;\n * }\n * });\n *\n * webRPC.register('tabs', {\n * createTab: async (url: string) => {\n * const tab = await chrome.tabs.create({ url });\n * return tab;\n * },\n * getCurrentTab: async () => {\n * const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });\n * return tab;\n * }\n * });\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Content script (service consumer)\n * // manifest.json: \"content_scripts\": [{ \"matches\": [\"<all_urls>\"], \"js\": [\"content.js\"] }]\n * const port = chrome.runtime.connect({ name: 'webRPC' });\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('content', transport);\n *\n * // Get background services\n * const storage = webRPC.get<{\n * getData: (key: string) => Promise<any>;\n * setData: (key: string, value: any) => Promise<boolean>;\n * }>('storage');\n *\n * const tabs = webRPC.get<{\n * createTab: (url: string) => Promise<chrome.tabs.Tab>;\n * getCurrentTab: () => Promise<chrome.tabs.Tab>;\n * }>('tabs');\n *\n * // Use the services\n * const userData = await storage.getData('user');\n * await storage.setData('lastVisit', new Date().toISOString());\n * const currentTab = await tabs.getCurrentTab();\n * ```\n *\n * @example\n * ```typescript\n * // Popup script (service consumer)\n * // manifest.json: \"action\": { \"default_popup\": \"popup.html\" }\n * // popup.html: <script src=\"popup.js\"></script>\n * const port = chrome.runtime.connect({ name: 'webRPC' });\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('popup', transport);\n *\n * const storage = webRPC.get<{\n * getData: (key: string) => Promise<any>;\n * setData: (key: string, value: any) => Promise<boolean>;\n * }>('storage');\n *\n * // Popup UI interactions\n * document.getElementById('saveButton')?.addEventListener('click', async () => {\n * const input = document.getElementById('userInput') as HTMLInputElement;\n * await storage.setData('userPreference', input.value);\n * console.log('Preference saved!');\n * });\n *\n * // Load data on popup open\n * window.addEventListener('load', async () => {\n * const preference = await storage.getData('userPreference');\n * const input = document.getElementById('userInput') as HTMLInputElement;\n * input.value = preference || '';\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Content script with page interaction services\n * const port = chrome.runtime.connect({ name: 'webRPC' });\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('content', transport);\n *\n * // Register content script services (accessible from popup/background)\n * webRPC.register('pageInteraction', {\n * getPageTitle: () => document.title,\n * getPageUrl: () => window.location.href,\n * highlightText: (text: string) => {\n * const elements = document.querySelectorAll('*');\n * elements.forEach(el => {\n * if (el.textContent?.includes(text)) {\n * el.style.backgroundColor = 'yellow';\n * }\n * });\n * },\n * extractData: (selector: string) => {\n * const elements = document.querySelectorAll(selector);\n * return Array.from(elements).map(el => el.textContent);\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Background script consuming content script services\n * chrome.runtime.onConnect.addListener((port) => {\n * if (port.name === 'webRPC') {\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('background', transport);\n *\n * // Get content script services\n * const pageInteraction = webRPC.get<{\n * getPageTitle: () => Promise<string>;\n * getPageUrl: () => Promise<string>;\n * highlightText: (text: string) => Promise<void>;\n * extractData: (selector: string) => Promise<string[]>;\n * }>('pageInteraction');\n *\n * // Use content script services\n * chrome.action.onClicked.addListener(async (tab) => {\n * const title = await pageInteraction.getPageTitle();\n * const url = await pageInteraction.getPageUrl();\n * console.log(`Tab clicked: ${title} (${url})`);\n *\n * await pageInteraction.highlightText('important');\n * });\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Options page (service consumer)\n * // manifest.json: \"options_page\": \"options.html\"\n * const port = chrome.runtime.connect({ name: 'webRPC' });\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('options', transport);\n *\n * const storage = webRPC.get<{\n * getData: (key: string) => Promise<any>;\n * setData: (key: string, value: any) => Promise<boolean>;\n * }>('storage');\n *\n * // Options page settings management\n * const form = document.getElementById('optionsForm') as HTMLFormElement;\n * form.addEventListener('submit', async (e) => {\n * e.preventDefault();\n * const formData = new FormData(form);\n * const settings = Object.fromEntries(formData);\n *\n * await storage.setData('extensionSettings', settings);\n * console.log('Settings saved!');\n * });\n * ```\n *\n * @example\n * ```typescript\n * // DevTools page communication\n * // manifest.json: \"devtools_page\": \"devtools.html\"\n * const port = chrome.runtime.connect({ name: 'webRPC' });\n * const transport = new BrowserExtensionTransport({ port });\n * const webRPC = new WebRPC('devtools', transport);\n *\n * const pageInteraction = webRPC.get<{\n * extractData: (selector: string) => Promise<string[]>;\n * getPageTitle: () => Promise<string>;\n * }>('pageInteraction');\n *\n * // Create DevTools panel\n * chrome.devtools.panels.create('My Extension', 'icon.png', 'panel.html', (panel) => {\n * panel.onShown.addListener(async () => {\n * const title = await pageInteraction.getPageTitle();\n * const links = await pageInteraction.extractData('a');\n * console.log(`Inspecting: ${title}, Found ${links.length} links`);\n * });\n * });\n * ```\n */\nexport class BrowserExtensionTransport implements Transport {\n private readonly port: browser.Runtime.Port;\n private listener?: (message: unknown, port: browser.Runtime.Port) => void;\n\n constructor(options: BrowserExtensionTransportOptions) {\n this.port = options.port;\n }\n\n send(data: SerializableData, transfer?: Transferable[]): void {\n if (transfer?.length) {\n console.warn('BrowserExtensionTransport does not support transferable objects.');\n }\n this.port.postMessage(data);\n }\n\n onMessage(callback: (data: SerializableData) => void): () => void {\n this.listener = message => {\n callback(message as SerializableData);\n };\n\n this.port.onMessage.addListener(this.listener);\n\n return () => {\n if (this.listener) {\n this.port.onMessage.removeListener(this.listener);\n this.listener = undefined;\n }\n };\n }\n\n close(): void {\n if (this.listener) {\n this.port.onMessage.removeListener(this.listener);\n this.listener = undefined;\n }\n this.port.disconnect();\n }\n}\n","import type { Transport } from '../core/Transport';\nimport type { SerializableData } from '../protocol/SerializableData';\n\ntype MessageSender = Worker | MessagePort | BroadcastChannel | ServiceWorker | DedicatedWorkerGlobalScope;\n\n/**\n * PostMessageTransport provides a generic Transport implementation for various message-based communication channels.\n *\n * This transport supports multiple types of message senders, making it versatile for different communication\n * scenarios including MessageChannel, BroadcastChannel, ServiceWorker, Worker, and DedicatedWorkerGlobalScope.\n * It automatically handles the differences between these communication mechanisms.\n *\n * @template T - The type of message sender (MessagePort, BroadcastChannel, ServiceWorker, Worker, or\n * DedicatedWorkerGlobalScope)\n *\n * @example\n * ```typescript\n * // MessagePort example (for MessageChannel communication)\n * const channel = new MessageChannel();\n * const transport = new PostMessageTransport(channel.port1);\n * const webRPC = new WebRPC('client-1', transport);\n *\n * // Send port2 to another context\n * otherContext.postMessage({ port: channel.port2 }, [channel.port2]);\n * ```\n *\n * @example\n * ```typescript\n * // BroadcastChannel example (for cross-tab communication)\n * const broadcastChannel = new BroadcastChannel('my-app-channel');\n * const transport = new PostMessageTransport(broadcastChannel);\n * const webRPC = new WebRPC('tab-1', transport);\n *\n * // Register a service that can be accessed from other tabs\n * webRPC.register('storage', {\n * setItem: (key: string, value: string) => localStorage.setItem(key, value),\n * getItem: (key: string) => localStorage.getItem(key)\n * });\n * ```\n *\n * @example\n * ```typescript\n * // ServiceWorker example (for main thread to service worker communication)\n * const registration = await navigator.serviceWorker.register('./sw.js');\n * const serviceWorker = registration.active;\n * if (serviceWorker) {\n * const transport = new PostMessageTransport(serviceWorker);\n * const webRPC = new WebRPC('main-thread', transport);\n *\n * const swAPI = webRPC.get<{\n * cacheResource: (url: string) => Promise<void>;\n * getCachedData: (key: string) => Promise<any>;\n * }>('cacheService');\n *\n * await swAPI.cacheResource('/api/data');\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Worker example (for main thread to worker communication)\n * const worker = new Worker('./worker.js');\n * const transport = new PostMessageTransport(worker);\n * const webRPC = new WebRPC('main-thread', transport);\n *\n * const workerAPI = webRPC.get<{\n * processData: (data: number[]) => Promise<number>;\n * }>('processor');\n *\n * const result = await workerAPI.processData([1, 2, 3, 4, 5]);\n * ```\n *\n * @example\n * ```typescript\n * // DedicatedWorkerGlobalScope example (inside a worker)\n * // worker.js\n * const transport = new PostMessageTransport(self);\n * const webRPC = new WebRPC('worker', transport);\n *\n * webRPC.register('computation', {\n * heavyCalculation: (data: number[]) => {\n * return data.reduce((sum, num) => sum + num * num, 0);\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Transferable objects example (works with MessagePort, ServiceWorker, Worker, and DedicatedWorkerGlobalScope)\n * const channel = new MessageChannel();\n * const transport = new PostMessageTransport(channel.port1);\n *\n * const buffer = new ArrayBuffer(1024);\n * transport.send({ type: 'process-buffer', buffer }, [buffer]);\n * // Note: BroadcastChannel does not support transferable objects\n * ```\n */\nexport class PostMessageTransport<T extends MessageSender> implements Transport {\n private readonly cleanup: Array<() => void> = [];\n constructor(private readonly sender: T) {\n if (!isPostMessageTarget(sender)) {\n throw new Error(`Invalid post message target: ${typeof sender} is not a valid post message target`);\n }\n if (isMessagePort(this.sender)) {\n this.sender.start();\n }\n }\n send(data: SerializableData, transfer?: Transferable[]): void {\n const sender = this.sender;\n if (isBroadcastChannel(sender)) {\n sender.postMessage(data);\n } else {\n sender.postMessage(data, transfer ?? []);\n }\n }\n onMessage(callback: (data: SerializableData) => void): () => void {\n const target = this.sender;\n const listener = (event: Event) => {\n if (event instanceof MessageEvent) {\n callback(event.data);\n }\n };\n target.addEventListener('message', listener);\n this.cleanup.push(() => {\n target.removeEventListener('message', listener);\n });\n return () => {\n target.removeEventListener('message', listener);\n };\n }\n close(): void {\n this.cleanup.forEach(cleanup => cleanup());\n this.cleanup.length = 0;\n }\n}\nfunction isMessagePort(target: MessageSender): target is MessagePort {\n return target instanceof MessagePort;\n}\n\nfunction isBroadcastChannel(target: MessageSender): target is BroadcastChannel {\n return target instanceof BroadcastChannel;\n}\n\nfunction isServiceWorker(target: MessageSender): target is ServiceWorker {\n return target instanceof ServiceWorker;\n}\n\nfunction isWorker(target: MessageSender): target is Worker {\n return target instanceof Worker;\n}\n\nfunction isDedicatedWorkerGlobalScope(target: MessageSender): target is DedicatedWorkerGlobalScope {\n if (typeof DedicatedWorkerGlobalScope === 'undefined') {\n return false;\n }\n return target instanceof DedicatedWorkerGlobalScope;\n}\n\nfunction isPostMessageTarget(target: MessageSender): target is MessageSender {\n return (\n isMessagePort(target) ||\n isBroadcastChannel(target) ||\n isServiceWorker(target) ||\n isWorker(target) ||\n isDedicatedWorkerGlobalScope(target)\n );\n}\n","import type { Transport } from '../core/Transport';\nimport type { SerializableData } from '../protocol/SerializableData';\n\nexport type WindowPostMessageOptions = {\n remote: Window;\n origin?: string;\n source?: Window;\n};\n\n/**\n * WindowPostMessageTransport provides a Transport implementation for Window-to-Window communication.\n *\n * This transport enables secure communication between different window contexts such as iframes, popups,\n * and parent-child windows. It includes origin-based security filtering to prevent unauthorized\n * cross-origin communication and supports transferable objects for efficient data transfer.\n *\n * @example\n * ```typescript\n * // Parent window communicating with iframe\n * const iframe = document.createElement('iframe');\n * iframe.src = 'https://trusted-domain.com/child.html';\n * document.body.appendChild(iframe);\n *\n * // Wait for iframe to load\n * iframe.onload = () => {\n * const transport = new WindowPostMessageTransport({\n * remote: iframe.contentWindow!,\n * origin: 'https://trusted-domain.com'\n * });\n *\n * const webRPC = new WebRPC('parent', transport);\n *\n * // Register parent services\n * webRPC.register('parentAPI', {\n * getUserData: () => ({ id: 1, name: 'John' }),\n * saveData: (data: any) => localStorage.setItem('data', JSON.stringify(data))\n * });\n *\n * // Get child services\n * const childAPI = webRPC.get<{\n * processData: (data: any) => Promise<any>;\n * }>('childAPI');\n *\n * const result = await childAPI.processData({ test: 'data' });\n * };\n * ```\n *\n * @example\n * ```typescript\n * // Child window/iframe communicating with parent\n * const transport = new WindowPostMessageTransport({\n * remote: window.parent,\n * origin: 'https://parent-domain.com'\n * });\n *\n * const webRPC = new WebRPC('child', transport);\n *\n * // Register child services\n * webRPC.register('childAPI', {\n * processData: (data: any) => {\n * return { processed: true, original: data };\n * }\n * });\n *\n * // Get parent services\n * const parentAPI = webRPC.get<{\n * getUserData: () => Promise<{ id: number; name: string }>;\n * saveData: (data: any) => Promise<void>;\n * }>('parentAPI');\n *\n * const userData = await parentAPI.getUserData();\n * ```\n *\n * @example\n * ```typescript\n * // Parent window communicating with popup\n * const popup = window.open('https://trusted-domain.com/popup.html', 'popup');\n *\n * if (popup) {\n * const transport = new WindowPostMessageTransport({\n * remote: popup,\n * origin: 'https://trusted-domain.com'\n * });\n *\n * const webRPC = new WebRPC('main', transport);\n *\n * const popupAPI = webRPC.get<{\n * authenticate: (credentials: any) => Promise<string>;\n * }>('authAPI');\n *\n * const token = await popupAPI.authenticate({ username: 'user', password: 'pass' });\n * }\n * ```\n *\n * @example\n * ```typescript\n * // Popup window communicating with parent\n * const transport = new WindowPostMessageTransport({\n * remote: window.opener,\n * origin: 'https://main-domain.com'\n * });\n *\n * const webRPC = new WebRPC('popup', transport);\n *\n * webRPC.register('authAPI', {\n * authenticate: async (credentials: any) => {\n * // Perform authentication\n * return 'auth-token-123';\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Using transferable objects for efficient data transfer\n * const transport = new WindowPostMessageTransport({\n * remote: iframe.contentWindow!,\n * origin: 'https://trusted-domain.com'\n * });\n *\n * const buffer = new ArrayBuffer(1024);\n * transport.send({ type: 'process-buffer', buffer }, [buffer]);\n * // buffer ownership is transferred to the iframe\n * ```\n *\n * @example\n * ```typescript\n * // Allowing any origin (NOT recommended for production)\n * const transport = new WindowPostMessageTransport({\n * remote: targetWindow,\n * origin: '*' // Use only for development or trusted environments\n * });\n * ```\n */\nexport class WindowPostMessageTransport implements Transport {\n private readonly remote: Window;\n private readonly origin: string;\n private readonly source: Window;\n private listener?: (event: MessageEvent) => void;\n\n constructor(options: WindowPostMessageOptions) {\n this.remote = options.remote;\n this.origin = options.origin ?? '*';\n this.source = options.source ?? window;\n }\n\n send(data: SerializableData, transfer?: Transferable[]): void {\n this.remote.postMessage(data, this.origin, transfer);\n }\n\n onMessage(callback: (data: SerializableData) => void): () => void {\n this.listener = (event: MessageEvent) => {\n if (this.origin !== '*' && event.origin !== this.origin) {\n return;\n }\n if (event.source !== this.remote) {\n return;\n }\n callback(event.data);\n };\n\n this.source.addEventListener('message', this.listener);\n\n return () => {\n if (this.listener) {\n this.source.removeEventListener('message', this.listener);\n this.listener = undefined;\n }\n };\n }\n\n close(): void {\n if (this.listener) {\n this.source.removeEventListener('message', this.listener);\n this.listener = undefined;\n }\n }\n}\n","export * from './core/WebRPC';\nexport * from './transports';\nexport * from './core/Transport';\nexport * from './core/SendFunctionTransport';\n\nimport { WebRPC } from './core/WebRPC';\nimport * as transports from './transports';\n\nObject.assign(WebRPC, transports, { WebRPC });\n\nexport default WebRPC;\n"],"mappings":"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