@gurezo/web-serial-rxjs 0.1.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.
Files changed (4) hide show
  1. package/LICENSE +22 -0
  2. package/README.ja.md +599 -0
  3. package/README.md +598 -0
  4. package/package.json +54 -0
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 gurezo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.ja.md ADDED
@@ -0,0 +1,599 @@
1
+ # web-serial-rxjs
2
+
3
+ Web Serial API を RxJS ベースのリアクティブなラッパーで提供する TypeScript ライブラリです。Web アプリケーションでシリアルポート通信を簡単に実現できます。
4
+
5
+ ## 目次
6
+
7
+ - [機能](#機能)
8
+ - [ブラウザサポート](#ブラウザサポート)
9
+ - [インストール](#インストール)
10
+ - [クイックスタート](#クイックスタート)
11
+ - [使用例](#使用例)
12
+ - [API リファレンス](#api-リファレンス)
13
+ - [フレームワーク別の例](#フレームワーク別の例)
14
+ - [高度な使用方法](#高度な使用方法)
15
+ - [貢献](#貢献)
16
+ - [ライセンス](#ライセンス)
17
+ - [リンク](#リンク)
18
+
19
+ ## 機能
20
+
21
+ - **RxJS ベースのリアクティブ API**: RxJS Observables を活用したリアクティブなシリアルポート通信
22
+ - **TypeScript サポート**: 完全な TypeScript 型定義を含む
23
+ - **ブラウザ検出**: ブラウザサポートの検出とエラーハンドリング機能を内蔵
24
+ - **エラーハンドリング**: カスタムエラークラスとエラーコードによる包括的なエラーハンドリング
25
+ - **フレームワーク非依存**: 任意の JavaScript/TypeScript フレームワークまたはバニラ JavaScript で使用可能
26
+
27
+ ## ブラウザサポート
28
+
29
+ Web Serial API は現在、Chromium ベースのブラウザでのみサポートされています:
30
+
31
+ - **Chrome** 89+
32
+ - **Edge** 89+
33
+ - **Opera** 75+
34
+
35
+ このライブラリには、使用前に Web Serial API のサポートを確認するためのブラウザ検出ユーティリティが含まれています。
36
+
37
+ ## インストール
38
+
39
+ npm または pnpm を使用してパッケージをインストールします:
40
+
41
+ ```bash
42
+ npm install @gurezo/web-serial-rxjs
43
+ # または
44
+ pnpm add @gurezo/web-serial-rxjs
45
+ ```
46
+
47
+ ### ピア依存関係
48
+
49
+ このライブラリは RxJS をピア依存関係として必要とします:
50
+
51
+ ```bash
52
+ npm install rxjs
53
+ # または
54
+ pnpm add rxjs
55
+ ```
56
+
57
+ **最小要件バージョン**: RxJS ^7.8.0
58
+
59
+ ## クイックスタート
60
+
61
+ 簡単な使用例:
62
+
63
+ ```typescript
64
+ import {
65
+ createSerialClient,
66
+ isBrowserSupported,
67
+ } from '@gurezo/web-serial-rxjs';
68
+
69
+ // ブラウザサポートをチェック
70
+ if (!isBrowserSupported()) {
71
+ console.error('このブラウザは Web Serial API をサポートしていません');
72
+ return;
73
+ }
74
+
75
+ // シリアルクライアントを作成
76
+ const client = createSerialClient({ baudRate: 9600 });
77
+
78
+ // シリアルポートに接続
79
+ client.connect().subscribe({
80
+ next: () => {
81
+ console.log('シリアルポートに接続しました');
82
+
83
+ // シリアルポートからデータを読み取る
84
+ client.getReadStream().subscribe({
85
+ next: (data: Uint8Array) => {
86
+ const decoder = new TextDecoder('utf-8');
87
+ const text = decoder.decode(data);
88
+ console.log('受信:', text);
89
+ },
90
+ error: (error) => {
91
+ console.error('読み取りエラー:', error);
92
+ },
93
+ });
94
+
95
+ // シリアルポートにデータを書き込む
96
+ const encoder = new TextEncoder();
97
+ const data = encoder.encode('Hello, Serial Port!\n');
98
+ client.write(data).subscribe({
99
+ next: () => console.log('データを書き込みました'),
100
+ error: (error) => console.error('書き込みエラー:', error),
101
+ });
102
+ },
103
+ error: (error) => {
104
+ console.error('接続エラー:', error);
105
+ },
106
+ });
107
+ ```
108
+
109
+ ## 使用例
110
+
111
+ ### 基本的な接続
112
+
113
+ ```typescript
114
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
115
+
116
+ const client = createSerialClient({
117
+ baudRate: 115200,
118
+ dataBits: 8,
119
+ stopBits: 1,
120
+ parity: 'none',
121
+ });
122
+
123
+ // 接続(ユーザーにポート選択を促す)
124
+ client.connect().subscribe({
125
+ next: () => console.log('接続しました'),
126
+ error: (error) => console.error('接続に失敗しました:', error),
127
+ });
128
+ ```
129
+
130
+ ### データの読み取り
131
+
132
+ ```typescript
133
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
134
+ import { map } from 'rxjs/operators';
135
+
136
+ const client = createSerialClient({ baudRate: 9600 });
137
+
138
+ client.connect().subscribe({
139
+ next: () => {
140
+ // データを読み取ってデコード
141
+ client
142
+ .getReadStream()
143
+ .pipe(
144
+ map((data: Uint8Array) => {
145
+ const decoder = new TextDecoder('utf-8');
146
+ return decoder.decode(data);
147
+ }),
148
+ )
149
+ .subscribe({
150
+ next: (text) => console.log('受信:', text),
151
+ error: (error) => console.error('読み取りエラー:', error),
152
+ });
153
+ },
154
+ });
155
+ ```
156
+
157
+ ### データの書き込み
158
+
159
+ ```typescript
160
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
161
+ import { from } from 'rxjs';
162
+ import { map } from 'rxjs/operators';
163
+
164
+ const client = createSerialClient({ baudRate: 9600 });
165
+
166
+ client.connect().subscribe({
167
+ next: () => {
168
+ // 単一のチャンクを書き込む
169
+ const encoder = new TextEncoder();
170
+ const data = encoder.encode('Hello\n');
171
+ client.write(data).subscribe({
172
+ next: () => console.log('書き込みました'),
173
+ error: (error) => console.error('書き込みエラー:', error),
174
+ });
175
+
176
+ // Observable ストリームから書き込む
177
+ const messages = ['メッセージ 1\n', 'メッセージ 2\n', 'メッセージ 3\n'];
178
+ const dataStream$ = from(messages).pipe(
179
+ map((msg) => new TextEncoder().encode(msg)),
180
+ );
181
+ client.writeStream(dataStream$).subscribe({
182
+ next: () => console.log('すべてのメッセージを書き込みました'),
183
+ error: (error) => console.error('ストリーム書き込みエラー:', error),
184
+ });
185
+ },
186
+ });
187
+ ```
188
+
189
+ ### エラーハンドリング
190
+
191
+ ```typescript
192
+ import {
193
+ createSerialClient,
194
+ SerialError,
195
+ SerialErrorCode,
196
+ } from '@gurezo/web-serial-rxjs';
197
+
198
+ const client = createSerialClient({ baudRate: 9600 });
199
+
200
+ client.connect().subscribe({
201
+ next: () => console.log('接続しました'),
202
+ error: (error) => {
203
+ if (error instanceof SerialError) {
204
+ switch (error.code) {
205
+ case SerialErrorCode.BROWSER_NOT_SUPPORTED:
206
+ console.error('ブラウザが Web Serial API をサポートしていません');
207
+ break;
208
+ case SerialErrorCode.PORT_NOT_AVAILABLE:
209
+ console.error('シリアルポートが利用できません');
210
+ break;
211
+ case SerialErrorCode.CONNECTION_LOST:
212
+ console.error('接続が切断されました');
213
+ break;
214
+ default:
215
+ console.error('シリアルエラー:', error.message);
216
+ }
217
+ } else {
218
+ console.error('不明なエラー:', error);
219
+ }
220
+ },
221
+ });
222
+ ```
223
+
224
+ ### ポートフィルタリング
225
+
226
+ ```typescript
227
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
228
+
229
+ // USB ベンダー ID でポートをフィルタリング
230
+ const client = createSerialClient({
231
+ baudRate: 9600,
232
+ filters: [{ usbVendorId: 0x1234 }, { usbVendorId: 0x5678 }],
233
+ });
234
+
235
+ // 特定のポートをリクエスト
236
+ client.requestPort().subscribe({
237
+ next: (port) => {
238
+ console.log('ポートが選択されました:', port);
239
+ // 選択されたポートに接続
240
+ client.connect(port).subscribe({
241
+ next: () => console.log('フィルタリングされたポートに接続しました'),
242
+ error: (error) => console.error('接続エラー:', error),
243
+ });
244
+ },
245
+ error: (error) => console.error('ポートリクエストエラー:', error),
246
+ });
247
+ ```
248
+
249
+ ## API リファレンス
250
+
251
+ ### `createSerialClient(options?)`
252
+
253
+ 新しい `SerialClient` インスタンスを作成します。
254
+
255
+ **パラメータ:**
256
+
257
+ - `options?` (オプション): `SerialClientOptions` - シリアルクライアントの設定オプション
258
+
259
+ **戻り値:** `SerialClient` - 新しい SerialClient インスタンス
260
+
261
+ **例:**
262
+
263
+ ```typescript
264
+ const client = createSerialClient({
265
+ baudRate: 9600,
266
+ dataBits: 8,
267
+ stopBits: 1,
268
+ parity: 'none',
269
+ });
270
+ ```
271
+
272
+ ### `SerialClient` インターフェース
273
+
274
+ シリアルポートと対話するためのメインインターフェースです。
275
+
276
+ #### メソッド
277
+
278
+ ##### `requestPort(): Observable<SerialPort>`
279
+
280
+ ユーザーからシリアルポートをリクエストします。ポート選択のためのブラウザダイアログを開きます。
281
+
282
+ **戻り値:** `Observable<SerialPort>` - 選択された `SerialPort` インスタンスを発行
283
+
284
+ ##### `getPorts(): Observable<SerialPort[]>`
285
+
286
+ ユーザーが以前にアクセスを許可したすべての利用可能なシリアルポートを取得します。
287
+
288
+ **戻り値:** `Observable<SerialPort[]>` - 利用可能な `SerialPort` インスタンスの配列を発行
289
+
290
+ ##### `connect(port?: SerialPort): Observable<void>`
291
+
292
+ シリアルポートに接続します。ポートが提供されない場合、ユーザーにリクエストします。
293
+
294
+ **パラメータ:**
295
+
296
+ - `port?` (オプション): `SerialPort` - 接続するポート
297
+
298
+ **戻り値:** `Observable<void>` - ポートが開かれたときに完了
299
+
300
+ ##### `disconnect(): Observable<void>`
301
+
302
+ シリアルポートから切断します。
303
+
304
+ **戻り値:** `Observable<void>` - ポートが閉じられたときに完了
305
+
306
+ ##### `getReadStream(): Observable<Uint8Array>`
307
+
308
+ シリアルポートから読み取ったデータを発行する Observable を取得します。
309
+
310
+ **戻り値:** `Observable<Uint8Array>` - データが受信されると `Uint8Array` チャンクを発行
311
+
312
+ ##### `writeStream(data$: Observable<Uint8Array>): Observable<void>`
313
+
314
+ Observable ストリームからシリアルポートにデータを書き込みます。
315
+
316
+ **パラメータ:**
317
+
318
+ - `data$`: `Observable<Uint8Array>` - 書き込む `Uint8Array` チャンクを発行する Observable
319
+
320
+ **戻り値:** `Observable<void>` - 書き込みが完了したときに完了
321
+
322
+ ##### `write(data: Uint8Array): Observable<void>`
323
+
324
+ シリアルポートに単一のデータチャンクを書き込みます。
325
+
326
+ **パラメータ:**
327
+
328
+ - `data`: `Uint8Array` - 書き込むデータ
329
+
330
+ **戻り値:** `Observable<void>` - データが書き込まれたときに完了
331
+
332
+ #### プロパティ
333
+
334
+ - `connected: boolean` - ポートが現在開いているかどうかを示す読み取り専用プロパティ
335
+ - `currentPort: SerialPort | null` - 現在の `SerialPort` インスタンス、または接続されていない場合は `null` の読み取り専用プロパティ
336
+
337
+ ### `SerialClientOptions` インターフェース
338
+
339
+ `SerialClient` を作成するための設定オプションです。
340
+
341
+ ```typescript
342
+ interface SerialClientOptions {
343
+ baudRate?: number; // デフォルト: 9600
344
+ dataBits?: 7 | 8; // デフォルト: 8
345
+ stopBits?: 1 | 2; // デフォルト: 1
346
+ parity?: 'none' | 'even' | 'odd'; // デフォルト: 'none'
347
+ bufferSize?: number; // デフォルト: 255
348
+ flowControl?: 'none' | 'hardware'; // デフォルト: 'none'
349
+ filters?: SerialPortFilter[]; // オプションのポートフィルター
350
+ }
351
+ ```
352
+
353
+ **オプション:**
354
+
355
+ - `baudRate` (オプション): 通信速度(ビット/秒)。デフォルト: `9600`
356
+ - `dataBits` (オプション): 文字あたりのデータビット数。`7` または `8`。デフォルト: `8`
357
+ - `stopBits` (オプション): ストップビット数。`1` または `2`。デフォルト: `1`
358
+ - `parity` (オプション): パリティチェックモード。`'none'`、`'even'`、または `'odd'`。デフォルト: `'none'`
359
+ - `bufferSize` (オプション): 読み取りバッファのサイズ。デフォルト: `255`
360
+ - `flowControl` (オプション): フロー制御モード。`'none'` または `'hardware'`。デフォルト: `'none'`
361
+ - `filters` (オプション): 利用可能なポートをフィルタリングする `SerialPortFilter` オブジェクトの配列
362
+
363
+ ### エラーハンドリング
364
+
365
+ #### `SerialError` クラス
366
+
367
+ シリアルポート操作のためのカスタムエラークラスです。
368
+
369
+ ```typescript
370
+ class SerialError extends Error {
371
+ readonly code: SerialErrorCode;
372
+ readonly originalError?: Error;
373
+
374
+ is(code: SerialErrorCode): boolean;
375
+ }
376
+ ```
377
+
378
+ **プロパティ:**
379
+
380
+ - `code`: `SerialErrorCode` - エラーコード
381
+ - `originalError?`: `Error` - このエラーを引き起こした元のエラー(存在する場合)
382
+
383
+ **メソッド:**
384
+
385
+ - `is(code: SerialErrorCode): boolean` - エラーが特定のエラーコードと一致するかチェック
386
+
387
+ #### `SerialErrorCode` 列挙型
388
+
389
+ さまざまなタイプのシリアルポートエラーのエラーコード:
390
+
391
+ - `BROWSER_NOT_SUPPORTED` - ブラウザが Web Serial API をサポートしていない
392
+ - `PORT_NOT_AVAILABLE` - シリアルポートが利用できない
393
+ - `PORT_OPEN_FAILED` - シリアルポートを開くのに失敗した
394
+ - `PORT_ALREADY_OPEN` - シリアルポートは既に開いている
395
+ - `PORT_NOT_OPEN` - シリアルポートが開いていない
396
+ - `READ_FAILED` - シリアルポートからの読み取りに失敗した
397
+ - `WRITE_FAILED` - シリアルポートへの書き込みに失敗した
398
+ - `CONNECTION_LOST` - シリアルポート接続が切断された
399
+ - `INVALID_FILTER_OPTIONS` - 無効なフィルターオプション
400
+ - `OPERATION_CANCELLED` - 操作がキャンセルされた
401
+ - `UNKNOWN` - 不明なエラー
402
+
403
+ ### ブラウザ検出ユーティリティ
404
+
405
+ #### `isBrowserSupported(): boolean`
406
+
407
+ ブラウザが Web Serial API をサポートしているかチェックします(例外を投げないバージョン)。
408
+
409
+ **戻り値:** `boolean` - サポートされている場合は `true`、それ以外は `false`
410
+
411
+ #### `checkBrowserSupport(): void`
412
+
413
+ ブラウザが Web Serial API をサポートしているかチェックします。サポートされていない場合は `SerialError` を投げます。
414
+
415
+ **例外:** Web Serial API をサポートしていない場合、`BROWSER_NOT_SUPPORTED` コードを持つ `SerialError` を投げます
416
+
417
+ #### `detectBrowserType(): BrowserType`
418
+
419
+ ユーザーエージェントからブラウザタイプを検出します。
420
+
421
+ **戻り値:** `BrowserType` - `CHROME`、`EDGE`、`OPERA`、または `UNKNOWN` のいずれか
422
+
423
+ #### `hasWebSerialSupport(): boolean`
424
+
425
+ 機能検出を使用してブラウザが Web Serial API サポートを持っているかチェックします。
426
+
427
+ **戻り値:** `boolean` - Web Serial API が利用可能な場合は `true`、それ以外は `false`
428
+
429
+ ### I/O ユーティリティ
430
+
431
+ #### `readableToObservable(stream: ReadableStream<Uint8Array>): Observable<Uint8Array>`
432
+
433
+ `ReadableStream` を RxJS `Observable` に変換します。
434
+
435
+ **パラメータ:**
436
+
437
+ - `stream`: `ReadableStream<Uint8Array>` - 変換するストリーム
438
+
439
+ **戻り値:** `Observable<Uint8Array>` - データチャンクを発行する Observable
440
+
441
+ #### `observableToWritable(observable: Observable<Uint8Array>): WritableStream<Uint8Array>`
442
+
443
+ RxJS `Observable` を `WritableStream` に変換します。
444
+
445
+ **パラメータ:**
446
+
447
+ - `observable`: `Observable<Uint8Array>` - 変換する observable
448
+
449
+ **戻り値:** `WritableStream<Uint8Array>` - observable からデータを書き込む書き込み可能なストリーム
450
+
451
+ #### `subscribeToWritable(observable: Observable<Uint8Array>, stream: WritableStream<Uint8Array>): { unsubscribe: () => void }`
452
+
453
+ Observable を購読し、その値を WritableStream に書き込みます。
454
+
455
+ **パラメータ:**
456
+
457
+ - `observable`: `Observable<Uint8Array>` - 購読する observable
458
+ - `stream`: `WritableStream<Uint8Array>` - 書き込むストリーム
459
+
460
+ **戻り値:** `unsubscribe()` メソッドを持つ購読オブジェクト
461
+
462
+ ## フレームワーク別の例
463
+
464
+ このリポジトリには、さまざまなフレームワークで web-serial-rxjs を使用する方法を示すサンプルアプリケーションが含まれています:
465
+
466
+ - **[Vanilla JavaScript](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-vanilla-js)** - バニラ JavaScript での基本的な使用方法
467
+ - **[Vanilla TypeScript](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-vanilla-ts)** - RxJS を使用した TypeScript の例
468
+ - **[React](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-react)** - カスタムフック(`useSerialClient`)を使用した React の例
469
+ - **[Vue](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-vue)** - Composition API を使用した Vue 3 の例
470
+ - **[Svelte](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-svelte)** - Svelte Store を使用した Svelte の例
471
+ - **[Angular](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-angular)** - Service を使用した Angular の例
472
+
473
+ 各例には、セットアップと使用方法の説明を含む README が含まれています。
474
+
475
+ ## 高度な使用方法
476
+
477
+ ### Observable パターン
478
+
479
+ RxJS オペレーターを使用してシリアルデータを処理できます:
480
+
481
+ ```typescript
482
+ import { map, filter, bufferTime } from 'rxjs/operators';
483
+
484
+ client
485
+ .getReadStream()
486
+ .pipe(
487
+ map((data: Uint8Array) => {
488
+ const decoder = new TextDecoder('utf-8');
489
+ return decoder.decode(data);
490
+ }),
491
+ filter((text) => text.trim().length > 0),
492
+ bufferTime(1000), // 1 秒間メッセージをバッファリング
493
+ )
494
+ .subscribe({
495
+ next: (messages) => {
496
+ console.log('バッファリングされたメッセージ:', messages);
497
+ },
498
+ });
499
+ ```
500
+
501
+ ### ストリーム処理
502
+
503
+ RxJS オペレーターでデータストリームを処理:
504
+
505
+ ```typescript
506
+ import { map, scan, debounceTime } from 'rxjs/operators';
507
+
508
+ // 受信データを累積
509
+ client
510
+ .getReadStream()
511
+ .pipe(
512
+ map((data: Uint8Array) => {
513
+ const decoder = new TextDecoder('utf-8');
514
+ return decoder.decode(data);
515
+ }),
516
+ scan((acc, current) => acc + current, ''),
517
+ debounceTime(500),
518
+ )
519
+ .subscribe({
520
+ next: (accumulated) => {
521
+ console.log('累積データ:', accumulated);
522
+ },
523
+ });
524
+ ```
525
+
526
+ ### カスタムフィルター
527
+
528
+ ポートフィルターを使用して利用可能なポートを制限:
529
+
530
+ ```typescript
531
+ const client = createSerialClient({
532
+ baudRate: 9600,
533
+ filters: [
534
+ { usbVendorId: 0x1234, usbProductId: 0x5678 },
535
+ { usbVendorId: 0xabcd },
536
+ ],
537
+ });
538
+ ```
539
+
540
+ ### エラー回復
541
+
542
+ エラー回復パターンを実装:
543
+
544
+ ```typescript
545
+ import { retry, catchError } from 'rxjs/operators';
546
+ import { of } from 'rxjs';
547
+
548
+ client
549
+ .getReadStream()
550
+ .pipe(
551
+ retry({
552
+ count: 3,
553
+ delay: 1000,
554
+ }),
555
+ catchError((error) => {
556
+ console.error('リトライ後も失敗:', error);
557
+ return of(null); // 空の observable を返す
558
+ }),
559
+ )
560
+ .subscribe({
561
+ next: (data) => {
562
+ if (data) {
563
+ console.log('受信:', data);
564
+ }
565
+ },
566
+ });
567
+ ```
568
+
569
+ ## 開発とリリース戦略
570
+
571
+ このプロジェクトは**trunk-based開発**アプローチに従います:
572
+
573
+ - **`main`ブランチ**: 常にリリース可能な状態
574
+ - **短命ブランチ**: `feature/*`, `fix/*`, `docs/*` はプルリクエスト用
575
+ - **リリース**: ブランチではなくGitタグ(例: `v1.0.0`)で管理
576
+ - **バージョン保守**: 複数のメジャーバージョンを保守する必要がある場合のみ `release/v*` ブランチを追加
577
+
578
+ 詳細な貢献ガイドラインについては、[CONTRIBUTING.ja.md](https://github.com/gurezo/web-serial-rxjs/blob/main/CONTRIBUTING.ja.md) を参照してください。
579
+
580
+ ## 貢献
581
+
582
+ 貢献を歓迎します!詳細については、[貢献ガイド](https://github.com/gurezo/web-serial-rxjs/blob/main/CONTRIBUTING.ja.md)を参照してください:
583
+
584
+ - 開発環境のセットアップ
585
+ - コードスタイルガイドライン
586
+ - コミットメッセージの規約
587
+ - プルリクエストのプロセス
588
+
589
+ 英語版の貢献ガイドは [CONTRIBUTING.md](https://github.com/gurezo/web-serial-rxjs/blob/main/CONTRIBUTING.md) を参照してください。
590
+
591
+ ## ライセンス
592
+
593
+ このプロジェクトは MIT ライセンスの下で公開されています。詳細は [LICENSE](https://github.com/gurezo/web-serial-rxjs/blob/main/LICENSE) ファイルを参照してください。
594
+
595
+ ## リンク
596
+
597
+ - **GitHub リポジトリ**: [https://github.com/gurezo/web-serial-rxjs](https://github.com/gurezo/web-serial-rxjs)
598
+ - **イシュー**: [https://github.com/gurezo/web-serial-rxjs/issues](https://github.com/gurezo/web-serial-rxjs/issues)
599
+ - **Web Serial API 仕様**: [https://wicg.github.io/serial/](https://wicg.github.io/serial/)
package/README.md ADDED
@@ -0,0 +1,598 @@
1
+ # web-serial-rxjs
2
+
3
+ A TypeScript library that provides a reactive RxJS-based wrapper for the Web Serial API, enabling easy serial port communication in web applications.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Features](#features)
8
+ - [Browser Support](#browser-support)
9
+ - [Installation](#installation)
10
+ - [Quick Start](#quick-start)
11
+ - [Usage Examples](#usage-examples)
12
+ - [API Reference](#api-reference)
13
+ - [Framework Examples](#framework-examples)
14
+ - [Advanced Usage](#advanced-usage)
15
+ - [Contributing](#contributing)
16
+ - [License](#license)
17
+ - [Links](#links)
18
+
19
+ ## Features
20
+
21
+ - **RxJS-based reactive API**: Leverage the power of RxJS Observables for reactive serial port communication
22
+ - **TypeScript support**: Full TypeScript type definitions included
23
+ - **Browser detection**: Built-in browser support detection and error handling
24
+ - **Error handling**: Comprehensive error handling with custom error classes and error codes
25
+ - **Framework agnostic**: Works with any JavaScript/TypeScript framework or vanilla JavaScript
26
+
27
+ ## Browser Support
28
+
29
+ The Web Serial API is currently only supported in Chromium-based browsers:
30
+
31
+ - **Chrome** 89+
32
+ - **Edge** 89+
33
+ - **Opera** 75+
34
+
35
+ The library includes built-in browser detection utilities to check for Web Serial API support before attempting to use it.
36
+
37
+ ## Installation
38
+
39
+ Install the package using npm or pnpm:
40
+
41
+ ```bash
42
+ npm install @gurezo/web-serial-rxjs
43
+ # or
44
+ pnpm add @gurezo/web-serial-rxjs
45
+ ```
46
+
47
+ ### Peer Dependencies
48
+
49
+ This library requires RxJS as a peer dependency:
50
+
51
+ ```bash
52
+ npm install rxjs
53
+ # or
54
+ pnpm add rxjs
55
+ ```
56
+
57
+ **Minimum required version**: RxJS ^7.8.0
58
+
59
+ ## Quick Start
60
+
61
+ Here's a simple example to get you started:
62
+
63
+ ```typescript
64
+ import {
65
+ createSerialClient,
66
+ isBrowserSupported,
67
+ } from '@gurezo/web-serial-rxjs';
68
+
69
+ // Check browser support
70
+ if (!isBrowserSupported()) {
71
+ console.error('Web Serial API is not supported in this browser');
72
+ return;
73
+ }
74
+
75
+ // Create a serial client
76
+ const client = createSerialClient({ baudRate: 9600 });
77
+
78
+ // Connect to a serial port
79
+ client.connect().subscribe({
80
+ next: () => {
81
+ console.log('Connected to serial port');
82
+
83
+ // Read data from the serial port
84
+ client.getReadStream().subscribe({
85
+ next: (data: Uint8Array) => {
86
+ const decoder = new TextDecoder('utf-8');
87
+ const text = decoder.decode(data);
88
+ console.log('Received:', text);
89
+ },
90
+ error: (error) => {
91
+ console.error('Read error:', error);
92
+ },
93
+ });
94
+
95
+ // Write data to the serial port
96
+ const encoder = new TextEncoder();
97
+ const data = encoder.encode('Hello, Serial Port!\n');
98
+ client.write(data).subscribe({
99
+ next: () => console.log('Data written'),
100
+ error: (error) => console.error('Write error:', error),
101
+ });
102
+ },
103
+ error: (error) => {
104
+ console.error('Connection error:', error);
105
+ },
106
+ });
107
+ ```
108
+
109
+ ## Usage Examples
110
+
111
+ ### Basic Connection
112
+
113
+ ```typescript
114
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
115
+
116
+ const client = createSerialClient({
117
+ baudRate: 115200,
118
+ dataBits: 8,
119
+ stopBits: 1,
120
+ parity: 'none',
121
+ });
122
+
123
+ // Connect (will prompt user to select a port)
124
+ client.connect().subscribe({
125
+ next: () => console.log('Connected'),
126
+ error: (error) => console.error('Connection failed:', error),
127
+ });
128
+ ```
129
+
130
+ ### Reading Data
131
+
132
+ ```typescript
133
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
134
+ import { map } from 'rxjs/operators';
135
+
136
+ const client = createSerialClient({ baudRate: 9600 });
137
+
138
+ client.connect().subscribe({
139
+ next: () => {
140
+ // Read and decode data
141
+ client
142
+ .getReadStream()
143
+ .pipe(
144
+ map((data: Uint8Array) => {
145
+ const decoder = new TextDecoder('utf-8');
146
+ return decoder.decode(data);
147
+ }),
148
+ )
149
+ .subscribe({
150
+ next: (text) => console.log('Received:', text),
151
+ error: (error) => console.error('Read error:', error),
152
+ });
153
+ },
154
+ });
155
+ ```
156
+
157
+ ### Writing Data
158
+
159
+ ```typescript
160
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
161
+ import { from } from 'rxjs';
162
+
163
+ const client = createSerialClient({ baudRate: 9600 });
164
+
165
+ client.connect().subscribe({
166
+ next: () => {
167
+ // Write a single chunk
168
+ const encoder = new TextEncoder();
169
+ const data = encoder.encode('Hello\n');
170
+ client.write(data).subscribe({
171
+ next: () => console.log('Written'),
172
+ error: (error) => console.error('Write error:', error),
173
+ });
174
+
175
+ // Write from an Observable stream
176
+ const messages = ['Message 1\n', 'Message 2\n', 'Message 3\n'];
177
+ const dataStream$ = from(messages).pipe(
178
+ map((msg) => new TextEncoder().encode(msg)),
179
+ );
180
+ client.writeStream(dataStream$).subscribe({
181
+ next: () => console.log('All messages written'),
182
+ error: (error) => console.error('Stream write error:', error),
183
+ });
184
+ },
185
+ });
186
+ ```
187
+
188
+ ### Error Handling
189
+
190
+ ```typescript
191
+ import {
192
+ createSerialClient,
193
+ SerialError,
194
+ SerialErrorCode,
195
+ } from '@gurezo/web-serial-rxjs';
196
+
197
+ const client = createSerialClient({ baudRate: 9600 });
198
+
199
+ client.connect().subscribe({
200
+ next: () => console.log('Connected'),
201
+ error: (error) => {
202
+ if (error instanceof SerialError) {
203
+ switch (error.code) {
204
+ case SerialErrorCode.BROWSER_NOT_SUPPORTED:
205
+ console.error('Browser does not support Web Serial API');
206
+ break;
207
+ case SerialErrorCode.PORT_NOT_AVAILABLE:
208
+ console.error('Serial port is not available');
209
+ break;
210
+ case SerialErrorCode.CONNECTION_LOST:
211
+ console.error('Connection lost');
212
+ break;
213
+ default:
214
+ console.error('Serial error:', error.message);
215
+ }
216
+ } else {
217
+ console.error('Unknown error:', error);
218
+ }
219
+ },
220
+ });
221
+ ```
222
+
223
+ ### Port Filtering
224
+
225
+ ```typescript
226
+ import { createSerialClient } from '@gurezo/web-serial-rxjs';
227
+
228
+ // Filter ports by USB vendor ID
229
+ const client = createSerialClient({
230
+ baudRate: 9600,
231
+ filters: [{ usbVendorId: 0x1234 }, { usbVendorId: 0x5678 }],
232
+ });
233
+
234
+ // Request a specific port
235
+ client.requestPort().subscribe({
236
+ next: (port) => {
237
+ console.log('Port selected:', port);
238
+ // Connect to the selected port
239
+ client.connect(port).subscribe({
240
+ next: () => console.log('Connected to filtered port'),
241
+ error: (error) => console.error('Connection error:', error),
242
+ });
243
+ },
244
+ error: (error) => console.error('Port request error:', error),
245
+ });
246
+ ```
247
+
248
+ ## API Reference
249
+
250
+ ### `createSerialClient(options?)`
251
+
252
+ Creates a new `SerialClient` instance.
253
+
254
+ **Parameters:**
255
+
256
+ - `options?` (optional): `SerialClientOptions` - Configuration options for the serial client
257
+
258
+ **Returns:** `SerialClient` - A new SerialClient instance
259
+
260
+ **Example:**
261
+
262
+ ```typescript
263
+ const client = createSerialClient({
264
+ baudRate: 9600,
265
+ dataBits: 8,
266
+ stopBits: 1,
267
+ parity: 'none',
268
+ });
269
+ ```
270
+
271
+ ### `SerialClient` Interface
272
+
273
+ The main interface for interacting with serial ports.
274
+
275
+ #### Methods
276
+
277
+ ##### `requestPort(): Observable<SerialPort>`
278
+
279
+ Requests a serial port from the user. Opens a browser dialog for port selection.
280
+
281
+ **Returns:** `Observable<SerialPort>` - Emits the selected `SerialPort` instance
282
+
283
+ ##### `getPorts(): Observable<SerialPort[]>`
284
+
285
+ Gets all available serial ports that the user has previously granted access to.
286
+
287
+ **Returns:** `Observable<SerialPort[]>` - Emits an array of available `SerialPort` instances
288
+
289
+ ##### `connect(port?: SerialPort): Observable<void>`
290
+
291
+ Connects to a serial port. If no port is provided, will request one from the user.
292
+
293
+ **Parameters:**
294
+
295
+ - `port?` (optional): `SerialPort` - The port to connect to
296
+
297
+ **Returns:** `Observable<void>` - Completes when the port is opened
298
+
299
+ ##### `disconnect(): Observable<void>`
300
+
301
+ Disconnects from the serial port.
302
+
303
+ **Returns:** `Observable<void>` - Completes when the port is closed
304
+
305
+ ##### `getReadStream(): Observable<Uint8Array>`
306
+
307
+ Gets an Observable that emits data read from the serial port.
308
+
309
+ **Returns:** `Observable<Uint8Array>` - Emits `Uint8Array` chunks as data is received
310
+
311
+ ##### `writeStream(data$: Observable<Uint8Array>): Observable<void>`
312
+
313
+ Writes data to the serial port from an Observable stream.
314
+
315
+ **Parameters:**
316
+
317
+ - `data$`: `Observable<Uint8Array>` - Observable that emits `Uint8Array` chunks to write
318
+
319
+ **Returns:** `Observable<void>` - Completes when writing is finished
320
+
321
+ ##### `write(data: Uint8Array): Observable<void>`
322
+
323
+ Writes a single chunk of data to the serial port.
324
+
325
+ **Parameters:**
326
+
327
+ - `data`: `Uint8Array` - Data to write
328
+
329
+ **Returns:** `Observable<void>` - Completes when the data is written
330
+
331
+ #### Properties
332
+
333
+ - `connected: boolean` - Read-only property indicating if the port is currently open
334
+ - `currentPort: SerialPort | null` - Read-only property with the current `SerialPort` instance, or `null` if not connected
335
+
336
+ ### `SerialClientOptions` Interface
337
+
338
+ Configuration options for creating a `SerialClient`.
339
+
340
+ ```typescript
341
+ interface SerialClientOptions {
342
+ baudRate?: number; // Default: 9600
343
+ dataBits?: 7 | 8; // Default: 8
344
+ stopBits?: 1 | 2; // Default: 1
345
+ parity?: 'none' | 'even' | 'odd'; // Default: 'none'
346
+ bufferSize?: number; // Default: 255
347
+ flowControl?: 'none' | 'hardware'; // Default: 'none'
348
+ filters?: SerialPortFilter[]; // Optional port filters
349
+ }
350
+ ```
351
+
352
+ **Options:**
353
+
354
+ - `baudRate` (optional): Communication speed in bits per second. Default: `9600`
355
+ - `dataBits` (optional): Number of data bits per character. Either `7` or `8`. Default: `8`
356
+ - `stopBits` (optional): Number of stop bits. Either `1` or `2`. Default: `1`
357
+ - `parity` (optional): Parity checking mode. `'none'`, `'even'`, or `'odd'`. Default: `'none'`
358
+ - `bufferSize` (optional): Size of the read buffer. Default: `255`
359
+ - `flowControl` (optional): Flow control mode. `'none'` or `'hardware'`. Default: `'none'`
360
+ - `filters` (optional): Array of `SerialPortFilter` objects to filter available ports
361
+
362
+ ### Error Handling
363
+
364
+ #### `SerialError` Class
365
+
366
+ Custom error class for serial port operations.
367
+
368
+ ```typescript
369
+ class SerialError extends Error {
370
+ readonly code: SerialErrorCode;
371
+ readonly originalError?: Error;
372
+
373
+ is(code: SerialErrorCode): boolean;
374
+ }
375
+ ```
376
+
377
+ **Properties:**
378
+
379
+ - `code`: `SerialErrorCode` - The error code
380
+ - `originalError?`: `Error` - The original error that caused this error (if any)
381
+
382
+ **Methods:**
383
+
384
+ - `is(code: SerialErrorCode): boolean` - Check if the error matches a specific error code
385
+
386
+ #### `SerialErrorCode` Enum
387
+
388
+ Error codes for different types of serial port errors:
389
+
390
+ - `BROWSER_NOT_SUPPORTED` - Browser does not support Web Serial API
391
+ - `PORT_NOT_AVAILABLE` - Serial port is not available
392
+ - `PORT_OPEN_FAILED` - Failed to open serial port
393
+ - `PORT_ALREADY_OPEN` - Serial port is already open
394
+ - `PORT_NOT_OPEN` - Serial port is not open
395
+ - `READ_FAILED` - Failed to read from serial port
396
+ - `WRITE_FAILED` - Failed to write to serial port
397
+ - `CONNECTION_LOST` - Serial port connection lost
398
+ - `INVALID_FILTER_OPTIONS` - Invalid filter options
399
+ - `OPERATION_CANCELLED` - Operation was cancelled
400
+ - `UNKNOWN` - Unknown error
401
+
402
+ ### Browser Detection Utilities
403
+
404
+ #### `isBrowserSupported(): boolean`
405
+
406
+ Checks if the browser supports the Web Serial API (non-throwing version).
407
+
408
+ **Returns:** `boolean` - `true` if supported, `false` otherwise
409
+
410
+ #### `checkBrowserSupport(): void`
411
+
412
+ Checks if the browser supports the Web Serial API. Throws a `SerialError` if not supported.
413
+
414
+ **Throws:** `SerialError` with code `BROWSER_NOT_SUPPORTED` if the browser doesn't support Web Serial API
415
+
416
+ #### `detectBrowserType(): BrowserType`
417
+
418
+ Detects the browser type from the user agent.
419
+
420
+ **Returns:** `BrowserType` - One of `CHROME`, `EDGE`, `OPERA`, or `UNKNOWN`
421
+
422
+ #### `hasWebSerialSupport(): boolean`
423
+
424
+ Checks if the browser has Web Serial API support using feature detection.
425
+
426
+ **Returns:** `boolean` - `true` if Web Serial API is available, `false` otherwise
427
+
428
+ ### I/O Utilities
429
+
430
+ #### `readableToObservable(stream: ReadableStream<Uint8Array>): Observable<Uint8Array>`
431
+
432
+ Converts a `ReadableStream` to an RxJS `Observable`.
433
+
434
+ **Parameters:**
435
+
436
+ - `stream`: `ReadableStream<Uint8Array>` - The stream to convert
437
+
438
+ **Returns:** `Observable<Uint8Array>` - Observable that emits data chunks
439
+
440
+ #### `observableToWritable(observable: Observable<Uint8Array>): WritableStream<Uint8Array>`
441
+
442
+ Converts an RxJS `Observable` to a `WritableStream`.
443
+
444
+ **Parameters:**
445
+
446
+ - `observable`: `Observable<Uint8Array>` - The observable to convert
447
+
448
+ **Returns:** `WritableStream<Uint8Array>` - Writable stream that writes data from the observable
449
+
450
+ #### `subscribeToWritable(observable: Observable<Uint8Array>, stream: WritableStream<Uint8Array>): { unsubscribe: () => void }`
451
+
452
+ Subscribes to an Observable and writes its values to a WritableStream.
453
+
454
+ **Parameters:**
455
+
456
+ - `observable`: `Observable<Uint8Array>` - The observable to subscribe to
457
+ - `stream`: `WritableStream<Uint8Array>` - The stream to write to
458
+
459
+ **Returns:** Subscription object with `unsubscribe()` method
460
+
461
+ ## Framework Examples
462
+
463
+ This repository includes example applications demonstrating how to use web-serial-rxjs with different frameworks:
464
+
465
+ - **[Vanilla JavaScript](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-vanilla-js)** - Basic usage with vanilla JavaScript
466
+ - **[Vanilla TypeScript](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-vanilla-ts)** - TypeScript example with RxJS
467
+ - **[React](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-react)** - React example with custom hook (`useSerialClient`)
468
+ - **[Vue](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-vue)** - Vue 3 example using Composition API
469
+ - **[Svelte](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-svelte)** - Svelte example using Svelte Store
470
+ - **[Angular](https://github.com/gurezo/web-serial-rxjs/tree/main/apps/example-angular)** - Angular example using a Service
471
+
472
+ Each example includes a README with setup and usage instructions.
473
+
474
+ ## Advanced Usage
475
+
476
+ ### Observable Patterns
477
+
478
+ You can use RxJS operators to process serial data:
479
+
480
+ ```typescript
481
+ import { map, filter, bufferTime } from 'rxjs/operators';
482
+
483
+ client
484
+ .getReadStream()
485
+ .pipe(
486
+ map((data: Uint8Array) => {
487
+ const decoder = new TextDecoder('utf-8');
488
+ return decoder.decode(data);
489
+ }),
490
+ filter((text) => text.trim().length > 0),
491
+ bufferTime(1000), // Buffer messages for 1 second
492
+ )
493
+ .subscribe({
494
+ next: (messages) => {
495
+ console.log('Buffered messages:', messages);
496
+ },
497
+ });
498
+ ```
499
+
500
+ ### Stream Processing
501
+
502
+ Process data streams with RxJS operators:
503
+
504
+ ```typescript
505
+ import { map, scan, debounceTime } from 'rxjs/operators';
506
+
507
+ // Accumulate received data
508
+ client
509
+ .getReadStream()
510
+ .pipe(
511
+ map((data: Uint8Array) => {
512
+ const decoder = new TextDecoder('utf-8');
513
+ return decoder.decode(data);
514
+ }),
515
+ scan((acc, current) => acc + current, ''),
516
+ debounceTime(500),
517
+ )
518
+ .subscribe({
519
+ next: (accumulated) => {
520
+ console.log('Accumulated data:', accumulated);
521
+ },
522
+ });
523
+ ```
524
+
525
+ ### Custom Filters
526
+
527
+ Use port filters to limit available ports:
528
+
529
+ ```typescript
530
+ const client = createSerialClient({
531
+ baudRate: 9600,
532
+ filters: [
533
+ { usbVendorId: 0x1234, usbProductId: 0x5678 },
534
+ { usbVendorId: 0xabcd },
535
+ ],
536
+ });
537
+ ```
538
+
539
+ ### Error Recovery
540
+
541
+ Implement error recovery patterns:
542
+
543
+ ```typescript
544
+ import { retry, catchError } from 'rxjs/operators';
545
+ import { of } from 'rxjs';
546
+
547
+ client
548
+ .getReadStream()
549
+ .pipe(
550
+ retry({
551
+ count: 3,
552
+ delay: 1000,
553
+ }),
554
+ catchError((error) => {
555
+ console.error('Failed after retries:', error);
556
+ return of(null); // Return empty observable
557
+ }),
558
+ )
559
+ .subscribe({
560
+ next: (data) => {
561
+ if (data) {
562
+ console.log('Received:', data);
563
+ }
564
+ },
565
+ });
566
+ ```
567
+
568
+ ## Development and Release Strategy
569
+
570
+ This project follows a **trunk-based development** approach:
571
+
572
+ - **`main` branch**: Always in a release-ready state
573
+ - **Short-lived branches**: `feature/*`, `fix/*`, `docs/*` for pull requests
574
+ - **Releases**: Managed via Git tags (e.g., `v1.0.0`), not branches
575
+ - **Version maintenance**: `release/v*` branches are added only when needed for maintaining multiple major versions
576
+
577
+ For detailed contribution guidelines, see [CONTRIBUTING.md](https://github.com/gurezo/web-serial-rxjs/blob/main/CONTRIBUTING.md).
578
+
579
+ ## Contributing
580
+
581
+ We welcome contributions! Please see our [Contributing Guide](https://github.com/gurezo/web-serial-rxjs/blob/main/CONTRIBUTING.md) for details on:
582
+
583
+ - Development setup
584
+ - Code style guidelines
585
+ - Commit message conventions
586
+ - Pull request process
587
+
588
+ For Japanese contributors, please see [CONTRIBUTING.ja.md](https://github.com/gurezo/web-serial-rxjs/blob/main/CONTRIBUTING.ja.md).
589
+
590
+ ## License
591
+
592
+ This project is licensed under the MIT License - see the [LICENSE](https://github.com/gurezo/web-serial-rxjs/blob/main/LICENSE) file for details.
593
+
594
+ ## Links
595
+
596
+ - **GitHub Repository**: [https://github.com/gurezo/web-serial-rxjs](https://github.com/gurezo/web-serial-rxjs)
597
+ - **Issues**: [https://github.com/gurezo/web-serial-rxjs/issues](https://github.com/gurezo/web-serial-rxjs/issues)
598
+ - **Web Serial API Specification**: [https://wicg.github.io/serial/](https://wicg.github.io/serial/)
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@gurezo/web-serial-rxjs",
3
+ "version": "0.1.0",
4
+ "description": "A TypeScript library that provides a reactive RxJS-based wrapper for the Web Serial API, enabling easy serial port communication in web applications.",
5
+ "author": "Akihiko Kigure <akihiko.kigure@gmail.com>",
6
+ "license": "MIT",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs"
13
+ },
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "dist/**",
18
+ "README.md",
19
+ "README.ja.md",
20
+ "LICENSE"
21
+ ],
22
+ "dependencies": {},
23
+ "peerDependencies": {
24
+ "rxjs": "^7.8.0"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/gurezo/web-serial-rxjs.git"
29
+ },
30
+ "bugs": {
31
+ "url": "https://github.com/gurezo/web-serial-rxjs/issues"
32
+ },
33
+ "homepage": "https://github.com/gurezo/web-serial-rxjs#readme",
34
+ "keywords": [
35
+ "rxjs",
36
+ "serial",
37
+ "web-serial",
38
+ "serial-port",
39
+ "reactive",
40
+ "observable",
41
+ "typescript",
42
+ "web-serial-api"
43
+ ],
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "scripts": {
48
+ "build": "pnpm run build:types && pnpm run build:bundle && pnpm run build:copy-files",
49
+ "build:types": "tsc --project tsconfig.lib.json",
50
+ "build:bundle": "node esbuild.config.mjs",
51
+ "build:copy-files": "cp ../../LICENSE . 2>/dev/null || true",
52
+ "clean": "rm -rf dist"
53
+ }
54
+ }