UNPKG

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