gnunet-svn
[Top][All Lists]
Advanced

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

[taler-wallet-core] branch master updated: idb: add first web platform t


From: gnunet
Subject: [taler-wallet-core] branch master updated: idb: add first web platform tests, fix issues detected by them
Date: Mon, 08 Feb 2021 19:59:25 +0100

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

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

The following commit(s) were added to refs/heads/master by this push:
     new 8c92499d idb: add first web platform tests, fix issues detected by them
8c92499d is described below

commit 8c92499d85917693d2f87252419f0eeccd239a2b
Author: Florian Dold <florian@dold.me>
AuthorDate: Mon Feb 8 19:59:19 2021 +0100

    idb: add first web platform tests, fix issues detected by them
---
 packages/idb-bridge/package.json                   |   4 +-
 packages/idb-bridge/src/MemoryBackend.ts           |  27 +-
 packages/idb-bridge/src/backend-interface.ts       |   6 +-
 packages/idb-bridge/src/bridge-idb.ts              | 316 +++++++++++++--------
 packages/idb-bridge/src/idb-wpt-ported/README      |   3 +
 .../idb-bridge/src/idb-wpt-ported/keypath.test.ts  | 191 +++++++++++++
 .../idb-bridge/src/idb-wpt-ported/value.test.ts    |  46 +++
 .../idb-bridge/src/idb-wpt-ported/wptsupport.ts    |  30 ++
 packages/idb-bridge/src/util/injectKey.ts          |   4 +-
 packages/idb-bridge/src/util/makeStoreKeyValue.ts  |  10 +-
 packages/idb-bridge/src/util/normalizeKeyPath.ts   |  41 +++
 packages/idb-bridge/src/util/structuredClone.ts    |  21 +-
 packages/taler-wallet-core/src/util/query.ts       |   6 +-
 pnpm-lock.yaml                                     |  57 +++-
 14 files changed, 614 insertions(+), 148 deletions(-)

