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 +111 -26
- package/dist/index.cjs +874 -47
- package/dist/index.d.ts +7 -4
- package/dist/index.mjs +874 -47
- package/dist/index.umd.js +1 -1
- package/package.json +7 -2
- package/src/index.ts +28 -54
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
|
|
17
|
-
-
|
|
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
|
-
-
|
|
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
|
|
62
|
+
class HyperStorage<T> {
|
|
43
63
|
constructor(
|
|
44
64
|
itemName: string,
|
|
45
65
|
defaultValue: T,
|
|
46
66
|
options: {
|
|
47
|
-
encodeFn?: (value:
|
|
48
|
-
decodeFn?: (value: 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(
|
|
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: '
|
|
98
|
+
// Present in localStorage
|
|
99
|
+
console.log(userStore.storage) // StorageΒ {userSettings: '{"json":{"theme":"dark","language":"en"}}', length: 1}
|
|
80
100
|
```
|
|
81
101
|
|
|
82
|
-
###
|
|
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
|
|
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,
|
|
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
|
|
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) //
|
|
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
|
-
|
|
198
|
+
// ... continues from the above example
|
|
172
199
|
|
|
173
|
-
|
|
174
|
-
|
|
200
|
+
const result = userStore.sync() // (method): unknown
|
|
201
|
+
// Right now, 'result' equals 'userStore.value' exactly
|
|
175
202
|
|
|
176
|
-
//
|
|
177
|
-
|
|
178
|
-
|
|
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(
|
|
234
|
+
### `set(keyOrCallback: (keyof T) | ((value: T) => T), value?: T[keyof T]): T`
|
|
201
235
|
|
|
202
|
-
-
|
|
203
|
-
-
|
|
204
|
-
- Returns the
|
|
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: '
|
|
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>
|