@unikvs/indexeddb 0.0.12 → 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/README.md +1 -1
- package/dist/index.d.ts +102 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +304 -0
- package/dist/index.js.map +1 -0
- package/package.json +18 -23
- package/src/index.ts +2 -0
- package/src/indexeddb.ts +333 -71
- package/dist/src/indexeddb.d.ts +0 -69
- package/dist/src/indexeddb.d.ts.map +0 -1
- package/dist/src/indexeddb.js +0 -119
package/README.md
CHANGED
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { IStorage } from "@unikvs/core";
|
|
2
|
+
//#region ../src/indexeddb.d.ts
|
|
3
|
+
/**
|
|
4
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
5
|
+
*/
|
|
6
|
+
type IndexeddbOptions = {};
|
|
7
|
+
/**
|
|
8
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
9
|
+
*/
|
|
10
|
+
declare class Indexeddb implements IStorage {
|
|
11
|
+
#private;
|
|
12
|
+
/**
|
|
13
|
+
* ストレージ名です。常に `Indexeddb` を返します。
|
|
14
|
+
*/
|
|
15
|
+
readonly name: string;
|
|
16
|
+
/**
|
|
17
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
18
|
+
*/
|
|
19
|
+
constructor(dbName?: string, storeName?: string, _options?: IndexeddbOptions);
|
|
20
|
+
/**
|
|
21
|
+
* ストレージが開いているかどうかを返します。
|
|
22
|
+
*
|
|
23
|
+
* @returns データベース接続があれば true を返します。
|
|
24
|
+
*/
|
|
25
|
+
get isOpen(): boolean;
|
|
26
|
+
/**
|
|
27
|
+
* データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。
|
|
28
|
+
*
|
|
29
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
30
|
+
*
|
|
31
|
+
* @returns 完了を知らせるプロミスです。
|
|
32
|
+
*/
|
|
33
|
+
open(c: IStorage.OpenContext): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* データベース接続を閉じます。
|
|
36
|
+
*
|
|
37
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
38
|
+
*
|
|
39
|
+
* @returns 完了を知らせるプロミスです。
|
|
40
|
+
*/
|
|
41
|
+
close(c: IStorage.CloseContext): Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* 指定したキーに値を保存します。Blob は中間形式に変換して保存します。
|
|
44
|
+
*
|
|
45
|
+
* @param c 書き込み対象のキーを持つコンテキストです。
|
|
46
|
+
* @param data 保存する値です。
|
|
47
|
+
*
|
|
48
|
+
* @returns 完了を知らせるプロミスです。
|
|
49
|
+
*/
|
|
50
|
+
write(c: IStorage.WriteContext, data: any): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* 指定したキーの値を読み出します。中間形式は Blob または File に戻します。
|
|
53
|
+
*
|
|
54
|
+
* @param c 読み出すキーを持つコンテキストです。
|
|
55
|
+
*
|
|
56
|
+
* @returns 保存されている値です。
|
|
57
|
+
*/
|
|
58
|
+
read(c: IStorage.ReadContext): Promise<any>;
|
|
59
|
+
/**
|
|
60
|
+
* 指定したキーが存在するかどうかを返します。
|
|
61
|
+
*
|
|
62
|
+
* @param c 確認するキーを持つコンテキストです。
|
|
63
|
+
*
|
|
64
|
+
* @returns キーが存在すれば true を返します。
|
|
65
|
+
*/
|
|
66
|
+
exists(c: IStorage.ExistsContext): Promise<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* 指定したキーを削除します。
|
|
69
|
+
*
|
|
70
|
+
* @param c 削除するキーを持つコンテキストです。
|
|
71
|
+
*
|
|
72
|
+
* @returns 完了を知らせるプロミスです。
|
|
73
|
+
*/
|
|
74
|
+
delete(c: IStorage.DeleteContext): Promise<void>;
|
|
75
|
+
/**
|
|
76
|
+
* オブジェクトストアのすべての値を削除します。
|
|
77
|
+
*
|
|
78
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
79
|
+
*
|
|
80
|
+
* @returns 完了を知らせるプロミスです。
|
|
81
|
+
*/
|
|
82
|
+
clear(c: IStorage.ClearContext): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。
|
|
85
|
+
*
|
|
86
|
+
* @param c 書き込み対象のキーを持つコンテキストです。
|
|
87
|
+
*
|
|
88
|
+
* @returns Uint8Array を受け付ける書き込みストリームです。
|
|
89
|
+
*/
|
|
90
|
+
getWritable(c: IStorage.GetWritableContext): WritableStream<Uint8Array>;
|
|
91
|
+
/**
|
|
92
|
+
* 保存済みの値を読み出すストリームを作成します。
|
|
93
|
+
*
|
|
94
|
+
* @param c 読み出すキーを持つコンテキストです。
|
|
95
|
+
*
|
|
96
|
+
* @returns Uint8Array を送出する読み取りストリームです。
|
|
97
|
+
*/
|
|
98
|
+
getReadable(c: IStorage.GetReadableContext): ReadableStream<Uint8Array>;
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
101
|
+
export { Indexeddb, type IndexeddbOptions };
|
|
102
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
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
ADDED
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { openDB } from "idb";
|
|
2
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/checkPrivateRedeclaration.js
|
|
3
|
+
function _checkPrivateRedeclaration(e, t) {
|
|
4
|
+
if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object");
|
|
5
|
+
}
|
|
6
|
+
//#endregion
|
|
7
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/classPrivateMethodInitSpec.js
|
|
8
|
+
function _classPrivateMethodInitSpec(e, a) {
|
|
9
|
+
_checkPrivateRedeclaration(e, a), a.add(e);
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/classPrivateFieldInitSpec.js
|
|
13
|
+
function _classPrivateFieldInitSpec(e, t, a) {
|
|
14
|
+
_checkPrivateRedeclaration(e, t), t.set(e, a);
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/assertClassBrand.js
|
|
18
|
+
function _assertClassBrand(e, t, n) {
|
|
19
|
+
if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n;
|
|
20
|
+
throw new TypeError("Private element is not present on this object");
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/classPrivateFieldSet2.js
|
|
24
|
+
function _classPrivateFieldSet2(s, a, r) {
|
|
25
|
+
return s.set(_assertClassBrand(s, a), r), r;
|
|
26
|
+
}
|
|
27
|
+
//#endregion
|
|
28
|
+
//#region \0@oxc-project+runtime@0.154.0/helpers/esm/classPrivateFieldGet2.js
|
|
29
|
+
function _classPrivateFieldGet2(s, a) {
|
|
30
|
+
return s.get(_assertClassBrand(s, a));
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region ../src/indexeddb.ts
|
|
34
|
+
/**
|
|
35
|
+
* 保存した中間形式かどうかを判定します。
|
|
36
|
+
*
|
|
37
|
+
* @param value 判定する値です。
|
|
38
|
+
*
|
|
39
|
+
* @returns 中間形式であれば true を返します。
|
|
40
|
+
*/
|
|
41
|
+
function isStoredBlob(value) {
|
|
42
|
+
if (typeof value !== "object" || value === null) return false;
|
|
43
|
+
return value["unikvsBlob"] === true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Blob を IndexedDB に保存できる中間形式に変換します。File の名前と更新時刻も保持します。
|
|
47
|
+
*
|
|
48
|
+
* @param signal 中断を知らせるシグナルです。
|
|
49
|
+
* @param data 変換する Blob です。
|
|
50
|
+
*
|
|
51
|
+
* @returns 中間形式に変換した値です。
|
|
52
|
+
*/
|
|
53
|
+
async function toStoredBlob(signal, data) {
|
|
54
|
+
const buffer = await data.arrayBuffer();
|
|
55
|
+
signal.throwIfAborted();
|
|
56
|
+
const isFile = typeof File !== "undefined" && data instanceof File;
|
|
57
|
+
return {
|
|
58
|
+
name: isFile ? data.name : "",
|
|
59
|
+
type: data.type,
|
|
60
|
+
buffer,
|
|
61
|
+
unikvsBlob: true,
|
|
62
|
+
unikvsFile: isFile,
|
|
63
|
+
lastModified: isFile ? data.lastModified : 0
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 中間形式を Blob または File に戻します。
|
|
68
|
+
*
|
|
69
|
+
* @param stored 中間形式の値です。
|
|
70
|
+
*
|
|
71
|
+
* @returns 復元した Blob または File です。
|
|
72
|
+
*/
|
|
73
|
+
function fromStoredBlob(stored) {
|
|
74
|
+
if (stored.unikvsFile) return new File([stored.buffer], stored.name, {
|
|
75
|
+
type: stored.type,
|
|
76
|
+
lastModified: stored.lastModified
|
|
77
|
+
});
|
|
78
|
+
return new Blob([stored.buffer], { type: stored.type });
|
|
79
|
+
}
|
|
80
|
+
var _db = /* @__PURE__ */ new WeakMap();
|
|
81
|
+
var _opening = /* @__PURE__ */ new WeakMap();
|
|
82
|
+
var _dbName = /* @__PURE__ */ new WeakMap();
|
|
83
|
+
var _storeName = /* @__PURE__ */ new WeakMap();
|
|
84
|
+
var _Indexeddb_brand = /* @__PURE__ */ new WeakSet();
|
|
85
|
+
/**
|
|
86
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
87
|
+
*/
|
|
88
|
+
var Indexeddb = class {
|
|
89
|
+
/**
|
|
90
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
91
|
+
*/
|
|
92
|
+
constructor(dbName = "unikvs_db", storeName = "kvs_store", _options = {}) {
|
|
93
|
+
_classPrivateMethodInitSpec(this, _Indexeddb_brand);
|
|
94
|
+
_classPrivateFieldInitSpec(this, _db, void 0);
|
|
95
|
+
_classPrivateFieldInitSpec(this, _opening, void 0);
|
|
96
|
+
_classPrivateFieldInitSpec(this, _dbName, void 0);
|
|
97
|
+
_classPrivateFieldInitSpec(this, _storeName, void 0);
|
|
98
|
+
this.name = "Indexeddb";
|
|
99
|
+
_classPrivateFieldSet2(_db, this, null);
|
|
100
|
+
_classPrivateFieldSet2(_opening, this, null);
|
|
101
|
+
_classPrivateFieldSet2(_dbName, this, dbName);
|
|
102
|
+
_classPrivateFieldSet2(_storeName, this, storeName);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* ストレージが開いているかどうかを返します。
|
|
106
|
+
*
|
|
107
|
+
* @returns データベース接続があれば true を返します。
|
|
108
|
+
*/
|
|
109
|
+
get isOpen() {
|
|
110
|
+
return _classPrivateFieldGet2(_db, this) !== null;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。
|
|
114
|
+
*
|
|
115
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
116
|
+
*
|
|
117
|
+
* @returns 完了を知らせるプロミスです。
|
|
118
|
+
*/
|
|
119
|
+
async open(c) {
|
|
120
|
+
c.signal.throwIfAborted();
|
|
121
|
+
if (_classPrivateFieldGet2(_db, this) !== null) return;
|
|
122
|
+
if (_classPrivateFieldGet2(_opening, this) !== null) {
|
|
123
|
+
await _classPrivateFieldGet2(_opening, this);
|
|
124
|
+
c.signal.throwIfAborted();
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const opening = openDB(_classPrivateFieldGet2(_dbName, this), 1, { upgrade: (upgraded) => {
|
|
128
|
+
if (!upgraded.objectStoreNames.contains(_classPrivateFieldGet2(_storeName, this))) upgraded.createObjectStore(_classPrivateFieldGet2(_storeName, this));
|
|
129
|
+
} });
|
|
130
|
+
_classPrivateFieldSet2(_opening, this, opening);
|
|
131
|
+
let opened = null;
|
|
132
|
+
try {
|
|
133
|
+
opened = await opening;
|
|
134
|
+
c.signal.throwIfAborted();
|
|
135
|
+
_classPrivateFieldSet2(_db, this, opened);
|
|
136
|
+
opened = null;
|
|
137
|
+
} catch (ex) {
|
|
138
|
+
if (opened !== null) try {
|
|
139
|
+
opened.close();
|
|
140
|
+
} catch {}
|
|
141
|
+
throw ex;
|
|
142
|
+
} finally {
|
|
143
|
+
_classPrivateFieldSet2(_opening, this, null);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* データベース接続を閉じます。
|
|
148
|
+
*
|
|
149
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
150
|
+
*
|
|
151
|
+
* @returns 完了を知らせるプロミスです。
|
|
152
|
+
*/
|
|
153
|
+
async close(c) {
|
|
154
|
+
c.signal.throwIfAborted();
|
|
155
|
+
const db = _classPrivateFieldGet2(_db, this);
|
|
156
|
+
if (db === null) throw new TypeError("Indexeddb is not open. Call open() first.");
|
|
157
|
+
await Promise.resolve();
|
|
158
|
+
db.close();
|
|
159
|
+
_classPrivateFieldSet2(_db, this, null);
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* 指定したキーに値を保存します。Blob は中間形式に変換して保存します。
|
|
163
|
+
*
|
|
164
|
+
* @param c 書き込み対象のキーを持つコンテキストです。
|
|
165
|
+
* @param data 保存する値です。
|
|
166
|
+
*
|
|
167
|
+
* @returns 完了を知らせるプロミスです。
|
|
168
|
+
*/
|
|
169
|
+
async write(c, data) {
|
|
170
|
+
c.signal.throwIfAborted();
|
|
171
|
+
const db = _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this);
|
|
172
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) {
|
|
173
|
+
const stored = await toStoredBlob(c.signal, data);
|
|
174
|
+
await db.put(_classPrivateFieldGet2(_storeName, this), stored, c.key);
|
|
175
|
+
c.signal.throwIfAborted();
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
await db.put(_classPrivateFieldGet2(_storeName, this), data, c.key);
|
|
179
|
+
c.signal.throwIfAborted();
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* 指定したキーの値を読み出します。中間形式は Blob または File に戻します。
|
|
183
|
+
*
|
|
184
|
+
* @param c 読み出すキーを持つコンテキストです。
|
|
185
|
+
*
|
|
186
|
+
* @returns 保存されている値です。
|
|
187
|
+
*/
|
|
188
|
+
async read(c) {
|
|
189
|
+
c.signal.throwIfAborted();
|
|
190
|
+
const db = _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this);
|
|
191
|
+
const value = await db.get(_classPrivateFieldGet2(_storeName, this), c.key);
|
|
192
|
+
c.signal.throwIfAborted();
|
|
193
|
+
if (isStoredBlob(value)) return fromStoredBlob(value);
|
|
194
|
+
if (value === void 0) {
|
|
195
|
+
const count = await db.count(_classPrivateFieldGet2(_storeName, this), c.key);
|
|
196
|
+
c.signal.throwIfAborted();
|
|
197
|
+
if (count === 0) throw new DOMException("A requested file or directory could not be found at the time an operation was processed.", "NotFoundError");
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* 指定したキーが存在するかどうかを返します。
|
|
203
|
+
*
|
|
204
|
+
* @param c 確認するキーを持つコンテキストです。
|
|
205
|
+
*
|
|
206
|
+
* @returns キーが存在すれば true を返します。
|
|
207
|
+
*/
|
|
208
|
+
async exists(c) {
|
|
209
|
+
c.signal.throwIfAborted();
|
|
210
|
+
const count = await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).count(_classPrivateFieldGet2(_storeName, this), c.key);
|
|
211
|
+
c.signal.throwIfAborted();
|
|
212
|
+
return count > 0;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* 指定したキーを削除します。
|
|
216
|
+
*
|
|
217
|
+
* @param c 削除するキーを持つコンテキストです。
|
|
218
|
+
*
|
|
219
|
+
* @returns 完了を知らせるプロミスです。
|
|
220
|
+
*/
|
|
221
|
+
async delete(c) {
|
|
222
|
+
c.signal.throwIfAborted();
|
|
223
|
+
await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).delete(_classPrivateFieldGet2(_storeName, this), c.key);
|
|
224
|
+
c.signal.throwIfAborted();
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* オブジェクトストアのすべての値を削除します。
|
|
228
|
+
*
|
|
229
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
230
|
+
*
|
|
231
|
+
* @returns 完了を知らせるプロミスです。
|
|
232
|
+
*/
|
|
233
|
+
async clear(c) {
|
|
234
|
+
c.signal.throwIfAborted();
|
|
235
|
+
await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).clear(_classPrivateFieldGet2(_storeName, this));
|
|
236
|
+
c.signal.throwIfAborted();
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。
|
|
240
|
+
*
|
|
241
|
+
* @param c 書き込み対象のキーを持つコンテキストです。
|
|
242
|
+
*
|
|
243
|
+
* @returns Uint8Array を受け付ける書き込みストリームです。
|
|
244
|
+
*/
|
|
245
|
+
getWritable(c) {
|
|
246
|
+
c.signal.throwIfAborted();
|
|
247
|
+
const chunks = [];
|
|
248
|
+
return new WritableStream({
|
|
249
|
+
abort: () => {
|
|
250
|
+
chunks.length = 0;
|
|
251
|
+
},
|
|
252
|
+
close: async () => {
|
|
253
|
+
c.signal.throwIfAborted();
|
|
254
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
255
|
+
const merged = new Uint8Array(totalLength);
|
|
256
|
+
let offset = 0;
|
|
257
|
+
for (const chunk of chunks) {
|
|
258
|
+
merged.set(chunk, offset);
|
|
259
|
+
offset += chunk.length;
|
|
260
|
+
}
|
|
261
|
+
await _assertClassBrand(_Indexeddb_brand, this, _getDb).call(this).put(_classPrivateFieldGet2(_storeName, this), merged, c.key);
|
|
262
|
+
c.signal.throwIfAborted();
|
|
263
|
+
},
|
|
264
|
+
write: (chunk) => {
|
|
265
|
+
c.signal.throwIfAborted();
|
|
266
|
+
chunks.push(chunk);
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* 保存済みの値を読み出すストリームを作成します。
|
|
272
|
+
*
|
|
273
|
+
* @param c 読み出すキーを持つコンテキストです。
|
|
274
|
+
*
|
|
275
|
+
* @returns Uint8Array を送出する読み取りストリームです。
|
|
276
|
+
*/
|
|
277
|
+
getReadable(c) {
|
|
278
|
+
c.signal.throwIfAborted();
|
|
279
|
+
return new ReadableStream({ pull: async (controller) => {
|
|
280
|
+
try {
|
|
281
|
+
const data = await this.read(c);
|
|
282
|
+
c.signal.throwIfAborted();
|
|
283
|
+
controller.enqueue(data);
|
|
284
|
+
controller.close();
|
|
285
|
+
} catch (ex) {
|
|
286
|
+
controller.error(ex);
|
|
287
|
+
}
|
|
288
|
+
} });
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* 開いているデータベースを返します。未オープン時はエラーを投げます。
|
|
293
|
+
*
|
|
294
|
+
* @returns 開いているデータベースです。
|
|
295
|
+
*/
|
|
296
|
+
function _getDb() {
|
|
297
|
+
const db = _classPrivateFieldGet2(_db, this);
|
|
298
|
+
if (db === null) throw new TypeError("Indexeddb is not open. Call open() first.");
|
|
299
|
+
return db;
|
|
300
|
+
}
|
|
301
|
+
//#endregion
|
|
302
|
+
export { Indexeddb };
|
|
303
|
+
|
|
304
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +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/**\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":"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"}
|
package/package.json
CHANGED
|
@@ -1,49 +1,44 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unikvs/indexeddb",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.14",
|
|
4
4
|
"description": "Storage plugin that stores arbitrary data in the browser IndexedDB.",
|
|
5
|
-
"homepage": "https://
|
|
5
|
+
"homepage": "https://tai-kun.github.io/unikvs/",
|
|
6
6
|
"license": "MIT",
|
|
7
|
-
"author": "tai-kun",
|
|
8
7
|
"repository": {
|
|
9
|
-
"url": "https://github.com/tai-kun/unikvs"
|
|
8
|
+
"url": "https://github.com/tai-kun/unikvs",
|
|
9
|
+
"directory": "packages/indexeddb"
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"dist",
|
|
13
13
|
"src",
|
|
14
14
|
"package.json",
|
|
15
|
-
"
|
|
16
|
-
"
|
|
15
|
+
"LICENSE",
|
|
16
|
+
"README.md"
|
|
17
17
|
],
|
|
18
18
|
"type": "module",
|
|
19
19
|
"sideEffects": false,
|
|
20
|
-
"main": "./dist/src/index.js",
|
|
21
|
-
"types": "./dist/src/index.d.ts",
|
|
22
20
|
"exports": {
|
|
23
21
|
".": {
|
|
24
|
-
"types": "./dist/
|
|
25
|
-
"default": "./dist/
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
26
24
|
}
|
|
27
25
|
},
|
|
28
26
|
"dependencies": {
|
|
29
|
-
"@unikvs/core": "0.0.
|
|
30
|
-
"idb": "8.0.
|
|
27
|
+
"@unikvs/core": "0.0.14",
|
|
28
|
+
"idb": "^8.0.4"
|
|
31
29
|
},
|
|
32
30
|
"devDependencies": {
|
|
33
|
-
"@
|
|
34
|
-
"@
|
|
35
|
-
"@
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"npm-check-updates": "20.0.2",
|
|
39
|
-
"playwright": "1.63.0",
|
|
40
|
-
"vite": "8.3.0",
|
|
41
|
-
"vitest": "4.1.11"
|
|
31
|
+
"@unikvs/testing": "0.0.0",
|
|
32
|
+
"@vitest/browser-playwright": "5.0.5",
|
|
33
|
+
"@vitest/coverage-istanbul": "5.0.5",
|
|
34
|
+
"playwright": "1.64.1",
|
|
35
|
+
"vite-plus": "1.3.0"
|
|
42
36
|
},
|
|
43
37
|
"peerDependencies": {
|
|
44
|
-
"@logtape/logtape": "2.0.0"
|
|
38
|
+
"@logtape/logtape": "^2.0.0"
|
|
45
39
|
},
|
|
40
|
+
"readme": "README.md",
|
|
46
41
|
"scripts": {
|
|
47
|
-
"build": "
|
|
42
|
+
"build": "vp -C .config pack"
|
|
48
43
|
}
|
|
49
44
|
}
|
package/src/index.ts
ADDED
package/src/indexeddb.ts
CHANGED
|
@@ -1,164 +1,426 @@
|
|
|
1
1
|
import type { IStorage } from "@unikvs/core";
|
|
2
|
-
import {
|
|
2
|
+
import { type IDBPDatabase, openDB } from "idb";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
6
|
+
*/
|
|
7
|
+
export type IndexeddbOptions = {};
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* WebKit で Blob を直接保存できない場合があるため、中間形式で保持します。
|
|
11
|
+
*/
|
|
12
|
+
type StoredBlob = {
|
|
13
|
+
/**
|
|
14
|
+
* File のときに保持するファイル名です。
|
|
15
|
+
*/
|
|
16
|
+
readonly name: string;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* 元の Blob の MIME 種別です。
|
|
20
|
+
*/
|
|
21
|
+
readonly type: string;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Blob のバイト列です。
|
|
25
|
+
*/
|
|
26
|
+
readonly buffer: ArrayBuffer;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 中間形式であることを示す目印です。
|
|
30
|
+
*/
|
|
31
|
+
readonly unikvsBlob: true;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* File 由来かどうかを示す目印です。
|
|
35
|
+
*/
|
|
36
|
+
readonly unikvsFile: boolean;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* File のときに保持する更新時刻です。
|
|
40
|
+
*/
|
|
41
|
+
readonly lastModified: number;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* 保存した中間形式かどうかを判定します。
|
|
46
|
+
*
|
|
47
|
+
* @param value 判定する値です。
|
|
48
|
+
*
|
|
49
|
+
* @returns 中間形式であれば true を返します。
|
|
50
|
+
*/
|
|
51
|
+
function isStoredBlob(value: unknown): value is StoredBlob {
|
|
52
|
+
if (typeof value !== "object" || value === null) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return (value as Record<string, unknown>)["unikvsBlob"] === true;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Blob を IndexedDB に保存できる中間形式に変換します。File の名前と更新時刻も保持します。
|
|
61
|
+
*
|
|
62
|
+
* @param signal 中断を知らせるシグナルです。
|
|
63
|
+
* @param data 変換する Blob です。
|
|
64
|
+
*
|
|
65
|
+
* @returns 中間形式に変換した値です。
|
|
66
|
+
*/
|
|
67
|
+
async function toStoredBlob(signal: AbortSignal, data: Blob): Promise<StoredBlob> {
|
|
68
|
+
const buffer = await data.arrayBuffer();
|
|
69
|
+
|
|
70
|
+
signal.throwIfAborted();
|
|
71
|
+
|
|
72
|
+
const isFile = typeof File !== "undefined" && data instanceof File;
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
name: isFile ? data.name : "",
|
|
76
|
+
type: data.type,
|
|
77
|
+
buffer,
|
|
78
|
+
unikvsBlob: true,
|
|
79
|
+
unikvsFile: isFile,
|
|
80
|
+
lastModified: isFile ? data.lastModified : 0,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* 中間形式を Blob または File に戻します。
|
|
86
|
+
*
|
|
87
|
+
* @param stored 中間形式の値です。
|
|
88
|
+
*
|
|
89
|
+
* @returns 復元した Blob または File です。
|
|
90
|
+
*/
|
|
91
|
+
function fromStoredBlob(stored: StoredBlob): Blob {
|
|
92
|
+
if (stored.unikvsFile) {
|
|
93
|
+
return new File([stored.buffer], stored.name, {
|
|
94
|
+
type: stored.type,
|
|
95
|
+
lastModified: stored.lastModified,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return new Blob([stored.buffer], { type: stored.type });
|
|
100
|
+
}
|
|
3
101
|
|
|
4
102
|
/**
|
|
5
103
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
6
104
|
*/
|
|
7
105
|
export default class Indexeddb implements IStorage {
|
|
106
|
+
/**
|
|
107
|
+
* ストレージ名です。常に `Indexeddb` を返します。
|
|
108
|
+
*/
|
|
109
|
+
public readonly name: string;
|
|
110
|
+
|
|
8
111
|
/**
|
|
9
112
|
* IndexedDB のデータベースインスタンスを保持します。
|
|
10
113
|
*
|
|
11
114
|
* ストレージがオープンされるまで null です。
|
|
12
115
|
*/
|
|
13
|
-
|
|
116
|
+
#db: IDBPDatabase | null;
|
|
14
117
|
|
|
15
118
|
/**
|
|
16
|
-
*
|
|
119
|
+
* 並行 open を束ねるための進行中 Promise です。
|
|
17
120
|
*/
|
|
18
|
-
|
|
121
|
+
#opening: Promise<IDBPDatabase> | null;
|
|
19
122
|
|
|
20
123
|
/**
|
|
21
|
-
*
|
|
124
|
+
* 使用するデータベース名です。
|
|
22
125
|
*/
|
|
23
|
-
|
|
126
|
+
readonly #dbName: string;
|
|
24
127
|
|
|
25
128
|
/**
|
|
26
|
-
*
|
|
129
|
+
* 使用するオブジェクトストア名です。
|
|
27
130
|
*/
|
|
28
|
-
|
|
131
|
+
readonly #storeName: string;
|
|
29
132
|
|
|
30
133
|
/**
|
|
31
134
|
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
32
135
|
*/
|
|
33
|
-
public constructor(
|
|
136
|
+
public constructor(
|
|
137
|
+
dbName = "unikvs_db",
|
|
138
|
+
storeName = "kvs_store",
|
|
139
|
+
_options: IndexeddbOptions = {},
|
|
140
|
+
) {
|
|
34
141
|
this.name = "Indexeddb";
|
|
35
|
-
this
|
|
36
|
-
this
|
|
37
|
-
this
|
|
142
|
+
this.#db = null;
|
|
143
|
+
this.#opening = null;
|
|
144
|
+
this.#dbName = dbName;
|
|
145
|
+
this.#storeName = storeName;
|
|
38
146
|
}
|
|
39
147
|
|
|
40
148
|
/**
|
|
41
|
-
*
|
|
149
|
+
* ストレージが開いているかどうかを返します。
|
|
150
|
+
*
|
|
151
|
+
* @returns データベース接続があれば true を返します。
|
|
42
152
|
*/
|
|
43
153
|
public get isOpen(): boolean {
|
|
44
|
-
return this
|
|
154
|
+
return this.#db !== null;
|
|
45
155
|
}
|
|
46
156
|
|
|
47
157
|
/**
|
|
48
|
-
*
|
|
158
|
+
* データベースを開きます。並行した呼び出しは一つの接続処理に束ねます。
|
|
159
|
+
*
|
|
160
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
161
|
+
*
|
|
162
|
+
* @returns 完了を知らせるプロミスです。
|
|
49
163
|
*/
|
|
50
|
-
public async open(): Promise<void> {
|
|
51
|
-
|
|
164
|
+
public async open(c: IStorage.OpenContext): Promise<void> {
|
|
165
|
+
c.signal.throwIfAborted();
|
|
166
|
+
|
|
167
|
+
if (this.#db !== null) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (this.#opening !== null) {
|
|
172
|
+
await this.#opening;
|
|
173
|
+
c.signal.throwIfAborted();
|
|
174
|
+
|
|
52
175
|
return;
|
|
53
176
|
}
|
|
54
177
|
|
|
55
|
-
|
|
56
|
-
upgrade: (
|
|
57
|
-
//
|
|
58
|
-
if (!
|
|
59
|
-
|
|
178
|
+
const opening = openDB(this.#dbName, 1, {
|
|
179
|
+
upgrade: (upgraded) => {
|
|
180
|
+
// オブジェクトストアが存在しない場合は作成します。
|
|
181
|
+
if (!upgraded.objectStoreNames.contains(this.#storeName)) {
|
|
182
|
+
upgraded.createObjectStore(this.#storeName);
|
|
60
183
|
}
|
|
61
184
|
},
|
|
62
185
|
});
|
|
186
|
+
this.#opening = opening;
|
|
187
|
+
|
|
188
|
+
let opened: IDBPDatabase | null = null;
|
|
189
|
+
|
|
190
|
+
try {
|
|
191
|
+
opened = await opening;
|
|
192
|
+
c.signal.throwIfAborted();
|
|
193
|
+
this.#db = opened;
|
|
194
|
+
opened = null;
|
|
195
|
+
} catch (ex) {
|
|
196
|
+
if (opened !== null) {
|
|
197
|
+
try {
|
|
198
|
+
opened.close();
|
|
199
|
+
} catch {
|
|
200
|
+
// 閉鎖時の失敗は無視します。
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
throw ex;
|
|
205
|
+
} finally {
|
|
206
|
+
this.#opening = null;
|
|
207
|
+
}
|
|
63
208
|
}
|
|
64
209
|
|
|
65
210
|
/**
|
|
66
|
-
*
|
|
211
|
+
* データベース接続を閉じます。
|
|
212
|
+
*
|
|
213
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
214
|
+
*
|
|
215
|
+
* @returns 完了を知らせるプロミスです。
|
|
67
216
|
*/
|
|
68
|
-
public async close(): Promise<void> {
|
|
69
|
-
|
|
70
|
-
|
|
217
|
+
public async close(c: IStorage.CloseContext): Promise<void> {
|
|
218
|
+
c.signal.throwIfAborted();
|
|
219
|
+
|
|
220
|
+
const db = this.#db;
|
|
221
|
+
|
|
222
|
+
if (db === null) {
|
|
223
|
+
throw new TypeError("Indexeddb is not open. Call open() first.");
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
await Promise.resolve();
|
|
227
|
+
db.close();
|
|
228
|
+
this.#db = null;
|
|
71
229
|
}
|
|
72
230
|
|
|
73
231
|
/**
|
|
74
|
-
*
|
|
232
|
+
* 指定したキーに値を保存します。Blob は中間形式に変換して保存します。
|
|
233
|
+
*
|
|
234
|
+
* @param c 書き込み対象のキーを持つコンテキストです。
|
|
235
|
+
* @param data 保存する値です。
|
|
236
|
+
*
|
|
237
|
+
* @returns 完了を知らせるプロミスです。
|
|
75
238
|
*/
|
|
76
|
-
public async write(
|
|
77
|
-
|
|
78
|
-
|
|
239
|
+
public async write(c: IStorage.WriteContext, data: any): Promise<void> {
|
|
240
|
+
c.signal.throwIfAborted();
|
|
241
|
+
|
|
242
|
+
const db = this.#getDb();
|
|
243
|
+
|
|
244
|
+
if (typeof Blob !== "undefined" && data instanceof Blob) {
|
|
245
|
+
const stored = await toStoredBlob(c.signal, data);
|
|
246
|
+
await db.put(this.#storeName, stored, c.key);
|
|
247
|
+
|
|
248
|
+
c.signal.throwIfAborted();
|
|
249
|
+
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
await db.put(this.#storeName, data, c.key);
|
|
254
|
+
|
|
255
|
+
c.signal.throwIfAborted();
|
|
79
256
|
}
|
|
80
257
|
|
|
81
258
|
/**
|
|
82
|
-
*
|
|
259
|
+
* 指定したキーの値を読み出します。中間形式は Blob または File に戻します。
|
|
260
|
+
*
|
|
261
|
+
* @param c 読み出すキーを持つコンテキストです。
|
|
262
|
+
*
|
|
263
|
+
* @returns 保存されている値です。
|
|
83
264
|
*/
|
|
84
|
-
public async read(
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
265
|
+
public async read(c: IStorage.ReadContext): Promise<any> {
|
|
266
|
+
c.signal.throwIfAborted();
|
|
267
|
+
|
|
268
|
+
const db = this.#getDb();
|
|
269
|
+
const value = await db.get(this.#storeName, c.key);
|
|
270
|
+
|
|
271
|
+
c.signal.throwIfAborted();
|
|
272
|
+
|
|
273
|
+
if (isStoredBlob(value)) {
|
|
274
|
+
return fromStoredBlob(value);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if (value === undefined) {
|
|
278
|
+
const count = await db.count(this.#storeName, c.key);
|
|
279
|
+
|
|
280
|
+
c.signal.throwIfAborted();
|
|
281
|
+
|
|
282
|
+
// 存在しない場合は DOMException (NotFoundError) を投げます。
|
|
283
|
+
if (count === 0) {
|
|
284
|
+
throw new DOMException(
|
|
285
|
+
"A requested file or directory could not be found at the time an operation was processed.",
|
|
286
|
+
"NotFoundError",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
92
289
|
}
|
|
93
290
|
|
|
94
|
-
return
|
|
291
|
+
return value;
|
|
95
292
|
}
|
|
96
293
|
|
|
97
294
|
/**
|
|
98
|
-
*
|
|
295
|
+
* 指定したキーが存在するかどうかを返します。
|
|
296
|
+
*
|
|
297
|
+
* @param c 確認するキーを持つコンテキストです。
|
|
298
|
+
*
|
|
299
|
+
* @returns キーが存在すれば true を返します。
|
|
99
300
|
*/
|
|
100
|
-
public async exists(
|
|
101
|
-
|
|
301
|
+
public async exists(c: IStorage.ExistsContext): Promise<boolean> {
|
|
302
|
+
c.signal.throwIfAborted();
|
|
303
|
+
|
|
304
|
+
const db = this.#getDb();
|
|
305
|
+
|
|
306
|
+
// 件数が 1 であれば存在し、0 であれば存在しません。
|
|
307
|
+
const count = await db.count(this.#storeName, c.key);
|
|
308
|
+
|
|
309
|
+
c.signal.throwIfAborted();
|
|
102
310
|
|
|
103
|
-
// count() はキーが存在すれば 1 を、存在しなければ 0 を返します
|
|
104
|
-
const count = await this.db!.count(this.storeName, key);
|
|
105
311
|
return count > 0;
|
|
106
312
|
}
|
|
107
313
|
|
|
108
314
|
/**
|
|
109
|
-
*
|
|
315
|
+
* 指定したキーを削除します。
|
|
316
|
+
*
|
|
317
|
+
* @param c 削除するキーを持つコンテキストです。
|
|
318
|
+
*
|
|
319
|
+
* @returns 完了を知らせるプロミスです。
|
|
110
320
|
*/
|
|
111
|
-
public async delete(
|
|
112
|
-
|
|
321
|
+
public async delete(c: IStorage.DeleteContext): Promise<void> {
|
|
322
|
+
c.signal.throwIfAborted();
|
|
113
323
|
|
|
114
|
-
|
|
324
|
+
const db = this.#getDb();
|
|
325
|
+
await db.delete(this.#storeName, c.key);
|
|
326
|
+
|
|
327
|
+
c.signal.throwIfAborted();
|
|
115
328
|
}
|
|
116
329
|
|
|
117
330
|
/**
|
|
118
|
-
*
|
|
331
|
+
* オブジェクトストアのすべての値を削除します。
|
|
332
|
+
*
|
|
333
|
+
* @param c 中断を知らせるシグナルを持つコンテキストです。
|
|
334
|
+
*
|
|
335
|
+
* @returns 完了を知らせるプロミスです。
|
|
119
336
|
*/
|
|
120
|
-
public async clear(): Promise<void> {
|
|
121
|
-
|
|
337
|
+
public async clear(c: IStorage.ClearContext): Promise<void> {
|
|
338
|
+
c.signal.throwIfAborted();
|
|
339
|
+
|
|
340
|
+
const db = this.#getDb();
|
|
341
|
+
await db.clear(this.#storeName);
|
|
342
|
+
|
|
343
|
+
c.signal.throwIfAborted();
|
|
122
344
|
}
|
|
123
345
|
|
|
124
346
|
/**
|
|
125
|
-
*
|
|
347
|
+
* Uint8Array を書き込むためのストリームを作成します。close 時にまとめて保存します。
|
|
348
|
+
*
|
|
349
|
+
* @param c 書き込み対象のキーを持つコンテキストです。
|
|
350
|
+
*
|
|
351
|
+
* @returns Uint8Array を受け付ける書き込みストリームです。
|
|
126
352
|
*/
|
|
127
|
-
public getWritable(
|
|
128
|
-
|
|
353
|
+
public getWritable(c: IStorage.GetWritableContext): WritableStream<Uint8Array> {
|
|
354
|
+
c.signal.throwIfAborted();
|
|
355
|
+
|
|
129
356
|
const chunks: Uint8Array[] = [];
|
|
130
357
|
|
|
131
|
-
return new WritableStream({
|
|
132
|
-
|
|
133
|
-
chunks.
|
|
358
|
+
return new WritableStream<Uint8Array>({
|
|
359
|
+
abort: (): void => {
|
|
360
|
+
chunks.length = 0;
|
|
134
361
|
},
|
|
135
|
-
close: async () => {
|
|
136
|
-
|
|
137
|
-
|
|
362
|
+
close: async (): Promise<void> => {
|
|
363
|
+
// 保存は close のときだけ行うため、中断時はここまでのチャンクを破棄できます。
|
|
364
|
+
c.signal.throwIfAborted();
|
|
365
|
+
|
|
366
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
367
|
+
const merged = new Uint8Array(totalLength);
|
|
138
368
|
let offset = 0;
|
|
139
369
|
|
|
140
370
|
for (const chunk of chunks) {
|
|
141
|
-
|
|
371
|
+
merged.set(chunk, offset);
|
|
142
372
|
offset += chunk.length;
|
|
143
373
|
}
|
|
144
374
|
|
|
145
|
-
|
|
375
|
+
const db = this.#getDb();
|
|
376
|
+
await db.put(this.#storeName, merged, c.key);
|
|
377
|
+
|
|
378
|
+
c.signal.throwIfAborted();
|
|
379
|
+
},
|
|
380
|
+
write: (chunk): void => {
|
|
381
|
+
c.signal.throwIfAborted();
|
|
382
|
+
chunks.push(chunk);
|
|
146
383
|
},
|
|
147
384
|
});
|
|
148
385
|
}
|
|
149
386
|
|
|
150
387
|
/**
|
|
151
|
-
*
|
|
388
|
+
* 保存済みの値を読み出すストリームを作成します。
|
|
389
|
+
*
|
|
390
|
+
* @param c 読み出すキーを持つコンテキストです。
|
|
391
|
+
*
|
|
392
|
+
* @returns Uint8Array を送出する読み取りストリームです。
|
|
152
393
|
*/
|
|
153
|
-
public getReadable(
|
|
154
|
-
|
|
394
|
+
public getReadable(c: IStorage.GetReadableContext): ReadableStream<Uint8Array> {
|
|
395
|
+
c.signal.throwIfAborted();
|
|
155
396
|
|
|
156
|
-
return new ReadableStream({
|
|
157
|
-
pull: async (controller) => {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
397
|
+
return new ReadableStream<Uint8Array>({
|
|
398
|
+
pull: async (controller: ReadableStreamDefaultController<Uint8Array>): Promise<void> => {
|
|
399
|
+
try {
|
|
400
|
+
const data = await this.read(c as unknown as IStorage.ReadContext);
|
|
401
|
+
|
|
402
|
+
c.signal.throwIfAborted();
|
|
403
|
+
controller.enqueue(data as Uint8Array);
|
|
404
|
+
controller.close();
|
|
405
|
+
} catch (ex) {
|
|
406
|
+
controller.error(ex);
|
|
407
|
+
}
|
|
161
408
|
},
|
|
162
409
|
});
|
|
163
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
|
+
}
|
|
164
426
|
}
|
package/dist/src/indexeddb.d.ts
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import type { IStorage } from "@unikvs/core";
|
|
2
|
-
/**
|
|
3
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
4
|
-
*/
|
|
5
|
-
export default class Indexeddb implements IStorage {
|
|
6
|
-
/**
|
|
7
|
-
* IndexedDB のデータベースインスタンスを保持します。
|
|
8
|
-
*
|
|
9
|
-
* ストレージがオープンされるまで null です。
|
|
10
|
-
*/
|
|
11
|
-
private db;
|
|
12
|
-
/**
|
|
13
|
-
* 使用するデータベース名です。
|
|
14
|
-
*/
|
|
15
|
-
private readonly dbName;
|
|
16
|
-
/**
|
|
17
|
-
* 使用するオブジェクトストア名です。
|
|
18
|
-
*/
|
|
19
|
-
private readonly storeName;
|
|
20
|
-
/**
|
|
21
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
22
|
-
*/
|
|
23
|
-
readonly name: string;
|
|
24
|
-
/**
|
|
25
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
26
|
-
*/
|
|
27
|
-
constructor(dbName?: string, storeName?: string);
|
|
28
|
-
/**
|
|
29
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
30
|
-
*/
|
|
31
|
-
get isOpen(): boolean;
|
|
32
|
-
/**
|
|
33
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
34
|
-
*/
|
|
35
|
-
open(): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
38
|
-
*/
|
|
39
|
-
close(): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
42
|
-
*/
|
|
43
|
-
write(args: Pick<IStorage.WriteArgs<any>, "key" | "data">): Promise<void>;
|
|
44
|
-
/**
|
|
45
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
46
|
-
*/
|
|
47
|
-
read(args: Pick<IStorage.ReadArgs, "key">): Promise<any>;
|
|
48
|
-
/**
|
|
49
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
50
|
-
*/
|
|
51
|
-
exists(args: Pick<IStorage.ExistsArgs, "key">): Promise<boolean>;
|
|
52
|
-
/**
|
|
53
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
54
|
-
*/
|
|
55
|
-
delete(args: Pick<IStorage.DeleteArgs, "key">): Promise<void>;
|
|
56
|
-
/**
|
|
57
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
58
|
-
*/
|
|
59
|
-
clear(): Promise<void>;
|
|
60
|
-
/**
|
|
61
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
62
|
-
*/
|
|
63
|
-
getWritable(args: Pick<IStorage.GetWritableArgs, "key">): WritableStream<Uint8Array>;
|
|
64
|
-
/**
|
|
65
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
66
|
-
*/
|
|
67
|
-
getReadable(args: Pick<IStorage.GetReadableArgs, "key">): ReadableStream<Uint8Array>;
|
|
68
|
-
}
|
|
69
|
-
//# sourceMappingURL=indexeddb.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"indexeddb.d.ts","sourceRoot":"","sources":["../../src/indexeddb.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAG7C;;GAEG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,YAAW,QAAQ;IAChD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAsB;IAEhC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAEhC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IAEnC;;OAEG;IACH,SAAgB,IAAI,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,YAAmB,MAAM,GAAE,MAAoB,EAAE,SAAS,GAAE,MAAoB,EAK/E;IAED;;OAEG;IACH,IAAW,MAAM,IAAI,OAAO,CAE3B;IAED;;OAEG;IACU,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAajC;IAED;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAGlC;IAED;;OAEG;IACU,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAGrF;IAED;;OAEG;IACU,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAWpE;IAED;;OAEG;IACU,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,CAM5E;IAED;;OAEG;IACU,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,EAAE,KAAK,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAIzE;IAED;;OAEG;IACU,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAElC;IAED;;OAEG;IACI,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,KAAK,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,CAqB1F;IAED;;OAEG;IACI,WAAW,CAAC,IAAI,EAAE,IAAI,CAAC,QAAQ,CAAC,eAAe,EAAE,KAAK,CAAC,GAAG,cAAc,CAAC,UAAU,CAAC,CAU1F;CACF"}
|
package/dist/src/indexeddb.js
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
import { openDB } from "idb";
|
|
2
|
-
/**
|
|
3
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
4
|
-
*/
|
|
5
|
-
export default class Indexeddb {
|
|
6
|
-
/**
|
|
7
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
8
|
-
*/
|
|
9
|
-
constructor(dbName = "unikvs_db", storeName = "kvs_store") {
|
|
10
|
-
this.name = "Indexeddb";
|
|
11
|
-
this.db = null;
|
|
12
|
-
this.dbName = dbName;
|
|
13
|
-
this.storeName = storeName;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
17
|
-
*/
|
|
18
|
-
get isOpen() {
|
|
19
|
-
return this.db !== null;
|
|
20
|
-
}
|
|
21
|
-
/**
|
|
22
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
23
|
-
*/
|
|
24
|
-
async open() {
|
|
25
|
-
if (this.db) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
this.db = await openDB(this.dbName, 1, {
|
|
29
|
-
upgrade: (db) => {
|
|
30
|
-
// オブジェクトストアが存在しない場合は作成します
|
|
31
|
-
if (!db.objectStoreNames.contains(this.storeName)) {
|
|
32
|
-
db.createObjectStore(this.storeName);
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
39
|
-
*/
|
|
40
|
-
async close() {
|
|
41
|
-
this.db.close();
|
|
42
|
-
this.db = null;
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
46
|
-
*/
|
|
47
|
-
async write(args) {
|
|
48
|
-
const { key, data } = args;
|
|
49
|
-
await this.db.put(this.storeName, data, key);
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
53
|
-
*/
|
|
54
|
-
async read(args) {
|
|
55
|
-
const { key } = args;
|
|
56
|
-
// Opfs の挙動に合わせて、存在しない場合は DOMException (NotFoundError) を投げます
|
|
57
|
-
if (!(await this.exists({ key }))) {
|
|
58
|
-
throw new DOMException(`A requested file or directory could not be found at the time an operation was processed.`, "NotFoundError");
|
|
59
|
-
}
|
|
60
|
-
return await this.db.get(this.storeName, key);
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
64
|
-
*/
|
|
65
|
-
async exists(args) {
|
|
66
|
-
const { key } = args;
|
|
67
|
-
// count() はキーが存在すれば 1 を、存在しなければ 0 を返します
|
|
68
|
-
const count = await this.db.count(this.storeName, key);
|
|
69
|
-
return count > 0;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
73
|
-
*/
|
|
74
|
-
async delete(args) {
|
|
75
|
-
const { key } = args;
|
|
76
|
-
await this.db.delete(this.storeName, key);
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#usage)
|
|
80
|
-
*/
|
|
81
|
-
async clear() {
|
|
82
|
-
await this.db.clear(this.storeName);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
86
|
-
*/
|
|
87
|
-
getWritable(args) {
|
|
88
|
-
const { key } = args;
|
|
89
|
-
const chunks = [];
|
|
90
|
-
return new WritableStream({
|
|
91
|
-
write: (chunk) => {
|
|
92
|
-
chunks.push(chunk);
|
|
93
|
-
},
|
|
94
|
-
close: async () => {
|
|
95
|
-
const totalLength = chunks.reduce((acc, chunk) => acc + chunk.length, 0);
|
|
96
|
-
const result = new Uint8Array(totalLength);
|
|
97
|
-
let offset = 0;
|
|
98
|
-
for (const chunk of chunks) {
|
|
99
|
-
result.set(chunk, offset);
|
|
100
|
-
offset += chunk.length;
|
|
101
|
-
}
|
|
102
|
-
await this.db.put(this.storeName, result, key);
|
|
103
|
-
},
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
/**
|
|
107
|
-
* [API Reference](https://tai-kun.github.io/unikvs/packages/indexeddb#data)
|
|
108
|
-
*/
|
|
109
|
-
getReadable(args) {
|
|
110
|
-
const { key } = args;
|
|
111
|
-
return new ReadableStream({
|
|
112
|
-
pull: async (controller) => {
|
|
113
|
-
const data = await this.read({ key });
|
|
114
|
-
controller.enqueue(data);
|
|
115
|
-
controller.close();
|
|
116
|
-
},
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|