# 機能設計書 38-readline

## 概要

本ドキュメントは、Node.js の `readline` モジュール（行単位入力インターフェース）の機能設計書である。このモジュールは、Readable ストリーム（標準入力など）から1行ずつデータを読み取るためのインターフェースを提供する。

### 本機能の処理概要

readline モジュールは、ストリームからの入力を行単位で処理するためのAPIを提供する。ターミナルでの対話型入力、行編集、コマンド履歴、タブ補完などの機能をサポートし、CLIアプリケーションの構築に広く利用される。

**業務上の目的・背景**：ターミナルベースのアプリケーションでは、ユーザーからの入力を行単位で受け取り、処理する必要がある。readline は、入力処理、行編集、履歴管理などの一般的なニーズに対応し、使いやすいCLI体験を提供する。

**機能の利用シーン**：
- CLIアプリケーションでのユーザー入力受け付け
- 対話型プロンプトの実装
- ファイルの行単位読み取り
- REPLの基盤機能
- ターミナルでのカーソル制御

**主要な処理内容**：
1. Interface の作成（`createInterface()`）
2. 行単位の入力読み取り（`line` イベント）
3. 質問プロンプトの表示（`question()`）
4. カーソル移動・画面制御（`cursorTo()` / `moveCursor()` / `clearLine()`）
5. キープレスイベントの発行（`emitKeypressEvents()`）

**関連システム・外部連携**：
- `tty` モジュール（端末制御）
- `stream` モジュール（ストリーム処理）
- `repl` モジュール（REPL基盤）

**権限による制御**：特段の権限制御は存在しない。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | ターミナルベースのCLIのため画面なし |

## 機能種別

入力処理 / 行編集 / ターミナル制御

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | Readable | Yes | 入力ストリーム | Readableであること |
| output | Writable | No | 出力ストリーム | Writableであること |
| completer | Function | No | タブ補完関数 | 関数であること |
| terminal | boolean | No | ターミナルとして扱うか | 真偽値 |
| historySize | number | No | 履歴の最大行数 | 正の整数（デフォルト: 30） |
| prompt | string | No | プロンプト文字列 | 文字列 |
| crlfDelay | number | No | CR/LF間の遅延認識（ミリ秒） | 正の整数 |
| removeHistoryDuplicates | boolean | No | 履歴の重複を削除するか | 真偽値 |
| escapeCodeTimeout | number | No | エスケープシーケンスタイムアウト | 正の整数 |
| tabSize | number | No | タブサイズ | 正の整数（デフォルト: 8） |

### 入力データソース

- 標準入力（process.stdin）
- ファイルストリーム
- ネットワークソケット
- その他の Readable ストリーム

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| line | string | 読み取った1行の文字列 |
| answer | string | question() への回答 |

### 出力先

- `line` イベントのコールバック
- `question()` のコールバック/Promise
- 指定された output ストリーム

## 処理フロー

### 処理シーケンス

```
1. Interface 作成
   └─ createInterface({ input, output, ... })
2. プロンプト表示
   └─ rl.prompt() でプロンプトを出力
3. 入力待機
   └─ input ストリームからデータを読み取り
4. 行編集
   └─ キー入力に応じてバッファを更新
5. 行確定
   └─ Enter キーで 'line' イベント発火
6. 処理
   └─ コールバックで行データを処理
7. 繰り返し
   └─ close されるまで 2-6 を繰り返す
```

### フローチャート

