# 機能設計書 2-fs/promises

## 概要

本ドキュメントは、Node.jsのfs/promisesモジュール（ファイルシステム操作のPromise版API）の機能設計を記載する。fs/promisesモジュールは、fsモジュールの機能をPromiseベースで提供し、async/await構文との親和性を高めたAPIである。

### 本機能の処理概要

fs/promisesモジュールは、従来のコールバックベースのfsモジュールをPromiseベースで再実装したものである。FileHandleクラスを中心としたオブジェクト指向的なAPIと、パスベースの関数形式APIの両方を提供する。

**業務上の目的・背景**：コールバック地獄を回避し、モダンなJavaScriptのasync/await構文でファイル操作を記述可能にする。これにより、コードの可読性と保守性が大幅に向上する。エラーハンドリングもtry/catchで統一的に行えるため、堅牢なアプリケーション開発が容易になる。

**機能の利用シーン**：
- モダンなNode.jsアプリケーション開発
- async/await構文を活用したファイル処理
- 複数ファイルの並列・直列処理
- トランザクション的なファイル操作（FileHandle使用時）
- Web API（fetch等）と一貫したPromiseベースの非同期処理

**主要な処理内容**：
1. FileHandleクラスによるファイルディスクリプタ管理
2. ファイルの読み込み（readFile, read）
3. ファイルへの書き込み（writeFile, write, appendFile）
4. ファイルのオープン・クローズ（open, FileHandle.close）
5. ディレクトリ操作（mkdir, readdir, rmdir, rm）
6. ファイル情報取得（stat, lstat）
7. ファイルの移動・コピー（rename, copyFile, cp）
8. シンボリックリンク操作（symlink, readlink, link）
9. ファイル監視（watch）
10. Glob パターンマッチング（glob）

**関連システム・外部連携**：内部的にはfsモジュールと同じC++バインディング（internalBinding('fs')）を使用する。kUsePromisesフラグによりPromise版の処理パスが選択される。

**権限による制御**：fsモジュールと同様、Node.jsのパーミッションモデルが有効な場合はアクセスが制限される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 直接関連する画面なし（fsモジュールと同じ用途で使用） |

## 機能種別

CRUD操作 / ストリーム処理 / ファイル監視

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | string / Buffer / URL | Yes | 操作対象のファイルパス | getValidatedPath()で検証 |
| filehandle | FileHandle | Yes（FH操作時） | FileHandleインスタンス | kRefs存在チェック |
| flags | string / number | No | オープンフラグ（'r', 'w', 'a'等） | stringToFlags()で変換 |
| mode | string / number | No | ファイルパーミッション | parseFileMode()で検証 |
| encoding | string | No | 文字エンコーディング | validateEncoding()で検証 |
| buffer | Buffer / TypedArray / DataView | Yes（read/write時） | データバッファ | validateBuffer()で検証 |
| options | object | No | 各種オプション | validateObject()で検証 |
| signal | AbortSignal | No | 中断シグナル | checkAborted()で検証 |

### 入力データソース

- ファイルパス：アプリケーションコードから直接指定
- FileHandle：open()の戻り値
- バッファ：Buffer.alloc()等で作成したデータ領域

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| data | Buffer / string | 読み込んだファイル内容 |
| bytesRead | number | 読み込んだバイト数 |
| bytesWritten | number | 書き込んだバイト数 |
| filehandle | FileHandle | オープンしたファイルハンドル |
| stats | Stats | ファイル情報オブジェクト |
| files | string[] / Buffer[] / Dirent[] | ディレクトリ内のエントリ一覧 |

### 出力先

- Promiseのresolve値

## 処理フロー

### 処理シーケンス

```
1. 入力パラメータの検証
   └─ パス、フラグ、モード、エンコーディング等を検証
2. AbortSignalチェック
   └─ シグナルが中断されていないか確認
3. ネイティブバインディング呼び出し（kUsePromises指定）
   └─ binding.xxx(..., kUsePromises)でPromise版実行
4. エラー処理
   └─ handleErrorFromBinding()でスタックトレース付きエラー生成
5. 結果の変換・整形
   └─ Stats、Dirent、FileHandle等のオブジェクト生成
6. Promiseの解決
   └─ resolve/rejectで結果通知
```

### フローチャート

