Skip to content
Peerix v0.5 Released

Peerix v0.5 Released

June 25, 2026·meefik
meefik

Peerix v0.5 is now available. This is a significant update that reimagines how data travels between peers — introducing streaming transfers with progress tracking, abortable transmissions, and optional metadata — while simplifying the underlying channel architecture.

Highlights: Streaming Data Transfers

v0.5 fundamentally changes how send works and how you receive data over channels. Instead of a fire-and-forget API, send now returns an async iterable that doubles as a promise — letting you track transfer progress or simply await completion.

ReadableStream for incoming and outgoing data

The send method now supports streaming any data through a single channel. Large payloads are automatically chunked and buffered on the wire, so you can transmit files and binary blobs without worrying about maximum message size limits.

On the receiving side, the channel:message event fires as soon as the first chunk arrives. The data payload is a ReadableStream/Promise that you can iterate over to receive chunks incrementally — or consume as a promise to get the fully assembled message when you don’t need progress tracking.

Progress tracking

The transfer object returned by send is an async iterator. By looping over it, you can observe how much data has been sent at each step:

const file = new File([new Uint8Array(1024 * 1024)], "example.dat");
const transfer = peer.send(file, {
  label: "chat", // channel label
  info: { name: file.name, size: file.size }, // metadata
  signal: AbortSignal.timeout(10000), // abort signal
});

// track the progress of the transfer
for await (const progress of transfer) {
  const { id, label, current, total } = progress;
  const percent = Math.round((current / total) * 100);
  console.log(`[${id}:${label}] Sending... ${percent}%`);
}

Receiving the file and tracking progress on the other end:

peer.on("channel:message", async (e) => {
  const { remote, label, data, info } = e;
  let current = 0;
  const chunks = [];

  // read data by chunks
  for await (const chunk of data) {
    chunks.push(chunk);
    current += chunk.length;
    const percent = Math.round((current / info.size) * 100);
    console.log(`[${remote.id}:${label}] Receiving... ${percent}%`);
  }

  const file = new File(chunks, info.name);
  console.log("Received:", file);

  // cancel the stream if needed
  // data.cancel();
});

Abortable transfers

You can cancel a transmission at any time using AbortSignal. The example above uses AbortSignal.timeout(10000) to auto-cancel after 10 seconds, but you can also create an AbortController and call .abort() manually whenever your application logic dictates.

Cancellation works on the receiver side as well. Since incoming data is a ReadableStream, you can call data.cancel() at any point during iteration to stop receiving further chunks.

Optional Metadata

The new info option on send lets you attach arbitrary metadata alongside your data. This metadata is delivered with every channel:message event, so the receiving peer can inspect it without waiting for the full payload. This is useful for describing the payload — file names, sizes, types, or any custom information the receiver needs to process the message correctly.

Breaking changes

  1. Data is now sent through a single data channel per peer. If you omit the label option, messages are routed to the default channel instead of all existing channels.
  2. Incoming data is now either a ReadableStream or a Promise. Read the stream to track receiving progress or use the promise to get a specific data type when the full message is received.

Peer Serialization

A new toJSON method was added to both local and remote peers. Use it to serialize peer identity information for logging, debugging, or state persistence:

console.log(peer.toJSON()); // { id: "...", room: "...", ... }

Testing Improvements

Unit tests were added for internal utilities and several signaling drivers. This improves confidence in core functionality and makes future refactoring safer.

Upgrade Notes

When upgrading to v0.5, review the following breaking changes:

  • send API: The return value is now an async iterable transfer object instead of a plain Promise. If you previously awaited send directly, it will still work (the transfer resolves when delivery completes), but you can now also iterate over it for progress updates.
  • Incoming data shape: The data in channel:message events is now always a ReadableStream/Promise. To receive the full message, await it: const payload = await event.data. To track receiving progress, iterate with for await (const chunk of event.data).
  • Single channel: Data is sent over one data channel instead of multiple. The default label is used when no label is provided.

Conclusion

v0.5 transforms data channel messaging into a first-class streaming experience with progress visibility and transfer control built in. For full details on these changes, please visit our updated resources: