# 機能設計書：設定サニタイズ

## 1. 概要

### 1.1 機能の目的
設定サニタイズ機能は、ユーザーが定義したPayload CMSの設定オブジェクト（Config）を正規化・検証し、実行時に使用可能なSanitizedConfigに変換する。デフォルト値の適用、フィールドの自動生成、関連機能の設定追加、およびバリデーションを行う。

### 1.2 機能の範囲
- 設定オブジェクトへのデフォルト値適用
- コレクション・グローバル・フィールドの個別サニタイズ
- システムコレクション・グローバルの自動追加
- ロケール設定の正規化
- i18n設定の正規化
- RichTextエディタの初期化
- エンドポイントの自動追加
- Joinフィールドの解決
- バージョン・ドラフト設定の正規化

### 1.3 関連画面
- なし（起動時処理）

### 1.4 関連機能
| 機能ID | 機能名 | 関連内容 |
|--------|--------|----------|
| 06 | フィールド基盤 | フィールドサニタイズ |
| 10 | 認証システム | 認証フィールド追加 |
| 14 | アップロード/ファイル処理 | アップロードフィールド追加 |
| 31 | バージョン管理 | バージョン設定正規化 |
| 37 | 型生成 | TypeScript設定 |

---

## 2. 機能種別

- [ ] API（REST）
- [ ] バッチ処理
- [ ] 画面機能
- [ ] 帳票出力
- [ ] 通知機能
- [x] システム内部機能

---

## 3. 入力仕様

### 3.1 Config（ユーザー設定）

| パラメータ | 型 | 必須 | デフォルト | 説明 |
|-----------|-----|------|-----------|------|
| db | DatabaseAdapter | Yes | - | データベースアダプター |
| secret | string | Yes | - | 暗号化シークレット |
| editor | RichTextEditor | No | - | デフォルトRichTextエディタ |
| collections | CollectionConfig[] | No | [] | コレクション定義 |
| globals | GlobalConfig[] | No | [] | グローバル定義 |
| admin | AdminConfig | No | defaults | 管理画面設定 |
| localization | LocalizationConfig | No | false | ローカライゼーション設定 |
| i18n | I18nConfig | No | {} | 国際化設定 |
| routes | RoutesConfig | No | defaults | ルート設定 |
| graphQL | GraphQLConfig | No | defaults | GraphQL設定 |
| typescript | TypeScriptConfig | No | defaults | TypeScript生成設定 |
| jobs | JobsConfig | No | {} | ジョブキュー設定 |
| folders | FoldersConfig | No | false | フォルダ機能設定 |

### 3.2 defaults（デフォルト値）

| 項目 | デフォルト値 |
|------|-------------|
| admin.avatar | 'gravatar' |
| admin.dateFormat | 'MMMM do yyyy, h:mm a' |
| admin.theme | 'all' |
| admin.routes.login | '/login' |
| cookiePrefix | 'payload' |
| defaultDepth | 2 |
| defaultMaxTextLength | 40000 |
| maxDepth | 10 |
| routes.admin | '/admin' |
| routes.api | '/api' |
| routes.graphQL | '/graphql' |
| graphQL.maxComplexity | 1000 |
| jobs.deleteJobOnComplete | true |
| jobs.depth | 0 |
| telemetry | true |
| typescript.autoGenerate | true |

---

## 4. 出力仕様

### 4.1 SanitizedConfig

サニタイズ後の設定オブジェクト。以下の追加/変換が行われる：

| 追加項目 | 説明 |
|---------|------|
| localization.localeCodes | ロケールコード文字列配列 |
| localization.locales[].toString | ロケールオブジェクトにtoString追加 |
| blocks | サニタイズ済みブロック配列 |
| upload.adapters | 使用中のアップロードアダプター一覧 |

### 4.2 自動追加されるコレクション

| スラッグ | 条件 | 説明 |
|---------|------|------|
| payload-jobs | jobs.enabled === true | ジョブキュー |
| payload-locked-documents | 常時 | ドキュメントロック |
| payload-preferences | 常時 | ユーザー設定 |
| payload-migrations | 常時 | マイグレーション履歴 |
| payload-folders | folders !== false | フォルダ管理 |
| payload-query-presets | enableQueryPresets | クエリプリセット |
| users | 認証コレクションなし時 | デフォルトユーザー |

### 4.3 自動追加されるグローバル

| スラッグ | 条件 | 説明 |
|---------|------|------|
| payload-cloud-instance | Payload Cloud時 | インスタンス識別 |
| payload-jobs-stats | jobs.scheduling | ジョブ統計 |

---

## 5. 処理フロー

### 5.1 サニタイズ全体フロー

