# 通知設計書 3-WebSocket再接続通知

## 概要

本ドキュメントは、Bun の Hot Module Reloading (HMR) 機能において、WebSocket 接続の切断後に再接続が成功した際にブラウザのコンソールに表示される通知の設計を記載する。

### 本通知の処理概要

この通知は、開発サーバーとブラウザクライアント間の WebSocket 接続が一度切断された後、自動再接続が成功したことを開発者に知らせる。これにより、開発者は HMR 機能が再び利用可能になったことを確認できる。

**業務上の目的・背景**：WebSocket 接続が切断された後、自動再接続が成功したことを開発者に明示的に通知することで、HMR 機能が正常に復旧したことを把握できるようにする。これにより、開発者は安心して開発を継続でき、手動でのページリロードが不要であることを確認できる。

**通知の送信タイミング**：WebSocket の再接続ループ内で、新しい WebSocket インスタンスの `onopen` イベントが発火した際に送信される。これは、切断後の再接続試行が成功したタイミングである。初回接続時はこの通知ではなく「WebSocket接続通知」が使用される。

**通知の受信者**：ブラウザのコンソールを通じて開発者に通知される。`console.info()` メソッドで情報レベルとして出力される。

**通知内容の概要**：`[Bun] Reconnected` という簡潔な固定メッセージがコンソールに出力される。このメッセージは、再接続が成功し、HMR 機能が再び利用可能になったことを示す。

**期待されるアクション**：開発者はこのメッセージを確認することで、HMR 機能が正常に復旧したことを把握できる。特別なアクションは不要で、通常の開発フローを継続できる。

## 通知種別

コンソール通知（ブラウザ開発者ツール）

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（再接続成功時の onopen ハンドラ内で即座に実行） |
| 優先度 | 低（情報通知のため） |
| リトライ | 無（コンソール出力は失敗しない） |

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

ブラウザの `console.info()` メソッドを使用してコンソールに直接出力するため、特別な送信先決定ロジックは存在しない。

## 通知テンプレート

### コンソール出力

| 項目 | 内容 |
|-----|------|
| 出力レベル | info |
| プレフィックス | `[Bun]` |

### 本文テンプレート

```
[Bun] Reconnected
```

### 添付ファイル

該当なし（コンソール出力のため）

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| なし | 固定メッセージのため変数なし | - | - |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| WebSocket イベント | `onopen` (再接続時) | 再接続ループ内での接続成功時 | 切断後の再接続が成功したとき |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| 初回接続時 | 初回接続時はこの通知ではなく「WebSocket接続通知」が使用される |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[WebSocket 切断検出] --> B[再接続ループ開始]
    B --> C{closed フラグ確認}
    C -->|true| D[再接続中止]
    C -->|false| E[新規 WebSocket 作成]
    E --> F{接続成功?}
    F -->|成功| G[onopen 発火]
    G --> H[console.info で Reconnected 出力]
    H --> I[onStatusChange true 呼び出し]
    I --> J[onError ハンドラ再設定]
    J --> K[再接続ループ終了]
    F -->|失敗| L[onerror 発火]
    L --> M[wait 関数で待機]
    M --> B
    K --> N[終了]
    D --> N
```

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

### 参照テーブル一覧

該当なし（クライアントサイドのコンソール出力のため、データベースアクセスは発生しない）

### 更新テーブル一覧

該当なし

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| 再接続失敗継続 | サーバーが長時間停止 | 再接続ループで継続的にリトライ（この通知は発行されない） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 該当なし（この通知は成功時のみ発行される） |
| リトライ間隔 | 該当なし |
| リトライ対象エラー | 該当なし |

## 配信設定

### レート制限

| 項目 | 内容 |
|-----|------|
| 1分あたり上限 | 制限なし（再接続成功時のみ発生） |
| 1日あたり上限 | 制限なし |

### 配信時間帯

制限なし（開発時に随時発生）

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

- この通知は開発環境でのみ使用されることを想定している
- 機密情報は含まれない（固定メッセージのみ）

## 備考

- この通知は「WebSocket切断通知」の後に表示される
- 再接続成功後、`onStatusChange` コールバックが `true` を引数として呼び出され、`bun:ws:connect` イベントが発行される
- 再接続後も WebSocket のメッセージハンドラ（`onmessage`）は同じものが使用される
- エラーハンドラ（`onerror`）は再接続成功後に再設定される

---

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

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

### 推奨読解順序

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

再接続処理に関連する状態管理を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | websocket.ts | `src/bake/client/websocket.ts` | `wsProxy.wrapped` プロパティ（42-43行目）が再接続時に再割り当てされることを理解する |
| 1-2 | websocket.ts | `src/bake/client/websocket.ts` | Promise パターン（166-167行目）で接続結果を待機する方法を理解する |

**読解のコツ**: 再接続ループ内では `Promise.withResolvers` を使用できないため（iOS サポートのため）、従来の Promise コンストラクタパターンが使用されている。

#### Step 2: 再接続ループを理解する

切断後の再接続処理の全体像を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | websocket.ts | `src/bake/client/websocket.ts` | `onClose` 関数内の再接続ループ（162-188行目）を理解する |

**主要処理フロー**:
1. **162行目**: `while (true)` で無限ループ開始
2. **163行目**: `closed` フラグ確認
3. **169行目**: 新しい WebSocket インスタンス作成、`wsProxy.wrapped` に再割り当て
4. **171-176行目**: 再接続成功時の処理

#### Step 3: 再接続成功時の通知処理を理解する

再接続成功時に実行される処理を詳細に理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | websocket.ts | `src/bake/client/websocket.ts` | 171-176行目の `onopen` ハンドラで再接続成功時の処理が実行される |

**主要処理フロー**:
- **172行目**: `console.info("[Bun] Reconnected");` でメッセージ出力
- **173行目**: `done(true)` で Promise を解決（成功）
- **174行目**: `onStatusChange?.(true)` でコールバック呼び出し
- **175行目**: `ws.onerror = onError` でエラーハンドラ再設定

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

```
onClose() - 再接続ループ
    │
    └─ while (true)
           │
           ├─ if (closed) return
           │
           ├─ new WebSocket(url)
           │      │
           │      └─ wsProxy.wrapped = ws (再割り当て)
           │
           ├─ ws.onopen = () => { ... }
           │      │
           │      ├─ console.info("Reconnected") ← 通知出力
           │      │
           │      ├─ done(true)
           │      │
           │      ├─ onStatusChange(true)
           │      │      └─ emitEvent('bun:ws:connect')
           │      │
           │      └─ ws.onerror = onError
           │
           ├─ ws.onmessage = onMessage
           │
           └─ ws.onerror = (ev) => done(false)
```

### データフロー図

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

切断イベント ─────────▶ 再接続ループ開始
                              │
                              ▼
                       new WebSocket(url)
                              │
                              ▼
                       WebSocket.onopen
                              │
                              ▼
                       console.info() ────────────────▶ [Bun] Reconnected
                              │
                              ▼
                       onStatusChange(true) ──────────▶ bun:ws:connect イベント発行
                              │
                              ▼
                       ループ終了 (break)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| websocket.ts | `src/bake/client/websocket.ts` | ソース | WebSocket 接続管理、再接続処理、通知出力 |
| hmr-runtime-client.ts | `src/bake/hmr-runtime-client.ts` | ソース | HMR クライアントのエントリーポイント、イベントハンドラ登録 |
| hmr-module.ts | `src/bake/hmr-module.ts` | ソース | HMR イベント発行（`emitEvent` 関数で `bun:ws:connect` を発行） |
