gs-idb-pro 0.1.5 → 1.0.0
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/lib/impl.cjs +1247 -0
- package/lib/impl.d.ts +265 -0
- package/lib/impl.mjs +1274 -0
- package/lib/index.cjs +11 -1269
- package/lib/index.d.ts +2 -1129
- package/lib/index.mjs +2 -1309
- package/lib/type.cjs +31 -0
- package/lib/type.d.ts +870 -0
- package/lib/type.mjs +45 -0
- package/package.json +13 -3
package/lib/impl.cjs
ADDED
|
@@ -0,0 +1,1247 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var type$1 = require('./type.cjs'), types = require('gs-base/types'), store = require('gs-idb-basic/store'), type = require('gs-idb-basic/type'), basic = require('gs-base/basic'), json = require('gs-base/json'), tx = require('gs-idb-basic/tx'), db = require('gs-idb-basic/db'), storage = require('gs-idb-basic/storage');
|
|
3
|
+
const toNum = (v) => v instanceof Date ? v.getTime() : v;
|
|
4
|
+
function parseIDbQuery(query) {
|
|
5
|
+
if (type.isDbQueryOrNull(query) || !types.isObject(query))
|
|
6
|
+
return query;
|
|
7
|
+
const range = query;
|
|
8
|
+
if ("lt" in range && "gt" in range) {
|
|
9
|
+
if (toNum(range.gt) > toNum(range.lt))
|
|
10
|
+
throw new Error(`Invalid IDBRange: gt (${range.gt}) cannot be greater than lt (${range.lt})`);
|
|
11
|
+
return IDBKeyRange.bound(range.gt, range.lt, !0, !0);
|
|
12
|
+
}
|
|
13
|
+
if ("lt" in range && "gte" in range) {
|
|
14
|
+
if (toNum(range.gte) > toNum(range.lt))
|
|
15
|
+
throw new Error(`Invalid IDBRange: gte (${range.gte}) cannot be greater than lt (${range.lt})`);
|
|
16
|
+
return IDBKeyRange.bound(range.gte, range.lt, !1, !0);
|
|
17
|
+
}
|
|
18
|
+
if ("lte" in range && "gt" in range) {
|
|
19
|
+
if (toNum(range.gt) > toNum(range.lte))
|
|
20
|
+
throw new Error(`Invalid IDBRange: gt (${range.gt}) cannot be greater than lte (${range.lte})`);
|
|
21
|
+
return IDBKeyRange.bound(range.gt, range.lte, !0, !1);
|
|
22
|
+
}
|
|
23
|
+
if ("lte" in range && "gte" in range) {
|
|
24
|
+
if (toNum(range.gte) > toNum(range.lte))
|
|
25
|
+
throw new Error(`Invalid IDBRange: gte (${range.gte}) cannot be greater than lte (${range.lte})`);
|
|
26
|
+
return IDBKeyRange.bound(range.gte, range.lte, !1, !1);
|
|
27
|
+
}
|
|
28
|
+
if ("lt" in range) return IDBKeyRange.upperBound(range.lt, !0);
|
|
29
|
+
if ("lte" in range) return IDBKeyRange.upperBound(range.lte, !1);
|
|
30
|
+
if ("gt" in range) return IDBKeyRange.lowerBound(range.gt, !0);
|
|
31
|
+
if ("gte" in range) return IDBKeyRange.lowerBound(range.gte, !1);
|
|
32
|
+
}
|
|
33
|
+
function isIDbQuery(query) {
|
|
34
|
+
return parseIDbQuery(query) != null;
|
|
35
|
+
}
|
|
36
|
+
async function openCursor(target, arg, fn) {
|
|
37
|
+
const { query, direction = "prev", preSkip, startKey, startPrimaryKey } = arg, request = target.openCursor(parseIDbQuery(query), direction);
|
|
38
|
+
return preSkip && (await store.requestDbResult(request)).advance(preSkip), startKey && (startPrimaryKey ? (await store.requestDbResult(request)).continuePrimaryKey(startKey, startPrimaryKey) : (await store.requestDbResult(request)).continue(startKey)), request;
|
|
39
|
+
}
|
|
40
|
+
function getMapperFn(mapper, keyPath, useArrayRecord) {
|
|
41
|
+
return Array.isArray(mapper) ? (v) => basic.copyFields({}, v, mapper) : types.isFunction(mapper) ? mapper : keyPath ? (v) => v : useArrayRecord ? (v, k) => [v, k] : (key, value) => ({ key, value });
|
|
42
|
+
}
|
|
43
|
+
class DataOperationBase {
|
|
44
|
+
idbPro;
|
|
45
|
+
target;
|
|
46
|
+
#storeSchema;
|
|
47
|
+
constructor(schema, db2) {
|
|
48
|
+
this.idbPro = db2, this.#storeSchema = schema.storeSchema, this.target = schema.target, type$1.isNativeTarget(schema.target) && (this.tx = this.#nativeOperation);
|
|
49
|
+
}
|
|
50
|
+
get storeName() {
|
|
51
|
+
return this.nativeStore?.name || this.target.store;
|
|
52
|
+
}
|
|
53
|
+
get storeSchema() {
|
|
54
|
+
if (this.#storeSchema) return this.#storeSchema;
|
|
55
|
+
const storeSchema = this.idbPro.getStoreSchema(this.storeName);
|
|
56
|
+
return Object.isFrozen(storeSchema) && (this.#storeSchema = storeSchema), storeSchema;
|
|
57
|
+
}
|
|
58
|
+
get factory() {
|
|
59
|
+
return this.idbPro.factory;
|
|
60
|
+
}
|
|
61
|
+
get nativeStore() {
|
|
62
|
+
const { target } = this;
|
|
63
|
+
if (target instanceof IDBObjectStore) return target;
|
|
64
|
+
if (target instanceof IDBIndex) return target.objectStore;
|
|
65
|
+
}
|
|
66
|
+
get keyPath() {
|
|
67
|
+
const { target } = this;
|
|
68
|
+
if (type$1.isNativeTarget(target))
|
|
69
|
+
return target.keyPath;
|
|
70
|
+
const { storeSchema } = this, { index } = target;
|
|
71
|
+
return index ? storeSchema.indexSchemas.find((i) => i.name === index)?.keyPath : storeSchema.keyPath;
|
|
72
|
+
}
|
|
73
|
+
async forEach(arg, returns) {
|
|
74
|
+
return arg = types.isFunction(arg) ? { fn: arg } : arg, returns ? this.cursorResult(arg, !1) : this.cursorVoid(arg, !1);
|
|
75
|
+
}
|
|
76
|
+
async tx(writable, fn, rollbackOnError) {
|
|
77
|
+
let { target } = this;
|
|
78
|
+
const { store: store2, index } = target, db2 = await this.idbPro.openNativeDb();
|
|
79
|
+
let tx2, nativeStore;
|
|
80
|
+
try {
|
|
81
|
+
tx2 = db2.transaction(store2, writable === !0 ? "readwrite" : "readonly"), target = nativeStore = tx2.objectStore(store2), index && (target = target.index(index));
|
|
82
|
+
} catch (e) {
|
|
83
|
+
throw db2.close(), e;
|
|
84
|
+
}
|
|
85
|
+
if (!fn)
|
|
86
|
+
return Object.freeze({ db: db2, tx: tx2, nativeStore, target });
|
|
87
|
+
try {
|
|
88
|
+
if (writable === !0) {
|
|
89
|
+
const result = await fn(target, nativeStore);
|
|
90
|
+
return tx2.commit(), result;
|
|
91
|
+
}
|
|
92
|
+
return fn(target);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
throw rollbackOnError !== !1 && tx2.abort(), e;
|
|
95
|
+
} finally {
|
|
96
|
+
db2.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
openCursor(arg, writable) {
|
|
100
|
+
return this.tx(writable, (store2) => new Promise(async (resolve, reject) => {
|
|
101
|
+
const { fn } = arg, request = await openCursor(store2, arg);
|
|
102
|
+
request.onsuccess = async () => {
|
|
103
|
+
request.result ? await fn(request.result) === !1 && resolve() : resolve();
|
|
104
|
+
}, request.onerror = () => reject(request.error);
|
|
105
|
+
}));
|
|
106
|
+
}
|
|
107
|
+
cursorVoid({ query, direction, preSkip, startKey, startPrimaryKey, fn }, writable) {
|
|
108
|
+
let i = 0;
|
|
109
|
+
return this.openCursor({
|
|
110
|
+
query,
|
|
111
|
+
direction,
|
|
112
|
+
preSkip,
|
|
113
|
+
startKey,
|
|
114
|
+
startPrimaryKey,
|
|
115
|
+
fn: async (cursor) => {
|
|
116
|
+
const { value: ov, primaryKey: op } = cursor, result = await fn(ov, op, i++), {
|
|
117
|
+
control,
|
|
118
|
+
key,
|
|
119
|
+
primaryKey,
|
|
120
|
+
modify,
|
|
121
|
+
value
|
|
122
|
+
} = types.isObject(result) ? result : { control: result };
|
|
123
|
+
switch (writable && (modify === type$1.Save ? cursor.update(value || ov) : modify === type$1.Delete && cursor.delete()), control) {
|
|
124
|
+
case type$1.Break:
|
|
125
|
+
return !1;
|
|
126
|
+
case type$1.ContinueKey:
|
|
127
|
+
cursor.continue(key);
|
|
128
|
+
break;
|
|
129
|
+
case type$1.ContinuePrimaryKey:
|
|
130
|
+
cursor.continuePrimaryKey(key, primaryKey);
|
|
131
|
+
break;
|
|
132
|
+
default:
|
|
133
|
+
cursor.continue();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}, !0);
|
|
137
|
+
}
|
|
138
|
+
async cursorResult({
|
|
139
|
+
query,
|
|
140
|
+
direction,
|
|
141
|
+
preSkip,
|
|
142
|
+
startKey,
|
|
143
|
+
startPrimaryKey,
|
|
144
|
+
fn,
|
|
145
|
+
mapper: mpr
|
|
146
|
+
}, writable) {
|
|
147
|
+
const { keyPath, defaultGetMapper } = this.storeSchema, mapper = getMapperFn(mpr || defaultGetMapper, keyPath), results = [];
|
|
148
|
+
let i = 0;
|
|
149
|
+
return await this.openCursor({
|
|
150
|
+
query,
|
|
151
|
+
direction,
|
|
152
|
+
preSkip,
|
|
153
|
+
startKey,
|
|
154
|
+
startPrimaryKey,
|
|
155
|
+
fn: async (cursor) => {
|
|
156
|
+
const { value: ov, primaryKey: op } = cursor, {
|
|
157
|
+
control,
|
|
158
|
+
value,
|
|
159
|
+
key,
|
|
160
|
+
primaryKey,
|
|
161
|
+
modify
|
|
162
|
+
} = await fn?.(ov, op, i, results) || {};
|
|
163
|
+
switch (writable && (modify === type$1.Save ? cursor.update(value || ov) : modify === type$1.Delete && cursor.delete()), (!control || control === type$1.Finished || control === type$1.NextKey || control === type$1.NextPrimaryKey) && results.push(mapper(value || ov, primaryKey || op, i)), control) {
|
|
164
|
+
case type$1.Break:
|
|
165
|
+
case type$1.Finished:
|
|
166
|
+
return !1;
|
|
167
|
+
case type$1.NextKey:
|
|
168
|
+
case type$1.ContinueKey:
|
|
169
|
+
cursor.continue(key);
|
|
170
|
+
break;
|
|
171
|
+
case type$1.NextPrimaryKey:
|
|
172
|
+
case type$1.ContinuePrimaryKey:
|
|
173
|
+
cursor.continuePrimaryKey(key, primaryKey);
|
|
174
|
+
break;
|
|
175
|
+
default:
|
|
176
|
+
cursor.continue();
|
|
177
|
+
}
|
|
178
|
+
i++;
|
|
179
|
+
}
|
|
180
|
+
}, writable), results;
|
|
181
|
+
}
|
|
182
|
+
// noinspection JSUnusedLocalSymbols
|
|
183
|
+
#nativeOperation(writable, fn) {
|
|
184
|
+
const { target } = this, nativeStore = target instanceof IDBObjectStore ? target : target.objectStore;
|
|
185
|
+
return fn ? fn(target, nativeStore) : Object.freeze({ nativeStore, target });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
class DataOperators {
|
|
189
|
+
idbPro;
|
|
190
|
+
schemas;
|
|
191
|
+
#_storeNames;
|
|
192
|
+
constructor(idbPro, schemas) {
|
|
193
|
+
this.idbPro = idbPro, this.schemas = schemas;
|
|
194
|
+
}
|
|
195
|
+
get storeNames() {
|
|
196
|
+
return this.#_storeNames || (this.#_storeNames = Array.from(new Set(this.schemas.map((s) => s.target.store))));
|
|
197
|
+
}
|
|
198
|
+
read(fn) {
|
|
199
|
+
return this.tx("newReader", fn);
|
|
200
|
+
}
|
|
201
|
+
write(fn, rollbackOnError = !0) {
|
|
202
|
+
return this.tx("newWriter", fn, !0, rollbackOnError);
|
|
203
|
+
}
|
|
204
|
+
export() {
|
|
205
|
+
return this.tx("newReader", async (...stores) => {
|
|
206
|
+
const result = {};
|
|
207
|
+
for (const store2 of stores)
|
|
208
|
+
result[store2.storeName] = await store2.export();
|
|
209
|
+
return result;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
import(data, returns, use) {
|
|
213
|
+
return use || (use = "addOrChangeMany"), this.tx("newWriter", async (...stores) => {
|
|
214
|
+
const result = {};
|
|
215
|
+
stores = Array.from(new Map(stores.map((s) => [s.storeName, s.asStore(!0)])).values());
|
|
216
|
+
for (const store2 of stores) {
|
|
217
|
+
const { storeName } = store2, rows = data[storeName];
|
|
218
|
+
rows && (result[storeName] = await store2[use](rows, returns));
|
|
219
|
+
}
|
|
220
|
+
if (returns) return result;
|
|
221
|
+
}, !0);
|
|
222
|
+
}
|
|
223
|
+
async tx(method, fn, writable, rollbackOnError) {
|
|
224
|
+
const { idbPro, schemas } = this, { factory } = idbPro.schema, db2 = await idbPro.openNativeDb();
|
|
225
|
+
try {
|
|
226
|
+
const tx2 = db2.transaction(this.storeNames, writable ? "readwrite" : "readonly");
|
|
227
|
+
try {
|
|
228
|
+
const stores = schemas.map(({ storeSchema, target: info }) => {
|
|
229
|
+
let target = tx2.objectStore(info.store);
|
|
230
|
+
return info.index && (target = target.index(info.index)), factory[method]({ storeSchema, target }, idbPro);
|
|
231
|
+
});
|
|
232
|
+
return await fn(...stores);
|
|
233
|
+
} catch (e) {
|
|
234
|
+
throw rollbackOnError !== !1 && tx2.abort(), e;
|
|
235
|
+
}
|
|
236
|
+
} finally {
|
|
237
|
+
db2.close();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
class DbIterator extends DataOperationBase {
|
|
242
|
+
direction;
|
|
243
|
+
query;
|
|
244
|
+
writable;
|
|
245
|
+
parser;
|
|
246
|
+
endsWithNull;
|
|
247
|
+
preSkip;
|
|
248
|
+
startKey;
|
|
249
|
+
startPrimaryKey;
|
|
250
|
+
constructor(schema, db2, option) {
|
|
251
|
+
if (super(schema, db2), !option) return;
|
|
252
|
+
const { parser } = option;
|
|
253
|
+
this.direction = option.direction, this.query = option.query, this.writable = !!option.writable, this.endsWithNull = !!option.endsWithNull, this.preSkip = option.preSkip, this.startKey = option.startKey, this.startPrimaryKey = option.startPrimaryKey, parser && (this.parser = types.isFunction(parser) ? parser : type$1.DbIteratorParsers[parser]);
|
|
254
|
+
}
|
|
255
|
+
async *[Symbol.asyncIterator]() {
|
|
256
|
+
const { parser, writable, endsWithNull } = this, { db: db2, tx: tx2, target } = await this.tx(writable);
|
|
257
|
+
try {
|
|
258
|
+
const request = await openCursor(target, this);
|
|
259
|
+
let cursor;
|
|
260
|
+
if (parser)
|
|
261
|
+
for (; cursor = await store.requestDbResult(request); ) {
|
|
262
|
+
const { control, value } = await parser(cursor);
|
|
263
|
+
if (control || (yield value), control === type$1.Break) break;
|
|
264
|
+
cursor.continue();
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
let ended = !1;
|
|
268
|
+
const end = () => {
|
|
269
|
+
ended = !0;
|
|
270
|
+
};
|
|
271
|
+
for (; !ended && (cursor = await store.requestDbResult(request)); )
|
|
272
|
+
yield { cursor, end };
|
|
273
|
+
}
|
|
274
|
+
writable && tx2?.commit(), endsWithNull && !cursor && (yield null);
|
|
275
|
+
} finally {
|
|
276
|
+
db2?.close();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
function parseFilterArg(arg1, arg2, arg3, limit) {
|
|
281
|
+
const args = types.isObject(arg1) ? arg1 : {};
|
|
282
|
+
return types.isFunction(arg1) ? args.fn = arg1 : isIDbQuery(arg1) && (args.query = arg1), types.isFunction(arg2) ? args.fn = arg2 : arg2 && (args.direction = arg2), arg3 && (args.direction = arg3), limit ? args.limit = limit : args.limit || (args.limit = 1e3), args.maxEmptyChecks || (args.maxEmptyChecks = 2e4), args;
|
|
283
|
+
}
|
|
284
|
+
async function queryPage(anyReader, info, arg) {
|
|
285
|
+
info.size || (info.size = 100), info.nextSkip || (info.nextSkip = 0);
|
|
286
|
+
const { query, direction, total, maxEmptyChecks, fn } = info;
|
|
287
|
+
return await anyReader.batchRead(async (nativeReader) => (total || (info.total = await nativeReader.count({ query, direction, maxEmptyChecks, fn }), info.pages = Math.ceil(info.total / info.size)), info.total < 1 ? { info: basic.deepFreeze(info), rows: [] } : fn ? await queryFnPage(nativeReader, info, arg) : await queryNoneFnPage(nativeReader, info)));
|
|
288
|
+
}
|
|
289
|
+
async function queryNoneFnPage(nativeReader, info) {
|
|
290
|
+
const { keyPath } = nativeReader.storeSchema, { page, query, direction, size, mapper } = info, preSkip = (page - 1) * size, rows = await nativeReader.filter({
|
|
291
|
+
query,
|
|
292
|
+
preSkip,
|
|
293
|
+
direction,
|
|
294
|
+
limit: size,
|
|
295
|
+
mapper: getMapperFn(mapper, keyPath, !0)
|
|
296
|
+
});
|
|
297
|
+
return info.nextSkip = preSkip + rows.length, {
|
|
298
|
+
info: basic.deepFreeze(info),
|
|
299
|
+
rows
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
async function queryFnPage(reader, info, arg) {
|
|
303
|
+
info.maxEmptyChecks || (info.maxEmptyChecks = 2e4);
|
|
304
|
+
const { page, query, direction, nextSkip } = info, { keyPath } = reader.storeSchema;
|
|
305
|
+
return page === 1 ? await queryFnRow(reader, await openCursor(reader.target, { query, direction }), info, keyPath) : arg && nextSkip && page - arg.page === 1 ? await queryFnRow(reader, await openCursor(reader.target, {
|
|
306
|
+
query,
|
|
307
|
+
direction,
|
|
308
|
+
preSkip: nextSkip
|
|
309
|
+
}), info, keyPath) : await queryFnRow(reader, await openCursor(reader.target, { query, direction }), info, keyPath, !0);
|
|
310
|
+
}
|
|
311
|
+
async function queryFnRow({ storeSchema: { defaultGetMapper } }, request, info, keyPath, hasSkip) {
|
|
312
|
+
const { page, size, total, maxEmptyChecks, fn } = info, mapper = getMapperFn(info.mapper || defaultGetMapper, keyPath, !0), rows = [], preSkip = (page - 1) * info.size;
|
|
313
|
+
if (preSkip >= total) return { info: basic.deepFreeze(info), rows: [] };
|
|
314
|
+
let i = 0;
|
|
315
|
+
return await new Promise(async (resolve, reject) => {
|
|
316
|
+
let ept = 0, skipped = 0;
|
|
317
|
+
const rowFn = async () => {
|
|
318
|
+
const { result: cursor } = request;
|
|
319
|
+
if (!cursor) return resolve();
|
|
320
|
+
let { value, primaryKey } = cursor;
|
|
321
|
+
if (await fn(value, primaryKey, i) ? (ept = 0, rows.push(mapper(value, primaryKey, i))) : ept++, rows.length >= size || ept >= maxEmptyChecks) return resolve();
|
|
322
|
+
i++, cursor.continue();
|
|
323
|
+
};
|
|
324
|
+
request.onerror = () => reject(request.error), hasSkip ? request.onsuccess = async () => {
|
|
325
|
+
const { result: cursor } = request;
|
|
326
|
+
if (!cursor)
|
|
327
|
+
return resolve();
|
|
328
|
+
let { value, primaryKey } = cursor;
|
|
329
|
+
if (await fn(value, primaryKey, i) ? (ept = 0, skipped++) : ept++, skipped >= preSkip || ept >= maxEmptyChecks) {
|
|
330
|
+
i = ept = 0, request.onsuccess = rowFn, cursor.continue();
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
cursor.continue(), i++;
|
|
334
|
+
} : request.onsuccess = rowFn;
|
|
335
|
+
}), i && (info.nextSkip += i + 1), {
|
|
336
|
+
info: basic.deepFreeze(info),
|
|
337
|
+
rows
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
class DataReader extends DataOperationBase {
|
|
341
|
+
all(query, limit) {
|
|
342
|
+
const param = isIDbQuery(query) ? { query } : query || {};
|
|
343
|
+
return types.isNumber(limit) && (param.count = limit), types.isNumber(param.count) || (param.count = 1e3), param.query = parseIDbQuery(param.query), this.tx(!1, (store$1) => store.requestDbResult(store$1.getAll(param)));
|
|
344
|
+
}
|
|
345
|
+
async count(arg1, arg2, arg3) {
|
|
346
|
+
const args = parseFilterArg(arg1, arg2, arg3), { query, direction, fn } = args;
|
|
347
|
+
if (!fn)
|
|
348
|
+
return await this.tx(!1, (store$1) => store.requestDbResult(store$1.count(parseIDbQuery(query))));
|
|
349
|
+
const { maxEmptyChecks } = args, findFn = fn;
|
|
350
|
+
let count = 0, ept = 0, i = 0;
|
|
351
|
+
return await this.openCursor({
|
|
352
|
+
query,
|
|
353
|
+
direction,
|
|
354
|
+
fn: (cursor) => {
|
|
355
|
+
if (findFn(cursor.value, cursor.primaryKey, i++))
|
|
356
|
+
count++, ept = 0;
|
|
357
|
+
else if (++ept >= maxEmptyChecks)
|
|
358
|
+
return !1;
|
|
359
|
+
cursor.continue();
|
|
360
|
+
}
|
|
361
|
+
}), count;
|
|
362
|
+
}
|
|
363
|
+
get(key) {
|
|
364
|
+
return this.tx(!1, (store$1) => store.requestDbResult(store$1.get(parseIDbQuery(key))));
|
|
365
|
+
}
|
|
366
|
+
getMany(keys, excludeEmpty) {
|
|
367
|
+
return this.batchRead(async (reader) => {
|
|
368
|
+
const rows = await basic.asyncMap(keys, (k) => reader.get(k));
|
|
369
|
+
return excludeEmpty ? rows.filter((v) => v) : rows;
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
getRange(query, direction) {
|
|
373
|
+
return this.forEach({ query, direction }, !0);
|
|
374
|
+
}
|
|
375
|
+
async getRangeMany(keys, direction, excludeEmpty) {
|
|
376
|
+
return (await this.batchRead((w) => basic.asyncMap(keys, (v) => w.getRange(v, direction)))).flat();
|
|
377
|
+
}
|
|
378
|
+
index(name, writable) {
|
|
379
|
+
let { target } = this;
|
|
380
|
+
return target instanceof IDBIndex && (target = target.objectStore), target instanceof IDBObjectStore ? (target = target.index(name), this.createOperator(target, writable)) : this.idbPro.store(target.store, name);
|
|
381
|
+
}
|
|
382
|
+
asStore(writable) {
|
|
383
|
+
let { target } = this;
|
|
384
|
+
if (target instanceof IDBObjectStore)
|
|
385
|
+
return this;
|
|
386
|
+
if (target instanceof IDBIndex)
|
|
387
|
+
target = target.objectStore;
|
|
388
|
+
else {
|
|
389
|
+
if (!("index" in target))
|
|
390
|
+
return this;
|
|
391
|
+
target = { store: target.store };
|
|
392
|
+
}
|
|
393
|
+
return this.createOperator(target, writable);
|
|
394
|
+
}
|
|
395
|
+
batchRead(fn) {
|
|
396
|
+
return type$1.isNativeTarget(this.target) ? fn(this) : this.tx(!1, (store2) => fn(this.idbPro.schema.factory.newReader({
|
|
397
|
+
storeSchema: this.storeSchema,
|
|
398
|
+
target: store2
|
|
399
|
+
}, this.idbPro)));
|
|
400
|
+
}
|
|
401
|
+
iterator(query, direction) {
|
|
402
|
+
const arg = {};
|
|
403
|
+
return types.isString(direction) && (arg.direction = direction), isIDbQuery(query) ? arg.query = query : types.isObject(query) && Object.assign(arg, query), new DbIterator({ storeSchema: this.storeSchema, target: this.target }, this.idbPro, {
|
|
404
|
+
...arg,
|
|
405
|
+
parser: "value"
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
async filter(arg1, arg2, arg3, arg4) {
|
|
409
|
+
const args = parseFilterArg(arg1, arg2, arg3, arg4), { maxEmptyChecks, limit, fn } = args, { keyPath, defaultGetMapper } = this.storeSchema, mapper = getMapperFn(args.mapper || defaultGetMapper, keyPath, !0);
|
|
410
|
+
if (!fn)
|
|
411
|
+
return await this.forEach({
|
|
412
|
+
...args,
|
|
413
|
+
mapper,
|
|
414
|
+
fn: (value, k, i) => {
|
|
415
|
+
if (i >= limit - 1)
|
|
416
|
+
return { control: type$1.Finished };
|
|
417
|
+
}
|
|
418
|
+
}, !0);
|
|
419
|
+
let ept = 0;
|
|
420
|
+
return this.forEach({
|
|
421
|
+
...args,
|
|
422
|
+
mapper,
|
|
423
|
+
fn: (value, k, i, results) => {
|
|
424
|
+
if (fn(value, k, i))
|
|
425
|
+
ept = 0;
|
|
426
|
+
else
|
|
427
|
+
return ++ept >= maxEmptyChecks ? { control: type$1.Break } : { control: type$1.Continue };
|
|
428
|
+
if (results.length >= limit - 1)
|
|
429
|
+
return { control: type$1.Finished };
|
|
430
|
+
}
|
|
431
|
+
}, !0);
|
|
432
|
+
}
|
|
433
|
+
async find(query, fn, direction) {
|
|
434
|
+
const [value] = await this.filter(query, fn, direction, 1);
|
|
435
|
+
return value;
|
|
436
|
+
}
|
|
437
|
+
async page(arg, page) {
|
|
438
|
+
const info = { ...arg };
|
|
439
|
+
return info.page = page || arg?.page || 1, queryPage(this, info, arg);
|
|
440
|
+
}
|
|
441
|
+
nextPage(info) {
|
|
442
|
+
return this.page(info, info.page + 1);
|
|
443
|
+
}
|
|
444
|
+
export(arg1, arg2) {
|
|
445
|
+
const args = parseFilterArg(arg1, arg2), { keyPath, exportMapper, name, defaultGetMapper } = this.storeSchema;
|
|
446
|
+
if (!keyPath && Array.isArray(exportMapper))
|
|
447
|
+
throw new Error(`When store [ ${name} ] keyPath does not exist, exportMapper does not support string[].`);
|
|
448
|
+
return args.direction = "next", args.mapper = getMapperFn(exportMapper || defaultGetMapper, keyPath, !0), args.limit = args.maxEmptyChecks = Number.MAX_SAFE_INTEGER, this.filter(args);
|
|
449
|
+
}
|
|
450
|
+
asMap() {
|
|
451
|
+
let { target } = this;
|
|
452
|
+
return target instanceof IDBIndex ? target = target.objectStore : target instanceof IDBObjectStore || (target = { store: target.store }), this.factory.newDbMap({ target }, this.idbPro);
|
|
453
|
+
}
|
|
454
|
+
createOperator(target, writable) {
|
|
455
|
+
const { idbPro } = this, tmp = this.storeSchema, schema = { storeSchema: Object.isFrozen(tmp) ? tmp : void 0, target };
|
|
456
|
+
return writable ? this.factory.newWriter(schema, idbPro) : this.factory.newReader(schema, idbPro);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
function getKeyValueToObject(keyPath, value) {
|
|
460
|
+
if (!Array.isArray(keyPath))
|
|
461
|
+
return { [keyPath]: value };
|
|
462
|
+
const rv = {}, arrayKeyPath = keyPath;
|
|
463
|
+
for (let i = 0; i < arrayKeyPath.length; i++)
|
|
464
|
+
rv[arrayKeyPath[i]] = value[i];
|
|
465
|
+
return rv;
|
|
466
|
+
}
|
|
467
|
+
function getValidKeyValue(keyPath, value) {
|
|
468
|
+
if (!Array.isArray(keyPath))
|
|
469
|
+
return value[keyPath];
|
|
470
|
+
const rv = [];
|
|
471
|
+
for (const k of keyPath) {
|
|
472
|
+
if (!value[k]) return;
|
|
473
|
+
rv.push(value[k]);
|
|
474
|
+
}
|
|
475
|
+
return rv;
|
|
476
|
+
}
|
|
477
|
+
function checkAddValue(storeSchema, value) {
|
|
478
|
+
if (!value || !(value instanceof Object) || Array.isArray(value)) return value;
|
|
479
|
+
const {
|
|
480
|
+
addedTimeField,
|
|
481
|
+
updatedTimeField,
|
|
482
|
+
updatedCountField,
|
|
483
|
+
softDeletedField
|
|
484
|
+
} = storeSchema;
|
|
485
|
+
return value = { ...value }, addedTimeField?.name && !types.isNumber(value[addedTimeField.name]) && (value[addedTimeField.name] = Date.now()), updatedTimeField?.name && !types.isNumber(value[updatedTimeField.name]) && (value[updatedTimeField.name] = Date.now()), softDeletedField?.name && !types.isNumber(value[softDeletedField.name]) && (value[softDeletedField.name] = 0), updatedCountField?.name && (value[updatedCountField.name] = 0), value;
|
|
486
|
+
}
|
|
487
|
+
function checkUpdateValue(storeSchema, newValue, oldValue) {
|
|
488
|
+
if (!newValue || !(newValue instanceof Object) || Array.isArray(newValue)) return newValue;
|
|
489
|
+
const {
|
|
490
|
+
updatedTimeField,
|
|
491
|
+
updatedCountField
|
|
492
|
+
} = storeSchema;
|
|
493
|
+
return newValue = { ...oldValue, ...newValue }, updatedTimeField.name && (newValue[updatedTimeField.name] = Date.now()), updatedCountField.name && (newValue[updatedCountField.name] = (newValue[updatedCountField.name] || 0) + 1), newValue;
|
|
494
|
+
}
|
|
495
|
+
class DataWriter extends DataReader {
|
|
496
|
+
add(record) {
|
|
497
|
+
return this.changeByPk({
|
|
498
|
+
record,
|
|
499
|
+
fn: async (store$1, pk, newValue, oldValue, keyPath) => {
|
|
500
|
+
const { storeSchema } = this;
|
|
501
|
+
return newValue = checkAddValue(storeSchema, newValue), keyPath ? [{ ...newValue, ...getKeyValueToObject(keyPath, await store.requestDbResult(store$1.add(newValue))) }, pk] : [newValue, await store.requestDbResult(store$1.add(newValue, pk))];
|
|
502
|
+
}
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
addMany(records, returns) {
|
|
506
|
+
return this.batchWrite((w) => returns ? basic.asyncMap(records, (v) => w.add(v)) : basic.asyncForEach(records, (v) => w.add(v)), !0);
|
|
507
|
+
}
|
|
508
|
+
addOrSkip(record) {
|
|
509
|
+
return this.batchWrite(async (w) => {
|
|
510
|
+
const { keyPath: storeKeyPath, defaultGetMapper } = w.storeSchema, { key: pk, value } = storeKeyPath ? {
|
|
511
|
+
key: getValidKeyValue(storeKeyPath, record),
|
|
512
|
+
value: record
|
|
513
|
+
} : type$1.parseDbNoneKeyPathRecord(record);
|
|
514
|
+
if (pk) {
|
|
515
|
+
const item = await store.requestDbResult(w.nativeStore.get(pk));
|
|
516
|
+
if (item)
|
|
517
|
+
return getMapperFn(defaultGetMapper, storeKeyPath)?.(item, pk);
|
|
518
|
+
}
|
|
519
|
+
if (w.target instanceof IDBIndex) {
|
|
520
|
+
const { keyPath } = w, keyValue = getValidKeyValue(keyPath, value);
|
|
521
|
+
if (!keyValue) return w.add(record);
|
|
522
|
+
let oldValue = await w.find(keyValue);
|
|
523
|
+
if (oldValue) return oldValue;
|
|
524
|
+
}
|
|
525
|
+
return w.add(record);
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
addOrSkipMany(records, returns) {
|
|
529
|
+
return this.batchWrite((w) => returns ? basic.asyncMap(records, (v) => w.addOrSkip(v)) : basic.asyncForEach(records, (v) => w.addOrSkip(v)), !0);
|
|
530
|
+
}
|
|
531
|
+
replace(record) {
|
|
532
|
+
return this.changeByPk({
|
|
533
|
+
record,
|
|
534
|
+
getOld: !0,
|
|
535
|
+
fn: async (store$1, pk, newValue, oldValue, keyPath) => {
|
|
536
|
+
const { storeSchema } = this, { updatedTimeField, updatedCountField, addedTimeField } = storeSchema;
|
|
537
|
+
return oldValue ? newValue = checkUpdateValue(storeSchema, newValue, {
|
|
538
|
+
[updatedTimeField.name]: oldValue[updatedTimeField.name],
|
|
539
|
+
[updatedCountField.name]: oldValue[updatedCountField.name],
|
|
540
|
+
[addedTimeField.name]: oldValue[addedTimeField.name]
|
|
541
|
+
}) : newValue = checkAddValue(storeSchema, newValue), keyPath ? [{ ...newValue, ...getKeyValueToObject(keyPath, await store.requestDbResult(store$1.put(newValue))) }, pk] : [newValue, await store.requestDbResult(store$1.put(newValue, pk))];
|
|
542
|
+
}
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
replaceMany(records, returns) {
|
|
546
|
+
return this.batchWrite((w) => returns ? basic.asyncMap(records, (v) => w.replace(v)) : basic.asyncForEach(records, (v) => w.replace(v)), !0);
|
|
547
|
+
}
|
|
548
|
+
change(record, throwIfMissing) {
|
|
549
|
+
return this.changeByPk({
|
|
550
|
+
record,
|
|
551
|
+
getOld: !0,
|
|
552
|
+
requiredOld: throwIfMissing,
|
|
553
|
+
requiredPk: throwIfMissing,
|
|
554
|
+
fn: async (store$1, pk, newValue, oldValue, keyPath) => {
|
|
555
|
+
const { storeSchema } = this;
|
|
556
|
+
if (oldValue)
|
|
557
|
+
return newValue = checkUpdateValue(storeSchema, newValue, oldValue), keyPath ? [{ ...newValue, ...getKeyValueToObject(keyPath, await store.requestDbResult(store$1.put(newValue))) }, pk] : [newValue, await store.requestDbResult(store$1.put(newValue, pk))];
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
}
|
|
561
|
+
changeMany(records, option) {
|
|
562
|
+
const {
|
|
563
|
+
returns,
|
|
564
|
+
throwIfMissing
|
|
565
|
+
} = types.isBoolean(option) ? { returns: option } : option || {};
|
|
566
|
+
return this.batchWrite((w) => returns ? basic.asyncMap(records, (v) => w.change(v, throwIfMissing)) : basic.asyncForEach(records, (v) => w.change(v, throwIfMissing)), !0);
|
|
567
|
+
}
|
|
568
|
+
addOrChange(record) {
|
|
569
|
+
return this.changeByPk({
|
|
570
|
+
record,
|
|
571
|
+
getOld: !0,
|
|
572
|
+
fn: async (store$1, pk, newValue, oldValue, keyPath) => {
|
|
573
|
+
const { storeSchema } = this;
|
|
574
|
+
return oldValue ? (newValue = checkUpdateValue(storeSchema, newValue, oldValue), keyPath ? [{ ...newValue, ...getKeyValueToObject(keyPath, await store.requestDbResult(store$1.put(newValue))) }, pk] : [newValue, await store.requestDbResult(store$1.put(newValue, pk))]) : (newValue = checkAddValue(storeSchema, newValue), keyPath ? [{ ...newValue, ...getKeyValueToObject(keyPath, await store.requestDbResult(store$1.add(newValue))) }, pk] : [newValue, await store.requestDbResult(store$1.add(newValue, pk))]);
|
|
575
|
+
}
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
addOrChangeMany(records, returns) {
|
|
579
|
+
return this.batchWrite((w) => returns ? basic.asyncMap(records, (v) => w.addOrChange(v)) : basic.asyncForEach(records, (v) => w.addOrChange(v)), !0);
|
|
580
|
+
}
|
|
581
|
+
delete(pk, returns) {
|
|
582
|
+
const {
|
|
583
|
+
returns: getOld,
|
|
584
|
+
physical
|
|
585
|
+
} = types.isBoolean(returns) ? { returns } : returns || {}, { storeSchema } = this, softDeletedField = storeSchema.softDeletedField, requiredOld = !!(!physical && softDeletedField?.name);
|
|
586
|
+
return this.changeByPk({
|
|
587
|
+
pk,
|
|
588
|
+
getOld: requiredOld || getOld,
|
|
589
|
+
fn: (store2, pk2, newValue, oldValue) => {
|
|
590
|
+
if (requiredOld ? oldValue && (oldValue = checkUpdateValue(storeSchema, { [softDeletedField.name]: type.Bool.True }, oldValue), store2.put(oldValue)) : store2.delete(pk2), !!getOld)
|
|
591
|
+
return [oldValue, pk2];
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
deleteMany(keys, returns) {
|
|
596
|
+
const { returns: hasRtn } = types.isBoolean(returns) ? { returns } : returns || {};
|
|
597
|
+
return this.batchWrite((w) => hasRtn ? basic.asyncMap(keys, (v) => w.delete(v, !0)) : basic.asyncForEach(keys, (v) => w.delete(v)), !0);
|
|
598
|
+
}
|
|
599
|
+
deleteRange(query, returns) {
|
|
600
|
+
const {
|
|
601
|
+
returns: hasRtn,
|
|
602
|
+
physical,
|
|
603
|
+
direction
|
|
604
|
+
} = types.isBoolean(returns) ? { returns } : returns || {}, { name } = this.storeSchema.softDeletedField || {};
|
|
605
|
+
return this.cursor({
|
|
606
|
+
query,
|
|
607
|
+
direction,
|
|
608
|
+
fn: (value) => physical || !name ? { modify: type$1.Delete } : (value[name] = type.Bool.True, { modify: type$1.Save })
|
|
609
|
+
}, hasRtn);
|
|
610
|
+
}
|
|
611
|
+
deleteRangeMany(keys, returns) {
|
|
612
|
+
const option = types.isBoolean(returns) ? { returns } : returns;
|
|
613
|
+
return this.batchWrite((w) => option?.returns ? basic.asyncMap(keys, (v) => w.deleteRange(v, option)) : basic.asyncForEach(keys, (v) => w.deleteRange(v, option)), !0);
|
|
614
|
+
}
|
|
615
|
+
changeRange(arg, returns) {
|
|
616
|
+
let {
|
|
617
|
+
direction = "next",
|
|
618
|
+
query,
|
|
619
|
+
newValue
|
|
620
|
+
} = "newValue" in arg ? arg : { newValue: arg };
|
|
621
|
+
if (query || (query = getValidKeyValue(this.keyPath, newValue)), !query)
|
|
622
|
+
throw new Error(`query is required:${JSON.stringify(arg)}`);
|
|
623
|
+
return this.cursor({
|
|
624
|
+
query,
|
|
625
|
+
direction,
|
|
626
|
+
fn: (value) => value instanceof Object ? { modify: type$1.Save, value: { ...value, ...newValue } } : { modify: type$1.Save, value: newValue }
|
|
627
|
+
}, returns);
|
|
628
|
+
}
|
|
629
|
+
changeRangeMany(args, returns) {
|
|
630
|
+
return this.batchWrite((w) => returns ? basic.asyncMap(args, (v) => w.changeRange(v, returns)) : basic.asyncForEach(args, (v) => w.changeRange(v, returns)), !0);
|
|
631
|
+
}
|
|
632
|
+
cursor(arg, returns) {
|
|
633
|
+
return arg = types.isFunction(arg) ? { fn: arg } : arg || {}, arg.fn ? returns ? this.cursorResult(arg, !0) : this.cursorVoid(arg, !0) : new DbIterator(this, this.idbPro);
|
|
634
|
+
}
|
|
635
|
+
batchWrite(fn, rollbackOnError) {
|
|
636
|
+
const { target } = this;
|
|
637
|
+
if (type$1.isNativeTarget(target))
|
|
638
|
+
try {
|
|
639
|
+
return fn(this);
|
|
640
|
+
} catch (e) {
|
|
641
|
+
throw rollbackOnError !== !1 && (target instanceof IDBIndex ? target.objectStore : target).transaction.abort(), e;
|
|
642
|
+
}
|
|
643
|
+
return this.tx(!0, (store2) => fn(this.idbPro.schema.factory.newWriter({
|
|
644
|
+
storeSchema: this.storeSchema,
|
|
645
|
+
target: store2
|
|
646
|
+
}, this.idbPro)), rollbackOnError);
|
|
647
|
+
}
|
|
648
|
+
changeByPk({ pk, record, fn, requiredPk, getOld, requiredOld, saveMapper, getMapper }) {
|
|
649
|
+
const { storeSchema } = this, { keyPath, defaultSaveMapper, defaultGetMapper } = storeSchema;
|
|
650
|
+
return record && (saveMapper || defaultSaveMapper) && (record = getMapperFn(saveMapper || defaultSaveMapper, keyPath)?.(record)), this.batchWrite(async (w) => {
|
|
651
|
+
let query, newValue = record;
|
|
652
|
+
if (pk)
|
|
653
|
+
query = parseIDbQuery(pk);
|
|
654
|
+
else if (keyPath)
|
|
655
|
+
query = getValidKeyValue(keyPath, record);
|
|
656
|
+
else {
|
|
657
|
+
const { key, value } = type$1.parseDbNoneKeyPathRecord(record);
|
|
658
|
+
query = key, newValue = value;
|
|
659
|
+
}
|
|
660
|
+
if (requiredPk && !query)
|
|
661
|
+
throw new Error(`key is required: ${JSON.stringify(record)}`);
|
|
662
|
+
const oldValue = query && (getOld || requiredOld) ? await store.requestDbResult(w.nativeStore.get(query)) : void 0;
|
|
663
|
+
if (requiredOld && !oldValue)
|
|
664
|
+
throw new Error(`record not found: ${JSON.stringify(record)}`);
|
|
665
|
+
const result = await fn(w.nativeStore, query, newValue, oldValue, keyPath);
|
|
666
|
+
if (result)
|
|
667
|
+
return getMapperFn(getMapper || defaultGetMapper, keyPath)?.(result[0], result[1]);
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
class DbMap extends DataOperationBase {
|
|
672
|
+
get size() {
|
|
673
|
+
return this.tx(!1, (store$1) => store.requestDbResult(store$1.count()));
|
|
674
|
+
}
|
|
675
|
+
delete(key) {
|
|
676
|
+
return this.tx(!0, async (t, store2) => {
|
|
677
|
+
store2.delete(key);
|
|
678
|
+
});
|
|
679
|
+
}
|
|
680
|
+
batch(fn) {
|
|
681
|
+
const { idbPro, storeSchema, factory } = this;
|
|
682
|
+
return this.tx(!0, async (t, store2) => await fn(factory.newDbMap({ storeSchema, target: store2 }, idbPro)));
|
|
683
|
+
}
|
|
684
|
+
asStore(writable) {
|
|
685
|
+
const { factory } = this.idbPro.schema;
|
|
686
|
+
return writable ? factory.newWriter(this, this.idbPro) : factory.newReader({ target: this.target }, this.idbPro);
|
|
687
|
+
}
|
|
688
|
+
entries() {
|
|
689
|
+
return new DbIterator({
|
|
690
|
+
target: this.target
|
|
691
|
+
}, this.idbPro, { parser: "keyValue" });
|
|
692
|
+
}
|
|
693
|
+
async get(key, defaultValue) {
|
|
694
|
+
return await this.tx(!1, (store$1) => store.requestDbResult(store$1.get(key))) || defaultValue;
|
|
695
|
+
}
|
|
696
|
+
getMany(keys) {
|
|
697
|
+
return this.tx(!1, async (store$1) => {
|
|
698
|
+
const result = [];
|
|
699
|
+
for (const k of keys)
|
|
700
|
+
result.push(await store.requestDbResult(store$1.get(k)));
|
|
701
|
+
return result;
|
|
702
|
+
});
|
|
703
|
+
}
|
|
704
|
+
async has(key) {
|
|
705
|
+
return !!await this.get(key);
|
|
706
|
+
}
|
|
707
|
+
keys() {
|
|
708
|
+
return new DbIterator({ storeSchema: this.storeSchema, target: this.target }, this.idbPro, { parser: "key" });
|
|
709
|
+
}
|
|
710
|
+
set(key, value) {
|
|
711
|
+
return this.tx(!0, async (t, store$1) => {
|
|
712
|
+
await store.requestDbResult(store$1.put(value, key));
|
|
713
|
+
});
|
|
714
|
+
}
|
|
715
|
+
setMany(values) {
|
|
716
|
+
return this.tx(!0, async (t, store2) => {
|
|
717
|
+
for (const [k, v] of values)
|
|
718
|
+
store2.put(v, k);
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
values() {
|
|
722
|
+
return new DbIterator({ storeSchema: this.storeSchema, target: this.target }, this.idbPro, { parser: "value" });
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
function equalKeyPath(p1, p2) {
|
|
726
|
+
if (p1 == p2) return !0;
|
|
727
|
+
if (typeof p1 != typeof p2) return !1;
|
|
728
|
+
if (Array.isArray(p1) && Array.isArray(p2)) {
|
|
729
|
+
if (p1.length !== p2.length) return !1;
|
|
730
|
+
for (let i = 0; i < p1.length; i++)
|
|
731
|
+
if (p1[i] !== p2[i]) return !1;
|
|
732
|
+
return !0;
|
|
733
|
+
}
|
|
734
|
+
return !1;
|
|
735
|
+
}
|
|
736
|
+
const versionDiffValidate = ({ stores, schema }) => {
|
|
737
|
+
const storeSchemas = schema.storeSchemas;
|
|
738
|
+
let info = "";
|
|
739
|
+
for (const store2 of stores) {
|
|
740
|
+
const ss = storeSchemas.find((s) => s.name === store2.name);
|
|
741
|
+
if (ss) {
|
|
742
|
+
if (!equalKeyPath(store2.keyPath, ss.keyPath)) {
|
|
743
|
+
info = `store [ ${store2.name} ] keyPath not equal,schema.keyPath:${ss.keyPath},store.keyPath:${store2.keyPath}[]`;
|
|
744
|
+
break;
|
|
745
|
+
}
|
|
746
|
+
if (!store2.autoIncrement != !ss.autoIncrement) {
|
|
747
|
+
info = `store [ ${store2.name} ] autoIncrement not equal`;
|
|
748
|
+
break;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
return info ? `The existing database is inconsistent with the definition and cannot be corrected: ${info}` : !0;
|
|
753
|
+
}, versionSameValidate = async (context) => {
|
|
754
|
+
let result = versionDiffValidate(context);
|
|
755
|
+
return types.isString(result) || (result = validateStoreAndIndexes(context)), result;
|
|
756
|
+
}, validateStoreAndIndexes = ({ stores, schema }) => {
|
|
757
|
+
const storeSchemas = schema.storeSchemas, dbStoreNames = stores.map((s) => s.name);
|
|
758
|
+
let info = "";
|
|
759
|
+
const missingStoreNames = storeSchemas.map((s) => s.name).filter((s) => !dbStoreNames.includes(s));
|
|
760
|
+
if (missingStoreNames.length)
|
|
761
|
+
info = `store [ ${missingStoreNames.join(",")} ] not exist`;
|
|
762
|
+
else for (const store2 of stores) {
|
|
763
|
+
const ss = storeSchemas.find((s) => s.name === store2.name);
|
|
764
|
+
if (ss && (info = validateIndexes$1(store2, Array.from(store2.indexNames), ss.indexSchemas), info))
|
|
765
|
+
break;
|
|
766
|
+
}
|
|
767
|
+
return info ? `The existing database Store index is inconsistent with the definition and requires a database version upgrade to be fixed: ${info}` : !0;
|
|
768
|
+
};
|
|
769
|
+
function validateIndexes$1(store2, indexNames, schemas) {
|
|
770
|
+
if (indexNames.length !== schemas.length)
|
|
771
|
+
return `store [ ${store2.name} ] index count not equal`;
|
|
772
|
+
for (const name of indexNames) {
|
|
773
|
+
const schema = schemas.find((s) => s.name === name);
|
|
774
|
+
if (!schema)
|
|
775
|
+
return `store [ ${store2.name} ] index [ ${name} ] not exist`;
|
|
776
|
+
const index = store2.index(name);
|
|
777
|
+
if (!schema.unique != !index.unique)
|
|
778
|
+
return `store [ ${store2.name} ] index [ ${name} ] unique not equal`;
|
|
779
|
+
if (!schema.multiEntry != !index.multiEntry)
|
|
780
|
+
return `store [ ${store2.name} ] index [ ${name} ] multiEntry not equal`;
|
|
781
|
+
if (!equalKeyPath(schema.keyPath, index.keyPath))
|
|
782
|
+
return `store [ ${store2.name} ] index [ ${name} ] keyPath not equal`;
|
|
783
|
+
}
|
|
784
|
+
return "";
|
|
785
|
+
}
|
|
786
|
+
class StoreUpgradeable {
|
|
787
|
+
upgradeContext;
|
|
788
|
+
storeSchema;
|
|
789
|
+
nativeStore;
|
|
790
|
+
#writer;
|
|
791
|
+
constructor(upgradeContext, storeSchema, nativeStore) {
|
|
792
|
+
this.upgradeContext = upgradeContext, this.storeSchema = storeSchema, this.nativeStore = nativeStore;
|
|
793
|
+
}
|
|
794
|
+
get writer() {
|
|
795
|
+
return this.#writer || (this.#writer = this.upgradeContext.dbSchema.factory?.newWriter({
|
|
796
|
+
target: this.nativeStore,
|
|
797
|
+
storeSchema: this.storeSchema
|
|
798
|
+
}));
|
|
799
|
+
}
|
|
800
|
+
add(versionBounds, values, returns) {
|
|
801
|
+
return this.upgradeContext.versionIn(versionBounds) ? this.writer.addMany(values, returns) : Promise.resolve();
|
|
802
|
+
}
|
|
803
|
+
addOrChange(versionBounds, values, returns) {
|
|
804
|
+
return this.upgradeContext.versionIn(versionBounds) ? this.writer.addOrChangeMany(values, returns) : Promise.resolve();
|
|
805
|
+
}
|
|
806
|
+
async call(versionBounds, fn) {
|
|
807
|
+
if (this.upgradeContext.versionIn(versionBounds))
|
|
808
|
+
return await fn(this.writer, this.upgradeContext);
|
|
809
|
+
}
|
|
810
|
+
replace(versionBounds, values, returns) {
|
|
811
|
+
return this.upgradeContext.versionIn(versionBounds) ? this.writer.replaceMany(values, returns) : Promise.resolve();
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
const DataOperatorFactory = Object.freeze({
|
|
815
|
+
newDataOperators(schemas, db2) {
|
|
816
|
+
return new DataOperators(db2, schemas);
|
|
817
|
+
},
|
|
818
|
+
newDbMap(schema, db2) {
|
|
819
|
+
return new DbMap(schema, db2);
|
|
820
|
+
},
|
|
821
|
+
newReader(schema, db2) {
|
|
822
|
+
return new DataReader(schema, db2);
|
|
823
|
+
},
|
|
824
|
+
newStoreUpgradeable(nativeStore, storeSchema, upgradeContext) {
|
|
825
|
+
return new StoreUpgradeable(upgradeContext, storeSchema, nativeStore);
|
|
826
|
+
},
|
|
827
|
+
newWriter(schema, db2) {
|
|
828
|
+
return new DataWriter(schema, db2);
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
function validateSpecialFields(schema) {
|
|
832
|
+
Object.isFrozen(schema) || (schema.addedTimeField = validateSpecialField(schema, schema.addedTimeField, "added_at", !0), schema.updatedTimeField = validateSpecialField(schema, schema.updatedTimeField, "updated_at", !0), schema.updatedCountField = validateSpecialField(schema, schema.updatedCountField, "updated_count", !1), schema.softDeletedField = validateSpecialField(schema, schema.softDeletedField, "deleted", !1));
|
|
833
|
+
}
|
|
834
|
+
function validateSpecialField(schema, field, defaultName, useDefault) {
|
|
835
|
+
const validField = validateSpecialFieldBase(field, defaultName, useDefault);
|
|
836
|
+
if (!validField)
|
|
837
|
+
return validField;
|
|
838
|
+
const schemaField = validField;
|
|
839
|
+
if (schemaField.isIndexed !== !1) {
|
|
840
|
+
schemaField.isIndexed || (schemaField.isIndexed = !0);
|
|
841
|
+
const { name } = schemaField, indexSchemas = schema.indexSchemas;
|
|
842
|
+
indexSchemas.some((i) => i === name || i.name === name) || indexSchemas.push(name);
|
|
843
|
+
}
|
|
844
|
+
return schemaField;
|
|
845
|
+
}
|
|
846
|
+
function validateSpecialFieldBase(field, defaultName, useDefault) {
|
|
847
|
+
if (field === !1)
|
|
848
|
+
return !1;
|
|
849
|
+
if (types.isString(field))
|
|
850
|
+
return { name: field };
|
|
851
|
+
if (types.isObject(field)) {
|
|
852
|
+
const r = field;
|
|
853
|
+
return types.isBoolean(r.name) && (r.name = defaultName), field;
|
|
854
|
+
} else if (field === !0 || useDefault)
|
|
855
|
+
return { name: defaultName };
|
|
856
|
+
return !1;
|
|
857
|
+
}
|
|
858
|
+
function validateStoreSchema(schema, storeTemplate) {
|
|
859
|
+
let validSchema = types.isString(schema) ? { name: schema } : schema;
|
|
860
|
+
return storeTemplate && (validSchema = { ...storeTemplate, ...validSchema }), validSchema.indexSchemas || (validSchema.indexSchemas = []), validateSpecialFields(validSchema), Object.isFrozen(validSchema) || (validSchema.indexSchemas = validSchema.indexSchemas.map(indexMapper)), validateDefaultData(validSchema), validSchema;
|
|
861
|
+
}
|
|
862
|
+
function indexMapper(index) {
|
|
863
|
+
const schema = types.isString(index) ? { name: index } : index;
|
|
864
|
+
return schema.keyPath || (schema.keyPath = schema.name), schema;
|
|
865
|
+
}
|
|
866
|
+
function validateDefaultData(schema) {
|
|
867
|
+
if (!(schema.keyPath || !schema.defaultData?.length)) {
|
|
868
|
+
for (const row of schema.defaultData)
|
|
869
|
+
if (!Array.isArray(row) && !("value" in row))
|
|
870
|
+
throw new Error(`When \`defaultData\` must contain \`value\` fields or be an array:${JSON.stringify(row)}`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
const validateSchemaWithDefaults = (schema) => {
|
|
874
|
+
const { versionDiffValidate: vdf, versionSameValidate: vsf, factory: dof } = schema;
|
|
875
|
+
return schema.storeSchemas || (schema.storeSchemas = []), schema.storeTemplate || (schema.storeTemplate = { ...type$1.defaultStoreSchemaTemplate }), !vdf && vdf !== !1 && (schema.versionDiffValidate = versionDiffValidate), !vsf && vsf !== !1 && (schema.versionSameValidate = versionSameValidate), dof ? dof !== DataOperatorFactory && (schema.factory = { ...DataOperatorFactory, ...dof }) : schema.factory = DataOperatorFactory, schema.storeSchemas = schema.storeSchemas.map((s) => validateStoreSchema(s, schema.storeTemplate)), schema;
|
|
876
|
+
};
|
|
877
|
+
async function validateBeforeOpen(schema) {
|
|
878
|
+
const { name, version } = schema, existDb = await db.findExistDb(name);
|
|
879
|
+
if (!existDb)
|
|
880
|
+
return !0;
|
|
881
|
+
const { versionDiffValidate: versionDiffValidate2, versionSameValidate: versionSameValidate2 } = schema, db$1 = await db.openDb(name);
|
|
882
|
+
try {
|
|
883
|
+
if (schema.version < db$1.version)
|
|
884
|
+
return "The existing database version is greater than the current version";
|
|
885
|
+
const validate = schema.version === void 0 || db$1.version === schema.version ? versionSameValidate2 : versionDiffValidate2;
|
|
886
|
+
if (!validate)
|
|
887
|
+
return !0;
|
|
888
|
+
const storeNames = Array.from(db$1.objectStoreNames);
|
|
889
|
+
if (storeNames.length < 1)
|
|
890
|
+
return `The existing database [ ${name} ] is empty`;
|
|
891
|
+
const stores = await tx.readTx(db$1, storeNames), diffResult = await validate({ schema, db: db$1, stores });
|
|
892
|
+
if (types.isString(diffResult) || version !== existDb.version)
|
|
893
|
+
return diffResult;
|
|
894
|
+
} finally {
|
|
895
|
+
db$1?.close();
|
|
896
|
+
}
|
|
897
|
+
return !0;
|
|
898
|
+
}
|
|
899
|
+
function validateIndexes(storeSchema, store2) {
|
|
900
|
+
const { indexSchemas } = storeSchema, existNames = store2.indexNames, schemaNames = indexSchemas.map((option) => option.name);
|
|
901
|
+
for (const name of Array.from(existNames))
|
|
902
|
+
schemaNames.includes(name) || store2.deleteIndex(name);
|
|
903
|
+
for (const indexSchema of indexSchemas)
|
|
904
|
+
existNames.contains(indexSchema.name) ? validateIndex(store2, indexSchema) : createIndex(store2, indexSchema);
|
|
905
|
+
return store2;
|
|
906
|
+
}
|
|
907
|
+
function validateIndex(store2, indexOption) {
|
|
908
|
+
const index = store2.index(indexOption.name);
|
|
909
|
+
checkIndex(index, indexOption) || (store2.deleteIndex(indexOption.name), createIndex(store2, indexOption));
|
|
910
|
+
}
|
|
911
|
+
function checkIndex(index, indexOption) {
|
|
912
|
+
return index.unique !== indexOption.unique || index.multiEntry !== indexOption.multiEntry ? !1 : equalKeyPath(indexOption.keyPath, index.keyPath);
|
|
913
|
+
}
|
|
914
|
+
function createIndex(store2, schema) {
|
|
915
|
+
try {
|
|
916
|
+
store2.createIndex(schema.name, schema.keyPath, {
|
|
917
|
+
unique: schema.unique,
|
|
918
|
+
multiEntry: schema.multiEntry
|
|
919
|
+
});
|
|
920
|
+
} catch {
|
|
921
|
+
throw new Error(`store [ ${store2.name} ] index [ ${schema.name} ] create error: ${JSON.stringify(schema)}`);
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
function validateStoreDefine(context, schema) {
|
|
925
|
+
return context.database.objectStoreNames.contains(schema.name) ? validateIndexes(schema, context.transaction?.objectStore(schema.name)) : defineStore(schema, context.database);
|
|
926
|
+
}
|
|
927
|
+
function defineStore(schema, db2) {
|
|
928
|
+
const store2 = db2.createObjectStore(schema.name, {
|
|
929
|
+
keyPath: schema.keyPath,
|
|
930
|
+
autoIncrement: schema.autoIncrement
|
|
931
|
+
});
|
|
932
|
+
for (const option of schema.indexSchemas)
|
|
933
|
+
createIndex(store2, option);
|
|
934
|
+
return store2;
|
|
935
|
+
}
|
|
936
|
+
class UpgradeContext {
|
|
937
|
+
database;
|
|
938
|
+
newVersion;
|
|
939
|
+
oldVersion;
|
|
940
|
+
dbSchema;
|
|
941
|
+
transaction;
|
|
942
|
+
#stores = {};
|
|
943
|
+
constructor(args) {
|
|
944
|
+
this.database = args.database, this.newVersion = args.newVersion, this.oldVersion = args.oldVersion, this.dbSchema = args.dbSchema, this.transaction = args.transaction;
|
|
945
|
+
}
|
|
946
|
+
deleteStoreIfExists(storeName) {
|
|
947
|
+
const db2 = this.database;
|
|
948
|
+
db2.objectStoreNames.contains(storeName) && db2.deleteObjectStore(storeName);
|
|
949
|
+
}
|
|
950
|
+
destroy() {
|
|
951
|
+
try {
|
|
952
|
+
basic.destroyRecords(this.#stores);
|
|
953
|
+
} finally {
|
|
954
|
+
for (const k of Object.keys(this.#stores))
|
|
955
|
+
delete this.#stores[k];
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
store(storeName) {
|
|
959
|
+
if (storeName in this.#stores)
|
|
960
|
+
return this.#stores[storeName];
|
|
961
|
+
const { factory } = this.dbSchema, { storeSchemas } = this.dbSchema, storeSchema = storeSchemas.find((s) => s.name === storeName), nativeStore = validateStoreDefine(this, storeSchema);
|
|
962
|
+
return this.#stores[storeName] = factory.newStoreUpgradeable(nativeStore, storeSchema, this);
|
|
963
|
+
}
|
|
964
|
+
versionIn({ oldMin, oldMax, newMax, newMin }) {
|
|
965
|
+
if (oldMax === void 0 && newMax === void 0 && oldMin === void 0 && newMin === void 0)
|
|
966
|
+
throw new Error(`versionIn bounds must not be empty ${JSON.stringify({ oldMax, newMax, oldMin, newMin })}`);
|
|
967
|
+
if (oldMax < oldMin)
|
|
968
|
+
throw new Error(`oldMax (${oldMax}) cannot be less than oldMin (${oldMin})`);
|
|
969
|
+
if (newMax < newMin)
|
|
970
|
+
throw new Error(`newMax (${newMax}) cannot be less than newMin (${newMin})`);
|
|
971
|
+
const { oldVersion, newVersion } = this;
|
|
972
|
+
return oldMin !== void 0 && oldVersion < oldMin || oldMax !== void 0 && oldVersion > oldMax || newMin !== void 0 && newVersion < newMin ? !1 : !(newMax !== void 0 && newVersion > newMax);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
async function upgradeDb(dbSchema, database, e, request) {
|
|
976
|
+
const { storeSchemas, beforeUpgrade, afterUpgrade, version } = dbSchema, { newVersion = version, oldVersion } = e, { transaction } = request, context = new UpgradeContext({ database, newVersion, oldVersion, dbSchema, transaction });
|
|
977
|
+
try {
|
|
978
|
+
const errors = [];
|
|
979
|
+
if (types.isFunction(beforeUpgrade)) try {
|
|
980
|
+
await beforeUpgrade(context);
|
|
981
|
+
} catch (e2) {
|
|
982
|
+
errors.push(e2);
|
|
983
|
+
}
|
|
984
|
+
for (const storeName of storeSchemas.map((s) => s.name))
|
|
985
|
+
try {
|
|
986
|
+
context.store(storeName);
|
|
987
|
+
} catch (e2) {
|
|
988
|
+
errors.push(e2);
|
|
989
|
+
}
|
|
990
|
+
for (const { name, defaultData } of storeSchemas)
|
|
991
|
+
if (defaultData) try {
|
|
992
|
+
await context.store(name).add({ oldMax: 0 }, defaultData);
|
|
993
|
+
} catch (e2) {
|
|
994
|
+
errors.push(e2);
|
|
995
|
+
}
|
|
996
|
+
for (const { name, versionData } of storeSchemas)
|
|
997
|
+
if (versionData) for (const { version: version2, data, use = "addOrChange" } of versionData)
|
|
998
|
+
try {
|
|
999
|
+
await context.store(name)[use](version2, data);
|
|
1000
|
+
} catch (e2) {
|
|
1001
|
+
errors.push(e2);
|
|
1002
|
+
}
|
|
1003
|
+
for (const { name, storeDefined } of storeSchemas)
|
|
1004
|
+
try {
|
|
1005
|
+
await storeDefined?.(context.store(name));
|
|
1006
|
+
} catch (e2) {
|
|
1007
|
+
errors.push(e2);
|
|
1008
|
+
}
|
|
1009
|
+
if (types.isFunction(afterUpgrade)) try {
|
|
1010
|
+
await afterUpgrade(context);
|
|
1011
|
+
} catch (e2) {
|
|
1012
|
+
errors.push(e2);
|
|
1013
|
+
}
|
|
1014
|
+
if (!errors.length) return;
|
|
1015
|
+
throw errors.length === 1 ? errors[0] : new AggregateError(errors, "Database upgrade error");
|
|
1016
|
+
} finally {
|
|
1017
|
+
basic.destroy(context);
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
function validateDataOperationSchema(schema, dbSchema) {
|
|
1021
|
+
return Object.isFrozen(dbSchema) ? frozenValidate(schema) : initValidate(schema, dbSchema);
|
|
1022
|
+
}
|
|
1023
|
+
function frozenValidate(schema) {
|
|
1024
|
+
let { store: store2, index } = schema;
|
|
1025
|
+
const target = { store: types.isString(store2) ? store2 : store2.name };
|
|
1026
|
+
return index && (target.index = types.isString(index) ? index : index.name), {
|
|
1027
|
+
target
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
function initValidate(schema, dbSchema) {
|
|
1031
|
+
let { store: store2, index } = schema;
|
|
1032
|
+
const { storeTemplate } = dbSchema, { storeSchemas = [] } = dbSchema, storeName = types.isString(store2) ? store2 : store2.name, storeIndex = storeSchemas.findIndex((s) => s === storeName || s.name === storeName), existStore = storeIndex > -1 && storeSchemas[storeIndex];
|
|
1033
|
+
let tmp;
|
|
1034
|
+
types.isString(store2) ? tmp = existStore || storeName : !existStore || types.isString(existStore) || store2 === existStore ? tmp = store2 : tmp = { ...existStore, ...store2 }, (index || existStore?.indexSchemas?.length || store2?.indexSchemas?.length) && (types.isString(tmp) && (tmp = { name: tmp }), tmp.indexSchemas = mergeIndexes(existStore.indexSchemas || [], store2.indexSchemas || [], index));
|
|
1035
|
+
const validSchema = validateStoreSchema(tmp, storeTemplate);
|
|
1036
|
+
storeIndex > -1 ? storeSchemas[storeIndex] = validSchema : storeSchemas.push(validSchema), dbSchema.storeSchemas = storeSchemas;
|
|
1037
|
+
const target = { store: storeName };
|
|
1038
|
+
return index && (target.index = types.isString(index) ? index : index.name), {
|
|
1039
|
+
target
|
|
1040
|
+
};
|
|
1041
|
+
}
|
|
1042
|
+
function mergeIndexes(existIndexes, newIndexes, index) {
|
|
1043
|
+
index && newIndexes.push(index);
|
|
1044
|
+
for (const index2 of newIndexes) {
|
|
1045
|
+
const indexName = types.isString(index2) ? index2 : index2.name, existIndex = existIndexes.findIndex((i) => i === indexName || i.name === indexName);
|
|
1046
|
+
if (existIndex > -1) {
|
|
1047
|
+
const old = existIndexes[existIndex];
|
|
1048
|
+
types.isString(old) ? existIndexes[existIndex] = index2 : types.isString(index2) || (existIndexes[existIndex] = Object.assign(old, index2));
|
|
1049
|
+
} else
|
|
1050
|
+
existIndexes.push(index2);
|
|
1051
|
+
}
|
|
1052
|
+
return existIndexes;
|
|
1053
|
+
}
|
|
1054
|
+
const mapStoreSchema = Object.assign({
|
|
1055
|
+
name: "",
|
|
1056
|
+
addedTimeField: !1,
|
|
1057
|
+
autoIncrement: !1,
|
|
1058
|
+
indexSchemas: [],
|
|
1059
|
+
keyPath: void 0,
|
|
1060
|
+
softDeletedField: !1,
|
|
1061
|
+
updatedCountField: !1,
|
|
1062
|
+
updatedTimeField: !1
|
|
1063
|
+
});
|
|
1064
|
+
class IDbPro {
|
|
1065
|
+
static #_db;
|
|
1066
|
+
#schema;
|
|
1067
|
+
#beforeUseValid;
|
|
1068
|
+
#storeSchemaRecord = {};
|
|
1069
|
+
constructor(schema, skipPreOpenValidation) {
|
|
1070
|
+
this.#schema = schema = types.isString(schema) ? { name: schema } : schema, Array.isArray(schema.storeSchemas) || (schema.storeSchemas = []), types.isObject(schema.storeTemplate) || (schema.storeTemplate = type$1.defaultStoreSchemaTemplate), skipPreOpenValidation && (this.#beforeUseValid = !0);
|
|
1071
|
+
}
|
|
1072
|
+
/**
|
|
1073
|
+
* 默认实例
|
|
1074
|
+
*/
|
|
1075
|
+
static get defaultDb() {
|
|
1076
|
+
return this.#_db || (this.#_db = new IDbPro(storage.DefaultDbName));
|
|
1077
|
+
}
|
|
1078
|
+
get initialized() {
|
|
1079
|
+
return Object.isFrozen(this.#schema);
|
|
1080
|
+
}
|
|
1081
|
+
get schema() {
|
|
1082
|
+
return this.#schema;
|
|
1083
|
+
}
|
|
1084
|
+
get storeNames() {
|
|
1085
|
+
return Array.from(new Set(this.#schema.storeSchemas.map((s) => types.isString(s) ? s : s.name)));
|
|
1086
|
+
}
|
|
1087
|
+
get factory() {
|
|
1088
|
+
return this.#schema.factory || (this.#schema.factory = DataOperatorFactory);
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* 释放默认数据库实例(如果存在)
|
|
1092
|
+
*/
|
|
1093
|
+
static releaseDefaultDB() {
|
|
1094
|
+
this.#_db = void 0;
|
|
1095
|
+
}
|
|
1096
|
+
/**
|
|
1097
|
+
* 从已经存在的`name`数据库中打开
|
|
1098
|
+
* @warning 该方法仅用于不需要修改原有数据库结构的场景
|
|
1099
|
+
* - 如果需要修改,请使用 `generateDbSchema()` 从现有数据库生成配置,且适当修改后重新打开
|
|
1100
|
+
* @param name
|
|
1101
|
+
*/
|
|
1102
|
+
static async openExistDb(name) {
|
|
1103
|
+
const { generateDbSchema: generateDbSchema2 } = await Promise.resolve().then(function() {
|
|
1104
|
+
return generateDbSchema$1;
|
|
1105
|
+
});
|
|
1106
|
+
return new IDbPro(await generateDbSchema2(name));
|
|
1107
|
+
}
|
|
1108
|
+
static store(arg1, index) {
|
|
1109
|
+
return IDbPro.defaultDb.store(arg1, index);
|
|
1110
|
+
}
|
|
1111
|
+
static stores(schemas) {
|
|
1112
|
+
return IDbPro.defaultDb.stores(schemas);
|
|
1113
|
+
}
|
|
1114
|
+
static map(storeName, defaultData) {
|
|
1115
|
+
return IDbPro.defaultDb.map(storeName, defaultData);
|
|
1116
|
+
}
|
|
1117
|
+
async openNativeDb() {
|
|
1118
|
+
const schema = this.#schema = basic.deepFreeze(this.initSchema());
|
|
1119
|
+
await this.#isBeforeUseValidate();
|
|
1120
|
+
const { name, version } = schema;
|
|
1121
|
+
return await db.openDb({
|
|
1122
|
+
name,
|
|
1123
|
+
version,
|
|
1124
|
+
onupgradeneeded: (db2, e, request) => upgradeDb(schema, db2, e, request)
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
store(arg1, index) {
|
|
1128
|
+
const operation = arg1.store ? arg1 : { store: arg1 };
|
|
1129
|
+
return operation.store || (operation.store = arg1), operation.index || (operation.index = index), this.factory.newWriter(validateDataOperationSchema(operation, this.schema), this);
|
|
1130
|
+
}
|
|
1131
|
+
stores(schemas) {
|
|
1132
|
+
const { schema } = this, results = schemas.map((store2) => validateDataOperationSchema(types.isString(store2) ? { store: store2 } : store2, schema));
|
|
1133
|
+
return this.factory.newDataOperators(results, this);
|
|
1134
|
+
}
|
|
1135
|
+
initSchema() {
|
|
1136
|
+
if (this.initialized) return this.#schema;
|
|
1137
|
+
const { validateSchemaWithDefaults: validate = validateSchemaWithDefaults } = this.#schema;
|
|
1138
|
+
return this.#schema = validate(this.#schema);
|
|
1139
|
+
}
|
|
1140
|
+
async traceSchema(showFn) {
|
|
1141
|
+
await json.logJson(this.schema, showFn);
|
|
1142
|
+
}
|
|
1143
|
+
map(storeName, defaultData) {
|
|
1144
|
+
const name = types.isString(storeName) ? storeName : types.isString(defaultData) ? defaultData : storage.DefaultStorageStoreName;
|
|
1145
|
+
Array.isArray(storeName) && (defaultData = storeName);
|
|
1146
|
+
const { storeSchemas } = this.schema;
|
|
1147
|
+
return storeSchemas.find((s) => s.name === name || s === name) || storeSchemas.push({ ...mapStoreSchema, name, defaultData }), this.factory.newDbMap({ target: { store: name } }, this);
|
|
1148
|
+
}
|
|
1149
|
+
export() {
|
|
1150
|
+
return this.stores(this.storeNames).export();
|
|
1151
|
+
}
|
|
1152
|
+
import(data, returns, use) {
|
|
1153
|
+
return this.stores(this.storeNames).import(data, returns, use);
|
|
1154
|
+
}
|
|
1155
|
+
getStoreSchema(name) {
|
|
1156
|
+
if (name in this.#storeSchemaRecord)
|
|
1157
|
+
return this.#storeSchemaRecord[name];
|
|
1158
|
+
const index = this.schema.storeSchemas.findIndex((s) => s === name || s.name === name);
|
|
1159
|
+
let storeSchema = this.schema.storeSchemas[index];
|
|
1160
|
+
return this.initialized ? this.#storeSchemaRecord[name] = storeSchema : types.isString(storeSchema) && (this.schema.storeSchemas[index] = storeSchema = { name: storeSchema }), storeSchema;
|
|
1161
|
+
}
|
|
1162
|
+
async #isBeforeUseValidate() {
|
|
1163
|
+
let valid = this.#beforeUseValid;
|
|
1164
|
+
if (valid === void 0 && (valid = this.#beforeUseValid = await validateBeforeOpen(this.#schema)), valid === !0)
|
|
1165
|
+
return !0;
|
|
1166
|
+
if (types.isString(valid))
|
|
1167
|
+
throw new Error(valid);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
function dbStore(arg1, index) {
|
|
1171
|
+
return IDbPro.defaultDb.store(arg1, index);
|
|
1172
|
+
}
|
|
1173
|
+
function dbMap(storeName, defaultData) {
|
|
1174
|
+
return IDbPro.defaultDb.map(storeName, defaultData);
|
|
1175
|
+
}
|
|
1176
|
+
function dbStores(schemas) {
|
|
1177
|
+
return IDbPro.defaultDb.stores(schemas);
|
|
1178
|
+
}
|
|
1179
|
+
function releaseDefaultDB() {
|
|
1180
|
+
IDbPro.releaseDefaultDB();
|
|
1181
|
+
}
|
|
1182
|
+
function generateStoreSchema(store2, fields) {
|
|
1183
|
+
const { name, keyPath, autoIncrement } = store2;
|
|
1184
|
+
return {
|
|
1185
|
+
name,
|
|
1186
|
+
keyPath,
|
|
1187
|
+
autoIncrement,
|
|
1188
|
+
...parseIndexes(store2, fields)
|
|
1189
|
+
};
|
|
1190
|
+
}
|
|
1191
|
+
function parseIndexes(store2, fields) {
|
|
1192
|
+
const indexes = Array.from(store2.indexNames).map((n) => store2.index(n)), indexSchemas = [], paths = [];
|
|
1193
|
+
for (const { name, keyPath, unique, multiEntry } of indexes)
|
|
1194
|
+
Array.isArray(keyPath) ? paths.push(...keyPath) : paths.push(keyPath), indexSchemas.push({ name, keyPath, unique, multiEntry });
|
|
1195
|
+
const keySet = new Set(paths);
|
|
1196
|
+
return {
|
|
1197
|
+
indexSchemas,
|
|
1198
|
+
addedTimeField: convertSpecialField(fields.addedTimeField, keySet),
|
|
1199
|
+
updatedTimeField: convertSpecialField(fields.updatedTimeField, keySet),
|
|
1200
|
+
updatedCountField: convertSpecialField(fields.updatedCountField, keySet),
|
|
1201
|
+
softDeletedField: convertSpecialField(fields.softDeletedField, keySet)
|
|
1202
|
+
};
|
|
1203
|
+
}
|
|
1204
|
+
function convertSpecialField(field, keySet) {
|
|
1205
|
+
return keySet.has(field) ? { name: field, isIndexed: !1 } : !1;
|
|
1206
|
+
}
|
|
1207
|
+
async function generateDbSchema(db$1, option) {
|
|
1208
|
+
if (!await db.findExistDb(db$1))
|
|
1209
|
+
throw new Error(`db [ ${db$1} ] not exist`);
|
|
1210
|
+
let { asString, specialFields = type$1.defaultSpecialFields, dataExportTarget } = option || {};
|
|
1211
|
+
asString === !0 && (asString = 160), isNaN(asString) || asString < 1 && (asString = 1);
|
|
1212
|
+
let dbSchema = await generateRoot(db$1, specialFields);
|
|
1213
|
+
return dataExportTarget && await generateData(dbSchema, dataExportTarget), asString ? await json.toJson({
|
|
1214
|
+
rootData$: dbSchema,
|
|
1215
|
+
spaceEffectiveLength: asString
|
|
1216
|
+
}) : dbSchema;
|
|
1217
|
+
}
|
|
1218
|
+
async function generateData(dbSchema, dataExportTarget) {
|
|
1219
|
+
const data = await new IDbPro(basic.copyObject(dbSchema), !0).export();
|
|
1220
|
+
for (const schema of dbSchema.storeSchemas) {
|
|
1221
|
+
const rows = data[schema.name];
|
|
1222
|
+
rows?.length && (dataExportTarget === "defaultData" ? schema.defaultData = rows : dataExportTarget === "versionData" && (schema.versionData || (schema.versionData = []), schema.versionData.push({
|
|
1223
|
+
version: { oldMax: dbSchema.version },
|
|
1224
|
+
data: rows
|
|
1225
|
+
})));
|
|
1226
|
+
}
|
|
1227
|
+
return dbSchema;
|
|
1228
|
+
}
|
|
1229
|
+
function generateRoot(db$1, specialFields) {
|
|
1230
|
+
return db.openDb(db$1, async function(existDb) {
|
|
1231
|
+
const names = Array.from(existDb.objectStoreNames), tx2 = existDb.transaction(names, "readonly");
|
|
1232
|
+
try {
|
|
1233
|
+
return {
|
|
1234
|
+
name: existDb.name,
|
|
1235
|
+
version: existDb.version,
|
|
1236
|
+
storeSchemas: names.map((name) => generateStoreSchema(tx2.objectStore(name), specialFields))
|
|
1237
|
+
};
|
|
1238
|
+
} finally {
|
|
1239
|
+
tx2.abort();
|
|
1240
|
+
}
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
var generateDbSchema$1 = /* @__PURE__ */ Object.freeze({
|
|
1244
|
+
__proto__: null,
|
|
1245
|
+
generateDbSchema
|
|
1246
|
+
});
|
|
1247
|
+
exports.DataOperationBase = DataOperationBase, exports.DataOperators = DataOperators, exports.DataReader = DataReader, exports.DataWriter = DataWriter, exports.DbIterator = DbIterator, exports.DbMap = DbMap, exports.IDbPro = IDbPro, exports.StoreUpgradeable = StoreUpgradeable, exports.UpgradeContext = UpgradeContext, exports.dbMap = dbMap, exports.dbStore = dbStore, exports.dbStores = dbStores, exports.generateDbSchema = generateDbSchema, exports.isIDbQuery = isIDbQuery, exports.parseIDbQuery = parseIDbQuery, exports.releaseDefaultDB = releaseDefaultDB, exports.validateSchemaWithDefaults = validateSchemaWithDefaults, exports.versionDiffValidate = versionDiffValidate, exports.versionSameValidate = versionSameValidate;
|