Skip to content

Parsers

Parsers transform a stream of raw Uint8Array chunks into structured values of type T. Pass a parser to SerialDeviceOptions.parser.

Built-in parsers

delimiter(separator, options?)

Accumulates bytes until the delimiter sequence is found, then emits the frame with the delimiter stripped (unless includeDelimiter is true).

  • When separator is a string, bytes are decoded as UTF-8 and a string is emitted.
  • When separator is a Uint8Array or number[], raw bytes are emitted as Uint8Array.
ts
import { delimiter } from "webserial-core";

// Split on newline — good for Arduino Serial.println()
parser: delimiter("\n");

// Split on carriage-return + newline
parser: delimiter("\r\n");

// Include the delimiter in the emitted value
parser: delimiter("\n", { includeDelimiter: true });

// Split on a binary sequence — emits Uint8Array
parser: delimiter(new Uint8Array([0x0d, 0x0a]));

// Split using a number array — emits Uint8Array
parser: delimiter([0xaa, 0xff]);
ParameterTypeDescription
separatorstring | Uint8Array | number[]Byte sequence that marks the end of a frame
options.includeDelimiterbooleanAppend the delimiter to each emitted value. Default: false

Emits: string (when separator is string) · Uint8Array (when separator is binary)


readline(options?)

Splits the byte stream on a configurable line delimiter and emits each line as a decoded string. Equivalent to @serialport/parser-readline.

ts
import { readline } from "webserial-core";

// Default — split on '\n', UTF-8
parser: readline();

// Custom delimiter and encoding
parser: readline({ delimiter: "\r\n", encoding: "ascii" });

// Include the delimiter
parser: readline({ delimiter: "\n", includeDelimiter: true });
OptionTypeDefaultDescription
delimiterstring | Uint8Array | number[]'\n'Line terminator sequence
includeDelimiterbooleanfalseAppend the delimiter to each emitted string
encodingstring'utf-8'Text encoding used to decode bytes

Emits: string


fixedLength(length)

Emits every N bytes as a raw Uint8Array. Useful for binary protocols with fixed-size packets.

ts
import { fixedLength } from "webserial-core";

// Emit every 16 bytes
parser: fixedLength(16);
ParameterTypeDescription
lengthnumberNumber of bytes per frame

Emits: Uint8Array


raw()

Passes each chunk from the port unchanged. Use this when you want to handle framing yourself.

ts
import { raw } from "webserial-core";

parser: raw();

Emits: Uint8Array


interByteTimeout(options)

Buffers incoming bytes and emits them after a period of silence (interval ms with no new bytes), or when the buffer reaches maxBufferSize. Equivalent to @serialport/parser-inter-byte-timeout.

ts
import { interByteTimeout } from "webserial-core";

// Emit buffered data after 30 ms of silence
parser: interByteTimeout({ interval: 30 });

// With a max buffer cap
parser: interByteTimeout({ interval: 30, maxBufferSize: 1024 });
OptionTypeDefaultDescription
intervalnumberSilence period in milliseconds before emitting (required)
maxBufferSizenumber65536Maximum bytes to buffer before forcing an emit

Emits: Uint8Array


packetLength(options?)

Frames packets using a leading delimiter byte and an embedded length field. Equivalent to @serialport/parser-packet-length.

Default packet layout: [0xAA][0x00][len][payload...][footer]

ts
import { packetLength } from "webserial-core";

// Default options
parser: packetLength();

// Custom packet structure: [0xBC][0x00][lenHi][lenLo][cargo...][footer0][footer1]
parser: packetLength({ delimiter: 0xbc, packetOverhead: 5, lengthBytes: 2, lengthOffset: 2 });
OptionTypeDefaultDescription
delimiternumber0xAAByte value that marks the start of each packet
packetOverheadnumber2Total fixed overhead bytes (delimiter + length field(s) + any footer)
lengthBytesnumber1Number of bytes that encode the payload length
lengthOffsetnumber1Byte offset from the delimiter to the first length byte
maxLennumber0xFFMaximum allowed payload length; packets exceeding this are discarded

Emits: Uint8Array


regexParser(options)

Decodes incoming bytes and splits the resulting string on a regular expression, emitting each segment. Equivalent to @serialport/parser-regex.

ts
import { regexParser } from "webserial-core";

// Split on any combination of \r and \n
parser: regexParser({ regex: /[\r\n]+/ });

// Split on semicolons, case-insensitive
parser: regexParser({ regex: /;+/ });
OptionTypeDefaultDescription
regexRegExp | stringPattern used to split the incoming text
encodingstring'utf-8'Text encoding used to decode bytes

Emits: string


readyParser(options)

Discards all incoming bytes until a configurable "ready" sequence is detected. After that, every subsequent byte chunk is emitted as-is. Fires an optional onReady callback when the sequence is found. Equivalent to @serialport/parser-ready.

ts
import { readyParser } from "webserial-core";

