From e3cc9c59bcc36eee8c3234574cfdfda3f5eea658 Mon Sep 17 00:00:00 2001 From: Florian Dold Date: Mon, 12 Sep 2016 17:41:12 +0200 Subject: stricter type checking --- lib/wallet/query.ts | 140 +++++++++++++++++++++++++--------------------- lib/wallet/wallet.ts | 50 ++++++++--------- lib/wallet/wxMessaging.ts | 28 ++++++---- 3 files changed, 118 insertions(+), 100 deletions(-) (limited to 'lib/wallet') diff --git a/lib/wallet/query.ts b/lib/wallet/query.ts index 4eccb696b..c7420a3f7 100644 --- a/lib/wallet/query.ts +++ b/lib/wallet/query.ts @@ -24,7 +24,7 @@ "use strict"; -export function Query(db) { +export function Query(db: IDBDatabase) { return new QueryRoot(db); } @@ -36,24 +36,27 @@ export interface QueryStream { indexJoin(storeName: string, indexName: string, keyFn: (obj: any) => any): QueryStream<[T,S]>; - filter(f: (any) => boolean): QueryStream; + filter(f: (x: any) => boolean): QueryStream; reduce(f: (v: T, acc: S) => S, start?: S): Promise; - flatMap(f: (T) => T[]): QueryStream; + flatMap(f: (x: T) => T[]): QueryStream; } /** * Get an unresolved promise together with its extracted resolve / reject * function. - * - * @returns {{resolve: any, reject: any, promise: Promise}} */ function openPromise() { - let resolve, reject; + let resolve: ((value?: T | PromiseLike) => void) | null = null; + let reject: ((reason?: any) => void) | null = null; const promise = new Promise((res, rej) => { resolve = res; reject = rej; }); + if (!(resolve && reject)) { + // Never happens, unless JS implementation is broken + throw Error(); + } return {resolve, reject, promise}; } @@ -61,7 +64,7 @@ function openPromise() { abstract class QueryStreamBase implements QueryStream { abstract subscribe(f: (isDone: boolean, value: any, - tx: IDBTransaction) => void); + tx: IDBTransaction) => void): void; root: QueryRoot; @@ -69,30 +72,28 @@ abstract class QueryStreamBase implements QueryStream { this.root = root; } - flatMap(f: (T) => T[]): QueryStream { + flatMap(f: (x: T) => T[]): QueryStream { return new QueryStreamFlatMap(this, f); } indexJoin(storeName: string, indexName: string, key: any): QueryStream<[T,S]> { - this.root.addWork(null, storeName, false); + this.root.addStoreAccess(storeName, false); return new QueryStreamIndexJoin(this, storeName, indexName, key); } - filter(f: (any) => boolean): QueryStream { + filter(f: (x: any) => boolean): QueryStream { return new QueryStreamFilter(this, f); } - reduce(f, acc?): Promise { - let leakedResolve; - let p = new Promise((resolve, reject) => { - leakedResolve = resolve; - }); + reduce(f: (x: any, acc?: A) => A, init?: A): Promise { + let {resolve, promise} = openPromise(); + let acc = init; this.subscribe((isDone, value) => { if (isDone) { - leakedResolve(acc); + resolve(acc); return; } acc = f(value, acc); @@ -100,22 +101,28 @@ abstract class QueryStreamBase implements QueryStream { return Promise.resolve() .then(() => this.root.finish()) - .then(() => p); + .then(() => promise); } } +type FilterFn = (e: any) => boolean; +type SubscribeFn = (done: boolean, value: any, tx: IDBTransaction) => void; + +interface FlatMapFn { + (v: T): T[]; +} class QueryStreamFilter extends QueryStreamBase { s: QueryStreamBase; - filterFn; + filterFn: FilterFn; - constructor(s: QueryStreamBase, filterFn) { + constructor(s: QueryStreamBase, filterFn: FilterFn) { super(s.root); this.s = s; this.filterFn = filterFn; } - subscribe(f) { + subscribe(f: SubscribeFn) { this.s.subscribe((isDone, value, tx) => { if (isDone) { f(true, undefined, tx); @@ -131,15 +138,15 @@ class QueryStreamFilter extends QueryStreamBase { class QueryStreamFlatMap extends QueryStreamBase { s: QueryStreamBase; - flatMapFn; + flatMapFn: (v: T) => T[]; - constructor(s: QueryStreamBase, flatMapFn) { + constructor(s: QueryStreamBase, flatMapFn: (v: T) => T[]) { super(s.root); this.s = s; - this.flatMap = flatMapFn; + this.flatMapFn = flatMapFn; } - subscribe(f) { + subscribe(f: SubscribeFn) { this.s.subscribe((isDone, value, tx) => { if (isDone) { f(true, undefined, tx); @@ -154,13 +161,13 @@ class QueryStreamFlatMap extends QueryStreamBase { } -class QueryStreamIndexJoin extends QueryStreamBase { +class QueryStreamIndexJoin extends QueryStreamBase<[T, S]> { s: QueryStreamBase; - storeName; - key; - indexName; + storeName: string; + key: any; + indexName: string; - constructor(s, storeName: string, indexName: string, key: any) { + constructor(s: QueryStreamBase, storeName: string, indexName: string, key: any) { super(s.root); this.s = s; this.storeName = storeName; @@ -168,7 +175,7 @@ class QueryStreamIndexJoin extends QueryStreamBase { this.indexName = indexName; } - subscribe(f) { + subscribe(f: SubscribeFn) { this.s.subscribe((isDone, value, tx) => { if (isDone) { f(true, undefined, tx); @@ -192,31 +199,31 @@ class QueryStreamIndexJoin extends QueryStreamBase { class IterQueryStream extends QueryStreamBase { - private storeName; - private options; - private subscribers; + private storeName: string; + private options: any; + private subscribers: SubscribeFn[]; - constructor(qr, storeName, options) { + constructor(qr: QueryRoot, storeName: string, options: any) { super(qr); this.options = options; this.storeName = storeName; this.subscribers = []; - let doIt = (tx) => { + let doIt = (tx: IDBTransaction) => { const {indexName = void 0, only = void 0} = this.options; - let s; + let s: any; if (indexName !== void 0) { s = tx.objectStore(this.storeName) .index(this.options.indexName); } else { s = tx.objectStore(this.storeName); } - let kr = undefined; - if (only !== void 0) { + let kr: IDBKeyRange|undefined = undefined; + if (only !== undefined) { kr = IDBKeyRange.only(this.options.only); } let req = s.openCursor(kr); - req.onsuccess = (e) => { + req.onsuccess = () => { let cursor: IDBCursorWithValue = req.result; if (cursor) { for (let f of this.subscribers) { @@ -231,32 +238,33 @@ class IterQueryStream extends QueryStreamBase { } }; - this.root.addWork(doIt, null, false); + this.root.addWork(doIt); } - subscribe(f) { + subscribe(f: SubscribeFn) { this.subscribers.push(f); } } class QueryRoot { - private work = []; + private work: ((t: IDBTransaction) => void)[] = []; private db: IDBDatabase; private stores = new Set(); - private kickoffPromise; + private kickoffPromise: Promise; /** * Some operations is a write operation, * and we need to do a "readwrite" transaction/ */ - private hasWrite; + private hasWrite: boolean; - constructor(db) { + constructor(db: IDBDatabase) { this.db = db; } - iter(storeName, {only = void 0, indexName = void 0} = {}): QueryStream { + iter(storeName: string, + {only = undefined, indexName = undefined} = {}): QueryStream { this.stores.add(storeName); return new IterQueryStream(this, storeName, {only, indexName}); } @@ -266,7 +274,7 @@ class QueryRoot { * Overrides if an existing object with the same key exists * in the store. */ - put(storeName, val): QueryRoot { + put(storeName: string, val: any): QueryRoot { let doPut = (tx: IDBTransaction) => { tx.objectStore(storeName).put(val); }; @@ -280,7 +288,7 @@ class QueryRoot { * Fails if the object's key is already present * in the object store. */ - putAll(storeName, iterable): QueryRoot { + putAll(storeName: string, iterable: any[]): QueryRoot { const doPutAll = (tx: IDBTransaction) => { for (const obj of iterable) { tx.objectStore(storeName).put(obj); @@ -295,7 +303,7 @@ class QueryRoot { * Fails if the object's key is already present * in the object store. */ - add(storeName, val): QueryRoot { + add(storeName: string, val: any): QueryRoot { const doAdd = (tx: IDBTransaction) => { tx.objectStore(storeName).add(val); }; @@ -306,16 +314,16 @@ class QueryRoot { /** * Get one object from a store by its key. */ - get(storeName, key): Promise { + get(storeName: any, key: any): Promise { if (key === void 0) { throw Error("key must not be undefined"); } const {resolve, promise} = openPromise(); - const doGet = (tx) => { + const doGet = (tx: IDBTransaction) => { const req = tx.objectStore(storeName).get(key); - req.onsuccess = (r) => { + req.onsuccess = () => { resolve(req.result); }; }; @@ -329,16 +337,16 @@ class QueryRoot { /** * Get one object from a store by its key. */ - getIndexed(storeName, indexName, key): Promise { + getIndexed(storeName: string, indexName: string, key: any): Promise { if (key === void 0) { throw Error("key must not be undefined"); } const {resolve, promise} = openPromise(); - const doGetIndexed = (tx) => { + const doGetIndexed = (tx: IDBTransaction) => { const req = tx.objectStore(storeName).index(indexName).get(key); - req.onsuccess = (r) => { + req.onsuccess = () => { resolve(req.result); }; }; @@ -356,7 +364,7 @@ class QueryRoot { if (this.kickoffPromise) { return this.kickoffPromise; } - this.kickoffPromise = new Promise((resolve, reject) => { + this.kickoffPromise = new Promise((resolve, reject) => { if (this.work.length == 0) { resolve(); return; @@ -376,8 +384,8 @@ class QueryRoot { /** * Delete an object by from the given object store. */ - delete(storeName: string, key): QueryRoot { - const doDelete = (tx) => { + delete(storeName: string, key: any): QueryRoot { + const doDelete = (tx: IDBTransaction) => { tx.objectStore(storeName).delete(key); }; this.addWork(doDelete, storeName, true); @@ -387,17 +395,21 @@ class QueryRoot { /** * Low-level function to add a task to the internal work queue. */ - addWork(workFn: (IDBTransaction) => void, - storeName: string, - isWrite: boolean) { + addWork(workFn: (t: IDBTransaction) => void, + storeName?: string, + isWrite?: boolean) { + this.work.push(workFn); + if (storeName) { + this.addStoreAccess(storeName, isWrite); + } + } + + addStoreAccess(storeName: string, isWrite?: boolean) { if (storeName) { this.stores.add(storeName); } if (isWrite) { this.hasWrite = true; } - if (workFn) { - this.work.push(workFn); - } } } \ No newline at end of file diff --git a/lib/wallet/wallet.ts b/lib/wallet/wallet.ts index 7cb30c358..209c7a253 100644 --- a/lib/wallet/wallet.ts +++ b/lib/wallet/wallet.ts @@ -154,12 +154,12 @@ interface Transaction { export interface Badge { setText(s: string): void; setColor(c: string): void; - startBusy(); - stopBusy(); + startBusy(): void; + stopBusy(): void; } -function deepEquals(x, y) { +function deepEquals(x: any, y: any): boolean { if (x === y) { return true; } @@ -179,7 +179,7 @@ function flatMap(xs: T[], f: (x: T) => U[]): U[] { } -function getTalerStampSec(stamp: string): number { +function getTalerStampSec(stamp: string): number|null { const m = stamp.match(/\/?Date\(([0-9]*)\)\/?/); if (!m) { return null; @@ -188,7 +188,7 @@ function getTalerStampSec(stamp: string): number { } -function setTimeout(f, t) { +function setTimeout(f: any, t: number) { return chrome.extension.getBackgroundPage().setTimeout(f, t); } @@ -211,13 +211,13 @@ interface HttpRequestLibrary { get(url: string|uri.URI): Promise; - postJson(url: string|uri.URI, body): Promise; + postJson(url: string|uri.URI, body: any): Promise; - postForm(url: string|uri.URI, form): Promise; + postForm(url: string|uri.URI, form: any): Promise; } -function copy(o) { +function copy(o: any) { return JSON.parse(JSON.stringify(o)); } @@ -240,7 +240,7 @@ interface KeyUpdateInfo { function getWithdrawDenomList(amountAvailable: AmountJson, denoms: Denomination[]): Denomination[] { let remaining = Amounts.copy(amountAvailable); - let ds: Denomination[] = []; + const ds: Denomination[] = []; console.log("available denoms"); console.log(denoms); @@ -362,7 +362,7 @@ export class Wallet { let x: number; - function storeExchangeCoin(mc, url) { + function storeExchangeCoin(mc: any, url: string) { let exchange: IExchangeInfo = mc[0]; console.log("got coin for exchange", url); let coin: Coin = mc[1]; @@ -471,7 +471,7 @@ export class Wallet { private recordConfirmPay(offer: Offer, payCoinInfo: PayCoinInfo, chosenExchange: string): Promise { - let payReq = {}; + let payReq: any = {}; payReq["amount"] = offer.contract.amount; payReq["coins"] = payCoinInfo.map((x) => x.sig); payReq["H_contract"] = offer.H_contract; @@ -588,7 +588,7 @@ export class Wallet { * Retrieve all necessary information for looking up the contract * with the given hash. */ - executePayment(H_contract): Promise { + executePayment(H_contract: string): Promise { return Promise.resolve().then(() => { return Query(this.db) .get("transactions", H_contract) @@ -614,7 +614,7 @@ export class Wallet { * First fetch information requred to withdraw from the reserve, * then deplete the reserve, withdrawing coins until it is empty. */ - private processReserve(reserveRecord): void { + private processReserve(reserveRecord: any): void { let retryDelayMs = 100; const opId = "reserve-" + reserveRecord.reserve_pub; this.startOperation(opId); @@ -644,7 +644,7 @@ export class Wallet { } - private processPreCoin(preCoin, retryDelayMs = 100): void { + private processPreCoin(preCoin: any, retryDelayMs = 100): void { this.withdrawExecute(preCoin) .then((c) => this.storeCoin(c)) .catch((e) => { @@ -810,7 +810,7 @@ export class Wallet { /** * Withdraw coins from a reserve until it is empty. */ - private depleteReserve(reserve, exchange: IExchangeInfo): Promise { + private depleteReserve(reserve: any, exchange: IExchangeInfo): Promise { let denomsAvailable: Denomination[] = copy(exchange.active_denoms); let denomsForWithdraw = getWithdrawDenomList(reserve.current_amount, denomsAvailable); @@ -919,7 +919,7 @@ export class Wallet { * Optionally link the reserve entry to the new or existing * exchange entry in then DB. */ - updateExchangeFromUrl(baseUrl): Promise { + updateExchangeFromUrl(baseUrl: string): Promise { baseUrl = canonicalizeBaseUrl(baseUrl); let reqUrl = URI("keys").absoluteTo(baseUrl); return this.http.get(reqUrl).then((resp) => { @@ -934,8 +934,8 @@ export class Wallet { private updateExchangeFromJson(baseUrl: string, exchangeKeysJson: KeysJson): Promise { - let updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date); - if (!updateTimeSec) { + const updateTimeSec = getTalerStampSec(exchangeKeysJson.list_issue_date); + if (updateTimeSec === null) { throw Error("invalid update time"); } @@ -973,9 +973,9 @@ export class Wallet { {indexName: "exchangeBaseUrl", only: baseUrl}) .reduce((coin: Coin, suspendedCoins: Coin[]) => { if (!updatedExchangeInfo.active_denoms.find((c) => c.denom_pub == coin.denomPub)) { - return [].concat(suspendedCoins, [coin]); + return Array.prototype.concat(suspendedCoins, [coin]); } - return [].concat(suspendedCoins); + return Array.prototype.concat(suspendedCoins); }, []) .then((suspendedCoins: Coin[]) => { let q = Query(this.db); @@ -1006,8 +1006,8 @@ export class Wallet { let found = false; for (let oldDenom of exchangeInfo.all_denoms) { if (oldDenom.denom_pub === newDenom.denom_pub) { - let a = Object.assign({}, oldDenom); - let b = Object.assign({}, newDenom); + let a: any = Object.assign({}, oldDenom); + let b: any = Object.assign({}, newDenom); // pub hash is only there for convenience in the wallet delete a["pub_hash"]; delete b["pub_hash"]; @@ -1055,7 +1055,7 @@ export class Wallet { * that is currenctly available for spending in the wallet. */ getBalances(): Promise { - function collectBalances(c: Coin, byCurrency) { + function collectBalances(c: Coin, byCurrency: any) { if (c.suspended) { return byCurrency; } @@ -1081,7 +1081,7 @@ export class Wallet { * Retrive the full event history for this wallet. */ getHistory(): Promise { - function collect(x, acc) { + function collect(x: any, acc: any) { acc.push(x); return acc; } @@ -1106,7 +1106,7 @@ export class Wallet { [contract.merchant_pub, contract.repurchase_correlation_id]) .then((result: Transaction) => { console.log("db result", result); - let isRepurchase; + let isRepurchase: boolean; if (result) { console.assert(result.contract.repurchase_correlation_id == contract.repurchase_correlation_id); return { diff --git a/lib/wallet/wxMessaging.ts b/lib/wallet/wxMessaging.ts index 3ab56af71..401fefd56 100644 --- a/lib/wallet/wxMessaging.ts +++ b/lib/wallet/wxMessaging.ts @@ -54,8 +54,12 @@ function makeHandlers(db: IDBDatabase, return exportDb(db); }, ["ping"]: function(detail, sender) { - let info = paymentRequestCookies[sender.tab.id]; - delete paymentRequestCookies[sender.tab.id]; + if (!sender || !sender.tab || !sender.tab.id) { + return Promise.resolve(); + } + let id: number = sender.tab.id; + let info: any = paymentRequestCookies[id]; + delete paymentRequestCookies[id]; return Promise.resolve(info); }, ["reset"]: function(detail, sender) { @@ -89,7 +93,7 @@ function makeHandlers(db: IDBDatabase, return wallet.confirmReserve(req); }, ["confirm-pay"]: function(detail, sender) { - let offer; + let offer: Offer; try { offer = Offer.checked(detail.offer); } catch (e) { @@ -108,7 +112,7 @@ function makeHandlers(db: IDBDatabase, return wallet.confirmPay(offer); }, ["check-pay"]: function(detail, sender) { - let offer; + let offer: Offer; try { offer = Offer.checked(detail.offer); } catch (e) { @@ -181,14 +185,14 @@ class ChromeBadge implements Badge { } -function dispatch(handlers, req, sender, sendResponse) { +function dispatch(handlers: any, req: any, sender: any, sendResponse: any) { if (req.type in handlers) { Promise .resolve() .then(() => { const p = handlers[req.type](req.detail, sender); - return p.then((r) => { + return p.then((r: any) => { sendResponse(r); }) }) @@ -242,13 +246,15 @@ class ChromeNotifier implements Notifier { /** * Mapping from tab ID to payment information (if any). */ -let paymentRequestCookies = {}; +let paymentRequestCookies: {[n: number]: any} = {}; function handleHttpPayment(headerList: chrome.webRequest.HttpHeader[], url: string, tabId: number): any { - const headers = {}; + const headers: {[s: string]: string} = {}; for (let kv of headerList) { - headers[kv.name.toLowerCase()] = kv.value; + if (kv.value) { + headers[kv.name.toLowerCase()] = kv.value; + } } const contractUrl = headers["x-taler-contract-url"]; @@ -288,7 +294,7 @@ export function wxMain() { chrome.tabs.query({}, function(tabs) { for (let tab of tabs) { - if (!tab.url) { + if (!tab.url || !tab.id) { return; } let uri = URI(tab.url); @@ -338,7 +344,7 @@ export function wxMain() { return; } console.log(`got 402 from ${details.url}`); - return handleHttpPayment(details.responseHeaders, + return handleHttpPayment(details.responseHeaders || [], details.url, details.tabId); }, {urls: [""]}, ["responseHeaders", "blocking"]); -- cgit v1.2.3