web-serial-rxjs API Documentation
    Preparing search index...

    API concepts and design notes

    For exhaustive public API specifications, see the English TypeDoc API Reference. This page is a Guide supplement (tables and design notes)—not a TypeDoc substitute.

    The public surface consists of a single factory (createSerialSession), the runtime SerialSession interface, one options type, one state union, and two error types.

    import {
    createSerialSession,
    isWebSerialSupported,
    createTerminalBuffer,
    DEFAULT_TERMINAL_BUFFER_OPTIONS,
    SerialError,
    SerialErrorCode,
    SerialSessionStatus,
    type SerialSession,
    type SerialSessionState,
    type SerialSessionOptions,
    type SerialSessionFeatureOptions,
    type SerialConnectionOptions,
    type TerminalBufferOptions,
    } from '@gurezo/web-serial-rxjs';

    The following remain available from the public export in v3.x but are not part of the canonical API. They are scheduled for removal in the next major version. See Migrating to v3 – §9 assertNever public export audit.

    Export Status Migration
    assertNever @deprecated in v3.x Define a local helper in application code, or use switch (state.status) with SerialSessionStatus
    // Deprecated (still works in v3.x but triggers warnings)
    import { assertNever } from '@gurezo/web-serial-rxjs';

    // Recommended: local helper
    function assertNever(value: never): never {
    throw new Error(`Unexpected value: ${String(value)}`);
    }

    Factory that returns a new SerialSession. Safe to call when navigator.serial is unavailable; in that case state$ is seeded with { status: 'unsupported' } and connect$ rejects with SerialErrorCode.BROWSER_NOT_SUPPORTED.

    function createSerialSession(options?: SerialSessionOptions): SerialSession;
    

    SerialSessionOptions composes W3C connection parameters (SerialConnectionOptions) with library-specific session features (SerialSessionFeatureOptions). It is the factory argument for createSerialSession.

    SerialSessionOptions = Partial<SerialConnectionOptions> & SerialSessionFeatureOptions
    

    Minimal callers typically only set baudRate; other fields keep safe defaults.

    const session = createSerialSession({ baudRate: 115200 });
    

    See Migrating to v3 – §10 Session options type responsibility audit for the audit rationale. See also Issue #488 for the Phase 2 options responsibility cleanup.

    Derived from W3C SerialOptions and passed to port.open. All fields are optional at factory time; omitted values fall back to the defaults below.

    Field Type Default Description
    baudRate number 9600 Bits per second. Must be a safe integer > 0.
    dataBits 7 | 8 8 Data bits per frame.
    stopBits 1 | 2 1 Stop bits per frame.
    parity 'none' | 'even' | 'odd' 'none' Parity checking mode.
    bufferSize number 255 Read-stream buffer size in bytes. Must be a safe integer > 0.
    flowControl 'none' | 'hardware' 'none' Flow control mode.

    Library-specific session features. Not passed to W3C port.open.

    Field Type Default Description
    filters SerialPortFilter[] | undefined Forwarded to navigator.serial.requestPort when selecting a port.
    terminalBuffer TerminalBufferOptions { maxLines: 10000, maxChars: 1048576, stripAnsi: true } Memory limits and ANSI stripping for terminalText$; see createTerminalBuffer.
    lineBuffer LineBufferOptions { maxChars: 1048576 } Memory limit for the incomplete line tail used by lines$; see below.

    At createSerialSession time (factory), resolveSerialSessionOptions validates the following. Invalid values throw SerialError:

    Target Validation Error code
    baudRate safe integer and > 0 INVALID_CONNECTION_OPTIONS
    bufferSize safe integer and > 0 INVALID_CONNECTION_OPTIONS
    filters USB vendor/product ID ranges INVALID_FILTER_OPTIONS
    terminalBuffer maxLines / maxChars are safe integers and >= 0 INVALID_TERMINAL_BUFFER_OPTIONS
    lineBuffer maxChars is a safe integer and >= 0 INVALID_LINE_BUFFER_OPTIONS
    Value Connection (baudRate / bufferSize) Buffer limits (terminalBuffer / lineBuffer)
    undefined Apply default Apply nested default
    0 Rejected Unlimited (disable that limit)
    negative / non-integer / NaN / Infinity Rejected Rejected

    Do not mix meanings: 0 is never “unlimited” for connection fields.

    Used by createTerminalBuffer and SerialSessionOptions.terminalBuffer. When a limit is exceeded, the oldest completed lines or leading characters are dropped so long-running terminal views do not grow without bound. Pass 0 for either field to disable that constraint. Character counts use UTF-16 string length (JavaScript .length).

    Field Type Default Description
    maxLines number 10000 Max number of completed lines retained in the cumulative display text.
    maxChars number 1048576 Max total characters in the display text (completed + current line).
    stripAnsi boolean true When true, removes ANSI escape sequences before folding \r redraws. Set false to preserve raw escape codes in terminalText$. receive$ is always unchanged.

    Invalid maxLines or maxChars values cause createSerialSession and standalone createTerminalBuffer to throw SerialError with INVALID_TERMINAL_BUFFER_OPTIONS.

    Used by SerialSessionOptions.lineBuffer for the incomplete line tail held while framing lines$. When maxChars is exceeded, leading characters of the tail are discarded and a non-fatal SerialError with SerialErrorCode.LINE_BUFFER_OVERFLOW is emitted on errors$. Completed lines are emitted in full before the tail is trimmed. Pass 0 to disable the limit. Character counts use UTF-16 string length.

    Field Type Default Description
    maxChars number 1048576 Max characters retained in the incomplete line tail (no line terminator yet).

    Invalid maxChars values cause createSerialSession to throw SerialError with INVALID_LINE_BUFFER_OPTIONS.

    Builds a terminal-oriented cumulative text stream from any Observable<string> of decoded chunks (typically SerialSession.receive$). Folds \r redraws while preserving normal newline behavior. Defaults match DEFAULT_TERMINAL_BUFFER_OPTIONS. Invalid maxLines / maxChars throw the same INVALID_TERMINAL_BUFFER_OPTIONS error as session factory validation.

    function createTerminalBuffer(
    receive$: Observable<string>,
    options?: TerminalBufferOptions,
    ): TerminalBuffer;

    v3 exposes SerialSessionStatus as lifecycle string constants (e.g. SerialSessionStatus.Connected is 'connected') and SerialSessionState as the discriminated union type emitted by state$.

    state$ emits objects such as:

    • { status: 'idle' } — no active port; initial state when Web Serial is supported.
    • { status: 'connecting' }connect$ is in flight.
    • { status: 'connected', portInfo } — port is open and the read pump is running.
    • { status: 'disconnecting' }disconnect$ is in flight.
    • { status: 'unsupported' }navigator.serial was not available at session creation time.
    • { status: 'error', error } — fatal failure; error is the same SerialError instance on errors$.
    • { status: 'disposed' } — session permanently torn down via dispose$.

    Example:

    import { filter } from 'rxjs';
    import { isConnectedSessionState, SerialSessionStatus } from '@gurezo/web-serial-rxjs';

    session.state$.subscribe((state) => {
    if (state.status === SerialSessionStatus.Connected) {
    console.log(state.portInfo);
    }
    });

    // RxJS pipelines: use the type predicate so TypeScript keeps ConnectedSessionState
    session.state$
    .pipe(filter(isConnectedSessionState))
    .subscribe((state) => {
    console.log(state.portInfo);
    });

    Type predicate for ConnectedSessionState. Use with RxJS filter() to preserve discriminated union narrowing in pipelines. Inline filter((s) => s.status === SerialSessionStatus.Connected) does not narrow types in TypeScript.

    import { filter } from 'rxjs';
    import { isConnectedSessionState } from '@gurezo/web-serial-rxjs';

    session.state$
    .pipe(filter(isConnectedSessionState))
    .subscribe((state) => {
    console.log(state.portInfo);
    });

    See Migrating to v3 for the v2 string migration.

    interface SerialSession {
    connect$(): Observable<void>;
    disconnect$(): Observable<void>;
    dispose$(): Observable<void>;

    readonly state$: Observable<SerialSessionState>;
    readonly errors$: Observable<SerialError>;
    readonly receive$: Observable<string>;
    readonly terminalText$: Observable<string>;
    readonly lines$: Observable<string>;

    send$(data: string | Uint8Array): Observable<void>;
    }

    Synchronous feature check. Returns true when navigator.serial is available. Prefer this before creating a session. After the session exists, drive unsupported UI from state$ with SerialSessionStatus.Unsupported. See Migrating to v4 – browser support.

    Opens a user-selected serial port and starts the internal read pump. Completes on success; errors via errors$ and the subscriber on failure. Transitions idle → connecting → connected. Runs when subscribed.

    Stops the read pump and closes the port. Safe to call when already idle or while a disconnect is already in progress. When called during 'connecting', cancels the in-flight connect$() (closes any opened port) and returns to 'idle' without reaching 'connected'. Transitions connected → disconnecting → idle. When called from 'error' it still tears the port down and returns to idle. The session remains reusable after disconnect$; use dispose$ for permanent teardown. Runs when subscribed.

    Permanently tears down the session. Closes any active connection (same port/pump cleanup as disconnect$), emits 'disposed' on state$, and completes every session observable (state$, errors$, receive$, lines$, terminalText$). Safe to call multiple times; subsequent calls complete immediately. Runs when subscribed.

    After disposal, connect$ and send$ fail with SerialErrorCode.SESSION_DISPOSED. disconnect$ completes immediately. Create a new SerialSession instead of reusing a disposed instance (for example when replacing a session after a baud-rate change).

    Replays the current state on subscribe. Prefer driving your UI from this stream instead of rebuilding a BehaviorSubject. When state.status is SerialSessionStatus.Connected, read state.portInfo for device identification. There is no separate portInfo$ / getPortInfo() / isConnected$ / destroy$() / getCurrentPort() / receiveReplay$ / isBrowserSupported() on the public API — see Migrating to v4.

    Primary error channel. Every connect / read / write / close failure is normalised to SerialError and pushed here. Fatal failures additionally drive state$ to { status: 'error', error } and tear down the live pump and port.

    UTF-8 decoded text pushed by the internal read pump as decoder chunks (not line-oriented). Not subscription-lazy — the pump is started by connect$ and chunks are multicast. Late subscribers see only new data. Carriage returns and other control characters are preserved. Use receive$ for terminal-like mirrors and any output that depends on \r (for example interactive shells or progress lines). Use lines$ for newline-framed logs and line-by-line parsing.

    Terminal-display oriented cumulative text derived from receive$. Collapses \r redraws while keeping normal newline behavior. By default strips ANSI escape sequences for plain-text views (for example <textarea>). Raw escape codes remain available on receive$. Equivalent to createTerminalBuffer(receive$, options.terminalBuffer).text$. By default retains at most 10,000 completed lines and 1,048,576 characters; configure via SerialSessionOptions.terminalBuffer or pass { maxLines: 0, maxChars: 0 } for unlimited growth.

    The same UTF-8 stream split into complete lines using \n, \r\n, and a lone interior \r (see library implementation). Trailing data without a line ending is buffered; incomplete tails are not emitted. By default the incomplete tail is capped at 1,048,576 characters via SerialSessionOptions.lineBuffer; overflow discards leading tail data and emits LINE_BUFFER_OVERFLOW on errors$ without disconnecting. Not subscription-lazy with respect to the read pump, like receive$. Choose lines$ for logs and parsers; for raw terminal display where \r redraw semantics matter, subscribe to receive$ instead.

    Enqueues a payload for ordered transmission. Strings are UTF-8 encoded through a shared TextEncoder. Concurrent send$ calls are serialised in call order by an internal FIFO queue. Write failures are normalised to SerialError with SerialErrorCode.WRITE_FAILED, multiplexed on errors$, and surfaced to the subscriber. Calling send$ while not 'connected' fails fast with SerialErrorCode.PORT_NOT_OPEN. Runs when subscribed.

    SerialError extends Error with a code: SerialErrorCode and structured per-code metadata on context. is(code) narrows both code and context to the literal types for that code.

    For cause-bearing error codes, context.cause (unknown) is the canonical source for the underlying failure. originalError remains in v3.x for backward compatibility but is deprecated and scheduled for removal in the next major version. See Migrating to v3 – originalError deprecation.

    session.errors$.subscribe((error) => {
    if (error.is(SerialErrorCode.READ_FAILED)) {
    console.error(error.context.cause);
    }
    });

    try {
    createSerialSession({ baudRate: 0 });
    } catch (error) {
    if (error instanceof SerialError && error.is(SerialErrorCode.INVALID_CONNECTION_OPTIONS)) {
    console.error(error.context.field, error.context.value, error.context.constraint);
    }
    }

    The same union is available as a const object SerialErrorCode (e.g. SerialErrorCode.READ_FAILED is 'READ_FAILED') for IDE completion and to avoid string typos. String literals stay valid for types and runtime comparisons. See Migrating to v3 for the enum-to-const declaration change.

    Runtime emission coverage for the implemented codes is audited in Migrating to v3 §8. Receive-replay codes were removed in Migrating to v4 – Phase 2.

    Code context shape When it is emitted
    LINE_BUFFER_OVERFLOW { maxChars: number } lines$ incomplete tail exceeded lineBuffer.maxChars; leading data discarded (non-fatal).
    INVALID_* validation codes ValidationErrorContext Factory-time options validation; see below. Narrow with error.is(code).
    Cause-bearing codes (e.g. PORT_OPEN_FAILED) { cause: unknown } See table below. Narrow with error.is(code) before reading context.cause.
    Other codes undefined See table below.

    ValidationErrorContext is { field: string; value: unknown; constraint: ValidationErrorConstraint; filterIndex?: number }. message stays human-readable; use context for programmatic handling.

    Code When it is emitted
    BROWSER_NOT_SUPPORTED connect$ without navigator.serial.
    PORT_OPEN_FAILED port.open() rejected.
    PORT_ALREADY_OPEN connect$ called while not in 'idle' / 'error'.
    PORT_NOT_OPEN send$ or disconnect$ called in an invalid session state.
    READ_FAILED Internal read pump errored.
    WRITE_FAILED port.writable.getWriter().write() rejected.
    CONNECTION_LOST port.close() failed or the port dropped mid-session.
    INVALID_FILTER_OPTIONS filters contained an invalid entry (at session creation).
    INVALID_TERMINAL_BUFFER_OPTIONS terminalBuffer.maxLines or terminalBuffer.maxChars was out of range at session creation.
    INVALID_LINE_BUFFER_OPTIONS lineBuffer.maxChars was out of range at session creation.
    INVALID_CONNECTION_OPTIONS baudRate was out of range at session creation.
    OPERATION_CANCELLED User cancelled the port picker.
    SESSION_DISPOSED connect$ or send$ called after dispose$.
    UNKNOWN Unclassified dispose / disconnect fallback; see context.cause.
    Code Notes
    PORT_NOT_AVAILABLE Deprecated. Unreachable without a getPorts API. Use PORT_OPEN_FAILED / OPERATION_CANCELLED for port acquisition failures.
    OPERATION_TIMEOUT Deprecated. Unreachable without a timeout / transaction API.