mirror of
https://github.com/vector-im/hydrogen-web.git
synced 2025-01-11 04:27:40 +01:00
wrap everything that can throw a idb DOMException in StorageError
as lumia gives very cryptic errors without a stacktrace.
This commit is contained in:
parent
0fd52be710
commit
bbb5e35bcb
@ -5,9 +5,6 @@ export class HomeServerError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class StorageError extends Error {
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RequestAbortError extends Error {
|
export class RequestAbortError extends Error {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,3 +4,17 @@ export const STORE_MAP = Object.freeze(STORE_NAMES.reduce((nameMap, name) => {
|
|||||||
nameMap[name] = name;
|
nameMap[name] = name;
|
||||||
return nameMap;
|
return nameMap;
|
||||||
}, {}));
|
}, {}));
|
||||||
|
|
||||||
|
export class StorageError extends Error {
|
||||||
|
constructor(message, cause) {
|
||||||
|
let fullMessage = message;
|
||||||
|
if (cause) {
|
||||||
|
fullMessage += ": ";
|
||||||
|
if (cause.name) {
|
||||||
|
fullMessage += `(${cause.name}) `;
|
||||||
|
}
|
||||||
|
fullMessage += cause.message;
|
||||||
|
}
|
||||||
|
super(fullMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Transaction from "./transaction.js";
|
import Transaction from "./transaction.js";
|
||||||
import { STORE_NAMES } from "../common.js";
|
import { STORE_NAMES, StorageError } from "../common.js";
|
||||||
|
|
||||||
export default class Storage {
|
export default class Storage {
|
||||||
constructor(idbDatabase) {
|
constructor(idbDatabase) {
|
||||||
@ -14,19 +14,27 @@ export default class Storage {
|
|||||||
_validateStoreNames(storeNames) {
|
_validateStoreNames(storeNames) {
|
||||||
const idx = storeNames.findIndex(name => !STORE_NAMES.includes(name));
|
const idx = storeNames.findIndex(name => !STORE_NAMES.includes(name));
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
throw new Error(`Tried to open a transaction for unknown store ${storeNames[idx]}`);
|
throw new StorageError(`Tried top, a transaction unknown store ${storeNames[idx]}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async readTxn(storeNames) {
|
async readTxn(storeNames) {
|
||||||
this._validateStoreNames(storeNames);
|
this._validateStoreNames(storeNames);
|
||||||
|
try {
|
||||||
const txn = this._db.transaction(storeNames, "readonly");
|
const txn = this._db.transaction(storeNames, "readonly");
|
||||||
return new Transaction(txn, storeNames);
|
return new Transaction(txn, storeNames);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("readTxn failed", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async readWriteTxn(storeNames) {
|
async readWriteTxn(storeNames) {
|
||||||
this._validateStoreNames(storeNames);
|
this._validateStoreNames(storeNames);
|
||||||
|
try {
|
||||||
const txn = this._db.transaction(storeNames, "readwrite");
|
const txn = this._db.transaction(storeNames, "readwrite");
|
||||||
return new Transaction(txn, storeNames);
|
return new Transaction(txn, storeNames);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("readWriteTxn failed", err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,64 @@
|
|||||||
import QueryTarget from "./query-target.js";
|
import QueryTarget from "./query-target.js";
|
||||||
import { reqAsPromise } from "./utils.js";
|
import { reqAsPromise } from "./utils.js";
|
||||||
|
import { StorageError } from "../common.js";
|
||||||
|
|
||||||
|
class QueryTargetWrapper {
|
||||||
|
constructor(qt) {
|
||||||
|
this._qt = qt;
|
||||||
|
}
|
||||||
|
|
||||||
|
openKeyCursor(...params) {
|
||||||
|
try {
|
||||||
|
return this._qt.openKeyCursor(...params);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("openKeyCursor failed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openCursor(...params) {
|
||||||
|
try {
|
||||||
|
return this._qt.openCursor(...params);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("openCursor failed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
put(...params) {
|
||||||
|
try {
|
||||||
|
return this._qt.put(...params);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("put failed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add(...params) {
|
||||||
|
try {
|
||||||
|
return this._qt.add(...params);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("add failed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(...params) {
|
||||||
|
try {
|
||||||
|
return this._qt.get(...params);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("get failed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
index(...params) {
|
||||||
|
try {
|
||||||
|
return this._qt.index(...params);
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError("index failed", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export default class Store extends QueryTarget {
|
export default class Store extends QueryTarget {
|
||||||
constructor(idbStore) {
|
constructor(idbStore) {
|
||||||
super(idbStore);
|
super(new QueryTargetWrapper(idbStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
get _idbStore() {
|
get _idbStore() {
|
||||||
@ -11,7 +66,7 @@ export default class Store extends QueryTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
index(indexName) {
|
index(indexName) {
|
||||||
return new QueryTarget(this._idbStore.index(indexName));
|
return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
put(value) {
|
put(value) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import EventKey from "../../../room/timeline/EventKey.js";
|
import EventKey from "../../../room/timeline/EventKey.js";
|
||||||
|
import { StorageError } from "../../common.js";
|
||||||
import Platform from "../../../../Platform.js";
|
import Platform from "../../../../Platform.js";
|
||||||
|
|
||||||
// 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
|
||||||
@ -30,6 +31,7 @@ class Range {
|
|||||||
}
|
}
|
||||||
|
|
||||||
asIDBKeyRange(roomId) {
|
asIDBKeyRange(roomId) {
|
||||||
|
try {
|
||||||
// only
|
// only
|
||||||
if (this._only) {
|
if (this._only) {
|
||||||
return IDBKeyRange.only(encodeKey(roomId, this._only.fragmentId, this._only.eventIndex));
|
return IDBKeyRange.only(encodeKey(roomId, this._only.fragmentId, this._only.eventIndex));
|
||||||
@ -63,6 +65,9 @@ class Range {
|
|||||||
this._upperOpen
|
this._upperOpen
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} catch(err) {
|
||||||
|
throw new StorageError(`IDBKeyRange failed with data: ` + JSON.stringify(this), err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { StorageError } from "../../common.js";
|
||||||
import Platform from "../../../../Platform.js";
|
import Platform from "../../../../Platform.js";
|
||||||
|
|
||||||
function encodeKey(roomId, fragmentId) {
|
function encodeKey(roomId, fragmentId) {
|
||||||
@ -12,10 +13,14 @@ export default class RoomFragmentStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_allRange(roomId) {
|
_allRange(roomId) {
|
||||||
|
try {
|
||||||
return IDBKeyRange.bound(
|
return IDBKeyRange.bound(
|
||||||
encodeKey(roomId, Platform.minStorageKey),
|
encodeKey(roomId, Platform.minStorageKey),
|
||||||
encodeKey(roomId, Platform.maxStorageKey)
|
encodeKey(roomId, Platform.maxStorageKey)
|
||||||
);
|
);
|
||||||
|
} catch (err) {
|
||||||
|
throw new StorageError(`error from IDBKeyRange with roomId ${roomId}`, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
all(roomId) {
|
all(roomId) {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {txnAsPromise} from "./utils.js";
|
import {txnAsPromise} from "./utils.js";
|
||||||
|
import {StorageError} from "../common.js";
|
||||||
import Store from "./store.js";
|
import Store from "./store.js";
|
||||||
import SessionStore from "./stores/SessionStore.js";
|
import SessionStore from "./stores/SessionStore.js";
|
||||||
import RoomSummaryStore from "./stores/RoomSummaryStore.js";
|
import RoomSummaryStore from "./stores/RoomSummaryStore.js";
|
||||||
@ -21,7 +22,7 @@ export default class Transaction {
|
|||||||
_idbStore(name) {
|
_idbStore(name) {
|
||||||
if (!this._allowedStoreNames.includes(name)) {
|
if (!this._allowedStoreNames.includes(name)) {
|
||||||
// more specific error? this is a bug, so maybe not ...
|
// more specific error? this is a bug, so maybe not ...
|
||||||
throw new Error(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`);
|
throw new StorageError(`Invalid store for transaction: ${name}, only ${this._allowedStoreNames.join(", ")} are allowed.`);
|
||||||
}
|
}
|
||||||
return new Store(this._txn.objectStore(name));
|
return new Store(this._txn.objectStore(name));
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import { StorageError } from "../common.js";
|
||||||
|
|
||||||
export function openDatabase(name, createObjectStore, version) {
|
export function openDatabase(name, createObjectStore, version) {
|
||||||
const req = window.indexedDB.open(name, version);
|
const req = window.indexedDB.open(name, version);
|
||||||
req.onupgradeneeded = (ev) => {
|
req.onupgradeneeded = (ev) => {
|
||||||
@ -8,17 +10,21 @@ 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(event.target.error));
|
req.addEventListener("error", event => reject(wrapError(event.target.error)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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", reject);
|
txn.addEventListener("abort", e => reject(wrapError(e)));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +48,8 @@ export function iterateCursor(cursor, processValue) {
|
|||||||
cursor.continue(jumpTo);
|
cursor.continue(jumpTo);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}).catch(err => {
|
||||||
|
throw new StorageError("iterateCursor failed", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,7 +105,7 @@ export async function findStoreValue(db, storeName, toCursor, matchesValue) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (!matched) {
|
if (!matched) {
|
||||||
throw new Error("Value not found");
|
throw new StorageError("Value not found");
|
||||||
}
|
}
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,4 @@
|
|||||||
import {
|
import {RequestAbortError} from "./error.js";
|
||||||
RequestAbortError,
|
|
||||||
HomeServerError,
|
|
||||||
StorageError
|
|
||||||
} from "./error.js";
|
|
||||||
import EventEmitter from "../EventEmitter.js";
|
import EventEmitter from "../EventEmitter.js";
|
||||||
|
|
||||||
const INCREMENTAL_TIMEOUT = 30000;
|
const INCREMENTAL_TIMEOUT = 30000;
|
||||||
@ -111,7 +107,8 @@ export default class Sync extends EventEmitter {
|
|||||||
await syncTxn.complete();
|
await syncTxn.complete();
|
||||||
console.info("syncTxn committed!!");
|
console.info("syncTxn committed!!");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
throw new StorageError("unable to commit sync tranaction", err);
|
console.error("unable to commit sync tranaction", err.message);
|
||||||
|
throw err;
|
||||||
}
|
}
|
||||||
// emit room related events after txn has been closed
|
// emit room related events after txn has been closed
|
||||||
for(let {room, changes} of roomChanges) {
|
for(let {room, changes} of roomChanges) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user