
# Schema 驅動驗證器（Validator）

> 本模組為一套可擴充、可處理巢狀結構與陣列元素的通用驗證器，透過 Schema 定義欄位規則，自動執行驗證、型別轉換與預設值套用。

---

## 1. 功能總覽

### 1.1 型別驗證（Type Validation）

* **基礎型別**：`string`、`number`、`int`、`positiveInt`、`bool`、`nonEmptyString`、`json`、`object`、`array` 等。
* **日期/時間型別**：`datetimeStr`、`isoDatetimeStr`、`yMDStr`、`twYMDStr` 等。
* **台灣在地格式**：

    * `twNationalId`（身分證字號）
    * `twMobileNo`（手機）
    * `twLandPhoneNo`（市話）
    * `twTaxId`（統一編號）
    * `twMobileInvoiceBarcode`（手機載具）
    * `twInvoiceDonationOrgCode`（捐贈碼）
* **其他格式**：`email`、`hostname`、`hostnameWithPath`、`hostnameWithProtocol`、`addressWithDefault`、`creditCardNum`、`creditCardCvvCvc`、`creditCardExpDate` 等。

---

### 1.2 規則插件（Rule Plugins）

內建多種驗證規則，支援條件式必填、長度、範圍、陣列檢查等：

* **欄位間關聯**

    * `same:<peer>`：與另一欄位值相同
    * `sameLengthArr:<peer>`：與另一陣列長度相同
    * `requiredWhenExists:<peer>`：若另一欄位存在則必填
    * `requiredIfValueIs:<peer>:<value>`：若另一欄位等於指定值則必填
    * `requiredIfValueIsIn:<peer>:[v1,v2]`：若另一欄位值在指定集合中則必填
    * `requiredIfValueIsAll:[a:x,b:y]`：多條件全部符合時必填
    * `requiredIfValueIsAny:[a:x,b:y]`：多條件任一符合時必填
* **長度限制**

    * `minLen:X` / `maxLen:X`：字串或陣列長度限制
    * `arrMinLen:X` / `arrMaxLen:X`：陣列長度限制
* **數值範圍**

    * `range:min,max`：數值介於區間
    * `minValue:X` / `maxValue:X`：數值上下限
* **格式**

    * `regex:/pattern/`：正則表達式匹配
    * `validValues:[a,b,null]`：必須為指定集合之一
* **陣列規則**

    * `arrUnique`：陣列元素需唯一
    * `arrUniqueBy:key`：陣列物件的某欄位值需唯一
    * `arrValidValues:[v1,v2]`：陣列每個元素需為允許值

> **可擴充性**：支援 `useRule` 註冊自訂規則，並可設定 `perElement = true` 針對陣列元素逐一驗證。

---

### 1.3 Schema 展平與結構檢查

* **展平功能**（`schemaToFlatRules`）

    * 支援巢狀物件（`properties`）與陣列（`items`）結構展平為 dot path 與 `*` 表示陣列元素。
    * 範例：

      ```js
      {
        'name': 'type:string',
        'tags.*.label': 'type:string|required'
      }
      ```
* **結構黑名單檢查**（`checkBlackListTypeCombinations`）

    * 禁止不合理型別組合（如 `object` + `array`）。
    * 檢查結構衝突（如 `array` 不可同時定義 `properties`）。
    * 特定規則限制（如 `arrUniqueBy` 必須搭配 `items` 定義）。

---

### 1.4 驗證流程

```mermaid
flowchart TD
    A["開始"] --> B["讀取 Schema"]
    B --> C["展平成 flatRules (schemaToFlatRules)"]
    C --> D{"檢查型別與結構黑名單<br>(checkBlackListTypeCombinations)"}
    D -- 有錯誤 --> E["回傳錯誤並中止"]
    D -- 無錯誤 --> F["建立 Validator 實例 (ValidatorFac)"]

    F --> G["讀取輸入資料"]
    G --> H["逐一處理 flatKey 規則"]

    H --> I{"欄位值是否為空值？"}
    I -- 是 --> J["套用 default 值"]
    I -- 否 --> K["保留原值"]

    J --> L{"是否 required 或條件必填？"}
    K --> L
    L -- 是且仍空值 --> M["紀錄必填錯誤"]
    L -- 否 --> N{"是否有 type 規則？"}

    N -- 有 --> O{"型別驗證 (typeValidator)"}
    O -- 失敗 --> P["紀錄型別錯誤"]
    O -- 成功 --> Q["型別轉換 (TYPE_CAST)"]

    N -- 無 --> R["直接進行規則驗證"]

    Q --> R
    R --> S["套用其他規則 (rulePlugins / builtInRulePlugin)"]
    S --> T{"值是否為陣列且規則 perElement？"}
    T -- 是 --> U["逐元素驗證並記錄錯誤"]
    T -- 否 --> V["驗證完成"]

    U --> V
    V --> W{"所有欄位驗證完成？"}
    W -- 否 --> H
    W -- 是 --> X{"有錯誤？"}
    X -- 是 --> Y["validated=false, 回傳 values 與 errors"]
    X -- 否 --> Z["validated=true, 回傳 values 與空 errors"]

    Y --> END["結束"]
    Z --> END
```

