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

@thermal-label/transport

v0.2.2

Published

USB, TCP, Serial, WebUSB, Web Serial, and Web Bluetooth transport classes for thermal-label drivers

Downloads

711

Readme

@thermal-label/transport

npm version CI License: MIT

USB, TCP, Serial, WebUSB, Web Serial, and Web Bluetooth transport classes for the thermal-label printer driver ecosystem. Every class implements the Transport interface from @thermal-label/contracts, so drivers program against one pull-based API regardless of the underlying channel.

Install

pnpm add @thermal-label/transport

Node consumers that use UsbTransport also need the native usb package, and consumers that use SerialTransport also need serialport:

pnpm add usb           # for UsbTransport
pnpm add serialport    # for SerialTransport

Both are declared as optional peer dependencies so browser-only consumers never download them.

What's in the box

| Export | Runtime | Purpose | | --------------------------------- | ------- | ------------------------------------------------------------------------------------ | | UsbTransport | Node | libusb bindings for USB Printer Class devices. Interface 0, bulk IN / OUT. | | TcpTransport | Node | Raw TCP (port 9100 / JetDirect by default) with partial-read buffering. | | SerialTransport | Node | serialport wrapper for /dev/rfcomm*, /dev/ttyUSB*, COM<n>. | | WebUsbTransport | Browser | navigator.usb picker + USBDevice wrapper. | | WebSerialTransport | Browser | navigator.serial picker + SerialPort wrapper (covers BT SPP + USB-serial). | | WebBluetoothTransport | Browser | GATT transport for BLE printers (Niimbot, Phomemo, Brother BLE, …). | | matchDevice / buildUsbFilters | Both | Helpers for matching and requesting USB devices. | | buildBluetoothRequestOptions | Both | Build navigator.bluetooth.requestDevice options from a BluetoothConfig. | | buildSerialRequestOptions | Both | Build navigator.serial.requestPort options, optionally allow BT service class IDs. | | discoverAll | Both | Aggregate PrinterDiscovery implementations across drivers. |

Subpath imports

Transport classes are split by runtime so browser bundlers never see the usb native addon:

// Node.js
import { UsbTransport, TcpTransport, SerialTransport } from '@thermal-label/transport/node';

// Browser
import {
  WebUsbTransport,
  WebSerialTransport,
  WebBluetoothTransport,
} from '@thermal-label/transport/web';

// Platform-neutral discovery helpers
import { matchDevice, buildUsbFilters, discoverAll } from '@thermal-label/transport';

The root entry point (@thermal-label/transport) is safe to import from either environment — it contains only discovery helpers.

Usage

UsbTransport (Node)

import { UsbTransport } from '@thermal-label/transport/node';

const transport = await UsbTransport.open(0x04f9, 0x209d); // Brother QL-820NWB
await transport.write(new Uint8Array([0x1b, 0x40])); // ESC @ (reset)
const status = await transport.read(32, 2000); // up to 32 bytes, 2s timeout
await transport.close();

UsbTransport opens the device, claims interface 0, detaches the usblp kernel driver on Linux if present, and locates Bulk IN / OUT endpoints. read() maps libusb timeouts to TransportTimeoutError.

TcpTransport (Node)

import { TcpTransport } from '@thermal-label/transport/node';

const transport = await TcpTransport.connect('192.0.2.42'); // default port 9100
await transport.write(payload);
await transport.close();

read(n) accumulates bytes across data events and only resolves once the buffer holds n bytes. Remainder bytes stay buffered for the next call. Connection timeouts reject with TransportError; read timeouts reject with TransportTimeoutError.

SerialTransport (Node)

import { SerialTransport } from '@thermal-label/transport/node';

// Bluetooth SPP printer (Linux, after `bluetoothctl` pair + `rfcomm bind`)
const transport = await SerialTransport.open('/dev/rfcomm0');
await transport.write(payload);
const status = await transport.read(32, 2000);
await transport.close();

