# 機能設計書 79-FormData

## 概要

本ドキュメントは、BunのFormData機能の機能設計を記述する。FormDataはWHATWG XMLHttpRequest標準に準拠したフォームデータを表現・操作するAPIを提供する。

### 本機能の処理概要

BunのFormData APIは、HTMLフォームのデータを表現するWeb標準APIを提供する。キーと値のペアを管理し、テキスト値またはBlobファイルを保持できる。HTTPリクエストのbodyとしてmultipart/form-data形式で送信可能。

**業務上の目的・背景**：HTMLフォームデータの送受信、ファイルアップロード処理、REST API呼び出し時のフォームデータ構築など、フォームベースのデータ交換で使用される。Web標準互換により、ブラウザとサーバーでコードを共有可能。

**機能の利用シーン**：
- ファイルアップロード処理
- HTMLフォームデータの送信
- multipart/form-dataリクエストの構築
- URLエンコードされたデータのパース
- fetch() APIでのbody指定

**主要な処理内容**：
1. FormDataオブジェクトの生成
2. キー・値ペアの追加（append）、設定（set）、削除（delete）
3. 値の取得（get, getAll）、存在確認（has）
4. イテレーション（entries, keys, values, forEach）
5. multipart/form-dataへのシリアライズ

**関連システム・外部連携**：fetch、Request、Response、Blobと連携。HTTPクライアント・サーバーと連携。

**権限による制御**：特に権限による制御は行われない。すべてのコードから利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | コマンドライン/API経由での利用（画面なし） |

## 機能種別

フォームデータ処理 / Web標準API

## 入力仕様

### FormDataコンストラクタ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| form | HTMLFormElement | No | HTMLフォーム要素（Bunではサポートなし） | - |

### FormDataEntryValue（内部型）

| バリアント | 説明 |
|-----------|------|
| String | テキスト値 |
| Blob | Blobオブジェクト（ファイル） |

### Item構造体（C++内部）

| フィールド | 型 | 説明 |
|-----------|-----|------|
| name | String | エントリ名 |
| data | FormDataEntryValue | 値（文字列またはBlob） |

### 入力データソース

API呼び出し時の引数として直接指定。または、multipart/form-dataやapplication/x-www-form-urlencodedからパース。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| formData | FormData | FormDataオブジェクト |

### FormDataメソッド

| メソッド | 引数 | 戻り値 | 説明 |
|---------|-----|--------|------|
| append(name, value) | name: string, value: string/Blob | void | エントリを追加 |
| append(name, blob, filename) | name: string, blob: Blob, filename?: string | void | Blobエントリを追加 |
| delete(name) | name: string | void | 指定名のエントリを削除 |
| get(name) | name: string | FormDataEntryValue / null | 最初の値を取得 |
| getAll(name) | name: string | FormDataEntryValue[] | すべての値を取得 |
| has(name) | name: string | boolean | 存在確認 |
| set(name, value) | name: string, value: string/Blob | void | 値を設定（既存は上書き） |
| set(name, blob, filename) | name: string, blob: Blob, filename?: string | void | Blob値を設定 |
| entries() | - | Iterator | キー・値ペアのイテレータ |
| keys() | - | Iterator | キーのイテレータ |
| values() | - | Iterator | 値のイテレータ |
| forEach(callback) | callback: function | void | 各エントリにコールバック実行 |

### FormData.from()（Bun拡張）

| メソッド | 引数 | 戻り値 | 説明 |
|---------|-----|--------|------|
| FormData.from(data, type) | data: string/ArrayBufferView, type?: string | FormData | multipartまたはURLエンコードデータからパース |

### 出力先

FormDataオブジェクトとして返却。

## 処理フロー

### 処理シーケンス（FormData操作）

```
1. FormData作成
   └─ DOMFormData::create() でm_items初期化
2. エントリ追加（append/set）
   └─ m_itemsにItem追加
3. エントリ取得（get/getAll）
   └─ m_itemsから検索
4. イテレーション
   └─ Iterator経由でm_items走査
```

