idb-ts 3.3.0 → 3.8.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/README.md +335 -22
- package/lib/index.d.ts +76 -23
- package/lib/index.esm.js +488 -138
- package/lib/index.js +488 -138
- package/lib/jest.setup.d.ts +1 -0
- package/lib/jest.setup.js +6 -0
- package/package.json +14 -2
package/lib/index.esm.js
CHANGED
|
@@ -1,60 +1,298 @@
|
|
|
1
1
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
2
|
-
exports.DataClass = exports.KeyPath = exports.Database = void 0;
|
|
2
|
+
exports.KeyGenerators = exports.Index = exports.DataClass = exports.CompositeKeyPath = exports.KeyPath = exports.Database = void 0;
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
require("reflect-metadata");
|
|
5
|
-
|
|
5
|
+
class QueryBuilder {
|
|
6
|
+
constructor(db, storeName) {
|
|
7
|
+
this.conditions = [];
|
|
8
|
+
this.orderDirection = 'asc';
|
|
9
|
+
this.db = db;
|
|
10
|
+
this.storeName = storeName;
|
|
11
|
+
}
|
|
12
|
+
where(field) {
|
|
13
|
+
this.currentField = field;
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
and(field) {
|
|
17
|
+
this.currentField = field;
|
|
18
|
+
return this;
|
|
19
|
+
}
|
|
20
|
+
equals(value) {
|
|
21
|
+
if (!this.currentField)
|
|
22
|
+
throw new Error('No field specified for equals');
|
|
23
|
+
this.conditions.push({ field: this.currentField, op: 'equals', value });
|
|
24
|
+
this.currentField = undefined;
|
|
25
|
+
return this;
|
|
26
|
+
}
|
|
27
|
+
gt(value) {
|
|
28
|
+
if (!this.currentField)
|
|
29
|
+
throw new Error('No field specified for gt');
|
|
30
|
+
this.conditions.push({ field: this.currentField, op: 'gt', value });
|
|
31
|
+
this.currentField = undefined;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
gte(value) {
|
|
35
|
+
if (!this.currentField)
|
|
36
|
+
throw new Error('No field specified for gte');
|
|
37
|
+
this.conditions.push({ field: this.currentField, op: 'gte', value });
|
|
38
|
+
this.currentField = undefined;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
lt(value) {
|
|
42
|
+
if (!this.currentField)
|
|
43
|
+
throw new Error('No field specified for lt');
|
|
44
|
+
this.conditions.push({ field: this.currentField, op: 'lt', value });
|
|
45
|
+
this.currentField = undefined;
|
|
46
|
+
return this;
|
|
47
|
+
}
|
|
48
|
+
lte(value) {
|
|
49
|
+
if (!this.currentField)
|
|
50
|
+
throw new Error('No field specified for lte');
|
|
51
|
+
this.conditions.push({ field: this.currentField, op: 'lte', value });
|
|
52
|
+
this.currentField = undefined;
|
|
53
|
+
return this;
|
|
54
|
+
}
|
|
55
|
+
orderBy(field, direction = 'asc') {
|
|
56
|
+
this.orderField = field;
|
|
57
|
+
this.orderDirection = direction;
|
|
58
|
+
return this;
|
|
59
|
+
}
|
|
60
|
+
limit(n) {
|
|
61
|
+
this.limitCount = n;
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
offset(n) {
|
|
65
|
+
this.offsetCount = n;
|
|
66
|
+
return this;
|
|
67
|
+
}
|
|
68
|
+
useIndex(indexName) {
|
|
69
|
+
this.indexName = indexName;
|
|
70
|
+
return this;
|
|
71
|
+
}
|
|
72
|
+
range(start, end) {
|
|
73
|
+
this.rangeStart = start;
|
|
74
|
+
this.rangeEnd = end;
|
|
75
|
+
return this;
|
|
76
|
+
}
|
|
77
|
+
execute() {
|
|
78
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const tx = this.db.transaction(this.storeName, 'readonly');
|
|
81
|
+
let store = tx.objectStore(this.storeName);
|
|
82
|
+
let request;
|
|
83
|
+
let results = [];
|
|
84
|
+
if (this.indexName) {
|
|
85
|
+
if (!store.indexNames.contains(this.indexName)) {
|
|
86
|
+
reject(new Error(`Index '${this.indexName}' does not exist on ${this.storeName}`));
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const index = store.index(this.indexName);
|
|
90
|
+
let keyRange;
|
|
91
|
+
if (this.rangeStart !== undefined && this.rangeEnd !== undefined) {
|
|
92
|
+
keyRange = IDBKeyRange.bound(this.rangeStart, this.rangeEnd);
|
|
93
|
+
}
|
|
94
|
+
else if (this.rangeStart !== undefined) {
|
|
95
|
+
keyRange = IDBKeyRange.lowerBound(this.rangeStart);
|
|
96
|
+
}
|
|
97
|
+
else if (this.rangeEnd !== undefined) {
|
|
98
|
+
keyRange = IDBKeyRange.upperBound(this.rangeEnd);
|
|
99
|
+
}
|
|
100
|
+
request = index.openCursor(keyRange);
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
request = store.openCursor();
|
|
104
|
+
}
|
|
105
|
+
request.onsuccess = () => {
|
|
106
|
+
const cursor = request.result;
|
|
107
|
+
if (cursor) {
|
|
108
|
+
let match = true;
|
|
109
|
+
const value = cursor.value;
|
|
110
|
+
for (const cond of this.conditions) {
|
|
111
|
+
const val = value[cond.field];
|
|
112
|
+
switch (cond.op) {
|
|
113
|
+
case 'equals':
|
|
114
|
+
match = match && val === cond.value;
|
|
115
|
+
break;
|
|
116
|
+
case 'gt':
|
|
117
|
+
match = match && val > cond.value;
|
|
118
|
+
break;
|
|
119
|
+
case 'gte':
|
|
120
|
+
match = match && val >= cond.value;
|
|
121
|
+
break;
|
|
122
|
+
case 'lt':
|
|
123
|
+
match = match && val < cond.value;
|
|
124
|
+
break;
|
|
125
|
+
case 'lte':
|
|
126
|
+
match = match && val <= cond.value;
|
|
127
|
+
break;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (match)
|
|
131
|
+
results.push(value);
|
|
132
|
+
cursor.continue();
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
if (this.orderField) {
|
|
136
|
+
results.sort((a, b) => {
|
|
137
|
+
const va = a[this.orderField];
|
|
138
|
+
const vb = b[this.orderField];
|
|
139
|
+
if (va < vb)
|
|
140
|
+
return this.orderDirection === 'asc' ? -1 : 1;
|
|
141
|
+
if (va > vb)
|
|
142
|
+
return this.orderDirection === 'asc' ? 1 : -1;
|
|
143
|
+
return 0;
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
if (this.offsetCount !== undefined)
|
|
147
|
+
results = results.slice(this.offsetCount);
|
|
148
|
+
if (this.limitCount !== undefined)
|
|
149
|
+
results = results.slice(0, this.limitCount);
|
|
150
|
+
resolve(results);
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
request.onerror = () => reject(request.error);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
class KeyGenerators {
|
|
159
|
+
static uuid() {
|
|
160
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
|
161
|
+
const r = Math.random() * 16 | 0;
|
|
162
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
163
|
+
return v.toString(16);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
static timestamp() {
|
|
167
|
+
return Date.now();
|
|
168
|
+
}
|
|
169
|
+
static random() {
|
|
170
|
+
return Math.random().toString(36).substring(2, 15);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
exports.KeyGenerators = KeyGenerators;
|
|
174
|
+
function KeyPath(options) {
|
|
6
175
|
return (target, propertyKey) => {
|
|
7
176
|
const constructor = target.constructor;
|
|
8
|
-
Reflect.
|
|
177
|
+
const existingKeypaths = Reflect.getMetadata("individual_keypaths", constructor) || [];
|
|
178
|
+
existingKeypaths.push(propertyKey);
|
|
179
|
+
Reflect.defineMetadata("individual_keypaths", existingKeypaths, constructor);
|
|
180
|
+
const metadata = {
|
|
181
|
+
fields: propertyKey,
|
|
182
|
+
options: options
|
|
183
|
+
};
|
|
184
|
+
Reflect.defineMetadata("keypath", metadata, constructor);
|
|
9
185
|
};
|
|
10
186
|
}
|
|
11
187
|
exports.KeyPath = KeyPath;
|
|
12
|
-
function
|
|
188
|
+
function CompositeKeyPath(fields, options) {
|
|
189
|
+
return (target) => {
|
|
190
|
+
const metadata = {
|
|
191
|
+
fields: fields,
|
|
192
|
+
options: options
|
|
193
|
+
};
|
|
194
|
+
Reflect.defineMetadata("keypath", metadata, target);
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
exports.CompositeKeyPath = CompositeKeyPath;
|
|
198
|
+
function Index() {
|
|
199
|
+
return (target, propertyKey) => {
|
|
200
|
+
const constructor = target.constructor;
|
|
201
|
+
const existing = Reflect.getMetadata("indexes", constructor) || [];
|
|
202
|
+
Reflect.defineMetadata("indexes", [...existing, propertyKey], constructor);
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
exports.Index = Index;
|
|
206
|
+
function DataClass(options = {}) {
|
|
13
207
|
return (target) => {
|
|
14
|
-
|
|
208
|
+
const keyPathMetadata = Reflect.getMetadata("keypath", target);
|
|
209
|
+
if (!keyPathMetadata) {
|
|
15
210
|
throw new Error(`No keypath field defined for the class ${target.name}.`);
|
|
16
211
|
}
|
|
17
|
-
const
|
|
18
|
-
if (
|
|
212
|
+
const individualKeypaths = Reflect.getMetadata("individual_keypaths", target) || [];
|
|
213
|
+
if (individualKeypaths.length > 1) {
|
|
19
214
|
throw new Error(`Only one keypath field can be defined for the class ${target.name}.`);
|
|
20
215
|
}
|
|
216
|
+
const version = options.version || 1;
|
|
21
217
|
Reflect.defineMetadata("dataclass", true, target);
|
|
218
|
+
Reflect.defineMetadata("version", version, target);
|
|
22
219
|
};
|
|
23
220
|
}
|
|
24
221
|
exports.DataClass = DataClass;
|
|
25
222
|
class Database {
|
|
26
223
|
constructor(dbName, classes) {
|
|
27
224
|
this.db = null;
|
|
225
|
+
this.entityRepositories = new Map();
|
|
28
226
|
this.dbName = dbName;
|
|
29
227
|
if (!classes.every(cls => Reflect.getMetadata("dataclass", cls))) {
|
|
30
228
|
throw new Error("All classes should be decorated with @DataClass.");
|
|
31
229
|
}
|
|
32
230
|
this.classes = classes;
|
|
231
|
+
this.dbVersion = this.calculateDatabaseVersion();
|
|
232
|
+
}
|
|
233
|
+
calculateDatabaseVersion() {
|
|
234
|
+
const versions = this.classes.map(cls => Reflect.getMetadata("version", cls) || 1);
|
|
235
|
+
return Math.max(...versions);
|
|
33
236
|
}
|
|
34
237
|
static build(dbName, classes) {
|
|
35
238
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
36
239
|
const instance = new Database(dbName, classes);
|
|
37
240
|
yield instance.initDB();
|
|
241
|
+
instance.generateEntityRepositories();
|
|
38
242
|
return instance;
|
|
39
243
|
});
|
|
40
244
|
}
|
|
41
245
|
initDB() {
|
|
42
246
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
43
247
|
return new Promise((resolve, reject) => {
|
|
44
|
-
const request = indexedDB.open(this.dbName,
|
|
45
|
-
request.onupgradeneeded = () => {
|
|
248
|
+
const request = indexedDB.open(this.dbName, this.dbVersion);
|
|
249
|
+
request.onupgradeneeded = (event) => {
|
|
46
250
|
const db = request.result;
|
|
251
|
+
const oldVersion = event.oldVersion;
|
|
252
|
+
const newVersion = event.newVersion || this.dbVersion;
|
|
253
|
+
console.debug(`Database upgrade from version ${oldVersion} to ${newVersion}`);
|
|
47
254
|
this.classes.forEach((cls) => {
|
|
48
|
-
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
255
|
+
var _a;
|
|
256
|
+
const keyPathMetadata = Reflect.getMetadata("keypath", cls);
|
|
257
|
+
const indexFields = Reflect.getMetadata("indexes", cls) || [];
|
|
258
|
+
const classVersion = Reflect.getMetadata("version", cls) || 1;
|
|
259
|
+
const storeName = cls.name.toLowerCase();
|
|
260
|
+
if (classVersion > oldVersion) {
|
|
261
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
262
|
+
console.debug(`Creating object store: ${storeName} (version ${classVersion})`);
|
|
263
|
+
const storeOptions = {};
|
|
264
|
+
if (keyPathMetadata) {
|
|
265
|
+
storeOptions.keyPath = keyPathMetadata.fields;
|
|
266
|
+
if ((_a = keyPathMetadata.options) === null || _a === void 0 ? void 0 : _a.autoIncrement) {
|
|
267
|
+
storeOptions.autoIncrement = true;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
const store = db.createObjectStore(storeName, storeOptions);
|
|
271
|
+
indexFields.forEach((indexField) => {
|
|
272
|
+
if (!store.indexNames.contains(indexField)) {
|
|
273
|
+
store.createIndex(indexField, indexField, { unique: false });
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
console.debug(`Updating object store: ${storeName} (version ${classVersion})`);
|
|
279
|
+
const transaction = request.transaction;
|
|
280
|
+
if (transaction) {
|
|
281
|
+
const store = transaction.objectStore(storeName);
|
|
282
|
+
indexFields.forEach((indexField) => {
|
|
283
|
+
if (!store.indexNames.contains(indexField)) {
|
|
284
|
+
console.debug(`Adding index: ${indexField} to ${storeName}`);
|
|
285
|
+
store.createIndex(indexField, indexField, { unique: false });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
52
290
|
}
|
|
53
291
|
});
|
|
54
292
|
};
|
|
55
293
|
request.onsuccess = () => {
|
|
56
294
|
this.db = request.result;
|
|
57
|
-
console.debug(`Database initialized with object stores for: ${this.classes.map(cls => cls.name).join(", ")}`);
|
|
295
|
+
console.debug(`Database initialized (version ${this.dbVersion}) with object stores for: ${this.classes.map(cls => `${cls.name}(v${Reflect.getMetadata("version", cls) || 1})`).join(", ")}`);
|
|
58
296
|
resolve();
|
|
59
297
|
};
|
|
60
298
|
request.onerror = () => {
|
|
@@ -64,141 +302,253 @@ class Database {
|
|
|
64
302
|
});
|
|
65
303
|
});
|
|
66
304
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
return new Promise((resolve, reject) => {
|
|
78
|
-
try {
|
|
79
|
-
const store = this.getObjectStore(cls.name, "readwrite");
|
|
80
|
-
const request = store.add(item);
|
|
81
|
-
request.onsuccess = () => {
|
|
82
|
-
console.debug(`Item added to ${cls.name}:`, item);
|
|
83
|
-
resolve();
|
|
84
|
-
};
|
|
85
|
-
request.onerror = () => {
|
|
86
|
-
console.error(`Error adding item to ${cls.name}:`, request.error);
|
|
87
|
-
reject(request.error);
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
reject(error);
|
|
92
|
-
}
|
|
305
|
+
generateEntityRepositories() {
|
|
306
|
+
this.classes.forEach(cls => {
|
|
307
|
+
const entityName = cls.name;
|
|
308
|
+
const repository = this.createEntityRepository(cls);
|
|
309
|
+
this.entityRepositories.set(entityName, repository);
|
|
310
|
+
Object.defineProperty(this, entityName, {
|
|
311
|
+
value: repository,
|
|
312
|
+
writable: false,
|
|
313
|
+
enumerable: true,
|
|
314
|
+
configurable: false
|
|
93
315
|
});
|
|
94
316
|
});
|
|
95
317
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
318
|
+
createEntityRepository(cls) {
|
|
319
|
+
const self = this;
|
|
320
|
+
const generateKey = (item) => {
|
|
321
|
+
var _a;
|
|
322
|
+
const keyPathMetadata = Reflect.getMetadata("keypath", cls);
|
|
323
|
+
if (!((_a = keyPathMetadata === null || keyPathMetadata === void 0 ? void 0 : keyPathMetadata.options) === null || _a === void 0 ? void 0 : _a.generator))
|
|
324
|
+
return undefined;
|
|
325
|
+
const generator = keyPathMetadata.options.generator;
|
|
326
|
+
if (typeof generator === 'function') {
|
|
327
|
+
return generator(item);
|
|
328
|
+
}
|
|
329
|
+
switch (generator) {
|
|
330
|
+
case 'uuid':
|
|
331
|
+
return KeyGenerators.uuid();
|
|
332
|
+
case 'timestamp':
|
|
333
|
+
return KeyGenerators.timestamp();
|
|
334
|
+
case 'random':
|
|
335
|
+
return KeyGenerators.random();
|
|
336
|
+
default:
|
|
337
|
+
return undefined;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const extractKey = (item) => {
|
|
341
|
+
const keyPathMetadata = Reflect.getMetadata("keypath", cls);
|
|
342
|
+
if (!keyPathMetadata)
|
|
343
|
+
return undefined;
|
|
344
|
+
const fields = keyPathMetadata.fields;
|
|
345
|
+
if (Array.isArray(fields)) {
|
|
346
|
+
return fields.map(field => item[field]);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
return item[fields];
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
const setKey = (item, key) => {
|
|
353
|
+
const keyPathMetadata = Reflect.getMetadata("keypath", cls);
|
|
354
|
+
if (!keyPathMetadata)
|
|
355
|
+
return;
|
|
356
|
+
const fields = keyPathMetadata.fields;
|
|
357
|
+
if (typeof fields === 'string') {
|
|
358
|
+
item[fields] = key;
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
return {
|
|
362
|
+
query() {
|
|
363
|
+
if (!self.db)
|
|
364
|
+
throw new Error('Database not initialized.');
|
|
365
|
+
const storeName = cls.name.toLowerCase();
|
|
366
|
+
return new QueryBuilder(self.db, storeName);
|
|
367
|
+
},
|
|
368
|
+
create: (item) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
369
|
+
var _a;
|
|
370
|
+
const keyPathMetadata = Reflect.getMetadata("keypath", cls);
|
|
371
|
+
if (((_a = keyPathMetadata === null || keyPathMetadata === void 0 ? void 0 : keyPathMetadata.options) === null || _a === void 0 ? void 0 : _a.generator) && !keyPathMetadata.options.autoIncrement) {
|
|
372
|
+
const currentKey = extractKey(item);
|
|
373
|
+
if (currentKey === undefined || currentKey === null || currentKey === '') {
|
|
374
|
+
const generatedKey = generateKey(item);
|
|
375
|
+
if (generatedKey !== undefined) {
|
|
376
|
+
setKey(item, generatedKey);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
113
379
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
380
|
+
return this.performOperation(cls.name, 'readwrite', (store) => {
|
|
381
|
+
const request = store.add(item);
|
|
382
|
+
return new Promise((resolve, reject) => {
|
|
383
|
+
request.onsuccess = () => {
|
|
384
|
+
console.debug(`Item added to ${cls.name}:`, item);
|
|
385
|
+
resolve();
|
|
386
|
+
};
|
|
387
|
+
request.onerror = () => reject(request.error);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
}),
|
|
391
|
+
read: (key) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
392
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
393
|
+
const request = store.get(key);
|
|
394
|
+
return new Promise((resolve, reject) => {
|
|
395
|
+
request.onsuccess = () => {
|
|
396
|
+
console.debug(`Item read from ${cls.name}:`, request.result);
|
|
397
|
+
resolve(request.result);
|
|
398
|
+
};
|
|
399
|
+
request.onerror = () => reject(request.error);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
}),
|
|
403
|
+
update: (item) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
404
|
+
return this.performOperation(cls.name, 'readwrite', (store) => {
|
|
122
405
|
const request = store.put(item);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
});
|
|
137
|
-
}
|
|
138
|
-
delete(cls, key) {
|
|
139
|
-
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
140
|
-
return new Promise((resolve, reject) => {
|
|
141
|
-
try {
|
|
142
|
-
const store = this.getObjectStore(cls.name, "readwrite");
|
|
406
|
+
return new Promise((resolve, reject) => {
|
|
407
|
+
request.onsuccess = () => {
|
|
408
|
+
console.debug(`Item updated in ${cls.name}:`, item);
|
|
409
|
+
resolve();
|
|
410
|
+
};
|
|
411
|
+
request.onerror = () => reject(request.error);
|
|
412
|
+
});
|
|
413
|
+
});
|
|
414
|
+
}),
|
|
415
|
+
delete: (key) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
416
|
+
return this.performOperation(cls.name, 'readwrite', (store) => {
|
|
143
417
|
const request = store.delete(key);
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
418
|
+
return new Promise((resolve, reject) => {
|
|
419
|
+
request.onsuccess = () => {
|
|
420
|
+
console.debug(`Item deleted from ${cls.name}:`, key);
|
|
421
|
+
resolve();
|
|
422
|
+
};
|
|
423
|
+
request.onerror = () => reject(request.error);
|
|
424
|
+
});
|
|
425
|
+
});
|
|
426
|
+
}),
|
|
427
|
+
list: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
428
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
429
|
+
const request = store.getAll();
|
|
430
|
+
return new Promise((resolve, reject) => {
|
|
431
|
+
request.onsuccess = () => {
|
|
432
|
+
console.debug(`All items from ${cls.name}:`, request.result);
|
|
433
|
+
resolve(request.result);
|
|
434
|
+
};
|
|
435
|
+
request.onerror = () => reject(request.error);
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
}),
|
|
439
|
+
listPaginated: (page, pageSize) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
440
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
441
|
+
const request = store.getAll();
|
|
442
|
+
return new Promise((resolve, reject) => {
|
|
443
|
+
request.onsuccess = () => {
|
|
444
|
+
const items = request.result;
|
|
445
|
+
const paginatedItems = items.slice((page - 1) * pageSize, page * pageSize);
|
|
446
|
+
console.debug(`Paginated items from ${cls.name}:`, paginatedItems);
|
|
447
|
+
resolve(paginatedItems);
|
|
448
|
+
};
|
|
449
|
+
request.onerror = () => reject(request.error);
|
|
450
|
+
});
|
|
451
|
+
});
|
|
452
|
+
}),
|
|
453
|
+
findByIndex: (indexName, value) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
454
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
455
|
+
if (!store.indexNames.contains(indexName)) {
|
|
456
|
+
throw new Error(`Index '${indexName}' does not exist on ${cls.name}`);
|
|
457
|
+
}
|
|
458
|
+
const index = store.index(indexName);
|
|
459
|
+
const request = index.getAll(value);
|
|
460
|
+
return new Promise((resolve, reject) => {
|
|
461
|
+
request.onsuccess = () => {
|
|
462
|
+
console.debug(`Items found by index ${indexName} with value ${value}:`, request.result);
|
|
463
|
+
resolve(request.result);
|
|
464
|
+
};
|
|
465
|
+
request.onerror = () => reject(request.error);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
}),
|
|
469
|
+
findOneByIndex: (indexName, value) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
470
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
471
|
+
if (!store.indexNames.contains(indexName)) {
|
|
472
|
+
throw new Error(`Index '${indexName}' does not exist on ${cls.name}`);
|
|
473
|
+
}
|
|
474
|
+
const index = store.index(indexName);
|
|
475
|
+
const request = index.get(value);
|
|
476
|
+
return new Promise((resolve, reject) => {
|
|
477
|
+
request.onsuccess = () => {
|
|
478
|
+
console.debug(`Item found by index ${indexName} with value ${value}:`, request.result);
|
|
479
|
+
resolve(request.result);
|
|
480
|
+
};
|
|
481
|
+
request.onerror = () => reject(request.error);
|
|
482
|
+
});
|
|
483
|
+
});
|
|
484
|
+
}),
|
|
485
|
+
count: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
486
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
487
|
+
const request = store.count();
|
|
488
|
+
return new Promise((resolve, reject) => {
|
|
489
|
+
request.onsuccess = () => {
|
|
490
|
+
console.debug(`Count for ${cls.name}:`, request.result);
|
|
491
|
+
resolve(request.result);
|
|
492
|
+
};
|
|
493
|
+
request.onerror = () => reject(request.error);
|
|
494
|
+
});
|
|
495
|
+
});
|
|
496
|
+
}),
|
|
497
|
+
exists: (key) => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
498
|
+
return this.performOperation(cls.name, 'readonly', (store) => {
|
|
499
|
+
const request = store.count(key);
|
|
500
|
+
return new Promise((resolve, reject) => {
|
|
501
|
+
request.onsuccess = () => {
|
|
502
|
+
const exists = request.result > 0;
|
|
503
|
+
console.debug(`Exists check for ${cls.name} with key ${key}:`, exists);
|
|
504
|
+
resolve(exists);
|
|
505
|
+
};
|
|
506
|
+
request.onerror = () => reject(request.error);
|
|
507
|
+
});
|
|
508
|
+
});
|
|
509
|
+
}),
|
|
510
|
+
clear: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
511
|
+
return this.performOperation(cls.name, 'readwrite', (store) => {
|
|
512
|
+
const request = store.clear();
|
|
513
|
+
return new Promise((resolve, reject) => {
|
|
514
|
+
request.onsuccess = () => {
|
|
515
|
+
console.debug(`Cleared all items from ${cls.name}`);
|
|
516
|
+
resolve();
|
|
517
|
+
};
|
|
518
|
+
request.onerror = () => reject(request.error);
|
|
519
|
+
});
|
|
520
|
+
});
|
|
521
|
+
})
|
|
522
|
+
};
|
|
158
523
|
}
|
|
159
|
-
|
|
524
|
+
performOperation(className, mode, operation) {
|
|
160
525
|
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
};
|
|
169
|
-
request.onerror = () => {
|
|
170
|
-
console.error(`Error listing items from ${cls.name}:`, request.error);
|
|
171
|
-
reject(request.error);
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
catch (error) {
|
|
175
|
-
reject(error);
|
|
176
|
-
}
|
|
177
|
-
});
|
|
526
|
+
if (!this.db) {
|
|
527
|
+
throw new Error("Database not initialized.");
|
|
528
|
+
}
|
|
529
|
+
const storeName = className.toLowerCase();
|
|
530
|
+
const transaction = this.db.transaction(storeName, mode);
|
|
531
|
+
const store = transaction.objectStore(storeName);
|
|
532
|
+
return operation(store);
|
|
178
533
|
});
|
|
179
534
|
}
|
|
180
|
-
|
|
181
|
-
return
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
};
|
|
192
|
-
request.onerror = () => {
|
|
193
|
-
console.error(`Error listing items from ${cls.name}:`, request.error);
|
|
194
|
-
reject(request.error);
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
reject(error);
|
|
199
|
-
}
|
|
200
|
-
});
|
|
535
|
+
getAvailableEntities() {
|
|
536
|
+
return Array.from(this.entityRepositories.keys());
|
|
537
|
+
}
|
|
538
|
+
getDatabaseVersion() {
|
|
539
|
+
return this.dbVersion;
|
|
540
|
+
}
|
|
541
|
+
getEntityVersions() {
|
|
542
|
+
const versions = new Map();
|
|
543
|
+
this.classes.forEach(cls => {
|
|
544
|
+
const version = Reflect.getMetadata("version", cls) || 1;
|
|
545
|
+
versions.set(cls.name, version);
|
|
201
546
|
});
|
|
547
|
+
return versions;
|
|
548
|
+
}
|
|
549
|
+
getEntityVersion(entityName) {
|
|
550
|
+
const cls = this.classes.find(c => c.name === entityName);
|
|
551
|
+
return cls ? (Reflect.getMetadata("version", cls) || 1) : undefined;
|
|
202
552
|
}
|
|
203
553
|
}
|
|
204
554
|
exports.Database = Database;
|