# 機能設計書 44-jest-leak-detector

## 概要

本ドキュメントは、jest-leak-detectorパッケージの機能設計について記載する。jest-leak-detectorは、メモリリークを検出するためのユーティリティを提供するパッケージである。

### 本機能の処理概要

jest-leak-detectorは、オブジェクトがガベージコレクションによって回収されたかどうかを検出する機能を提供する。FinalizationRegistryを使用して、オブジェクトの解放を追跡する。

**業務上の目的・背景**：テストにおいてメモリリークは重要な問題である。テスト間でオブジェクトが保持され続けると、メモリ使用量が増大し、テストスイート全体のパフォーマンスが低下する。jest-leak-detectorは、テスト終了後にオブジェクトが適切に解放されているかを検証することで、メモリリークを早期に発見し、品質向上に貢献する。

**機能の利用シーン**：`jest --detectLeaks`オプションを使用した際に、テスト終了後のメモリリークを検出するために利用される。また、テスト環境のクリーンアップが正しく行われているかの検証にも使用される。

**主要な処理内容**：
1. LeakDetectorクラス: 監視対象オブジェクトを登録し、解放状態を追跡
2. FinalizationRegistry: オブジェクトがGCされた際にコールバックを実行
3. ガベージコレクション強制実行: v8.setFlagsFromStringで--expose-gcを有効化
4. V8ヒープスナップショット: より積極的なGCトリガーとしてヒープスナップショットを使用

**関連システム・外部連携**：Node.jsのv8モジュール（setFlagsFromString、getHeapSnapshot）、vmモジュール（runInNewContext）、@jest/get-typeパッケージを使用。

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

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLIのみで使用される機能のため関連画面なし |

## 機能種別

メモリ管理 / ガベージコレクション

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| value | unknown | Yes | 監視対象のオブジェクト | プリミティブ型は不可 |
| opt.shouldGenerateV8HeapSnapshot | boolean | No | V8ヒープスナップショットを生成するか（デフォルト: true） | - |

### 入力データソース

- テスト対象のオブジェクト参照

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| isLeaking | Promise<boolean> | リークしているかどうか（trueならリーク） |

### 出力先

- 呼び出し元への戻り値

## 処理フロー

### 処理シーケンス

```
1. LeakDetectorインスタンス作成
   └─ 監視対象オブジェクトをFinalizationRegistryに登録
2. isLeaking()呼び出し
   └─ ガベージコレクションを強制実行
3. GC実行
   └─ --expose-gcフラグを設定し、gc()を呼び出し
4. tick待機
   └─ 10回のsetImmediateでGC完了を待機
5. 追加のGCトリガー（オプション）
   └─ まだリークしている場合、V8ヒープスナップショットを生成
6. 再度tick待機
   └─ 10回のsetImmediateで待機
7. 結果返却
   └─ _isReferenceBeingHeldの状態を返却
```

### フローチャート

```mermaid
flowchart TD
    A[LeakDetector作成] --> B{プリミティブ?}
    B -->|Yes| C[TypeError throw]
    B -->|No| D[FinalizationRegistryに登録]
    D --> E[isLeaking呼び出し]
    E --> F[GC強制実行]
    F --> G[10回tick待機]
    G --> H{リーク検出?}
    H -->|Yes| I{V8スナップショット有効?}
    I -->|Yes| J[getHeapSnapshot実行]
    J --> K[10回tick待機]
    I -->|No| L[結果返却]
    K --> L
    H -->|No| L
    L --> M[_isReferenceBeingHeld返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-44-01 | プリミティブ拒否 | プリミティブ型は監視できない | コンストラクタ呼び出し時 |
| BR-44-02 | GCフラグ復元 | GC実行後、元のフラグ状態に復元 | GC非公開だった場合 |
| BR-44-03 | ヒープスナップショット | 通常GCで解放されない場合に追加実行 | リーク検出かつオプション有効時 |

### 計算ロジック

リーク判定ロジック:
```
FinalizationRegistryコールバックが呼ばれた場合 → _isReferenceBeingHeld = false
コールバックが呼ばれない場合 → _isReferenceBeingHeld = true（リーク）
```

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

### 操作別データベース影響一覧

該当なし（データベース操作は行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | TypeError | プリミティブ型を渡した場合 | オブジェクトを渡す |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- GC実行: 数十ミリ秒
- tick待機: 各回数ミリ秒
- ヒープスナップショット: 数百ミリ秒〜数秒（メモリサイズに依存）

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

- v8.setFlagsFromStringを使用してGCを有効化するため、本番環境での使用は推奨されない
- テスト環境専用の機能として設計されている

## 備考

- FinalizationRegistryはES2021の機能（Node.js 14.6.0+で利用可能）
- V8ヒープスナップショットはメモリ使用量が大きい場合に時間がかかる可能性がある

---

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

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

### 推奨読解順序

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

LeakDetectorクラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | index.ts | `packages/jest-leak-detector/src/index.ts` | LeakDetectorクラスの全体構造 |

**読解のコツ**: FinalizationRegistryはオブジェクトがGCされた時にコールバックを呼び出すES2021の機能。

#### Step 2: コンストラクタを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/jest-leak-detector/src/index.ts` | コンストラクタの処理フロー |

