### Minimal Protomux Setup Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md This example shows the most basic setup for Protomux, including creating two instances and establishing a channel. It requires `@hyperswarm/secret-stream` for secure communication. ```javascript const Protomux = require('protomux') const SecretStream = require('@hyperswarm/secret-stream') // Create two sides const initiator = new Protomux(new SecretStream(true)) const responder = new Protomux(new SecretStream(false)) // Pipe them (in real use: network connection) initiator.stream.pipe(responder.stream) responder.stream.pipe(initiator.stream) // Create channel on initiator const channel = initiator.createChannel({ protocol: 'hello' }) if (channel) { channel.open() console.log('Initiated') } // Listen on responder responder.pair({ protocol: 'hello' }, async (id) => { const ch = responder.createChannel({ protocol: 'hello', id }) if (ch) ch.open() }) ``` -------------------------------- ### Install Protomux Source: https://github.com/holepunchto/protomux/blob/main/README.md Install the protomux library using npm. ```bash npm install protomux ``` -------------------------------- ### Basic Chat Example Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Sets up a basic chat channel between two Protomux instances. Use this to establish simple bidirectional communication. ```javascript const Protomux = require('protomux') const c = require('compact-encoding') // Setup channels (pipes/replication omitted) const mux1 = new Protomux(stream1) const mux2 = new Protomux(stream2) // Side 1: Responder mux1.pair({ protocol: 'chat' }, async (id) => { const channel = mux1.createChannel({ protocol: 'chat', id }) if (!channel) return const msg = channel.addMessage({ encoding: c.string, onmessage(text) { console.log('Peer:', text) } }) channel.open() }) // Side 2: Initiator const channel = mux2.createChannel({ protocol: 'chat', onopen() { msg.send('Hello!') } }) const msg = channel.addMessage({ encoding: c.string }) channel.open() ``` -------------------------------- ### Basic Usage Example Source: https://github.com/holepunchto/protomux/blob/main/README.md Demonstrates how to set up a Protomux instance, create a protocol channel, add message types, and send messages. Requires a framed stream like @hyperswarm/secret-stream. ```javascript const Protomux = require('protomux') const c = require('compact-encoding') // By framed stream, it has be a stream that preserves the messages, ie something that length prefixes // like @hyperswarm/secret-stream const mux = new Protomux(aStreamThatFrames) // Now add some protocol channels const cool = mux.createChannel({ protocol: 'cool-protocol', id: Buffer.from('optional binary id'), onopen() { console.log('the other side opened this protocol!') }, onclose() { console.log('either side closed the protocol') } }) // And add some messages const one = cool.addMessage({ encoding: c.string, onmessage(m) { console.log('recv message (1)', m) } }) const two = cool.addMessage({ encoding: c.bool, onmessage(m) { console.log('recv message (2)', m) } }) // open the channel cool.open() // And send some data one.send('a string') two.send(true) ``` -------------------------------- ### FIFO Channel Pairing Example Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Demonstrates how Protomux pairs local channels with incoming remote channels based on a first-in, first-out queue. When a remote channel opens, it pairs with the oldest available local channel. If no local channels are waiting, the remote channel is queued for future pairing. ```javascript mux.createChannel({ protocol: 'foo' }).open() // ID 1, waiting Mux.createChannel({ protocol: 'foo' }).open() // ID 2, waiting // Remote opens 'foo' // Pairs with channel 1 (FIFO) // Remote opens 'foo' again // Pairs with channel 2 // Remote opens 'foo' third time // No local channel waiting, queued in incoming // Pair callback fires ``` -------------------------------- ### Protomux Message Encoding and Sending Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/types.md Example of defining a message structure with compact-encoding and sending a message. Ensure the 'channel' object is properly initialized. ```javascript const msg = channel.addMessage({ encoding: c.object({ type: c.string, value: c.uint32 }), onmessage(data) { // data = { type: "foo", value: 123 } } }) msg.send({ type: "foo", value: 123 }) ``` -------------------------------- ### Handshake with Protocol Negotiation Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Demonstrates how to perform a handshake with custom protocol data, including version and network information. Use this for initial connection setup and compatibility checks. ```javascript const mux = new Protomux(stream) const channel = mux.createChannel({ protocol: 'dht', handshake: c.object({ version: c.uint, network: c.string }), onopen(hs) { if (hs.version !== 1) { channel.close() return } console.log('Connected to', hs.network) } }) channel.open({ version: 1, network: 'main' }) ``` -------------------------------- ### Sending a Message with Protomux Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Example of how to add a message type and send data using the Message.send method. Handles backpressure by checking the return value. ```javascript const msg = channel.addMessage({ encoding: c.string }) const ok = msg.send('hello') if (!ok) { // Stream is backpressured, wait for drain } ``` -------------------------------- ### Handle Channel Open Event Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/configuration.md Execute a callback function when the channel is fully opened and ready for communication. This is the appropriate place to start sending messages. ```javascript const channel = mux.createChannel({ protocol: 'sync', onopen() { console.log('Channel opened, ready to use') // Safe to send messages now } }) channel.open() ``` -------------------------------- ### Error in onopen Callback Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/errors.md Errors thrown in the onopen callback are caught and forwarded to stream.destroy(). This ensures that setup failures are properly handled. ```javascript const channel = mux.createChannel({ protocol: 'test', onopen() { throw new Error('Setup failed') // Error is caught and stream.destroy(err) is called } }) ``` -------------------------------- ### Protomux Essential Classes and Operations Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/README.md Demonstrates the creation of a Protomux instance, opening channels, adding message types, and performing control operations. Also shows pairing, batching, and querying functionalities. ```javascript const Protomux = require('protomux') // Create muxer const mux = new Protomux(stream, { alloc: fn }) // Create channel const channel = mux.createChannel({ protocol: 'myproto', id: Buffer.from('...'), onopen: (hs) => { }, onclose: (isRemote) => { } }) // Add message type const msg = channel.addMessage({ encoding: c.string, onmessage: (data) => { } }) // Control channel.open([handshake]) channel.close() msg.send(data) // Pairing mux.pair({ protocol, id }, (id) => { }) mux.unpair({ protocol, id }) // Batching mux.cork() msg.send(data1) msg.send(data2) mux.uncork() // Query mux.opened({ protocol, id }) mux.isIdle() for (const ch of mux) { } ``` -------------------------------- ### Get Last Channel Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Retrieves the most recently created channel for a given protocol and optional ID. Useful for accessing existing communication channels. ```javascript getLastChannel({ protocol, id }) ``` ```javascript const lastChat = mux.getLastChannel({ protocol: 'chat' }) if (lastChat && lastChat.opened) { lastChat.addMessage(/* ... */).send(data) } ``` -------------------------------- ### Protomux Constructor and Methods Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/README.md Details on creating a Protomux instance and its core methods for managing channels and stream multiplexing. ```APIDOC ## Protomux ### Description Provides functionality to multiplex multiple protocols over a single stream. It allows for the creation and management of independent channels for different protocols. ### Methods - **`new Protomux(stream, opts)`** - **Purpose:** Create a new Protomux instance. - **Returns:** `Protomux` - **`.createChannel(opts)`** - **Purpose:** Create a new protocol channel within the muxer. - **Returns:** `Channel | null` - **`.pair(filter, cb)`** - **Purpose:** Register a callback to be executed when a new channel matching the filter is paired. - **Returns:** `void` - **`.unpair(filter)`** - **Purpose:** Unregister a previously registered pair callback. - **Returns:** `void` - **`.opened(filter)`** - **Purpose:** Check if a channel matching the filter is currently open. - **Returns:** `boolean` - **`.getLastChannel(filter)`** - **Purpose:** Retrieve the last created channel that matches the filter. - **Returns:** `Channel | null` - **`.cork()`** - **Purpose:** Begin batching outgoing messages to improve efficiency. - **Returns:** `void` - **`.uncork()`** - **Purpose:** End batching and flush all batched messages. - **Returns:** `void` - **`.isIdle()`** - **Purpose:** Check if the muxer has any active channels. - **Returns:** `boolean` - **`.destroy(err)`** - **Purpose:** Destroy the muxer, optionally with an error. - **Returns:** `void` - **`[Symbol.iterator]()`** - **Purpose:** Returns an iterator to loop over all active channels. - **Returns:** `Iterator` ### Static Methods - **`.from(stream | mux, opts)`** - **Purpose:** Get an existing Protomux instance or create a new one from a stream. - **Returns:** `Protomux` - **`.isProtomux(obj)`** - **Purpose:** Type check to determine if an object is a Protomux instance. - **Returns:** `boolean` ``` -------------------------------- ### Protomux Instance Creation Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/00-START-HERE.txt Instantiate the Protomux multiplexer with an underlying stream. This is the foundational step for establishing communication. ```javascript const mux = new Protomux(stream) ``` -------------------------------- ### Instantiate Protomux with SecretStream Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Demonstrates how to create two Protomux instances, each wrapped around a SecretStream, and establish bidirectional communication by piping their data streams. ```javascript const Protomux = require('protomux') const SecretStream = require('@hyperswarm/secret-stream') // Create two multiplexers over secret streams const initiator = new Protomux(new SecretStream(true)) const responder = new Protomux(new SecretStream(false)) // Pipe their data initiator.stream.pipe(responder.stream) responder.stream.pipe(initiator.stream) ``` -------------------------------- ### Create Protomux Instance using Protomux.from Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md A utility method to obtain a Protomux instance from either a raw stream or an existing Protomux instance. If the input is already a Protomux, it is returned directly. ```javascript const mux = Protomux.from(someStream) // Always returns a Protomux, whether someStream was raw or already wrapped ``` -------------------------------- ### new Protomux(stream, [options]) Source: https://github.com/holepunchto/protomux/blob/main/README.md Creates a new Protomux instance. The stream should be a framed stream that preserves messages. ```APIDOC ## new Protomux(stream, [options]) ### Description Make a new instance. `stream` should be a framed stream, preserving the messages written. ### Options ```js { // Called when the muxer wants to allocate a message that is written, defaults to Buffer.allocUnsafe. alloc (size) {} } ``` ``` -------------------------------- ### Protomux Class Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/INDEX.txt Documentation for the Protomux class, including its constructor, static methods, and instance methods. ```APIDOC ## Protomux Class ### Description Provides the core functionality for managing multiplexed streams and channels. ### Methods - **constructor(options?: ProtomuxOptions)**: Initializes a new Protomux instance. - **static create(options?: ProtomuxOptions)**: Factory method to create a Protomux instance. - **channel(channelOptions?: ChannelOptions)**: Creates a new channel. - **destroy()**: Destroys the Protomux instance and all associated channels. Refer to `protomux-reference.md` for full method signatures and details. ``` -------------------------------- ### Send and Receive Strings with Protomux Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Demonstrates how to send and receive string messages over a Protomux channel. Both sides need to define the message type and its encoding using `compact-encoding`. ```javascript const c = require('compact-encoding') // Side A: Responder responder.pair({ protocol: 'chat' }, async (id) => { const channel = responder.createChannel({ protocol: 'chat', id }) if (!channel) return const msg = channel.addMessage({ encoding: c.string, onmessage(text) { console.log('Responder received:', text) } }) channel.open() }) // Side B: Initiator const channel = initiator.createChannel({ protocol: 'chat', onopen() { msg.send('Hello from initiator') } }) const msg = channel.addMessage({ encoding: c.string, onmessage(text) { console.log('Initiator received:', text) } }) channel.open() ``` -------------------------------- ### Protomux Constructor Options Source: https://github.com/holepunchto/protomux/blob/main/README.md Configuration options for the Protomux constructor, specifically the `alloc` function for message allocation. ```javascript { // Called when the muxer wants to allocate a message that is written, defaults to Buffer.allocUnsafe. alloc (size) {} } ``` -------------------------------- ### Protomux.from(stream | muxer, [options]) Source: https://github.com/holepunchto/protomux/blob/main/README.md A helper function to create a Protomux instance, accepting either an existing muxer or a stream. ```APIDOC ## Protomux.from(stream | muxer, [options]) ### Description Helper to accept either an existing muxer instance or a stream (which creates a new one). ``` -------------------------------- ### ProtomuxOptions Configuration Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/types.md Configuration object for initializing Protomux. Allows for a custom buffer allocator function. ```javascript { alloc: function(size: number): Buffer } ``` -------------------------------- ### Protomux Multiple Channels Same Protocol Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/README.md Shows how to create multiple channels for the same protocol, each with a unique ID. This is useful for scenarios like chat rooms where multiple instances of the same protocol are needed. ```javascript for (const room of ['general', 'random']) { const ch = mux.createChannel({ protocol: 'chat', id: Buffer.from(room), unique: false }) // ... configure and open } ``` -------------------------------- ### Multiple Channels, Same Protocol Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Shows how to open multiple channels for the same protocol and ID, useful for scenarios like chat rooms where multiple connections to the same logical entity are needed. Set `unique: false` to allow duplicates. ```javascript const channel1 = mux.createChannel({ protocol: 'relay', id: Buffer.from('room-1'), unique: false // Allow duplicates }) const channel2 = mux.createChannel({ protocol: 'relay', id: Buffer.from('room-1'), unique: false }) channel1.open() channel2.open() // Both are open simultaneously for same protocol+id ``` -------------------------------- ### Trace Protomux Channel Creation Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Wrap the `createChannel` method to log details about newly created channels, including their protocol and ID, before they are actually created. ```javascript const create = mux.createChannel.bind(mux) MUX.createChannel = function(opts) { console.log('createChannel:', opts.protocol, opts.id?.toString('hex')) return create(opts) } ``` -------------------------------- ### Protocol Tracing Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Wraps the `createChannel` and `pair` methods of the mux object to log channel creation and pairing events. This helps in tracing the lifecycle of channels. ```javascript const origCreateChannel = mux.createChannel.bind(mux) Mux.createChannel = function(opts) { console.log('[CREATE]', opts.protocol, opts.id?.toString('hex')) const ch = origCreateChannel(opts) if (ch) { const origClose = ch.close.bind(ch) ch.close = function() { console.log('[CLOSE]', ch.protocol) return origClose() } } return ch } const origPair = mux.pair.bind(mux) Mux.pair = function(filter, notify) { console.log('[PAIR]', filter.protocol, filter.id?.toString('hex')) return origPair(filter, notify) } ``` -------------------------------- ### ChannelOptions Configuration Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/types.md Configuration object for creating a new channel. Includes options for protocol, aliases, handshake, and various callbacks. ```javascript { userData: any, protocol: string, aliases: string[], id: Buffer | null, unique: boolean, handshake: Encoding | null, messages: MessageOptions[], onopen: function, onclose: function, ondestroy: function, ondrain: function } ``` -------------------------------- ### Protomux Request-Reply Pattern Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/README.md Demonstrates a request-reply pattern using Protomux. It sets up two message types: one for sending queries and another for receiving replies, managing pending requests with a Map. ```javascript const pending = new Map() let nextId = 0 const reply = channel.addMessage({ encoding: c.object({ id: c.uint, response: c.string }), onmessage: (msg) => pending.get(msg.id)?.(msg.response) }) const request = channel.addMessage({ encoding: c.object({ id: c.uint, query: c.string }), onmessage: (msg) => reply.send({ id: msg.id, response: 'answer' }) }) await new Promise((resolve) => { const id = nextId++ pending.set(id, resolve) request.send({ id, query: 'question' }) }) ``` -------------------------------- ### Custom Buffer Allocation with Protomux Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Configure Protomux to use a custom buffer allocation strategy by providing an `alloc` function in the options. This function should accept a size and return a Buffer. Alternatively, assign an allocation function to `stream.alloc`. ```javascript const mux = new Protomux(stream, { alloc(size) { return Buffer.alloc(size) // Zeroed } }) ``` ```javascript stream.alloc = (size) => myBufferPool.allocate(size) ``` -------------------------------- ### Channel Creation and Opening Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/00-START-HERE.txt Create a new channel for a specific protocol and open it. Channels are used to isolate different communication protocols over a single stream. ```javascript const channel = mux.createChannel({ protocol: 'chat' }) channel.open() ``` -------------------------------- ### Channel.open() Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Opens the channel and sends an open message to the remote peer. It handles pairing with remote channels and calling the `onopen` callback when the channel is fully established. ```APIDOC ## Channel.open([handshake]) ### Description Open the channel and send open message to remote. ### Method open ### Parameters #### Path Parameters - **handshake** (any) - Optional - Data to encode with handshake encoding (if defined in createChannel) ### Request Example ```javascript const channel = mux.createChannel({ protocol: 'greet', handshake: c.string, onopen(greeting) { console.log('Peer says:', greeting) } }) channel.open('Hello from server') ``` ### Behavior: - Allocates a local channel id - Encodes and sends control message (type=1: open) to remote - If a matching remote channel is waiting, pairs immediately (async via queueTick) - If remoteId is already set, pairs synchronously in `_fullyOpen()` - `onopen` is called when remote side acknowledges (async) ``` -------------------------------- ### Create Channel Options Source: https://github.com/holepunchto/protomux/blob/main/README.md Options available when creating a new protocol channel using `mux.createChannel`. ```javascript { // Used to match the protocol protocol: 'name of the protocol', // Optional additional binary id to identify this channel id: buffer, // Optional encoding for a handshake handshake: encoding, // Optional array of messages types you want to send/receive. messages: [], // Called when the remote side adds this protocol. // Errors here are caught and forwared to stream.destroy async onopen (handshake) {}, // Called when the channel closes - ie the remote side closes or rejects this protocol or we closed it. // Errors here are caught and forwared to stream.destroy async onclose () {}, // Called after onclose when all pending promises has resolved. async ondestroy () {} } ``` -------------------------------- ### Protomux Architecture Diagram Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Illustrates the communication flow between two sides in Protomux, showing the sequence of actions for opening channels and sending messages. ```text Side A Side B ────────────────────────────────────── createChannel(foo) ─open──→ pair callback ↓ createChannel(foo) onopen() ←────open ack──── onopen() send(data) ─data msg──→ onmessage() ``` -------------------------------- ### Protomux Constructor Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Initializes a new Protomux instance to manage protocol multiplexing over a given framed stream. It accepts a stream and optional configuration options for buffer allocation. ```APIDOC ## Protomux Constructor ### Description Initializes a new Protomux instance to manage protocol multiplexing over a given framed stream. It accepts a stream and optional configuration options for buffer allocation. ### Signature ```javascript new Protomux(stream, [options]) ``` ### Parameters #### Parameters - **stream** (Stream) - Required - A framed stream that preserves message boundaries (e.g., @hyperswarm/secret-stream). Must emit `data`, `drain`, `end`, `error`, and `close` events. Must not have `userData` set to another Protomux instance. - **options** (object) - Optional - Configuration options - **alloc** (function) - Optional - Custom buffer allocator. Receives size (number) and returns Buffer. If stream has an `alloc` method, that is used by default. ### Returns Protomux instance ### Properties - **stream** (Stream) — The underlying stream passed to constructor - **corked** (number) — Internal counter for corking depth (0 = not corked) - **drained** (boolean) — True if the stream is drained and ready for writes - **isProtomux** (boolean) — Always `true`, marker for identity checking ### Example ```javascript const Protomux = require('protomux') const SecretStream = require('@hyperswarm/secret-stream') // Create two multiplexers over secret streams const initiator = new Protomux(new SecretStream(true)) const responder = new Protomux(new SecretStream(false)) // Pipe their data initiator.stream.pipe(responder.stream) responder.stream.pipe(initiator.stream) ``` ``` -------------------------------- ### Protomux.from Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md A static helper method to obtain a Protomux instance, either by wrapping a given stream or by returning an existing Protomux instance. ```APIDOC ## Protomux.from(stream | muxer, [options]) ### Description A static helper method to obtain a Protomux instance, either by wrapping a given stream or by returning an existing Protomux instance. It simplifies the process of getting a usable Protomux object from various inputs. ### Signature ```javascript static from(stream | muxer, options) ``` ### Parameters #### Parameters - **stream | muxer** (Stream | Protomux) - A stream to wrap or an existing Protomux instance to return. - **options** (object) - Options to be passed to the Protomux constructor if a new instance needs to be created. ### Returns Protomux instance (existing or newly created) ### Behavior - If the input is already a Protomux instance (identified by the `isProtomux` property), it is returned directly. - If the input has a `userData` property that points to a Protomux instance, that instance is returned. - Otherwise, the input stream is wrapped in a new Protomux instance. ### Example ```javascript const mux = Protomux.from(someStream) // Always returns a Protomux, whether someStream was raw or already wrapped ``` ``` -------------------------------- ### Multiple Channels for the Same Protocol Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Demonstrates creating multiple independent channels for the same protocol ('chat'), each identified by a unique room ID. Includes message handling for text messages within each room. ```javascript // Create multiple independent channels for same protocol const roomIds = ['general', 'announcements', 'random'] for (const roomId of roomIds) { const channel = mux.createChannel({ protocol: 'chat', id: Buffer.from(roomId), unique: false }) if (channel) { const msg = channel.addMessage({ encoding: c.string, onmessage(text) { console.log(`[${roomId}]`, text) } }) channel.open() } } ``` -------------------------------- ### Good Protocol Naming Conventions Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/configuration.md Employ descriptive and hierarchical names for protocols to improve clarity and organization. ```javascript // Good 'hypercore-discovery' 'hyperdrive-sync' 'replicator-v2' // Avoid 'foo', 'bar', 'x' ``` -------------------------------- ### Promise-Based State Tracking in Protomux Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Illustrates how Protomux uses promises to track the state of asynchronous operations like onopen and onclose callbacks. The `_track` method increments a pending count for promises, and `_dec` decrements it, ensuring that `_destroy` is only called after all promises have settled. ```javascript _track(p) { if (isPromise(p)) { this._active++ // Increment pending count return p.then( this._decBound, // On resolve this._decAndDestroyBound // On error ) } } _dec() { if (--this._active === 0 && this.closed === true) { this._destroy() // Only destroy when all promises done } } ``` -------------------------------- ### Implement Request-Reply Pattern in JavaScript Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Use this pattern for synchronous-like request and response communication. Ensure the channel is opened before sending requests. ```javascript const c = require('compact-encoding') class RequestReply { constructor(channel) { this.channel = channel this.pending = new Map() this.nextId = 0 this.reply = channel.addMessage({ encoding: c.object({ requestId: c.uint, response: c.string }), onmessage: (msg) => { const resolve = this.pending.get(msg.requestId) if (resolve) { resolve(msg.response) this.pending.delete(msg.requestId) } } }) this.request = channel.addMessage({ encoding: c.object({ requestId: c.uint, query: c.string }), onmessage: (msg) => { const response = this.handleQuery(msg.query) this.reply.send({ requestId: msg.requestId, response }) } }) } async send(query) { const requestId = this.nextId++ const promise = new Promise((resolve) => { this.pending.set(requestId, resolve) }) this.request.send({ requestId, query }) return promise } handleQuery(q) { // Application-specific return 'Response to: ' + q } } // Usage const channel = mux.createChannel({ protocol: 'rpc' }) const rpc = new RequestReply(channel) channel.open() const result = await rpc.send('Who am I?') console.log(result) // "Response to: Who am I?" ``` -------------------------------- ### Negotiate Protocol Version with Handshake Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/configuration.md Implement handshake versioning to ensure compatible protocol versions between peers. Close the channel if versions are incompatible. ```javascript const channel = mux.createChannel({ protocol: 'myproto', handshake: c.object({ version: c.uint }), onopen(hs) { if (hs.version !== 1) { console.error('Incompatible version') channel.close() } } }) channel.open({ version: 1 }) ``` -------------------------------- ### Batch Buffering and Optimization Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Details the process of batching messages when cork() is called, including batch creation, encoding, and optimizations for consecutive messages. ```text 1. Create batch: `mux._batch = []` 2. All message sends encode into batch buffer 3. On `uncork()`: compress batch (consecutive same-channel msgs), encode once, write to stream ``` -------------------------------- ### Allocation Phase for Encoding Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md After determining the required buffer size in the preencode phase, allocate the buffer using the configured allocator. Reset `state.start` to 0 for the subsequent encoding step. ```javascript state.buffer = allocator(state.end) state.start = 0 ``` -------------------------------- ### Protomux Echo Server Pattern Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/README.md Implements an echo server using Protomux. It pairs channels, creates a new channel for the 'echo' protocol, adds a string message type, and sends back received messages. ```javascript mux.pair({ protocol: 'echo' }, (id) => { const ch = mux.createChannel({ protocol: 'echo', id }) if (!ch) return const msg = ch.addMessage({ encoding: c.string, onmessage: (text) => msg.send('echo: ' + text) }) ch.open() }) ``` -------------------------------- ### Dynamic Channel Acceptance (Server) Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Server-side code to dynamically accept channels for a specific protocol ('app') with any ID. It sets up message handling for incoming string data and echoes it back. ```javascript // Server: listen for any protocol with any id mux.pair({ protocol: 'app', id: null }, async (id) => { const channel = mux.createChannel({ protocol: 'app', id }) if (!channel) return const msg = channel.addMessage({ encoding: c.string, onmessage(data) { console.log('From client:', data) msg.send('Echo: ' + data) } }) channel.open() }) ``` -------------------------------- ### Create a New Protocol Channel Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Use `createChannel` to establish a new protocol channel. It returns null if the channel cannot be opened due to a duplicate unique channel or if the stream is already destroyed. Configure channel behavior with various options like protocol, ID, and event handlers. ```javascript createChannel(options) ``` ```javascript const channel = mux.createChannel({ protocol: 'chat', id: Buffer.from('user-123'), handshake: c.string, onopen(greeting) { console.log('Remote says:', greeting) }, onclose(isRemote) { console.log('Closed by', isRemote ? 'remote' : 'us') } }) if (channel) { channel.open('Hello from ' + require('os').hostname()) } ``` -------------------------------- ### Encode Phase with compact-encoding Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Write the value into the allocated buffer during the encode phase. The `state.start` property is updated to point to the next available position in the buffer. ```javascript c.uint.encode(state, value) // state.start moves to next position ``` -------------------------------- ### Pre-populate Channel Messages Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/configuration.md Define message types upfront when creating a channel. This is equivalent to calling `addMessage()` for each item in the array during channel initialization. ```javascript const channel = mux.createChannel({ protocol: 'api', messages: [ { encoding: c.string, onmessage(msg) { console.log('String:', msg) } }, { encoding: c.uint32, onmessage(num) { console.log('Number:', num) } } ] }) ``` -------------------------------- ### Open a Protomux Channel Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Opens a channel and sends an open message to the remote peer. The `handshake` parameter can include user data for initial communication. ```javascript channel.open(handshake) ``` ```javascript const channel = mux.createChannel({ protocol: 'greet', handshake: c.string, onopen(greeting) { console.log('Peer says:', greeting) } }) channel.open('Hello from server') ``` -------------------------------- ### Implement Pub-Sub Pattern in JavaScript Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Use this pattern for broadcasting messages to multiple subscribers. The remote peer is notified when a new topic is subscribed to. ```javascript class PubSub { constructor(channel) { this.subscribers = new Map() this.pubMsg = channel.addMessage({ encoding: c.object({ topic: c.string, data: c.buffer }), onmessage: (msg) => { const subs = this.subscribers.get(msg.topic) || [] for (const sub of subs) sub(msg.data) } }) this.subMsg = channel.addMessage({ encoding: c.string, onmessage: (topic) => { console.log('Remote subscribed to:', topic) } }) } subscribe(topic, callback) { if (!this.subscribers.has(topic)) { this.subscribers.set(topic, []) this.subMsg.send(topic) // Notify remote } this.subscribers.get(topic).push(callback) } publish(topic, data) { this.pubMsg.send({ topic, data }) } } // Usage const channel = mux.createChannel({ protocol: 'pubsub' }) const ps = new PubSub(channel) channel.open() ps.subscribe('news', (data) => { console.log('News:', data.toString()) }) ps.publish('news', Buffer.from('Breaking news!')) ``` -------------------------------- ### Optimize Writes with Corking Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Use `mux.cork()` and `mux.uncork()` to batch multiple `msg.send()` calls into a single network write, improving efficiency. Nested corking is supported. ```javascript const msg = channel.addMessage({ encoding: c.uint32 }) // Without corking: 1000 individual writes for (let i = 0; i < 1000; i++) { msg.send(i) } // With corking: single write mux.cork() for (let i = 0; i < 1000; i++) { msg.send(i) } mux.uncork() // Nested corking is allowed mux.cork() channel.cork() // Just delegates to mux for (let i = 0; i < 100; i++) msg.send(i) channel.uncork() mux.uncork() // Flushes all at once ``` -------------------------------- ### Iterate Open Channels Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Allows iteration over the Protomux object to access currently open channels. Yields non-null session objects from the internal _local array. ```javascript for (const channel of mux) { // Yields open channels from mux._local } ``` -------------------------------- ### Iterate Over Open Channels Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Provides an iterator to loop through all currently open channels. Each iteration yields a Channel instance. ```javascript for (const channel of mux) { // channel is a Channel instance } ``` ```javascript for (const channel of mux) { console.log('Open protocol:', channel.protocol) } ``` -------------------------------- ### Channel Methods Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/README.md Methods for interacting with individual protocol channels managed by Protomux. ```APIDOC ## Channel ### Description Represents an individual protocol channel within a Protomux instance. Used for sending and receiving messages specific to a protocol. ### Methods - **`.open(hs)`** - **Purpose:** Open the channel. - **Parameters:** - `hs` (any): Handshake data or information. - **Returns:** `void` - **`.close()`** - **Purpose:** Close the channel. - **Returns:** `void` - **`.addMessage(opts)`** - **Purpose:** Register a message handler or definition for this channel. - **Parameters:** - `opts` (object): Options for the message. - **Returns:** `Message` - **`.cork()`** - **Purpose:** Batch outgoing messages for this channel (delegates to the muxer). - **Returns:** `void` - **`.uncork()`** - **Purpose:** Flush batched messages for this channel (delegates to the muxer). - **Returns:** `void` - **`.fullyOpened()`** - **Purpose:** Returns a promise that resolves when the channel is fully open. - **Returns:** `Promise` - **`.fullyClosed()`** - **Purpose:** Returns a promise that resolves when the channel is fully closed. - **Returns:** `Promise` ``` -------------------------------- ### createChannel Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Creates a new protocol channel with specified options. Returns null if the channel cannot be opened due to duplicates or stream destruction. ```APIDOC ## Method: `createChannel(options)` ### Description Create a new protocol channel. Returns `null` if the channel cannot be opened (duplicate unique channel or stream already destroyed). ### Parameters #### Path Parameters - `options` (object) - Required - Channel configuration - `options.userData` (any) - Optional - Arbitrary user data attached to channel for application use - `options.protocol` (string) - Required - Protocol name to match against remote channels - `options.id` (Buffer) - Optional - Optional binary ID for disambiguating channels with same protocol - `options.aliases` (string[]) - Optional - Alternative protocol names for matching. Allows a channel to accept open requests for multiple protocol names. - `options.unique` (boolean) - Optional - If true, prevents duplicate channels with same protocol+id. If false, allows multiple. - `options.handshake` (encoding) - Optional - Compact-encoding codec for handshake data passed to `open()` - `options.messages` (object[]) - Optional - Array of message options (pre-add messages at creation) - `options.onopen` (function) - Optional - Called when channel opens. Receives `(handshake, channel)`. Can be async. Errors destroy stream. - `options.onclose` (function) - Optional - Called when channel closes. Receives `(isRemote, channel)` where isRemote indicates if remote initiated close. Can be async. Errors destroy stream. - `options.ondestroy` (function) - Optional - Called after onclose when all pending promises resolve. Receives `(channel)`. Can be async. - `options.ondrain` (function) - Optional - Called when stream drains (ready for more writes). Receives `(channel)`. Can be async. ### Returns Channel | null ### Semantics - If `unique: true` and a channel with the same protocol+id is already open, returns null - If a matching incoming remote channel exists (waiting to pair), pairs with it immediately - Otherwise creates a new outgoing channel - Channel pairing is queue-based: first local channel matches first incoming remote channel with same protocol+id - Multiple channels must use `unique: false` ### Request Example ```javascript const channel = mux.createChannel({ protocol: 'chat', id: Buffer.from('user-123'), handshake: c.string, onopen(greeting) { console.log('Remote says:', greeting) }, onclose(isRemote) { console.log('Closed by', isRemote ? 'remote' : 'us') } }) if (channel) { channel.open('Hello from ' + require('os').hostname()) } ``` ``` -------------------------------- ### Incoming Message Buffering Logic Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Explains how incoming messages are buffered when a channel is in a pending state, including buffering limits and stream pausing/resuming. ```text 1. Message is buffered in `remote.pending` array 2. Buffered byte count tracked in `mux._buffered` 3. When buffered > MAX_BUFFERED (32 KB): stream.pause() 4. When channel opens: `_drain()` replays pending messages 5. After replayed: stream.resume() if buffered dropped below threshold ``` -------------------------------- ### mux.createChannel(opts) Source: https://github.com/holepunchto/protomux/blob/main/README.md Adds a new protocol channel to the muxer. Returns null if the channel should not be opened. ```APIDOC ## mux.createChannel(opts) ### Description Add a new protocol channel. Options include: ```js { // Used to match the protocol protocol: 'name of the protocol', // Optional additional binary id to identify this channel id: buffer, // Optional encoding for a handshake handshake: encoding, // Optional array of messages types you want to send/receive. messages: [], // Called when the remote side adds this protocol. // Errors here are caught and forwared to stream.destroy async onopen (handshake) {}, // Called when the channel closes - ie the remote side closes or rejects this protocol or we closed it. // Errors here are caught and forwared to stream.destroy async onclose () {}, // Called after onclose when all pending promises has resolved. async ondestroy () {} } ``` Sessions are paired based on a queue, so the first remote channel with the same `protocol` and `id`. **NOTE**: `mux.createChannel` returns `null` if the channel should not be opened, ie it's a duplicate channel or the remote has already closed this one. If you want multiple sessions with the same `protocol` and `id`, set `unique: false` as an option. ``` -------------------------------- ### onopen(handshake, channel) Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/types.md Callback invoked when a new channel is opened. It receives handshake data and the channel object. ```APIDOC ## onopen(handshake, channel) ### Description Callback invoked when a new channel is opened. It receives handshake data and the channel object. ### Parameters #### Path Parameters - **handshake** (any) - Description: Decoded handshake data (if handshake encoding provided), null otherwise - **channel** (Channel) - Description: The channel being opened ### Errors Caught and forwarded to stream.destroy() ``` -------------------------------- ### Define Aliases for Protomux Channel Protocol Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/configuration.md Provide alternative protocol names for backward compatibility. The channel will match if the remote protocol matches the primary or any alias. ```javascript const channel = mux.createChannel({ protocol: 'myproto-v2', aliases: ['myproto', 'myproto-v1'] // Accept old names too }) ``` -------------------------------- ### Protomux Multiple Message Types Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Shows how to register and exchange multiple message types (string, uint32, buffer) on the same Protomux channel. Ensure messages are registered in the same order on both sides. ```javascript // Both sides: register messages in same order const msgText = channel.addMessage({ encoding: c.string, onmessage(text) { console.log('Text:', text) } }) const msgNumber = channel.addMessage({ encoding: c.uint32, onmessage(num) { console.log('Number:', num) } }) const msgBuffer = channel.addMessage({ encoding: c.buffer, onmessage(buf) { console.log('Buffer:', buf) } }) // Send various types msgText.send('hello') msgNumber.send(42) msgBuffer.send(Buffer.from('data')) ``` -------------------------------- ### Fix Protocol Name Mismatch in Protomux Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/errors.md Ensure protocol names match exactly when creating channels. Use aliases for backward compatibility if needed. ```javascript // Side A const a = mux.createChannel({ protocol: 'sync' }) a.open() // Side B const b = mux.createChannel({ protocol: 'sync-v1' }) // Different name! b.open() // Neither side knows the other exists ``` ```javascript // Both sides const ch = mux.createChannel({ protocol: 'sync' }) ch.open() ``` ```javascript // Side A (new version) const a = mux.createChannel({ protocol: 'sync-v2', aliases: ['sync'] }) // Side B (old version) const b = mux.createChannel({ protocol: 'sync' }) // Will pair because 'sync' matches the alias ``` -------------------------------- ### channel.open([handshake]) Source: https://github.com/holepunchto/protomux/blob/main/README.md Opens the protocol channel, optionally with a handshake payload. ```APIDOC ## channel.open([handshake]) ### Description Open the channel. ``` -------------------------------- ### Detect Channel Open Failures Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/examples.md Check the return value of `mux.createChannel()` to detect if a channel was rejected. Use `channel.fullyOpened()` to asynchronously confirm if the channel is open. ```javascript const channel = mux.createChannel({ protocol: 'sync' }) if (!channel) { // Channel rejected (already open or stream destroyed) console.error('Cannot create channel') } else { channel.open() const opened = await channel.fullyOpened() if (opened) { console.log('Channel is open') } else { console.log('Channel rejected or closed') } } ``` -------------------------------- ### MessageOptions Configuration Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/types.md Configure message handling with options for encoding, auto-batching, and an on-message callback. The autoBatch flag enables an internal corking optimization. ```typescript { encoding: Encoding, autoBatch: boolean, onmessage: function } ``` -------------------------------- ### Decode Phase with compact-encoding Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Decode a value from a buffer. The `state.start` property is advanced to the position after the decoded value, indicating the next read position. ```javascript const state = { buffer, start: 0, end: buffer.length } const value = c.uint.decode(state) // state.start moves to next position ``` -------------------------------- ### High-Throughput Batching Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/protomux-reference.md Utilizes `cork()` and `uncork()` to batch multiple message writes into a single, more efficient operation. This is optimal for high-throughput scenarios where minimizing overhead is critical. ```javascript const channel = mux.createChannel({ protocol: 'stream' }) const msg = channel.addMessage({ encoding: c.uint32 }) channel.open() // Without corking: each send() writes independently // With corking: batches multiple writes mux.cork() for (let i = 0; i < 1000; i++) { msg.send(i) } mux.uncork() // All sent in single batch ``` -------------------------------- ### Protomux Channel State Machine Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/internals.md Defines the lifecycle states of a Protomux channel, from its initial pending state to being fully destroyed. ```text PENDING → (waiting for remote to open) ↓ OPENED → (remote opened, onopen called) ↓ CLOSED → (close() called or remote closed) ↓ DESTROYED → (ondestroy called, cleanup done) ``` -------------------------------- ### Channel Class Source: https://github.com/holepunchto/protomux/blob/main/_autodocs/INDEX.txt Documentation for the Channel class, covering its creation, lifecycle, and message handling. ```APIDOC ## Channel Class ### Description Represents an individual multiplexed channel within a Protomux instance. ### Methods - **send(message: Message)**: Sends a message on the channel. - **receive()**: Returns a stream or promise for receiving messages. - **close()**: Closes the channel. - **destroy()**: Destroys the channel. Refer to `protomux-reference.md` for full method signatures and details. ```