@frostpillar/frostpillar-storage-engine 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/LICENSE +21 -0
  2. package/README-JA.md +1205 -0
  3. package/README.md +1204 -0
  4. package/dist/drivers/file.cjs +960 -0
  5. package/dist/drivers/file.d.ts +3 -0
  6. package/dist/drivers/file.js +18 -0
  7. package/dist/drivers/indexedDB.cjs +570 -0
  8. package/dist/drivers/indexedDB.d.ts +3 -0
  9. package/dist/drivers/indexedDB.js +18 -0
  10. package/dist/drivers/localStorage.cjs +668 -0
  11. package/dist/drivers/localStorage.d.ts +3 -0
  12. package/dist/drivers/localStorage.js +23 -0
  13. package/dist/drivers/opfs.cjs +550 -0
  14. package/dist/drivers/opfs.d.ts +3 -0
  15. package/dist/drivers/opfs.js +18 -0
  16. package/dist/drivers/syncStorage.cjs +898 -0
  17. package/dist/drivers/syncStorage.d.ts +3 -0
  18. package/dist/drivers/syncStorage.js +22 -0
  19. package/dist/drivers/validation.d.ts +1 -0
  20. package/dist/drivers/validation.js +8 -0
  21. package/dist/errors/index.d.ts +32 -0
  22. package/dist/errors/index.js +48 -0
  23. package/dist/frostpillar-storage-engine.min.js +1 -0
  24. package/dist/index.cjs +2957 -0
  25. package/dist/index.d.ts +7 -0
  26. package/dist/index.js +6 -0
  27. package/dist/storage/backend/asyncDurableAutoCommitController.d.ts +26 -0
  28. package/dist/storage/backend/asyncDurableAutoCommitController.js +188 -0
  29. package/dist/storage/backend/asyncMutex.d.ts +7 -0
  30. package/dist/storage/backend/asyncMutex.js +38 -0
  31. package/dist/storage/backend/autoCommit.d.ts +2 -0
  32. package/dist/storage/backend/autoCommit.js +22 -0
  33. package/dist/storage/backend/capacity.d.ts +2 -0
  34. package/dist/storage/backend/capacity.js +27 -0
  35. package/dist/storage/backend/capacityResolver.d.ts +3 -0
  36. package/dist/storage/backend/capacityResolver.js +25 -0
  37. package/dist/storage/backend/encoding.d.ts +17 -0
  38. package/dist/storage/backend/encoding.js +148 -0
  39. package/dist/storage/backend/types.d.ts +184 -0
  40. package/dist/storage/backend/types.js +1 -0
  41. package/dist/storage/btree/recordKeyIndexBTree.d.ts +39 -0
  42. package/dist/storage/btree/recordKeyIndexBTree.js +104 -0
  43. package/dist/storage/config/config.browser.d.ts +4 -0
  44. package/dist/storage/config/config.browser.js +8 -0
  45. package/dist/storage/config/config.d.ts +1 -0
  46. package/dist/storage/config/config.js +1 -0
  47. package/dist/storage/config/config.node.d.ts +4 -0
  48. package/dist/storage/config/config.node.js +74 -0
  49. package/dist/storage/config/config.shared.d.ts +6 -0
  50. package/dist/storage/config/config.shared.js +105 -0
  51. package/dist/storage/datastore/Datastore.d.ts +47 -0
  52. package/dist/storage/datastore/Datastore.js +525 -0
  53. package/dist/storage/datastore/datastoreClose.d.ts +12 -0
  54. package/dist/storage/datastore/datastoreClose.js +60 -0
  55. package/dist/storage/datastore/datastoreKeyDefinition.d.ts +7 -0
  56. package/dist/storage/datastore/datastoreKeyDefinition.js +60 -0
  57. package/dist/storage/datastore/datastoreLifecycle.d.ts +18 -0
  58. package/dist/storage/datastore/datastoreLifecycle.js +63 -0
  59. package/dist/storage/datastore/mutationById.d.ts +29 -0
  60. package/dist/storage/datastore/mutationById.js +71 -0
  61. package/dist/storage/drivers/IndexedDB/indexedDBBackend.d.ts +11 -0
  62. package/dist/storage/drivers/IndexedDB/indexedDBBackend.js +109 -0
  63. package/dist/storage/drivers/IndexedDB/indexedDBBackendController.d.ts +27 -0
  64. package/dist/storage/drivers/IndexedDB/indexedDBBackendController.js +60 -0
  65. package/dist/storage/drivers/IndexedDB/indexedDBConfig.d.ts +7 -0
  66. package/dist/storage/drivers/IndexedDB/indexedDBConfig.js +24 -0
  67. package/dist/storage/drivers/file/fileBackend.d.ts +5 -0
  68. package/dist/storage/drivers/file/fileBackend.js +168 -0
  69. package/dist/storage/drivers/file/fileBackendController.d.ts +31 -0
  70. package/dist/storage/drivers/file/fileBackendController.js +72 -0
  71. package/dist/storage/drivers/file/fileBackendSnapshot.d.ts +10 -0
  72. package/dist/storage/drivers/file/fileBackendSnapshot.js +166 -0
  73. package/dist/storage/drivers/localStorage/localStorageBackend.d.ts +10 -0
  74. package/dist/storage/drivers/localStorage/localStorageBackend.js +156 -0
  75. package/dist/storage/drivers/localStorage/localStorageBackendController.d.ts +24 -0
  76. package/dist/storage/drivers/localStorage/localStorageBackendController.js +35 -0
  77. package/dist/storage/drivers/localStorage/localStorageConfig.d.ts +10 -0
  78. package/dist/storage/drivers/localStorage/localStorageConfig.js +16 -0
  79. package/dist/storage/drivers/localStorage/localStorageLayout.d.ts +5 -0
  80. package/dist/storage/drivers/localStorage/localStorageLayout.js +29 -0
  81. package/dist/storage/drivers/opfs/opfsBackend.d.ts +12 -0
  82. package/dist/storage/drivers/opfs/opfsBackend.js +142 -0
  83. package/dist/storage/drivers/opfs/opfsBackendController.d.ts +26 -0
  84. package/dist/storage/drivers/opfs/opfsBackendController.js +44 -0
  85. package/dist/storage/drivers/syncStorage/syncStorageAdapter.d.ts +2 -0
  86. package/dist/storage/drivers/syncStorage/syncStorageAdapter.js +123 -0
  87. package/dist/storage/drivers/syncStorage/syncStorageBackend.d.ts +11 -0
  88. package/dist/storage/drivers/syncStorage/syncStorageBackend.js +169 -0
  89. package/dist/storage/drivers/syncStorage/syncStorageBackendController.d.ts +24 -0
  90. package/dist/storage/drivers/syncStorage/syncStorageBackendController.js +34 -0
  91. package/dist/storage/drivers/syncStorage/syncStorageChunkMaintenance.d.ts +2 -0
  92. package/dist/storage/drivers/syncStorage/syncStorageChunkMaintenance.js +28 -0
  93. package/dist/storage/drivers/syncStorage/syncStorageConfig.d.ts +13 -0
  94. package/dist/storage/drivers/syncStorage/syncStorageConfig.js +42 -0
  95. package/dist/storage/drivers/syncStorage/syncStorageQuota.d.ts +3 -0
  96. package/dist/storage/drivers/syncStorage/syncStorageQuota.js +45 -0
  97. package/dist/storage/record/ordering.d.ts +3 -0
  98. package/dist/storage/record/ordering.js +7 -0
  99. package/dist/types.d.ts +125 -0
  100. package/dist/types.js +1 -0
  101. package/dist/validation/metadata.d.ts +1 -0
  102. package/dist/validation/metadata.js +7 -0
  103. package/dist/validation/payload.d.ts +7 -0
  104. package/dist/validation/payload.js +135 -0
  105. package/dist/validation/typeGuards.d.ts +1 -0
  106. package/dist/validation/typeGuards.js +7 -0
  107. package/package.json +110 -0
