fix: persist autoRestoreRan on globalThis to survive Vite HMR (BUG-F7)
Module re-evaluation during HMR reset autoRestoreRan to false, causing duplicate auth-restore API calls. Now persists flag on globalThis. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
1c296c6f1c
commit
c288b23c13
@@ -128,4 +128,34 @@ describe('useNostr', () => {
|
|||||||
expect(result).toBe(false)
|
expect(result).toBe(false)
|
||||||
vi.useRealTimers()
|
vi.useRealTimers()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('BUG-F7: autoRestoreRan survives module re-import (HMR)', async () => {
|
||||||
|
// Set up conditions that trigger auto-restore: pubkey stored, no bot, valid JWT
|
||||||
|
localStorage.setItem('bf_pubkey', JSON.stringify('a'.repeat(64)))
|
||||||
|
mockGetToken.mockReturnValue('valid-jwt')
|
||||||
|
mockIsTokenExpired.mockReturnValue(false)
|
||||||
|
mockAuthFetch.mockResolvedValue({ json: () => Promise.resolve({ exists: true, bot: { id: 'b1', name: 'Bot' } }) })
|
||||||
|
|
||||||
|
// Clear globalThis flag
|
||||||
|
delete (globalThis as any).__bf_autoRestoreRan
|
||||||
|
|
||||||
|
// First import triggers auto-restore
|
||||||
|
vi.resetModules()
|
||||||
|
await import('../useNostr')
|
||||||
|
await new Promise(r => setTimeout(r, 0)) // flush microtasks
|
||||||
|
expect(mockAuthFetch).toHaveBeenCalledTimes(1)
|
||||||
|
|
||||||
|
// globalThis flag should be set
|
||||||
|
expect((globalThis as any).__bf_autoRestoreRan).toBe(true)
|
||||||
|
|
||||||
|
// Second import (simulating HMR) should NOT trigger auto-restore again
|
||||||
|
vi.resetModules()
|
||||||
|
mockAuthFetch.mockClear()
|
||||||
|
await import('../useNostr')
|
||||||
|
await new Promise(r => setTimeout(r, 0))
|
||||||
|
expect(mockAuthFetch).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
delete (globalThis as any).__bf_autoRestoreRan
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -95,8 +95,8 @@ const bot = ref<BotData | null>(loadStored('bf_bot'))
|
|||||||
const profilePicUrl = ref<string | null>(loadStored('bf_pic'))
|
const profilePicUrl = ref<string | null>(loadStored('bf_pic'))
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
// Guard: only auto-restore once across all component mounts
|
// Guard: only auto-restore once — survives Vite HMR module re-evaluation
|
||||||
let autoRestoreRan = false
|
let autoRestoreRan = (globalThis as any).__bf_autoRestoreRan ?? false
|
||||||
// Flag: skip relay pic fetch for freshly generated keys (no profile exists)
|
// Flag: skip relay pic fetch for freshly generated keys (no profile exists)
|
||||||
let freshlyGenerated = false
|
let freshlyGenerated = false
|
||||||
// In-memory nsec for current session (never auto-persisted to localStorage)
|
// In-memory nsec for current session (never auto-persisted to localStorage)
|
||||||
@@ -116,7 +116,8 @@ if (typeof document !== 'undefined') {
|
|||||||
|
|
||||||
// Auto-restore session from JWT on first load
|
// Auto-restore session from JWT on first load
|
||||||
if (!autoRestoreRan && pubkey.value && !bot.value && getToken() && !isTokenExpired()) {
|
if (!autoRestoreRan && pubkey.value && !bot.value && getToken() && !isTokenExpired()) {
|
||||||
autoRestoreRan = true
|
autoRestoreRan = true;
|
||||||
|
(globalThis as any).__bf_autoRestoreRan = true
|
||||||
authFetch('/api/auth/login', {
|
authFetch('/api/auth/login', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@@ -129,7 +130,8 @@ if (!autoRestoreRan && pubkey.value && !bot.value && getToken() && !isTokenExpir
|
|||||||
}).catch(err => console.warn('[Nostr] auto-restore failed:', err))
|
}).catch(err => console.warn('[Nostr] auto-restore failed:', err))
|
||||||
} else if (!autoRestoreRan && pubkey.value && !getToken()) {
|
} else if (!autoRestoreRan && pubkey.value && !getToken()) {
|
||||||
// No JWT — clear stale pubkey from before the JWT migration
|
// No JWT — clear stale pubkey from before the JWT migration
|
||||||
autoRestoreRan = true
|
autoRestoreRan = true;
|
||||||
|
(globalThis as any).__bf_autoRestoreRan = true
|
||||||
pubkey.value = null
|
pubkey.value = null
|
||||||
store('bf_pubkey', null)
|
store('bf_pubkey', null)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user