Channel
Purpose
The Channel module provides a named, bidirectional communication pipe that combines a Sender and Receiver with coordinated lifecycle controls. Channels provide a high-level abstraction for managing message flow between two endpoints.
Key Interfaces
Channel<T>
The main channel interface that extends StopResumeControl.
interface Channel<T = any> extends StopResumeControl {
label: string // Unique channel identifier
send: SendFn<T> // Send messages through outbound pipeline
receive: ReceiveFn // Process incoming packets
outbound: OutboundQueues & StopResumeControl // Access to outbound queue chain
inbound: InboundQueues & StopResumeControl // Access to inbound queue chain
}
StopResumeControl
Lifecycle control interface for pausing/resuming message processing.
interface StopResumeControl {
stop: () => void // Pause processing (messages accumulate)
resume: () => void // Resume processing accumulated messages
}
Protocol<T>
The protocol object providing security operations.
interface Protocol<T = any> {
packetEncryption: PacketEncryption<T> // Encrypt outbound packets
packetDecryption: PacketDecryption<T> // Decrypt inbound packets
packetObfuscation: PacketObfuscation // Obfuscate outbound packets
packetDeobfuscation: PacketDeobfuscation // Deobfuscate inbound packets
send: SendPacketFn // Transport send function
receive: ReceivePacketFn<T> // Receive callback
getLogger: () => Logger // Logger accessor
}
ProtocolProvider<T>
Factory function that creates a Protocol from transport functions.
type ProtocolProvider<T = any> = (send: SendPacketFn, receive: ReceivePacketFn<T>) => Protocol<T>
ChannelCreater<T>
Factory function type that creates Channel instances.
type ChannelCreater<T = any> = (label: string, send: SendPacketFn, receive: ReceivePacketFn, protocol: ProtocolProvider<T>) => Channel<T>
ChannelStore<T>
Store for managing multiple channels.
interface ChannelStore<T = any> {
readonly create: (label, send, receive, protocol) => Channel<T>
readonly add: (...channels: Channel<T>[]) => void
readonly existsByName: (name: string) => boolean
readonly existsById: (id: string) => boolean
readonly removeByName: (...names: string[]) => void
readonly removeById: (...ids: string[]) => void
readonly clear: () => void
readonly getByName: (name: string) => Channel<T> | null
readonly getById: (id: string) => Channel<T> | null
readonly list: readonly ChannelEntry<T>[]
}
Factory Functions
createChannelFactory
Creates a channel factory with injected sender and receiver factories.
Signature:
function createChannelFactory(createSender: SenderFactory, createReceiver: ReceiverFactory): ChannelCreater
Parameters:
| Parameter | Type | Description |
|---|---|---|
createSender |
SenderFactory |
Factory to create outbound pipelines |
createReceiver |
ReceiverFactory |
Factory to create inbound pipelines |
Returns: A ChannelCreater function.
Example:
import { createChannelFactory } from '@hyperfrontend/network-protocol/lib/channel'
import { createSenderFactory } from '@hyperfrontend/network-protocol/lib/sender'
import { createReceiverFactory } from '@hyperfrontend/network-protocol/lib/receiver'
// Step 1: Create sender and receiver factories (platform-specific)
const createSender = createSenderFactory(/* platform dependencies */)
const createReceiver = createReceiverFactory(/* platform dependencies */)
// Step 2: Create channel factory
const createChannel = createChannelFactory(createSender, createReceiver)
// Step 3: Create a channel
const channel = createChannel(
'my-channel',
(packet) => transport.send(packet), // Send transport function
(packet) => handleMessage(packet.data), // Receive callback
protocolProvider // Security provider
)
createChannelStoreFactory
Creates a channel store factory for managing multiple channels.
Signature:
function createChannelStoreFactory(createChannel: ChannelCreater): () => ChannelStore
Example:
import { createChannelStoreFactory } from '@hyperfrontend/network-protocol/lib/channel'
const createChannelStore = createChannelStoreFactory(createChannel)
const channelStore = createChannelStore()
// Create and automatically register a channel
const channel1 = channelStore.create('channel-1', sendFn, receiveFn, protocolProvider)
// Or add an externally created channel
const channel2 = createChannel('channel-2', sendFn, receiveFn, protocolProvider)
channelStore.add(channel2)
// Look up channels
const found = channelStore.getByName('channel-1')
console.log(channelStore.existsByName('channel-1')) // true
// List all channels
channelStore.list.forEach((entry) => {
console.log(`${entry.id}: ${entry.name}`)
})
// Remove channels
channelStore.removeByName('channel-1')
channelStore.clear() // Remove all
Pipeline Architecture
When a channel is created, it internally constructs two pipelines:
Outbound Pipeline (Sender)
When you call channel.send(origin, target, data):
Inbound Pipeline (Receiver)
When a packet arrives via channel.receive(packet):
Lifecycle Management
Stop/Resume Controls
Channels provide granular control over message processing:
// Stop all processing (both directions)
channel.stop()
// Or stop just one direction
channel.outbound.stop() // Pause outbound only
channel.inbound.stop() // Pause inbound only
// Resume processing
channel.resume()
// Or resume just one direction
channel.outbound.resume()
channel.inbound.resume()
What Happens When Stopped
- Messages Continue to Accumulate:
addMessage()still adds to queues - Processing Pauses: No messages are transformed or sent
- Queue Sizes Grow: Monitor with
queue.size()for backpressure
What Happens When Resumed
- FIFO Processing Resumes: Accumulated messages process in order
- Pipeline Continues: Each queue feeds the next in sequence
Queue Visibility & Monitoring
Access individual queues for monitoring and metrics:
// Outbound queue access
const encryptQueueSize = channel.outbound.encryptionQueue.size()
const serializeQueueSize = channel.outbound.serializationQueue.size()
const obfuscateQueueSize = channel.outbound.obfuscationQueue.size()
// Inbound queue access
const deobfuscateQueueSize = channel.inbound.deobfuscationQueue.size()
const deserializeQueueSize = channel.inbound.deserializationQueue.size()
const decryptQueueSize = channel.inbound.decryptionQueue.size()
// Total pending outbound messages
const totalOutbound = encryptQueueSize + serializeQueueSize + obfuscateQueueSize
// Backpressure detection example
if (totalOutbound > 100) {
console.warn('Outbound backpressure detected!')
// Optionally slow down or pause upstream
}
Backpressure Management
Pattern: Pause on High Queue Depth
const BACKPRESSURE_THRESHOLD = 50
function checkBackpressure(channel: Channel) {
const depth = channel.outbound.encryptionQueue.size()
if (depth > BACKPRESSURE_THRESHOLD) {
channel.outbound.stop()
console.warn(`Pausing channel ${channel.label}: queue depth ${depth}`)
return true
}
return false
}
// Resume when queue drains
function maybeResume(channel: Channel) {
const depth = channel.outbound.encryptionQueue.size()
if (depth < BACKPRESSURE_THRESHOLD / 2) {
channel.outbound.resume()
console.log(`Resuming channel ${channel.label}`)
}
}
Pattern: Graceful Shutdown
async function gracefulShutdown(channel: Channel, timeoutMs = 5000) {
// Stop accepting new messages
channel.stop()
// Wait for queues to drain
const start = Date.now()
while (Date.now() - start < timeoutMs) {
const outboundEmpty =
channel.outbound.encryptionQueue.size() === 0 &&
channel.outbound.serializationQueue.size() === 0 &&
channel.outbound.obfuscationQueue.size() === 0
if (outboundEmpty) {
console.log(`Channel ${channel.label} drained successfully`)
return
}
await new Promise((r) => setTimeout(r, 100))
}
console.warn(`Channel ${channel.label} shutdown timeout - some messages may be lost`)
}
Error Handling
Errors in queues are handled via the onFail callback pattern:
// Errors are logged and passed to fail callbacks
// The channel continues processing subsequent messages
// Access error handling through sender/receiver creation
// See sender/ and receiver/ modules for error callback patterns
Complete Example
import { createProtocol } from '@hyperfrontend/network-protocol/browser/v1'
import { createLogger } from '@hyperfrontend/logging'
// Setup
const logger = createLogger({ level: 'info' })
const protocolProvider = createProtocol(logger, 60) // 60-min refresh
// Create channel
const channel = createChannel(
'iframe-communication',
(packet) => iframe.contentWindow.postMessage(packet, '*'),
(packet) => handleIncomingMessage(packet.data.message),
protocolProvider
)
// Listen for incoming messages
window.addEventListener('message', (event) => {
if (event.data instanceof Uint8Array) {
channel.receive(event.data)
}
})
// Send messages
channel.send('https://parent.example.com', 'https://iframe.example.com', createData(messagePayload))
// Monitor health
setInterval(() => {
console.log(`Outbound queue depth: ${channel.outbound.encryptionQueue.size()}`)
}, 1000)
Relationship to Other Modules
- Depends on:
sender/,receiver/,protocol/,packet/ - Used by:
routing/(for topic-based message routing)
See Also
- Library Index - All modules
- Architecture Guide - Channel architecture
- Browser Entry - Browser-specific channel
- Node Entry - Node.js-specific channel
Related Modules
| Module | Relationship |
|---|---|
| sender/ | Outbound pipeline component |
| receiver/ | Inbound pipeline component |
| protocol/ | Provides security operations |
| queue/ | Underlying queue implementation |
| routing/ | Uses channels for message distribution |