Readonlyerrors$Canonical fatal / non-fatal error channel.
All SerialError instances produced by the session (connect / read / write / close) are multiplexed here. This is the main channel, not a supplementary one.
Every emission is the exact same instance that is also surfaced to
the relevant call-site subscriber (for example connect$().subscribe
receives the same SerialError that errors$ emits for that
failure), so a single subscription is enough to observe the full
error history without double-normalisation.
Fatal failures (connect / read / close) additionally drive state$
to 'error' and tear down the live pump + port; non-fatal failures
(currently only send$ write failures) are multiplexed here without
mutating state$, on the assumption that a real connection loss is
detected by the read pump on the next tick.
Readonlyistrue when state$ has status: 'connected', false otherwise.
Derived from state$ with distinctUntilChanged so UIs can bind
connect/disabled flags without reimplementing the comparison.
Prefer narrowing state$ with SerialSessionStatus.Connected. Retained for backward compatibility; scheduled for removal in the next major version.
Readonlylines$Decoded text split into complete lines using \n, \r\n, and
lone interior \r (see implementation). Intended for logs,
newline-framed command responses, and parsers—not for mirroring raw
terminal output where \r must be preserved for progress/redraw. For
rendering terminal text, prefer terminalText$.
A trailing fragment without a line terminator is buffered until a later
chunk completes a line, or discarded on disconnect. The incomplete tail is
bounded by SerialSessionOptions.lineBuffer maxChars (default
1,048,576); when exceeded, leading characters are discarded and a
non-fatal SerialErrorCode.LINE_BUFFER_OVERFLOW is emitted on
errors$. Pass { maxChars: 0 } for unlimited growth. It is not
subscription-lazy: the same framing runs whenever the read pump is active,
independent of subscribers.
ReadonlyportThe active port’s SerialPort.getInfo snapshot, or null when no
port is open (including SerialSessionStatus.Idle,
SerialSessionStatus.Error, and SerialSessionStatus.Unsupported).
Emits the current value on subscribe. Use with state$ to know when the value is valid for your UI.
Prefer narrowing state$ with
SerialSessionStatus.Connected and using state.portInfo.
Retained for backward compatibility; scheduled for removal in the next
major version.
Readonlyreceive$Incoming data from the serial port as UTF-8 decoded text.
The stream is driven by the read pump started by connect$ and is
decoded internally with a streaming TextDecoder, so multi-byte
characters split across chunks are joined correctly. It is not
subscription-lazy: emissions happen regardless of whether a consumer
is currently subscribed, so late subscribers see only new data.
Emits raw decoder chunks (not line-aligned): carriage returns and
other control characters from the peer are preserved. Use this for
terminal-like mirrors, progress output that relies on \r, or raw
inspection. Do not drive those UIs from lines$, which may
split on interior \r and break redraw semantics.
For newline-framed protocols, logs, or line-by-line parsing, prefer
lines$ or derive custom framing from receive$.
ReadonlyreceiveSame source data as receive$ but, when
SerialSessionOptions.receiveReplay has enabled: true, it uses a
replay buffer per open connection so new subscribers can receive the
last N decoded text chunks from that connection. When receive replay
is off (default), this is the same hot stream as receive$.
Does not change lines$ (line framing is not replayed here).
Readonlystate$Canonical lifecycle source for the session.
Reactive session lifecycle state as a discriminated union. Replays the
current state on subscribe. Switch on state.status and use the
per-variant fields (portInfo when SerialSessionStatus.Connected,
error when SerialSessionStatus.Error) instead of correlating
separate streams.
ReadonlyterminalTerminal-display oriented cumulative text derived from receive$.
This stream collapses carriage-return redraws (\r) and keeps normal
newline behavior (\n, \r\n) so apps can bind terminal-like output
directly without wrapping createTerminalBuffer in every consumer.
By default, retains at most 10,000 completed lines and 1,048,576
characters; configure via SerialSessionOptions.terminalBuffer.
Equivalent behavior:
createTerminalBuffer(receive$).text$
Open a serial port and start the internal read pump.
Returns an Observable that completes when the port is fully opened and
the read pump is running. Subscribing to receive$ before calling
connect$ is safe: emissions simply start after the pump is active.
An Observable that completes on successful connection.
Close the active serial port and stop the internal read pump.
Safe to call when already disconnected or while a disconnect is already
in progress. When called during 'connecting', cancels the in-flight
connect$() (closes any opened port) and returns the session to
'idle' without reaching 'connected'.
An Observable that completes when the port is fully closed.
Permanently tear down the session and complete all observables.
Unlike disconnect$, which returns the session to 'idle' for
reuse, dispose$ closes any active connection, releases internal
resources, emits 'disposed' on state$, and completes every
session stream. After disposal, connect$ and send$
fail with SerialErrorCode.SESSION_DISPOSED; create a new
session instead of reusing this instance.
Safe to call multiple times; subsequent calls complete immediately.
An Observable that completes when disposal has finished.
Synchronous read of the last portInfo$ value.
The same as SerialPort.getInfo for the open port, or
null when not connected.
Prefer narrowing state$ with
SerialSessionStatus.Connected and using state.portInfo.
Retained for backward compatibility; scheduled for removal in the next
major version.
Synchronous feature detection for the Web Serial API.
Never throws; intended for UI branching before calling connect$.
true when navigator.serial is available.
Enqueue data for ordered transmission.
Writes are serialized internally through a FIFO send queue so that
concurrent send$ calls are delivered to the port in call order,
regardless of how quickly each subscriber runs. String payloads are
UTF-8 encoded via a shared TextEncoder; Uint8Array payloads are
passed through unchanged. Write failures are normalized into
SerialError with SerialErrorCode.WRITE_FAILED and
multiplexed on SerialSession.errors$ in addition to being
surfaced to the subscriber, so a single subscription is enough to
observe every I/O error. Calling send$ while the session is not in
'connected' state fails fast with
SerialErrorCode.PORT_NOT_OPEN.
The returned Observable completes once the enqueued payload has been flushed to the underlying writer.
Text (UTF-8 encoded) or raw bytes to send.
An Observable that completes when the payload is written.
Public API for interacting with the Web Serial API through a minimal, session-oriented surface.
The session is intentionally slim so that apps (Angular, Vue, React, etc.) can drive their UI from
state$(canonical lifecycle state) +errors$(error event channel) +receive$+terminalText$+lines$and never have to rebuild BehaviorSubjects, manage a read loop, or serialize writes themselves.All imperative Web Serial work (open / read loop / write / close) is encapsulated by the implementation. Only Observables are exposed.
Example
See