# 機能設計書 105-node:diagnostics_channel

## 概要

本ドキュメントは、BunにおけるNode.js互換の`node:diagnostics_channel`モジュールの機能設計を記述したものである。このモジュールは、名前付きチャネルを介したパブリッシュ/サブスクライブ形式のメッセージ通信機能を提供する。

### 本機能の処理概要

`node:diagnostics_channel`モジュールは、モジュール間でイベントやデータをやり取りするための低オーバーヘッドのメッセージングAPIを提供する。診断情報の収集、APMツール（Application Performance Monitoring）との連携、デバッグ目的でのイベント監視などに活用される。

**業務上の目的・背景**：アプリケーションの内部状態を監視・診断する仕組みは、本番環境でのトラブルシューティングやパフォーマンス分析に不可欠である。`diagnostics_channel`は、アプリケーションコードを変更することなく、外部からイベントをフックして診断情報を収集できる機能を提供する。

**機能の利用シーン**：
- APMツール（DataDog、New Relic等）によるHTTPリクエストの監視
- データベースクエリのパフォーマンス計測
- カスタムイベントの発行と購読
- トレーシングフレームワークとの統合

**主要な処理内容**：
1. `channel()`: 名前付きチャネルの取得または作成
2. `subscribe()`/`unsubscribe()`: チャネルへの購読/購読解除
3. `publish()`: チャネルへのメッセージ発行
4. `TracingChannel`: 同期/非同期処理のトレーシング

**関連システム・外部連携**：
- `AsyncLocalStorage`: ストアバインディングによるコンテキスト伝播
- APMツール: 診断情報の収集

**権限による制御**：特になし

## 関連画面

本機能はバックエンドモジュールであり、直接的な関連画面は存在しない。

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | - |

## 機能種別

メッセージング / 診断チャネル

## 入力仕様

### 入力パラメータ

#### `channel(name)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string \| symbol | Yes | チャネル名 | 文字列またはシンボル |

#### `subscribe(name, subscription)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| name | string \| symbol | Yes | チャネル名 | - |
| subscription | function | Yes | 購読コールバック | 関数であること |

#### `Channel.bindStore(store, transform)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| store | AsyncLocalStorage | Yes | バインドするストア | - |
| transform | function | No | データ変換関数 | - |

#### `TracingChannel.traceSync(fn, context, thisArg, ...args)`

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| fn | function | Yes | トレース対象の関数 | - |
| context | object | No | コンテキストオブジェクト | デフォルト: {} |
| thisArg | any | No | thisの値 | - |
| args | any[] | No | 関数への引数 | - |

### 入力データソース

- 関数呼び出しの引数

## 出力仕様

### 出力データ

#### `channel()`の戻り値

| 項目名 | 型 | 説明 |
|--------|-----|------|
| channel | Channel | チャネルインスタンス |

#### `Channel.hasSubscribers`プロパティ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| hasSubscribers | boolean | 購読者が存在するかどうか |

#### 購読コールバックへの引数

| 項目名 | 型 | 説明 |
|--------|-----|------|
| data | any | 発行されたデータ |
| name | string | チャネル名 |

### 出力先

- 購読コールバック関数への引数

## 処理フロー

### 処理シーケンス（publish）

```
1. チャネル取得
   └─ channel(name)でチャネルインスタンスを取得
2. 購読者の確認
   └─ hasSubscribersでアクティブな購読者を確認
3. メッセージ発行
   └─ publish(data)で全購読者に通知
4. 購読者への配信
   └─ 各購読者のコールバックを呼び出し
   └─ エラーはprocess.nextTickでreportError
```

### フローチャート

