### Install and Run Browser Extension Example Source: https://github.com/molvqingtai/comctx/blob/master/examples/browser-extension/README.md Use these commands to install dependencies and start the development server for the browser extension example. ```shell pnpm install ``` ```shell pnpm dev ``` -------------------------------- ### Run development and build scripts Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Use these commands to install dependencies and start the development server for the browser extension. ```bash pnpm install pnpm dev ``` -------------------------------- ### Start Docs Development Server Source: https://github.com/molvqingtai/comctx/blob/master/docs/README.md Run this command from the repository root to start the documentation development server. ```bash pnpm dev:docs ``` -------------------------------- ### Install dependencies Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Install the required packages for the browser extension project. ```bash pnpm add comctx pnpm add -D wxt ``` ```bash npm install comctx npm install -D wxt ``` ```bash yarn add comctx yarn add -D wxt ``` ```bash bun add comctx bun add -d wxt ``` -------------------------------- ### Install Comctx Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/web-worker.mdx Install the package using your preferred package manager. ```bash pnpm add comctx ``` ```bash npm install comctx ``` ```bash yarn add comctx ``` ```bash bun add comctx ``` -------------------------------- ### Install Comctx with bun Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Install the comctx package in your project using bun. ```bash bun add comctx ``` -------------------------------- ### Install Comctx with pnpm Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Install the comctx package in your project using pnpm. ```bash pnpm install comctx ``` -------------------------------- ### Provider Example Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/core-concepts.mdx This code demonstrates how to set up a Provider that owns the real service instance. It's typically used in environments like Web Workers or background scripts. ```typescript import CustomAdapter from './adapter' import { provideCounter } from './shared' const originCounter = provideCounter(new CustomAdapter()) originCounter.onChange(console.log) ``` -------------------------------- ### Install Comctx with yarn Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Install the comctx package in your project using yarn. ```bash yarn add comctx ``` -------------------------------- ### Start Docs Development Server (Docs Workspace) Source: https://github.com/molvqingtai/comctx/blob/master/docs/README.md Alternatively, run the documentation development server directly from the `docs` workspace. ```bash pnpm --filter docs dev ``` -------------------------------- ### Install Comctx with npm Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Install the comctx package in your project using npm. ```bash npm install comctx ``` -------------------------------- ### Example debug output logs Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/debug-logging.mdx Sample output showing the format of message and event logs. ```txt comctx:message provider onMessage apply comctx:event provider onMessage apply comctx:event injector sendMessage ping ``` -------------------------------- ### Injector Example Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/core-concepts.mdx This code shows how to set up an Injector that receives a virtual proxy to a service. It's used in environments like main pages or content scripts. Method calls on the proxy are forwarded to the Provider. ```typescript import CustomAdapter from './adapter' import { injectCounter } from './shared' const proxyCounter = injectCounter(new CustomAdapter()) proxyCounter.onChange(console.log) await proxyCounter.increment() const count = await proxyCounter.getValue() ``` -------------------------------- ### Custom Adapter Implementation Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/core-concepts.mdx An example implementation of the Comctx Adapter interface. This class handles sending and receiving messages, enabling communication across different JavaScript environments. ```typescript import type { Adapter, SendMessage, OnMessage } from 'comctx' export default class CustomAdapter implements Adapter { sendMessage: SendMessage = (message) => { postMessage(message) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) addEventListener('message', handler) return () => removeEventListener('message', handler) } } ``` -------------------------------- ### Transfer-enabled adapter implementation Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/adapter.mdx Example of an adapter class that forwards the transfer parameter to the underlying transport. ```ts import type { Adapter, SendMessage } from 'comctx' export default class TransferAdapter implements Adapter { sendMessage: SendMessage = (message, transfer) => { this.worker.postMessage(message, transfer) } // ... rest of implementation } ``` -------------------------------- ### Initialize Web Worker Injector Source: https://github.com/molvqingtai/comctx/blob/master/README.md Setup the counter injector in the main thread to communicate with the Web Worker. ```typescript import { injectCounter } from './shared' import WorkerAdapter from './WorkerAdapter' const worker = new Worker(new URL('./web-worker.ts', import.meta.url), { type: 'module' }) const counter = injectCounter(new WorkerAdapter(worker, 'web-worker-injector')) counter.onChange((value) => { console.log('WebWorker Value:', value) // 1,0 }) await counter.getValue() // 0 await counter.increment() // 1 await counter.decrement() // 0 ``` -------------------------------- ### Define Shared Service with Comctx Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Define a shared service class and create provider/injector functions using `defineProxy`. This example sets up a Counter service with methods for incrementing, decrementing, and observing value changes. ```typescript import { defineProxy } from 'comctx' class Counter { public value: number constructor(initialValue: number = 0) { this.value = initialValue } async getValue() { return this.value } async onChange(callback: (value: number) => void) { let oldValue = this.value setInterval(() => { const newValue = this.value if (oldValue !== newValue) { callback(newValue) oldValue = newValue } }) } async increment() { return ++this.value } async decrement() { return --this.value } } export const [provideCounter, injectCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__' }) ``` -------------------------------- ### Define Custom Adapter for Comctx Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Implement a custom adapter for Comctx by extending the `Adapter` interface. This example uses `postMessage` and `addEventListener` for message handling, suitable for web workers or iframes. ```typescript import type { Adapter, SendMessage, OnMessage } from 'comctx' export default class CustomAdapter implements Adapter { // Implement message sending sendMessage: SendMessage = (message) => { postMessage(message) } // Implement message listener onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) addEventListener('message', handler) return () => removeEventListener('message', handler) } } ``` -------------------------------- ### Enable Transferable Objects in Comctx Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/transferable-objects.mdx Enable the `transfer: true` option in `defineProxy` to allow Comctx to transfer transferable objects instead of copying them. This example demonstrates transferring a `ReadableStream` from an AI service. ```typescript import { defineProxy } from 'comctx' import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' class AiService { async translate(text: string, targetLanguage: string) { const result = await streamText({ model: openai('gpt-4o-mini'), prompt: `Translate to ${targetLanguage}:\n${text}`, }) return result.textStream } } export const [provideAi, injectAi] = defineProxy(() => new AiService(), { namespace: '__worker-transfer-example__', transfer: true, }) ``` -------------------------------- ### Build Docs for Production Source: https://github.com/molvqingtai/comctx/blob/master/docs/README.md Create a production build for the documentation site using this command from the repository root. ```bash pnpm build:docs ``` -------------------------------- ### Build Docs for Production (Docs Workspace) Source: https://github.com/molvqingtai/comctx/blob/master/docs/README.md Build the documentation site for production from the `docs` workspace. ```bash pnpm --filter docs build ``` -------------------------------- ### Initialize Service Provider Source: https://github.com/molvqingtai/comctx/blob/master/README.md Instantiate the provider side of the service using a custom adapter. ```typescript // Provider side, typically for web-workers, background, etc. import CustomAdapter from 'CustomAdapter' import { provideCounter } from './shared' const originCounter = provideCounter(new CustomAdapter()) originCounter.onChange(console.log) ``` -------------------------------- ### Initialize Background Script Counter Source: https://github.com/molvqingtai/comctx/blob/master/README.md Sets up the counter provider in the background script using the ProvideAdapter. ```typescript import { provideCounter } from './shared' import ProvideAdapter from './ProvideAdapter' const counter = provideCounter(new ProvideAdapter()) counter.onChange((value) => { console.log('Background Value:', value) // 1,0 }) ``` -------------------------------- ### Configure the Main Page Entry Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/web-worker.mdx Sets up the main thread to communicate with the worker, injecting the counter proxy and binding UI events to remote methods. ```typescript import { name, description } from '../package.json' import createElement from './utils/createElement' import { WorkerAdapter } from './service/adapter' import { injectCounter } from './service/counter' import './style.css' void (async () => { const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module', }) const counter = injectCounter(new WorkerAdapter(worker, 'web-worker-injector')) const initValue = await counter.getValue() document.querySelector('#app')!.appendChild( createElement(`

