@live-change/db-store-indexeddb 0.6.1 → 0.6.2
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 +136 -16
- package/package.json +2 -2
package/lib/Store.js
CHANGED
|
@@ -221,14 +221,107 @@ class RangeObservable extends ReactiveDao.ObservableList {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
+
class CountObservable extends ReactiveDao.ObservableValue {
|
|
225
|
+
constructor(store, range) {
|
|
226
|
+
super()
|
|
227
|
+
this.store = store
|
|
228
|
+
this.range = range
|
|
229
|
+
|
|
230
|
+
this.disposed = false
|
|
231
|
+
this.ready = false
|
|
232
|
+
this.respawnId = 0
|
|
233
|
+
this.refillId = 0
|
|
234
|
+
this.refillPromise = null
|
|
235
|
+
|
|
236
|
+
this.forward = null
|
|
237
|
+
|
|
238
|
+
this.rangeKey = JSON.stringify(this.range)
|
|
239
|
+
this.rangeDescr = [ this.range.gt || this.range.gte || '', this.range.lt || this.range.lte || '\xFF\xFF\xFF\xFF']
|
|
240
|
+
|
|
241
|
+
this.readPromise = this.startReading()
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
async startReading() {
|
|
245
|
+
this.store.rangeObservables.set(this.rangeKey, this)
|
|
246
|
+
const treeInsert = this.rangeDescr
|
|
247
|
+
this.store.rangeObservablesTree.insert(treeInsert, this)
|
|
248
|
+
this.set(await this.store.countGet(this.range))
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
async putObject(object, oldObject) {
|
|
252
|
+
const id = object.id
|
|
253
|
+
if(this.range.gt && !(id > this.range.gt)) return
|
|
254
|
+
if(this.range.lt && !(id < this.range.lt)) return
|
|
255
|
+
await this.readPromise
|
|
256
|
+
if(this.range.limit) {
|
|
257
|
+
this.set(await this.store.countGet(this.range))
|
|
258
|
+
} else {
|
|
259
|
+
if(object && !oldObject) {
|
|
260
|
+
this.set(this.value + 1)
|
|
261
|
+
} else if(!object && oldObject) {
|
|
262
|
+
this.set(this.value - 1)
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
async deleteObject(object) {
|
|
268
|
+
const id = object.id
|
|
269
|
+
if(this.range.gt && !(id > this.range.gt)) return
|
|
270
|
+
if(this.range.lt && !(id < this.range.lt)) return
|
|
271
|
+
this.set(this.value - 1)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
dispose() {
|
|
275
|
+
if(this.forward) {
|
|
276
|
+
this.forward.unobserve(this)
|
|
277
|
+
this.forward = null
|
|
278
|
+
return
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
this.disposed = true
|
|
282
|
+
this.respawnId++
|
|
283
|
+
this.changesStream = null
|
|
284
|
+
|
|
285
|
+
this.store.rangeObservables.delete(this.rangeKey)
|
|
286
|
+
let removed = this.store.rangeObservablesTree.remove(this.rangeDescr, this)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
respawn() {
|
|
290
|
+
const existingObservable = this.store.rangeObservables.get(JSON.stringify(this.range))
|
|
291
|
+
if(existingObservable) {
|
|
292
|
+
this.forward = existingObservable
|
|
293
|
+
this.forward.observe(this)
|
|
294
|
+
return
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
this.respawnId++
|
|
298
|
+
this.ready = false
|
|
299
|
+
this.disposed = false
|
|
300
|
+
this.startReading()
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
224
304
|
class Store {
|
|
225
|
-
constructor(dbName, storeName) {
|
|
305
|
+
constructor(dbName, storeName, options = {}) {
|
|
226
306
|
if(!dbName) throw new Error("dbName argument is required")
|
|
227
307
|
if(!storeName) throw new Error("storeName argument is required")
|
|
228
308
|
|
|
229
309
|
this.dbName = dbName
|
|
230
310
|
this.storeName = storeName
|
|
231
311
|
|
|
312
|
+
if(options.noSerialization) {
|
|
313
|
+
this.serialization = {
|
|
314
|
+
stringify: x => x,
|
|
315
|
+
parse: x => x
|
|
316
|
+
}
|
|
317
|
+
} else {
|
|
318
|
+
const serialization = options.serialization ?? JSON
|
|
319
|
+
this.serialization = {
|
|
320
|
+
stringify: x => x ? ({ id: x.id, data: serialization.stringify(x) }) : null,
|
|
321
|
+
parse: x => x?.data ? serialization.parse(x.data) : null
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
232
325
|
this.finished = false
|
|
233
326
|
|
|
234
327
|
this.db = null
|
|
@@ -237,6 +330,7 @@ class Store {
|
|
|
237
330
|
|
|
238
331
|
this.objectObservables = new Map()
|
|
239
332
|
this.rangeObservables = new Map()
|
|
333
|
+
this.countObservables = new Map()
|
|
240
334
|
this.rangeObservablesTree = new IntervalTree()
|
|
241
335
|
}
|
|
242
336
|
|
|
@@ -278,6 +372,10 @@ class Store {
|
|
|
278
372
|
await this.channel.close()
|
|
279
373
|
;(await this.dbPromise).close()
|
|
280
374
|
}
|
|
375
|
+
async ensureOpen() {
|
|
376
|
+
if(!this.dbPromise) await this.open()
|
|
377
|
+
await this.dbPromise
|
|
378
|
+
}
|
|
281
379
|
|
|
282
380
|
async deleteDb() {
|
|
283
381
|
if(!this.finished) await this.close()
|
|
@@ -287,7 +385,9 @@ class Store {
|
|
|
287
385
|
async handleChannelMessage(message) {
|
|
288
386
|
switch(message.type) {
|
|
289
387
|
case 'put' : {
|
|
290
|
-
const { object,
|
|
388
|
+
const { object: objectJson, oldObjectJson } = message
|
|
389
|
+
const object = this.serialization.parse(objectJson)
|
|
390
|
+
const oldObject = this.serialization.parse(oldObjectJson)
|
|
291
391
|
const id = object?.id || oldObject?.id
|
|
292
392
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
293
393
|
const objectObservable = this.objectObservables.get(id)
|
|
@@ -298,7 +398,8 @@ class Store {
|
|
|
298
398
|
}
|
|
299
399
|
} break
|
|
300
400
|
case 'delete' : {
|
|
301
|
-
const { object } = message
|
|
401
|
+
const { object: objectJson } = message
|
|
402
|
+
const object = this.serialization.parse(objectJson)
|
|
302
403
|
const id = object?.id
|
|
303
404
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
304
405
|
const objectObservable = this.objectObservables.get(id)
|
|
@@ -314,9 +415,11 @@ class Store {
|
|
|
314
415
|
}
|
|
315
416
|
|
|
316
417
|
async objectGet(id) {
|
|
418
|
+
await this.ensureOpen()
|
|
317
419
|
if(!id) throw new Error("key is required")
|
|
318
420
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
319
|
-
|
|
421
|
+
const json = await this.db.get(this.storeName, id) || null
|
|
422
|
+
return json && this.serialization.parse(json)
|
|
320
423
|
}
|
|
321
424
|
|
|
322
425
|
objectObservable(key) {
|
|
@@ -328,6 +431,7 @@ class Store {
|
|
|
328
431
|
|
|
329
432
|
async rangeGet(range) {
|
|
330
433
|
if(!range) throw new Error("range not defined")
|
|
434
|
+
await this.ensureOpen()
|
|
331
435
|
console.log("RANGE GET!")
|
|
332
436
|
|
|
333
437
|
const txn = this.db.transaction(this.storeName, 'readonly')
|
|
@@ -349,7 +453,8 @@ class Store {
|
|
|
349
453
|
if (range.gt && cursor.key <= range.gt) break
|
|
350
454
|
if (range.gte && cursor.key < range.gte) break
|
|
351
455
|
if ((!range.lt || cursor.key < range.lt) && (!range.lte || cursor.key <= range.lte)) {
|
|
352
|
-
|
|
456
|
+
const json = cursor.value
|
|
457
|
+
data.push(this.serialization.parse(json))
|
|
353
458
|
}
|
|
354
459
|
cursor = await cursor.continue()
|
|
355
460
|
}
|
|
@@ -360,7 +465,8 @@ class Store {
|
|
|
360
465
|
if(range.lt && cursor.key >= range.lt) break
|
|
361
466
|
if(range.lte && cursor.key > range.lte) break
|
|
362
467
|
if((!range.gt || cursor.key > range.gt) && (!range.gte || cursor.key >= range.gte)) {
|
|
363
|
-
|
|
468
|
+
const json = cursor.value
|
|
469
|
+
data.push(this.serialization.parse(json))
|
|
364
470
|
}
|
|
365
471
|
cursor = await cursor.continue()
|
|
366
472
|
//console.log("CURSOR C", cursor)
|
|
@@ -379,6 +485,7 @@ class Store {
|
|
|
379
485
|
|
|
380
486
|
async countGet(range) {
|
|
381
487
|
if(!range) throw new Error("range not defined")
|
|
488
|
+
await this.ensureOpen()
|
|
382
489
|
const min = range.gt || range.gte
|
|
383
490
|
const max = range.lt || range.lte
|
|
384
491
|
let keyRange = undefined
|
|
@@ -389,7 +496,7 @@ class Store {
|
|
|
389
496
|
} else if(max) {
|
|
390
497
|
keyRange = IDBKeyRange.upperBound(min, !!range.gt)
|
|
391
498
|
}
|
|
392
|
-
const count = await this.count(this.storeName, keyRange)
|
|
499
|
+
const count = await this.db.count(this.storeName, keyRange)
|
|
393
500
|
if(range.limit && count > range.limit) return range.limit
|
|
394
501
|
return count
|
|
395
502
|
}
|
|
@@ -403,6 +510,7 @@ class Store {
|
|
|
403
510
|
|
|
404
511
|
async rangeDelete(range) {
|
|
405
512
|
if(!range) throw new Error("range not defined")
|
|
513
|
+
await this.ensureOpen()
|
|
406
514
|
|
|
407
515
|
const txn = this.db.transaction(this.storeName)
|
|
408
516
|
let count = 0, last
|
|
@@ -425,7 +533,8 @@ class Store {
|
|
|
425
533
|
if ((!range.lt || cursor.key < range.lt) && (!range.lte || cursor.key <= range.lte)) {
|
|
426
534
|
count++
|
|
427
535
|
const id = cursor.key
|
|
428
|
-
const
|
|
536
|
+
const json = cursor.value
|
|
537
|
+
const object = this.serialization.parse(json)
|
|
429
538
|
last = id
|
|
430
539
|
await cursor.delete()
|
|
431
540
|
|
|
@@ -435,7 +544,7 @@ class Store {
|
|
|
435
544
|
for(const rangeObservable of rangeObservables) {
|
|
436
545
|
rangeObservable.deleteObject(object)
|
|
437
546
|
}
|
|
438
|
-
this.channel.postMessage({ type: "delete", object })
|
|
547
|
+
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
439
548
|
}
|
|
440
549
|
cursor = await cursor.continue()
|
|
441
550
|
}
|
|
@@ -447,7 +556,8 @@ class Store {
|
|
|
447
556
|
if((!range.gt || cursor.key > range.gt) && (!range.gte || cursor.key >= range.gte)) {
|
|
448
557
|
count++
|
|
449
558
|
const id = cursor.key
|
|
450
|
-
const
|
|
559
|
+
const json = cursor.value
|
|
560
|
+
const object = this.serialization.parse(json)
|
|
451
561
|
last = id
|
|
452
562
|
await cursor.delete()
|
|
453
563
|
|
|
@@ -457,7 +567,7 @@ class Store {
|
|
|
457
567
|
for(const rangeObservable of rangeObservables) {
|
|
458
568
|
rangeObservable.deleteObject(object)
|
|
459
569
|
}
|
|
460
|
-
this.channel.postMessage({ type: "delete", object })
|
|
570
|
+
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
461
571
|
}
|
|
462
572
|
cursor = await cursor.continue()
|
|
463
573
|
}
|
|
@@ -468,22 +578,32 @@ class Store {
|
|
|
468
578
|
async put(object) {
|
|
469
579
|
const id = object.id
|
|
470
580
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
471
|
-
|
|
581
|
+
await this.ensureOpen()
|
|
582
|
+
|
|
583
|
+
const oldObjectJson = await this.db.get(this.storeName, id)
|
|
584
|
+
const oldObject = oldObjectJson ? this.serialization.parse(oldObjectJson) : null
|
|
472
585
|
console.log("PUT", object)
|
|
473
|
-
|
|
586
|
+
const json = this.serialization.stringify(object)
|
|
587
|
+
await this.db.put(this.storeName, json)
|
|
474
588
|
const objectObservable = this.objectObservables.get(id)
|
|
475
589
|
if(objectObservable) objectObservable.set(object, oldObject)
|
|
476
590
|
const rangeObservables = this.rangeObservablesTree.search([id, id])
|
|
477
591
|
for(const rangeObservable of rangeObservables) {
|
|
478
592
|
rangeObservable.putObject(object, oldObject)
|
|
479
593
|
}
|
|
480
|
-
this.channel.postMessage({ type: "put",
|
|
594
|
+
this.channel.postMessage({ type: "put",
|
|
595
|
+
object: this.serialization.stringify(object),
|
|
596
|
+
oldObject: this.serialization.stringify(oldObject)
|
|
597
|
+
})
|
|
481
598
|
return oldObject
|
|
482
599
|
}
|
|
483
600
|
|
|
484
601
|
async delete(id) {
|
|
485
602
|
if(typeof id != 'string') throw new Error(`ID is not string: ${JSON.stringify(id)}`)
|
|
486
|
-
|
|
603
|
+
await this.ensureOpen()
|
|
604
|
+
|
|
605
|
+
const json = await this.db.get(this.storeName, id)
|
|
606
|
+
const object = json ? this.serialization.parse(json) : null
|
|
487
607
|
await this.db.delete(this.storeName, id)
|
|
488
608
|
const objectObservable = this.objectObservables.get(id)
|
|
489
609
|
if(objectObservable) objectObservable.set(null)
|
|
@@ -491,7 +611,7 @@ class Store {
|
|
|
491
611
|
for(const rangeObservable of rangeObservables) {
|
|
492
612
|
rangeObservable.deleteObject(object)
|
|
493
613
|
}
|
|
494
|
-
this.channel.postMessage({ type: "delete", object })
|
|
614
|
+
this.channel.postMessage({ type: "delete", object: this.serialization.stringify(object) })
|
|
495
615
|
return object
|
|
496
616
|
}
|
|
497
617
|
|
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.2",
|
|
4
4
|
"description": "Database with observable data for live queries",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -30,5 +30,5 @@
|
|
|
30
30
|
"@live-change/interval-tree": "^1.0.12",
|
|
31
31
|
"broadcast-channel": "^4.2.0"
|
|
32
32
|
},
|
|
33
|
-
"gitHead": "
|
|
33
|
+
"gitHead": "9a1b104864c08f3e35b009f191889e3308e3eeb0"
|
|
34
34
|
}
|