```mermaid
flowchart TD
    A[channel取得] --> B{チャネル存在?}
    B -->|No| C[新規Channel作成]
    B -->|Yes| D[既存Channel取得]
    C --> E[WeakRefMapに登録]
    E --> F[Channel返却]
    D --> F
    F --> G[subscribe呼び出し]
    G --> H{初回購読?}
    H -->|Yes| I[ActiveChannelに変換]
    H -->|No| J[購読者追加]
    I --> J
    J --> K[publish呼び出し]
    K --> L[全購読者にdata配信]
    L --> M{エラー?}
    M -->|Yes| N[nextTickでreportError]
    M -->|No| O[完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-501 | 遅延チャネル作成 | 購読者がいるまでInactiveChannel | 初期状態 |
| BR-502 | プロトタイプ切り替え | 購読者追加時にActiveChannelに変換 | subscribe時 |
| BR-503 | 自動非アクティブ化 | 購読者0でChannel（Inactive）に戻る | unsubscribe時 |
| BR-504 | WeakRef管理 | GCでチャネルが自動削除 | 参照なし時 |
| BR-505 | エラー隔離 | 購読者のエラーは他に影響しない | publish時 |

### 計算ロジック

**チャネル状態管理**：
- `Channel`（Inactive）: `hasSubscribers`は常にfalse、`publish`は何もしない
- `ActiveChannel`: `hasSubscribers`はtrue、`publish`で全購読者に配信

**WeakRefMap管理**：
- `FinalizationRegistry`でGC時にチャネルをMapから削除
- `incRef`/`decRef`で参照カウントを管理

## データベース操作仕様

本機能はデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR_INVALID_ARG_TYPE | TypeError | nameがstring/symbolでない | 正しい型を指定 |
| ERR_INVALID_ARG_TYPE | TypeError | subscriptionが関数でない | 関数を指定 |
| ERR_INVALID_ARG_TYPE | TypeError | TracingChannelのチャネルが不正 | Channelインスタンスを指定 |

### リトライ仕様

本機能はリトライを行わない。

## トランザクション仕様

本機能はトランザクションを使用しない。

## パフォーマンス要件

- 購読者がいないチャネルへの`publish`はほぼゼロコスト
- プロトタイプ切り替えにより、非アクティブ時のオーバーヘッドを最小化
- `WeakRef`によりメモリリークを防止

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

- 診断チャネルを通じて機密情報が漏洩する可能性があるため、本番環境での使用に注意
- 購読者は発行されたデータを自由に参照可能

## 備考

- `TracingChannel`は同期処理、Promise、コールバック形式の非同期処理をトレース可能
- `AsyncLocalStorage`との連携により、トレースコンテキストを自動伝播

---

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

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

### 推奨読解順序

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

まず、チャネル管理のデータ構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | diagnostics_channel.ts | `src/js/node/diagnostics_channel.ts` | WeakRefMapとチャネル管理 |

**読解のコツ**:
- **20-34行目**: `WeakReference`クラスでWeakRefを参照カウント付きで拡張
- **38-59行目**: `WeakRefMap`でGC時に自動削除されるチャネルMap
- **215行目**: `channels`グローバル変数でチャネルを管理

#### Step 2: Channelクラスの状態遷移を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | diagnostics_channel.ts | `src/js/node/diagnostics_channel.ts` | Channel/ActiveChannelの切り替え |

**主要処理フロー**:
1. **61-65行目**: `markActive`でActiveChannelプロトタイプに切り替え
2. **67-74行目**: `maybeMarkInactive`で購読者0時にChannelに戻す
3. **168-213行目**: `Channel`クラス（非アクティブ状態）
4. **94-166行目**: `ActiveChannel`クラス（アクティブ状態）

#### Step 3: 購読/発行の仕組みを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | diagnostics_channel.ts | `src/js/node/diagnostics_channel.ts` | subscribe/publish |

**主要処理フロー**:
- **99-104行目**: `ActiveChannel.subscribe`で購読者を追加
- **106-116行目**: `ActiveChannel.unsubscribe`で購読者を削除
- **141-150行目**: `ActiveChannel.publish`で全購読者に配信
- **217-226行目**: `channel`関数でチャネル取得/作成

#### Step 4: ストアバインディングを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | diagnostics_channel.ts | `src/js/node/diagnostics_channel.ts` | bindStore/runStores |

**主要処理フロー**:
- **118-122行目**: `bindStore`でAsyncLocalStorageをチャネルにバインド
- **124-135行目**: `unbindStore`でバインド解除
- **76-92行目**: `wrapStoreRun`でストアのrun呼び出しをラップ
- **152-165行目**: `runStores`でバインドされた全ストアでラップして実行

#### Step 5: TracingChannelを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | diagnostics_channel.ts | `src/js/node/diagnostics_channel.ts` | TracingChannelクラス |

**主要処理フロー**:
- **251-282行目**: コンストラクタで5つのサブチャネルを作成
- **306-322行目**: `traceSync`で同期関数のトレース
- **324-360行目**: `tracePromise`でPromiseのトレース
- **362-400行目**: `traceCallback`でコールバック形式のトレース

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

```
JavaScript
    │
    ├─ channel(name)
    │      └─ channels.get(name) または new Channel(name)
    │
    ├─ subscribe(name, fn)
    │      └─ channel(name).subscribe(fn)
    │             └─ markActive()  [初回のみ]
    │                    └─ ObjectSetPrototypeOf → ActiveChannel
    │
    ├─ publish(data)
    │      └─ ActiveChannel.publish(data)
    │             └─ for each subscriber: subscriber(data, name)
    │
    └─ TracingChannel.traceSync(fn)
           └─ start.runStores(context, fn)
                  └─ wrapStoreRun() [バインドされたストアごと]
                         └─ store.run(context, next)
```

### データフロー図

```
[発行者]                    [チャネル]                    [購読者]

publish(data) ─────────────▶ Channel ────────────────────▶ subscriber1(data, name)
                               │
                               ├───────────────────────────▶ subscriber2(data, name)
                               │
                               └───────────────────────────▶ subscriberN(data, name)

[ストアバインド]

bindStore(als) ─────────────▶ Channel._stores.set(als) ───▶ runStores()でstore.run()
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| diagnostics_channel.ts | `src/js/node/diagnostics_channel.ts` | ソース | diagnostics_channelモジュールの実装 |
| validators.ts | `src/js/internal/validators.ts` | ソース | 入力検証関数群 |
| async_hooks.ts | `src/js/node/async_hooks.ts` | ソース | AsyncLocalStorage（ストアバインド用） |
