# 機能設計書 41-punycode

## 概要

本ドキュメントは、Node.jsの`punycode`モジュールの機能設計を記載する。Punycodeは国際化ドメイン名（IDN）をASCII互換エンコーディング（ACE）に変換するためのエンコーディング方式であり、RFC 3492で定義されている。

### 本機能の処理概要

punycodeモジュールは、Unicode文字列とPunycode（ASCII）文字列間の相互変換機能を提供する。国際化ドメイン名の処理において、非ASCII文字を含むドメイン名をDNSシステムで扱えるASCII形式に変換する際に使用される。

**業務上の目的・背景**：インターネットのドメイン名システム（DNS）は元来ASCII文字のみをサポートしていた。しかし、世界中のユーザーが自国語でドメイン名を使用したいというニーズがあり、IDN（Internationalized Domain Names）が開発された。Punycodeはこの国際化ドメイン名をASCII互換形式に変換するための標準エンコーディングである。例えば、日本語ドメイン「日本語.jp」を「xn--wgv71a119e.jp」のような形式に変換する。

**機能の利用シーン**：
- 国際化ドメイン名を含むURLの処理
- メールアドレスのドメイン部分の国際化対応
- DNSルックアップ前のドメイン名正規化
- ブラウザやメールクライアントでの国際化ドメイン表示

**主要な処理内容**：
1. `encode`: Unicode文字列をPunycode文字列にエンコード
2. `decode`: Punycode文字列をUnicode文字列にデコード
3. `toASCII`: ドメイン名全体をASCII互換形式に変換
4. `toUnicode`: ASCII形式のドメイン名をUnicode形式に変換
5. `ucs2.encode`: コードポイント配列をUCS-2文字列に変換
6. `ucs2.decode`: UCS-2文字列をコードポイント配列に変換

**関連システム・外部連携**：
- `url`モジュール：URL解析時の国際化ドメイン処理
- DNS関連機能：名前解決前のドメイン名正規化

**権限による制御**：特になし（純粋なエンコーディング変換機能）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面機能マッピングに該当なし |

## 機能種別

データ変換処理（エンコード/デコード）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | String | Yes | 変換対象の文字列 | 空文字列可、encode/decodeは単一ラベル、toASCII/toUnicodeはドメイン名全体 |
| encoding（getAsset） | String | No | デコード時のエンコーディング指定 | 有効なエンコーディング名 |

### 入力データソース

JavaScriptコード内からの直接呼び出し

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| encoded | String | Punycodeエンコードされた文字列（encode/toASCII） |
| decoded | String | Unicodeデコードされた文字列（decode/toUnicode） |
| codePoints | Array | コードポイントの配列（ucs2.decode） |
| ucs2String | String | UCS-2文字列（ucs2.encode） |

### 出力先

呼び出し元への戻り値

## 処理フロー

### 処理シーケンス

