hyperstorage-js 5.0.7 β†’ 6.0.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
@@ -13,12 +13,32 @@ The biggest burdens of working with the **Storage API** is verifying values on e
13
13
  ## Features
14
14
 
15
15
  - πŸ“ **Default values**: are automatically set when the key is not in Storage.
16
- - 🧩 **JSON support**: automatically serializes and parses objects or non-string primitives (numbers, `undefined`, `NaN`, etc.) which the Storage API does not support by default.
17
- - πŸ› οΈ **Utility helpers**: built-in helper methods (like `.set()` and `.isDefault()`) to simplify storage operations.
16
+ - 🧩 **(Super)JSON support**: automatically serializes and parses everything using [superjson](https://github.com/flightcontrolhq/superjson).
17
+ - 🧠 **TypeScript support**: full type safety with strongly typed keys, values, and callbacks.
18
+ - πŸ”’ **Optional encoding/decoding**: hooks to obfuscate data or change the serializer.
18
19
  - ⚑ **Fast caching**: memory cache avoids repeated JSON convertions.
19
- - πŸ”’ **Optional encoding/decoding** hooks to obfuscate data.
20
+ - πŸ› οΈ **Utility helpers**: use `.set()` and `.isDefault()` to simplify storage operations.
20
21
  - 🌐 **Custom storage**: works with any object implementing the standard Storage API. (`localStorage`, `sessionStorage`, ...)
21
22
 
23
+ ### Supported Types
24
+
25
+ | Type | Supported by Storage API | Supported by HyperStorage (trough superjson) |
26
+ | --------- | ------------------------ | -------------------------------------------- |
27
+ | string | βœ… | βœ… |
28
+ | number | ❌ | βœ… |
29
+ | boolean | ❌ | βœ… |
30
+ | null | ❌ | βœ… |
31
+ | Array | ❌ | βœ… |
32
+ | Object | ❌ | βœ… |
33
+ | undefined | ❌ | βœ… |
34
+ | bigint | ❌ | βœ… |
35
+ | Date | ❌ | βœ… |
36
+ | RegExp | ❌ | βœ… |
37
+ | Set | ❌ | βœ… |
38
+ | Map | ❌ | βœ… |
39
+ | Error | ❌ | βœ… |
40
+ | URL | ❌ | βœ… |
41
+
22
42
  <br>
23
43
 
24
44
  ## Installation
@@ -39,13 +59,13 @@ yarn add hyperstorage-js
39
59
  ## Constructor Syntax
40
60
 
41
61
  ```ts
42
- class StorageManager<T> {
62
+ class HyperStorage<T> {
43
63
  constructor(
44
64
  itemName: string,
45
65
  defaultValue: T,
46
66
  options: {
47
- encodeFn?: (value: string) => string
48
- decodeFn?: (value: string) => string
67
+ encodeFn?: (value: T) => string
68
+ decodeFn?: (value: string) => T
49
69
  storage?: Storage
50
70
  } = {}
51
71
  )
@@ -67,19 +87,19 @@ const userStore = new HyperStorage('userSettings', defaultValue)
67
87
  // If 'userSettings' is not present in the Storage, the defaultValue is set:
68
88
  console.log(userStore.value) // { theme: 'light', language: 'en' }
69
89
 
70
- // Change theme to dark:
90
+ // Change theme to dark
71
91
  userStore.value = { theme: 'dark', language: 'en' }
72
92
  // or
73
- userStore.set((v) => (v.theme = 'dark'))
93
+ userStore.set('theme', 'dark')
74
94
 
75
95
  console.log(userStore.value) // { theme: 'dark', language: 'en' }
76
96
  console.log(userStore.value.theme) // 'dark'
77
97
 
78
- // Present in localStorage:
79
- console.log(userStore.storage) // StorageΒ {userSettings: '\x00{"theme":"dark","language":"en"}', length: 1}
98
+ // Present in localStorage
99
+ console.log(userStore.storage) // StorageΒ {userSettings: '{"json":{"theme":"dark","language":"en"}}', length: 1}
80
100
  ```
81
101
 
82
- ### Different Ways to Assign a New Value
102
+ ### Advanced Ways to Assign a New Value
83
103
 
84
104
  ```js
85
105
  // Using setter
@@ -106,12 +126,12 @@ const sessionStore = new HyperStorage('sessionData', 'none', {
106
126
 
107
127
  sessionStore.value = 'temporary'
108
128
  console.log(sessionStore.value) // 'temporary'
109
- console.log(sessionStore.storage) // StorageΒ {sessionData: 'temporary', length: 1}
129
+ console.log(sessionStore.storage) // Storage {sessionData: '{"json":"temporary"}', length: 1}
110
130
  ```
111
131
 
112
132
  ### Using Encoding and Decoding Functions
113
133
 
114
- If you want to make stored data significantly harder to reverse-engineer, you should use the `encodeFn` and `decodeFn` options.
134
+ If you want to make stored data significantly harder to reverse-engineer, use the `encodeFn` and `decodeFn` options.
115
135
 
116
136
  Apply Base64 encoding using JavaScript's `btoa` (String to Base64) and `atob` (Base64 to String).
117
137
 
@@ -123,7 +143,14 @@ const sessionStore = new HyperStorage('sessionData', 'none', {
123
143
 
124
144
  sessionStore.value = 'temporary'
125
145
  console.log(sessionStore.value) // 'temporary'
126
- console.log(sessionStore.storage) // StorageΒ {sessionData: 'hN0IEUdoqmJ/', length: 1}
146
+ console.log(sessionStore.storage) // StorageΒ Β {sessionData: 'dGVtcG9yYXJ5', length: 1}
147
+ ```
148
+
149
+ The default values for `encodeFn` and `decodeFn` are:
150
+
151
+ ```ts
152
+ encodeFn = (value) => HyperStorage.superjson.stringify(value)
153
+ decodeFn = (value) => HyperStorage.superjson.parse<T>(value)
127
154
  ```
128
155
 
129
156
  ### Resetting Values
@@ -140,7 +167,7 @@ Internally uses `Storage.removeItem()` to remove the item from storage and sets
140
167
 
141
168
  ```js
142
169
  sessionStore.remove()
143
- console.log(sessionStore.value) // undefined
170
+ console.log(sessionStore.value) // 'none'
144
171
  console.log(sessionStore.storage) // StorageΒ {length: 0}
145
172
  ```
146
173
 
@@ -168,14 +195,21 @@ userStore.value = { theme: 'dark' }
168
195
  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
196
 
170
197
  ```ts
171
- const current = userStore.sync() // (method): unknown
198
+ // ... continues from the above example
172
199
 
173
- // 'current' is of type 'unknown'. ts(18046)
174
- console.log(current.theme) // { theme: 'dark' }
200
+ const result = userStore.sync() // (method): unknown
201
+ // Right now, 'result' equals 'userStore.value' exactly
175
202
 
176
- // Must narrow down
177
- if (current && typeof current === 'object' && 'theme' in current) {
178
- console.log(current.theme)
203
+ // 'result' is of type 'unknown'. ts(18046)
204
+ console.log(result.theme) // 'dark'
205
+
206
+ // No error, but unsafe
207
+ console.log(userStore.value.theme) // 'dark'
208
+
209
+ // Must narrow down to be safe
210
+ if (result && typeof result === 'object' && 'theme' in result) {
211
+ // No error, safe
212
+ console.log(result.theme) // 'dark'
179
213
  }
180
214
  ```
181
215
 
@@ -197,11 +231,11 @@ if (current && typeof current === 'object' && 'theme' in current) {
197
231
  - **Getter** β€” returns the cached value (very fast, does not use `JSON.parse`).
198
232
  - **Setter** β€” sets and caches the value, serializing and encoding it into `Storage`.
199
233
 
200
- ### `set(callback: (value: T) => T): T`
234
+ ### `set(keyOrCallback: (keyof T) | ((value: T) => T), value?: T[keyof T]): T`
201
235
 
202
- - Updates the stored value using a callback function.
203
- - The callback receives the current value and must return the new value.
204
- - Returns the newly stored value.
236
+ - If a **callback** is provided, it receives the current value and must return the new value.
237
+ - If a **key and value** are provided, it updates that single property on the stored object.
238
+ - Returns the updated stored value.
205
239
 
206
240
  ### `reset(): T`
207
241
 
@@ -251,7 +285,58 @@ localStorage.setItem('userSettings', '{"theme":"dark"}')
251
285
  userStore.sync((value) => JSON.parse(value))
252
286
 
253
287
  console.log(userStore.value) // { theme: 'dark' }
254
- console.log(userStore.storage) // Storage {userSettings: '\x00{"theme":"dark"}', length: 1}
288
+ console.log(userStore.storage) // Storage {userSettings: '{"json":{"theme":"dark"}}', length: 1}
289
+ ```
290
+
291
+ #### Why `sync()` is unsafe
292
+
293
+ 1. The item in `Storage` is undecodable by the `decodeFn` passed to `sync()`.
294
+
295
+ ```js
296
+ localStorage.setItem('userSettings', 'not an object')
297
+
298
+ // SyntaxError: Unexpected token 'o', "not an object" is not valid JSON
299
+ const result = userStore.sync((value) => JSON.parse(value))
300
+ // Execution continues because sync() uses a try-catch
301
+
302
+ console.log(result) // instance of SyntaxError
303
+
304
+ // Reset to default value
305
+ console.log(userStore.value) // {theme: 'system', language: 'en'}
306
+ ```
307
+
308
+ 2. The item in `Storage`, after being decoded, is not of type `T`.
309
+
310
+ ```js
311
+ localStorage.setItem('userSettings', '{"not":"valid"}')
312
+
313
+ const result = userStore.sync((value) => JSON.parse(value))
314
+ console.log(result) // {not: 'valid'}
315
+ console.log(userStore.value) // {not: 'valid'}
316
+ ```
317
+
318
+ No errors, but `result` and `userStore.value` are both not of type `T`.
319
+
320
+ This **must** be manually checked and `userStore.reset()` should be called if the check fails.
321
+
322
+ ```js
323
+ // This example is specifically for type 'Settings'
324
+ if (
325
+ result &&
326
+ typeof result === 'object' &&
327
+ 'theme' in result &&
328
+ 'language' in result &&
329
+ (result.theme === 'system' || result.theme === 'light' || result.theme === 'dark') &&
330
+ typeof result.language === 'string'
331
+ ) {
332
+ // 'result' is of type 'T'
333
+ } else {
334
+ // 'result' is not of type 'T'
335
+ userStore.reset()
336
+ console.log(userStore.value) // {theme: 'system', language: 'en'}
337
+ }
338
+
339
+ // 'userStore.value' is of type 'T'
255
340
  ```
256
341
 
257
342
  <br>