hyperstorage-js 5.0.7 → 5.1.0

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/README.md CHANGED
@@ -39,7 +39,7 @@ yarn add hyperstorage-js
39
39
  ## Constructor Syntax
40
40
 
41
41
  ```ts
42
- class StorageManager<T> {
42
+ class HyperStorage<T> {
43
43
  constructor(
44
44
  itemName: string,
45
45
  defaultValue: T,
@@ -67,7 +67,7 @@ const userStore = new HyperStorage('userSettings', defaultValue)
67
67
  // If 'userSettings' is not present in the Storage, the defaultValue is set:
68
68
  console.log(userStore.value) // { theme: 'light', language: 'en' }
69
69
 
70
- // Change theme to dark:
70
+ // Change theme to dark
71
71
  userStore.value = { theme: 'dark', language: 'en' }
72
72
  // or
73
73
  userStore.set((v) => (v.theme = 'dark'))
@@ -75,7 +75,7 @@ userStore.set((v) => (v.theme = 'dark'))
75
75
  console.log(userStore.value) // { theme: 'dark', language: 'en' }
76
76
  console.log(userStore.value.theme) // 'dark'
77
77
 
78
- // Present in localStorage:
78
+ // Present in localStorage
79
79
  console.log(userStore.storage) // Storage {userSettings: '\x00{"theme":"dark","language":"en"}', length: 1}
80
80
  ```
81
81
 
@@ -168,14 +168,19 @@ userStore.value = { theme: 'dark' }
168
168
  Safe usage of `sync()` requires explicit runtime validation before accessing any properties. It quickly becomes clear how type-unsafe `sync()` is and why it should be avoided.
169
169
 
170
170
  ```ts
171
- const current = userStore.sync() // (method): unknown
171
+ const result = userStore.sync() // (method): unknown
172
+ // Right now, 'result' equals 'userStore.value' exactly
172
173
 
173
- // 'current' is of type 'unknown'. ts(18046)
174
- console.log(current.theme) // { theme: 'dark' }
174
+ // 'result' is of type 'unknown'. ts(18046)
175
+ console.log(result.theme) // 'dark'
175
176
 
176
- // Must narrow down
177
- if (current && typeof current === 'object' && 'theme' in current) {
178
- console.log(current.theme)
177
+ // No error, but unsafe
178
+ console.log(userStore.value.theme) // 'dark'
179
+
180
+ // Must narrow down to be safe
181
+ if (result && typeof result === 'object' && 'theme' in result) {
182
+ // No error, safe
183
+ console.log(result.theme) // 'dark'
179
184
  }
180
185
  ```
181
186
 
@@ -254,6 +259,59 @@ console.log(userStore.value) // { theme: 'dark' }
254
259
  console.log(userStore.storage) // Storage {userSettings: '\x00{"theme":"dark"}', length: 1}
