Repository navigation
Expand file tree
/
Copy pathbrowser-storage.js
More file actions
129 lines (119 loc) 路 3.65 KB
/
Copy pathbrowser-storage.js
File metadata and controls
129 lines (119 loc) 路 3.65 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import BaseStorage from './base-storage.js';
import { createStore, hasOwn } from './helpers.js';
let localStorageDriver;
/**
* @locus Client
* @class BrowserStorage
* @summary localStorage driven storage. Extends BaseStorage for shared cache/TTL/get/has logic.
*/
class BrowserStorage extends BaseStorage {
constructor(clientStorage) {
super(clientStorage);
this.init();
}
/**
* @locus Client
* @memberOf BrowserStorage
* @name init
* @summary Load from localStorage, cleanup expired TTL items, populate data/ttlData. Fixes TTL key mapping bug.
* @returns {void 0}
*/
init() {
localStorageDriver = window.localStorage || localStorage;
const expiredKeys = createStore();
const storageKeys = [];
const now = Date.now();
let i = localStorageDriver.length;
while (i--) {
const key = localStorageDriver.key(i);
if (typeof key !== 'string') continue;
storageKeys.push(key);
}
storageKeys.forEach((key) => {
if (key.endsWith(this.TTL_SUFFIX)) {
const expireAt = parseInt(localStorageDriver.getItem(key), 10);
const mainKey = key.slice(0, -this.TTL_SUFFIX.length);
if (expireAt <= now || isNaN(expireAt)) {
expiredKeys[mainKey] = true;
localStorageDriver.removeItem(key);
localStorageDriver.removeItem(mainKey);
delete this.data[mainKey];
delete this.ttlData[mainKey];
} else {
this.ttlData[mainKey] = expireAt;
}
}
});
storageKeys.forEach((key) => {
if (!key.endsWith(this.TTL_SUFFIX) && !hasOwn(expiredKeys, key)) {
const item = localStorageDriver.getItem(key);
if (item !== null) {
this.data[key] = this.unescape(item);
}
}
});
}
/**
* @locus Client
* @memberOf BrowserStorage
* @name set
* @param {String} key - Key to create/overwrite
* @param {any} value - Value (string, object, array, boolean, null, undefined supported via JSON)
* @param {Number} [ttl] - TTL in seconds
* @summary Create/overwrite record in localStorage (with TTL companion key).
* @returns {Boolean}
*/
set(key, value, ttl) {
if (super.set(key, value, ttl)) {
localStorageDriver.setItem(key, this.escape(value));
if (hasOwn(this.ttlData, key)) {
localStorageDriver.setItem(key + this.TTL_SUFFIX, this.ttlData[key]);
} else {
localStorageDriver.removeItem(key + this.TTL_SUFFIX);
}
return true;
}
return false;
}
/**
* @locus Client
* @memberOf BrowserStorage
* @name remove
* @param {String} key - The name of the record to remove
* @summary Remove a record(s).
* @returns {Boolean}
*/
remove(key) {
const result = super.remove(key);
if (typeof key === 'string') {
localStorageDriver.removeItem(key);
localStorageDriver.removeItem(key + this.TTL_SUFFIX);
}
// For empty(), base already looped and called remove on each key (storage cleaned via override)
return result;
}
/**
* @locus Client
* @memberOf BrowserStorage
* @name isSupported
* @summary Returns true if localStorage is supported (handles Private mode in Safari).
* @returns {Boolean}
*/
static isSupported() {
try {
if (typeof window === 'undefined') {
return false;
}
if ('localStorage' in window && window.localStorage !== null) {
// Safari will throw an exception in Private mode
window.localStorage.setItem('___test___', 'test');
window.localStorage.removeItem('___test___');
return true;
}
return false;
} catch (_error) {
return false;
}
}
}
export default BrowserStorage;