# 通知設計書 26-Turbopackメッセージ通知

## 概要

本ドキュメントは、Next.js開発サーバーにおけるTurbopackメッセージ通知の設計を記述する。Turbopackのモジュール更新情報をブラウザクライアントへWebSocket経由で送信し、クライアント側でHot Module Replacement（HMR）を実行する仕組みについて定義する。

### 本通知の処理概要

Turbopackモードにおいて、ファイル変更に伴うモジュール更新情報（TurbopackUpdate）をブラウザクライアントへWebSocket経由で送信し、クライアント側でモジュールのホットリプレースメントを実行させる。

**業務上の目的・背景**：Turbopackモードの開発サーバーでは、ファイル変更時にTurbopackが差分コンパイルを行い、更新されたモジュール情報を生成する。この情報をブラウザクライアントへ即座に送信することで、ページのフルリロードなしにコード変更を反映するHMRを実現する。これにより、開発中のステート維持と高速なフィードバックループが可能になる。

**通知の送信タイミング**：TurbopackのHMRイベント購読（`subscribeToHmrEvents`）でモジュール更新が検出された時、およびエントリーポイント変更購読で更新が検出された時に、2msのデバウンスを経てバッチ送信される。

**通知の受信者**：WebSocket接続中のすべてのブラウザクライアント。

**通知内容の概要**：`type: "turbopack-message"`と`data: TurbopackUpdate | TurbopackUpdate[]`を含むJSONメッセージ。`TurbopackUpdate`にはモジュールの更新内容、チャンク情報等が含まれる。

**期待されるアクション**：App Routerクライアントは`turbopackHmr.onTurbopackMessage()`でモジュール更新を処理し、`processTurbopackMessage`関数でTurbopack HMRランタイムにメッセージを転送する。更新適用後に`dispatcher.onRefresh()`でUI状態をリセットする。

## 通知種別

WebSocket（HMR）メッセージ（アプリ内通知）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（WebSocket push、2msデバウンス） |
| 優先度 | 高（HMRの中核メッセージ） |
| リトライ | なし（コンパイルエラー時はメッセージキューに保持し、エラー解消後に送信） |

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

`sendTurbopackMessage`関数で全クライアントの`clientStates`の`turbopackUpdates`配列にメッセージを追加し、`sendEnqueuedMessagesDebounce`（2msデバウンス）で実際の送信を行う。コンパイルエラー中は送信が遅延される。

## 通知テンプレート

### メール通知の場合

該当なし（WebSocketメッセージのため）

### 本文テンプレート

```json
{
  "type": "turbopack-message",
  "data": [
    {
      "type": "partial",
      "instruction": {
        "type": "ChunkListUpdate",
        "merged": [...],
        "chunks": {}
      }
    }
  ]
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| type | メッセージ種別 | 固定値 `"turbopack-message"` | Yes |
| data | Turbopack更新ペイロード | `TurbopackUpdate \| TurbopackUpdate[]` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サーバーイベント | HMRイベント購読でモジュール更新検出 | `data.type !== 'issues'` | Turbopack HMRイベントストリーム |
| サーバーイベント | エントリーポイント変更購読で更新検出 | メッセージキュー内にデータあり | `subscribeToChanges`経由 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| コンパイルエラー中 | `currentEntryIssues`にseverityがwarning以外のissueがある場合、送信が遅延される |
| issuesタイプ | `data.type === 'issues'`の場合は`sendTurbopackMessage`をスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Turbopack HMRイベント検出] --> B[sendTurbopackMessage]
    B --> C[全クライアントのturbopackUpdates配列にpush]
    C --> D[sendEnqueuedMessagesDebounce 2ms]
    D --> E{コンパイルエラーあり?}
    E -->|Yes| F[送信遅延]
    E -->|No| G[sendEnqueuedMessages実行]
    G --> H[turbopackUpdatesをバッチ送信]
    H --> I[クライアント: turbopackHmr.onTurbopackMessage]
    I --> J[processTurbopackMessage]
    J --> K[モジュールホットリプレースメント実行]
    K --> L[dispatcher.onRefresh]
```

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

### 参照テーブル一覧

該当なし（Turbopackのコンパイル結果をインメモリで処理）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| HMR購読エラー | HMRイベントストリームでエラー発生 | `ReloadPageMessage`を送信しクライアント接続を閉じる |
| ランタイムエラー | クライアント側でのモジュール適用失敗 | `performFullReload`でフルリロードを実行 |
| 古いセッション | クライアントが前回サーバーのHMRセッションを使用 | ReloadPageメッセージでフルリロードを強制 |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | なし（エラー時はフルリロードで対応） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（2msデバウンスによるバッチ化あり） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

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

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