---

**五個階段**

<details>


#### **1️⃣ Schema 載入與檢查階段**

```mermaid
flowchart TD
    A["開始"] --> B["讀取 Schema"]
    B --> C["展平成 flatRules (schemaToFlatRules)"]
    C --> D{"檢查型別與結構黑名單<br>(checkBlackListTypeCombinations)"}
    D -- 有錯誤 --> E["回傳錯誤並中止"]
    D -- 無錯誤 --> F["建立 Validator 實例 (ValidatorFac)"]
```

---

#### **2️⃣ 輸入資料讀取與欄位處理迴圈**

```mermaid
flowchart TD
    F["建立 Validator 實例 (ValidatorFac)"] --> G["讀取輸入資料"]
    G --> H["逐一處理 flatKey 規則"]
```

---

#### **3️⃣ 空值檢查與必填處理**

```mermaid
flowchart TD
    H["逐一處理 flatKey 規則"] --> I{"欄位值是否為空值？"}
    I -- 是 --> J["套用 default 值"]
    I -- 否 --> K["保留原值"]

    J --> L{"是否 required 或條件必填？"}
    K --> L
    L -- 是且仍空值 --> M["紀錄必填錯誤"]
    L -- 否 --> N{"是否有 type 規則？"}
```

---

#### **4️⃣ 型別檢查與規則驗證**

```mermaid
flowchart TD
    N -- 有 --> O{"型別驗證 (typeValidator)"}
    O -- 失敗 --> P["紀錄型別錯誤"]
    O -- 成功 --> Q["型別轉換 (TYPE_CAST)"]

    N -- 無 --> R["直接進行規則驗證"]
    Q --> R

    R --> S["套用其他規則 (rulePlugins / builtInRulePlugin)"]
    S --> T{"值是否為陣列且規則 perElement？"}
    T -- 是 --> U["逐元素驗證並記錄錯誤"]
    T -- 否 --> V["驗證完成"]

    U --> V
```

---

#### **5️⃣ 完成檢查與結果輸出**

```mermaid
flowchart TD
    V["驗證完成"] --> W{"所有欄位驗證完成？"}
    W -- 否 --> H["處理下一個 flatKey 規則"]
    W -- 是 --> X{"有錯誤？"}
    X -- 是 --> Y["validated=false, 回傳 values 與 errors"]
    X -- 否 --> Z["validated=true, 回傳 values 與空 errors"]

    Y --> END["結束"]
    Z --> END
```

---

### 1.4.1 `validateFlatKey` 函式流程圖

<details>

```mermaid
flowchart TD
  A["接收 flatKey 與 ruleStr"] --> B{"parts 是否包含 '*' ？"}
  B -- 是 --> C["計算陣列路徑，取值為陣列"]
  C -- 非陣列 --> D["直接結束此 flatKey（不驗證）"]
  C -- 是陣列 --> E["對每個索引 i 產生新路徑 '...i...'"]
  E --> F["遞迴呼叫 'validateFlatKey'（以展開後的 parts）"]
  F --> G["彙整各索引的錯誤與 values 後返回"]

  B -- 否 --> H["以 'parts.join('.')' 取得 flatKey"]
  H --> I["從輸入資料取值 'val = getValueByPath(data, flatKey)'"]
  I --> J{"val 是否為空？（'undefined'/'null'/''）"}
  J -- 是 --> K["嘗試從規則解析 'default' 並套用"]
  K --> L{"仍為空？"}
  L -- 是 --> M{"是否 'required' 或條件必填？"}
  M -- 是 --> N["記錄錯誤：'xxx is required'（以 'arr[0].x' 風格）"]
  M -- 否 --> O["非必填且為空 → 跳過此欄位驗證"]
  O --> Z["返回（不寫入 values）"]

  J -- 否 --> P{"是否有 'type:*' 規則？"}
  P -- 有 --> Q{"執行型別驗證（typeValidator）"}
  Q -- 失敗 --> R["記錄型別錯誤並返回"]
  Q -- 成功 --> S["執行型別轉型（'TYPE_CAST[typeName]'）"]
  P -- 無 --> T["直接進入規則驗證階段"]

  S --> U["進入規則驗證"]
  T --> U

  U --> V{"逐一套用規則（排除 'type'/'default'）"}
  V --> W{"規則是否標記 'perElement' 且 'val' 為陣列？"}
  W -- 是 --> X["迭代每個元素呼叫規則，錯誤以 'arr[i].key' 形式記錄"]
  W -- 否 --> Y["直接呼叫規則，若有錯誤記錄在對應欄位"]

  X --> AA{"此欄位是否有錯誤？"}
  Y --> AA
  AA -- 有 --> AB["保留錯誤，不寫入 values"]
  AA -- 無 --> AC["寫入轉型後的值至 'values'（'setValueByPath'）"]

  AB --> Z["返回"]
  AC --> Z["返回"]

```

