@gurezo/web-serial-rxjs 0.1.21 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -23,6 +23,8 @@ Web Serial API を RxJS ベースのリアクティブなラッパーで提供
23
23
  ## 機能
24
24
 
25
25
  - **RxJS ベースのリアクティブ API**: RxJS Observables を活用したリアクティブなシリアルポート通信
26
+ - **テキスト向け API**: `getReadStreamAsText()` と `writeText()` で手動のエンコード/デコードを不要化
27
+ - **シェルユーティリティ層**: `createShellClient()` でコマンド実行とプロンプト待ちのワークフローを簡潔化
26
28
  - **TypeScript サポート**: 完全な TypeScript 型定義を含む
27
29
  - **ブラウザ検出**: ブラウザサポートの検出とエラーハンドリング機能を内蔵
28
30
  - **エラーハンドリング**: カスタムエラークラスとエラーコードによる包括的なエラーハンドリング
package/README.md CHANGED
@@ -24,6 +24,8 @@ A TypeScript library that provides a reactive RxJS-based wrapper for the Web Ser
24
24
  ## Features
25
25
 
26
26
  - **RxJS-based reactive API**: Leverage the power of RxJS Observables for reactive serial port communication
27
+ - **Text-friendly API**: Use `getReadStreamAsText()` and `writeText()` without manual encoding/decoding
28
+ - **Shell utility layer**: Execute command/prompt workflows with `createShellClient()`
27
29
  - **TypeScript support**: Full TypeScript type definitions included
28
30
  - **Browser detection**: Built-in browser support detection and error handling
29
31
  - **Error handling**: Comprehensive error handling with custom error classes and error codes