${name}

${description}

${initValue}

WebWorker Value: ${initValue}

`), ) document .querySelector('#decrement')! .addEventListener('click', async () => { await counter.decrement() }) document .querySelector('#increment')! .addEventListener('click', async () => { await counter.increment() }) counter.onChange((value) => { document.querySelector('#value')!.textContent = value.toString() document.querySelector('#worker-value')!.textContent = `WebWorker Value: ${value}` }) })().catch(console.error) ``` -------------------------------- ### Implement Browser Runtime Adapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Defines ProvideAdapter and InjectAdapter classes to handle cross-context communication via browser runtime messaging. Includes routing logic for content scripts and popups. ```ts import { browser } from '#imports' import type { Adapter, Message, SendMessage, OnMessage } from 'comctx' export interface MessageMeta { url: string injector?: 'content' | 'popup' } export class ProvideAdapter implements Adapter { name: string constructor(name: string) { this.name = name } sendMessage: SendMessage = async (message) => { switch (message.meta.injector) { case 'content': { const tabs = await browser.tabs.query({ url: message.meta.url }) tabs.map((tab) => browser.tabs.sendMessage(tab.id!, message)) break } case 'popup': { await browser.runtime.sendMessage(message).catch((error) => { if (error.message.includes('Receiving end does not exist')) { return } throw error }) break } } } onMessage: OnMessage = (callback) => { const handler = (message?: Partial>) => { if (!message?.meta) { return callback(message) } callback({ ...message, meta: { ...message.meta, injector: message?.sender?.name as MessageMeta['injector'] } }) } browser.runtime.onMessage.addListener(handler) return () => browser.runtime.onMessage.removeListener(handler) } } export class InjectAdapter implements Adapter { name: string constructor(name: string) { this.name = name } sendMessage: SendMessage = (message) => { browser.runtime.sendMessage(browser.runtime.id, { ...message, meta: { url: document.location.href } }) } onMessage: OnMessage = (callback) => { const handler = (message?: Partial>) => { callback(message) } browser.runtime.onMessage.addListener(handler) return () => browser.runtime.onMessage.removeListener(handler) } } ``` -------------------------------- ### Implement the WindowAdapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/iframe.mdx Create an adapter that uses window.postMessage for communication. The same adapter is used by both host and iframe by passing the appropriate window endpoint. ```ts import { Adapter, SendMessage, OnMessage } from 'comctx' export type WindowEndpoint = Pick export class WindowAdapter implements Adapter { constructor( private window: WindowEndpoint, public name?: string, private targetOrigin = '*', ) {} sendMessage: SendMessage = (message) => { this.window.postMessage(message, this.targetOrigin) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => { callback(event.data) } this.window.addEventListener('message', handler) return () => this.window.removeEventListener('message', handler) } } ``` -------------------------------- ### Initialize Main Page Counter Source: https://github.com/molvqingtai/comctx/blob/master/README.md Sets up the counter injector on the main page using the WindowAdapter to communicate with an iframe. ```typescript import { injectCounter } from './shared' import WindowAdapter from './WindowAdapter' const counter = injectCounter(new WindowAdapter(window, 'iframe-injector')) counter.onChange((value) => { console.log('iframe Value:', value) // 1,0 }) await counter.getValue() // 0 await counter.increment() // 1 await counter.decrement() // 0 ``` -------------------------------- ### Implement a minimal adapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/adapter.mdx A basic implementation of the Adapter interface using standard postMessage and addEventListener. ```typescript import type { Adapter, SendMessage, OnMessage } from 'comctx' export default class CustomAdapter implements Adapter { name = 'custom-adapter' sendMessage: SendMessage = (message) => { postMessage(message) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) addEventListener('message', handler) return () => removeEventListener('message', handler) } } ``` -------------------------------- ### Initialize the Provider Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx The Provider owns the actual service instance. It is typically used in environments like web workers or background scripts. ```ts import CustomAdapter from 'CustomAdapter' import { provideCounter } from './shared' const originCounter = provideCounter(new CustomAdapter()) originCounter.onChange(console.log) ``` -------------------------------- ### Implement ProvideAdapter for browser extensions Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/adapter.mdx Defines an adapter to send messages from the background script to specific tabs based on the provided URL metadata. ```ts import browser from 'webextension-polyfill' import { Adapter, Message, SendMessage, OnMessage } from 'comctx' export interface MessageMeta { url: string } export default class ProvideAdapter implements Adapter { sendMessage: SendMessage = async (message) => { const tabs = await browser.tabs.query({ url: message.meta.url }) tabs.map((tab) => browser.tabs.sendMessage(tab.id!, message)) } onMessage: OnMessage = (callback) => { const handler = (message?: Partial>) => { callback(message) } browser.runtime.onMessage.addListener(handler) return () => browser.runtime.onMessage.removeListener(handler) } } ``` -------------------------------- ### Configure WXT Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Define the WXT configuration to map the source directory and entrypoints for the extension. ```typescript import { defineConfig } from 'wxt' import path from 'node:path' import { name } from './package.json' export default defineConfig({ srcDir: path.resolve('src'), entrypointsDir: 'app', imports: false, webExt: { startUrls: ['https://www.example.com/'] }, manifest: () => { return { name: name, permissions: ['tabs', 'webNavigation'] } } }) ``` -------------------------------- ### Proxy Backup Configuration Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Enable backup to support static template operations like Reflect.has on the Injector side. ```ts const [provideCounter, injectCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__', backup: true, }) ``` -------------------------------- ### Web Worker adapter implementation Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/adapter.mdx A complete adapter implementation for Web Workers using postMessage and event listeners. ```ts import { Adapter, SendMessage, OnMessage } from 'comctx' export type WorkerEndpoint = Pick export default class WorkerAdapter implements Adapter { constructor( private worker: WorkerEndpoint, public name?: string ) {} sendMessage: SendMessage = (message, transfer) => { this.worker.postMessage(message, transfer) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) this.worker.addEventListener('message', handler) return () => this.worker.removeEventListener('message', handler) } } ``` -------------------------------- ### debug Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Enables console diagnostics for adapter integration and cross-context message flow. ```APIDOC ## debug ### Description Enables console diagnostics for adapter integration and cross-context message flow. ### Configuration - **debug** (boolean | 'message' | 'event') - Optional - Controls the level of logging output. ### Values - **false**: Disables debug output. - **true**: Logs both `comctx:message` and `comctx:event`. - **'message'**: Logs only `comctx:message` (messages seen by the adapter). - **'event'**: Logs only `comctx:event` (messages accepted by Comctx and routed into handling). ``` -------------------------------- ### Create Comctx Provider Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Instantiate the shared service using the `provideCounter` function and a custom adapter. This sets up the provider side of the Comctx connection. ```typescript import CustomAdapter from 'CustomAdapter' import { provideCounter } from './shared' const originCounter = provideCounter(new CustomAdapter()) originCounter.onChange(console.log) ``` -------------------------------- ### Create a service proxy with defineProxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Initializes a Provider/Injector pair for a shared service. The namespace option is required to identify the service across boundaries. ```ts import { defineProxy } from 'comctx' class Counter { public value: number constructor(initialValue: number = 0) { this.value = initialValue } async getValue() { return this.value } async onChange(callback: (value: number) => void) { let oldValue = this.value setInterval(() => { const newValue = this.value if (oldValue !== newValue) { callback(newValue) oldValue = newValue } }) } async increment() { return ++this.value } async decrement() { return --this.value } } export const [provideCounter, injectCounter] = defineProxy( () => new Counter(), { namespace: '__comctx-example__', }, ) ``` -------------------------------- ### Initialize Content Script Counter Source: https://github.com/molvqingtai/comctx/blob/master/README.md Sets up the counter injector in the content script using the InjectAdapter and performs basic operations. ```typescript import { injectCounter } from './shared' import InjectAdapter from './InjectAdapter' const counter = injectCounter(new InjectAdapter()) counter.onChange((value) => { console.log('Background Value:', value) // 1,0 }) await counter.getValue() // 0 await counter.increment() // 1 await counter.decrement() // 0 ``` -------------------------------- ### Initialize IFrame Counter Source: https://github.com/molvqingtai/comctx/blob/master/README.md Sets up the counter provider within an iframe using the WindowAdapter. ```typescript import { provideCounter } from './shared' import WindowAdapter from './WindowAdapter' const counter = provideCounter(new WindowAdapter(window.parent, 'iframe-provider')) counter.onChange((value) => { console.log('iframe Value:', value) // 1,0 }) ``` -------------------------------- ### Implement InjectAdapter for browser extensions Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/adapter.mdx Defines an adapter to send messages from the content script to the background script, attaching the current document URL as metadata. ```ts import browser from 'webextension-polyfill' import { Adapter, Message, SendMessage, OnMessage } from 'comctx' export interface MessageMeta { url: string } export default class InjectAdapter implements Adapter { sendMessage: SendMessage = (message) => { browser.runtime.sendMessage(browser.runtime.id, { ...message, meta: { url: document.location.href }, }) } onMessage: OnMessage = (callback) => { const handler = (message?: Partial>) => { callback(message) } browser.runtime.onMessage.addListener(handler) return () => browser.runtime.onMessage.removeListener(handler) } } ``` -------------------------------- ### Configure heartbeatCheck in defineProxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Enables or disables provider readiness checks during proxy initialization. Use this to handle asynchronous provider startup scenarios like worker or iframe initialization. ```ts const [, inject] = defineProxy(() => ({}) as T, { namespace: '__comlink_like__', heartbeatCheck: false, transfer: true, }) ``` ```ts const [provide] = defineProxy(() => target, { namespace: '__comlink_like__', heartbeatCheck: false, transfer: true, }) ``` -------------------------------- ### Popup Page Entry Implementation Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Configures the popup page to inject the background service using the browser runtime adapter. ```ts import { browser } from '#imports' import { defineProxy } from 'comctx' import { InjectAdapter as BrowserRuntimeInjectAdapter } from '@/service/adapter/browserRuntime' import type { Counter } from '@/service/counter' import createElement from '@/utils/createElement' void (async () => { const [, injectBackgroundCounter] = defineProxy(() => ({}) as Counter, { namespace: browser.runtime.id, debug: import.meta.env.DEV }) const counter = injectBackgroundCounter( new BrowserRuntimeInjectAdapter('popup') ) const initValue = await counter.getValue() document.querySelector('#root')!.replaceWith( createElement(` `) ) ``` -------------------------------- ### Create the shared service Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Implement the counter service class used across different extension runtimes. ```typescript export class Counter { public value: number constructor(initialValue: number = 0) { this.value = initialValue } async getValue() { return this.value } async onChange(callback: (value: number) => void) { let oldValue = this.value setInterval(() => { const newValue = this.value if (oldValue !== newValue) { callback(newValue) oldValue = newValue } }) } async increment() { return ++this.value } async decrement() { return --this.value } } ``` -------------------------------- ### Implement Custom Adapter Source: https://github.com/molvqingtai/comctx/blob/master/README.md Define communication logic by implementing the Adapter interface with sendMessage and onMessage methods. ```typescript import type { Adapter, SendMessage, OnMessage } from 'comctx' export default class CustomAdapter implements Adapter { // Implement message sending sendMessage: SendMessage = (message) => { postMessage(message) } // Implement message listener onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) addEventListener('message', handler) return () => removeEventListener('message', handler) } } ``` -------------------------------- ### Initialize the Injector Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx The Injector receives a virtual proxy that forwards requests to the Provider. It is typically used in main pages or content scripts. ```ts import CustomAdapter from 'CustomAdapter' import { injectCounter } from './shared' const proxyCounter = injectCounter(new CustomAdapter()) proxyCounter.onChange(console.log) await proxyCounter.increment() const count = await proxyCounter.getValue() ``` -------------------------------- ### Implement Adapter Interface Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Defines the structure required for custom communication channel adapters. ```ts interface Adapter { /** Optional adapter name for debug logs and message diagnostics */ name?: string /** Send a message to the other side */ sendMessage: ( message: Message, transfer: Transferable[], ) => MaybePromise /** Register a message listener */ onMessage: ( callback: (message?: Partial>) => void, ) => MaybePromise } ``` -------------------------------- ### Define HTML Entry Files Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/iframe.mdx HTML structure for the host page and the iframe page, linking the respective TypeScript entry points. ```html Index Page
``` ```html IFrame Page
``` -------------------------------- ### Initialize Service Injector Source: https://github.com/molvqingtai/comctx/blob/master/README.md Instantiate the injector side to interact with the remote service via a proxy. ```typescript // Injector side, typically for the main page, content-script, etc. import CustomAdapter from 'CustomAdapter' import { injectCounter } from './shared' const proxyCounter = injectCounter(new CustomAdapter()) // Support for callbacks proxyCounter.onChange(console.log) // Transparently call remote methods await proxyCounter.increment() const count = await proxyCounter.getValue() ``` -------------------------------- ### Enable Transferable Objects Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/advanced-usage.mdx Set the transfer option to true to enable zero-copy semantics for transferable objects. ```ts import { defineProxy } from 'comctx' import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' class AiService { async translate(text: string, targetLanguage: string) { const result = await streamText({ model: openai('gpt-4o-mini'), prompt: `Translate to ${targetLanguage}:\n${text}` }) return result.textStream } } export const [provideAi, injectAi] = defineProxy(() => new AiService(), { namespace: '__worker-transfer-example__', transfer: true }) const ai = injectAi(adapter) const stream = await ai.translate('Hello world', 'zh-CN') for await (const chunk of stream) { console.log(chunk) } ``` -------------------------------- ### Implement Custom Event Adapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Creates a DOM event bridge for communication between injected scripts and content scripts. Uses cloneInto for cross-context compatibility where available. ```ts import type { Adapter, Message, OnMessage, SendMessage } from 'comctx' export class ProvideAdapter implements Adapter { name: string constructor(name: string) { this.name = name } sendMessage: SendMessage = (message) => { const detail = typeof cloneInto === 'function' ? cloneInto(message, document.defaultView) : message document.dispatchEvent(new CustomEvent('message', { detail })) } onMessage: OnMessage = (callback) => { const handler = (event: Event) => { callback((event as CustomEvent | undefined>).detail) } document.addEventListener('message', handler) return () => document.removeEventListener('message', handler) } } export const InjectAdapter = ProvideAdapter ``` -------------------------------- ### Type Check Documentation Content Source: https://github.com/molvqingtai/comctx/blob/master/docs/README.md Validate generated MDX content and run TypeScript checks with this command. ```bash pnpm check:docs ``` -------------------------------- ### Asynchronous Proxy Interaction Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Use await for method calls and value retrieval when interacting with remote services via the Injector. ```ts await proxyCounter.increment() const count = await proxyCounter.getValue() ``` ```ts proxyCounter.value proxyCounter.value = 1 ``` -------------------------------- ### Enable debug logging Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Configures console diagnostics for adapter integration and message flow. The debug option accepts a boolean or specific string categories for granular logging. ```ts const [provideCounter, injectCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__', debug: import.meta.env.DEV, }) ``` ```ts debug?: boolean | 'message' | 'event' ``` -------------------------------- ### Deep Method Calls Through Proxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/core-concepts.mdx Demonstrates deep method calls that can be resolved through the proxy channel as long as the access stays within the service shape. Method calls are the safest mental model for proxy interactions. ```typescript await proxyCounter.foo.bar() ``` -------------------------------- ### Create Comlink-style API Wrapper Source: https://github.com/molvqingtai/comctx/blob/master/README.md Build a custom worker communication layer using Comctx primitives to mimic Comlink's API. ```typescript import { defineProxy, type Adapter, type OnMessage, type SendMessage } from 'comctx' type WorkerEndpoint = Pick class WorkerAdapter implements Adapter { constructor( private worker: WorkerEndpoint, public name?: string ) {} sendMessage: SendMessage = (message, transfer) => { this.worker.postMessage(message, transfer) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) this.worker.addEventListener('message', handler) return () => this.worker.removeEventListener('message', handler) } } export function wrap(worker: Worker) { const [, inject] = defineProxy(() => ({}) as T, { namespace: '__comlink_like__', heartbeatCheck: false, transfer: true }) return inject(new WorkerAdapter(worker)) } export function expose(target: T) { const [provide] = defineProxy(() => target, { namespace: '__comlink_like__', heartbeatCheck: false, transfer: true }) provide(new WorkerAdapter(self)) } ``` ```typescript import { expose } from './comlink' const api = { async increment(value: number) { return value + 1 } } expose(api) export type WorkerAPI = typeof api ``` ```typescript import { wrap } from './comlink' import type { WorkerAPI } from './worker' const worker = new Worker(new URL('./worker.ts', import.meta.url), { type: 'module' }) const api = wrap(worker) console.log(await api.increment(1)) ``` -------------------------------- ### Implement Provider Side in Iframe Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/iframe.mdx Registers the Counter service as a provider within the iframe, allowing the host to interact with it via a proxy. ```typescript import './style.css' import { defineProxy } from 'comctx' import Counter from '../service/counter' import { WindowAdapter } from '../service/adapter' // Register the proxy object void (async () => { const [provideCounter] = defineProxy(() => new Counter(), { namespace: '__iframe-example__', debug: import.meta.env.DEV }) const counter = provideCounter(new WindowAdapter(window.parent, 'iframe-provider')) document.querySelector('#app')!.innerHTML = `

