@bizjournals/js-storage 0.0.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.gitlab-ci.yml +15 -0
- package/README.md +96 -0
- package/jest.config.cjs +3 -0
- package/lib/abstract/__tests__/storage.clear.spec.js +0 -5
- package/lib/abstract/__tests__/storage.constructor.spec.js +1 -7
- package/lib/abstract/__tests__/storage.getitem.spec.js +16 -7
- package/lib/abstract/__tests__/storage.hasitem.spec.js +6 -0
- package/lib/abstract/__tests__/storage.issupported.spec.js +2 -3
- package/lib/abstract/__tests__/storage.parser.spec.js +7 -3
- package/lib/abstract/__tests__/storage.removeitem.spec.js +14 -21
- package/lib/abstract/__tests__/storage.setitem.spec.js +5 -31
- package/lib/abstract/__tests__/storage.translator.spec.js +0 -1
- package/lib/abstract/index.js +69 -107
- package/lib/abstract/store/index.js +10 -0
- package/package.json +6 -6
- package/lib/abstract/__tests__/storage.resetattempts.spec.js +0 -20
- /package/{babel.config.js → babel.config.cjs} +0 -0
package/.gitlab-ci.yml
ADDED
package/README.md
CHANGED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
# Storage
|
|
2
|
+
This bundle has helper classes to interact with browser storage options. At present we
|
|
3
|
+
support `localstorage` and `sessionstorage`. These share a common abstract parent.
|
|
4
|
+
|
|
5
|
+
Storage references are critical to the implementation of our architecture. A reference
|
|
6
|
+
is a named key that contains your data in the selected storage type.
|
|
7
|
+
|
|
8
|
+
## Installing
|
|
9
|
+
|
|
10
|
+
Using npm:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @bizjournals/vue-core
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Using yarn:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
yarn add @bizjournals/vue-core
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Configuring your environment:**
|
|
23
|
+
|
|
24
|
+
You will need to configure an `NPM_TOKEN` within your project to include these
|
|
25
|
+
private modules. The best idea is to use an existing project as an example.
|
|
26
|
+
|
|
27
|
+
_For simple setups the .gitlab-ci.yml file within this project as a guide to accessing
|
|
28
|
+
project variables via the .npmrc file_
|
|
29
|
+
|
|
30
|
+
## Example
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
import { StorageReference, LocalStorage, SessionStorage } from "@bizjournals/js-storage";
|
|
34
|
+
|
|
35
|
+
// This will setup a reference that is namespaced to bizj
|
|
36
|
+
// The full key in localstorage will be bizj.mykey
|
|
37
|
+
// This key will have an expiration partner that by default will be one hour
|
|
38
|
+
const something = new StorageReference('mykey', new LocalStorage());
|
|
39
|
+
|
|
40
|
+
something.saveAs({
|
|
41
|
+
my: 'data'
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// local storage has a configurable API as well, and this can be used in storage
|
|
45
|
+
// reference objects too.
|
|
46
|
+
const local = new LocalStorage({
|
|
47
|
+
// by changing the namespace what was bizj by default will now be notbizj
|
|
48
|
+
// Example: notbizj.mykey
|
|
49
|
+
namespace: 'notbizj',
|
|
50
|
+
expires: 3600 // one hour
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
// session storage uses the same api as local storage
|
|
55
|
+
const somethingElse = new StorageReference('difkey', new SessionStorage({
|
|
56
|
+
namespace: 'woa'
|
|
57
|
+
}));
|
|
58
|
+
|
|
59
|
+
const somethingElsesData = somethingElse.obtain();
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
## Storage Reference
|
|
63
|
+
A storage reference is a keyed object that exists within either local or session storage.
|
|
64
|
+
It may be prefixed with a namespace since of the respective storage container.
|
|
65
|
+
|
|
66
|
+
### API
|
|
67
|
+
|
|
68
|
+
**constructor**(cacheKey : String, storageSystem : StorageAbstract)
|
|
69
|
+
**obtain**()
|
|
70
|
+
**bust**()
|
|
71
|
+
**saveAs**(data : Mixed)
|
|
72
|
+
|
|
73
|
+
## Storage Abstract
|
|
74
|
+
The parent class for both local and session storage creates the API used by each of them.
|
|
75
|
+
|
|
76
|
+
### API
|
|
77
|
+
|
|
78
|
+
**constructor**(options : Object : { namespace: String, expires: Number })
|
|
79
|
+
**getItem**(key: String)
|
|
80
|
+
**hasItem**(key : String)
|
|
81
|
+
**setItem**(key : String, value : Mixed, expires = true : Boolean)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
### Reference
|
|
85
|
+
|
|
86
|
+
#### getItem
|
|
87
|
+
Gets an item with a specified key. The namespace set in the construction of the object will
|
|
88
|
+
be pre-fixed to the beginning of the string, by default this is `bizj`.
|
|
89
|
+
|
|
90
|
+
#### hasItem
|
|
91
|
+
This checks to see if the item is available. If it is it'll cache it, so that if you need to
|
|
92
|
+
get the item later you don't need to worry about additional reads.
|
|
93
|
+
|
|
94
|
+
#### setItem
|
|
95
|
+
Sets the item in storage and updates the item in cache. It will automatically prefix itself
|
|
96
|
+
with the namespace, by default `bizj`.
|
package/jest.config.cjs
ADDED
|
@@ -14,11 +14,6 @@ describe('core:class >> storageabstract:clear', () => {
|
|
|
14
14
|
}).not.toThrow();
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
-
it('always returns `this`', () => {
|
|
18
|
-
const test = storageAbstract.clear();
|
|
19
|
-
expect(test).toEqual(storageAbstract);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
17
|
it('runs the clear method on the storage system when defined', () => {
|
|
23
18
|
storageAbstract.storage = {
|
|
24
19
|
clear: jest.fn()
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import StorageAbstract from '../';
|
|
2
2
|
import { DEFAULT_EXPIRATION, DEFAULT_NAMESPACE } from '../constants';
|
|
3
|
-
import { NAMESPACE_MUST_BE_STRING, EXPIRATION_MUST_BE_POSITIVE_INTEGER } from '../exceptions';
|
|
4
3
|
|
|
5
4
|
describe('core:class >> storageabstract:constructor', () => {
|
|
6
5
|
it('defaults to constant as it\'s namespace when none provided', () => {
|
|
@@ -25,7 +24,7 @@ describe('core:class >> storageabstract:constructor', () => {
|
|
|
25
24
|
for (const namespace of namespaces) {
|
|
26
25
|
expect(() => {
|
|
27
26
|
new StorageAbstract({ namespace });
|
|
28
|
-
}).toThrow(
|
|
27
|
+
}).toThrow();
|
|
29
28
|
}
|
|
30
29
|
});
|
|
31
30
|
|
|
@@ -40,11 +39,6 @@ describe('core:class >> storageabstract:constructor', () => {
|
|
|
40
39
|
expect(storageAbstract.expires).toEqual(expires);
|
|
41
40
|
});
|
|
42
41
|
|
|
43
|
-
it('defaults the number of attempts to zero', () => {
|
|
44
|
-
const storageAbstract = new StorageAbstract();
|
|
45
|
-
expect(storageAbstract.attempts).toEqual(0);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
42
|
it('sets the storage to a default of null', () => {
|
|
49
43
|
const storageAbstract = new StorageAbstract();
|
|
50
44
|
expect(storageAbstract.storage).toEqual({});
|
|
@@ -19,19 +19,20 @@ describe('core:class >> storageabstract:getitem', () => {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
storageClassMock.storage = mockStorage;
|
|
22
|
+
StorageAbstract.supported = undefined;
|
|
22
23
|
});
|
|
23
24
|
|
|
24
|
-
it('returns
|
|
25
|
+
it('returns undefined when it is not supported', () => {
|
|
25
26
|
storageClassMock.isSupported = jest.fn().mockImplementation(() => false);
|
|
26
27
|
|
|
27
|
-
expect(storageClassMock.getItem()).toEqual(
|
|
28
|
+
expect(storageClassMock.getItem()).toEqual(undefined);
|
|
28
29
|
expect(storageClassMock.namespaceKey).not.toHaveBeenCalled();
|
|
29
30
|
});
|
|
30
31
|
|
|
31
|
-
it('returns
|
|
32
|
+
it('returns undefined when the item is expired', () => {
|
|
32
33
|
storageClassMock.isItemExpired = jest.fn().mockImplementation(() => true);
|
|
33
34
|
|
|
34
|
-
expect(storageClassMock.getItem()).toEqual(
|
|
35
|
+
expect(storageClassMock.getItem()).toEqual(undefined);
|
|
35
36
|
expect(storageClassMock.namespaceKey).not.toHaveBeenCalled();
|
|
36
37
|
});
|
|
37
38
|
|
|
@@ -64,8 +65,16 @@ describe('core:class >> storageabstract:getitem', () => {
|
|
|
64
65
|
expect(storageClassMock.parser).toHaveBeenCalledWith(data);
|
|
65
66
|
});
|
|
66
67
|
|
|
67
|
-
it('
|
|
68
|
-
|
|
69
|
-
|
|
68
|
+
it('does not call the `parser` method with the data fetched from suspend or getItem', () => {
|
|
69
|
+
const data = { hey: 'you' };
|
|
70
|
+
|
|
71
|
+
const localStorage = {
|
|
72
|
+
getItem: jest.fn().mockImplementation(() => data)
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
storageClassMock.storage = localStorage;
|
|
76
|
+
|
|
77
|
+
storageClassMock.getItem(key, false);
|
|
78
|
+
expect(storageClassMock.parser).not.toHaveBeenCalled();
|
|
70
79
|
});
|
|
71
80
|
});
|
|
@@ -16,4 +16,10 @@ describe('core:class >> storageabstract:hasitem', () => {
|
|
|
16
16
|
|
|
17
17
|
expect(storageClassMock.getItem).toHaveBeenCalledTimes(1);
|
|
18
18
|
});
|
|
19
|
+
|
|
20
|
+
it('returns false when the obtained item is undefined', () => {
|
|
21
|
+
storageClassMock.getItem = jest.fn().mockImplementation(() => undefined);
|
|
22
|
+
|
|
23
|
+
expect(storageClassMock.hasItem(key)).toBeFalsy();
|
|
24
|
+
});
|
|
19
25
|
});
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import StorageAbstract from '../';
|
|
2
2
|
import mockStorage from '../__mocks__/mock-storage';
|
|
3
|
+
import store from '../store';
|
|
3
4
|
|
|
4
5
|
describe('core:class >> storageabstract:issupported', () => {
|
|
5
6
|
let storageAbstract;
|
|
@@ -9,10 +10,8 @@ describe('core:class >> storageabstract:issupported', () => {
|
|
|
9
10
|
mockStorage.setItem.mockClear();
|
|
10
11
|
mockStorage.removeItem.mockClear();
|
|
11
12
|
|
|
12
|
-
if (mockStorage._supported) {
|
|
13
|
-
delete mockStorage._supported;
|
|
14
|
-
}
|
|
15
13
|
storageAbstract.storage = mockStorage;
|
|
14
|
+
store.supported = undefined;
|
|
16
15
|
});
|
|
17
16
|
|
|
18
17
|
it('returns false when the storage system is defaulted', () => {
|
|
@@ -27,10 +27,14 @@ describe('core:class >> storageabstract:parser', () => {
|
|
|
27
27
|
expect(JSON.parse).toHaveBeenCalledTimes(1);
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
it('
|
|
30
|
+
it('returns the provided string if it could not be JSON parsed', () => {
|
|
31
31
|
JSON.parse = copyParse;
|
|
32
|
+
let response;
|
|
33
|
+
|
|
32
34
|
expect(() => {
|
|
33
|
-
storageAbstract.parser('wowowow');
|
|
34
|
-
}).toThrow();
|
|
35
|
+
response = storageAbstract.parser('wowowow');
|
|
36
|
+
}).not.toThrow();
|
|
37
|
+
|
|
38
|
+
expect(response).toEqual('wowowow');
|
|
35
39
|
});
|
|
36
40
|
});
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import StorageAbstract from '../';
|
|
2
2
|
|
|
3
|
+
import store from '../store';
|
|
4
|
+
|
|
3
5
|
describe('core:class >> storageabstract:removeitem', () => {
|
|
4
6
|
let storageClassMock, mockStorage, key = 'test';
|
|
5
7
|
|
|
@@ -19,28 +21,22 @@ describe('core:class >> storageabstract:removeitem', () => {
|
|
|
19
21
|
removeItem: jest.fn()
|
|
20
22
|
};
|
|
21
23
|
|
|
22
|
-
storageClassMock.attempts = 0;
|
|
23
24
|
storageClassMock.storage = mockStorage;
|
|
25
|
+
store.supported = undefined;
|
|
24
26
|
});
|
|
25
27
|
|
|
26
|
-
it('
|
|
28
|
+
it('halts when it is not supported', () => {
|
|
27
29
|
storageClassMock.isSupported = jest.fn().mockImplementation(() => false);
|
|
28
|
-
storageClassMock.removeItem(key);
|
|
29
|
-
|
|
30
|
-
expect(storageClassMock.resetAttempts).toHaveBeenCalledTimes(1);
|
|
31
|
-
expect(storageClassMock.storage.removeItem).not.toHaveBeenCalled();
|
|
32
|
-
});
|
|
33
30
|
|
|
34
|
-
it('resets the attempts and halts when attempts are >= 2', () => {
|
|
35
|
-
storageClassMock.attempts = 2;
|
|
36
31
|
storageClassMock.removeItem(key);
|
|
37
32
|
|
|
38
|
-
expect(storageClassMock.
|
|
33
|
+
expect(storageClassMock.isSupported).toHaveBeenCalledTimes(1);
|
|
39
34
|
expect(storageClassMock.storage.removeItem).not.toHaveBeenCalled();
|
|
40
|
-
|
|
41
35
|
});
|
|
42
36
|
|
|
43
|
-
it('
|
|
37
|
+
it('falls through when encountering an exception', () => {
|
|
38
|
+
store.supported = true;
|
|
39
|
+
|
|
44
40
|
const badStorage = {
|
|
45
41
|
removeItem: jest.fn().mockImplementation(() => {
|
|
46
42
|
throw new Error('testing code');
|
|
@@ -48,23 +44,20 @@ describe('core:class >> storageabstract:removeitem', () => {
|
|
|
48
44
|
};
|
|
49
45
|
|
|
50
46
|
storageClassMock.storage = badStorage;
|
|
51
|
-
storageClassMock.removeItem();
|
|
52
47
|
|
|
53
|
-
expect(
|
|
48
|
+
expect(() => {
|
|
49
|
+
storageClassMock.removeItem();
|
|
50
|
+
}).not.toThrow();
|
|
51
|
+
|
|
52
|
+
expect(storageClassMock.storage.removeItem).toHaveBeenCalledTimes(1);
|
|
54
53
|
});
|
|
55
54
|
|
|
56
55
|
it('removes both the item at namespace and expiration keys', () => {
|
|
56
|
+
store.supported = true;
|
|
57
57
|
storageClassMock.removeItem();
|
|
58
58
|
|
|
59
59
|
expect(storageClassMock.namespaceKey).toHaveBeenCalledTimes(1);
|
|
60
60
|
expect(storageClassMock.expirationKey).toHaveBeenCalledTimes(1);
|
|
61
61
|
expect(storageClassMock.storage.removeItem).toHaveBeenCalledTimes(2);
|
|
62
62
|
});
|
|
63
|
-
|
|
64
|
-
it('resets attempts to zero on successful return', () => {
|
|
65
|
-
storageClassMock.removeItem();
|
|
66
|
-
|
|
67
|
-
expect(storageClassMock.storage.removeItem).toHaveBeenCalledTimes(2);
|
|
68
|
-
expect(storageClassMock.resetAttempts).toHaveBeenCalledTimes(1);
|
|
69
|
-
});
|
|
70
63
|
});
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import StorageAbstract from '../';
|
|
2
|
+
import store from '../store';
|
|
2
3
|
|
|
3
4
|
describe('core:class >> storageabstract:setitem', () => {
|
|
4
5
|
let storageClassMock, mockStorage, key = 'test';
|
|
@@ -26,22 +27,16 @@ describe('core:class >> storageabstract:setitem', () => {
|
|
|
26
27
|
|
|
27
28
|
storageClassMock.attempts = 0;
|
|
28
29
|
storageClassMock.storage = mockStorage;
|
|
30
|
+
store.supported = undefined;
|
|
29
31
|
});
|
|
30
32
|
|
|
31
33
|
it('resets the attempts and returns when it is not supported without continuing', () => {
|
|
32
34
|
storageClassMock.isSupported = jest.fn().mockImplementation(() => false);
|
|
33
35
|
storageClassMock.setItem();
|
|
34
36
|
|
|
35
|
-
expect(storageClassMock.
|
|
36
|
-
expect(storageClassMock.namespaceKey).not.toHaveBeenCalled();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('resets the attempts and returns when attempts is >= 2 without continuing', () => {
|
|
40
|
-
storageClassMock.attempts = 3;
|
|
41
|
-
storageClassMock.setItem();
|
|
42
|
-
|
|
43
|
-
expect(storageClassMock.resetAttempts).toHaveBeenCalledTimes(1);
|
|
37
|
+
expect(storageClassMock.isSupported).toHaveBeenCalledTimes(1);
|
|
44
38
|
expect(storageClassMock.namespaceKey).not.toHaveBeenCalled();
|
|
39
|
+
expect(mockStorage.setItem).not.toHaveBeenCalled();
|
|
45
40
|
});
|
|
46
41
|
|
|
47
42
|
it('uses a dynamically generated namespaceKey to set the item', () => {
|
|
@@ -59,7 +54,7 @@ describe('core:class >> storageabstract:setitem', () => {
|
|
|
59
54
|
expect(storageClassMock.storage.setItem).not.toHaveBeenCalled();
|
|
60
55
|
});
|
|
61
56
|
|
|
62
|
-
it('calls to set the item one time when not
|
|
57
|
+
it('calls to set the item one time when not expiring', () => {
|
|
63
58
|
const data = { it: true };
|
|
64
59
|
|
|
65
60
|
storageClassMock.setItem(key, data, false);
|
|
@@ -77,25 +72,4 @@ describe('core:class >> storageabstract:setitem', () => {
|
|
|
77
72
|
expect(storageClassMock.storage.setItem).toHaveBeenCalledTimes(2);
|
|
78
73
|
expect(storageClassMock.expirationKey).toHaveBeenCalledTimes(1);
|
|
79
74
|
});
|
|
80
|
-
|
|
81
|
-
it('calls to cache the data with the key', () => {
|
|
82
|
-
const data = { it: true };
|
|
83
|
-
|
|
84
|
-
storageClassMock.setItem(key, data);
|
|
85
|
-
expect(storageClassMock.cache).toHaveBeenCalledTimes(1);
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it('calls clear when there is an error and attempts up to 2 times', () => {
|
|
89
|
-
storageClassMock.translator = jest.fn().mockImplementation(() => {
|
|
90
|
-
throw new Error('testing script error');
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
storageClassMock.setItem(key, { data: true });
|
|
94
|
-
expect(storageClassMock.translator).toHaveBeenCalledTimes(2);
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
it('calls to reset attempts to zero once completed', () => {
|
|
98
|
-
storageClassMock.setItem(key, { data: true });
|
|
99
|
-
expect(storageClassMock.resetAttempts).toHaveBeenCalledTimes(1);
|
|
100
|
-
});
|
|
101
75
|
});
|
package/lib/abstract/index.js
CHANGED
|
@@ -1,11 +1,6 @@
|
|
|
1
|
-
import { empty } from "@bizjournals/js-utilities";
|
|
2
1
|
import { DEFAULT_EXPIRATION, DEFAULT_NAMESPACE, EXPIRES_KEY_SUFFIX } from "./constants";
|
|
3
2
|
import { KEY_IS_REQUIRED_TO_FETCH_DATA } from "./exceptions";
|
|
4
|
-
|
|
5
|
-
// module scoped caching object
|
|
6
|
-
// does share data between session / local / any other
|
|
7
|
-
// If this becomes necessary attach it to the storage object
|
|
8
|
-
const cache = {};
|
|
3
|
+
import store from './store';
|
|
9
4
|
|
|
10
5
|
/*
|
|
11
6
|
|--------------------------------------------------------------------------
|
|
@@ -25,41 +20,36 @@ export default class StorageAbstract {
|
|
|
25
20
|
* @param {object} options
|
|
26
21
|
* @constructor
|
|
27
22
|
*/
|
|
28
|
-
constructor(options = {}) {
|
|
23
|
+
constructor (options = {}) {
|
|
29
24
|
this.namespace = options.namespace || DEFAULT_NAMESPACE;
|
|
30
25
|
this.expires = options.expires || DEFAULT_EXPIRATION;
|
|
31
|
-
this.attempts = 0;
|
|
32
26
|
|
|
33
27
|
this.namespace = this.namespace.toLowerCase();
|
|
34
28
|
|
|
35
|
-
cache[this.namespace] = cache[this.namespace] || {};
|
|
36
|
-
|
|
37
29
|
this.storage = {};
|
|
38
30
|
}
|
|
39
31
|
|
|
40
32
|
/**
|
|
41
|
-
* Gets an item from storage
|
|
33
|
+
* Gets an item from storage and runs the parser unless explicitly
|
|
34
|
+
* disabled in the method request.
|
|
42
35
|
*
|
|
43
|
-
* @param {
|
|
44
|
-
* @
|
|
36
|
+
* @param {String} key
|
|
37
|
+
* @param {Boolean} parse
|
|
38
|
+
* @returns {(Array|Number|Object|String|Null)}
|
|
45
39
|
*/
|
|
46
|
-
getItem(key) {
|
|
47
|
-
|
|
48
|
-
return null;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
let response = null;
|
|
40
|
+
getItem (key, parse = true) {
|
|
41
|
+
let response;
|
|
52
42
|
|
|
53
|
-
if (
|
|
54
|
-
return
|
|
43
|
+
if (!this.isSupported() || this.isItemExpired(key)) {
|
|
44
|
+
return response;
|
|
55
45
|
}
|
|
56
46
|
|
|
57
47
|
try {
|
|
58
|
-
|
|
48
|
+
response = this.storage.getItem(this.namespaceKey(key));
|
|
59
49
|
|
|
60
|
-
response
|
|
50
|
+
if (response === null) return undefined;
|
|
61
51
|
|
|
62
|
-
this.
|
|
52
|
+
response = parse ? this.parser(response) : response;
|
|
63
53
|
} catch (e) {
|
|
64
54
|
// fall through
|
|
65
55
|
}
|
|
@@ -71,10 +61,18 @@ export default class StorageAbstract {
|
|
|
71
61
|
* Parses the data returned from the storage system
|
|
72
62
|
*
|
|
73
63
|
* @param data
|
|
74
|
-
* @returns {
|
|
64
|
+
* @returns {*}
|
|
75
65
|
*/
|
|
76
|
-
parser(data) {
|
|
77
|
-
|
|
66
|
+
parser (data) {
|
|
67
|
+
try {
|
|
68
|
+
if (typeof data === "string") {
|
|
69
|
+
data = JSON.parse(data);
|
|
70
|
+
}
|
|
71
|
+
} catch (e) {
|
|
72
|
+
// fall through
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return data;
|
|
78
76
|
}
|
|
79
77
|
|
|
80
78
|
/**
|
|
@@ -83,8 +81,8 @@ export default class StorageAbstract {
|
|
|
83
81
|
* @param value
|
|
84
82
|
* @returns {any}
|
|
85
83
|
*/
|
|
86
|
-
translator(value) {
|
|
87
|
-
return typeof value ===
|
|
84
|
+
translator (value) {
|
|
85
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
88
86
|
}
|
|
89
87
|
|
|
90
88
|
/**
|
|
@@ -94,9 +92,9 @@ export default class StorageAbstract {
|
|
|
94
92
|
* @param {(array|object|number|string)} value
|
|
95
93
|
* @param {boolean} expires
|
|
96
94
|
*/
|
|
97
|
-
setItem(key, value, expires = true) {
|
|
98
|
-
if (!this.isSupported()
|
|
99
|
-
return
|
|
95
|
+
setItem (key, value, expires = true) {
|
|
96
|
+
if (!this.isSupported()) {
|
|
97
|
+
return;
|
|
100
98
|
}
|
|
101
99
|
|
|
102
100
|
const generatedKey = this.namespaceKey(key);
|
|
@@ -105,16 +103,14 @@ export default class StorageAbstract {
|
|
|
105
103
|
this.storage.setItem(generatedKey, this.translator(value));
|
|
106
104
|
|
|
107
105
|
if (expires) {
|
|
108
|
-
this.storage.setItem(
|
|
106
|
+
this.storage.setItem(
|
|
107
|
+
this.expirationKey(key),
|
|
108
|
+
String(this.timestamp(this.expires))
|
|
109
|
+
);
|
|
109
110
|
}
|
|
110
|
-
|
|
111
|
-
this.cache(key, value);
|
|
112
111
|
} catch (e) {
|
|
113
|
-
|
|
114
|
-
this.setItem(key, value, expires);
|
|
112
|
+
// fall through
|
|
115
113
|
}
|
|
116
|
-
|
|
117
|
-
return this.resetAttempts();
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
/**
|
|
@@ -122,48 +118,28 @@ export default class StorageAbstract {
|
|
|
122
118
|
*
|
|
123
119
|
* @param {string} key
|
|
124
120
|
*/
|
|
125
|
-
removeItem(key) {
|
|
126
|
-
if (!this.isSupported()
|
|
127
|
-
return
|
|
121
|
+
removeItem (key) {
|
|
122
|
+
if (!this.isSupported()) {
|
|
123
|
+
return;
|
|
128
124
|
}
|
|
129
125
|
|
|
130
126
|
try {
|
|
131
127
|
this.storage.removeItem(this.namespaceKey(key));
|
|
132
128
|
this.storage.removeItem(this.expirationKey(key));
|
|
133
|
-
|
|
134
|
-
this.free(key);
|
|
135
129
|
} catch (e) {
|
|
136
|
-
|
|
137
|
-
this.removeItem(key);
|
|
130
|
+
// fall through
|
|
138
131
|
}
|
|
139
|
-
|
|
140
|
-
return this.resetAttempts();
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Resets the number of attempt operations to zero.
|
|
145
|
-
*
|
|
146
|
-
* @returns {StorageAbstract}
|
|
147
|
-
*/
|
|
148
|
-
resetAttempts() {
|
|
149
|
-
this.attempts = 0;
|
|
150
|
-
|
|
151
|
-
return this;
|
|
152
132
|
}
|
|
153
133
|
|
|
154
134
|
/**
|
|
155
135
|
* Clears all items from storage.
|
|
156
136
|
*/
|
|
157
|
-
clear() {
|
|
137
|
+
clear () {
|
|
158
138
|
try {
|
|
159
139
|
this.storage.clear();
|
|
160
|
-
|
|
161
|
-
cache[this.namespace] = {};
|
|
162
140
|
} catch (e) {
|
|
163
141
|
// fall through
|
|
164
142
|
}
|
|
165
|
-
|
|
166
|
-
return this;
|
|
167
143
|
}
|
|
168
144
|
|
|
169
145
|
/**
|
|
@@ -172,8 +148,8 @@ export default class StorageAbstract {
|
|
|
172
148
|
* @param {string} key
|
|
173
149
|
* @returns {boolean}
|
|
174
150
|
*/
|
|
175
|
-
hasItem(key) {
|
|
176
|
-
return
|
|
151
|
+
hasItem (key) {
|
|
152
|
+
return this.getItem(key, false) !== undefined;
|
|
177
153
|
}
|
|
178
154
|
|
|
179
155
|
/**
|
|
@@ -182,7 +158,7 @@ export default class StorageAbstract {
|
|
|
182
158
|
* @param {number} offset
|
|
183
159
|
* @returns {number}
|
|
184
160
|
*/
|
|
185
|
-
timestamp(offset = 0) {
|
|
161
|
+
timestamp (offset = 0) {
|
|
186
162
|
return Math.ceil(Date.now() / 1000) + offset;
|
|
187
163
|
}
|
|
188
164
|
|
|
@@ -192,16 +168,16 @@ export default class StorageAbstract {
|
|
|
192
168
|
* @param {string} key
|
|
193
169
|
* @returns {string}
|
|
194
170
|
*/
|
|
195
|
-
namespaceKey(key) {
|
|
171
|
+
namespaceKey (key) {
|
|
196
172
|
if (typeof key !== "string") {
|
|
197
173
|
throw new Error(KEY_IS_REQUIRED_TO_FETCH_DATA);
|
|
198
174
|
}
|
|
199
175
|
|
|
200
|
-
if (key.indexOf(`${this.namespace}.`) === 0) {
|
|
176
|
+
if (key.indexOf(`${ this.namespace }.`) === 0) {
|
|
201
177
|
key = key.slice(this.namespace.length + 1);
|
|
202
178
|
}
|
|
203
179
|
|
|
204
|
-
return `${this.namespace}.${key.toLowerCase()}`;
|
|
180
|
+
return `${ this.namespace }.${ key.toLowerCase() }`;
|
|
205
181
|
}
|
|
206
182
|
|
|
207
183
|
/**
|
|
@@ -209,8 +185,8 @@ export default class StorageAbstract {
|
|
|
209
185
|
*
|
|
210
186
|
* @returns {string}
|
|
211
187
|
*/
|
|
212
|
-
expirationKey(key) {
|
|
213
|
-
return `${this.namespaceKey(key)}.${EXPIRES_KEY_SUFFIX}`;
|
|
188
|
+
expirationKey (key) {
|
|
189
|
+
return `${ this.namespaceKey(key) }.${ EXPIRES_KEY_SUFFIX }`;
|
|
214
190
|
}
|
|
215
191
|
|
|
216
192
|
/**
|
|
@@ -219,7 +195,7 @@ export default class StorageAbstract {
|
|
|
219
195
|
* @param {string} key
|
|
220
196
|
* @returns {boolean}
|
|
221
197
|
*/
|
|
222
|
-
isItemExpired(key) {
|
|
198
|
+
isItemExpired (key) {
|
|
223
199
|
if (!this.isSupported()) {
|
|
224
200
|
return false;
|
|
225
201
|
}
|
|
@@ -242,52 +218,38 @@ export default class StorageAbstract {
|
|
|
242
218
|
return false;
|
|
243
219
|
}
|
|
244
220
|
|
|
245
|
-
/**
|
|
246
|
-
* Stores the data in memory.
|
|
247
|
-
*
|
|
248
|
-
* @param key
|
|
249
|
-
* @param value
|
|
250
|
-
*/
|
|
251
|
-
cache(key, value) {
|
|
252
|
-
cache[this.namespace][key] = value;
|
|
253
|
-
|
|
254
|
-
return this;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
/**
|
|
258
|
-
* Frees the data from memory.
|
|
259
|
-
*
|
|
260
|
-
* @param key
|
|
261
|
-
* @param value
|
|
262
|
-
*/
|
|
263
|
-
free(key) {
|
|
264
|
-
if (cache[this.namespace][key]) {
|
|
265
|
-
delete cache[this.namespace][key];
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return this;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
221
|
/**
|
|
272
222
|
* Checks to see if the storage system is supported
|
|
273
223
|
*
|
|
274
224
|
* @returns {boolean}
|
|
275
225
|
*/
|
|
276
|
-
isSupported() {
|
|
277
|
-
if (typeof
|
|
278
|
-
return
|
|
226
|
+
isSupported () {
|
|
227
|
+
if (typeof store.supported === "boolean") {
|
|
228
|
+
return store.supported;
|
|
279
229
|
}
|
|
280
230
|
|
|
281
|
-
const test =
|
|
231
|
+
const test = 'biz.storage.test';
|
|
282
232
|
|
|
283
233
|
try {
|
|
284
|
-
this.storage.setItem(test,
|
|
234
|
+
this.storage.setItem(test, '1');
|
|
285
235
|
this.storage.removeItem(test);
|
|
286
|
-
|
|
236
|
+
|
|
237
|
+
store.supported = true;
|
|
287
238
|
} catch (e) {
|
|
288
|
-
|
|
239
|
+
store.supported = e instanceof DOMException &&
|
|
240
|
+
(
|
|
241
|
+
// everything except Firefox
|
|
242
|
+
e.code === 22 ||
|
|
243
|
+
// Firefox
|
|
244
|
+
e.code === 1014 ||
|
|
245
|
+
// test name field too, because code might not be present
|
|
246
|
+
// everything except Firefox
|
|
247
|
+
e.name === 'QuotaExceededError' ||
|
|
248
|
+
// Firefox
|
|
249
|
+
e.name === 'NS_ERROR_DOM_QUOTA_REACHED'
|
|
250
|
+
) && (this.storage && this.storage.length !== 0);
|
|
289
251
|
}
|
|
290
252
|
|
|
291
|
-
return
|
|
253
|
+
return store.supported;
|
|
292
254
|
}
|
|
293
255
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bizjournals/js-storage",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "ACBJ javascript storage classes",
|
|
5
5
|
"repository": {
|
|
6
6
|
"git": "https://gitlab.bizjournals.com/bizjournals/js-storage"
|
|
@@ -9,17 +9,17 @@
|
|
|
9
9
|
"type": "module",
|
|
10
10
|
"sideEffects": false,
|
|
11
11
|
"scripts": {
|
|
12
|
-
"test": "jest
|
|
12
|
+
"test": "jest",
|
|
13
|
+
"preversion": "npm test",
|
|
14
|
+
"postversion": "git push && git push --tags && npm publish"
|
|
13
15
|
},
|
|
14
16
|
"keywords": [],
|
|
15
17
|
"author": "DPD",
|
|
16
18
|
"license": "UNLICENSED",
|
|
17
19
|
"devDependencies": {
|
|
18
|
-
"@babel/preset-env": "^7.
|
|
20
|
+
"@babel/preset-env": "^7.10.3",
|
|
19
21
|
"babel-jest": "^25.4.0",
|
|
20
22
|
"jest": "^25.4.0"
|
|
21
23
|
},
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"@bizjournals/js-utilities": "0.0.1"
|
|
24
|
-
}
|
|
24
|
+
"dependencies": {}
|
|
25
25
|
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import StorageAbstract from '../';
|
|
2
|
-
|
|
3
|
-
describe('core:class >> storageabstract:resetattempts', () => {
|
|
4
|
-
let storageAbstract;
|
|
5
|
-
|
|
6
|
-
beforeAll(() => {
|
|
7
|
-
storageAbstract = new StorageAbstract();
|
|
8
|
-
});
|
|
9
|
-
|
|
10
|
-
it('resets the attempts to 0 when called', () => {
|
|
11
|
-
storageAbstract.attempts = 2;
|
|
12
|
-
|
|
13
|
-
storageAbstract.resetAttempts();
|
|
14
|
-
expect(storageAbstract.attempts).toEqual(0);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it('returns `this`', () => {
|
|
18
|
-
expect(storageAbstract.resetAttempts()).toEqual(storageAbstract);
|
|
19
|
-
});
|
|
20
|
-
});
|
|
File without changes
|