gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-wallet-webex] 02/05: idb-bridge: test cases, package


From: gnunet
Subject: [GNUnet-SVN] [taler-wallet-webex] 02/05: idb-bridge: test cases, package structure and missing functionality
Date: Thu, 01 Aug 2019 23:21:22 +0200

This is an automated email from the git hooks/post-receive script.

dold pushed a commit to branch master
in repository wallet-webex.

commit bcefbd7aab5f33f93d626c6421a1a1218c1a91a2
Author: Florian Dold <address@hidden>
AuthorDate: Wed Jul 31 01:33:23 2019 +0200

    idb-bridge: test cases, package structure and missing functionality
---
 packages/idb-bridge/.vscode/settings.json          |   3 +
 packages/idb-bridge/src/BridgeIDBCursor.ts         |  36 +++-
 .../idb-bridge/src/BridgeIDBCursorWithValue.ts     |  49 ++---
 packages/idb-bridge/src/BridgeIDBIndex.ts          |   2 +-
 packages/idb-bridge/src/BridgeIDBKeyRange.ts       |  11 +-
 packages/idb-bridge/src/BridgeIDBObjectStore.ts    |  39 +++-
 packages/idb-bridge/src/BridgeIDBTransaction.ts    |  21 +-
 packages/idb-bridge/src/MemoryBackend.test.ts      |  57 ++++++
 packages/idb-bridge/src/MemoryBackend.ts           | 211 ++++++++++++++++++---
 packages/idb-bridge/src/backend-interface.ts       |   8 +-
 packages/idb-bridge/src/index.ts                   |  60 ++++++
 packages/idb-bridge/src/util/FakeEventTarget.ts    |   4 +-
 packages/idb-bridge/src/util/makeStoreKeyValue.ts  |   2 +-
 13 files changed, 409 insertions(+), 94 deletions(-)

diff --git a/packages/idb-bridge/.vscode/settings.json 
b/packages/idb-bridge/.vscode/settings.json
new file mode 100644
index 00000000..ff30c446
--- /dev/null
+++ b/packages/idb-bridge/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+    "editor.tabSize": 2
+}
\ No newline at end of file
diff --git a/packages/idb-bridge/src/BridgeIDBCursor.ts 
b/packages/idb-bridge/src/BridgeIDBCursor.ts
index ed5aa3e8..6c313908 100644
--- a/packages/idb-bridge/src/BridgeIDBCursor.ts
+++ b/packages/idb-bridge/src/BridgeIDBCursor.ts
@@ -42,12 +42,14 @@ import {
   Backend,
   DatabaseTransaction,
   RecordStoreRequest,
+  StoreLevel,
 } from "./backend-interface";
+import BridgeIDBFactory from "./BridgeIDBFactory";
 
 /**
  * http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#cursor
  */
