Improve IDB error reporting

Hope this can help with
https://github.com/vector-im/hydrogen-web/issues/50
This commit is contained in:
Bruno Windels 2020-08-18 17:27:40 +02:00
parent 991f5fee4c
commit cc4c9d7893
3 changed files with 32 additions and 10 deletions

View File

@ -39,6 +39,11 @@ export class StorageError extends Error {
if (typeof cause.code === "number") { if (typeof cause.code === "number") {
fullMessage += `(code: ${cause.name}) `; fullMessage += `(code: ${cause.name}) `;
} }
}
if (value) {
fullMessage += `(value: ${JSON.stringify(value)}) `;
}
if (cause) {
fullMessage += cause.message; fullMessage += cause.message;
} }
super(fullMessage); super(fullMessage);
@ -48,4 +53,8 @@ export class StorageError extends Error {
this.cause = cause; this.cause = cause;
this.value = value; this.value = value;
} }
get name() {
return "StorageError";
}
} }

View File

@ -114,7 +114,7 @@ export class Store extends QueryTarget {
return await reqAsPromise(this._idbStore.put(value)); return await reqAsPromise(this._idbStore.put(value));
} catch(err) { } catch(err) {
const originalErr = err.cause; const originalErr = err.cause;
throw new StorageError(`put on ${this._idbStore.name} failed`, originalErr, value); throw new StorageError(`put on ${err.databaseName}.${err.storeName} failed`, originalErr, value);
} }
} }
@ -123,11 +123,17 @@ export class Store extends QueryTarget {
return await reqAsPromise(this._idbStore.add(value)); return await reqAsPromise(this._idbStore.add(value));
} catch(err) { } catch(err) {
const originalErr = err.cause; const originalErr = err.cause;
throw new StorageError(`add on ${this._idbStore.name} failed`, originalErr, value); throw new StorageError(`add on ${err.databaseName}.${err.storeName} failed`, originalErr, value);
} }
} }
delete(keyOrKeyRange) { async delete(keyOrKeyRange) {
return reqAsPromise(this._idbStore.delete(keyOrKeyRange)); try {
return await reqAsPromise(this._idbStore.delete(keyOrKeyRange));
} catch(err) {
const originalErr = err.cause;
throw new StorageError(`delete on ${err.databaseName}.${err.storeName} failed`, originalErr, keyOrKeyRange);
}
} }
} }

View File

@ -15,7 +15,18 @@ limitations under the License.
*/ */
import { StorageError } from "../common.js"; import { StorageError } from "../common.js";
import { readPath } from "../../../utils/validate.js";
class WrappedDOMException extends StorageError {
constructor(request) {
// protect against browsers not implementing any of these properties by using readPath
const storeName = readPath(request, ["source", "name"], "<unknown store>");
const databaseName = readPath(request, ["source", "transaction", "db", "name"], "<unknown db>");
super(`Failed IDBRequest on ${databaseName}.${storeName}`, request.error);
this.storeName = storeName;
this.databaseName = databaseName;
}
}
// storage keys are defined to be unsigned 32bit numbers in WebPlatform.js, which is assumed by idb // storage keys are defined to be unsigned 32bit numbers in WebPlatform.js, which is assumed by idb
export function encodeUint32(n) { export function encodeUint32(n) {
@ -37,21 +48,17 @@ export function openDatabase(name, createObjectStore, version) {
return reqAsPromise(req); return reqAsPromise(req);
} }
function wrapError(err) {
return new StorageError(`wrapped DOMException`, err);
}
export function reqAsPromise(req) { export function reqAsPromise(req) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
req.addEventListener("success", event => resolve(event.target.result)); req.addEventListener("success", event => resolve(event.target.result));
req.addEventListener("error", event => reject(wrapError(event.target.error))); req.addEventListener("error", event => reject(new WrappedDOMException(event.target)));
}); });
} }
export function txnAsPromise(txn) { export function txnAsPromise(txn) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
txn.addEventListener("complete", resolve); txn.addEventListener("complete", resolve);
txn.addEventListener("abort", event => reject(wrapError(event.target.error))); txn.addEventListener("abort", event => reject(new WrappedDOMException(event.target)));
}); });
} }