UNPKG

16.2 kBJavaScriptView Raw
1"use strict";
2// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3// SPDX-License-Identifier: Apache-2.0
4Object.defineProperty(exports, "__esModule", { value: true });
5var tslib_1 = require("tslib");
6var core_1 = require("@aws-amplify/core");
7var Utils_1 = require("./Utils");
8var StorageCache_1 = require("./StorageCache");
9var logger = new core_1.ConsoleLogger('Cache');
10/**
11 * Customized storage based on the SessionStorage or LocalStorage with LRU implemented
12 */
13var BrowserStorageCacheClass = /** @class */ (function (_super) {
14 tslib_1.__extends(BrowserStorageCacheClass, _super);
15 /**
16 * initialize the cache
17 * @param config - the configuration of the cache
18 */
19 function BrowserStorageCacheClass(config) {
20 var _this = this;
21 var cacheConfig = config
22 ? Object.assign({}, Utils_1.defaultConfig, config)
23 : Utils_1.defaultConfig;
24 _this = _super.call(this, cacheConfig) || this;
25 _this.config.storage = cacheConfig.storage;
26 _this.getItem = _this.getItem.bind(_this);
27 _this.setItem = _this.setItem.bind(_this);
28 _this.removeItem = _this.removeItem.bind(_this);
29 return _this;
30 }
31 /**
32 * decrease current size of the cache
33 *
34 * @private
35 * @param amount - the amount of the cache size which needs to be decreased
36 */
37 BrowserStorageCacheClass.prototype._decreaseCurSizeInBytes = function (amount) {
38 var curSize = this.getCacheCurSize();
39 this.config.storage.setItem(this.cacheCurSizeKey, (curSize - amount).toString());
40 };
41 /**
42 * increase current size of the cache
43 *
44 * @private
45 * @param amount - the amount of the cache szie which need to be increased
46 */
47 BrowserStorageCacheClass.prototype._increaseCurSizeInBytes = function (amount) {
48 var curSize = this.getCacheCurSize();
49 this.config.storage.setItem(this.cacheCurSizeKey, (curSize + amount).toString());
50 };
51 /**
52 * update the visited time if item has been visited
53 *
54 * @private
55 * @param item - the item which need to be refreshed
56 * @param prefixedKey - the key of the item
57 *
58 * @return the refreshed item
59 */
60 BrowserStorageCacheClass.prototype._refreshItem = function (item, prefixedKey) {
61 item.visitedTime = Utils_1.getCurrTime();
62 this.config.storage.setItem(prefixedKey, JSON.stringify(item));
63 return item;
64 };
65 /**
66 * check wether item is expired
67 *
68 * @private
69 * @param key - the key of the item
70 *
71 * @return true if the item is expired.
72 */
73 BrowserStorageCacheClass.prototype._isExpired = function (key) {
74 var text = this.config.storage.getItem(key);
75 var item = JSON.parse(text);
76 if (Utils_1.getCurrTime() >= item.expires) {
77 return true;
78 }
79 return false;
80 };
81 /**
82 * delete item from cache
83 *
84 * @private
85 * @param prefixedKey - the key of the item
86 * @param size - optional, the byte size of the item
87 */
88 BrowserStorageCacheClass.prototype._removeItem = function (prefixedKey, size) {
89 var itemSize = size
90 ? size
91 : JSON.parse(this.config.storage.getItem(prefixedKey)).byteSize;
92 this._decreaseCurSizeInBytes(itemSize);
93 // remove the cache item
94 this.config.storage.removeItem(prefixedKey);
95 };
96 /**
97 * put item into cache
98 *
99 * @private
100 * @param prefixedKey - the key of the item
101 * @param itemData - the value of the item
102 * @param itemSizeInBytes - the byte size of the item
103 */
104 BrowserStorageCacheClass.prototype._setItem = function (prefixedKey, item) {
105 // update the cache size
106 this._increaseCurSizeInBytes(item.byteSize);
107 try {
108 this.config.storage.setItem(prefixedKey, JSON.stringify(item));
109 }
110 catch (setItemErr) {
111 // if failed, we need to rollback the cache size
112 this._decreaseCurSizeInBytes(item.byteSize);
113 logger.error("Failed to set item " + setItemErr);
114 }
115 };
116 /**
117 * total space needed when poping out items
118 *
119 * @private
120 * @param itemSize
121 *
122 * @return total space needed
123 */
124 BrowserStorageCacheClass.prototype._sizeToPop = function (itemSize) {
125 var spaceItemNeed = this.getCacheCurSize() + itemSize - this.config.capacityInBytes;
126 var cacheThresholdSpace = (1 - this.config.warningThreshold) * this.config.capacityInBytes;
127 return spaceItemNeed > cacheThresholdSpace
128 ? spaceItemNeed
129 : cacheThresholdSpace;
130 };
131 /**
132 * see whether cache is full
133 *
134 * @private
135 * @param itemSize
136 *
137 * @return true if cache is full
138 */
139 BrowserStorageCacheClass.prototype._isCacheFull = function (itemSize) {
140 return itemSize + this.getCacheCurSize() > this.config.capacityInBytes;
141 };
142 /**
143 * scan the storage and find out all the keys owned by this cache
144 * also clean the expired keys while scanning
145 *
146 * @private
147 *
148 * @return array of keys
149 */
150 BrowserStorageCacheClass.prototype._findValidKeys = function () {
151 var keys = [];
152 var keyInCache = [];
153 // get all keys in Storage
154 for (var i = 0; i < this.config.storage.length; i += 1) {
155 keyInCache.push(this.config.storage.key(i));
156 }
157 // find those items which belong to our cache and also clean those expired items
158 for (var i = 0; i < keyInCache.length; i += 1) {
159 var key = keyInCache[i];
160 if (key.indexOf(this.config.keyPrefix) === 0 &&
161 key !== this.cacheCurSizeKey) {
162 if (this._isExpired(key)) {
163 this._removeItem(key);
164 }
165 else {
166 keys.push(key);
167 }
168 }
169 }
170 return keys;
171 };
172 /**
173 * get all the items we have, sort them by their priority,
174 * if priority is same, sort them by their last visited time
175 * pop out items from the low priority (5 is the lowest)
176 *
177 * @private
178 * @param keys - all the keys in this cache
179 * @param sizeToPop - the total size of the items which needed to be poped out
180 */
181 BrowserStorageCacheClass.prototype._popOutItems = function (keys, sizeToPop) {
182 var items = [];
183 var remainedSize = sizeToPop;
184 // get the items from Storage
185 for (var i = 0; i < keys.length; i += 1) {
186 var val = this.config.storage.getItem(keys[i]);
187 if (val != null) {
188 var item = JSON.parse(val);
189 items.push(item);
190 }
191 }
192 // first compare priority
193 // then compare visited time
194 items.sort(function (a, b) {
195 if (a.priority > b.priority) {
196 return -1;
197 }
198 else if (a.priority < b.priority) {
199 return 1;
200 }
201 else {
202 if (a.visitedTime < b.visitedTime) {
203 return -1;
204 }
205 else
206 return 1;
207 }
208 });
209 for (var i = 0; i < items.length; i += 1) {
210 // pop out items until we have enough room for new item
211 this._removeItem(items[i].key, items[i].byteSize);
212 remainedSize -= items[i].byteSize;
213 if (remainedSize <= 0) {
214 return;
215 }
216 }
217 };
218 /**
219 * Set item into cache. You can put number, string, boolean or object.
220 * The cache will first check whether has the same key.
221 * If it has, it will delete the old item and then put the new item in
222 * The cache will pop out items if it is full
223 * You can specify the cache item options. The cache will abort and output a warning:
224 * If the key is invalid
225 * If the size of the item exceeds itemMaxSize.
226 * If the value is undefined
227 * If incorrect cache item configuration
228 * If error happened with browser storage
229 *
230 * @param key - the key of the item
231 * @param value - the value of the item
232 * @param {Object} [options] - optional, the specified meta-data
233 */
234 BrowserStorageCacheClass.prototype.setItem = function (key, value, options) {
235 logger.log("Set item: key is " + key + ", value is " + value + " with options: " + options);
236 var prefixedKey = this.config.keyPrefix + key;
237 // invalid keys
238 if (prefixedKey === this.config.keyPrefix ||
239 prefixedKey === this.cacheCurSizeKey) {
240 logger.warn("Invalid key: should not be empty or 'CurSize'");
241 return;
242 }
243 if (typeof value === 'undefined') {
244 logger.warn("The value of item should not be undefined!");
245 return;
246 }
247 var cacheItemOptions = {
248 priority: options && options.priority !== undefined
249 ? options.priority
250 : this.config.defaultPriority,
251 expires: options && options.expires !== undefined
252 ? options.expires
253 : this.config.defaultTTL + Utils_1.getCurrTime(),
254 };
255 if (cacheItemOptions.priority < 1 || cacheItemOptions.priority > 5) {
256 logger.warn("Invalid parameter: priority due to out or range. It should be within 1 and 5.");
257 return;
258 }
259 var item = this.fillCacheItem(prefixedKey, value, cacheItemOptions);
260 // check wether this item is too big;
261 if (item.byteSize > this.config.itemMaxSize) {
262 logger.warn("Item with key: " + key + " you are trying to put into is too big!");
263 return;
264 }
265 try {
266 // first look into the storage, if it exists, delete it.
267 var val = this.config.storage.getItem(prefixedKey);
268 if (val) {
269 this._removeItem(prefixedKey, JSON.parse(val).byteSize);
270 }
271 // check whether the cache is full
272 if (this._isCacheFull(item.byteSize)) {
273 var validKeys = this._findValidKeys();
274 // check again and then pop out items
275 if (this._isCacheFull(item.byteSize)) {
276 var sizeToPop = this._sizeToPop(item.byteSize);
277 this._popOutItems(validKeys, sizeToPop);
278 }
279 }
280 // put item in the cache
281 // may failed due to storage full
282 this._setItem(prefixedKey, item);
283 }
284 catch (e) {
285 logger.warn("setItem failed! " + e);
286 }
287 };
288 /**
289 * Get item from cache. It will return null if item doesn’t exist or it has been expired.
290 * If you specified callback function in the options,
291 * then the function will be executed if no such item in the cache
292 * and finally put the return value into cache.
293 * Please make sure the callback function will return the value you want to put into the cache.
294 * The cache will abort output a warning:
295 * If the key is invalid
296 * If error happened with browser storage
297 *
298 * @param key - the key of the item
299 * @param {Object} [options] - the options of callback function
300 *
301 * @return - return the value of the item
302 */
303 BrowserStorageCacheClass.prototype.getItem = function (key, options) {
304 logger.log("Get item: key is " + key + " with options " + options);
305 var ret = null;
306 var prefixedKey = this.config.keyPrefix + key;
307 if (prefixedKey === this.config.keyPrefix ||
308 prefixedKey === this.cacheCurSizeKey) {
309 logger.warn("Invalid key: should not be empty or 'CurSize'");
310 return null;
311 }
312 try {
313 ret = this.config.storage.getItem(prefixedKey);
314 if (ret != null) {
315 if (this._isExpired(prefixedKey)) {
316 // if expired, remove that item and return null
317 this._removeItem(prefixedKey, JSON.parse(ret).byteSize);
318 ret = null;
319 }
320 else {
321 // if not expired, great, return the value and refresh it
322 var item = JSON.parse(ret);
323 item = this._refreshItem(item, prefixedKey);
324 return item.data;
325 }
326 }
327 if (options && options.callback !== undefined) {
328 var val = options.callback();
329 if (val !== null) {
330 this.setItem(key, val, options);
331 }
332 return val;
333 }
334 return null;
335 }
336 catch (e) {
337 logger.warn("getItem failed! " + e);
338 return null;
339 }
340 };
341 /**
342 * remove item from the cache
343 * The cache will abort output a warning:
344 * If error happened with browser storage
345 * @param key - the key of the item
346 */
347 BrowserStorageCacheClass.prototype.removeItem = function (key) {
348 logger.log("Remove item: key is " + key);
349 var prefixedKey = this.config.keyPrefix + key;
350 if (prefixedKey === this.config.keyPrefix ||
351 prefixedKey === this.cacheCurSizeKey) {
352 return;
353 }
354 try {
355 var val = this.config.storage.getItem(prefixedKey);
356 if (val) {
357 this._removeItem(prefixedKey, JSON.parse(val).byteSize);
358 }
359 }
360 catch (e) {
361 logger.warn("removeItem failed! " + e);
362 }
363 };
364 /**
365 * clear the entire cache
366 * The cache will abort output a warning:
367 * If error happened with browser storage
368 */
369 BrowserStorageCacheClass.prototype.clear = function () {
370 logger.log("Clear Cache");
371 var keysToRemove = [];
372 for (var i = 0; i < this.config.storage.length; i += 1) {
373 var key = this.config.storage.key(i);
374 if (key.indexOf(this.config.keyPrefix) === 0) {
375 keysToRemove.push(key);
376 }
377 }
378 try {
379 for (var i = 0; i < keysToRemove.length; i += 1) {
380 this.config.storage.removeItem(keysToRemove[i]);
381 }
382 }
383 catch (e) {
384 logger.warn("clear failed! " + e);
385 }
386 };
387 /**
388 * Return all the keys in the cache.
389 *
390 * @return - all keys in the cache
391 */
392 BrowserStorageCacheClass.prototype.getAllKeys = function () {
393 var keys = [];
394 for (var i = 0; i < this.config.storage.length; i += 1) {
395 var key = this.config.storage.key(i);
396 if (key.indexOf(this.config.keyPrefix) === 0 &&
397 key !== this.cacheCurSizeKey) {
398 keys.push(key.substring(this.config.keyPrefix.length));
399 }
400 }
401 return keys;
402 };
403 /**
404 * return the current size of the cache
405 *
406 * @return - current size of the cache
407 */
408 BrowserStorageCacheClass.prototype.getCacheCurSize = function () {
409 var ret = this.config.storage.getItem(this.cacheCurSizeKey);
410 if (!ret) {
411 this.config.storage.setItem(this.cacheCurSizeKey, '0');
412 ret = '0';
413 }
414 return Number(ret);
415 };
416 /**
417 * Return a new instance of cache with customized configuration.
418 * @param config - the customized configuration
419 *
420 * @return - new instance of Cache
421 */
422 BrowserStorageCacheClass.prototype.createInstance = function (config) {
423 if (!config.keyPrefix || config.keyPrefix === Utils_1.defaultConfig.keyPrefix) {
424 logger.error('invalid keyPrefix, setting keyPrefix with timeStamp');
425 config.keyPrefix = Utils_1.getCurrTime.toString();
426 }
427 return new BrowserStorageCacheClass(config);
428 };
429 return BrowserStorageCacheClass;
430}(StorageCache_1.StorageCache));
431exports.BrowserStorageCacheClass = BrowserStorageCacheClass;
432exports.BrowserStorageCache = new BrowserStorageCacheClass();
433core_1.Amplify.register(exports.BrowserStorageCache);
434//# sourceMappingURL=BrowserStorageCache.js.map
\No newline at end of file