forked from mystiq/hydrogen-web
track storage write requests internally, as we never await their promise
This commit is contained in:
parent
482b5f4d22
commit
37690cffe3
2 changed files with 40 additions and 28 deletions
|
@ -106,8 +106,9 @@ class QueryTargetWrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Store extends QueryTarget {
|
export class Store extends QueryTarget {
|
||||||
constructor(idbStore) {
|
constructor(idbStore, transaction) {
|
||||||
super(new QueryTargetWrapper(idbStore));
|
super(new QueryTargetWrapper(idbStore));
|
||||||
|
this._transaction = transaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
get _idbStore() {
|
get _idbStore() {
|
||||||
|
@ -118,33 +119,15 @@ export class Store extends QueryTarget {
|
||||||
return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName)));
|
return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName)));
|
||||||
}
|
}
|
||||||
|
|
||||||
async put(value) {
|
put(value) {
|
||||||
try {
|
this._transaction._addWriteRequest(this._idbStore.put(value));
|
||||||
return await reqAsPromise(this._idbStore.put(value));
|
|
||||||
} catch(err) {
|
|
||||||
const originalErr = err.cause;
|
|
||||||
throw new StorageError(`put on ${err.databaseName}.${err.storeName} failed`, originalErr, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async add(value) {
|
add(value) {
|
||||||
try {
|
this._transaction._addWriteRequest(this._idbStore.add(value));
|
||||||
// this will catch both the sync error already mapped
|
|
||||||
// in the QueryTargetWrapper above, and also the async request errors, which are still DOMException's
|
|
||||||
return await reqAsPromise(this._idbStore.add(value));
|
|
||||||
} catch(err) {
|
|
||||||
const originalErr = err.cause;
|
|
||||||
throw new StorageError(`add on ${err.databaseName}.${err.storeName} failed`, originalErr, value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async delete(keyOrKeyRange) {
|
delete(keyOrKeyRange) {
|
||||||
try {
|
this._transaction._addWriteRequest(this._idbStore.delete(keyOrKeyRange));
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {IDBRequestError} from "./error.js";
|
||||||
import {txnAsPromise} from "./utils.js";
|
import {txnAsPromise} from "./utils.js";
|
||||||
import {StorageError} from "../common.js";
|
import {StorageError} from "../common.js";
|
||||||
import {Store} from "./Store.js";
|
import {Store} from "./Store.js";
|
||||||
|
@ -38,6 +39,14 @@ export class Transaction {
|
||||||
this._txn = txn;
|
this._txn = txn;
|
||||||
this._allowedStoreNames = allowedStoreNames;
|
this._allowedStoreNames = allowedStoreNames;
|
||||||
this._stores = {};
|
this._stores = {};
|
||||||
|
this._writeRequests = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_addWriteRequest(request) {
|
||||||
|
if (!this._writeRequests) {
|
||||||
|
this._writeRequests = [];
|
||||||
|
}
|
||||||
|
this._writeRequests.push(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
_idbStore(name) {
|
_idbStore(name) {
|
||||||
|
@ -45,7 +54,7 @@ export class Transaction {
|
||||||
// more specific error? this is a bug, so maybe not ...
|
// more specific error? this is a bug, so maybe not ...
|
||||||
throw new StorageError(`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), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
_store(name, mapStore) {
|
_store(name, mapStore) {
|
||||||
|
@ -116,8 +125,28 @@ export class Transaction {
|
||||||
return this._store("accountData", idbStore => new AccountDataStore(idbStore));
|
return this._store("accountData", idbStore => new AccountDataStore(idbStore));
|
||||||
}
|
}
|
||||||
|
|
||||||
complete() {
|
async complete() {
|
||||||
return txnAsPromise(this._txn);
|
// check the write requests if we haven't failed yet
|
||||||
|
if (this._writeRequests) {
|
||||||
|
for (const request of this._writeRequests) {
|
||||||
|
if (request.error) {
|
||||||
|
try {
|
||||||
|
this.abort();
|
||||||
|
} catch (err) {/* ignore abort error, although it would be useful to know if the other stuff got committed or not... */}
|
||||||
|
return Promise.reject(new IDBRequestError(request, "Write request failed"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const result = await txnAsPromise(this._txn);
|
||||||
|
// check the write requests if we haven't failed yet
|
||||||
|
if (this._writeRequests) {
|
||||||
|
for (const request of this._writeRequests) {
|
||||||
|
if (request.readyState !== "done") {
|
||||||
|
return Promise.reject(new IDBRequestError(request, "Request is still pending after transaction finished"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
abort() {
|
abort() {
|
||||||
|
|
Loading…
Reference in a new issue