# 機能設計書 92-メモリストレージ

## 概要

本ドキュメントは、Giselleプラットフォームにおけるインメモリデータストレージ機能について記述する。

### 本機能の処理概要

**業務上の目的・背景**：テスト環境やモック環境において、実際のファイルシステムやクラウドストレージを使用せずにストレージ操作をシミュレートするため、メモリ上にデータを保持するストレージドライバを提供する。これにより、ユニットテストの高速実行や、外部依存のない環境構築が可能となる。

**機能の利用シーン**：
- ユニットテスト実行時のストレージモック
- 開発時の一時的なデータ保持
- CI/CD環境でのテスト実行
- ストレージ操作のプロトタイピング

**主要な処理内容**：
1. JSONデータのメモリ内保存・取得（Zodスキーマバリデーション付き）
2. バイナリデータ（Blob）のメモリ内保存・取得
3. データのコピー・削除・存在確認
4. データサイズの計算
5. 保存データの一覧取得（ページネーション対応）
6. 初期データの注入機能

**関連システム・外部連携**：外部システムへの依存なし（完全にインメモリで動作）

**権限による制御**：特になし（インメモリ操作のため）

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 本機能は画面を持たないテスト用ストレージ機能 |

## 機能種別

データ永続化（インメモリ） / テストユーティリティ

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| config.initialJson | Record<string, unknown> | No | 初期JSONデータ | - |
| config.initialBlob | Record<string, Uint8Array> | No | 初期Blobデータ | - |
| path | string | Yes | データの識別キー | - |
| schema | z.ZodType | No | JSONデータのバリデーションスキーマ | 有効なZodスキーマ |
| data | unknown | Yes (setJson) | 保存するデータ | schemaに準拠 |
| prefix | string | No | 一覧取得時のキープレフィックス | - |
| limit | number | No | 一覧取得時の最大件数 | 正の整数 |
| cursor | string | No | ページネーションカーソル | 数値文字列 |

### 入力データソース

- テストコードからのAPI呼び出し
- 初期化時のconfigオブジェクト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| JSON data | z.infer<T> | Zodスキーマでパースされたデータ |
| Blob data | Uint8Array | バイナリデータ |
| exists | boolean | データの存在有無 |
| contentLength | number | データサイズ（バイト） |
| blobs | ListedBlob[] | データ一覧 |
| hasMore | boolean | 追加データの有無 |
| cursor | string | 次ページのカーソル |

### 出力先

メモリ（Map<string, unknown>、Map<string, Uint8Array>）

## 処理フロー

### 処理シーケンス

```
1. 初期化処理
   └─ initialJsonからjsonStoreを初期化 → initialBlobからblobStoreを初期化 → uploadedAtStoreを初期化

2. getJson処理
   └─ jsonStoreからデータ取得 → 存在確認 → Zodスキーマバリデーション → データ返却

3. setJson処理
   └─ スキーマバリデーション → jsonStoreに保存 → uploadedAtを記録

4. getBlob処理
   └─ blobStoreからデータ取得 → 存在確認 → Uint8Arrayコピーを返却

5. setBlob処理
   └─ Uint8Array変換 → blobStoreに保存 → uploadedAtを記録

6. listBlobs処理
   └─ jsonStoreとblobStoreのキーをマージ → プレフィックスフィルタリング → ソート → ページネーション適用
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{操作種別}
    B -->|getJson| C[jsonStoreから取得]
    C --> D{データ存在?}
    D -->|No| E[エラー返却]
    D -->|Yes| F[Zodバリデーション]
    F --> G[データ返却]

    B -->|setJson| H{スキーマあり?}
    H -->|Yes| I[スキーマバリデーション]
    H -->|No| J[そのまま保存]
    I --> J
    J --> K[jsonStoreに保存]
    K --> L[uploadedAt記録]
    L --> M[完了]

    B -->|getBlob| N[blobStoreから取得]
    N --> O{データ存在?}
    O -->|No| P[エラー返却]
    O -->|Yes| Q[Uint8Arrayコピー]
    Q --> R[データ返却]

    B -->|copy| S{ソースの種別}
    S -->|JSON| T[jsonStoreからコピー]
    S -->|Blob| U[blobStoreからコピー]
    S -->|なし| V[エラー返却]
    T --> W[uploadedAt記録]
    U --> W
    W --> X[完了]

    B -->|listBlobs| Y[全キー収集]
    Y --> Z[プレフィックスフィルタ]
    Z --> AA[ソート]
    AA --> AB[ページネーション]
    AB --> AC[結果返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-001 | データ分離 | JSONデータとBlobデータは別々のMapで管理 | 常時 |
| BR-002 | アップロード日時追跡 | 各データの保存日時をuploadedAtStoreで管理 | setJson/setBlob/copy時 |
| BR-003 | コピー先の種別継承 | copyはソースの種別（JSON/Blob）を維持してコピー | copy時 |
| BR-004 | 完全削除 | removeはjsonStore、blobStore、uploadedAtStoreすべてから削除 | remove時 |
| BR-005 | contentTypeの付与 | listBlobsでJSONデータには"application/json"を付与 | listBlobs時 |

### 計算ロジック

- JSONのcontentLength: `new TextEncoder().encode(JSON.stringify(data)).length`
- BlobのcontentLength: `uint8Array.length`

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

該当なし（インメモリ操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| NO_JSON_STORED | データ不在 | 指定パスにJSONデータが存在しない | 存在確認後にアクセス |
| NO_BLOB_STORED | データ不在 | 指定パスにBlobデータが存在しない | 存在確認後にアクセス |
| NO_DATA_STORED | データ不在 | copy時にソースデータが存在しない | 存在確認後にコピー |
| INVALID_CURSOR | カーソル不正 | 無効なカーソル値 | 正しいカーソル値を使用 |

### リトライ仕様

リトライ不要（同期的なインメモリ操作）

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

インメモリ操作のため、トランザクション保証なし。各操作は独立して実行される。

## パフォーマンス要件

- O(1)のデータアクセス（Map使用）
- listBlobsはO(n log n)（ソートのため）

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

- メモリ上のデータはプロセス終了で消失
- テスト用途のため、本番環境での使用は想定しない

## 備考

本機能はGiselleStorage抽象インターフェースの具体実装であり、ファイルシステムストレージ等と差し替え可能。主にテスト目的で使用される。

---

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

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

### 推奨読解順序

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/storage/src/types.ts` | GiselleStorageインターフェース、各メソッドの型定義を理解 |
| 1-2 | memory-storage-driver.ts | `packages/storage/src/memory-storage-driver.ts` | MemoryStorageDriverConfig型（9-12行目）を確認 |

