@thermal-label/transport
USB, TCP, Serial, WebUSB, Web Serial, and Web Bluetooth transport classes for the thermal-label ecosystem. Every class implements Transport from @thermal-label/contracts, so drivers program against one pull-based API regardless of the underlying channel.
Install
pnpm add @thermal-label/transportNative peers are optional — install only what your code path needs:
pnpm add usb # for UsbTransport (Node)
pnpm add serialport # for SerialTransport (Node)Browser-only consumers download neither.
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. |
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
import { UsbTransport, TcpTransport, SerialTransport } from '@thermal-label/transport/node';
// Browser
import {
WebUsbTransport,
WebSerialTransport,
WebBluetoothTransport,
} from '@thermal-label/transport/web';
// Platform-neutral helpers
import { matchDevice, buildUsbFilters, discoverAll } from '@thermal-label/transport';The root entry (@thermal-label/transport) is safe to import from either environment — it contains only discovery helpers.
Examples
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();Opens the device, claims interface 0, detaches the usblp kernel driver on Linux when 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.
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);
// USB-to-serial — baud rate matters
const wired = await SerialTransport.open('/dev/ttyUSB0', 115200);macOS: classic Bluetooth SPP was removed years ago. macOS users 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-web';
const filters = buildUsbFilters(DEVICES);
const transport = await WebUsbTransport.request(filters);WebUsbTransport.fromDevice(device) wraps a USBDevice from navigator.usb.getDevices() for previously-paired devices.
WebSerialTransport (Browser)
import { WebSerialTransport } from '@thermal-label/transport/web';
const transport = await WebSerialTransport.request();
await transport.write(payload);Chrome/Edge desktop and Chrome Android only. Firefox and Safari do not implement Web Serial. The browser picker lists USB-serial adapters and any Bluetooth SPP devices already paired at the OS level.
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 aggregation
import { discoverAll } from '@thermal-label/transport';
import { discovery as labelwriter } from '@thermal-label/labelwriter-node';
import { discovery as brotherQl } from '@thermal-label/brother-ql-node';
const printers = await discoverAll([labelwriter, brotherQl]);discoverAll uses Promise.allSettled, so a failing driver does not block discovery from the others.
Compatibility
| Node | ≥ 24 (for UsbTransport, TcpTransport, SerialTransport) |
| Browsers | Chrome/Edge 89+ (WebUSB, WebSerial); Web Bluetooth on Chrome/Edge desktop and Chrome Android |
| Optional peers | usb (Node USB), serialport (Node Serial) |
| License | MIT |