fix(ui): handle blocked storage getters (#1330)

Co-authored-by: Isaac Sanchez <isanchez-hawkins@arize.com>
This commit is contained in:
Isaac Sanchez-Hawkins
2026-05-19 17:32:36 +03:00
committed by GitHub
co-authored by Isaac Sanchez
parent 69ea4767f7
commit 5189eb586e
2 changed files with 66 additions and 4 deletions
@@ -0,0 +1,48 @@
import { describe, expect, test } from 'bun:test';
const importSafeStorage = async () => {
return await import(`./safeStorage.ts?test=${Date.now()}-${Math.random()}`) as typeof import('./safeStorage');
};
describe('safeStorage', () => {
test('falls back to memory when storage getters throw', async () => {
const previousWindow = Object.getOwnPropertyDescriptor(globalThis, 'window');
const throwingWindow = {};
Object.defineProperties(throwingWindow, {
localStorage: {
get() {
throw new Error('localStorage blocked');
},
},
sessionStorage: {
get() {
throw new Error('sessionStorage blocked');
},
},
});
Object.defineProperty(globalThis, 'window', {
configurable: true,
value: throwingWindow,
});
try {
const { getSafeSessionStorage, getSafeStorage } = await importSafeStorage();
const storage = getSafeStorage();
const sessionStorage = getSafeSessionStorage();
storage.setItem('local-key', 'local-value');
sessionStorage.setItem('session-key', 'session-value');
expect(storage.getItem('local-key')).toBe('local-value');
expect(sessionStorage.getItem('session-key')).toBe('session-value');
} finally {
if (previousWindow) {
Object.defineProperty(globalThis, 'window', previousWindow);
} else {
delete (globalThis as { window?: unknown }).window;
}
}
});
});
+18 -4
View File
@@ -1,6 +1,18 @@
let safeStorageInstance: Storage | null = null;
let safeSessionStorageInstance: Storage | null = null;
const getWindowStorage = (key: 'localStorage' | 'sessionStorage'): Storage | null => {
if (typeof window === 'undefined') {
return null;
}
try {
return window[key] ?? null;
} catch {
return null;
}
};
const createInMemoryStorage = (): Storage => {
const store = new Map<string, string>();
return {
@@ -22,11 +34,12 @@ const createInMemoryStorage = (): Storage => {
};
const createSafeStorage = (): Storage => {
if (typeof window === 'undefined' || !window.localStorage) {
const baseStorage = getWindowStorage('localStorage');
if (!baseStorage) {
return createInMemoryStorage();
}
const baseStorage = window.localStorage;
const fallback = createInMemoryStorage();
let storageAvailable = true;
@@ -123,11 +136,12 @@ export const getSafeStorage = (): Storage => {
};
const createSafeSessionStorage = (): Storage => {
if (typeof window === 'undefined' || !window.sessionStorage) {
const baseStorage = getWindowStorage('sessionStorage');
if (!baseStorage) {
return createInMemoryStorage();
}
const baseStorage = window.sessionStorage;
const fallback = createInMemoryStorage();
let storageAvailable = true;