//! SPDX-FileCopyrightText: 2025 Chris Duncan <chris@codecow.com>
//! SPDX-License-Identifier: GPL-3.0-or-later
+/**
+ * Executes a function after a timeout, with additional controls to manage the
+ * timer. Note: `this.#ticker` is a no-op every second in order to keep the
+ * timer from being put to sleep by the system.
+ */
export class VaultTimer {
#f: () => any
#isPaused: boolean = false
}
}
+ reset (t: number) {
+ this.stop()
+ this.#remaining = t
+ this.#start = performance.now()
+ this.#ticker = setInterval(Function.prototype, 1000)
+ this.#timeout = setTimeout(this.#f, this.#remaining)
+ }
+
resume () {
if (this.#isPaused) {
this.#start = performance.now()
this.#isPaused = false
}
}
+
+ stop () {
+ clearTimeout(this.#timeout)
+ clearInterval(this.#ticker)
+ this.#remaining = 0
+ this.#start = 0
+ this.#isPaused = false
+ }
}
let _locked: boolean = true
let _timeout: number = 120_000
-let _timer: VaultTimer = new VaultTimer(() => { }, 0)
+let _timer: VaultTimer = new VaultTimer(_autolock, _timeout)
let _type: 'BIP-44' | 'BLAKE2b' | 'Exodus' | undefined = undefined
let _id: UUID | undefined = undefined
let _seed: ArrayBuffer | undefined = undefined
*/
function config (timeout?: number): Promise<void> {
try {
- _timer?.pause()
+ _timer.pause()
if (_locked) {
throw new VaultError('Wallet is locked')
}
- if (typeof timeout === 'number') {
- if (timeout < 10) {
- throw new VaultError('Timeout must be at least 10 seconds')
- }
- if (timeout > 600) {
- throw new VaultError('Timeout must be at most 10 minutes')
- }
- _timeout = timeout * 1000
- _timer = new VaultTimer(_autolock, _timeout)
+ if (typeof timeout !== 'number') {
+ throw new TypeError('Invalid timeout period', { cause: timeout })
}
+ if (timeout < 10) {
+ throw new VaultError('Timeout must be at least 10 seconds')
+ }
+ if (timeout > 600) {
+ throw new VaultError('Timeout must be at most 10 minutes')
+ }
+ _timeout = timeout * 1000
+ _timer.reset(_timeout)
return Promise.resolve()
} catch (err) {
console.error(err)
- _timer?.resume()
+ _timer.resume()
throw new VaultError('Failed to configure Vault', { cause: err })
}
}
for (const result of results) {
data[result.index] = result.publicKey
}
- _timer = new VaultTimer(_autolock, _timeout)
+ _timer.reset(_timeout)
return data
})
.catch(err => {
pub.fill(0)
const sig = nano25519_sign(new Uint8Array(data), sk)
sk.fill(0)
- _timer = new VaultTimer(_autolock, _timeout)
+ _timer.reset(_timeout)
return { signature: sig.buffer }
})
.catch(err => {
_seed = seed
_mnemonic = mnemonic
_locked = false
- _timer = new VaultTimer(_autolock, _timeout)
+ _timer.reset(_timeout)
return Promise.resolve({ isLocked: false })
})
.catch(err => {
}
return WalletAesGcm.encrypt(_type, _id, key, _seed, _mnemonic)
.then(({ iv, encrypted }) => {
- _timer = new VaultTimer(_autolock, _timeout)
+ _timer.reset(_timeout)
return { iv, salt, encrypted }
})
.catch(err => {