Docs/TuffTransport API
Universal Developer

TuffTransport API

TuffTransport API

Overview

TuffTransport is the next-generation IPC communication system for Tuff plugins, providing type-safe, high-performance messaging with built-in support for batching and streaming.

TuffTransport replaces the legacy Channel API. While the old API remains functional, we recommend migrating to TuffTransport for new development.

Why TuffTransport?

FeatureLegacy ChannelTuffTransport
Type Safety❌ String-based events✅ Compile-time type checking
Autocomplete❌ No IDE support✅ Full IntelliSense
Batching❌ Manual✅ Automatic request batching
Streaming❌ Not supported✅ MessagePort streaming
Error Handling❌ Generic errors✅ Structured error types

Introduction

Quick Start

EXAMPLE.TS
import { useTuffTransport, CoreBoxEvents, StorageEvents } from '@talex-touch/utils/transport'

const transport = useTuffTransport()

// Send a typed request
await transport.send(CoreBoxEvents.ui.hide)

// Send with payload
const result = await transport.send(CoreBoxEvents.search.query, {
  query: { text: 'hello' }
})

// Batch requests (automatic)
const [theme, lang] = await Promise.all([
  transport.send(StorageEvents.app.get, { key: 'theme' }),
  transport.send(StorageEvents.app.get, { key: 'language' }),
])

Core Concepts

TuffEvent

Every communication in TuffTransport uses a TuffEvent - a type-safe event definition that encodes request/response types at compile time.

EXAMPLE.TS
// ❌ Legacy: String-based, no type safety
channel.send('core-box:search:query', { text: 'hello' })

// ✅ TuffTransport: Type-safe, autocomplete
transport.send(CoreBoxEvents.search.query, { query: { text: 'hello' } })
//                                          ↑ TypeScript enforces correct payload

Event Builder

Create custom events using the defineEvent builder:

EXAMPLE.TS
import { defineEvent } from '@talex-touch/utils/transport'

// Define a typed event
const MyPluginEvents = {
  data: {
    fetch: defineEvent('my-plugin')
      .module('data')
      .event('fetch')
      .define<{ id: string }, { name: string; value: number }>()
  }
}

// Usage - fully typed!
const result = await transport.send(MyPluginEvents.data.fetch, { id: '123' })
console.log(result.name, result.value) // ✅ Type-safe access

API Reference

useTuffTransport()

Get the transport instance in a plugin renderer.

EXAMPLE.TS
import { useTuffTransport } from '@talex-touch/utils/transport'

const transport = useTuffTransport()

transport.send(event, payload?, options?)

Send a request and wait for response.

Parameters:

  • event - A TuffEvent instance (required)
  • payload - Request payload matching event's request type
  • options - Send options

Options:

OptionTypeDefaultDescription
immediatebooleanfalseSkip batching, send immediately
timeoutnumber10000Request timeout in ms

Examples:

EXAMPLE.TS
// Simple request
await transport.send(CoreBoxEvents.ui.hide)

// Request with payload
const result = await transport.send(StorageEvents.app.get, { key: 'theme' })

// Immediate (skip batching)
await transport.send(StorageEvents.app.set, 
  { key: 'urgent', value: true },
  { immediate: true }
)

// Custom timeout
await transport.send(SlowEvent, data, { timeout: 30000 })

transport.stream(event, payload, options)

Initiate a streaming request via MessagePort.

EXAMPLE.TS
const controller = await transport.stream(
  CoreBoxEvents.search.query,
  { query: { text: 'hello' } },
  {
    onData: (result) => {
      console.log('Received:', result)
    },
    onEnd: () => {
      console.log('Stream complete')
    },
    onError: (err) => {
      console.error('Stream error:', err)
    }
  }
)

// Cancel stream if needed
controller.cancel()

MessagePort Upgrade & Fallback

  • stream/on auto-upgrades to MessagePort on allowlisted channels; falls back to channel if unavailable.
  • Runtime switch: TALEX_TRANSPORT_PORT_CHANNELS (comma-separated event names), empty disables. Default: ClipboardEvents.change, AppEvents.fileIndex.progress, CoreBoxEvents.search.update, CoreBoxEvents.search.end, CoreBoxEvents.search.noResults.
  • Manual control: set options.port = false to force channel, or configure options.port.timeoutMs / options.port.scope.
EXAMPLE.TS
// Custom port timeout
const controller = await transport.stream(
  AppEvents.fileIndex.progress,
  undefined,
  {
    onData: (data) => console.log(data),
    port: { timeoutMs: 800 }
  }
)
EXAMPLE.TS
// Advanced: explicitly open a port (use only when needed)
const handle = await transport.openPort({
  channel: CoreBoxEvents.search.update.toEventName(),
  scope: 'window'
})
handle?.port.postMessage({ type: 'data', payload: { hello: 'world' } })

