Comctx
Guides

Transferable Objects

Use transferable objects with Comctx when you want values to be transferred instead of copied.

Comctx supports transferable objects natively.

By default, every method parameter, return value, and object property value is copied through structured cloning.

If you want a value to be transferred rather than copied — provided the value is or contains a transferable object — enable the transfer option.

Supported transferable objects

Use the MDN page for Transferable objects as the authoritative list.

The MDN page lists the following supported objects (the list may not be exhaustive):

  • ArrayBuffer
  • AudioData
  • ImageBitmap
  • MediaSourceHandle
  • MediaStreamTrack
  • MessagePort
  • MIDIAccess
  • OffscreenCanvas
  • ReadableStream
  • RTCDataChannel
  • TransformStream
  • VideoFrame
  • WebTransportReceiveStream
  • WebTransportSendStream
  • WritableStream

Support depends on the runtime and platform, so check the MDN page for the current details.

How transfer works

When transfer: true is enabled, Comctx automatically extracts transferable objects from messages and passes them to your adapter through the transfer parameter.

This allows zero-copy transfer when the runtime and transport both support it.

Enable transfer

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,
})

In this example, result.textStream is a ReadableStream, which can be transferred when transfer: true is enabled.

Usage

const ai = injectAi(adapter)
const stream = await ai.translate('Hello world', 'zh-CN')

for await (const chunk of stream) {
  console.log(chunk)
}

Adapter implementation

When transfer is enabled, your adapter must forward the transfer parameter to the underlying transport.

import type { Adapter, SendMessage } from 'comctx'

export default class TransferAdapter implements Adapter {
  sendMessage: SendMessage = (message, transfer) => {
    this.worker.postMessage(message, transfer)
  }

  // ... rest of implementation
}

Important note

AsyncIterable is not transferable and cannot be sent across workers.

If you need to return or send stream-like output across a worker boundary, wrap it with ReadableStream.from(...) so it can be transferred.

Next steps

On this page