I am an iframe page

Value: ${counter.value}

` counter.onChange((value) => { document.querySelector('#value')!.textContent = `${value}` }) })().catch(console.error) ``` -------------------------------- ### Implement Comlink-like Adapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/comlink-like.mdx Defines the WorkerAdapter class and the wrap/expose functions to facilitate Comlink-style communication. ```ts import { defineProxy, type Adapter, type OnMessage, type SendMessage } from 'comctx' type WorkerEndpoint = Pick class WorkerAdapter implements Adapter { constructor( private worker: WorkerEndpoint, public name?: string ) {} sendMessage: SendMessage = (message, transfer) => { this.worker.postMessage(message, transfer) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) this.worker.addEventListener('message', handler) return () => this.worker.removeEventListener('message', handler) } } export function wrap(worker: Worker) { const [, inject] = defineProxy(() => ({}) as T, { namespace: '__comlink_like__', heartbeatCheck: false, transfer: true }) return inject(new WorkerAdapter(worker)) } export function expose(target: T) { const [provide] = defineProxy(() => target, { namespace: '__comlink_like__', heartbeatCheck: false, transfer: true }) provide(new WorkerAdapter(self)) } ``` -------------------------------- ### Define Provider and Injector Proxies Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Separates proxy definitions for multi-package architectures to minimize shared code bundling. ```ts import { defineProxy } from 'comctx' import { Counter } from './shared' export const [provideCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__', }) ``` ```ts import { defineProxy } from 'comctx' import type { Counter } from './shared' export const [, injectCounter] = defineProxy(() => ({}) as Counter, { namespace: '__comctx-example__', }) ``` -------------------------------- ### Implement the Worker Provider Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/web-worker.mdx Initializes the counter service within the Web Worker using the WorkerAdapter to handle incoming remote calls. ```typescript import { WorkerAdapter } from './service/adapter' import { provideCounter } from './service/counter' const counter = provideCounter(new WorkerAdapter(self, 'web-worker-provider')) counter.onChange((value) => { console.log('WebWorker Value:', value) }) ``` -------------------------------- ### Enable debug logging in defineProxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/debug-logging.mdx Configure the debug property within defineProxy to enable logging during development. ```ts export const [provideCounter, injectCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__', debug: import.meta.env.DEV }) ``` -------------------------------- ### Create Comctx Injector Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/getting-started.mdx Instantiate a proxy to the shared service using the `injectCounter` function and a custom adapter. This allows the injector side to interact with the remote service transparently. ```typescript import CustomAdapter from 'CustomAdapter' import { injectCounter } from './shared' const proxyCounter = injectCounter(new CustomAdapter()) // Support for callbacks proxyCounter.onChange(console.log) // Transparently call remote methods await proxyCounter.increment() const count = await proxyCounter.getValue() ``` -------------------------------- ### defineProxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx The defineProxy function is used to create provider and injector proxies for cross-package communication, allowing services to be shared across boundaries. ```APIDOC ## defineProxy ### Description Creates a proxy pair for providing and injecting services across different packages. The provider side initializes the actual implementation, while the injector side acts as a virtual proxy. ### Usage ```ts // Provider side export const [provideCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__', }) // Injector side export const [, injectCounter] = defineProxy(() => ({}) as Counter, { namespace: '__comctx-example__', }) ``` ``` -------------------------------- ### Implement Worker Adapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/web-worker.mdx Create a reusable adapter that handles message communication between the main thread and the worker. ```typescript import { Adapter, SendMessage, OnMessage } from 'comctx' export type WorkerEndpoint = Pick export class WorkerAdapter implements Adapter { constructor( private worker: WorkerEndpoint, public name?: string, ) {} sendMessage: SendMessage = (message, transfer) => { this.worker.postMessage(message, transfer) } onMessage: OnMessage = (callback) => { const handler = (event: MessageEvent) => callback(event.data) this.worker.addEventListener('message', handler) return () => this.worker.removeEventListener('message', handler) } } ``` -------------------------------- ### Registering a callback with Comctx Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/faq.mdx Use this pattern to register a callback function for event notifications from a remote service. Ensure the remote service is set up to handle such callbacks. ```typescript proxyCounter.onChange(console.log) ``` -------------------------------- ### defineProxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Creates a Provider/Injector pair for a shared service, allowing communication between different execution contexts. ```APIDOC ## defineProxy ### Description Creates a Provider/Injector pair for a shared service. The Provider owns the real service implementation, while the Injector receives a virtual proxy that forwards requests to the Provider. ### Signature `defineProxy(factory: () => T, options: { namespace: string })` ### Parameters - **factory** (Function) - Required - A function that returns the service instance. - **options** (Object) - Required - Configuration object. - **namespace** (string) - Required - A unique identifier for the service proxy. ### Example ```ts import { defineProxy } from 'comctx' export const [provideCounter, injectCounter] = defineProxy( () => new Counter(), { namespace: '__comctx-example__', }, ) ``` ``` -------------------------------- ### Injected Script Entry Implementation Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Defines a content script running in the MAIN world that communicates with the background service via a custom event adapter. ```ts import { defineContentScript } from '#imports' import createElement from '@/utils/createElement' import { InjectAdapter as CustomEventInjectAdapter } from '@/service/adapter/customEvent' import '@/assets/style.css' import { defineProxy } from 'comctx' import type { Counter } from '@/service/counter' export default defineContentScript({ world: 'MAIN', runAt: 'document_end', matches: ['*://*.example.com/*'], main: async () => { document.head.querySelectorAll('style').forEach((style) => style.remove()) document.body.querySelector('div')?.remove() const [, injectContentCounter] = defineProxy(() => ({}) as Counter, { namespace: '__comctx-example__', debug: import.meta.env.DEV }) const counter = injectContentCounter(new CustomEventInjectAdapter('injected')) const initValue = await counter.getValue() const app = createElement(`

