@@ -76,7 +76,7 @@ type SettingsSwitchProps = Omit
& {
}
export const SettingSwitch = ({ checked, onCheckedChange, displayToggle = true, ...props }: SettingsSwitchProps) => {
return (
- onCheckedChange && onCheckedChange(!checked)}>
+ onCheckedChange && onCheckedChange(!checked)}>
{displayToggle && }
)
diff --git a/src/components/settings/SettingsPage.tsx b/src/components/settings/SettingsPage.tsx
index adc00aa4..b2b93e38 100644
--- a/src/components/settings/SettingsPage.tsx
+++ b/src/components/settings/SettingsPage.tsx
@@ -54,7 +54,7 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps)
const [showXpubsDialog, setShowXpubsDialog] = useState(false)
const [showFeeLimitDialog, setShowFeeLimitDialog] = useState(false)
const hashedPassword = useStore(authStore, (state) => state.state?.hashed_password)
- const { isLogsEnabled } = useFeatures()
+ const { isFeatureEnabled } = useFeatures()
const [isLockingWallet, setIsLockingWallet] = useState(false)
const doOnLockWallet = async () => {
@@ -115,9 +115,7 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps)
{
- setShowFeeLimitDialog(true)
- }}
+ action={() => setShowFeeLimitDialog(true)}
/>
@@ -131,14 +129,14 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps)
setShowSeedDialog(true)}
+ action={() => setShowSeedDialog(true)}
disabled={hashedPassword === undefined}
/>
setShowXpubsDialog(true)}
+ action={() => setShowXpubsDialog(true)}
disabled={hashedPassword === undefined}
/>
@@ -157,7 +155,7 @@ export const SettingsPage = ({ walletFileName, onLockWallet }: SettingPageProps)
icon={FileTextIcon}
title={t('settings.show_logs')}
to={routes.logs}
- disabled={!isLogsEnabled}
+ disabled={!isFeatureEnabled('logs')}
/>
diff --git a/src/components/settings/TxFeeInputField.tsx b/src/components/settings/TxFeeInputField.tsx
index 3f1e3821..0263b76e 100644
--- a/src/components/settings/TxFeeInputField.tsx
+++ b/src/components/settings/TxFeeInputField.tsx
@@ -57,7 +57,7 @@ export const TxFeeInputField = ({
diff --git a/src/components/ui/jam/LockWalletConfirmDialog.tsx b/src/components/ui/jam/LockWalletConfirmDialog.tsx
index d850c282..70c2580f 100644
--- a/src/components/ui/jam/LockWalletConfirmDialog.tsx
+++ b/src/components/ui/jam/LockWalletConfirmDialog.tsx
@@ -52,7 +52,7 @@ export const LockWalletConfirmDialog = ({
-
- walletInfo.refetch()}>
+ void walletInfo.refetch()}
+ >
{t('global.refresh')}
@@ -201,7 +205,7 @@ export const UtxosContent = ({ enabled, walletFileName, addressSummary, jar }: U
size="sm"
variant={selectedUtxos.length === 0 ? 'outline' : undefined}
disabled={!operationsEnabled || selectedUtxos.length === 0 || allSelectedUtxosFrozen}
- onClick={onFreezeClick}
+ onClick={() => void onFreezeClick()}
>
{freezeUtxos.isPending ? : }
{t('jar_details.utxo_list.button_freeze')}
@@ -210,7 +214,7 @@ export const UtxosContent = ({ enabled, walletFileName, addressSummary, jar }: U
size="sm"
variant={selectedUtxos.length === 0 ? 'outline' : undefined}
disabled={!operationsEnabled || selectedUtxos.length === 0 || allSelectedUtxosUnfrozen}
- onClick={onUnfreezeClick}
+ onClick={() => void onUnfreezeClick()}
>
{unfreezeUtxos.isPending ? : }
{t('jar_details.utxo_list.button_unfreeze')}
diff --git a/src/constants/jam.ts b/src/constants/jam.ts
index ed2b562b..fa9a52f7 100644
--- a/src/constants/jam.ts
+++ b/src/constants/jam.ts
@@ -2,6 +2,7 @@ import { percentageToFactor, parseSemanticVersion } from '@/lib/utils'
import type { AmountSats, Milliseconds } from '@/types/global'
import { version as packageInfoVersion } from '../../package.json'
import { JM_API_AUTH_TOKEN_EXPIRY, JM_DUST_THRESHOLD, JM_WALLET_FILE_EXTENSION } from './jm'
+import { parseAsIntOrDefault } from './meta-env-utils'
export const APP_DISPLAY_VERSION = (() => {
return parseSemanticVersion(packageInfoVersion)
@@ -41,7 +42,7 @@ export const OFFER_MINSIZE_DEFAULT: AmountSats = 100_000
export const JAM_JM_SESSION_REFRESH_MIN_INTERVAL: Milliseconds = 5_000
export const JAM_JM_SESSION_REFRESH_DEFAULT_INTERVAL: Milliseconds = 30_000
export const JAM_JM_SESSION_REFRESH_INTERVAL: Milliseconds = Math.max(
- import.meta.env.VITE_JAM_JM_SESSION_REFRESH_INTERVAL ?? JAM_JM_SESSION_REFRESH_DEFAULT_INTERVAL,
+ parseAsIntOrDefault(import.meta.env.VITE_JAM_JM_SESSION_REFRESH_INTERVAL, JAM_JM_SESSION_REFRESH_DEFAULT_INTERVAL),
JAM_JM_SESSION_REFRESH_MIN_INTERVAL,
)
export const JAM_API_AUTH_TOKEN_RENEW_INTERVAL: Milliseconds = Math.round(JM_API_AUTH_TOKEN_EXPIRY * 0.75)
@@ -49,29 +50,31 @@ export const JAM_API_AUTH_TOKEN_RENEW_INTERVAL: Milliseconds = Math.round(JM_API
export const JAM_RESCAN_PROGRESS_MIN_INTERVAL: Milliseconds = 5_000
export const JAM_RESCAN_PROGRESS_DEFAULT_INTERVAL: Milliseconds = 21_000
export const JAM_RESCAN_PROGRESS_INTERVAL: Milliseconds = Math.max(
- import.meta.env.VITE_JAM_RESCAN_PROGRESS_INTERVAL ?? JAM_RESCAN_PROGRESS_DEFAULT_INTERVAL,
+ parseAsIntOrDefault(import.meta.env.VITE_JAM_RESCAN_PROGRESS_INTERVAL, JAM_RESCAN_PROGRESS_DEFAULT_INTERVAL),
JAM_RESCAN_PROGRESS_MIN_INTERVAL,
)
const JAM_SEED_MODAL_MIN_TIMEOUT: Milliseconds = 5_000
const JAM_SEED_MODAL_DEFAULT_TIMEOUT: Milliseconds = 30_000
export const JAM_SEED_MODAL_TIMEOUT: Milliseconds = Math.max(
- import.meta.env.VITE_JAM_SEED_MODAL_TIMEOUT ?? JAM_SEED_MODAL_DEFAULT_TIMEOUT,
+ parseAsIntOrDefault(import.meta.env.VITE_JAM_SEED_MODAL_TIMEOUT, JAM_SEED_MODAL_DEFAULT_TIMEOUT),
JAM_SEED_MODAL_MIN_TIMEOUT,
)
// minimum amount of time in milliseconds the connection must stay open to be considered "healthy"
const JAM_JM_WEBSOCKET_CONNECTION_HEALTHY_MIN_DURATION: Milliseconds = 1_000
export const JAM_JM_WEBSOCKET_CONNECTION_HEALTHY_DURATION: Milliseconds = Math.max(
- import.meta.env.VITE_JM_WEBSOCKET_CONNECTION_HEALTHY_DURATION ?? 0,
+ parseAsIntOrDefault(import.meta.env.VITE_JM_WEBSOCKET_CONNECTION_HEALTHY_DURATION, 0),
JAM_JM_WEBSOCKET_CONNECTION_HEALTHY_MIN_DURATION,
)
const JAM_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_MIN_DURATION: Milliseconds = 1_000
const JAM_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DEFAULT_DURATION: Milliseconds = 3_000
export const JAM_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DURATION: Milliseconds = Math.max(
- import.meta.env.VITE_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DURATION ??
+ parseAsIntOrDefault(
+ import.meta.env.VITE_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DURATION,
JAM_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DEFAULT_DURATION,
+ ),
JAM_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_MIN_DURATION,
)
@@ -80,16 +83,19 @@ export const JAM_JM_WEBSOCKET_CONNECTION_AUTHENTICATED_DURATION: Milliseconds =
const JAM_JM_WEBSOCKET_KEEPALIVE_MESSAGE_MIN_INTERVAL: Milliseconds = 5_000
const JAM_JM_WEBSOCKET_KEEPALIVE_MESSAGE_DEFAULT_INTERVAL: Milliseconds = 30_000
export const JAM_JM_WEBSOCKET_KEEPALIVE_MESSAGE_INTERVAL: Milliseconds = Math.max(
- import.meta.env.VITE_JM_WEBSOCKET_KEEPALIVE_MESSAGE_INTERVAL ?? JAM_JM_WEBSOCKET_KEEPALIVE_MESSAGE_DEFAULT_INTERVAL,
+ parseAsIntOrDefault(
+ import.meta.env.VITE_JM_WEBSOCKET_KEEPALIVE_MESSAGE_INTERVAL,
+ JAM_JM_WEBSOCKET_KEEPALIVE_MESSAGE_DEFAULT_INTERVAL,
+ ),
JAM_JM_WEBSOCKET_KEEPALIVE_MESSAGE_MIN_INTERVAL,
)
export const JAM_JM_WEBSOCKET_RECONNECT_INTERVAL_MIN: Milliseconds = Math.max(
- import.meta.env.VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MIN ?? 0,
+ parseAsIntOrDefault(import.meta.env.VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MIN, 0),
5_000,
)
export const JAM_JM_WEBSOCKET_RECONNECT_INTERVAL_MAX: Milliseconds = Math.max(
- import.meta.env.VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MAX ?? 0,
+ parseAsIntOrDefault(import.meta.env.VITE_JM_WEBSOCKET_RECONNECT_INTERVAL_MAX, 0),
60_000,
)
diff --git a/src/constants/jm.ts b/src/constants/jm.ts
index 1be1686f..60762530 100644
--- a/src/constants/jm.ts
+++ b/src/constants/jm.ts
@@ -1,4 +1,5 @@
import type { AmountSats, Milliseconds } from '@/types/global'
+import { parseAsIntOrDefault } from './meta-env-utils'
export const JM_WALLET_FILE_EXTENSION = '.jmdat'
@@ -9,7 +10,10 @@ export const JM_API_AUTH_TOKEN_EXPIRY_MAX: Milliseconds = Math.round(JM_API_AUTH
export const JM_API_AUTH_TOKEN_EXPIRY: Milliseconds = Math.max(
JM_API_AUTH_TOKEN_EXPIRY_MIN,
Math.min(
- Number.parseInt(import.meta.env.VITE_JM_API_AUTH_TOKEN_EXPIRY_SECONDS, 10) * 1_000,
+ parseAsIntOrDefault(
+ import.meta.env.VITE_JM_API_AUTH_TOKEN_EXPIRY_SECONDS,
+ JM_API_AUTH_TOKEN_EXPIRY_DEFAULT / 1_000,
+ ) * 1_000,
JM_API_AUTH_TOKEN_EXPIRY_MAX,
),
)
diff --git a/src/constants/meta-env-utils.ts b/src/constants/meta-env-utils.ts
new file mode 100644
index 00000000..6f7971da
--- /dev/null
+++ b/src/constants/meta-env-utils.ts
@@ -0,0 +1,5 @@
+// eslint-disable-next-line @typescript-eslint/no-explicit-any -- okay when trying to parse arbitrary value on purpose
+export const parseAsIntOrDefault = (value: any, defaultValue: number) => {
+ const parsed = Number.parseInt(`${value ?? defaultValue}`, 10)
+ return Number.isSafeInteger(parsed) ? parsed : defaultValue
+}
diff --git a/src/context/JamWalletInfoContextProvider.tsx b/src/context/JamWalletInfoContextProvider.tsx
index 3a986832..46194f3b 100644
--- a/src/context/JamWalletInfoContextProvider.tsx
+++ b/src/context/JamWalletInfoContextProvider.tsx
@@ -12,7 +12,6 @@ import {
type AccountMeta,
type AccountSummary,
type AddressMeta,
- type AddressStatus,
type AddressSummary,
type FidelityBondSummary,
type Jar,
@@ -35,7 +34,7 @@ const toAccountSummary = (walletInfo: WalletInfoApiObject): AccountSummary => {
})
const meta: AccountMeta = {
- jarIndex: Number.parseInt(String(__raw.account), 10) as JarIndex,
+ jarIndex: Number.parseInt(String(__raw.account), 10),
branches,
__raw,
}
@@ -58,7 +57,7 @@ const toAddressSummary = (accountSummary: AccountSummary): AddressSummary => {
const meta: AddressMeta = {
address: __raw.address,
- status: __raw.status as AddressStatus,
+ status: __raw.status,
info: {
bech32: info.bech32,
network: info.network,
@@ -131,9 +130,9 @@ export const JamWalletInfoContextProvider = ({
const walletBalanceSummary = toBalanceSummary(utxos)
const utxosByJarIndex = utxos.reduce((acc, utxo) => {
- const key = utxo.mixdepth as JarIndex
+ const key: JarIndex = utxo.mixdepth
acc[key] = acc[key] || []
- acc[key].push(utxo as Utxo)
+ acc[key].push(utxo)
return acc
}, {} as UtxosByJarIndex)
diff --git a/src/hooks/useFeatures.ts b/src/hooks/useFeatures.ts
index c977cee0..cc91615a 100644
--- a/src/hooks/useFeatures.ts
+++ b/src/hooks/useFeatures.ts
@@ -4,15 +4,17 @@ import { isDevMode } from '@/constants/debugFeatures'
import { fetchFeatures } from '@/lib/api/logs'
import { authStore } from '@/store/authStore'
-export interface Features {
- logs?: boolean
-}
+type SupportedFeature = 'logs' // add on demand
-export interface FeatureItem {
+type FeatureItem = {
name: string
enabled: boolean
}
+type FeaturesApiResponse = {
+ features: Record | FeatureItem[]
+}
+
export const useFeatures = () => {
const authState = useStore(authStore, (state) => state.state)
@@ -37,41 +39,41 @@ export const useFeatures = () => {
throw new Error(`Features request failed with status ${response.status}`)
}
- const data = await response.json()
- return data.features as Features
+ return (await response.json()) as FeaturesApiResponse
},
enabled: !!authState?.auth?.token,
retry: false,
+ select: (data: FeaturesApiResponse): FeatureItem[] => {
+ if (!data.features) {
+ return []
+ }
+ // New format: { features: [{ name: 'logs', enabled: true }] }
+ else if (Array.isArray(data.features)) {
+ return data.features
+ }
+ // Old format: { features: { logs: true } }
+ else if (typeof data.features === 'object') {
+ return Object.entries(data.features).map(([name, enabled]) => ({ name, enabled }))
+ } else {
+ console.warn('Could not parse feature response. Disabling all optional features.')
+ return []
+ }
+ },
})
- const isLogsSupported = () => {
- if (error) {
- return false
- }
-
- if (features) {
- // Old format: { features: { logs: true } }
- if (typeof features.logs === 'boolean') {
- return features.logs
- }
-
- // New format: { features: [{ name: 'logs', enabled: true }] }
- if (Array.isArray(features)) {
- return features.some((feature: FeatureItem) => feature.name === 'logs' && feature.enabled === true)
- }
- }
- return false
+ const isFeatureSupported = (featureName: SupportedFeature) => {
+ return features?.some((feature) => feature.name === featureName && feature.enabled === true)
+ }
+ const isFeatureEnabled = (featureName: SupportedFeature) => {
+ return isFeatureSupported(featureName) || isDevMode()
}
-
- // Show logs UI in dev mode even when not supported, but with unsupported message
- const isLogsEnabled = isLogsSupported() || (error && isDevMode())
return {
features,
error,
isLoading,
isFetching,
- isLogsEnabled,
- isLogsSupported: isLogsSupported(),
+ isFeatureSupported: isFeatureSupported,
+ isFeatureEnabled: isFeatureEnabled,
}
}
diff --git a/src/hooks/useJmWebsocket.ts b/src/hooks/useJmWebsocket.ts
index cb27f848..df6a239b 100644
--- a/src/hooks/useJmWebsocket.ts
+++ b/src/hooks/useJmWebsocket.ts
@@ -20,7 +20,7 @@ const calcReconnectInterval = (attemptNumber: number): Milliseconds => {
)
}
-const basePath: string = import.meta.env.VITE_JM_WEBSOCKET_ENDPOINT_PATH
+const basePath: string = String(import.meta.env.VITE_JM_WEBSOCKET_ENDPOINT_PATH)
const basePathWithoutLeadingSlash = basePath.replace(/^\//, '') // remove leading slash
const { protocol, host } = window.location
diff --git a/src/i18n/config.ts b/src/i18n/config.ts
index 8aa8f9a9..ccf80479 100644
--- a/src/i18n/config.ts
+++ b/src/i18n/config.ts
@@ -10,7 +10,7 @@ const resources = languages.reduce((acc, lng) => {
}
}, {})
-i18n.use(LanguageDetector).use(initReactI18next).init({
+void i18n.use(LanguageDetector).use(initReactI18next).init({
resources,
fallbackLng: 'en',
})
diff --git a/src/i18n/testConfig.ts b/src/i18n/testConfig.ts
index f1a75c5e..d9bfb5c4 100644
--- a/src/i18n/testConfig.ts
+++ b/src/i18n/testConfig.ts
@@ -1,7 +1,7 @@
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
-i18n.use(initReactI18next).init({
+void i18n.use(initReactI18next).init({
lng: 'en',
fallbackLng: 'en',
resources: { en: { translations: {} } },
diff --git a/src/lib/api/logs.ts b/src/lib/api/logs.ts
index 8a1e430c..2d059ede 100644
--- a/src/lib/api/logs.ts
+++ b/src/lib/api/logs.ts
@@ -10,7 +10,7 @@ const buildAuthHeader = (token: string) => {
/**
* Validate response content type
*/
-const withExpectedContentTypeOrThrow = async (response: Response, expectedContentType: string) => {
+const withExpectedContentTypeOrThrow = (response: Response, expectedContentType: string) => {
if (!response.ok) {
throw new Error(`Request failed with status ${response.status}`)
}
diff --git a/src/lib/api/orderbook.ts b/src/lib/api/orderbook.ts
index 36a66895..5fe18b8d 100644
--- a/src/lib/api/orderbook.ts
+++ b/src/lib/api/orderbook.ts
@@ -30,14 +30,14 @@ export const fetchOrderbook = async (): Promise => {
throw new Error(`Failed to fetch orderbook: ${response.status}`)
}
- const data = await response.json()
+ const data: unknown = await response.json()
- if (!data || !Array.isArray(data.offers)) {
+ if (!data || typeof data !== 'object' || !('offers' in data) || !Array.isArray(data.offers)) {
console.warn('Unexpected orderbook response structure:', data)
return { offers: [], fidelitybonds: [] }
}
- return data
+ return data as OrderbookResponse
}
export const refreshOrderbook = async (): Promise => {
diff --git a/src/lib/balanceSummary.test.ts b/src/lib/balanceSummary.test.ts
index 6509eec9..d2ffaffa 100644
--- a/src/lib/balanceSummary.test.ts
+++ b/src/lib/balanceSummary.test.ts
@@ -28,7 +28,7 @@ describe('BalanceSummary', () => {
frozen: true,
} as Utxo,
{
- value: 4,
+ value: 5,
mixdepth: 0,
// unfrozen and expired
frozen: false,
@@ -39,10 +39,9 @@ describe('BalanceSummary', () => {
now,
)
- expect(balanceSummary).not.toBeNull()
- expect(balanceSummary!.calculatedTotalBalanceInSats).toBe(10)
- expect(balanceSummary!.calculatedAvailableBalanceInSats).toBe(5)
- expect(balanceSummary!.calculatedFrozenOrLockedBalanceInSats).toBe(5)
+ expect(balanceSummary.calculatedTotalBalanceInSats).toBe(11)
+ expect(balanceSummary.calculatedAvailableBalanceInSats).toBe(6)
+ expect(balanceSummary.calculatedFrozenOrLockedBalanceInSats).toBe(5)
})
it('should populate account balance data', () => {
@@ -71,9 +70,8 @@ describe('BalanceSummary', () => {
now,
)
- expect(balanceSummary).not.toBeNull()
- expect(balanceSummary!.calculatedTotalBalanceInSats).toBe(677777777)
- expect(balanceSummary!.calculatedAvailableBalanceInSats).toBe(333333333)
- expect(balanceSummary!.calculatedFrozenOrLockedBalanceInSats).toBe(344444444)
+ expect(balanceSummary.calculatedTotalBalanceInSats).toBe(677777777)
+ expect(balanceSummary.calculatedAvailableBalanceInSats).toBe(333333333)
+ expect(balanceSummary.calculatedFrozenOrLockedBalanceInSats).toBe(344444444)
})
})
diff --git a/src/lib/balanceSummary.ts b/src/lib/balanceSummary.ts
index 65509cbb..9955c69d 100644
--- a/src/lib/balanceSummary.ts
+++ b/src/lib/balanceSummary.ts
@@ -26,37 +26,8 @@ const calculateFrozenOrLockedBalance = (utxos: Utxo[], refTime: Milliseconds = D
export const toBalanceSummary = (utxos: Utxo[], now?: Milliseconds): BalanceSummary => {
const refTime = now !== undefined ? now : Date.now()
- const utxosByAccount = (utxos ?? []).reduce(
- (acc, utxo) => {
- const key = `${utxo.mixdepth}`
- acc[key] = acc[key] || []
- acc[key].push(utxo as Utxo)
- return acc
- },
- {} as { [key: string]: Utxo[] },
- )
-
- const totalCalculatedByAccount = Object.fromEntries(
- Object.entries(utxosByAccount).map(([account, utxos]) => {
- return [account, utxos.reduce((acc, utxo) => acc + utxo.value, 0)]
- }),
- )
- const frozenOrLockedCalculatedByAccount = Object.fromEntries(
- Object.entries(utxosByAccount).map(([account, utxos]) => {
- return [account, calculateFrozenOrLockedBalance(utxos, refTime)]
- }),
- )
-
- const walletTotalCalculated: AmountSats = Object.values(totalCalculatedByAccount).reduce(
- (acc, totalSats) => acc + totalSats,
- 0,
- )
-
- const walletFrozenOrLockedCalculated: AmountSats = Object.values(frozenOrLockedCalculatedByAccount).reduce(
- (acc, frozenOrLockedSats) => acc + frozenOrLockedSats,
- 0,
- )
-
+ const walletTotalCalculated: AmountSats = utxos.reduce((acc, utxo) => acc + utxo.value, 0)
+ const walletFrozenOrLockedCalculated: AmountSats = calculateFrozenOrLockedBalance(utxos, refTime)
const walletAvailableCalculated = walletTotalCalculated - walletFrozenOrLockedCalculated
return {
diff --git a/src/lib/config.ts b/src/lib/config.ts
index 47197b87..ee0b65b6 100644
--- a/src/lib/config.ts
+++ b/src/lib/config.ts
@@ -10,18 +10,18 @@ const buildAuthHeader = (token: ApiToken): [string, string] => {
return ['x-jm-authorization', `Bearer ${token}`]
}
-async function loggingRequestInterceptor(request: Request) {
+function loggingRequestInterceptor(request: Request) {
console.debug('[onRequest]', request)
return request
}
-async function loggingResponseInterceptor(response: Response) {
+function loggingResponseInterceptor(response: Response) {
console.debug('[onResponse]', response)
return response
}
const createJamAuthenticationMiddleware = () => {
// eslint-disable-next-line unicorn/consistent-function-scoping -- false positive
- return async (request: Request) => {
+ return (request: Request) => {
const authState = authStore.getState().state
if (authState?.auth?.token) {
const authHeader = buildAuthHeader(authState.auth.token)
@@ -32,7 +32,7 @@ const createJamAuthenticationMiddleware = () => {
}
export const createApiClient = (): Client => {
- const baseUrl: string = import.meta.env.VITE_JM_API_BASE_URL
+ const baseUrl = String(import.meta.env.VITE_JM_API_BASE_URL)
const clientOptions: ClientOptions = { baseUrl }
console.debug('Setting up JM API client…', clientOptions)
diff --git a/src/lib/hash.test.ts b/src/lib/hash.test.ts
index ff4a4caa..cfe6a244 100644
--- a/src/lib/hash.test.ts
+++ b/src/lib/hash.test.ts
@@ -2,7 +2,7 @@ import { describe, it, expect } from 'vitest'
import { DEFAULT_PBKDF_ITERATIONS, hashPassword } from './hash'
describe('hash', () => {
- it('DEFAULT_PBKDF_ITERATIONS', async () => {
+ it('DEFAULT_PBKDF_ITERATIONS', () => {
expect(DEFAULT_PBKDF_ITERATIONS).toBe(210_000)
})
diff --git a/src/lib/utils.test.ts b/src/lib/utils.test.ts
index dcaa0037..cd2e94ff 100644
--- a/src/lib/utils.test.ts
+++ b/src/lib/utils.test.ts
@@ -116,7 +116,9 @@ describe('setIntervalDebounced', () => {
const error = new Error('Test error')
const callback = vi.fn().mockRejectedValueOnce(error).mockResolvedValueOnce(undefined)
const onTimerIdChanged = vi.fn()
- const onError = vi.fn((_, loop) => loop())
+ const onError = vi.fn((_, loop) => {
+ loop()
+ })
setIntervalDebounced(callback, 1000, onTimerIdChanged, onError)
@@ -406,7 +408,7 @@ describe('delayedPromise', () => {
// Initially the promise should not be resolved
let resolved = false
- delayPromise.then(() => {
+ void delayPromise.then(() => {
resolved = true
})
@@ -417,13 +419,14 @@ describe('delayedPromise', () => {
// Now the promise should be resolved
await expect(delayPromise).resolves.toBeUndefined()
+ expect(resolved).toBe(true)
})
it('should not resolve before 500ms', async () => {
const delayPromise = delayedPromise(500)
let resolved = false
- delayPromise.then(() => {
+ void delayPromise.then(() => {
resolved = true
})
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index 7fd28158..cf221e4f 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -75,10 +75,11 @@ export function setIntervalDebounced(
) {
;(function loop() {
onTimerIdChanged(
- setTimeout(async () => {
+ setTimeout(() => {
try {
- await callback()
- loop()
+ Promise.resolve(callback())
+ .then(() => loop())
+ .catch((error) => onError(error, loop))
} catch (error: unknown) {
onError(error, loop)
}
@@ -100,7 +101,7 @@ export const percentageToFactor = (val: number, precision = 6) => {
return Number((val / 100).toFixed(precision))
}
-export const isValidNumber = (val: number | undefined | null): val is number => {
+export const isValidNumber = (val: unknown): val is number => {
return val !== undefined && typeof val === 'number' && !Number.isNaN(val)
}
diff --git a/vite.config.ts b/vite.config.ts
index 21be9e68..573553c5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -21,7 +21,7 @@ const {
// https://vite.dev/config/
export default defineConfig((): UserConfig => {
if (!SUPPORTED_BACKENDS.includes(JAM_BACKEND)) {
- throw new Error(`Unsupported backend: Use one of ${SUPPORTED_BACKENDS}`)
+ throw new Error(`Unsupported backend: Use one of [${SUPPORTED_BACKENDS.join(', ')}]`)
}
if (JAM_BACKEND === BACKEND_STANDALONE && JAM_API_PORT === undefined) {