@unikvs/indexeddb 0.0.13 → 0.0.14

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.
package/dist/index.d.ts CHANGED
@@ -9,20 +9,92 @@ type IndexeddbOptions = {};
9
9
  */
10
10
  declare class Indexeddb implements IStorage {
11
11
  #private;
12
+ /**
13
+ * ストレージ名です。常に `Indexeddb` を返します。
14
+ */
12
15
  readonly name: string;
13
16
  /**
14
17
  * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
15
18
  */
16
19
  constructor(dbName?: string, storeName?: string, _options?: IndexeddbOptions);
20
+ /**
21
+ * ストレージが開いているかどうかを返します。
22
+ *
23
+ * @returns データベース接続があれば true を返します。
24
+ */
17
25
  get isOpen(): boolean;
26
+ /**
27
+ * データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。
28
+ *
29
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
30
+ *
31
+ * @returns 完了を知らせるプロミスです。
32
+ */
18
33
  open(c: IStorage.OpenContext): Promise<void>;
34
+ /**
35
+ * データベース接続を閉じます。
36
+ *
37
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
38
+ *
39
+ * @returns 完了を知らせるプロミスです。
40
+ */
19
41
  close(c: IStorage.CloseContext): Promise<void>;
42
+ /**
43
+ * 指定したキーに値を保存します。Blob は中間形式に変換して保存します。
44
+ *
45
+ * @param c 書き込み対象のキーを持つコンテキストです。
46
+ * @param data 保存する値です。
47
+ *
48
+ * @returns 完了を知らせるプロミスです。
49
+ */
20
50
  write(c: IStorage.WriteContext, data: any): Promise<void>;
51
+ /**
52
+ * 指定したキーの値を読み出します。中間形式は Blob または File に戻します。
53
+ *
54
+ * @param c 読み出すキーを持つコンテキストです。
55
+ *
56
+ * @returns 保存されている値です。
57
+ */
21
58
  read(c: IStorage.ReadContext): Promise<any>;
59
+ /**
60
+ * 指定したキーが存在するかどうかを返します。
61
+ *
62
+ * @param c 確認するキーを持つコンテキストです。
63
+ *
64
+ * @returns キーが存在すれば true を返します。
65
+ */
22
66
  exists(c: IStorage.ExistsContext): Promise<boolean>;
67
+ /**
68
+ * 指定したキーを削除します。
69
+ *
70
+ * @param c 削除するキーを持つコンテキストです。
71
+ *
72
+ * @returns 完了を知らせるプロミスです。
73
+ */
23
74
  delete(c: IStorage.DeleteContext): Promise<void>;
75
+ /**
76
+ * オブジェクトストアのすべての値を削除します。
77
+ *
78
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
79
+ *
80
+ * @returns 完了を知らせるプロミスです。
81
+ */
24
82
  clear(c: IStorage.ClearContext): Promise<void>;
83
+ /**
84
+ * Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。
85
+ *
86
+ * @param c 書き込み対象のキーを持つコンテキストです。
87
+ *
88
+ * @returns Uint8Array を受け付ける書き込みストリームです。
89
+ */
25
90
  getWritable(c: IStorage.GetWritableContext): WritableStream<Uint8Array>;
91
+ /**
92
+ * 保存済みの値を読み出すストリームを作成します。
93
+ *
94
+ * @param c 読み出すキーを持つコンテキストです。
95
+ *
96
+ * @returns Uint8Array を送出する読み取りストリームです。
97
+ */
26
98
  getReadable(c: IStorage.GetReadableContext): ReadableStream<Uint8Array>;
27
99
  }
28
100
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/indexeddb.ts"],"mappings":";;;;;KAMY;;;;cAuDS,qBAAqB;;WAuBxB;;;;EAKhB,YACE,iBACA,oBACA,WAAU;MASD;EAgBX,KAAkB,GAAG,SAAS,cAAc;EA8C5C,MAAmB,GAAG,SAAS,eAAe;EAc9C,MAAmB,GAAG,SAAS,cAAc,YAAY;EAmBzD,KAAkB,GAAG,SAAS,cAAc;EA6B5C,OAAoB,GAAG,SAAS,gBAAgB;EAahD,OAAoB,GAAG,SAAS,gBAAgB;EAShD,MAAmB,GAAG,SAAS,eAAe;EAS9C,YAAmB,GAAG,SAAS,qBAAqB,eAAe;EAkCnE,YAAmB,GAAG,SAAS,qBAAqB,eAAe"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/indexeddb.ts"],"mappings":";;;;;KAMY;;;;cAkGS,qBAAqB;;;;;WAIxB;;;;EA2BhB,YACE,iBACA,oBACA,WAAU;;;;;;MAcD;;;;;;;;EAWX,KAAkB,GAAG,SAAS,cAAc;;;;;;;;EAqD5C,MAAmB,GAAG,SAAS,eAAe;;;;;;;;;EAsB9C,MAAmB,GAAG,SAAS,cAAc,YAAY;;;;;;;;EA0BzD,KAAkB,GAAG,SAAS,cAAc;;;;;;;;EAoC5C,OAAoB,GAAG,SAAS,gBAAgB;;;;;;;;EAoBhD,OAAoB,GAAG,SAAS,gBAAgB;;;;;;;;EAgBhD,MAAmB,GAAG,SAAS,eAAe;;;;;;;;EAgB9C,YAAmB,GAAG,SAAS,qBAAqB,eAAe;;;;;;;;EAyCnE,YAAmB,GAAG,SAAS,qBAAqB,eAAe"}
package/dist/index.js CHANGED
@@ -31,10 +31,25 @@ function _classPrivateFieldGet2(s, a) {
31
31
  }
32
32
  //#endregion
33
33
  //#region ../src/indexeddb.ts
34
+ /**
35
+ * 保存した中間形式かどうかを判定します。
36
+ *
37
+ * @param value 判定する値です。
38
+ *
39
+ * @returns 中間形式であれば true を返します。
40
+ */
34
41
  function isStoredBlob(value) {
35
42
  if (typeof value !== "object" || value === null) return false;
36
43
  return value["unikvsBlob"] === true;
37
44
  }
45
+ /**
46
+ * Blob を IndexedDB に保存できる中間形式に変換します。File の名前と更新時刻も保持します。
47
+ *
48
+ * @param signal 中断を知らせるシグナルです。
49
+ * @param data 変換する Blob です。
50
+ *
51
+ * @returns 中間形式に変換した値です。
52
+ */
38
53
  async function toStoredBlob(signal, data) {
39
54
  const buffer = await data.arrayBuffer();
40
55
  signal.throwIfAborted();
@@ -48,6 +63,13 @@ async function toStoredBlob(signal, data) {
48
63
  lastModified: isFile ? data.lastModified : 0
49
64
  };
50
65
  }
