UNPKG

webserial-core

Version:

A strongly-typed, event-driven, abstract TypeScript library for the Web Serial API with custom parsers, command queue, handshake validation, and auto-reconnect.

126 lines (125 loc) 4.97 kB
import { SerialEventEmitter } from './SerialEventEmitter.js'; import { SerialDeviceOptions, SerialPolyfillOptions, SerialProvider } from '../types/index.js'; /** * Abstract base class for all serial devices. * * @typeParam T - Type of parsed data emitted by `"serial:data"` events. * Use `string` with a delimiter parser, `Uint8Array` for raw/fixed-length, * or any custom type with a custom parser. */ export declare abstract class AbstractSerialDevice<T> extends SerialEventEmitter<T> { /** The currently open serial port, or `null` when disconnected. */ protected port: SerialPort | null; private reader; private writer; private queue; private options; private isConnecting; private abortController; private userInitiatedDisconnect; private reconnectTimerId; private isHandshaking; /** * Custom serial provider (e.g. a WebUSB polyfill). * Falls back to `navigator.serial` when not set. */ private static customProvider; /** * Options forwarded to the polyfill provider on every * `requestPort()` / `getPorts()` call. */ private static polyfillOptions; constructor(options: SerialDeviceOptions<T>); /** * Override this method in your subclass to perform a handshake * after the port is opened. Return `true` if the handshake * succeeds (correct device), or `false` to reject the port. * * The method receives the opened port and should write/read * directly to validate the device identity. * * If not overridden, all ports are accepted (no handshake). */ protected handshake(): Promise<boolean>; connect(): Promise<void>; disconnect(): Promise<void>; /** * Returns `true` if the device is currently connected and the port * is open, readable, and writable. Note that a port can become * disconnected at any time (e.g. unplugged), so this is not a * guarantee that a subsequent read/write will succeed, but it is * a useful check before attempting communication. * @returns `true` if the device is connected and ready for communication, `false` otherwise. */ isConnected(): boolean; /** * Returns `true` if the device is not connected or in the process of connecting. * This is a convenience method equivalent to `!isConnected()`, but may be more * semantically clear in certain contexts (e.g. when checking for disconnection * in a read loop catch block). * @returns `true` if the device is disconnected or not ready, `false` if it is currently connected. */ isDisconnected(): boolean; /** * Internal cleanup: tears down the port, reader, writer without * marking it as user-initiated. This allows auto-reconnect to trigger. */ private cleanupPort; forget(): Promise<void>; send(data: string | Uint8Array): Promise<void>; clearQueue(): void; private writeToPort; private readLoop; /** * Opens a port, locks it in the registry, starts reading, and runs the handshake. * If handshake fails, tears down reader/queue, closes and unlocks the port. */ private openAndHandshake; /** * Cleans up after a failed handshake attempt and restores the queue. */ private teardownHandshake; /** * Cancels and releases the current reader. */ private stopReader; /** * Wraps the handshake() in a timeout race. */ private runHandshakeWithTimeout; /** * Iterates ALL previously-authorized ports matching filters. * For each match, opens + handshake. Returns the first port that passes. * If a port fails handshake, it is closed and the next one is tried. */ private findAndValidatePort; private startReconnecting; stopReconnecting(): void; private reconnect; static getInstances(): AbstractSerialDevice<unknown>[]; static connectAll(): Promise<void>; /** * Sets a custom serial provider (e.g. a WebUSB polyfill for Android). * Call this once before any `connect()` if the native Web Serial API * is not available. * * @param provider The provider object (`{ requestPort, getPorts }`). * @param options Polyfill options forwarded on every `requestPort` / * `getPorts` call. Use `usbControlInterfaceClass` to * support devices that don't use the standard CDC class * code (2). E.g. `{ usbControlInterfaceClass: 255 }`. * * @example * ```ts * import { serial as polyfill } from 'web-serial-polyfill'; * AbstractSerialDevice.setProvider(polyfill, { * usbControlInterfaceClass: 255, * }); * ``` */ static setProvider(provider: SerialProvider, options?: SerialPolyfillOptions): void; /** * Returns the serial provider: instance provider > custom provider > navigator.serial > null. */ private getSerial; }