```mermaid
flowchart TD
    A[sanitizeConfig] --> B[addDefaultsToConfig]
    B --> C[sanitizeAdminConfig]
    C --> D[setupOrderable]
    D --> E[Add auth root endpoints]
    E --> F[Sanitize localization]
    F --> G[Sanitize i18n]
    G --> H[Sanitize blocks]
    H --> I[Sanitize collections]
    I --> J[Sanitize globals]
    J --> K[Add system collections]
    K --> L[Sanitize RichText fields]
    L --> M[return SanitizedConfig]
```

### 5.2 コレクションサニタイズフロー

```mermaid
flowchart TD
    A[sanitizeCollection] --> B{_sanitized?}
    B -->|Yes| C[return as-is]
    B -->|No| D[addDefaultsToCollectionConfig]
    D --> E[sanitizeFields]
    E --> F[Add endpoints]
    F --> G[Add timestamp fields]
    G --> H[Format labels]
    H --> I{versions?}
    I -->|Yes| J[Sanitize versions config]
    I -->|No| K[Continue]
    J --> K
    K --> L{upload?}
    L -->|Yes| M[Add upload fields]
    L -->|No| N[Continue]
    M --> N
    N --> O{auth?}
    O -->|Yes| P[Add auth fields]
    O -->|No| Q[Continue]
    P --> Q
    Q --> R[validateUseAsTitle]
    R --> S[flattenAllFields]
    S --> T[sanitizeCompoundIndexes]
    T --> U[return SanitizedCollectionConfig]
```

### 5.3 フィールドサニタイズフロー

```mermaid
flowchart TD
    A[sanitizeFields] --> B[For each field]
    B --> C{field._sanitized?}
    C -->|Yes| D[Skip]
    C -->|No| E[Validate field type]
    E --> F[Validate field name]
    F --> G[Auto-label if needed]
    G --> H{join field?}
    H -->|Yes| I[sanitizeJoinField]
    H -->|No| J[Continue]
    I --> J
    J --> K{relationship/upload?}
    K -->|Yes| L[Validate relationTo]
    K -->|No| M[Continue]
    L --> M
    M --> N[Add default validate]
    N --> O{richText?}
    O -->|Yes| P[Setup editor]
    O -->|No| Q[Continue]
    P --> Q
    Q --> R{blocks?}
    R -->|Yes| S[Sanitize block fields]
    R -->|No| T[Continue]
    S --> T
    T --> U{has nested fields?}
    U -->|Yes| V[Recursive sanitizeFields]
    U -->|No| W[Next field]
    V --> W
    W --> B
```

---

## 6. ビジネスルール

### 6.1 コレクションサニタイズルール

| ID | ルール | 説明 |
|----|--------|------|
| BR-01 | スラッグ重複禁止 | 同一スラッグのコレクションは定義不可 |
| BR-02 | timestamps必須（versions時） | versions有効時はtimestamps: falseは不可 |
| BR-03 | 認証コレクション重複無効化 | auth有効時はdisableDuplicate: true |
| BR-04 | アップロードuseAsTitle | upload時のuseAsTitleデフォルトは'filename' |

### 6.2 フィールドサニタイズルール

| ID | ルール | 説明 |
|----|--------|------|
| BR-05 | フィールド名にドット禁止 | field.nameに'.'は含められない |
| BR-06 | 予約フィールド名 | auth/upload有効時は予約名が使用不可 |
| BR-07 | 自動ラベル生成 | labelが未定義ならnameからtoWordsで生成 |
| BR-08 | デフォルトバリデーション | validate未定義時はフィールドタイプのデフォルトを適用 |
| BR-09 | 親localizedの継承 | 親がlocalizedなら子のlocalizedは無効化 |
| BR-10 | relationTo検証 | relationship/uploadのrelationToは有効なコレクションのみ |
| BR-11 | ブロックスラッグ重複禁止 | 同一blocksフィールド内でスラッグ重複不可 |

### 6.3 バージョンサニタイズルール

| ID | ルール | 説明 |
|----|--------|------|
| BR-12 | maxPerDocデフォルト | 未指定時は100 |
| BR-13 | autosave.interval | autosave: trueの場合デフォルト間隔を適用 |
| BR-14 | drafts.validate | 未指定時はfalse |
| BR-15 | localizeStatus実験的 | experimental.localizeStatus未設定なら無効 |

### 6.4 システムコレクションルール

| ID | ルール | 説明 |
|----|--------|------|
| BR-16 | jobs.enabled判定 | tasks/workflowsが1つ以上あればtrue |
| BR-17 | jobs.scheduling判定 | tasks/workflowsにscheduleプロパティがあればtrue |
| BR-18 | schedulePublishTask | schedulePublish有効なcollection/globalがあれば自動追加 |

---

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

本機能は起動時の設定処理のため、データベース操作は行わない。

---

## 8. エラー処理

### 8.1 エラー種別