**主要処理フロー**:
1. **26-34行目**: プリミティブ型チェック - isPrimitiveでチェックしTypeError throw
2. **37-39行目**: FinalizationRegistry作成 - コールバックで_isReferenceBeingHeldをfalseに
3. **40行目**: オブジェクトをレジストリに登録
4. **42行目**: _isReferenceBeingHeldをtrueに初期化
5. **44-45行目**: shouldGenerateV8HeapSnapshotオプション処理
6. **48行目**: クロージャによるリーク防止のためvalueをnullに

#### Step 3: isLeaking()メソッドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | index.ts | `packages/jest-leak-detector/src/index.ts` | リーク検出ロジック |

**主要処理フロー**:
- **51行目**: _runGarbageCollector()でGC強制実行
- **54-57行目**: 10回のtick（setImmediate）で待機
- **59-70行目**: まだリークしている場合、ヒープスナップショットを生成
- **72行目**: 最終的な_isReferenceBeingHeld状態を返却

#### Step 4: _runGarbageCollector()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/jest-leak-detector/src/index.ts` | GC強制実行ロジック |

**主要処理フロー**:
- **76行目**: globalThis.gcの存在チェック
- **79行目**: --expose-gcフラグを設定
- **80行目**: runInNewContext('gc')でGC実行
- **83-85行目**: 元々GCが非公開だった場合は--no-expose-gcで復元

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

```
LeakDetector
    │
    ├─ constructor(value, opt?)
    │      ├─ isPrimitive(value) → 検証
    │      ├─ new FinalizationRegistry(callback)
    │      │      └─ callback: () => { _isReferenceBeingHeld = false }
    │      └─ _finalizationRegistry.register(value)
    │
    └─ isLeaking(): Promise<boolean>
           ├─ _runGarbageCollector()
           │      ├─ setFlagsFromString('--expose-gc')
           │      └─ runInNewContext('gc')()
           │
           ├─ tick() × 10
           │
           ├─ (リーク中の場合)
           │      ├─ getHeapSnapshot()
           │      └─ tick() × 10
           │
           └─ return _isReferenceBeingHeld
```

### データフロー図

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

オブジェクト ────────────▶ FinalizationRegistry登録 ─────▶ 監視開始
                              │
                              ▼
                         isLeaking()
                              │
                              ├─ GC強制実行
                              │      │
                              │      ▼
                              │  オブジェクト解放?
                              │      │
                              │      ├─ Yes → コールバック発火
                              │      │         └─ _isReferenceBeingHeld = false
                              │      │
                              │      └─ No → _isReferenceBeingHeld = true
                              │
                              ├─ (オプション) ヒープスナップショット
                              │
                              └─ return _isReferenceBeingHeld ──▶ boolean
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/jest-leak-detector/src/index.ts` | ソース | LeakDetectorクラス実装 |
| __tests__/index.test.ts | `packages/jest-leak-detector/src/__tests__/index.test.ts` | テスト | ユニットテスト |