diff --git a/packages/idb-bridge/package.json b/packages/idb-bridge/package.json
index cfa0fa9f..5f7b3d11 100644
--- a/packages/idb-bridge/package.json
+++ b/packages/idb-bridge/package.json
@@ -25,7 +25,9 @@
     "typescript": "^4.1.3"
   },
   "dependencies": {
-    "tslib": "^2.1.0"
+    "tslib": "^2.1.0",
+    "typeson": "^5.18.2",
+    "typeson-registry": "^1.0.0-alpha.38"
   },
   "ava": {
     "require": [
diff --git a/packages/idb-bridge/src/MemoryBackend.ts 
b/packages/idb-bridge/src/MemoryBackend.ts
index 6a52a555..2b4437bc 100644
--- a/packages/idb-bridge/src/MemoryBackend.ts
+++ b/packages/idb-bridge/src/MemoryBackend.ts
@@ -27,7 +27,11 @@ import {
   StoreLevel,
   RecordStoreResponse,
 } from "./backend-interface";
-import structuredClone from "./util/structuredClone";
+import {
+  structuredClone,
+  structuredEncapsulate,
+  structuredRevive,
+} from "./util/structuredClone";
 import {
   InvalidStateError,
   InvalidAccessError,
@@ -39,7 +43,12 @@ import compareKeys from "./util/cmp";
 import { StoreKeyResult, makeStoreKeyValue } from "./util/makeStoreKeyValue";
 import getIndexKeys from "./util/getIndexKeys";
 import openPromise from "./util/openPromise";
-import { IDBKeyPath, IDBKeyRange, IDBTransactionMode, IDBValidKey } from 
"./idbtypes";
+import {
+  IDBKeyPath,
+  IDBKeyRange,
+  IDBTransactionMode,
+  IDBValidKey,
+} from "./idbtypes";
 import { BridgeIDBKeyRange } from "./bridge-idb";
 
 type Key = IDBValidKey;
@@ -742,7 +751,7 @@ export class MemoryBackend implements Backend {
   createObjectStore(
     btx: DatabaseTransaction,
     name: string,
-    keyPath: string | string[] | null,
+    keyPath: string[] | null,
     autoIncrement: boolean,
   ): void {
     if (this.enableTracing) {
@@ -776,9 +785,6 @@ export class MemoryBackend implements Backend {
     if (!schema) {
       throw Error("no schema for versionchange tx");
     }
-    if (Array.isArray(keyPath)) {
-      throw Error("array key path not supported for object stores");
-    }
     schema.objectStores[name] = {
       autoIncrement,
       keyPath,
@@ -791,7 +797,7 @@ export class MemoryBackend implements Backend {
     btx: DatabaseTransaction,
     indexName: string,
     objectStoreName: string,
-    keyPath: IDBKeyPath,
+    keyPath: string[],
     multiEntry: boolean,
     unique: boolean,
   ): void {
@@ -1401,9 +1407,10 @@ export class MemoryBackend implements Backend {
         schema.objectStores[storeReq.objectStoreName].autoIncrement;
       const keyPath = schema.objectStores[storeReq.objectStoreName].keyPath;
       let storeKeyResult: StoreKeyResult;
+      const revivedValue = structuredRevive(storeReq.value);
       try {
         storeKeyResult = makeStoreKeyValue(
-          storeReq.value,
+          revivedValue,
           storeReq.key,
           keygen,
           autoIncrement,
@@ -1413,7 +1420,9 @@ export class MemoryBackend implements Backend {
         if (e instanceof DataError) {
           const kp = JSON.stringify(keyPath);
           const n = storeReq.objectStoreName;
-          const m = `Could not extract key from value, objectStore=${n}, 
keyPath=${kp}`;
+          const m = `Could not extract key from value, objectStore=${n}, 
keyPath=${kp}, value=${JSON.stringify(
+            storeReq.value,
+          )}`;
           if (this.enableTracing) {
             console.error(e);
             console.error("value was:", storeReq.value);
diff --git a/packages/idb-bridge/src/backend-interface.ts 
b/packages/idb-bridge/src/backend-interface.ts
index 756a5b96..a9e3e960 100644
--- a/packages/idb-bridge/src/backend-interface.ts
+++ b/packages/idb-bridge/src/backend-interface.ts
@@ -25,14 +25,14 @@ import {
 
 /** @public */
 export interface ObjectStoreProperties {
-  keyPath: IDBKeyPath | null;
+  keyPath: string[] | null;
   autoIncrement: boolean;
   indexes: { [nameame: string]: IndexProperties };
 }
 
 /** @public */
 export interface IndexProperties {
-  keyPath: IDBKeyPath;
+  keyPath: string[];
   multiEntry: boolean;
   unique: boolean;
 }
@@ -199,7 +199,7 @@ export interface Backend {
     btx: DatabaseTransaction,
     indexName: string,
     objectStoreName: string,
-    keyPath: IDBKeyPath,
+    keyPath: string | string[],
     multiEntry: boolean,
     unique: boolean,
   ): void;
diff --git a/packages/idb-bridge/src/bridge-idb.ts 
b/packages/idb-bridge/src/bridge-idb.ts
index 2bced800..ce09fcb8 100644
--- a/packages/idb-bridge/src/bridge-idb.ts
+++ b/packages/idb-bridge/src/bridge-idb.ts
@@ -25,7 +25,23 @@ import {
   Schema,
   StoreLevel,
 } from "./backend-interface";
-import { EventListener, IDBCursorDirection, IDBKeyPath, IDBKeyRange, 
IDBTransactionMode, IDBValidKey } from "./idbtypes";
+import {
+  DOMException,
+  DOMStringList,
+  EventListener,
+  IDBCursor,
+  IDBCursorDirection,
+  IDBDatabase,
+  IDBIndex,
+  IDBKeyPath,
+  IDBKeyRange,
+  IDBObjectStore,
+  IDBOpenDBRequest,
+  IDBRequest,
+  IDBTransaction,
+  IDBTransactionMode,
+  IDBValidKey,
+} from "./idbtypes";
 import compareKeys from "./util/cmp";
 import enforceRange from "./util/enforceRange";
 import {
@@ -42,9 +58,10 @@ import {
 import { fakeDOMStringList } from "./util/fakeDOMStringList";
 import FakeEvent from "./util/FakeEvent";
 import FakeEventTarget from "./util/FakeEventTarget";
+import { normalizeKeyPath } from "./util/normalizeKeyPath";
 import openPromise from "./util/openPromise";
 import queueTask from "./util/queueTask";
-import structuredClone from "./util/structuredClone";
+import { structuredClone, structuredEncapsulate, structuredRevive } from 
"./util/structuredClone";
 import validateKeyPath from "./util/validateKeyPath";
 import valueToKey from "./util/valueToKey";
 
@@ -87,7 +104,7 @@ function simplifyRange(
  *
  * @public
  */
-export class BridgeIDBCursor {
+export class BridgeIDBCursor implements IDBCursor {
   _request: BridgeIDBRequest | undefined;
 
   private _gotValue: boolean = false;
@@ -127,7 +144,7 @@ export class BridgeIDBCursor {
     if (this.source instanceof BridgeIDBObjectStore) {
       return this.source;
     }
-    return this.source.objectStore;
+    return this.source._objectStore;
   }
 
   get _backend(): Backend {
@@ -149,15 +166,23 @@ export class BridgeIDBCursor {
     /* For babel */
   }
 
-  get key() {
-    return this._key;
+  get key(): IDBValidKey {
+    const k = this._key;
+    if (k === null || k === undefined) {
+      throw Error("no key");
+    }
+    return k;
   }
   set key(val) {
     /* For babel */
   }
 
-  get primaryKey() {
-    return this._primaryKey;
+  get primaryKey(): IDBValidKey {
+    const k = this._primaryKey;
+    if (k === 0 || k === undefined) {
+      throw Error("no key");
+    }
+    return k;
   }
 
   set primaryKey(val) {
@@ -221,7 +246,7 @@ export class BridgeIDBCursor {
     this._primaryKey = response.primaryKeys![0];
 
     if (!this._keyOnly) {
-      this._value = response.values![0];
+      this._value = structuredRevive(response.values![0]);
     }
 
     this._gotValue = true;
@@ -239,7 +264,7 @@ export class BridgeIDBCursor {
       throw new TypeError();
     }
 
-    const transaction = this._effectiveObjectStore.transaction;
+    const transaction = this._effectiveObjectStore._transaction;
 
     if (transaction._state !== "active") {
       throw new TransactionInactiveError();
@@ -266,7 +291,7 @@ export class BridgeIDBCursor {
 
     const storeReq: RecordStoreRequest = {
       key: this._primaryKey,
-      value: value,
+      value: structuredEncapsulate(value),
       objectStoreName: this._objectStoreName,
       storeLevel: StoreLevel.UpdateExisting,
     };
@@ -295,7 +320,7 @@ export class BridgeIDBCursor {
    * 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBCursor-continue-void-any-key
    */
   public continue(key?: IDBValidKey) {
-    const transaction = this._effectiveObjectStore.transaction;
+    const transaction = this._effectiveObjectStore._transaction;
 
     if (transaction._state !== "active") {
       throw new TransactionInactiveError();
@@ -357,7 +382,7 @@ export class BridgeIDBCursor {
   }
 
   public delete() {
-    const transaction = this._effectiveObjectStore.transaction;
+    const transaction = this._effectiveObjectStore._transaction;
 
     if (transaction._state !== "active") {
       throw new TransactionInactiveError();
@@ -547,7 +572,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
     transaction._backend.createObjectStore(
       backendTx,
       name,
-      keyPath,
+      (keyPath !== null) ? normalizeKeyPath(keyPath) : null,
       autoIncrement,
     );
 
@@ -583,7 +608,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
         return (
           transaction._state === "active" &&
           transaction.mode === "versionchange" &&
-          transaction.db === this
+          transaction._db === this
         );
       },
     );
@@ -656,7 +681,7 @@ export class BridgeIDBFactory {
   // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-deleteDatabase-IDBOpenDBRequest-DOMString-name
   public deleteDatabase(name: string): BridgeIDBOpenDBRequest {
     const request = new BridgeIDBOpenDBRequest();
-    request.source = null;
+    request._source = null;
 
     queueTask(async () => {
       const databases = await this.backend.getDatabases();
@@ -709,7 +734,7 @@ export class BridgeIDBFactory {
 
   // tslint:disable-next-line max-line-length
   // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#widl-IDBFactory-open-IDBOpenDBRequest-DOMString-name-unsigned-long-long-version
-  public open(name: string, version?: number) {
+  public open(name: string, version?: number): BridgeIDBOpenDBRequest {
     if (arguments.length > 1 && version !== undefined) {
       // Based on spec, not sure why "MAX_SAFE_INTEGER" instead of "unsigned 
long long", but it's needed to pass
       // tests
@@ -761,9 +786,7 @@ export class BridgeIDBFactory {
         });
         event2.eventPath = [request];
         request.dispatchEvent(event2);
-      }
-
-      if (existingVersion < requestedVersion) {
+      } else if (existingVersion < requestedVersion) {
         // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-running-a-versionchange-transaction
 
         for (const otherConn of this.connections) {
@@ -803,7 +826,9 @@ export class BridgeIDBFactory {
         request.transaction = transaction;
         request.dispatchEvent(event);
 
+        console.log("awaiting until initial transaction is done");
         await transaction._waitDone();
+        console.log("initial transaction is done");
 
         // We don't explicitly exit the versionchange transaction,
         // since this is already done by the BridgeIDBTransaction.
@@ -842,47 +867,51 @@ export class BridgeIDBFactory {
 const confirmActiveTransaction = (
   index: BridgeIDBIndex,
 ): BridgeIDBTransaction => {
-  if (index._deleted || index.objectStore._deleted) {
+  if (index._deleted || index._objectStore._deleted) {
     throw new InvalidStateError();
   }
 
-  if (index.objectStore.transaction._state !== "active") {
+  if (index._objectStore._transaction._state !== "active") {
     throw new TransactionInactiveError();
   }
 
-  return index.objectStore.transaction;
+  return index._objectStore._transaction;
 };
 
 // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#idl-def-IDBIndex
 /** @public */
-export class BridgeIDBIndex {
-  objectStore: BridgeIDBObjectStore;
+export class BridgeIDBIndex implements IDBIndex {
+  _objectStore: BridgeIDBObjectStore;
+
+  get objectStore(): IDBObjectStore {
+    return this._objectStore;
+  }
 
   get _schema(): Schema {
-    return this.objectStore.transaction.db._schema;
+    return this._objectStore._transaction._db._schema;
   }
 
-  get keyPath(): IDBKeyPath {
-    return this._schema.objectStores[this.objectStore.name].indexes[this._name]
+  get keyPath(): IDBKeyPath | IDBKeyPath[] {
+    return 
this._schema.objectStores[this._objectStore.name].indexes[this._name]
       .keyPath;
   }
 
   get multiEntry(): boolean {
-    return this._schema.objectStores[this.objectStore.name].indexes[this._name]
+    return 
this._schema.objectStores[this._objectStore.name].indexes[this._name]
       .multiEntry;
   }
 
   get unique(): boolean {
-    return this._schema.objectStores[this.objectStore.name].indexes[this._name]
+    return 
this._schema.objectStores[this._objectStore.name].indexes[this._name]
       .unique;
   }
 
   get _backend(): Backend {
-    return this.objectStore._backend;
+    return this._objectStore._backend;
   }
 
   _confirmActiveTransaction(): { btx: DatabaseTransaction } {
-    return this.objectStore._confirmActiveTransaction();
+    return this._objectStore._confirmActiveTransaction();
   }
 
   private _name: string;
@@ -891,7 +920,7 @@ export class BridgeIDBIndex {
 
   constructor(objectStore: BridgeIDBObjectStore, name: string) {
     this._name = name;
-    this.objectStore = objectStore;
+    this._objectStore = objectStore;
   }
 
   get name() {
@@ -900,9 +929,9 @@ export class BridgeIDBIndex {
 
   // https://w3c.github.io/IndexedDB/#dom-idbindex-name
   set name(name: any) {
-    const transaction = this.objectStore.transaction;
+    const transaction = this._objectStore._transaction;
 
-    if (!transaction.db._runningVersionchangeTransaction) {
+    if (!transaction._db._runningVersionchangeTransaction) {
       throw new InvalidStateError();
     }
 
@@ -919,9 +948,9 @@ export class BridgeIDBIndex {
       return;
     }
 
-    this._backend.renameIndex(btx, this.objectStore.name, oldName, newName);
+    this._backend.renameIndex(btx, this._objectStore.name, oldName, newName);
 
-    if (this.objectStore.indexNames.indexOf(name) >= 0) {
+    if (this._objectStore._indexNames.indexOf(name) >= 0) {
       throw new ConstraintError();
     }
   }
@@ -942,12 +971,12 @@ export class BridgeIDBIndex {
     }
 
     const request = new BridgeIDBRequest();
-    request.source = this;
-    request.transaction = this.objectStore.transaction;
+    request._source = this;
+    request.transaction = this._objectStore._transaction;
 
     const cursor = new BridgeIDBCursorWithValue(
       this,
-      this.objectStore.name,
+      this._objectStore.name,
       this._name,
       range,
       direction,
@@ -958,7 +987,7 @@ export class BridgeIDBIndex {
       return cursor._iterate();
     };
 
-    return this.objectStore.transaction._execRequestAsync({
+    return this._objectStore._transaction._execRequestAsync({
       operation,
       request,
       source: this,
@@ -981,12 +1010,12 @@ export class BridgeIDBIndex {
     }
 
     const request = new BridgeIDBRequest();
-    request.source = this;
-    request.transaction = this.objectStore.transaction;
+    request._source = this;
+    request.transaction = this._objectStore._transaction;
 
     const cursor = new BridgeIDBCursor(
       this,
-      this.objectStore.name,
+      this._objectStore.name,
       this._name,
       range,
       direction,
@@ -994,7 +1023,7 @@ export class BridgeIDBIndex {
       true,
     );
 
-    return this.objectStore.transaction._execRequestAsync({
+    return this._objectStore._transaction._execRequestAsync({
       operation: cursor._iterate.bind(cursor),
       request,
       source: this,
@@ -1013,7 +1042,7 @@ export class BridgeIDBIndex {
       indexName: this._name,
       limit: 1,
       range: key,
-      objectStoreName: this.objectStore._name,
+      objectStoreName: this._objectStore._name,
       resultLevel: ResultLevel.Full,
     };
 
@@ -1027,17 +1056,20 @@ export class BridgeIDBIndex {
       if (!values) {
         throw Error("invariant violated");
       }
-      return values[0];
+      return structuredRevive(values[0]);
     };
 
-    return this.objectStore.transaction._execRequestAsync({
+    return this._objectStore._transaction._execRequestAsync({
       operation,
       source: this,
     });
   }
 
   // http://w3c.github.io/IndexedDB/#dom-idbindex-getall
-  public getAll(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
+  public getAll(
+    query?: BridgeIDBKeyRange | IDBValidKey,
+    count?: number,
+  ): IDBRequest<any[]> {
     throw Error("not implemented");
   }
 
@@ -1054,7 +1086,7 @@ export class BridgeIDBIndex {
       indexName: this._name,
       limit: 1,
       range: key,
-      objectStoreName: this.objectStore._name,
+      objectStoreName: this._objectStore._name,
       resultLevel: ResultLevel.OnlyKeys,
     };
 
@@ -1071,14 +1103,17 @@ export class BridgeIDBIndex {
       return primaryKeys[0];
     };
 
-    return this.objectStore.transaction._execRequestAsync({
+    return this._objectStore._transaction._execRequestAsync({
       operation,
       source: this,
     });
   }
 
   // http://w3c.github.io/IndexedDB/#dom-idbindex-getallkeys
-  public getAllKeys(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
+  public getAllKeys(
+    query?: BridgeIDBKeyRange | IDBValidKey,
+    count?: number,
+  ): IDBRequest<IDBValidKey[]> {
     throw Error("not implemented");
   }
 
@@ -1098,7 +1133,7 @@ export class BridgeIDBIndex {
       indexName: this._name,
       limit: 1,
       range: key,
-      objectStoreName: this.objectStore._name,
+      objectStoreName: this._objectStore._name,
       resultLevel: ResultLevel.OnlyCount,
     };
 
@@ -1108,7 +1143,7 @@ export class BridgeIDBIndex {
       return result.count;
     };
 
-    return this.objectStore.transaction._execRequestAsync({
+    return this._objectStore._transaction._execRequestAsync({
       operation,
       source: this,
     });
@@ -1231,36 +1266,44 @@ export class BridgeIDBKeyRange {
 
 // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#object-store
 /** @public */
-export class BridgeIDBObjectStore {
+export class BridgeIDBObjectStore implements IDBObjectStore {
   _indexesCache: Map<string, BridgeIDBIndex> = new Map();
 
-  transaction: BridgeIDBTransaction;
+  _transaction: BridgeIDBTransaction;
+
+  get transaction(): IDBTransaction {
+    return this._transaction;
+  }
 
   get autoIncrement(): boolean {
     return this._schema.objectStores[this._name].autoIncrement;
   }
 
-  get indexNames(): FakeDOMStringList {
+  get _indexNames(): FakeDOMStringList {
     return fakeDOMStringList(
       Object.keys(this._schema.objectStores[this._name].indexes),
-    ).sort();
+    ).sort()
+  }
+
+  get indexNames(): DOMStringList {
+    return this._indexNames as DOMStringList;
   }
 
-  get keyPath(): IDBKeyPath | null {
-    return this._schema.objectStores[this._name].keyPath;
+  get keyPath(): IDBKeyPath | IDBKeyPath[] {
+    return this._schema.objectStores[this._name].keyPath!;
   }
 
   _name: string;
 
   get _schema(): Schema {
-    return this.transaction.db._schema;
+    return this._transaction._db._schema;
   }
 
   _deleted: boolean = false;
 
   constructor(transaction: BridgeIDBTransaction, name: string) {
     this._name = name;
-    this.transaction = transaction;
+    this._transaction = transaction;
   }
 
   get name() {
@@ -1268,15 +1311,15 @@ export class BridgeIDBObjectStore {
   }
 
   get _backend(): Backend {
-    return this.transaction.db._backend;
+    return this._transaction._db._backend;
   }
 
   get _backendConnection(): DatabaseConnection {
-    return this.transaction.db._backendConnection;
+    return this._transaction._db._backendConnection;
   }
 
   _confirmActiveTransaction(): { btx: DatabaseTransaction } {
-    const btx = this.transaction._backendTransaction;
+    const btx = this._transaction._backendTransaction;
     if (!btx) {
       throw new InvalidStateError();
     }
@@ -1285,9 +1328,9 @@ export class BridgeIDBObjectStore {
 
   // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-name
   set name(newName: any) {
-    const transaction = this.transaction;
+    const transaction = this._transaction;
 
-    if (!transaction.db._runningVersionchangeTransaction) {
+    if (!transaction._db._runningVersionchangeTransaction) {
       throw new InvalidStateError();
     }
 
@@ -1302,7 +1345,7 @@ export class BridgeIDBObjectStore {
     }
 
     this._backend.renameObjectStore(btx, oldName, newName);
-    this.transaction.db._schema = this._backend.getSchema(
+    this._transaction._db._schema = this._backend.getSchema(
       this._backendConnection,
     );
   }
@@ -1311,7 +1354,7 @@ export class BridgeIDBObjectStore {
     if (BridgeIDBFactory.enableTracing) {
       console.log(`TRACE: IDBObjectStore._store`);
     }
-    if (this.transaction.mode === "readonly") {
+    if (this._transaction.mode === "readonly") {
       throw new ReadOnlyError();
     }
     const operation = async () => {
@@ -1319,7 +1362,7 @@ export class BridgeIDBObjectStore {
       const result = await this._backend.storeRecord(btx, {
         objectStoreName: this._name,
         key: key,
-        value: value,
+        value: structuredEncapsulate(value),
         storeLevel: overwrite
           ? StoreLevel.AllowOverwrite
           : StoreLevel.NoOverwrite,
@@ -1327,7 +1370,7 @@ export class BridgeIDBObjectStore {
       return result.key;
     };
 
-    return this.transaction._execRequestAsync({ operation, source: this });
+    return this._transaction._execRequestAsync({ operation, source: this });
   }
 
   public put(value: any, key?: IDBValidKey) {
@@ -1349,7 +1392,7 @@ export class BridgeIDBObjectStore {
       throw new TypeError();
     }
 
-    if (this.transaction.mode === "readonly") {
+    if (this._transaction.mode === "readonly") {
       throw new ReadOnlyError();
     }
 
@@ -1366,7 +1409,7 @@ export class BridgeIDBObjectStore {
       return this._backend.deleteRecord(btx, this._name, keyRange);
     };
 
-    return this.transaction._execRequestAsync({
+    return this._transaction._execRequestAsync({
       operation,
       source: this,
     });
@@ -1424,31 +1467,39 @@ export class BridgeIDBObjectStore {
       if (!values) {
         throw Error("invariant violated");
       }
-      return values[0];
+      return structuredRevive(values[0]);
     };
 
-    return this.transaction._execRequestAsync({
+    return this._transaction._execRequestAsync({
       operation,
       source: this,
     });
   }
 
   // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getall
-  public getAll(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
+  public getAll(
+    query?: BridgeIDBKeyRange | IDBValidKey,
+    count?: number,
+  ): IDBRequest<any[]> {
     throw Error("not implemented");
   }
 
   // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getkey
-  public getKey(key?: BridgeIDBKeyRange | IDBValidKey) {
+  public getKey(
+    key?: BridgeIDBKeyRange | IDBValidKey,
+  ): IDBRequest<IDBValidKey | undefined> {
     throw Error("not implemented");
   }
 
   // http://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallkeys
-  public getAllKeys(query?: BridgeIDBKeyRange | IDBValidKey, count?: number) {
+  public getAllKeys(
+    query?: BridgeIDBKeyRange | IDBValidKey,
+    count?: number,
+  ): IDBRequest<any[]> {
     throw Error("not implemented");
   }
 
-  public clear() {
+  public clear(): IDBRequest<undefined> {
     throw Error("not implemented");
   }
 
@@ -1464,8 +1515,8 @@ export class BridgeIDBObjectStore {
     }
 
     const request = new BridgeIDBRequest();
-    request.source = this;
-    request.transaction = this.transaction;
+    request._source = this;
+    request.transaction = this._transaction;
 
     const cursor = new BridgeIDBCursorWithValue(
       this,
@@ -1476,7 +1527,7 @@ export class BridgeIDBObjectStore {
       request,
     );
 
-    return this.transaction._execRequestAsync({
+    return this._transaction._execRequestAsync({
       operation: () => cursor._iterate(),
       request,
       source: this,
@@ -1499,8 +1550,8 @@ export class BridgeIDBObjectStore {
     }
 
     const request = new BridgeIDBRequest();
-    request.source = this;
-    request.transaction = this.transaction;
+    request._source = this;
+    request.transaction = this._transaction;
 
     const cursor = new BridgeIDBCursor(
       this,
@@ -1512,7 +1563,7 @@ export class BridgeIDBObjectStore {
       true,
     );
 
-    return this.transaction._execRequestAsync({
+    return this._transaction._execRequestAsync({
       operation: cursor._iterate.bind(cursor),
       request,
       source: this,
@@ -1530,7 +1581,7 @@ export class BridgeIDBObjectStore {
       throw new TypeError();
     }
 
-    if (!this.transaction.db._runningVersionchangeTransaction) {
+    if (!this._transaction._db._runningVersionchangeTransaction) {
       throw new InvalidStateError();
     }
 
@@ -1545,11 +1596,11 @@ export class BridgeIDBObjectStore {
         ? optionalParameters.unique
         : false;
 
-    if (this.transaction.mode !== "versionchange") {
+    if (this._transaction.mode !== "versionchange") {
       throw new InvalidStateError();
     }
 
-    if (this.indexNames.indexOf(indexName) >= 0) {
+    if (this._indexNames.indexOf(indexName) >= 0) {
       throw new ConstraintError();
     }
 
@@ -1563,7 +1614,7 @@ export class BridgeIDBObjectStore {
       btx,
       indexName,
       this._name,
-      keyPath,
+      normalizeKeyPath(keyPath),
       multiEntry,
       unique,
     );
@@ -1577,7 +1628,7 @@ export class BridgeIDBObjectStore {
       throw new TypeError();
     }
 
-    if (this.transaction._state === "finished") {
+    if (this._transaction._state === "finished") {
       throw new InvalidStateError();
     }
 
@@ -1594,11 +1645,11 @@ export class BridgeIDBObjectStore {
       throw new TypeError();
     }
 
-    if (this.transaction.mode !== "versionchange") {
+    if (this._transaction.mode !== "versionchange") {
       throw new InvalidStateError();
     }
 
-    if (!this.transaction.db._runningVersionchangeTransaction) {
+    if (!this._transaction._db._runningVersionchangeTransaction) {
       throw new InvalidStateError();
     }
 
@@ -1638,7 +1689,7 @@ export class BridgeIDBObjectStore {
       return result.count;
     };
 
-    return this.transaction._execRequestAsync({ operation, source: this });
+    return this._transaction._execRequestAsync({ operation, source: this });
   }
 
   public toString() {
@@ -1647,10 +1698,20 @@ export class BridgeIDBObjectStore {
 }
 
 /** @public */
-export class BridgeIDBRequest extends FakeEventTarget {
+export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
   _result: any = null;
   _error: Error | null | undefined = null;
-  source: BridgeIDBCursor | BridgeIDBIndex | BridgeIDBObjectStore | null = 
null;
+  get source(): IDBObjectStore | IDBIndex | IDBCursor {
+    if (this._source) {
+      return this._source;
+    }
+    throw Error("source is null");
+  }
+  _source:
+    | BridgeIDBCursor
+    | BridgeIDBIndex
+    | BridgeIDBObjectStore
+    | null = null;
   transaction: BridgeIDBTransaction | null = null;
   readyState: "done" | "pending" = "pending";
   onsuccess: EventListener | null = null;
@@ -1708,14 +1769,16 @@ export class BridgeIDBRequest extends FakeEventTarget {
 }
 
 /** @public */
-export class BridgeIDBOpenDBRequest extends BridgeIDBRequest {
+export class BridgeIDBOpenDBRequest
+  extends BridgeIDBRequest
+  implements IDBOpenDBRequest {
   public onupgradeneeded: EventListener | null = null;
   public onblocked: EventListener | null = null;
 
   constructor() {
     super();
     // https://www.w3.org/TR/IndexedDB/#open-requests
-    this.source = null;
+    this._source = null;
   }
 
   public toString() {
@@ -1725,17 +1788,32 @@ export class BridgeIDBOpenDBRequest extends 
BridgeIDBRequest {
 
 // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#transaction
 /** @public */
-export class BridgeIDBTransaction extends FakeEventTarget {
+export class BridgeIDBTransaction
+  extends FakeEventTarget
+  implements IDBTransaction {
   public _state: "active" | "inactive" | "committing" | "finished" = "active";
   public _started = false;
   public _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
 
   public _backendTransaction?: DatabaseTransaction;
 
-  public objectStoreNames: FakeDOMStringList;
+  public _objectStoreNames: FakeDOMStringList;
+  get objectStoreNames(): DOMStringList {
+    return this._objectStoreNames as DOMStringList;
+  }
   public mode: IDBTransactionMode;
-  public db: BridgeIDBDatabase;
-  public error: Error | null = null;
+  public _db: BridgeIDBDatabase;
+
+  get db(): IDBDatabase {
+    return this.db;
+  }
+
+  public _error: Error | null = null;
+
+  get error(): DOMException {
+    return this._error as DOMException;
+  }
+
   public onabort: EventListener | null = null;
   public oncomplete: EventListener | null = null;
   public onerror: EventListener | null = null;
@@ -1750,7 +1828,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
   }> = [];
 
   get _backend(): Backend {
-    return this.db._backend;
+    return this._db._backend;
   }
 
   constructor(
@@ -1768,10 +1846,10 @@ export class BridgeIDBTransaction extends 
FakeEventTarget {
     this._scope = new Set(storeNames);
     this._backendTransaction = backendTransaction;
     this.mode = mode;
-    this.db = db;
-    this.objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
+    this._db = db;
+    this._objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
 
-    this.db._transactions.push(this);
+    this._db._transactions.push(this);
   }
 
   // 
http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
@@ -1781,14 +1859,14 @@ export class BridgeIDBTransaction extends 
FakeEventTarget {
     if (errName !== null) {
       const e = new Error();
       e.name = errName;
-      this.error = e;
+      this._error = e;
     }
 
     // Should this directly remove from _requests?
     for (const { request } of this._requests) {
       if (request.readyState !== "done") {
         request.readyState = "done"; // This will cancel execution of this 
request's operation
-        if (request.source) {
+        if (request._source) {
           request.result = undefined;
           request.error = new AbortError();
 
@@ -1796,7 +1874,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
             bubbles: true,
             cancelable: true,
           });
-          event.eventPath = [this.db, this];
+          event.eventPath = [this._db, this];
           request.dispatchEvent(event);
         }
       }
@@ -1813,7 +1891,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
         bubbles: true,
         cancelable: false,
       });
-      event.eventPath = [this.db];
+      event.eventPath = [this._db];
       this.dispatchEvent(event);
     });
   }
@@ -1828,7 +1906,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
   }
 
   // http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
-  public objectStore(name: string) {
+  public objectStore(name: string): BridgeIDBObjectStore {
     if (this._state !== "active") {
       throw new InvalidStateError();
     }
@@ -1858,7 +1936,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
         request = new BridgeIDBRequest();
       } else {
         request = new BridgeIDBRequest();
-        request.source = source;
+        request._source = source;
         request.transaction = (source as any).transaction;
       }
     }
@@ -1884,7 +1962,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
 
     if (!this._backendTransaction) {
       this._backendTransaction = await this._backend.beginTransaction(
-        this.db._backendConnection,
+        this._db._backendConnection,
         Array.from(this._scope),
         this.mode,
       );
@@ -1905,7 +1983,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
     }
 
     if (request && operation) {
-      if (!request.source) {
+      if (!request._source) {
         // Special requests like indexes that just need to run some code, with 
error handling already built into
         // operation
         await operation();
@@ -1933,7 +2011,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
           });
 
           try {
-            event.eventPath = [request, this, this.db];
+            event.eventPath = [request, this, this._db];
             request.dispatchEvent(event);
           } catch (err) {
             if (this._state !== "committing") {
@@ -1959,7 +2037,7 @@ export class BridgeIDBTransaction extends FakeEventTarget 
{
           });
 
           try {
-            event.eventPath = [this.db, this];
+            event.eventPath = [this._db, this];
             request.dispatchEvent(event);
           } catch (err) {
             if (this._state !== "committing") {
@@ -1994,20 +2072,20 @@ export class BridgeIDBTransaction extends 
FakeEventTarget {
 
       this._state = "finished";
 
-      if (!this.error) {
+      if (!this._error) {
         if (BridgeIDBFactory.enableTracing) {
           console.log("dispatching 'complete' event on transaction");
         }
         const event = new FakeEvent("complete");
-        event.eventPath = [this, this.db];
+        event.eventPath = [this, this._db];
         this.dispatchEvent(event);
       }
 
-      const idx = this.db._transactions.indexOf(this);
+      const idx = this._db._transactions.indexOf(this);
       if (idx < 0) {
         throw Error("invariant failed");
       }
-      this.db._transactions.splice(idx, 1);
+      this._db._transactions.splice(idx, 1);
 
       this._resolveWait();
     }
diff --git a/packages/idb-bridge/src/idb-wpt-ported/README 
b/packages/idb-bridge/src/idb-wpt-ported/README
new file mode 100644
index 00000000..e0b665aa
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/README
@@ -0,0 +1,3 @@
+This directory contains test cases from the W3C Web Platform Tests suite for 
IndexedDB.
+
+The original code for these tests can be found here: 
https://github.com/web-platform-tests/wpt/tree/master/IndexedDB
\ No newline at end of file
diff --git a/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts 
b/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts
new file mode 100644
index 00000000..61e416a5
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/keypath.test.ts
@@ -0,0 +1,191 @@
+import test from "ava";
+import { assert_key_equals, createdb } from "./wptsupport";
+
+test("WPT test keypath.htm", async (t) => {
+  function keypath(
+    keypath: any,
+    objects: any[],
+    expected_keys: any[],
+    desc?: string,
+  ) {
+    return new Promise<void>((resolve, reject) => {
+      console.log("key path", keypath);
+      console.log("checking", desc);
+      let db: any;
+      const store_name = "store-" + Date.now() + Math.random();
+
+      var open_rq = createdb(t);
+      open_rq.onupgradeneeded = function (e) {
+        db = (e.target as any).result;
+        var objStore = db.createObjectStore(store_name, { keyPath: keypath });
+
+        for (var i = 0; i < objects.length; i++) objStore.add(objects[i]);
+      };
+
+      open_rq.onsuccess = function (e) {
+        var actual_keys: any[] = [],
+          rq = db.transaction(store_name).objectStore(store_name).openCursor();
+
+        rq.onsuccess = (e: any) => {
+          var cursor = e.target.result;
+
+          if (cursor) {
+            actual_keys.push(cursor.key.valueOf());
+            cursor.continue();
+          } else {
+            assert_key_equals(actual_keys, expected_keys, "keyorder array");
+            resolve();
+          }
+        };
+      };
+    });
+  }
+
+  await keypath("my.key", [{ my: { key: 10 } }], [10]);
+
+  await keypath("my.køi", [{ my: { køi: 5 } }], [5]);
+
+  await keypath("my.key_ya", [{ my: { key_ya: 10 } }], [10]);
+
+  await keypath("public.key$ya", [{ public: { key$ya: 10 } }], [10]);
+
+  await keypath("true.$", [{ true: { $: 10 } }], [10]);
+
+  await keypath("my._", [{ my: { _: 10 } }], [10]);
+
+  await keypath("delete.a7", [{ delete: { a7: 10 } }], [10]);
+
+  await keypath(
+    "p.p.p.p.p.p.p.p.p.p.p.p.p.p",
+    [
+      {
+        p: {
+          p: {
+            p: {
+              p: {
+                p: {
+                  p: { p: { p: { p: { p: { p: { p: { p: { p: 10 } } } } } } } 
},
+                },
+              },
+            },
+          },
+        },
+      },
+    ],
+    [10],
+  );
+
+  await keypath(
+    "str.length",
+    [{ str: "pony" }, { str: "my" }, { str: "little" }, { str: "" }],
+    [0, 2, 4, 6],
+  );
+
+  await keypath(
+    "arr.length",
+    [
+      { arr: [0, 0, 0, 0] },
+      { arr: [{}, 0, "hei", "length", Infinity, []] },
+      { arr: [10, 10] },
+      { arr: [] },
+    ],
+    [0, 2, 4, 6],
+  );
+
+  await keypath("length", [[10, 10], "123", { length: 20 }], [2, 3, 20]);
+
+  await keypath(
+    "",
+    [["bags"], "bean", 10],
+    [10, "bean", ["bags"]],
+    "'' uses value as key",
+  );
+
+  await keypath(
+    [""],
+    [["bags"], "bean", 10],
+    [[10], ["bean"], [["bags"]]],
+    "[''] uses value as [key]",
+  );
+
+  await keypath(
+    ["x", "y"],
+    [
+      { x: 10, y: 20 },
+      { y: 1.337, x: 100 },
+    ],
+    [
+      [10, 20],
+      [100, 1.337],
+    ],
+    "['x', 'y']",
+  );
+
+  await keypath(
+    [["x"], ["y"]],
+    [
+      { x: 10, y: 20 },
+      { y: 1.337, x: 100 },
+    ],
+    [
+      [10, 20],
+      [100, 1.337],
+    ],
+    "[['x'], 'y'] (stringifies)",
+  );
+
+  await keypath(
+    [
+      "x",
+      {
+        toString: function () {
+          return "y";
+        },
+      },
+    ],
+    [
+      { x: 10, y: 20 },
+      { y: 1.337, x: 100 },
+    ],
+    [
+      [10, 20],
+      [100, 1.337],
+    ],
+    "['x', {toString->'y'}] (stringifies)",
+  );
+
+  await keypath(
+    ["name", "type"],
+    [
+      { name: "orange", type: "fruit" },
+      { name: "orange", type: ["telecom", "french"] },
+    ],
+    [
+      ["orange", "fruit"],
+      ["orange", ["telecom", "french"]],
+    ],
+  );
+
+  await keypath(
+    ["name", "type.name"],
+    [
+      { name: "orange", type: { name: "fruit" } },
+      { name: "orange", type: { name: "telecom" } },
+    ],
+    [
+      ["orange", "fruit"],
+      ["orange", "telecom"],
+    ],
+  );
+
+  const loop_array: any[] = [];
+  loop_array.push(loop_array);
+  await keypath(
+    loop_array,
+    ["a", 1, ["k"]],
+    [[1], ["a"], [["k"]]],
+    "array loop -> stringify becomes ['']",
+  );
+
+  t.pass();
+});
diff --git a/packages/idb-bridge/src/idb-wpt-ported/value.test.ts 
b/packages/idb-bridge/src/idb-wpt-ported/value.test.ts
new file mode 100644
index 00000000..c4a8315c
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/value.test.ts
@@ -0,0 +1,46 @@
+import test from "ava";
+import { IDBVersionChangeEvent } from "../idbtypes";
+import { createdb } from "./wptsupport";
+
+test.cb("WPT test value.htm, array", (t) => {
+  const value = new Array();
+  const _instanceof = Array;
+
+  t.plan(1);
+
+  createdb(t).onupgradeneeded = function (e: IDBVersionChangeEvent) {
+    (e.target as any).result.createObjectStore("store").add(value, 1);
+    (e.target as any).onsuccess = (e: any) => {
+      console.log("in first onsuccess");
+      e.target.result
+        .transaction("store")
+        .objectStore("store")
+        .get(1).onsuccess = (e: any) => {
+        t.assert(e.target.result instanceof _instanceof, "instanceof");
+        t.end();
+      };
+    };
+  };
+});
+
+test.cb("WPT test value.htm, date", (t) => {
+    const value = new Date();
+    const _instanceof = Date;
+  
+    t.plan(1);
+  
+    createdb(t).onupgradeneeded = function (e: IDBVersionChangeEvent) {
+      (e.target as any).result.createObjectStore("store").add(value, 1);
+      (e.target as any).onsuccess = (e: any) => {
+        console.log("in first onsuccess");
+        e.target.result
+          .transaction("store")
+          .objectStore("store")
+          .get(1).onsuccess = (e: any) => {
+          t.assert(e.target.result instanceof _instanceof, "instanceof");
+          t.end();
+        };
+      };
+    };
+  });
+  
\ No newline at end of file
diff --git a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts 
b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
new file mode 100644
index 00000000..10c11b7a
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
@@ -0,0 +1,30 @@
+import { ExecutionContext } from "ava";
+import { BridgeIDBFactory } from "..";
+import { IDBOpenDBRequest } from "../idbtypes";
+import MemoryBackend from "../MemoryBackend";
+import compareKeys from "../util/cmp";
+
+BridgeIDBFactory.enableTracing = true;
+const idbFactory = new BridgeIDBFactory(new MemoryBackend());
+
+const self = {
+  indexedDB: idbFactory,
+};
+
+export function createdb(
+  t: ExecutionContext<unknown>,
+  dbname?: string,
+  version?: number,
+): IDBOpenDBRequest {
+  var rq_open: IDBOpenDBRequest;
+  dbname = dbname ? dbname : "testdb-" + new Date().getTime() + Math.random();
+  if (version) rq_open = self.indexedDB.open(dbname, version);
+  else rq_open = self.indexedDB.open(dbname);
+  return rq_open;
+}
+
+export function assert_key_equals(actual: any, expected: any, description?: 
string) {
+  if (0 != compareKeys(actual, expected)) {
+    throw Error("expected keys to be the same");
+  }
+}
diff --git a/packages/idb-bridge/src/util/injectKey.ts 
b/packages/idb-bridge/src/util/injectKey.ts
index 678f42d2..63c8deda 100644
--- a/packages/idb-bridge/src/util/injectKey.ts
+++ b/packages/idb-bridge/src/util/injectKey.ts
@@ -16,10 +16,10 @@
 */
 
 import { IDBKeyPath, IDBValidKey } from "../idbtypes";
-import structuredClone from "./structuredClone";
+import { structuredClone } from "./structuredClone";
 
 export function injectKey(
-  keyPath: IDBKeyPath,
+  keyPath: IDBKeyPath | IDBKeyPath[],
   value: any,
   key: IDBValidKey,
 ): any {
diff --git a/packages/idb-bridge/src/util/makeStoreKeyValue.ts 
b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
index b535bced..2281e983 100644
--- a/packages/idb-bridge/src/util/makeStoreKeyValue.ts
+++ b/packages/idb-bridge/src/util/makeStoreKeyValue.ts
@@ -17,7 +17,7 @@
 import extractKey from "./extractKey";
 import { DataError } from "./errors";
 import valueToKey from "./valueToKey";
-import structuredClone from "./structuredClone";
+import { structuredClone } from "./structuredClone";
 import injectKey from "./injectKey";
 import { IDBKeyPath, IDBValidKey } from "../idbtypes";
 
@@ -32,7 +32,7 @@ export function makeStoreKeyValue(
   key: IDBValidKey | undefined,
   currentKeyGenerator: number,
   autoIncrement: boolean,
-  keyPath: IDBKeyPath | null,
+  keyPath: IDBKeyPath | IDBKeyPath[] | null,
 ): StoreKeyResult {
   const haveKey = key !== null && key !== undefined;
   const haveKeyPath = keyPath !== null && keyPath !== undefined;
@@ -63,7 +63,11 @@ export function makeStoreKeyValue(
         };
       } else {
         // (yes, no, no)
-        throw new DataError();
+        return {
+          key: key!,
+          value: value,
+          updatedKeyGenerator: currentKeyGenerator,
+        };
       }
     }
   } else {
diff --git a/packages/idb-bridge/src/util/normalizeKeyPath.ts 
b/packages/idb-bridge/src/util/normalizeKeyPath.ts
new file mode 100644
index 00000000..4e194b2d
--- /dev/null
+++ b/packages/idb-bridge/src/util/normalizeKeyPath.ts
@@ -0,0 +1,41 @@
+/*
+ Copyright 2017 Jeremy Scheff
+ Copyright 2019 Florian Dold
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
+ or implied. See the License for the specific language governing
+ permissions and limitations under the License.
+*/
+
+import { IDBKeyPath } from "../idbtypes";
+
+export function normalizeKeyPath(
+  keyPath: IDBKeyPath | IDBKeyPath[],
+): string | string[] {
+  if (Array.isArray(keyPath)) {
+    const path: string[] = [];
+    for (let item of keyPath) {
+      // This doesn't make sense to me based on the spec, but it is needed to 
pass the W3C KeyPath tests (see same
+      // comment in validateKeyPath)
+      if (
+        item !== undefined &&
+        item !== null &&
+        typeof item !== "string" &&
+        (item as any).toString
+      ) {
+        item = (item as any).toString();
+      }
+      path.push(item);
+    }
+    return path;
+  }
+  return keyPath;
+}
diff --git a/packages/idb-bridge/src/util/structuredClone.ts 
b/packages/idb-bridge/src/util/structuredClone.ts
index c49d0377..9bbeb715 100644
--- a/packages/idb-bridge/src/util/structuredClone.ts
+++ b/packages/idb-bridge/src/util/structuredClone.ts
@@ -14,17 +14,24 @@
  permissions and limitations under the License.
 */
 
-function structuredCloneImpl(val: any, visited: WeakMap<any, boolean>): any {
-  // FIXME: replace with real implementation!
-  return JSON.parse(JSON.stringify(val));
+// @ts-ignore
+import Typeson from "typeson";
+// @ts-ignore
+import structuredCloningThrowing from 
"typeson-registry/dist/presets/structured-cloning-throwing";
+
+const TSON = new Typeson().register(structuredCloningThrowing);
+
+export function structuredEncapsulate(val: any): any {
+  return TSON.encapsulate(val);
+}
+
+export function structuredRevive(val: any): any {
+  return TSON.revive(val);
 }
 
 /**
  * Structured clone for IndexedDB.
  */
 export function structuredClone(val: any): any {
-  const visited: WeakMap<any, boolean> = new WeakMap<any, boolean>();
-  return structuredCloneImpl(val, visited);
+  return structuredRevive(structuredEncapsulate(val));
 }
-
-export default structuredClone;
diff --git a/packages/taler-wallet-core/src/util/query.ts 
b/packages/taler-wallet-core/src/util/query.ts
index d0b8c2ef..71c809ec 100644
--- a/packages/taler-wallet-core/src/util/query.ts
+++ b/packages/taler-wallet-core/src/util/query.ts
@@ -106,7 +106,7 @@ function transactionToPromise(tx: IDBTransaction): 
Promise<void> {
     };
     tx.onerror = () => {
       console.error("Transaction failed:", stack);
-      reject(tx.error);
+      reject(tx._error);
     };
   });
 }
@@ -394,8 +394,8 @@ function runWithTransaction<T, StoreTypes extends 
Store<string, {}>>(
       logger.error(`${stack}`);
     };
     tx.onabort = () => {
-      if (tx.error) {
-        logger.error("Transaction aborted with error:", tx.error);
+      if (tx._error) {
+        logger.error("Transaction aborted with error:", tx._error);
       } else {
         logger.error("Trasaction aborted (no error)");
       }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 2a75c2f2..70a45839 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4,6 +4,8 @@ importers:
   packages/idb-bridge:
     dependencies:
       tslib: 2.1.0
+      typeson: 5.18.2
+      typeson-registry: 1.0.0-alpha.38
     devDependencies:
       '@types/node': 14.14.22
       ava: 3.15.0
@@ -21,6 +23,8 @@ importers:
       rollup: ^2.37.1
       tslib: ^2.1.0
       typescript: ^4.1.3
+      typeson: ^5.18.2
+      typeson-registry: ^1.0.0-alpha.38
   packages/pogen:
     dependencies:
       '@types/node': 14.14.22
@@ -1193,6 +1197,14 @@ packages:
   /balanced-match/1.0.0:
     resolution:
       integrity: sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
+  /base64-arraybuffer-es6/0.6.0:
+    dev: false
+    engines:
+      node: '>=0.10.0'
+    peerDependencies:
+      core-js-bundle: ^3.6.5
+    resolution:
+      integrity: 
sha512-57nLqKj4ShsDwFJWJsM4sZx6u60WbCge35rWRSevUwqxDtRwwxiKAO800zD2upPv4CfdWjQp//wSLar35nDKvA==
   /base64-js/1.5.1:
     dev: true
     resolution:
@@ -3283,6 +3295,9 @@ packages:
     dev: true
     resolution:
       integrity: sha1-QVxEePK8wwEgwizhDtMib30+GOA=
+  /lodash.sortby/4.7.0:
+    resolution:
+      integrity: sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
   /lodash/4.17.20:
     dev: true
     resolution:
@@ -4005,7 +4020,6 @@ packages:
     resolution:
       integrity: 
sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
   /punycode/2.1.1:
-    dev: true
     engines:
       node: '>=6'
     resolution:
@@ -4764,6 +4778,13 @@ packages:
       node: '>=8.0'
     resolution:
       integrity: 
sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+  /tr46/2.0.2:
+    dependencies:
+      punycode: 2.1.1
+    engines:
+      node: '>=8'
+    resolution:
+      integrity: 
sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==
   /trim-off-newlines/1.0.1:
     dev: true
     engines:
@@ -4871,6 +4892,25 @@ packages:
     hasBin: true
     resolution:
       integrity: 
sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==
+  /typeson-registry/1.0.0-alpha.38:
+    dependencies:
+      base64-arraybuffer-es6: 0.6.0
+      typeson: 5.18.2
+      whatwg-url: 8.4.0
+    dev: false
+    engines:
+      node: '>=10.0.0'
+    resolution:
+      integrity: 
sha512-6lt2IhbNT9hyow5hljZqjWtVDXBIaC1X8bBGlBva0Pod2f42g23bVqww09ruquwSC48I8BSSCPi+B2dFHM5ihQ==
+  /typeson/5.18.2:
+    dev: false
+    engines:
+      node: '>=0.1.14'
+    peerDependencies:
+      core-js-bundle: ^3.6.4
+      regenerator-runtime: ^0.13.3
+    resolution:
+      integrity: 
sha512-Vetd+OGX05P4qHyHiSLdHZ5Z5GuQDrHHwSdjkqho9NSCYVSLSfRMjklD/unpHH8tXBR9Z/R05rwJSuMpMFrdsw==
   /uglify-js/3.12.5:
     dev: true
     engines:
@@ -4974,12 +5014,27 @@ packages:
     dev: true
     resolution:
       integrity: sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+  /webidl-conversions/6.1.0:
+    engines:
+      node: '>=10.4'
+    resolution:
+      integrity: 
sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==
   /well-known-symbols/2.0.0:
     dev: true
     engines:
       node: '>=6'
     resolution:
       integrity: 
sha512-ZMjC3ho+KXo0BfJb7JgtQ5IBuvnShdlACNkKkdsqBmYw3bPAaJfPeYUo6tLUaT5tG/Gkh7xkpBhKRQ9e7pyg9Q==
+  /whatwg-url/8.4.0:
+    dependencies:
+      lodash.sortby: 4.7.0
+      tr46: 2.0.2
+      webidl-conversions: 6.1.0
+    dev: false
+    engines:
+      node: '>=10'
+    resolution:
+      integrity: 
sha512-vwTUFf6V4zhcPkWp/4CQPr1TW9Ml6SF4lVyaIMBdJw5i6qUUJ1QWM4Z6YYVkfka0OUIzVo/0aNtGVGk256IKWw==
   /which-module/2.0.0:
     dev: true
     resolution:

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

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