aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorian Dold <florian@dold.me>2021-02-16 10:31:55 +0100
committerFlorian Dold <florian@dold.me>2021-02-16 13:47:00 +0100
commitd1f00aeaa26af6835ea3f47ac280b1e67d672fa2 (patch)
tree9a5a364f8a5eb1e038bca936d621c015dab53304
parentd384bd5c62198f1160119e60776350109a8ca7d3 (diff)
get IDB tests to pass again, add new one
-rw-r--r--packages/idb-bridge/src/backend-interface.ts1
-rw-r--r--packages/idb-bridge/src/bridge-idb.ts256
-rw-r--r--packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts34
-rw-r--r--packages/idb-bridge/src/idb-wpt-ported/value.test.ts37
-rw-r--r--packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts12
-rw-r--r--packages/idb-bridge/src/util/FakeEventTarget.ts15
-rw-r--r--packages/idb-bridge/src/util/validateKeyPath.ts2
-rw-r--r--packages/idb-bridge/tsconfig.json1
8 files changed, 239 insertions, 119 deletions
diff --git a/packages/idb-bridge/src/backend-interface.ts b/packages/idb-bridge/src/backend-interface.ts
index a9e3e960e..14b5da64a 100644
--- a/packages/idb-bridge/src/backend-interface.ts
+++ b/packages/idb-bridge/src/backend-interface.ts
@@ -17,7 +17,6 @@
import { BridgeIDBDatabaseInfo, BridgeIDBKeyRange } from "./bridge-idb";
import {
IDBCursorDirection,
- IDBKeyPath,
IDBTransactionMode,
IDBValidKey,
} from "./idbtypes";
diff --git a/packages/idb-bridge/src/bridge-idb.ts b/packages/idb-bridge/src/bridge-idb.ts
index ce09fcb8e..c4c589790 100644
--- a/packages/idb-bridge/src/bridge-idb.ts
+++ b/packages/idb-bridge/src/bridge-idb.ts
@@ -61,7 +61,11 @@ import FakeEventTarget from "./util/FakeEventTarget";
import { normalizeKeyPath } from "./util/normalizeKeyPath";
import openPromise from "./util/openPromise";
import queueTask from "./util/queueTask";
-import { structuredClone, structuredEncapsulate, structuredRevive } from "./util/structuredClone";
+import {
+ structuredClone,
+ structuredEncapsulate,
+ structuredRevive,
+} from "./util/structuredClone";
import validateKeyPath from "./util/validateKeyPath";
import valueToKey from "./util/valueToKey";
@@ -266,7 +270,7 @@ export class BridgeIDBCursor implements IDBCursor {
const transaction = this._effectiveObjectStore._transaction;
- if (transaction._state !== "active") {
+ if (!transaction._active) {
throw new TransactionInactiveError();
}
@@ -322,7 +326,7 @@ export class BridgeIDBCursor implements IDBCursor {
public continue(key?: IDBValidKey) {
const transaction = this._effectiveObjectStore._transaction;
- if (transaction._state !== "active") {
+ if (!transaction._active) {
throw new TransactionInactiveError();
}
@@ -384,7 +388,7 @@ export class BridgeIDBCursor implements IDBCursor {
public delete() {
const transaction = this._effectiveObjectStore._transaction;
- if (transaction._state !== "active") {
+ if (!transaction._active) {
throw new TransactionInactiveError();
}
@@ -455,7 +459,7 @@ export class BridgeIDBCursorWithValue extends BridgeIDBCursor {
* Ensure that an active version change transaction is currently running.
*/
const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
- if (!database._runningVersionchangeTransaction) {
+ if (!database._upgradeTransaction) {
throw new InvalidStateError();
}
@@ -467,11 +471,11 @@ const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
);
const transaction = transactions[transactions.length - 1];
- if (!transaction || transaction._state === "finished") {
+ if (!transaction || transaction._finished) {
throw new InvalidStateError();
}
- if (transaction._state !== "active") {
+ if (!transaction._active) {
throw new TransactionInactiveError();
}
@@ -480,12 +484,13 @@ const confirmActiveVersionchangeTransaction = (database: BridgeIDBDatabase) => {
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#database-interface
/** @public */
-export class BridgeIDBDatabase extends FakeEventTarget {
+export class BridgeIDBDatabase extends FakeEventTarget implements IDBDatabase {
_closePending = false;
_closed = false;
- _runningVersionchangeTransaction = false;
_transactions: Array<BridgeIDBTransaction> = [];
+ _upgradeTransaction: BridgeIDBTransaction | null = null;
+
_backendConnection: DatabaseConnection;
_backend: Backend;
@@ -499,8 +504,10 @@ export class BridgeIDBDatabase extends FakeEventTarget {
return this._schema.databaseVersion;
}
- get objectStoreNames(): FakeDOMStringList {
- return fakeDOMStringList(Object.keys(this._schema.objectStores)).sort();
+ get objectStoreNames(): DOMStringList {
+ return fakeDOMStringList(
+ Object.keys(this._schema.objectStores),
+ ).sort() as DOMStringList;
}
/**
@@ -509,9 +516,11 @@ export class BridgeIDBDatabase extends FakeEventTarget {
_closeConnection() {
this._closePending = true;
+ // Spec is unclear what "complete" means, we assume it's
+ // the same as "finished".
const transactionsComplete = this._transactions.every(
(transaction: BridgeIDBTransaction) => {
- return transaction._state === "finished";
+ return transaction._finished;
},
);
@@ -525,6 +534,13 @@ export class BridgeIDBDatabase extends FakeEventTarget {
}
}
+ /**
+ * Refresh the schema by querying it from the backend.
+ */
+ _refreshSchema() {
+ this._schema = this._backend.getSchema(this._backendConnection);
+ }
+
constructor(backend: Backend, backendConnection: DatabaseConnection) {
super();
@@ -537,7 +553,10 @@ export class BridgeIDBDatabase extends FakeEventTarget {
// http://w3c.github.io/IndexedDB/#dom-idbdatabase-createobjectstore
public createObjectStore(
name: string,
- options: { autoIncrement?: boolean; keyPath?: IDBKeyPath } | null = {},
+ options: {
+ autoIncrement?: boolean;
+ keyPath?: null | IDBKeyPath | IDBKeyPath[];
+ } | null = {},
): BridgeIDBObjectStore {
if (name === undefined) {
throw new TypeError();
@@ -572,7 +591,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
transaction._backend.createObjectStore(
backendTx,
name,
- (keyPath !== null) ? normalizeKeyPath(keyPath) : null,
+ keyPath !== null ? normalizeKeyPath(keyPath) : null,
autoIncrement,
);
@@ -593,6 +612,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
storeNames: string | string[],
mode?: IDBTransactionMode,
backendTransaction?: DatabaseTransaction,
+ openRequest?: BridgeIDBOpenDBRequest,
): BridgeIDBTransaction {
mode = mode !== undefined ? mode : "readonly";
if (
@@ -603,16 +623,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
throw new TypeError("Invalid mode: " + mode);
}
- const hasActiveVersionchange = this._transactions.some(
- (transaction: BridgeIDBTransaction) => {
- return (
- transaction._state === "active" &&
- transaction.mode === "versionchange" &&
- transaction._db === this
- );
- },
- );
- if (hasActiveVersionchange) {
+ if (this._upgradeTransaction) {
throw new InvalidStateError();
}
@@ -627,7 +638,7 @@ export class BridgeIDBDatabase extends FakeEventTarget {
throw new InvalidAccessError();
}
for (const storeName of storeNames) {
- if (this.objectStoreNames.indexOf(storeName) < 0) {
+ if (!this.objectStoreNames.contains(storeName)) {
throw new NotFoundError(
"No objectStore named " + storeName + " in this database",
);
@@ -639,9 +650,12 @@ export class BridgeIDBDatabase extends FakeEventTarget {
mode,
this,
backendTransaction,
+ openRequest,
);
this._transactions.push(tx);
queueTask(() => tx._start());
+ // "When a transaction is created its active flag is initially set."
+ tx._active = true;
return tx;
}
@@ -809,20 +823,25 @@ export class BridgeIDBFactory {
dbconn,
requestedVersion,
);
- db._runningVersionchangeTransaction = true;
const transaction = db._internalTransaction(
[],
"versionchange",
backendTransaction,
+ request,
);
+
+ db._upgradeTransaction = transaction;
+
const event = new BridgeIDBVersionChangeEvent("upgradeneeded", {
newVersion: version,
oldVersion: existingVersion,
});
- request.result = db;
+ transaction._active = true;
+
request.readyState = "done";
+ request.result = db;
request.transaction = transaction;
request.dispatchEvent(event);
@@ -832,15 +851,30 @@ export class BridgeIDBFactory {
// We don't explicitly exit the versionchange transaction,
// since this is already done by the BridgeIDBTransaction.
- db._runningVersionchangeTransaction = false;
+ db._upgradeTransaction = null;
- const event2 = new FakeEvent("success", {
- bubbles: false,
- cancelable: false,
- });
- event2.eventPath = [request];
+ // We re-use the same transaction (as per spec) here.
+ transaction._active = true;
+ if (transaction._aborted) {
+ request.result = undefined;
+ request.error = new AbortError();
+ request.readyState = "done";
+ const event2 = new FakeEvent("error", {
+ bubbles: false,
+ cancelable: false,
+ });
+ event2.eventPath = [request];
+ request.dispatchEvent(event2);
+ } else {
+ console.log(`dispatching success event, _active=${transaction._active}`);
+ const event2 = new FakeEvent("success", {
+ bubbles: false,
+ cancelable: false,
+ });
+ event2.eventPath = [request];
- request.dispatchEvent(event2);
+ request.dispatchEvent(event2);
+ }
}
this.connections.push(db);
@@ -871,7 +905,7 @@ const confirmActiveTransaction = (
throw new InvalidStateError();
}
- if (index._objectStore._transaction._state !== "active") {
+ if (!index._objectStore._transaction._active) {
throw new TransactionInactiveError();
}
@@ -931,11 +965,11 @@ export class BridgeIDBIndex implements IDBIndex {
set name(name: any) {
const transaction = this._objectStore._transaction;
- if (!transaction._db._runningVersionchangeTransaction) {
+ if (!transaction._db._upgradeTransaction) {
throw new InvalidStateError();
}
- if (transaction._state !== "active") {
+ if (!transaction._active) {
throw new TransactionInactiveError();
}
@@ -1282,7 +1316,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
get _indexNames(): FakeDOMStringList {
return fakeDOMStringList(
Object.keys(this._schema.objectStores[this._name].indexes),
- ).sort()
+ ).sort();
}
get indexNames(): DOMStringList {
@@ -1330,7 +1364,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
set name(newName: any) {
const transaction = this._transaction;
- if (!transaction._db._runningVersionchangeTransaction) {
+ if (!transaction._db._upgradeTransaction) {
throw new InvalidStateError();
}
@@ -1581,7 +1615,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new TypeError();
}
- if (!this._transaction._db._runningVersionchangeTransaction) {
+ if (!this._transaction._db._upgradeTransaction) {
throw new InvalidStateError();
}
@@ -1628,7 +1662,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new TypeError();
}
- if (this._transaction._state === "finished") {
+ if (this._transaction._finished) {
throw new InvalidStateError();
}
@@ -1649,7 +1683,7 @@ export class BridgeIDBObjectStore implements IDBObjectStore {
throw new InvalidStateError();
}
- if (!this._transaction._db._runningVersionchangeTransaction) {
+ if (!this._transaction._db._upgradeTransaction) {
throw new InvalidStateError();
}
@@ -1755,6 +1789,7 @@ export class BridgeIDBRequest extends FakeEventTarget implements IDBRequest {
cancelable: true,
});
event.eventPath = [];
+
this.dispatchEvent(event);
}
@@ -1791,24 +1826,41 @@ export class BridgeIDBOpenDBRequest
export class BridgeIDBTransaction
extends FakeEventTarget
implements IDBTransaction {
- public _state: "active" | "inactive" | "committing" | "finished" = "active";
- public _started = false;
- public _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
+ _committed: boolean = false;
+ /**
+ * A transaction is active as long as new operations can be
+ * placed against it.
+ */
+ _active: boolean = false;
+ _started: boolean = false;
+ _aborted: boolean = false;
+ _objectStoresCache: Map<string, BridgeIDBObjectStore> = new Map();
+
+ /**
+ * https://www.w3.org/TR/IndexedDB-2/#transaction-lifetime-concept
+ *
+ * When a transaction is committed or aborted, it is said to be finished.
+ */
+ get _finished(): boolean {
+ return this._committed || this._aborted;
+ }
+
+ _openRequest: BridgeIDBOpenDBRequest | null = null;
- public _backendTransaction?: DatabaseTransaction;
+ _backendTransaction?: DatabaseTransaction;
- public _objectStoreNames: FakeDOMStringList;
+ _objectStoreNames: FakeDOMStringList;
get objectStoreNames(): DOMStringList {
return this._objectStoreNames as DOMStringList;
}
- public mode: IDBTransactionMode;
- public _db: BridgeIDBDatabase;
+ mode: IDBTransactionMode;
+ _db: BridgeIDBDatabase;
get db(): IDBDatabase {
- return this.db;
+ return this._db;
}
- public _error: Error | null = null;
+ _error: Error | null = null;
get error(): DOMException {
return this._error as DOMException;
@@ -1823,7 +1875,7 @@ export class BridgeIDBTransaction
public _scope: Set<string>;
private _requests: Array<{
- operation: () => void;
+ operation: () => Promise<void>;
request: BridgeIDBRequest;
}> = [];
@@ -1836,6 +1888,7 @@ export class BridgeIDBTransaction
mode: IDBTransactionMode,
db: BridgeIDBDatabase,
backendTransaction?: DatabaseTransaction,
+ openRequest?: BridgeIDBOpenDBRequest,
) {
super();
@@ -1850,11 +1903,17 @@ export class BridgeIDBTransaction
this._objectStoreNames = fakeDOMStringList(Array.from(this._scope).sort());
this._db._transactions.push(this);
+
+ this._openRequest = openRequest ?? null;
}
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-steps-for-aborting-a-transaction
async _abort(errName: string | null) {
- this._state = "finished";
+ if (BridgeIDBFactory.enableTracing) {
+ console.log("TRACE: aborting transaction");
+ }
+
+ this._aborted = true;
if (errName !== null) {
const e = new Error();
@@ -1862,30 +1921,45 @@ export class BridgeIDBTransaction
this._error = e;
}
+ if (BridgeIDBFactory.enableTracing) {
+ console.log(`TRACE: aborting ${this._requests.length} requests`);
+ }
+
// Should this directly remove from _requests?
for (const { request } of this._requests) {
+ console.log("ready state:", request.readyState);
if (request.readyState !== "done") {
- request.readyState = "done"; // This will cancel execution of this request's operation
- if (request._source) {
- request.result = undefined;
- request.error = new AbortError();
-
- const event = new FakeEvent("error", {
- bubbles: true,
- cancelable: true,
- });
- event.eventPath = [this._db, this];
- request.dispatchEvent(event);
+ // This will cancel execution of this request's operation
+ request.readyState = "done";
+ if (BridgeIDBFactory.enableTracing) {
+ console.log("dispatching error event");
}
+ request.result = undefined;
+ request.error = new AbortError();
+
+ const event = new FakeEvent("error", {
+ bubbles: true,
+ cancelable: true,
+ });
+ event.eventPath = [request, this, this._db];
+ console.log("dispatching error event for request after abort");
+ request.dispatchEvent(event);
}
}
+ // ("abort a transaction", step 5.1)
+ if (this._openRequest) {
+ this._db._upgradeTransaction = null;
+ }
+
// Only roll back if we actually executed the scheduled operations.
const maybeBtx = this._backendTransaction;
if (maybeBtx) {
await this._backend.rollback(maybeBtx);
}
+ this._db._refreshSchema();
+
queueTask(() => {
const event = new FakeEvent("abort", {
bubbles: true,
@@ -1894,20 +1968,24 @@ export class BridgeIDBTransaction
event.eventPath = [this._db];
this.dispatchEvent(event);
});
+
+ if (this._openRequest) {
+ this._openRequest.transaction = null;
+ this._openRequest.result = undefined;
+ this._openRequest.readyState = "pending";
+ }
}
public abort() {
- if (this._state === "committing" || this._state === "finished") {
+ if (this._finished) {
throw new InvalidStateError();
}
- this._state = "active";
-
this._abort(null);
}
// http://w3c.github.io/IndexedDB/#dom-idbtransaction-objectstore
public objectStore(name: string): BridgeIDBObjectStore {
- if (this._state !== "active") {
+ if (!this._active) {
throw new InvalidStateError();
}
@@ -1925,7 +2003,7 @@ export class BridgeIDBTransaction
const operation = obj.operation;
let request = obj.hasOwnProperty("request") ? obj.request : null;
- if (this._state !== "active") {
+ if (!this._active) {
throw new TransactionInactiveError();
}
@@ -2001,10 +2079,8 @@ export class BridgeIDBTransaction
request.result = result;
request.error = undefined;
- // http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-a-success-event
- if (this._state === "inactive") {
- this._state = "active";
- }
+ // https://www.w3.org/TR/IndexedDB-2/#fire-error-event
+ this._active = true;
event = new FakeEvent("success", {
bubbles: false,
cancelable: false,
@@ -2014,9 +2090,11 @@ export class BridgeIDBTransaction
event.eventPath = [request, this, this._db];
request.dispatchEvent(event);
} catch (err) {
- if (this._state !== "committing") {
- this._abort("AbortError");
+ if (BridgeIDBFactory.enableTracing) {
+ console.log("TRACING: caught error in transaction success event handler");
}
+ this._abort("AbortError");
+ this._active = false;
throw err;
}
} catch (err) {
@@ -2028,9 +2106,7 @@ export class BridgeIDBTransaction
request.error = err;
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-fire-an-error-event
- if (this._state === "inactive") {
- this._state = "active";
- }
+ this._active = true;
event = new FakeEvent("error", {
bubbles: true,
cancelable: true,
@@ -2040,9 +2116,7 @@ export class BridgeIDBTransaction
event.eventPath = [this._db, this];
request.dispatchEvent(event);
} catch (err) {
- if (this._state !== "committing") {
- this._abort("AbortError");
- }
+ this._abort("AbortError");
throw err;
}
if (!event.canceled) {
@@ -2061,17 +2135,13 @@ export class BridgeIDBTransaction
return;
}
- if (this._state !== "finished" && this._state !== "committing") {
+ if (!this._finished && !this._committed) {
if (BridgeIDBFactory.enableTracing) {
console.log("finishing transaction");
}
- this._state = "committing";
-
await this._backend.commit(this._backendTransaction);
-
- this._state = "finished";
-
+ this._committed = true;
if (!this._error) {
if (BridgeIDBFactory.enableTracing) {
console.log("dispatching 'complete' event on transaction");
@@ -2089,15 +2159,19 @@ export class BridgeIDBTransaction
this._resolveWait();
}
+ if (this._aborted) {
+ this._resolveWait();
+ }
}
public commit() {
- if (this._state !== "active") {
+ // The current spec doesn't even have an explicit commit method.
+ // We still support it, effectively as a "no-operation" that
+ // prevents new operations from being scheduled.
+ if (!this._active) {
throw new InvalidStateError();
}
-
- this._state = "committing";
- // We now just wait for auto-commit ...
+ this._active = false;
}
public toString() {
diff --git a/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts b/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts
new file mode 100644
index 000000000..da9ed2632
--- /dev/null
+++ b/packages/idb-bridge/src/idb-wpt-ported/abort-in-initial-upgradeneeded.test.ts
@@ -0,0 +1,34 @@
+import test from "ava";
+import { createdb } from "./wptsupport";
+
+test("WPT test abort-in-initial-upgradeneeded.htm", async (t) => {
+ await new Promise<void>((resolve, reject) => {
+ var db: any;
+ var open_rq = createdb(t, undefined, 2);
+
+ open_rq.onupgradeneeded = function (e) {
+ const tgt = e.target as any;
+ db = tgt.result;
+ t.assert(db.version === 2);
+ var transaction = tgt.transaction;
+ transaction.oncomplete = () => t.fail("unexpected transaction.complete");
+ transaction.onabort = function (e: any) {
+ console.log(`version: ${e.target.db.version}`);
+ t.deepEqual(e.target.db.version, 0);
+ };
+ db.onabort = function () {};
+ transaction.abort();
+ };
+
+ open_rq.onerror = function (e) {
+ const tgt = e.target as any;
+ t.deepEqual(open_rq, e.target);
+ t.deepEqual(tgt.result, undefined);
+ t.deepEqual(tgt.error.name, "AbortError");
+ console.log(`version (onerror): ${db.version}`);
+ t.deepEqual(db.version, 0);
+ t.deepEqual(open_rq.transaction, null);
+ resolve();
+ };
+ });
+});
diff --git a/packages/idb-bridge/src/idb-wpt-ported/value.test.ts b/packages/idb-bridge/src/idb-wpt-ported/value.test.ts
index c4a8315c6..b1c2b3bee 100644
--- a/packages/idb-bridge/src/idb-wpt-ported/value.test.ts
+++ b/packages/idb-bridge/src/idb-wpt-ported/value.test.ts
@@ -24,23 +24,24 @@ test.cb("WPT test value.htm, array", (t) => {
});
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();
- };
+ 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) => {
+ console.log("target", e.target);
+ console.log("result", e.target.result);
+ 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
index 10c11b7a6..f7b065f23 100644
--- a/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
+++ b/packages/idb-bridge/src/idb-wpt-ported/wptsupport.ts
@@ -23,8 +23,18 @@ export function createdb(
return rq_open;
}
-export function assert_key_equals(actual: any, expected: any, description?: string) {
+export function assert_key_equals(
+ actual: any,
+ expected: any,
+ description?: string,
+) {
if (0 != compareKeys(actual, expected)) {
throw Error("expected keys to be the same");
}
}
+
+export function assert_equals(actual: any, expected: any) {
+ if (actual !== expected) {
+ throw Error("assert_equals failed");
+ }
+}
diff --git a/packages/idb-bridge/src/util/FakeEventTarget.ts b/packages/idb-bridge/src/util/FakeEventTarget.ts
index d2f46c98f..95489b4ac 100644
--- a/packages/idb-bridge/src/util/FakeEventTarget.ts
+++ b/packages/idb-bridge/src/util/FakeEventTarget.ts
@@ -97,13 +97,14 @@ abstract class FakeEventTarget implements EventTarget {
public readonly listeners: Listener[] = [];
// These will be overridden in individual subclasses and made not readonly
- public readonly onabort: EventListener | null | undefined;
- public readonly onblocked: EventListener | null | undefined;
- public readonly oncomplete: EventListener | null | undefined;
- public readonly onerror: EventListener | null | undefined;
- public readonly onsuccess: EventListener | null | undefined;
- public readonly onupgradeneeded: EventListener | null | undefined;
- public readonly onversionchange: EventListener | null | undefined;
+ public readonly onabort: EventListener | null = null;
+ public readonly onblocked: EventListener | null = null;
+ public readonly oncomplete: EventListener | null = null;
+ public readonly onerror: EventListener | null = null;
+ public readonly onsuccess: EventListener | null = null;
+ public readonly onclose: EventListener | null = null;
+ public readonly onupgradeneeded: EventListener | null = null;
+ public readonly onversionchange: EventListener | null = null;
static enableTracing: boolean = false;
diff --git a/packages/idb-bridge/src/util/validateKeyPath.ts b/packages/idb-bridge/src/util/validateKeyPath.ts
index 8057172df..2beb3c468 100644
--- a/packages/idb-bridge/src/util/validateKeyPath.ts
+++ b/packages/idb-bridge/src/util/validateKeyPath.ts
@@ -17,7 +17,7 @@
import { IDBKeyPath } from "../idbtypes";
// http://www.w3.org/TR/2015/REC-IndexedDB-20150108/#dfn-valid-key-path
-const validateKeyPath = (keyPath: IDBKeyPath, parent?: "array" | "string") => {
+const validateKeyPath = (keyPath: IDBKeyPath | IDBKeyPath[], parent?: "array" | "string") => {
// 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 extractKey)
let myKeyPath: IDBKeyPath | IDBKeyPath[] = keyPath;
diff --git a/packages/idb-bridge/tsconfig.json b/packages/idb-bridge/tsconfig.json
index 99c5e6e3c..4f730e1c5 100644
--- a/packages/idb-bridge/tsconfig.json
+++ b/packages/idb-bridge/tsconfig.json
@@ -5,6 +5,7 @@
"module": "ESNext",
"moduleResolution": "node",
"target": "ES6",
+ "allowJs": true,
"noImplicitAny": true,
"outDir": "lib",
"declaration": true,