UNPKG

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