**読解のコツ**: 3つのMapストア（jsonStore, blobStore, uploadedAtStore）の役割を理解する。

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

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/storage/src/index.ts` | パッケージのエクスポート構造 |
| 2-2 | memory-storage-driver.ts | `packages/storage/src/memory-storage-driver.ts` | memoryStorageDriver関数がストレージインスタンスを生成 |

**主要処理フロー**:
1. **14-16行目**: memoryStorageDriver関数で初期化
2. **17-25行目**: 3つのMapストアの初期化
3. **28-34行目**: initializeUploadedAt関数で初期データのアップロード日時設定
4. **59-68行目**: getJson実装
5. **70-77行目**: setJson実装
6. **79-85行目**: getBlob実装
7. **87-92行目**: setBlob実装
8. **94-110行目**: copy実装（JSON/Blobの種別判定）
9. **138-196行目**: listBlobs実装

#### Step 3: ヘルパー関数を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | memory-storage-driver.ts | `packages/storage/src/memory-storage-driver.ts` | recordUpload関数（39-41行目）でアップロード日時記録 |
| 3-2 | memory-storage-driver.ts | `packages/storage/src/memory-storage-driver.ts` | contentLengthForKey関数（43-56行目）でサイズ計算 |

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

```
memoryStorageDriver(config)
    │
    ├─ [初期化]
    │      ├─ new Map(Object.entries(config.initialJson))
    │      ├─ new Map(Object.entries(config.initialBlob))
    │      └─ initializeUploadedAt(keys)
    │
    ├─ getJson(params)
    │      ├─ jsonStore.get(params.path)
    │      └─ schema.parse(data)
    │
    ├─ setJson(params)
    │      ├─ schema.parse(data) [if schema]
    │      ├─ jsonStore.set(path, parsed)
    │      └─ recordUpload(path)
    │
    ├─ getBlob(path)
    │      ├─ blobStore.get(path)
    │      └─ new Uint8Array(data) [コピー]
    │
    ├─ setBlob(path, data)
    │      ├─ new Uint8Array(data)
    │      ├─ blobStore.set(path, uint8Array)
    │      └─ recordUpload(path)
    │
    ├─ copy(source, destination)
    │      ├─ jsonStore.get(source) → jsonStore.set(destination)
    │      └─ blobStore.get(source) → blobStore.set(destination)
    │
    ├─ remove(path)
    │      ├─ jsonStore.delete(path)
    │      ├─ blobStore.delete(path)
    │      └─ uploadedAtStore.delete(path)
    │
    ├─ exists(path)
    │      └─ jsonStore.has(path) || blobStore.has(path)
    │
    ├─ contentLength(path)
    │      └─ contentLengthForKey(path)
    │
    └─ listBlobs(params)
           ├─ Set.add(jsonStore.keys())
           ├─ Set.add(blobStore.keys())
           ├─ filter(startsWith(prefix))
           ├─ sort(localeCompare)
           └─ slice(startIndex, endIndex)
```

### データフロー図

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

config.initialJson ──────┐
                         │
config.initialBlob ──────┼──▶ memoryStorageDriver ──▶ メモリ上のMap
                         │         │
path ────────────────────┤         │
                         │         ├──▶ jsonStore (Map<string, unknown>)
schema ──────────────────┤         │
                         │         ├──▶ blobStore (Map<string, Uint8Array>)
data ────────────────────┤         │
                         │         └──▶ uploadedAtStore (Map<string, Date>)
prefix/limit/cursor ─────┘

```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| memory-storage-driver.ts | `packages/storage/src/memory-storage-driver.ts` | ソース | メモリストレージドライバ実装 |
| types.ts | `packages/storage/src/types.ts` | ソース | GiselleStorageインターフェース定義 |
| index.ts | `packages/storage/src/index.ts` | ソース | パッケージエクスポート |
| fs-storage-driver.ts | `packages/storage/src/fs-storage-driver.ts` | ソース | 比較参照用：ファイルシステム実装 |
