Skip to content

Commit ea35fba

Browse files
authored
Merge pull request #186 from moneytree/pr/fallback-session-storage
[LON-142] feat(storage): use sessionStorage when localStorage is not available
2 parents 3da692c + fc1f2cc commit ea35fba

File tree

2 files changed

+81
-14
lines changed

2 files changed

+81
-14
lines changed

src/__tests__/storage.test.ts

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,58 @@
1-
import { set, get, STORE_KEY } from '../storage';
1+
import { set, get, del, STORE_KEY } from '../storage';
2+
3+
function clearStorage() {
4+
window.localStorage.removeItem(STORE_KEY);
5+
window.sessionStorage.removeItem(STORE_KEY);
6+
}
27

38
describe('storage', () => {
4-
test('set, get', () => {
9+
beforeEach(() => {
10+
clearStorage();
11+
});
12+
13+
test('falls back to sessionStorage if localStorage is unavailable', () => {
14+
const originalLocalStorage = window.localStorage;
15+
// @ts-ignore, Suppress TypeScript error when deleting window.localStorage for test simulation.
16+
delete window.localStorage;
17+
window.sessionStorage.setItem(STORE_KEY, JSON.stringify({ key1: 'value1' }));
18+
expect(get('key1')).toBe('value1');
19+
window.localStorage = originalLocalStorage;
20+
});
21+
22+
test('throws if neither storage is available', () => {
23+
const originalLocalStorage = window.localStorage;
24+
const originalSessionStorage = window.sessionStorage;
25+
// @ts-ignore, Suppress TypeScript error when deleting window.localStorage for test simulation.
26+
delete window.localStorage;
27+
// @ts-ignore, Suppress TypeScript error when deleting window.sessionStorage for test simulation.
28+
delete window.sessionStorage;
29+
expect(() => get('key1')).toThrow('Neither localStorage nor sessionStorage is available');
30+
window.localStorage = originalLocalStorage;
31+
window.sessionStorage = originalSessionStorage;
32+
});
33+
34+
test('returns undefined if storage contains invalid JSON', () => {
35+
window.localStorage.setItem(STORE_KEY, '{invalid json}');
536
expect(get('key1')).toBeUndefined();
37+
});
638

39+
test('set, get', () => {
40+
expect(get('key1')).toBeUndefined();
741
set('key1', 'value1');
8-
942
expect(get('key1')).toBe('value1');
1043
});
1144

1245
test('get with invalid existing storage value', () => {
1346
window.localStorage.setItem(STORE_KEY, '"abc"');
14-
1547
expect(get('key1')).toBeUndefined();
16-
1748
window.localStorage.setItem(STORE_KEY, '');
49+
expect(get('key1')).toBeUndefined();
50+
});
1851

52+
test('delete removes key', () => {
53+
set('key1', 'value1');
54+
expect(get('key1')).toBe('value1');
55+
del('key1');
1956
expect(get('key1')).toBeUndefined();
2057
});
2158
});

src/storage.ts

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,48 @@
11
export const STORE_KEY = 'mt-link-javascript-sdk';
22

3-
const localStorage = window.localStorage;
3+
interface StorageData {
4+
[key: string]: string;
5+
}
6+
7+
function isStorageAvailable(storage: Storage): boolean {
8+
try {
9+
const test = '__storage_test__';
10+
storage.setItem(test, test);
11+
storage.removeItem(test);
12+
return true;
13+
} catch (_) {
14+
return false;
15+
}
16+
}
17+
18+
function getAvailableStorage(): Storage {
19+
if (isStorageAvailable(window.localStorage)) return window.localStorage;
420

5-
function getStorageObject(): { [key: string]: string } {
6-
const stringifiedData = localStorage.getItem(STORE_KEY) || '';
7-
let data = {};
21+
if (isStorageAvailable(window.sessionStorage)) {
22+
console.error('localStorage not available, falling back to sessionStorage');
23+
return window.sessionStorage;
24+
}
25+
26+
throw new Error('Neither localStorage nor sessionStorage is available');
27+
}
28+
29+
function getStorageObject(): StorageData {
30+
const storage = getAvailableStorage();
831

932
try {
10-
data = JSON.parse(stringifiedData);
33+
const stringifiedData = storage.getItem(STORE_KEY);
34+
if (!stringifiedData) return {};
35+
36+
return JSON.parse(stringifiedData);
1137
} catch (error) {
12-
data = {};
38+
console.error(`Failed to load or parse data from storage key "${STORE_KEY}":`, error);
39+
return {};
1340
}
41+
}
1442

15-
return data;
43+
function saveStorageObject(data: StorageData): void {
44+
const storage = getAvailableStorage();
45+
storage.setItem(STORE_KEY, JSON.stringify(data));
1646
}
1747

1848
export function get(key: string): string | undefined {
@@ -23,14 +53,14 @@ export function set(key: string, value: string): void {
2353
const data = getStorageObject();
2454
data[key] = value;
2555

26-
localStorage.setItem(STORE_KEY, JSON.stringify(data));
56+
saveStorageObject(data);
2757
}
2858

2959
export function del(key: string): void {
3060
const data = getStorageObject();
3161
delete data[key];
3262

33-
localStorage.setItem(STORE_KEY, JSON.stringify(data));
63+
saveStorageObject(data);
3464
}
3565

3666
export default {

0 commit comments

Comments
 (0)