</details>

---
* **ValidatorFac(schema)**

    1. **Schema 檢查**：展平規則並檢查型別/結構衝突。
    2. **執行驗證**：

        * 讀取欄位值 → 套用 `default` 預設值（若未給值）
        * 檢查 `required` 與條件必填規則
        * 型別檢查與轉換（cast）
        * 執行其他規則（內建與自訂）
        * 支援展開 `*` 針對陣列每個元素驗證
    3. **輸出結果**：

       ```js
       {
         validated: true/false,
         values: {...}, // 預設值與型別轉換後
         errors: {...}  // 錯誤訊息
       }
       ```

---

</details>

### 1.5 輔助工具

* **資料存取**

    * `getValueByPath(obj, path)` / `setValueByPath(obj, path, val)`
      支援 dot path 與陣列索引存取。
* **預設值解析**

    * `getDefaultValue(ruleStr)`：從規則字串中解析 `default` 值（支援字串、數字、布林、null、JSON）。
* **型別處理**

    * `checkAndCastType(val, typeNames)`：檢查並轉型成符合型別的值。
* **錯誤紀錄**

    * `setError(errors, flatKey, msg)`：以陣列索引形式（`arr[0].foo`）記錄錯誤訊息。

---

---

## 1.6 Default 行為策略（validate / applyDefault / getDefault）

> 避免混淆：`validate()` 與 `applyDefault()` 的「空值」定義不同。

* **`validate(data)`**

  * 會 **套用 default**（空值判定採用 `isEmpty`：通常 `undefined/null/''` 都算空）
  * 會 **型別轉換** + **規則驗證**
  * 回傳 `{ validated, values, errors }`

* **`applyDefault(data)`**

  * 只做 **default 補值**，**不轉型、不驗證**
  * **只有 `undefined` 視為空值** 才會補 default
    `null`、`''`、`0`、`false`、`NaN` 都視為「已提供」，不覆蓋
  * 支援 `*` 展開：若輸入中某陣列已有元素，會對子欄位補 default

* **`getDefault()`**

  * 產生 **純 default 模板**（不看輸入）
  * 遇到 `*` 只在父層放 `[]`，**不展開元素**

**示例：**

```js
// applyDefault：只有 undefined 會補
applyDefault({ age: undefined, name: '', active: false })
// => { age: '18', name: '', active: false }  // 只補 age
```

---

## 1.7 快取策略（解耦原則）

> **Validator 不負責快取**。若需效能最佳化，請用外部 cacher/LRU 包裝。

* **後端建議**：以 schema 為 key 快取 `ValidatorFac(schema)` 的實例或 `getDefault()` 的結果
* **前端建議**：預設不必快取；表單初始化時呼叫一次 `getDefault()` 即可
* **為何解耦**：單一責任，方便替換實作（LRU、Redis、TTL、熱重載）


## 2. 使用範例

* **驗證結果**
```json
 {
   validated: true/false,
   values: { ... }, // 經過預設值與型別轉換後的資料
   errors: { ... }  // 錯誤訊息，若有錯誤則 validated 為 false
 }
```


### 2.1 基本使用

