# 通知設計書 10-DevTools通知

## 概要

本ドキュメントは、Vue.jsランタイムからVue DevTools拡張機能への通知機構の設計仕様を記述する。`packages/runtime-core/src/devtools.ts`に実装されており、アプリ初期化（`app:init`）、アンマウント（`app:unmount`）、コンポーネント追加/更新/削除、イベント発行、パフォーマンス計測の各イベントをDevToolsに通知する。DevTools未インストール時は3秒間バッファリング後に破棄する。

### 本通知の処理概要

この通知は、Vue DevTools拡張機能と連携し、アプリケーションの状態変化をリアルタイムでDevToolsに伝達する機構である。`DevtoolsHooks` enumで8種類のフックイベントが定義されている。

**業務上の目的・背景**：Vue DevToolsはVue.jsアプリケーションのデバッグ・パフォーマンス分析に不可欠な開発者ツールである。コンポーネントツリーの可視化、状態の検査・編集、イベントの監視、パフォーマンスプロファイリングなどの機能を提供する。本通知機構は、ランタイムとDevTools拡張機能をブリッジする役割を果たし、アプリケーションのライフサイクルイベントをDevToolsにリアルタイムで伝達する。DevToolsが未インストールの場合でも、遅延インストールに対応するためイベントをバッファリングする。

**通知の送信タイミング**：アプリの初期化/アンマウント時、コンポーネントの追加/更新/削除時、コンポーネントイベントのemit時、パフォーマンス計測の開始/終了時にそれぞれ発火する。

**通知の受信者**：Vue DevTools拡張機能（ブラウザ拡張）。`DevtoolsHook`インターフェースを実装したオブジェクトが受信する。

**通知内容の概要**：イベント種別（`DevtoolsHooks` enum値）と、イベントに応じたペイロード（アプリインスタンス、コンポーネントUID、親UID、コンポーネントインスタンス、イベント名、パフォーマンスデータなど）が通知される。

**期待されるアクション**：DevToolsがイベントを受信し、UI（コンポーネントツリー、イベントログ、パフォーマンスタイムライン等）を更新する。

## 通知種別

DevTools連携（イベントエミッション）/ バッファリング

## 送信仕様

### 基本情報

| 項目 | 内容 |
|-----|------|
| 送信方式 | 同期（バッファリング付き） |
| 優先度 | 低（デバッグ・開発支援用） |
| リトライ | なし（バッファリングによる遅延送信あり） |

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

1. `devtools`変数（`DevtoolsHook`）が設定済みの場合：即座に`devtools.emit(event, ...args)`で送信
2. `devtools`変数が未設定かつ`devtoolsNotInstalled`がfalseの場合：`buffer`配列にイベントをバッファリング
3. `devtoolsNotInstalled`がtrueの場合：イベントを破棄

## 通知テンプレート

### DevToolsイベントの場合

| 項目 | 内容 |
|-----|------|
| 送信元アドレス | N/A（インプロセス通信） |
| 送信元名称 | Vue Runtime |
| 件名 | DevtoolsHooks enum値（例：`app:init`, `component:updated`） |
| 形式 | イベントエミッション（event + payload） |

### 本文テンプレート

各イベントのペイロード構造：

```javascript
// app:init
emit('app:init', app, version, { Fragment, Text, Comment, Static })

// app:unmount
emit('app:unmount', app)

// component:added / component:updated / component:removed
emit('component:*', app, uid, parentUid, component)

// component:emit
emit('component:emit', app, component, event, params)

// perf:start / perf:end
emit('perf:*', app, uid, component, type, time)
```

### 添付ファイル

| ファイル名 | 形式 | 条件 | 説明 |
|----------|------|------|------|
| N/A | N/A | N/A | インプロセス通信のため添付ファイルなし |

## テンプレート変数