```mermaid
flowchart TD
    A[API呼び出し] --> B{パラメータ検証}
    B -->|エラー| C[Promise reject]
    B -->|OK| D{AbortSignalチェック}
    D -->|中断| E[AbortError reject]
    D -->|継続| F[binding呼び出し kUsePromises]
    F --> G{成功?}
    G -->|No| H[handleErrorFromBinding]
    H --> C
    G -->|Yes| I[結果変換]
    I --> J[Promise resolve]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | FileHandle参照カウント | FileHandleは参照カウントで管理され、0になると自動クローズ | FileHandle使用時 |
| BR-002 | AbortSignal対応 | signal.abortedがtrueの場合は即座にAbortErrorをthrow | signalオプション指定時 |
| BR-003 | 非同期イテレータ対応 | watchはAsyncIteratorを返し、for await...ofで利用可能 | watch API |
| BR-004 | Symbol.asyncDispose対応 | FileHandleはusing構文で自動クローズ可能 | ES2024+ |
| BR-005 | チャンク書き込み | 大きなデータはkWriteFileMaxChunkSizeごとに分割書き込み | writeFile |

### 計算ロジック

- ファイルサイズ上限: `kIoMaxLength`
- 読み込みバッファサイズ: `kReadFileBufferLength`、未知サイズ時は`kReadFileUnknownBufferLength`
- 書き込みチャンクサイズ: `kWriteFileMaxChunkSize`

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

本モジュールはデータベースを直接操作しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EBADF | OSエラー | クローズ済みFileHandleへの操作 | FileHandleの状態を確認 |
| ERR_FS_FILE_TOO_LARGE | サイズエラー | ファイルサイズがkIoMaxLengthを超過 | ストリームAPIを使用 |
| ERR_INVALID_STATE | 状態エラー | FileHandleがクローズ中またはロック中 | 操作完了を待機 |
| ERR_ACCESS_DENIED | 権限エラー | symlinkでfs権限不足 | 適切な権限を設定 |
| AbortError | 中断エラー | AbortSignalで操作が中断 | リトライまたは処理終了 |

### リトライ仕様

rm()のみmaxRetriesオプションでリトライ回数を指定可能。

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

FileHandleを使用することで、同一ファイルへの複数操作をまとめて行うことができる。ただし、OSレベルのアトミック性は保証されない。

## パフォーマンス要件

- Promise版はコールバック版と同等のパフォーマンス
- FileHandleの再利用により、open/close のオーバーヘッドを削減可能
- AbortSignalによる早期中断でリソースを効率的に解放

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

- fsモジュールと同様のセキュリティ考慮が必要
- symlinkはセキュリティモデルをバイパスする可能性があるため、完全なfs権限が必要
- FileHandleの適切なクローズ（リソースリーク防止）

## 備考

- Node.js v10.0.0で導入
- ES2024のSymbol.asyncDispose対応（using構文）
- for await...of構文でのファイル監視が可能

---

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

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

### 推奨読解順序

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

まず、FileHandleクラスとその内部状態を理解することが重要である。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | promises.js | `lib/internal/fs/promises.js` | FileHandleクラスの定義 |

**読解のコツ**: FileHandleはEventEmitterを継承し、kHandle、kFd、kRefs等のSymbolプロパティで内部状態を管理する。kRefsは参照カウントであり、操作中に増減する。

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

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | promises.js | `lib/internal/fs/promises.js` | エクスポートされる関数群 |

**主要処理フロー**:
1. **105-114行目**: Symbolの定義（kHandle, kFd, kRefs等）
2. **151-163行目**: FileHandleコンストラクタ - 初期化処理
3. **236-267行目**: close()メソッド - 参照カウントとクローズ処理
4. **449-467行目**: fsCall()関数 - FileHandle操作のラッパー
5. **632-641行目**: open()関数 - FileHandle生成

#### Step 3: 主要API実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | promises.js | `lib/internal/fs/promises.js` | readFile, writeFile等の実装 |

**主要処理フロー（readFile）**:
- **509-598行目**: readFileHandle()関数
  - **516-520行目**: fstatでファイルサイズ取得
  - **526-528行目**: 通常ファイルのサイズ取得
  - **534-535行目**: サイズ上限チェック（kIoMaxLength）
  - **545-597行目**: チャンク読み込みループ

**主要処理フロー（writeFile）**:
- **474-507行目**: writeFileHandle()関数
  - **476-490行目**: イテラブルデータの処理
  - **495-506行目**: チャンク書き込みループ（kWriteFileMaxChunkSize）

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

```
fsPromises.readFile(path, options)
    │
    ├─ getOptions(options)             // オプション正規化
    │
    ├─ checkAborted(signal)            // AbortSignalチェック
    │
    ├─ open(path, flag, 0o666)         // FileHandle取得
    │      └─ binding.openFileHandle(path, flags, mode, kUsePromises)
    │             └─ new FileHandle(handle)
    │
    └─ handleFdClose(readFileHandle(), fd.close)
           │
           ├─ readFileHandle(filehandle, options)
           │      ├─ binding.fstat(fd, false, kUsePromises)
           │      └─ binding.read(fd, buffer, offset, length, position, kUsePromises)
           │
           └─ fd.close()
                  └─ kHandle.close()
```

### データフロー図

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

パス(string)    ───▶  getValidatedPath()  ───▶  検証済みパス
                           │
オプション      ───▶  getOptions()        ───▶  正規化オプション
                           │
AbortSignal     ───▶  checkAborted()      ───▶  継続/中断判定
                           │
                      binding.openFileHandle() ─▶  FileHandle
                           │                        kHandle: native handle
                           │                        kFd: file descriptor
                           │                        kRefs: 1
                           │
                      binding.read()      ───▶  Buffer(データ)
                           │
                      close()             ───▶  void
                           │
                      toString()          ───▶  Promise<string>
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| promises.js | `lib/internal/fs/promises.js` | ソース | Promise版API実装（メイン） |
| fs.js | `lib/fs.js` | ソース | promises属性でエクスポート |
| utils.js | `lib/internal/fs/utils.js` | ソース | 共通ユーティリティ |
| streams.js | `lib/internal/fs/streams.js` | ソース | ストリームAPI（FileHandleから利用） |
| watchers.js | `lib/internal/fs/watchers.js` | ソース | watch実装 |
| glob.js | `lib/internal/fs/glob.js` | ソース | glob関数実装 |
| cp.js | `lib/internal/fs/cp/cp.js` | ソース | cp関数実装 |
| rimraf.js | `lib/internal/fs/rimraf.js` | ソース | rm関数の再帰削除実装 |
