# 機能設計書 79-NextURL

## 概要

本ドキュメントは、Next.jsのNextURLクラスの設計について記述する。NextURLはWeb標準のURL APIを拡張し、Next.js固有のURL構造（basePath、国際化ロケール、ビルドID、trailingSlash）を解析・操作するためのユーティリティクラスである。

### 本機能の処理概要

NextURLクラスは、URL文字列をパースしてNext.jsの設定に基づくパスセグメント（basePath、locale、buildId）を分離・管理する。Middleware内のNextRequestオブジェクトの`nextUrl`プロパティやNextResponse内部で使用され、Next.js固有のルーティング情報にアクセスするためのインターフェースを提供する。

**業務上の目的・背景**：Next.jsアプリケーションはbasePath（サブパスでのホスティング）、国際化（i18n）ロケール、trailingSlashなどの設定によりURL構造が複雑になる。NextURLはこれらの設定を考慮したURL解析・再構築を行い、Middleware開発者がURL操作を安全かつ正確に行えるようにする。

**機能の利用シーン**：Middlewareでのパス名判定、ロケール取得・変更、basePath操作、クエリパラメータの取得、URLの再構築、リダイレクト先URLの生成。

**主要な処理内容**：
1. URL文字列のパースとlocalhost正規化
2. `getNextPathnameInfo()`によるパス情報の分析（basePath、locale、buildId、trailingSlashの抽出）
3. `detectDomainLocale()`によるドメインベースのロケール検出
4. getter/setterによるURL各部分（pathname, host, hostname, port, protocol, search, hash等）の操作
5. `formatNextPathnameInfo()`によるURL再構築（toString/toJSON/href）

**関連システム・外部連携**：NextRequest（nextUrlプロパティ）、NextResponse（URL操作）、i18n設定、next.config.js設定

**権限による制御**：特になし。NextURLはURL操作ユーティリティとして任意のコンテキストで使用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | NextURLは画面に直接関連しない。サーバーサイドのURL操作ユーティリティとして使用される |

## 機能種別

データ操作 / URLパース・構築ユーティリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input | string \| URL | Yes | パース対象のURL | - |
| baseOrOpts | string \| URL \| Options | No | ベースURLまたはオプション | - |
| opts | Options | No | nextConfig、headers、forceLocale、base、i18nProviderを含むオプション | - |

### 入力データソース

- HTTPリクエストURL
- next.config.jsの設定（basePath, i18n, trailingSlash）
- リクエストヘッダー（ホスト名の解決用）

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| basePath | string | Next.jsのbasePath設定に基づくパスプレフィックス |
| buildId | string \| undefined | Next.jsビルドID |
| locale | string | 現在のロケール文字列 |
| defaultLocale | string \| undefined | デフォルトロケール |
| domainLocale | DomainLocale \| undefined | ドメインベースのロケール設定 |
| pathname | string | basePath/locale/buildIdを除いたパス名 |
| searchParams | URLSearchParams | クエリパラメータ |
| host | string | ホスト（hostname:port） |
| hostname | string | ホスト名 |
| port | string | ポート番号 |
| protocol | string | プロトコル（http: / https:） |
| href | string | 完全URL文字列（basePath/locale/buildIdを含む再構築） |
| origin | string | オリジン（protocol://host） |
| search | string | クエリ文字列 |
| hash | string | フラグメント |

### 出力先

Middleware内やadapter内部でURL情報として参照される。

## 処理フロー

### 処理シーケンス

```
1. コンストラクタ呼び出し
   └─ 引数の解析（baseOrOptsが文字列/URLかOptionsかを判定）
2. parseURL()
   └─ localhost正規化（127.0.0.1, [::1]をlocalhostに変換）
   └─ new URL()でパース
3. analyze()
   └─ getNextPathnameInfo()でパスセグメントを分析
   └─ getHostname()でホスト名を取得
   └─ detectDomainLocale()でドメインロケールを検出
   └─ 内部状態の設定（pathname, defaultLocale, basePath, buildId, locale, trailingSlash）
4. プロパティアクセス
   └─ getter/setterで各URL部分にアクセス
5. href/toString/toJSON呼び出し時
   └─ formatNextPathnameInfo()でbasePath/locale/buildId/trailingSlashを含むパスを再構築
   └─ protocol + host + pathname + search + hashで完全URLを構成
```

### フローチャート