| 変数名 | 説明 | データ取得元 | 必須 |
|--------|------|-------------|-----|
| event | イベント種別（DevtoolsHooks値） | 各通知関数で固定 | Yes |
| app | アプリインスタンス | component.appContext.app | Yes |
| version | Vueバージョン文字列 | importされたversion | app:init時のみ |
| uid | コンポーネントUID | component.uid | コンポーネント系イベント |
| parentUid | 親コンポーネントUID | component.parent?.uid | コンポーネント系イベント |
| component | コンポーネントインスタンス | ComponentInternalInstance | コンポーネント系イベント |
| type | パフォーマンス計測タイプ | 計測関数の引数 | perf系イベント |
| time | パフォーマンス計測時間 | 計測関数の引数 | perf系イベント |

## 送信トリガー・条件

### トリガー一覧

| トリガー種別 | トリガーイベント | 送信条件 | 説明 |
|------------|----------------|---------|------|
| アプリ初期化 | devtoolsInitApp() | 常時 | app:initイベント。Fragment, Text, Comment, StaticのVNode型も送信 |
| アプリ終了 | devtoolsUnmountApp() | 常時 | app:unmountイベント |
| コンポーネント追加 | devtoolsComponentAdded() | 常時 | component:addedイベント |
| コンポーネント更新 | devtoolsComponentUpdated() | 常時 | component:updatedイベント |
| コンポーネント削除 | devtoolsComponentRemoved() | 条件付き | component:removedイベント。cleanupBufferでバッファ内のイベントをクリーンアップ |
| イベント発行 | devtoolsComponentEmit() | 常時 | component:emitイベント |
| パフォーマンス開始 | devtoolsPerfStart() | 常時 | perf:startイベント |
| パフォーマンス終了 | devtoolsPerfEnd() | 常時 | perf:endイベント |

### 送信抑止条件

| 条件 | 説明 |
|-----|------|
| devtoolsNotInstalled がtrue | DevTools未インストール確定後は全イベントを破棄 |
| devtools.cleanupBuffer() が true を返した場合 | component:removedイベントでバッファ内にある場合はスキップ |

## 処理フロー

### 送信フロー

```mermaid
flowchart TD
    A[Vue内部でイベント発生] --> B[対応するdevtools*関数呼び出し]
    B --> C[内部emit関数呼び出し]
    C --> D{devtools変数が<br>設定済みか?}
    D -->|Yes| E["devtools.emit(event, ...args)"]
    D -->|No| F{devtoolsNotInstalled?}
    F -->|Yes| G[イベント破棄]
    F -->|No| H["buffer.push({event, args})"]
    E --> I[終了]
    G --> I
    H --> I

    J[setDevtoolsHook呼び出し] --> K{hookが有効か?}
    K -->|Yes| L[devtools = hook, enabled = true]
    L --> M[バッファ内イベントを全て送信]
    M --> N[バッファクリア]
    K -->|No| O{ブラウザ環境か?}
    O -->|Yes| P[__VUE_DEVTOOLS_HOOK_REPLAY__に登録]
    P --> Q[3秒後にタイムアウト]
    Q --> R{devtools設定済み?}
    R -->|No| S[devtoolsNotInstalled = true, バッファクリア]
    R -->|Yes| T[何もしない]
    O -->|No| S
```

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

### 参照テーブル一覧

| テーブル名 | 用途 | 備考 |
|-----------|------|------|
| N/A | N/A | データベースは使用しない |

### テーブル別参照項目詳細

本通知はデータベースを使用しない。以下のインメモリデータ構造を参照する。

| 参照データ | 用途 | 取得方法 |
|-----------|------|---------|
| devtools (DevtoolsHook) | DevToolsフックオブジェクト | setDevtoolsHookで設定されるモジュール変数 |
| buffer (Array) | イベントバッファ | モジュールスコープの配列 |
| devtoolsNotInstalled (boolean) | DevTools未インストールフラグ | モジュールスコープの変数 |

### 更新テーブル一覧