### フローチャート

```mermaid
flowchart TD
    A[new FormData] --> B[空のFormData作成]
    B --> C{操作?}

    C -->|append| D[m_items.append]
    C -->|set| E[既存を置換または追加]
    C -->|delete| F[m_items.removeAllMatching]
    C -->|get| G[最初の一致を返却]
    C -->|getAll| H[すべての一致を返却]
    C -->|has| I[一致があるかbool]

    D --> C
    E --> C
    F --> C
    G --> J[値またはnull]
    H --> K[配列]
    I --> L[true/false]

    subgraph シリアライズ
    M[fetch/Response] --> N{Content-Type}
    N -->|multipart| O[multipart/form-data生成]
    N -->|urlencoded| P[application/x-www-form-urlencoded生成]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-79-01 | 複数値許容 | 同じ名前で複数のエントリを持てる | append使用時 |
| BR-79-02 | set上書き | setは同名の既存エントリをすべて削除して設定 | set使用時 |
| BR-79-03 | サロゲートペア置換 | 不正なサロゲートペアは置換文字に変換 | 常時 |
| BR-79-04 | ファイル名設定 | Blobにはfilenameを設定可能 | Blob追加時 |

### 計算ロジック

なし

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

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

なし（データベース操作を行わない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| TypeError | Error | 無効な引数型 | 引数型を確認 |
| Error | Error | multipartパースエラー | データ形式を確認 |
| Error | Error | 境界文字列がない | boundary指定を確認 |

### リトライ仕様

なし（呼び出し側で制御）

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

なし

## パフォーマンス要件

- Vector<Item>による効率的な格納
- イテレータによる遅延評価
- Blobは参照で保持（コピーなし）

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

- 不正なサロゲートペアは安全に置換
- ファイル名は適切にエスケープ
- multipartボディは適切なboundaryで区切り

## 備考

Bun拡張機能：
- `FormData.from()`でmultipart/URLエンコードデータから直接パース可能
- `length`プロパティでエントリ数を取得可能
- `toJSON()`でJSON形式に変換可能

---

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

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

### 推奨読解順序

#### Step 1: FormDataのZigバインディングを理解する

Zig側のFormDataインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | DOMFormData.zig | `src/bun.js/bindings/DOMFormData.zig` | DOMFormData opaque型（1-122行目） |
| 1-2 | DOMFormData.zig | `src/bun.js/bindings/DOMFormData.zig` | FormDataEntry union型（83-89行目） |

**読解のコツ**: DOMFormDataはopaque型でC++実装を隠蔽。FormDataEntryでstring/fileを区別。

**主要処理フロー**:
1. **1-10行目**: extern関数宣言（C++との連携）
2. **11-22行目**: create/createFromURLQuery関数
3. **50-66行目**: append/appendBlob関数
4. **83-89行目**: FormDataEntry union（stringまたはfile）
5. **90-121行目**: forEach関数（各エントリをコールバック）

#### Step 2: C++実装を理解する

DOMFormDataの実際の実装。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | DOMFormData.h | `src/bun.js/bindings/DOMFormData.h` | DOMFormDataクラス定義（48-101行目） |
| 2-2 | DOMFormData.cpp | `src/bun.js/bindings/DOMFormData.cpp` | 実装（40-200行目） |

**主要処理フロー**:
- **54-57行目**: Item構造体（name, data）
- **52行目**: FormDataEntryValue = variant<String, RefPtr<Blob>>
- **66-73行目**: append/set/get/getAll/has/removeメソッド宣言
- **103-106行目**: append実装（m_itemsにpush）
- **152-186行目**: set実装（既存を削除してから設定）

#### Step 3: JSバインディングを理解する

JavaScriptへの公開インターフェース。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | JSDOMFormData.cpp | `src/bun.js/bindings/webcore/JSDOMFormData.cpp` | JSバインディング（1-200行目） |

**主要処理フロー**:
- **93-102行目**: プロトタイプ関数宣言（append, delete, get, getAll等）
- **111-121行目**: lengthプロパティゲッター
- **157-176行目**: コンストラクタ実装
- **187-197行目**: FormData.fromの登録

#### Step 4: パース機能を理解する

multipart/URLエンコードデータのパース。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | url.zig | `src/url.zig` | FormData struct（889-1073行目） |

**主要処理フロー**:
- **889-960行目**: FormData struct、Encoding enum、AsyncFormData
- **997-1067行目**: toJS関数（URLエンコード/multipartをJSに変換）
- **1075-1100行目以降**: toJSFromMultipartData関数

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

```
new FormData()
    │
    └─ JSDOMFormDataDOMConstructor::construct()
           └─ DOMFormData::create()
                  └─ m_items初期化（空Vector）

