diff --git a/packages/transport-web-serial/src/transport.ts b/packages/transport-web-serial/src/transport.ts index 47d98586..aeaaa684 100644 --- a/packages/transport-web-serial/src/transport.ts +++ b/packages/transport-web-serial/src/transport.ts @@ -5,6 +5,8 @@ export class TransportWebSerial implements Types.Transport { private _toDevice: WritableStream; private _fromDevice: ReadableStream; private connection: SerialPort; + private pipePromise: Promise | null = null; + private abortController = new AbortController(); public static async create(baudRate?: number): Promise { const port = await navigator.serial.requestPort(); @@ -27,7 +29,11 @@ export class TransportWebSerial implements Types.Transport { this.connection = connection; - Utils.toDeviceStream.readable.pipeTo(connection.writable); + // Set up the pipe with abort signal for clean cancellation + this.pipePromise = Utils.toDeviceStream.readable.pipeTo( + connection.writable, + { signal: this.abortController.signal } + ); this._toDevice = Utils.toDeviceStream.writable; this._fromDevice = connection.readable.pipeThrough( @@ -43,7 +49,39 @@ export class TransportWebSerial implements Types.Transport { return this._fromDevice; } - disconnect() { - return this.connection.close(); + /** + * Safely disconnects the serial port, following best practices from + * https://github.com/WICG/serial/. Cancels any active pipe + * operations and only closes the port after streams are unlocked. + */ + async disconnect() { + try { + this.abortController.abort(); + + if (this.pipePromise) { + try { + await this.pipePromise; + } catch (error) { + if (error instanceof Error && error.name !== 'AbortError') { + throw error; + } + } + } + + // Cancel any remaining streams + if (this._fromDevice && this._fromDevice.locked) { + try { + await this._fromDevice.cancel(); + } catch (error) { + // Stream cancellation might fail if already cancelled + } + } + + await this.connection.close(); + + } catch (error) { + // If we can't close cleanly, let the browser handle cleanup + console.warn('Could not cleanly disconnect serial port:', error); + } } }