| テーブル名 | 操作 | 概要 |
|-----------|------|------|
| devtools | UPDATE | setDevtoolsHookでフックオブジェクトを設定 |
| buffer | UPDATE | イベントのバッファリング/クリア |
| devtoolsNotInstalled | UPDATE | 3秒タイムアウト後にtrueに設定 |

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 対処方法 |
|----------|---------|---------|
| DevTools未インストール | 3秒以内にDevToolsフックが設定されない | devtoolsNotInstalledをtrueに設定し、バッファをクリアしてメモリリークを防止 |
| バッファのメモリリーク | DevTools未インストール時にイベントが蓄積 | 3秒タイムアウトでバッファをクリア（#4738対策） |

### リトライ仕様

| 項目 | 内容 |
|-----|------|
| リトライ回数 | 0（リトライなし、ただしバッファリングによる遅延送信あり） |
| リトライ間隔 | N/A |
| リトライ対象エラー | N/A |

## 配信設定

### レート制限

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

### 配信時間帯

制限なし。ただしバッファリングのタイムアウトは3秒。

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

- コンポーネントインスタンスの内部状態がDevToolsに送信されるため、開発環境でのみ使用を推奨
- `__FEATURE_PROD_DEVTOOLS__`フラグで本番環境でもDevToolsを有効にできるが、パフォーマンスとセキュリティのトレードオフがある
- DevToolsフックの`emit`メソッドにより、サードパーティが同じインターフェースでイベントを受信できる可能性がある

## 備考

- `DevtoolsHooks` enumは8種類のイベントを定義：`app:init`、`app:unmount`、`component:updated`、`component:added`、`component:removed`、`component:emit`、`perf:start`、`perf:end`
- `devtoolsComponentRemoved`は特殊処理があり、`cleanupBuffer`メソッドでバッファ内のイベントをクリーンアップしてからイベントを送信する
- `setDevtoolsHook`はランタイム初期化時に呼び出され、DevToolsフックの設定と遅延インストール対応を行う
- `__VUE_DEVTOOLS_HOOK_REPLAY__`は遅延インストールされたDevToolsが過去のイベントを受信するためのメカニズム
- jsdom環境（テスト環境）はブラウザ環境とみなさず、DevToolsは無効化される
- `/*@__PURE__*/` アノテーションにより、DevTools関連の関数はツリーシェイキング対象となる
- `/*@__NO_SIDE_EFFECTS__*/` アノテーションにより、`createDevtoolsComponentHook`の副作用なし宣言

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L6-11: AppRecordインターフェース（id, app, version, types） |
| 1-2 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L13-22: DevtoolsHooks enum（8種類のイベント定義） |
| 1-3 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L24-36: DevtoolsHookインターフェース（emit, on, once, off, appRecords, cleanupBuffer） |

**読解のコツ**: DevtoolsHookインターフェースはイベントエミッターパターンを実装する。DevToolsフック自体はVue DevTools拡張機能側で実装され、`setDevtoolsHook`を通じてVueランタイムに注入される。

#### Step 2: エントリーポイントを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L44-50: emit関数（内部）。devtools設定済みならemit、未設定ならバッファリング |
| 2-2 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L52-88: setDevtoolsHook関数。DevToolsフックの設定、バッファフラッシュ、遅延インストール対応 |

**主要処理フロー**:
1. **L44-50**: 内部emit関数。devtoolsが設定されていれば即送信、未設定かつ未インストール確定前ならバッファに追加
2. **L52-57**: setDevtoolsHook - フック有効時、devtoolsにフックを設定しバッファをフラッシュ
3. **L58-82**: フック無効時（null/undefined）、ブラウザ環境なら`__VUE_DEVTOOLS_HOOK_REPLAY__`で遅延対応、3秒タイムアウト設定
4. **L83-86**: 非ブラウザ環境では即座にdevtoolsNotInstalled = trueに設定

#### Step 3: 各通知関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L90-97: devtoolsInitApp（app:init通知、VNodeタイプ情報付き） |
| 3-2 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L99-101: devtoolsUnmountApp（app:unmount通知） |
| 3-3 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L103-124: devtoolsComponentAdded/Updated/Removed（コンポーネントライフサイクル通知） |
| 3-4 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L143-160: devtoolsPerfStart/End（パフォーマンス計測通知） |
| 3-5 | devtools.ts | `packages/runtime-core/src/devtools.ts` | L162-174: devtoolsComponentEmit（コンポーネントイベント通知） |

