webserial-core
v2.2.0
Published
A strongly-typed, event-driven, abstract TypeScript library for the Web Serial API with custom parsers, command queue, handshake validation, and auto-reconnect.
Maintainers
Readme
webserial-core
A strongly-typed, event-driven, abstract TypeScript library for serial communication on the web. Supports Web Serial, WebUSB, Web Bluetooth, and WebSocket transports through a unified API.
⚠️ Breaking Changes — v2
Version 2 is a complete rewrite. Tthe public API is completally changed, please if you has an implementation of this library is better read docs as like a new integration See the Migration Guide before upgrading.
Features
- Provider-agnostic — swap between Web Serial, WebUSB, Web Bluetooth, or WebSocket transport by injecting a single provider.
- Strictly typed — full TypeScript generics, no implicit
any, compatible withstrict: true. - Typed events —
connecting,connected,disconnected,data,sent,error,timeout,reconnecting, and more — all fully typed. - Built-in parsers —
delimiter,fixedLength,raw. ImplementSerialParser<T>for any custom binary or text protocol. - Command queue — FIFO write queue with optional per-command timeouts.
- Auto-reconnect — configurable reconnect loop with back-off.
- WebUSB polyfill — full WebUSB serial polyfill (CDC ACM, CP210x, CH340).
- BLE NUS adapter — Nordic UART Service over Web Bluetooth GATT.
- WebSocket bridge — relay serial I/O through a Node.js bridge server.
Installation
npm install webserial-coreQuick start
import { AbstractSerialDevice, delimiter } from "webserial-core";
class MyDevice extends AbstractSerialDevice<string> {
constructor() {
super({
baudRate: 9600,
parser: delimiter("\n"),
autoReconnect: true,
});
}
protected async handshake(): Promise<boolean> {
return true; // return false to reject the port
}
}
const device = new MyDevice();
device.on("serial:connected", () => console.log("Connected!"));
device.on("serial:data", (line) => console.log("←", line));
device.on("serial:disconnected", () => console.log("Disconnected."));
device.on("serial:error", (err) => console.error(err.message));
await device.connect(); // opens the browser port picker
await device.send("PING\n"); // enqueues a write
await device.disconnect();Transport adapters
All four adapters expose the same SerialProvider interface. Inject your
chosen adapter once before constructing any device:
import { AbstractSerialDevice, WebUsbProvider } from "webserial-core";
import { createBluetoothProvider } from "webserial-core";
import { createWebSocketProvider } from "webserial-core";
// WebUSB polyfill (Android Chrome, or desktop for testing)
AbstractSerialDevice.setProvider(new WebUsbProvider());
// Web Bluetooth (Nordic UART Service over BLE GATT)
AbstractSerialDevice.setProvider(createBluetoothProvider());
// WebSocket bridge (requires Node.js server — see demos/websocket/)
AbstractSerialDevice.setProvider(
createWebSocketProvider("ws://127.0.0.1:8080/?token=BRIDGE_TOKEN"),
);Parsers
import { delimiter, fixedLength, raw } from "webserial-core";
// Newline-delimited strings (Arduino Serial.println)
parser: delimiter("\n");
// 16-byte binary packets
parser: fixedLength(16);
// Raw Uint8Array chunks
parser: raw();Events
| Event | Payload | Description |
| ------------------------ | ------------ | --------------------------------------------- |
| serial:connecting | — | connect() called, port picker about to open |
| serial:connected | — | Port open, handshake passed |
| serial:disconnected | — | Port closed |
| serial:data | T | Parser emitted a complete message |
| serial:sent | Uint8Array | Bytes written to the port |
| serial:error | Error | Unrecoverable error |
| serial:need-permission | — | User denied access |
| serial:timeout | Uint8Array | Command timed out |
| serial:queue-empty | — | Write queue is now idle |
| serial:reconnecting | — | Auto-reconnect attempt starting |
Project structure
src/
core/ AbstractSerialDevice, SerialEventEmitter, SerialRegistry
adapters/
web-usb/ WebUsbProvider (WebUSB polyfill)
web-bluetooth/ createBluetoothProvider (BLE NUS)
websocket/ createWebSocketProvider (Node.js bridge)
parsers/ delimiter, fixedLength, raw
queue/ CommandQueue
errors/ SerialPortConflictError, SerialPermissionError, …
types/ SerialDeviceOptions, SerialProvider, SerialParser, …
demos/
web-serial/ Native Web Serial demo
web-usb/ WebUSB polyfill demo
web-bluetooth/ Web Bluetooth BLE demo
websocket/ WebSocket bridge demo + Node.js server
docs/ VitePress documentation siteBuilding
npm run buildOutput in dist/:
| File | Format | Use case |
| ------------------------ | ---------- | ------------------------- |
| webserial-core.mjs | ESM | Bundlers, modern browsers |
| webserial-core.cjs | CJS | Node.js, legacy bundlers |
| webserial-core.umd.cjs | UMD | <script> tag, CDN |
| index.d.ts | TypeScript | Type declarations |
Documentation
npm run docs:dev # live VitePress server
npm run docs:build # static build → docs/.vitepress/distBrowser compatibility
| Feature | Browser requirement | | ------------- | -------------------- | | Web Serial | Chrome 89+, Edge 89+ | | WebUSB | Chrome 61+, Edge 79+ | | Web Bluetooth | Chrome 56+, Edge 79+ | | WebSocket | All modern browsers |
All browser APIs require a secure context (HTTPS or localhost).
License
MIT © danidoble
