Add initial stab at annotating utils

This commit is contained in:
Danila Fedorin 2021-08-09 19:11:49 -07:00
parent 4fb93ad104
commit 0c80f78e9b

View file

@ -25,7 +25,7 @@ let needsSyncPromise = false;
If this is the case, promises need to be resolved If this is the case, promises need to be resolved
synchronously from the idb request handler to prevent the transaction from closing prematurely. synchronously from the idb request handler to prevent the transaction from closing prematurely.
*/ */
export async function checkNeedsSyncPromise() { export async function checkNeedsSyncPromise(): Promise<boolean> {
// important to have it turned off while doing the test, // important to have it turned off while doing the test,
// otherwise reqAsPromise would not fail // otherwise reqAsPromise would not fail
needsSyncPromise = false; needsSyncPromise = false;
@ -49,51 +49,57 @@ export async function checkNeedsSyncPromise() {
} }
// storage keys are defined to be unsigned 32bit numbers in KeyLimits, which is assumed by idb // storage keys are defined to be unsigned 32bit numbers in KeyLimits, which is assumed by idb
export function encodeUint32(n) { export function encodeUint32(n: number): string {
const hex = n.toString(16); const hex = n.toString(16);
return "0".repeat(8 - hex.length) + hex; return "0".repeat(8 - hex.length) + hex;
} }
// used for logs where timestamp is part of key, which is larger than 32 bit // used for logs where timestamp is part of key, which is larger than 32 bit
export function encodeUint64(n) { export function encodeUint64(n: number): string {
const hex = n.toString(16); const hex = n.toString(16);
return "0".repeat(16 - hex.length) + hex; return "0".repeat(16 - hex.length) + hex;
} }
export function decodeUint32(str) { export function decodeUint32(str: string): number {
return parseInt(str, 16); return parseInt(str, 16);
} }
export function openDatabase(name, createObjectStore, version, idbFactory = window.indexedDB) { type CreateObjectStore = (db : IDBDatabase, txn: IDBTransaction | null, oldVersion: number, version: number) => any
export function openDatabase(name: string, createObjectStore: CreateObjectStore, version: number, idbFactory: IDBFactory = window.indexedDB): Promise<IDBDatabase> {
const req = idbFactory.open(name, version); const req = idbFactory.open(name, version);
req.onupgradeneeded = (ev) => { req.onupgradeneeded = (ev : IDBVersionChangeEvent) => {
const db = ev.target.result; const req = ev.target as IDBRequest<IDBDatabase>;
const txn = ev.target.transaction; const db = req.result;
const txn = req.transaction;
const oldVersion = ev.oldVersion; const oldVersion = ev.oldVersion;
createObjectStore(db, txn, oldVersion, version); createObjectStore(db, txn, oldVersion, version);
}; };
return reqAsPromise(req); return reqAsPromise(req);
} }
export function reqAsPromise(req) { export function reqAsPromise<T>(req: IDBRequest<T>): Promise<T> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
req.addEventListener("success", event => { req.addEventListener("success", event => {
resolve(event.target.result); resolve((event.target as IDBRequest<T>).result);
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}); });
req.addEventListener("error", event => { req.addEventListener("error", event => {
const error = new IDBRequestError(event.target); const error = new IDBRequestError(event.target as IDBRequest<T>);
reject(error); reject(error);
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}); });
}); });
} }
export function txnAsPromise(txn) { export function txnAsPromise(txn): Promise<void> {
let error; let error;
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
txn.addEventListener("complete", () => { txn.addEventListener("complete", () => {
resolve(); resolve();
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}); });
txn.addEventListener("error", event => { txn.addEventListener("error", event => {
@ -112,23 +118,28 @@ export function txnAsPromise(txn) {
error = new StorageError(`Transaction on ${dbName} with stores ${storeNames} was aborted.`); error = new StorageError(`Transaction on ${dbName} with stores ${storeNames} was aborted.`);
} }
reject(error); reject(error);
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}); });
}); });
} }
export function iterateCursor(cursorRequest, processValue) { type CursorIterator<T> = (value: T, key?: IDBValidKey, cursor?: IDBCursorWithValue) => { done: boolean, jumpTo?: IDBValidKey }
export function iterateCursor<T>(cursorRequest: IDBRequest<IDBCursorWithValue>, processValue: CursorIterator<T>): Promise<boolean> {
// TODO: does cursor already have a value here?? // TODO: does cursor already have a value here??
return new Promise((resolve, reject) => { return new Promise<boolean>((resolve, reject) => {
cursorRequest.onerror = () => { cursorRequest.onerror = () => {
reject(new IDBRequestError(cursorRequest)); reject(new IDBRequestError(cursorRequest));
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
}; };
// collect results // collect results
cursorRequest.onsuccess = (event) => { cursorRequest.onsuccess = (event) => {
const cursor = event.target.result; const cursor = (event.target as IDBRequest<IDBCursorWithValue>).result;
if (!cursor) { if (!cursor) {
resolve(false); resolve(false);
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
return; // end of results return; // end of results
} }
@ -139,6 +150,7 @@ export function iterateCursor(cursorRequest, processValue) {
if (done) { if (done) {
resolve(true); resolve(true);
// @ts-ignore
needsSyncPromise && Promise._flush && Promise._flush(); needsSyncPromise && Promise._flush && Promise._flush();
} else if(jumpTo) { } else if(jumpTo) {
cursor.continue(jumpTo); cursor.continue(jumpTo);
@ -151,16 +163,20 @@ export function iterateCursor(cursorRequest, processValue) {
}); });
} }
export async function fetchResults(cursor, isDone) { type Pred<T> = (value: T) => boolean
const results = [];
await iterateCursor(cursor, (value) => { export async function fetchResults<T>(cursor: IDBRequest, isDone: Pred<T[]>): Promise<T[]> {
const results: T[] = [];
await iterateCursor<T>(cursor, (value) => {
results.push(value); results.push(value);
return {done: isDone(results)}; return {done: isDone(results)};
}); });
return results; return results;
} }
export async function select(db, storeName, toCursor, isDone) { type ToCursor = (store: IDBObjectStore) => IDBRequest
export async function select<T>(db: IDBDatabase, storeName: string, toCursor: ToCursor, isDone: Pred<T[]>): Promise<T[]> {
if (!isDone) { if (!isDone) {
isDone = () => false; isDone = () => false;
} }
@ -173,7 +189,7 @@ export async function select(db, storeName, toCursor, isDone) {
return await fetchResults(cursor, isDone); return await fetchResults(cursor, isDone);
} }
export async function findStoreValue(db, storeName, toCursor, matchesValue) { export async function findStoreValue<T>(db: IDBDatabase, storeName: string, toCursor: ToCursor, matchesValue: Pred<T>): Promise<T> {
if (!matchesValue) { if (!matchesValue) {
matchesValue = () => true; matchesValue = () => true;
} }
@ -185,7 +201,8 @@ export async function findStoreValue(db, storeName, toCursor, matchesValue) {
const store = tx.objectStore(storeName); const store = tx.objectStore(storeName);
const cursor = await reqAsPromise(toCursor(store)); const cursor = await reqAsPromise(toCursor(store));
let match; let match;
const matched = await iterateCursor(cursor, (value) => { // @ts-ignore
const matched = await iterateCursor<T>(cursor, (value) => {
if (matchesValue(value)) { if (matchesValue(value)) {
match = value; match = value;
return true; return true;