From 35859ede71007b51f8a3fd1833a43d143acade0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Sun, 19 Mar 2023 12:24:16 -0600 Subject: [PATCH 01/14] Update Typescript to latest v3 --- .vscode/settings.json | 3 ++ package.json | 2 +- src/components/RecoveryWords.js | 24 ++++++++----- src/lib/AutoComplete.js | 1 + src/lib/CookieJar.js | 5 ++- src/lib/CurrencyInfo.js | 22 +++--------- src/lib/FiatApi.js | 34 ++++++++----------- src/lib/IqonHash.js | 2 +- src/lib/Iqons.js | 2 +- src/lib/KeyInfo.js | 1 - src/lib/NonPartitionedSessionStorage.js | 5 ++- .../SignBtcTransactionApi.js | 5 ++- src/request/sign-swap/SignSwapApi.js | 5 ++- .../sign-transaction/SignTransactionApi.js | 5 ++- tests/DummyData.spec.js | 4 +-- tests/lib/AccountStore.spec.js | 4 +-- tests/lib/IframeApi.spec.js | 8 ++--- tests/lib/KeyStore.spec.js | 22 ++++++------ tests/lib/TopLevelApi.spec.js | 6 ++-- yarn.lock | 8 ++--- 20 files changed, 79 insertions(+), 89 deletions(-) create mode 100644 .vscode/settings.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..72446f434 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/package.json b/package.json index c57a173c4..e0105a2e4 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,6 @@ "karma-jasmine": "^1.1.2", "terser": "^5.14.2", "tsc-watch": "^1.0.22", - "typescript": "^3.5.0" + "typescript": "3" } } diff --git a/src/components/RecoveryWords.js b/src/components/RecoveryWords.js index d0843ed36..5c2066981 100644 --- a/src/components/RecoveryWords.js +++ b/src/components/RecoveryWords.js @@ -13,7 +13,7 @@ class RecoveryWords extends Nimiq.Observable { constructor($el, providesInput) { super(); - /** @type {Object[]} */ + /** @type {(RecoveryWordsInputField | HTMLSpanElement)[]} */ this.$fields = []; /** @type {HTMLElement} */ @@ -27,11 +27,15 @@ class RecoveryWords extends Nimiq.Observable { } /** + * Set the recovery words. Only works when `providesInput` is `false`. * @param {string[]} words */ setWords(words) { for (let i = 0; i < 24; i++) { - this.$fields[i].textContent = words[i]; + const field = this.$fields[i]; + if ('textContent' in field) { + field.textContent = words[i]; + } } } @@ -105,7 +109,10 @@ class RecoveryWords extends Nimiq.Observable { this._animateError(); window.setTimeout(() => { for (let i = 0; i < 24; i++) { - this.$fields[i].value = ''; + const field = this.$fields[i]; + if ('value' in field) { + field.value = ''; + } } }, 500); } @@ -123,13 +130,13 @@ class RecoveryWords extends Nimiq.Observable { async _checkPhraseComplete() { // Check if all fields are complete - if (this.$fields.some(field => !field.complete)) { + if (this.$fields.some(field => 'complete' in field && !field.complete)) { this._onFieldIncomplete(); return; } try { - const mnemonic = this.$fields.map(field => field.value); + const mnemonic = this.$fields.map(field => ('value' in field ? field.value : '-')); const type = Nimiq.MnemonicUtils.getMnemonicType(mnemonic); // throws on invalid mnemonic this._mnemonic = { words: mnemonic, type }; this.fire(RecoveryWords.Events.COMPLETE, mnemonic, type); @@ -157,9 +164,10 @@ class RecoveryWords extends Nimiq.Observable { _setFocusToNextInput(index, paste) { index = Math.max(index, 0); if (index < this.$fields.length) { - this.$fields[index].focus(); - if (paste) { - this.$fields[index].fillValueFrom(paste); + const field = this.$fields[index]; + field.focus(); + if (paste && 'fillValueFrom' in field) { + field.fillValueFrom(paste); } } } diff --git a/src/lib/AutoComplete.js b/src/lib/AutoComplete.js index 0f188cd9f..840110adf 100644 --- a/src/lib/AutoComplete.js +++ b/src/lib/AutoComplete.js @@ -1,3 +1,4 @@ +// @ts-nocheck /* JavaScript autoComplete v1.0.4 Copyright (c) 2014 Simon Steinberger / Pixabay diff --git a/src/lib/CookieJar.js b/src/lib/CookieJar.js index c7bc12920..10dddd1ab 100644 --- a/src/lib/CookieJar.js +++ b/src/lib/CookieJar.js @@ -182,10 +182,9 @@ class CookieJar { // eslint-disable-line no-unused-vars } /** - * @readonly * @enum { 'lang' | 'k' | 'removeKey' | 'ssp' | 'cs.' | 'npss.' | 'accounts' | 'migrate' } */ -CookieJar.Cookie = { +CookieJar.Cookie = Object.freeze({ LANGUAGE: /** @type {'lang'} */ ('lang'), KEYS: /** @type {'k'} */ ('k'), REMOVE_KEY: /** @type {'removeKey'} */ ('removeKey'), @@ -202,7 +201,7 @@ CookieJar.Cookie = { * @type {'migrate'} */ DEPRECATED_MIGRATION_FLAG: ('migrate'), -}; +}); CookieJar.DEFAULT_MAX_AGE = 31536000; // 1 year; in seconds // Maximum size per cookie which should be safe for all browsers, measured as the sum of the cookie name and value, // but excluding the equal sign and cookie options. The maximum size for different browsers can be tested with diff --git a/src/lib/CurrencyInfo.js b/src/lib/CurrencyInfo.js index b32c9486c..86598681e 100644 --- a/src/lib/CurrencyInfo.js +++ b/src/lib/CurrencyInfo.js @@ -69,7 +69,7 @@ class CurrencyInfo { } const cacheKey = `${this.code} ${this.locale}`; - const cachedCurrencyInfo = CurrencyInfo.CACHED_AUTO_GENERATED_CURRENCY_INFOS[cacheKey]; + const cachedCurrencyInfo = CurrencyInfo._CACHED_AUTO_GENERATED_CURRENCY_INFOS[cacheKey]; if (cachedCurrencyInfo) { return cachedCurrencyInfo; } @@ -154,14 +154,12 @@ class CurrencyInfo { } } - CurrencyInfo.CACHED_AUTO_GENERATED_CURRENCY_INFOS[cacheKey] = this; + CurrencyInfo._CACHED_AUTO_GENERATED_CURRENCY_INFOS[cacheKey] = this; } } /** - * @private - * @readonly - * @type {{[code: string]: string | [string, string]}} + * @type {Readonly<{[code: string]: string | [string, string]}>} */ CurrencyInfo.EXTRA_SYMBOLS = { AED: ['DH', 'د.إ'], @@ -221,9 +219,7 @@ CurrencyInfo.EXTRA_SYMBOLS = { * Some currencies have been devalued so much by inflation that their sub-units have been removed from circulation * or are effectively not being used anymore. This is not for all currencies reflected yet in toLocaleString, such * that we mark some currencies manually as decimal-less. - * @private - * @readonly - * @type {Set} + * @type {Readonly>} */ CurrencyInfo.CUSTOM_DECIMAL_LESS_CURRENCIES = new Set([ 'AMD', // sub-unit rarely used @@ -272,24 +268,18 @@ CurrencyInfo.CUSTOM_DECIMAL_LESS_CURRENCIES = new Set([ /** * Cached auto-generated CurrencyInfos such that they do not need to be recalculated. - * @private - * @readonly * @type {{[currencyAndLocale: string]: CurrencyInfo}} */ -CurrencyInfo.CACHED_AUTO_GENERATED_CURRENCY_INFOS = {}; +CurrencyInfo._CACHED_AUTO_GENERATED_CURRENCY_INFOS = {}; /** * Regex for detecting valid currency codes. - * @private - * @readonly * @type {RegExp} */ CurrencyInfo.CURRENCY_CODE_REGEX = /[A-Z]{3}/i; /** * Regex for detecting the number with optional decimals in a formatted string for useGrouping: false - * @private - * @readonly * @type {RegExp} */ CurrencyInfo.NUMBER_REGEX = /\d+(?:\D(\d+))?/; @@ -298,8 +288,6 @@ CurrencyInfo.NUMBER_REGEX = /\d+(?:\D(\d+))?/; * Regex for detecting right-to-left text. * Simplified and adapted from https://stackoverflow.com/a/14824756. * Note that this rtl detection is incomplete but good enough for our needs. - * @private - * @readonly * @type {RegExp} */ CurrencyInfo.RIGHT_TO_LEFT_DETECTION_REGEX = /[\u0591-\u07FF\uFB1D-\uFDFD\uFE70-\uFEFC]/; diff --git a/src/lib/FiatApi.js b/src/lib/FiatApi.js index 65a9ce28c..9a5c491e6 100644 --- a/src/lib/FiatApi.js +++ b/src/lib/FiatApi.js @@ -42,36 +42,33 @@ class FiatApi { } /** - * @readonly * @enum { 'CoinGecko' } * Supported API providers. */ -FiatApi.SupportedProvider = { +FiatApi.SupportedProvider = Object.freeze({ CoinGecko: /** @type {'CoinGecko'} */ ('CoinGecko'), -}; +}); /** - * @readonly * @type {FiatApi.SupportedProvider} * The effective provider used by the FiatApi. As this is not meant to be changeable at runtime, all code for other * providers can be commented out, to avoid type confusion. */ FiatApi.Provider = FiatApi.SupportedProvider.CoinGecko; -switch (FiatApi.Provider) { // eslint-disable-line default-case, -- no default to let ts warn us on missing providers - case FiatApi.SupportedProvider.CoinGecko: { +// switch (FiatApi.Provider) { // eslint-disable-line default-case, -- no default to let ts warn us on missing providers +// case FiatApi.SupportedProvider.CoinGecko: { +/* eslint-disable indent */ /** - * @readonly * @enum { 'nim' | 'btc' } * Crypto currencies supported by the coingecko api that are currently of interest to us. */ - FiatApi.SupportedCryptoCurrency = { + FiatApi.SupportedCryptoCurrency = Object.freeze({ NIM: /** @type {'nim'} */ ('nim'), BTC: /** @type {'btc'} */ ('btc'), - }; + }); /** - * @readonly * @enum {'aed' | 'ars' | 'aud' | 'bdt' | 'bhd' | 'bmd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'czk' | 'dkk' * | 'eur' | 'gbp' | 'gel' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'lkr' | 'mmk' * | 'mxn' | 'myr' | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb' @@ -81,7 +78,7 @@ switch (FiatApi.Provider) { // eslint-disable-line default-case, -- no default t * ounces of gold amongst others that are not fiat currencies. See FiatApi in @nimiq/utils for how this list was * assembled. */ - FiatApi.SupportedFiatCurrency = { + FiatApi.SupportedFiatCurrency = Object.freeze({ AED: /** @type {'aed'} */ ('aed'), // Arab Emirates Dirham ARS: /** @type {'ars'} */ ('ars'), // Argentine Peso AUD: /** @type {'aud'} */ ('aud'), // Australian Dollar @@ -128,23 +125,22 @@ switch (FiatApi.Provider) { // eslint-disable-line default-case, -- no default t // VEF: /** @type {'vef'} */ ('vef'), // Retired Venezuelan Bolívar Fuerte replaced by VES. Rates are off. VND: /** @type {'vnd'} */ ('vnd'), // Vietnamese Đồng ZAR: /** @type {'zar'} */ ('zar'), // South African Rand - }; + }); /** - * @readonly * Coingecko api url. Note that the origin must be whitelisted in the csp. */ FiatApi.API_URL = 'https://api.coingecko.com/api/v3'; /** - * @readonly * Crypto currency tickers mapped to coingecko coin ids. */ - FiatApi.COINGECKO_COIN_IDS = { + FiatApi.COINGECKO_COIN_IDS = Object.freeze({ [FiatApi.SupportedCryptoCurrency.NIM]: 'nimiq-2', [FiatApi.SupportedCryptoCurrency.BTC]: 'bitcoin', - }; + }); - break; - } -} +/* eslint-enable indent */ +// break; +// } +// } diff --git a/src/lib/IqonHash.js b/src/lib/IqonHash.js index d94e10016..4c7116664 100644 --- a/src/lib/IqonHash.js +++ b/src/lib/IqonHash.js @@ -57,7 +57,7 @@ class IqonHash { /* eslint-disable-line no-unused-vars */ * @returns {string} */ static _padEnd(string, maxLength, fillString) { - if (String.prototype.padEnd) return string.padEnd(maxLength, fillString); + if ('padEnd' in String.prototype) return string.padEnd(maxLength, fillString); while (string.length < maxLength) { string += fillString; diff --git a/src/lib/Iqons.js b/src/lib/Iqons.js index 16873c93f..227244deb 100644 --- a/src/lib/Iqons.js +++ b/src/lib/Iqons.js @@ -200,7 +200,7 @@ class Iqons { ]; } - /** @type {object} */ + /** @type {Record}} */ static get assetCounts() { return { face: Iqons.CATALOG.face.length, diff --git a/src/lib/KeyInfo.js b/src/lib/KeyInfo.js index 5ba104721..c3027e38a 100644 --- a/src/lib/KeyInfo.js +++ b/src/lib/KeyInfo.js @@ -23,7 +23,6 @@ class KeyInfo { /** * @description Used for distinguishing pre-migration accountInfo from regular keyInfo * @type {boolean} - * @private */ this.useLegacyStore = false; } diff --git a/src/lib/NonPartitionedSessionStorage.js b/src/lib/NonPartitionedSessionStorage.js index 37b44d4e9..ef456dc3e 100644 --- a/src/lib/NonPartitionedSessionStorage.js +++ b/src/lib/NonPartitionedSessionStorage.js @@ -218,13 +218,12 @@ NonPartitionedSessionStorage.TOP_LEVEL_SESSION_STORAGE_ENTRY = '_topLevelEntry'; NonPartitionedSessionStorage.IFRAME_SESSION_STORAGE_ENTRY = '_iframeEntry'; /** * Prefix for the FLAG_SESSION_STORAGE_PARTITIONED cookie. - * @readonly * @enum { '1' | '0' } */ -NonPartitionedSessionStorage.PREFIX_FLAG_SESSION_STORAGE_PARTITIONED = { +NonPartitionedSessionStorage.PREFIX_FLAG_SESSION_STORAGE_PARTITIONED = Object.freeze({ TRUE: /** @type {'1'} */ ('1'), FALSE: /** @type {'0'} */ ('0'), -}; +}); /** @type {boolean | undefined} */ NonPartitionedSessionStorage._hasSessionStorageAccess = undefined; diff --git a/src/request/sign-btc-transaction/SignBtcTransactionApi.js b/src/request/sign-btc-transaction/SignBtcTransactionApi.js index dfce6c755..ba7041c9d 100644 --- a/src/request/sign-btc-transaction/SignBtcTransactionApi.js +++ b/src/request/sign-btc-transaction/SignBtcTransactionApi.js @@ -97,9 +97,8 @@ class SignBtcTransactionApi extends BitcoinRequestParserMixin(TopLevelApi) { /** * @enum {KeyguardRequest.SignBtcTransactionRequestLayout} - * @readonly */ -SignBtcTransactionApi.Layouts = { +SignBtcTransactionApi.Layouts = Object.freeze({ STANDARD: /** @type {'standard'} */ ('standard'), CHECKOUT: /** @type {'checkout'} */ ('checkout'), -}; +}); diff --git a/src/request/sign-swap/SignSwapApi.js b/src/request/sign-swap/SignSwapApi.js index 368c03953..0badaf59f 100644 --- a/src/request/sign-swap/SignSwapApi.js +++ b/src/request/sign-swap/SignSwapApi.js @@ -457,9 +457,8 @@ class SignSwapApi extends PolygonRequestParserMixin(BitcoinRequestParserMixin(To /** * @enum {KeyguardRequest.SignSwapRequestLayout} - * @readonly */ -SignSwapApi.Layouts = { +SignSwapApi.Layouts = Object.freeze({ STANDARD: /** @type {'standard'} */ ('standard'), SLIDER: /** @type {'slider'} */ ('slider'), -}; +}); diff --git a/src/request/sign-transaction/SignTransactionApi.js b/src/request/sign-transaction/SignTransactionApi.js index cf6ac662e..d9f537783 100644 --- a/src/request/sign-transaction/SignTransactionApi.js +++ b/src/request/sign-transaction/SignTransactionApi.js @@ -92,10 +92,9 @@ class SignTransactionApi extends TopLevelApi { /** * @enum {KeyguardRequest.SignTransactionRequestLayout} - * @readonly */ -SignTransactionApi.Layouts = { +SignTransactionApi.Layouts = Object.freeze({ STANDARD: /** @type {'standard'} */ ('standard'), CHECKOUT: /** @type {'checkout'} */ ('checkout'), CASHLINK: /** @type {'cashlink'} */ ('cashlink'), -}; +}); diff --git a/tests/DummyData.spec.js b/tests/DummyData.spec.js index c06052d75..37ea6c8df 100644 --- a/tests/DummyData.spec.js +++ b/tests/DummyData.spec.js @@ -193,7 +193,7 @@ const Utils = { /** @returns {Promise} */ createDummyKeyStore: async () => { // The key store can be created and filled by its api - await KeyStore.instance.connect(); + await KeyStore.instance['connect'](); await Promise.all([ KeyStore.instance.putPlain(keyRecords()[0]), KeyStore.instance.putPlain(keyRecords()[1]), @@ -228,7 +228,7 @@ const Utils = { deleteDummyKeyStore: async () => { await KeyStore.instance.close(); await Utils.deleteDatabase(DUMMY_KEY_DATABASE_NAME); - delete KeyStore._instance; + KeyStore._instance = null; }, }; diff --git a/tests/lib/AccountStore.spec.js b/tests/lib/AccountStore.spec.js index 0fe2ca4e5..c7cfc7e66 100644 --- a/tests/lib/AccountStore.spec.js +++ b/tests/lib/AccountStore.spec.js @@ -12,7 +12,7 @@ describe('AccountStore', () => { }); it('can open and close a connection', async () => { - const db = /** @type {IDBDatabase} */ (await AccountStore.instance.connect()); + const db = /** @type {IDBDatabase} */ (await AccountStore.instance['connect']()); expect(db.constructor).toBe(IDBDatabase); expect(AccountStore.instance._dbPromise).toBeTruthy(); expect(db.name).toBe(Dummy.DUMMY_ACCOUNT_DATABASE_NAME); @@ -43,7 +43,7 @@ describe('AccountStore', () => { it('can be dropped', async function () { await AccountStore.instance.drop(); // database should not exist anymore - const db = await AccountStore.instance.connect(); + const db = await AccountStore.instance['connect'](); expect(db).toBeNull(); }); diff --git a/tests/lib/IframeApi.spec.js b/tests/lib/IframeApi.spec.js index 31245362b..ecb0267e7 100644 --- a/tests/lib/IframeApi.spec.js +++ b/tests/lib/IframeApi.spec.js @@ -86,7 +86,7 @@ describe('IframeApi', () => { expect(cookieSet).toBe(true); expect(KeyStore.instance.migrateAccountsToKeys).not.toHaveBeenCalled(); - const accountsDbAfter = await AccountStore.instance.connect(); + const accountsDbAfter = await AccountStore.instance['connect'](); expect(accountsDbAfter).not.toBe(null); Dummy.Utils.deleteDummyAccountStore(); @@ -96,7 +96,7 @@ describe('IframeApi', () => { await Dummy.Utils.createDummyAccountStore(); spyOn(BrowserDetection, 'isIOS').and.returnValue(false); - const accountsDbBefore = await AccountStore.instance.connect(); + const accountsDbBefore = await AccountStore.instance['connect'](); expect(accountsDbBefore).not.toBe(null); await iframeApi.migrateAccountsToKeys(null); @@ -105,12 +105,12 @@ describe('IframeApi', () => { // check that keys have been copied correctly const ids = (await KeyStore.instance.list()).map(record => record.id); for (let id of ids) { - const keyRecord = await KeyStore.instance._get(id); + const keyRecord = await KeyStore.instance['_get'](id); const expectedKeyRecord = /** @type {KeyRecord} */(Dummy.keyRecords().find(record => record.id === id)); expect(keyRecord).toEqual(expectedKeyRecord); } - const accountsDb = await AccountStore.instance.connect(); + const accountsDb = await AccountStore.instance['connect'](); expect(accountsDb).toBe(null); await Promise.all([ diff --git a/tests/lib/KeyStore.spec.js b/tests/lib/KeyStore.spec.js index 4e0328b48..248f389c7 100644 --- a/tests/lib/KeyStore.spec.js +++ b/tests/lib/KeyStore.spec.js @@ -14,7 +14,7 @@ describe('KeyStore', () => { }); it('can open and close a connection', async () => { - const db = await KeyStore.instance.connect(); + const db = await KeyStore.instance['connect'](); expect(db.constructor).toBe(IDBDatabase); expect(KeyStore.instance._dbPromise).toBeTruthy(); expect(db.name).toBe(Dummy.DUMMY_KEY_DATABASE_NAME); @@ -24,8 +24,8 @@ describe('KeyStore', () => { it('can get plain keys', async () => { const [key1, key2] = await Promise.all([ - KeyStore.instance._get(Dummy.keyInfos()[0].id), - KeyStore.instance._get(Dummy.keyInfos()[1].id), + KeyStore.instance['_get'](Dummy.keyInfos()[0].id), + KeyStore.instance['_get'](Dummy.keyInfos()[1].id), ]); expect(key1).toEqual(Dummy.keyRecords()[0]); expect(key2).toEqual(Dummy.keyRecords()[1]); @@ -66,8 +66,8 @@ describe('KeyStore', () => { // check that we can't get a removed key by address const removedKeys = await Promise.all([ - KeyStore.instance._get(Dummy.keyInfos()[0].id), - KeyStore.instance._get(Dummy.keyInfos()[1].id), + KeyStore.instance['_get'](Dummy.keyInfos()[0].id), + KeyStore.instance['_get'](Dummy.keyInfos()[1].id), ]); expect(removedKeys[0]).toBeUndefined(); expect(removedKeys[1]).toBeUndefined(); @@ -114,7 +114,7 @@ describe('KeyStore', () => { Dummy.Utils.createDummyAccountStore(), ]); - const accountsDbBefore = await AccountStore.instance.connect(); + const accountsDbBefore = await AccountStore.instance['connect'](); expect(accountsDbBefore).not.toBe(null); spyOn(BrowserDetection, 'isIOS').and.returnValue(false); @@ -127,10 +127,10 @@ describe('KeyStore', () => { await KeyStore.instance.migrateAccountsToKeys(); expect(cookieSet).toBe(false); - const key1 = await KeyStore.instance._get(Dummy.keyInfos()[1].id); + const key1 = await KeyStore.instance['_get'](Dummy.keyInfos()[1].id); expect(key1).toEqual(Dummy.keyRecords()[1]); - const accountsDbAfter = await AccountStore.instance.connect(); + const accountsDbAfter = await AccountStore.instance['connect'](); expect(accountsDbAfter).toBe(null); await Dummy.Utils.deleteDummyAccountStore(); @@ -143,7 +143,7 @@ describe('KeyStore', () => { Dummy.Utils.createDummyAccountStore(), ]); - const accountsDbBefore = await AccountStore.instance.connect(); + const accountsDbBefore = await AccountStore.instance['connect'](); expect(accountsDbBefore).not.toBe(null); spyOn(BrowserDetection, 'isIOS').and.returnValue(true); @@ -163,10 +163,10 @@ describe('KeyStore', () => { await KeyStore.instance.migrateAccountsToKeys(); expect(migrationCookieDeleted && accountsCookieDeleted).toBe(true); - const key1 = await KeyStore.instance._get(Dummy.keyInfos()[1].id); + const key1 = await KeyStore.instance['_get'](Dummy.keyInfos()[1].id); expect(key1).toEqual(Dummy.keyRecords()[1]); - const accountsDb = await AccountStore.instance.connect(); + const accountsDb = await AccountStore.instance['connect'](); expect(accountsDb).toBe(null); await Dummy.Utils.deleteDummyAccountStore(); diff --git a/tests/lib/TopLevelApi.spec.js b/tests/lib/TopLevelApi.spec.js index 49442ae9f..5503b3f8d 100644 --- a/tests/lib/TopLevelApi.spec.js +++ b/tests/lib/TopLevelApi.spec.js @@ -3,16 +3,16 @@ describe("TopLevelApi", function () { it("can detect migration cookie", function () { - expect(TopLevelApi._hasMigrateFlag()).toBe(false); + expect(TopLevelApi['_hasMigrateFlag']()).toBe(false); // Set migrate cookie document.cookie = 'migrate=1;max-age=31536000'; - expect(TopLevelApi._hasMigrateFlag()).toBe(true); + expect(TopLevelApi['_hasMigrateFlag']()).toBe(true); // Delete migrate cookie document.cookie = 'migrate=0; expires=Thu, 01 Jan 1970 00:00:01 GMT;'; - expect(TopLevelApi._hasMigrateFlag()).toBe(false); + expect(TopLevelApi['_hasMigrateFlag']()).toBe(false); }); }); diff --git a/yarn.lock b/yarn.lock index cf95acbf6..6a8ab659a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5304,10 +5304,10 @@ typeforce@^1.11.3, typeforce@^1.11.5: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@^3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.1.tgz#ba72a6a600b2158139c5dd8850f700e231464202" - integrity sha512-64HkdiRv1yYZsSe4xC1WVgamNigVYjlssIoaH2HcZF0+ijsk5YK2g0G34w9wJkze8+5ow4STd22AynfO6ZYYLw== +typescript@3: + version "3.9.10" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" + integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== ua-parser-js@^0.7.30: version "0.7.36" From c460379bee8f507c3fdc5c17b85833a9bd6fdf32 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Tue, 14 Nov 2023 11:13:17 -0600 Subject: [PATCH 02/14] Update to Typescript v4 --- package.json | 2 +- src/components/DownloadLoginFile.js | 22 ++--- src/components/FileImporter.js | 9 +- src/components/FlippableHandler.js | 6 +- src/components/Identicon.js | 3 +- src/components/LoginFileAnimation.js | 3 +- src/components/PasswordBox.js | 5 +- src/components/PasswordSetterBox.js | 2 +- src/components/PaymentInfoLine.js | 31 +++---- src/components/ProgressIndicator.js | 2 +- src/components/QrVideoScanner.js | 11 +-- src/components/RecoveryWords.js | 5 +- src/components/SwapFeesTooltip.js | 10 +-- src/components/Timer.js | 20 ++--- src/components/ValidateWords.js | 3 +- src/lib/AnimationUtils.js | 2 +- src/lib/BrowserDetection.js | 3 +- src/lib/CurrencyInfo.js | 7 +- src/lib/KeyStore.js | 4 +- src/lib/LoginFile.js | 6 +- src/lib/QrEncoder.js | 6 +- src/lib/QrScanner.js | 7 +- src/lib/RequestParser.js | 12 ++- src/lib/RpcServer.es.js | 3 +- src/lib/polygon/PolygonRequestParserMixin.js | 6 +- src/request/TopLevelApi.js | 12 +-- src/request/change-password/ChangePassword.js | 39 ++++----- src/request/create/Create.js | 47 +++++------ src/request/create/IdenticonSelector.js | 2 +- src/request/derive-address/DeriveAddress.js | 6 +- src/request/derive-btc-xpub/DeriveBtcXPub.js | 6 +- .../DerivePolygonAddress.js | 6 +- src/request/export/Export.js | 12 +-- src/request/export/ExportFile.js | 48 ++++++----- src/request/export/ExportWords.js | 39 ++++----- src/request/import/ImportFile.js | 38 ++++----- src/request/import/ImportWords.js | 37 ++++---- src/request/remove-key/RemoveKey.js | 30 ++++--- .../SignBtcTransaction.js | 33 +++----- src/request/sign-message/SignMessage.js | 21 ++--- .../SignPolygonTransaction.js | 25 +++--- .../SignPolygonTransactionApi.js | 50 +++++------ src/request/sign-swap/SignSwap.js | 84 ++++++++----------- .../sign-transaction/SignTransaction.js | 44 ++++------ tsconfig.json | 4 +- yarn.lock | 8 +- 46 files changed, 350 insertions(+), 431 deletions(-) diff --git a/package.json b/package.json index e0105a2e4..61f0fc274 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,6 @@ "karma-jasmine": "^1.1.2", "terser": "^5.14.2", "tsc-watch": "^1.0.22", - "typescript": "3" + "typescript": "4" } } diff --git a/src/components/DownloadLoginFile.js b/src/components/DownloadLoginFile.js index eeed2d581..628a23f6e 100644 --- a/src/components/DownloadLoginFile.js +++ b/src/components/DownloadLoginFile.js @@ -19,20 +19,11 @@ class DownloadLoginFile extends Nimiq.Observable { /** @type {LoginFile | null} */ this._file = null; - /** @type {HTMLImageElement} */ - this.$loginfile = (this.$el.querySelector('.loginfile')); - - /** @type {HTMLAnchorElement} */ - this.$loginfileLink = (this.$el.querySelector('.loginfile-link')); - - /** @type {HTMLAnchorElement} */ - this.$downloadButton = (this.$el.querySelector('.download-button')); - - /** @type {HTMLButtonElement} */ - this.$continueButton = (this.$el.querySelector('.continue')); - - /** @type {SVGElement} */ - this.$longTouchIndicator = (this.$el.querySelector('.long-touch-indicator')); + this.$loginfile = /** @type {HTMLImageElement} */ (this.$el.querySelector('.loginfile')); + this.$loginfileLink = /** @type {HTMLAnchorElement} */ (this.$el.querySelector('.loginfile-link')); + this.$downloadButton = /** @type {HTMLAnchorElement} */ (this.$el.querySelector('.download-button')); + this.$continueButton = /** @type {HTMLButtonElement} */ (this.$el.querySelector('.continue')); + this.$longTouchIndicator = /** @type {SVGElement} */ (this.$el.querySelector('.long-touch-indicator')); this.$loginfile.addEventListener('mousedown', e => this._onMouseDown(e)); this.$loginfile.addEventListener('touchstart', () => this._onTouchStart()); @@ -159,8 +150,7 @@ class DownloadLoginFile extends Nimiq.Observable { * @param {MouseEvent} event */ _onMouseDown(event) { - /** @type {HTMLElement} */ - const target = (event.target); + const target = /** @type {HTMLElement} */ (event.target); // Clicks on the continue or download buttons are already covered by a 'click' handler. if (target.matches('.continue') || target.matches('.download-button')) return; diff --git a/src/components/FileImporter.js b/src/components/FileImporter.js index 3f8954a32..f9b45f1f0 100644 --- a/src/components/FileImporter.js +++ b/src/components/FileImporter.js @@ -46,10 +46,8 @@ class FileImporter extends Nimiq.Observable { this.$el = FileImporter._createElement($el); this._displayFile = displayFile; - /** @type {HTMLElement} */ - this.$errorMessage = (this.$el.querySelector('.error-message')); - /** @type {HTMLInputElement} */ - this.$fileInput = (this.$el.querySelector('input')); + this.$errorMessage = /** @type {HTMLElement} */ (this.$el.querySelector('.error-message')); + this.$fileInput = /** @type {HTMLInputElement} */ (this.$el.querySelector('input')); // Add drag-and-drop handlers this.$el.addEventListener('dragover', this._onDragOver.bind(this)); @@ -96,8 +94,7 @@ class FileImporter extends Nimiq.Observable { * @param {Event} event */ _onFileSelected(event) { - /** @type {HTMLInputElement} */ - const eventTarget = (event.target); + const eventTarget = /** @type {HTMLInputElement} */ (event.target); if (!eventTarget.files || !eventTarget.files.length) return; this._handleFile(eventTarget.files[0]); } diff --git a/src/components/FlippableHandler.js b/src/components/FlippableHandler.js index 63ad0d46e..3cdda5fb9 100644 --- a/src/components/FlippableHandler.js +++ b/src/components/FlippableHandler.js @@ -6,8 +6,7 @@ class FlippableHandler { */ static init(classname = 'flipped') { if (!FlippableHandler.flippableHandlerInitialised) { - /** @type {HTMLElement} */ - const $rotationContainer = (document.getElementById('rotation-container')); + const $rotationContainer = /** @type {HTMLElement} */ (document.getElementById('rotation-container')); if (window.location.hash) { const $page = document.querySelector(window.location.hash); if ($page) { @@ -68,8 +67,7 @@ class FlippableHandler { * Must be a child of `#rotation-container` */ static _updateContainerHeight($enforcedElement) { - /** @type {HTMLElement} */ - const $rotationContainer = (document.getElementById('rotation-container')); + const $rotationContainer = /** @type {HTMLElement} */ (document.getElementById('rotation-container')); if ($enforcedElement && $rotationContainer.contains($enforcedElement)) { $rotationContainer.style.height = `${$enforcedElement.clientHeight}px`; } else { diff --git a/src/components/Identicon.js b/src/components/Identicon.js index aa7ed4abf..724b4116c 100644 --- a/src/components/Identicon.js +++ b/src/components/Identicon.js @@ -10,8 +10,7 @@ class Identicon { // eslint-disable-line no-unused-vars this.$el = Identicon._createElement($el); - /** @type {HTMLImageElement} */ - this.$imgEl = (this.$el.firstChild); + this.$imgEl = /** @type {HTMLImageElement} */ (this.$el.firstChild); this._updateIqon(); } diff --git a/src/components/LoginFileAnimation.js b/src/components/LoginFileAnimation.js index d5c0d9044..d90b16d8b 100644 --- a/src/components/LoginFileAnimation.js +++ b/src/components/LoginFileAnimation.js @@ -9,8 +9,7 @@ class LoginFileAnimation { this._color = 0; this.$el = LoginFileAnimation._createElement($el); - /** @type {HTMLDivElement} */ - this.$background = (this.$el.querySelector('.background')); + this.$background = /** @type {HTMLDivElement} */ (this.$el.querySelector('.background')); } /** diff --git a/src/components/PasswordBox.js b/src/components/PasswordBox.js index 669a4d415..6ed953517 100644 --- a/src/components/PasswordBox.js +++ b/src/components/PasswordBox.js @@ -60,7 +60,7 @@ class PasswordBox extends Nimiq.Observable { } /** - * @param {HTMLFormElement} [$el] + * @param {HTMLFormElement | undefined} $el * @param {PasswordBoxOptions} options * @returns {HTMLFormElement} */ @@ -116,8 +116,7 @@ class PasswordBox extends Nimiq.Observable { `; /* eslint-enable max-len */ - /** @type {HTMLButtonElement} */ - const submitButton = ($el.querySelector('button.submit')); + const submitButton = /** @type {HTMLButtonElement} */ ($el.querySelector('button.submit')); submitButton.classList.add('nq-button', options.bgColor); submitButton.classList.toggle('inverse', !options.hideInput); diff --git a/src/components/PasswordSetterBox.js b/src/components/PasswordSetterBox.js index 3a19fc190..e2a6faf90 100644 --- a/src/components/PasswordSetterBox.js +++ b/src/components/PasswordSetterBox.js @@ -52,7 +52,7 @@ class PasswordSetterBox extends Nimiq.Observable { } /** - * @param {?HTMLFormElement} [$el] + * @param {?HTMLFormElement} $el * @param {{bgColor: string, buttonI18nTag: string}} options * @returns {HTMLFormElement} */ diff --git a/src/components/PaymentInfoLine.js b/src/components/PaymentInfoLine.js index a57f704b9..6c15c325e 100644 --- a/src/components/PaymentInfoLine.js +++ b/src/components/PaymentInfoLine.js @@ -38,8 +38,7 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars this.paymentInfo = paymentInfo; this.$el = PaymentInfoLine._createElement($el); - /** @type HTMLElement */ - const $amount = (this.$el.querySelector('.amount')); + const $amount = /** @type {HTMLElement} */ (this.$el.querySelector('.amount')); const amount = NumberFormatting.formatNumber( paymentInfo.unitsToCoins(paymentInfo.amount), paymentInfo.currency === 'nim' ? 4 : 7, @@ -56,8 +55,7 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars }); recipientInfo.renderTo(/** @type HTMLElement */ (this.$el.querySelector('.recipient'))); - /** @type HTMLElement */ - const $timer = (this.$el.querySelector('.timer')); + const $timer = /** @type {HTMLElement} */ (this.$el.querySelector('.timer')); if (paymentInfo.time && paymentInfo.expires) { new Timer(paymentInfo.time, paymentInfo.expires, $timer); // eslint-disable-line no-new } else { @@ -149,16 +147,15 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars $fiatAmount.textContent = formattedFiatAmount; }); - /** @type {HTMLTemplateElement} */ - const $vendorMarkupTemplate = ($tooltip.querySelector('.vendor-markup-template')); + const $vendorMarkupTemplate = /** @type {HTMLTemplateElement} */ ( + $tooltip.querySelector('.vendor-markup-template')); if (vendorMarkup !== undefined) { // Convert to percent and round to two decimals. Always ceil to avoid displaying a lower fee than charged or // larger discount than applied. Subtract small epsilon to avoid that numbers get rounded up as a result of // floating point imprecision after multiplication. Otherwise formatting for example .07 results in 7.01%. const vendorMarkupPercent = Math.ceil(vendorMarkup * 100 * 100 - 1e-10) / 100; $vendorMarkupTemplate.replaceWith($vendorMarkupTemplate.content); - /** @type {HTMLElement} */ - const $vendorMarkup = ($tooltip.querySelector('.vendor-markup')); + const $vendorMarkup = /** @type {HTMLElement} */ ($tooltip.querySelector('.vendor-markup')); $vendorMarkup.textContent = `${vendorMarkup >= 0 ? '+' : ''}${vendorMarkupPercent}%`; } else { $vendorMarkupTemplate.remove(); @@ -166,8 +163,7 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars const ticker = this.paymentInfo.currency.toUpperCase(); - /** @type {HTMLElement} */ - const $effectiveRate = ($tooltip.querySelector('.effective-rate')); + const $effectiveRate = /** @type {HTMLElement} */ ($tooltip.querySelector('.effective-rate')); // Fiat/crypto rate. Higher fiat/crypto rate means user is paying less crypto for the requested fiat amount // and is therefore better for the user. Note: precision loss should be acceptable here. const effectiveRate = fiatAmount / this.paymentInfo.unitsToCoins(amount); @@ -177,25 +173,21 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars 0.0001, )} / ${ticker}`; - /** @type {HTMLElement} */ - const $total = ($tooltip.querySelector('.total')); + const $total = /** @type {HTMLElement} */ ($tooltip.querySelector('.total')); $total.textContent = `${NumberFormatting.formatNumber(this.paymentInfo.unitsToCoins(amount))} ${ticker}`; // Note that in the Keyguard the fee is never undefined. if (networkFee !== 0) { - /** @type {HTMLElement} */ - const $networkFee = ($tooltip.querySelector('.network-fee')); + const $networkFee = /** @type {HTMLElement} */ ($tooltip.querySelector('.network-fee')); $networkFee.textContent = `${NumberFormatting.formatNumber( this.paymentInfo.unitsToCoins(networkFee), )} ${ticker}`; } else { - /** @type {HTMLElement} */ - const $networkFeeInfo = ($tooltip.querySelector('.network-fee-info')); + const $networkFeeInfo = /** @type {HTMLElement} */ ($tooltip.querySelector('.network-fee-info')); $networkFeeInfo.remove(); } - /** @type {HTMLElement} */ - const $rateInfo = ($tooltip.querySelector('.rate-info')); + const $rateInfo = /** @type {HTMLElement} */ ($tooltip.querySelector('.rate-info')); const updateRateComparison = this._updateRateComparison.bind(this, effectiveRate, $tooltip, $rateInfo); updateRateComparison(); window.setInterval(updateRateComparison, PaymentInfoLine.REFERENCE_RATE_UPDATE_INTERVAL); @@ -212,8 +204,7 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars */ async _updateRateComparison(effectiveRate, $tooltip, $rateInfo) { if (!this.paymentInfo.fiatCurrency) return; - /** @type {FiatApi.SupportedFiatCurrency} */ - const fiatCurrency = (this.paymentInfo.fiatCurrency.toLowerCase()); + const fiatCurrency = /** @type {FiatApi.SupportedFiatCurrency} */ (this.paymentInfo.fiatCurrency.toLowerCase()); if (!Object.values(FiatApi.SupportedFiatCurrency).includes(fiatCurrency)) return; let referenceRate; diff --git a/src/components/ProgressIndicator.js b/src/components/ProgressIndicator.js index 91c3cfaf6..a43afa44b 100644 --- a/src/components/ProgressIndicator.js +++ b/src/components/ProgressIndicator.js @@ -14,7 +14,7 @@ class ProgressIndicator extends Nimiq.Observable { // eslint-disable-line no-unu } /** - * @param {?HTMLElement} [$el] + * @param {?HTMLElement} $el * @param {number} numberOfSteps * @returns {HTMLElement} */ diff --git a/src/components/QrVideoScanner.js b/src/components/QrVideoScanner.js index 1f1a11dbd..880a9360d 100644 --- a/src/components/QrVideoScanner.js +++ b/src/components/QrVideoScanner.js @@ -24,14 +24,9 @@ class QrVideoScanner extends Nimiq.Observable { this.$el = QrVideoScanner._createElement($el); - /** @type {HTMLVideoElement} */ - const $video = (this.$el.querySelector('video')); - - /** @type {HTMLDivElement} */ - this.$overlay = (this.$el.querySelector('.overlay')); - - /** @type {HTMLButtonElement} */ - const $cancelButton = (this.$el.querySelector('.cancel-button')); + const $video = /** @type {HTMLVideoElement} */ (this.$el.querySelector('video')); + this.$overlay = /** @type {HTMLDivElement} */ (this.$el.querySelector('.overlay')); + const $cancelButton = /** @type {HTMLButtonElement} */ (this.$el.querySelector('.cancel-button')); this._scanner = new QrScanner($video, result => this._onResult(result)); diff --git a/src/components/RecoveryWords.js b/src/components/RecoveryWords.js index 5c2066981..0f91fee57 100644 --- a/src/components/RecoveryWords.js +++ b/src/components/RecoveryWords.js @@ -8,7 +8,7 @@ class RecoveryWords extends Nimiq.Observable { /** * * @param {HTMLElement} [$el] - * @param {boolean} providesInput + * @param {boolean} [providesInput] */ constructor($el, providesInput) { super(); @@ -141,7 +141,8 @@ class RecoveryWords extends Nimiq.Observable { this._mnemonic = { words: mnemonic, type }; this.fire(RecoveryWords.Events.COMPLETE, mnemonic, type); } catch (e) { - if (e.message !== 'Invalid checksum') { + const err = /** @type {Error} */ (e); + if (err.message !== 'Invalid checksum') { console.error(e); // eslint-disable-line no-console } else { // wrong words diff --git a/src/components/SwapFeesTooltip.js b/src/components/SwapFeesTooltip.js index 2c14e48ec..7c71b2d0b 100644 --- a/src/components/SwapFeesTooltip.js +++ b/src/components/SwapFeesTooltip.js @@ -24,12 +24,10 @@ class SwapFeesTooltip { // eslint-disable-line no-unused-vars this.$el = SwapFeesTooltip._createElement(); - /** @private - * @type {SVGCircleElement} */ - this.$tooltip = (this.$el.querySelector('.tooltip-box')); - /** @private - * @type {SVGCircleElement} */ - this.$fees = (this.$el.querySelector('.fees')); + /** @private */ + this.$tooltip = /** @type {SVGCircleElement} */ (this.$el.querySelector('.tooltip-box')); + /** @private */ + this.$fees = /** @type {SVGCircleElement} */ (this.$el.querySelector('.fees')); let totalFiatFees = 0; diff --git a/src/components/Timer.js b/src/components/Timer.js index 4f289c26e..3e2dc87a7 100644 --- a/src/components/Timer.js +++ b/src/components/Timer.js @@ -21,18 +21,14 @@ class Timer extends Nimiq.Observable { super(); this.$el = Timer._createElement($el); - /** @private - * @type {SVGCircleElement} */ - this.$timeCircle = (this.$el.querySelector('.time-circle')); - /** @private - * @type {SVGCircleElement} */ - this.$fillerCircle = (this.$el.querySelector('.filler-circle')); - /** @private - * @type {SVGTextElement} */ - this.$countdown = (this.$el.querySelector('.countdown')); - /** @private - * @type {HTMLDivElement} */ - this.$tooltipCountdown = (this.$el.querySelector('.tooltip-countdown')); + /** @private */ + this.$timeCircle = /** @type {SVGCircleElement} */ (this.$el.querySelector('.time-circle')); + /** @private */ + this.$fillerCircle = /** @type {SVGCircleElement} */ (this.$el.querySelector('.filler-circle')); + /** @private */ + this.$countdown = /** @type {SVGTextElement} */ (this.$el.querySelector('.countdown')); + /** @private */ + this.$tooltipCountdown = /** @type {HTMLDivElement} */ (this.$el.querySelector('.tooltip-countdown')); /** @private * @type {number} */ diff --git a/src/components/ValidateWords.js b/src/components/ValidateWords.js index f0a650835..4b4eda02e 100644 --- a/src/components/ValidateWords.js +++ b/src/components/ValidateWords.js @@ -25,8 +25,7 @@ class ValidateWords extends Nimiq.Observable { this.$buttons = this.$el.querySelectorAll('button'); this.$targetIndex = /** @type {HTMLElement} */ (this.$el.querySelector('.target-index')); this.$el.addEventListener('click', this._onClick.bind(this)); - /** @type {HTMLElement} */ - this.$textHint = (this.$el.querySelector('p')); + this.$textHint = /** @type {HTMLElement} */ (this.$el.querySelector('p')); } /** diff --git a/src/lib/AnimationUtils.js b/src/lib/AnimationUtils.js index 3985548f5..3c7b8ee96 100644 --- a/src/lib/AnimationUtils.js +++ b/src/lib/AnimationUtils.js @@ -14,7 +14,7 @@ class AnimationUtils { // eslint-disable-line no-unused-vars if (beforeEndCallback instanceof Function) beforeEndCallback(); this.stopAnimate(className, el); el.removeEventListener('animationend', listener); - resolve(); + resolve(undefined); }; el.addEventListener('animationend', listener); el.classList.add(className); diff --git a/src/lib/BrowserDetection.js b/src/lib/BrowserDetection.js index a96e02284..06800287d 100644 --- a/src/lib/BrowserDetection.js +++ b/src/lib/BrowserDetection.js @@ -66,6 +66,7 @@ class BrowserDetection { // eslint-disable-line no-unused-vars static isTouchDevice() { return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) - || (navigator.msMaxTouchPoints > 0)); + || ('msMaxTouchPoints' in navigator + && /** @type {{ msMaxTouchPoints: number }} */ (navigator).msMaxTouchPoints > 0)); } } diff --git a/src/lib/CurrencyInfo.js b/src/lib/CurrencyInfo.js index 86598681e..173106eaa 100644 --- a/src/lib/CurrencyInfo.js +++ b/src/lib/CurrencyInfo.js @@ -1,12 +1,17 @@ // Adapted and simplified from @nimiq/utils. // See there for extended documentation on how the data was generated. // This file should also be updated whenever CurrencyInfo in @nimiq/utils is updated. + +/** + * @typedef {Intl.NumberFormatOptions & { currencyDisplay: string }} NumberFormatOptions + */ + class CurrencyInfo { /** * @private * @param {number} value * @param {string | string[]} [locales] - * @param {Intl.NumberFormatOptions} [options] + * @param {NumberFormatOptions} [options] * @returns {string | null} */ static _failsafeNumberToLocaleString(value, locales, options) { diff --git a/src/lib/KeyStore.js b/src/lib/KeyStore.js index cb64eb84f..20c06e281 100644 --- a/src/lib/KeyStore.js +++ b/src/lib/KeyStore.js @@ -230,7 +230,7 @@ class KeyStore { await new Promise(async resolve => { setTimeout(resolve, 2000); // Wait 2s and then just continue await AccountStore.instance.drop(); - resolve(); + resolve(undefined); }); if (BrowserDetection.isIOS() || BrowserDetection.isSafari()) { @@ -309,7 +309,7 @@ class KeyStore { request.onerror = () => reject(request.error); }), new Promise((resolve, reject) => { - transaction.oncomplete = () => resolve(); + transaction.oncomplete = () => resolve(undefined); transaction.onabort = () => reject(transaction.error); transaction.onerror = () => reject(transaction.error); }), diff --git a/src/lib/LoginFile.js b/src/lib/LoginFile.js index 742ba293a..fc651b86f 100644 --- a/src/lib/LoginFile.js +++ b/src/lib/LoginFile.js @@ -20,8 +20,7 @@ class LoginFile { this._label = label && label.trim() ? label.trim() : I18n.translatePhrase('login-file-default-account-label').replace('{color}', this._config.name); - /** @type {CanvasRenderingContext2D} */ - this._ctx = ($canvas.getContext('2d')); + this._ctx = /** @type {CanvasRenderingContext2D} */ ($canvas.getContext('2d')); this._drawPromise = this._draw(encodedSecret); } @@ -68,6 +67,7 @@ class LoginFile { return new Promise(resolve => { this.$canvas.toBlob(blob => { + if (!blob) throw new Error('Cannot generate URL'); const url = URL.createObjectURL(blob); resolve(url); }); @@ -141,7 +141,7 @@ class LoginFile { this._ctx.fillText(this._getLabelForDisplay(), x, y, LoginFile.WIDTH - LoginFile.BORDER_WIDTH * 4); // reset filter, composite operation and font this._ctx.filter = ''; - this._ctx.globalCompositeOperation = ''; + this._ctx.globalCompositeOperation = 'source-over'; // This is the default according to MDN this._setFont(); } diff --git a/src/lib/QrEncoder.js b/src/lib/QrEncoder.js index 4e73bd72e..5a04ce725 100644 --- a/src/lib/QrEncoder.js +++ b/src/lib/QrEncoder.js @@ -14,8 +14,7 @@ class QrEncoder { * @param {QrEncoderConfig} options */ static _qrCodeGenerator(options) { - /** @type {QrEncoderSettings} */ - const settings = ({}); + const settings = /** @type {QrEncoderSettings} */ ({}); Object.assign(settings, this.defaults, options); return this._createCanvas(settings); } @@ -248,8 +247,7 @@ class QrEncoder { return null; } - /** @type {CanvasRenderingContext2D} */ - const context = (canvas.getContext('2d')); + const context = /** @type {CanvasRenderingContext2D} */ (canvas.getContext('2d')); this._drawBackground(qr, context, settings); this._drawModules(qr, context, settings); diff --git a/src/lib/QrScanner.js b/src/lib/QrScanner.js index 3b064d8c5..ff1b24291 100644 --- a/src/lib/QrScanner.js +++ b/src/lib/QrScanner.js @@ -342,14 +342,15 @@ class QrScanner { this._qrEnginePromise, this.$canvas, ); - } catch (error) { + } catch (err) { if (!this._active) return; - const errorMessage = error.message || error; + const error = /** @type {Error | string} */ (err); + const errorMessage = typeof error === 'string' ? error : error.message; if (errorMessage.includes('service unavailable')) { // When the native BarcodeDetector crashed, create a new one this._qrEnginePromise = QrScanner.createQrEngine(); } - this._onDecodeError(error); + this._onDecodeError(errorMessage); } if (result) { diff --git a/src/lib/RequestParser.js b/src/lib/RequestParser.js index d0563b8c4..0555ab1c6 100644 --- a/src/lib/RequestParser.js +++ b/src/lib/RequestParser.js @@ -184,7 +184,8 @@ class RequestParser { // eslint-disable-line no-unused-vars flags, data, ); - } catch (error) { + } catch (err) { + const error = /** @type {Error} */ (err); throw new Errors.InvalidRequestError(error); } } @@ -204,7 +205,8 @@ class RequestParser { // eslint-disable-line no-unused-vars return Nimiq.Address.fromString(address); } return new Nimiq.Address(address); - } catch (error) { + } catch (err) { + const error = /** @type {Error} */ (err); throw new Errors.InvalidRequestError(`${name} must be a valid Nimiq address (${error.message})`); } } @@ -240,7 +242,8 @@ class RequestParser { // eslint-disable-line no-unused-vars try { const parsedUrl = this._parseUrl(url, 'shopOrigin'); return parsedUrl.origin; - } catch (error) { + } catch (err) { + const error = /** @type {Error} */ (err); throw new Errors.InvalidRequestError(error); } } @@ -256,7 +259,8 @@ class RequestParser { // eslint-disable-line no-unused-vars } try { return this._parseUrl(url, 'shopLogoUrl'); - } catch (error) { + } catch (err) { + const error = /** @type {Error} */ (err); throw new Errors.InvalidRequestError(error); } } diff --git a/src/lib/RpcServer.es.js b/src/lib/RpcServer.es.js index e10d68c04..7c97054c7 100644 --- a/src/lib/RpcServer.es.js +++ b/src/lib/RpcServer.es.js @@ -314,7 +314,8 @@ class RpcServer { // eslint-disable-line no-unused-vars RpcServer._ok(state, result); } return true; - } catch (error) { + } catch (err) { + const error = /** @type {Error} */ (err); if (_state) { RpcServer._error(_state, error); } diff --git a/src/lib/polygon/PolygonRequestParserMixin.js b/src/lib/polygon/PolygonRequestParserMixin.js index 986f43186..35de47489 100644 --- a/src/lib/polygon/PolygonRequestParserMixin.js +++ b/src/lib/polygon/PolygonRequestParserMixin.js @@ -49,8 +49,7 @@ function PolygonRequestParserMixin(clazz) { // eslint-disable-line no-unused-var if (typeof request !== 'object' || request === null) { throw new Errors.InvalidRequestError('request must be an object'); } - /** @type {KeyguardRequest.OpenGsnForwardRequest} */ - const forwardRequest = (request); + const forwardRequest = /** @type {KeyguardRequest.OpenGsnForwardRequest} */ (request); this.parsePolygonAddress(forwardRequest.from, 'request.from'); this.parsePolygonAddress(forwardRequest.to, 'request.to'); @@ -76,8 +75,7 @@ function PolygonRequestParserMixin(clazz) { // eslint-disable-line no-unused-var if (typeof relayData !== 'object' || relayData === null) { throw new Errors.InvalidRequestError('relayData must be an object'); } - /** @type {KeyguardRequest.OpenGsnRelayData} */ - const parsedRelayData = (relayData); + const parsedRelayData = /** @type {KeyguardRequest.OpenGsnRelayData} */ (relayData); this.parsePolygonAddress(parsedRelayData.forwarder, 'relayData.forwarder'); this.parsePolygonAddress(parsedRelayData.paymaster, 'relayData.paymaster'); diff --git a/src/request/TopLevelApi.js b/src/request/TopLevelApi.js index 5368c1af9..56ed84292 100644 --- a/src/request/TopLevelApi.js +++ b/src/request/TopLevelApi.js @@ -232,10 +232,10 @@ class TopLevelApi extends RequestParser { * @param {string|Parsed} requestOrCustomButtonText */ enableGlobalCloseButton(requestOrCustomButtonText) { - /** @type {HTMLElement} */ - const $globalCloseText = (document.querySelector('#global-close-text')); - /** @type {HTMLSpanElement} */ - const $button = ($globalCloseText.parentNode); + const $globalCloseText = /** @type {HTMLElement} */ ( + document.querySelector('#global-close-text')); + const $button = /** @type {HTMLSpanElement} */ ( + $globalCloseText.parentNode); if (!$button.classList.contains('display-none')) return; // eslint-disable-next-line require-jsdoc-except/require-jsdoc @@ -305,8 +305,8 @@ class TopLevelApi extends RequestParser { static showNoRequestErrorPage() { const errorPage = new NoRequestErrorPage(); - /** @type {HTMLDivElement} */ - const $target = (document.querySelector('#rotation-container') || document.querySelector('#app')); + const $target = /** @type {HTMLDivElement} */ ( + document.querySelector('#rotation-container') || document.querySelector('#app')); $target.appendChild(errorPage.getElement()); window.location.hash = 'error'; TopLevelApi.setLoading(false); diff --git a/src/request/change-password/ChangePassword.js b/src/request/change-password/ChangePassword.js index 6a05708e6..0a59c3e3e 100644 --- a/src/request/change-password/ChangePassword.js +++ b/src/request/change-password/ChangePassword.js @@ -32,26 +32,26 @@ class ChangePassword { this._key = null; // Pages - /** @type {HTMLFormElement} */ - const $enterPassword = (document.getElementById(ChangePassword.Pages.ENTER_PASSWORD)); - /** @type {HTMLFormElement} */ - const $setPassword = (document.getElementById(ChangePassword.Pages.SET_PASSWORD)); - /** @type {HTMLFormElement} */ - const $downloadFile = (document.getElementById(ChangePassword.Pages.DOWNLOAD_FILE)); + const $enterPassword = /** @type {HTMLFormElement} */ ( + document.getElementById(ChangePassword.Pages.ENTER_PASSWORD)); + const $setPassword = /** @type {HTMLFormElement} */ ( + document.getElementById(ChangePassword.Pages.SET_PASSWORD)); + const $downloadFile = /** @type {HTMLFormElement} */ ( + document.getElementById(ChangePassword.Pages.DOWNLOAD_FILE)); // Elements - /** @type {HTMLFormElement} */ - const $passwordGetter = ($enterPassword.querySelector('.password-box')); - /** @type {HTMLFormElement} */ - const $passwordSetter = ($setPassword.querySelector('.password-setter-box')); - /** @type {HTMLDivElement} */ - const $loginFileIcon = ($setPassword.querySelector('.login-file-icon')); - /** @type {HTMLLinkElement} */ - this.$setPasswordBackButton = ($setPassword.querySelector('a.page-header-back-button')); - /** @type {HTMLDivElement} */ - const $downloadLoginFile = ($downloadFile.querySelector('.download-login-file')); - /** @type {HTMLLinkElement} */ - this.$skipDownloadButton = ($downloadFile.querySelector('.skip')); + const $passwordGetter = /** @type {HTMLFormElement} */ ( + $enterPassword.querySelector('.password-box')); + const $passwordSetter = /** @type {HTMLFormElement} */ ( + $setPassword.querySelector('.password-setter-box')); + const $loginFileIcon = /** @type {HTMLDivElement} */ ( + $setPassword.querySelector('.login-file-icon')); + this.$setPasswordBackButton = /** @type {HTMLLinkElement} */ ( + $setPassword.querySelector('a.page-header-back-button')); + const $downloadLoginFile = /** @type {HTMLDivElement} */ ( + $downloadFile.querySelector('.download-login-file')); + this.$skipDownloadButton = /** @type {HTMLLinkElement} */ ( + $downloadFile.querySelector('.skip')); // Components this._passwordSetter = new PasswordSetterBox($passwordSetter); @@ -136,7 +136,8 @@ class ChangePassword { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, oldPasswordBytes); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordGetter.onPasswordIncorrect(); diff --git a/src/request/create/Create.js b/src/request/create/Create.js index a6293eff4..0d70701d5 100644 --- a/src/request/create/Create.js +++ b/src/request/create/Create.js @@ -32,31 +32,28 @@ class Create { FlippableHandler.init(); - /** @type {HTMLDivElement} */ - const $loginFileFan = (document.querySelector('.login-file-fan')); - - /** @type {HTMLButtonElement} */ - const $startButton = (document.querySelector('.start')); - - /** @type {HTMLDivElement} */ - this.$loginFileAnimation = (document.querySelector('.login-file-animation')); - - /** @type {HTMLFormElement} */ - const $setPassword = (document.querySelector('.password-box')); - - /** @type {HTMLFormElement} */ - this.$setPasswordPage = (document.getElementById('set-password')); - - /** @type {HTMLElement} */ - const $downloadFilePage = (document.getElementById(Create.Pages.LOGIN_FILE_DOWNLOAD)); - /** @type {HTMLDivElement} */ - const $downloadLoginFile = (document.querySelector('.download-loginfile')); - - /** @type {HTMLImageElement} */ - const $loginFilePreviewImage = (document.getElementById('loginfile-preview')); - - /** @type {HTMLButtonElement} */ - const $loginFileExplainerBackButton = (document.getElementById('loginfile-explainer-go-back')); + const $loginFileFan = /** @type {HTMLDivElement} */ ( + document.querySelector('.login-file-fan')); + const $startButton = /** @type {HTMLButtonElement} */ ( + document.querySelector('.start')); + + this.$loginFileAnimation = /** @type {HTMLDivElement} */ ( + document.querySelector('.login-file-animation')); + + const $setPassword = /** @type {HTMLFormElement} */ ( + document.querySelector('.password-box')); + this.$setPasswordPage = /** @type {HTMLFormElement} */ ( + document.getElementById('set-password')); + + const $downloadFilePage = /** @type {HTMLElement} */ ( + document.getElementById(Create.Pages.LOGIN_FILE_DOWNLOAD)); + const $downloadLoginFile = /** @type {HTMLDivElement} */ ( + document.querySelector('.download-loginfile')); + + const $loginFilePreviewImage = /** @type {HTMLImageElement} */ ( + document.getElementById('loginfile-preview')); + const $loginFileExplainerBackButton = /** @type {HTMLButtonElement} */ ( + document.getElementById('loginfile-explainer-go-back')); // Create components Promise.all([ diff --git a/src/request/create/IdenticonSelector.js b/src/request/create/IdenticonSelector.js index bd47fbd9b..e93d888b9 100644 --- a/src/request/create/IdenticonSelector.js +++ b/src/request/create/IdenticonSelector.js @@ -5,7 +5,7 @@ class IdenticonSelector extends Nimiq.Observable { /** - * @param {HTMLElement} [$el] + * @param {HTMLElement} $el * @param {string} keyPath */ constructor($el, keyPath) { diff --git a/src/request/derive-address/DeriveAddress.js b/src/request/derive-address/DeriveAddress.js index 7bfa8b041..b3a28371c 100644 --- a/src/request/derive-address/DeriveAddress.js +++ b/src/request/derive-address/DeriveAddress.js @@ -20,8 +20,7 @@ class DeriveAddress { this._resolve = resolve; this._reject = reject; - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('.password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('.password-box')); // Create components @@ -51,7 +50,8 @@ class DeriveAddress { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); diff --git a/src/request/derive-btc-xpub/DeriveBtcXPub.js b/src/request/derive-btc-xpub/DeriveBtcXPub.js index 2cb00288a..2aeb6b58e 100644 --- a/src/request/derive-btc-xpub/DeriveBtcXPub.js +++ b/src/request/derive-btc-xpub/DeriveBtcXPub.js @@ -21,8 +21,7 @@ class DeriveBtcXPub { this._resolve = resolve; this._reject = reject; - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('.password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('.password-box')); // Create components @@ -52,7 +51,8 @@ class DeriveBtcXPub { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); diff --git a/src/request/derive-polygon-address/DerivePolygonAddress.js b/src/request/derive-polygon-address/DerivePolygonAddress.js index 4d92d60c2..770925561 100644 --- a/src/request/derive-polygon-address/DerivePolygonAddress.js +++ b/src/request/derive-polygon-address/DerivePolygonAddress.js @@ -21,8 +21,7 @@ class DerivePolygonAddress { this._resolve = resolve; this._reject = reject; - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('.password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('.password-box')); // Create components @@ -50,7 +49,8 @@ class DerivePolygonAddress { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); diff --git a/src/request/export/Export.js b/src/request/export/Export.js index db5e92442..f657663df 100644 --- a/src/request/export/Export.js +++ b/src/request/export/Export.js @@ -42,18 +42,18 @@ class Export { .classList.add('display-none'); } - /** @type {HTMLDivElement} */ - this._fileSuccessPage = (document.getElementById(Export.Pages.LOGIN_FILE_SUCCESS)); + this._fileSuccessPage = /** @type {HTMLDivElement} */ ( + document.getElementById(Export.Pages.LOGIN_FILE_SUCCESS)); - /** @type {HTMLLinkElement} */ - const skipWordsButton = (this._fileSuccessPage.querySelector('.skip')); + const skipWordsButton = /** @type {HTMLLinkElement} */ ( + this._fileSuccessPage.querySelector('.skip')); skipWordsButton.addEventListener('click', e => { e.preventDefault(); this._resolve(this.exported); }); - /** @type {HTMLButtonElement} */ - const goToWordsButton = (this._fileSuccessPage.querySelector('.page-footer > button')); + const goToWordsButton = /** @type {HTMLButtonElement} */ ( + this._fileSuccessPage.querySelector('.page-footer > button')); goToWordsButton.addEventListener('click', () => { this._exportWordsHandler.run(); }); diff --git a/src/request/export/ExportFile.js b/src/request/export/ExportFile.js index 8b66e83d0..e34152544 100644 --- a/src/request/export/ExportFile.js +++ b/src/request/export/ExportFile.js @@ -33,27 +33,27 @@ class ExportFile extends Nimiq.Observable { /** @type {Key?} */ this._key = null; - /** @type {HTMLElement} */ - this.$exportFileIntroPage = (document.getElementById(ExportFile.Pages.LOGIN_FILE_INTRO)); - /** @type {HTMLElement} */ - const $unlockFilePage = (document.getElementById(ExportFile.Pages.LOGIN_FILE_UNLOCK)); - /** @type {HTMLElement} */ - const $setPasswordPage = (document.getElementById(ExportFile.Pages.LOGIN_FILE_SET_PASSWORD)); - /** @type {HTMLElement} */ - this.$downloadFilePage = (document.getElementById(ExportFile.Pages.LOGIN_FILE_DOWNLOAD)); - - /** @type {HTMLButtonElement} */ - const $fileButton = (this.$exportFileIntroPage.querySelector('.login-file')); - /** @type {HTMLDivElement} */ - const $loginFileIcon = ($setPasswordPage.querySelector('.login-file-icon')); - /** @type {HTMLFormElement} */ - const $passwordBox = ($unlockFilePage.querySelector('.password-box')); - /** @type {HTMLLinkElement} */ - this.$setPasswordBackButton = ($setPasswordPage.querySelector('a.page-header-back-button')); - /** @type {HTMLFormElement} */ - const $passwordSetterBox = ($setPasswordPage.querySelector('.password-setter-box')); - /** @type {HTMLDivElement} */ - const $downloadLoginFile = (this.$downloadFilePage.querySelector('.download-loginfile')); + this.$exportFileIntroPage = /** @type {HTMLElement} */ ( + document.getElementById(ExportFile.Pages.LOGIN_FILE_INTRO)); + const $unlockFilePage = /** @type {HTMLElement} */ ( + document.getElementById(ExportFile.Pages.LOGIN_FILE_UNLOCK)); + const $setPasswordPage = /** @type {HTMLElement} */ ( + document.getElementById(ExportFile.Pages.LOGIN_FILE_SET_PASSWORD)); + this.$downloadFilePage = /** @type {HTMLElement} */ ( + document.getElementById(ExportFile.Pages.LOGIN_FILE_DOWNLOAD)); + + const $fileButton = /** @type {HTMLButtonElement} */ ( + this.$exportFileIntroPage.querySelector('.login-file')); + const $loginFileIcon = /** @type {HTMLDivElement} */ ( + $setPasswordPage.querySelector('.login-file-icon')); + const $passwordBox = /** @type {HTMLFormElement} */ ( + $unlockFilePage.querySelector('.password-box')); + this.$setPasswordBackButton = /** @type {HTMLLinkElement} */ ( + $setPasswordPage.querySelector('a.page-header-back-button')); + const $passwordSetterBox = /** @type {HTMLFormElement} */ ( + $setPasswordPage.querySelector('.password-setter-box')); + const $downloadLoginFile = /** @type {HTMLDivElement} */ ( + this.$downloadFilePage.querySelector('.download-loginfile')); this._passwordBox = new PasswordBox( $passwordBox, { @@ -161,7 +161,8 @@ class ExportFile extends Nimiq.Observable { let key = this._key; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); @@ -193,7 +194,8 @@ class ExportFile extends Nimiq.Observable { if (!key || !key.id) { try { key = await KeyStore.instance.get(this._request.keyInfo.id); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); diff --git a/src/request/export/ExportWords.js b/src/request/export/ExportWords.js index d8900d1c5..1690297a4 100644 --- a/src/request/export/ExportWords.js +++ b/src/request/export/ExportWords.js @@ -35,26 +35,26 @@ class ExportWords extends Nimiq.Observable { this._key = null; // pages - /** @type {HTMLElement} */ - this._$noRecoveryPage = (document.getElementById(ExportWords.Pages.RECOVERY_WORDS_INTRO)); - /** @type {HTMLElement} */ - this._$recoveryWordsUnlockPage = (document.getElementById(ExportWords.Pages.RECOVERY_WORDS_UNLOCK)); - /** @type {HTMLElement} */ - this._$recoveryWordsPage = (document.getElementById(ExportWords.Pages.SHOW_WORDS)); - /** @type {HTMLElement} */ - this._$validateWordsPage = (document.getElementById(ExportWords.Pages.VALIDATE_WORDS)); + this._$noRecoveryPage = /** @type {HTMLElement} */ ( + document.getElementById(ExportWords.Pages.RECOVERY_WORDS_INTRO)); + this._$recoveryWordsUnlockPage = /** @type {HTMLElement} */ ( + document.getElementById(ExportWords.Pages.RECOVERY_WORDS_UNLOCK)); + this._$recoveryWordsPage = /** @type {HTMLElement} */ ( + document.getElementById(ExportWords.Pages.SHOW_WORDS)); + this._$validateWordsPage = /** @type {HTMLElement} */ ( + document.getElementById(ExportWords.Pages.VALIDATE_WORDS)); // elements - /** @type {HTMLButtonElement} */ - const $recoveryWordsIntroButton = (this._$noRecoveryPage.querySelector('.page-footer > button')); - /** @type {HTMLFormElement} */ - const $wordsPasswordBox = (this._$recoveryWordsUnlockPage.querySelector('.password-box')); - /** @type {HTMLElement} */ - this.$recoveryWords = (this._$recoveryWordsPage.querySelector('.recovery-words')); - /** @type {HTMLButtonElement} */ - this.$recoveryWordsContinue = (this._$recoveryWordsPage.querySelector('button')); - /** @type {HTMLElement} */ - const $validateWords = (this._$validateWordsPage.querySelector('.validate-words')); + const $recoveryWordsIntroButton = /** @type {HTMLButtonElement} */ ( + this._$noRecoveryPage.querySelector('.page-footer > button')); + const $wordsPasswordBox = /** @type {HTMLFormElement} */ ( + this._$recoveryWordsUnlockPage.querySelector('.password-box')); + this.$recoveryWords = /** @type {HTMLElement} */ ( + this._$recoveryWordsPage.querySelector('.recovery-words')); + this.$recoveryWordsContinue = /** @type {HTMLButtonElement} */ ( + this._$recoveryWordsPage.querySelector('button')); + const $validateWords = /** @type {HTMLElement} */ ( + this._$validateWordsPage.querySelector('.validate-words')); // components this._wordsPasswordBox = new PasswordBox($wordsPasswordBox, { @@ -112,7 +112,8 @@ class ExportWords extends Nimiq.Observable { /** @type {Uint8Array} */ (passwordBuffer), ) : await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { this._wordsPasswordBox.onPasswordIncorrect(); TopLevelApi.setLoading(false); diff --git a/src/request/import/ImportFile.js b/src/request/import/ImportFile.js index 647cac64e..f861ff358 100644 --- a/src/request/import/ImportFile.js +++ b/src/request/import/ImportFile.js @@ -39,29 +39,24 @@ class ImportFile { this.importWordsHandler = new ImportWords(request, resolve, reject); - /** @type {HTMLElement} */ - this.$importFilePage = (document.getElementById(ImportFile.Pages.IMPORT_FILE)); - /** @type {HTMLElement} */ - this.$unlockAccountPage = (document.getElementById(ImportFile.Pages.UNLOCK_ACCOUNT)); + this.$importFilePage = /** @type {HTMLElement} */ (document.getElementById(ImportFile.Pages.IMPORT_FILE)); + this.$unlockAccountPage = /** @type {HTMLElement} */ (document.getElementById(ImportFile.Pages.UNLOCK_ACCOUNT)); if (request.isKeyLost) { - /** @type {HTMLElement} */ - (this.$importFilePage.querySelector('.login-to-continue')).classList.remove('display-none'); + const $link = /** @type {HTMLElement} */ ( + this.$importFilePage.querySelector('.login-to-continue')); + $link.classList.remove('display-none'); } - /** @type {HTMLLabelElement} */ - const $fileImport = (this.$importFilePage.querySelector('.file-import')); + const $fileImport = /** @type {HTMLLabelElement} */ ( + this.$importFilePage.querySelector('.file-import')); const fileImport = new FileImporter($fileImport, false); - /** @type {HTMLButtonElement} */ - this.$qrVideoButton = (this.$importFilePage.querySelector('.qr-video-button')); - - /** @type {HTMLDivElement} */ - this.$qrVideoScanner = (this.$importFilePage.querySelector('.qr-video-scanner')); + this.$qrVideoButton = /** @type {HTMLButtonElement} */ (this.$importFilePage.querySelector('.qr-video-button')); + this.$qrVideoScanner = /** @type {HTMLDivElement} */ (this.$importFilePage.querySelector('.qr-video-scanner')); this.qrVideoScanner = new QrVideoScanner(this.$qrVideoScanner, FileImporter.isLoginFileData); - /** @type {HTMLElement} */ - const $gotoWords = (this.$importFilePage.querySelector('#goto-words')); + const $gotoWords = /** @type {HTMLElement} */ (this.$importFilePage.querySelector('#goto-words')); $gotoWords.addEventListener('click', () => { this.importWordsHandler.run(); }); const $gotoCreate = this.$importFilePage.querySelector('#goto-create'); @@ -69,11 +64,11 @@ class ImportFile { $gotoCreate.addEventListener('click', this._goToCreate.bind(this)); } - /** @type {HTMLImageElement} */ - this.$loginFileImage = (this.$unlockAccountPage.querySelector('.loginfile-image')); + this.$loginFileImage = /** @type {HTMLImageElement} */ ( + this.$unlockAccountPage.querySelector('.loginfile-image')); - /** @type {HTMLFormElement} */ - const $passwordBox = (this.$unlockAccountPage.querySelector('.password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ ( + this.$unlockAccountPage.querySelector('.password-box')); this.passwordBox = new PasswordBox( $passwordBox, { buttonI18nTag: 'passwordbox-log-in' }, @@ -214,8 +209,9 @@ class ImportFile { } else { throw new Error(`Unknown key type ${key.type}`); } - } catch (error) { - this._reject(new Errors.KeyguardError(error.message || error)); + } catch (e) { + const err = /** @type {Error | string} */ (e); + this._reject(new Errors.KeyguardError(typeof err === 'string' ? err : err.message)); return; } diff --git a/src/request/import/ImportWords.js b/src/request/import/ImportWords.js index 331067463..cfa2715c7 100644 --- a/src/request/import/ImportWords.js +++ b/src/request/import/ImportWords.js @@ -45,26 +45,24 @@ class ImportWords { this._encryptedSecret = null; // Pages - /** @type {HTMLFormElement} */ - this.$words = (document.getElementById(ImportWords.Pages.ENTER_WORDS)); - /** @type {HTMLFormElement} */ - this.$setPassword = (document.getElementById(ImportWords.Pages.SET_PASSWORD)); - /** @type {HTMLFormElement} */ - const $downloadFile = (document.getElementById(ImportWords.Pages.DOWNLOAD_LOGINFILE)); + this.$words = /** @type {HTMLFormElement} */ (document.getElementById(ImportWords.Pages.ENTER_WORDS)); + this.$setPassword = /** @type {HTMLFormElement} */ (document.getElementById(ImportWords.Pages.SET_PASSWORD)); + const $downloadFile = /** @type {HTMLFormElement} */ ( + document.getElementById(ImportWords.Pages.DOWNLOAD_LOGINFILE)); // Elements - /** @type {HTMLFormElement} */ - const $recoveryWords = (this.$words.querySelector('.recovery-words')); - /** @type {HTMLLinkElement} */ - this.$setPasswordBackButton = (this.$setPassword.querySelector('a.page-header-back-button')); - /** @type {HTMLFormElement} */ - const $passwordSetter = (this.$setPassword.querySelector('.password-setter-box')); - /** @type {HTMLDivElement} */ - const $loginFileIcon = (this.$setPassword.querySelector('.login-file-icon')); - /** @type {HTMLDivElement} */ - const $downloadLoginFile = ($downloadFile.querySelector('.download-loginfile')); - /** @type {HTMLLinkElement} */ - const $skipDownloadButton = ($downloadFile.querySelector('.skip')); + const $recoveryWords = /** @type {HTMLFormElement} */ ( + this.$words.querySelector('.recovery-words')); + this.$setPasswordBackButton = /** @type {HTMLLinkElement} */ ( + this.$setPassword.querySelector('a.page-header-back-button')); + const $passwordSetter = /** @type {HTMLFormElement} */ ( + this.$setPassword.querySelector('.password-setter-box')); + const $loginFileIcon = /** @type {HTMLDivElement} */ ( + this.$setPassword.querySelector('.login-file-icon')); + const $downloadLoginFile = /** @type {HTMLDivElement} */ ( + $downloadFile.querySelector('.download-loginfile')); + const $skipDownloadButton = /** @type {HTMLLinkElement} */ ( + $downloadFile.querySelector('.skip')); // Components this._recoveryWords = new RecoveryWords($recoveryWords, true); @@ -246,7 +244,8 @@ class ImportWords { } else { TopLevelApi.setLoading(false); } - } catch (e) { // Keystore.instance.put throws Errors.KeyguardError + } catch (err) { // Keystore.instance.put throws Errors.KeyguardError + const e = /** @type {Error} */ (err); console.log(e); TopLevelApi.setLoading(false); this._reject(e); diff --git a/src/request/remove-key/RemoveKey.js b/src/request/remove-key/RemoveKey.js index 906604fdb..0c3a241ce 100644 --- a/src/request/remove-key/RemoveKey.js +++ b/src/request/remove-key/RemoveKey.js @@ -29,24 +29,22 @@ class RemoveKey { this._exportWordsHandler = new ExportWords(request, this.run.bind(this), this._reject.bind(this)); this._exportFileHandler = new ExportFile(request, this.run.bind(this), this._reject.bind(this)); - /** @type {HTMLElement} */ - const $removeKey = (document.getElementById(RemoveKey.Pages.REMOVE_KEY)); + const $removeKey = /** @type {HTMLElement} */ (document.getElementById(RemoveKey.Pages.REMOVE_KEY)); // remove key - /** @type {HTMLDivElement} */ - const $loginFileContainer = ($removeKey.querySelector('.login-file-svg-container')); - /** @type {HTMLButtonElement} */ - const $goToDownloadFile = ($removeKey.querySelector('#show-download-login-file')); - /** @type {HTMLButtonElement} */ - const $goToShowRecoveryWords = ($removeKey.querySelector('#show-recovery-words')); - /** @type {HTMLDivElement} */ - const $labelConfirm = ($removeKey.querySelector('#remove-key-label-confirm')); - /** @type {HTMLDivElement} */ - const $labelConfirmInstructions = ($labelConfirm.querySelector('#remove-key-label-confirm-instructions')); - /** @type {HTMLInputElement} */ - this.$labelInput = ($labelConfirm.querySelector('input')); - /** @type {HTMLButtonElement} */ - const $finalConfirmButton = ($removeKey.querySelector('#remove-key-final-confirm')); + const $loginFileContainer = /** @type {HTMLDivElement} */ ( + $removeKey.querySelector('.login-file-svg-container')); + const $goToDownloadFile = /** @type {HTMLButtonElement} */ ( + $removeKey.querySelector('#show-download-login-file')); + const $goToShowRecoveryWords = /** @type {HTMLButtonElement} */ ( + $removeKey.querySelector('#show-recovery-words')); + const $labelConfirm = /** @type {HTMLDivElement} */ ( + $removeKey.querySelector('#remove-key-label-confirm')); + const $labelConfirmInstructions = /** @type {HTMLDivElement} */ ( + $labelConfirm.querySelector('#remove-key-label-confirm-instructions')); + this.$labelInput = /** @type {HTMLInputElement} */ ($labelConfirm.querySelector('input')); + const $finalConfirmButton = /** @type {HTMLButtonElement} */ ( + $removeKey.querySelector('#remove-key-final-confirm')); if (request.keyInfo.type === Nimiq.Secret.Type.PRIVATE_KEY) { /** @type {HTMLElement} */ diff --git a/src/request/sign-btc-transaction/SignBtcTransaction.js b/src/request/sign-btc-transaction/SignBtcTransaction.js index d40664598..ea0b936fb 100644 --- a/src/request/sign-btc-transaction/SignBtcTransaction.js +++ b/src/request/sign-btc-transaction/SignBtcTransaction.js @@ -29,8 +29,7 @@ class SignBtcTransaction { */ constructor(request, resolve, reject) { this._request = request; - /** @type {HTMLElement} */ - this.$el = (document.getElementById(SignBtcTransaction.Pages.CONFIRM_TRANSACTION)); + this.$el = /** @type {HTMLElement} */ (document.getElementById(SignBtcTransaction.Pages.CONFIRM_TRANSACTION)); this.$el.classList.add(request.layout); const recipientOutput = request.recipientOutput; @@ -40,12 +39,9 @@ class SignBtcTransaction { - recipientOutput.value - (changeOutput ? changeOutput.value : 0); - /** @type {HTMLLinkElement} */ - const $recipientAvatar = (this.$el.querySelector('#avatar')); - /** @type {HTMLLinkElement} */ - const $recipientLabel = (this.$el.querySelector('#label')); - /** @type {HTMLLinkElement} */ - const $recipientAddress = (this.$el.querySelector('#address')); + const $recipientAvatar = /** @type {HTMLLinkElement} */ (this.$el.querySelector('#avatar')); + const $recipientLabel = /** @type {HTMLLinkElement} */ (this.$el.querySelector('#label')); + const $recipientAddress = /** @type {HTMLLinkElement} */ (this.$el.querySelector('#address')); const recipientAddress = recipientOutput.address; /* eslint-disable no-nested-ternary */ @@ -75,8 +71,7 @@ class SignBtcTransaction { $recipientAddress.textContent = recipientAddress; - /** @type {HTMLElement} */ - const $paymentInfoLine = (this.$el.querySelector('.payment-info-line')); + const $paymentInfoLine = /** @type {HTMLElement} */ (this.$el.querySelector('.payment-info-line')); if (request.layout === SignBtcTransactionApi.Layouts.CHECKOUT) { // eslint-disable-next-line no-new new PaymentInfoLine(Object.assign({}, request, { @@ -92,23 +87,19 @@ class SignBtcTransaction { $paymentInfoLine.remove(); } - /** @type {HTMLDivElement} */ - const $value = (this.$el.querySelector('#value')); - /** @type {HTMLDivElement} */ - const $fee = (this.$el.querySelector('#fee')); + const $value = /** @type {HTMLDivElement} */ (this.$el.querySelector('#value')); + const $fee = /** @type {HTMLDivElement} */ (this.$el.querySelector('#fee')); // Set value and fee. $value.textContent = NumberFormatting.formatNumber(BitcoinUtils.satoshisToCoins(recipientOutput.value), 8); if ($fee && fee > 0) { $fee.textContent = NumberFormatting.formatNumber(BitcoinUtils.satoshisToCoins(fee), 8); - /** @type {HTMLDivElement} */ - const $feeSection = (this.$el.querySelector('.fee-section')); + const $feeSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.fee-section')); $feeSection.classList.remove('display-none'); } // Set up password box. - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('#password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('#password-box')); this._passwordBox = new PasswordBox($passwordBox, { hideInput: !request.keyInfo.encrypted, buttonI18nTag: 'passwordbox-confirm-tx', @@ -142,7 +133,8 @@ class SignBtcTransaction { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); @@ -321,7 +313,8 @@ class SignBtcTransaction { raw: tx.toHex(), }; resolve(result); - } catch (error) { + } catch (err) { + const error = /** @type {Error} */ (err); TopLevelApi.setLoading(false); console.error(error); alert(`ERROR: ${error.message}`); diff --git a/src/request/sign-message/SignMessage.js b/src/request/sign-message/SignMessage.js index 315fee9b1..9b3dc3124 100644 --- a/src/request/sign-message/SignMessage.js +++ b/src/request/sign-message/SignMessage.js @@ -21,17 +21,13 @@ class SignMessage { * @param {reject} reject */ constructor(request, resolve, reject) { - /** @type {HTMLDivElement} */ - const $page = (document.getElementById(SignMessage.Pages.SIGN_MESSAGE)); + const $page = /** @type {HTMLDivElement} */ (document.getElementById(SignMessage.Pages.SIGN_MESSAGE)); - /** @type {HTMLDivElement} */ - const $signerIdenticon = ($page.querySelector('#signer-identicon')); + const $signerIdenticon = /** @type {HTMLDivElement} */ ($page.querySelector('#signer-identicon')); - /** @type {HTMLDivElement} */ - const $signerLabel = ($page.querySelector('#signer-label')); + const $signerLabel = /** @type {HTMLDivElement} */ ($page.querySelector('#signer-label')); - /** @type {HTMLInputElement} */ - const $message = ($page.querySelector('#message')); + const $message = /** @type {HTMLInputElement} */ ($page.querySelector('#message')); // Set message if (typeof request.message === 'string') { @@ -41,8 +37,7 @@ class SignMessage { if (request.message.includes('\t')) { // Init tab width selector - /** @type {HTMLDivElement} */ - const $tabWidthSelector = ($page.querySelector('#tab-width-selector')); + const $tabWidthSelector = /** @type {HTMLDivElement} */ ($page.querySelector('#tab-width-selector')); const tws = new TabWidthSelector($tabWidthSelector); // @ts-ignore Property 'tabSize' does not exist on type 'CSSStyleDeclaration' @@ -65,8 +60,7 @@ class SignMessage { $signerLabel.textContent = request.signerLabel; // Set up password box - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('#password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('#password-box')); this._passwordBox = new PasswordBox($passwordBox, { hideInput: !request.keyInfo.encrypted, buttonI18nTag: 'passwordbox-sign-msg', @@ -96,7 +90,8 @@ class SignMessage { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); diff --git a/src/request/sign-polygon-transaction/SignPolygonTransaction.js b/src/request/sign-polygon-transaction/SignPolygonTransaction.js index e0509191b..dde14641c 100644 --- a/src/request/sign-polygon-transaction/SignPolygonTransaction.js +++ b/src/request/sign-polygon-transaction/SignPolygonTransaction.js @@ -25,13 +25,12 @@ class SignPolygonTransaction { * @param {reject} reject */ constructor(request, resolve, reject) { - /** @type {HTMLElement} */ - this.$el = (document.getElementById(SignPolygonTransaction.Pages.CONFIRM_TRANSACTION)); + this.$el = /** @type {HTMLElement} */ ( + document.getElementById(SignPolygonTransaction.Pages.CONFIRM_TRANSACTION)); const relayRequest = request.request; - /** @type {HTMLLinkElement} */ - const $sender = (this.$el.querySelector('.accounts .sender')); + const $sender = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .sender')); if (['redeem', 'redeemWithSecretInData', 'refund'].includes(request.description.name)) { new PolygonAddressInfo(relayRequest.to, request.senderLabel, 'unknown').renderTo($sender); } else if (request.description.name === 'swap' || request.description.name === 'swapWithApproval') { @@ -40,8 +39,7 @@ class SignPolygonTransaction { new PolygonAddressInfo(relayRequest.from, request.keyLabel, 'usdc').renderTo($sender); } - /** @type {HTMLLinkElement} */ - const $recipient = (this.$el.querySelector('.accounts .recipient')); + const $recipient = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .recipient')); if (['redeem', 'redeemWithSecretInData', 'refund'].includes(request.description.name)) { const recipientAddress = /** @type {string} */ (request.description.args.target); new PolygonAddressInfo(recipientAddress, request.keyLabel, 'usdc').renderTo($recipient); @@ -52,10 +50,8 @@ class SignPolygonTransaction { new PolygonAddressInfo(recipientAddress, request.recipientLabel, 'none').renderTo($recipient); } - /** @type {HTMLDivElement} */ - const $value = (this.$el.querySelector('#value')); - /** @type {HTMLDivElement} */ - const $fee = (this.$el.querySelector('#fee')); + const $value = /** @type {HTMLDivElement} */ (this.$el.querySelector('#value')); + const $fee = /** @type {HTMLDivElement} */ (this.$el.querySelector('#fee')); // Set value and fee. $value.textContent = NumberFormatting.formatNumber( @@ -69,14 +65,12 @@ class SignPolygonTransaction { if (feeUnits > 0) { // For the fee, we do not display more than two decimals, as it would not add any value for the user $fee.textContent = NumberFormatting.formatNumber(PolygonUtils.unitsToCoins(feeUnits), 2, 2); - /** @type {HTMLDivElement} */ - const $feeSection = (this.$el.querySelector('.fee-section')); + const $feeSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.fee-section')); $feeSection.classList.remove('display-none'); } // Set up password box. - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('#password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('#password-box')); this._passwordBox = new PasswordBox($passwordBox, { hideInput: !request.keyInfo.encrypted, buttonI18nTag: 'passwordbox-confirm-tx', @@ -106,7 +100,8 @@ class SignPolygonTransaction { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (e) { + } catch (err) { + const e = /** @type {Error} */ (err); if (e.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); diff --git a/src/request/sign-polygon-transaction/SignPolygonTransactionApi.js b/src/request/sign-polygon-transaction/SignPolygonTransactionApi.js index 301869acb..885528129 100644 --- a/src/request/sign-polygon-transaction/SignPolygonTransactionApi.js +++ b/src/request/sign-polygon-transaction/SignPolygonTransactionApi.js @@ -86,11 +86,12 @@ class SignPolygonTransactionApi extends PolygonRequestParserMixin(TopLevelApi) { PolygonContractABIs.NATIVE_USDC_TRANSFER_CONTRACT_ABI, ); - /** @type {PolygonTransferDescription | PolygonTransferWithPermitDescription} */ - description = (nativeUsdcTransferContract.interface.parseTransaction({ - data: forwardRequest.data, - value: forwardRequest.value, - })); + description = /** @type {PolygonTransferDescription | PolygonTransferWithPermitDescription} */ ( + nativeUsdcTransferContract.interface.parseTransaction({ + data: forwardRequest.data, + value: forwardRequest.value, + }) + ); if (description.args.token !== CONFIG.NATIVE_USDC_CONTRACT_ADDRESS) { throw new Errors.InvalidRequestError('Invalid native USDC token contract in request data'); @@ -105,16 +106,13 @@ class SignPolygonTransactionApi extends PolygonRequestParserMixin(TopLevelApi) { PolygonContractABIs.NATIVE_USDC_HTLC_CONTRACT_ABI, ); - /** - * @type {PolygonRedeemDescription - * | PolygonRedeemWithSecretInDataDescription - * | PolygonRefundDescription - * } - */ - description = (usdcHtlcContract.interface.parseTransaction({ - data: forwardRequest.data, - value: forwardRequest.value, - })); + // eslint-disable-next-line max-len + description = /** @type {PolygonRedeemDescription | PolygonRedeemWithSecretInDataDescription | PolygonRefundDescription} */ ( + usdcHtlcContract.interface.parseTransaction({ + data: forwardRequest.data, + value: forwardRequest.value, + }) + ); if (!['redeem', 'redeemWithSecretInData', 'refund'].includes(description.name)) { throw new Errors.InvalidRequestError('Requested Polygon contract method is invalid'); @@ -125,11 +123,12 @@ class SignPolygonTransactionApi extends PolygonRequestParserMixin(TopLevelApi) { PolygonContractABIs.BRIDGED_USDC_HTLC_CONTRACT_ABI, ); - /** @type {PolygonRefundDescription} */ - description = (usdcHtlcContract.interface.parseTransaction({ - data: forwardRequest.data, - value: forwardRequest.value, - })); + description = /** @type {PolygonRefundDescription} */ ( + usdcHtlcContract.interface.parseTransaction({ + data: forwardRequest.data, + value: forwardRequest.value, + }) + ); if (!['refund'].includes(description.name)) { throw new Errors.InvalidRequestError('Requested Polygon contract method is invalid'); @@ -140,11 +139,12 @@ class SignPolygonTransactionApi extends PolygonRequestParserMixin(TopLevelApi) { PolygonContractABIs.SWAP_CONTRACT_ABI, ); - /** @type {PolygonSwapDescription | PolygonSwapWithApprovalDescription} */ - description = (usdcTransferContract.interface.parseTransaction({ - data: forwardRequest.data, - value: forwardRequest.value, - })); + description = /** @type {PolygonSwapDescription | PolygonSwapWithApprovalDescription} */( + usdcTransferContract.interface.parseTransaction({ + data: forwardRequest.data, + value: forwardRequest.value, + }) + ); if (description.args.token !== CONFIG.BRIDGED_USDC_CONTRACT_ADDRESS) { throw new Errors.InvalidRequestError('Invalid USDC token contract in request data'); diff --git a/src/request/sign-swap/SignSwap.js b/src/request/sign-swap/SignSwap.js index 3c6897653..3148877f2 100644 --- a/src/request/sign-swap/SignSwap.js +++ b/src/request/sign-swap/SignSwap.js @@ -34,48 +34,34 @@ class SignSwap { */ constructor(request, resolve, reject) { this._request = request; - /** @type {HTMLElement} */ - this.$el = (document.getElementById(SignSwap.Pages.CONFIRM_SWAP)); + this.$el = /** @type {HTMLElement} */ (document.getElementById(SignSwap.Pages.CONFIRM_SWAP)); const fundTx = request.fund; const redeemTx = request.redeem; // Remove unused layout HTML before getting DOM nodes if (request.layout === SignSwapApi.Layouts.STANDARD) { - /** @type {HTMLDivElement} */ - const sliderLayout = (this.$el.querySelector('.layout-slider')); + const sliderLayout = /** @type {HTMLDivElement} */ (this.$el.querySelector('.layout-slider')); this.$el.removeChild(sliderLayout); } if (request.layout === SignSwapApi.Layouts.SLIDER) { - /** @type {HTMLDivElement} */ - const standardLayout = (this.$el.querySelector('.layout-standard')); + const standardLayout = /** @type {HTMLDivElement} */ (this.$el.querySelector('.layout-standard')); this.$el.removeChild(standardLayout); } this.$el.classList.add(`layout-${request.layout}`); - /** @type {HTMLDivElement} */ - const $leftAccount = (this.$el.querySelector('.left-account')); - /** @type {HTMLDivElement} */ - const $rightAccount = (this.$el.querySelector('.right-account')); - /** @type {HTMLDivElement} */ - const $leftIdenticon = ($leftAccount.querySelector('.identicon')); - /** @type {HTMLDivElement} */ - const $rightIdenticon = ($rightAccount.querySelector('.identicon')); - /** @type {HTMLSpanElement} */ - const $leftLabel = ($leftAccount.querySelector('.label')); - /** @type {HTMLSpanElement} */ - const $rightLabel = ($rightAccount.querySelector('.label')); - /** @type {HTMLSpanElement} */ - const $leftNewBalance = ($leftAccount.querySelector('.new-balance')); - /** @type {HTMLDivElement} */ - const $rightNewBalance = ($rightAccount.querySelector('.new-balance')); - /** @type {HTMLDivElement} */ - const $swapValues = (this.$el.querySelector('.swap-values')); - /** @type {HTMLSpanElement} */ - const $swapLeftValue = (this.$el.querySelector('#swap-left-value')); - /** @type {HTMLSpanElement} */ - const $swapRightValue = (this.$el.querySelector('#swap-right-value')); + const $leftAccount = /** @type {HTMLDivElement} */ (this.$el.querySelector('.left-account')); + const $rightAccount = /** @type {HTMLDivElement} */ (this.$el.querySelector('.right-account')); + const $leftIdenticon = /** @type {HTMLDivElement} */ ($leftAccount.querySelector('.identicon')); + const $rightIdenticon = /** @type {HTMLDivElement} */ ($rightAccount.querySelector('.identicon')); + const $leftLabel = /** @type {HTMLSpanElement} */ ($leftAccount.querySelector('.label')); + const $rightLabel = /** @type {HTMLSpanElement} */ ($rightAccount.querySelector('.label')); + const $leftNewBalance = /** @type {HTMLSpanElement} */ ($leftAccount.querySelector('.new-balance')); + const $rightNewBalance = /** @type {HTMLDivElement} */ ($rightAccount.querySelector('.new-balance')); + const $swapValues = /** @type {HTMLDivElement} */ (this.$el.querySelector('.swap-values')); + const $swapLeftValue = /** @type {HTMLSpanElement} */ (this.$el.querySelector('#swap-left-value')); + const $swapRightValue = /** @type {HTMLSpanElement} */ (this.$el.querySelector('#swap-right-value')); // The total amount the user loses let swapFromValue = 0; @@ -167,8 +153,7 @@ class SignSwap { exchangeOtherAsset === 'EUR' ? CryptoUtils.assetDecimals(exchangeOtherAsset) : 0, )} ${exchangeOtherAsset}`; - /** @type {HTMLDivElement} */ - const $topRow = (this.$el.querySelector('.nq-notice')); + const $topRow = /** @type {HTMLDivElement} */ (this.$el.querySelector('.nq-notice')); $topRow.appendChild( new SwapFeesTooltip( request, @@ -183,10 +168,8 @@ class SignSwap { $rightAccount.classList.add(CryptoUtils.assetToCurrency(request.redeem.type)); // Add ticker symbols - /** @type {HTMLSpanElement} */ - const $fromSymbol = (this.$el.querySelector('.swap-values .from-symbol')); - /** @type {HTMLSpanElement} */ - const $toSymbol = (this.$el.querySelector('.swap-values .to-symbol')); + const $fromSymbol = /** @type {HTMLSpanElement} */ (this.$el.querySelector('.swap-values .from-symbol')); + const $toSymbol = /** @type {HTMLSpanElement} */ (this.$el.querySelector('.swap-values .to-symbol')); $fromSymbol.classList.add(`${CryptoUtils.assetToCurrency(request.fund.type)}-symbol`); $toSymbol.classList.add(`${CryptoUtils.assetToCurrency(request.redeem.type)}-symbol`); @@ -233,16 +216,16 @@ class SignSwap { if (request.layout === SignSwapApi.Layouts.SLIDER) { $swapValues.classList.add(request.direction); - /** @type {HTMLDivElement} */ - const $balanceDistributionBar = (this.$el.querySelector('.balance-distribution-bar')); - /** @type {HTMLSpanElement} */ - const $swapLeftValueFiat = (this.$el.querySelector('#swap-left-value-fiat')); - /** @type {HTMLSpanElement} */ - const $swapRightValueFiat = (this.$el.querySelector('#swap-right-value-fiat')); - /** @type {HTMLSpanElement} */ - const $swapLeftSymbol = (this.$el.querySelector('#swap-left-symbol')); - /** @type {HTMLSpanElement} */ - const $swapRightSymbol = (this.$el.querySelector('#swap-right-symbol')); + const $balanceDistributionBar = /** @type {HTMLDivElement} */ ( + this.$el.querySelector('.balance-distribution-bar')); + const $swapLeftValueFiat = /** @type {HTMLSpanElement} */ ( + this.$el.querySelector('#swap-left-value-fiat')); + const $swapRightValueFiat = /** @type {HTMLSpanElement} */ ( + this.$el.querySelector('#swap-right-value-fiat')); + const $swapLeftSymbol = /** @type {HTMLSpanElement} */ ( + this.$el.querySelector('#swap-left-symbol')); + const $swapRightSymbol = /** @type {HTMLSpanElement} */ ( + this.$el.querySelector('#swap-right-symbol')); $swapLeftSymbol.classList.add(`${CryptoUtils.assetToCurrency(leftAsset)}-symbol`); $swapRightSymbol.classList.add(`${CryptoUtils.assetToCurrency(rightAsset)}-symbol`); @@ -409,8 +392,7 @@ class SignSwap { } // Set up password box. - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('#password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('#password-box')); this._passwordBox = new PasswordBox($passwordBox, { hideInput: !request.keyInfo.encrypted, buttonI18nTag: 'passwordbox-confirm-swap', @@ -491,12 +473,13 @@ class SignSwap { try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); } catch (e) { - if (e.message === 'Invalid key') { + const err = /** @type {Error} */ (e); + if (err.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(e)); + reject(new Errors.CoreError(err)); return; } if (!key) { @@ -658,8 +641,9 @@ class SignSwap { // the Hub is not compromised. An attacker would need to get access to the Keyguard and Hub servers. tmpCookieEncryptionKey, }); - } catch (error) { - reject(error); + } catch (e) { + const err = /** @type {Error} */ (e); + reject(err); } } diff --git a/src/request/sign-transaction/SignTransaction.js b/src/request/sign-transaction/SignTransaction.js index e4370c9a3..3243d07ca 100644 --- a/src/request/sign-transaction/SignTransaction.js +++ b/src/request/sign-transaction/SignTransaction.js @@ -25,17 +25,14 @@ class SignTransaction { */ constructor(request, resolve, reject) { this._request = request; - /** @type {HTMLElement} */ - this.$el = (document.getElementById(SignTransaction.Pages.CONFIRM_TRANSACTION)); + this.$el = /** @type {HTMLElement} */ (document.getElementById(SignTransaction.Pages.CONFIRM_TRANSACTION)); this.$el.classList.add(request.layout); const transaction = request.transaction; - /** @type {HTMLElement} */ - this.$accountDetails = (this.$el.querySelector('#account-details')); + this.$accountDetails = /** @type {HTMLElement} */ (this.$el.querySelector('#account-details')); - /** @type {HTMLLinkElement} */ - const $sender = (this.$el.querySelector('.accounts .sender')); + const $sender = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .sender')); this._senderAddressInfo = new AddressInfo({ userFriendlyAddress: transaction.sender.toUserFriendlyAddress(), label: request.senderLabel || null, @@ -47,8 +44,7 @@ class SignTransaction { this._openDetails(this._senderAddressInfo); }); - /** @type {HTMLLinkElement} */ - const $recipient = (this.$el.querySelector('.accounts .recipient')); + const $recipient = /** @type {HTMLLinkElement} */ (this.$el.querySelector('.accounts .recipient')); const recipientAddress = transaction.recipient.toUserFriendlyAddress(); /* eslint-disable no-nested-ternary */ const recipientLabel = 'shopOrigin' in request && !!request.shopOrigin @@ -73,8 +69,7 @@ class SignTransaction { }); } - /** @type {HTMLElement} */ - const $paymentInfoLine = (this.$el.querySelector('.payment-info-line')); + const $paymentInfoLine = /** @type {HTMLElement} */ (this.$el.querySelector('.payment-info-line')); if (request.layout === SignTransactionApi.Layouts.CHECKOUT) { // eslint-disable-next-line no-new new PaymentInfoLine(Object.assign({}, request, { @@ -90,23 +85,18 @@ class SignTransaction { $paymentInfoLine.remove(); } - /** @type {HTMLButtonElement} */ - const $closeDetails = (this.$accountDetails.querySelector('#close-details')); + const $closeDetails = /** @type {HTMLButtonElement} */ (this.$accountDetails.querySelector('#close-details')); $closeDetails.addEventListener('click', this._closeDetails.bind(this)); - /** @type {HTMLDivElement} */ - const $value = (this.$el.querySelector('#value')); - /** @type {HTMLDivElement} */ - const $fee = (this.$el.querySelector('#fee')); - /** @type {HTMLDivElement} */ - const $data = (this.$el.querySelector('#data')); + const $value = /** @type {HTMLDivElement} */ (this.$el.querySelector('#value')); + const $fee = /** @type {HTMLDivElement} */ (this.$el.querySelector('#fee')); + const $data = /** @type {HTMLDivElement} */ (this.$el.querySelector('#data')); // Set value and fee. $value.textContent = NumberFormatting.formatNumber(Nimiq.Policy.lunasToCoins(transaction.value)); if ($fee && transaction.fee > 0) { $fee.textContent = NumberFormatting.formatNumber(Nimiq.Policy.lunasToCoins(transaction.fee)); - /** @type {HTMLDivElement} */ - const $feeSection = (this.$el.querySelector('.fee-section')); + const $feeSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.fee-section')); $feeSection.classList.remove('display-none'); } @@ -114,21 +104,18 @@ class SignTransaction { && Nimiq.BufferUtils.equals(transaction.data, Constants.CASHLINK_FUNDING_DATA)) { if (request.cashlinkMessage) { $data.textContent = request.cashlinkMessage; - /** @type {HTMLDivElement} */ - const $dataSection = (this.$el.querySelector('.data-section')); + const $dataSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.data-section')); $dataSection.classList.remove('display-none'); } } else if ($data && transaction.data.byteLength > 0) { // Set transaction extra data. $data.textContent = this._formatData(transaction); - /** @type {HTMLDivElement} */ - const $dataSection = (this.$el.querySelector('.data-section')); + const $dataSection = /** @type {HTMLDivElement} */ (this.$el.querySelector('.data-section')); $dataSection.classList.remove('display-none'); } // Set up password box. - /** @type {HTMLFormElement} */ - const $passwordBox = (document.querySelector('#password-box')); + const $passwordBox = /** @type {HTMLFormElement} */ (document.querySelector('#password-box')); this._passwordBox = new PasswordBox($passwordBox, { hideInput: !request.keyInfo.encrypted, buttonI18nTag: request.layout === SignTransactionApi.Layouts.CASHLINK @@ -180,12 +167,13 @@ class SignTransaction { try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); } catch (e) { - if (e.message === 'Invalid key') { + const err = /** @type {Error} */ (e); + if (err.message === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(e)); + reject(new Errors.CoreError(err)); return; } if (!key) { diff --git a/tsconfig.json b/tsconfig.json index 9d23083d6..2afac6c37 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { /* Basic Options */ - "target": "ES2015", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ + "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["DOM"], /* Specify library files to be included in the compilation. */ "skipLibCheck": true, /* Skip checking .d.ts files. We need this currently, because types in */ /* lib.webworker.d.ts referenced in ServiceWorker.js clash with types */ diff --git a/yarn.lock b/yarn.lock index 6a8ab659a..39802ba02 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5304,10 +5304,10 @@ typeforce@^1.11.3, typeforce@^1.11.5: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@3: - version "3.9.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" - integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== +typescript@4: + version "4.9.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== ua-parser-js@^0.7.30: version "0.7.36" From ce1f711610f6d48c6ad8fac10a03ca504c54b1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Sun, 19 Mar 2023 13:32:11 -0600 Subject: [PATCH 03/14] Update to Typescript v5 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 61f0fc274..2fcaa3d64 100644 --- a/package.json +++ b/package.json @@ -60,6 +60,6 @@ "karma-jasmine": "^1.1.2", "terser": "^5.14.2", "tsc-watch": "^1.0.22", - "typescript": "4" + "typescript": "5.4" } } diff --git a/yarn.lock b/yarn.lock index 39802ba02..455798fe9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5304,10 +5304,10 @@ typeforce@^1.11.3, typeforce@^1.11.5: resolved "https://registry.yarnpkg.com/typeforce/-/typeforce-1.18.0.tgz#d7416a2c5845e085034d70fcc5b6cc4a90edbfdc" integrity sha512-7uc1O8h1M1g0rArakJdf0uLRSSgFcYexrVoKo+bzJd32gd4gDy2L/Z+8/FjPnU9ydY3pEnVPtr9FyscYY60K1g== -typescript@4: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@5.4: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== ua-parser-js@^0.7.30: version "0.7.36" From ee95cf000a2b4aaecf66ebeccbba290ec906522b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Sun, 19 Mar 2023 13:33:26 -0600 Subject: [PATCH 04/14] Remove TS compatibility fix --- package.json | 2 +- tools/typescriptCompatibilityFix.js | 16 ---------------- 2 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 tools/typescriptCompatibilityFix.js diff --git a/package.json b/package.json index 2fcaa3d64..82eecbdb5 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build:bitcoinjs": "yarn --silent browserify tools/bitcoinjs-parts.js -s BitcoinJS | yarn terser --compress --mangle --source-map --output src/lib/bitcoin/BitcoinJS.js", "build:opengsn": "yarn --silent browserify -r @opengsn/common/dist/EIP712/TypedRequestData -s OpenGSN | yarn terser --compress --mangle --source-map --output src/lib/polygon/OpenGSN.js", "test": "karma start", - "typecheck": "node tools/typescriptCompatibilityFix.js && tsc", + "typecheck": "tsc", "lint": "yarn i18n:build-dictionary && eslint src tools && if ( grep 'fit\\|fdescribe' tests/lib/* ); then exit 1; else exit 0; fi", "lintfix": "eslint --fix src tools", "checkdeps": "node tools/dependencyValidator.js", diff --git a/tools/typescriptCompatibilityFix.js b/tools/typescriptCompatibilityFix.js deleted file mode 100644 index 958f6e02e..000000000 --- a/tools/typescriptCompatibilityFix.js +++ /dev/null @@ -1,16 +0,0 @@ -const fs = require('fs').promises; -const path = require('path'); - -/** - * Replace `import type` with `import` in TS declaration files, as the Typescript version - * used by the Keyguard (v3.5) does not support `import type`. - * @param {string} file - */ -async function fix(file) { - const content = await fs.readFile(path.join(process.cwd(), file), 'utf8'); - const replaced = content.replace(/import type/g, 'import'); - await fs.writeFile(file, replaced, 'utf8'); -} - -fix('node_modules/@ethersproject/providers/lib/ankr-provider.d.ts'); -fix('node_modules/ethers/lib/ethers.d.ts'); From 5a9d4429c4355ff3aba09202b384c45cb7fd1551 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 5 Sep 2024 00:24:06 +0200 Subject: [PATCH 05/14] LoginFile: avoid swallowing errors in canvas.toBlob --- src/lib/LoginFile.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/LoginFile.js b/src/lib/LoginFile.js index fc651b86f..109f1010f 100644 --- a/src/lib/LoginFile.js +++ b/src/lib/LoginFile.js @@ -65,9 +65,12 @@ class LoginFile { async toObjectUrl() { await this._drawPromise; - return new Promise(resolve => { + return new Promise((resolve, reject) => { this.$canvas.toBlob(blob => { - if (!blob) throw new Error('Cannot generate URL'); + if (!blob) { + reject(new Error('Cannot generate URL')); + return; + } const url = URL.createObjectURL(blob); resolve(url); }); From 06b6a8bc28a366d07757c8da94b6aff927796ead Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 5 Sep 2024 23:42:20 +0200 Subject: [PATCH 06/14] Typechecks instead of type assertions for caught errors --- src/components/RecoveryWords.js | 8 +++---- src/lib/QrScanner.js | 5 ++--- src/lib/RequestParser.js | 21 ++++++++----------- src/lib/RpcServer.es.js | 5 ++--- src/request/TopLevelApi.js | 2 +- src/request/change-password/ChangePassword.js | 8 +++---- src/request/derive-address/DeriveAddress.js | 8 +++---- src/request/derive-btc-xpub/DeriveBtcXPub.js | 8 +++---- .../DerivePolygonAddress.js | 8 +++---- src/request/export/ExportFile.js | 16 +++++++------- src/request/export/ExportWords.js | 8 +++---- src/request/import/ImportFile.js | 5 ++--- src/request/import/ImportWords.js | 7 +++---- .../SignBtcTransaction.js | 14 ++++++------- src/request/sign-message/SignMessage.js | 8 +++---- .../SignPolygonTransaction.js | 8 +++---- src/request/sign-swap/SignSwap.js | 13 ++++++------ .../sign-transaction/SignTransaction.js | 8 +++---- 18 files changed, 76 insertions(+), 84 deletions(-) diff --git a/src/components/RecoveryWords.js b/src/components/RecoveryWords.js index 0f91fee57..f72c91660 100644 --- a/src/components/RecoveryWords.js +++ b/src/components/RecoveryWords.js @@ -140,10 +140,10 @@ class RecoveryWords extends Nimiq.Observable { const type = Nimiq.MnemonicUtils.getMnemonicType(mnemonic); // throws on invalid mnemonic this._mnemonic = { words: mnemonic, type }; this.fire(RecoveryWords.Events.COMPLETE, mnemonic, type); - } catch (e) { - const err = /** @type {Error} */ (e); - if (err.message !== 'Invalid checksum') { - console.error(e); // eslint-disable-line no-console + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage !== 'Invalid checksum') { + console.error(error); // eslint-disable-line no-console } else { // wrong words if (this._mnemonic) this._mnemonic = null; diff --git a/src/lib/QrScanner.js b/src/lib/QrScanner.js index ff1b24291..3b39845e8 100644 --- a/src/lib/QrScanner.js +++ b/src/lib/QrScanner.js @@ -342,10 +342,9 @@ class QrScanner { this._qrEnginePromise, this.$canvas, ); - } catch (err) { + } catch (error) { if (!this._active) return; - const error = /** @type {Error | string} */ (err); - const errorMessage = typeof error === 'string' ? error : error.message; + const errorMessage = error instanceof Error ? error.message : String(error); if (errorMessage.includes('service unavailable')) { // When the native BarcodeDetector crashed, create a new one this._qrEnginePromise = QrScanner.createQrEngine(); diff --git a/src/lib/RequestParser.js b/src/lib/RequestParser.js index 0555ab1c6..5f7eae74a 100644 --- a/src/lib/RequestParser.js +++ b/src/lib/RequestParser.js @@ -184,9 +184,8 @@ class RequestParser { // eslint-disable-line no-unused-vars flags, data, ); - } catch (err) { - const error = /** @type {Error} */ (err); - throw new Errors.InvalidRequestError(error); + } catch (error) { + throw new Errors.InvalidRequestError(error instanceof Error ? error : String(error)); } } @@ -205,9 +204,9 @@ class RequestParser { // eslint-disable-line no-unused-vars return Nimiq.Address.fromString(address); } return new Nimiq.Address(address); - } catch (err) { - const error = /** @type {Error} */ (err); - throw new Errors.InvalidRequestError(`${name} must be a valid Nimiq address (${error.message})`); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + throw new Errors.InvalidRequestError(`${name} must be a valid Nimiq address (${errorMessage})`); } } @@ -242,9 +241,8 @@ class RequestParser { // eslint-disable-line no-unused-vars try { const parsedUrl = this._parseUrl(url, 'shopOrigin'); return parsedUrl.origin; - } catch (err) { - const error = /** @type {Error} */ (err); - throw new Errors.InvalidRequestError(error); + } catch (error) { + throw new Errors.InvalidRequestError(error instanceof Error ? error : String(error)); } } @@ -259,9 +257,8 @@ class RequestParser { // eslint-disable-line no-unused-vars } try { return this._parseUrl(url, 'shopLogoUrl'); - } catch (err) { - const error = /** @type {Error} */ (err); - throw new Errors.InvalidRequestError(error); + } catch (error) { + throw new Errors.InvalidRequestError(error instanceof Error ? error : String(error)); } } diff --git a/src/lib/RpcServer.es.js b/src/lib/RpcServer.es.js index 7c97054c7..2d25e54a1 100644 --- a/src/lib/RpcServer.es.js +++ b/src/lib/RpcServer.es.js @@ -314,10 +314,9 @@ class RpcServer { // eslint-disable-line no-unused-vars RpcServer._ok(state, result); } return true; - } catch (err) { - const error = /** @type {Error} */ (err); + } catch (error) { if (_state) { - RpcServer._error(_state, error); + RpcServer._error(_state, error instanceof Error ? error : new Error(String(error))); } return false; } diff --git a/src/request/TopLevelApi.js b/src/request/TopLevelApi.js index 56ed84292..79ab0f2d3 100644 --- a/src/request/TopLevelApi.js +++ b/src/request/TopLevelApi.js @@ -128,7 +128,7 @@ class TopLevelApi extends RequestParser { } window.addEventListener('unhandledrejection', event => { - const error = new Errors.UnclassifiedError(/** @type {PromiseRejectionEvent} */(event).reason); + const error = new Errors.UnclassifiedError(/** @type {PromiseRejectionEvent} */ (event).reason); this.reject(error); return false; }); diff --git a/src/request/change-password/ChangePassword.js b/src/request/change-password/ChangePassword.js index 0a59c3e3e..94b3c1941 100644 --- a/src/request/change-password/ChangePassword.js +++ b/src/request/change-password/ChangePassword.js @@ -136,14 +136,14 @@ class ChangePassword { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, oldPasswordBytes); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordGetter.onPasswordIncorrect(); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } if (!key) { diff --git a/src/request/derive-address/DeriveAddress.js b/src/request/derive-address/DeriveAddress.js index b3a28371c..7310c73b1 100644 --- a/src/request/derive-address/DeriveAddress.js +++ b/src/request/derive-address/DeriveAddress.js @@ -50,14 +50,14 @@ class DeriveAddress { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } diff --git a/src/request/derive-btc-xpub/DeriveBtcXPub.js b/src/request/derive-btc-xpub/DeriveBtcXPub.js index 2aeb6b58e..475f4a501 100644 --- a/src/request/derive-btc-xpub/DeriveBtcXPub.js +++ b/src/request/derive-btc-xpub/DeriveBtcXPub.js @@ -51,14 +51,14 @@ class DeriveBtcXPub { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } diff --git a/src/request/derive-polygon-address/DerivePolygonAddress.js b/src/request/derive-polygon-address/DerivePolygonAddress.js index 770925561..1dfe4bac1 100644 --- a/src/request/derive-polygon-address/DerivePolygonAddress.js +++ b/src/request/derive-polygon-address/DerivePolygonAddress.js @@ -49,14 +49,14 @@ class DerivePolygonAddress { let key = null; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } diff --git a/src/request/export/ExportFile.js b/src/request/export/ExportFile.js index e34152544..d2df72321 100644 --- a/src/request/export/ExportFile.js +++ b/src/request/export/ExportFile.js @@ -161,14 +161,14 @@ class ExportFile extends Nimiq.Observable { let key = this._key; try { key = await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } if (!key) { @@ -194,14 +194,14 @@ class ExportFile extends Nimiq.Observable { if (!key || !key.id) { try { key = await KeyStore.instance.get(this._request.keyInfo.id); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } } diff --git a/src/request/export/ExportWords.js b/src/request/export/ExportWords.js index 1690297a4..a5ce2d3af 100644 --- a/src/request/export/ExportWords.js +++ b/src/request/export/ExportWords.js @@ -112,14 +112,14 @@ class ExportWords extends Nimiq.Observable { /** @type {Uint8Array} */ (passwordBuffer), ) : await KeyStore.instance.get(this._request.keyInfo.id, passwordBuffer); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { this._wordsPasswordBox.onPasswordIncorrect(); TopLevelApi.setLoading(false); return; } - this._reject(new Errors.CoreError(e)); + this._reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } diff --git a/src/request/import/ImportFile.js b/src/request/import/ImportFile.js index f861ff358..cb976a415 100644 --- a/src/request/import/ImportFile.js +++ b/src/request/import/ImportFile.js @@ -209,9 +209,8 @@ class ImportFile { } else { throw new Error(`Unknown key type ${key.type}`); } - } catch (e) { - const err = /** @type {Error | string} */ (e); - this._reject(new Errors.KeyguardError(typeof err === 'string' ? err : err.message)); + } catch (error) { + this._reject(new Errors.KeyguardError(error instanceof Error ? error : String(error))); return; } diff --git a/src/request/import/ImportWords.js b/src/request/import/ImportWords.js index cfa2715c7..3adbc3bd1 100644 --- a/src/request/import/ImportWords.js +++ b/src/request/import/ImportWords.js @@ -244,11 +244,10 @@ class ImportWords { } else { TopLevelApi.setLoading(false); } - } catch (err) { // Keystore.instance.put throws Errors.KeyguardError - const e = /** @type {Error} */ (err); - console.log(e); + } catch (error) { // Keystore.instance.put throws Errors.KeyguardError + console.log(error); TopLevelApi.setLoading(false); - this._reject(e); + this._reject(error instanceof Error ? error : new Error(String(error))); } } diff --git a/src/request/sign-btc-transaction/SignBtcTransaction.js b/src/request/sign-btc-transaction/SignBtcTransaction.js index ea0b936fb..ae3410c5f 100644 --- a/src/request/sign-btc-transaction/SignBtcTransaction.js +++ b/src/request/sign-btc-transaction/SignBtcTransaction.js @@ -133,14 +133,14 @@ class SignBtcTransaction { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(e)); + reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } if (!key) { @@ -313,11 +313,11 @@ class SignBtcTransaction { raw: tx.toHex(), }; resolve(result); - } catch (err) { - const error = /** @type {Error} */ (err); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); TopLevelApi.setLoading(false); console.error(error); - alert(`ERROR: ${error.message}`); + alert(`ERROR: ${errorMessage}`); // eslint-disable-line no-alert } } diff --git a/src/request/sign-message/SignMessage.js b/src/request/sign-message/SignMessage.js index 9b3dc3124..31b9569d5 100644 --- a/src/request/sign-message/SignMessage.js +++ b/src/request/sign-message/SignMessage.js @@ -90,14 +90,14 @@ class SignMessage { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(e)); + reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } diff --git a/src/request/sign-polygon-transaction/SignPolygonTransaction.js b/src/request/sign-polygon-transaction/SignPolygonTransaction.js index dde14641c..518049d99 100644 --- a/src/request/sign-polygon-transaction/SignPolygonTransaction.js +++ b/src/request/sign-polygon-transaction/SignPolygonTransaction.js @@ -100,14 +100,14 @@ class SignPolygonTransaction { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (err) { - const e = /** @type {Error} */ (err); - if (e.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(e)); + reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } if (!key) { diff --git a/src/request/sign-swap/SignSwap.js b/src/request/sign-swap/SignSwap.js index 3148877f2..29cc375fa 100644 --- a/src/request/sign-swap/SignSwap.js +++ b/src/request/sign-swap/SignSwap.js @@ -472,14 +472,14 @@ class SignSwap { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (e) { - const err = /** @type {Error} */ (e); - if (err.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(err)); + reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } if (!key) { @@ -641,9 +641,8 @@ class SignSwap { // the Hub is not compromised. An attacker would need to get access to the Keyguard and Hub servers. tmpCookieEncryptionKey, }); - } catch (e) { - const err = /** @type {Error} */ (e); - reject(err); + } catch (error) { + reject(error instanceof Error ? error : new Error(String(error))); } } diff --git a/src/request/sign-transaction/SignTransaction.js b/src/request/sign-transaction/SignTransaction.js index 3243d07ca..bd0f372d3 100644 --- a/src/request/sign-transaction/SignTransaction.js +++ b/src/request/sign-transaction/SignTransaction.js @@ -166,14 +166,14 @@ class SignTransaction { let key = null; try { key = await KeyStore.instance.get(request.keyInfo.id, passwordBuf); - } catch (e) { - const err = /** @type {Error} */ (e); - if (err.message === 'Invalid key') { + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + if (errorMessage === 'Invalid key') { TopLevelApi.setLoading(false); this._passwordBox.onPasswordIncorrect(); return; } - reject(new Errors.CoreError(err)); + reject(new Errors.CoreError(error instanceof Error ? error : errorMessage)); return; } if (!key) { From 567e828be1e2a110c948e7e13b44603cb78b8d72 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 06:24:32 +0200 Subject: [PATCH 07/14] Typecheck service worker separately to avoid DOM and Webworker lib type clashs Typecheck service worker separately with ts WebWorker lib, and the rest of the Keyguard with ts DOM lib. Previously, both libs were active in all files (even though webworker was referenced only in the ServiceWorker via a triple slash directive, this enables it globally), which by itself is inappropriate, but also causes type clashs between the DOM and the WebWorker types, which previously were silenced by skipping lib checks. Now, with this change, and typescript having been updated to a recent version, checking libs has been enabled again. --- package.json | 2 +- src/{ => service-worker}/ServiceWorker.js | 3 --- src/service-worker/tsconfig.json | 8 ++++++++ tools/build.sh | 2 +- tsconfig.json | 8 ++------ 5 files changed, 12 insertions(+), 11 deletions(-) rename src/{ => service-worker}/ServiceWorker.js (98%) create mode 100644 src/service-worker/tsconfig.json diff --git a/package.json b/package.json index 82eecbdb5..32511a6dd 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "build:bitcoinjs": "yarn --silent browserify tools/bitcoinjs-parts.js -s BitcoinJS | yarn terser --compress --mangle --source-map --output src/lib/bitcoin/BitcoinJS.js", "build:opengsn": "yarn --silent browserify -r @opengsn/common/dist/EIP712/TypedRequestData -s OpenGSN | yarn terser --compress --mangle --source-map --output src/lib/polygon/OpenGSN.js", "test": "karma start", - "typecheck": "tsc", + "typecheck": "tsc && tsc -p src/service-worker/tsconfig.json", "lint": "yarn i18n:build-dictionary && eslint src tools && if ( grep 'fit\\|fdescribe' tests/lib/* ); then exit 1; else exit 0; fi", "lintfix": "eslint --fix src tools", "checkdeps": "node tools/dependencyValidator.js", diff --git a/src/ServiceWorker.js b/src/service-worker/ServiceWorker.js similarity index 98% rename from src/ServiceWorker.js rename to src/service-worker/ServiceWorker.js index f52f38a65..0d4dd3aa0 100644 --- a/src/ServiceWorker.js +++ b/src/service-worker/ServiceWorker.js @@ -1,6 +1,3 @@ -// eslint-disable-next-line spaced-comment -/// - /** * Simplified definition of the browser's native CookieStore. Not to be confused with our CookieStorage. * @typedef {{get: (name: string) => Promise<{ value: string } | undefined>}} CookieStore diff --git a/src/service-worker/tsconfig.json b/src/service-worker/tsconfig.json new file mode 100644 index 000000000..eb563f41e --- /dev/null +++ b/src/service-worker/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "lib": ["WebWorker"] + }, + "include": ["*.js"], + "exclude": [] // Overwrite exclusion of service worker +} diff --git a/tools/build.sh b/tools/build.sh index 2efc41270..38b108994 100755 --- a/tools/build.sh +++ b/tools/build.sh @@ -358,7 +358,7 @@ cp -rv src/assets/* dist/assets/ cp -v src/lib/QrScannerWorker.js dist/lib/ cp -v node_modules/@nimiq/style/nimiq-style.icons.svg dist/assets/ # copy service worker (which has to be in root to work) -cp -v src/ServiceWorker.js dist +cp -v src/service-worker/ServiceWorker.js dist # copy Nimiq files output "‼️ Copying Nimiq files" diff --git a/tsconfig.json b/tsconfig.json index 2afac6c37..ed8047193 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,11 +4,6 @@ "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "lib": ["DOM"], /* Specify library files to be included in the compilation. */ - "skipLibCheck": true, /* Skip checking .d.ts files. We need this currently, because types in */ - /* lib.webworker.d.ts referenced in ServiceWorker.js clash with types */ - /* in lib.dom.d.ts, and also because our typescript version is too old */ - /* to handle the types of ethers. */ - /* TODO once typescript has been updated, this can be removed. */ "allowJs": true, /* Allow javascript files to be compiled. */ "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ @@ -72,8 +67,9 @@ "instrumented", "coverage", "src/lib/QrScannerWorker.js", - "src/translations/index.js", "src/lib/bitcoin/BitcoinJS.js", "src/lib/polygon/OpenGSN.js", + "src/service-worker", // Service worker is checked with a separate tsconfig in src/service-worker + "src/translations/index.js" ] } From d809966542b57040422de117f5552f9b3d63ab73 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 08:17:29 +0200 Subject: [PATCH 08/14] Remove @ts-ignores resolved with updated ts; change others to @ts-expect-error --- src/common.js | 5 ++--- src/components/RecoveryWordsInputField.js | 2 +- src/config/config.local.js | 2 +- src/config/config.mainnet.js | 2 +- src/config/config.testnet.js | 2 +- src/lib/BrowserDetection.js | 2 +- src/lib/ClipboardUtils.js | 1 - src/lib/CookieStorage.js | 2 +- src/lib/CurrencyInfo.js | 4 +--- src/lib/QrScanner.js | 2 -- src/lib/bitcoin/BitcoinKey.js | 2 +- src/lib/bitcoin/BitcoinRequestParserMixin.js | 2 +- src/lib/bitcoin/BitcoinUtils.js | 4 ++-- src/lib/swap/HtlcUtils.js | 6 +++--- .../sign-btc-transaction/SignBtcTransaction.js | 2 +- .../SignBtcTransactionApi.js | 3 +-- src/request/sign-message/SignMessage.js | 2 -- src/request/sign-swap/SignSwapApi.js | 3 +-- .../sign-transaction/SignTransactionApi.js | 3 +-- src/request/swap-iframe/SwapIFrameApi.js | 18 +++++++++--------- tsconfig.json | 2 +- 21 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/common.js b/src/common.js index 8e15de0bc..c79e7dbf1 100644 --- a/src/common.js +++ b/src/common.js @@ -88,8 +88,7 @@ async function runKeyguard(RequestApiClass, opts) { // eslint-disable-line no-un // Back arrow functionality document.body.addEventListener('click', event => { - // @ts-ignore (Property 'matches' does not exist on type 'EventTarget'.) - if (!event.target || !event.target.matches('a.page-header-back-button')) return; + if (!(event.target instanceof HTMLElement && event.target.matches('a.page-header-back-button'))) return; window.history.back(); }); @@ -125,7 +124,7 @@ async function runKeyguard(RequestApiClass, opts) { // eslint-disable-line no-un window.rpcServer = new RpcServer(CONFIG.ALLOWED_ORIGIN); options.whitelist.forEach(/** @param {string} method */ method => { - // @ts-ignore (Element implicitly has an 'any' type because type 'TopLevelApi' has no index signature.) + // @ts-expect-error (Element implicitly has an 'any' type because type 'TopLevelApi' has no index signature.) window.rpcServer.onRequest(method, api[method].bind(api)); }); diff --git a/src/components/RecoveryWordsInputField.js b/src/components/RecoveryWordsInputField.js index e7ce9981c..6b1508fcb 100644 --- a/src/components/RecoveryWordsInputField.js +++ b/src/components/RecoveryWordsInputField.js @@ -137,7 +137,7 @@ class RecoveryWordsInputField extends Nimiq.Observable { * @param {ClipboardEvent} e */ _onPaste(e) { - // @ts-ignore (Property 'clipboardData' does not exist on type 'Window'.) + // @ts-expect-error (Property 'clipboardData' does not exist on type 'Window'.) let paste = (e.clipboardData || window.clipboardData).getData('text'); paste = paste.replace(/\s+/g, ' '); if (paste && paste.split(' ').length > 1) { diff --git a/src/config/config.local.js b/src/config/config.local.js index 7f223ec3b..c14a2d6bb 100644 --- a/src/config/config.local.js +++ b/src/config/config.local.js @@ -9,7 +9,7 @@ if (!/^(?:localhost|bs-local.com)$/.test(window.location.hostname) && !ipRegEx.t throw new Error('Using development config is only allowed locally'); } -// @ts-ignore +// @ts-expect-error (ts thinks CONFIG is redeclared in other config files as it doesn't know that only one is active) const CONFIG = { // eslint-disable-line no-unused-vars ALLOWED_ORIGIN: '*', NETWORK: Constants.NETWORK.TEST, diff --git a/src/config/config.mainnet.js b/src/config/config.mainnet.js index c775fdc20..22adf1f46 100644 --- a/src/config/config.mainnet.js +++ b/src/config/config.mainnet.js @@ -1,6 +1,6 @@ /* global Constants */ -// @ts-ignore +// @ts-expect-error (ts thinks CONFIG is redeclared in other config files as it doesn't know that only one is active) const CONFIG = { // eslint-disable-line no-unused-vars ALLOWED_ORIGIN: 'https://hub.nimiq.com', NETWORK: Constants.NETWORK.MAIN, diff --git a/src/config/config.testnet.js b/src/config/config.testnet.js index d90b71d3d..310139c30 100644 --- a/src/config/config.testnet.js +++ b/src/config/config.testnet.js @@ -1,6 +1,6 @@ /* global Constants */ -// @ts-ignore +// @ts-expect-error (ts thinks CONFIG is redeclared in other config files as it doesn't know that only one is active) const CONFIG = { // eslint-disable-line no-unused-vars ALLOWED_ORIGIN: 'https://hub.nimiq-testnet.com', NETWORK: Constants.NETWORK.TEST, diff --git a/src/lib/BrowserDetection.js b/src/lib/BrowserDetection.js index 06800287d..320036cc6 100644 --- a/src/lib/BrowserDetection.js +++ b/src/lib/BrowserDetection.js @@ -27,7 +27,7 @@ class BrowserDetection { // eslint-disable-line no-unused-vars * @returns {boolean} */ static isIOS() { - // @ts-ignore (MSStream is not on window) + // @ts-expect-error (MSStream is not on window) return /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream; } diff --git a/src/lib/ClipboardUtils.js b/src/lib/ClipboardUtils.js index 7e609d435..defa6354d 100644 --- a/src/lib/ClipboardUtils.js +++ b/src/lib/ClipboardUtils.js @@ -13,7 +13,6 @@ class ClipboardUtils { // eslint-disable-line no-unused-vars // Prevent keyboard from showing on mobile element.setAttribute('readonly', ''); - // @ts-ignore: css property contain not known to current ts version element.style.contain = 'strict'; element.style.position = 'absolute'; element.style.left = '-9999px'; diff --git a/src/lib/CookieStorage.js b/src/lib/CookieStorage.js index def4a1ff0..8175cd111 100644 --- a/src/lib/CookieStorage.js +++ b/src/lib/CookieStorage.js @@ -313,7 +313,7 @@ CookieStorage.ENCRYPTION_ALGORITHM = 'AES-GCM'; CookieStorage.ENCRYPTION_KEY_SIZE = 256 / 8; // 12 bytes; as recommended in 5.2.1.1 of https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-38d.pdf CookieStorage.ENCRYPTION_INIT_VECTOR_SIZE = 96 / 8; -// @ts-ignore _calculateEncodedMetadataSize is private +// @ts-expect-error _calculateEncodedMetadataSize is private CookieStorage.MAX_ENCODED_METADATA_SIZE = CookieStorage._calculateEncodedMetadataSize({ isEncrypted: true, chunkCount: 1, diff --git a/src/lib/CurrencyInfo.js b/src/lib/CurrencyInfo.js index 173106eaa..0a9750e51 100644 --- a/src/lib/CurrencyInfo.js +++ b/src/lib/CurrencyInfo.js @@ -62,7 +62,6 @@ class CurrencyInfo { let supportsDisplayNames = 'DisplayNames' in Intl; // also normalizes the locales [this.locale] = supportsDisplayNames - // @ts-ignore TODO use proper types once https://github.com/microsoft/TypeScript/pull/44022 is available ? Intl.DisplayNames.supportedLocalesOf(nameLocalesToTry) : Intl.NumberFormat.supportedLocalesOf(nameLocalesToTry); if (supportsDisplayNames && !this.locale) { @@ -90,8 +89,7 @@ class CurrencyInfo { if (supportsDisplayNames) { try { // Use DisplayNames if available as it provides better names. - // @ts-ignore TODO use proper types once https://github.com/microsoft/TypeScript/pull/44022 is merged - this.name = new Intl.DisplayNames(this.locale, { type: 'currency' }).of(currencyCode); + this.name = new Intl.DisplayNames(this.locale, { type: 'currency' }).of(currencyCode) || ''; } catch (e) { // Ignore and continue with if block below. } diff --git a/src/lib/QrScanner.js b/src/lib/QrScanner.js index 3b39845e8..4f4f2a648 100644 --- a/src/lib/QrScanner.js +++ b/src/lib/QrScanner.js @@ -61,12 +61,10 @@ class QrScanner { // Allow inline playback on iPhone instead of requiring full screen playback, // see https://webkit.org/blog/6784/new-video-policies-for-ios/ - // @ts-ignore Property 'playsInline' does not exist on type 'HTMLVideoElement' this.$video.playsInline = true; // Allow play() on iPhone without requiring a user gesture. Should not really be needed as camera stream // includes no audio, but just to be safe. this.$video.muted = true; - // @ts-ignore Property 'disablePictureInPicture' does not exist on type 'HTMLVideoElement' this.$video.disablePictureInPicture = true; this.$video.addEventListener('play', this._onPlay); this.$video.addEventListener('loadedmetadata', this._onLoadedMetaData); diff --git a/src/lib/bitcoin/BitcoinKey.js b/src/lib/bitcoin/BitcoinKey.js index caa1a7d0c..08c98127b 100644 --- a/src/lib/bitcoin/BitcoinKey.js +++ b/src/lib/bitcoin/BitcoinKey.js @@ -92,7 +92,7 @@ class BitcoinKey { // eslint-disable-line no-unused-vars const mnemonic = Nimiq.MnemonicUtils.entropyToMnemonic(this.secret); const seed = Nimiq.MnemonicUtils.mnemonicToSeed(mnemonic); - // @ts-ignore Argument of type 'import("...").Buffer' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'import("...").Buffer' is not assignable to parameter of type 'Buffer'. const master = BitcoinJS.bip32.fromSeed(BitcoinJS.Buffer.from(seed), network); return master.derivePath(path); } diff --git a/src/lib/bitcoin/BitcoinRequestParserMixin.js b/src/lib/bitcoin/BitcoinRequestParserMixin.js index 75bed3054..db2d19e3f 100644 --- a/src/lib/bitcoin/BitcoinRequestParserMixin.js +++ b/src/lib/bitcoin/BitcoinRequestParserMixin.js @@ -72,7 +72,7 @@ function BitcoinRequestParserMixin(clazz) { : undefined, keyPath: this.parseBitcoinPath(input.keyPath, `input[${index}].keypath`), // Address added only for display - // @ts-ignore Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. address: BitcoinJS.address.fromOutputScript(script, BitcoinUtils.Network), }; diff --git a/src/lib/bitcoin/BitcoinUtils.js b/src/lib/bitcoin/BitcoinUtils.js index 324ec2dfd..f76399ddf 100644 --- a/src/lib/bitcoin/BitcoinUtils.js +++ b/src/lib/bitcoin/BitcoinUtils.js @@ -129,7 +129,7 @@ class BitcoinUtils { // eslint-disable-line no-unused-vars if (bip === BitcoinConstants.BIP.BIP49) { return BitcoinJS.address.toBase58Check( - // @ts-ignore Argument of type 'Uint8Array | Buffer' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'Uint8Array | Buffer' is not assignable to type 'Buffer'. bytes, BitcoinConstants.BIP49_ADDRESS_VERSIONS[CONFIG.BTC_NETWORK][1], // 0 => BIP44, 1 => BIP49 ); @@ -137,7 +137,7 @@ class BitcoinUtils { // eslint-disable-line no-unused-vars if (bip === BitcoinConstants.BIP.BIP84) { return BitcoinJS.address.toBech32( - // @ts-ignore Argument of type 'Uint8Array | Buffer' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'Uint8Array | Buffer' is not assignable to type 'Buffer'. bytes, BitcoinConstants.BIP84_ADDRESS_VERSION, BitcoinConstants.BIP84_ADDRESS_PREFIX[CONFIG.BTC_NETWORK], diff --git a/src/lib/swap/HtlcUtils.js b/src/lib/swap/HtlcUtils.js index d4ef8bec0..65c432bbb 100644 --- a/src/lib/swap/HtlcUtils.js +++ b/src/lib/swap/HtlcUtils.js @@ -42,7 +42,7 @@ class HtlcUtils { // eslint-disable-line no-unused-vars const error = new Errors.InvalidRequestError('Invalid BTC HTLC script'); if (!script || !(script instanceof Uint8Array) || !script.length) throw error; - // @ts-ignore Type 'import(...).Buffer' is not assignable to type 'Buffer'. + // @ts-expect-error Type 'import(...).Buffer' is not assignable to type 'Buffer'. const chunks = BitcoinJS.script.decompile(BitcoinJS.Buffer.from(script)); if (!chunks) throw error; const asm = BitcoinJS.script.toASM(chunks).split(' '); @@ -77,7 +77,7 @@ class HtlcUtils { // eslint-disable-line no-unused-vars // Check timeout // Bitcoin HTLC timeouts are backdated 1 hour, to account for Bitcoin's // minimum age for valid transaction locktimes (6 blocks). - // @ts-ignore Argument of type 'Buffer' is not assignable to parameter of type 'Buffer' + // @ts-expect-error Argument of type 'Buffer' is not assignable to parameter of type 'Buffer' const timeoutTimestamp = BitcoinJS.script.number.decode(BitcoinJS.Buffer.from(asm[++i], 'hex')) + (60 * 60); if (asm[++i] !== 'OP_CHECKLOCKTIMEVERIFY' || asm[++i] !== 'OP_DROP') throw error; @@ -164,7 +164,7 @@ class HtlcUtils { // eslint-disable-line no-unused-vars writeVector(witness); - // @ts-ignore Type 'Buffer' is not assignable to type 'Buffer'. + // @ts-expect-error Type 'Buffer' is not assignable to type 'Buffer'. return BitcoinJS.Buffer.from(buffer); } } diff --git a/src/request/sign-btc-transaction/SignBtcTransaction.js b/src/request/sign-btc-transaction/SignBtcTransaction.js index ae3410c5f..778e98f12 100644 --- a/src/request/sign-btc-transaction/SignBtcTransaction.js +++ b/src/request/sign-btc-transaction/SignBtcTransaction.js @@ -217,7 +217,7 @@ class SignBtcTransaction { const psbt = new BitcoinJS.Psbt({ network: BitcoinUtils.Network }); // Add inputs - // @ts-ignore Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. psbt.addInputs(request.inputs); // Add outputs psbt.addOutputs(outputs); diff --git a/src/request/sign-btc-transaction/SignBtcTransactionApi.js b/src/request/sign-btc-transaction/SignBtcTransactionApi.js index ba7041c9d..9bd31e210 100644 --- a/src/request/sign-btc-transaction/SignBtcTransactionApi.js +++ b/src/request/sign-btc-transaction/SignBtcTransactionApi.js @@ -74,8 +74,7 @@ class SignBtcTransactionApi extends BitcoinRequestParserMixin(TopLevelApi) { if (!layout) { return SignBtcTransactionApi.Layouts.STANDARD; } - // @ts-ignore (Property 'values' does not exist on type 'ObjectConstructor'.) - if (Object.values(SignBtcTransactionApi.Layouts).indexOf(layout) === -1) { + if (!Object.values(SignBtcTransactionApi.Layouts).includes(/** @type {any} */ (layout))) { throw new Errors.InvalidRequestError('Invalid selected layout'); } return /** @type KeyguardRequest.SignBtcTransactionRequestLayout */ (layout); diff --git a/src/request/sign-message/SignMessage.js b/src/request/sign-message/SignMessage.js index 31b9569d5..d2bc61aeb 100644 --- a/src/request/sign-message/SignMessage.js +++ b/src/request/sign-message/SignMessage.js @@ -40,11 +40,9 @@ class SignMessage { const $tabWidthSelector = /** @type {HTMLDivElement} */ ($page.querySelector('#tab-width-selector')); const tws = new TabWidthSelector($tabWidthSelector); - // @ts-ignore Property 'tabSize' does not exist on type 'CSSStyleDeclaration' $message.style.tabSize = tws.width; tws.on(TabWidthSelector.Events.INPUT, width => { - // @ts-ignore Property 'tabSize' does not exist on type 'CSSStyleDeclaration' $message.style.tabSize = width; }); diff --git a/src/request/sign-swap/SignSwapApi.js b/src/request/sign-swap/SignSwapApi.js index 0badaf59f..4e09448c8 100644 --- a/src/request/sign-swap/SignSwapApi.js +++ b/src/request/sign-swap/SignSwapApi.js @@ -285,8 +285,7 @@ class SignSwapApi extends PolygonRequestParserMixin(BitcoinRequestParserMixin(To if (!layout) { return SignSwapApi.Layouts.STANDARD; } - // @ts-ignore (Property 'values' does not exist on type 'ObjectConstructor'.) - if (Object.values(SignSwapApi.Layouts).indexOf(layout) === -1) { + if (!Object.values(SignSwapApi.Layouts).includes(/** @type {any} */ (layout))) { throw new Errors.InvalidRequestError('Invalid selected layout'); } return /** @type KeyguardRequest.SignSwapRequestLayout */ (layout); diff --git a/src/request/sign-transaction/SignTransactionApi.js b/src/request/sign-transaction/SignTransactionApi.js index d9f537783..34c72ec3b 100644 --- a/src/request/sign-transaction/SignTransactionApi.js +++ b/src/request/sign-transaction/SignTransactionApi.js @@ -69,8 +69,7 @@ class SignTransactionApi extends TopLevelApi { if (!layout) { return SignTransactionApi.Layouts.STANDARD; } - // @ts-ignore (Property 'values' does not exist on type 'ObjectConstructor'.) - if (Object.values(SignTransactionApi.Layouts).indexOf(layout) === -1) { + if (!Object.values(SignTransactionApi.Layouts).includes(/** @type {any} */ (layout))) { throw new Errors.InvalidRequestError('Invalid selected layout'); } return /** @type KeyguardRequest.SignTransactionRequestLayout */ (layout); diff --git a/src/request/swap-iframe/SwapIFrameApi.js b/src/request/swap-iframe/SwapIFrameApi.js index fcb605229..fa72f57ff 100644 --- a/src/request/swap-iframe/SwapIFrameApi.js +++ b/src/request/swap-iframe/SwapIFrameApi.js @@ -180,7 +180,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint } const htlcAddress = BitcoinJS.payments.p2wsh({ - // @ts-ignore Type 'Uint8Array' is not assignable to type 'Buffer'. + // @ts-expect-error Type 'Uint8Array' is not assignable to type 'Buffer'. witness: [BitcoinJS.Buffer.from(request.fund.htlcScript)], network: BitcoinUtils.Network, }).address; @@ -205,7 +205,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint } const outputScript = BitcoinJS.payments.p2wsh({ - // @ts-ignore Type 'Uint8Array' is not assignable to type 'Buffer'. + // @ts-expect-error Type 'Uint8Array' is not assignable to type 'Buffer'. witness: [BitcoinJS.Buffer.from(request.redeem.htlcScript)], network: BitcoinUtils.Network, }).output; @@ -423,7 +423,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint const psbt = new BitcoinJS.Psbt({ network: BitcoinUtils.Network }); // Add inputs - // @ts-ignore Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. psbt.addInputs(inputs); // Add outputs psbt.addOutputs(outputs); @@ -434,7 +434,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint // Sign const keyPairs = privateKeys.btc.map(privateKey => BitcoinJS.ECPair.fromPrivateKey( - // @ts-ignore Argument of type 'import("...").Buffer' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'import("...").Buffer' is not assignable to type 'Buffer'. BitcoinJS.Buffer.from(privateKey, 'hex'), )); for (const keyPair of keyPairs) { @@ -468,7 +468,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint const htlcAddress = parsedRequest.fund.htlcAddress; const htlcScript = /** @type {Buffer} */ (BitcoinJS.payments.p2wsh({ - // @ts-ignore Type 'import("...").Buffer' is not assignable to type 'Buffer'. + // @ts-expect-error Type 'import("...").Buffer' is not assignable to type 'Buffer'. witness: [BitcoinJS.Buffer.from(parsedRequest.fund.htlcScript, 'hex')], network: BitcoinUtils.Network, }).output); @@ -484,7 +484,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint script: htlcScript, value: storedRequest.fund.recipientOutput.value, }, - // @ts-ignore Type of type 'import("...").Buffer' is not assignable to type 'Buffer'. + // @ts-expect-error Type of type 'import("...").Buffer' is not assignable to type 'Buffer'. witnessScript: BitcoinJS.Buffer.from(parsedRequest.fund.htlcScript), }); @@ -504,7 +504,7 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint // Sign const refundKeyPair = BitcoinJS.ECPair.fromPrivateKey( - // @ts-ignore Argument of type 'import("...").Buffer' is not assignable to parameter of + // @ts-expect-error Argument of type 'import("...").Buffer' is not assignable to parameter of // type 'Buffer'. BitcoinJS.Buffer.from(privateKeys.btc_refund, 'hex'), ); @@ -643,14 +643,14 @@ class SwapIFrameApi extends BitcoinRequestParserMixin(RequestParser) { // eslint const psbt = new BitcoinJS.Psbt({ network: BitcoinUtils.Network }); // Add inputs - // @ts-ignore Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'Uint8Array' is not assignable to parameter of type 'Buffer'. psbt.addInputs(inputs); // Add outputs psbt.addOutput(output); // Sign const keyPair = BitcoinJS.ECPair.fromPrivateKey( - // @ts-ignore Argument of type 'import("...").Buffer' is not assignable to parameter of type 'Buffer'. + // @ts-expect-error Argument of type 'import("...").Buffer' is not assignable to type 'Buffer'. BitcoinJS.Buffer.from(privateKeys.btc[0], 'hex'), ); psbt.signInput(0, keyPair); diff --git a/tsconfig.json b/tsconfig.json index ed8047193..1f0b8229a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,8 @@ "compilerOptions": { /* Basic Options */ "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "lib": ["DOM", "ES2020"], /* Specify library files to be included in the compilation. */ "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ - "lib": ["DOM"], /* Specify library files to be included in the compilation. */ "allowJs": true, /* Allow javascript files to be compiled. */ "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ From 0ce8286a9de372cf3acc37a6c6607315ea26cab9 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 08:18:59 +0200 Subject: [PATCH 09/14] tsconfig: bump es target version --- tsconfig.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tsconfig.json b/tsconfig.json index 1f0b8229a..d23dca395 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { /* Basic Options */ - "target": "ES2020", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ - "lib": ["DOM", "ES2020"], /* Specify library files to be included in the compilation. */ + "target": "ES2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017','ES2018' or 'ESNEXT'. */ + "lib": ["DOM", "ES2021"], /* Specify library files to be included in the compilation. */ "module": "CommonJS", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ "allowJs": true, /* Allow javascript files to be compiled. */ "checkJs": true, /* Report errors in .js files. */ From 43d954b4eebc1219d4f897cd28f2aaffb6fb4fd2 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 08:25:26 +0200 Subject: [PATCH 10/14] IqonHash: remove String.padEnd polyfill Browser support for padEnd is now sufficiently good. --- src/lib/IqonHash.js | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/lib/IqonHash.js b/src/lib/IqonHash.js index 4c7116664..dd2cf13cf 100644 --- a/src/lib/IqonHash.js +++ b/src/lib/IqonHash.js @@ -32,7 +32,7 @@ class IqonHash { /* eslint-disable-line no-unused-vars */ // leading to an invalid bottom index and feature color. Adding // padding creates a bottom feature and accent color where no // existed previously, thus it's not a disrupting change. - return this._padEnd(hash, 13, fullHash[5]); + return hash.padEnd(13, fullHash[5]); } /** @@ -47,21 +47,4 @@ class IqonHash { /* eslint-disable-line no-unused-vars */ } return an; } - - /** - * Polyfill for String.padEnd() - * - * @param {string} string - * @param {number} maxLength - * @param {string} fillString - * @returns {string} - */ - static _padEnd(string, maxLength, fillString) { - if ('padEnd' in String.prototype) return string.padEnd(maxLength, fillString); - - while (string.length < maxLength) { - string += fillString; - } - return string.substring(0, Math.max(string.length, maxLength)); - } } From ef203e13a165d1bcd2040a916b93a545f91708af Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 09:56:00 +0200 Subject: [PATCH 11/14] Consistent typing of $els in components Allow them to be passed as null or undefined everywhere. --- src/components/AddressInfo.js | 5 ++++- src/components/BalanceDistributionBar.js | 4 ++-- src/components/Copyable.js | 4 ++-- src/components/DownloadLoginFile.js | 2 +- src/components/FileImporter.js | 4 ++-- src/components/Identicon.js | 4 ++-- src/components/LanguagePicker.js | 4 ++-- src/components/LoginFileAnimation.js | 4 ++-- src/components/LoginFileIcon.js | 2 +- src/components/PasswordBox.js | 6 +++--- src/components/PasswordInput.js | 6 +++--- src/components/PasswordSetterBox.js | 6 +++--- src/components/PaymentInfoLine.js | 4 ++-- src/components/PolygonAddressInfo.js | 2 +- src/components/ProgressIndicator.js | 10 +++++----- src/components/QrVideoScanner.js | 4 ++-- src/components/RecoveryWords.js | 6 +++--- src/components/TabWidthSelector.js | 2 +- src/components/Timer.js | 4 ++-- src/components/ValidateWords.js | 4 ++-- src/request/create/IdenticonSelector.js | 6 +++--- 21 files changed, 48 insertions(+), 45 deletions(-) diff --git a/src/components/AddressInfo.js b/src/components/AddressInfo.js index 8b6feb03a..ac776d786 100644 --- a/src/components/AddressInfo.js +++ b/src/components/AddressInfo.js @@ -14,8 +14,9 @@ class AddressInfo { // eslint-disable-line no-unused-vars /** * Inserts this AddressInfo into $el overwriting the original content of $el. - * @param {HTMLElement} $el + * @param {?HTMLElement} [$el] * @param {boolean} [isDetailedView = false] + * @returns {HTMLElement} */ renderTo($el, isDetailedView = false) { $el = $el || document.createElement('div'); @@ -94,5 +95,7 @@ class AddressInfo { // eslint-disable-line no-unused-vars const copyableAddress = new Copyable(this._addressInfo.userFriendlyAddress, $address); $el.appendChild(copyableAddress.getElement()); } + + return $el; } } diff --git a/src/components/BalanceDistributionBar.js b/src/components/BalanceDistributionBar.js index 6d4d70c21..692e11b45 100644 --- a/src/components/BalanceDistributionBar.js +++ b/src/components/BalanceDistributionBar.js @@ -15,7 +15,7 @@ class BalanceDistributionBar { // eslint-disable-line no-unused-vars * leftFiatRate: number, * rightFiatRate: number, * }} settings - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] */ constructor(settings, $el) { this.$el = BalanceDistributionBar._createElement($el); @@ -74,7 +74,7 @@ class BalanceDistributionBar { // eslint-disable-line no-unused-vars } /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] * @returns {HTMLDivElement} */ static _createElement($el) { diff --git a/src/components/Copyable.js b/src/components/Copyable.js index 788427f35..becdffce8 100644 --- a/src/components/Copyable.js +++ b/src/components/Copyable.js @@ -4,7 +4,7 @@ class Copyable { /** * @param {string} text - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] */ constructor(text, $el) { this._text = text; @@ -37,7 +37,7 @@ class Copyable { } /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] * @returns {HTMLDivElement} */ static _createElement($el) { diff --git a/src/components/DownloadLoginFile.js b/src/components/DownloadLoginFile.js index 628a23f6e..39bd3a9ed 100644 --- a/src/components/DownloadLoginFile.js +++ b/src/components/DownloadLoginFile.js @@ -9,7 +9,7 @@ class DownloadLoginFile extends Nimiq.Observable { /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] */ constructor($el) { super(); diff --git a/src/components/FileImporter.js b/src/components/FileImporter.js index f9b45f1f0..ffcd8027a 100644 --- a/src/components/FileImporter.js +++ b/src/components/FileImporter.js @@ -38,7 +38,7 @@ class FileImporter extends Nimiq.Observable { } /** - * @param {HTMLLabelElement} [$el] + * @param {?HTMLLabelElement} [$el] * @param {boolean} [displayFile = true] */ constructor($el, displayFile = true) { @@ -63,7 +63,7 @@ class FileImporter extends Nimiq.Observable { } /** - * @param {HTMLLabelElement} [$el] + * @param {?HTMLLabelElement} [$el] * @returns {HTMLLabelElement} */ static _createElement($el) { diff --git a/src/components/Identicon.js b/src/components/Identicon.js index 724b4116c..3def97b87 100644 --- a/src/components/Identicon.js +++ b/src/components/Identicon.js @@ -3,7 +3,7 @@ class Identicon { // eslint-disable-line no-unused-vars /** * @param {string} [address] - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] */ constructor(address, $el) { this._address = address; @@ -31,7 +31,7 @@ class Identicon { // eslint-disable-line no-unused-vars } /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] * @returns {HTMLDivElement} */ static _createElement($el) { diff --git a/src/components/LanguagePicker.js b/src/components/LanguagePicker.js index 06f038107..04a151ac3 100644 --- a/src/components/LanguagePicker.js +++ b/src/components/LanguagePicker.js @@ -2,7 +2,7 @@ class LanguagePicker { // eslint-disable-line no-unused-vars /** - * @param {HTMLSelectElement} [$el] + * @param {?HTMLSelectElement} [$el] */ constructor($el) { this.$el = $el || LanguagePicker._createElement($el); @@ -10,7 +10,7 @@ class LanguagePicker { // eslint-disable-line no-unused-vars /** * Produces a select element that the user can chose an available language from. - * @param {HTMLSelectElement} [$el] + * @param {?HTMLSelectElement} [$el] * @returns {HTMLSelectElement} */ static _createElement($el) { diff --git a/src/components/LoginFileAnimation.js b/src/components/LoginFileAnimation.js index d90b16d8b..86ef55ddf 100644 --- a/src/components/LoginFileAnimation.js +++ b/src/components/LoginFileAnimation.js @@ -3,7 +3,7 @@ class LoginFileAnimation { /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] */ constructor($el) { this._color = 0; @@ -58,7 +58,7 @@ class LoginFileAnimation { } /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] * @returns {HTMLDivElement} */ static _createElement($el) { diff --git a/src/components/LoginFileIcon.js b/src/components/LoginFileIcon.js index ca517b279..48c1bf0e9 100644 --- a/src/components/LoginFileIcon.js +++ b/src/components/LoginFileIcon.js @@ -3,7 +3,7 @@ class LoginFileIcon { // eslint-disable-line no-unused-vars /** * - * @param {HTMLDivElement?} [$el] + * @param {?HTMLDivElement} [$el] */ constructor($el) { this.colorClass = ''; diff --git a/src/components/PasswordBox.js b/src/components/PasswordBox.js index 6ed953517..c5bb17f08 100644 --- a/src/components/PasswordBox.js +++ b/src/components/PasswordBox.js @@ -19,7 +19,7 @@ class PasswordBox extends Nimiq.Observable { // eslint-disable-next-line valid-jsdoc /** - * @param {HTMLFormElement} [$el] + * @param {?HTMLFormElement} [$el] * @param {Partial} [options] */ constructor($el, options = {}) { @@ -41,7 +41,7 @@ class PasswordBox extends Nimiq.Observable { this.$el.classList.toggle('hide-input', this.options.hideInput); - this._passwordInput = new PasswordInput(this.$el.querySelector('[password-input]')); + this._passwordInput = new PasswordInput(/** @type {HTMLElement} */(this.$el.querySelector('[password-input]'))); this._passwordInput.on(PasswordInput.Events.VALID, isValid => this._onInputChangeValidity(isValid)); this.setMinLength(this.options.minLength); @@ -60,7 +60,7 @@ class PasswordBox extends Nimiq.Observable { } /** - * @param {HTMLFormElement | undefined} $el + * @param {?HTMLFormElement | undefined} $el * @param {PasswordBoxOptions} options * @returns {HTMLFormElement} */ diff --git a/src/components/PasswordInput.js b/src/components/PasswordInput.js index ed09692cb..917f23553 100644 --- a/src/components/PasswordInput.js +++ b/src/components/PasswordInput.js @@ -5,10 +5,10 @@ class PasswordInput extends Nimiq.Observable { /** - * @param {?HTMLElement} $el + * @param {?HTMLElement} [$el] * @param {object} [options] - * @param {number=} [options.maxLength = Infinity] - * @param {string=} [options.placeholder = '••••••••'] + * @param {number} [options.maxLength = Infinity] + * @param {string} [options.placeholder = '••••••••'] * @param {'current-password' | 'new-password' | undefined} [options.autocomplete = 'current-password'] */ constructor($el, options = {}) { diff --git a/src/components/PasswordSetterBox.js b/src/components/PasswordSetterBox.js index e2a6faf90..cc03b4dc3 100644 --- a/src/components/PasswordSetterBox.js +++ b/src/components/PasswordSetterBox.js @@ -8,7 +8,7 @@ class PasswordSetterBox extends Nimiq.Observable { // eslint-disable-next-line valid-jsdoc /** - * @param {?HTMLFormElement} $el + * @param {?HTMLFormElement} [$el] * @param {{bgColor?: string, buttonI18nTag?: string}} [options] */ constructor($el, options = {}) { @@ -27,7 +27,7 @@ class PasswordSetterBox extends Nimiq.Observable { this.$el = PasswordSetterBox._createElement($el, this.options); this._passwordInput = new PasswordInput( - this.$el.querySelector('[password-input]'), + /** @type {HTMLElement} */ (this.$el.querySelector('[password-input]')), { maxLength: PasswordSetterBox.PASSWORD_MAX_LENGTH, autocomplete: 'new-password', @@ -52,7 +52,7 @@ class PasswordSetterBox extends Nimiq.Observable { } /** - * @param {?HTMLFormElement} $el + * @param {?HTMLFormElement | undefined} $el * @param {{bgColor: string, buttonI18nTag: string}} options * @returns {HTMLFormElement} */ diff --git a/src/components/PaymentInfoLine.js b/src/components/PaymentInfoLine.js index 6c15c325e..b6d316317 100644 --- a/src/components/PaymentInfoLine.js +++ b/src/components/PaymentInfoLine.js @@ -32,7 +32,7 @@ const FIAT_API_PROVIDER_URL = FIAT_API_PROVIDER_URLS[FiatApi.Provider]; class PaymentInfoLine { // eslint-disable-line no-unused-vars /** * @param {PaymentInfo} paymentInfo - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] */ constructor(paymentInfo, $el) { this.paymentInfo = paymentInfo; @@ -65,7 +65,7 @@ class PaymentInfoLine { // eslint-disable-line no-unused-vars /** * @private - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * @returns {HTMLElement} */ static _createElement($el) { diff --git a/src/components/PolygonAddressInfo.js b/src/components/PolygonAddressInfo.js index 3e9e789bd..bf9718c9c 100644 --- a/src/components/PolygonAddressInfo.js +++ b/src/components/PolygonAddressInfo.js @@ -17,7 +17,7 @@ class PolygonAddressInfo { // eslint-disable-line no-unused-vars /** * Inserts this AddressInfo into $el overwriting the original content of $el. - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * @returns {HTMLElement} */ renderTo($el) { diff --git a/src/components/ProgressIndicator.js b/src/components/ProgressIndicator.js index a43afa44b..bc35151d5 100644 --- a/src/components/ProgressIndicator.js +++ b/src/components/ProgressIndicator.js @@ -3,7 +3,7 @@ class ProgressIndicator extends Nimiq.Observable { // eslint-disable-line no-unused-vars /** - * @param {?HTMLElement} $el + * @param {?Element | undefined} $el * @param {number} numberOfSteps * @param {number} [currentStep] */ @@ -14,9 +14,9 @@ class ProgressIndicator extends Nimiq.Observable { // eslint-disable-line no-unu } /** - * @param {?HTMLElement} $el + * @param {?Element | undefined} $el * @param {number} numberOfSteps - * @returns {HTMLElement} + * @returns {Element} */ static _createElement($el, numberOfSteps) { $el = $el || document.createElement('div'); @@ -33,12 +33,12 @@ class ProgressIndicator extends Nimiq.Observable { // eslint-disable-line no-unu return $el; } - /** @returns {HTMLElement} @deprecated */ + /** @returns {Element} @deprecated */ getElement() { return this.$el; } - /** @type {HTMLElement} */ + /** @type {Element} */ get element() { return this.$el; } diff --git a/src/components/QrVideoScanner.js b/src/components/QrVideoScanner.js index 880a9360d..854be7a90 100644 --- a/src/components/QrVideoScanner.js +++ b/src/components/QrVideoScanner.js @@ -6,7 +6,7 @@ class QrVideoScanner extends Nimiq.Observable { // eslint-disable-next-line valid-jsdoc /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] * @param {(result: string) => boolean} [validator] * @param {number} [reportFrequency = 7000] */ @@ -110,7 +110,7 @@ class QrVideoScanner extends Nimiq.Observable { } /** - * @param {HTMLDivElement} [$el] + * @param {?HTMLDivElement} [$el] * @returns {HTMLDivElement} */ static _createElement($el) { diff --git a/src/components/RecoveryWords.js b/src/components/RecoveryWords.js index f72c91660..ec8ed066c 100644 --- a/src/components/RecoveryWords.js +++ b/src/components/RecoveryWords.js @@ -7,7 +7,7 @@ class RecoveryWords extends Nimiq.Observable { /** * - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * @param {boolean} [providesInput] */ constructor($el, providesInput) { @@ -40,7 +40,7 @@ class RecoveryWords extends Nimiq.Observable { } /** - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * @param {boolean} input * @returns {HTMLElement} * */ @@ -160,7 +160,7 @@ class RecoveryWords extends Nimiq.Observable { /** * @param {number} index - * @param {?string} paste + * @param {string} [paste] */ _setFocusToNextInput(index, paste) { index = Math.max(index, 0); diff --git a/src/components/TabWidthSelector.js b/src/components/TabWidthSelector.js index a776319fa..35867b706 100644 --- a/src/components/TabWidthSelector.js +++ b/src/components/TabWidthSelector.js @@ -4,7 +4,7 @@ class TabWidthSelector extends Nimiq.Observable { /** - * @param {?HTMLElement} $el + * @param {?HTMLElement} [$el] */ constructor($el) { super(); diff --git a/src/components/Timer.js b/src/components/Timer.js index 3e2dc87a7..3a7370ce9 100644 --- a/src/components/Timer.js +++ b/src/components/Timer.js @@ -15,7 +15,7 @@ class Timer extends Nimiq.Observable { /** * @param {number} startTime * @param {number} endTime - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] */ constructor(startTime, endTime, $el) { super(); @@ -52,7 +52,7 @@ class Timer extends Nimiq.Observable { /** * @private - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * @returns {HTMLElement} */ static _createElement($el) { diff --git a/src/components/ValidateWords.js b/src/components/ValidateWords.js index 4b4eda02e..58e6111d5 100644 --- a/src/components/ValidateWords.js +++ b/src/components/ValidateWords.js @@ -4,7 +4,7 @@ class ValidateWords extends Nimiq.Observable { /** - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] */ constructor($el) { super(); @@ -29,7 +29,7 @@ class ValidateWords extends Nimiq.Observable { } /** - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * @returns {HTMLElement} */ static _createElement($el) { diff --git a/src/request/create/IdenticonSelector.js b/src/request/create/IdenticonSelector.js index e93d888b9..97178295c 100644 --- a/src/request/create/IdenticonSelector.js +++ b/src/request/create/IdenticonSelector.js @@ -5,7 +5,7 @@ class IdenticonSelector extends Nimiq.Observable { /** - * @param {HTMLElement} $el + * @param {?HTMLElement | undefined} $el * @param {string} keyPath */ constructor($el, keyPath) { @@ -15,7 +15,7 @@ class IdenticonSelector extends Nimiq.Observable { this.$el = IdenticonSelector._createElement($el); - /** @type {{ [address: string]: Nimiq.Entropy}} */ + /** @type {{ [address: string]: Nimiq.Entropy }} */ this._volatileEntropies = {}; this.$identicons = /** @type {HTMLElement} */ (this.$el.querySelector('.identicons')); @@ -25,7 +25,7 @@ class IdenticonSelector extends Nimiq.Observable { } /** - * @param {HTMLElement} [$el] + * @param {?HTMLElement} [$el] * * @returns {HTMLElement} */ From 911729a1c0e6fb2e25db89f808416b5976e000ba Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 10:08:42 +0200 Subject: [PATCH 12/14] client: update typescript version --- client/package.json | 2 +- client/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/package.json b/client/package.json index 471ccba20..1fc4b20dd 100644 --- a/client/package.json +++ b/client/package.json @@ -33,6 +33,6 @@ "devDependencies": { "rollup": "^0.64.0", "tslint": "^5.11.0", - "typescript": "^4.9.5" + "typescript": "^5.5.4" } } diff --git a/client/yarn.lock b/client/yarn.lock index fc9742f39..da6ab7e47 100644 --- a/client/yarn.lock +++ b/client/yarn.lock @@ -2865,10 +2865,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.9.5: - version "4.9.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.9.5.tgz#095979f9bcc0d09da324d58d03ce8f8374cbe65a" - integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== +typescript@^5.5.4: + version "5.5.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba" + integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q== ultron@~1.1.0: version "1.1.1" From 5f0f70230578762c03c8816a0bb69de5f016361b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 6 Sep 2024 13:20:59 +0200 Subject: [PATCH 13/14] FiatApi: assign enum values and types based on the provider This was already the case before updating typescript to the latest v3 version in 35859ede, which temporarily removed that handling as is was not compatible with newer typescript versions. This commit reimplements this behavior in a different fashion. --- src/lib/FiatApi.js | 164 ++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 76 deletions(-) diff --git a/src/lib/FiatApi.js b/src/lib/FiatApi.js index 9a5c491e6..1d442d2c0 100644 --- a/src/lib/FiatApi.js +++ b/src/lib/FiatApi.js @@ -50,82 +50,84 @@ FiatApi.SupportedProvider = Object.freeze({ }); /** - * @type {FiatApi.SupportedProvider} - * The effective provider used by the FiatApi. As this is not meant to be changeable at runtime, all code for other - * providers can be commented out, to avoid type confusion. + * @typedef {'CoinGecko'} FiatApi.Provider + * @type {FiatApi.Provider} + * The effective provider used by the FiatApi. As this is not meant to be changeable at runtime, a constant value and + * type are assigned which trigger the correct assignments of enum values and types below. */ FiatApi.Provider = FiatApi.SupportedProvider.CoinGecko; -// switch (FiatApi.Provider) { // eslint-disable-line default-case, -- no default to let ts warn us on missing providers -// case FiatApi.SupportedProvider.CoinGecko: { -/* eslint-disable indent */ - /** - * @enum { 'nim' | 'btc' } - * Crypto currencies supported by the coingecko api that are currently of interest to us. - */ - FiatApi.SupportedCryptoCurrency = Object.freeze({ - NIM: /** @type {'nim'} */ ('nim'), - BTC: /** @type {'btc'} */ ('btc'), - }); +/** + * @enum { 'nim' | 'btc' } + * Crypto currencies supported by the coingecko api that are currently of interest to us. + */ +FiatApi.SupportedCryptoCurrencyCoinGecko = Object.freeze({ + NIM: /** @type {'nim'} */ ('nim'), + BTC: /** @type {'btc'} */ ('btc'), +}); - /** - * @enum {'aed' | 'ars' | 'aud' | 'bdt' | 'bhd' | 'bmd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'czk' | 'dkk' - * | 'eur' | 'gbp' | 'gel' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'lkr' | 'mmk' - * | 'mxn' | 'myr' | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb' - * | 'try' | 'twd' | 'uah' | 'usd' | 'vnd' | 'zar'} - * Fiat currencies supported by the coingecko api. Note that coingecko supports more vs_currencies (see - * https://api.coingecko.com/api/v3/simple/supported_vs_currencies) but also includes crypto currencies and - * ounces of gold amongst others that are not fiat currencies. See FiatApi in @nimiq/utils for how this list was - * assembled. - */ - FiatApi.SupportedFiatCurrency = Object.freeze({ - AED: /** @type {'aed'} */ ('aed'), // Arab Emirates Dirham - ARS: /** @type {'ars'} */ ('ars'), // Argentine Peso - AUD: /** @type {'aud'} */ ('aud'), // Australian Dollar - BDT: /** @type {'bdt'} */ ('bdt'), // Bangladeshi Taka - BHD: /** @type {'bhd'} */ ('bhd'), // Bahraini Dinar - BMD: /** @type {'bmd'} */ ('bmd'), // Bermudan Dollar - BRL: /** @type {'brl'} */ ('brl'), // Brazilian Real - CAD: /** @type {'cad'} */ ('cad'), // Canadian Dollar - CHF: /** @type {'chf'} */ ('chf'), // Swiss Franc - CLP: /** @type {'clp'} */ ('clp'), // Chilean Peso - CNY: /** @type {'cny'} */ ('cny'), // Chinese Yuan - CZK: /** @type {'czk'} */ ('czk'), // Czech Koruna - DKK: /** @type {'dkk'} */ ('dkk'), // Danish Krone - EUR: /** @type {'eur'} */ ('eur'), // Euro - GBP: /** @type {'gbp'} */ ('gbp'), // British Pound - GEL: /** @type {'gel'} */ ('gel'), // Georgian Lari - HKD: /** @type {'hkd'} */ ('hkd'), // Hong Kong Dollar - HUF: /** @type {'huf'} */ ('huf'), // Hungarian Forint - IDR: /** @type {'idr'} */ ('idr'), // Indonesian Rupiah - ILS: /** @type {'ils'} */ ('ils'), // Israeli New Shekel - INR: /** @type {'inr'} */ ('inr'), // Indian Rupee - JPY: /** @type {'jpy'} */ ('jpy'), // Japanese Yen - KRW: /** @type {'krw'} */ ('krw'), // South Korean Won - KWD: /** @type {'kwd'} */ ('kwd'), // Kuwaiti Dinar - LKR: /** @type {'lkr'} */ ('lkr'), // Sri Lankan Rupee - MMK: /** @type {'mmk'} */ ('mmk'), // Burmese Kyat - MXN: /** @type {'mxn'} */ ('mxn'), // Mexican Peso - MYR: /** @type {'myr'} */ ('myr'), // Malaysian Ringgit - NGN: /** @type {'ngn'} */ ('ngn'), // Nigerian Naira - NOK: /** @type {'nok'} */ ('nok'), // Norwegian Krone - NZD: /** @type {'nzd'} */ ('nzd'), // New Zealand Dollar - PHP: /** @type {'php'} */ ('php'), // Philippine Peso - PKR: /** @type {'pkr'} */ ('pkr'), // Pakistani Rupee - PLN: /** @type {'pln'} */ ('pln'), // Poland Złoty - RUB: /** @type {'rub'} */ ('rub'), // Russian Ruble - SAR: /** @type {'sar'} */ ('sar'), // Saudi Riyal - SEK: /** @type {'sek'} */ ('sek'), // Swedish Krona - SGD: /** @type {'sgd'} */ ('sgd'), // Singapore Dollar - THB: /** @type {'thb'} */ ('thb'), // Thai Baht - TRY: /** @type {'try'} */ ('try'), // Turkish Lira - TWD: /** @type {'twd'} */ ('twd'), // New Taiwan Dollar - UAH: /** @type {'uah'} */ ('uah'), // Ukrainian Hryvnia - USD: /** @type {'usd'} */ ('usd'), // United States Dollar - // VEF: /** @type {'vef'} */ ('vef'), // Retired Venezuelan Bolívar Fuerte replaced by VES. Rates are off. - VND: /** @type {'vnd'} */ ('vnd'), // Vietnamese Đồng - ZAR: /** @type {'zar'} */ ('zar'), // South African Rand - }); +/** + * @enum {'aed' | 'ars' | 'aud' | 'bdt' | 'bhd' | 'bmd' | 'brl' | 'cad' | 'chf' | 'clp' | 'cny' | 'czk' | 'dkk' | 'eur' + * | 'gbp' | 'gel' | 'hkd' | 'huf' | 'idr' | 'ils' | 'inr' | 'jpy' | 'krw' | 'kwd' | 'lkr' | 'mmk' | 'mxn' | 'myr' + * | 'ngn' | 'nok' | 'nzd' | 'php' | 'pkr' | 'pln' | 'rub' | 'sar' | 'sek' | 'sgd' | 'thb' | 'try' | 'twd' | 'uah' + * | 'usd' | 'vnd' | 'zar'} + * Fiat currencies supported by the coingecko api, see https://api.coingecko.com/api/v3/simple/supported_vs_currencies. + * More vs_currencies are supported, but those also include crypto currencies and ounces of gold amongst others which + * are not fiat currencies. See FiatApi in @nimiq/utils for how this list was assembled. + */ +FiatApi.SupportedFiatCurrencyCoinGecko = Object.freeze({ + AED: /** @type {'aed'} */ ('aed'), // Arab Emirates Dirham + ARS: /** @type {'ars'} */ ('ars'), // Argentine Peso + AUD: /** @type {'aud'} */ ('aud'), // Australian Dollar + BDT: /** @type {'bdt'} */ ('bdt'), // Bangladeshi Taka + BHD: /** @type {'bhd'} */ ('bhd'), // Bahraini Dinar + BMD: /** @type {'bmd'} */ ('bmd'), // Bermudan Dollar + BRL: /** @type {'brl'} */ ('brl'), // Brazilian Real + CAD: /** @type {'cad'} */ ('cad'), // Canadian Dollar + CHF: /** @type {'chf'} */ ('chf'), // Swiss Franc + CLP: /** @type {'clp'} */ ('clp'), // Chilean Peso + CNY: /** @type {'cny'} */ ('cny'), // Chinese Yuan + CZK: /** @type {'czk'} */ ('czk'), // Czech Koruna + DKK: /** @type {'dkk'} */ ('dkk'), // Danish Krone + EUR: /** @type {'eur'} */ ('eur'), // Euro + GBP: /** @type {'gbp'} */ ('gbp'), // British Pound + GEL: /** @type {'gel'} */ ('gel'), // Georgian Lari + HKD: /** @type {'hkd'} */ ('hkd'), // Hong Kong Dollar + HUF: /** @type {'huf'} */ ('huf'), // Hungarian Forint + IDR: /** @type {'idr'} */ ('idr'), // Indonesian Rupiah + ILS: /** @type {'ils'} */ ('ils'), // Israeli New Shekel + INR: /** @type {'inr'} */ ('inr'), // Indian Rupee + JPY: /** @type {'jpy'} */ ('jpy'), // Japanese Yen + KRW: /** @type {'krw'} */ ('krw'), // South Korean Won + KWD: /** @type {'kwd'} */ ('kwd'), // Kuwaiti Dinar + LKR: /** @type {'lkr'} */ ('lkr'), // Sri Lankan Rupee + MMK: /** @type {'mmk'} */ ('mmk'), // Burmese Kyat + MXN: /** @type {'mxn'} */ ('mxn'), // Mexican Peso + MYR: /** @type {'myr'} */ ('myr'), // Malaysian Ringgit + NGN: /** @type {'ngn'} */ ('ngn'), // Nigerian Naira + NOK: /** @type {'nok'} */ ('nok'), // Norwegian Krone + NZD: /** @type {'nzd'} */ ('nzd'), // New Zealand Dollar + PHP: /** @type {'php'} */ ('php'), // Philippine Peso + PKR: /** @type {'pkr'} */ ('pkr'), // Pakistani Rupee + PLN: /** @type {'pln'} */ ('pln'), // Poland Złoty + RUB: /** @type {'rub'} */ ('rub'), // Russian Ruble + SAR: /** @type {'sar'} */ ('sar'), // Saudi Riyal + SEK: /** @type {'sek'} */ ('sek'), // Swedish Krona + SGD: /** @type {'sgd'} */ ('sgd'), // Singapore Dollar + THB: /** @type {'thb'} */ ('thb'), // Thai Baht + TRY: /** @type {'try'} */ ('try'), // Turkish Lira + TWD: /** @type {'twd'} */ ('twd'), // New Taiwan Dollar + UAH: /** @type {'uah'} */ ('uah'), // Ukrainian Hryvnia + USD: /** @type {'usd'} */ ('usd'), // United States Dollar + // VEF: /** @type {'vef'} */ ('vef'), // Retired Venezuelan Bolívar Fuerte replaced by VES. Rates are off. + VND: /** @type {'vnd'} */ ('vnd'), // Vietnamese Đồng + ZAR: /** @type {'zar'} */ ('zar'), // South African Rand +}); + +switch (FiatApi.Provider) { // eslint-disable-line default-case, -- no default to let ts warn us on missing providers + case FiatApi.SupportedProvider.CoinGecko: { + FiatApi.SupportedCryptoCurrency = FiatApi.SupportedCryptoCurrencyCoinGecko; + FiatApi.SupportedFiatCurrency = FiatApi.SupportedFiatCurrencyCoinGecko; /** * Coingecko api url. Note that the origin must be whitelisted in the csp. @@ -140,7 +142,17 @@ FiatApi.Provider = FiatApi.SupportedProvider.CoinGecko; [FiatApi.SupportedCryptoCurrency.BTC]: 'bitcoin', }); -/* eslint-enable indent */ -// break; -// } -// } + break; + } +} + +/** + * @typedef {FiatApi.Provider extends 'CoinGecko' + * ? FiatApi.SupportedCryptoCurrencyCoinGecko + * : never + * } FiatApi.SupportedCryptoCurrency + * @typedef {FiatApi.Provider extends 'CoinGecko' + * ? FiatApi.SupportedFiatCurrencyCoinGecko + * : never + * } FiatApi.SupportedFiatCurrency + */ From d4101cb4ac1feefa995c7face9cbfe946dbe4039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6ren?= Date: Mon, 9 Sep 2024 15:32:28 +0200 Subject: [PATCH 14/14] Wrapped negative AND to double negative OR For readability and easier understanding. --- src/common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common.js b/src/common.js index c79e7dbf1..931e3b063 100644 --- a/src/common.js +++ b/src/common.js @@ -88,7 +88,7 @@ async function runKeyguard(RequestApiClass, opts) { // eslint-disable-line no-un // Back arrow functionality document.body.addEventListener('click', event => { - if (!(event.target instanceof HTMLElement && event.target.matches('a.page-header-back-button'))) return; + if (!(event.target instanceof HTMLElement) || !event.target.matches('a.page-header-back-button')) return; window.history.back(); });