forked from mystiq/hydrogen-web
Migrate Store.js to TypeScript
This commit is contained in:
parent
c4e8ed8851
commit
db66570d7a
3 changed files with 176 additions and 165 deletions
|
@ -1,164 +0,0 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
|
||||
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 {QueryTarget} from "./QueryTarget";
|
||||
import {IDBRequestAttemptError} from "./error";
|
||||
|
||||
const LOG_REQUESTS = false;
|
||||
|
||||
function logRequest(method, params, source) {
|
||||
const storeName = source?.name;
|
||||
const databaseName = source?.transaction?.db?.name;
|
||||
console.info(`${databaseName}.${storeName}.${method}(${params.map(p => JSON.stringify(p)).join(", ")})`);
|
||||
}
|
||||
|
||||
class QueryTargetWrapper {
|
||||
constructor(qt) {
|
||||
this._qt = qt;
|
||||
}
|
||||
|
||||
get keyPath() {
|
||||
if (this._qt.objectStore) {
|
||||
return this._qt.objectStore.keyPath;
|
||||
} else {
|
||||
return this._qt.keyPath;
|
||||
}
|
||||
}
|
||||
|
||||
supports(methodName) {
|
||||
return !!this._qt[methodName];
|
||||
}
|
||||
|
||||
openKeyCursor(...params) {
|
||||
try {
|
||||
// not supported on Edge 15
|
||||
if (!this._qt.openKeyCursor) {
|
||||
LOG_REQUESTS && logRequest("openCursor", params, this._qt);
|
||||
return this.openCursor(...params);
|
||||
}
|
||||
LOG_REQUESTS && logRequest("openKeyCursor", params, this._qt);
|
||||
return this._qt.openKeyCursor(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("openKeyCursor", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
openCursor(...params) {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("openCursor", params, this._qt);
|
||||
return this._qt.openCursor(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("openCursor", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
put(...params) {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("put", params, this._qt);
|
||||
return this._qt.put(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("put", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
add(...params) {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("add", params, this._qt);
|
||||
return this._qt.add(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("add", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
get(...params) {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("get", params, this._qt);
|
||||
return this._qt.get(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("get", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
getKey(...params) {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("getKey", params, this._qt);
|
||||
return this._qt.getKey(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("getKey", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
delete(...params) {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("delete", params, this._qt);
|
||||
return this._qt.delete(...params);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("delete", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
|
||||
index(...params) {
|
||||
try {
|
||||
return this._qt.index(...params);
|
||||
} catch(err) {
|
||||
// TODO: map to different error? this is not a request
|
||||
throw new IDBRequestAttemptError("index", this._qt, err, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Store extends QueryTarget {
|
||||
constructor(idbStore, transaction) {
|
||||
super(new QueryTargetWrapper(idbStore));
|
||||
this._transaction = transaction;
|
||||
}
|
||||
|
||||
get IDBKeyRange() {
|
||||
return this._transaction.IDBKeyRange;
|
||||
}
|
||||
|
||||
get _idbStore() {
|
||||
return this._target;
|
||||
}
|
||||
|
||||
index(indexName) {
|
||||
return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName)));
|
||||
}
|
||||
|
||||
put(value) {
|
||||
// If this request fails, the error will bubble up to the transaction and abort it,
|
||||
// which is the behaviour we want. Therefore, it is ok to not create a promise for this
|
||||
// request and await it.
|
||||
//
|
||||
// Perhaps at some later point, we will want to handle an error (like ConstraintError) for
|
||||
// individual write requests. In that case, we should add a method that returns a promise (e.g. putAndObserve)
|
||||
// and call preventDefault on the event to prevent it from aborting the transaction
|
||||
//
|
||||
// Note that this can still throw synchronously, like it does for TransactionInactiveError,
|
||||
// see https://www.w3.org/TR/IndexedDB-2/#transaction-lifetime-concept
|
||||
this._idbStore.put(value);
|
||||
}
|
||||
|
||||
add(value) {
|
||||
// ok to not monitor result of request, see comment in `put`.
|
||||
this._idbStore.add(value);
|
||||
}
|
||||
|
||||
delete(keyOrKeyRange) {
|
||||
// ok to not monitor result of request, see comment in `put`.
|
||||
this._idbStore.delete(keyOrKeyRange);
|
||||
}
|
||||
}
|
175
src/matrix/storage/idb/Store.ts
Normal file
175
src/matrix/storage/idb/Store.ts
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||
|
||||
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 {QueryTarget, IDBQuery} from "./QueryTarget";
|
||||
import {IDBRequestAttemptError} from "./error";
|
||||
import {reqAsPromise} from "./utils";
|
||||
import {Transaction} from "./Transaction";
|
||||
|
||||
const LOG_REQUESTS = false;
|
||||
|
||||
function logRequest(method: string, params: any[], source: any): void {
|
||||
const storeName = source?.name;
|
||||
const databaseName = source?.transaction?.db?.name;
|
||||
console.info(`${databaseName}.${storeName}.${method}(${params.map(p => JSON.stringify(p)).join(", ")})`);
|
||||
}
|
||||
|
||||
class QueryTargetWrapper<T> {
|
||||
private _qt: IDBIndex | IDBObjectStore;
|
||||
|
||||
constructor(qt: IDBIndex | IDBObjectStore) {
|
||||
this._qt = qt;
|
||||
}
|
||||
|
||||
get keyPath(): string | string[] {
|
||||
if (this._qt["objectStore"]) {
|
||||
return (this._qt as IDBIndex).objectStore.keyPath;
|
||||
} else {
|
||||
return this._qt.keyPath;
|
||||
}
|
||||
}
|
||||
|
||||
get _qtStore(): IDBObjectStore {
|
||||
return this._qt as IDBObjectStore;
|
||||
}
|
||||
|
||||
supports(methodName: string): boolean {
|
||||
return !!this._qt[methodName];
|
||||
}
|
||||
|
||||
openKeyCursor(range?: IDBQuery, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursor | null> {
|
||||
try {
|
||||
// not supported on Edge 15
|
||||
if (!this._qt.openKeyCursor) {
|
||||
LOG_REQUESTS && logRequest("openCursor", [range, direction], this._qt);
|
||||
return this.openCursor(range, direction);
|
||||
}
|
||||
LOG_REQUESTS && logRequest("openKeyCursor", [range, direction], this._qt);
|
||||
return this._qt.openKeyCursor(range, direction)
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("openKeyCursor", this._qt, err, [range, direction]);
|
||||
}
|
||||
}
|
||||
|
||||
openCursor(range?: IDBQuery, direction?: IDBCursorDirection | undefined): IDBRequest<IDBCursorWithValue | null> {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("openCursor", [], this._qt);
|
||||
return this._qt.openCursor(range, direction)
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("openCursor", this._qt, err, [range, direction]);
|
||||
}
|
||||
}
|
||||
|
||||
put(item: T, key?: IDBValidKey | undefined): IDBRequest<IDBValidKey> {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("put", [item, key], this._qt);
|
||||
return this._qtStore.put(item, key);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("put", this._qt, err, [item, key]);
|
||||
}
|
||||
}
|
||||
|
||||
add(item: T, key?: IDBValidKey | undefined): IDBRequest<IDBValidKey> {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("add", [item, key], this._qt);
|
||||
return this._qtStore.add(item, key);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("add", this._qt, err, [item, key]);
|
||||
}
|
||||
}
|
||||
|
||||
get(key: IDBValidKey | IDBKeyRange): IDBRequest<T | null> {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("get", [key], this._qt);
|
||||
return this._qt.get(key);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("get", this._qt, err, [key]);
|
||||
}
|
||||
}
|
||||
|
||||
getKey(key: IDBValidKey | IDBKeyRange): IDBRequest<IDBValidKey | undefined> {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("getKey", [key], this._qt);
|
||||
return this._qt.getKey(key)
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("getKey", this._qt, err, [key]);
|
||||
}
|
||||
}
|
||||
|
||||
delete(key: IDBValidKey | IDBKeyRange): IDBRequest<undefined> {
|
||||
try {
|
||||
LOG_REQUESTS && logRequest("delete", [key], this._qt);
|
||||
return this._qtStore.delete(key);
|
||||
} catch(err) {
|
||||
throw new IDBRequestAttemptError("delete", this._qt, err, [key]);
|
||||
}
|
||||
}
|
||||
|
||||
index(name: string): IDBIndex {
|
||||
try {
|
||||
return this._qtStore.index(name);
|
||||
} catch(err) {
|
||||
// TODO: map to different error? this is not a request
|
||||
throw new IDBRequestAttemptError("index", this._qt, err, [name]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class Store<T> extends QueryTarget<T> {
|
||||
private _transaction: Transaction;
|
||||
|
||||
constructor(idbStore: IDBObjectStore, transaction: Transaction) {
|
||||
super(new QueryTargetWrapper<T>(idbStore));
|
||||
this._transaction = transaction;
|
||||
}
|
||||
|
||||
get IDBKeyRange() {
|
||||
// @ts-ignore
|
||||
return this._transaction.IDBKeyRange;
|
||||
}
|
||||
|
||||
get _idbStore(): QueryTargetWrapper<T> {
|
||||
return (this._target as QueryTargetWrapper<T>);
|
||||
}
|
||||
|
||||
index(indexName: string): QueryTarget<T> {
|
||||
return new QueryTarget<T>(new QueryTargetWrapper<T>(this._idbStore.index(indexName)));
|
||||
}
|
||||
|
||||
put(value: T): Promise<IDBValidKey> {
|
||||
// If this request fails, the error will bubble up to the transaction and abort it,
|
||||
// which is the behaviour we want. Therefore, it is ok to not create a promise for this
|
||||
// request and await it.
|
||||
//
|
||||
// Perhaps at some later point, we will want to handle an error (like ConstraintError) for
|
||||
// individual write requests. In that case, we should add a method that returns a promise (e.g. putAndObserve)
|
||||
// and call preventDefault on the event to prevent it from aborting the transaction
|
||||
//
|
||||
// Note that this can still throw synchronously, like it does for TransactionInactiveError,
|
||||
// see https://www.w3.org/TR/IndexedDB-2/#transaction-lifetime-concept
|
||||
return reqAsPromise(this._idbStore.put(value));
|
||||
}
|
||||
|
||||
add(value: T): Promise<IDBValidKey> {
|
||||
// ok to not monitor result of request, see comment in `put`.
|
||||
return reqAsPromise(this._idbStore.add(value));
|
||||
}
|
||||
|
||||
delete(keyOrKeyRange: IDBValidKey | IDBKeyRange): Promise<undefined> {
|
||||
// ok to not monitor result of request, see comment in `put`.
|
||||
return reqAsPromise(this._idbStore.delete(keyOrKeyRange));
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
|||
|
||||
import {txnAsPromise} from "./utils";
|
||||
import {StorageError} from "../common";
|
||||
import {Store} from "./Store.js";
|
||||
import {Store} from "./Store";
|
||||
import {SessionStore} from "./stores/SessionStore.js";
|
||||
import {RoomSummaryStore} from "./stores/RoomSummaryStore.js";
|
||||
import {InviteStore} from "./stores/InviteStore.js";
|
||||
|
|
Loading…
Reference in a new issue