From cd9fe360a45bb475c1dc933bdbed36a0a20e63c8 Mon Sep 17 00:00:00 2001 From: Danila Fedorin Date: Mon, 9 Aug 2021 13:56:20 -0700 Subject: [PATCH] Start migrating utils.js to TypeScript --- src/matrix/storage/idb/QueryTarget.js | 2 +- src/matrix/storage/idb/Storage.js | 2 +- src/matrix/storage/idb/StorageFactory.js | 2 +- src/matrix/storage/idb/Transaction.js | 2 +- src/matrix/storage/idb/export.js | 2 +- src/matrix/storage/idb/quirks.js | 2 +- src/matrix/storage/idb/schema.js | 2 +- .../storage/idb/stores/PendingEventStore.js | 2 +- .../storage/idb/stores/TimelineEventStore.js | 2 +- .../idb/stores/TimelineFragmentStore.js | 2 +- src/matrix/storage/idb/{utils.js => utils.ts} | 66 ++++++++++++------- src/platform/web/legacy-polyfill.js | 2 +- 12 files changed, 55 insertions(+), 33 deletions(-) rename src/matrix/storage/idb/{utils.js => utils.ts} (70%) diff --git a/src/matrix/storage/idb/QueryTarget.js b/src/matrix/storage/idb/QueryTarget.js index 348e67f9..c2657c52 100644 --- a/src/matrix/storage/idb/QueryTarget.js +++ b/src/matrix/storage/idb/QueryTarget.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {iterateCursor, reqAsPromise} from "./utils.js"; +import {iterateCursor, reqAsPromise} from "./utils"; export class QueryTarget { constructor(target) { diff --git a/src/matrix/storage/idb/Storage.js b/src/matrix/storage/idb/Storage.js index 0a357dda..cef636fb 100644 --- a/src/matrix/storage/idb/Storage.js +++ b/src/matrix/storage/idb/Storage.js @@ -16,7 +16,7 @@ limitations under the License. import {Transaction} from "./Transaction.js"; import { STORE_NAMES, StorageError } from "../common"; -import { reqAsPromise } from "./utils.js"; +import { reqAsPromise } from "./utils"; const WEBKITEARLYCLOSETXNBUG_BOGUS_KEY = "782rh281re38-boguskey"; diff --git a/src/matrix/storage/idb/StorageFactory.js b/src/matrix/storage/idb/StorageFactory.js index 719d2672..a9dc8eed 100644 --- a/src/matrix/storage/idb/StorageFactory.js +++ b/src/matrix/storage/idb/StorageFactory.js @@ -15,7 +15,7 @@ limitations under the License. */ import {Storage} from "./Storage.js"; -import { openDatabase, reqAsPromise } from "./utils.js"; +import { openDatabase, reqAsPromise } from "./utils"; import { exportSession, importSession } from "./export.js"; import { schema } from "./schema.js"; import { detectWebkitEarlyCloseTxnBug } from "./quirks.js"; diff --git a/src/matrix/storage/idb/Transaction.js b/src/matrix/storage/idb/Transaction.js index ae0f75be..d1c91d69 100644 --- a/src/matrix/storage/idb/Transaction.js +++ b/src/matrix/storage/idb/Transaction.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {txnAsPromise} from "./utils.js"; +import {txnAsPromise} from "./utils"; import {StorageError} from "../common"; import {Store} from "./Store.js"; import {SessionStore} from "./stores/SessionStore.js"; diff --git a/src/matrix/storage/idb/export.js b/src/matrix/storage/idb/export.js index 34dd8706..27979ce0 100644 --- a/src/matrix/storage/idb/export.js +++ b/src/matrix/storage/idb/export.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { iterateCursor, txnAsPromise } from "./utils.js"; +import { iterateCursor, txnAsPromise } from "./utils"; import { STORE_NAMES } from "../common"; export async function exportSession(db) { diff --git a/src/matrix/storage/idb/quirks.js b/src/matrix/storage/idb/quirks.js index 9739b7da..5eaa6836 100644 --- a/src/matrix/storage/idb/quirks.js +++ b/src/matrix/storage/idb/quirks.js @@ -15,7 +15,7 @@ limitations under the License. */ -import {openDatabase, txnAsPromise, reqAsPromise} from "./utils.js"; +import {openDatabase, txnAsPromise, reqAsPromise} from "./utils"; // filed as https://bugs.webkit.org/show_bug.cgi?id=222746 export async function detectWebkitEarlyCloseTxnBug(idbFactory) { diff --git a/src/matrix/storage/idb/schema.js b/src/matrix/storage/idb/schema.js index 352c810c..d593f6b9 100644 --- a/src/matrix/storage/idb/schema.js +++ b/src/matrix/storage/idb/schema.js @@ -1,4 +1,4 @@ -import {iterateCursor, reqAsPromise} from "./utils.js"; +import {iterateCursor, reqAsPromise} from "./utils"; import {RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../room/members/RoomMember.js"; import {RoomMemberStore} from "./stores/RoomMemberStore.js"; import {SessionStore} from "./stores/SessionStore.js"; diff --git a/src/matrix/storage/idb/stores/PendingEventStore.js b/src/matrix/storage/idb/stores/PendingEventStore.js index 385f3c9a..61071f11 100644 --- a/src/matrix/storage/idb/stores/PendingEventStore.js +++ b/src/matrix/storage/idb/stores/PendingEventStore.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { encodeUint32, decodeUint32 } from "../utils.js"; +import { encodeUint32, decodeUint32 } from "../utils"; import {KeyLimits} from "../../common"; function encodeKey(roomId, queueIndex) { diff --git a/src/matrix/storage/idb/stores/TimelineEventStore.js b/src/matrix/storage/idb/stores/TimelineEventStore.js index 664958e6..8ff445f0 100644 --- a/src/matrix/storage/idb/stores/TimelineEventStore.js +++ b/src/matrix/storage/idb/stores/TimelineEventStore.js @@ -16,7 +16,7 @@ limitations under the License. import {EventKey} from "../../../room/timeline/EventKey.js"; import { StorageError } from "../../common"; -import { encodeUint32 } from "../utils.js"; +import { encodeUint32 } from "../utils"; import {KeyLimits} from "../../common"; function encodeKey(roomId, fragmentId, eventIndex) { diff --git a/src/matrix/storage/idb/stores/TimelineFragmentStore.js b/src/matrix/storage/idb/stores/TimelineFragmentStore.js index 208b7300..07a8ff42 100644 --- a/src/matrix/storage/idb/stores/TimelineFragmentStore.js +++ b/src/matrix/storage/idb/stores/TimelineFragmentStore.js @@ -16,7 +16,7 @@ limitations under the License. import { StorageError } from "../../common"; import {KeyLimits} from "../../common"; -import { encodeUint32 } from "../utils.js"; +import { encodeUint32 } from "../utils"; function encodeKey(roomId, fragmentId) { return `${roomId}|${encodeUint32(fragmentId)}`; diff --git a/src/matrix/storage/idb/utils.js b/src/matrix/storage/idb/utils.ts similarity index 70% rename from src/matrix/storage/idb/utils.js rename to src/matrix/storage/idb/utils.ts index f90324cd..9954e747 100644 --- a/src/matrix/storage/idb/utils.js +++ b/src/matrix/storage/idb/utils.ts @@ -20,12 +20,15 @@ import { StorageError } from "../common"; let needsSyncPromise = false; +export const DONE = { done: true } +export const NOT_DONE = { done: false } + /* should be called on legacy platforms to see if transactions close before draining the microtask queue (IE11 on Windows 7). If this is the case, promises need to be resolved synchronously from the idb request handler to prevent the transaction from closing prematurely. */ -export async function checkNeedsSyncPromise() { +export async function checkNeedsSyncPromise(): Promise { // important to have it turned off while doing the test, // otherwise reqAsPromise would not fail needsSyncPromise = false; @@ -49,51 +52,57 @@ export async function checkNeedsSyncPromise() { } // 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); return "0".repeat(8 - hex.length) + hex; } // 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); return "0".repeat(16 - hex.length) + hex; } -export function decodeUint32(str) { +export function decodeUint32(str: string): number { 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 { const req = idbFactory.open(name, version); - req.onupgradeneeded = (ev) => { - const db = ev.target.result; - const txn = ev.target.transaction; + req.onupgradeneeded = (ev : IDBVersionChangeEvent) => { + const req = ev.target as IDBRequest; + const db = req.result; + const txn = req.transaction; const oldVersion = ev.oldVersion; createObjectStore(db, txn, oldVersion, version); }; return reqAsPromise(req); } -export function reqAsPromise(req) { +export function reqAsPromise(req: IDBRequest): Promise { return new Promise((resolve, reject) => { req.addEventListener("success", event => { - resolve(event.target.result); + resolve((event.target as IDBRequest).result); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); }); req.addEventListener("error", event => { - const error = new IDBRequestError(event.target); + const error = new IDBRequestError(event.target as IDBRequest); reject(error); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); }); }); } -export function txnAsPromise(txn) { +export function txnAsPromise(txn): Promise { let error; return new Promise((resolve, reject) => { txn.addEventListener("complete", () => { resolve(); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); }); txn.addEventListener("error", event => { @@ -112,33 +121,41 @@ export function txnAsPromise(txn) { error = new StorageError(`Transaction on ${dbName} with stores ${storeNames} was aborted.`); } reject(error); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); }); }); } -export function iterateCursor(cursorRequest, processValue) { +type CursorIterator = I extends IDBCursorWithValue ? + (value: T, key: IDBValidKey, cursor: IDBCursorWithValue) => { done: boolean, jumpTo?: IDBValidKey } : + (value: undefined, key: IDBValidKey, cursor: IDBCursor) => { done: boolean, jumpTo?: IDBValidKey } + +export function iterateCursor(cursorRequest: IDBRequest, processValue: CursorIterator): Promise { // TODO: does cursor already have a value here?? - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { cursorRequest.onerror = () => { reject(new IDBRequestError(cursorRequest)); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); }; // collect results cursorRequest.onsuccess = (event) => { - const cursor = event.target.result; + const cursor = (event.target as IDBRequest).result; if (!cursor) { resolve(false); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); return; // end of results } - const result = processValue(cursor.value, cursor.key, cursor); + const result = processValue(cursor["value"], cursor.key, cursor); // TODO: don't use object for result and assume it's jumpTo when not === true/false or undefined const done = result?.done; const jumpTo = result?.jumpTo; if (done) { resolve(true); + // @ts-ignore needsSyncPromise && Promise._flush && Promise._flush(); } else if(jumpTo) { cursor.continue(jumpTo); @@ -151,16 +168,20 @@ export function iterateCursor(cursorRequest, processValue) { }); } -export async function fetchResults(cursor, isDone) { - const results = []; - await iterateCursor(cursor, (value) => { +type Pred = (value: T) => boolean + +export async function fetchResults(cursor: IDBRequest, isDone: Pred): Promise { + const results: T[] = []; + await iterateCursor(cursor, (value) => { results.push(value); return {done: isDone(results)}; }); return results; } -export async function select(db, storeName, toCursor, isDone) { +type ToCursor = (store: IDBObjectStore) => IDBRequest + +export async function select(db: IDBDatabase, storeName: string, toCursor: ToCursor, isDone: Pred): Promise { if (!isDone) { isDone = () => false; } @@ -173,7 +194,7 @@ export async function select(db, storeName, toCursor, isDone) { return await fetchResults(cursor, isDone); } -export async function findStoreValue(db, storeName, toCursor, matchesValue) { +export async function findStoreValue(db: IDBDatabase, storeName: string, toCursor: ToCursor, matchesValue: Pred): Promise { if (!matchesValue) { matchesValue = () => true; } @@ -185,7 +206,8 @@ export async function findStoreValue(db, storeName, toCursor, matchesValue) { const store = tx.objectStore(storeName); const cursor = await reqAsPromise(toCursor(store)); let match; - const matched = await iterateCursor(cursor, (value) => { + // @ts-ignore + const matched = await iterateCursor(cursor, (value) => { if (matchesValue(value)) { match = value; return true; diff --git a/src/platform/web/legacy-polyfill.js b/src/platform/web/legacy-polyfill.js index ea2461f7..45628f2c 100644 --- a/src/platform/web/legacy-polyfill.js +++ b/src/platform/web/legacy-polyfill.js @@ -16,7 +16,7 @@ limitations under the License. // polyfills needed for IE11 import Promise from "../../../lib/es6-promise/index.js"; -import {checkNeedsSyncPromise} from "../../matrix/storage/idb/utils.js"; +import {checkNeedsSyncPromise} from "../../matrix/storage/idb/utils"; if (typeof window.Promise === "undefined") { window.Promise = Promise;