transport.on(event, handler)

Register an event handler for incoming messages.

EXAMPLE.TS
const cleanup = transport.on(SomeEvent, (payload) => {
  console.log('Received:', payload)
  return { success: true } // Return response
})

// Cleanup when done
onUnmounted(() => cleanup())

transport.flush()

Force flush all pending batch requests.

EXAMPLE.TS
await transport.flush()

Predefined Events

CoreBoxEvents

EXAMPLE.TS
import { CoreBoxEvents } from '@talex-touch/utils/transport'

// UI Control
CoreBoxEvents.ui.show       // Show CoreBox
CoreBoxEvents.ui.hide       // Hide CoreBox
CoreBoxEvents.ui.expand     // Expand/collapse

// Search
CoreBoxEvents.search.query  // Execute search (streaming)
CoreBoxEvents.search.cancel // Cancel search

// Input
CoreBoxEvents.input.get     // Get input value
CoreBoxEvents.input.set     // Set input value
CoreBoxEvents.input.clear   // Clear input

// Provider
CoreBoxEvents.provider.deactivate    // Deactivate provider
CoreBoxEvents.provider.getDetails    // Get provider details (batching)

StorageEvents

EXAMPLE.TS
import { StorageEvents } from '@talex-touch/utils/transport'

// App Storage (with batching)
StorageEvents.app.get       // Get value
StorageEvents.app.set       // Set value
StorageEvents.app.delete    // Delete value

// Plugin Storage (with batching)
StorageEvents.plugin.get    // Get plugin value
StorageEvents.plugin.set    // Set plugin value

PluginEvents

EXAMPLE.TS
import { PluginEvents } from '@talex-touch/utils/transport'

// Lifecycle
PluginEvents.lifecycle.load     // Load plugin
PluginEvents.lifecycle.unload   // Unload plugin
PluginEvents.lifecycle.reload   // Reload plugin

// Features
PluginEvents.feature.trigger    // Trigger feature

// Logging (batched)
PluginEvents.log.write          // Write log entry

BoxItemEvents

EXAMPLE.TS
import { BoxItemEvents } from '@talex-touch/utils/transport'

// CRUD (with batching)
BoxItemEvents.crud.create   // Create item
BoxItemEvents.crud.update   // Update item
BoxItemEvents.crud.upsert   // Create or update
BoxItemEvents.crud.delete   // Delete item

// Batch operations
BoxItemEvents.batch.upsert  // Batch upsert
BoxItemEvents.batch.delete  // Batch delete
BoxItemEvents.batch.clear   // Clear by source

ClipboardEvents v0.9.0

EXAMPLE.TS
import { ClipboardEvents } from '@talex-touch/utils/transport'

// Monitor (streaming)
ClipboardEvents.change      // Subscribe to clipboard changes

// History (with batching)
ClipboardEvents.getHistory  // Query history with pagination
ClipboardEvents.getLatest   // Get most recent item

// Actions
ClipboardEvents.apply       // Apply item to active app
ClipboardEvents.delete      // Delete history item
ClipboardEvents.setFavorite // Toggle favorite status
ClipboardEvents.clearHistory // Clear history
ClipboardEvents.getImageUrl // Resolve original image URL
ClipboardEvents.write       // Write to system clipboard
ClipboardEvents.read        // Read current clipboard snapshot
ClipboardEvents.readImage   // Read clipboard image
ClipboardEvents.readFiles   // Read clipboard file paths
ClipboardEvents.clear       // Clear current clipboard data
ClipboardEvents.copyAndPaste // Write and auto-paste

Example: Subscribe to clipboard changes

EXAMPLE.TS
const controller = await transport.stream(
  ClipboardEvents.change,
  undefined,
  {
    onData: ({ latest, history }) => {
      console.log('latest:', latest?.type, latest?.value)
      console.log('history size:', history.length)
    }
  }
)

onUnmounted(() => controller.cancel())

Example: Query clipboard history

EXAMPLE.TS
const { items, total } = await transport.send(ClipboardEvents.getHistory, {
  page: 1,
  pageSize: 20,
  type: 'text',
  keyword: 'search term'
})

console.log(items[0]?.value, total)

Batching

TuffTransport automatically batches requests for events that support it:

EXAMPLE.TS
// These 3 requests are automatically combined into 1 IPC call
const [a, b, c] = await Promise.all([
  transport.send(StorageEvents.app.get, { key: 'a' }),
  transport.send(StorageEvents.app.get, { key: 'b' }),
  transport.send(StorageEvents.app.get, { key: 'c' }),
])
// Result: Single IPC with 3 requests, 500%+ faster