```
1. 入力文字列の受け取り
   └─ Unicode文字列またはPunycode文字列
2. 文字列の分析
   └─ ドメイン名の場合はラベル（.区切り）に分割
   └─ メールアドレスの場合は@の前後を分離
3. エンコード/デコード処理
   └─ encode: Bootstring アルゴリズムでASCII変換
   └─ decode: Bootstring アルゴリズムでUnicode復元
4. 結果の組み立て
   └─ ラベルを結合してドメイン名を再構成
5. 結果を返却
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{関数種別}
    B -->|encode| C[UCS-2デコード]
    B -->|decode| D[基本コードポイント抽出]
    B -->|toASCII| E[ドメインを分割]
    B -->|toUnicode| F[ドメインを分割]
    C --> G[基本コードポイント出力]
    G --> H[非基本コードポイントをBootstringエンコード]
    H --> I[結果を結合]
    D --> J[可変長整数デコード]
    J --> K[コードポイント挿入]
    K --> L[文字列に変換]
    E --> M{非ASCII文字あり?}
    M -->|Yes| N[xn--プレフィックス + encode]
    M -->|No| O[そのまま出力]
    F --> P{xn--プレフィックス?}
    P -->|Yes| Q[プレフィックス除去 + decode]
    P -->|No| R[そのまま出力]
    I --> S[終了]
    L --> S
    N --> S
    O --> S
    Q --> S
    R --> S
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | 基数36エンコード | Punycodeは基数36（a-z, 0-9）を使用 | 常時 |
| BR-02 | デリミタ | 基本コードポイントと非基本を'-'で区切る | エンコード時 |
| BR-03 | xn--プレフィックス | Punycodeドメインラベルは'xn--'で始まる | toASCII/toUnicode |
| BR-04 | RFC 3490セパレータ | 複数のドットセパレータを認識 | ドメイン処理時 |

### 計算ロジック

Bootstringアルゴリズム（RFC 3492）:
- 基底: 36
- tmin: 1
- tmax: 26
- skew: 38
- damp: 700
- 初期バイアス: 72
- 初期n: 128

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

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

該当なし（メモリ内処理のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| RangeError | overflow | 入力がより広い整数を必要とする | 入力データの確認 |
| RangeError | not-basic | 0x80以上の非基本コードポイント検出 | 入力形式の確認 |
| RangeError | invalid-input | 無効な入力 | 入力データの確認 |

### リトライ仕様

リトライ不要（同期処理、入力エラーは再試行しても解決しない）

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

該当なし（ステートレスな変換処理）

## パフォーマンス要件

- 同期処理のため、大量のドメイン変換時はイベントループのブロッキングに注意
- 短い文字列の変換は即座に完了

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

- 非推奨警告（DEP0040）が発行される
- IDNホモグラフ攻撃への対策は呼び出し側の責任
- ユーザーランドの代替ライブラリ使用を推奨

## 備考

- Node.js v7.0.0以降、このモジュールは非推奨（DEP0040）
- `url.domainToASCII()`と`url.domainToUnicode()`の使用を推奨
- 互換性のためnode_modules内からの使用時は警告が抑制される

---

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

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

### 推奨読解順序

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

Punycodeモジュールは比較的シンプルで、主にBootstringアルゴリズムのパラメータ定義を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | punycode.js | `lib/punycode.js` | 15-31行目のBootstringパラメータと正規表現定義 |

**読解のコツ**: RFC 3492の仕様書と照らし合わせながら、各定数の意味を理解する。`base=36`は使用する文字種（a-z26文字 + 0-9の10文字）を表す。

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

公開APIの定義を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | punycode.js | `lib/punycode.js` | 430-455行目のモジュールエクスポート定義 |

**主要処理フロー**:
1. **430-437行目**: バージョン情報の定義（'2.1.0'）
2. **445-448行目**: UCS-2エンコード/デコードユーティリティ
3. **449-452行目**: 基本的なencode/decode関数
4. **451-452行目**: ドメイン名用のtoASCII/toUnicode関数

#### Step 3: 基本エンコード処理を理解する

encode関数の実装を詳細に読む。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | punycode.js | `lib/punycode.js` | 302-388行目のencode関数 |

**主要処理フロー**:
- **306行目**: UCS-2文字列をコードポイント配列に変換
- **317-321行目**: 基本コードポイント（ASCII）を出力配列に追加
- **330-331行目**: 基本文字列が存在する場合はデリミタ'-'を追加
- **335-386行目**: 非基本コードポイントをBootstringアルゴリズムでエンコード
- **362-374行目**: 可変長整数エンコーディングのコアロジック

#### Step 4: 基本デコード処理を理解する

decode関数の実装を詳細に読む。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | punycode.js | `lib/punycode.js` | 208-293行目のdecode関数 |

**主要処理フロー**:
- **220-231行目**: 基本コードポイントの抽出（最後の'-'より前の部分）
- **236-290行目**: 可変長整数のデコードとコードポイント挿入
- **250-254行目**: 文字からデジット値への変換
- **288行目**: 算出したコードポイントを出力配列に挿入

#### Step 5: ドメイン名処理を理解する

toASCII/toUnicode関数の実装を読む。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | punycode.js | `lib/punycode.js` | 401-426行目のtoUnicode/toASCII関数 |
| 5-2 | punycode.js | `lib/punycode.js` | 84-98行目のmapDomain関数 |

**主要処理フロー**:
- **84-98行目**: ドメイン名をラベルに分割し、各ラベルに変換を適用
- **85-91行目**: メールアドレスの場合、@より前の部分を保持
- **94行目**: RFC 3490で定義された複数のセパレータを正規化
- **403-406行目**: toUnicodeはxn--プレフィックスのラベルをデコード
- **421-424行目**: toASCIIは非ASCII文字を含むラベルにxn--プレフィックスを付加

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

```
punycode.toASCII(domain)
    │
    ├─ mapDomain(domain, callback)
    │      │
    │      ├─ domain.split('@')  // メールアドレス対応
    │      ├─ domain.replace(regexSeparators)  // セパレータ正規化
    │      └─ map(labels, callback)
    │             │
    │             └─ encode(string)  // 非ASCIIラベルのみ
    │                    │
    │                    ├─ ucs2decode(input)  // UCS-2→コードポイント
    │                    ├─ basicToDigit()
    │                    ├─ digitToBasic()
    │                    └─ adapt()  // バイアス調整
    │
    └─ 'xn--' + encoded  // プレフィックス付加

punycode.toUnicode(domain)
    │
    └─ mapDomain(domain, callback)
           │
           └─ decode(string.slice(4))  // xn--除去後デコード
                  │
                  ├─ basicToDigit()
                  ├─ adapt()
                  └─ String.fromCodePoint()
```

### データフロー図

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

Unicode文字列       ┌─────────────────────────┐
"日本語.jp"    ───▶│  mapDomain()            │
                   │  ├─ ラベル分割           │
                   │  ├─ 各ラベルを変換       │
                   │  │   └─ encode()        │───▶  "xn--wgv71a119e.jp"
                   │  └─ ラベル結合           │      ASCII形式
                   └─────────────────────────┘

Punycode文字列      ┌─────────────────────────┐
"xn--wgv71a119e.jp"│  mapDomain()            │
               ───▶│  ├─ ラベル分割           │
                   │  ├─ 各ラベルを変換       │
                   │  │   └─ decode()        │───▶  "日本語.jp"
                   │  └─ ラベル結合           │      Unicode形式
                   └─────────────────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| punycode.js | `lib/punycode.js` | ソース | Punycodeエンコード/デコードの主要実装 |
| url.js | `lib/url.js` | ソース | domainToASCII/domainToUnicodeの提供（代替推奨API） |
| internal/util.js | `lib/internal/util.js` | ソース | isInsideNodeModules関数の提供 |