-class BridgeIDBCursor {
+export class BridgeIDBCursor {
   _request: BridgeIDBRequest | undefined;
 
   private _gotValue: boolean = false;
@@ -119,14 +121,24 @@ class BridgeIDBCursor {
   get primaryKey() {
     return this._primaryKey;
   }
+
   set primaryKey(val) {
     /* For babel */
   }
 
+  protected get _isValueCursor(): boolean {
+    return false;
+  }
+
   /**
    * https://w3c.github.io/IndexedDB/#iterate-a-cursor
    */
   async _iterate(key?: Key, primaryKey?: Key): Promise<any> {
+    BridgeIDBFactory.enableTracing &&
+      console.log(
+        `iterating cursor os=${this._objectStoreName},idx=${this._indexName}`,
+      );
+    BridgeIDBFactory.enableTracing && console.log("cursor type ", 
this.toString());
     const recordGetRequest: RecordGetRequest = {
       direction: this.direction,
       indexName: this._indexName,
@@ -145,7 +157,10 @@ class BridgeIDBCursor {
     let response = await this._backend.getRecords(btx, recordGetRequest);
 
     if (response.count === 0) {
-      console.log("cursor is returning empty result");
+      if (BridgeIDBFactory.enableTracing) {
+        console.log("cursor is returning empty result");
+      }
+      this._gotValue = false;
       return null;
     }
 
@@ -153,8 +168,10 @@ class BridgeIDBCursor {
       throw Error("invariant failed");
     }
 
-    console.log("request is:", JSON.stringify(recordGetRequest));
-    console.log("get response is:", JSON.stringify(response));
+    if (BridgeIDBFactory.enableTracing) {
+      console.log("request is:", JSON.stringify(recordGetRequest));
+      console.log("get response is:", JSON.stringify(response));
+    }
 
     if (this._indexName !== undefined) {
       this._key = response.indexKeys![0];
@@ -204,20 +221,23 @@ class BridgeIDBCursor {
       throw new InvalidStateError();
     }
 
-    if (!this._gotValue || !this.hasOwnProperty("value")) {
+    if (!this._gotValue || !this._isValueCursor) {
       throw new InvalidStateError();
     }
 
     const storeReq: RecordStoreRequest = {
-      overwrite: true,
       key: this._primaryKey,
       value: value,
       objectStoreName: this._objectStoreName,
+      storeLevel: StoreLevel.UpdateExisting,
     };
 
     const operation = async () => {
+      if (BridgeIDBFactory.enableTracing) {
+        console.log("updating at cursor")
+      }
       const { btx } = this.source._confirmActiveTransaction();
-      this._backend.storeRecord(btx, storeReq);
+      await this._backend.storeRecord(btx, storeReq);
     };
     return transaction._execRequestAsync({
       operation,
@@ -318,7 +338,7 @@ class BridgeIDBCursor {
       throw new InvalidStateError();
     }
 
-    if (!this._gotValue || !this.hasOwnProperty("value")) {
+    if (!this._gotValue || !this._isValueCursor) {
       throw new InvalidStateError();
     }
 
diff --git a/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts 
b/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
index b2f23147..d75bd21e 100644
--- a/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
+++ b/packages/idb-bridge/src/BridgeIDBCursorWithValue.ts
@@ -16,32 +16,35 @@
 
 import BridgeIDBCursor from "./BridgeIDBCursor";
 import {
-    CursorRange,
-    CursorSource,
-    BridgeIDBCursorDirection,
-    Value,
+  CursorRange,
+  CursorSource,
+  BridgeIDBCursorDirection,
+  Value,
 } from "./util/types";
 
 class BridgeIDBCursorWithValue extends BridgeIDBCursor {
-
-    get value(): Value {
-        return this._value;
-    }
-    
-    constructor(
-        source: CursorSource,
-        objectStoreName: string,
-        indexName: string | undefined,
-        range: CursorRange,
-        direction: BridgeIDBCursorDirection,
-        request?: any,
-    ) {
-        super(source, objectStoreName, indexName, range, direction, request, 
false);
-    }
-
-    public toString() {
-        return "[object IDBCursorWithValue]";
-    }
+  get value(): Value {
+    return this._value;
+  }
+
+  protected get _isValueCursor(): boolean {
+    return true;
+  }
+
+  constructor(
+    source: CursorSource,
+    objectStoreName: string,
+    indexName: string | undefined,
+    range: CursorRange,
+    direction: BridgeIDBCursorDirection,
+    request?: any,
+  ) {
+    super(source, objectStoreName, indexName, range, direction, request, 
false);
+  }
+
+  public toString() {
+    return "[object IDBCursorWithValue]";
+  }
 }
 
 export default BridgeIDBCursorWithValue;
diff --git a/packages/idb-bridge/src/BridgeIDBIndex.ts 
b/packages/idb-bridge/src/BridgeIDBIndex.ts
index 8179be83..4d2022d3 100644
--- a/packages/idb-bridge/src/BridgeIDBIndex.ts
+++ b/packages/idb-bridge/src/BridgeIDBIndex.ts
@@ -50,7 +50,7 @@ const confirmActiveTransaction = (
 };
 
 // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#idl-def-IDBIndex
-class BridgeIDBIndex {
+export class BridgeIDBIndex {
   objectStore: BridgeIDBObjectStore;
 
   get _schema(): Schema {
diff --git a/packages/idb-bridge/src/BridgeIDBKeyRange.ts 
b/packages/idb-bridge/src/BridgeIDBKeyRange.ts
index 2b32c7e0..4055e092 100644
--- a/packages/idb-bridge/src/BridgeIDBKeyRange.ts
+++ b/packages/idb-bridge/src/BridgeIDBKeyRange.ts
@@ -113,21 +113,20 @@ class BridgeIDBKeyRange {
 
   static _valueToKeyRange(value: any, nullDisallowedFlag: boolean = false) {
     if (value instanceof BridgeIDBKeyRange) {
-        return value;
+      return value;
     }
 
     if (value === null || value === undefined) {
-        if (nullDisallowedFlag) {
-            throw new DataError();
-        }
-        return new BridgeIDBKeyRange(undefined, undefined, false, false);
+      if (nullDisallowedFlag) {
+        throw new DataError();
+      }
+      return new BridgeIDBKeyRange(undefined, undefined, false, false);
     }
 
     const key = valueToKey(value);
 
     return BridgeIDBKeyRange.only(key);
   }
-
 }
 
 export default BridgeIDBKeyRange;
diff --git a/packages/idb-bridge/src/BridgeIDBObjectStore.ts 
b/packages/idb-bridge/src/BridgeIDBObjectStore.ts
index eca4c198..af5f8051 100644
--- a/packages/idb-bridge/src/BridgeIDBObjectStore.ts
+++ b/packages/idb-bridge/src/BridgeIDBObjectStore.ts
@@ -46,6 +46,7 @@ import {
   DatabaseTransaction,
   RecordGetRequest,
   ResultLevel,
+  StoreLevel,
 } from "./backend-interface";
 import BridgeIDBFactory from "./BridgeIDBFactory";
 
@@ -137,7 +138,7 @@ class BridgeIDBObjectStore {
         objectStoreName: this._name,
         key: key,
         value: value,
-        overwrite,
+        storeLevel: overwrite ? StoreLevel.AllowOverwrite : 
StoreLevel.NoOverwrite,
       });
     };
 
@@ -158,7 +159,7 @@ class BridgeIDBObjectStore {
     return this._store(value, key, false);
   }
 
-  public delete(key: Key) {
+  public delete(key: Key | BridgeIDBKeyRange) {
     if (arguments.length === 0) {
       throw new TypeError();
     }
@@ -167,13 +168,17 @@ class BridgeIDBObjectStore {
       throw new ReadOnlyError();
     }
 
-    if (!(key instanceof BridgeIDBKeyRange)) {
-      key = valueToKey(key);
+    let keyRange: BridgeIDBKeyRange;
+
+    if (key instanceof BridgeIDBKeyRange) {
+      keyRange = key;
+    } else {
+      keyRange = BridgeIDBKeyRange.only(valueToKey(key));
     }
 
     const operation = async () => {
       const { btx } = this._confirmActiveTransaction();
-      return this._backend.deleteRecord(btx, this._name, key);
+      return this._backend.deleteRecord(btx, this._name, keyRange);
     }
       
     return this.transaction._execRequestAsync({
@@ -183,12 +188,20 @@ class BridgeIDBObjectStore {
   }
 
   public get(key?: BridgeIDBKeyRange | Key) {
+    if (BridgeIDBFactory.enableTracing) {
+      console.log(`getting from object store ${this._name} key ${key}`);
+    }
+
     if (arguments.length === 0) {
       throw new TypeError();
     }
 
-    if (!(key instanceof BridgeIDBKeyRange)) {
-      key = valueToKey(key);
+    let keyRange: BridgeIDBKeyRange;
+
+    if (key instanceof BridgeIDBKeyRange) {
+      keyRange = key;
+    } else {
+      keyRange = BridgeIDBKeyRange.only(valueToKey(key));
     }
 
     const recordRequest: RecordGetRequest = {
@@ -199,16 +212,24 @@ class BridgeIDBObjectStore {
       direction: "next",
       limit: 1,
       resultLevel: ResultLevel.Full,
-      range: key,
+      range: keyRange,
     };
 
     const operation = async () => {
+      if (BridgeIDBFactory.enableTracing) {
+        console.log("running get operation:", recordRequest);
+      }
       const { btx } = this._confirmActiveTransaction();
       const result = await this._backend.getRecords(
         btx,
         recordRequest,
       );
-      if (result.count == 0) {
+
+      if (BridgeIDBFactory.enableTracing) {
+        console.log("get operation result count:", result.count);
+      }
+
+      if (result.count === 0) {
         return undefined;
       }
       const values = result.values;
diff --git a/packages/idb-bridge/src/BridgeIDBTransaction.ts 
b/packages/idb-bridge/src/BridgeIDBTransaction.ts
index a9f0201d..250e2714 100644
--- a/packages/idb-bridge/src/BridgeIDBTransaction.ts
+++ b/packages/idb-bridge/src/BridgeIDBTransaction.ts
@@ -174,12 +174,12 @@ class BridgeIDBTransaction extends FakeEventTarget {
    */
   public async _start() {
     if (BridgeIDBFactory.enableTracing) {
-      console.log(`TRACE: IDBTransaction._start, ${this._requests.length} 
queued`);
+      console.log(
+        `TRACE: IDBTransaction._start, ${this._requests.length} queued`,
+      );
     }
     this._started = true;
 
-    console.log("beginning transaction");
-
     if (!this._backendTransaction) {
       this._backendTransaction = await this._backend.beginTransaction(
         this.db._backendConnection,
@@ -188,8 +188,6 @@ class BridgeIDBTransaction extends FakeEventTarget {
       );
     }
 
-    console.log("beginTransaction completed");
-
     // Remove from request queue - cursor ones will be added back if necessary 
by cursor.continue and such
     let operation;
     let request;
@@ -208,16 +206,17 @@ class BridgeIDBTransaction extends FakeEventTarget {
       if (!request.source) {
         // Special requests like indexes that just need to run some code, with 
error handling already built into
         // operation
-        console.log("running operation without source");
         await operation();
       } else {
-        console.log("running operation with source");
         let event;
         try {
+          BridgeIDBFactory.enableTracing &&
+            console.log("TRACE: running operation in transaction");
           const result = await operation();
-          if (BridgeIDBFactory.enableTracing) {
-            console.log("TRACE: tx operation finished with success");
-          }
+          BridgeIDBFactory.enableTracing &&
+            console.log(
+              "TRACE: operation in transaction finished with success",
+            );
           request.readyState = "done";
           request.result = result;
           request.error = undefined;
@@ -295,7 +294,7 @@ class BridgeIDBTransaction extends FakeEventTarget {
 
       if (!this.error) {
         if (BridgeIDBFactory.enableTracing) {
-          console.log("dispatching 'complete' event");
+          console.log("dispatching 'complete' event on transaction");
         }
         const event = new FakeEvent("complete");
         event.eventPath = [this, this.db];
diff --git a/packages/idb-bridge/src/MemoryBackend.test.ts 
b/packages/idb-bridge/src/MemoryBackend.test.ts
index c21c2d06..5ec818f5 100644
--- a/packages/idb-bridge/src/MemoryBackend.test.ts
+++ b/packages/idb-bridge/src/MemoryBackend.test.ts
@@ -235,3 +235,60 @@ test("Spec: Example 1 Part 3", async t => {
 
   t.pass();
 });
+
+
+test("simple deletion", async t => {
+  const backend = new MemoryBackend();
+  const idb = new BridgeIDBFactory(backend);
+
+  const request = idb.open("library");
+  request.onupgradeneeded = () => {
+    const db = request.result;
+    const store = db.createObjectStore("books", { keyPath: "isbn" });
+    const titleIndex = store.createIndex("by_title", "title", { unique: true 
});
+    const authorIndex = store.createIndex("by_author", "author");
+  };
+
+  const db: BridgeIDBDatabase = await promiseFromRequest(request);
+
+  t.is(db.name, "library");
+
+  const tx = db.transaction("books", "readwrite");
+  tx.oncomplete = () => {
+    console.log("oncomplete called");
+  };
+
+  const store = tx.objectStore("books");
+
+  store.put({ title: "Quarry Memories", author: "Fred", isbn: 123456 });
+  store.put({ title: "Water Buffaloes", author: "Fred", isbn: 234567 });
+  store.put({ title: "Bedrock Nights", author: "Barney", isbn: 345678 });
+
+  await promiseFromTransaction(tx);
+
+  const tx2 = db.transaction("books", "readwrite");
+
+  const store2 = tx2.objectStore("books");
+
+  const req1 = store2.get(234567);
+  await promiseFromRequest(req1);
+  t.is(req1.readyState, "done");
+  t.is(req1.result.author, "Fred");
+
+  store2.delete(123456);
+
+  const req2 = store2.get(123456);
+  await promiseFromRequest(req2);
+  t.is(req2.readyState, "done");
+  t.is(req2.result, undefined);
+
+  const req3 = store2.get(234567);
+  await promiseFromRequest(req3);
+  t.is(req3.readyState, "done");
+  t.is(req3.result.author, "Fred");
+
+  await promiseFromTransaction(tx2);
+
+  t.pass();
+});
+
diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
index 1a85a739..a31adb82 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -8,6 +8,7 @@ import {
   RecordGetRequest,
   RecordGetResponse,
   ResultLevel,
+  StoreLevel,
 } from "./backend-interface";
 import structuredClone from "./util/structuredClone";
 import {
@@ -655,10 +656,10 @@ export class MemoryBackend implements Backend {
   async deleteRecord(
     btx: DatabaseTransaction,
     objectStoreName: string,
-    range: import("./BridgeIDBKeyRange").default,
+    range: BridgeIDBKeyRange,
   ): Promise<void> {
     if (this.enableTracing) {
-      console.log(`TRACING: deleteRecord`);
+      console.log(`TRACING: deleteRecord from store ${objectStoreName}`);
     }
     const myConn = this.connectionsByTransaction[btx.transactionCookie];
     if (!myConn) {
@@ -671,7 +672,112 @@ export class MemoryBackend implements Backend {
     if (db.txLevel < TransactionLevel.Write) {
       throw Error("only allowed in write transaction");
     }
-    throw Error("not implemented");
+    if (typeof range !== "object") {
+      throw Error("deleteRecord got invalid range (must be object)");
+    }
+    if (!("lowerOpen" in range)) {
+      throw Error("deleteRecord got invalid range (sanity check failed, 
'lowerOpen' missing)");
+    }
+
+    const schema = myConn.modifiedSchema
+      ? myConn.modifiedSchema
+      : db.committedSchema;
+    const objectStore = myConn.objectStoreMap[objectStoreName];
+
+    if (!objectStore.modifiedData) {
+      objectStore.modifiedData = objectStore.originalData;
+    }
+
+    let modifiedData = objectStore.modifiedData;
+    let currKey: Key | undefined;
+
+    if (range.lower === undefined || range.lower === null) {
+      currKey = modifiedData.minKey();
+    } else {
+      currKey = range.lower;
+      // We have a range with an lowerOpen lower bound, so don't start
+      // deleting the upper bound.  Instead start with the next higher key.
+      if (range.lowerOpen && currKey !== undefined) {
+       currKey = modifiedData.nextHigherKey(currKey);
+      }
+    }
+
+    // invariant: (currKey is undefined) or (currKey is a valid key)
+
+    while (true) {
+      if (currKey === undefined) {
+        // nothing more to delete!
+        break;
+      }
+      if (range.upper !== null && range.upper !== undefined) {
+        if (range.upperOpen && compareKeys(currKey, range.upper) === 0) {
+          // We have a range that's upperOpen, so stop before we delete the 
upper bound.
+          break;
+        }
+        if ((!range.upperOpen) && compareKeys(currKey, range.upper) > 0) {
+          // The upper range is inclusive, only stop if we're after the upper 
range.
+          break;
+        }
+      }
+
+      const storeEntry = modifiedData.get(currKey);
+      if (!storeEntry) {
+        throw Error("assertion failed");
+      }
+
+      for (const indexName of schema.objectStores[objectStoreName].indexes) {
+        const index = myConn.indexMap[indexName];
+        if (!index) {
+          throw Error("index referenced by object store does not exist");
+        }
+        const indexProperties = schema.indexes[indexName];
+        this.deleteFromIndex(index, storeEntry.primaryKey, storeEntry.value, 
indexProperties);
+      }
+
+      modifiedData = modifiedData.without(currKey);
+
+      currKey = modifiedData.nextHigherKey(currKey);
+    }
+
+    objectStore.modifiedData = modifiedData;
+  }
+
+  private deleteFromIndex(
+    index: Index,
+    primaryKey: Key,
+    value: Value,
+    indexProperties: IndexProperties,
+  ): void {
+    if (this.enableTracing) {
+      console.log(
+        `deleteFromIndex(${index.modifiedName || index.originalName})`,
+      );
+    }
+    if (value === undefined || value === null) {
+      throw Error("cannot delete null/undefined value from index");
+    }
+    let indexData = index.modifiedData || index.originalData;
+    const indexKeys = getIndexKeys(
+      value,
+      indexProperties.keyPath,
+      indexProperties.multiEntry,
+    );
+    for (const indexKey of indexKeys) {
+      const existingRecord = indexData.get(indexKey);
+      if (!existingRecord) {
+        throw Error("db inconsistent: expected index entry missing");
+      }
+      const newPrimaryKeys = existingRecord.primaryKeys.filter((x) => 
compareKeys(x, primaryKey) !== 0);
+      if (newPrimaryKeys.length === 0) {
+        index.originalData = indexData.without(indexKey);
+      } else {
+        const newIndexRecord = {
+          indexKey,
+          primaryKeys: newPrimaryKeys,
+        }
+        index.modifiedData = indexData.with(indexKey, newIndexRecord, true);
+      }
+    }
   }
 
   async getRecords(
@@ -705,6 +811,18 @@ export class MemoryBackend implements Backend {
       range = req.range;
     }
 
+    if (typeof range !== "object") {
+      throw Error(
+        "getRecords was given an invalid range (sanity check failed, not an 
object)",
+      );
+    }
+
+    if (!("lowerOpen" in range)) {
+      throw Error(
+        "getRecords was given an invalid range (sanity check failed, lowerOpen 
missing)",
+      );
+    }
+
     let numResults = 0;
     let indexKeys: Key[] = [];
     let primaryKeys: Key[] = [];
@@ -779,20 +897,21 @@ export class MemoryBackend implements Backend {
         compareKeys(indexEntry.indexKey, req.lastIndexPosition) === 0
       ) {
         let pos = forward ? 0 : indexEntry.primaryKeys.length - 1;
-        console.log("number of primary keys", indexEntry.primaryKeys.length);
-        console.log("start pos is", pos);
+        this.enableTracing &&
+          console.log("number of primary keys", indexEntry.primaryKeys.length);
+        this.enableTracing && console.log("start pos is", pos);
         // Advance past the lastObjectStorePosition
         do {
           const cmpResult = compareKeys(
             req.lastObjectStorePosition,
             indexEntry.primaryKeys[pos],
           );
-          console.log("cmp result is", cmpResult);
+          this.enableTracing && console.log("cmp result is", cmpResult);
           if ((forward && cmpResult < 0) || (!forward && cmpResult > 0)) {
             break;
           }
           pos += forward ? 1 : -1;
-          console.log("now pos is", pos);
+          this.enableTracing && console.log("now pos is", pos);
         } while (pos >= 0 && pos < indexEntry.primaryKeys.length);
 
         // Make sure we're at least at advancedPrimaryPos
@@ -815,8 +934,10 @@ export class MemoryBackend implements Backend {
         primkeySubPos = forward ? 0 : indexEntry.primaryKeys.length - 1;
       }
 
-      console.log("subPos=", primkeySubPos);
-      console.log("indexPos=", indexPos);
+      if (this.enableTracing) {
+        console.log("subPos=", primkeySubPos);
+        console.log("indexPos=", indexPos);
+      }
 
       while (1) {
         if (req.limit != 0 && numResults == req.limit) {
@@ -867,12 +988,16 @@ export class MemoryBackend implements Backend {
           }
         }
         if (!skip) {
-          console.log(`not skipping!, subPos=${primkeySubPos}`);
+          if (this.enableTracing) {
+            console.log(`not skipping!, subPos=${primkeySubPos}`);
+          }
           indexKeys.push(indexEntry.indexKey);
           primaryKeys.push(indexEntry.primaryKeys[primkeySubPos]);
           numResults++;
         } else {
-          console.log("skipping!");
+          if (this.enableTracing) {
+            console.log("skipping!");
+          }
         }
         primkeySubPos += forward ? 1 : -1;
       }
@@ -885,7 +1010,7 @@ export class MemoryBackend implements Backend {
           if (!result) {
             throw Error("invariant violated");
           }
-          values.push(result);
+          values.push(result.value);
         }
       }
     } else {
@@ -905,7 +1030,9 @@ export class MemoryBackend implements Backend {
         // Advance store position if we are either still at the last returned
         // store key, or if we are currently not on a key.
         const storeEntry = storeData.get(storePos);
-        console.log("store entry:", storeEntry);
+        if (this.enableTracing) {
+          console.log("store entry:", storeEntry);
+        }
         if (
           !storeEntry ||
           (req.lastObjectStorePosition !== undefined &&
@@ -915,7 +1042,9 @@ export class MemoryBackend implements Backend {
         }
       } else {
         storePos = forward ? storeData.minKey() : storeData.maxKey();
-        console.log("setting starting store store pos to", storePos);
+        if (this.enableTracing) {
+          console.log("setting starting store pos to", storePos);
+        }
       }
 
       while (1) {
@@ -940,7 +1069,7 @@ export class MemoryBackend implements Backend {
         }
 
         if (req.resultLevel >= ResultLevel.Full) {
-          values.push(res);
+          values.push(res.value);
         }
 
         numResults++;
@@ -983,30 +1112,50 @@ export class MemoryBackend implements Backend {
     const schema = myConn.modifiedSchema
       ? myConn.modifiedSchema
       : db.committedSchema;
-
     const objectStore = myConn.objectStoreMap[storeReq.objectStoreName];
 
-    const storeKeyResult: StoreKeyResult = makeStoreKeyValue(
-      storeReq.value,
-      storeReq.key,
-      objectStore.modifiedKeyGenerator || objectStore.originalKeyGenerator,
-      schema.objectStores[storeReq.objectStoreName].autoIncrement,
-      schema.objectStores[storeReq.objectStoreName].keyPath,
-    );
-    let key = storeKeyResult.key;
-    let value = storeKeyResult.value;
-    objectStore.modifiedKeyGenerator = storeKeyResult.updatedKeyGenerator;
-
     if (!objectStore.modifiedData) {
       objectStore.modifiedData = objectStore.originalData;
     }
     const modifiedData = objectStore.modifiedData;
-    const hasKey = modifiedData.has(key);
-    if (hasKey && !storeReq.overwrite) {
-      throw Error("refusing to overwrite");
+
+    let key;
+    let value;
+
+    if (storeReq.storeLevel === StoreLevel.UpdateExisting) {
+      if (storeReq.key === null || storeReq.key === undefined) {
+        throw Error("invalid update request (key not given)");
+      }
+
+      if (!objectStore.modifiedData.has(storeReq.key)) {
+        throw Error("invalid update request (record does not exist)");
+      }
+      key = storeReq.key;
+      value = storeReq.value;
+    } else {
+      const storeKeyResult: StoreKeyResult = makeStoreKeyValue(
+        storeReq.value,
+        storeReq.key,
+        objectStore.modifiedKeyGenerator || objectStore.originalKeyGenerator,
+        schema.objectStores[storeReq.objectStoreName].autoIncrement,
+        schema.objectStores[storeReq.objectStoreName].keyPath,
+      );
+      key = storeKeyResult.key;
+      value = storeKeyResult.value;
+      objectStore.modifiedKeyGenerator = storeKeyResult.updatedKeyGenerator;
+      const hasKey = modifiedData.has(key);
+
+      if (hasKey && storeReq.storeLevel !== StoreLevel.AllowOverwrite) {
+        throw Error("refusing to overwrite");
+      }
     }
 
-    objectStore.modifiedData = modifiedData.with(key, value, true);
+    const objectStoreRecord: ObjectStoreRecord = {
+      primaryKey: key,
+      value: value,
+    };
+
+    objectStore.modifiedData = modifiedData.with(key, objectStoreRecord, true);
 
     for (const indexName of schema.objectStores[storeReq.objectStoreName]
       .indexes) {
diff --git a/packages/idb-bridge/src/backend-interface.ts 
b/packages/idb-bridge/src/backend-interface.ts
index ab093d9c..7329ed96 100644
--- a/packages/idb-bridge/src/backend-interface.ts
+++ b/packages/idb-bridge/src/backend-interface.ts
@@ -41,6 +41,12 @@ export enum ResultLevel {
   Full,
 }
 
+export enum StoreLevel {
+  NoOverwrite,
+  AllowOverwrite,
+  UpdateExisting,
+}
+
 export interface RecordGetRequest {
   direction: BridgeIDBCursorDirection;
   objectStoreName: string;
@@ -94,7 +100,7 @@ export interface RecordStoreRequest {
   objectStoreName: string;
   value: Value;
   key: Key | undefined;
-  overwrite: boolean;
+  storeLevel: StoreLevel;
 }
 
 export interface Backend {
diff --git a/packages/idb-bridge/src/index.ts b/packages/idb-bridge/src/index.ts
new file mode 100644
index 00000000..a6545874
--- /dev/null
+++ b/packages/idb-bridge/src/index.ts
@@ -0,0 +1,60 @@
+import { BridgeIDBFactory } from "./BridgeIDBFactory";
+import { BridgeIDBCursor } from "./BridgeIDBCursor";
+import { BridgeIDBIndex } from "./BridgeIDBIndex";
+import BridgeIDBDatabase from "./BridgeIDBDatabase";
+import BridgeIDBKeyRange from "./BridgeIDBKeyRange";
+import BridgeIDBObjectStore from "./BridgeIDBObjectStore";
+import BridgeIDBOpenDBRequest from "./BridgeIDBOpenDBRequest";
+import BridgeIDBRequest from "./BridgeIDBRequest";
+import BridgeIDBTransaction from "./BridgeIDBTransaction";
+import BridgeIDBVersionChangeEvent from "./BridgeIDBVersionChangeEvent";
+
+export { BridgeIDBFactory, BridgeIDBCursor };
+
+export { MemoryBackend } from "./MemoryBackend";
+
+// globalThis polyfill, see https://mathiasbynens.be/notes/globalthis
+(function() {
+  if (typeof globalThis === "object") return;
+  Object.defineProperty(Object.prototype, "__magic__", {
+    get: function() {
+      return this;
+    },
+    configurable: true, // This makes it possible to `delete` the getter later.
+  });
+  // @ts-ignore: polyfill magic
+  __magic__.globalThis = __magic__; // lolwat
+  // @ts-ignore: polyfill magic
+  delete Object.prototype.__magic__;
+})();
+
+/**
+ * Populate the global name space such that the given IndexedDB factory is made
+ * available globally.
+ */
+export function shimIndexedDB(factory: BridgeIDBFactory): void {
+  // @ts-ignore: shimming
+  globalThis.indexedDB = factory;
+  // @ts-ignore: shimming
+  globalThis.IDBCursor = BridgeIDBCursor;
+  // @ts-ignore: shimming
+  globalThis.IDBKeyRange = BridgeIDBKeyRange;
+  // @ts-ignore: shimming
+  globalThis.IDBDatabase = BridgeIDBDatabase;
+  // @ts-ignore: shimming
+  globalThis.IDBFactory = BridgeIDBFactory;
+  // @ts-ignore: shimming
+  globalThis.IDBIndex = BridgeIDBIndex;
+  // @ts-ignore: shimming
+  globalThis.IDBKeyRange = BridgeIDBKeyRange;
+  // @ts-ignore: shimming
+  globalThis.IDBObjectStore = BridgeIDBObjectStore;
+  // @ts-ignore: shimming
+  globalThis.IDBOpenDBRequest = BridgeIDBOpenDBRequest;
+  // @ts-ignore: shimming
+  globalThis.IDBRequest = BridgeIDBRequest;
+  // @ts-ignore: shimming
+  globalThis.IDBTransaction = BridgeIDBTransaction;
+  // @ts-ignore: shimming
+  globalThis.IDBVersionChangeEvent = BridgeIDBVersionChangeEvent;
+}
diff --git a/packages/idb-bridge/src/util/FakeEventTarget.ts 
b/packages/idb-bridge/src/util/FakeEventTarget.ts
index f20432df..025f21b4 100644
--- a/packages/idb-bridge/src/util/FakeEventTarget.ts
+++ b/packages/idb-bridge/src/util/FakeEventTarget.ts
@@ -54,7 +54,6 @@ const invokeEventListeners = (event: FakeEvent, obj: 
FakeEventTarget) => {
       continue;
     }
 
-    console.log(`invoking ${event.type} event listener`, listener);
     // @ts-ignore
     listener.callback.call(event.currentTarget, event);
   }
@@ -81,7 +80,6 @@ const invokeEventListeners = (event: FakeEvent, obj: 
FakeEventTarget) => {
       type: event.type,
     };
     if (!stopped(event, listener)) {
-      console.log(`invoking on${event.type} event listener`, listener);
       // @ts-ignore
       listener.callback.call(event.currentTarget, event);
     }
@@ -100,7 +98,7 @@ abstract class FakeEventTarget {
   public readonly onupgradeneeded: EventCallback | null | undefined;
   public readonly onversionchange: EventCallback | null | undefined;
 
-  static enableTracing: boolean = true;
+  static enableTracing: boolean = false;
 
   public addEventListener(
     type: EventType,
diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.ts 
b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
index 4f45e0d8..845634ac 100644
--- a/packages/idb-bridge/src/util/makeStoreKeyValue.ts
+++ b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
@@ -18,7 +18,7 @@ export function makeStoreKeyValue(
   autoIncrement: boolean,
   keyPath: KeyPath | null,
 ): StoreKeyResult {
-  const haveKey = key !== undefined && key !== null;
+  const haveKey = key !== null && key !== undefined;
   const haveKeyPath = keyPath !== null && keyPath !== undefined;
 
   // This models a decision table on (haveKey, haveKeyPath, autoIncrement)

-- 
To stop receiving notification emails like this one, please contact
address@hidden.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]