本通知はローカル開発環境専用。モジュール更新情報にはソースコードの差分情報が含まれるが、開発環境のみで使用されるため外部漏洩リスクは低い。`diagnostics`と`issues`フィールドは送信前に空配列に置き換えられる（パフォーマンス最適化）。

## 備考

- `sendTurbopackMessage`内で`payload.diagnostics = []`と`payload.issues = []`に強制的に設定される。これはPACK-2049の問題（大規模アプリで大量の重複issueが送信される）への対処。
- メッセージはクライアントごとの`turbopackUpdates`配列に蓄積され、`sendEnqueuedMessages`で一括送信される。
- Pages Routerクライアント（`page-bootstrap.ts`）ではこのメッセージタイプはno-opとして処理される（120行目）。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 31行目: `TURBOPACK_MESSAGE = 'turbopack-message'`のenum定義。58-61行目: `TurbopackMessage`インターフェースで`data: TurbopackUpdate \| TurbopackUpdate[]`の構造を確認 |

**読解のコツ**: `TurbopackUpdate`型は`packages/next/src/build/swc/types.ts`で定義されるSWCバインディングの型。

#### Step 2: サーバー側送信ロジックを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 521-537行目: `sendTurbopackMessage`関数。diagnostics/issuesの空配列化、全クライアントへのpush、デバウンス送信 |
| 2-2 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 590-628行目: `subscribeToHmrEvents`関数。HMRイベントストリームからのデータ受信と`sendTurbopackMessage`呼び出し |
| 2-3 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 463-506行目: `sendEnqueuedMessages`関数。バッチ送信ロジック |

**主要処理フロー**:
1. **590行目**: `subscribeToHmrEvents`でHMRイベントストリームを購読
2. **610行目**: `for await (const data of subscription)`でイベントを受信
3. **613行目**: `data.type !== 'issues'`の場合に`sendTurbopackMessage(data)`を呼び出し
4. **521行目**: `sendTurbopackMessage`で`diagnostics`/`issues`を空配列化
5. **528-533行目**: 全クライアントの`turbopackUpdates`配列にpush
6. **536行目**: `sendEnqueuedMessagesDebounce()`で2ms後にバッチ送信
7. **498-503行目**: `sendEnqueuedMessages`内で`TURBOPACK_MESSAGE`としてまとめて送信

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | 384-396行目: `TURBOPACK_MESSAGE`受信時の処理。`turbopackHmr.onTurbopackMessage`、`dispatcher.onBeforeRefresh`、`processTurbopackMessage`、ランタイムエラー時のフルリロード、`dispatcher.onRefresh` |

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

```
subscribeToHmrEvents(client, id) [hot-reloader-turbopack.ts:590]
    |
    +-- project.hmrEvents(id) [hot-reloader-turbopack.ts:602]
           |
           +-- for await (const data of subscription)
                  |
                  +-- sendTurbopackMessage(data) [hot-reloader-turbopack.ts:521]
                         |
                         +-- clientStates.get(client)?.turbopackUpdates.push(payload)
                         +-- sendEnqueuedMessagesDebounce() [2ms debounce]
                                |
                                +-- sendEnqueuedMessages() [hot-reloader-turbopack.ts:463]
                                       |
                                       +-- sendToClient(client, { type: TURBOPACK_MESSAGE, data })
                                              |
                                              +-- [Browser] processMessage(TURBOPACK_MESSAGE)
                                                     +-- turbopackHmr.onTurbopackMessage(message)
                                                     +-- processTurbopackMessage(message)
                                                     +-- dispatcher.onRefresh()
```

### データフロー図

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

Turbopack HMRイベント     ──>  sendTurbopackMessage()           ──> WebSocket JSON メッセージ
  (TurbopackUpdate)             diagnostics/issues空配列化          type: "turbopack-message"
                                クライアントキューにpush             data: [TurbopackUpdate...]
                                2msデバウンスバッチ送信                |
                                                                      v
                                                                 [ブラウザクライアント]
                                                                 turbopackHmr.onTurbopackMessage()
                                                                 processTurbopackMessage()
                                                                 モジュールHot Replace
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | ソース | メッセージ型定義・enum定義 |
| hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | ソース | サーバー側送信ロジック |
| hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | ソース | App Routerクライアント側受信処理 |
| page-bootstrap.ts | `packages/next/src/client/page-bootstrap.ts` | ソース | Pages Routerクライアント（no-op） |
| turbopack-hot-reloader-common.ts | `packages/next/src/client/dev/hot-reloader/turbopack-hot-reloader-common.ts` | ソース | TurbopackHmrクラス |
