---
title: "基本概念"
description: "UniKVS のトランスフォーマーとストレージの組み合わせ、値の型、変数について説明します。"
---

## アーキテクチャー [#architecture]

UniKVS はビルダーパターンで構成します。入力はトランスフォーマーの列を通り、ストレージに届きます。

```mermaid
flowchart LR
  Input --> Transformer1
  Transformer1 --> Transformer2
  Transformer2 --> Storage1
  Transformer2 --> Storage2
```

役割は次の 2 つです。

- トランスフォーマーは、データのエンコード・デコードを透過的に行います。
- ストレージは永続化先です。複数指定すると、すべてに並列で書き込みます。

各ストレージは、登録時点までのトランスフォーマーを前段として使います。読み取り時は、見つかったストレージの前段を逆順に適用してデコードします。

:::note
書き込みはすべてに並列で行い、読み取りは登録順に探して最初に見つけたキーで返します。よく読む保存先を先に登録すると読み取りが速くなります。
:::

## 設定ビルダー [#config-builder]

`UniKvs.config()` でビルダーを作り、次の順序で設定します。`schema` を渡すと Valibot スキーマによる入出力検証が有効になり、キーと値のマッピング型も自動推論します。

```ts
import { PlainValue, StreamValue, UniKvs } from "unikvs";
import * as v from "valibot";

const kvs = UniKvs.config({
  schema: {
    message: PlainValue(v.string()),
    logs: StreamValue(v.instance(Uint8Array)),
  },
})
  .appendStorage(storage)
  .create();
```

Valibot スキーマを使う場合は `valibot` を別途インストールします。`schema` を省略した場合は、型パラメーターでマッピングを指定します。この場合は型チェックのみで、実行時の検証は行いません。

1. **setVariables(vars)**

    実行時変数を設定します (任意)。

2. **appendTransformer(transformer)**

    トランスフォーマーを追加します (任意)。

3. **appendStorage(storage)**

    ストレージを追加します (必須、複数可)。

4. **create()**

    KVS クライアントを作ります。

## クライアント操作 [#client-operations]

| メソッド | 説明 |
| --- | --- |
| `open()` | ストレージとトランスフォーマーを初期化します。 |
| `close()` | すべてのストレージとトランスフォーマーをクローズします。 |
| `set(key, value)` | キーに値を保存します。 |
| `get(key)` | キーから値を取得します。 |
| `stream(key)` | キーからストリームを取得します。 |
| `has(key)` | キーが存在するかを確認します。 |
| `delete(key)` | キーを削除します。 |
| `clear()` | すべてのデータを削除します。 |

すべての操作は `AbortSignal` によるキャンセルと、実行時変数の受け渡しに対応します。

## 値の型 [#value-types]

`Value`・`PlainValue`・`StreamValue` は型と関数の両方です。型として使うとキーごとに使えるメソッドが型レベルで決まり、関数として使うと Valibot スキーマから型を推論しつつ実行時の検証も行います。

| 型 | 書き込み | 読み取り | ストリーム読み取り |
| --- | --- | --- | --- |
| `PlainValue<T>` | `set(key, T)` | `get(key): T` | 不可。 |
| `StreamValue<T>` | `set(key, T \| ReadableStream<T>)` | 不可。 | `stream(key): ValueStream<T>` |
| `Value<T>` | `set(key, T \| ReadableStream<T>)` | `get(key): T` | `stream(key): ValueStream<T>` |

`Value<T>` は両方に対応する糖衣構文で、`PlainValue<T> | StreamValue<T>` と等価です。

**PlainValue**

単一値の保存と取得に使います。`stream()` は型エラーです。

```ts
import { UniKvs, type PlainValue } from "unikvs";

const kvs = UniKvs.config<{
  message: PlainValue<string>;
}>()
  .appendStorage(storage)
  .create();

await kvs.set("message", "hello");
const msg = await kvs.get("message");
```

**StreamValue**

大規模データの逐次処理に使います。`get()` は型エラーです。

```ts
import { UniKvs, type StreamValue } from "unikvs";

const kvs = UniKvs.config<{
  logs: StreamValue<Uint8Array>;
}>()
  .appendStorage(storage)
  .create();

await kvs.set("logs", new Uint8Array([0x01]));
const valueStream = await kvs.stream("logs");
```

**Value**

両方に対応する糖衣構文で、`PlainValue<T> | StreamValue<T>` と等価です。

```ts
import { UniKvs, type Value } from "unikvs";

const kvs = UniKvs.config<{
  blob: Value<Uint8Array>;
}>()
  .appendStorage(storage)
  .create();

await kvs.set("blob", new Uint8Array([1, 2, 3]));
const all = await kvs.get("blob");
const valueStream = await kvs.stream("blob");
```

