TuffTransport API
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?
| Feature | Legacy Channel | TuffTransport |
|---|---|---|
| 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 |
Quick Start
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.
// ❌ 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:
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.
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 typeoptions- Send options
Options:
| Option | Type | Default | Description |
|---|---|---|---|
immediate | boolean | false | Skip batching, send immediately |
timeout | number | 10000 | Request timeout in ms |
Examples:
// 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.
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()
transport.on(event, handler)
Register an event handler for incoming messages.
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.
await transport.flush()
Predefined Events
CoreBoxEvents
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
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
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
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
Batching
TuffTransport automatically batches requests for events that support it:
// 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:
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 orderdedupe- Duplicate payloads share responselatest- Only latest request for same key kept
Streaming
For large or continuous data, use MessagePort streaming:
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:
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:
| Code | Description |
|---|---|
INVALID_EVENT | Not a valid TuffEvent |
UNKNOWN_EVENT | No handler registered |
TIMEOUT | Request timed out |
STREAM_CANCELLED | Stream was cancelled |
BATCH_FAILED | Batch request failed |
SERIALIZE_FAILED | Payload serialization failed |
Migration from Legacy Channel
Before (Legacy)
import { useChannel } from '@talex-touch/utils/plugin/sdk'
const channel = useChannel()
const result = await channel.send('core-box:search:query', { text: 'hello' })
After (TuffTransport)
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:
// Legacy (still works)
await channel.send('storage:app:get', { key: 'theme' })
// New (recommended)
await transport.send(StorageEvents.app.get, { key: 'theme' })
Best Practices
- Always use TuffEvent - Never pass strings to
transport.send() - Define custom events - Use
defineEvent()for plugin-specific communication - Leverage batching - Use
Promise.all()for multiple requests - Clean up handlers - Call cleanup functions in
onUnmounted() - Handle errors - Check
TuffTransportErrorCodefor specific handling - Use streaming - For large data or real-time updates
Type Definitions
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
}