parser: readyParser({
  delimiter: "READY\n",
  onReady: () => console.log("device is ready"),
});
OptionTypeDescription
delimiterstring | Uint8Array | number[]Byte sequence that signals the device is ready (required)
onReady() => voidCalled once when the ready sequence is detected

Emits: Uint8Array (all data received after the ready sequence)


ccTalk(maxDelayBetweenBytesMs?)

Parses ccTalk bus packets. Each packet is N + 5 bytes where N is the data length in byte 1. The internal buffer is discarded if no byte arrives within maxDelayBetweenBytesMs. Equivalent to @serialport/parser-cctalk.

ts
import { ccTalk } from "webserial-core";

// Default 50 ms inter-byte timeout
parser: ccTalk();

// Custom timeout
parser: ccTalk(100);
ParameterTypeDefaultDescription
maxDelayBetweenBytesMsnumber50Maximum silence between bytes before the buffer is reset

Emits: Uint8Array


slipDecoder(options?)

Decodes a SLIP-framed byte stream (RFC 1055) and emits each packet. Optional custom escape / end bytes are supported. Equivalent to SlipDecoder from @serialport/parser-slip-encoder.

ts
import { slipDecoder } from "webserial-core";

// Standard SLIP
parser: slipDecoder();

// Custom framing bytes
parser: slipDecoder({ END: 0xc0, ESC: 0xdb, ESC_END: 0xdc, ESC_ESC: 0xdd });

// Bluetooth quirk — expects a START byte before each packet
parser: slipDecoder({ START: 0xc0 });
OptionTypeDefaultDescription
ENDnumber0xC0Packet end byte
ESCnumber0xDBEscape byte
ESC_ENDnumber0xDCESC sequence for the END byte
ESC_ESCnumber0xDDESC sequence for the ESC byte
STARTnumberOptional packet start byte
ESC_STARTnumberESCESC sequence for the START byte

Emits: Uint8Array

slipEncode(data, options?)

Utility function (not a parser) that SLIP-encodes an outgoing packet before writing it to the serial port.

ts
import { slipEncode } from "webserial-core";

const frame = slipEncode(new Uint8Array([0x01, 0xc0, 0x02]));
await device.send(frame);

// Bluetooth quirk — prepends an extra END byte
const frame = slipEncode(payload, { bluetoothQuirk: true });
OptionTypeDefaultDescription
bluetoothQuirkbooleanfalsePrepend an END byte at the beginning of each packet
(+ all SLIP bytes from above)

Returns: Uint8Array


spacePacket(options?)

Parses CCSDS Space Packet Protocol frames and emits a structured SpacePacket object for each complete packet. Equivalent to @serialport/parser-spacepacket.

ts
import { spacePacket } from "webserial-core";

// No secondary header
parser: spacePacket();

// With a time code field in the secondary header
parser: spacePacket({ timeCodeFieldLength: 8 });

// With both time code and ancillary data fields
parser: spacePacket({ timeCodeFieldLength: 4, ancillaryDataFieldLength: 2 });
OptionTypeDefaultDescription
timeCodeFieldLengthnumber0Length in bytes of the Time Code Field
ancillaryDataFieldLengthnumber0Length in bytes of the Ancillary Data Field

Emits: SpacePacket

ts
interface SpacePacket {
  header: {
    versionNumber: number;
    identification: { apid: number; secondaryHeader: number; type: number };
    sequenceControl: { packetName: number; sequenceFlags: number };
    dataLength: number;
  };
  secondaryHeader?: {
    timeCode?: string;
    ancillaryData?: string;
  };
  data: string;
}

Custom parsers

Implement SerialParser<T> to handle any custom protocol:

ts
import type { SerialParser } from "webserial-core";

interface MyPacket {
  command: number;
  payload: Uint8Array;
}

function myProtocolParser(): SerialParser<MyPacket> {
  const buffer: number[] = [];

  return {
    parse(chunk, emit) {
      for (const byte of chunk) {
        buffer.push(byte);

        // Simple framing: packets start with 0xAA and are 5 bytes long
        if (buffer[0] !== 0xaa) {
          buffer.shift();
          continue;
        }
        if (buffer.length < 5) continue;

        emit({
          command: buffer[1],
          payload: new Uint8Array(buffer.splice(0, 5).slice(2)),
        });
      }
    },

    reset() {
      buffer.length = 0;
    },
  };
}

Then use it in your device:

ts
super({
  baudRate: 115200,
  parser: myProtocolParser(),
});

The T type parameter on AbstractSerialDevice<T> and SerialEventMap<T> will automatically match the type your parser emits.

Parser interface

ts
interface SerialParser<T> {
  /** Called for each raw chunk received from the port. */
  parse(chunk: Uint8Array, emit: (value: T) => void): void;

  /** Optional — resets internal buffer state (e.g. on reconnect). */
  reset?(): void;
}

Released under the MIT License.