### スキーマ定義 [#schema-definition]

`UniKvs.config({ schema })` に渡すと、Valibot スキーマからマッピング型を推論し、入出力値を実行時に検証します。

**オブジェクト形式**

キーをそのままキーとして扱います。固定キーに使います。

```ts
import { PlainValue, StreamValue, UniKvs } from "unikvs";
import * as v from "valibot";

const kvs = UniKvs.config({
  schema: {
    message: PlainValue(v.pipe(v.string(), v.minLength(1))),
    logs: StreamValue(v.instance(Uint8Array)),
  },
})
  .appendStorage(storage)
  .create();

await kvs.set("message", "hello");
const msg = await kvs.get("message");
// msg は string 型になります
```

**配列形式**

キースキーマと値スキーマの組を並べます。動的キーに使います。最初に一致した定義を採用します。

```ts
import { PlainValue, StreamValue, UniKvs } from "unikvs";
import * as v from "valibot";

const kvs = UniKvs.config({
  schema: [
    [v.pipe(v.string(), v.regex(/^msg-.+/)), PlainValue(v.string())],
    [v.pipe(v.string(), v.regex(/^img-.+/)), StreamValue(v.instance(Uint8Array))],
  ],
})
  .appendStorage(storage)
  .create();

await kvs.set("msg-1", "hello");
```

:::warning[スキーマ定義が不正な場合]
`PlainValue()`・`StreamValue()`・`Value()` 以外を値に指定するなど、`schema` の形式が不正な場合は `config()` の時点で `InvalidInputError` を投げます。
:::

### 検証のタイミング [#schema-validation]

| 操作 | 検証内容 | 失敗時のエラー |
| --- | --- | --- |
| `set` | 入力値を検証します。 | `InvalidInputError` |
| `get` | 出力値を検証します。 | `InvalidOutputError` |
| `stream` | チャンクを 1 つずつ検証します。 | `InvalidOutputError` |
| `has`・`delete` | 配列形式の場合のみ、キーを検証します。 | `InvalidInputError` |

## 実行時変数 [#variables]

変数は操作の挙動を切り替える実行時コンテキストです。ビルダーの `setVariables()` で初期値を設定し、操作ごとの `vars` で上書きできます。

```ts
const kvs = UniKvs.config<{ foo: Value<Uint8Array> }>()
  .setVariables({ region: "ap-northeast-1" })
  .appendStorage(storage)
  .create();

await kvs.set("foo", new Uint8Array([1]), {
  vars: { region: "us-east-1" },
});
```

## プラグインの種類 [#plugin-kinds]

| 種別 | パッケージ | 説明 |
| --- | --- | --- |
| トランスフォーマー | `@unikvs/compression` | gzip・deflate・deflate-raw による圧縮と展開を行います。 |
| トランスフォーマー | `@unikvs/checksum` | MD5・SHA-1・SHA-224・SHA-256・SHA-384・SHA-512 の検証を行います。 |
| トランスフォーマー | `@unikvs/debug` | 読み書きされる操作とキーとデータのデバッグ情報をログに記録します。 |
| トランスフォーマー | `@unikvs/passthrough` | データを何も変換せずそのまま透過させます。 |
| トランスフォーマー | `@unikvs/json` | 値を JSON に、ストリームでは JSON Lines に変換します。 |
| トランスフォーマー | `@unikvs/superjson` | Date・Map・Set・BigInt・undefined を保持したまま SuperJSON で変換します。 |
| トランスフォーマー | `@unikvs/cbor` | 値を CBOR に、ストリームでは CBOR シーケンスに変換します。 |
| ストレージ | `@unikvs/memory` | メモリー上に保存します。すべての環境に対応します。 |
| ストレージ | `@unikvs/fs.node` | ローカルファイルシステムに保存します。Node.js 専用です。 |
| ストレージ | `@unikvs/fs.bun` | ローカルファイルシステムに保存します。Bun 専用です。 |
| ストレージ | `@unikvs/redis.bun` | Redis に保存します。Bun 専用です。 |
| ストレージ | `@unikvs/s3.node` | S3 互換のオブジェクトストレージに保存します。Node.js 専用です。 |
| ストレージ | `@unikvs/s3.bun` | S3 互換のオブジェクトストレージに保存します。Bun 専用です。 |
| ストレージ | `@unikvs/indexeddb` | ブラウザーの IndexedDB に保存します。 |
| ストレージ | `@unikvs/opfs` | ブラウザーの OPFS に保存します。 |
| ストレージ | `@unikvs/writeonly` | 既存のストレージを書き込み専用にします。 |

:::tip
自作方法は自作プラグインのガイドを参照してください。ストレージは `IStorage`、トランスフォーマーは `ITransformer` から始めます。
:::
