# 通知設計書 17-ビルド同期通知

## 概要

本ドキュメントは、Next.js開発サーバーにおける「ビルド同期通知」（HMR_MESSAGE_SENT_TO_BROWSER.SYNC）の設計を記述する。この通知は、コンパイル完了時にハッシュ、エラー、警告、バージョン情報、DevIndicator状態、DevTools設定情報をブラウザへWebSocket経由で送信する同期メッセージである。

### 本通知の処理概要

この通知は、WebSocket接続確立時にブラウザとサーバー間の状態を同期するための初回メッセージとして送信される。BUILTメッセージとは異なり、SYNCはバージョン情報やDevIndicator状態などの追加メタデータを含む。

**業務上の目的・背景**：ブラウザがWebSocket接続を確立した直後に、現在のコンパイル状態（エラー、警告）、Next.jsバージョン情報、開発インジケーターの状態、DevTools設定などを同期する必要がある。この通知により、新規接続したブラウザタブや再接続したクライアントが、即座にサーバーの現在の状態を把握でき、バージョン古さ通知の表示やDevIndicatorの正しい状態表示が可能になる。

**通知の送信タイミング**：(1) Turbopackモードでは、WebSocket接続確立直後にTURBOPACK_CONNECTEDの後に送信。(2) Webpackモードでは、onHMRコールバック内でクライアント接続時に最新のコンパイル統計情報とともに送信。

**通知の受信者**：WebSocket接続を確立した個別のブラウザクライアント。

**通知内容の概要**：コンパイルハッシュ、エラー配列、警告配列、Next.jsバージョン情報（VersionInfo）、デバッグ情報、DevIndicator状態、DevTools設定が含まれる。

**期待されるアクション**：ブラウザは受信したバージョン情報を表示し、エラーがあればエラーオーバーレイを表示する。DevIndicatorの状態を反映し、DevToolsの設定を適用する。エラーも警告もない場合は正常状態として処理する。

## 通知種別

WebSocket（アプリ内通知） - ブラウザへのHMRメッセージ

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（WebSocket push） |
| 優先度 | 高 |
| リトライ | 無 |

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

Turbopackモードでは接続時に`sendToClient`関数で接続したクライアントのみに送信される。Webpackモードでは`onHMR`コールバック内で`publish`を通じて全クライアントに送信される。

## 通知テンプレート

### メール通知の場合

該当なし

### 本文テンプレート

```json
{
  "type": "sync",
  "hash": "{コンパイルハッシュ}",
  "errors": [],
  "warnings": [],
  "versionInfo": {
    "installed": "15.x.x",
    "staleness": "fresh|stale-patch|stale-minor|stale-major|stale-prerelease|unknown"
  },
  "debug": {
    "devtoolsFrontendUrl": "{Node.jsデバッガURL}"
  },
  "devIndicator": {
    "disabledUntil": 0
  },
  "devToolsConfig": {
    "position": "bottom-left"
  }
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| type | メッセージタイプ識別子 | HMR_MESSAGE_SENT_TO_BROWSER.SYNC定数 (`sync`) | Yes |
| hash | コンパイルハッシュ値 | Webpackの場合stats.hash、Turbopackの場合は空文字列 | Yes |
| errors | コンパイルエラー配列 | currentEntryIssuesまたはWebpack stats.errors | Yes |
| warnings | コンパイル警告配列 | Webpack stats.warningsまたは空配列 | Yes |
| versionInfo | Next.jsバージョン情報 | getVersionInfo()によるnpmレジストリ問い合わせ結果 | Yes |
| debug | デバッグ情報（devtoolsFrontendUrl等） | Node.js inspectorから取得 | No |
| updatedModules | 更新モジュール一覧 | Webpack stats.updatedModulesまたは未定義 | No |
| devIndicator | DevIndicatorの状態 | devIndicatorServerState | Yes |
| devToolsConfig | DevToolsの設定情報 | getDevToolsConfig(distDir) | No |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| WebSocket接続 | クライアントWebSocket接続確立 | 常に送信（Turbopackモード） | hot-reloader-turbopack.ts 1103-1121行目 |
| WebSocket接続 | クライアントWebSocket接続確立 | コンパイル統計情報が存在する場合（Webpackモード） | hot-middleware.ts 183-211行目 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Webpackモードで統計情報なし | getStatsForSyncEventがnullを返す場合は送信されない |
| closed状態 | WebpackHotMiddlewareがclose()された場合は送信しない |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[WebSocket接続確立] --> B{バンドラー種別}
    B -->|Turbopack| C[TURBOPACK_CONNECTED送信]
    C --> D[エラー情報収集]
    D --> E[getVersionInfoCached取得]
    E --> F[getDevToolsConfig取得]
    F --> G[SyncMessage生成・送信]
    B -->|Webpack| H[getStatsForSyncEvent]
    H --> I{統計情報あり?}
    I -->|Yes| J[SyncMessage生成・送信]
    I -->|No| K[送信スキップ]
    G --> L[クライアント側処理]
    J --> L
    L --> M[バージョン情報表示]
    L --> N[エラー/警告処理]
    L --> O[DevIndicator状態反映]
```

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

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| バージョン情報取得失敗 | npmレジストリへの接続失敗 | バージョン情報は空またはunknownとして送信 |
| DevTools設定読み込み失敗 | distDir内の設定ファイルが存在しない | デフォルト値またはundefinedとして送信 |
| コンパイルエラー存在 | コンパイル時にエラーが発生 | errors配列にエラー情報を含めて送信 |

