diff --git a/src/lnurlcash/storageLock.test.ts b/src/lnurlcash/storageLock.test.ts new file mode 100644 index 0000000..a6a9afb --- /dev/null +++ b/src/lnurlcash/storageLock.test.ts @@ -0,0 +1,30 @@ +import {describe, expect, it, vi} from 'vitest' + +import {withStorageLock} from './storageLock' + +describe('withStorageLock fallback', () => { + it('runs unlocked when Web Locks are unavailable without promising serialization', async () => { + vi.stubGlobal('navigator', {}) + const entered: string[] = [] + let releaseFirst: (() => void) | undefined + + try { + const first = withStorageLock('registry', async () => { + entered.push('first') + await new Promise((resolve) => { + releaseFirst = resolve + }) + }) + const second = withStorageLock('registry', () => { + entered.push('second') + }) + + await second + expect(entered).toEqual(['first', 'second']) + releaseFirst?.() + await first + } finally { + vi.unstubAllGlobals() + } + }) +}) diff --git a/src/lnurlcash/storageLock.ts b/src/lnurlcash/storageLock.ts index dc96281..16ff91b 100644 --- a/src/lnurlcash/storageLock.ts +++ b/src/lnurlcash/storageLock.ts @@ -3,12 +3,13 @@ // lose each other's records (worst case: a stale tab overwrites a freshly // persisted rotated note after its old k1 was burned). Falls back to // running unlocked where Web Locks is unavailable (plain-Node tests, very -// old browsers). -export const withStorageLock = ( - name: string, - fn: () => T | Promise -): Promise => { +// old browsers). That fallback provides no cross-tab serialization +// guarantee; the promise hop only normalizes synchronous callback errors. +export const withStorageLock = (name: string, fn: () => T | Promise): Promise => { const locks = typeof navigator !== 'undefined' ? navigator.locks : undefined - if (locks) return locks.request(name, fn) - return Promise.resolve(fn()) + if (locks) return locks.request(name, () => Promise.resolve().then(fn)) + return Promise.resolve().then(fn) } + +export const storageLocksAvailable = (): boolean => + typeof navigator !== 'undefined' && navigator.locks !== undefined