| エラークラス | 発生条件 | 説明 |
|-------------|----------|------|
| DuplicateCollection | 同一スラッグのコレクション | コレクションスラッグ重複 |
| InvalidConfiguration | 不正な設定値 | admin.userが有効な認証コレクションでない |
| MissingFieldType | field.typeが未定義 | フィールドタイプ必須 |
| MissingEditorProp | richTextにeditorなし | エディタ必須 |
| InvalidFieldName | フィールド名にドット含有 | '.'を含むフィールド名 |
| InvalidFieldRelationship | 無効なrelationTo | 存在しないコレクションへの参照 |
| DuplicateFieldName | 同一名のフィールド | フィールド名重複 |
| ReservedFieldName | 予約名使用 | auth/uploadの予約フィールド名 |
| TimestampsRequired | versions時にtimestamps: false | タイムスタンプ必須エラー |

---

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

本機能にトランザクション制御は含まれない。

---

## 10. パフォーマンス要件

| 項目 | 要件 | 備考 |
|------|------|------|
| 実行タイミング | 起動時1回 | サーバー起動時のみ |
| _sanitizedフラグ | 二重サニタイズ防止 | 既にサニタイズ済みならスキップ |
| RichText遅延サニタイズ | richTextSanitizationPromises | 設定完了後に一括処理 |

---

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

| 項目 | 対応 |
|------|------|
| 予約フィールド名チェック | auth/uploadの内部フィールド名を保護 |
| relationTo検証 | 存在しないコレクションへの参照を禁止 |
| csrf自動追加 | serverURLをcsrf配列に追加 |

---

## 12. 備考

### 12.1 defaultsオブジェクトの非推奨

`defaults`オブジェクトは4.0で削除予定。`addDefaultsToConfig`関数を使用すること。

### 12.2 localizedフィールドの互換性

`PAYLOAD_DO_NOT_SANITIZE_LOCALIZED_PROPERTY=true`で親がlocalizedでも子のlocalizedを維持可能（4.0でデフォルト化予定）。

### 12.3 RichTextサニタイズの遅延実行

RichTextフィールドのエディタ初期化は、全設定のサニタイズ完了後に`Promise.all`で一括実行される。

---

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

### 13.1 推奨読解順序

1. **デフォルト値**（`config/defaults.ts`）- デフォルト設定の理解
2. **メインサニタイズ**（`config/sanitize.ts`）- 全体フロー
3. **コレクションサニタイズ**（`collections/config/sanitize.ts`）- コレクション処理
4. **グローバルサニタイズ**（`globals/config/sanitize.ts`）- グローバル処理
5. **フィールドサニタイズ**（`fields/config/sanitize.ts`）- フィールド処理

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

```
sanitizeConfig (config/sanitize.ts)
├── addDefaultsToConfig() → config/defaults.ts
├── sanitizeAdminConfig() [内部関数]
│   └── validateTimezones()
├── setupOrderable() → config/orderable/index.ts
├── sanitizeFields() [dashboard widgets] → fields/config/sanitize.ts
├── sanitizeFields() [blocks] → fields/config/sanitize.ts
├── sanitizeCollection() [each collection] → collections/config/sanitize.ts
│   ├── addDefaultsToCollectionConfig()
│   ├── sanitizeFields()
│   │   ├── sanitizeJoinField() → fields/config/sanitizeJoinField.ts
│   │   └── [recursive for nested fields]
│   ├── getBaseAuthFields() → auth/getAuthFields.ts
│   ├── getBaseUploadFields() → uploads/getBaseFields.ts
│   ├── validateUseAsTitle() → collections/config/useAsTitle.ts
│   ├── flattenAllFields() → utilities/flattenAllFields.ts
│   └── sanitizeCompoundIndexes() → collections/config/sanitizeCompoundIndexes.ts
├── sanitizeGlobal() [each global] → globals/config/sanitize.ts
│   ├── sanitizeFields()
│   └── flattenAllFields()
├── addFolderCollection() → folders/addFolderCollection.ts
├── getLockedDocumentsCollection() → locked-documents/config.ts
├── getPreferencesCollection() → preferences/config.ts
├── getDefaultJobsCollection() → queues/config/collection.ts
├── getQueryPresetsConfig() → query-presets/config.ts
└── [RichText sanitization promises]
```

### 13.3 データフロー図

