npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@roboto-ai/mcap-codec-browser

v0.3.0

Published

Decodes one topic of an MCAP file (ROS, DDS/IDL, JSON or MessagePack messages) into Apache Arrow in the browser.

Downloads

1,136

Readme

@roboto-ai/mcap-codec-browser

Decodes one topic of an MCAP file into Apache Arrow in the browser. A WASM module reads the file through a byte-range reader you supply: its footer and summary, then only the chunks that hold the topic's messages within the requested row range and, for a window on log time, overlap that window. It decodes ROS 1 messages, ROS 2 and OMG IDL messages over CDR, and JSON or MessagePack messages described by a JSON Schema. The rows arrive as Arrow IPC streams, one per batch, each carrying its own schema and dictionaries, so tableFromIPC decodes any one of them alone.

Each openMcapFile call opens a cursor over one topic of one file, and one decoder runs many cursors, over many files, concurrently. The cursors share WASM module instances, each limited to 512 MiB of memory; with the default limits, one cursor reads messages of up to 64 MiB, such as camera images and lidar point clouds.

The package ships prebuilt, untranspiled ES modules, their TypeScript declarations and the WASM module, and it runs no install script.

Install

npm install @roboto-ai/mcap-codec-browser

Quick start

import { McapDecoder } from "@roboto-ai/mcap-codec-browser";
import { tableFromIPC } from "apache-arrow";

const decoder = new McapDecoder();

await using cursor = await decoder.openMcapFile({
  fileSize: BigInt(size),
  // Called for each byte range the cursor reads; resolve exactly `length` bytes.
  readBytes: async (offset, length, signal) => {
    const response = await fetch(url, {
      signal,
      headers: { range: `bytes=${offset}-${offset + length - 1n}` },
    });
    if (response.status !== 206) {
      throw new Error(`range request failed with HTTP ${response.status}`);
    }
    return new Uint8Array(await response.arrayBuffer());
  },
  request: {
    channel: { topicName: "/odometry" },
    projection: { include: [["speed"], ["pose", "x"]] },
    timestamp: { kind: "message_log_time" },
    timeWindow: {
      startNs: 1_700_000_000_000_000_000n,
      endNs: 1_700_000_060_000_000_000n,
    },
  },
});

for await (const batch of cursor) {
  const table = tableFromIPC(batch.ipc);
  // ... use the rows ...
  batch.release();
}

await decoder.close();
  • File sizes, byte offsets and lengths, time bounds and row numbers are bigint.
  • Column 0 of every batch is the row number: the message's zero-based position among the topic's messages, in the order the file stores them. Column 1 is the timestamp in nanoseconds, from the source the request's timestamp names. The projected fields follow: the projection's include paths, or every field without include, minus its exclude paths. A request without projection gets every top-level field, and an empty include gets none.
  • A McapDecoder decodes on the thread that calls it and blocks that thread for each decoding step, so create one in each worker thread that reads data and open all of that thread's cursors on it. On the page's main thread, use a DecoderPool instead: it opens cursors the same way but runs each module instance in a worker of its own.
  • A cursor holds one batch at a time: call batch.release() before asking for the next, or next() rejects with invalid_state.
  • Dispose of a cursor you stop reading, so its module instance can take another; await using disposes it when its scope ends. An AbortSignal passed to openMcapFile as signal cancels the cursor when the signal aborts.
  • A failure rejects with a CodecError whose code names it, such as unknown_channel, corrupt_input or resource_limit. The exceptions are two values you supply, which reject unchanged: an error your readBytes throws and the reason of an aborted signal. Recognize a CodecError with isCodecError(value), which you can import alone, without the wasm module or the decoders, from @roboto-ai/mcap-codec-browser/errors.

The declarations in dist/index.d.ts document every request member, decoder option, event and error code.

Serving the package

Both decoders load dist/bindings/mcap_codec_browser_bg.wasm, and a DecoderPool also starts dist/worker.js, each by a URL relative to the package's own modules. Without a bundler, serve the package's files at their installed paths. A Vite production build bundles the package with Vite's default options, worker and WASM included. Vite's dev server needs the package excluded from dependency pre-bundling, which would move its modules away from those files:

// vite.config.js
export default { optimizeDeps: { exclude: ["@roboto-ai/mcap-codec-browser"] } };

Send the WASM file as application/wasm, so the browser can compile it while it downloads.

Where the WASM file cannot be served next to the package, or under Node, whose fetch cannot load it, compile the module yourself and pass it as the module option; the decoders then fetch nothing. The package exports the file as @roboto-ai/mcap-codec-browser/wasm. Under Node, which runs a McapDecoder (tested on 22.17.0) but not a DecoderPool:

import { readFile } from "node:fs/promises";
import { createRequire } from "node:module";
import { McapDecoder } from "@roboto-ai/mcap-codec-browser";

const path = createRequire(import.meta.url).resolve(
  "@roboto-ai/mcap-codec-browser/wasm",
);
const decoder = new McapDecoder({
  module: await WebAssembly.compile(await readFile(path)),
});

License

The package is licensed under MPL-2.0; see LICENSE. NOTICE and THIRD_PARTY_LICENSES/ cover the third-party components compiled into the module.