* Creates a storage adapter that prefixes localStorage keys with window ID. * This allows each Electron window to have its own isolated storage namespace. * * On first read, if the window-scoped key doesn't exist but the legacy key does, * it migrates the data to the window-scoped key. * * Mig
()
| 52 | * Migration is performed only once per key per session for performance. |
| 53 | */ |
| 54 | function createWindowScopedStorage<T>() { |
| 55 | return createJSONStorage<T>(() => ({ |
| 56 | getItem: (key: string) => { |
| 57 | const windowKey = `${getWindowId()}:${key}` |
| 58 | let value = localStorage.getItem(windowKey) |
| 59 | |
| 60 | // Only attempt migration if value not found and not already migrated |
| 61 | if (value === null && !migratedKeys.has(windowKey)) { |
| 62 | // Migration 1: Check for old numeric window ID keys (from previous implementation) |
| 63 | const migratedValue = migrateFromNumericWindowId(key, windowKey) |
| 64 | if (migratedValue !== null) { |
| 65 | try { |
| 66 | localStorage.setItem(windowKey, migratedValue) |
| 67 | } catch (e) { |
| 68 | console.warn(`[WindowStorage] Failed to save migrated value for ${windowKey}:`, e) |
| 69 | } |
| 70 | value = migratedValue |
| 71 | } |
| 72 | |
| 73 | // Migration 2: Check legacy key (without any window prefix) |
| 74 | if (value === null) { |
| 75 | const legacyValue = localStorage.getItem(key) |
| 76 | if (legacyValue !== null) { |
| 77 | try { |
| 78 | localStorage.setItem(windowKey, legacyValue) |
| 79 | console.log(`[WindowStorage] Migrated ${key} to ${windowKey}`) |
| 80 | } catch (e) { |
| 81 | console.warn(`[WindowStorage] Failed to save migrated value for ${windowKey}:`, e) |
| 82 | } |
| 83 | value = legacyValue |
| 84 | } |
| 85 | // Mark as migrated even if no legacy key found |
| 86 | migratedKeys.add(windowKey) |
| 87 | } |
| 88 | } |
| 89 | |
| 90 | return value |
| 91 | }, |
| 92 | setItem: (key: string, value: string) => { |
| 93 | const windowKey = `${getWindowId()}:${key}` |
| 94 | try { |
| 95 | localStorage.setItem(windowKey, value) |
| 96 | } catch (e) { |
| 97 | // Handle QuotaExceededError gracefully |
| 98 | console.error(`[WindowStorage] Failed to save ${windowKey}:`, e) |
| 99 | } |
| 100 | }, |
| 101 | removeItem: (key: string) => { |
| 102 | const windowKey = `${getWindowId()}:${key}` |
| 103 | localStorage.removeItem(windowKey) |
| 104 | }, |
| 105 | })) |
| 106 | } |
| 107 | |
| 108 | /** |
| 109 | * Atom with storage that is scoped to the current window. |
no test coverage detected