Add initial stab at annotating utils
This commit is contained in:
parent
4fb93ad104
commit
0c80f78e9b
1 changed files with 38 additions and 21 deletions
|
@ -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;
|
||||||
|
|
Reference in a new issue