255
260
  ```
256
261
 
262
+ #### Why `sync()` is unsafe
263
+
264
+ 1. The item in `Storage` is undecodable by the `decodeFn` passed to `sync()`.
265
+
266
+ ```js
267
+ localStorage.setItem('userSettings', 'not an object')
268
+
269
+ // SyntaxError: Unexpected token 'o', "not an object" is not valid JSON
270
+ const result = userStore.sync((value) => JSON.parse(value))
271
+ // Execution continues because sync() uses a try-catch
272
+
273
+ console.log(result) // instance of SyntaxError
274
+
275
+ // Reset to default value
276
+ console.log(userStore.value) // {theme: 'system', language: 'en'}
277
+ ```
278
+
279
+ 2. The item in `Storage`, after being decoded, is not of type `T`.
280
+
281
+ ```js
282
+ localStorage.setItem('userSettings', '{"not":"valid"}')
283
+
284
+ const result = userStore.sync((value) => JSON.parse(value))
285
+ console.log(result) // {not: 'valid'}
286
+ console.log(userStore.value) // {not: 'valid'}
287
+ ```
288
+
289
+ No errors, but `result` and `userStore.value` are both not of type `T`.
290
+
291
+ This **must** be manually checked and `userStore.reset()` should be called if the check fails.
292
+
293
+ ```js
294
+ // This example is specifically for type 'Settings'
295
+ if (
296
+ result &&
297
+ typeof result === 'object' &&
298
+ 'theme' in result &&
299
+ 'language' in result &&
300
+ (result.theme === 'system' ||
301
+ result.theme === 'light' ||
302
+ result.theme === 'dark') &&
303
+ typeof result.language === 'string'
304
+ ) {
305
+ // 'result' is of type 'T'
306
+ } else {
307
+ // 'result' is not of type 'T'
308
+ userStore.reset()
309
+ console.log(userStore.value) // {theme: 'system', language: 'en'}
310
+ }
311
+
312
+ // 'userStore.value' is of type 'T'
313
+ ```
314
+
257
315
  <br>
258
316
 
259
317
  ## Source
package/dist/index.cjs CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
  class HyperStorage {
11
11
  /** Version of the library, injected via Rollup replace plugin. */
12
- static version = "5.0.7";
12
+ static version = "5.1.0";
13
13
  /** Key name under which the data is stored. */
14
14
  itemName;
15
15
  /** Default value used when the key does not exist in storage. */
@@ -72,7 +72,7 @@ class HyperStorage {
72
72
  (typeof value === 'number' &&
73
73
  (isNaN(value) || value === Infinity || value === -Infinity))) {
74
74
  // Manually stringify non-JSON values
75
- stringValue = String(value);
75
+ stringValue = '\0' + String(value);
76
76
  }
77
77
  else {
78
78
  stringValue = '\0' + JSON.stringify(value);
@@ -107,8 +107,9 @@ class HyperStorage {
107
107
  value = decodeFn(value);
108
108
  }
109
109
  catch (err) {
110
+ this.reset();
110
111
  console.error(err);
111
- return this.reset();
112
+ return err;
112
113
  }
113
114
  if (value[0] !== '\0')
114
115
  return (this.value = value); // Raw string value
package/dist/index.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  class HyperStorage {
9
9
  /** Version of the library, injected via Rollup replace plugin. */
10
- static version = "5.0.7";
10
+ static version = "5.1.0";
11
11
  /** Key name under which the data is stored. */
12
12
  itemName;
13
13
  /** Default value used when the key does not exist in storage. */
@@ -70,7 +70,7 @@ class HyperStorage {
70
70
  (typeof value === 'number' &&
71
71
  (isNaN(value) || value === Infinity || value === -Infinity))) {
72
72
  // Manually stringify non-JSON values
73
- stringValue = String(value);
73
+ stringValue = '\0' + String(value);
74
74
  }
75
75
  else {
76
76
  stringValue = '\0' + JSON.stringify(value);
@@ -105,8 +105,9 @@ class HyperStorage {
105
105
  value = decodeFn(value);
106
106
  }
107
107
  catch (err) {
108
+ this.reset();
108
109
  console.error(err);
109
- return this.reset();
110
+ return err;
110
111
  }
111
112
  if (value[0] !== '\0')
112
113
  return (this.value = value); // Raw string value
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).StorageManager=t()}(this,function(){"use strict";return class{static version="5.0.7";itemName;defaultValue;encodeFn;decodeFn;storage;#e;constructor(e,t,i={}){const{encodeFn:n,decodeFn:s,storage:o=window.localStorage}=i;if("string"!=typeof e)throw new TypeError("itemName is not a string");if(this.itemName=e,this.defaultValue=t,n&&"function"!=typeof n)throw new TypeError("encodeFn is defined but is not a function");if(this.encodeFn=n||(e=>e),s&&"function"!=typeof s)throw new TypeError("decodeFn is defined but is not a function");if(this.decodeFn=s||(e=>e),!(o instanceof Storage))throw new TypeError("storage must be an instance of Storage");this.storage=o,this.sync()}set value(e){let t;this.#e=e,t="string"==typeof e?"\0"===e[0]?"\0"+e:e:void 0===e||"number"==typeof e&&(isNaN(e)||e===1/0||e===-1/0)?String(e):"\0"+JSON.stringify(e),this.storage.setItem(this.itemName,this.encodeFn(t))}get value(){return this.#e??this.defaultValue}set(e){return this.value=e(this.value)}sync(e=this.decodeFn){let t=this.storage.getItem(this.itemName);if("string"!=typeof t)return this.reset();try{t=e(t)}catch(e){return console.error(e),this.reset()}return"\0"!==t[0]?this.value=t:(t=t.slice(1),"\0"===t[0]?this.value=t:this.value="undefined"===t?void 0:"NaN"===t?NaN:"Infinity"===t?1/0:"-Infinity"===t?-1/0:JSON.parse(t))}reset(){return this.value=this.defaultValue}remove(){this.#e=void 0,this.storage.removeItem(this.itemName)}clear(){this.#e=void 0,this.storage.clear()}isDefault(){return this.#e===this.defaultValue}}});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).HyperStorage=t()}(this,function(){"use strict";return class{static version="5.1.0";itemName;defaultValue;encodeFn;decodeFn;storage;#e;constructor(e,t,i={}){const{encodeFn:s,decodeFn:n,storage:o=window.localStorage}=i;if("string"!=typeof e)throw new TypeError("itemName is not a string");if(this.itemName=e,this.defaultValue=t,s&&"function"!=typeof s)throw new TypeError("encodeFn is defined but is not a function");if(this.encodeFn=s||(e=>e),n&&"function"!=typeof n)throw new TypeError("decodeFn is defined but is not a function");if(this.decodeFn=n||(e=>e),!(o instanceof Storage))throw new TypeError("storage must be an instance of Storage");this.storage=o,this.sync()}set value(e){let t;this.#e=e,t="string"==typeof e?"\0"===e[0]?"\0"+e:e:void 0===e||"number"==typeof e&&(isNaN(e)||e===1/0||e===-1/0)?"\0"+String(e):"\0"+JSON.stringify(e),this.storage.setItem(this.itemName,this.encodeFn(t))}get value(){return this.#e??this.defaultValue}set(e){return this.value=e(this.value)}sync(e=this.decodeFn){let t=this.storage.getItem(this.itemName);if("string"!=typeof t)return this.reset();try{t=e(t)}catch(e){return this.reset(),console.error(e),e}return"\0"!==t[0]?this.value=t:(t=t.slice(1),"\0"===t[0]?this.value=t:this.value="undefined"===t?void 0:"NaN"===t?NaN:"Infinity"===t?1/0:"-Infinity"===t?-1/0:JSON.parse(t))}reset(){return this.value=this.defaultValue}remove(){this.#e=void 0,this.storage.removeItem(this.itemName)}clear(){this.#e=void 0,this.storage.clear()}isDefault(){return this.#e===this.defaultValue}}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hyperstorage-js",
3
- "version": "5.0.7",
3
+ "version": "5.1.0",
4
4
  "description": "A lightweight wrapper for localStorage/sessionStorage with efficient caching and type-preserving serialization.",
5
5
  "license": "MIT",
6
6
  "author": "Khoeckman",
package/src/index.ts CHANGED
@@ -95,7 +95,7 @@ class HyperStorage<T> {
95
95
  (isNaN(value) || value === Infinity || value === -Infinity))
96
96
  ) {
97
97
  // Manually stringify non-JSON values
98
- stringValue = String(value)
98
+ stringValue = '\0' + String(value)
99
99
  } else {
100
100
  stringValue = '\0' + JSON.stringify(value)
101
101
  }
@@ -133,8 +133,9 @@ class HyperStorage<T> {
133
133
  try {
134
134
  value = decodeFn(value)
135
135
  } catch (err) {
136
+ this.reset()
136
137
  console.error(err)
137
- return this.reset()
138
+ return err
138
139
  }
139
140
 
140
141
  if (value[0] !== '\0') return (this.value = value as T) // Raw string value