// USB-to-serial adapter — baud rate matters, default is 9600
const wired = await SerialTransport.open('/dev/ttyUSB0', 115200);

The same class covers Bluetooth SPP (/dev/rfcomm*, COM<n> after Windows pairing), USB-to-serial adapters, and native UARTs. The baudRate argument is forwarded to the OS driver; it is ignored for RFCOMM links (flow control happens on the Bluetooth layer) but still required by the serialport API, hence the 9600 default.

macOS: classic Bluetooth SPP was removed from macOS years ago. Printers like the Brother QL-820NWB are reachable over serial on Linux and Windows only — macOS users should connect via USB or TCP instead.

WebUsbTransport (Browser)

import { WebUsbTransport } from '@thermal-label/transport/web';
import { buildUsbFilters } from '@thermal-label/transport';
import { devices } from '@thermal-label/labelwriter'; // hypothetical driver registry

const filters = buildUsbFilters(devices);
const transport = await WebUsbTransport.request(filters);

WebUsbTransport.request shows the browser's USB picker. WebUsbTransport.fromDevice(device) wraps a USBDevice obtained from navigator.usb.getDevices() (previously paired devices).

WebSerialTransport (Browser)

import { WebSerialTransport } from '@thermal-label/transport/web';

// Paired Bluetooth SPP printer appears alongside wired serial ports
const transport = await WebSerialTransport.request();
await transport.write(payload);
await transport.close();

// Wrap a previously-authorized port returned by navigator.serial.getPorts()
const [port] = await navigator.serial.getPorts();
const wrapped = await WebSerialTransport.fromPort(port, 115200);

WebSerialTransport.fromPort expects a port that is not yet open — it calls port.open({ baudRate }) internally. The browser picker lists USB-serial adapters and any Bluetooth SPP devices already paired at the OS level; Bluetooth pairing is not part of the Web Serial API and must happen in OS settings first.

Chrome/Edge only on desktop (and Chrome Android). Firefox and Safari do not implement Web Serial. Same macOS caveat as SerialTransport: no classic Bluetooth SPP, so macOS users connect via USB or TCP instead.

WebBluetoothTransport (Browser)

import { WebBluetoothTransport } from '@thermal-label/transport/web';

const transport = await WebBluetoothTransport.request({
  serviceUuid: '0000ff00-0000-1000-8000-00805f9b34fb',
  txCharacteristicUuid: '0000ff02-0000-1000-8000-00805f9b34fb',
  rxCharacteristicUuid: '0000ff01-0000-1000-8000-00805f9b34fb',
  namePrefix: 'QL-820',
  mtu: 512,
});

write() chunks data to mtu bytes (default 20) and sends sequentially via writeValueWithoutResponse. read(n) buffers incoming characteristicvaluechanged notifications and resolves once n bytes are available.

Discovery

import { discoverAll } from '@thermal-label/transport';
import { labelwriterDiscovery } from '@thermal-label/labelwriter';
import { brotherQlDiscovery } from '@thermal-label/brother-ql';

const printers = await discoverAll([labelwriterDiscovery, brotherQlDiscovery]);

discoverAll uses Promise.allSettled, so a failing driver does not block discovery from the others.

Contributing a new printer

For a full guide covering protocol reverse engineering, implementing PrinterAdapter and PrinterDiscovery, BLE sniffing, and publishing a driver package, see the thermal-label contributing guide.

Reference drivers

These packages implement PrinterAdapter / PrinterDiscovery against the transports in this package:

| Package | Printer family | | ----------------------------------- | ------------------------------------ | | @thermal-label/labelmanager | DYMO LabelManager (thermal transfer) | | @thermal-label/labelwriter | DYMO LabelWriter (direct thermal) | | @thermal-label/brother-ql | Brother QL series (direct thermal) |

Attribution

This project is not affiliated with DYMO, Brother, Niimbot, Phomemo, or any other printer manufacturer. Trademarks belong to their respective owners.

License

MIT — see LICENSE.