@live-change/db-store-indexeddb 0.6.2 → 0.6.3
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/Store.js +150 -93
- package/package.json +3 -3
package/lib/Store.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const IntervalTree = require('@live-change/interval-tree').default
|
|
2
2
|
const ReactiveDao = require("@live-change/dao")
|
|
3
3
|
const { BroadcastChannel, createLeaderElection } = require('broadcast-channel')
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class ObjectObservable extends ReactiveDao.ObservableValue {
|
|
@@ -301,6 +301,18 @@ class CountObservable extends ReactiveDao.ObservableValue {
|
|
|
301
301
|
}
|
|
302
302
|
}
|
|
303
303
|
|
|
304
|
+
async function handleRequest(request, onUpgrade = ()=>{}) {
|
|
305
|
+
return new Promise((resolve, reject) => {
|
|
306
|
+
request.onerror = (event) => {
|
|
307
|
+
reject(request.error)
|
|
308
|
+
}
|
|
309
|
+
request.onsuccess = (event) => {
|
|
310
|
+
resolve(request.result)
|
|
311
|
+
}
|
|
312
|
+
request.onupgradeneeded = onUpgrade
|
|
313
|
+
})
|
|
314
|
+
}
|
|
315
|
+
|
|
304
316
|
class Store {
|
|
305
317
|
constructor(dbName, storeName, options = {}) {
|
|
306
318
|
if(!dbName) throw new Error("dbName argument is required")
|
|
@@ -308,6 +320,7 @@ class Store {
|
|
|
308
320
|
|
|
309
321
|
this.dbName = dbName
|
|
310
322
|
this.storeName = storeName
|
|
323
|
+
this.idbName = dbName + '.' + storeName
|
|
311
324
|
|
|
312
325
|
if(options.noSerialization) {
|
|
313
326
|
this.serialization = {
|
|
@@ -328,6 +341,8 @@ class Store {
|
|
|
328
341
|
this.dbPromise = null
|
|
329
342
|
this.channel = null
|
|
330
343
|
|
|
344
|
+
this.openPromise = null
|
|
345
|
+
|
|
331
346
|
this.objectObservables = new Map()
|
|
332
347
|
this.rangeObservables = new Map()
|
|
333
348
|
this.countObservables = new Map()
|
|
@@ -335,24 +350,19 @@ class Store {
|
|
|
335
350
|
}
|
|
336
351
|
|
|
337
352
|
async openDb() {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
for(const [key, value] of this.objectObservables) value.dispose()
|
|
346
|
-
for(const [key, value] of this.rangeObservables) value.dispose()
|
|
347
|
-
await this.openDb()
|
|
348
|
-
for(const [key, value] of this.objectObservables) value.respawn()
|
|
349
|
-
for(const [key, value] of this.rangeObservables) value.respawn()
|
|
350
|
-
}
|
|
353
|
+
//console.log("Opening db", this.dbName, this.storeName)
|
|
354
|
+
const openRequest = globalThis.indexedDB.open(this.idbName, 1)
|
|
355
|
+
globalThis.lastOpenRequest = openRequest
|
|
356
|
+
this.dbPromise = handleRequest(openRequest, (event) => {
|
|
357
|
+
//console.error("Upgrading db", this.dbName, this.storeName)
|
|
358
|
+
const db = event.target.result
|
|
359
|
+
const store = db.createObjectStore(this.storeName, { keyPath: 'id' })
|
|
351
360
|
})
|
|
352
361
|
this.db = await this.dbPromise
|
|
362
|
+
//console.log("Opened db", this.dbName, this.storeName)
|
|
353
363
|
}
|
|
354
364
|
async openChannel() {
|
|
355
|
-
this.channel = new BroadcastChannel('lc-db-channel' + this.dbName, {
|
|
365
|
+
this.channel = new BroadcastChannel('lc-db-channel' + this.dbName + '-' + this.storeName, {
|
|
356
366
|
idb: {
|
|
357
367
|
onclose: () => {
|
|
358
368
|
if(this.finished) return
|
|
@@ -373,13 +383,13 @@ class Store {
|
|
|
373
383
|
;(await this.dbPromise).close()
|
|
374
384
|
}
|
|
375
385
|
async ensureOpen() {
|
|
376
|
-
if(!this.
|
|
377
|
-
await this.
|
|
386
|
+
if(!this.openPromise) this.openPromise = this.open()
|
|
387
|
+
await this.openPromise
|
|
378
388
|
}
|
|
379
389
|
|
|
380
390
|
async deleteDb() {
|
|
391
|
+
;(await this.dbPromise).deleteObjectStore(this.storeName)
|
|
381
392
|
if(!this.finished) await this.close()
|
|
382
|
-
await idb.deleteDB('lc-db-' + this.dbName)
|
|
383
393
|
}
|
|
384
394
|
|
|
385
395
|
async handleChannelMessage(message) {
|
|
@@ -418,8 +428,10 @@ class Store {
|
|
|
418
428
|
await this.ensureOpen()
|
|
419
429
|
if(!id) throw new Error("key is required")
|
|
420
430
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
421
|
-
const
|
|
422
|
-
|
|
431
|
+
const transaction = this.db.transaction([this.storeName], 'readonly')
|
|
432
|
+
const store = transaction.objectStore(this.storeName)
|
|
433
|
+
const json = await handleRequest(store.get(id) || null)
|
|
434
|
+
return json ? this.serialization.parse(json) : null
|
|
423
435
|
}
|
|
424
436
|
|
|
425
437
|
objectObservable(key) {
|
|
@@ -432,9 +444,6 @@ class Store {
|
|
|
432
444
|
async rangeGet(range) {
|
|
433
445
|
if(!range) throw new Error("range not defined")
|
|
434
446
|
await this.ensureOpen()
|
|
435
|
-
console.log("RANGE GET!")
|
|
436
|
-
|
|
437
|
-
const txn = this.db.transaction(this.storeName, 'readonly')
|
|
438
447
|
let data = []
|
|
439
448
|
const min = range.gt || range.gte
|
|
440
449
|
const max = range.lt || range.lte
|
|
@@ -444,34 +453,53 @@ class Store {
|
|
|
444
453
|
} else if(min) {
|
|
445
454
|
keyRange = IDBKeyRange.lowerBound(min, !!range.gt)
|
|
446
455
|
} else if(max) {
|
|
447
|
-
keyRange = IDBKeyRange.upperBound(
|
|
456
|
+
keyRange = IDBKeyRange.upperBound(max, !!range.gt)
|
|
448
457
|
}
|
|
449
458
|
|
|
459
|
+
const txn = this.db.transaction([this.storeName], 'readonly')
|
|
460
|
+
const store = txn.objectStore(this.storeName)
|
|
450
461
|
if(range.reverse) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
462
|
+
await new Promise((resolve, reject) => {
|
|
463
|
+
const cursorRequest = store.openCursor(keyRange, 'prev')
|
|
464
|
+
cursorRequest.onsuccess = (event) => {
|
|
465
|
+
const cursor = event.target.result
|
|
466
|
+
if ((!range.limit || data.length < range.limit) && cursor) {
|
|
467
|
+
if (range.gt && cursor.key <= range.gt) return resolve()
|
|
468
|
+
if (range.gte && cursor.key < range.gte) return resolve()
|
|
469
|
+
if ((!range.lt || cursor.key < range.lt) && (!range.lte || cursor.key <= range.lte)) {
|
|
470
|
+
const json = cursor.value
|
|
471
|
+
data.push(this.serialization.parse(json))
|
|
472
|
+
}
|
|
473
|
+
cursor.continue()
|
|
474
|
+
} else {
|
|
475
|
+
return resolve()
|
|
476
|
+
}
|
|
458
477
|
}
|
|
459
|
-
|
|
460
|
-
|
|
478
|
+
cursorRequest.onerror = (event) => {
|
|
479
|
+
reject(event.target.error)
|
|
480
|
+
}
|
|
481
|
+
})
|
|
461
482
|
} else {
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
483
|
+
await new Promise((resolve, reject) => {
|
|
484
|
+
const cursorRequest = store.openCursor(keyRange, 'next')
|
|
485
|
+
cursorRequest.onsuccess = (event) => {
|
|
486
|
+
const cursor = event.target.result
|
|
487
|
+
if ((!range.limit || data.length < range.limit) && cursor) {
|
|
488
|
+
if(range.lt && cursor.key >= range.lt) return resolve()
|
|
489
|
+
if(range.lte && cursor.key > range.lte) return resolve()
|
|
490
|
+
if((!range.gt || cursor.key > range.gt) && (!range.gte || cursor.key >= range.gte)) {
|
|
491
|
+
const json = cursor.value
|
|
492
|
+
data.push(this.serialization.parse(json))
|
|
493
|
+
}
|
|
494
|
+
cursor.continue()
|
|
495
|
+
} else {
|
|
496
|
+
return resolve()
|
|
497
|
+
}
|
|
470
498
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
499
|
+
cursorRequest.onerror = (event) => {
|
|
500
|
+
reject(event.target.error)
|
|
501
|
+
}
|
|
502
|
+
})
|
|
475
503
|
}
|
|
476
504
|
return data
|
|
477
505
|
}
|
|
@@ -496,7 +524,9 @@ class Store {
|
|
|
496
524
|
} else if(max) {
|
|
497
525
|
keyRange = IDBKeyRange.upperBound(min, !!range.gt)
|
|
498
526
|
}
|
|
499
|
-
const
|
|
527
|
+
const txn = this.db.transaction([this.storeName], 'readonly')
|
|
528
|
+
const store = txn.objectStore(this.storeName)
|
|
529
|
+
const count = await handleRequest(store.count(keyRange))
|
|
500
530
|
if(range.limit && count > range.limit) return range.limit
|
|
501
531
|
return count
|
|
502
532
|
}
|
|
@@ -512,7 +542,6 @@ class Store {
|
|
|
512
542
|
if(!range) throw new Error("range not defined")
|
|
513
543
|
await this.ensureOpen()
|
|
514
544
|
|
|
515
|
-
const txn = this.db.transaction(this.storeName)
|
|
516
545
|
let count = 0, last
|
|
517
546
|
const min = range.gt || range.gte
|
|
518
547
|
const max = range.lt || range.lte
|
|
@@ -525,52 +554,74 @@ class Store {
|
|
|
525
554
|
keyRange = IDBKeyRange.upperBound(min, !!range.gt)
|
|
526
555
|
}
|
|
527
556
|
|
|
557
|
+
const txn = this.db.transaction([this.storeName], 'readonly')
|
|
558
|
+
const store = txn.objectStore(this.storeName)
|
|
528
559
|
if(range.reverse) {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
560
|
+
await new Promise((resolve, reject) => {
|
|
561
|
+
const cursorRequest = store.openCursor(keyRange, 'prev')
|
|
562
|
+
cursorRequest.onsuccess = async (event) => {
|
|
563
|
+
const cursor = event.target.result
|
|
564
|
+
if ((!range.limit || count < range.limit) && cursor) {
|
|
565
|
+
if (range.gt && cursor.key <= range.gt) return resolve()
|
|
566
|
+
if (range.gte && cursor.key < range.gte) return resolve()
|
|
567
|
+
if ((!range.lt || cursor.key < range.lt) && (!range.lte || cursor.key <= range.lte)) {
|
|
568
|
+
count++
|
|
569
|
+
const id = cursor.key
|
|
570
|
+
const json = cursor.value
|
|
571
|
+
const object = this.serialization.parse(json)
|
|
572
|
+
last = id
|
|
573
|
+
await handleRequest(cursor.delete())
|
|
574
|
+
|
|
575
|
+
const objectObservable = this.objectObservables.get(id)
|
|
576
|
+
if(objectObservable) objectObservable.set(null)
|
|
577
|
+
const rangeObservables = this.rangeObservablesTree.search([id, id])
|
|
578
|
+
for(const rangeObservable of rangeObservables) {
|
|
579
|
+
rangeObservable.deleteObject(object)
|
|
580
|
+
}
|
|
581
|
+
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
582
|
+
}
|
|
583
|
+
cursor.continue()
|
|
584
|
+
} else {
|
|
585
|
+
return resolve()
|
|
546
586
|
}
|
|
547
|
-
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
548
587
|
}
|
|
549
|
-
|
|
550
|
-
|
|
588
|
+
cursorRequest.onerror = (event) => {
|
|
589
|
+
reject(event.target.error)
|
|
590
|
+
}
|
|
591
|
+
})
|
|
551
592
|
} else {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
593
|
+
await new Promise((resolve, reject) => {
|
|
594
|
+
const cursorRequest = store.openCursor(keyRange, 'next')
|
|
595
|
+
cursorRequest.onsuccess = async (event) => {
|
|
596
|
+
const cursor = event.target.result
|
|
597
|
+
if ((!range.limit || count < range.limit) && cursor) {
|
|
598
|
+
if(range.lt && cursor.key >= range.lt) return resolve()
|
|
599
|
+
if(range.lte && cursor.key > range.lte) return resolve()
|
|
600
|
+
if((!range.gt || cursor.key > range.gt) && (!range.gte || cursor.key >= range.gte)) {
|
|
601
|
+
count++
|
|
602
|
+
const id = cursor.key
|
|
603
|
+
const json = cursor.value
|
|
604
|
+
const object = this.serialization.parse(json)
|
|
605
|
+
last = id
|
|
606
|
+
await handleRequest(cursor.delete())
|
|
607
|
+
|
|
608
|
+
const objectObservable = this.objectObservables.get(id)
|
|
609
|
+
if(objectObservable) objectObservable.set(null)
|
|
610
|
+
const rangeObservables = this.rangeObservablesTree.search([id, id])
|
|
611
|
+
for(const rangeObservable of rangeObservables) {
|
|
612
|
+
rangeObservable.deleteObject(object)
|
|
613
|
+
}
|
|
614
|
+
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
615
|
+
}
|
|
616
|
+
cursor.continue()
|
|
617
|
+
} else {
|
|
618
|
+
return resolve()
|
|
569
619
|
}
|
|
570
|
-
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
571
620
|
}
|
|
572
|
-
|
|
573
|
-
|
|
621
|
+
cursorRequest.onerror = (event) => {
|
|
622
|
+
reject(event.target.error)
|
|
623
|
+
}
|
|
624
|
+
})
|
|
574
625
|
}
|
|
575
626
|
return { count, last }
|
|
576
627
|
}
|
|
@@ -579,12 +630,15 @@ class Store {
|
|
|
579
630
|
const id = object.id
|
|
580
631
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
581
632
|
await this.ensureOpen()
|
|
582
|
-
|
|
583
|
-
|
|
633
|
+
//console.error("put", id, object, 'in', this.storeName)
|
|
634
|
+
//console.error("storeNames", this.db.objectStoreNames)
|
|
635
|
+
const transaction = this.db.transaction([this.storeName], 'readwrite')
|
|
636
|
+
const store = transaction.objectStore(this.storeName)
|
|
637
|
+
const oldObjectJson = await handleRequest(store.get(id))
|
|
584
638
|
const oldObject = oldObjectJson ? this.serialization.parse(oldObjectJson) : null
|
|
585
|
-
console.log("PUT", object)
|
|
639
|
+
//console.log("PUT", object)
|
|
586
640
|
const json = this.serialization.stringify(object)
|
|
587
|
-
await
|
|
641
|
+
await handleRequest(store.put(json))
|
|
588
642
|
const objectObservable = this.objectObservables.get(id)
|
|
589
643
|
if(objectObservable) objectObservable.set(object, oldObject)
|
|
590
644
|
const rangeObservables = this.rangeObservablesTree.search([id, id])
|
|
@@ -602,9 +656,12 @@ class Store {
|
|
|
602
656
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
603
657
|
await this.ensureOpen()
|
|
604
658
|
|
|
605
|
-
const
|
|
659
|
+
const transaction = this.db.transaction([this.storeName], 'readwrite')
|
|
660
|
+
const store = transaction.objectStore(this.storeName)
|
|
661
|
+
|
|
662
|
+
const json = await handleRequest(store.get(id))
|
|
606
663
|
const object = json ? this.serialization.parse(json) : null
|
|
607
|
-
await
|
|
664
|
+
await handleRequest(store.delete(id))
|
|
608
665
|
const objectObservable = this.objectObservables.get(id)
|
|
609
666
|
if(objectObservable) objectObservable.set(null)
|
|
610
667
|
const rangeObservables = this.rangeObservablesTree.search([id, id])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@live-change/db-store-indexeddb",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.3",
|
|
4
4
|
"description": "Database with observable data for live queries",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,9 +26,9 @@
|
|
|
26
26
|
"tape": "^5.3.2"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
|
-
"@live-change/dao": "0.5.
|
|
29
|
+
"@live-change/dao": "0.5.15",
|
|
30
30
|
"@live-change/interval-tree": "^1.0.12",
|
|
31
31
|
"broadcast-channel": "^4.2.0"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "8dc4ac726243970c9f1431bf67b4390f7845ce76"
|
|
34
34
|
}
|