66
+ /**
67
+ * 中間形式を Blob または File に戻します。
68
+ *
69
+ * @param stored 中間形式の値です。
70
+ *
71
+ * @returns 復元した Blob または File です。
72
+ */
51
73
  function fromStoredBlob(stored) {
52
74
  if (stored.unikvsFile) return new File([stored.buffer], stored.name, {
53
75
  type: stored.type,
@@ -79,9 +101,21 @@ var Indexeddb = class {
79
101
  _classPrivateFieldSet2(_dbName, this, dbName);
80
102
  _classPrivateFieldSet2(_storeName, this, storeName);
81
103
  }
104
+ /**
105
+ * ストレージが開いているかどうかを返します。
106
+ *
107
+ * @returns データベース接続があれば true を返します。
108
+ */
82
109
  get isOpen() {
83
110
  return _classPrivateFieldGet2(_db, this) !== null;
84
111
  }
112
+ /**
113
+ * データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。
114
+ *
115
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
116
+ *
117
+ * @returns 完了を知らせるプロミスです。
118
+ */
85
119
  async open(c) {
86
120
  c.signal.throwIfAborted();
87
121
  if (_classPrivateFieldGet2(_db, this) !== null) return;
@@ -109,6 +143,13 @@ var Indexeddb = class {
109
143
  _classPrivateFieldSet2(_opening, this, null);
110
144
  }
111
145
  }
146
+ /**
147
+ * データベース接続を閉じます。
148
+ *
149
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
150
+ *
151
+ * @returns 完了を知らせるプロミスです。
152
+ */
112
153
  async close(c) {
113
154
  c.signal.throwIfAborted();
114
155
  const db = _classPrivateFieldGet2(_db, this);
@@ -117,6 +158,14 @@ var Indexeddb = class {
117
158
  db.close();
118
159
  _classPrivateFieldSet2(_db, this, null);
119
160
  }
161
+ /**
162
+ * 指定したキーに値を保存します。Blob は中間形式に変換して保存します。
163
+ *
164
+ * @param c 書き込み対象のキーを持つコンテキストです。
165
+ * @param data 保存する値です。
166
+ *
167
+ * @returns 完了を知らせるプロミスです。
168
+ */
120
169
  async write(c, data) {
121
170
  c.signal.throwIfAborted();
122
171
  const db = _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this);
@@ -129,6 +178,13 @@ var Indexeddb = class {
129
178
  await db.put(_classPrivateFieldGet2(_storeName, this), data, c.key);
130
179
  c.signal.throwIfAborted();
131
180
  }
181
+ /**
182
+ * 指定したキーの値を読み出します。中間形式は Blob または File に戻します。
183
+ *
184
+ * @param c 読み出すキーを持つコンテキストです。
185
+ *
186
+ * @returns 保存されている値です。
187
+ */
132
188
  async read(c) {
133
189
  c.signal.throwIfAborted();
134
190
  const db = _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this);
@@ -142,22 +198,50 @@ var Indexeddb = class {
142
198
  }
143
199
  return value;
144
200
  }
201
+ /**
202
+ * 指定したキーが存在するかどうかを返します。
203
+ *
204
+ * @param c 確認するキーを持つコンテキストです。
205
+ *
206
+ * @returns キーが存在すれば true を返します。
207
+ */
145
208
  async exists(c) {
146
209
  c.signal.throwIfAborted();
147
210
  const count = await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).count(_classPrivateFieldGet2(_storeName, this), c.key);
148
211
  c.signal.throwIfAborted();
149
212
  return count > 0;
150
213
  }
214
+ /**
215
+ * 指定したキーを削除します。
216
+ *
217
+ * @param c 削除するキーを持つコンテキストです。
218
+ *
219
+ * @returns 完了を知らせるプロミスです。
220
+ */
151
221
  async delete(c) {
152
222
  c.signal.throwIfAborted();
153
223
  await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).delete(_classPrivateFieldGet2(_storeName, this), c.key);
154
224
  c.signal.throwIfAborted();
155
225
  }
226
+ /**
227
+ * オブジェクトストアのすべての値を削除します。
228
+ *
229
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
230
+ *
231
+ * @returns 完了を知らせるプロミスです。
232
+ */
156
233
  async clear(c) {
157
234
  c.signal.throwIfAborted();
158
235
  await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).clear(_classPrivateFieldGet2(_storeName, this));
159
236
  c.signal.throwIfAborted();
160
237
  }
238
+ /**
239
+ * Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。
240
+ *
241
+ * @param c 書き込み対象のキーを持つコンテキストです。
242
+ *
243
+ * @returns Uint8Array を受け付ける書き込みストリームです。
244
+ */
161
245
  getWritable(c) {
162
246
  c.signal.throwIfAborted();
163
247
  const chunks = [];
@@ -183,6 +267,13 @@ var Indexeddb = class {
183
267
  }
184
268
  });
185
269
  }
270
+ /**
271
+ * 保存済みの値を読み出すストリームを作成します。
272
+ *
273
+ * @param c 読み出すキーを持つコンテキストです。
274
+ *
275
+ * @returns Uint8Array を送出する読み取りストリームです。
276
+ */
186
277
  getReadable(c) {
187
278
  c.signal.throwIfAborted();
188
279
  return new ReadableStream({ pull: async (controller) => {
@@ -197,6 +288,11 @@ var Indexeddb = class {
197
288
  } });
198
289
  }
199
290
  };
