# ITmpRuntimeData 技術文件
# ITmpRuntimeData Technical Documentation

## 概述

`ITmpRuntimeData` 是 deepmerge 庫中的一個內部執行時資料介面，用於在遞迴合併過程中追蹤上下文資訊。它記錄了當前合併操作的位置、路徑和相關物件引用。

`ITmpRuntimeData` is an internal runtime data interface in the deepmerge library, used to track context information during recursive merge operations. It records the current merge operation's position, path, and related object references.

---

## 類型定義

### 核心介面

```typescript
export interface ITmpRuntimeData<
  T extends IAnyRecord = IAnyRecord,
  R extends IAnyRecord = IAnyRecord
>
{
  /** 遞迴深度計數（根為 0）/ Recursion depth count (root is 0) */
  readonly level: number;

  /** 從根到目前位置的路徑陣列 / Path array from root to current position */
  readonly paths: (keyof R | keyof T | ITSPropertyKey | number)[];

  /** 最頂層的 destination 物件（永遠不變）/ Top-level destination object (always unchanged) */
  readonly root: R;

  /** 目前層級的父物件 / Parent object at current level */
  readonly parent: T;

  /** 目前正在處理的鍵名 / Key currently being processed */
  readonly key: keyof T;
}
```

### 相關介面：ICache

```typescript
export interface ICache
{
  /** 鍵名 / Key name */
  key?
  /** 來源物件 / Source object */
  source?
  /** 目標物件 / Target object */
  target?
  /** 目的物件 / Destination object */
  destination?
}
```

---

## 核心概念

### 1. 遞迴深度追蹤 (Level Tracking)

每次呼叫 `deepmerge` 遞迴時，`level` 會遞增：

```
根層級: level = 0
第一層:  level = 1
第二層:  level = 2
...以此類推
```

### 2. 路徑陣列 (Paths Array)

`paths` 陣列記錄了從根物件到目前位置的完整路徑：

```
合併 { a: { b: { c: 1 } } } 時:
- 根層級: paths = []
- a:       paths = ['a']
- b:       paths = ['a', 'b']
- c:       paths = ['a', 'b', 'c']
```

### 3. Root 永遠不變

`root` 指向最開始建立的 destination 物件，在整個遞迴過程中保持不變。這使得我們可以：

- 使用 `_.get(root, paths)` 取得目前位置的物件
- 在任何層級都能取得最頂層的資料

### 4. Key/Parent 關係

- `parent`: 目前層級的父物件
- `key`: 目前正在處理的鍵名
- 可透過 `parent[key]` 取得目前的值

---

## 遞迴流程範例

### 範例：合併巢狀物件

```typescript
const target = {
  level1: {
    level2: {
      value: 'original'
    }
  }
};

const source = {
  level1: {
    level2: {
      value: 'updated',
      extra: 'data'
    }
  }
};

merge(target, source);
```

### 執行時序

| 階段 | level | paths | root | parent | key |
|------|-------|-------|------|--------|-----|
| 初始 | 0 | [] | {} | {} | undefined |
| level1 | 1 | ['level1'] | {} | {} | 'level1' |
| level2 | 2 | ['level1', 'level2'] | {} | {level1:{}} | 'level2' |
| value | 3 | ['level1', 'level2', 'value'] | {} | {level1:{level2:{}}} | 'value' |

---

## ITmpRuntimeData 與 ICache 的差異

| 特性 | ITmpRuntimeData | ICache |
|------|-----------------|--------|
| 主要用途 | 追蹤遞迴路徑和深度 | 維護物件引用關係 |
| 關注點 | **在哪裡** (位置) | **是什麼** (物件) |
| 關鍵屬性 | level, paths, root | source, target, destination |
| 變化頻率 | 每次遞迴都會變化 | 每次處理鍵值時會變化 |

---

## 使用場景

### 1. 自訂 isMergeableObject

在自訂合併邏輯中，可以利用 `tmpRuntimeData` 取得目前位置資訊：

```typescript
const options = {
  isMergeableObject: (value, defaultCheck, options, tmpRuntimeTarget, tmpRuntimeData) => {
    // 根據目前路徑決定是否可合併
    if (tmpRuntimeData?.paths.includes('protected')) {
      return false; // protected 欄位不可合併
    }
    return defaultCheck(value);
  }
};
```

### 2. 自訂 arrayMerge

在自訂陣列合併策略中，可以使用 `tmpRuntimeData` 取得上下文：

```typescript
const options = {
  arrayMerge: (target, source, options, tmpRuntimeData) => {
    console.log(`目前 level: ${tmpRuntimeData.level}`);
    console.log(`目前路徑: ${tmpRuntimeData.paths.join('.')}`);
    // 自訂合併邏輯...
    return [...target, ...source];
  }
};
```

---

## 測試驗證要點

根據 `test\tmp-runtime-data.test.ts`，以下 invariant 必須維持：

### 1. level 等於 paths.length

```typescript
expect(tmpRuntimeData.level).toBe(tmpRuntimeData.paths.length);
```

### 2. root 是最頂層物件

```typescript
// root 在所有巢狀層級中保持不變
expect(data.root).toBe(rootObject);
```

### 3. 可使用 _.get(root, paths) 取得值

```typescript
const value = _.get(data.root, data.paths);
expect(value).toBeDefined();
```

### 4. parent[key] 關係正確

```typescript
if (data.key !== undefined) {
  expect(data.parent[data.key]).toBeDefined();
}
```

---

## 工具函數參考

### test/lib/tmp-runtime-data.ts

| 函數 | 用途 |
|------|------|
| `_createTmpRuntimeDataCapturer` | 建立自訂 isMergeableObject 來捕捉 tmpRuntimeData |
| `_getValidTmpRuntimeData` | 過濾掉 undefined 值 |
| `_validateTmpRuntimeData` | 驗證單一 tmpRuntimeData 的正確性 |
| `_validateAllTmpRuntimeData` | 批次驗證多個 tmpRuntimeData |
| `_findByPath` | 按路徑條件查找 tmpRuntimeData |
| `_filterByPathLength` | 按路徑長度篩選 tmpRuntimeData |

---

## 注意事項

1. **最深層可能為 undefined**：在最深層的 deepmerge 內，tmpRuntimeData 有可能是 undefined，這是正常行為。

2. **Root 可能是新物件**：deepmerge 內部會建立新的 destination 物件作為 root，這個物件會包含合併後的結果。

3. **僅供內部使用**：ITmpRuntimeData 是內部資料結構，主要用於讓自訂合併函式取得上下文資訊。

---

## 相關檔案

- `src/index.ts` - 類型定義與主要邏輯
- `test/tmp-runtime-data.test.ts` - 測試檔案
- `test/lib/tmp-runtime-data.ts` - 測試工具函數