fix: WebSocket reconnect race, parse error tracking, RPC timeout reduction, vendor chunk split
- F8: Add isReconnecting flag to prevent parallel reconnection attempts - F9: Track JSON parse errors, force reconnect after 3 consecutive failures - F11: Reduce RPC timeout to 15s, add jitter to retry backoff - F12: Add vendor chunk splitting for vue/router/pinia - F13: DOMPurify already applied to QR SVGs — verified - F14: Replace O(n) goals alias lookup with Map-based O(1) - F15: Wrap 7 localStorage.setItem calls in try/catch across 5 stores Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
94f2de4a64
commit
8e38342d53
@@ -103,7 +103,7 @@ export const useAIPermissionsStore = defineStore('aiPermissions', () => {
|
||||
}
|
||||
|
||||
function save() {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify([...enabled.value]))
|
||||
try { localStorage.setItem(STORAGE_KEY, JSON.stringify([...enabled.value])) } catch { /* localStorage full or unavailable */ }
|
||||
}
|
||||
|
||||
function isEnabled(category: AIContextCategory): boolean {
|
||||
|
||||
@@ -41,7 +41,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
|
||||
isAuthenticated.value = true
|
||||
sessionValidated = true
|
||||
localStorage.setItem('neode-auth', 'true')
|
||||
try { localStorage.setItem('neode-auth', 'true') } catch { /* localStorage full or unavailable */ }
|
||||
|
||||
// Initialize data structure immediately so dashboard can render
|
||||
await initializeData()
|
||||
@@ -62,7 +62,7 @@ export const useAppStore = defineStore('app', () => {
|
||||
async function completeLoginAfterTotp(): Promise<void> {
|
||||
isAuthenticated.value = true
|
||||
sessionValidated = true
|
||||
localStorage.setItem('neode-auth', 'true')
|
||||
try { localStorage.setItem('neode-auth', 'true') } catch { /* localStorage full or unavailable */ }
|
||||
await initializeData()
|
||||
connectWebSocket().catch((err) => {
|
||||
if (import.meta.env.DEV) console.warn('[Store] WebSocket connection failed after TOTP login, will retry:', err)
|
||||
|
||||
@@ -75,7 +75,7 @@ function getApprovedOrigins(): Set<string> {
|
||||
function saveApprovedOrigin(origin: string) {
|
||||
const origins = getApprovedOrigins()
|
||||
origins.add(origin)
|
||||
localStorage.setItem(APPROVED_ORIGINS_KEY, JSON.stringify([...origins]))
|
||||
try { localStorage.setItem(APPROVED_ORIGINS_KEY, JSON.stringify([...origins])) } catch { /* localStorage full or unavailable */ }
|
||||
}
|
||||
|
||||
export interface NostrConsentRequest {
|
||||
|
||||
@@ -13,10 +13,20 @@ const APP_ALIASES: Record<string, string[]> = {
|
||||
'bitcoin-knots': ['bitcoin', 'bitcoin-core'],
|
||||
}
|
||||
|
||||
/** Pre-built reverse lookup: variant pkgId → Set of canonical appIds it matches */
|
||||
const ALIAS_REVERSE = new Map<string, Set<string>>()
|
||||
for (const [canonical, aliases] of Object.entries(APP_ALIASES)) {
|
||||
for (const alias of aliases) {
|
||||
let set = ALIAS_REVERSE.get(alias)
|
||||
if (!set) { set = new Set(); ALIAS_REVERSE.set(alias, set) }
|
||||
set.add(canonical)
|
||||
}
|
||||
}
|
||||
|
||||
function matchesAppId(pkgId: string, appId: string): boolean {
|
||||
if (pkgId === appId) return true
|
||||
const aliases = APP_ALIASES[appId]
|
||||
return aliases ? aliases.includes(pkgId) : false
|
||||
const reverseHits = ALIAS_REVERSE.get(pkgId)
|
||||
return reverseHits ? reverseHits.has(appId) : false
|
||||
}
|
||||
|
||||
export const useGoalStore = defineStore('goals', () => {
|
||||
@@ -32,7 +42,7 @@ export const useGoalStore = defineStore('goals', () => {
|
||||
}
|
||||
|
||||
function save() {
|
||||
localStorage.setItem(STORAGE_KEY, JSON.stringify(progress.value))
|
||||
try { localStorage.setItem(STORAGE_KEY, JSON.stringify(progress.value)) } catch { /* localStorage full or unavailable */ }
|
||||
}
|
||||
|
||||
function getGoalStatus(goalId: string): GoalStatus {
|
||||
|
||||
@@ -16,13 +16,13 @@ export const useUIModeStore = defineStore('uiMode', () => {
|
||||
function syncFromBackend(backendMode: UIMode | undefined) {
|
||||
if (backendMode && ['gamer', 'easy', 'chat'].includes(backendMode)) {
|
||||
mode.value = backendMode
|
||||
localStorage.setItem(STORAGE_KEY, backendMode)
|
||||
try { localStorage.setItem(STORAGE_KEY, backendMode) } catch { /* localStorage full or unavailable */ }
|
||||
}
|
||||
}
|
||||
|
||||
function setMode(newMode: UIMode) {
|
||||
mode.value = newMode
|
||||
localStorage.setItem(STORAGE_KEY, newMode)
|
||||
try { localStorage.setItem(STORAGE_KEY, newMode) } catch { /* localStorage full or unavailable */ }
|
||||
}
|
||||
|
||||
function cycleMode(): UIMode {
|
||||
|
||||
Reference in New Issue
Block a user