291
+ /**
292
+ * 開いているデータベースを返します。未オープン時はエラーを投げます。
293
+ *
294
+ * @returns 開いているデータベースです。
295
+ */
200
296
  function _getDb() {
201
297
  const db = _classPrivateFieldGet2(_db, this);
202
298
  if (db === null) throw new TypeError("Indexeddb is not open. Call open() first.");
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../src/indexeddb.ts"],"sourcesContent":["import type { IStorage } from \"@unikvs/core\";\nimport { type IDBPDatabase, openDB } from \"idb\";\n\n/**\n * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)\n */\nexport type IndexeddbOptions = {};\n\n// WebKit で `Blob` を直接保存できない場合があるため、中間形式で保持します。\ntype StoredBlob = {\n readonly name: string;\n readonly type: string;\n readonly buffer: ArrayBuffer;\n readonly unikvsBlob: true;\n readonly unikvsFile: boolean;\n readonly lastModified: number;\n};\n\n// 保存した中間形式かどうかを判定します。\nfunction isStoredBlob(value: unknown): value is StoredBlob {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n return (value as Record<string, unknown>)[\"unikvsBlob\"] === true;\n}\n\n// `Blob` を IndexedDB に保存できる中間形式に変換します。\n// `File` の名前と更新時刻も保持します。\nasync function toStoredBlob(signal: AbortSignal, data: Blob): Promise<StoredBlob> {\n const buffer = await data.arrayBuffer();\n\n signal.throwIfAborted();\n\n const isFile = typeof File !== \"undefined\" && data instanceof File;\n\n return {\n name: isFile ? data.name : \"\",\n type: data.type,\n buffer,\n unikvsBlob: true,\n unikvsFile: isFile,\n lastModified: isFile ? data.lastModified : 0,\n };\n}\n\n// 中間形式を `Blob` または `File` に戻します。\nfunction fromStoredBlob(stored: StoredBlob): Blob {\n if (stored.unikvsFile) {\n return new File([stored.buffer], stored.name, {\n type: stored.type,\n lastModified: stored.lastModified,\n });\n }\n\n return new Blob([stored.buffer], { type: stored.type });\n}\n\n/**\n * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)\n */\nexport default class Indexeddb implements IStorage {\n /**\n * IndexedDB のデータベースインスタンスを保持します。\n *\n * ストレージがオープンされるまで null です。\n */\n #db: IDBPDatabase | null;\n\n /**\n * 並行 open を束ねるための進行中 Promise です。\n */\n #opening: Promise<IDBPDatabase> | null;\n\n /**\n * 使用するデータベース名です。\n */\n readonly #dbName: string;\n\n /**\n * 使用するオブジェクトストア名です。\n */\n readonly #storeName: string;\n\n public readonly name: string;\n\n /**\n * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)\n */\n public constructor(\n dbName = \"unikvs_db\",\n storeName = \"kvs_store\",\n _options: IndexeddbOptions = {},\n ) {\n this.name = \"Indexeddb\";\n this.#db = null;\n this.#opening = null;\n this.#dbName = dbName;\n this.#storeName = storeName;\n }\n\n public get isOpen(): boolean {\n return this.#db !== null;\n }\n\n // 開いているデータベースを返します。\n // 未オープン時は `TypeError` を投げます。\n #getDb(): IDBPDatabase {\n const db = this.#db;\n\n if (db === null) {\n throw new TypeError(\"Indexeddb is not open. Call open() first.\");\n }\n\n return db;\n }\n\n public async open(c: IStorage.OpenContext): Promise<void> {\n c.signal.throwIfAborted();\n\n if (this.#db !== null) {\n return;\n }\n\n if (this.#opening !== null) {\n await this.#opening;\n c.signal.throwIfAborted();\n\n return;\n }\n\n const opening = openDB(this.#dbName, 1, {\n upgrade: (upgraded) => {\n // オブジェクトストアが存在しない場合は作成します。\n if (!upgraded.objectStoreNames.contains(this.#storeName)) {\n upgraded.createObjectStore(this.#storeName);\n }\n },\n });\n this.#opening = opening;\n\n let opened: IDBPDatabase | null = null;\n\n try {\n opened = await opening;\n c.signal.throwIfAborted();\n this.#db = opened;\n opened = null;\n } catch (ex) {\n if (opened !== null) {\n try {\n opened.close();\n } catch {\n // 閉鎖時の失敗は無視します。\n }\n }\n\n throw ex;\n } finally {\n this.#opening = null;\n }\n }\n\n public async close(c: IStorage.CloseContext): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#db;\n\n if (db === null) {\n throw new TypeError(\"Indexeddb is not open. Call open() first.\");\n }\n\n await Promise.resolve();\n db.close();\n this.#db = null;\n }\n\n public async write(c: IStorage.WriteContext, data: any): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n\n if (typeof Blob !== \"undefined\" && data instanceof Blob) {\n const stored = await toStoredBlob(c.signal, data);\n await db.put(this.#storeName, stored, c.key);\n\n c.signal.throwIfAborted();\n\n return;\n }\n\n await db.put(this.#storeName, data, c.key);\n\n c.signal.throwIfAborted();\n }\n\n public async read(c: IStorage.ReadContext): Promise<any> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n const value = await db.get(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n\n if (isStoredBlob(value)) {\n return fromStoredBlob(value);\n }\n\n if (value === undefined) {\n const count = await db.count(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n\n // 存在しない場合は DOMException (NotFoundError) を投げます。\n if (count === 0) {\n throw new DOMException(\n \"A requested file or directory could not be found at the time an operation was processed.\",\n \"NotFoundError\",\n );\n }\n }\n\n return value;\n }\n\n public async exists(c: IStorage.ExistsContext): Promise<boolean> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n\n // 件数が 1 であれば存在し、0 であれば存在しません。\n const count = await db.count(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n\n return count > 0;\n }\n\n public async delete(c: IStorage.DeleteContext): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n await db.delete(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n }\n\n public async clear(c: IStorage.ClearContext): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n await db.clear(this.#storeName);\n\n c.signal.throwIfAborted();\n }\n\n public getWritable(c: IStorage.GetWritableContext): WritableStream<Uint8Array> {\n c.signal.throwIfAborted();\n\n const chunks: Uint8Array[] = [];\n\n return new WritableStream<Uint8Array>({\n abort: (): void => {\n chunks.length = 0;\n },\n close: async (): Promise<void> => {\n // 保存は close のときだけ行うため、中断時はここまでのチャンクを破棄できます。\n c.signal.throwIfAborted();\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n\n const db = this.#getDb();\n await db.put(this.#storeName, merged, c.key);\n\n c.signal.throwIfAborted();\n },\n write: (chunk): void => {\n c.signal.throwIfAborted();\n chunks.push(chunk);\n },\n });\n }\n\n public getReadable(c: IStorage.GetReadableContext): ReadableStream<Uint8Array> {\n c.signal.throwIfAborted();\n\n return new ReadableStream<Uint8Array>({\n pull: async (controller: ReadableStreamDefaultController<Uint8Array>): Promise<void> => {\n try {\n const data = await this.read(c as unknown as IStorage.ReadContext);\n\n c.signal.throwIfAborted();\n controller.enqueue(data as Uint8Array);\n controller.close();\n } catch (ex) {\n controller.error(ex);\n }\n },\n });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,SAAS,aAAa,OAAqC;CACzD,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,OAAQ,MAAkC,kBAAkB;AAC9D;AAIA,eAAe,aAAa,QAAqB,MAAiC;CAChF,MAAM,SAAS,MAAM,KAAK,YAAY;CAEtC,OAAO,eAAe;CAEtB,MAAM,SAAS,OAAO,SAAS,eAAe,gBAAgB;CAE9D,OAAO;EACL,MAAM,SAAS,KAAK,OAAO;EAC3B,MAAM,KAAK;EACX;EACA,YAAY;EACZ,YAAY;EACZ,cAAc,SAAS,KAAK,eAAe;CAC7C;AACF;AAGA,SAAS,eAAe,QAA0B;CAChD,IAAI,OAAO,YACT,OAAO,IAAI,KAAK,CAAC,OAAO,MAAM,GAAG,OAAO,MAAM;EAC5C,MAAM,OAAO;EACb,cAAc,OAAO;CACvB,CAAC;CAGH,OAAO,IAAI,KAAK,CAAC,OAAO,MAAM,GAAG,EAAE,MAAM,OAAO,KAAK,CAAC;AACxD;;;;;;;;;AAKA,IAAqB,YAArB,MAAmD;;;;CA4BjD,YACE,SAAS,aACT,YAAY,aACZ,WAA6B,CAAC,GAC9B;;;;;;EACA,KAAK,OAAO;EACZ,uBAAA,KAAA,MAAW,IAAA;EACX,uBAAA,UAAA,MAAgB,IAAA;EAChB,uBAAA,SAAA,MAAe,MAAA;EACf,uBAAA,YAAA,MAAkB,SAAA;CACpB;CAEA,IAAW,SAAkB;EAC3B,OAAA,uBAAA,KAAO,IAAA,MAAa;CACtB;CAcA,MAAa,KAAK,GAAwC;EACxD,EAAE,OAAO,eAAe;EAExB,IAAA,uBAAA,KAAI,IAAA,MAAa,MACf;EAGF,IAAA,uBAAA,UAAI,IAAA,MAAkB,MAAM;GAC1B,MAAA,uBAAA,UAAM,IAAA;GACN,EAAE,OAAO,eAAe;GAExB;EACF;EAEA,MAAM,UAAU,OAAA,uBAAA,SAAO,IAAA,GAAc,GAAG,EACtC,UAAU,aAAa;GAErB,IAAI,CAAC,SAAS,iBAAiB,SAAA,uBAAA,YAAS,IAAA,CAAe,GACrD,SAAS,kBAAA,uBAAA,YAAkB,IAAA,CAAe;EAE9C,EACF,CAAC;EACD,uBAAA,UAAA,MAAgB,OAAA;EAEhB,IAAI,SAA8B;EAElC,IAAI;GACF,SAAS,MAAM;GACf,EAAE,OAAO,eAAe;GACxB,uBAAA,KAAA,MAAW,MAAA;GACX,SAAS;EACX,SAAS,IAAI;GACX,IAAI,WAAW,MACb,IAAI;IACF,OAAO,MAAM;GACf,QAAQ,CAER;GAGF,MAAM;EACR,UAAU;GACR,uBAAA,UAAA,MAAgB,IAAA;EAClB;CACF;CAEA,MAAa,MAAM,GAAyC;EAC1D,EAAE,OAAO,eAAe;EAExB,MAAM,KAAA,uBAAA,KAAK,IAAA;EAEX,IAAI,OAAO,MACT,MAAM,IAAI,UAAU,2CAA2C;EAGjE,MAAM,QAAQ,QAAQ;EACtB,GAAG,MAAM;EACT,uBAAA,KAAA,MAAW,IAAA;CACb;CAEA,MAAa,MAAM,GAA0B,MAA0B;EACrE,EAAE,OAAO,eAAe;EAExB,MAAM,KAAA,kBAAA,kBAAK,MAAA,MAAA,CAAA,CAAA,KAAA,IAAY;EAEvB,IAAI,OAAO,SAAS,eAAe,gBAAgB,MAAM;GACvD,MAAM,SAAS,MAAM,aAAa,EAAE,QAAQ,IAAI;GAChD,MAAM,GAAG,IAAA,uBAAA,YAAI,IAAA,GAAiB,QAAQ,EAAE,GAAG;GAE3C,EAAE,OAAO,eAAe;GAExB;EACF;EAEA,MAAM,GAAG,IAAA,uBAAA,YAAI,IAAA,GAAiB,MAAM,EAAE,GAAG;EAEzC,EAAE,OAAO,eAAe;CAC1B;CAEA,MAAa,KAAK,GAAuC;EACvD,EAAE,OAAO,eAAe;EAExB,MAAM,KAAA,kBAAA,kBAAK,MAAA,MAAA,CAAA,CAAA,KAAA,IAAY;EACvB,MAAM,QAAQ,MAAM,GAAG,IAAA,uBAAA,YAAI,IAAA,GAAiB,EAAE,GAAG;EAEjD,EAAE,OAAO,eAAe;EAExB,IAAI,aAAa,KAAK,GACpB,OAAO,eAAe,KAAK;EAG7B,IAAI,UAAU,KAAA,GAAW;GACvB,MAAM,QAAQ,MAAM,GAAG,MAAA,uBAAA,YAAM,IAAA,GAAiB,EAAE,GAAG;GAEnD,EAAE,OAAO,eAAe;GAGxB,IAAI,UAAU,GACZ,MAAM,IAAI,aACR,4FACA,eACF;EAEJ;EAEA,OAAO;CACT;CAEA,MAAa,OAAO,GAA6C;EAC/D,EAAE,OAAO,eAAe;EAKxB,MAAM,QAAQ,MAAA,kBAAA,kBAHH,MAAA,MAAA,CAAA,CAAA,KAAA,IAGU,CAAC,CAAC,MAAA,uBAAA,YAAM,IAAA,GAAiB,EAAE,GAAG;EAEnD,EAAE,OAAO,eAAe;EAExB,OAAO,QAAQ;CACjB;CAEA,MAAa,OAAO,GAA0C;EAC5D,EAAE,OAAO,eAAe;EAGxB,MAAA,kBAAA,kBADW,MAAA,MAAA,CAAA,CAAA,KAAA,IACJ,CAAC,CAAC,OAAA,uBAAA,YAAO,IAAA,GAAiB,EAAE,GAAG;EAEtC,EAAE,OAAO,eAAe;CAC1B;CAEA,MAAa,MAAM,GAAyC;EAC1D,EAAE,OAAO,eAAe;EAGxB,MAAA,kBAAA,kBADW,MAAA,MAAA,CAAA,CAAA,KAAA,IACJ,CAAC,CAAC,MAAA,uBAAA,YAAM,IAAA,CAAe;EAE9B,EAAE,OAAO,eAAe;CAC1B;CAEA,YAAmB,GAA4D;EAC7E,EAAE,OAAO,eAAe;EAExB,MAAM,SAAuB,CAAC;EAE9B,OAAO,IAAI,eAA2B;GACpC,aAAmB;IACjB,OAAO,SAAS;GAClB;GACA,OAAO,YAA2B;IAEhC,EAAE,OAAO,eAAe;IAExB,MAAM,cAAc,OAAO,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;IACvE,MAAM,SAAS,IAAI,WAAW,WAAW;IACzC,IAAI,SAAS;IAEb,KAAK,MAAM,SAAS,QAAQ;KAC1B,OAAO,IAAI,OAAO,MAAM;KACxB,UAAU,MAAM;IAClB;IAGA,MAAA,kBAAA,kBADW,MAAA,MAAA,CAAA,CAAA,KAAA,IACJ,CAAC,CAAC,IAAA,uBAAA,YAAI,IAAA,GAAiB,QAAQ,EAAE,GAAG;IAE3C,EAAE,OAAO,eAAe;GAC1B;GACA,QAAQ,UAAgB;IACtB,EAAE,OAAO,eAAe;IACxB,OAAO,KAAK,KAAK;GACnB;EACF,CAAC;CACH;CAEA,YAAmB,GAA4D;EAC7E,EAAE,OAAO,eAAe;EAExB,OAAO,IAAI,eAA2B,EACpC,MAAM,OAAO,eAA2E;GACtF,IAAI;IACF,MAAM,OAAO,MAAM,KAAK,KAAK,CAAoC;IAEjE,EAAE,OAAO,eAAe;IACxB,WAAW,QAAQ,IAAkB;IACrC,WAAW,MAAM;GACnB,SAAS,IAAI;IACX,WAAW,MAAM,EAAE;GACrB;EACF,EACF,CAAC;CACH;AACF;AAxME,SAAA,SAAuB;CACrB,MAAM,KAAA,uBAAA,KAAK,IAAA;CAEX,IAAI,OAAO,MACT,MAAM,IAAI,UAAU,2CAA2C;CAGjE,OAAO;AACT"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../src/indexeddb.ts"],"sourcesContent":["import type { IStorage } from \"@unikvs/core\";\nimport { type IDBPDatabase, openDB } from \"idb\";\n\n/**\n * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)\n */\nexport type IndexeddbOptions = {};\n\n/**\n * WebKit で Blob を直接保存できない場合があるため、中間形式で保持します。\n */\ntype StoredBlob = {\n /**\n * File のときに保持するファイル名です。\n */\n readonly name: string;\n\n /**\n * 元の Blob の MIME 種別です。\n */\n readonly type: string;\n\n /**\n * Blob のバイト列です。\n */\n readonly buffer: ArrayBuffer;\n\n /**\n * 中間形式であることを示す目印です。\n */\n readonly unikvsBlob: true;\n\n /**\n * File 由来かどうかを示す目印です。\n */\n readonly unikvsFile: boolean;\n\n /**\n * File のときに保持する更新時刻です。\n */\n readonly lastModified: number;\n};\n\n/**\n * 保存した中間形式かどうかを判定します。\n *\n * @param value 判定する値です。\n *\n * @returns 中間形式であれば true を返します。\n */\nfunction isStoredBlob(value: unknown): value is StoredBlob {\n if (typeof value !== \"object\" || value === null) {\n return false;\n }\n\n return (value as Record<string, unknown>)[\"unikvsBlob\"] === true;\n}\n\n/**\n * Blob を IndexedDB に保存できる中間形式に変換します。File の名前と更新時刻も保持します。\n *\n * @param signal 中断を知らせるシグナルです。\n * @param data 変換する Blob です。\n *\n * @returns 中間形式に変換した値です。\n */\nasync function toStoredBlob(signal: AbortSignal, data: Blob): Promise<StoredBlob> {\n const buffer = await data.arrayBuffer();\n\n signal.throwIfAborted();\n\n const isFile = typeof File !== \"undefined\" && data instanceof File;\n\n return {\n name: isFile ? data.name : \"\",\n type: data.type,\n buffer,\n unikvsBlob: true,\n unikvsFile: isFile,\n lastModified: isFile ? data.lastModified : 0,\n };\n}\n\n/**\n * 中間形式を Blob または File に戻します。\n *\n * @param stored 中間形式の値です。\n *\n * @returns 復元した Blob または File です。\n */\nfunction fromStoredBlob(stored: StoredBlob): Blob {\n if (stored.unikvsFile) {\n return new File([stored.buffer], stored.name, {\n type: stored.type,\n lastModified: stored.lastModified,\n });\n }\n\n return new Blob([stored.buffer], { type: stored.type });\n}\n\n/**\n * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)\n */\nexport default class Indexeddb implements IStorage {\n /**\n * ストレージ名です。常に `Indexeddb` を返します。\n */\n public readonly name: string;\n\n /**\n * IndexedDB のデータベースインスタンスを保持します。\n *\n * ストレージがオープンされるまで null です。\n */\n #db: IDBPDatabase | null;\n\n /**\n * 並行 open を束ねるための進行中 Promise です。\n */\n #opening: Promise<IDBPDatabase> | null;\n\n /**\n * 使用するデータベース名です。\n */\n readonly #dbName: string;\n\n /**\n * 使用するオブジェクトストア名です。\n */\n readonly #storeName: string;\n\n /**\n * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)\n */\n public constructor(\n dbName = \"unikvs_db\",\n storeName = \"kvs_store\",\n _options: IndexeddbOptions = {},\n ) {\n this.name = \"Indexeddb\";\n this.#db = null;\n this.#opening = null;\n this.#dbName = dbName;\n this.#storeName = storeName;\n }\n\n /**\n * ストレージが開いているかどうかを返します。\n *\n * @returns データベース接続があれば true を返します。\n */\n public get isOpen(): boolean {\n return this.#db !== null;\n }\n\n /**\n * データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。\n *\n * @param c 中断を知らせるシグナルを持つコンテキストです。\n *\n * @returns 完了を知らせるプロミスです。\n */\n public async open(c: IStorage.OpenContext): Promise<void> {\n c.signal.throwIfAborted();\n\n if (this.#db !== null) {\n return;\n }\n\n if (this.#opening !== null) {\n await this.#opening;\n c.signal.throwIfAborted();\n\n return;\n }\n\n const opening = openDB(this.#dbName, 1, {\n upgrade: (upgraded) => {\n // オブジェクトストアが存在しない場合は作成します。\n if (!upgraded.objectStoreNames.contains(this.#storeName)) {\n upgraded.createObjectStore(this.#storeName);\n }\n },\n });\n this.#opening = opening;\n\n let opened: IDBPDatabase | null = null;\n\n try {\n opened = await opening;\n c.signal.throwIfAborted();\n this.#db = opened;\n opened = null;\n } catch (ex) {\n if (opened !== null) {\n try {\n opened.close();\n } catch {\n // 閉鎖時の失敗は無視します。\n }\n }\n\n throw ex;\n } finally {\n this.#opening = null;\n }\n }\n\n /**\n * データベース接続を閉じます。\n *\n * @param c 中断を知らせるシグナルを持つコンテキストです。\n *\n * @returns 完了を知らせるプロミスです。\n */\n public async close(c: IStorage.CloseContext): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#db;\n\n if (db === null) {\n throw new TypeError(\"Indexeddb is not open. Call open() first.\");\n }\n\n await Promise.resolve();\n db.close();\n this.#db = null;\n }\n\n /**\n * 指定したキーに値を保存します。Blob は中間形式に変換して保存します。\n *\n * @param c 書き込み対象のキーを持つコンテキストです。\n * @param data 保存する値です。\n *\n * @returns 完了を知らせるプロミスです。\n */\n public async write(c: IStorage.WriteContext, data: any): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n\n if (typeof Blob !== \"undefined\" && data instanceof Blob) {\n const stored = await toStoredBlob(c.signal, data);\n await db.put(this.#storeName, stored, c.key);\n\n c.signal.throwIfAborted();\n\n return;\n }\n\n await db.put(this.#storeName, data, c.key);\n\n c.signal.throwIfAborted();\n }\n\n /**\n * 指定したキーの値を読み出します。中間形式は Blob または File に戻します。\n *\n * @param c 読み出すキーを持つコンテキストです。\n *\n * @returns 保存されている値です。\n */\n public async read(c: IStorage.ReadContext): Promise<any> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n const value = await db.get(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n\n if (isStoredBlob(value)) {\n return fromStoredBlob(value);\n }\n\n if (value === undefined) {\n const count = await db.count(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n\n // 存在しない場合は DOMException (NotFoundError) を投げます。\n if (count === 0) {\n throw new DOMException(\n \"A requested file or directory could not be found at the time an operation was processed.\",\n \"NotFoundError\",\n );\n }\n }\n\n return value;\n }\n\n /**\n * 指定したキーが存在するかどうかを返します。\n *\n * @param c 確認するキーを持つコンテキストです。\n *\n * @returns キーが存在すれば true を返します。\n */\n public async exists(c: IStorage.ExistsContext): Promise<boolean> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n\n // 件数が 1 であれば存在し、0 であれば存在しません。\n const count = await db.count(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n\n return count > 0;\n }\n\n /**\n * 指定したキーを削除します。\n *\n * @param c 削除するキーを持つコンテキストです。\n *\n * @returns 完了を知らせるプロミスです。\n */\n public async delete(c: IStorage.DeleteContext): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n await db.delete(this.#storeName, c.key);\n\n c.signal.throwIfAborted();\n }\n\n /**\n * オブジェクトストアのすべての値を削除します。\n *\n * @param c 中断を知らせるシグナルを持つコンテキストです。\n *\n * @returns 完了を知らせるプロミスです。\n */\n public async clear(c: IStorage.ClearContext): Promise<void> {\n c.signal.throwIfAborted();\n\n const db = this.#getDb();\n await db.clear(this.#storeName);\n\n c.signal.throwIfAborted();\n }\n\n /**\n * Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。\n *\n * @param c 書き込み対象のキーを持つコンテキストです。\n *\n * @returns Uint8Array を受け付ける書き込みストリームです。\n */\n public getWritable(c: IStorage.GetWritableContext): WritableStream<Uint8Array> {\n c.signal.throwIfAborted();\n\n const chunks: Uint8Array[] = [];\n\n return new WritableStream<Uint8Array>({\n abort: (): void => {\n chunks.length = 0;\n },\n close: async (): Promise<void> => {\n // 保存は close のときだけ行うため、中断時はここまでのチャンクを破棄できます。\n c.signal.throwIfAborted();\n\n const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);\n const merged = new Uint8Array(totalLength);\n let offset = 0;\n\n for (const chunk of chunks) {\n merged.set(chunk, offset);\n offset += chunk.length;\n }\n\n const db = this.#getDb();\n await db.put(this.#storeName, merged, c.key);\n\n c.signal.throwIfAborted();\n },\n write: (chunk): void => {\n c.signal.throwIfAborted();\n chunks.push(chunk);\n },\n });\n }\n\n /**\n * 保存済みの値を読み出すストリームを作成します。\n *\n * @param c 読み出すキーを持つコンテキストです。\n *\n * @returns Uint8Array を送出する読み取りストリームです。\n */\n public getReadable(c: IStorage.GetReadableContext): ReadableStream<Uint8Array> {\n c.signal.throwIfAborted();\n\n return new ReadableStream<Uint8Array>({\n pull: async (controller: ReadableStreamDefaultController<Uint8Array>): Promise<void> => {\n try {\n const data = await this.read(c as unknown as IStorage.ReadContext);\n\n c.signal.throwIfAborted();\n controller.enqueue(data as Uint8Array);\n controller.close();\n } catch (ex) {\n controller.error(ex);\n }\n },\n });\n }\n\n /**\n * 開いているデータベースを返します。未オープン時はエラーを投げます。\n *\n * @returns 開いているデータベースです。\n */\n #getDb(): IDBPDatabase {\n const db = this.#db;\n\n if (db === null) {\n throw new TypeError(\"Indexeddb is not open. Call open() first.\");\n }\n\n return db;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkDA,SAAS,aAAa,OAAqC;CACzD,IAAI,OAAO,UAAU,YAAY,UAAU,MACzC,OAAO;CAGT,OAAQ,MAAkC,kBAAkB;AAC9D;;;;;;;;;AAUA,eAAe,aAAa,QAAqB,MAAiC;CAChF,MAAM,SAAS,MAAM,KAAK,YAAY;CAEtC,OAAO,eAAe;CAEtB,MAAM,SAAS,OAAO,SAAS,eAAe,gBAAgB;CAE9D,OAAO;EACL,MAAM,SAAS,KAAK,OAAO;EAC3B,MAAM,KAAK;EACX;EACA,YAAY;EACZ,YAAY;EACZ,cAAc,SAAS,KAAK,eAAe;CAC7C;AACF;;;;;;;;AASA,SAAS,eAAe,QAA0B;CAChD,IAAI,OAAO,YACT,OAAO,IAAI,KAAK,CAAC,OAAO,MAAM,GAAG,OAAO,MAAM;EAC5C,MAAM,OAAO;EACb,cAAc,OAAO;CACvB,CAAC;CAGH,OAAO,IAAI,KAAK,CAAC,OAAO,MAAM,GAAG,EAAE,MAAM,OAAO,KAAK,CAAC;AACxD;;;;;;;;;AAKA,IAAqB,YAArB,MAAmD;;;;CA+BjD,YACE,SAAS,aACT,YAAY,aACZ,WAA6B,CAAC,GAC9B;;;;;;EACA,KAAK,OAAO;EACZ,uBAAA,KAAA,MAAW,IAAA;EACX,uBAAA,UAAA,MAAgB,IAAA;EAChB,uBAAA,SAAA,MAAe,MAAA;EACf,uBAAA,YAAA,MAAkB,SAAA;CACpB;;;;;;CAOA,IAAW,SAAkB;EAC3B,OAAA,uBAAA,KAAO,IAAA,MAAa;CACtB;;;;;;;;CASA,MAAa,KAAK,GAAwC;EACxD,EAAE,OAAO,eAAe;EAExB,IAAA,uBAAA,KAAI,IAAA,MAAa,MACf;EAGF,IAAA,uBAAA,UAAI,IAAA,MAAkB,MAAM;GAC1B,MAAA,uBAAA,UAAM,IAAA;GACN,EAAE,OAAO,eAAe;GAExB;EACF;EAEA,MAAM,UAAU,OAAA,uBAAA,SAAO,IAAA,GAAc,GAAG,EACtC,UAAU,aAAa;GAErB,IAAI,CAAC,SAAS,iBAAiB,SAAA,uBAAA,YAAS,IAAA,CAAe,GACrD,SAAS,kBAAA,uBAAA,YAAkB,IAAA,CAAe;EAE9C,EACF,CAAC;EACD,uBAAA,UAAA,MAAgB,OAAA;EAEhB,IAAI,SAA8B;EAElC,IAAI;GACF,SAAS,MAAM;GACf,EAAE,OAAO,eAAe;GACxB,uBAAA,KAAA,MAAW,MAAA;GACX,SAAS;EACX,SAAS,IAAI;GACX,IAAI,WAAW,MACb,IAAI;IACF,OAAO,MAAM;GACf,QAAQ,CAER;GAGF,MAAM;EACR,UAAU;GACR,uBAAA,UAAA,MAAgB,IAAA;EAClB;CACF;;;;;;;;CASA,MAAa,MAAM,GAAyC;EAC1D,EAAE,OAAO,eAAe;EAExB,MAAM,KAAA,uBAAA,KAAK,IAAA;EAEX,IAAI,OAAO,MACT,MAAM,IAAI,UAAU,2CAA2C;EAGjE,MAAM,QAAQ,QAAQ;EACtB,GAAG,MAAM;EACT,uBAAA,KAAA,MAAW,IAAA;CACb;;;;;;;;;CAUA,MAAa,MAAM,GAA0B,MAA0B;EACrE,EAAE,OAAO,eAAe;EAExB,MAAM,KAAA,kBAAA,kBAAK,MAAA,MAAA,CAAA,CAAA,KAAA,IAAY;EAEvB,IAAI,OAAO,SAAS,eAAe,gBAAgB,MAAM;GACvD,MAAM,SAAS,MAAM,aAAa,EAAE,QAAQ,IAAI;GAChD,MAAM,GAAG,IAAA,uBAAA,YAAI,IAAA,GAAiB,QAAQ,EAAE,GAAG;GAE3C,EAAE,OAAO,eAAe;GAExB;EACF;EAEA,MAAM,GAAG,IAAA,uBAAA,YAAI,IAAA,GAAiB,MAAM,EAAE,GAAG;EAEzC,EAAE,OAAO,eAAe;CAC1B;;;;;;;;CASA,MAAa,KAAK,GAAuC;EACvD,EAAE,OAAO,eAAe;EAExB,MAAM,KAAA,kBAAA,kBAAK,MAAA,MAAA,CAAA,CAAA,KAAA,IAAY;EACvB,MAAM,QAAQ,MAAM,GAAG,IAAA,uBAAA,YAAI,IAAA,GAAiB,EAAE,GAAG;EAEjD,EAAE,OAAO,eAAe;EAExB,IAAI,aAAa,KAAK,GACpB,OAAO,eAAe,KAAK;EAG7B,IAAI,UAAU,KAAA,GAAW;GACvB,MAAM,QAAQ,MAAM,GAAG,MAAA,uBAAA,YAAM,IAAA,GAAiB,EAAE,GAAG;GAEnD,EAAE,OAAO,eAAe;GAGxB,IAAI,UAAU,GACZ,MAAM,IAAI,aACR,4FACA,eACF;EAEJ;EAEA,OAAO;CACT;;;;;;;;CASA,MAAa,OAAO,GAA6C;EAC/D,EAAE,OAAO,eAAe;EAKxB,MAAM,QAAQ,MAAA,kBAAA,kBAHH,MAAA,MAAA,CAAA,CAAA,KAAA,IAGU,CAAC,CAAC,MAAA,uBAAA,YAAM,IAAA,GAAiB,EAAE,GAAG;EAEnD,EAAE,OAAO,eAAe;EAExB,OAAO,QAAQ;CACjB;;;;;;;;CASA,MAAa,OAAO,GAA0C;EAC5D,EAAE,OAAO,eAAe;EAGxB,MAAA,kBAAA,kBADW,MAAA,MAAA,CAAA,CAAA,KAAA,IACJ,CAAC,CAAC,OAAA,uBAAA,YAAO,IAAA,GAAiB,EAAE,GAAG;EAEtC,EAAE,OAAO,eAAe;CAC1B;;;;;;;;CASA,MAAa,MAAM,GAAyC;EAC1D,EAAE,OAAO,eAAe;EAGxB,MAAA,kBAAA,kBADW,MAAA,MAAA,CAAA,CAAA,KAAA,IACJ,CAAC,CAAC,MAAA,uBAAA,YAAM,IAAA,CAAe;EAE9B,EAAE,OAAO,eAAe;CAC1B;;;;;;;;CASA,YAAmB,GAA4D;EAC7E,EAAE,OAAO,eAAe;EAExB,MAAM,SAAuB,CAAC;EAE9B,OAAO,IAAI,eAA2B;GACpC,aAAmB;IACjB,OAAO,SAAS;GAClB;GACA,OAAO,YAA2B;IAEhC,EAAE,OAAO,eAAe;IAExB,MAAM,cAAc,OAAO,QAAQ,KAAK,UAAU,MAAM,MAAM,QAAQ,CAAC;IACvE,MAAM,SAAS,IAAI,WAAW,WAAW;IACzC,IAAI,SAAS;IAEb,KAAK,MAAM,SAAS,QAAQ;KAC1B,OAAO,IAAI,OAAO,MAAM;KACxB,UAAU,MAAM;IAClB;IAGA,MAAA,kBAAA,kBADW,MAAA,MAAA,CAAA,CAAA,KAAA,IACJ,CAAC,CAAC,IAAA,uBAAA,YAAI,IAAA,GAAiB,QAAQ,EAAE,GAAG;IAE3C,EAAE,OAAO,eAAe;GAC1B;GACA,QAAQ,UAAgB;IACtB,EAAE,OAAO,eAAe;IACxB,OAAO,KAAK,KAAK;GACnB;EACF,CAAC;CACH;;;;;;;;CASA,YAAmB,GAA4D;EAC7E,EAAE,OAAO,eAAe;EAExB,OAAO,IAAI,eAA2B,EACpC,MAAM,OAAO,eAA2E;GACtF,IAAI;IACF,MAAM,OAAO,MAAM,KAAK,KAAK,CAAoC;IAEjE,EAAE,OAAO,eAAe;IACxB,WAAW,QAAQ,IAAkB;IACrC,WAAW,MAAM;GACnB,SAAS,IAAI;IACX,WAAW,MAAM,EAAE;GACrB;EACF,EACF,CAAC;CACH;AAgBF;;;;;;AATE,SAAA,SAAuB;CACrB,MAAM,KAAA,uBAAA,KAAK,IAAA;CAEX,IAAI,OAAO,MACT,MAAM,IAAI,UAAU,2CAA2C;CAGjE,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unikvs/indexeddb",
3
- "version": "0.0.13",
3
+ "version": "0.0.14",
4
4
  "description": "Storage plugin that stores arbitrary data in the browser IndexedDB.",
5
5
  "homepage": "https://tai-kun.github.io/unikvs/",
6
6
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  }
25
25
  },
