# 通知設計書 15-サーバーのみ変更通知

## 概要

本ドキュメントは、Next.js開発サーバーにおける「サーバーのみ変更通知」（HMR_MESSAGE_SENT_TO_BROWSER.SERVER_ONLY_CHANGES）の設計を記述する。この通知は、サーバーサイドのみのコード変更を検出した際にブラウザへWebSocket経由で通知し、該当ページのデータ再取得またはリロードをトリガーする。

### 本通知の処理概要

この通知は、Pages RouterのdataEndpoint（getServerSideProps等のサーバーサイドデータ取得コード）に変更が検出された際に、開発サーバーからブラウザへHMRメッセージとして送信される仕組みである。

**業務上の目的・背景**：Next.jsのPages Routerにおいて、`getServerSideProps`や`getStaticProps`などのサーバーサイドデータ取得ロジックが変更された場合、クライアントサイドのコードは変更されていないが、サーバーから返されるデータが変わるためページの再取得が必要となる。この通知により、サーバーサイドのみの変更時に、ブラウザが該当ページのデータを再取得し、最新のサーバーサイドロジックの結果を表示できる。

**通知の送信タイミング**：Pages RouterページのdataEndpointのchangeサブスクリプションが変更を検出した場合。

**通知の受信者**：WebSocket接続を確立しているすべてのブラウザクライアント。

**通知内容の概要**：メッセージタイプ`serverOnlyChanges`と、変更があったページのパス一覧（pages配列）が含まれる。

**期待されるアクション**：ブラウザは該当ページを表示中の場合、`router.replace()`によりページデータを再取得する。APIルートの場合やランタイムエラー発生中はフルリロードを実行する。

## 通知種別

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

## 送信仕様

### 基本情報

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

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

Turbopackモードでは`sendHmr`関数を通じて全クライアントのメッセージキューに追加される。

## 通知テンプレート

### メール通知の場合

該当なし

### 本文テンプレート

```json
{
  "type": "serverOnlyChanges",
  "pages": ["/page-path"]
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| type | メッセージタイプ識別子 | HMR_MESSAGE_SENT_TO_BROWSER.SERVER_ONLY_CHANGES定数 (`serverOnlyChanges`) | Yes |
| pages | 変更があったページパスの配列 | changeサブスクリプションのコールバックで設定されるpage変数 | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| ファイル変更 | Pages RouterページのdataEndpoint変更 | changeサブスクリプションで変更が通知された場合 | turbopack-utils.ts handleRouteType pageケースのserverKeyサブスクリプション（264-282行目） |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| WebSocket未接続 | ブラウザがWebSocket接続を確立していない場合 |
| コンパイルエラー中 | sendEnqueuedMessagesのエラーチェックにより保留 |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Pages Routerページのサーバーコード変更] --> B[Turbopack dataEndpoint changeサブスクリプション]
    B --> C[readyIds.deleteで再コンパイル可能にマーク]
    C --> D[ServerOnlyChangesMessage生成]
    D --> E[sendHmr経由で全クライアントのキューに追加]
    E --> F[sendEnqueuedMessagesでJSON送信]
    F --> G{クライアント側処理}
    G -->|ランタイムエラーあり| H[performFullReload]
    G -->|APIルートの場合| I[window.location.reload]
    G -->|該当ページ表示中| J[router.replaceでデータ再取得]
    G -->|その他| K[何もしない]
```

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

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| ランタイムエラー既存 | RuntimeErrorHandler.hadRuntimeErrorがtrue | performFullReload実行 |
| データ再取得失敗 | router.replace()が失敗 | location.reload()にフォールバック |
| サブスクリプションエラー | dataEndpointのchangeサブスクリプションでエラー | RELOAD_PAGEメッセージにフォールバック |

### リトライ仕様

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

## 配信設定

### レート制限

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

### 配信時間帯

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

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

- 開発環境専用
- pagesフィールドにはルートパスが含まれるが、機密情報ではない

## 備考

- `readyIds.delete(pathname)`により、次回コンパイル時に再ビルドが実行されるようマークされる（turbopack-utils.ts 270行目）
- page-bootstrap.tsでは、APIルート（`router.query.__NEXT_PAGE`とpagesの一致）の場合は`window.location.reload()`を実行する
- 通常のページでは、`router.clc`（キャンセルフラグ）がなく、`router.pathname`がpagesに含まれる場合にのみ`router.replace()`でデータ再取得を行う
- データ再取得中はbuilding indicatorを表示し、完了後に非表示にする

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 123-126行目: ServerOnlyChangesMessage型定義。typeフィールドとpages配列 |
| 1-2 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 26行目: SERVER_ONLY_CHANGES = `serverOnlyChanges` |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | turbopack-utils.ts | `packages/next/src/server/dev/turbopack-utils.ts` | 264-282行目: Pages RouterページのserverKeyに対するdataEndpointのchangeサブスクリプション |

**主要処理フロー**:
- **268-270行目**: createMessage内でreadyIds.delete(pathname)を実行し、SERVER_ONLY_CHANGESメッセージを返す
- **276-280行目**: エラー発生時はRELOAD_PAGEにフォールバック

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | page-bootstrap.ts | `packages/next/src/client/page-bootstrap.ts` | 71-112行目: SERVER_ONLY_CHANGES受信処理。ランタイムエラーチェック、APIルート判定、router.replaceによるデータ再取得 |

**主要処理フロー**:
- **72-75行目**: ランタイムエラーがあればperformFullReload
- **82-84行目**: APIルートの場合はwindow.location.reload()
- **86-109行目**: 通常ページではrouter.replace()でデータ再取得、失敗時はlocation.reload()

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

```
[サーバー側]
turbopack-utils.ts: handleRouteType() [pageケース]
    |
    +-- subscribeToChanges(serverKey, false, route.dataEndpoint, ...)
            |
            +-- createMessage():
            |       readyIds.delete(pathname)
            |       return { type: SERVER_ONLY_CHANGES, pages: [page] }
            |
            +-- sendHmr()
                    +-- sendEnqueuedMessages() -> WebSocket送信

[クライアント側]
page-bootstrap.ts: addMessageListener()
    |
    +-- case SERVER_ONLY_CHANGES:
            +-- ランタイムエラーチェック -> performFullReload
            +-- APIルートチェック -> window.location.reload()
            +-- 該当ページ表示中 -> router.replace() -> データ再取得
                    +-- 失敗時 -> location.reload()
```

### データフロー図

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

Pages Router                  Turbopack                      ブラウザ
サーバーコード変更 ---------> dataEndpoint検出
(getServerSideProps等)        |
                              v
                       ServerOnlyChangesMessage生成
                       { type: "serverOnlyChanges",
                         pages: ["/page"] }
                              |
                              v
                       WebSocket JSON送信 -----------> page-bootstrap.ts
                                                       |
                                                       v
                                                router.replace()
                                                (サーバーデータ再取得)
                                                       |
                                                       v
                                                ページUI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | ソース | ServerOnlyChangesMessage型定義 |
| turbopack-utils.ts | `packages/next/src/server/dev/turbopack-utils.ts` | ソース | dataEndpointサブスクリプション登録 |
| hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | ソース | WebSocket送信インフラ |
| page-bootstrap.ts | `packages/next/src/client/page-bootstrap.ts` | ソース | クライアント側SERVER_ONLY_CHANGES処理 |
| 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側メッセージルーティング |