package/README-JA.md ADDED
@@ -0,0 +1,1205 @@
1
+ # Frostpillar Storage Engine
2
+
3
+ [English/英語](./README.md) | [Japanese/日本語](./README-JA.md)
4
+
5
+ [![npm version](https://img.shields.io/npm/v/@frostpillar/frostpillar-storage-engine)](https://www.npmjs.com/package/@frostpillar/frostpillar-storage-engine)
6
+ [![Node.js >=24](https://img.shields.io/badge/Node.js-%3E%3D24-green.svg)](https://nodejs.org/)
7
+ [![CI](https://github.com/hjmsano/frostpillar-storage-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/hjmsano/frostpillar-storage-engine/actions/workflows/ci.yml)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
9
+
10
+ JavaScript 向けの軽量な組み込み Key-Value データベースです。Node.js、ブラウザ、ブラウザ拡張で構造化レコードを保存・取得できます。サーバー不要で動作します。
11
+
12
+ 内部的には、多数の小さなエントリを単一のバッキングストアにパッキングするチャンクベースのストレージエンジンであり、プラガブルなドライバ、容量制御、自動コミットをサポートします。Frostpillar エコシステムの一部です:
13
+
14
+ ```
15
+ frostpillar-db — データベース管理とオーケストレーション、ネイティブクエリーを提供
16
+ ├── frostpillar-query-interface — SQL-like / Lucene-like クエリ API
17
+ ├── frostpillar-storage-engine — コアストレージとチャンクハンドリング(本パッケージ)
18
+ │ └── frostpillar-btree — B+ tree インデキシング
19
+ frostpillar-http-api — RESTful API レイヤー
20
+ frostpillar-mcp — AI エージェント連携用 MCP インターフェース
21
+ frostpillar-cli — コマンドラインインターフェース
22
+ ```
23
+
24
+ ## 特徴
25
+
26
+ - **マルチランタイム** — Node.js、ブラウザ、ブラウザ拡張で動作
27
+ - **プラガブルドライバ** — in-memory、file、localStorage、IndexedDB、OPFS、ブラウザ拡張 sync storage
28
+ - **容量制御** — 厳格なクォータ強制または自動ターンオーバー方式での古いレコードの退避
29
+ - **自動コミット** — 時間間隔およびバイト閾値ベースのバックグラウンド永続化
30
+ - **カスタムキー** — normalize/compare/serialize/deserialize を提供して独自のキー型を定義可能
31
+ - **Tree-shakable** — `sideEffects: false` の ESM 配布。未使用ドライバはバンドラが除去
32
+ - **サードパーティランタイム依存なし** — Frostpillar ファミリパッケージのみ
33
+
34
+ ## クイック例
35
+
36
+ **Node.js / TypeScript:**
37
+
38
+ ```ts
39
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
40
+
41
+ const db = new Datastore({});
42
+
43
+ await db.put({
44
+ key: 'tenant-001',
45
+ payload: { event: 'login', userId: 'u-001' },
46
+ });
47
+
48
+ const rows = await db.get('tenant-001');
49
+ console.log(rows[0].payload.event); // login
50
+
51
+ await db.close();
52
+ ```
53
+
54
+ **ブラウザ(ESM):**
55
+
56
+ ```js
57
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
58
+
59
+ const db = new Datastore({});
60
+
61
+ await db.put({
62
+ key: 'user-001',
63
+ payload: { event: 'open' },
64
+ });
65
+
66
+ const rows = await db.get('user-001');
67
+ console.log(rows[0].payload.event); // open
68
+ ```
69
+
70
+ **ブラウザ(バンドル):**
71
+
72
+ ```js
73
+ const { Datastore } = window.FrostpillarStorageEngine;
74
+
75
+ const db = new Datastore({});
76
+
77
+ await db.put({
78
+ key: 'user-001',
79
+ payload: { event: 'open' },
80
+ });
81
+
82
+ const rows = await db.get('user-001');
83
+ console.log(rows[0].payload.event); // open
84
+ ```
85
+
86
+ > **注意:** IIFE バンドルには `Datastore`、エラークラス、およびブラウザ用ストレージドライバ(`localStorageDriver`、`indexedDBDriver`、`opfsDriver`、`syncStorageDriver`)が含まれます。`fileDriver` は Node.js 専用であり、バンドルには含まれません — サブパス `@frostpillar/frostpillar-storage-engine/drivers/file` からインポートしてください。
87
+
88
+ ---
89
+
90
+ ## 目次
91
+
92
+ - [はじめに](#はじめに)
93
+ - [ユーザーマニュアル](#ユーザーマニュアル)
94
+ - [基本コンセプト](#基本コンセプト)
95
+ - [CRUD 操作](#crud-操作)
96
+ - [レコード ID (`_id`)](#レコード-id-_id)
97
+ - [ストレージドライバ](#ストレージドライバ)
98
+ - [自動コミット](#自動コミット)
99
+ - [容量制御](#容量制御)
100
+ - [カスタムキー定義](#カスタムキー定義)
101
+ - [エラーハンドリング](#エラーハンドリング)
102
+ - [API リファレンス](#api-リファレンス)
103
+ - [コントリビュートガイド](#コントリビュートガイド)
104
+ - [ライセンス](#ライセンス)
105
+
106
+ ---
107
+
108
+ ## はじめに
109
+
110
+ ### インストール(Node.js / TypeScript)
111
+
112
+ ```bash
113
+ pnpm add @frostpillar/frostpillar-storage-engine
114
+ ```
115
+
116
+ 本パッケージは [npm](https://www.npmjs.com/package/@frostpillar/frostpillar-storage-engine) で公開されています。
117
+
118
+ ### インストール(ブラウザ)
119
+
120
+ [GitHub Releases](https://github.com/hjmsano/frostpillar-storage-engine/releases) から minify 済み IIFE バンドルをダウンロードし、`<script>` タグで読み込みます。`<TAG>` はリリース済みタグ(例: `v0.2.1`)に置き換えてください。
121
+
122
+ ```html
123
+ <script src="https://github.com/hjmsano/frostpillar-storage-engine/releases/download/<TAG>/frostpillar-storage-engine.min.js"></script>
124
+ ```
125
+
126
+ `Datastore`、エラークラス、およびブラウザ用ストレージドライバ(`localStorageDriver`、`indexedDBDriver`、`opfsDriver`、`syncStorageDriver`)は `window.FrostpillarStorageEngine` で利用できます。`type="module"` は不要です。
127
+
128
+ ### 互換性
129
+
130
+ | 環境 | 要件 |
131
+ | ---------- | ------------------------------------------------------------ |
132
+ | Node.js | >= 24.0.0(ESM および CJS) |
133
+ | ブラウザ | ES2020 互換(Chrome 80+、Firefox 74+、Safari 14+、Edge 80+) |
134
+ | TypeScript | >= 5.0 |
135
+
136
+ > **プレリリースについて:** 本パッケージは [SemVer](https://semver.org/) に従います。メジャーバージョンが `0` の間は、マイナーバージョンの更新に破壊的変更が含まれる場合があります。依存バージョンを固定し、アップグレード前にチェンジログを確認してください。
137
+
138
+ ---
139
+
140
+ ## ユーザーマニュアル
141
+
142
+ ### 基本コンセプト
143
+
144
+ **Datastore** が唯一のエントリポイントです。基本的なライフサイクルは次のとおりです:
145
+
146
+ 1. **作成** — `new Datastore(config)`(デフォルトは in-memory、`driver` を渡すと永続化)
147
+ 2. **書き込み** — `put()` / `putMany()` でレコードを挿入
148
+ 3. **読み取り** — `get()`、`getFirst()`、`getLast()`、`getAll()` など
149
+ 4. **永続化** — `commit()` で永続ストレージにフラッシュ(または `autoCommit` を使用)
150
+ 5. **クローズ** — `close()` でリソースとロックを解放
151
+
152
+ 各レコードのフィールド:
153
+
154
+ | フィールド | 説明 |
155
+ | ---------- | -------------------------------------------------------- |
156
+ | `key` | ユーザーが指定するルックアップキー(デフォルトは文字列) |
157
+ | `payload` | JSON 互換のデータオブジェクト |
158
+ | `_id` | エフェメラルなシステム生成 `EntryId`、読み取り専用 |
159
+
160
+ > **防御的クローン:** ペイロードは挿入時に防御的にクローンされますが、**凍結はされません**。読み取り API はクローンせずに内部参照を返します。返されたペイロードを変更してもエラーはスローされませんが、内部状態が破損する可能性があります — 返されたペイロードは読み取り専用として扱ってください。変更可能なコピーが必要な場合は自分でクローンしてください(例: `structuredClone(record.payload)`)。
161
+
162
+ レコードは `key` 昇順(デフォルトは辞書順)、同一キー内では挿入順で並びます。
163
+
164
+ #### 重複キーポリシー
165
+
166
+ デフォルトでは重複キーが許可されており、複数のレコードが同じキーを持てます。構築時にポリシーを設定できます:
167
+
168
+ **Node.js / TypeScript:**
169
+
170
+ ```ts
171
+ const db = new Datastore({
172
+ duplicateKeys: 'allow', // デフォルト — キーごとに複数レコード可
173
+ // duplicateKeys: 'replace', // キーごとに1レコード、最後の書き込みが勝つ
174
+ // duplicateKeys: 'reject', // キーごとに1レコード、重複時にスロー
175
+ });
176
+ ```
177
+
178
+ **ブラウザ(ESM):**
179
+
180
+ ```js
181
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
182
+
183
+ const db = new Datastore({
184
+ duplicateKeys: 'allow', // デフォルト — キーごとに複数レコード可
185
+ // duplicateKeys: 'replace', // キーごとに1レコード、最後の書き込みが勝つ
186
+ // duplicateKeys: 'reject', // キーごとに1レコード、重複時にスロー
187
+ });
188
+ ```
189
+
190
+ **ブラウザ(バンドル):**
191
+
192
+ ```js
193
+ const { Datastore } = window.FrostpillarStorageEngine;
194
+
195
+ const db = new Datastore({
196
+ duplicateKeys: 'allow', // デフォルト — キーごとに複数レコード可
197
+ // duplicateKeys: 'replace', // キーごとに1レコード、最後の書き込みが勝つ
198
+ // duplicateKeys: 'reject', // キーごとに1レコード、重複時にスロー
199
+ });
200
+ ```
201
+
202
+ | ポリシー | 動作 | ユースケース |
203
+ | ----------- | ----------------------------------- | ---------------------------- |
204
+ | `'allow'` | キーごとに複数レコード | ログ、イベント、時系列データ |
205
+ | `'replace'` | 最後の書き込みで上書き | 設定、キャッシュ |
206
+ | `'reject'` | 重複時に `ValidationError` をスロー | ユニーク制約 |
207
+
208
+ #### ペイロードバリデーション
209
+
210
+ ペイロードは `put()`、`putMany()`、`updateById()` の呼び出しごとにバリデーションされます。以下の制限が適用されます:
211
+
212
+ | 制約 | 上限 |
213
+ |------|------|
214
+ | ペイロード合計バイト数 | 1,048,576(1 MB) |
215
+ | 最大ネスト深度 | 64 オブジェクトレベル |
216
+ | キー合計数 | 4,096 |
217
+ | オブジェクトあたりの最大キー数 | 256 |
218
+ | キーの最大サイズ | 1,024 バイト(UTF-8) |
219
+ | 文字列値の最大サイズ | 65,535 バイト(UTF-8) |
220
+
221
+ 追加ルール:
222
+ - ペイロードはプレーンオブジェクトである必要があります(トップレベルで配列、関数、`BigInt` は不可)。
223
+ - キーは空でなく、空白のみの文字列は不可。
224
+ - 予約キー(`__proto__`、`constructor`、`prototype`)は禁止。
225
+ - 循環参照は禁止。
226
+ - 違反時は `ValidationError` がスローされます。
227
+
228
+ 信頼できる入力に対してはバリデーションをスキップできます:
229
+
230
+ ```ts
231
+ const db = new Datastore({ skipPayloadValidation: true });
232
+ ```
233
+
234
+ > **警告:** バリデーションのスキップは、すべてのペイロード安全チェックを無効化します。入力が正しい形式であることが確実な場合のみ使用してください。
235
+
236
+ ---
237
+
238
+ ### CRUD 操作
239
+
240
+ #### 書き込み
241
+
242
+ **`put(record)`** — 1 件のレコードを挿入します。
243
+
244
+ ```ts
245
+ await db.put({ key: 'k1', payload: { name: 'Alice' } });
246
+ ```
247
+
248
+ **`putMany(records)`** — 複数レコードを挿入します(非アトミック、左から右へ順次実行)。
249
+
250
+ ```ts
251
+ await db.putMany([
252
+ { key: 'k1', payload: { name: 'Alice' } },
253
+ { key: 'k2', payload: { name: 'Bob' } },
254
+ ]);
255
+ ```
256
+
257
+ `put()` はレコードを挿入します。重複キーの動作は `duplicateKeys` ポリシーに依存します(デフォルト: `'allow'`)。
258
+
259
+ #### 読み取り
260
+
261
+ **`get(key)`** — 指定キーのすべてのレコードを返します。
262
+
263
+ ```ts
264
+ const rows = await db.get('k1');
265
+ ```
266
+
267
+ **`getFirst(key)`** — 指定キーの最初のレコード、または `null` を返します。
268
+
269
+ ```ts
270
+ const row = await db.getFirst('k1');
271
+ ```
272
+
273
+ **`getLast(key)`** — 指定キーの最後(最新挿入)のレコード、または `null` を返します。`duplicateKeys` が `'replace'` または `'reject'` の場合、`getFirst()` と同じ動作になります。
274
+
275
+ ```ts
276
+ const row = await db.getLast('k1');
277
+ ```
278
+
279
+ **`getById(id)`** — `_id` で 1 件のレコードを取得、または `null` を返します。
280
+
281
+ ```ts
282
+ const row = await db.getById(id);
283
+ ```
284
+
285
+ **`getAll()`** — 全レコードをキー順・挿入順で返します。
286
+
287
+ ```ts
288
+ const all = await db.getAll();
289
+ ```
290
+
291
+ **`getRange(start, end)`** — `start <= key <= end`(両端含む)のレコードを返します。
292
+
293
+ ```ts
294
+ const range = await db.getRange('a', 'f');
295
+ ```
296
+
297
+ **`getMany(keys)`** — 複数キーのレコードをまとめて取得します。
298
+
299
+ ```ts
300
+ const rows = await db.getMany(['k1', 'k3', 'k5']);
301
+ ```
302
+
303
+ **`has(key)`** — 指定キーのレコードが存在するか確認します。
304
+
305
+ ```ts
306
+ const exists = await db.has('k1');
307
+ ```
308
+
309
+ すべてのレコード返却 API は結果に `_id` フィールドを含みます。
310
+
311
+ #### 更新
312
+
313
+ **`updateById(id, patch)`** — 既存の payload に `patch` を shallow merge します。見つかった場合は `true`、見つからなかった場合は `false` を返します。`key` や `_id` は変更されません。
314
+
315
+ ```ts
316
+ const updated = await db.updateById(id, { name: 'Alice V2' });
317
+ ```
318
+
319
+ #### 削除
320
+
321
+ **`delete(key)`** — 指定キーのすべてのレコードを削除します。削除件数を返します。
322
+
323
+ ```ts
324
+ const count = await db.delete('k1');
325
+ ```
326
+
327
+ **`deleteById(id)`** — `_id` で 1 件のレコードを削除します。見つかった場合は `true` を返します。
328
+
329
+ ```ts
330
+ const removed = await db.deleteById(id);
331
+ ```
332
+
333
+ **`deleteMany(keys)`** — 複数キーのレコードを削除します(非アトミック)。合計削除件数を返します。
334
+
335
+ ```ts
336
+ const count = await db.deleteMany(['k1', 'k2']);
337
+ ```
338
+
339
+ **`clear()`** — 全レコードを削除します。
340
+
341
+ ```ts
342
+ await db.clear();
343
+ ```
344
+
345
+ #### メタデータ
346
+
347
+ **`count()`** — レコードの総数を返します。
348
+
349
+ ```ts
350
+ const n = await db.count();
351
+ ```
352
+
353
+ **`keys()`** — 重複なしのキーを昇順で返します(payload は読み込みません)。
354
+
355
+ ```ts
356
+ const allKeys = await db.keys();
357
+ ```
358
+
359
+ ---
360
+
361
+ ### レコード ID (`_id`)
362
+
363
+ `_id` は読み取り API が返すすべてのレコードに含まれるシステム生成の `EntryId`(ブランド付き数値)です。エフェメラルであり、永続ストレージからの復元時に再発行されます。
364
+
365
+ - `put()` からは `_id` は返されません。レコードを読み取ることで `_id` を取得します。
366
+ - 再起動または `fromJSON()` による復元後、以前に取得した `_id` は無効になります。新しい値を得るには再クエリしてください。
367
+ - `EntryId` は型アノテーション用にパッケージから再エクスポートされています:
368
+
369
+ **Node.js / TypeScript:**
370
+
371
+ ```ts
372
+ import type { EntryId } from '@frostpillar/frostpillar-storage-engine';
373
+ ```
374
+
375
+ **ブラウザ(ESM / バンドル):**
376
+
377
+ ```js
378
+ // EntryId はランタイムではプレーンな number です。import は不要です。
379
+ // レコード結果から直接使用してください:
380
+ const record = await db.getFirst('k1');
381
+ const id = record._id; // EntryId
382
+ ```
383
+
384
+ ---
385
+
386
+ ### ストレージドライバ
387
+
388
+ #### ドライバ比較表
389
+
390
+ | ドライバ | 環境 | 永続性 | 想定ユースケース |
391
+ | -------------------- | ------------------ | ---------------------------------------------- | ---------------------------------- |
392
+ | _(なし)_ | Node.js / ブラウザ | in-memory のみ | キャッシュ、テスト、一時データ |
393
+ | `fileDriver` | Node.js | ファイルシステム | サーバーサイドの永続ストレージ |
394
+ | `localStorageDriver` | ブラウザ | localStorage | ブラウザ側の小規模永続化 |
395
+ | `indexedDBDriver` | ブラウザ | IndexedDB | ブラウザ側のより大きなストレージ |
396
+ | `opfsDriver` | ブラウザ | Origin Private File System | 高スループットのブラウザストレージ |
397
+ | `syncStorageDriver` | ブラウザ拡張 | `browser.storage.sync` / `chrome.storage.sync` | デバイス間の拡張データ同期 |
398
+
399
+ #### In-Memory(デフォルト)
400
+
401
+ ドライバ不要。データはメモリ上にのみ存在します。
402
+
403
+ **Node.js / TypeScript:**
404
+
405
+ ```ts
406
+ const db = new Datastore({});
407
+ ```
408
+
409
+ **ブラウザ(ESM):**
410
+
411
+ ```js
412
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
413
+
414
+ const db = new Datastore({});
415
+ ```
416
+
417
+ **ブラウザ(バンドル):**
418
+
419
+ ```js
420
+ const { Datastore } = window.FrostpillarStorageEngine;
421
+
422
+ const db = new Datastore({});
423
+ ```
424
+
425
+ #### File ドライバ(Node.js)
426
+
427
+ **Node.js / TypeScript:**
428
+
429
+ ```ts
430
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
431
+ import { fileDriver } from '@frostpillar/frostpillar-storage-engine/drivers/file';
432
+
433
+ const db = new Datastore({
434
+ autoCommit: {
435
+ frequency: '5s',
436
+ maxPendingBytes: 1024 * 1024,
437
+ },
438
+ driver: fileDriver({
439
+ filePath: './data/events.fpdb',
440
+ }),
441
+ });
442
+
443
+ await db.put({
444
+ key: 'tenant-001',
445
+ payload: { event: 'purchase', amount: 1200 },
446
+ });
447
+
448
+ await db.commit();
449
+ await db.close();
450
+ ```
451
+
452
+ | オプション | 型 | 説明 |
453
+ |------------|------|------|
454
+ | `filePath` | `string` | データファイルへの直接パス(例: `'./data/events.fpdb'`) |
455
+
456
+ ディレクトリベースのターゲティングも `target` オプションで利用できます:
457
+
458
+ | オプション | 型 | 説明 |
459
+ |------------|------|------|
460
+ | `target.kind` | `'directory'` | ディレクトリベースのファイル解決を使用 |
461
+ | `target.directory` | `string` | データファイルを含むディレクトリ |
462
+ | `target.fileName` | `string` | オプションのファイル名(デフォルト: 自動生成) |
463
+ | `target.filePrefix` | `string` | オプションのファイル名プレフィックス |
464
+
465
+ **ロックファイルの動作:**
466
+
467
+ `fileDriver` は単一 writer を保証するために `${filePath}.lock` を使用します。プロセスが `close()` を呼ばずに終了すると、以降のオープンは `DatabaseLockedError` で失敗します。
468
+
469
+ 復旧手順:
470
+
471
+ 1. 同じデータストアファイルを使用している writer プロセスが存在しないことを確認する。
472
+ 2. stale なロックファイル(`<resolved-data-file>.lock`)を手動で削除する。
473
+ 3. データストアを再オープンする。
474
+
475
+ > **注意:** `fileDriver` は Node.js 専用であり、ブラウザ IIFE バンドルには**含まれません**。サブパス `@frostpillar/frostpillar-storage-engine/drivers/file` からインポートしてください。
476
+
477
+ #### localStorage ドライバ
478
+
479
+ **Node.js / TypeScript:**
480
+
481
+ ```ts
482
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
483
+ import { localStorageDriver } from '@frostpillar/frostpillar-storage-engine/drivers/localStorage';
484
+
485
+ const db = new Datastore({
486
+ driver: localStorageDriver({
487
+ databaseKey: 'app-events',
488
+ keyPrefix: 'frostpillar',
489
+ maxChunkChars: 32768,
490
+ maxChunks: 64,
491
+ }),
492
+ });
493
+ ```
494
+
495
+ **ブラウザ(ESM):**
496
+
497
+ ```js
498
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
499
+ import { localStorageDriver } from '@frostpillar/frostpillar-storage-engine/drivers/localStorage';
500
+
501
+ const db = new Datastore({
502
+ driver: localStorageDriver({
503
+ databaseKey: 'app-events',
504
+ keyPrefix: 'frostpillar',
505
+ maxChunkChars: 32768,
506
+ maxChunks: 64,
507
+ }),
508
+ });
509
+ ```
510
+
511
+ **ブラウザ(バンドル):**
512
+
513
+ ```js
514
+ const { Datastore, localStorageDriver } = window.FrostpillarStorageEngine;
515
+
516
+ const db = new Datastore({
517
+ driver: localStorageDriver({
518
+ databaseKey: 'app-events',
519
+ keyPrefix: 'frostpillar',
520
+ maxChunkChars: 32768,
521
+ maxChunks: 64,
522
+ }),
523
+ });
524
+ ```
525
+
526
+ | オプション | 型 | 説明 |
527
+ | --------------- | -------- | --------------------------------------------------- |
528
+ | `databaseKey` | `string` | localStorage 内の論理データベース名 |
529
+ | `keyPrefix` | `string` | localStorage キーのプレフィックス(名前空間の分離) |
530
+ | `maxChunkChars` | `number` | チャンクあたりの最大文字数 |
531
+ | `maxChunks` | `number` | チャンクの最大数 |
532
+
533
+ #### IndexedDB ドライバ
534
+
535
+ **Node.js / TypeScript:**
536
+
537
+ ```ts
538
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
539
+ import { indexedDBDriver } from '@frostpillar/frostpillar-storage-engine/drivers/indexedDB';
540
+
541
+ const db = new Datastore({
542
+ autoCommit: { frequency: 'immediate' },
543
+ driver: indexedDBDriver({
544
+ databaseName: 'frostpillar-demo',
545
+ objectStoreName: 'records',
546
+ version: 1,
547
+ }),
548
+ });
549
+ ```
550
+
551
+ **ブラウザ(ESM):**
552
+
553
+ ```js
554
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
555
+ import { indexedDBDriver } from '@frostpillar/frostpillar-storage-engine/drivers/indexedDB';
556
+
557
+ const db = new Datastore({
558
+ autoCommit: { frequency: 'immediate' },
559
+ driver: indexedDBDriver({
560
+ databaseName: 'frostpillar-demo',
561
+ objectStoreName: 'records',
562
+ version: 1,
563
+ }),
564
+ });
565
+ ```
566
+
567
+ **ブラウザ(バンドル):**
568
+
569
+ ```js
570
+ const { Datastore, indexedDBDriver } = window.FrostpillarStorageEngine;
571
+
572
+ const db = new Datastore({
573
+ autoCommit: { frequency: 'immediate' },
574
+ driver: indexedDBDriver({
575
+ databaseName: 'frostpillar-demo',
576
+ objectStoreName: 'records',
577
+ version: 1,
578
+ }),
579
+ });
580
+ ```
581
+
582
+ | オプション | 型 | 説明 |
583
+ | ----------------- | -------- | ------------------------------------ |
584
+ | `databaseName` | `string` | IndexedDB のデータベース名 |
585
+ | `objectStoreName` | `string` | データベース内のオブジェクトストア名 |
586
+ | `version` | `number` | データベーススキーマバージョン |
587
+
588
+ #### OPFS ドライバ
589
+
590
+ **Node.js / TypeScript:**
591
+
592
+ ```ts
593
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
594
+ import { opfsDriver } from '@frostpillar/frostpillar-storage-engine/drivers/opfs';
595
+
596
+ const db = new Datastore({
597
+ autoCommit: { frequency: 'immediate' },
598
+ driver: opfsDriver({
599
+ directoryName: 'frostpillar-opfs',
600
+ }),
601
+ });
602
+ ```
603
+
604
+ **ブラウザ(ESM):**
605
+
606
+ ```js
607
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
608
+ import { opfsDriver } from '@frostpillar/frostpillar-storage-engine/drivers/opfs';
609
+
610
+ const db = new Datastore({
611
+ autoCommit: { frequency: 'immediate' },
612
+ driver: opfsDriver({
613
+ directoryName: 'frostpillar-opfs',
614
+ }),
615
+ });
616
+ ```
617
+
618
+ **ブラウザ(バンドル):**
619
+
620
+ ```js
621
+ const { Datastore, opfsDriver } = window.FrostpillarStorageEngine;
622
+
623
+ const db = new Datastore({
624
+ autoCommit: { frequency: 'immediate' },
625
+ driver: opfsDriver({
626
+ directoryName: 'frostpillar-opfs',
627
+ }),
628
+ });
629
+ ```
630
+
631
+ | オプション | 型 | 説明 |
632
+ | --------------- | -------- | --------------------- |
633
+ | `directoryName` | `string` | OPFS のディレクトリ名 |
634
+
635
+ #### Sync Storage ドライバ(ブラウザ拡張)
636
+
637
+ **Node.js / TypeScript:**
638
+
639
+ ```ts
640
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
641
+ import { syncStorageDriver } from '@frostpillar/frostpillar-storage-engine/drivers/syncStorage';
642
+
643
+ const db = new Datastore({
644
+ capacity: {
645
+ maxSize: 'backendLimit',
646
+ policy: 'strict',
647
+ },
648
+ autoCommit: {
649
+ frequency: '10s',
650
+ maxPendingBytes: 32768,
651
+ },
652
+ driver: syncStorageDriver({
653
+ databaseKey: 'extension-events',
654
+ keyPrefix: 'frostpillar-ext',
655
+ maxChunkChars: 6000,
656
+ maxChunks: 128,
657
+ maxItemBytes: 8192,
658
+ maxTotalBytes: 102400,
659
+ maxItems: 256,
660
+ }),
661
+ });
662
+ ```
663
+
664
+ **ブラウザ(ESM):**
665
+
666
+ ```js
667
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
668
+ import { syncStorageDriver } from '@frostpillar/frostpillar-storage-engine/drivers/syncStorage';
669
+
670
+ const db = new Datastore({
671
+ capacity: {
672
+ maxSize: 'backendLimit',
673
+ policy: 'strict',
674
+ },
675
+ autoCommit: {
676
+ frequency: '10s',
677
+ maxPendingBytes: 32768,
678
+ },
679
+ driver: syncStorageDriver({
680
+ databaseKey: 'extension-events',
681
+ keyPrefix: 'frostpillar-ext',
682
+ maxChunkChars: 6000,
683
+ maxChunks: 128,
684
+ maxItemBytes: 8192,
685
+ maxTotalBytes: 102400,
686
+ maxItems: 256,
687
+ }),
688
+ });
689
+ ```
690
+
691
+ **ブラウザ(バンドル):**
692
+
693
+ ```js
694
+ const { Datastore, syncStorageDriver } = window.FrostpillarStorageEngine;
695
+
696
+ const db = new Datastore({
697
+ capacity: {
698
+ maxSize: 'backendLimit',
699
+ policy: 'strict',
700
+ },
701
+ autoCommit: {
702
+ frequency: '10s',
703
+ maxPendingBytes: 32768,
704
+ },
705
+ driver: syncStorageDriver({
706
+ databaseKey: 'extension-events',
707
+ keyPrefix: 'frostpillar-ext',
708
+ maxChunkChars: 6000,
709
+ maxChunks: 128,
710
+ maxItemBytes: 8192,
711
+ maxTotalBytes: 102400,
712
+ maxItems: 256,
713
+ }),
714
+ });
715
+ ```
716
+
717
+ | オプション | 型 | 説明 |
718
+ | --------------- | -------- | ------------------------------------------------ |
719
+ | `databaseKey` | `string` | 論理データベース名 |
720
+ | `keyPrefix` | `string` | ストレージキーのプレフィックス(名前空間の分離) |
721
+ | `maxChunkChars` | `number` | チャンクあたりの最大文字数 |
722
+ | `maxChunks` | `number` | チャンクの最大数 |
723
+ | `maxItemBytes` | `number` | ストレージアイテムあたりの最大バイト数 |
724
+ | `maxTotalBytes` | `number` | 全アイテム合計の最大バイト数 |
725
+ | `maxItems` | `number` | ストレージアイテムの最大数 |
726
+
727
+ 両方の API が利用可能な場合、ドライバは `browser.storage.sync` の Promise API を優先し、`chrome.storage.sync` のコールバック API をフォールバックとして使用します。
728
+
729
+ ---
730
+
731
+ ### 自動コミット
732
+
733
+ 永続ドライバ使用時、`commit()` を手動で呼ぶ代わりにバックグラウンドで自動永続化を設定できます。
734
+
735
+ **Node.js / TypeScript:**
736
+
737
+ ```ts
738
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
739
+ import { fileDriver } from '@frostpillar/frostpillar-storage-engine/drivers/file';
740
+
741
+ const db = new Datastore({
742
+ autoCommit: {
743
+ frequency: '5s', // 5 秒ごとにコミット
744
+ maxPendingBytes: 1024 * 1024, // または 1 MB の書き込みが溜まったとき
745
+ },
746
+ driver: fileDriver({ filePath: './data/events.fpdb' }),
747
+ });
748
+ ```
749
+
750
+ **ブラウザ(ESM):**
751
+
752
+ ```js
753
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
754
+ import { indexedDBDriver } from '@frostpillar/frostpillar-storage-engine/drivers/indexedDB';
755
+
756
+ const db = new Datastore({
757
+ autoCommit: {
758
+ frequency: '5s', // 5 秒ごとにコミット
759
+ maxPendingBytes: 1024 * 1024, // または 1 MB の書き込みが溜まったとき
760
+ },
761
+ driver: indexedDBDriver({
762
+ databaseName: 'my-app',
763
+ objectStoreName: 'records',
764
+ version: 1,
765
+ }),
766
+ });
767
+ ```
768
+
769
+ **ブラウザ(バンドル):**
770
+
771
+ ```js
772
+ const { Datastore, indexedDBDriver } = window.FrostpillarStorageEngine;
773
+
774
+ const db = new Datastore({
775
+ autoCommit: {
776
+ frequency: '5s', // 5 秒ごとにコミット
777
+ maxPendingBytes: 1024 * 1024, // または 1 MB の書き込みが溜まったとき
778
+ },
779
+ driver: indexedDBDriver({
780
+ databaseName: 'my-app',
781
+ objectStoreName: 'records',
782
+ version: 1,
783
+ }),
784
+ });
785
+ ```
786
+
787
+ | オプション | 型 | 説明 |
788
+ | ----------------- | ------------------------------------------------------------------------------ | -------------------------------------- |
789
+ | `frequency` | `'immediate'` \| `number` \| `'${n}ms'` \| `'${n}s'` \| `'${n}m'` \| `'${n}h'` | 自動コミットの頻度 |
790
+ | `maxPendingBytes` | `number` | 即座にコミットをトリガーするバイト閾値 |
791
+
792
+ `autoCommit` は永続 `driver` が必要です。`driver` なしで `autoCommit` を設定すると `ConfigurationError` で失敗します。
793
+
794
+ `autoCommit` が設定されていても、`commit()` を手動で呼んで明示的にフラッシュできます。
795
+
796
+ #### 自動コミットエラーの監視
797
+
798
+ 自動コミットの失敗は非同期で通知され、トリガーとなった `put()` 呼び出しは reject されません。`on('error')` で監視できます:
799
+
800
+ **Node.js / TypeScript:**
801
+
802
+ ```ts
803
+ const unsubscribe = db.on('error', (event) => {
804
+ console.error('autoCommit error:', event.error);
805
+ });
806
+
807
+ // 監視を停止する場合:
808
+ unsubscribe();
809
+
810
+ // または明示的に:
811
+ // db.off('error', listener);
812
+ ```
813
+
814
+ **ブラウザ(ESM):**
815
+
816
+ ```js
817
+ const unsubscribe = db.on('error', (event) => {
818
+ console.error('autoCommit error:', event.error);
819
+ });
820
+
821
+ // 監視を停止する場合:
822
+ unsubscribe();
823
+ ```
824
+
825
+ **ブラウザ(バンドル):**
826
+
827
+ ```js
828
+ const unsubscribe = db.on('error', (event) => {
829
+ console.error('autoCommit error:', event.error);
830
+ });
831
+
832
+ // 監視を停止する場合:
833
+ unsubscribe();
834
+ ```
835
+
836
+ ---
837
+
838
+ ### 容量制御
839
+
840
+ `capacity` 設定でデータストアのサイズを制限できます。
841
+
842
+ **Node.js / TypeScript:**
843
+
844
+ ```ts
845
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
846
+ import { fileDriver } from '@frostpillar/frostpillar-storage-engine/drivers/file';
847
+
848
+ const db = new Datastore({
849
+ capacity: {
850
+ maxSize: '10MB',
851
+ policy: 'strict',
852
+ },
853
+ driver: fileDriver({ filePath: './data/events.fpdb' }),
854
+ });
855
+ ```
856
+
857
+ **ブラウザ(ESM):**
858
+
859
+ ```js
860
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
861
+ import { localStorageDriver } from '@frostpillar/frostpillar-storage-engine/drivers/localStorage';
862
+
863
+ const db = new Datastore({
864
+ capacity: {
865
+ maxSize: '10MB',
866
+ policy: 'strict',
867
+ },
868
+ driver: localStorageDriver({
869
+ databaseKey: 'my-app',
870
+ }),
871
+ });
872
+ ```
873
+
874
+ **ブラウザ(バンドル):**
875
+
876
+ ```js
877
+ const { Datastore, localStorageDriver } = window.FrostpillarStorageEngine;
878
+
879
+ const db = new Datastore({
880
+ capacity: {
881
+ maxSize: '10MB',
882
+ policy: 'strict',
883
+ },
884
+ driver: localStorageDriver({
885
+ databaseKey: 'my-app',
886
+ }),
887
+ });
888
+ ```
889
+
890
+ | オプション | 型 | 説明 |
891
+ | ---------- | ----------------------------------------------------------------------------------- | ------------------------ |
892
+ | `maxSize` | `number` \| `'${n}B'` \| `'${n}KB'` \| `'${n}MB'` \| `'${n}GB'` \| `'backendLimit'` | データストアの最大サイズ |
893
+ | `policy` | `'strict'` \| `'turnover'` | 容量超過時の動作 |
894
+
895
+ **ポリシー:**
896
+
897
+ - **`strict`**(デフォルト)— 制限を超える書き込みを `QuotaExceededError` で拒否します。
898
+ - **`turnover`** — 新しいレコードが収まるまで、最も古いレコード(挿入順)を退避します。
899
+
900
+ **`backendLimit` センチネル:**
901
+
902
+ `maxSize: 'backendLimit'` を設定すると、ドライバ固有の制限値を使用します(例: `localStorageDriver` では `maxChunkChars * maxChunks`、`syncStorageDriver` では `maxTotalBytes`)。バックエンド制限解決をサポートする永続ドライバが必要です。
903
+
904
+ ---
905
+
906
+ ### カスタムキー定義
907
+
908
+ デフォルトでは、キーは空でない文字列で辞書順に並べられます。4 つのコールバックをすべて提供することでカスタムキー型を定義できます:
909
+
910
+ **Node.js / TypeScript:**
911
+
912
+ ```ts
913
+ const db = new Datastore({
914
+ key: {
915
+ normalize: (value, fieldName) => {
916
+ if (typeof value === 'number' && Number.isSafeInteger(value)) {
917
+ return value;
918
+ }
919
+ throw new TypeError(`${fieldName} は安全整数である必要があります。`);
920
+ },
921
+ compare: (left, right) => left - right,
922
+ serialize: (key) => key.toString(10),
923
+ deserialize: (serialized) => {
924
+ const parsed = Number(serialized);
925
+ if (!Number.isSafeInteger(parsed)) {
926
+ throw new TypeError('serialized key は安全整数である必要があります。');
927
+ }
928
+ return parsed;
929
+ },
930
+ },
931
+ });
932
+ ```
933
+
934
+ **ブラウザ(ESM):**
935
+
936
+ ```js
937
+ import { Datastore } from '@frostpillar/frostpillar-storage-engine';
938
+
939
+ const db = new Datastore({
940
+ key: {
941
+ normalize: (value, fieldName) => {
942
+ if (typeof value === 'number' && Number.isSafeInteger(value)) {
943
+ return value;
944
+ }
945
+ throw new TypeError(fieldName + ' は安全整数である必要があります。');
946
+ },
947
+ compare: (left, right) => left - right,
948
+ serialize: (key) => key.toString(10),
949
+ deserialize: (serialized) => {
950
+ const parsed = Number(serialized);
951
+ if (!Number.isSafeInteger(parsed)) {
952
+ throw new TypeError('serialized key は安全整数である必要があります。');
953
+ }
954
+ return parsed;
955
+ },
956
+ },
957
+ });
958
+ ```
959
+
960
+ **ブラウザ(バンドル):**
961
+
962
+ ```js
963
+ const { Datastore } = window.FrostpillarStorageEngine;
964
+
965
+ const db = new Datastore({
966
+ key: {
967
+ normalize: (value, fieldName) => {
968
+ if (typeof value === 'number' && Number.isSafeInteger(value)) {
969
+ return value;
970
+ }
971
+ throw new TypeError(fieldName + ' は安全整数である必要があります。');
972
+ },
973
+ compare: (left, right) => left - right,
974
+ serialize: (key) => key.toString(10),
975
+ deserialize: (serialized) => {
976
+ const parsed = Number(serialized);
977
+ if (!Number.isSafeInteger(parsed)) {
978
+ throw new TypeError('serialized key は安全整数である必要があります。');
979
+ }
980
+ return parsed;
981
+ },
982
+ },
983
+ });
984
+ ```
985
+
986
+ | コールバック | 説明 |
987
+ | ----------------------------- | --------------------------------------------------- |
988
+ | `normalize(value, fieldName)` | 入力をキー型にバリデーション・正規化 |
989
+ | `compare(left, right)` | 順序付けのための有限整数を返す(`< 0`、`0`、`> 0`) |
990
+ | `serialize(key)` | キーをストレージ用の文字列に変換 |
991
+ | `deserialize(serialized)` | 格納された文字列からキーを復元 |
992
+
993
+ `config.key` を指定する場合、4 つすべてが必須です。`compare` は有限整数を返す必要があります。`NaN`、`Infinity`、非整数値は `IndexCorruptionError` で失敗します。
994
+
995
+ ---
996
+
997
+ ### エラーハンドリング
998
+
999
+ すべての公開エラーは `FrostpillarError`(`Error` を継承)を継承しています。
1000
+
1001
+ **Node.js / TypeScript:**
1002
+
1003
+ ```ts
1004
+ import {
1005
+ Datastore,
1006
+ FrostpillarError,
1007
+ } from '@frostpillar/frostpillar-storage-engine';
1008
+
1009
+ try {
1010
+ await db.put({ key: 'k1', payload: { event: 'login' } });
1011
+ } catch (error) {
1012
+ if (error instanceof FrostpillarError) {
1013
+ console.error(error.name, error.message);
1014
+ } else {
1015
+ throw error;
1016
+ }
1017
+ }
1018
+ ```
1019
+
1020
+ **ブラウザ(ESM):**
1021
+
1022
+ ```js
1023
+ import {
1024
+ Datastore,
1025
+ FrostpillarError,
1026
+ } from '@frostpillar/frostpillar-storage-engine';
1027
+
1028
+ try {
1029
+ await db.put({ key: 'k1', payload: { event: 'login' } });
1030
+ } catch (error) {
1031
+ if (error instanceof FrostpillarError) {
1032
+ console.error(error.name, error.message);
1033
+ } else {
1034
+ throw error;
1035
+ }
1036
+ }
1037
+ ```
1038
+
1039
+ **ブラウザ(バンドル):**
1040
+
1041
+ ```js
1042
+ const { Datastore, FrostpillarError } = window.FrostpillarStorageEngine;
1043
+
1044
+ try {
1045
+ await db.put({ key: 'k1', payload: { event: 'login' } });
1046
+ } catch (error) {
1047
+ if (error instanceof FrostpillarError) {
1048
+ console.error(error.name, error.message);
1049
+ } else {
1050
+ throw error;
1051
+ }
1052
+ }
1053
+ ```
1054
+
1055
+ #### エラー型一覧
1056
+
1057
+ | エラー | 説明 |
1058
+ | ------------------------- | --------------------------------------------------------------------- |
1059
+ | `FrostpillarError` | すべての Frostpillar エラーのルートクラス |
1060
+ | `ValidationError` | 不正な入力(payload キー、ネスト深度など) |
1061
+ | `ConfigurationError` | 不正なデータストア設定 |
1062
+ | `InvalidQueryRangeError` | `getRange()` で `start > end` |
1063
+ | `ClosedDatastoreError` | クローズ済みデータストアへの操作 |
1064
+ | `QuotaExceededError` | `strict` ポリシーで容量超過 |
1065
+ | `StorageEngineError` | ストレージ層の I/O または内部エラー |
1066
+ | `DatabaseLockedError` | ファイルロック競合(`StorageEngineError` を継承) |
1067
+ | `BinaryFormatError` | バイナリデータの破損(`StorageEngineError` を継承) |
1068
+ | `PageCorruptionError` | ページ/世代データの破損(`StorageEngineError` を継承) |
1069
+ | `IndexCorruptionError` | インデックスの破損または不正な内部状態(`StorageEngineError` を継承) |
1070
+ | `UnsupportedBackendError` | 現在の環境でバックエンドが利用不可 |
1071
+
1072
+ #### `close()` のエラー集約
1073
+
1074
+ 遅延バックエンド初期化の失敗とバックエンドクローズの失敗が同じ `close()` 呼び出しで発生した場合、`close()` はネイティブの `AggregateError` をスローします。初期化エラーが最初、クローズエラーが 2 番目に含まれます。
1075
+
1076
+ ---
1077
+
1078
+ ## API リファレンス
1079
+
1080
+ ### キーベース操作
1081
+
1082
+ | メソッド | パラメータ | 戻り値 | 説明 |
1083
+ | --------------- | ------------------ | ------------------------------ | ---------------------- |
1084
+ | `put(record)` | `{ key, payload }` | `Promise<void>` | レコードを挿入 |
1085
+ | `get(key)` | key | `Promise<KeyedRecord[]>` | キーの全レコード |
1086
+ | `getFirst(key)` | key | `Promise<KeyedRecord \| null>` | キーの最初のレコード |
1087
+ | `getLast(key)` | key | `Promise<KeyedRecord \| null>` | キーの最後のレコード |
1088
+ | `has(key)` | key | `Promise<boolean>` | キーの存在確認 |
1089
+ | `delete(key)` | key | `Promise<number>` | キーの全レコードを削除 |
1090
+
1091
+ ### ID ベース操作
1092
+
1093
+ | メソッド | パラメータ | 戻り値 | 説明 |
1094
+ | ----------------------- | ------------------------- | ------------------------------ | -------------------- |
1095
+ | `getById(id)` | `EntryId` | `Promise<KeyedRecord \| null>` | レコード ID で取得 |
1096
+ | `updateById(id, patch)` | `EntryId`、payload パッチ | `Promise<boolean>` | shallow merge で更新 |
1097
+ | `deleteById(id)` | `EntryId` | `Promise<boolean>` | レコード ID で削除 |
1098
+
1099
+ ### バルク操作
1100
+
1101
+ | メソッド | パラメータ | 戻り値 | 説明 |
1102
+ | ---------------------- | ------------------ | ------------------------ | ------------------------ |
1103
+ | `getAll()` | — | `Promise<KeyedRecord[]>` | 全レコード |
1104
+ | `getRange(start, end)` | 開始キー、終了キー | `Promise<KeyedRecord[]>` | 両端含む範囲クエリ |
1105
+ | `getMany(keys)` | キー配列 | `Promise<KeyedRecord[]>` | 複数キーのレコード |
1106
+ | `putMany(records)` | レコード配列 | `Promise<void>` | 複数レコードを挿入 |
1107
+ | `deleteMany(keys)` | キー配列 | `Promise<number>` | 複数キーのレコードを削除 |
1108
+ | `clear()` | — | `Promise<void>` | 全レコードを削除 |
1109
+
1110
+ ### メタデータ
1111
+
1112
+ | メソッド | 戻り値 | 説明 |
1113
+ | --------- | -------------------- | ---------------------- |
1114
+ | `count()` | `Promise<number>` | レコード総数 |
1115
+ | `keys()` | `Promise<unknown[]>` | 重複なしのキー(昇順) |
1116
+
1117
+ ### ライフサイクル
1118
+
1119
+ | メソッド | 戻り値 | 説明 |
1120
+ | ------------------------ | ------------------------ | -------------------------------------------------- |
1121
+ | `commit()` | `Promise<void>` | 永続ストレージにフラッシュ(ドライバなしの場合は何もしない) |
1122
+ | `close()` | `Promise<void>` | リソースとロックを解放 |
1123
+ | `on('error', listener)` | `() => void`(購読解除) | 非同期エラーを監視 |
1124
+ | `off('error', listener)` | `void` | エラーリスナーを削除 |
1125
+
1126
+ ### エクスポートされた型
1127
+
1128
+ | 型 | 説明 |
1129
+ |----|------|
1130
+ | `DatastoreConfig` | コンストラクタ設定オブジェクト |
1131
+ | `DatastoreKeyDefinition` | カスタムキーの normalize/compare/serialize/deserialize コールバック |
1132
+ | `InputRecord` | `put()` および `putMany()` が受け付けるレコード形式 |
1133
+ | `KeyedRecord` | `key`、`payload`、`_id` フィールドを持つレコードオブジェクト |
1134
+ | `PersistedRecord` | `payload` と `sizeBytes` を持つ内部レコード形式 |
1135
+ | `RecordPayload` | ペイロードの値型(文字列、数値、真偽値、null、配列のネストされたレコード) |
1136
+ | `EntryId` | レコードを識別するブランド付き `number`(エフェメラル、復元時に再発行) |
1137
+ | `DuplicateKeyPolicy` | `'allow' \| 'reject' \| 'replace'` |
1138
+ | `CapacityConfig` | 容量制御設定(`maxSize` + `policy`) |
1139
+ | `CapacityPolicy` | `'strict' \| 'turnover'` |
1140
+ | `AutoCommitConfig` | 自動コミット設定(`frequency` + `maxPendingBytes`) |
1141
+ | `AutoCommitFrequencyInput` | 頻度値(`'immediate'` \| 数値 \| 時間文字列) |
1142
+ | `DatastoreDriver` | プラガブルバックエンドのドライバインターフェース |
1143
+ | `DatastoreDriverController` | ドライバコントローラのライフサイクルインターフェース |
1144
+ | `DatastoreDriverInitContext` | 初期化時にドライバに渡されるコンテキスト |
1145
+ | `DatastoreDriverInitResult` | ドライバ初期化の戻り値 |
1146
+ | `DatastoreDriverSnapshot` | 永続化用のスナップショットペイロード |
1147
+ | `DatastoreErrorEvent` | `on('error')` で送出されるエラーイベントの形状 |
1148
+ | `DatastoreErrorListener` | エラーイベント用のリスナーコールバック型 |
1149
+ | `FileBackendConfig` | File ドライバ設定 |
1150
+ | `FileTargetConfig` | ファイルターゲット(パスまたはディレクトリ)のユニオン型 |
1151
+ | `FileTargetByPathConfig` | 直接 `filePath` を指定するファイルターゲット |
1152
+ | `FileTargetByDirectoryConfig` | ディレクトリベースのファイル解決ターゲット |
1153
+ | `IndexedDBConfig` | IndexedDB ドライバ設定 |
1154
+ | `LocalStorageConfig` | localStorage ドライバ設定 |
1155
+ | `OpfsConfig` | OPFS ドライバ設定 |
1156
+ | `SyncStorageConfig` | Sync Storage ドライバ設定 |
1157
+ | `FrostpillarError` | すべての Frostpillar エラーのルートクラス |
1158
+ | `ValidationError` | 不正な入力エラー |
1159
+ | `ConfigurationError` | 不正な設定エラー |
1160
+ | `QuotaExceededError` | 容量超過エラー |
1161
+ | `StorageEngineError` | ストレージ層エラー |
1162
+
1163
+ 詳細な動作仕様は [Datastore API spec](docs/specs/01_DatastoreAPI.md) および [Durable Backends spec](docs/specs/02_DurableBackends.md) を参照してください。
1164
+
1165
+ ---
1166
+
1167
+ ## コントリビュートガイド
1168
+
1169
+ ### 必要な環境
1170
+
1171
+ - Node.js `>=24.0.0`
1172
+ - pnpm `>=10.0.0`
1173
+
1174
+ ### 開発コマンド
1175
+
1176
+ | コマンド | 説明 |
1177
+ | ------------------- | ----------------------------------------- |
1178
+ | `pnpm check` | 型チェック、lint、テスト、textlint を実行 |
1179
+ | `pnpm test` | テストを実行 |
1180
+ | `pnpm build` | パッケージをビルド |
1181
+ | `pnpm build:bundle` | ブラウザ IIFE バンドルをビルド |
1182
+
1183
+ ### 開発ワークフロー
1184
+
1185
+ 本プロジェクトは厳格な SDD/TDD ワークフローに従います:
1186
+
1187
+ 1. **Spec** — 実装前に `docs/specs/` の仕様を更新または作成する。
1188
+ 2. **Test** — コードより先にテストを書く。
1189
+ 3. **Code** — テストをパスする最小限のロジックを実装する。
1190
+ 4. **Verify** — `pnpm check` を実行してすべてがパスすることを確認する。
1191
+
1192
+ ### ドキュメント
1193
+
1194
+ - [README (English)](README.md)
1195
+ - [Architecture overview](docs/architecture/overview.md)
1196
+ - [Vision and principles](docs/architecture/vision-and-principles.md)
1197
+ - [Testing strategy](docs/architecture/testing-strategy.md)
1198
+ - [Specs index](docs/specs/README.md)
1199
+ - [ADRs](docs/adr)
1200
+
1201
+ ---
1202
+
1203
+ ## ライセンス
1204
+
1205
+ [MIT](LICENSE)