**主要処理フロー**:
- **L90-97**: `app:init`イベントでは、Fragment/Text/Comment/StaticのVNode型定義も同時に送信
- **L113-124**: `component:removed`は`cleanupBuffer`で未送信バッファのクリーンアップ後に送信
- **L129-141**: `createDevtoolsComponentHook` - コンポーネントフック生成ファクトリ（app, uid, parentUid, componentを送信）
- **L154-159**: `createDevtoolsPerformanceHook` - パフォーマンスフック生成ファクトリ（app, uid, component, type, timeを送信）

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

```
[Vue ランタイム]
    |
    +-- devtoolsInitApp(app, version)                [devtools.ts:90]
    |       +-- emit('app:init', app, version, {...})
    |
    +-- devtoolsUnmountApp(app)                      [devtools.ts:99]
    |       +-- emit('app:unmount', app)
    |
    +-- devtoolsComponentAdded(component)             [devtools.ts:103]
    |       +-- emit('component:added', app, uid, parentUid, component)
    |
    +-- devtoolsComponentUpdated(component)           [devtools.ts:106]
    |       +-- emit('component:updated', app, uid, parentUid, component)
    |
    +-- devtoolsComponentRemoved(component)           [devtools.ts:113]
    |       +-- devtools.cleanupBuffer(component)
    |       +-- emit('component:removed', app, uid, parentUid, component)
    |
    +-- devtoolsComponentEmit(component, event, params) [devtools.ts:162]
    |       +-- emit('component:emit', app, component, event, params)
    |
    +-- devtoolsPerfStart(component, type, time)      [devtools.ts:143]
    |       +-- emit('perf:start', app, uid, component, type, time)
    |
    +-- devtoolsPerfEnd(component, type, time)        [devtools.ts:146]
            +-- emit('perf:end', app, uid, component, type, time)

emit(event, ...args)                                  [devtools.ts:44]
    |
    +-- [devtools設定済み] devtools.emit(event, ...args)
    +-- [未設定] buffer.push({event, args})
    +-- [未インストール確定] 破棄
```

### データフロー図

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

Vueランタイムイベント        +-- devtools*関数                  +-- DevTools拡張機能
  |                          |     |                            |     (コンポーネントツリー,
app (App)                    |     +-- emit(event, args)        |      イベントログ,
component (Instance)    ---->|           |                 ---->|      パフォーマンスタイムライン)
event (string)               |           +-- devtools.emit()   |
params (any[])               |           |                     +-- buffer (一時保存)
                             |           +-- buffer.push()      |     (3秒後に破棄)
                             +--                                +--

setDevtoolsHook(hook)        +-- hook設定                       +-- バッファフラッシュ
    |                        |     |                            |     (過去イベントを送信)
    +-- hook (DevtoolsHook)  |     +-- devtools = hook          |
                             |     +-- buffer replay            +-- __VUE_DEVTOOLS_HOOK_REPLAY__
                             +--                                      (遅延インストール対応)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| devtools.ts | `packages/runtime-core/src/devtools.ts` | ソース | DevTools通知機構全体（emit, setDevtoolsHook, 各通知関数） |
| apiCreateApp.ts | `packages/runtime-core/src/apiCreateApp.ts` | ソース | devtoolsInitApp/devtoolsUnmountAppの呼び出し元 |
| renderer.ts | `packages/runtime-core/src/renderer.ts` | ソース | devtoolsComponentAdded/Updated/Removedの呼び出し元 |
| componentEmits.ts | `packages/runtime-core/src/componentEmits.ts` | ソース | devtoolsComponentEmitの呼び出し元 |
| profiling.ts | `packages/runtime-core/src/profiling.ts` | ソース | devtoolsPerfStart/Endの呼び出し元 |
| vnode.ts | `packages/runtime-core/src/vnode.ts` | ソース | Fragment, Text, Comment, Static（app:initで送信） |