```js
import { ValidatorFac } from './CompilerAndValidator.js';

const schema = {
  properties: {
    name: { rules: 'type:string|required|minLen:2' },
    age: { rules: 'type:int|minValue:0' }
  }
};

const validator = ValidatorFac(schema);

const result = validator.validate({ name: 'Joe', age: -1 });
console.log(result.errors);
// { age: ['age should be greater than or equal to 0.'] }
```

---

### 2.2 巢狀結構與陣列驗證

```js
const schema = {
  properties: {
    tags: {
      rules: 'type:array|arrUnique',
      items: {
        label: { rules: 'type:string|required' }
      }
    }
  }
};

const validator = ValidatorFac(schema);
const data = { tags: [{ label: 'A' }, { label: '' }] };

console.log(validator.validate(data).errors);
// { 'tags[1].label': ['label is required'] }
```

---

### 2.3 註冊自訂規則

```js
validator.useRule('mustBeFoo')((val) => {
  if (val !== 'foo') return { error: 'must be foo' };
});

const schema2 = {
  properties: {
    code: { rules: 'type:string|mustBeFoo' }
  }
};
```

---


## Validator 對外 API 使用說明

> 本模組僅將 `undefined` 視為空值。  
> `null`、`''`、`0`、`false` 均視為已提供值，不會觸發 default，也不會觸發 `required` 錯誤。

---

### API 一覽
```js
return {
  validate: validate_(flatRules_),
  useType,
  typePlugins,
  useRule,
  rulePlugins,
  getDefault: getDefault_(flatRules_),
  applyDefault: applyDefault_(flatRules_, () => getDefault_(flatRules_)()),
};
````

---

### 1) `validate(data) => { validated, values, errors }`

* 依 schema 驗證資料，補 default（僅 `undefined` 視為空）、型別轉換、套用所有規則。
* 回傳：

  * `validated: boolean`
  * `values: object`（已補 default、已 cast）
  * `errors: Record<string, string[]>`

**範例**

```js
const schema = {
  properties: {
    name: { rules: 'type:string|required|minLen:2|default:"John"' },
    age:  { rules: 'type:int|minValue:0|default:18' }
  }
};
const validator = ValidatorFac(schema);