@@ -145,6 +145,16 @@ export interface SerialClient {
145
145
  * ```
146
146
  */
147
147
  getReadStream(): Observable<Uint8Array>;
148
+ /**
149
+ * Get an Observable that emits text read from the serial port.
150
+ *
151
+ * This is a convenience API on top of {@link getReadStream} that decodes bytes
152
+ * with TextDecoder and emits text chunks.
153
+ *
154
+ * @returns An Observable that emits decoded text chunks
155
+ * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open
156
+ */
157
+ getReadStreamAsText(): Observable<string>;
148
158
  /**
149
159
  * Write data to the serial port from an Observable.
150
160
  *
@@ -195,12 +205,35 @@ export interface SerialClient {
195
205
  * ```
196
206
  */
197
207
  write(data: Uint8Array): Observable<void>;
208
+ /**
209
+ * Write text data to the serial port.
210
+ *
211
+ * This is a convenience API on top of {@link write} that encodes text with TextEncoder.
212
+ *
213
+ * @param data - Text data to write to the serial port
214
+ * @returns An Observable that completes when the data has been written
215
+ * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open
216
+ * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing fails
217
+ */
218
+ writeText(data: string): Observable<void>;
198
219
  /**
199
220
  * Check if the port is currently open and connected.
200
221
  *
201
222
  * @returns `true` if a port is currently open, `false` otherwise
202
223
  */
203
224
  readonly connected: boolean;
225
+ /**
226
+ * Reactive connection state stream.
227
+ *
228
+ * Emits `true` when connected and `false` when disconnected.
229
+ */
230
+ readonly connected$: Observable<boolean>;
231
+ /**
232
+ * Reactive connection event stream.
233
+ *
234
+ * Emits `'connected'` on successful connection and `'disconnected'` on disconnection.
235
+ */
236
+ readonly connectionEvents$: Observable<'connected' | 'disconnected'>;
204
237
  /**
205
238
  * Get the current SerialPort instance.
206
239
  *
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IAEtC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE7C;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,aAAa,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IAExC;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE7D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE1C;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,UAAU,GAAG,IAAI,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY,CAEd"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAClC,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAGvD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;;;;;;;;;;;;;;OAkBG;IACH,WAAW,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IAEtC;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,QAAQ,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC,CAAC;IAErC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE7C;;;;;;;;;;;;;;;;OAgBG;IACH,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAE/B;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,aAAa,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;IAExC;;;;;;;;OAQG;IACH,mBAAmB,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;IAE1C;;;;;;;;;;;;;;;;;;;;;;;;;OAyBG;IACH,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE7D;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE1C;;;;;;;;;OASG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAE1C;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC;IAE5B;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,OAAO,CAAC,CAAC;IAEzC;;;;OAIG;IACH,QAAQ,CAAC,iBAAiB,EAAE,UAAU,CAAC,WAAW,GAAG,cAAc,CAAC,CAAC;IAErE;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,UAAU,GAAG,IAAI,CAAC;CACzC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,CAAC,EAAE,mBAAmB,GAC5B,YAAY,CAEd"}
@@ -19,6 +19,16 @@ export declare class SerialClientImpl {
19
19
  /** @internal */
20
20
  private writeSubscription;
21
21
  /** @internal */
22
+ private sharedReadStream$;
23
+ /** @internal */
24
+ private textEncoder;
25
+ /** @internal */
26
+ private textDecoder;
27
+ /** @internal */
28
+ private readonly connectedState$;
29
+ /** @internal */
30
+ private readonly connectionEventsSubject$;
31
+ /** @internal */
22
32
  private readonly options;
23
33
  /**
24
34
  * Creates a new SerialClientImpl instance.
@@ -64,6 +74,13 @@ export declare class SerialClientImpl {
64
74
  * @internal
65
75
  */
66
76
  getReadStream(): Observable<Uint8Array>;
77
+ /**
78
+ * Get an Observable that emits decoded text from the serial port.
79
+ *
80
+ * @returns Observable that emits text chunks
81
+ * @internal
82
+ */
83
+ getReadStreamAsText(): Observable<string>;
67
84
  /**
68
85
  * Write data to the serial port from an Observable.
69
86
  *
@@ -80,6 +97,14 @@ export declare class SerialClientImpl {
80
97
  * @internal
81
98
  */
82
99
  write(data: Uint8Array): Observable<void>;
100
+ /**
101
+ * Write text data to the serial port.
102
+ *
103
+ * @param data - Text data to write
104
+ * @returns Observable that completes when the data is written
105
+ * @internal
106
+ */
107
+ writeText(data: string): Observable<void>;
83
108
  /**
84
109
  * Check if the port is currently open.
85
110
  *
@@ -87,6 +112,20 @@ export declare class SerialClientImpl {
87
112
  * @internal
88
113
  */
89
114
  get connected(): boolean;
115
+ /**
116
+ * Get an Observable that emits connection state changes.
117
+ *
118
+ * @returns Observable that emits `true` when connected and `false` when disconnected
119
+ * @internal
120
+ */
121
+ get connected$(): Observable<boolean>;
122
+ /**
123
+ * Get an Observable that emits connection lifecycle events.
124
+ *
125
+ * @returns Observable that emits 'connected' or 'disconnected'
126
+ * @internal
127
+ */
128
+ get connectionEvents$(): Observable<'connected' | 'disconnected'>;
90
129
  /**
91
130
  * Get the current SerialPort instance.
92
131
  *
@@ -1 +1 @@
1
- {"version":3,"file":"serial-client.d.ts","sourceRoot":"","sources":["../../src/client/serial-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAoB,MAAM,MAAM,CAAC;AAMpD,OAAO,EAEL,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;GAQG;AACH,qBAAa,gBAAgB;IAC3B,gBAAgB;IAChB,OAAO,CAAC,IAAI,CAA2B;IACvC,gBAAgB;IAChB,OAAO,CAAC,MAAM,CAAS;IACvB,gBAAgB;IAChB,OAAO,CAAC,gBAAgB,CAA4C;IACpE,gBAAgB;IAChB,OAAO,CAAC,iBAAiB,CAA4C;IACrE,gBAAgB;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;IAEF;;;;;;OAMG;gBACS,OAAO,CAAC,EAAE,mBAAmB;IASzC;;;;;OAKG;IACH,WAAW,IAAI,UAAU,CAAC,UAAU,CAAC;IAuBrC;;;;;OAKG;IACH,QAAQ,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;IAcpC;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC;IAyD5C;;;;;OAKG;IACH,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC;IAqC9B;;;;;OAKG;IACH,aAAa,IAAI,UAAU,CAAC,UAAU,CAAC;IAWvC;;;;;;OAMG;IACH,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;IAuD5D;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC;IA0BzC;;;;;OAKG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;;OAKG;IACH,IAAI,WAAW,IAAI,UAAU,GAAG,IAAI,CAEnC;CACF"}
1
+ {"version":3,"file":"serial-client.d.ts","sourceRoot":"","sources":["../../src/client/serial-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,UAAU,EAMX,MAAM,MAAM,CAAC;AAMd,OAAO,EAEL,mBAAmB,EACpB,MAAM,kBAAkB,CAAC;AAE1B;;;;;;;;GAQG;AACH,qBAAa,gBAAgB;IAC3B,gBAAgB;IAChB,OAAO,CAAC,IAAI,CAA2B;IACvC,gBAAgB;IAChB,OAAO,CAAC,MAAM,CAAS;IACvB,gBAAgB;IAChB,OAAO,CAAC,gBAAgB,CAA4C;IACpE,gBAAgB;IAChB,OAAO,CAAC,iBAAiB,CAA4C;IACrE,gBAAgB;IAChB,OAAO,CAAC,iBAAiB,CAAuC;IAChE,gBAAgB;IAChB,OAAO,CAAC,WAAW,CAAqB;IACxC,gBAAgB;IAChB,OAAO,CAAC,WAAW,CAAqB;IACxC,gBAAgB;IAChB,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAuC;IACvE,gBAAgB;IAChB,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAErC;IACJ,gBAAgB;IAChB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAEtB;IAEF;;;;;;OAMG;gBACS,OAAO,CAAC,EAAE,mBAAmB;IASzC;;;;;OAKG;IACH,WAAW,IAAI,UAAU,CAAC,UAAU,CAAC;IAuBrC;;;;;OAKG;IACH,QAAQ,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;IAcpC;;;;;;OAMG;IACH,OAAO,CAAC,IAAI,CAAC,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC;IA2D5C;;;;;OAKG;IACH,UAAU,IAAI,UAAU,CAAC,IAAI,CAAC;IA2C9B;;;;;OAKG;IACH,aAAa,IAAI,UAAU,CAAC,UAAU,CAAC;IAqBvC;;;;;OAKG;IACH,mBAAmB,IAAI,UAAU,CAAC,MAAM,CAAC;IAMzC;;;;;;OAMG;IACH,WAAW,CAAC,KAAK,EAAE,UAAU,CAAC,UAAU,CAAC,GAAG,UAAU,CAAC,IAAI,CAAC;IAuD5D;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,UAAU,CAAC,IAAI,CAAC;IA0BzC;;;;;;OAMG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC;IAIzC;;;;;OAKG;IACH,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED;;;;;OAKG;IACH,IAAI,UAAU,IAAI,UAAU,CAAC,OAAO,CAAC,CAEpC;IAED;;;;;OAKG;IACH,IAAI,iBAAiB,IAAI,UAAU,CAAC,WAAW,GAAG,cAAc,CAAC,CAEhE;IAED;;;;;OAKG;IACH,IAAI,WAAW,IAAI,UAAU,GAAG,IAAI,CAEnC;CACF"}
package/dist/index.d.ts CHANGED
@@ -42,7 +42,9 @@
42
42
  * ```
43
43
  */
44
44
  export { createSerialClient } from './client';
45
+ export { createShellClient } from './shell';
45
46
  export type { SerialClient } from './client';
47
+ export type { ShellClient, ShellClientOptions, ShellExecResult } from './shell';
46
48
  export { SerialError } from './errors/serial-error';
47
49
  export { SerialErrorCode } from './errors/serial-error-code';
48
50
  export type { SerialClientOptions } from './types/options';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAG9C,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAG7C,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAChB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAG5C,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,YAAY,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGhF,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG7D,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG3D,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAChB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAGnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,13 @@
1
1
  // packages/web-serial-rxjs/src/client/serial-client.ts
2
- import { Observable as Observable2, defer, switchMap } from "rxjs";
2
+ import {
3
+ BehaviorSubject,
4
+ Observable as Observable2,
5
+ Subject,
6
+ defer,
7
+ map,
8
+ share,
9
+ switchMap
10
+ } from "rxjs";
3
11
 
4
12
  // packages/web-serial-rxjs/src/errors/serial-error-code.ts
5
13
  var SerialErrorCode = /* @__PURE__ */ ((SerialErrorCode2) => {
@@ -323,6 +331,16 @@ var SerialClientImpl = class {
323
331
  this.readSubscription = null;
324
332
  /** @internal */
325
333
  this.writeSubscription = null;
334
+ /** @internal */
335
+ this.sharedReadStream$ = null;
336
+ /** @internal */
337
+ this.textEncoder = new TextEncoder();
338
+ /** @internal */
339
+ this.textDecoder = new TextDecoder();
340
+ /** @internal */
341
+ this.connectedState$ = new BehaviorSubject(false);
342
+ /** @internal */
343
+ this.connectionEventsSubject$ = new Subject();
326
344
  checkBrowserSupport();
327
345
  this.options = {
328
346
  ...DEFAULT_SERIAL_CLIENT_OPTIONS,
@@ -409,6 +427,8 @@ var SerialClientImpl = class {
409
427
  flowControl: this.options.flowControl
410
428
  }).then(() => {
411
429
  this.isOpen = true;
430
+ this.connectedState$.next(true);
431
+ this.connectionEventsSubject$.next("connected");
412
432
  }).catch((error) => {
413
433
  this.port = null;
414
434
  this.isOpen = false;
@@ -440,6 +460,8 @@ var SerialClientImpl = class {
440
460
  this.readSubscription.unsubscribe();
441
461
  this.readSubscription = null;
442
462
  }
463
+ this.sharedReadStream$ = null;
464
+ this.textDecoder = new TextDecoder();
443
465
  if (this.writeSubscription) {
444
466
  this.writeSubscription.unsubscribe();
445
467
  this.writeSubscription = null;
@@ -447,9 +469,13 @@ var SerialClientImpl = class {
447
469
  return this.port.close().then(() => {
448
470
  this.port = null;
449
471
  this.isOpen = false;
472
+ this.connectedState$.next(false);
473
+ this.connectionEventsSubject$.next("disconnected");
450
474
  }).catch((error) => {
451
475
  this.port = null;
452
476
  this.isOpen = false;
477
+ this.connectedState$.next(false);
478
+ this.connectionEventsSubject$.next("disconnected");
453
479
  throw new SerialError(
454
480
  "CONNECTION_LOST" /* CONNECTION_LOST */,
455
481
  `Failed to close port: ${error instanceof Error ? error.message : String(error)}`,
@@ -471,7 +497,27 @@ var SerialClientImpl = class {
471
497
  "Port is not open or readable stream is not available"
472
498
  );
473
499
  }
474
- return readableToObservable(this.port.readable);
500
+ if (!this.sharedReadStream$) {
501
+ this.sharedReadStream$ = readableToObservable(this.port.readable).pipe(
502
+ share({
503
+ resetOnError: true,
504
+ resetOnComplete: true,
505
+ resetOnRefCountZero: true
506
+ })
507
+ );
508
+ }
509
+ return this.sharedReadStream$;
510
+ }
511
+ /**
512
+ * Get an Observable that emits decoded text from the serial port.
513
+ *
514
+ * @returns Observable that emits text chunks
515
+ * @internal
516
+ */
517
+ getReadStreamAsText() {
518
+ return this.getReadStream().pipe(
519
+ map((chunk) => this.textDecoder.decode(chunk, { stream: true }))
520
+ );
475
521
  }
476
522
  /**
477
523
  * Write data to the serial port from an Observable.
@@ -554,6 +600,16 @@ var SerialClientImpl = class {
554
600
  });
555
601
  });
556
602
  }
603
+ /**
604
+ * Write text data to the serial port.
605
+ *
606
+ * @param data - Text data to write
607
+ * @returns Observable that completes when the data is written
608
+ * @internal
609
+ */
610
+ writeText(data) {
611
+ return this.write(this.textEncoder.encode(data));
612
+ }
557
613
  /**
558
614
  * Check if the port is currently open.
559
615
  *
@@ -563,6 +619,24 @@ var SerialClientImpl = class {
563
619
  get connected() {
564
620
  return this.isOpen;
565
621
  }
622
+ /**
623
+ * Get an Observable that emits connection state changes.
624
+ *
625
+ * @returns Observable that emits `true` when connected and `false` when disconnected
626
+ * @internal
627
+ */
628
+ get connected$() {
629
+ return this.connectedState$.asObservable();
630
+ }
631
+ /**
632
+ * Get an Observable that emits connection lifecycle events.
633
+ *
634
+ * @returns Observable that emits 'connected' or 'disconnected'
635
+ * @internal
636
+ */
637
+ get connectionEvents$() {
638
+ return this.connectionEventsSubject$.asObservable();
639
+ }
566
640
  /**
567
641
  * Get the current SerialPort instance.
568
642
  *
@@ -578,6 +652,165 @@ var SerialClientImpl = class {
578
652
  function createSerialClient(options) {
579
653
  return new SerialClientImpl(options);
580
654
  }
655
+
656
+ // packages/web-serial-rxjs/src/shell/create-shell-client.ts
657
+ import {
658
+ Observable as Observable3,
659
+ Subject as Subject2,
660
+ defaultIfEmpty,
661
+ defer as defer2,
662
+ firstValueFrom,
663
+ shareReplay
664
+ } from "rxjs";
665
+ var DEFAULT_TIMEOUT = 1e4;
666
+ var DEFAULT_RETRY = 0;
667
+ var DEFAULT_LINE_ENDING = "\r\n";
668
+ var ShellClientImpl = class {
669
+ constructor(client, options) {
670
+ this.client = client;
671
+ this.bufferTick$ = new Subject2();
672
+ this.queueChain = Promise.resolve();
673
+ this.readBuffer = "";
674
+ this.prompt = options.prompt;
675
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
676
+ this.retry = options.retry ?? DEFAULT_RETRY;
677
+ this.lineEnding = options.lineEnding ?? DEFAULT_LINE_ENDING;
678
+ this.promptRegex = this.prompt instanceof RegExp ? this.createAnchoredRegex(this.prompt) : null;
679
+ this.read$ = this.client.getReadStreamAsText().pipe(shareReplay(1));
680
+ this.read$.subscribe({
681
+ next: (chunk) => {
682
+ this.readBuffer += chunk;
683
+ this.bufferTick$.next();
684
+ },
685
+ error: (error) => this.bufferTick$.error(error),
686
+ complete: () => this.bufferTick$.complete()
687
+ });
688
+ }
689
+ exec$(command) {
690
+ return this.enqueue(async () => {
691
+ for (let attempt = 0; attempt <= this.retry; attempt += 1) {
692
+ try {
693
+ await this.writeText(command + this.lineEnding);
694
+ const stdout = await this.waitUntilPrompt(this.timeout);
695
+ return { stdout };
696
+ } catch (error) {
697
+ if (!this.isTimeoutError(error) || attempt >= this.retry) {
698
+ throw error;
699
+ }
700
+ }
701
+ }
702
+ throw new Error("Unexpected command execution state");
703
+ });
704
+ }
705
+ readUntilPrompt$() {
706
+ return this.enqueue(async () => {
707
+ for (let attempt = 0; attempt <= this.retry; attempt += 1) {
708
+ try {
709
+ return await this.waitUntilPrompt(this.timeout);
710
+ } catch (error) {
711
+ if (!this.isTimeoutError(error) || attempt >= this.retry) {
712
+ throw error;
713
+ }
714
+ }
715
+ }
716
+ throw new Error("Unexpected prompt waiting state");
717
+ });
718
+ }
719
+ enqueue(operation) {
720
+ return defer2(
721
+ () => new Observable3((subscriber) => {
722
+ let cancelled = false;
723
+ const run = async () => {
724
+ try {
725
+ const value = await operation();
726
+ if (!cancelled) {
727
+ subscriber.next(value);
728
+ subscriber.complete();
729
+ }
730
+ } catch (error) {
731
+ if (!cancelled) {
732
+ subscriber.error(error);
733
+ }
734
+ }
735
+ };
736
+ const scheduled = this.queueChain.then(run, run);
737
+ this.queueChain = scheduled.then(
738
+ () => void 0,
739
+ () => void 0
740
+ );
741
+ return () => {
742
+ cancelled = true;
743
+ };
744
+ })
745
+ );
746
+ }
747
+ async writeText(text) {
748
+ await firstValueFrom(this.client.writeText(text).pipe(defaultIfEmpty(void 0)));
749
+ }
750
+ waitUntilPrompt(timeoutMs) {
751
+ const immediate = this.tryConsumePrompt();
752
+ if (immediate !== null) {
753
+ return Promise.resolve(immediate);
754
+ }
755
+ return new Promise((resolve, reject) => {
756
+ const timer = setTimeout(() => {
757
+ subscription.unsubscribe();
758
+ reject(new Error(`Timed out waiting for prompt after ${timeoutMs}ms`));
759
+ }, timeoutMs);
760
+ const complete = (value) => {
761
+ clearTimeout(timer);
762
+ subscription.unsubscribe();
763
+ resolve(value);
764
+ };
765
+ const fail = (error) => {
766
+ clearTimeout(timer);
767
+ subscription.unsubscribe();
768
+ reject(error);
769
+ };
770
+ const tryResolve = () => {
771
+ const output = this.tryConsumePrompt();
772
+ if (output !== null) {
773
+ complete(output);
774
+ }
775
+ };
776
+ const subscription = this.bufferTick$.subscribe({
777
+ next: () => tryResolve(),
778
+ error: (error) => fail(error),
779
+ complete: () => fail(new Error("Read stream completed before prompt was found"))
780
+ });
781
+ });
782
+ }
783
+ tryConsumePrompt() {
784
+ if (typeof this.prompt === "string") {
785
+ if (this.readBuffer.length >= this.prompt.length && this.readBuffer.endsWith(this.prompt)) {
786
+ const body2 = this.readBuffer.slice(0, this.readBuffer.length - this.prompt.length);
787
+ this.readBuffer = "";
788
+ return body2.trimEnd();
789
+ }
790
+ return null;
791
+ }
792
+ if (!this.promptRegex) {
793
+ return null;
794
+ }
795
+ const match = this.promptRegex.exec(this.readBuffer);
796
+ if (!match || match.index == null) {
797
+ return null;
798
+ }
799
+ const body = this.readBuffer.slice(0, match.index);
800
+ this.readBuffer = "";
801
+ return body.trimEnd();
802
+ }
803
+ createAnchoredRegex(source) {
804
+ const flags = source.flags.replace(/g/g, "");
805
+ return new RegExp(`(?:${source.source})$`, flags);
806
+ }
807
+ isTimeoutError(error) {
808
+ return error instanceof Error && error.message.includes("Timed out waiting for prompt");
809
+ }
810
+ };
811
+ function createShellClient(client, options) {
812
+ return new ShellClientImpl(client, options);
813
+ }
581
814
  export {
582
815
  BrowserType,
583
816
  SerialError,
@@ -585,6 +818,7 @@ export {
585
818
  buildRequestOptions,
586
819
  checkBrowserSupport,
587
820
  createSerialClient,
821
+ createShellClient,
588
822
  detectBrowserType,
589
823
  hasWebSerialSupport,
590
824
  isBrowserSupported,
package/dist/index.mjs CHANGED
@@ -1,5 +1,13 @@
1
1
  // src/client/serial-client.ts
2
- import { Observable as Observable2, defer, switchMap } from "rxjs";
2
+ import {
3
+ BehaviorSubject,
4
+ Observable as Observable2,
5
+ Subject,
6
+ defer,
7
+ map,
8
+ share,
9
+ switchMap
10
+ } from "rxjs";
3
11
 
4
12
  // src/errors/serial-error-code.ts
5
13
  var SerialErrorCode = /* @__PURE__ */ ((SerialErrorCode2) => {
@@ -323,6 +331,16 @@ var SerialClientImpl = class {
323
331
  this.readSubscription = null;
324
332
  /** @internal */
325
333
  this.writeSubscription = null;
334
+ /** @internal */
335
+ this.sharedReadStream$ = null;
336
+ /** @internal */
337
+ this.textEncoder = new TextEncoder();
338
+ /** @internal */
339
+ this.textDecoder = new TextDecoder();
340
+ /** @internal */
341
+ this.connectedState$ = new BehaviorSubject(false);
342
+ /** @internal */
343
+ this.connectionEventsSubject$ = new Subject();
326
344
  checkBrowserSupport();
327
345
  this.options = {
328
346
  ...DEFAULT_SERIAL_CLIENT_OPTIONS,
@@ -409,6 +427,8 @@ var SerialClientImpl = class {
409
427
  flowControl: this.options.flowControl
410
428
  }).then(() => {
411
429
  this.isOpen = true;
430
+ this.connectedState$.next(true);
431
+ this.connectionEventsSubject$.next("connected");
412
432
  }).catch((error) => {
413
433
  this.port = null;
414
434
  this.isOpen = false;
@@ -440,6 +460,8 @@ var SerialClientImpl = class {
440
460
  this.readSubscription.unsubscribe();
441
461
  this.readSubscription = null;
442
462
  }
463
+ this.sharedReadStream$ = null;
464
+ this.textDecoder = new TextDecoder();
443
465
  if (this.writeSubscription) {
444
466
  this.writeSubscription.unsubscribe();
445
467
  this.writeSubscription = null;
@@ -447,9 +469,13 @@ var SerialClientImpl = class {
447
469
  return this.port.close().then(() => {
448
470
  this.port = null;
449
471
  this.isOpen = false;
472
+ this.connectedState$.next(false);
473
+ this.connectionEventsSubject$.next("disconnected");
450
474
  }).catch((error) => {
451
475
  this.port = null;
452
476
  this.isOpen = false;
477
+ this.connectedState$.next(false);
478
+ this.connectionEventsSubject$.next("disconnected");
453
479
  throw new SerialError(
454
480
  "CONNECTION_LOST" /* CONNECTION_LOST */,
455
481
  `Failed to close port: ${error instanceof Error ? error.message : String(error)}`,
@@ -471,7 +497,27 @@ var SerialClientImpl = class {
471
497
  "Port is not open or readable stream is not available"
472
498
  );
473
499
  }
474
- return readableToObservable(this.port.readable);
500
+ if (!this.sharedReadStream$) {
501
+ this.sharedReadStream$ = readableToObservable(this.port.readable).pipe(
502
+ share({
503
+ resetOnError: true,
504
+ resetOnComplete: true,
505
+ resetOnRefCountZero: true
506
+ })
507
+ );
508
+ }
509
+ return this.sharedReadStream$;
510
+ }
511
+ /**
512
+ * Get an Observable that emits decoded text from the serial port.
513
+ *
514
+ * @returns Observable that emits text chunks
515
+ * @internal
516
+ */
517
+ getReadStreamAsText() {
518
+ return this.getReadStream().pipe(
519
+ map((chunk) => this.textDecoder.decode(chunk, { stream: true }))
520
+ );
475
521
  }
476
522
  /**
477
523
  * Write data to the serial port from an Observable.
@@ -554,6 +600,16 @@ var SerialClientImpl = class {
554
600
  });
555
601
  });
556
602
  }
603
+ /**
604
+ * Write text data to the serial port.
605
+ *
606
+ * @param data - Text data to write
607
+ * @returns Observable that completes when the data is written
608
+ * @internal
609
+ */
610
+ writeText(data) {
611
+ return this.write(this.textEncoder.encode(data));
612
+ }
557
613
  /**
558
614
  * Check if the port is currently open.
559
615
  *
@@ -563,6 +619,24 @@ var SerialClientImpl = class {
563
619
  get connected() {
564
620
  return this.isOpen;
565
621
  }
622
+ /**
623
+ * Get an Observable that emits connection state changes.
624
+ *
625
+ * @returns Observable that emits `true` when connected and `false` when disconnected
626
+ * @internal
627
+ */
628
+ get connected$() {
629
+ return this.connectedState$.asObservable();
630
+ }
631
+ /**
632
+ * Get an Observable that emits connection lifecycle events.
633
+ *
634
+ * @returns Observable that emits 'connected' or 'disconnected'
635
+ * @internal
636
+ */
637
+ get connectionEvents$() {
638
+ return this.connectionEventsSubject$.asObservable();
639
+ }
566
640
  /**
567
641
  * Get the current SerialPort instance.
568
642
  *
@@ -578,6 +652,165 @@ var SerialClientImpl = class {
578
652
  function createSerialClient(options) {
579
653
  return new SerialClientImpl(options);
580
654
  }
655
+
656
+ // src/shell/create-shell-client.ts
657
+ import {
658
+ Observable as Observable3,
659
+ Subject as Subject2,
660
+ defaultIfEmpty,
661
+ defer as defer2,
662
+ firstValueFrom,
663
+ shareReplay
664
+ } from "rxjs";
665
+ var DEFAULT_TIMEOUT = 1e4;
666
+ var DEFAULT_RETRY = 0;
667
+ var DEFAULT_LINE_ENDING = "\r\n";
668
+ var ShellClientImpl = class {
669
+ constructor(client, options) {
670
+ this.client = client;
671
+ this.bufferTick$ = new Subject2();
672
+ this.queueChain = Promise.resolve();
673
+ this.readBuffer = "";
674
+ this.prompt = options.prompt;
675
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT;
676
+ this.retry = options.retry ?? DEFAULT_RETRY;
677
+ this.lineEnding = options.lineEnding ?? DEFAULT_LINE_ENDING;
678
+ this.promptRegex = this.prompt instanceof RegExp ? this.createAnchoredRegex(this.prompt) : null;
679
+ this.read$ = this.client.getReadStreamAsText().pipe(shareReplay(1));
680
+ this.read$.subscribe({
681
+ next: (chunk) => {
682
+ this.readBuffer += chunk;
683
+ this.bufferTick$.next();
684
+ },
685
+ error: (error) => this.bufferTick$.error(error),
686
+ complete: () => this.bufferTick$.complete()
687
+ });
688
+ }
689
+ exec$(command) {
690
+ return this.enqueue(async () => {
691
+ for (let attempt = 0; attempt <= this.retry; attempt += 1) {
692
+ try {
693
+ await this.writeText(command + this.lineEnding);
694
+ const stdout = await this.waitUntilPrompt(this.timeout);
695
+ return { stdout };
696
+ } catch (error) {
697
+ if (!this.isTimeoutError(error) || attempt >= this.retry) {
698
+ throw error;
699
+ }
700
+ }
701
+ }
702
+ throw new Error("Unexpected command execution state");
703
+ });
704
+ }
705
+ readUntilPrompt$() {
706
+ return this.enqueue(async () => {
707
+ for (let attempt = 0; attempt <= this.retry; attempt += 1) {
708
+ try {
709
+ return await this.waitUntilPrompt(this.timeout);
710
+ } catch (error) {
711
+ if (!this.isTimeoutError(error) || attempt >= this.retry) {
712
+ throw error;
713
+ }
714
+ }
715
+ }
716
+ throw new Error("Unexpected prompt waiting state");
717
+ });
718
+ }
719
+ enqueue(operation) {
720
+ return defer2(
721
+ () => new Observable3((subscriber) => {
722
+ let cancelled = false;
723
+ const run = async () => {
724
+ try {
725
+ const value = await operation();
726
+ if (!cancelled) {
727
+ subscriber.next(value);
728
+ subscriber.complete();
729
+ }
730
+ } catch (error) {
731
+ if (!cancelled) {
732
+ subscriber.error(error);
733
+ }
734
+ }
735
+ };
736
+ const scheduled = this.queueChain.then(run, run);
737
+ this.queueChain = scheduled.then(
738
+ () => void 0,
739
+ () => void 0
740
+ );
741
+ return () => {
742
+ cancelled = true;
743
+ };
744
+ })
745
+ );
746
+ }
747
+ async writeText(text) {
748
+ await firstValueFrom(this.client.writeText(text).pipe(defaultIfEmpty(void 0)));
749
+ }
750
+ waitUntilPrompt(timeoutMs) {
751
+ const immediate = this.tryConsumePrompt();
752
+ if (immediate !== null) {
753
+ return Promise.resolve(immediate);
754
+ }
755
+ return new Promise((resolve, reject) => {
756
+ const timer = setTimeout(() => {
757
+ subscription.unsubscribe();
758
+ reject(new Error(`Timed out waiting for prompt after ${timeoutMs}ms`));
759
+ }, timeoutMs);
760
+ const complete = (value) => {
761
+ clearTimeout(timer);
762
+ subscription.unsubscribe();
763
+ resolve(value);
764
+ };
765
+ const fail = (error) => {
766
+ clearTimeout(timer);
767
+ subscription.unsubscribe();
768
+ reject(error);
769
+ };
770
+ const tryResolve = () => {
771
+ const output = this.tryConsumePrompt();
772
+ if (output !== null) {
773
+ complete(output);
774
+ }
775
+ };
776
+ const subscription = this.bufferTick$.subscribe({
777
+ next: () => tryResolve(),
778
+ error: (error) => fail(error),
779
+ complete: () => fail(new Error("Read stream completed before prompt was found"))
780
+ });
781
+ });
782
+ }
783
+ tryConsumePrompt() {
784
+ if (typeof this.prompt === "string") {
785
+ if (this.readBuffer.length >= this.prompt.length && this.readBuffer.endsWith(this.prompt)) {
786
+ const body2 = this.readBuffer.slice(0, this.readBuffer.length - this.prompt.length);
787
+ this.readBuffer = "";
788
+ return body2.trimEnd();
789
+ }
790
+ return null;
791
+ }
792
+ if (!this.promptRegex) {
793
+ return null;
794
+ }
795
+ const match = this.promptRegex.exec(this.readBuffer);
796
+ if (!match || match.index == null) {
797
+ return null;
798
+ }
799
+ const body = this.readBuffer.slice(0, match.index);
800
+ this.readBuffer = "";
801
+ return body.trimEnd();
802
+ }
803
+ createAnchoredRegex(source) {
804
+ const flags = source.flags.replace(/g/g, "");
805
+ return new RegExp(`(?:${source.source})$`, flags);
806
+ }
807
+ isTimeoutError(error) {
808
+ return error instanceof Error && error.message.includes("Timed out waiting for prompt");
809
+ }
810
+ };
811
+ function createShellClient(client, options) {
812
+ return new ShellClientImpl(client, options);
813
+ }
581
814
  export {
582
815
  BrowserType,
583
816
  SerialError,
@@ -585,6 +818,7 @@ export {
585
818
  buildRequestOptions,
586
819
  checkBrowserSupport,
587
820
  createSerialClient,
821
+ createShellClient,
588
822
  detectBrowserType,
589
823
  hasWebSerialSupport,
590
824
  isBrowserSupported,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/client/serial-client.ts", "../src/errors/serial-error-code.ts", "../src/errors/serial-error.ts", "../src/browser/browser-detection.ts", "../src/browser/browser-support.ts", "../src/filters/build-request-options.ts", "../src/io/observable-to-writable.ts", "../src/io/readable-to-observable.ts", "../src/types/options.ts", "../src/client/index.ts"],
4
- "sourcesContent": ["import { Observable, defer, switchMap } from 'rxjs';\nimport { checkBrowserSupport } from '../browser/browser-support';\nimport { SerialError, SerialErrorCode } from '../errors/serial-error';\nimport { buildRequestOptions } from '../filters/build-request-options';\nimport { subscribeToWritable } from '../io/observable-to-writable';\nimport { readableToObservable } from '../io/readable-to-observable';\nimport {\n DEFAULT_SERIAL_CLIENT_OPTIONS,\n SerialClientOptions,\n} from '../types/options';\n\n/**\n * Internal implementation of SerialClient interface.\n *\n * This class implements the {@link SerialClient} interface and provides the actual\n * functionality for serial port communication. Users should not instantiate this class\n * directly; instead, use {@link createSerialClient} to create a SerialClient instance.\n *\n * @internal\n */\nexport class SerialClientImpl {\n /** @internal */\n private port: SerialPort | null = null;\n /** @internal */\n private isOpen = false;\n /** @internal */\n private readSubscription: { unsubscribe: () => void } | null = null;\n /** @internal */\n private writeSubscription: { unsubscribe: () => void } | null = null;\n /** @internal */\n private readonly options: Required<Omit<SerialClientOptions, 'filters'>> & {\n filters?: SerialClientOptions['filters'];\n };\n\n /**\n * Creates a new SerialClientImpl instance.\n *\n * @param options - Optional configuration options for the serial port connection\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n * @internal\n */\n constructor(options?: SerialClientOptions) {\n checkBrowserSupport();\n this.options = {\n ...DEFAULT_SERIAL_CLIENT_OPTIONS,\n ...options,\n filters: options?.filters,\n };\n }\n\n /**\n * Request a serial port from the user.\n *\n * @returns Observable that emits the selected SerialPort\n * @internal\n */\n requestPort(): Observable<SerialPort> {\n return defer(() => {\n checkBrowserSupport();\n\n return navigator.serial\n .requestPort(buildRequestOptions(this.options))\n .catch((error) => {\n if (error instanceof DOMException && error.name === 'NotFoundError') {\n throw new SerialError(\n SerialErrorCode.OPERATION_CANCELLED,\n 'Port selection was cancelled by the user',\n error,\n );\n }\n throw new SerialError(\n SerialErrorCode.PORT_NOT_AVAILABLE,\n `Failed to request port: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Get available serial ports.\n *\n * @returns Observable that emits an array of available SerialPorts\n * @internal\n */\n getPorts(): Observable<SerialPort[]> {\n return defer(() => {\n checkBrowserSupport();\n\n return navigator.serial.getPorts().catch((error) => {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_AVAILABLE,\n `Failed to get ports: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Connect to a serial port.\n *\n * @param port - Optional SerialPort to connect to. If not provided, will request one.\n * @returns Observable that completes when the port is opened\n * @internal\n */\n connect(port?: SerialPort): Observable<void> {\n checkBrowserSupport();\n\n if (this.isOpen) {\n return new Observable<void>((subscriber) => {\n subscriber.error(\n new SerialError(\n SerialErrorCode.PORT_ALREADY_OPEN,\n 'Port is already open',\n ),\n );\n });\n }\n\n const port$ = port\n ? new Observable<SerialPort>((subscriber) => {\n subscriber.next(port);\n subscriber.complete();\n })\n : this.requestPort();\n\n return port$.pipe(\n switchMap((selectedPort) => {\n return defer(() => {\n this.port = selectedPort;\n\n return this.port\n .open({\n baudRate: this.options.baudRate,\n dataBits: this.options.dataBits,\n stopBits: this.options.stopBits,\n parity: this.options.parity,\n bufferSize: this.options.bufferSize,\n flowControl: this.options.flowControl,\n })\n .then(() => {\n this.isOpen = true;\n })\n .catch((error) => {\n this.port = null;\n this.isOpen = false;\n\n if (error instanceof SerialError) {\n throw error;\n }\n\n throw new SerialError(\n SerialErrorCode.PORT_OPEN_FAILED,\n `Failed to open port: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }),\n );\n }\n\n /**\n * Disconnect from the serial port.\n *\n * @returns Observable that completes when the port is closed\n * @internal\n */\n disconnect(): Observable<void> {\n return defer(() => {\n if (!this.isOpen || !this.port) {\n return Promise.resolve();\n }\n\n // Unsubscribe from read/write streams\n if (this.readSubscription) {\n this.readSubscription.unsubscribe();\n this.readSubscription = null;\n }\n\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n this.writeSubscription = null;\n }\n\n // Close the port\n return this.port\n .close()\n .then(() => {\n this.port = null;\n this.isOpen = false;\n })\n .catch((error) => {\n this.port = null;\n this.isOpen = false;\n\n throw new SerialError(\n SerialErrorCode.CONNECTION_LOST,\n `Failed to close port: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Get an Observable that emits data read from the serial port.\n *\n * @returns Observable that emits Uint8Array chunks\n * @internal\n */\n getReadStream(): Observable<Uint8Array> {\n if (!this.isOpen || !this.port || !this.port.readable) {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_OPEN,\n 'Port is not open or readable stream is not available',\n );\n }\n\n return readableToObservable(this.port.readable);\n }\n\n /**\n * Write data to the serial port from an Observable.\n *\n * @param data$ - Observable that emits Uint8Array chunks to write\n * @returns Observable that completes when writing is finished\n * @internal\n */\n writeStream(data$: Observable<Uint8Array>): Observable<void> {\n if (!this.isOpen || !this.port || !this.port.writable) {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_OPEN,\n 'Port is not open or writable stream is not available',\n );\n }\n\n // Cancel previous write subscription if exists\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n }\n\n this.writeSubscription = subscribeToWritable(data$, this.port.writable);\n\n return new Observable<void>((subscriber) => {\n // The subscription is already active, we just need to track completion\n if (!this.writeSubscription) {\n subscriber.error(\n new SerialError(\n SerialErrorCode.WRITE_FAILED,\n 'Write subscription is not available',\n ),\n );\n return;\n }\n const originalUnsubscribe = this.writeSubscription.unsubscribe;\n\n this.writeSubscription = {\n unsubscribe: () => {\n originalUnsubscribe();\n subscriber.complete();\n },\n };\n\n // If the observable completes, complete the subscriber\n data$.subscribe({\n complete: () => {\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n this.writeSubscription = null;\n }\n subscriber.complete();\n },\n error: (error) => {\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n this.writeSubscription = null;\n }\n subscriber.error(error);\n },\n });\n });\n }\n\n /**\n * Write a single chunk of data to the serial port.\n *\n * @param data - Data to write\n * @returns Observable that completes when the data is written\n * @internal\n */\n write(data: Uint8Array): Observable<void> {\n return defer(() => {\n if (!this.isOpen || !this.port || !this.port.writable) {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_OPEN,\n 'Port is not open or writable stream is not available',\n );\n }\n\n const writer = this.port.writable.getWriter();\n return writer\n .write(data)\n .then(() => {\n writer.releaseLock();\n })\n .catch((error) => {\n writer.releaseLock();\n throw new SerialError(\n SerialErrorCode.WRITE_FAILED,\n `Failed to write data: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Check if the port is currently open.\n *\n * @returns `true` if a port is currently open, `false` otherwise\n * @internal\n */\n get connected(): boolean {\n return this.isOpen;\n }\n\n /**\n * Get the current SerialPort instance.\n *\n * @returns The current SerialPort instance, or `null` if no port is open\n * @internal\n */\n get currentPort(): SerialPort | null {\n return this.port;\n }\n}\n", "/**\n * Error codes for serial port operations.\n *\n * These codes identify specific error conditions that can occur when working with\n * serial ports. Each error code corresponds to a specific failure scenario, making\n * it easier to handle errors programmatically.\n *\n * @example\n * ```typescript\n * try {\n * await client.connect().toPromise();\n * } catch (error) {\n * if (error instanceof SerialError) {\n * switch (error.code) {\n * case SerialErrorCode.BROWSER_NOT_SUPPORTED:\n * console.error('Please use a Chromium-based browser');\n * break;\n * case SerialErrorCode.OPERATION_CANCELLED:\n * console.log('User cancelled port selection');\n * break;\n * // ... handle other error codes\n * }\n * }\n * }\n * ```\n */\nexport enum SerialErrorCode {\n /**\n * Browser does not support the Web Serial API.\n *\n * This error occurs when attempting to use serial port functionality in a browser\n * that doesn't support the Web Serial API. Only Chromium-based browsers (Chrome,\n * Edge, Opera) support this API.\n *\n * **Suggested action**: Inform the user to use a supported browser.\n */\n BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',\n\n /**\n * Serial port is not available.\n *\n * This error occurs when a requested port cannot be accessed, such as when\n * getting previously granted ports fails or when the port is already in use\n * by another application.\n *\n * **Suggested action**: Check if the port is available or being used by another application.\n */\n PORT_NOT_AVAILABLE = 'PORT_NOT_AVAILABLE',\n\n /**\n * Failed to open the serial port.\n *\n * This error occurs when the port cannot be opened, typically due to incorrect\n * connection parameters, hardware issues, or permission problems.\n *\n * **Suggested action**: Verify connection parameters and check hardware connections.\n */\n PORT_OPEN_FAILED = 'PORT_OPEN_FAILED',\n\n /**\n * Serial port is already open.\n *\n * This error occurs when attempting to open a port that is already connected.\n * Only one connection can be active at a time per SerialClient instance.\n *\n * **Suggested action**: Disconnect the current port before connecting a new one.\n */\n PORT_ALREADY_OPEN = 'PORT_ALREADY_OPEN',\n\n /**\n * Serial port is not open.\n *\n * This error occurs when attempting to read from or write to a port that hasn't\n * been opened yet. The port must be connected before performing I/O operations.\n *\n * **Suggested action**: Call {@link SerialClient.connect} before reading or writing.\n */\n PORT_NOT_OPEN = 'PORT_NOT_OPEN',\n\n /**\n * Failed to read from the serial port.\n *\n * This error occurs when reading data from the port fails, typically due to\n * connection loss, hardware issues, or stream errors.\n *\n * **Suggested action**: Check the connection and hardware, then retry the read operation.\n */\n READ_FAILED = 'READ_FAILED',\n\n /**\n * Failed to write to the serial port.\n *\n * This error occurs when writing data to the port fails, typically due to\n * connection loss, hardware issues, or stream errors.\n *\n * **Suggested action**: Check the connection and hardware, then retry the write operation.\n */\n WRITE_FAILED = 'WRITE_FAILED',\n\n /**\n * Serial port connection was lost.\n *\n * This error occurs when the connection to the serial port is unexpectedly\n * terminated, such as when the device is disconnected or the port is closed\n * by another process.\n *\n * **Suggested action**: Check the physical connection and reconnect if needed.\n */\n CONNECTION_LOST = 'CONNECTION_LOST',\n\n /**\n * Invalid filter options provided.\n *\n * This error occurs when port filter options are invalid, such as when\n * filter values are out of range or missing required fields.\n *\n * **Suggested action**: Verify filter options match the expected format and value ranges.\n */\n INVALID_FILTER_OPTIONS = 'INVALID_FILTER_OPTIONS',\n\n /**\n * Operation was cancelled by the user.\n *\n * This error occurs when the user cancels a port selection dialog or aborts\n * an operation before it completes.\n *\n * **Suggested action**: This is a normal condition - no action required, but you may want\n * to inform the user that the operation was cancelled.\n */\n OPERATION_CANCELLED = 'OPERATION_CANCELLED',\n\n /**\n * Unknown error occurred.\n *\n * This error code is used for errors that don't fit into any other category.\n * The original error details may be available in the error's message or originalError property.\n *\n * **Suggested action**: Check the error message and originalError for more details.\n */\n UNKNOWN = 'UNKNOWN',\n}\n", "import { SerialErrorCode } from './serial-error-code';\n\n// Re-export SerialErrorCode for convenience\nexport { SerialErrorCode };\n\n/**\n * Custom error class for serial port operations.\n *\n * This error class extends the standard Error class and includes additional information\n * about the type of error that occurred. It provides an error code for programmatic\n * error handling and may include the original error that caused the failure.\n *\n * @example\n * ```typescript\n * try {\n * await client.connect().toPromise();\n * } catch (error) {\n * if (error instanceof SerialError) {\n * console.error(`Error code: ${error.code}`);\n * console.error(`Message: ${error.message}`);\n * if (error.originalError) {\n * console.error(`Original error:`, error.originalError);\n * }\n *\n * // Check specific error code\n * if (error.is(SerialErrorCode.BROWSER_NOT_SUPPORTED)) {\n * // Handle browser not supported\n * }\n * }\n * }\n * ```\n */\nexport class SerialError extends Error {\n /**\n * The error code identifying the type of error that occurred.\n *\n * Use this code to programmatically handle specific error conditions.\n *\n * @see {@link SerialErrorCode} for all available error codes\n */\n public readonly code: SerialErrorCode;\n\n /**\n * The original error that caused this SerialError, if available.\n *\n * This property contains the underlying error (e.g., DOMException, TypeError)\n * that was wrapped in this SerialError. It may be undefined if no original error exists.\n */\n public readonly originalError?: Error;\n\n /**\n * Creates a new SerialError instance.\n *\n * @param code - The error code identifying the type of error\n * @param message - A human-readable error message\n * @param originalError - The original error that caused this SerialError, if any\n */\n constructor(code: SerialErrorCode, message: string, originalError?: Error) {\n super(message);\n this.name = 'SerialError';\n this.code = code;\n this.originalError = originalError;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if ((Error as any).captureStackTrace) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (Error as any).captureStackTrace(this, SerialError);\n }\n }\n\n /**\n * Check if the error matches a specific error code.\n *\n * This is a convenience method for checking the error code without directly\n * comparing the code property.\n *\n * @param code - The error code to check against\n * @returns `true` if this error's code matches the provided code, `false` otherwise\n *\n * @example\n * ```typescript\n * if (error.is(SerialErrorCode.PORT_NOT_OPEN)) {\n * // Handle port not open error\n * }\n * ```\n */\n public is(code: SerialErrorCode): boolean {\n return this.code === code;\n }\n}\n", "/**\n * Browser type enumeration for identifying the browser environment.\n *\n * This enum is used to identify the specific browser type, which is useful for\n * browser-specific behavior or error messages.\n *\n * @example\n * ```typescript\n * const browserType = detectBrowserType();\n * if (browserType === BrowserType.CHROME) {\n * console.log('Running in Chrome');\n * }\n * ```\n */\nexport enum BrowserType {\n /** Google Chrome browser */\n CHROME = 'chrome',\n /** Microsoft Edge browser */\n EDGE = 'edge',\n /** Opera browser */\n OPERA = 'opera',\n /** Unknown or unsupported browser */\n UNKNOWN = 'unknown',\n}\n\n/**\n * Feature detection for Web Serial API.\n *\n * Checks if the browser supports the Web Serial API by verifying the presence\n * of `navigator.serial`. This is a non-throwing check that returns `false` if\n * the API is not available.\n *\n * Note: This function only checks for API availability, not whether the browser\n * type is supported. For a throwing check that provides better error messages,\n * use {@link checkBrowserSupport}.\n *\n * @returns `true` if the Web Serial API is available, `false` otherwise\n *\n * @example\n * ```typescript\n * if (hasWebSerialSupport()) {\n * // Safe to use serial port functionality\n * const client = createSerialClient();\n * } else {\n * console.error('Web Serial API is not supported');\n * }\n * ```\n *\n * @see {@link checkBrowserSupport} for a throwing version with better error messages\n * @see {@link isBrowserSupported} for an alias to this function\n */\nexport function hasWebSerialSupport(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n 'serial' in navigator &&\n navigator.serial !== undefined &&\n navigator.serial !== null\n );\n}\n\n/**\n * Detect browser type from the user agent string.\n *\n * Analyzes the browser's user agent string to identify the browser type.\n * This function is useful for providing browser-specific functionality or\n * error messages.\n *\n * @returns The detected {@link BrowserType}, or {@link BrowserType.UNKNOWN} if the browser cannot be identified\n *\n * @example\n * ```typescript\n * const browserType = detectBrowserType();\n * switch (browserType) {\n * case BrowserType.CHROME:\n * console.log('Running in Chrome');\n * break;\n * case BrowserType.EDGE:\n * console.log('Running in Edge');\n * break;\n * case BrowserType.OPERA:\n * console.log('Running in Opera');\n * break;\n * default:\n * console.log('Unknown browser');\n * }\n * ```\n *\n * @see {@link isChromiumBased} for checking if the browser is Chromium-based\n */\nexport function detectBrowserType(): BrowserType {\n if (typeof navigator === 'undefined' || !navigator.userAgent) {\n return BrowserType.UNKNOWN;\n }\n\n const ua = navigator.userAgent.toLowerCase();\n\n if (ua.includes('edg/')) {\n return BrowserType.EDGE;\n }\n\n if (ua.includes('opr/') || ua.includes('opera/')) {\n return BrowserType.OPERA;\n }\n\n if (ua.includes('chrome/')) {\n return BrowserType.CHROME;\n }\n\n return BrowserType.UNKNOWN;\n}\n\n/**\n * Check if the browser is Chromium-based.\n *\n * Determines if the current browser is based on Chromium, which includes\n * Chrome, Edge, and Opera. These browsers support the Web Serial API.\n *\n * @returns `true` if the browser is Chromium-based (Chrome, Edge, or Opera), `false` otherwise\n *\n * @example\n * ```typescript\n * if (isChromiumBased()) {\n * // Browser supports Web Serial API\n * const client = createSerialClient();\n * } else {\n * console.error('Please use a Chromium-based browser (Chrome, Edge, or Opera)');\n * }\n * ```\n *\n * @see {@link detectBrowserType} for identifying the specific browser type\n * @see {@link hasWebSerialSupport} for checking Web Serial API availability\n */\nexport function isChromiumBased(): boolean {\n const browserType = detectBrowserType();\n return (\n browserType === BrowserType.CHROME ||\n browserType === BrowserType.EDGE ||\n browserType === BrowserType.OPERA\n );\n}\n", "import { SerialError, SerialErrorCode } from '../errors/serial-error';\nimport {\n BrowserType,\n detectBrowserType,\n hasWebSerialSupport,\n} from './browser-detection';\n\n/**\n * Check if the browser supports the Web Serial API, throwing an error if not supported.\n *\n * This function performs a feature detection check and throws a {@link SerialError}\n * with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the Web Serial API\n * is not available. The error message includes the detected browser type for better\n * user feedback.\n *\n * This is the recommended way to check browser support before using serial port\n * functionality, as it provides clear error messages.\n *\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED}\n * if the browser doesn't support the Web Serial API\n *\n * @example\n * ```typescript\n * try {\n * checkBrowserSupport();\n * // Safe to use serial port functionality\n * const client = createSerialClient();\n * } catch (error) {\n * if (error instanceof SerialError && error.code === SerialErrorCode.BROWSER_NOT_SUPPORTED) {\n * console.error(error.message);\n * // Show user-friendly message: \"Please use a Chromium-based browser...\"\n * }\n * }\n * ```\n *\n * @see {@link isBrowserSupported} for a non-throwing version that returns a boolean\n * @see {@link hasWebSerialSupport} for the underlying feature detection function\n */\nexport function checkBrowserSupport(): void {\n if (!hasWebSerialSupport()) {\n const browserType = detectBrowserType();\n const browserName =\n browserType === BrowserType.UNKNOWN\n ? 'your browser'\n : browserType.toUpperCase();\n\n throw new SerialError(\n SerialErrorCode.BROWSER_NOT_SUPPORTED,\n `Web Serial API is not supported in ${browserName}. Please use a Chromium-based browser (Chrome, Edge, or Opera).`,\n );\n }\n}\n\n/**\n * Check if the browser supports the Web Serial API (non-throwing version).\n *\n * This is a convenience function that returns a boolean indicating whether\n * the Web Serial API is available. Unlike {@link checkBrowserSupport}, this\n * function does not throw an error if the API is not available.\n *\n * @returns `true` if the Web Serial API is supported, `false` otherwise\n *\n * @example\n * ```typescript\n * if (isBrowserSupported()) {\n * const client = createSerialClient();\n * // Use serial port functionality\n * } else {\n * console.error('Web Serial API is not supported in this browser');\n * // Show fallback UI or message\n * }\n * ```\n *\n * @see {@link checkBrowserSupport} for a throwing version with better error messages\n * @see {@link hasWebSerialSupport} which this function calls internally\n */\nexport function isBrowserSupported(): boolean {\n return hasWebSerialSupport();\n}\n", "import { SerialError, SerialErrorCode } from '../errors/serial-error';\nimport { SerialClientOptions } from '../types/options';\n\n/**\n * Build SerialPortRequestOptions from SerialClientOptions.\n *\n * This utility function converts filter options from {@link SerialClientOptions} into\n * the format expected by the Web Serial API's `navigator.serial.requestPort()` method.\n * It validates the filter options to ensure they are valid before returning them.\n *\n * If no filters are provided in the options, this function returns `undefined`, which\n * allows the port selection dialog to show all available ports.\n *\n * @param options - Optional SerialClientOptions containing filter configuration\n * @returns SerialPortRequestOptions object with validated filters, or `undefined` if no filters are provided\n * @throws {@link SerialError} with code {@link SerialErrorCode.INVALID_FILTER_OPTIONS} if filter validation fails\n *\n * @example\n * ```typescript\n * // With filters\n * const options = {\n * baudRate: 9600,\n * filters: [\n * { usbVendorId: 0x1234 },\n * { usbVendorId: 0x5678, usbProductId: 0x9abc },\n * ],\n * };\n * const requestOptions = buildRequestOptions(options);\n * // Returns: { filters: [...] }\n *\n * // Without filters\n * const requestOptions = buildRequestOptions({ baudRate: 9600 });\n * // Returns: undefined\n *\n * // Invalid filter (will throw)\n * try {\n * buildRequestOptions({ filters: [{ usbVendorId: -1 }] });\n * } catch (error) {\n * // SerialError with code INVALID_FILTER_OPTIONS\n * }\n * ```\n */\nexport function buildRequestOptions(\n options?: SerialClientOptions,\n): SerialPortRequestOptions | undefined {\n if (!options || !options.filters || options.filters.length === 0) {\n return undefined;\n }\n\n // Validate filters\n for (const filter of options.filters) {\n if (!filter.usbVendorId && !filter.usbProductId) {\n throw new SerialError(\n SerialErrorCode.INVALID_FILTER_OPTIONS,\n 'Filter must have at least usbVendorId or usbProductId',\n );\n }\n\n if (filter.usbVendorId !== undefined) {\n if (\n !Number.isInteger(filter.usbVendorId) ||\n filter.usbVendorId < 0 ||\n filter.usbVendorId > 0xffff\n ) {\n throw new SerialError(\n SerialErrorCode.INVALID_FILTER_OPTIONS,\n `Invalid usbVendorId: ${filter.usbVendorId}. Must be an integer between 0 and 65535.`,\n );\n }\n }\n\n if (filter.usbProductId !== undefined) {\n if (\n !Number.isInteger(filter.usbProductId) ||\n filter.usbProductId < 0 ||\n filter.usbProductId > 0xffff\n ) {\n throw new SerialError(\n SerialErrorCode.INVALID_FILTER_OPTIONS,\n `Invalid usbProductId: ${filter.usbProductId}. Must be an integer between 0 and 65535.`,\n );\n }\n }\n }\n\n return {\n filters: options.filters,\n };\n}\n", "import { Observable } from 'rxjs';\nimport { SerialError, SerialErrorCode } from '../errors/serial-error';\n\n/**\n * Convert an RxJS Observable to a WritableStream.\n *\n * This utility function converts an RxJS Observable into a Web Streams API WritableStream.\n * Values emitted by the Observable will be written to the returned WritableStream. The stream\n * will close when the Observable completes or abort if the Observable errors.\n *\n * Note: This function creates a new WritableStream. For directly subscribing to an Observable\n * and writing to an existing WritableStream, use {@link subscribeToWritable} instead.\n *\n * @param observable - The Observable to convert to a WritableStream\n * @returns A WritableStream that writes Uint8Array chunks emitted by the Observable\n *\n * @example\n * ```typescript\n * const data$ = from([\n * new TextEncoder().encode('Hello'),\n * new TextEncoder().encode('World'),\n * ]);\n *\n * const writableStream = observableToWritable(data$);\n * // Use the writable stream with other Web Streams APIs\n * ```\n */\nexport function observableToWritable(\n observable: Observable<Uint8Array>,\n): WritableStream<Uint8Array> {\n let writer: WritableStreamDefaultWriter<Uint8Array> | null = null;\n let subscription: { unsubscribe: () => void } | null = null;\n let stream: WritableStream<Uint8Array> | null = null;\n\n stream = new WritableStream<Uint8Array>({\n async start() {\n if (!stream) {\n return;\n }\n writer = stream.getWriter();\n\n subscription = observable.subscribe({\n next: async (chunk) => {\n if (writer) {\n try {\n await writer.write(chunk);\n } catch (error) {\n subscription?.unsubscribe();\n if (writer) {\n writer.releaseLock();\n }\n throw error;\n }\n }\n },\n error: async (error) => {\n if (writer) {\n try {\n await writer.abort(error);\n } catch {\n // Ignore abort errors\n } finally {\n writer.releaseLock();\n writer = null;\n }\n }\n },\n complete: async () => {\n if (writer) {\n try {\n await writer.close();\n } catch {\n // Ignore close errors\n } finally {\n writer.releaseLock();\n writer = null;\n }\n }\n },\n });\n },\n\n abort(reason) {\n if (subscription) {\n subscription.unsubscribe();\n subscription = null;\n }\n if (writer) {\n writer.abort(reason).catch(() => {\n // Ignore abort errors\n });\n writer.releaseLock();\n writer = null;\n }\n },\n });\n\n return stream;\n}\n\n/**\n * Subscribe to an Observable and write its values to a WritableStream.\n *\n * This utility function subscribes to an RxJS Observable and writes all emitted values\n * to the provided WritableStream. This is commonly used to write Observable data to a\n * serial port's writable stream.\n *\n * The function returns a subscription object that can be used to unsubscribe and clean up.\n * When unsubscribed, the writer lock will be released properly.\n *\n * @param observable - The Observable to subscribe to and read data from\n * @param stream - The WritableStream to write data to\n * @returns A subscription object with an `unsubscribe` method for cleanup\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing to the stream fails\n *\n * @example\n * ```typescript\n * const data$ = from([\n * new TextEncoder().encode('Hello'),\n * new TextEncoder().encode('World'),\n * ]);\n *\n * const subscription = subscribeToWritable(data$, port.writable);\n *\n * // Later, to cancel and clean up:\n * subscription.unsubscribe();\n *\n * // Use with RxJS operators\n * const processedData$ = of('Hello, Serial!').pipe(\n * map((text) => new TextEncoder().encode(text))\n * );\n *\n * subscribeToWritable(processedData$, port.writable);\n * ```\n */\nexport function subscribeToWritable(\n observable: Observable<Uint8Array>,\n stream: WritableStream<Uint8Array>,\n): { unsubscribe: () => void } {\n const writer = stream.getWriter();\n\n // Define error handler separately so we can call it directly\n const errorHandler = async (error: unknown) => {\n try {\n await writer.abort(error);\n } catch {\n // Ignore abort errors\n } finally {\n writer.releaseLock();\n }\n };\n\n const subscription = observable.subscribe({\n next: async (chunk) => {\n try {\n await writer.write(chunk);\n } catch (error) {\n subscription.unsubscribe();\n writer.releaseLock();\n // Convert write error to SerialError and pass to error handler\n const serialError = new SerialError(\n SerialErrorCode.WRITE_FAILED,\n `Failed to write to stream: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n // Manually trigger error handler to avoid unhandled rejection\n await errorHandler(serialError);\n }\n },\n error: errorHandler,\n complete: async () => {\n try {\n await writer.close();\n } catch {\n // Ignore close errors\n } finally {\n writer.releaseLock();\n }\n },\n });\n\n return {\n unsubscribe: () => {\n subscription.unsubscribe();\n writer.releaseLock();\n },\n };\n}\n", "import { Observable } from 'rxjs';\nimport { SerialError, SerialErrorCode } from '../errors/serial-error';\n\n/**\n * Convert a ReadableStream to an RxJS Observable.\n *\n * This utility function converts a Web Streams API ReadableStream into an RxJS Observable,\n * allowing you to use RxJS operators with stream data. The Observable will emit Uint8Array\n * chunks as they are read from the stream, complete when the stream ends, or error if\n * the stream encounters an error.\n *\n * The returned Observable handles stream cleanup automatically - if you unsubscribe before\n * the stream completes, the reader lock will be released properly.\n *\n * @param stream - The ReadableStream to convert to an Observable\n * @returns An Observable that emits Uint8Array chunks from the stream\n * @throws {@link SerialError} with code {@link SerialErrorCode.READ_FAILED} if reading from the stream fails\n *\n * @example\n * ```typescript\n * // Convert a serial port's readable stream to an Observable\n * const readable$ = readableToObservable(port.readable);\n *\n * readable$.subscribe({\n * next: (chunk) => {\n * console.log('Received chunk:', chunk);\n * },\n * complete: () => {\n * console.log('Stream completed');\n * },\n * error: (error) => {\n * console.error('Stream error:', error);\n * },\n * });\n *\n * // Use with RxJS operators\n * readableToObservable(port.readable)\n * .pipe(\n * map((chunk) => new TextDecoder().decode(chunk)),\n * filter((text) => text.includes('OK'))\n * )\n * .subscribe((text) => console.log('Filtered text:', text));\n * ```\n */\nexport function readableToObservable(\n stream: ReadableStream<Uint8Array>,\n): Observable<Uint8Array> {\n return new Observable<Uint8Array>((subscriber) => {\n const reader = stream.getReader();\n\n const pump = async (): Promise<void> => {\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n subscriber.complete();\n break;\n }\n\n if (value) {\n subscriber.next(value);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n subscriber.error(\n new SerialError(\n SerialErrorCode.READ_FAILED,\n `Failed to read from stream: ${error.message}`,\n error,\n ),\n );\n } else {\n subscriber.error(\n new SerialError(\n SerialErrorCode.READ_FAILED,\n 'Failed to read from stream: Unknown error',\n error as Error,\n ),\n );\n }\n } finally {\n reader.releaseLock();\n }\n };\n\n pump().catch((error) => {\n if (!subscriber.closed) {\n subscriber.error(error);\n }\n });\n\n // Cleanup function\n return () => {\n reader.releaseLock();\n };\n });\n}\n", "/**\n * Options for creating a SerialClient instance.\n *\n * These options configure the serial port connection parameters. All properties are optional\n * and will use default values if not specified. See {@link DEFAULT_SERIAL_CLIENT_OPTIONS} for\n * the default values used.\n *\n * @example\n * ```typescript\n * const client = createSerialClient({\n * baudRate: 115200,\n * dataBits: 8,\n * stopBits: 1,\n * parity: 'none',\n * flowControl: 'none',\n * filters: [{ usbVendorId: 0x1234, usbProductId: 0x5678 }],\n * });\n * ```\n */\nexport interface SerialClientOptions {\n /**\n * Baud rate for the serial port connection (bits per second).\n *\n * Common values include 9600, 19200, 38400, 57600, 115200, etc.\n * Must match the baud rate configured on the connected device.\n *\n * @default 9600\n */\n baudRate?: number;\n\n /**\n * Number of data bits per character (7 or 8).\n *\n * - `7`: Seven data bits (used with parity)\n * - `8`: Eight data bits (most common, used without parity)\n *\n * @default 8\n */\n dataBits?: 7 | 8;\n\n /**\n * Number of stop bits (1 or 2).\n *\n * - `1`: One stop bit (most common)\n * - `2`: Two stop bits (less common, used for slower devices)\n *\n * @default 1\n */\n stopBits?: 1 | 2;\n\n /**\n * Parity checking mode.\n *\n * - `'none'`: No parity checking (most common)\n * - `'even'`: Even parity\n * - `'odd'`: Odd parity\n *\n * @default 'none'\n */\n parity?: 'none' | 'even' | 'odd';\n\n /**\n * Buffer size for reading data from the serial port, in bytes.\n *\n * This determines how much data can be buffered before it needs to be read.\n * Larger buffers can improve performance but use more memory.\n *\n * @default 255\n */\n bufferSize?: number;\n\n /**\n * Flow control mode.\n *\n * - `'none'`: No flow control (most common)\n * - `'hardware'`: Hardware flow control (RTS/CTS)\n *\n * @default 'none'\n */\n flowControl?: 'none' | 'hardware';\n\n /**\n * Filters for port selection when requesting a port.\n *\n * When specified, the port selection dialog will only show devices matching\n * these filters. Each filter can specify `usbVendorId` and/or `usbProductId`\n * to filter by USB device identifiers.\n *\n * @example\n * ```typescript\n * filters: [\n * { usbVendorId: 0x1234 },\n * { usbVendorId: 0x1234, usbProductId: 0x5678 },\n * ]\n * ```\n *\n * @see {@link SerialPortFilter} for the filter structure\n */\n filters?: SerialPortFilter[];\n}\n\n/**\n * Default options for SerialClient instances.\n *\n * These are the default values used when creating a SerialClient if no options\n * are provided or if specific options are omitted. The values are chosen to work\n * with most common serial devices.\n *\n * @see {@link SerialClientOptions} for details on each option\n */\nexport const DEFAULT_SERIAL_CLIENT_OPTIONS: Required<\n Omit<SerialClientOptions, 'filters'>\n> & { filters?: SerialPortFilter[] } = {\n baudRate: 9600,\n dataBits: 8,\n stopBits: 1,\n parity: 'none',\n bufferSize: 255,\n flowControl: 'none',\n filters: undefined,\n};\n", "import { Observable } from 'rxjs';\nimport { SerialClientOptions } from '../types/options';\nimport { SerialClientImpl } from './serial-client';\n\n/**\n * SerialClient interface for interacting with serial ports using RxJS Observables.\n *\n * This interface provides a reactive API for serial port communication, allowing you to\n * connect to serial devices, read and write data using RxJS Observables.\n *\n * @example\n * ```typescript\n * const client = createSerialClient({ baudRate: 9600 });\n *\n * // Connect to a port\n * client.connect().subscribe({\n * next: () => {\n * console.log('Connected!');\n *\n * // Read data\n * client.getReadStream().subscribe({\n * next: (data) => console.log('Received:', data),\n * });\n *\n * // Write data\n * const encoder = new TextEncoder();\n * client.write(encoder.encode('Hello')).subscribe();\n * },\n * error: (error) => console.error('Connection error:', error),\n * });\n * ```\n */\nexport interface SerialClient {\n /**\n * Request a serial port from the user.\n *\n * This method opens the browser's port selection dialog and returns an Observable\n * that emits the selected SerialPort when the user chooses a port.\n *\n * @returns An Observable that emits the selected {@link SerialPort} when the user selects a port\n * @throws {@link SerialError} with code {@link SerialErrorCode.OPERATION_CANCELLED} if the user cancels the selection\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_AVAILABLE} if the port request fails\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * client.requestPort().subscribe({\n * next: (port) => console.log('Selected port:', port),\n * error: (error) => console.error('Port selection failed:', error),\n * });\n * ```\n */\n requestPort(): Observable<SerialPort>;\n\n /**\n * Get available serial ports that have been previously granted access.\n *\n * This method returns an Observable that emits an array of SerialPort instances\n * that the user has previously granted access to in this browser session.\n *\n * @returns An Observable that emits an array of available {@link SerialPort} instances\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_AVAILABLE} if getting ports fails\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * client.getPorts().subscribe({\n * next: (ports) => {\n * console.log(`Found ${ports.length} available ports`);\n * if (ports.length > 0) {\n * client.connect(ports[0]).subscribe();\n * }\n * },\n * });\n * ```\n */\n getPorts(): Observable<SerialPort[]>;\n\n /**\n * Connect to a serial port.\n *\n * Opens the specified port (or requests one if not provided) and configures it\n * with the options passed to {@link createSerialClient}. The port must be connected\n * before reading or writing data.\n *\n * @param port - Optional {@link SerialPort} to connect to. If not provided, will call {@link requestPort} to prompt the user.\n * @returns An Observable that completes when the port is successfully opened\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_ALREADY_OPEN} if a port is already open\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_OPEN_FAILED} if opening the port fails\n * @throws {@link SerialError} with code {@link SerialErrorCode.OPERATION_CANCELLED} if the user cancels port selection\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * // Connect by requesting a port\n * client.connect().subscribe({\n * next: () => console.log('Connected!'),\n * error: (error) => console.error('Connection failed:', error),\n * });\n *\n * // Connect to a specific port\n * client.getPorts().subscribe({\n * next: (ports) => {\n * if (ports.length > 0) {\n * client.connect(ports[0]).subscribe();\n * }\n * },\n * });\n * ```\n */\n connect(port?: SerialPort): Observable<void>;\n\n /**\n * Disconnect from the serial port.\n *\n * Closes the currently open port and stops all active read/write streams.\n * This method is safe to call even if no port is currently open.\n *\n * @returns An Observable that completes when the port is successfully closed\n * @throws {@link SerialError} with code {@link SerialErrorCode.CONNECTION_LOST} if closing the port fails\n *\n * @example\n * ```typescript\n * client.disconnect().subscribe({\n * next: () => console.log('Disconnected'),\n * error: (error) => console.error('Disconnect failed:', error),\n * });\n * ```\n */\n disconnect(): Observable<void>;\n\n /**\n * Get an Observable that emits data read from the serial port.\n *\n * Returns an Observable stream that emits Uint8Array chunks as data is received\n * from the serial port. The stream will continue until the port is disconnected\n * or an error occurs.\n *\n * @returns An Observable that emits Uint8Array chunks containing data read from the serial port\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n *\n * @example\n * ```typescript\n * client.getReadStream().subscribe({\n * next: (data) => {\n * const text = new TextDecoder().decode(data);\n * console.log('Received:', text);\n * },\n * error: (error) => console.error('Read error:', error),\n * });\n * ```\n */\n getReadStream(): Observable<Uint8Array>;\n\n /**\n * Write data to the serial port from an Observable.\n *\n * Writes data from an Observable stream to the serial port. The Observable should\n * emit Uint8Array chunks that will be written sequentially to the port. If a previous\n * write stream is active, it will be cancelled before starting the new one.\n *\n * @param data$ - Observable that emits Uint8Array chunks to write to the serial port\n * @returns An Observable that completes when all data has been written and the stream completes\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing fails\n *\n * @example\n * ```typescript\n * const data$ = from([\n * new TextEncoder().encode('Hello'),\n * new TextEncoder().encode('World'),\n * ]);\n *\n * client.writeStream(data$).subscribe({\n * next: () => console.log('Writing...'),\n * complete: () => console.log('All data written'),\n * error: (error) => console.error('Write error:', error),\n * });\n * ```\n */\n writeStream(data$: Observable<Uint8Array>): Observable<void>;\n\n /**\n * Write a single chunk of data to the serial port.\n *\n * Writes a single Uint8Array chunk to the serial port. For writing multiple chunks,\n * consider using {@link writeStream} with an Observable instead.\n *\n * @param data - Uint8Array data to write to the serial port\n * @returns An Observable that completes when the data has been written\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing fails\n *\n * @example\n * ```typescript\n * const encoder = new TextEncoder();\n * const data = encoder.encode('Hello, Serial!');\n *\n * client.write(data).subscribe({\n * next: () => console.log('Data written'),\n * error: (error) => console.error('Write error:', error),\n * });\n * ```\n */\n write(data: Uint8Array): Observable<void>;\n\n /**\n * Check if the port is currently open and connected.\n *\n * @returns `true` if a port is currently open, `false` otherwise\n */\n readonly connected: boolean;\n\n /**\n * Get the current SerialPort instance.\n *\n * Returns the currently connected SerialPort instance, or `null` if no port is open.\n * This allows direct access to the underlying Web Serial API SerialPort object if needed.\n *\n * @returns The current {@link SerialPort} instance, or `null` if no port is open\n */\n readonly currentPort: SerialPort | null;\n}\n\n/**\n * Create a new SerialClient instance for interacting with serial ports.\n *\n * This is the main entry point for creating a serial client. The client provides\n * a reactive RxJS-based API for connecting to serial ports and reading/writing data.\n *\n * @param options - Optional configuration options for the serial port connection.\n * If not provided, default values will be used (9600 baud, 8 data bits, etc.)\n * @returns A new {@link SerialClient} instance\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * // Create a client with default settings (9600 baud)\n * const client = createSerialClient();\n *\n * // Create a client with custom settings\n * const client = createSerialClient({\n * baudRate: 115200,\n * dataBits: 8,\n * stopBits: 1,\n * parity: 'none',\n * filters: [{ usbVendorId: 0x1234 }],\n * });\n *\n * // Check browser support before creating a client\n * import { isBrowserSupported } from '@gurezo/web-serial-rxjs';\n *\n * if (!isBrowserSupported()) {\n * console.error('Web Serial API is not supported');\n * } else {\n * const client = createSerialClient();\n * }\n * ```\n */\nexport function createSerialClient(\n options?: SerialClientOptions,\n): SerialClient {\n return new SerialClientImpl(options);\n}\n"],
5
- "mappings": "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",
6
- "names": ["Observable", "SerialErrorCode", "BrowserType", "Observable"]
3
+ "sources": ["../src/client/serial-client.ts", "../src/errors/serial-error-code.ts", "../src/errors/serial-error.ts", "../src/browser/browser-detection.ts", "../src/browser/browser-support.ts", "../src/filters/build-request-options.ts", "../src/io/observable-to-writable.ts", "../src/io/readable-to-observable.ts", "../src/types/options.ts", "../src/client/index.ts", "../src/shell/create-shell-client.ts"],
4
+ "sourcesContent": ["import {\n BehaviorSubject,\n Observable,\n Subject,\n defer,\n map,\n share,\n switchMap,\n} from 'rxjs';\nimport { checkBrowserSupport } from '../browser/browser-support';\nimport { SerialError, SerialErrorCode } from '../errors/serial-error';\nimport { buildRequestOptions } from '../filters/build-request-options';\nimport { subscribeToWritable } from '../io/observable-to-writable';\nimport { readableToObservable } from '../io/readable-to-observable';\nimport {\n DEFAULT_SERIAL_CLIENT_OPTIONS,\n SerialClientOptions,\n} from '../types/options';\n\n/**\n * Internal implementation of SerialClient interface.\n *\n * This class implements the {@link SerialClient} interface and provides the actual\n * functionality for serial port communication. Users should not instantiate this class\n * directly; instead, use {@link createSerialClient} to create a SerialClient instance.\n *\n * @internal\n */\nexport class SerialClientImpl {\n /** @internal */\n private port: SerialPort | null = null;\n /** @internal */\n private isOpen = false;\n /** @internal */\n private readSubscription: { unsubscribe: () => void } | null = null;\n /** @internal */\n private writeSubscription: { unsubscribe: () => void } | null = null;\n /** @internal */\n private sharedReadStream$: Observable<Uint8Array> | null = null;\n /** @internal */\n private textEncoder = new TextEncoder();\n /** @internal */\n private textDecoder = new TextDecoder();\n /** @internal */\n private readonly connectedState$ = new BehaviorSubject<boolean>(false);\n /** @internal */\n private readonly connectionEventsSubject$ = new Subject<\n 'connected' | 'disconnected'\n >();\n /** @internal */\n private readonly options: Required<Omit<SerialClientOptions, 'filters'>> & {\n filters?: SerialClientOptions['filters'];\n };\n\n /**\n * Creates a new SerialClientImpl instance.\n *\n * @param options - Optional configuration options for the serial port connection\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n * @internal\n */\n constructor(options?: SerialClientOptions) {\n checkBrowserSupport();\n this.options = {\n ...DEFAULT_SERIAL_CLIENT_OPTIONS,\n ...options,\n filters: options?.filters,\n };\n }\n\n /**\n * Request a serial port from the user.\n *\n * @returns Observable that emits the selected SerialPort\n * @internal\n */\n requestPort(): Observable<SerialPort> {\n return defer(() => {\n checkBrowserSupport();\n\n return navigator.serial\n .requestPort(buildRequestOptions(this.options))\n .catch((error) => {\n if (error instanceof DOMException && error.name === 'NotFoundError') {\n throw new SerialError(\n SerialErrorCode.OPERATION_CANCELLED,\n 'Port selection was cancelled by the user',\n error,\n );\n }\n throw new SerialError(\n SerialErrorCode.PORT_NOT_AVAILABLE,\n `Failed to request port: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Get available serial ports.\n *\n * @returns Observable that emits an array of available SerialPorts\n * @internal\n */\n getPorts(): Observable<SerialPort[]> {\n return defer(() => {\n checkBrowserSupport();\n\n return navigator.serial.getPorts().catch((error) => {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_AVAILABLE,\n `Failed to get ports: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Connect to a serial port.\n *\n * @param port - Optional SerialPort to connect to. If not provided, will request one.\n * @returns Observable that completes when the port is opened\n * @internal\n */\n connect(port?: SerialPort): Observable<void> {\n checkBrowserSupport();\n\n if (this.isOpen) {\n return new Observable<void>((subscriber) => {\n subscriber.error(\n new SerialError(\n SerialErrorCode.PORT_ALREADY_OPEN,\n 'Port is already open',\n ),\n );\n });\n }\n\n const port$ = port\n ? new Observable<SerialPort>((subscriber) => {\n subscriber.next(port);\n subscriber.complete();\n })\n : this.requestPort();\n\n return port$.pipe(\n switchMap((selectedPort) => {\n return defer(() => {\n this.port = selectedPort;\n\n return this.port\n .open({\n baudRate: this.options.baudRate,\n dataBits: this.options.dataBits,\n stopBits: this.options.stopBits,\n parity: this.options.parity,\n bufferSize: this.options.bufferSize,\n flowControl: this.options.flowControl,\n })\n .then(() => {\n this.isOpen = true;\n this.connectedState$.next(true);\n this.connectionEventsSubject$.next('connected');\n })\n .catch((error) => {\n this.port = null;\n this.isOpen = false;\n\n if (error instanceof SerialError) {\n throw error;\n }\n\n throw new SerialError(\n SerialErrorCode.PORT_OPEN_FAILED,\n `Failed to open port: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }),\n );\n }\n\n /**\n * Disconnect from the serial port.\n *\n * @returns Observable that completes when the port is closed\n * @internal\n */\n disconnect(): Observable<void> {\n return defer(() => {\n if (!this.isOpen || !this.port) {\n return Promise.resolve();\n }\n\n // Unsubscribe from read/write streams\n if (this.readSubscription) {\n this.readSubscription.unsubscribe();\n this.readSubscription = null;\n }\n this.sharedReadStream$ = null;\n this.textDecoder = new TextDecoder();\n\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n this.writeSubscription = null;\n }\n\n // Close the port\n return this.port\n .close()\n .then(() => {\n this.port = null;\n this.isOpen = false;\n this.connectedState$.next(false);\n this.connectionEventsSubject$.next('disconnected');\n })\n .catch((error) => {\n this.port = null;\n this.isOpen = false;\n this.connectedState$.next(false);\n this.connectionEventsSubject$.next('disconnected');\n\n throw new SerialError(\n SerialErrorCode.CONNECTION_LOST,\n `Failed to close port: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Get an Observable that emits data read from the serial port.\n *\n * @returns Observable that emits Uint8Array chunks\n * @internal\n */\n getReadStream(): Observable<Uint8Array> {\n if (!this.isOpen || !this.port || !this.port.readable) {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_OPEN,\n 'Port is not open or readable stream is not available',\n );\n }\n\n if (!this.sharedReadStream$) {\n this.sharedReadStream$ = readableToObservable(this.port.readable).pipe(\n share({\n resetOnError: true,\n resetOnComplete: true,\n resetOnRefCountZero: true,\n }),\n );\n }\n\n return this.sharedReadStream$;\n }\n\n /**\n * Get an Observable that emits decoded text from the serial port.\n *\n * @returns Observable that emits text chunks\n * @internal\n */\n getReadStreamAsText(): Observable<string> {\n return this.getReadStream().pipe(\n map((chunk) => this.textDecoder.decode(chunk, { stream: true })),\n );\n }\n\n /**\n * Write data to the serial port from an Observable.\n *\n * @param data$ - Observable that emits Uint8Array chunks to write\n * @returns Observable that completes when writing is finished\n * @internal\n */\n writeStream(data$: Observable<Uint8Array>): Observable<void> {\n if (!this.isOpen || !this.port || !this.port.writable) {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_OPEN,\n 'Port is not open or writable stream is not available',\n );\n }\n\n // Cancel previous write subscription if exists\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n }\n\n this.writeSubscription = subscribeToWritable(data$, this.port.writable);\n\n return new Observable<void>((subscriber) => {\n // The subscription is already active, we just need to track completion\n if (!this.writeSubscription) {\n subscriber.error(\n new SerialError(\n SerialErrorCode.WRITE_FAILED,\n 'Write subscription is not available',\n ),\n );\n return;\n }\n const originalUnsubscribe = this.writeSubscription.unsubscribe;\n\n this.writeSubscription = {\n unsubscribe: () => {\n originalUnsubscribe();\n subscriber.complete();\n },\n };\n\n // If the observable completes, complete the subscriber\n data$.subscribe({\n complete: () => {\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n this.writeSubscription = null;\n }\n subscriber.complete();\n },\n error: (error) => {\n if (this.writeSubscription) {\n this.writeSubscription.unsubscribe();\n this.writeSubscription = null;\n }\n subscriber.error(error);\n },\n });\n });\n }\n\n /**\n * Write a single chunk of data to the serial port.\n *\n * @param data - Data to write\n * @returns Observable that completes when the data is written\n * @internal\n */\n write(data: Uint8Array): Observable<void> {\n return defer(() => {\n if (!this.isOpen || !this.port || !this.port.writable) {\n throw new SerialError(\n SerialErrorCode.PORT_NOT_OPEN,\n 'Port is not open or writable stream is not available',\n );\n }\n\n const writer = this.port.writable.getWriter();\n return writer\n .write(data)\n .then(() => {\n writer.releaseLock();\n })\n .catch((error) => {\n writer.releaseLock();\n throw new SerialError(\n SerialErrorCode.WRITE_FAILED,\n `Failed to write data: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n });\n });\n }\n\n /**\n * Write text data to the serial port.\n *\n * @param data - Text data to write\n * @returns Observable that completes when the data is written\n * @internal\n */\n writeText(data: string): Observable<void> {\n return this.write(this.textEncoder.encode(data));\n }\n\n /**\n * Check if the port is currently open.\n *\n * @returns `true` if a port is currently open, `false` otherwise\n * @internal\n */\n get connected(): boolean {\n return this.isOpen;\n }\n\n /**\n * Get an Observable that emits connection state changes.\n *\n * @returns Observable that emits `true` when connected and `false` when disconnected\n * @internal\n */\n get connected$(): Observable<boolean> {\n return this.connectedState$.asObservable();\n }\n\n /**\n * Get an Observable that emits connection lifecycle events.\n *\n * @returns Observable that emits 'connected' or 'disconnected'\n * @internal\n */\n get connectionEvents$(): Observable<'connected' | 'disconnected'> {\n return this.connectionEventsSubject$.asObservable();\n }\n\n /**\n * Get the current SerialPort instance.\n *\n * @returns The current SerialPort instance, or `null` if no port is open\n * @internal\n */\n get currentPort(): SerialPort | null {\n return this.port;\n }\n}\n", "/**\n * Error codes for serial port operations.\n *\n * These codes identify specific error conditions that can occur when working with\n * serial ports. Each error code corresponds to a specific failure scenario, making\n * it easier to handle errors programmatically.\n *\n * @example\n * ```typescript\n * try {\n * await client.connect().toPromise();\n * } catch (error) {\n * if (error instanceof SerialError) {\n * switch (error.code) {\n * case SerialErrorCode.BROWSER_NOT_SUPPORTED:\n * console.error('Please use a Chromium-based browser');\n * break;\n * case SerialErrorCode.OPERATION_CANCELLED:\n * console.log('User cancelled port selection');\n * break;\n * // ... handle other error codes\n * }\n * }\n * }\n * ```\n */\nexport enum SerialErrorCode {\n /**\n * Browser does not support the Web Serial API.\n *\n * This error occurs when attempting to use serial port functionality in a browser\n * that doesn't support the Web Serial API. Only Chromium-based browsers (Chrome,\n * Edge, Opera) support this API.\n *\n * **Suggested action**: Inform the user to use a supported browser.\n */\n BROWSER_NOT_SUPPORTED = 'BROWSER_NOT_SUPPORTED',\n\n /**\n * Serial port is not available.\n *\n * This error occurs when a requested port cannot be accessed, such as when\n * getting previously granted ports fails or when the port is already in use\n * by another application.\n *\n * **Suggested action**: Check if the port is available or being used by another application.\n */\n PORT_NOT_AVAILABLE = 'PORT_NOT_AVAILABLE',\n\n /**\n * Failed to open the serial port.\n *\n * This error occurs when the port cannot be opened, typically due to incorrect\n * connection parameters, hardware issues, or permission problems.\n *\n * **Suggested action**: Verify connection parameters and check hardware connections.\n */\n PORT_OPEN_FAILED = 'PORT_OPEN_FAILED',\n\n /**\n * Serial port is already open.\n *\n * This error occurs when attempting to open a port that is already connected.\n * Only one connection can be active at a time per SerialClient instance.\n *\n * **Suggested action**: Disconnect the current port before connecting a new one.\n */\n PORT_ALREADY_OPEN = 'PORT_ALREADY_OPEN',\n\n /**\n * Serial port is not open.\n *\n * This error occurs when attempting to read from or write to a port that hasn't\n * been opened yet. The port must be connected before performing I/O operations.\n *\n * **Suggested action**: Call {@link SerialClient.connect} before reading or writing.\n */\n PORT_NOT_OPEN = 'PORT_NOT_OPEN',\n\n /**\n * Failed to read from the serial port.\n *\n * This error occurs when reading data from the port fails, typically due to\n * connection loss, hardware issues, or stream errors.\n *\n * **Suggested action**: Check the connection and hardware, then retry the read operation.\n */\n READ_FAILED = 'READ_FAILED',\n\n /**\n * Failed to write to the serial port.\n *\n * This error occurs when writing data to the port fails, typically due to\n * connection loss, hardware issues, or stream errors.\n *\n * **Suggested action**: Check the connection and hardware, then retry the write operation.\n */\n WRITE_FAILED = 'WRITE_FAILED',\n\n /**\n * Serial port connection was lost.\n *\n * This error occurs when the connection to the serial port is unexpectedly\n * terminated, such as when the device is disconnected or the port is closed\n * by another process.\n *\n * **Suggested action**: Check the physical connection and reconnect if needed.\n */\n CONNECTION_LOST = 'CONNECTION_LOST',\n\n /**\n * Invalid filter options provided.\n *\n * This error occurs when port filter options are invalid, such as when\n * filter values are out of range or missing required fields.\n *\n * **Suggested action**: Verify filter options match the expected format and value ranges.\n */\n INVALID_FILTER_OPTIONS = 'INVALID_FILTER_OPTIONS',\n\n /**\n * Operation was cancelled by the user.\n *\n * This error occurs when the user cancels a port selection dialog or aborts\n * an operation before it completes.\n *\n * **Suggested action**: This is a normal condition - no action required, but you may want\n * to inform the user that the operation was cancelled.\n */\n OPERATION_CANCELLED = 'OPERATION_CANCELLED',\n\n /**\n * Unknown error occurred.\n *\n * This error code is used for errors that don't fit into any other category.\n * The original error details may be available in the error's message or originalError property.\n *\n * **Suggested action**: Check the error message and originalError for more details.\n */\n UNKNOWN = 'UNKNOWN',\n}\n", "import { SerialErrorCode } from './serial-error-code';\n\n// Re-export SerialErrorCode for convenience\nexport { SerialErrorCode };\n\n/**\n * Custom error class for serial port operations.\n *\n * This error class extends the standard Error class and includes additional information\n * about the type of error that occurred. It provides an error code for programmatic\n * error handling and may include the original error that caused the failure.\n *\n * @example\n * ```typescript\n * try {\n * await client.connect().toPromise();\n * } catch (error) {\n * if (error instanceof SerialError) {\n * console.error(`Error code: ${error.code}`);\n * console.error(`Message: ${error.message}`);\n * if (error.originalError) {\n * console.error(`Original error:`, error.originalError);\n * }\n *\n * // Check specific error code\n * if (error.is(SerialErrorCode.BROWSER_NOT_SUPPORTED)) {\n * // Handle browser not supported\n * }\n * }\n * }\n * ```\n */\nexport class SerialError extends Error {\n /**\n * The error code identifying the type of error that occurred.\n *\n * Use this code to programmatically handle specific error conditions.\n *\n * @see {@link SerialErrorCode} for all available error codes\n */\n public readonly code: SerialErrorCode;\n\n /**\n * The original error that caused this SerialError, if available.\n *\n * This property contains the underlying error (e.g., DOMException, TypeError)\n * that was wrapped in this SerialError. It may be undefined if no original error exists.\n */\n public readonly originalError?: Error;\n\n /**\n * Creates a new SerialError instance.\n *\n * @param code - The error code identifying the type of error\n * @param message - A human-readable error message\n * @param originalError - The original error that caused this SerialError, if any\n */\n constructor(code: SerialErrorCode, message: string, originalError?: Error) {\n super(message);\n this.name = 'SerialError';\n this.code = code;\n this.originalError = originalError;\n\n // Maintains proper stack trace for where our error was thrown (only available on V8)\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n if ((Error as any).captureStackTrace) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (Error as any).captureStackTrace(this, SerialError);\n }\n }\n\n /**\n * Check if the error matches a specific error code.\n *\n * This is a convenience method for checking the error code without directly\n * comparing the code property.\n *\n * @param code - The error code to check against\n * @returns `true` if this error's code matches the provided code, `false` otherwise\n *\n * @example\n * ```typescript\n * if (error.is(SerialErrorCode.PORT_NOT_OPEN)) {\n * // Handle port not open error\n * }\n * ```\n */\n public is(code: SerialErrorCode): boolean {\n return this.code === code;\n }\n}\n", "/**\n * Browser type enumeration for identifying the browser environment.\n *\n * This enum is used to identify the specific browser type, which is useful for\n * browser-specific behavior or error messages.\n *\n * @example\n * ```typescript\n * const browserType = detectBrowserType();\n * if (browserType === BrowserType.CHROME) {\n * console.log('Running in Chrome');\n * }\n * ```\n */\nexport enum BrowserType {\n /** Google Chrome browser */\n CHROME = 'chrome',\n /** Microsoft Edge browser */\n EDGE = 'edge',\n /** Opera browser */\n OPERA = 'opera',\n /** Unknown or unsupported browser */\n UNKNOWN = 'unknown',\n}\n\n/**\n * Feature detection for Web Serial API.\n *\n * Checks if the browser supports the Web Serial API by verifying the presence\n * of `navigator.serial`. This is a non-throwing check that returns `false` if\n * the API is not available.\n *\n * Note: This function only checks for API availability, not whether the browser\n * type is supported. For a throwing check that provides better error messages,\n * use {@link checkBrowserSupport}.\n *\n * @returns `true` if the Web Serial API is available, `false` otherwise\n *\n * @example\n * ```typescript\n * if (hasWebSerialSupport()) {\n * // Safe to use serial port functionality\n * const client = createSerialClient();\n * } else {\n * console.error('Web Serial API is not supported');\n * }\n * ```\n *\n * @see {@link checkBrowserSupport} for a throwing version with better error messages\n * @see {@link isBrowserSupported} for an alias to this function\n */\nexport function hasWebSerialSupport(): boolean {\n return (\n typeof navigator !== 'undefined' &&\n 'serial' in navigator &&\n navigator.serial !== undefined &&\n navigator.serial !== null\n );\n}\n\n/**\n * Detect browser type from the user agent string.\n *\n * Analyzes the browser's user agent string to identify the browser type.\n * This function is useful for providing browser-specific functionality or\n * error messages.\n *\n * @returns The detected {@link BrowserType}, or {@link BrowserType.UNKNOWN} if the browser cannot be identified\n *\n * @example\n * ```typescript\n * const browserType = detectBrowserType();\n * switch (browserType) {\n * case BrowserType.CHROME:\n * console.log('Running in Chrome');\n * break;\n * case BrowserType.EDGE:\n * console.log('Running in Edge');\n * break;\n * case BrowserType.OPERA:\n * console.log('Running in Opera');\n * break;\n * default:\n * console.log('Unknown browser');\n * }\n * ```\n *\n * @see {@link isChromiumBased} for checking if the browser is Chromium-based\n */\nexport function detectBrowserType(): BrowserType {\n if (typeof navigator === 'undefined' || !navigator.userAgent) {\n return BrowserType.UNKNOWN;\n }\n\n const ua = navigator.userAgent.toLowerCase();\n\n if (ua.includes('edg/')) {\n return BrowserType.EDGE;\n }\n\n if (ua.includes('opr/') || ua.includes('opera/')) {\n return BrowserType.OPERA;\n }\n\n if (ua.includes('chrome/')) {\n return BrowserType.CHROME;\n }\n\n return BrowserType.UNKNOWN;\n}\n\n/**\n * Check if the browser is Chromium-based.\n *\n * Determines if the current browser is based on Chromium, which includes\n * Chrome, Edge, and Opera. These browsers support the Web Serial API.\n *\n * @returns `true` if the browser is Chromium-based (Chrome, Edge, or Opera), `false` otherwise\n *\n * @example\n * ```typescript\n * if (isChromiumBased()) {\n * // Browser supports Web Serial API\n * const client = createSerialClient();\n * } else {\n * console.error('Please use a Chromium-based browser (Chrome, Edge, or Opera)');\n * }\n * ```\n *\n * @see {@link detectBrowserType} for identifying the specific browser type\n * @see {@link hasWebSerialSupport} for checking Web Serial API availability\n */\nexport function isChromiumBased(): boolean {\n const browserType = detectBrowserType();\n return (\n browserType === BrowserType.CHROME ||\n browserType === BrowserType.EDGE ||\n browserType === BrowserType.OPERA\n );\n}\n", "import { SerialError, SerialErrorCode } from '../errors/serial-error';\nimport {\n BrowserType,\n detectBrowserType,\n hasWebSerialSupport,\n} from './browser-detection';\n\n/**\n * Check if the browser supports the Web Serial API, throwing an error if not supported.\n *\n * This function performs a feature detection check and throws a {@link SerialError}\n * with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the Web Serial API\n * is not available. The error message includes the detected browser type for better\n * user feedback.\n *\n * This is the recommended way to check browser support before using serial port\n * functionality, as it provides clear error messages.\n *\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED}\n * if the browser doesn't support the Web Serial API\n *\n * @example\n * ```typescript\n * try {\n * checkBrowserSupport();\n * // Safe to use serial port functionality\n * const client = createSerialClient();\n * } catch (error) {\n * if (error instanceof SerialError && error.code === SerialErrorCode.BROWSER_NOT_SUPPORTED) {\n * console.error(error.message);\n * // Show user-friendly message: \"Please use a Chromium-based browser...\"\n * }\n * }\n * ```\n *\n * @see {@link isBrowserSupported} for a non-throwing version that returns a boolean\n * @see {@link hasWebSerialSupport} for the underlying feature detection function\n */\nexport function checkBrowserSupport(): void {\n if (!hasWebSerialSupport()) {\n const browserType = detectBrowserType();\n const browserName =\n browserType === BrowserType.UNKNOWN\n ? 'your browser'\n : browserType.toUpperCase();\n\n throw new SerialError(\n SerialErrorCode.BROWSER_NOT_SUPPORTED,\n `Web Serial API is not supported in ${browserName}. Please use a Chromium-based browser (Chrome, Edge, or Opera).`,\n );\n }\n}\n\n/**\n * Check if the browser supports the Web Serial API (non-throwing version).\n *\n * This is a convenience function that returns a boolean indicating whether\n * the Web Serial API is available. Unlike {@link checkBrowserSupport}, this\n * function does not throw an error if the API is not available.\n *\n * @returns `true` if the Web Serial API is supported, `false` otherwise\n *\n * @example\n * ```typescript\n * if (isBrowserSupported()) {\n * const client = createSerialClient();\n * // Use serial port functionality\n * } else {\n * console.error('Web Serial API is not supported in this browser');\n * // Show fallback UI or message\n * }\n * ```\n *\n * @see {@link checkBrowserSupport} for a throwing version with better error messages\n * @see {@link hasWebSerialSupport} which this function calls internally\n */\nexport function isBrowserSupported(): boolean {\n return hasWebSerialSupport();\n}\n", "import { SerialError, SerialErrorCode } from '../errors/serial-error';\nimport { SerialClientOptions } from '../types/options';\n\n/**\n * Build SerialPortRequestOptions from SerialClientOptions.\n *\n * This utility function converts filter options from {@link SerialClientOptions} into\n * the format expected by the Web Serial API's `navigator.serial.requestPort()` method.\n * It validates the filter options to ensure they are valid before returning them.\n *\n * If no filters are provided in the options, this function returns `undefined`, which\n * allows the port selection dialog to show all available ports.\n *\n * @param options - Optional SerialClientOptions containing filter configuration\n * @returns SerialPortRequestOptions object with validated filters, or `undefined` if no filters are provided\n * @throws {@link SerialError} with code {@link SerialErrorCode.INVALID_FILTER_OPTIONS} if filter validation fails\n *\n * @example\n * ```typescript\n * // With filters\n * const options = {\n * baudRate: 9600,\n * filters: [\n * { usbVendorId: 0x1234 },\n * { usbVendorId: 0x5678, usbProductId: 0x9abc },\n * ],\n * };\n * const requestOptions = buildRequestOptions(options);\n * // Returns: { filters: [...] }\n *\n * // Without filters\n * const requestOptions = buildRequestOptions({ baudRate: 9600 });\n * // Returns: undefined\n *\n * // Invalid filter (will throw)\n * try {\n * buildRequestOptions({ filters: [{ usbVendorId: -1 }] });\n * } catch (error) {\n * // SerialError with code INVALID_FILTER_OPTIONS\n * }\n * ```\n */\nexport function buildRequestOptions(\n options?: SerialClientOptions,\n): SerialPortRequestOptions | undefined {\n if (!options || !options.filters || options.filters.length === 0) {\n return undefined;\n }\n\n // Validate filters\n for (const filter of options.filters) {\n if (!filter.usbVendorId && !filter.usbProductId) {\n throw new SerialError(\n SerialErrorCode.INVALID_FILTER_OPTIONS,\n 'Filter must have at least usbVendorId or usbProductId',\n );\n }\n\n if (filter.usbVendorId !== undefined) {\n if (\n !Number.isInteger(filter.usbVendorId) ||\n filter.usbVendorId < 0 ||\n filter.usbVendorId > 0xffff\n ) {\n throw new SerialError(\n SerialErrorCode.INVALID_FILTER_OPTIONS,\n `Invalid usbVendorId: ${filter.usbVendorId}. Must be an integer between 0 and 65535.`,\n );\n }\n }\n\n if (filter.usbProductId !== undefined) {\n if (\n !Number.isInteger(filter.usbProductId) ||\n filter.usbProductId < 0 ||\n filter.usbProductId > 0xffff\n ) {\n throw new SerialError(\n SerialErrorCode.INVALID_FILTER_OPTIONS,\n `Invalid usbProductId: ${filter.usbProductId}. Must be an integer between 0 and 65535.`,\n );\n }\n }\n }\n\n return {\n filters: options.filters,\n };\n}\n", "import { Observable } from 'rxjs';\nimport { SerialError, SerialErrorCode } from '../errors/serial-error';\n\n/**\n * Convert an RxJS Observable to a WritableStream.\n *\n * This utility function converts an RxJS Observable into a Web Streams API WritableStream.\n * Values emitted by the Observable will be written to the returned WritableStream. The stream\n * will close when the Observable completes or abort if the Observable errors.\n *\n * Note: This function creates a new WritableStream. For directly subscribing to an Observable\n * and writing to an existing WritableStream, use {@link subscribeToWritable} instead.\n *\n * @param observable - The Observable to convert to a WritableStream\n * @returns A WritableStream that writes Uint8Array chunks emitted by the Observable\n *\n * @example\n * ```typescript\n * const data$ = from([\n * new TextEncoder().encode('Hello'),\n * new TextEncoder().encode('World'),\n * ]);\n *\n * const writableStream = observableToWritable(data$);\n * // Use the writable stream with other Web Streams APIs\n * ```\n */\nexport function observableToWritable(\n observable: Observable<Uint8Array>,\n): WritableStream<Uint8Array> {\n let writer: WritableStreamDefaultWriter<Uint8Array> | null = null;\n let subscription: { unsubscribe: () => void } | null = null;\n let stream: WritableStream<Uint8Array> | null = null;\n\n stream = new WritableStream<Uint8Array>({\n async start() {\n if (!stream) {\n return;\n }\n writer = stream.getWriter();\n\n subscription = observable.subscribe({\n next: async (chunk) => {\n if (writer) {\n try {\n await writer.write(chunk);\n } catch (error) {\n subscription?.unsubscribe();\n if (writer) {\n writer.releaseLock();\n }\n throw error;\n }\n }\n },\n error: async (error) => {\n if (writer) {\n try {\n await writer.abort(error);\n } catch {\n // Ignore abort errors\n } finally {\n writer.releaseLock();\n writer = null;\n }\n }\n },\n complete: async () => {\n if (writer) {\n try {\n await writer.close();\n } catch {\n // Ignore close errors\n } finally {\n writer.releaseLock();\n writer = null;\n }\n }\n },\n });\n },\n\n abort(reason) {\n if (subscription) {\n subscription.unsubscribe();\n subscription = null;\n }\n if (writer) {\n writer.abort(reason).catch(() => {\n // Ignore abort errors\n });\n writer.releaseLock();\n writer = null;\n }\n },\n });\n\n return stream;\n}\n\n/**\n * Subscribe to an Observable and write its values to a WritableStream.\n *\n * This utility function subscribes to an RxJS Observable and writes all emitted values\n * to the provided WritableStream. This is commonly used to write Observable data to a\n * serial port's writable stream.\n *\n * The function returns a subscription object that can be used to unsubscribe and clean up.\n * When unsubscribed, the writer lock will be released properly.\n *\n * @param observable - The Observable to subscribe to and read data from\n * @param stream - The WritableStream to write data to\n * @returns A subscription object with an `unsubscribe` method for cleanup\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing to the stream fails\n *\n * @example\n * ```typescript\n * const data$ = from([\n * new TextEncoder().encode('Hello'),\n * new TextEncoder().encode('World'),\n * ]);\n *\n * const subscription = subscribeToWritable(data$, port.writable);\n *\n * // Later, to cancel and clean up:\n * subscription.unsubscribe();\n *\n * // Use with RxJS operators\n * const processedData$ = of('Hello, Serial!').pipe(\n * map((text) => new TextEncoder().encode(text))\n * );\n *\n * subscribeToWritable(processedData$, port.writable);\n * ```\n */\nexport function subscribeToWritable(\n observable: Observable<Uint8Array>,\n stream: WritableStream<Uint8Array>,\n): { unsubscribe: () => void } {\n const writer = stream.getWriter();\n\n // Define error handler separately so we can call it directly\n const errorHandler = async (error: unknown) => {\n try {\n await writer.abort(error);\n } catch {\n // Ignore abort errors\n } finally {\n writer.releaseLock();\n }\n };\n\n const subscription = observable.subscribe({\n next: async (chunk) => {\n try {\n await writer.write(chunk);\n } catch (error) {\n subscription.unsubscribe();\n writer.releaseLock();\n // Convert write error to SerialError and pass to error handler\n const serialError = new SerialError(\n SerialErrorCode.WRITE_FAILED,\n `Failed to write to stream: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error : new Error(String(error)),\n );\n // Manually trigger error handler to avoid unhandled rejection\n await errorHandler(serialError);\n }\n },\n error: errorHandler,\n complete: async () => {\n try {\n await writer.close();\n } catch {\n // Ignore close errors\n } finally {\n writer.releaseLock();\n }\n },\n });\n\n return {\n unsubscribe: () => {\n subscription.unsubscribe();\n writer.releaseLock();\n },\n };\n}\n", "import { Observable } from 'rxjs';\nimport { SerialError, SerialErrorCode } from '../errors/serial-error';\n\n/**\n * Convert a ReadableStream to an RxJS Observable.\n *\n * This utility function converts a Web Streams API ReadableStream into an RxJS Observable,\n * allowing you to use RxJS operators with stream data. The Observable will emit Uint8Array\n * chunks as they are read from the stream, complete when the stream ends, or error if\n * the stream encounters an error.\n *\n * The returned Observable handles stream cleanup automatically - if you unsubscribe before\n * the stream completes, the reader lock will be released properly.\n *\n * @param stream - The ReadableStream to convert to an Observable\n * @returns An Observable that emits Uint8Array chunks from the stream\n * @throws {@link SerialError} with code {@link SerialErrorCode.READ_FAILED} if reading from the stream fails\n *\n * @example\n * ```typescript\n * // Convert a serial port's readable stream to an Observable\n * const readable$ = readableToObservable(port.readable);\n *\n * readable$.subscribe({\n * next: (chunk) => {\n * console.log('Received chunk:', chunk);\n * },\n * complete: () => {\n * console.log('Stream completed');\n * },\n * error: (error) => {\n * console.error('Stream error:', error);\n * },\n * });\n *\n * // Use with RxJS operators\n * readableToObservable(port.readable)\n * .pipe(\n * map((chunk) => new TextDecoder().decode(chunk)),\n * filter((text) => text.includes('OK'))\n * )\n * .subscribe((text) => console.log('Filtered text:', text));\n * ```\n */\nexport function readableToObservable(\n stream: ReadableStream<Uint8Array>,\n): Observable<Uint8Array> {\n return new Observable<Uint8Array>((subscriber) => {\n const reader = stream.getReader();\n\n const pump = async (): Promise<void> => {\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n subscriber.complete();\n break;\n }\n\n if (value) {\n subscriber.next(value);\n }\n }\n } catch (error) {\n if (error instanceof Error) {\n subscriber.error(\n new SerialError(\n SerialErrorCode.READ_FAILED,\n `Failed to read from stream: ${error.message}`,\n error,\n ),\n );\n } else {\n subscriber.error(\n new SerialError(\n SerialErrorCode.READ_FAILED,\n 'Failed to read from stream: Unknown error',\n error as Error,\n ),\n );\n }\n } finally {\n reader.releaseLock();\n }\n };\n\n pump().catch((error) => {\n if (!subscriber.closed) {\n subscriber.error(error);\n }\n });\n\n // Cleanup function\n return () => {\n reader.releaseLock();\n };\n });\n}\n", "/**\n * Options for creating a SerialClient instance.\n *\n * These options configure the serial port connection parameters. All properties are optional\n * and will use default values if not specified. See {@link DEFAULT_SERIAL_CLIENT_OPTIONS} for\n * the default values used.\n *\n * @example\n * ```typescript\n * const client = createSerialClient({\n * baudRate: 115200,\n * dataBits: 8,\n * stopBits: 1,\n * parity: 'none',\n * flowControl: 'none',\n * filters: [{ usbVendorId: 0x1234, usbProductId: 0x5678 }],\n * });\n * ```\n */\nexport interface SerialClientOptions {\n /**\n * Baud rate for the serial port connection (bits per second).\n *\n * Common values include 9600, 19200, 38400, 57600, 115200, etc.\n * Must match the baud rate configured on the connected device.\n *\n * @default 9600\n */\n baudRate?: number;\n\n /**\n * Number of data bits per character (7 or 8).\n *\n * - `7`: Seven data bits (used with parity)\n * - `8`: Eight data bits (most common, used without parity)\n *\n * @default 8\n */\n dataBits?: 7 | 8;\n\n /**\n * Number of stop bits (1 or 2).\n *\n * - `1`: One stop bit (most common)\n * - `2`: Two stop bits (less common, used for slower devices)\n *\n * @default 1\n */\n stopBits?: 1 | 2;\n\n /**\n * Parity checking mode.\n *\n * - `'none'`: No parity checking (most common)\n * - `'even'`: Even parity\n * - `'odd'`: Odd parity\n *\n * @default 'none'\n */\n parity?: 'none' | 'even' | 'odd';\n\n /**\n * Buffer size for reading data from the serial port, in bytes.\n *\n * This determines how much data can be buffered before it needs to be read.\n * Larger buffers can improve performance but use more memory.\n *\n * @default 255\n */\n bufferSize?: number;\n\n /**\n * Flow control mode.\n *\n * - `'none'`: No flow control (most common)\n * - `'hardware'`: Hardware flow control (RTS/CTS)\n *\n * @default 'none'\n */\n flowControl?: 'none' | 'hardware';\n\n /**\n * Filters for port selection when requesting a port.\n *\n * When specified, the port selection dialog will only show devices matching\n * these filters. Each filter can specify `usbVendorId` and/or `usbProductId`\n * to filter by USB device identifiers.\n *\n * @example\n * ```typescript\n * filters: [\n * { usbVendorId: 0x1234 },\n * { usbVendorId: 0x1234, usbProductId: 0x5678 },\n * ]\n * ```\n *\n * @see {@link SerialPortFilter} for the filter structure\n */\n filters?: SerialPortFilter[];\n}\n\n/**\n * Default options for SerialClient instances.\n *\n * These are the default values used when creating a SerialClient if no options\n * are provided or if specific options are omitted. The values are chosen to work\n * with most common serial devices.\n *\n * @see {@link SerialClientOptions} for details on each option\n */\nexport const DEFAULT_SERIAL_CLIENT_OPTIONS: Required<\n Omit<SerialClientOptions, 'filters'>\n> & { filters?: SerialPortFilter[] } = {\n baudRate: 9600,\n dataBits: 8,\n stopBits: 1,\n parity: 'none',\n bufferSize: 255,\n flowControl: 'none',\n filters: undefined,\n};\n", "import { Observable } from 'rxjs';\nimport { SerialClientOptions } from '../types/options';\nimport { SerialClientImpl } from './serial-client';\n\n/**\n * SerialClient interface for interacting with serial ports using RxJS Observables.\n *\n * This interface provides a reactive API for serial port communication, allowing you to\n * connect to serial devices, read and write data using RxJS Observables.\n *\n * @example\n * ```typescript\n * const client = createSerialClient({ baudRate: 9600 });\n *\n * // Connect to a port\n * client.connect().subscribe({\n * next: () => {\n * console.log('Connected!');\n *\n * // Read data\n * client.getReadStream().subscribe({\n * next: (data) => console.log('Received:', data),\n * });\n *\n * // Write data\n * const encoder = new TextEncoder();\n * client.write(encoder.encode('Hello')).subscribe();\n * },\n * error: (error) => console.error('Connection error:', error),\n * });\n * ```\n */\nexport interface SerialClient {\n /**\n * Request a serial port from the user.\n *\n * This method opens the browser's port selection dialog and returns an Observable\n * that emits the selected SerialPort when the user chooses a port.\n *\n * @returns An Observable that emits the selected {@link SerialPort} when the user selects a port\n * @throws {@link SerialError} with code {@link SerialErrorCode.OPERATION_CANCELLED} if the user cancels the selection\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_AVAILABLE} if the port request fails\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * client.requestPort().subscribe({\n * next: (port) => console.log('Selected port:', port),\n * error: (error) => console.error('Port selection failed:', error),\n * });\n * ```\n */\n requestPort(): Observable<SerialPort>;\n\n /**\n * Get available serial ports that have been previously granted access.\n *\n * This method returns an Observable that emits an array of SerialPort instances\n * that the user has previously granted access to in this browser session.\n *\n * @returns An Observable that emits an array of available {@link SerialPort} instances\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_AVAILABLE} if getting ports fails\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * client.getPorts().subscribe({\n * next: (ports) => {\n * console.log(`Found ${ports.length} available ports`);\n * if (ports.length > 0) {\n * client.connect(ports[0]).subscribe();\n * }\n * },\n * });\n * ```\n */\n getPorts(): Observable<SerialPort[]>;\n\n /**\n * Connect to a serial port.\n *\n * Opens the specified port (or requests one if not provided) and configures it\n * with the options passed to {@link createSerialClient}. The port must be connected\n * before reading or writing data.\n *\n * @param port - Optional {@link SerialPort} to connect to. If not provided, will call {@link requestPort} to prompt the user.\n * @returns An Observable that completes when the port is successfully opened\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_ALREADY_OPEN} if a port is already open\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_OPEN_FAILED} if opening the port fails\n * @throws {@link SerialError} with code {@link SerialErrorCode.OPERATION_CANCELLED} if the user cancels port selection\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * // Connect by requesting a port\n * client.connect().subscribe({\n * next: () => console.log('Connected!'),\n * error: (error) => console.error('Connection failed:', error),\n * });\n *\n * // Connect to a specific port\n * client.getPorts().subscribe({\n * next: (ports) => {\n * if (ports.length > 0) {\n * client.connect(ports[0]).subscribe();\n * }\n * },\n * });\n * ```\n */\n connect(port?: SerialPort): Observable<void>;\n\n /**\n * Disconnect from the serial port.\n *\n * Closes the currently open port and stops all active read/write streams.\n * This method is safe to call even if no port is currently open.\n *\n * @returns An Observable that completes when the port is successfully closed\n * @throws {@link SerialError} with code {@link SerialErrorCode.CONNECTION_LOST} if closing the port fails\n *\n * @example\n * ```typescript\n * client.disconnect().subscribe({\n * next: () => console.log('Disconnected'),\n * error: (error) => console.error('Disconnect failed:', error),\n * });\n * ```\n */\n disconnect(): Observable<void>;\n\n /**\n * Get an Observable that emits data read from the serial port.\n *\n * Returns an Observable stream that emits Uint8Array chunks as data is received\n * from the serial port. The stream will continue until the port is disconnected\n * or an error occurs.\n *\n * @returns An Observable that emits Uint8Array chunks containing data read from the serial port\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n *\n * @example\n * ```typescript\n * client.getReadStream().subscribe({\n * next: (data) => {\n * const text = new TextDecoder().decode(data);\n * console.log('Received:', text);\n * },\n * error: (error) => console.error('Read error:', error),\n * });\n * ```\n */\n getReadStream(): Observable<Uint8Array>;\n\n /**\n * Get an Observable that emits text read from the serial port.\n *\n * This is a convenience API on top of {@link getReadStream} that decodes bytes\n * with TextDecoder and emits text chunks.\n *\n * @returns An Observable that emits decoded text chunks\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n */\n getReadStreamAsText(): Observable<string>;\n\n /**\n * Write data to the serial port from an Observable.\n *\n * Writes data from an Observable stream to the serial port. The Observable should\n * emit Uint8Array chunks that will be written sequentially to the port. If a previous\n * write stream is active, it will be cancelled before starting the new one.\n *\n * @param data$ - Observable that emits Uint8Array chunks to write to the serial port\n * @returns An Observable that completes when all data has been written and the stream completes\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing fails\n *\n * @example\n * ```typescript\n * const data$ = from([\n * new TextEncoder().encode('Hello'),\n * new TextEncoder().encode('World'),\n * ]);\n *\n * client.writeStream(data$).subscribe({\n * next: () => console.log('Writing...'),\n * complete: () => console.log('All data written'),\n * error: (error) => console.error('Write error:', error),\n * });\n * ```\n */\n writeStream(data$: Observable<Uint8Array>): Observable<void>;\n\n /**\n * Write a single chunk of data to the serial port.\n *\n * Writes a single Uint8Array chunk to the serial port. For writing multiple chunks,\n * consider using {@link writeStream} with an Observable instead.\n *\n * @param data - Uint8Array data to write to the serial port\n * @returns An Observable that completes when the data has been written\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing fails\n *\n * @example\n * ```typescript\n * const encoder = new TextEncoder();\n * const data = encoder.encode('Hello, Serial!');\n *\n * client.write(data).subscribe({\n * next: () => console.log('Data written'),\n * error: (error) => console.error('Write error:', error),\n * });\n * ```\n */\n write(data: Uint8Array): Observable<void>;\n\n /**\n * Write text data to the serial port.\n *\n * This is a convenience API on top of {@link write} that encodes text with TextEncoder.\n *\n * @param data - Text data to write to the serial port\n * @returns An Observable that completes when the data has been written\n * @throws {@link SerialError} with code {@link SerialErrorCode.PORT_NOT_OPEN} if the port is not open\n * @throws {@link SerialError} with code {@link SerialErrorCode.WRITE_FAILED} if writing fails\n */\n writeText(data: string): Observable<void>;\n\n /**\n * Check if the port is currently open and connected.\n *\n * @returns `true` if a port is currently open, `false` otherwise\n */\n readonly connected: boolean;\n\n /**\n * Reactive connection state stream.\n *\n * Emits `true` when connected and `false` when disconnected.\n */\n readonly connected$: Observable<boolean>;\n\n /**\n * Reactive connection event stream.\n *\n * Emits `'connected'` on successful connection and `'disconnected'` on disconnection.\n */\n readonly connectionEvents$: Observable<'connected' | 'disconnected'>;\n\n /**\n * Get the current SerialPort instance.\n *\n * Returns the currently connected SerialPort instance, or `null` if no port is open.\n * This allows direct access to the underlying Web Serial API SerialPort object if needed.\n *\n * @returns The current {@link SerialPort} instance, or `null` if no port is open\n */\n readonly currentPort: SerialPort | null;\n}\n\n/**\n * Create a new SerialClient instance for interacting with serial ports.\n *\n * This is the main entry point for creating a serial client. The client provides\n * a reactive RxJS-based API for connecting to serial ports and reading/writing data.\n *\n * @param options - Optional configuration options for the serial port connection.\n * If not provided, default values will be used (9600 baud, 8 data bits, etc.)\n * @returns A new {@link SerialClient} instance\n * @throws {@link SerialError} with code {@link SerialErrorCode.BROWSER_NOT_SUPPORTED} if the browser doesn't support Web Serial API\n *\n * @example\n * ```typescript\n * // Create a client with default settings (9600 baud)\n * const client = createSerialClient();\n *\n * // Create a client with custom settings\n * const client = createSerialClient({\n * baudRate: 115200,\n * dataBits: 8,\n * stopBits: 1,\n * parity: 'none',\n * filters: [{ usbVendorId: 0x1234 }],\n * });\n *\n * // Check browser support before creating a client\n * import { isBrowserSupported } from '@gurezo/web-serial-rxjs';\n *\n * if (!isBrowserSupported()) {\n * console.error('Web Serial API is not supported');\n * } else {\n * const client = createSerialClient();\n * }\n * ```\n */\nexport function createSerialClient(\n options?: SerialClientOptions,\n): SerialClient {\n return new SerialClientImpl(options);\n}\n", "import {\n Observable,\n Subject,\n defaultIfEmpty,\n defer,\n firstValueFrom,\n shareReplay,\n} from 'rxjs';\nimport type { SerialClient } from '../client';\n\nexport interface ShellClientOptions {\n prompt: string | RegExp;\n timeout?: number;\n retry?: number;\n lineEnding?: string;\n}\n\nexport interface ShellExecResult {\n stdout: string;\n}\n\nexport interface ShellClient {\n exec$(command: string): Observable<ShellExecResult>;\n readUntilPrompt$(): Observable<string>;\n}\n\nconst DEFAULT_TIMEOUT = 10_000;\nconst DEFAULT_RETRY = 0;\nconst DEFAULT_LINE_ENDING = '\\r\\n';\n\nclass ShellClientImpl implements ShellClient {\n private readonly timeout: number;\n private readonly retry: number;\n private readonly lineEnding: string;\n private readonly prompt: string | RegExp;\n private readonly promptRegex: RegExp | null;\n private readonly bufferTick$ = new Subject<void>();\n private readonly read$: Observable<string>;\n private queueChain: Promise<void> = Promise.resolve();\n private readBuffer = '';\n\n constructor(\n private readonly client: SerialClient,\n options: ShellClientOptions,\n ) {\n this.prompt = options.prompt;\n this.timeout = options.timeout ?? DEFAULT_TIMEOUT;\n this.retry = options.retry ?? DEFAULT_RETRY;\n this.lineEnding = options.lineEnding ?? DEFAULT_LINE_ENDING;\n this.promptRegex =\n this.prompt instanceof RegExp ? this.createAnchoredRegex(this.prompt) : null;\n this.read$ = this.client.getReadStreamAsText().pipe(shareReplay(1));\n\n this.read$.subscribe({\n next: (chunk) => {\n this.readBuffer += chunk;\n this.bufferTick$.next();\n },\n error: (error) => this.bufferTick$.error(error),\n complete: () => this.bufferTick$.complete(),\n });\n }\n\n exec$(command: string): Observable<ShellExecResult> {\n return this.enqueue(async () => {\n for (let attempt = 0; attempt <= this.retry; attempt += 1) {\n try {\n await this.writeText(command + this.lineEnding);\n const stdout = await this.waitUntilPrompt(this.timeout);\n return { stdout };\n } catch (error) {\n if (!this.isTimeoutError(error) || attempt >= this.retry) {\n throw error;\n }\n }\n }\n\n throw new Error('Unexpected command execution state');\n });\n }\n\n readUntilPrompt$(): Observable<string> {\n return this.enqueue(async () => {\n for (let attempt = 0; attempt <= this.retry; attempt += 1) {\n try {\n return await this.waitUntilPrompt(this.timeout);\n } catch (error) {\n if (!this.isTimeoutError(error) || attempt >= this.retry) {\n throw error;\n }\n }\n }\n\n throw new Error('Unexpected prompt waiting state');\n });\n }\n\n private enqueue<T>(operation: () => Promise<T>): Observable<T> {\n return defer(\n () =>\n new Observable<T>((subscriber) => {\n let cancelled = false;\n const run = async (): Promise<void> => {\n try {\n const value = await operation();\n if (!cancelled) {\n subscriber.next(value);\n subscriber.complete();\n }\n } catch (error) {\n if (!cancelled) {\n subscriber.error(error);\n }\n }\n };\n\n const scheduled = this.queueChain.then(run, run);\n this.queueChain = scheduled.then(\n () => undefined,\n () => undefined,\n );\n\n return () => {\n cancelled = true;\n };\n }),\n );\n }\n\n private async writeText(text: string): Promise<void> {\n await firstValueFrom(this.client.writeText(text).pipe(defaultIfEmpty(undefined)));\n }\n\n private waitUntilPrompt(timeoutMs: number): Promise<string> {\n const immediate = this.tryConsumePrompt();\n if (immediate !== null) {\n return Promise.resolve(immediate);\n }\n\n return new Promise<string>((resolve, reject) => {\n const timer = setTimeout(() => {\n subscription.unsubscribe();\n reject(new Error(`Timed out waiting for prompt after ${timeoutMs}ms`));\n }, timeoutMs);\n\n const complete = (value: string): void => {\n clearTimeout(timer);\n subscription.unsubscribe();\n resolve(value);\n };\n\n const fail = (error: unknown): void => {\n clearTimeout(timer);\n subscription.unsubscribe();\n reject(error);\n };\n\n const tryResolve = (): void => {\n const output = this.tryConsumePrompt();\n if (output !== null) {\n complete(output);\n }\n };\n\n const subscription = this.bufferTick$.subscribe({\n next: () => tryResolve(),\n error: (error) => fail(error),\n complete: () => fail(new Error('Read stream completed before prompt was found')),\n });\n });\n }\n\n private tryConsumePrompt(): string | null {\n if (typeof this.prompt === 'string') {\n if (\n this.readBuffer.length >= this.prompt.length &&\n this.readBuffer.endsWith(this.prompt)\n ) {\n const body = this.readBuffer.slice(0, this.readBuffer.length - this.prompt.length);\n this.readBuffer = '';\n return body.trimEnd();\n }\n return null;\n }\n\n if (!this.promptRegex) {\n return null;\n }\n\n const match = this.promptRegex.exec(this.readBuffer);\n if (!match || match.index == null) {\n return null;\n }\n\n const body = this.readBuffer.slice(0, match.index);\n this.readBuffer = '';\n return body.trimEnd();\n }\n\n private createAnchoredRegex(source: RegExp): RegExp {\n const flags = source.flags.replace(/g/g, '');\n return new RegExp(`(?:${source.source})$`, flags);\n }\n\n private isTimeoutError(error: unknown): boolean {\n return error instanceof Error && error.message.includes('Timed out waiting for prompt');\n }\n}\n\nexport function createShellClient(\n client: SerialClient,\n options: ShellClientOptions,\n): ShellClient {\n return new ShellClientImpl(client, options);\n}\n"],
5
+ "mappings": "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",
6
+ "names": ["Observable", "SerialErrorCode", "BrowserType", "Observable", "Observable", "Subject", "defer", "body"]
7
7
  }
@@ -0,0 +1,17 @@
1
+ import { Observable } from 'rxjs';
2
+ import type { SerialClient } from '../client';
3
+ export interface ShellClientOptions {
4
+ prompt: string | RegExp;
5
+ timeout?: number;
6
+ retry?: number;
7
+ lineEnding?: string;
8
+ }
9
+ export interface ShellExecResult {
10
+ stdout: string;
11
+ }
12
+ export interface ShellClient {
13
+ exec$(command: string): Observable<ShellExecResult>;
14
+ readUntilPrompt$(): Observable<string>;
15
+ }
16
+ export declare function createShellClient(client: SerialClient, options: ShellClientOptions): ShellClient;
17
+ //# sourceMappingURL=create-shell-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-shell-client.d.ts","sourceRoot":"","sources":["../../src/shell/create-shell-client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EAMX,MAAM,MAAM,CAAC;AACd,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC,eAAe,CAAC,CAAC;IACpD,gBAAgB,IAAI,UAAU,CAAC,MAAM,CAAC,CAAC;CACxC;AAyLD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,YAAY,EACpB,OAAO,EAAE,kBAAkB,GAC1B,WAAW,CAEb"}
@@ -0,0 +1,2 @@
1
+ export { createShellClient, type ShellClient, type ShellClientOptions, type ShellExecResult, } from './create-shell-client';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gurezo/web-serial-rxjs",
3
- "version": "0.1.21",
3
+ "version": "0.2.0",
4
4
  "description": "RxJS-based utilities for the Web Serial API, usable from Angular, React, Svelte, and Vanilla JavaScript/TypeScript.",
5
5
  "author": "Akihiko Kigure <akihiko.kigure@gmail.com>",
6
6
  "license": "MIT",
@@ -11,6 +11,10 @@
11
11
  "types": "./dist/index.d.ts",
12
12
  "import": "./dist/index.mjs"
13
13
  },
14
+ "./shell": {
15
+ "types": "./dist/shell/index.d.ts",
16
+ "import": "./dist/shell/index.mjs"
17
+ },
14
18
  "./package.json": "./package.json"
15
19
  },
16
20
  "files": [