Add initial stab at annotating Store

This commit is contained in:
Danila Fedorin 2021-08-10 15:31:54 -07:00
parent 2b44878332
commit 97a50c835d
2 changed files with 67 additions and 54 deletions

View file

@ -20,15 +20,15 @@ type Reducer<A,B> = (acc: B, val: A) => B
interface QueryTargetInterface<T> { interface QueryTargetInterface<T> {
openCursor: (range?: IDBKeyRange | null , direction?: IDBCursorDirection) => IDBRequest<IDBCursorWithValue | null> openCursor: (range?: IDBKeyRange | null , direction?: IDBCursorDirection) => IDBRequest<IDBCursorWithValue | null>
openKeyCursor: (range: IDBKeyRange, direction: IDBCursorDirection) => IDBRequest<IDBCursor | null> openKeyCursor: (range?: IDBKeyRange, direction?: IDBCursorDirection) => IDBRequest<IDBCursor | null>
supports: (method: string) => boolean supports: (method: string) => boolean
keyPath: string keyPath: string | string[]
get: (key: IDBValidKey) => IDBRequest<T | null> get: (key: IDBValidKey) => IDBRequest<T | null>
getKey: (key: IDBValidKey) => IDBRequest<IDBValidKey | undefined> getKey: (key: IDBValidKey) => IDBRequest<IDBValidKey | undefined>
} }
export class QueryTarget<T> { export class QueryTarget<T> {
private _target: QueryTargetInterface<T> protected _target: QueryTargetInterface<T>
constructor(target: QueryTargetInterface<T>) { constructor(target: QueryTargetInterface<T>) {
this._target = target; this._target = target;
@ -60,7 +60,11 @@ export class QueryTarget<T> {
} else { } else {
return reqAsPromise(this._target.get(key)).then(value => { return reqAsPromise(this._target.get(key)).then(value => {
if (value) { if (value) {
return value[this._target.keyPath]; let keyPath = this._target.keyPath;
if (typeof keyPath === "string") {
keyPath = [keyPath];
}
return keyPath.reduce((obj, key) => obj[key], value);
} }
}); });
} }

View file

@ -19,126 +19,135 @@ import {IDBRequestAttemptError} from "./error";
const LOG_REQUESTS = false; const LOG_REQUESTS = false;
function logRequest(method, params, source) { function logRequest(method: string, params: any[], source: any) {
const storeName = source?.name; const storeName = source?.name;
const databaseName = source?.transaction?.db?.name; const databaseName = source?.transaction?.db?.name;
console.info(`${databaseName}.${storeName}.${method}(${params.map(p => JSON.stringify(p)).join(", ")})`); console.info(`${databaseName}.${storeName}.${method}(${params.map(p => JSON.stringify(p)).join(", ")})`);
} }
class QueryTargetWrapper { class QueryTargetWrapper<T> {
constructor(qt) { private _qt: IDBIndex | IDBObjectStore
constructor(qt: IDBIndex | IDBObjectStore) {
this._qt = qt; this._qt = qt;
} }
get keyPath() { get keyPath(): string | string[] {
if (this._qt.objectStore) { if (this._qt["objectStore"]) {
return this._qt.objectStore.keyPath; return (this._qt as IDBIndex).objectStore.keyPath;
} else { } else {
return this._qt.keyPath; return this._qt.keyPath;
} }
} }
supports(methodName) { get _qtStore(): IDBObjectStore {
return this._qt as IDBObjectStore;
}
supports(methodName: string): boolean {
return !!this._qt[methodName]; return !!this._qt[methodName];
} }
openKeyCursor(...params) { openKeyCursor(range?: IDBKeyRange, direction?: IDBCursorDirection): IDBRequest<IDBCursor | null> {
try { try {
// not supported on Edge 15 // not supported on Edge 15
if (!this._qt.openKeyCursor) { if (!this._qt.openKeyCursor) {
LOG_REQUESTS && logRequest("openCursor", params, this._qt); LOG_REQUESTS && logRequest("openCursor", [range, direction], this._qt);
return this.openCursor(...params); return this.openCursor(range, direction);
} }
LOG_REQUESTS && logRequest("openKeyCursor", params, this._qt); LOG_REQUESTS && logRequest("openKeyCursor", [range, direction], this._qt);
return this._qt.openKeyCursor(...params); return this._qt.openKeyCursor(range, direction)
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("openKeyCursor", this._qt, err, params); throw new IDBRequestAttemptError("openKeyCursor", this._qt, err, [range, direction]);
} }
} }
openCursor(...params) { openCursor(range?: IDBKeyRange, direction?: IDBCursorDirection): IDBRequest<IDBCursorWithValue | null> {
try { try {
LOG_REQUESTS && logRequest("openCursor", params, this._qt); LOG_REQUESTS && logRequest("openCursor", [], this._qt);
return this._qt.openCursor(...params); return this._qt.openCursor(range, direction)
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("openCursor", this._qt, err, params); throw new IDBRequestAttemptError("openCursor", this._qt, err, [range, direction]);
} }
} }
put(...params) { put(item: T, key?: IDBValidKey): IDBRequest<IDBValidKey> {
try { try {
LOG_REQUESTS && logRequest("put", params, this._qt); LOG_REQUESTS && logRequest("put", [item, key], this._qt);
return this._qt.put(...params); return this._qtStore.put(item, key);
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("put", this._qt, err, params); throw new IDBRequestAttemptError("put", this._qt, err, [item, key]);
} }
} }
add(...params) { add(item: T, key?: IDBValidKey): IDBRequest<IDBValidKey> {
try { try {
LOG_REQUESTS && logRequest("add", params, this._qt); LOG_REQUESTS && logRequest("add", [item, key], this._qt);
return this._qt.add(...params); return this._qtStore.add(item, key);
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("add", this._qt, err, params); throw new IDBRequestAttemptError("add", this._qt, err, [item, key]);
} }
} }
get(...params) { get(key: IDBValidKey): IDBRequest<T> {
try { try {
LOG_REQUESTS && logRequest("get", params, this._qt); LOG_REQUESTS && logRequest("get", [key], this._qt);
return this._qt.get(...params); return this._qt.get(key);
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("get", this._qt, err, params); throw new IDBRequestAttemptError("get", this._qt, err, [key]);
} }
} }
getKey(...params) { getKey(key: IDBValidKey): IDBRequest<IDBValidKey | undefined> {
try { try {
LOG_REQUESTS && logRequest("getKey", params, this._qt); LOG_REQUESTS && logRequest("getKey", [key], this._qt);
return this._qt.getKey(...params); return this._qt.getKey(key)
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("getKey", this._qt, err, params); throw new IDBRequestAttemptError("getKey", this._qt, err, [key]);
} }
} }
delete(...params) { delete(key: IDBValidKey | IDBKeyRange): IDBRequest<undefined> {
try { try {
LOG_REQUESTS && logRequest("delete", params, this._qt); LOG_REQUESTS && logRequest("delete", [key], this._qt);
return this._qt.delete(...params); return this._qtStore.delete(key);
} catch(err) { } catch(err) {
throw new IDBRequestAttemptError("delete", this._qt, err, params); throw new IDBRequestAttemptError("delete", this._qt, err, [key]);
} }
} }
index(...params) { index(name: string): IDBIndex {
try { try {
return this._qt.index(...params); return this._qtStore.index(name);
} catch(err) { } catch(err) {
// TODO: map to different error? this is not a request // TODO: map to different error? this is not a request
throw new IDBRequestAttemptError("index", this._qt, err, params); throw new IDBRequestAttemptError("index", this._qt, err, [name]);
} }
} }
} }
export class Store extends QueryTarget { export class Store<T> extends QueryTarget<T> {
constructor(idbStore, transaction) { private _transaction: IDBTransaction
super(new QueryTargetWrapper(idbStore));
constructor(idbStore: IDBObjectStore, transaction: IDBTransaction) {
super(new QueryTargetWrapper<T>(idbStore));
this._transaction = transaction; this._transaction = transaction;
} }
get IDBKeyRange() { get IDBKeyRange() {
// @ts-ignore
return this._transaction.IDBKeyRange; return this._transaction.IDBKeyRange;
} }
get _idbStore() { get _idbStore(): QueryTargetWrapper<T> {
return this._target; return (this._target as QueryTargetWrapper<T>);
} }
index(indexName) { index(indexName: string): QueryTarget<T> {
return new QueryTarget(new QueryTargetWrapper(this._idbStore.index(indexName))); return new QueryTarget<T>(new QueryTargetWrapper<T>(this._idbStore.index(indexName)));
} }
put(value) { put(value: T) {
// If this request fails, the error will bubble up to the transaction and abort it, // 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 // which is the behaviour we want. Therefore, it is ok to not create a promise for this
// request and await it. // request and await it.
@ -152,12 +161,12 @@ export class Store extends QueryTarget {
this._idbStore.put(value); this._idbStore.put(value);
} }
add(value) { add(value: T) {
// ok to not monitor result of request, see comment in `put`. // ok to not monitor result of request, see comment in `put`.
this._idbStore.add(value); this._idbStore.add(value);
} }
delete(keyOrKeyRange) { delete(keyOrKeyRange: IDBValidKey | IDBKeyRange) {
// ok to not monitor result of request, see comment in `put`. // ok to not monitor result of request, see comment in `put`.
this._idbStore.delete(keyOrKeyRange); this._idbStore.delete(keyOrKeyRange);
} }