### リトライ仕様

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

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし |
| 1日あたり上限 | 制限なし |

### 配信時間帯

開発サーバー稼働中は常時送信可能

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

- 開発環境専用
- バージョン情報にはインストール済みNext.jsのバージョン番号が含まれる
- devtoolsFrontendUrlにはNode.jsデバッガのURLが含まれるが、ローカル環境での使用に限定

## 備考

- SyncMessageはBUILTメッセージの上位互換であり、versionInfo、debug、devIndicator、devToolsConfigの追加フィールドを持つ
- バージョン情報（VersionInfo）はnpmレジストリへの非同期fetchで取得され、初回SSL証明書キャッシュ読み込みのブロッキングを回避するためlazy初期化される（hot-reloader-turbopack.ts 819-824行目）
- Webpackモードでは、サーバーコンパイラにエラーがある場合はサーバーの統計情報を優先する（hot-middleware.ts 55-71行目 getStatsForSyncEvent関数）
- devIndicatorServerState.disabledUntilの期限切れチェックがSYNC送信前に実行される

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 75-85行目: SyncMessage型定義。hash, errors, warnings, versionInfo, updatedModules, debug, devIndicator, devToolsConfigフィールド |
| 1-2 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 27行目: SYNC = `sync` |

**読解のコツ**: SyncMessageはBUILTメッセージとUnion型で処理されるケースが多い（クライアント側でcase BUILT / case SYNCが同じブロック）。

#### Step 2: サーバー側送信ロジック

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 1103-1121行目: WebSocket接続時のSyncMessage生成・送信。非同期でバージョン情報とDevTools設定を取得 |
| 2-2 | hot-middleware.ts | `packages/next/src/server/dev/hot-middleware.ts` | 166-211行目: onHMRコールバック内でのSyncMessage生成・送信 |

#### Step 3: クライアント側受信処理

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | 298-374行目: BUILT/SYNCの共通処理。versionInfo, debug, devIndicator, devToolsConfigの処理 |
| 3-2 | hot-reloader-pages.ts | `packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts` | 293-341行目: Pages RouterでのBUILT/SYNC処理 |

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

```
[サーバー側 - Turbopack]
hot-reloader-turbopack.ts: onHMR() -> wsServer.handleUpgrade()
    |
    +-- sendToClient(client, turbopackConnectedMessage)
    +-- async () {
            getVersionInfoCached()
            getDevToolsConfig(distDir)
            sendToClient(client, syncMessage)
        }

[サーバー側 - Webpack]
hot-middleware.ts: onHMR(client, htmlRequestId)
    |
    +-- getStatsForSyncEvent(clientStats, serverStats)
    +-- statsToJson(stats)
    +-- publish(syncMessage)

[クライアント側]
processMessage()
    |
    +-- case SYNC:
            +-- dispatcher.buildingIndicatorHide()
            +-- handleAvailableHash(hash)
            +-- dispatcher.onVersionInfo(versionInfo)
            +-- dispatcher.onDebugInfo(debug)
            +-- dispatcher.onDevIndicator(devIndicator)
            +-- dispatcher.onDevToolsConfig(devToolsConfig)
            +-- [エラーあり] handleErrors(errors)
            +-- [警告あり] 警告処理
            +-- [正常] sendMessage({ event: 'client-success' })
```

### データフロー図

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

WebSocket接続確立            サーバー側                      ブラウザ
                       ----> エラー情報収集
                             バージョン情報取得
                             DevTools設定取得
                                  |
                                  v
                            SyncMessage生成
                            { type: "sync",
                              hash, errors, warnings,
                              versionInfo, debug,
                              devIndicator, devToolsConfig }
                                  |
                                  v
                            WebSocket JSON送信 ---------> processMessage()
                                                          |
                                                          +-> バージョン情報表示
                                                          +-> エラー/警告処理
                                                          +-> DevIndicator状態反映
                                                          +-> DevTools設定適用
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | ソース | SyncMessage型定義 |
| hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | ソース | Turbopackモード送信ロジック |
| hot-middleware.ts | `packages/next/src/server/dev/hot-middleware.ts` | ソース | Webpackモード送信ロジック |
| hot-reloader-shared-utils.ts | `packages/next/src/server/dev/hot-reloader-shared-utils.ts` | ソース | getVersionInfo()関数 |
| dev-indicator-server-state.ts | `packages/next/src/server/dev/dev-indicator-server-state.ts` | ソース | devIndicatorServerState管理 |
| hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | ソース | App Routerクライアント側処理 |
| hot-reloader-pages.ts | `packages/next/src/client/dev/hot-reloader/pages/hot-reloader-pages.ts` | ソース | Pages Routerクライアント側処理 |
