# 通知設計書 27-Reactデバッグチャンク通知

## 概要

本ドキュメントは、Next.js開発サーバーにおけるReactデバッグチャンク通知の設計を記述する。Reactのデバッグチャンネルから取得したデバッグデータをバイナリメッセージとしてブラウザクライアントへWebSocket経由で送信し、クライアント側でReact DevToolsとの連携に使用する仕組みについて定義する。

### 本通知の処理概要

サーバーサイドのReactレンダリングプロセスからデバッグチャンネル（ReadableStream）経由で取得したデバッグデータチャンクを、バイナリ形式のWebSocketメッセージとしてブラウザクライアントへストリーミング送信する。

**業務上の目的・背景**：React Server Components（RSC）のデバッグ情報をブラウザ側のReact DevToolsに伝達するために、サーバーサイドレンダリング中に生成されるデバッグデータをリアルタイムにストリーミングする必要がある。本通知によりサーバーサイドのコンポーネントツリー情報やレンダリング詳細をクライアント側で利用可能にする。

**通知の送信タイミング**：サーバーサイドレンダリング中にReactデバッグチャンネルからチャンクデータが読み取られるたびに送信される。チャンクの読み取りが完了すると`chunk: null`のメッセージが送信され、ストリーム終了を示す。バッファリング（128KBまで）を行い、効率的な送信を実現する。

**通知の受信者**：HTMLリクエストIDに対応するWebSocketクライアント（App Routerクライアント）。

**通知内容の概要**：バイナリメッセージ（`type: 0`）。先頭バイトがメッセージ種別（0）、2バイト目がリクエストIDの長さ、続いてリクエストID文字列、最後にデバッグデータチャンク（Uint8Array）。チャンクが`null`の場合はストリーム終了を示す。

**期待されるアクション**：クライアントは`getOrCreateDebugChannelReadableWriterPair(requestId)`でWritableStreamを取得し、チャンクデータを書き込む。チャンクが`null`の場合はWriterをクローズする。

## 通知種別

WebSocket バイナリメッセージ（アプリ内通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（ストリーミング、128KBバッファリング） |
| 優先度 | 中 |
| リトライ | なし |

### 送信先決定ロジック

`setReactDebugChannel`メソッドで`htmlRequestId`に対応するクライアントを検索。クライアント接続済みの場合は`connectReactDebugChannel`で即座にストリーミング開始。未接続の場合は`setReactDebugChannelForHtmlRequest`でデバッグチャンネルを保存し、接続時に`connectReactDebugChannelForHtmlRequest`で開始する。

## 通知テンプレート

### メール通知の場合

該当なし（WebSocketバイナリメッセージのため）

### 本文テンプレート

```
バイナリ形式:
[0x00] [requestId長さ: 1byte] [requestId: N bytes] [チャンクデータ: M bytes]

ストリーム終了:
[0x00] [requestId長さ: 1byte] [requestId: N bytes]  (チャンクデータなし)
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| type | メッセージ種別（バイナリ） | 固定値 `0`（REACT_DEBUG_CHUNK） | Yes |
| requestId | リクエスト識別子 | HTMLリクエストID（最大255バイト） | Yes |
| chunk | デバッグデータチャンク | ReactデバッグチャンネルのReadableStream | No（nullの場合ストリーム終了） |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サーバーイベント | ReactデバッグチャンネルからのReadableStreamデータ | チャンク読み取り成功 | デバッグデータの各チャンク |
| サーバーイベント | ReadableStreamの完了 | `entry.done === true` | ストリーム終了シグナル（chunk: null） |
| サーバーイベント | ReadableStreamのエラー | ストリームエラー発生 | エラー後にストリーム終了シグナル送信 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| デバッグチャンネルなし | ReactデバッグチャンネルのReadableStreamが提供されない場合 |
| requestIdが255バイト超過 | `InvariantError`がスローされる |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Reactデバッグチャンネル生成] --> B[setReactDebugChannel呼び出し]
    B --> C{クライアント接続済み?}
    C -->|Yes| D[connectReactDebugChannel]
    C -->|No| E[デバッグチャンネルを保存]
    E --> F[クライアント接続時にconnect]
    D --> G[ReadableStreamをバッファリングpipe]
    G --> H{チャンク読み取り}
    H -->|データあり| I[バイナリメッセージ作成・送信]
    I --> H
    H -->|done| J[chunk:null メッセージ送信]
    H -->|エラー| K[エラーログ + chunk:null送信]
    J --> L[終了]
    K --> L
```

## データベース参照・更新仕様

### 参照テーブル一覧

該当なし（ストリーミングデータをインメモリで処理）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ストリームエラー | ReadableStreamの読み取り中にエラー発生 | `console.error`でエラー出力し、`chunk: null`メッセージでストリーム終了 |
| requestID長さ超過 | requestIdが255バイトを超える | `InvariantError`をスロー |
| クライアント未接続 | HTMLリクエストIDに対応するクライアントがない | デバッグチャンネルを保存し接続待ち（タイムアウトなし） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（128KBバッファリングによる効率化あり） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（開発サーバー稼働中は常時送信可能）

## セキュリティ考慮事項

