# 機能設計書 15-querystring

## 概要

本ドキュメントは、Node.jsのquerystringモジュール（URLクエリ文字列のパース・シリアライズ）の機能設計を記述したものである。

### 本機能の処理概要

querystring機能は、URLのクエリ文字列（`key=value&key2=value2`形式）の解析（パース）と生成（シリアライズ）を行う。HTTPリクエストのパラメータ処理やURL構築に使用される。

**業務上の目的・背景**：Webアプリケーションでは、GETリクエストのパラメータやフォームデータがクエリ文字列形式で送信される。これらを効率的に処理するためのパース・シリアライズ機能が必要である。

**機能の利用シーン**：
- HTTPリクエストのクエリパラメータ解析
- フォームデータ（application/x-www-form-urlencoded）の処理
- URLの動的生成
- APIリクエストパラメータの構築
- リダイレクトURLの生成

**主要な処理内容**：
1. parse(): クエリ文字列をオブジェクトに変換
2. stringify(): オブジェクトをクエリ文字列に変換
3. escape(): URL安全なエンコード
4. unescape(): URLデコード
5. unescapeBuffer(): バッファへのデコード

**関連システム・外部連携**：
- http/https: リクエストURL処理
- url: URLモジュールとの連携
- buffer: バイナリデータ処理

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

## 関連画面

本機能はCLI/APIレベルの機能であり、直接関連する画面はない。

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

## 機能種別

データ変換 / URL処理

## 入力仕様

### 入力パラメータ

#### parse(str[, sep[, eq[, options]]])

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| str | string | Yes | パース対象のクエリ文字列 | - |
| sep | string | No | キー/値ペアの区切り文字 | デフォルト'&' |
| eq | string | No | キーと値の区切り文字 | デフォルト'=' |
| options.maxKeys | number | No | 最大キー数 | デフォルト1000、-1で無制限 |
| options.decodeURIComponent | function | No | デコード関数 | - |

#### stringify(obj[, sep[, eq[, options]]])

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| obj | Object | Yes | シリアライズ対象オブジェクト | - |
| sep | string | No | キー/値ペアの区切り文字 | デフォルト'&' |
| eq | string | No | キーと値の区切り文字 | デフォルト'=' |
| options.encodeURIComponent | function | No | エンコード関数 | - |

### 入力データソース

- クエリ文字列（URLから取得）
- オブジェクト（アプリケーションで構築）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| parsedObject | Object | パース結果（キー: 値 または キー: [値の配列]）|
| queryString | string | シリアライズ結果 |

### 出力先

- アプリケーションコード（戻り値）

## 処理フロー

### 処理シーケンス

```
parse():
1. 区切り文字コード取得
   └─ sep/eqをcharCode配列に変換
2. 文字列走査
   └─ sep/eqを検出しながらkey/value抽出
3. URLデコード
   └─ %エンコードされた文字をデコード
4. オブジェクト構築
   └─ 同一キーは配列化

stringify():
1. オブジェクトキー取得
2. 各キー/値をエンコード
   └─ 配列値は複数ペアに展開
3. 区切り文字で結合
4. 文字列返却
```

### フローチャート

```mermaid
flowchart TD
    subgraph parse
        A1[クエリ文字列] --> B1[区切り文字解析]
        B1 --> C1{ペア検出}
        C1 -->|あり| D1[key/value分離]
        D1 --> E1[URLデコード]
        E1 --> F1{同一キー存在?}
        F1 -->|Yes| G1[配列に追加]
        F1 -->|No| H1[オブジェクトに追加]
        G1 --> C1
        H1 --> C1
        C1 -->|なし| I1[オブジェクト返却]
    end

    subgraph stringify
        A2[オブジェクト] --> B2[キー取得]
        B2 --> C2{値が配列?}
        C2 -->|Yes| D2[各要素をペア化]
        C2 -->|No| E2[単一ペア化]
        D2 --> F2[URLエンコード]
        E2 --> F2
        F2 --> G2[区切り文字で結合]
        G2 --> H2[文字列返却]
    end
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | 最大キー数 | デフォルト1000個まで、-1で無制限 | parse()時 |
| BR-002 | 配列化 | 同一キーが複数ある場合は配列として格納 | parse()時 |
| BR-003 | プラス記号 | '+'はスペースにデコード | unescape時 |
| BR-004 | 空値スキップ | 配列の空要素はスキップ | stringify()時 |
| BR-005 | 型変換 | 値はstring/number/bigint/booleanを許容 | stringify()時 |

### 計算ロジック

#### URLエンコード（escape）
```javascript
// エスケープ不要な文字
const noEscape = [
  // ! - . _ ~ ' ( ) * digits alpha(upper/lower)
];