formData.append(name, value)
    │
    ├─ jsDOMFormDataPrototypeFunction_append()
    │      └─ DOMFormData::append()
    │             └─ m_items.append(Item{name, value})
    │
    └─ Blobの場合
           └─ DOMFormData::append(name, blob, filename)
                  └─ blob.setFileName(filename)
                  └─ m_items.append(Item{name, blob})

formData.get(name)
    │
    └─ jsDOMFormDataPrototypeFunction_get()
           └─ DOMFormData::get()
                  └─ m_itemsを走査、最初の一致を返却

FormData.from(data, type)
    │
    └─ FormData__jsFunctionFromMultipartData()
           └─ FormData::fromMultipartData()
                  ├─ URLEncoded: createFromURLQuery()
                  └─ Multipart: toJSFromMultipartData()

Blobからの変換:
blob.formData()
    │
    └─ Blob::getFormData()
           └─ FormData.toJS() / AsyncFormData.toJS()
```

### データフロー図

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

new FormData()     ───▶  DOMFormData::create()
                         └─ m_items = []              ───▶   FormData

append(name, val)  ───▶  DOMFormData::append()
                         └─ m_items.push({name, val}) ───▶   void

get(name)          ───▶  DOMFormData::get()
                         └─ m_items.find(name)        ───▶   value | null

set(name, val)     ───▶  DOMFormData::set()
                         ├─ 既存削除
                         └─ 追加/置換               ───▶   void

FormData.from()    ───▶  FormData::fromMultipartData()
                         ├─ URLEncoded解析
                         │    └─ URLParser::parseURLEncodedForm
                         └─ Multipart解析
                              └─ boundary区切りパース
                                        │
                                        ▼
                                   FormData             ───▶   FormData

fetch(url, {body: fd})
                   ───▶  multipart/form-data生成
                         ├─ boundary生成
                         ├─ 各Itemをシリアライズ
                         └─ Content-Type設定         ───▶   HTTP Request
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| DOMFormData.zig | `src/bun.js/bindings/DOMFormData.zig` | ソース | Zigバインディング |
| DOMFormData.h | `src/bun.js/bindings/DOMFormData.h` | ヘッダ | C++クラス定義 |
| DOMFormData.cpp | `src/bun.js/bindings/DOMFormData.cpp` | ソース | C++実装 |
| JSDOMFormData.cpp | `src/bun.js/bindings/webcore/JSDOMFormData.cpp` | ソース | JSバインディング |
| JSDOMFormData.h | `src/bun.js/bindings/webcore/JSDOMFormData.h` | ヘッダ | JSバインディング定義 |
| url.zig | `src/url.zig` | ソース | FormDataパース機能 |
| Blob.zig | `src/bun.js/webcore/Blob.zig` | ソース | Blob連携（fromDOMFormData） |
| Body.zig | `src/bun.js/webcore/Body.zig` | ソース | HTTPボディ連携 |