本通知はローカル開発環境専用。Reactのデバッグデータにはコンポーネントツリー情報が含まれる可能性があるが、開発環境のみで使用されるため外部漏洩リスクは低い。

## 備考

- バイナリメッセージ形式のため、JSON形式のHMRメッセージとは異なるエンコーディングが使用される（`createBinaryHmrMessageData`関数）。
- `createBufferedTransformStream`により128KBまでバッファリングされ、小さなチャンクが多数送信されることを防止している。
- クライアント未接続時のデバッグチャンネル保存にはTODOコメント（タイムアウトによるクリーンアップ）が記載されており、将来の改善が見込まれる。

---

## コードリーディングガイド

本通知を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 42行目: `REACT_DEBUG_CHUNK = 0`のenum定義（バイナリメッセージ種別）。152-159行目: `ReactDebugChunkMessage`インターフェース |
| 1-2 | messages.ts | `packages/next/src/server/dev/messages.ts` | 27-52行目: `createBinaryHmrMessageData`関数の`REACT_DEBUG_CHUNK`ケース。バイナリエンコーディング仕様 |

**読解のコツ**: バイナリメッセージは先頭1バイトでメッセージ種別を識別する。REACT_DEBUG_CHUNK(0)は`[種別(1byte)][requestIdLen(1byte)][requestId(N bytes)][chunk(M bytes)]`の構造。

#### Step 2: サーバー側デバッグチャンネル管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | debug-channel.ts | `packages/next/src/server/dev/debug-channel.ts` | 17-57行目: `connectReactDebugChannel`関数。ReadableStreamのバッファリングpipe、チャンク読み取りループ、終了・エラー処理 |
| 2-2 | debug-channel.ts | `packages/next/src/server/dev/debug-channel.ts` | 59-71行目: `connectReactDebugChannelForHtmlRequest`関数。保存されたデバッグチャンネルの接続 |
| 2-3 | debug-channel.ts | `packages/next/src/server/dev/debug-channel.ts` | 74-81行目: `setReactDebugChannelForHtmlRequest`関数。未接続時のチャンネル保存 |

**主要処理フロー**:
1. **23-27行目**: `debugChannel.readable.pipeThrough(createBufferedTransformStream({maxBufferByteLength: 128 * 1024}))`でバッファリング
2. **29-35行目**: `stop`関数で`chunk: null`メッセージを送信
3. **42-56行目**: `progress`関数でチャンクを読み取り、`sendToClient`でメッセージ送信、再帰的に次チャンクを読み取り

#### Step 3: HotReloaderでのチャンネル設定を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 1171-1198行目: `setReactDebugChannel`メソッド。HTMLリクエストかサブリクエストかの判定、接続済み/未接続の分岐 |
| 3-2 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 919-922行目: `onHMR`内で接続時に`connectReactDebugChannelForHtmlRequest`を呼び出し |

#### Step 4: クライアント側受信処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | 471-487行目: `REACT_DEBUG_CHUNK`受信時の処理。`getOrCreateDebugChannelReadableWriterPair`でWriterを取得し、チャンクをwriteまたはclose |

### プログラム呼び出し階層図

```
setReactDebugChannel(debugChannel, htmlRequestId, requestId) [hot-reloader-turbopack.ts:1171]
    |
    +-- [接続済み] connectReactDebugChannel(requestId, debugChannel, sendToClient)
    |     [debug-channel.ts:17]
    |     |
    |     +-- pipeThrough(createBufferedTransformStream({maxBuffer: 128KB}))
    |     +-- reader.read().then(progress, onError) [ループ]
    |            |
    |            +-- sendToClient({ type: REACT_DEBUG_CHUNK, requestId, chunk })
    |            |     |
    |            |     +-- createBinaryHmrMessageData(message) [messages.ts:27]
    |            |     +-- client.send(binaryData)
    |            |
    |            +-- [done] sendToClient({ type: REACT_DEBUG_CHUNK, requestId, chunk: null })
    |
    +-- [未接続] setReactDebugChannelForHtmlRequest(htmlRequestId, debugChannel)
          |
          +-- [接続時] connectReactDebugChannelForHtmlRequest() [debug-channel.ts:59]
```

### データフロー図

```
[入力]                           [処理]                                [出力]

React Debug Channel        ──> connectReactDebugChannel()          ──> WebSocket バイナリメッセージ
  (ReadableStream<Uint8Array>)  128KB バッファリング                    [0x00][idLen][requestId][chunk]
                                チャンク読み取りループ                     |
                                                                        v
                                                                   [ブラウザクライアント]
                                                                   getOrCreateDebugChannelReadableWriterPair()
                                                                   writer.write(chunk) / writer.close()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | ソース | メッセージ型定義（ReactDebugChunkMessage） |
| messages.ts | `packages/next/src/server/dev/messages.ts` | ソース | バイナリメッセージエンコーディング |
| debug-channel.ts | `packages/next/src/server/dev/debug-channel.ts` | ソース | デバッグチャンネル管理・ストリーミング送信 |
| hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | ソース | setReactDebugChannelメソッド |
| hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | ソース | クライアント側受信処理 |
| web-socket.ts | `packages/next/src/client/dev/hot-reloader/app/web-socket.ts` | ソース | バイナリメッセージのデコード |
