# 通知設計書 24-キャッシュインジケーター通知

## 概要

本ドキュメントは、Next.js開発サーバーにおけるキャッシュインジケーター通知の設計を記述する。Cache Componentsの処理状態（filling/filled/bypass/ready）をブラウザクライアントへWebSocket経由で送信し、Dev Overlayのキャッシュインジケーターに反映する仕組みについて定義する。

### 本通知の処理概要

Cache Components機能が有効な場合に、サーバー側のキャッシュ処理状態をWebSocket経由でブラウザクライアントへリアルタイムに通知し、Dev Overlayのキャッシュインジケーターの表示を更新する。

**業務上の目的・背景**：Cache Components（実験的機能）が有効な場合、ページのレンダリングにおけるキャッシュの状態をリアルタイムに可視化する必要がある。開発者がキャッシュの効果を確認し、パフォーマンスチューニングを行うための情報提供を目的とする。

**通知の送信タイミング**：サーバーサイドでキャッシュ処理の状態が変化した際に`setCacheStatus`メソッドを通じて送信される。また、WebSocket接続確立時に、接続前に蓄積されたキャッシュ状態がある場合はそれも送信される。

**通知の受信者**：Cache Components有効のApp Routerクライアント（`htmlRequestId`を持つ非レガシークライアント）。Pages Routerクライアントおよびレガシーのapp Routerクライアントには送信されない。

**通知内容の概要**：`type: "cacheIndicator"`と`state: CacheIndicatorState`を含むJSONメッセージ。stateの値は`"filling"`（キャッシュ充填中）、`"filled"`（キャッシュ充填完了）、`"bypass"`（キャッシュバイパス）、`"ready"`（準備完了）のいずれか。

**期待されるアクション**：クライアントの`dispatcher.onCacheIndicator()`を通じてDev OverlayのキャッシュインジケーターUIを更新する。

## 通知種別

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

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 非同期（状態変化時にWebSocket push） |
| 優先度 | 中 |
| リトライ | あり（接続前の状態は`cacheStatusesByHtmlRequestId`に保持し、接続時に送信） |

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

`setCacheStatus(status, htmlRequestId)`メソッドで、`htmlRequestId`に対応するWebSocketクライアントを`clientsByHtmlRequestId`から検索し、個別に送信する。クライアントが未接続の場合は`cacheStatusesByHtmlRequestId`に状態を保存し、接続確立時に送信する。

## 通知テンプレート

### メール通知の場合

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

### 本文テンプレート

```json
{
  "type": "cacheIndicator",
  "state": "filling"
}
```

### 添付ファイル

該当なし

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| type | メッセージ種別 | 固定値 `"cacheIndicator"` | Yes |
| state | キャッシュ状態 | `ServerCacheStatus`型: `"filling"` / `"filled"` / `"bypass"` / `"ready"` | Yes |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| サーバーイベント | キャッシュ処理状態変化 | Cache Components有効かつクライアント接続済み | `setCacheStatus`メソッド呼び出し時 |
| サーバーイベント | WebSocket接続確立 | `cacheStatusesByHtmlRequestId`に保持された状態がある | 接続前に蓄積された状態の遅延送信 |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| Cache Components無効 | レガシークライアントにはキャッシュインジケーターは送信されない |
| `htmlRequestId`不一致 | 該当するクライアントが見つからない場合は保存のみ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[キャッシュ状態変化] --> B[setCacheStatus呼び出し]
    B --> C{クライアント接続済み?}
    C -->|Yes| D[sendToClientでCACHE_INDICATORメッセージ送信]
    C -->|No| E[cacheStatusesByHtmlRequestIdに保存]
    E --> F[クライアント接続時に送信]
    D --> G[クライアント: dispatcher.onCacheIndicator]
    F --> G
    G --> H[Dev Overlayキャッシュインジケーター更新]