```mermaid
flowchart TD
    A[new NextURL input, opts] --> B[引数解析]
    B --> C[parseURL localhost正規化]
    C --> D[analyze]
    D --> E[getNextPathnameInfo]
    E --> F[basePath/locale/buildId抽出]
    D --> G[getHostname]
    G --> H[detectDomainLocale]
    F --> I[内部状態設定]
    H --> I
    I --> J[NextURLインスタンス]
    J --> K{プロパティアクセス}
    K -->|pathname/host等| L[getter/setter]
    K -->|href/toString| M[formatNextPathnameInfo]
    M --> N[完全URL再構築]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-79-01 | localhost正規化 | 127.x.x.x, [::1], localhostはすべてlocalhostに統一される | URL パース時 |
| BR-79-02 | ロケール検証 | locale setterで設定するロケールはi18n.localesに含まれている必要がある | locale設定時 |
| BR-79-03 | URL正規化スキップ | __NEXT_NO_MIDDLEWARE_URL_NORMALIZEがtrueの場合、パス情報解析でデータパースをスキップ | 環境変数設定時 |
| BR-79-04 | basePath先頭スラッシュ | basePath setterで先頭にスラッシュがない場合自動付与 | basePath設定時 |
| BR-79-05 | デフォルトロケール非表示 | forceLocaleがfalseの場合、デフォルトロケールはパスに含まれない | href生成時 |
| BR-79-06 | ドメインロケール優先 | ドメインベースのi18n設定がある場合、ドメインのdefaultLocaleが優先される | i18n domains設定時 |

### 計算ロジック

`href`プロパティの算出：
```
href = protocol + '//' + host + formatNextPathnameInfo({basePath, buildId, defaultLocale, locale, pathname, trailingSlash}) + search + hash
```

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

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

NextURLクラスはデータベース操作を行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| TypeError | ロケールエラー | i18n.localesに含まれないロケールをlocaleに設定した場合 | 有効なロケール文字列を使用する |
| TypeError | URLパースエラー | 不正なURL文字列をinputに渡した場合 | 有効なURL文字列を使用する |

### リトライ仕様

該当なし。

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

該当なし。

## パフォーマンス要件

- コンストラクタでanalyze()が同期的に実行される
- getNextPathnameInfo()は正規表現ベースのパス解析で軽量
- clone()メソッドは新しいインスタンスを生成する

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

- URL文字列のパースはWeb標準URLコンストラクタに委譲しており、不正なURL形式は標準的に処理される
- localhostの正規化により、同一ホストの判定が統一される

## 備考

- `[Internal]` Symbolによる内部状態の隠蔽
- `[Symbol.for('edge-runtime.inspect.custom')]`によるEdge Runtimeでのカスタム表示
- `clone()`メソッドで現在のオプションを保持したコピーを生成可能
- `forceLocale`オプションはリダイレクト時のURL生成でデフォルトロケールの非表示を制御

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | next-url.ts | `packages/next/src/server/web/next-url.ts` | Optionsインターフェース（10-20行目）と[Internal]シンボルの内部構造（35-45行目）を確認。basePath, buildId, locale, defaultLocale, domainLocale, url(URL), optionsの保持を理解する |

**読解のコツ**: NextURLは内部に標準URLオブジェクトを保持し、その上にNext.js固有のプロパティ層を追加する二層構造。pathnameはbasePath/locale/buildIdを除いた「純粋なパス」として保持され、href取得時にformatNextPathnameInfo()で再構築される。

#### Step 2: コンストラクタとanalyze()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | next-url.ts | `packages/next/src/server/web/next-url.ts` | コンストラクタ（47-74行目）とanalyze()メソッド（76-104行目）を確認 |

**主要処理フロー**:
1. **47-74行目**: コンストラクタ。オーバーロードされた引数を解析し、parseURL()でURLをパース後、analyze()を呼び出す
2. **76-104行目**: analyze()。getNextPathnameInfo()でパスセグメント分析、getHostname()でホスト名取得、detectDomainLocale()でドメインロケール検出

#### Step 3: getter/setterとformatPathname()を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | next-url.ts | `packages/next/src/server/web/next-url.ts` | getter/setter群（123-257行目）とformatPathname()（106-117行目）を確認。hrefプロパティ（192-196行目）がformatPathname()を使って完全URLを再構築する仕組みを理解する |

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

```
NextURL(input, opts) [next-url.ts:47]
    |
    +-- parseURL(input, base) [next-url.ts:25]
    |       +-- new URL() [Web API]
    |       +-- REGEX_LOCALHOST_HOSTNAME [正規化]
    |
    +-- analyze() [next-url.ts:76]
    |       +-- getNextPathnameInfo() [router/utils]
    |       +-- getHostname() [shared/lib]
    |       +-- detectDomainLocale() [shared/lib/i18n]
    |
    +-- formatPathname() [next-url.ts:106]
    |       +-- formatNextPathnameInfo() [router/utils]
    |
    +-- clone() [next-url.ts:280]
            +-- new NextURL(String(this), options)
```

### データフロー図

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

URL文字列 ----------> parseURL()                    NextURLインスタンス
                         |                               |
Options ------------->   +-> analyze()                   +-- basePath: string
  (nextConfig,           |   +-> getNextPathnameInfo()   +-- locale: string
   headers,              |   +-> detectDomainLocale()    +-- pathname: string
   forceLocale,          |                               +-- searchParams
   i18nProvider)         +-> [Internal]状態設定            +-- host, hostname
                         |                               +-- port, protocol
                         +-> formatPathname()             +-- href (再構築URL)
                             +-> formatNextPathnameInfo()  +-- origin, search, hash
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| next-url.ts | `packages/next/src/server/web/next-url.ts` | ソース | NextURLクラス本体（284行） |
| get-next-pathname-info.ts | `packages/next/src/shared/lib/router/utils/get-next-pathname-info.ts` | ソース | パスセグメント解析 |
| format-next-pathname-info.ts | `packages/next/src/shared/lib/router/utils/format-next-pathname-info.ts` | ソース | パスセグメント再構築 |
| detect-domain-locale.ts | `packages/next/src/shared/lib/i18n/detect-domain-locale.ts` | ソース | ドメインロケール検出 |
| get-hostname.ts | `packages/next/src/shared/lib/get-hostname.ts` | ソース | ホスト名取得 |