```mermaid
flowchart TD
    A[createInterface/options] --> B[プロンプト表示]
    B --> C[キー入力待機]
    C --> D{キー種別}
    D -->|通常文字| E[バッファに追加]
    D -->|Enter| F[line イベント発火]
    D -->|Ctrl+C| G[SIGINT イベント]
    D -->|Ctrl+D| H[close イベント]
    D -->|Tab| I[completer 呼び出し]
    E --> J[画面更新]
    J --> C
    F --> K[コールバック実行]
    K --> B
    G --> L{リスナーあり?}
    L -->|Yes| M[ハンドラ実行]
    L -->|No| N[終了]
    M --> B
    I --> O[補完候補表示]
    O --> C
    H --> P[終了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 行区切り | CR、LF、または CRLF で行を区切る | 常時 |
| BR-02 | 履歴管理 | 入力した行は履歴に保存される | terminal:true 時 |
| BR-03 | Raw モード | TTYの場合、キー入力ごとに処理される | terminal:true 時 |
| BR-04 | CRLF遅延 | crlfDelay 内の CR+LF は1つの改行として扱う | crlfDelay 設定時 |

### 計算ロジック

特段の複雑な計算ロジックはなし。

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

該当なし

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

該当なし

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ERR_INVALID_ARG_TYPE | TypeError | 引数の型が不正 | 正しい型を渡す |
| ERR_INVALID_ARG_VALUE | Error | 引数の値が不正 | 有効な値を渡す |
| AbortError | Error | AbortSignal でキャンセル | シグナル状態を確認 |

### リトライ仕様

リトライは行わない。

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

該当なし

## パフォーマンス要件

- キー入力のレスポンスは即時であること
- 補完処理はユーザー体験を損なわない速度で実行されること

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

- パスワード入力時は `muted` オプションまたは Raw モードを使用すること
- 入力のサニタイズはアプリケーション側で行うこと

## 備考

- `readline/promises` でPromiseベースのAPIを利用可能
- `emitKeypressEvents()` でストリームにキープレスイベント機能を追加可能
- REPL モジュールは readline を基盤として構築されている

---

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

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

### 推奨読解順序

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

まず、readline モジュールのエクスポートとInterface クラスの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | readline.js | `lib/readline.js` | module.exports（514-523行目） |

**読解のコツ**: readline.js は内部の Interface 実装をラップして互換性を維持している。実際の実装は `internal/readline/interface.js` にある。

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

モジュールのエクスポートと主要関数を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | readline.js | `lib/readline.js` | internal/readline インポート（36-41行目、62-98行目） |
| 2-2 | readline.js | `lib/readline.js` | createInterface（211-213行目） |
| 2-3 | readline.js | `lib/readline.js` | module.exports（514-523行目） |

**主要処理フロー**:
1. **36-41行目**: callbacks からカーソル制御関数をインポート
2. **62-98行目**: internal/readline/interface から内部シンボルをインポート
3. **101-121行目**: Interface コンストラクタ
4. **211-213行目**: createInterface - Interface インスタンス作成

#### Step 3: Interface クラスの実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | readline.js | `lib/readline.js` | Interface コンストラクタ（101-121行目） |
| 3-2 | readline.js | `lib/readline.js` | question メソッド（133-160行目） |
| 3-3 | readline.js | `lib/readline.js` | プロパティプロキシ（215-425行目） |

**主要処理フロー**:
- **101-121行目**: コンストラクタ - オプション処理と completer の正規化
- **133-160行目**: question() - 質問プロンプトの表示と回答待機
- **161-188行目**: question のPromise版カスタム実装

#### Step 4: カーソル制御関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | readline.js | `lib/readline.js` | cursorTo/moveCursor/clearLine（516-520行目） |

**主要処理フロー**:
- **516行目**: clearLine - 行のクリア
- **517行目**: clearScreenDown - 画面下部のクリア
- **518行目**: createInterface - インターフェース作成
- **519行目**: cursorTo - カーソル移動
- **520行目**: emitKeypressEvents - キープレスイベント有効化
- **521行目**: moveCursor - カーソル相対移動

#### Step 5: 内部実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | interface.js | `lib/internal/readline/interface.js` | _Interface クラス |
| 5-2 | callbacks.js | `lib/internal/readline/callbacks.js` | カーソル制御関数 |

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

```
createInterface(options)
    │
    └─ new Interface(input, output, completer, terminal)
           │
           ├─ InterfaceConstructor.call(this, input, output, completer, terminal)
           │      └─ 内部 _Interface 初期化
           │
           └─ プロパティプロキシ設定
                  └─ _prompt, _decoder 等の内部プロパティへのアクセス

rl.question(query, options, callback)
    │
    ├─ AbortSignal 処理
    │      └─ options.signal があれば addAbortListener
    │
    └─ this[kQuestion](query, callback)
           └─ 内部 question 実装呼び出し

on('line', callback)
    │
    └─ 入力処理
           │
           ├─ キー入力読み取り
           ├─ バッファ更新
           ├─ 画面更新
           │
           └─ Enter → callback(line)
```

### データフロー図

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

stdin/stream ──────────▶ readline Interface ──────────▶ 'line' イベント
      │                        │
      ▼                        ▼
キー入力 ─────────────────▶ _ttyWrite() ─────────────────▶ 画面更新
      │                        │
      ▼                        ▼
Enter ────────────────────▶ _line() ──────────────────▶ コールバック
      │                        │
      ▼                        ▼
Tab ──────────────────────▶ completer() ─────────────▶ 補完候補
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| readline.js | `lib/readline.js` | ソース | メインモジュール（公開API） |
| interface.js | `lib/internal/readline/interface.js` | ソース | Interface クラス実装 |
| callbacks.js | `lib/internal/readline/callbacks.js` | ソース | カーソル制御関数 |
| emitKeypressEvents.js | `lib/internal/readline/emitKeypressEvents.js` | ソース | キープレスイベント |
| utils.js | `lib/internal/readline/utils.js` | ソース | ユーティリティ関数 |
| promises.js | `lib/readline/promises.js` | ソース | Promise版API |