```

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

### 参照テーブル一覧

該当なし（インメモリのキャッシュ状態マップを使用）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| クライアント未接続 | `htmlRequestId`に対応するクライアントが存在しない | `cacheStatusesByHtmlRequestId`に保存し、接続時に送信 |
| WebSocket送信失敗 | クライアントとの接続が切断済み | エラーは発生しない（送信先がない場合は保存のみ） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 1回（接続時の遅延送信） |
| リトライ間隔 | 接続確立まで待機 |
| リトライ対象エラー | クライアント未接続 |

## 配信設定

### レート制限

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

### 配信時間帯

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

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

本通知はローカル開発環境専用であり、キャッシュ状態の文字列値のみを含むため、機密情報は含まれない。

## 備考

- `ServerCacheStatus`型は`packages/next/src/next-devtools/dev-overlay/cache-indicator.tsx`で定義されている。
- `CacheIndicatorState`は`ServerCacheStatus | 'disabled'`の型エイリアスで、クライアント側では`'disabled'`状態も含めて管理される。
- Webpackモードでも`hot-reloader-webpack.ts`で同等の`setCacheStatus`メソッドが実装されている。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cache-indicator.tsx | `packages/next/src/next-devtools/dev-overlay/cache-indicator.tsx` | 1-2行目: `ServerCacheStatus`と`CacheIndicatorState`の型定義 |
| 1-2 | hot-reloader-types.ts | `packages/next/src/server/dev/hot-reloader-types.ts` | 35行目: `CACHE_INDICATOR = 'cacheIndicator'`のenum定義。176-179行目: `CacheIndicatorMessage`インターフェース |

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 1156-1168行目: `setCacheStatus`メソッドでクライアント接続判定と送信/保存の分岐 |
| 2-2 | hot-reloader-turbopack.ts | `packages/next/src/server/dev/hot-reloader-turbopack.ts` | 902-914行目: `onHMR`内でクライアント接続時に保存されたキャッシュ状態を送信 |

**主要処理フロー**:
1. **1156行目**: `setCacheStatus(status, htmlRequestId)`メソッド
2. **1158行目**: `clientsByHtmlRequestId.get(htmlRequestId)`でクライアント検索
3. **1159-1163行目**: クライアント接続済みなら`sendToClient`で即座に送信
4. **1167行目**: 未接続なら`cacheStatusesByHtmlRequestId.set`で保存
5. **907-913行目**: 接続時に保存状態があれば送信し、マップから削除

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | 510-512行目: `CACHE_INDICATOR`受信時に`dispatcher.onCacheIndicator(message.state)`を呼び出し |
| 3-2 | shared.ts | `packages/next/src/next-devtools/dev-overlay/shared.ts` | 365-366行目: `ACTION_CACHE_INDICATOR`アクションによるreducer処理 |

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

```
setCacheStatus(status, htmlRequestId) [hot-reloader-turbopack.ts:1156]
    |
    +-- [クライアント接続済み] sendToClient(client, { type: CACHE_INDICATOR, state })
    |     |
    |     +-- client.send(JSON.stringify(message))
    |           |
    |           +-- [Browser] processMessage(CACHE_INDICATOR) [hot-reloader-app.tsx:510]
    |                  +-- dispatcher.onCacheIndicator(state) [hot-reloader-app.tsx:511]
    |
    +-- [クライアント未接続] cacheStatusesByHtmlRequestId.set(htmlRequestId, status)
          |
          +-- [接続時] onHMR → sendToClient [hot-reloader-turbopack.ts:909-913]
```

### データフロー図

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

ServerCacheStatus     ──>  setCacheStatus()              ──> WebSocket JSON メッセージ
  ("filling"|"filled"|      クライアント接続判定              type: "cacheIndicator"
   "bypass"|"ready")        遅延送信キュー管理               state: <CacheStatus>
                                                               |
                                                               v
                                                          [ブラウザクライアント]
                                                          dispatcher.onCacheIndicator()
                                                          Dev Overlay UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cache-indicator.tsx | `packages/next/src/next-devtools/dev-overlay/cache-indicator.tsx` | ソース | 型定義（ServerCacheStatus, CacheIndicatorState） |
| 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` | ソース | Turbopackモードでの送信ロジック |
| hot-reloader-webpack.ts | `packages/next/src/server/dev/hot-reloader-webpack.ts` | ソース | Webpackモードでの送信ロジック |
| hot-reloader-app.tsx | `packages/next/src/client/dev/hot-reloader/app/hot-reloader-app.tsx` | ソース | App Routerクライアント側受信処理 |
| shared.ts | `packages/next/src/next-devtools/dev-overlay/shared.ts` | ソース | OverlayState reducer（ACTION_CACHE_INDICATOR） |
