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 {txnAsPromise} from "./utils";
|
||||||
import {StorageError} from "../common";
|
import {StorageError} from "../common";
|
||||||
import {Store} from "./Store.js";
|
import {Store} from "./Store";
|
||||||
import {SessionStore} from "./stores/SessionStore.js";
|
import {SessionStore} from "./stores/SessionStore.js";
|
||||||
import {RoomSummaryStore} from "./stores/RoomSummaryStore.js";
|
import {RoomSummaryStore} from "./stores/RoomSummaryStore.js";
|
||||||
import {InviteStore} from "./stores/InviteStore.js";
|
import {InviteStore} from "./stores/InviteStore.js";
|
||||||
|
|
Loading…
Reference in a new issue