本ページは、将来のバイナリ受信 API(例: receiveBytes$)に関する設計検討の記録です。実装仕様ではなく、現行リリースで公開の Uint8Array 受信ストリームを追加するものでもありません。
Parent: #555 · Issue: #545 · 関連: 対応範囲 · receive$ / lines$ / terminalText$ の選び方 · v1 → v2 マイグレーション
当面、receiveBytes$(および公開のワイヤバイト受信ストリーム)は追加しない。
既存のテキスト API や、利用側での Web Serial 直接利用では足りないという具体的な需要が確認され、複雑性の増加に見合う価値がある場合にのみ再検討する。
| 判断 | 状態 |
|---|---|
| いま公開 API にバイナリ受信を追加する | しない(defer) |
| 現行の対応範囲を明文化する | 済み(対応範囲、#540) |
| 将来再検討時の preferred 形状 | 加算的 opt-in(加算的スケッチ) |
| 実装 | 本設計メモとは別 Issue・別 PR |
親 Issue #555 の方針(コア API を安易に増やさない。ギャップが証明されるまで既存ストリームと Recipes を優先)と整合します。
本ライブラリは UTF-8 テキスト中心です。
port.readable(ReadableStream<Uint8Array>)を読み、直後にストリーミング TextDecoder(UTF-8、fatal: false、stream: true)でデコードするreceive$ / lines$ / terminalText$ はいずれも、そのデコード済みチャンク由来の Observable<string>send$(Uint8Array))は対応、バイナリ受信は非対応。バイナリについては送受信が非対称receive$ の 「raw」は行未分割のデコード済みテキストであり、ワイヤ上の生バイトではない公開のエンコーディングオプションやバイトストリームは、現状ありません。
生バイトが必須か、テキストストリームで足りるかを切り分けます。
| 要件 | 現行 API との適合 |
|---|---|
| 改行/プロンプトのテキストプロトコル、シェル、ログ | lines$ / receive$ / terminalText$ を使う |
| デコード済みチャンク上のカスタムテキストフレーミング | receive$ 上で RxJS を合成(高度な使用方法) |
| Modbus RTU、COBS、SLIP、独自バイナリフレーム | コアの対象外 — 利用側(または本ライブラリ外)で扱う |
| UTF-8 以外の文字コード(例: Shift_JIS) | 非対応。バイト先行でもアプリ側デコードが必要 |
| 不正な UTF-8 / 任意オクテットを保持する必要がある | TextDecoder より前のワイヤバイトが必要 — 現行 pump では不可 |
ゲート: 共有セッションのライフサイクルが複数の実利用者に必要で、かつ Web Serial を自前で開く/デコード済みテキストでは合理的に足りない場合にのみ、ライブラリのバイトストリーム追加を検討する。
デコード済み文字列の再エンコード(new TextEncoder().encode(chunk))は、元のワイヤバイトを復元しない。不正 UTF-8 やバイナリプロトコルでは損失がある。receiveBytes$ の代替にしてはいけません。
ReadableStream の read サイズWeb Serial が渡す Uint8Array のサイズはブラウザ/OS 側の都合であり、アプリプロトコルのフレーム境界ではない。
receiveBytes$ も 未フレーミングのバイトチャンク を emit する。長さ前置・CRC・COBS などのフレーミングは利用側の責務 — receive$ の「チャンク境界に依存しない」と同じ(選び方)ポートの readable は1 本で、アクティブな reader も実質 1 つです。テキスト用とバイト用で別々に getReader() を持つことはできません。
バイト API を将来追加する場合の preferred 内部形状:
Uint8Array を読むTextDecoder を実行(receive$ → lines$ / terminalText$)バイトはデコード前に取る必要があります。バイナリを先に TextDecoder へ通し、あとからオクテットを復元しようとするのは誤りです。
現行の受信経路はデコード済みテキストを multicast Subject で流します。pump は読み続け、Web Serial への Observable バックプレッシャーはありません。遅い receive$ 購読者が reader.read() を止めません。
バイトストリームも同じ緊張関係を継承します。
将来再検討時の preferred 方針: pump 駆動の multicast を維持し、無制限 replay は約束しない。遅れて購読した consumer は過去チャンクを見逃す(receive$ と同じ)。過負荷の実例が出てから明示的な上限/破棄/エラー方針を検討する。RxJS だけで TCP 風バックプレッシャーを約束しない。
receive$ / lines$ / terminalText$ との関係| ストリーム | 役割 |
|---|---|
receive$ |
未フレーミングの UTF-8 デコード済みチャンク |
lines$ |
テキスト経路由来の改行区切り文字列 |
terminalText$ |
receive$ の表示向け畳み込み |
receiveBytes$(仮) |
未フレーミングのワイヤ Uint8Array — 並列の opt-in。置換ではない |
receive$ をバイトへ置き換えたり意味を変えたりしない。テキストストリームは安定に保ち、バイト API は加算的であること。
例示のみ — 未実装:
interface SerialSession {
readonly receive$: Observable<string>;
readonly lines$: Observable<string>;
readonly terminalText$: Observable<string>;
/** 仮 — 本リリースには存在しない */
readonly receiveBytes$: Observable<Uint8Array>;
}
将来の実装 PR 向け制約:
receive$ / lines$ / terminalText$ の削除や意味変更をしないTextDecoder任意の後続案(これも defer): バイトのみ利用時にテキストデコードを止めるオプション、明示的バッファ上限。go 条件を満たすまで不要です。
client.bytes$ との互換v1 は client.bytes$ を公開していました。v2+ で削除され、いまもバイナリ受信 API はありません。v2 への移行 を参照してください。
| 方針 | 判断 |
|---|---|
互換のため bytes$ 名をそのまま復活 |
不要。v2 マイグレーションで削除済みと記載済み |
receive$ チャンクへの TextEncoder.encode |
非推奨 — ワイヤバイトを復元しない |
需要があれば加算的 receiveBytes$ |
形状としては妥当。設計なしの v1 意味の暗黙復活はしない |
将来バイト API を出す場合、マイグレーション注記は本ページと migration-v2.md を指し、デコード済み文字列からのロスレス往復を主張しないこと。
機能を再検討するときに使う表です。
| 観点 | 現状の評価 |
|---|---|
| テキスト API やアプリ所有の Web Serial では足りない実ユースケースが確認できるか | コア投入には未確認 |
receiveBytes$ を本ライブラリに置く明確な理由があるか |
今日は不十分 |
receive$ / lines$ / terminalText$ との責務が整理されているか |
はい(本ページ) |
| 同一 loop の fan-out(デコード前の bytes)が理解されているか | はい |
| 遅い購読者/バッファ方針が述べられているか | はい(無制限を約束しない) |
| 破壊的変更なしの加算 API が可能か | はい |
| API・pump 複雑性に見合う価値があるか | 即時追加には否 |
| 実装しない判断が妥当か | はい — 現状の決定 |
実装 Issue を立てる前に、次がすべて真であること:
receive$ / Recipes / 直接 Web Serial では満たせない具体的な利用需要があるreceiveBytes$(または同等)に合意できるreceiveBytes$ 実装client.bytes$ 削除