```
[Config (user input)]
         ↓
┌─────────────────────────────────┐
│  addDefaultsToConfig()          │
│  - デフォルト値マージ            │
│  - admin, routes, graphQL等     │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  sanitizeAdminConfig()          │
│  - loggingLevels設定            │
│  - dashboard widgets設定        │
│  - admin.user検証               │
│  - timezones検証                │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  localization/i18n正規化        │
│  - localeCodes生成              │
│  - toString追加                 │
│  - supportedLanguages設定       │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  blocks/collections/globals     │
│  sanitizeFields()               │
│  - フィールドタイプ検証          │
│  - 予約名チェック                │
│  - auto-label                   │
│  - validate関数設定             │
│  - richText editor設定          │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  システムコレクション追加        │
│  - payload-jobs                 │
│  - payload-locked-documents     │
│  - payload-preferences          │
│  - payload-migrations           │
└─────────────────────────────────┘
         ↓
┌─────────────────────────────────┐
│  RichText一括サニタイズ          │
│  Promise.all(richTextPromises)  │
└─────────────────────────────────┘
         ↓
[SanitizedConfig]
```

### 13.4 関連ファイル一覧

| ファイル | 行数 | 概要 |
|---------|------|------|
| `packages/payload/src/config/sanitize.ts` | 1-502 | メインサニタイズ関数 |
| `packages/payload/src/config/defaults.ts` | 1-195 | デフォルト値定義 |
| `packages/payload/src/config/types.ts` | - | Config/SanitizedConfig型定義 |
| `packages/payload/src/collections/config/sanitize.ts` | 1-313 | コレクションサニタイズ |
| `packages/payload/src/collections/config/defaults.ts` | - | コレクションデフォルト |
| `packages/payload/src/globals/config/sanitize.ts` | 1-195 | グローバルサニタイズ |
| `packages/payload/src/fields/config/sanitize.ts` | 1-547 | フィールドサニタイズ |
| `packages/payload/src/fields/config/sanitizeJoinField.ts` | - | Joinフィールドサニタイズ |
| `packages/payload/src/fields/config/reservedFieldNames.ts` | - | 予約フィールド名定義 |
| `packages/payload/src/fields/validations.ts` | - | デフォルトバリデーション |
| `packages/payload/src/utilities/formatLabels.ts` | - | ラベル自動生成 |
| `packages/payload/src/utilities/flattenAllFields.ts` | - | フィールドフラット化 |

### 13.5 重要な実装ポイント

#### 13.5.1 sanitizeConfig（config/sanitize.ts:116-502）
```typescript
// 116-119行目: デフォルト適用とadmin設定サニタイズ
export const sanitizeConfig = async (incomingConfig: Config): Promise<SanitizedConfig> => {
  const configWithDefaults = addDefaultsToConfig(incomingConfig)
  const config: Partial<SanitizedConfig> = sanitizeAdminConfig(configWithDefaults)
```

#### 13.5.2 _sanitizedフラグ（collections/config/sanitize.ts:42-46）
```typescript
// 42-46行目: 二重サニタイズ防止
if (collection._sanitized) {
  return collection as SanitizedCollectionConfig
}
collection._sanitized = true
```

#### 13.5.3 タイムスタンプ自動追加（collections/config/sanitize.ts:98-161）
```typescript
// 98-161行目: updatedAt, createdAt, deletedAt自動追加
if (sanitized.timestamps !== false) {
  let hasUpdatedAt: boolean | null = null
  let hasCreatedAt: boolean | null = null
  // ... 既存フィールドチェック後、不足分を追加
}
```

#### 13.5.4 フィールドバリデーション設定（fields/config/sanitize.ts:273-281）
```typescript
// 273-281行目: デフォルトvalidate関数設定
if (typeof field.validate === 'undefined') {
  const defaultValidate = validations[field.type as keyof typeof validations]
  if (defaultValidate) {
    field.validate = (val: any, options: any) =>
      defaultValidate(val, { ...field, ...options })
  } else {
    field.validate = (): true => true
  }
}
```

#### 13.5.5 RichTextエディタ遅延サニタイズ（fields/config/sanitize.ts:299-327）
```typescript
// 299-327行目: RichTextエディタの遅延初期化
if (field.type === 'richText') {
  const sanitizeRichText = async (_config: SanitizedConfig) => {
    if (!field.editor) {
      if (_config.editor && !requireFieldLevelRichTextEditor) {
        field.editor = _config.editor
      } else {
        throw new MissingEditorProp(field)
      }
    }
    // ...
  }
  if (richTextSanitizationPromises) {
    richTextSanitizationPromises.push(sanitizeRichText)
  }
}
```

#### 13.5.6 システムコレクション追加（config/sanitize.ts:337-451）
```typescript
// 337-390行目: ジョブコレクション追加
if (config.jobs.enabled) {
  let defaultJobsCollection = getDefaultJobsCollection(config.jobs)
  // ...
  ;(config.collections ??= []).push(sanitizedJobsCollection)
}

// 402-413行目: ロックドキュメントコレクション追加
const lockedDocumentsCollection = getLockedDocumentsCollection(config as unknown as Config)
if (lockedDocumentsCollection) {
  configWithDefaults.collections!.push(
    await sanitizeCollection(...)
  )
}
```