26
26
  "dependencies": {
27
- "@unikvs/core": "0.0.13",
27
+ "@unikvs/core": "0.0.14",
28
28
  "idb": "^8.0.4"
29
29
  },
30
30
  "devDependencies": {
package/src/indexeddb.ts CHANGED
@@ -6,17 +6,48 @@ import { type IDBPDatabase, openDB } from "idb";
6
6
  */
7
7
  export type IndexeddbOptions = {};
8
8
 
9
- // WebKit で `Blob` を直接保存できない場合があるため、中間形式で保持します。
9
+ /**
10
+ * WebKit で Blob を直接保存できない場合があるため、中間形式で保持します。
11
+ */
10
12
  type StoredBlob = {
13
+ /**
14
+ * File のときに保持するファイル名です。
15
+ */
11
16
  readonly name: string;
17
+
18
+ /**
19
+ * 元の Blob の MIME 種別です。
20
+ */
12
21
  readonly type: string;
22
+
23
+ /**
24
+ * Blob のバイト列です。
25
+ */
13
26
  readonly buffer: ArrayBuffer;
27
+
28
+ /**
29
+ * 中間形式であることを示す目印です。
30
+ */
14
31
  readonly unikvsBlob: true;
32
+
33
+ /**
34
+ * File 由来かどうかを示す目印です。
35
+ */
15
36
  readonly unikvsFile: boolean;
37
+
38
+ /**
39
+ * File のときに保持する更新時刻です。
40
+ */
16
41
  readonly lastModified: number;
17
42
  };
18
43
 
19
- // 保存した中間形式かどうかを判定します。
44
+ /**
45
+ * 保存した中間形式かどうかを判定します。
46
+ *
47
+ * @param value 判定する値です。
48
+ *
49
+ * @returns 中間形式であれば true を返します。
50
+ */
20
51
  function isStoredBlob(value: unknown): value is StoredBlob {
21
52
  if (typeof value !== "object" || value === null) {
22
53
  return false;
@@ -25,8 +56,14 @@ function isStoredBlob(value: unknown): value is StoredBlob {
25
56
  return (value as Record<string, unknown>)["unikvsBlob"] === true;
26
57
  }
27
58
 
28
- // `Blob` を IndexedDB に保存できる中間形式に変換します。
29
- // `File` の名前と更新時刻も保持します。
59
+ /**
60
+ * Blob を IndexedDB に保存できる中間形式に変換します。File の名前と更新時刻も保持します。
61
+ *
62
+ * @param signal 中断を知らせるシグナルです。
63
+ * @param data 変換する Blob です。
64
+ *
65
+ * @returns 中間形式に変換した値です。
66
+ */
30
67
  async function toStoredBlob(signal: AbortSignal, data: Blob): Promise<StoredBlob> {
31
68
  const buffer = await data.arrayBuffer();
32
69
 
@@ -44,7 +81,13 @@ async function toStoredBlob(signal: AbortSignal, data: Blob): Promise<StoredBlob
44
81
  };
45
82
  }
46
83
 
47
- // 中間形式を `Blob` または `File` に戻します。
84
+ /**
85
+ * 中間形式を Blob または File に戻します。
86
+ *
87
+ * @param stored 中間形式の値です。
88
+ *
89
+ * @returns 復元した Blob または File です。
90
+ */
48
91
  function fromStoredBlob(stored: StoredBlob): Blob {
49
92
  if (stored.unikvsFile) {
50
93
  return new File([stored.buffer], stored.name, {
@@ -60,6 +103,11 @@ function fromStoredBlob(stored: StoredBlob): Blob {
60
103
  * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
61
104
  */
62
105
  export default class Indexeddb implements IStorage {
106
+ /**
107
+ * ストレージ名です。常に `Indexeddb` を返します。
108
+ */
109
+ public readonly name: string;
110
+
63
111
  /**
64
112
  * IndexedDB のデータベースインスタンスを保持します。
65
113
  *
@@ -82,8 +130,6 @@ export default class Indexeddb implements IStorage {
82
130
  */
83
131
  readonly #storeName: string;
84
132
 
85
- public readonly name: string;
86
-
87
133
  /**
88
134
  * [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
89
135
  */
@@ -99,22 +145,22 @@ export default class Indexeddb implements IStorage {
99
145
  this.#storeName = storeName;
100
146
  }
101
147
 
148
+ /**
149
+ * ストレージが開いているかどうかを返します。
150
+ *
151
+ * @returns データベース接続があれば true を返します。
152
+ */
102
153
  public get isOpen(): boolean {
103
154
  return this.#db !== null;
104
155
  }
105
156
 
106
- // 開いているデータベースを返します。
107
- // 未オープン時は `TypeError` を投げます。
108
- #getDb(): IDBPDatabase {
109
- const db = this.#db;
110
-
111
- if (db === null) {
112
- throw new TypeError("Indexeddb is not open. Call open() first.");
113
- }
114
-
115
- return db;
116
- }
117
-
157
+ /**
158
+ * データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。
159
+ *
160
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
161
+ *
162
+ * @returns 完了を知らせるプロミスです。
163
+ */
118
164
  public async open(c: IStorage.OpenContext): Promise<void> {
119
165
  c.signal.throwIfAborted();
120
166
 
@@ -161,6 +207,13 @@ export default class Indexeddb implements IStorage {
161
207
  }
162
208
  }
163
209
 
210
+ /**
211
+ * データベース接続を閉じます。
212
+ *
213
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
214
+ *
215
+ * @returns 完了を知らせるプロミスです。
216
+ */
164
217
  public async close(c: IStorage.CloseContext): Promise<void> {
165
218
  c.signal.throwIfAborted();
166
219
 
@@ -175,6 +228,14 @@ export default class Indexeddb implements IStorage {
175
228
  this.#db = null;
176
229
  }
177
230
 
231
+ /**
232
+ * 指定したキーに値を保存します。Blob は中間形式に変換して保存します。
233
+ *
234
+ * @param c 書き込み対象のキーを持つコンテキストです。
235
+ * @param data 保存する値です。
236
+ *
237
+ * @returns 完了を知らせるプロミスです。
238
+ */
178
239
  public async write(c: IStorage.WriteContext, data: any): Promise<void> {
179
240
  c.signal.throwIfAborted();
180
241
 
@@ -194,6 +255,13 @@ export default class Indexeddb implements IStorage {
194
255
  c.signal.throwIfAborted();
195
256
  }
196
257
 
258
+ /**
259
+ * 指定したキーの値を読み出します。中間形式は Blob または File に戻します。
260
+ *
261
+ * @param c 読み出すキーを持つコンテキストです。
262
+ *
263
+ * @returns 保存されている値です。
264
+ */
197
265
  public async read(c: IStorage.ReadContext): Promise<any> {
198
266
  c.signal.throwIfAborted();
199
267
 
@@ -223,6 +291,13 @@ export default class Indexeddb implements IStorage {
223
291
  return value;
224
292
  }
225
293
 
294
+ /**
295
+ * 指定したキーが存在するかどうかを返します。
296
+ *
297
+ * @param c 確認するキーを持つコンテキストです。
298
+ *
299
+ * @returns キーが存在すれば true を返します。
300
+ */
226
301
  public async exists(c: IStorage.ExistsContext): Promise<boolean> {
227
302
  c.signal.throwIfAborted();
228
303
 
@@ -236,6 +311,13 @@ export default class Indexeddb implements IStorage {
236
311
  return count > 0;
237
312
  }
238
313
 
314
+ /**
315
+ * 指定したキーを削除します。
316
+ *
317
+ * @param c 削除するキーを持つコンテキストです。
318
+ *
319
+ * @returns 完了を知らせるプロミスです。
320
+ */
239
321
  public async delete(c: IStorage.DeleteContext): Promise<void> {
240
322
  c.signal.throwIfAborted();
241
323
 
@@ -245,6 +327,13 @@ export default class Indexeddb implements IStorage {
245
327
  c.signal.throwIfAborted();
246
328
  }
247
329
 
330
+ /**
331
+ * オブジェクトストアのすべての値を削除します。
332
+ *
333
+ * @param c 中断を知らせるシグナルを持つコンテキストです。
334
+ *
335
+ * @returns 完了を知らせるプロミスです。
336
+ */
248
337
  public async clear(c: IStorage.ClearContext): Promise<void> {
249
338
  c.signal.throwIfAborted();
250
339
 
@@ -254,6 +343,13 @@ export default class Indexeddb implements IStorage {
254
343
  c.signal.throwIfAborted();
255
344
  }
256
345
 
346
+ /**
347
+ * Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。
348
+ *
349
+ * @param c 書き込み対象のキーを持つコンテキストです。
350
+ *
351
+ * @returns Uint8Array を受け付ける書き込みストリームです。
352
+ */
257
353
  public getWritable(c: IStorage.GetWritableContext): WritableStream<Uint8Array> {
258
354
  c.signal.throwIfAborted();
259
355
 
@@ -288,6 +384,13 @@ export default class Indexeddb implements IStorage {
288
384
  });
289
385
  }
290
386
 
387
+ /**
388
+ * 保存済みの値を読み出すストリームを作成します。
389
+ *
390
+ * @param c 読み出すキーを持つコンテキストです。
391
+ *
392
+ * @returns Uint8Array を送出する読み取りストリームです。
393
+ */
291
394
  public getReadable(c: IStorage.GetReadableContext): ReadableStream<Uint8Array> {
292
395
  c.signal.throwIfAborted();
293
396
 
@@ -305,4 +408,19 @@ export default class Indexeddb implements IStorage {
305
408
  },
306
409
  });
307
410
  }
411
+
412
+ /**
413
+ * 開いているデータベースを返します。未オープン時はエラーを投げます。
414
+ *
415
+ * @returns 開いているデータベースです。
416
+ */
417
+ #getDb(): IDBPDatabase {
418
+ const db = this.#db;
419
+
420
+ if (db === null) {
421
+ throw new TypeError("Indexeddb is not open. Call open() first.");
422
+ }
423
+
424
+ return db;
425
+ }
308
426
  }