function qsEscape(str) {
  return encodeStr(str, noEscape, hexTable);
}
```

#### URLデコード（unescape）
```javascript
function qsUnescape(s, decodeSpaces) {
  try {
    return decodeURIComponent(s);
  } catch {
    return QueryString.unescapeBuffer(s, decodeSpaces).toString();
  }
}
```

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

本機能はデータベースを直接操作しない。

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | - |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | - | 本モジュールは明示的なエラーを投げない | - |

parse()は不正な入力に対してもエラーを投げず、可能な限りパースを試みる。

### リトライ仕様

本機能にリトライ仕様はない。

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

本機能にトランザクション仕様はない。

## パフォーマンス要件

- 文字コード判定はテーブル参照で高速化
- maxKeysによる処理量制限でDoS防止
- decodeURIComponentの失敗時のみフォールバック処理

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

- maxKeysによるパラメータ数制限（DoS防止）
- プロトタイプ汚染防止：結果オブジェクトは`{ __proto__: null }`で作成
- 不正なエンコードは無視（エラーにしない）

## 備考

- WHATWGのURLSearchParamsが推奨される場合もある
- 同一キーの複数値は配列として格納される

---

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

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

### 推奨読解順序

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

ヘルパーテーブルの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | querystring.js | `lib/querystring.js` | unhexTable/noEscapeテーブル定義 |

**読解のコツ**: hexテーブルは16進数デコード用、noEscapeはエンコード不要文字の判定用。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | querystring.js | `lib/querystring.js` | module.exports（45-58行目） |

**主要処理フロー**:
- **45-58行目**: エクスポート定義（parse/stringify/escape/unescape等）

#### Step 3: parse()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | querystring.js | `lib/querystring.js` | parse()（317-467行目） |

**主要処理フロー**:
- **318行目**: 結果オブジェクト初期化（__proto__: null）
- **324-325行目**: 区切り文字コード取得
- **329-338行目**: maxKeys処理
- **355-451行目**: 文字列走査とkey/value抽出
- **380行目**: addKeyVal関数で値追加

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | querystring.js | `lib/querystring.js` | stringify()（227-269行目） |

**主要処理フロー**:
- **228-229行目**: デフォルト区切り文字設定
- **231-234行目**: エンコード関数取得
- **239-266行目**: オブジェクト走査とペア生成

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

```
querystring (lib/querystring.js)
    │
    ├─ parse(str, sep, eq, options)
    │      ├─ charCodes() - 区切り文字コード取得
    │      ├─ addKeyVal() - 値をオブジェクトに追加
    │      │      └─ decodeStr() - URLデコード
    │      └─ qsUnescape() / decodeURIComponent
    │
    ├─ stringify(obj, sep, eq, options)
    │      ├─ encodeStringified() - 値のエンコード
    │      └─ qsEscape() / encodeURIComponent
    │             └─ encodeStr() (internal/querystring)
    │
    ├─ escape(str)
    │      └─ encodeStr(str, noEscape, hexTable)
    │
    ├─ unescape(s, decodeSpaces)
    │      ├─ decodeURIComponent(s) [try]
    │      └─ unescapeBuffer(s, decodeSpaces).toString() [catch]
    │
    └─ unescapeBuffer(s, decodeSpaces)
           └─ %xx形式のデコード処理
```

### データフロー図

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

"a=1&b=2" ─────────▶ parse()
                          │
                          ▼
                    charCodes('&', '=')
                          │
                          ▼
                    文字列走査
                          │
                          ├─ key: "a", value: "1"
                          ├─ key: "b", value: "2"
                          │
                          ▼
                    URLデコード
                          │
                          ▼
                    { a: "1", b: "2" } ─────────▶ オブジェクト


{ a: 1, b: [2, 3] } ───▶ stringify()
                          │
                          ▼
                    キー走査
                          │
                          ├─ "a=1"
                          ├─ "b=2"
                          ├─ "b=3"
                          │
                          ▼
                    URLエンコード
                          │
                          ▼
                    "a=1&b=2&b=3" ─────────▶ 文字列
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| querystring.js | `lib/querystring.js` | ソース | メインエントリポイント |
| internal/querystring.js | `lib/internal/querystring.js` | ソース | encodeStr、hexTable、isHexTable |
| buffer.js | `lib/buffer.js` | ソース | Buffer処理 |