const { validated, values, errors } = validator.validate({ name: undefined });
```

---

### 2) `getDefault() => object`

* 只根據 schema `default` 產生純預設值模板。
* 不驗證、不轉型、不展開陣列元素。

**範例**

```js
const form = ref(validator.getDefault());
// { name: 'John', age: '18', items: [] }
```

---

### 3) `applyDefault(data) => object`

* 基於 `getDefault()` 結果，只補 `data` 中 `undefined` 欄位的 default。
* 不做型別轉換、不做規則驗證。
* 陣列元素會展開 `*` 子欄位的 default。

**範例**

```js
const prefilled = validator.applyDefault({ name: undefined, items: [{}] });
// { name: 'John', age: '18', items: [{ name: 'N/A' }] }
```

---

### 4) `useType(typeName)(validatorFn)`

* 註冊 / 覆蓋自訂型別驗證器。
* `validatorFn(v) => boolean`

**範例**

```js
v.useType('emailLike')((s) => typeof s === 'string' && s.includes('@'));
```

---

### 5) `useRule(ruleName)(ruleFn)`

* 註冊 / 覆蓋自訂規則插件。
* `ruleFn(value, arg, ctx, pathArr) => { error } | undefined`
* 若規則需檢查陣列每個元素：`ruleFn.perElement = true`

**範例**

```js
function noBadWords(value, arg) {
  const list = JSON.parse(arg.replace(/'/g, '"'));
  return !list.some(w => value.includes(w))
    ? undefined
    : { error: `contains forbidden words ${list.join(',')}` };
}
v.useRule('noBadWords')(noBadWords);
```

---

### 6) `typePlugins` / `rulePlugins`

* 已註冊之型別 / 規則 Map。
* 可用於偵錯或測試檢查註冊狀態。

---

### 差異比較表

| 方法               | 補 default         | 型別轉換 | 規則驗證 | `*` 子欄位補 default | 空值定義           |
| ---------------- | ----------------- | ---- | ---- | ---------------- | -------------- |
| `getDefault()`   | ✅                 | ❌    | ❌    | 父層 `[]`          | N/A            |
| `applyDefault()` | ✅(只補 `undefined`) | ❌    | ❌    | ✅                | 只有 `undefined` |
| `validate()`     | ✅(只補 `undefined`) | ✅    | ✅    | ✅                | 只有 `undefined` |

---

### 測試範例

```js
import assert from 'node:assert';

describe('validator usage', () => {
  it('applyDefault only fills undefined', () => {
    const pre = validator.applyDefault({ name: undefined, items: [{}] });
    assert.deepEqual(pre, { name: 'John', age: '18', items: [{ name: 'N/A' }] });
  });

  it('validate does cast & rules', () => {
    const { validated, values } = validator.validate({ name: 'Ab', age: '20' });
    equal(validated, true);
    assert.strictEqual(values.age, 20);
  });
});
```





## 設計特點

* **結構優先**：Schema 與驗證流程嚴格分離，核心不被特殊案例污染。
* **可擴充性**：支援自訂型別驗證與規則插件。
* **支援條件必填**：可依其他欄位值動態決定必填性。
* **陣列展開驗證**：`*` 路徑自動展開，逐元素驗證。
* **在地化支援**：內建多種台灣格式驗證。

---


# Schema Specification (alias sch.spec)

##  ✅ Minimal Sufficient Schema Example
```js

// ✅ Minimal Complete Schema Example (with byId)
const schema = {
  rules: 'type:object|required',
  description: '根物件，必填',
  properties: {
    id: {
      rules: 'type:int|required|minValue:1',
      description: '固定鍵：整數 ID，必須大於 0'
    },

    name: {
      rules: 'type:string|required|minLen:1',
      description: '固定鍵：名稱，必填非空字串'
    },

    meta: {
      rules: 'type:object',
      description: '固定鍵 + 動態鍵並存',
      properties: {
        version: { rules: 'type:string|required' }
      },
      additionalProperties: {
        rules: 'type:string',
        description: '允許任意鍵，但值必須是字串'
      }
    },

    tags: {
      rules: 'type:array|required|arrMinLen:1|arrUnique',
      description: '陣列，每個元素為唯一字串，至少一個元素',
      items: {
        rules: 'type:string|nonEmptyString'
      }
    },

    records: {
      rules: 'type:array|required',
      description: '巢狀結構：array → object → array → object',
      items: {
        rules: 'type:object|required',
        properties: {
          title: { rules: 'type:string|required' },
          values: {
            rules: 'type:array|required|arrMinLen:1',
            items: {
              rules: 'type:object|required',
              properties: {
                label: { rules: 'type:string|required' },
                value: { rules: 'type:number|required|minValue:0' }
              }
            }
          }
        },
        additionalProperties: false
      }
    },

    byId: {
      rules: 'type:object|required',
      description: '典型字典型結構：動態鍵 → object',
      additionalProperties: {
        rules: 'type:object|required',
        properties: {
          id: { rules: 'type:nonEmptyString|required' },
          meta: {
            rules: 'type:object',
            properties: {
              createdAt: { rules: 'type:positiveNumWithZero|default:0' }
            }
          }
        }
      }
    }
  },
  additionalProperties: false
};

```

## validator example
```js
const someSchema = { ... }; // your schema here

const validator = ValidatorFac(someSchema);

const  data = { ... }; // your data here

const result = validator.validate(data);

// validated = truu
// result = { validated: true, values: {...}, errors:{}}

// validate = false 
// result = { validated: false, values: {...}, errors:{.....}}
````


---

## I. Structural Rules

* **SR-1**
  `type:object` 只能定義 `properties`，不可同時擁有 `items`。

* **SR-2**
  `type:array` 只能定義 `items`，不可同時擁有 `properties`。

* **SR-3**
  不允許 `type:[object,array]` 這種多型定義（不得同時列出多個第一類型別）。

* **SR-6**
  若節點定義了 `items`，不可同時出現 `properties` 或 **物件型** `additionalProperties`。
  （例外：`additionalProperties: true` 可與 `properties` 並存）

* **SR-7**
  `properties` 與 **物件型** `additionalProperties` 可並存，用於「固定鍵 + 萬用鍵」字典結構。

* **SR-8**
  `type:array` 節點不可同時使用 `items` 與 `arrValidValues`。

* **SR-9**
  使用 **Composite Array Types (CAT)**（如 `arrayOfInt`, `arrayOfString`）時，不可再定義 `items`。

* **SR-10**
  使用 `arrUniqueBy:key` 時，必須定義 `items`（且應為物件元素，含該 `key` 欄位）。

---

## II. First-Class Keywords

* **SR-4**
  以下為 **保留字 / First-Class Keywords**，僅能依規範使用，禁止濫用為業務屬性名稱：

  * `type`：定義資料型別
  * `rules`：規則集合（required, minValue …）
  * `properties`：僅用於 `type:object`
  * `items`：僅用於 `type:array`
  * `additionalProperties`：僅用於 `type:object`
  * `description`：文字說明
  * `key`：schema 唯一識別
  * `in`：schema 的資料來源（body, query, params …）

---

### 🔎 Special Note: `additionalProperties`

`additionalProperties` 在 schema 裡是一個容易誤解的欄位，既能放 `false/true`，又能放物件 schema。不同寫法語意差異極大：

#### 1. 值類型

* `false` → 禁止未知鍵
* `true` → 允許任意鍵（不驗證值）
* `{ rules, properties, … }` → 未宣告的鍵按此 schema 驗證

#### 2. 基本範例

```js
// 禁止未知鍵
{
  rules: 'type:object',
  additionalProperties: false,
  properties: { id: { rules:'type:int' } }
}

// 允許未知鍵，但不驗證其值
{
  rules: 'type:object',
  additionalProperties: true
}

// Map 結構：允許未知鍵，且值必須是 object
{
  rules: 'type:object',
  additionalProperties: {
    rules:'type:object',
    properties:{ name:{ rules:'type:string'} }
  }
}
```

#### 3. 與 properties 並存範例

用於「固定鍵 + 動態鍵」的結構：

```js
{
  rules: 'type:object',
  properties: {
    id: { rules: 'type:int|required' },                
    name: { rules: 'type:string|required' }           
  },
  additionalProperties: {
    rules: 'type:string'   // 動態欄位規則
  }
}
```

* 每個物件必須有 `id` 和 `name`
* 可以有其他任意欄位，但值必須是字串
* 常見於 metadata、tag dictionaries

#### 4. 進階範例：多層 Map

```js
{
  rules: 'type:object',
  properties: { id: { rules: 'type:int|required' } },
  additionalProperties: {
    rules: 'type:object|required',
    properties: {
      label: { rules: 'type:string|required' }
    },
    additionalProperties: { rules: 'type:number' }
  }
}
```

* `id` 是固定欄位
* 其他欄位值必須是物件
* 物件內必須有 `label`，但允許更多未知欄位，且值必須是數字

---

### 🔎 Special Note: `items`

`items` 僅用於 **`type:array`** 的 schema，用來定義「陣列元素的結構」。
它和 `additionalProperties` 在精神上相似：一個管「object 的動態鍵」，另一個管「array 的動態元素」。

#### 1. 基本語義

* `items` 下必須是 schema 定義物件
* 所有陣列元素都會依 `items` 的 schema 驗證
* 支援多層巢狀（array of array, array of object）

#### 2. 基本範例

```js
// 陣列元素是 string
{
  rules: 'type:array',
  items: { rules: 'type:string' }
}

// 陣列元素是 object
{
  rules: 'type:array',
  items: {
    rules: 'type:object',
    properties: {
      id: { rules: 'type:int|required' },
      name: { rules: 'type:string|required' }
    }
  }
}
```

#### 3. 巢狀範例

```js
{
  rules: 'type:array',
  items: {
    rules: 'type:array',
    items: {
      rules: 'type:object',
      properties: {
        label: { rules: 'type:string|required' },
        value: { rules: 'type:number|required' }
      }
    }
  }
}
```

語意：

* 最外層是一個陣列
* 每個元素本身又是一個陣列
* 內層陣列的每個元素必須是 object，且擁有 `label` 與 `value`

---

## III. Type Classes

* **SR-5** 型別分層：

  * **FCT — First-Class Types**：`object`, `array`
  * **ST — Scalar Types**：`string`, `number`, `int`, `boolean`, `null`
  * **JT — Opaque JSON Type**：`json`
  * **CAT — Composite Array Types**：`arrayOfString`, `arrayOfInt`, `arrayOfObject`, `arrayOfNull`, …
  * **FR — Format Rules（非 type）**：`isoDatetimeStr`, `nonEmptyString` 等

👉 僅允許 **FCT** 作為結構型根；**ST/JT** 搭配值域規則；**CAT** 是語法糖，實際等價於 `array + 元素型別`；**FR** 屬於規則層。

---

## IV. Validation Behavior Rules

* **SR-11**
  逐元素規則（per-element, 如 `validValues`）在值為陣列時會自動逐一驗證。

* **SR-12**
  預設值套用僅在「空值」時觸發，且依專案語義僅 `undefined` 視為空值。

* **SR-13**
  對含 `*` 的路徑無法推知長度：

  * 父層若為 array → 設 `[]`
  * 父層若為 map（AP）→ 設 `{}`
  * 且不覆蓋已存在的父層值

* **SR-14** Unknown keys 檢查規則：

  * `additionalProperties: false` → 禁止未知鍵
  * 非 strict 且未宣告 AP → 允許
  * strict 且未宣告 AP → 視為禁止
  * 錯誤掛點：

    * 根層未知鍵 → 該鍵
    * 內層且 AP=false → 父容器

* **SR-15**
  `errors` 為「路徑 → 訊息陣列」，路徑格式允許 `arr[2].name`，同一路徑可累積多筆訊息。

* **SR-16**
  萬用星號 `*` 在驗證時展開 array 索引或 map 的每個鍵，對每一實例路徑各自驗證。

* **SR-17**
  型別檢查通過後做轉型，例如 `number/int/bool/json`；未知 type caster 採 identity 並警告。

* **SR-18**
  `validValues:[...]` 以 **字串化** 值比對，`null` 必須以字串 `'null'` 才能匹配。

* **SR-19**
  `requiredIfValueIs` 依 peer 欄位的 `type` 嘗試轉型後比對（支援 `boolean/number/null/string`），比單純字串比對更嚴謹。

* **SR-20**
  `schemaToFlatRules` 要求每個節點（含 `items`、AP 下的萬用鍵）都必須提供 `rules`，缺失會報錯。

---





---

### `type:array` vs `type:object` 可用關鍵字對照

| Type 定義       | 可用 Keywords                                | 語意說明                                                            |
| ------------- | ------------------------------------------ | --------------------------------------------------------------- |
| `type:array`  | `items`                                    | 陣列元素的結構定義，每個元素依此驗證                                              |
|               | `rules`                                    | 基本規則（如 `arrMinLen`, `arrMaxLen`, `arrUnique`, `arrValidValues`） |
|               | `description`                              | 說明文字                                                            |
|               | `default`                                  | 預設值（通常是 `[]` 或指定陣列內容）                                           |
|               | `key` / `in`                               | schema 管理用 metadata                                             |
| *(禁止)*        | `properties`                               | 不可與 array 並用                                                    |
| *(禁止)*        | `additionalProperties`                     | 不可與 array 並用                                                    |
|               |                                            |                                                                 |
| `type:object` | `properties`                               | 固定鍵定義，每個屬性 schema 必須明確列出                                        |
|               | `additionalProperties` (true/false/schema) | 控制未知鍵，或定義未知鍵的驗證規則                                               |
|               | `rules`                                    | 基本規則（如 required）                                                |
|               | `description`                              | 說明文字                                                            |
|               | `default`                                  | 預設值（通常是 `{}` 或含指定欄位）                                            |
|               | `key` / `in`                               | schema 管理用 metadata                                             |
| *(禁止)*        | `items`                                    | 不可與 object 並用                                                   |

---


### 表：允許的正確組合 vs 常見錯誤組合

| 類別       | 組合示例                                                                | 狀態      | 說明                                           |
| -------- | ------------------------------------------------------------------- | ------- | -------------------------------------------- |
| **正確組合** | `type:object` + `properties`                                        | ✅ OK    | 最標準的物件 schema，僅允許定義過的固定鍵                     |
|          | `type:object` + `properties` + `additionalProperties:false`         | ✅ OK    | 嚴格物件，禁止任何未知鍵                                 |
|          | `type:object` + `properties` + `additionalProperties:true`          | ✅ OK    | 固定鍵 + 任意其他鍵（不驗證值）                            |
|          | `type:object` + `properties` + `additionalProperties:{...}`         | ✅ OK    | 固定鍵 + 動態鍵必須符合指定 schema，常見於 dictionary/map 結構 |
|          | `type:object` + `additionalProperties:{ rules:'type:string' }`      | ✅ OK    | 純 Map 結構，所有鍵都必須是字串值                          |
|          | `type:array` + `items:{ rules:'type:string' }`                      | ✅ OK    | 陣列元素是字串                                      |
|          | `type:array` + `items:{ rules:'type:object', properties:{...} }`    | ✅ OK    | 陣列元素是物件，每個元素依 properties 驗證                  |
|          | 巢狀 `type:array` + `items:{ type:array, items:{ type:object ... } }` | ✅ OK    | 多維陣列，結構正確                                    |
| **錯誤組合** | `type:object` + `items:{...}`                                       | ❌ Error | object 不可搭配 items                            |
|          | `type:array` + `properties:{...}`                                   | ❌ Error | array 不可搭配 properties                        |
|          | `type:[object,array]`                                               | ❌ Error | 禁止 union of FCT（第一類型別）                       |
|          | `type:array` + `items` + `arrValidValues:[...]`                     | ❌ Error | 不可同時定義元素 schema 與 arrValidValues             |
|          | `type:arrayOfInt` + `items:{...}`                                   | ❌ Error | Composite Array Type 不可再自帶 items             |
|          | `type:array` + `arrUniqueBy:id` （缺少 items object 定義）                | ❌ Error | arrUniqueBy 必須與 items:{...} 同時存在             |

---
### 表：允許的正確組合 vs 結構黑名單（錯誤組合）

| 類別                               | 組合示例                                                                                        | 狀態      | 說明                                                                                |
| -------------------------------- | ------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------- |
| **正確組合：Object**                  | `type:object` + `properties`                                                                | ✅ OK    | 最標準的物件 schema，僅允許定義過的固定鍵。                                                         |
|                                  | `type:object` + `properties` + `additionalProperties:false`                                 | ✅ OK    | 嚴格物件，**完全禁止未知鍵**。                                                                 |
|                                  | `type:object` + `properties` + `additionalProperties:true`                                  | ✅ OK    | 固定鍵 + 任意其他鍵，未知鍵存在但「不驗證值」。                                                         |
|                                  | `type:object` + `properties` + `additionalProperties:{ properties:{...} }`                  | ✅ OK    | 固定鍵 + 動態鍵皆需符合指定 value schema，對應 dictionary/map 結構，例如：`koo.*.title`、`koo.*.label`。 |
|                                  | `type:object` + `additionalProperties:{ rules:'type:string' }`                              | ✅ OK    | 純 Map 結構，所有 key 的 value 都是字串（不再定義固定鍵）。                                            |
| **正確組合：Array**                   | `type:array` + `items:{ rules:'type:string' }`                                              | ✅ OK    | 陣列元素是字串。                                                                          |
|                                  | `type:array` + `items:{ rules:'type:object', properties:{...} }`                            | ✅ OK    | 陣列元素是物件，依 properties 驗證。                                                          |
|                                  | 巢狀 `type:array` + `items:{ rules:'type:array', items:{ rules:'type:object', ... } }`        | ✅ OK    | 多維陣列結構正確：array of array of object。                                                |
|                                  | `type:array` + `arrMinLen` / `arrMaxLen` / `arrUnique`                                      | ✅ OK    | 對整個陣列長度或唯一性做檢查，可與 `items` 併用。                                                     |
| **正確組合：Composite Array Family**  | `type:arrayOfString` / `type:arrayOfInt` / `type:arrayOfNull` / `type:emptyArray`           | ✅ OK    | Composite Array Type（已內建元素型別），適用於簡單 primitive 陣列。                                 |
| **錯誤組合：Object vs Array 結構**      | `type:object` + `items:{...}`                                                               | ❌ Error | object 不可搭配 `items`，`items` 只屬於 array。                                            |
|                                  | `type:array` + `properties:{...}`                                                           | ❌ Error | array 不可搭配 `properties`，`properties` 只屬於 object。                                  |
| **錯誤組合：Union 黑名單**               | `type:[object,array]`                                                                       | ❌ Error | 禁止 first-class types（object/array）互相 union，語意不清，default/typeCast 難以正確處理。          |
|                                  | `type:[object,arrayOfObject]` / `type:[object,emptyArray]`                                  | ❌ Error | 禁止 `object` 與 Composite Array Family 形成 union，同樣屬於語意不明的混合容器型別。                    |
| **錯誤組合：Array + items/規則 衝突**     | `type:array` + `items:{...}` + `arrValidValues:[...]`                                       | ❌ Error | `items` 用來描述結構化元素（object/nested），`arrValidValues` 用於 primitive set，兩者語意不同不可同時存在。  |
|                                  | `type:arrayOfInt` / `type:arrayOfString` + `items:{...}`                                    | ❌ Error | Composite Array Type 已內建元素型別，再加 `items` 會造成雙重定義。                                  |
| **錯誤組合：arrUniqueBy 缺少元素 schema** | `type:array` + `arrUniqueBy:id`（但未定義 `items:{ rules:'type:object', properties:{ id... } }`） | ❌ Error | `arrUniqueBy` 必須搭配 `items`，且元素為 object、內含指定 key，否則無法正確判斷唯一性。                      |