injected-script example

injected-script -> content-script -> background

${initValue}

Background Value: ${initValue}

`) app.querySelector('#decrement')! .addEventListener('click', async () => { await counter.decrement() }) app.querySelector('#increment')! .addEventListener('click', async () => { await counter.increment() }) counter.onChange((value) => { app.querySelector('#value')!.textContent = value.toString() app.querySelector('#background-value')!.textContent = `Background Value: ${value}` }) document.body.append(app) } }) ``` -------------------------------- ### heartbeatCheck Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Enables a provider readiness check where the Injector sends heartbeat messages and waits for the Provider to respond before continuing. ```APIDOC ## heartbeatCheck ### Description Enables a provider readiness check. When enabled, the Injector sends heartbeat messages and waits for the Provider to respond before continuing. This is useful for asynchronous initialization scenarios like worker startup or iframe loading. ### Configuration - **heartbeatCheck** (boolean) - Optional - If true, the Injector waits for a heartbeat response from the Provider. If the Provider does not respond within the timeout, the call fails with a provider unavailable error. ``` -------------------------------- ### Enable Backup for Reflective Operations Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/core-concepts.mdx Enable the `backup: true` option to support reflective operations like `Reflect.has`. This creates a static copy on the Injector side that acts as a template. ```typescript Reflect.has(proxyCounter, 'key') ``` -------------------------------- ### Define the Adapter interface Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/adapter.mdx The core interface required to adapt Comctx to a specific environment. ```typescript interface Adapter { /** Optional adapter name for debug logs and message diagnostics */ name?: string /** Send a message to the other side */ sendMessage: (message: Message, transfer: Transferable[]) => MaybePromise /** Register a message listener */ onMessage: (callback: (message?: Partial>) => void) => MaybePromise } ``` -------------------------------- ### Adapter Interface Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx The Adapter interface defines the contract for implementing custom communication channels within Comctx. ```APIDOC ## Adapter Interface ### Description Implement this interface to adapt Comctx to different communication channels. It requires methods for sending messages and registering listeners. ### Interface Definition ```ts interface Adapter { name?: string sendMessage: (message: Message, transfer: Transferable[]) => MaybePromise onMessage: (callback: (message?: Partial>) => void) => MaybePromise } ``` ``` -------------------------------- ### Implement Transfer Adapter Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/advanced-usage.mdx Pass the transfer parameter to postMessage when implementing a custom adapter. ```ts import type { Adapter, SendMessage } from 'comctx' export default class TransferAdapter implements Adapter { sendMessage: SendMessage = (message, transfer) => { this.worker.postMessage(message, transfer) } // ... rest of implementation } ``` -------------------------------- ### Define Provider Proxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/advanced-usage.mdx Use defineProxy to export a provider instance with a specific namespace. ```ts import { defineProxy } from 'comctx' import { Counter } from './shared' export const [provideCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__' }) ``` -------------------------------- ### Callback Registration Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Register callbacks to handle events across the service boundary. ```ts proxyCounter.onChange(console.log) ``` -------------------------------- ### Define the shared counter service Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/iframe.mdx Create a service class with async methods to manage state across the Comctx boundary. ```ts export default class Counter { public value = 0 async getValue() { return this.value } async onChange(callback: (value: number) => void) { let oldValue = this.value setInterval(() => { const newValue = this.value if (oldValue !== newValue) { callback(newValue) oldValue = newValue } }) } async increment() { return ++this.value } async decrement() { return --this.value } } ``` -------------------------------- ### Define Background Service Proxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/examples/browser-extension.mdx Initializes a Counter service in the background script using the browser runtime adapter. ```ts import { browser, defineBackground } from '#imports' import { Counter } from '@/service/counter' import { ProvideAdapter } from '@/service/adapter/browserRuntime' import { defineProxy } from 'comctx' export default defineBackground({ type: 'module', main() { browser.webNavigation.onHistoryStateUpdated.addListener(() => { console.log('background active') }) const [provideBackgroundCounter] = defineProxy( (initialValue: number) => new Counter(initialValue), { namespace: browser.runtime.id, debug: import.meta.env.DEV } ) const counter = provideBackgroundCounter( new ProvideAdapter('background'), 0 ) counter.onChange((value) => { console.log('background Value:', value) }) } }) ``` -------------------------------- ### Transferable Object Proxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Enable transfer to optimize data movement for supported transferable objects like streams. ```ts import { defineProxy } from 'comctx' import { streamText } from 'ai' import { openai } from '@ai-sdk/openai' class AiService { async translate(text: string, targetLanguage: string) { const result = await streamText({ model: openai('gpt-4o-mini'), prompt: `Translate to ${targetLanguage}:\n${text}`, }) return result.textStream } } export const [provideAi, injectAi] = defineProxy(() => new AiService(), { namespace: '__worker-transfer-example__', transfer: true, }) ``` -------------------------------- ### defineProxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/reference/api-reference.mdx Defines a proxy for a service to enable cross-boundary communication. ```APIDOC ## defineProxy ### Description Creates a provider and injector pair for a service, allowing the injector to call methods on the provider across boundaries. ### Parameters - **factory** (Function) - Required - A function that returns an instance of the service. - **options** (Object) - Required - **namespace** (string) - Required - Identifies the service channel. Provider and Injector must use the same namespace. - **backup** (boolean) - Optional - Enables static template support for operations like Reflect.has. - **transfer** (boolean) - Optional - Enables zero-copy transfer for transferable objects instead of structured cloning. ### Example ```ts const [provideCounter, injectCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__', backup: true }); ``` ``` -------------------------------- ### Define Provider Proxy Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/guides/separate-definitions.mdx Create the Provider definition on the side that owns the real implementation. This side handles incoming remote calls. ```typescript import { defineProxy } from 'comctx' import { Counter } from './shared' export const [provideCounter] = defineProxy(() => new Counter(), { namespace: '__comctx-example__' }) ``` -------------------------------- ### Namespace Configuration Source: https://github.com/molvqingtai/comctx/blob/master/docs/content/docs/introduction/core-concepts.mdx Specifies the communication channel identifier. Both Provider and Injector must use the same namespace to establish communication. ```typescript namespace: '__comctx-example__' ```