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