pamoja for TypeScript - v0.1.17
    Preparing search index...

    Module @pamoja/serial

    Ergonomic facade over the generated serial-framing binding.

    A serial line is a stream of bytes with no packet boundaries, so something has to mark where one message ends and the next begins. SLIP and COBS are the two ways to do that, and each is offered both as a one-shot call over a complete frame and as a streaming decoder for the arbitrary chunks a port delivers.

    The streaming decoders are what a real read loop uses. A corrupt frame does not throw, because the frames around it are still good; it is dropped and counted on SlipDecoder.discarded.

    @pamoja/serial

    SLIP and COBS byte stuffing with streaming decoders, so a UART byte stream carries discrete packets. One capability of pamoja, one memory-safe Rust core with bindings for TypeScript, Python, and C#.

    API reference read the guide documentation

    npm install @pamoja/serial
    

    This pulls in @pamoja/native, the compiled engine. npm install pamoja is the whole framework in one package.

    The test that runs in CI, spliced here as it ran.

    From bindings/node/guides/serial.ts:

    import {
    COBS_DELIMITER_BYTE,
    SLIP_END_BYTE,
    SLIP_ESC_BYTE,
    SlipDecoder,
    cobs,
    slip,
    } from '@pamoja/serial'

    // A UART carries bytes, not packets, so a framing has to mark where one packet ends.
    // SLIP reserves two byte values for that, and the package names both: the end byte closes
    // a frame, the escape byte carries a value that would otherwise look like one.
    const payload = Buffer.concat([Buffer.from('lvl='), Buffer.from([SLIP_END_BYTE, SLIP_ESC_BYTE])])
    const framed = slip.encode(payload)
    console.log(`slip ${payload.length} payload bytes framed as ${framed.length}`)

    // Decoding gives the payload back unchanged, reserved bytes and all.
    const restored = slip.decode(framed)
    console.log(`slip decoded back to ${restored.length} bytes`)

    // COBS trades that escaping for one code byte per run of up to 254 non-zero bytes, each
    // run led by its own length, so a frame never grows by more than a byte per 254. Zero is
    // the delimiter, and never appears inside a frame.
    const packet = Buffer.concat([Buffer.from('lvl='), Buffer.from([COBS_DELIMITER_BYTE]), Buffer.from('7')])
    const cobsFramed = cobs.encode(packet)
    console.log(`cobs ${packet.length} payload bytes framed as ${cobsFramed.length}`)

    // A read from a port returns whatever arrived, which is rarely one whole frame. This
    // chunk holds two good frames with a truncated one between them; the decoder hands over
    // the good ones and discards only the bad frame.
    const decoder = new SlipDecoder()
    const chunk = Buffer.concat([
    Buffer.from('ok'),
    Buffer.from([SLIP_END_BYTE]),
    Buffer.from([SLIP_ESC_BYTE]), // a frame that ends before its escape pair completes
    Buffer.from([SLIP_END_BYTE]),
    Buffer.from('go'),
    Buffer.from([SLIP_END_BYTE]),
    ])
    const frames = decoder.feed(chunk)
    for (const frame of frames) {
    console.log(`received ${frame.toString()}`)
    }
    console.log(`discarded ${decoder.discarded} frame the stream mangled`)
    Language Package Reference
    Rust pamoja-serial reference, docs.rs, install
    TypeScript @pamoja/serial reference, install
    Python pamoja-serial reference, install
    C# Pamoja.Serial reference, install

    MIT

    CobsDecoder
    SlipDecoder
    Framing
    cobs
    COBS_DELIMITER_BYTE
    slip
    SLIP_END_BYTE
    SLIP_ESC_BYTE
    SLIP_ESC_END_BYTE
    SLIP_ESC_ESC_BYTE