Batch Configuration

When defining custom events, configure batching:

EXAMPLE.TS
const MyEvent = defineEvent('my-plugin')
  .module('data')
  .event('fetch')
  .define<Request, Response>({
    batch: {
      enabled: true,
      windowMs: 50,        // Collect for 50ms
      maxSize: 20,         // Max 20 requests per batch
      mergeStrategy: 'dedupe' // 'queue' | 'dedupe' | 'latest'
    }
  })

Merge Strategies:

  • queue - All requests processed in order
  • dedupe - Duplicate payloads share response
  • latest - Only latest request for same key kept

Streaming

For large or continuous data, use MessagePort streaming:

EXAMPLE.TS
const MyStreamEvent = defineEvent('my-plugin')
  .module('data')
  .event('stream')
  .define<{ filter: string }, AsyncIterable<DataItem>>({
    stream: {
      enabled: true,
      bufferSize: 100
    }
  })

// Consume stream
const controller = await transport.stream(
  MyStreamEvent,
  { filter: 'active' },
  {
    onData: (item) => items.push(item),
    onEnd: () => console.log('Done'),
    onError: (err) => console.error(err)
  }
)

// Cancel if needed
onUnmounted(() => controller.cancel())

Error Handling

TuffTransport provides structured errors:

EXAMPLE.TS
import { TuffTransportError, TuffTransportErrorCode } from '@talex-touch/utils/transport'

try {
  await transport.send(SomeEvent, data)
} catch (err) {
  if (err instanceof TuffTransportError) {
    switch (err.code) {
      case TuffTransportErrorCode.TIMEOUT:
        console.log('Request timed out')
        break
      case TuffTransportErrorCode.INVALID_EVENT:
        console.log('Invalid event - use defineEvent()')
        break
      case TuffTransportErrorCode.UNKNOWN_EVENT:
        console.log('No handler registered')
        break
    }
  }
}

Error Codes:

CodeDescription
INVALID_EVENTNot a valid TuffEvent
UNKNOWN_EVENTNo handler registered
TIMEOUTRequest timed out
STREAM_CANCELLEDStream was cancelled
BATCH_FAILEDBatch request failed
SERIALIZE_FAILEDPayload serialization failed

Migration from Legacy Channel

Before (Legacy)

EXAMPLE.TS
import { useChannel } from '@talex-touch/utils/plugin/sdk'

const channel = useChannel()
const result = await channel.send('core-box:search:query', { text: 'hello' })

After (TuffTransport)

EXAMPLE.TS
import { useTuffTransport, CoreBoxEvents } from '@talex-touch/utils/transport'

const transport = useTuffTransport()
const result = await transport.send(CoreBoxEvents.search.query, { query: { text: 'hello' } })

Gradual Migration

Both APIs work simultaneously - migrate at your own pace:

EXAMPLE.TS
// Legacy (still works)
await channel.send('storage:app:get', { key: 'theme' })

// New (recommended)
await transport.send(StorageEvents.app.get, { key: 'theme' })

Technical Notes

  • Events are defined via defineEvent().module().event().define() with compile-time request/response typing.
  • Batching aggregates client requests and merges them by strategy before dispatch.
  • Streaming uses MessagePort for continuous data delivery.

Best Practices

  1. Always use TuffEvent - Never pass strings to transport.send()
  2. Define custom events - Use defineEvent() for plugin-specific communication
  3. Leverage batching - Use Promise.all() for multiple requests
  4. Clean up handlers - Call cleanup functions in onUnmounted()
  5. Handle errors - Check TuffTransportErrorCode for specific handling
  6. Use streaming - For large data or real-time updates

Type Definitions

EXAMPLE.TS
interface ITuffTransport {
  send<TReq, TRes>(
    event: TuffEvent<TReq, TRes>,
    payload: TReq,
    options?: SendOptions
  ): Promise<TRes>

  stream<TReq, TChunk>(
    event: TuffEvent<TReq, AsyncIterable<TChunk>>,
    payload: TReq,
    options: StreamOptions<TChunk>
  ): Promise<StreamController>

  on<TReq, TRes>(
    event: TuffEvent<TReq, TRes>,
    handler: (payload: TReq) => TRes | Promise<TRes>
  ): () => void

  flush(): Promise<void>
  destroy(): void
}

interface SendOptions {
  immediate?: boolean
  timeout?: number
}

interface StreamOptions<T> {
  onData: (chunk: T) => void
  onError?: (error: Error) => void
  onEnd?: () => void
}

interface StreamController {
  cancel(): void
  readonly cancelled: boolean
  readonly streamId: string
}
Was this helpful?