hyperstorage-js 6.0.4 β 6.0.6
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 +385 -332
- package/dist/index.cjs +4 -4
- package/dist/index.mjs +4 -4
- package/dist/index.umd.js +1 -1
- package/package.json +74 -74
- package/src/index.ts +4 -4
package/README.md
CHANGED
|
@@ -1,332 +1,385 @@
|
|
|
1
|
-
# HyperStorage: Storage Manager for JavaScript/TypeScript
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
##
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
<
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
//
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
### `
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
```js
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
1
|
+
# HyperStorage: Storage Manager for JavaScript/TypeScript
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/hyperstorage-js)
|
|
4
|
+
[](https://www.npmjs.com/package/hyperstorage-js)
|
|
5
|
+
[](https://www.jsdelivr.com/package/npm/hyperstorage-js)
|
|
6
|
+
|
|
7
|
+
## Description
|
|
8
|
+
|
|
9
|
+
A lightweight wrapper for Storage interfaces (e.g., `localStorage` or `sessionStorage`) with **efficient caching** and **type-preserving serialization**.
|
|
10
|
+
|
|
11
|
+
The biggest burdens of working with the **Storage API** is verifying values on every read, providing proper default values and only being able to store strings, having to `JSON.stringify()` and `JSON.parse()` manually everytime. This package eliminates this all by providing a safe, automatic and efficient wrapper that handles everything for you. You can read/store numbers and objects without any extra steps, lose no performance and improve code readability.
|
|
12
|
+
|
|
13
|
+
## Table of Contents
|
|
14
|
+
|
|
15
|
+
- [Features](#features)
|
|
16
|
+
- [Installation](#installation)
|
|
17
|
+
- [Constructor Syntax](#constructor-syntax)
|
|
18
|
+
- [Usage](#usage)
|
|
19
|
+
- [Putting a New Value in Storage](#putting-a-new-value-in-storage)
|
|
20
|
+
- [Using Another Storage API](#using-another-storage-api)
|
|
21
|
+
- [Using Encoding and Decoding Functions](#using-encoding-and-decoding-functions)
|
|
22
|
+
- [Reset to Default](#reset-to-default)
|
|
23
|
+
- [TypeScript Usage](#typescript-usage)
|
|
24
|
+
- [Using Type Parameter `T` in `HyperStorage<T>`](#using-type-parameter-t-in-hyperstoraget)
|
|
25
|
+
- [Using `sync()`](#using-sync)
|
|
26
|
+
- [Supported Types In Storage](#supported-types-in-storage)
|
|
27
|
+
- [Class Reference](#class-reference)
|
|
28
|
+
|
|
29
|
+
<br>
|
|
30
|
+
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- π **Default values**: are automatically set when the key is not in Storage.
|
|
34
|
+
- π§© **(Super)JSON support**: automatically serializes and parses everything using [superjson](https://github.com/flightcontrolhq/superjson).
|
|
35
|
+
- π§ **TypeScript support**: full type safety with strongly typed keys, values, and callbacks.
|
|
36
|
+
- π **Optional encoding/decoding**: hooks to obfuscate data or change the serializer.
|
|
37
|
+
- β‘ **Fast caching**: memory cache avoids repeated JSON convertions.
|
|
38
|
+
- π οΈ **Utility helpers**: use `.set()` and `.isDefault()` to simplify storage operations.
|
|
39
|
+
- π **Custom storage**: works with any object implementing the standard Storage API. (`localStorage`, `sessionStorage`, ...)
|
|
40
|
+
|
|
41
|
+
<br>
|
|
42
|
+
|
|
43
|
+
## Installation
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# npm
|
|
47
|
+
npm install hyperstorage-js
|
|
48
|
+
|
|
49
|
+
# pnpm
|
|
50
|
+
pnpm add hyperstorage-js
|
|
51
|
+
|
|
52
|
+
# bun
|
|
53
|
+
bun i hyperstorage-js
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
<br>
|
|
57
|
+
|
|
58
|
+
## Constructor Syntax
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
class HyperStorage<T> {
|
|
62
|
+
constructor(
|
|
63
|
+
itemName: string,
|
|
64
|
+
defaultValue: T,
|
|
65
|
+
options: {
|
|
66
|
+
encodeFn?: (value: T) => string
|
|
67
|
+
decodeFn?: (value: string) => T
|
|
68
|
+
storage?: Storage
|
|
69
|
+
} = {}
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
<br>
|
|
75
|
+
|
|
76
|
+
## Usage
|
|
77
|
+
|
|
78
|
+
```js
|
|
79
|
+
import HyperStorage from 'hyperstorage-js'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const defaultValue = { theme: 'light', language: 'en' }
|
|
84
|
+
const settingsStore = new HyperStorage('settings', defaultValue)
|
|
85
|
+
|
|
86
|
+
// If 'settings' is not present in localStorage, the defaultValue is set
|
|
87
|
+
console.log(settingsStore.value) // { theme: 'light', language: 'en' }
|
|
88
|
+
|
|
89
|
+
// Change theme to dark (one of multiple ways)
|
|
90
|
+
settingsStore.set('theme', 'dark')
|
|
91
|
+
|
|
92
|
+
console.log(settingsStore.value) // { theme: 'dark', language: 'en' }
|
|
93
|
+
console.log(window.localStorage) // StorageΒ {userSettings: '{"json":{"theme":"dark","language":"en"}}', length: 1}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Putting a New Value in Storage
|
|
97
|
+
|
|
98
|
+
The default method that works for all use cases is the `value` setter.
|
|
99
|
+
|
|
100
|
+
```js
|
|
101
|
+
store.value = 'anything'
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
The [`set()` (click)](#setkeyorcallback-keyof-t--value-t--t-value-tkeyof-t-t) method is a handy wrapper for assigning and can be used as shown in the examples below, which are sorted from best to worst practice.
|
|
105
|
+
|
|
106
|
+
<details>
|
|
107
|
+
<summary><strong>Object: Change Only One Property</strong></summary>
|
|
108
|
+
|
|
109
|
+
```js
|
|
110
|
+
// Property setter
|
|
111
|
+
settingsStore.set('theme', 'dark')
|
|
112
|
+
|
|
113
|
+
// Callback setter
|
|
114
|
+
settingsStore.set((v) => (v.theme = 'dark'))
|
|
115
|
+
|
|
116
|
+
// Spread operator
|
|
117
|
+
settingsStore.value = { ...settingsStore.value, theme: 'dark' }
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
</details>
|
|
121
|
+
|
|
122
|
+
<details>
|
|
123
|
+
<summary><strong>Number: Increment</strong></summary>
|
|
124
|
+
|
|
125
|
+
```js
|
|
126
|
+
// Increment operator
|
|
127
|
+
numberStore.value += 2
|
|
128
|
+
|
|
129
|
+
// Callback setter
|
|
130
|
+
numberStore.set((v) => (v += 2))
|
|
131
|
+
|
|
132
|
+
// Assign new value
|
|
133
|
+
numberStore.value = numberStore.value + 2
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
</details>
|
|
137
|
+
|
|
138
|
+
### Using Another Storage API
|
|
139
|
+
|
|
140
|
+
Use `sessionStorage` to only remember data for the duration of a session.
|
|
141
|
+
|
|
142
|
+
```js
|
|
143
|
+
const sessionStore = new HyperStorage('sessionData', 'none', {
|
|
144
|
+
storage: window.sessionStorage,
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
sessionStore.value = 'temporary'
|
|
148
|
+
console.log(sessionStore.value) // 'temporary'
|
|
149
|
+
console.log(sessionStore.storage) // Storage {sessionData: '{"json":"temporary"}', length: 1}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Using Encoding and Decoding Functions
|
|
153
|
+
|
|
154
|
+
If you want to make stored data harder to reverse-engineer, use the `encodeFn` and `decodeFn` options.
|
|
155
|
+
|
|
156
|
+
The default values for `encodeFn` and `decodeFn` are:
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
encodeFn = (value) => HyperStorage.superjson.stringify(value)
|
|
160
|
+
decodeFn = (value) => HyperStorage.superjson.parse(value)
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
<details>
|
|
164
|
+
<summary><strong>Base64 Example</strong></summary>
|
|
165
|
+
|
|
166
|
+
Apply Base64 encoding using JavaScript's `btoa` (String to Base64) and `atob` (Base64 to String).
|
|
167
|
+
|
|
168
|
+
```js
|
|
169
|
+
const sessionStore = new HyperStorage('sessionData', 'none', {
|
|
170
|
+
encodeFn: (value) => btoa(value),
|
|
171
|
+
decodeFn: (value) => atob(value),
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
sessionStore.value = 'temporary'
|
|
175
|
+
console.log(sessionStore.value) // 'temporary'
|
|
176
|
+
console.log(sessionStore.storage) // StorageΒ Β {sessionData: 'dGVtcG9yYXJ5', length: 1}
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
</details>
|
|
180
|
+
|
|
181
|
+
### Reset to Default
|
|
182
|
+
|
|
183
|
+
```js
|
|
184
|
+
console.log(sessionStore.value) // 'temporary'
|
|
185
|
+
console.log(sessionStore.defaultValue) // 'none'
|
|
186
|
+
|
|
187
|
+
sessionStore.reset()
|
|
188
|
+
console.log(sessionStore.value) // 'none'
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
<br>
|
|
192
|
+
|
|
193
|
+
## TypeScript Usage
|
|
194
|
+
|
|
195
|
+
### Using Type Parameter `T` in `HyperStorage<T>`
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
interface Settings {
|
|
199
|
+
theme: 'system' | 'light' | 'dark'
|
|
200
|
+
language: string
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const defaultValue: Settings = { theme: 'system', language: 'en' }
|
|
204
|
+
const userStore = new HyperStorage<Settings>('userSettings', defaultValue)
|
|
205
|
+
|
|
206
|
+
// Property 'language' is missing in type '{ theme: "dark"; }' but required in type 'Settings'. ts(2741)
|
|
207
|
+
userStore.value = { theme: 'dark' }
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Using `sync()`
|
|
211
|
+
|
|
212
|
+
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.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
// ... continues from the above example
|
|
216
|
+
|
|
217
|
+
const result = userStore.sync() // (method): unknown
|
|
218
|
+
// Right now, 'result' equals 'userStore.value' exactly
|
|
219
|
+
|
|
220
|
+
// 'result' is of type 'unknown'. ts(18046)
|
|
221
|
+
console.log(result.theme) // 'dark'
|
|
222
|
+
|
|
223
|
+
// No error, but unsafe
|
|
224
|
+
console.log(userStore.value.theme) // 'dark'
|
|
225
|
+
|
|
226
|
+
// Must narrow down to be safe
|
|
227
|
+
if (result && typeof result === 'object' && 'theme' in result) {
|
|
228
|
+
// No error, safe
|
|
229
|
+
console.log(result.theme) // 'dark'
|
|
230
|
+
}
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
<br>
|
|
234
|
+
|
|
235
|
+
## Supported Types in Storage
|
|
236
|
+
|
|
237
|
+
| Type | Supported by Storage API | Supported by HyperStorage (trough superjson) |
|
|
238
|
+
| --------- | ------------------------ | -------------------------------------------- |
|
|
239
|
+
| string | β
| β
|
|
|
240
|
+
| undefined | β | β
|
|
|
241
|
+
| null | β | β
|
|
|
242
|
+
| boolean | β | β
|
|
|
243
|
+
| number | β | β
|
|
|
244
|
+
| bigint | β | β
|
|
|
245
|
+
| Object | β | β
|
|
|
246
|
+
| Array | β | β
|
|
|
247
|
+
| Set | β | β
|
|
|
248
|
+
| Map | β | β
|
|
|
249
|
+
| URL | β | β
|
|
|
250
|
+
| Date | β | β
|
|
|
251
|
+
| RegExp | β | β
|
|
|
252
|
+
| Error | β | β
|
|
|
253
|
+
|
|
254
|
+
<br>
|
|
255
|
+
|
|
256
|
+
## Class Reference
|
|
257
|
+
|
|
258
|
+
### `constructor<T>(itemName: string, defaultValue: T, options = {})`
|
|
259
|
+
|
|
260
|
+
- **itemName**: `string` β key under which the data is stored.
|
|
261
|
+
- **defaultValue**: default value to be stored if none exists.
|
|
262
|
+
- **options** _(optional)_:
|
|
263
|
+
- `encodeFn` β function to encode values before writing to the `Storage`.
|
|
264
|
+
- `decodeFn` β function to decode values when reading from the `Storage`.
|
|
265
|
+
- `storage` β a `Storage` instance (e.g., `localStorage` or `sessionStorage`).
|
|
266
|
+
|
|
267
|
+
### `value`
|
|
268
|
+
|
|
269
|
+
- **Getter** β returns the cached value (very fast, does not use `JSON.parse`).
|
|
270
|
+
- **Setter** β sets and caches the value, serializing and encoding it into `Storage`.
|
|
271
|
+
|
|
272
|
+
### `set(keyOrCallback: (keyof T) | ((value: T) => T), value?: T[keyof T]): T`
|
|
273
|
+
|
|
274
|
+
- If a **callback** is provided, it receives the current value and must return the new value.
|
|
275
|
+
- If a **key and value** are provided, it updates that single property on the stored object.
|
|
276
|
+
- Returns the updated stored value.
|
|
277
|
+
|
|
278
|
+
### `reset(): T`
|
|
279
|
+
|
|
280
|
+
- Resets the stored value to `defaultValue`.
|
|
281
|
+
- Updates both `Storage` and internal cache.
|
|
282
|
+
- Returns the restored default value.
|
|
283
|
+
|
|
284
|
+
### `isDefault(): boolean`
|
|
285
|
+
|
|
286
|
+
- Checks whether the cached value equals the configured default.
|
|
287
|
+
- Uses reference comparison for objects and strict equality for primitives.
|
|
288
|
+
- Returns `true` if the current value matches the default, otherwise `false`.
|
|
289
|
+
|
|
290
|
+
```js
|
|
291
|
+
if (userStore.isDefault()) {
|
|
292
|
+
console.log('value equals the default value.')
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### `sync(decodeFn = this.decodeFn): unknown`
|
|
297
|
+
|
|
298
|
+
If the underlying `Storage` is not modified through the value setter, the internal cache will **not automatically update**. Use `sync()` to synchronize the internal cache with the actual value stored in `Storage`.
|
|
299
|
+
|
|
300
|
+
- **decodeFn** _(optional)_ β a function to decode values when reading (defaults to `this.decodeFn`).
|
|
301
|
+
- Reads the value from storage.
|
|
302
|
+
- Decodes it using `decodeFn`.
|
|
303
|
+
- Updates the internal cache.
|
|
304
|
+
- Returns the synchronized value.
|
|
305
|
+
|
|
306
|
+
The return type is `unknown` because data read from `Storage` cannot be type-checked or trusted at compile time because it may have been modified externally.
|
|
307
|
+
|
|
308
|
+
```js
|
|
309
|
+
// External change to storage (to be avoided)
|
|
310
|
+
localStorage.setItem('userSettings', '{"theme":"dark"}')
|
|
311
|
+
|
|
312
|
+
// Resynchronize the cache, optionally with a custom decoder
|
|
313
|
+
userStore.sync((value) => JSON.parse(value))
|
|
314
|
+
|
|
315
|
+
console.log(userStore.value) // { theme: 'dark' }
|
|
316
|
+
console.log(userStore.storage) // Storage {userSettings: '{"json":{"theme":"dark"}}', length: 1}
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
<br>
|
|
320
|
+
|
|
321
|
+
<details>
|
|
322
|
+
<summary><strong> Why <code>sync()</code> is unsafe</strong></summary>
|
|
323
|
+
|
|
324
|
+
1. The item in `Storage` is undecodable by the `decodeFn` passed to `sync()`.
|
|
325
|
+
|
|
326
|
+
```js
|
|
327
|
+
localStorage.setItem('userSettings', 'not an object')
|
|
328
|
+
|
|
329
|
+
// SyntaxError: Unexpected token 'o', "not an object" is not valid JSON
|
|
330
|
+
const result = userStore.sync((value) => JSON.parse(value))
|
|
331
|
+
// Execution continues because sync() uses a try-catch
|
|
332
|
+
|
|
333
|
+
console.log(result) // instance of SyntaxError
|
|
334
|
+
|
|
335
|
+
// Reset to default value
|
|
336
|
+
console.log(userStore.value) // {theme: 'system', language: 'en'}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
2. The item in `Storage`, after being decoded, is not of type `T`.
|
|
340
|
+
|
|
341
|
+
```js
|
|
342
|
+
localStorage.setItem('userSettings', '{"not":"valid"}')
|
|
343
|
+
|
|
344
|
+
const result = userStore.sync((value) => JSON.parse(value))
|
|
345
|
+
console.log(result) // {not: 'valid'}
|
|
346
|
+
console.log(userStore.value) // {not: 'valid'}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
No errors, but `result` and `userStore.value` are both not of type `T`.
|
|
350
|
+
|
|
351
|
+
This **must** be manually checked and `userStore.reset()` should be called if the check fails.
|
|
352
|
+
|
|
353
|
+
```js
|
|
354
|
+
// This example is specifically for type 'Settings'
|
|
355
|
+
if (
|
|
356
|
+
result &&
|
|
357
|
+
typeof result === 'object' &&
|
|
358
|
+
'theme' in result &&
|
|
359
|
+
'language' in result &&
|
|
360
|
+
(result.theme === 'system' || result.theme === 'light' || result.theme === 'dark') &&
|
|
361
|
+
typeof result.language === 'string'
|
|
362
|
+
) {
|
|
363
|
+
// 'result' is of type 'T'
|
|
364
|
+
} else {
|
|
365
|
+
// 'result' is not of type 'T'
|
|
366
|
+
userStore.reset()
|
|
367
|
+
console.log(userStore.value) // {theme: 'system', language: 'en'}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// 'userStore.value' is of type 'T'
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
</details>
|
|
374
|
+
|
|
375
|
+
<br>
|
|
376
|
+
|
|
377
|
+
## Source
|
|
378
|
+
|
|
379
|
+
[GitHub Repository](https://github.com/Khoeckman/HyperStorage)
|
|
380
|
+
|
|
381
|
+
<br>
|
|
382
|
+
|
|
383
|
+
## License
|
|
384
|
+
|
|
385
|
+
MIT
|
package/dist/index.cjs
CHANGED
|
@@ -867,7 +867,7 @@ SuperJSON.allowErrorProps;
|
|
|
867
867
|
*/
|
|
868
868
|
class HyperStorage {
|
|
869
869
|
/** Version of the library, injected via Rollup replace plugin. */
|
|
870
|
-
static version = "6.0.
|
|
870
|
+
static version = "6.0.6";
|
|
871
871
|
static superjson = SuperJSON;
|
|
872
872
|
/** Key name under which the data is stored. */
|
|
873
873
|
itemName;
|
|
@@ -941,15 +941,15 @@ class HyperStorage {
|
|
|
941
941
|
* Using this function should be avoided when possible and is not type safe.
|
|
942
942
|
*/
|
|
943
943
|
sync(decodeFn = this.#decodeFn) {
|
|
944
|
-
|
|
944
|
+
const json = this.storage.getItem(this.itemName);
|
|
945
945
|
// Reset value to defaultValue if it does not exist in storage
|
|
946
|
-
if (
|
|
946
|
+
if (json === null)
|
|
947
947
|
return this.reset();
|
|
948
|
-
// Reset value to defaultValue if the incoming value is not properly encoded
|
|
949
948
|
try {
|
|
950
949
|
return (this.value = decodeFn(json));
|
|
951
950
|
}
|
|
952
951
|
catch (err) {
|
|
952
|
+
// Reset value to defaultValue if the incoming value is not properly encoded
|
|
953
953
|
console.error(err);
|
|
954
954
|
this.reset();
|
|
955
955
|
return err;
|
package/dist/index.mjs
CHANGED
|
@@ -865,7 +865,7 @@ SuperJSON.allowErrorProps;
|
|
|
865
865
|
*/
|
|
866
866
|
class HyperStorage {
|
|
867
867
|
/** Version of the library, injected via Rollup replace plugin. */
|
|
868
|
-
static version = "6.0.
|
|
868
|
+
static version = "6.0.6";
|
|
869
869
|
static superjson = SuperJSON;
|
|
870
870
|
/** Key name under which the data is stored. */
|
|
871
871
|
itemName;
|
|
@@ -939,15 +939,15 @@ class HyperStorage {
|
|
|
939
939
|
* Using this function should be avoided when possible and is not type safe.
|
|
940
940
|
*/
|
|
941
941
|
sync(decodeFn = this.#decodeFn) {
|
|
942
|
-
|
|
942
|
+
const json = this.storage.getItem(this.itemName);
|
|
943
943
|
// Reset value to defaultValue if it does not exist in storage
|
|
944
|
-
if (
|
|
944
|
+
if (json === null)
|
|
945
945
|
return this.reset();
|
|
946
|
-
// Reset value to defaultValue if the incoming value is not properly encoded
|
|
947
946
|
try {
|
|
948
947
|
return (this.value = decodeFn(json));
|
|
949
948
|
}
|
|
950
949
|
catch (err) {
|
|
950
|
+
// Reset value to defaultValue if the incoming value is not properly encoded
|
|
951
951
|
console.error(err);
|
|
952
952
|
this.reset();
|
|
953
953
|
return err;
|
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).HyperStorage=t()}(this,function(){"use strict";class e{constructor(){this.keyToValue=new Map,this.valueToKey=new Map}set(e,t){this.keyToValue.set(e,t),this.valueToKey.set(t,e)}getByKey(e){return this.keyToValue.get(e)}getByValue(e){return this.valueToKey.get(e)}clear(){this.keyToValue.clear(),this.valueToKey.clear()}}class t{constructor(t){this.generateIdentifier=t,this.kv=new e}register(e,t){this.kv.getByValue(e)||(t||(t=this.generateIdentifier(e)),this.kv.set(t,e))}clear(){this.kv.clear()}getIdentifier(e){return this.kv.getByValue(e)}getValue(e){return this.kv.getByKey(e)}}class r extends t{constructor(){super(e=>e.name),this.classToAllowedProps=new Map}register(e,t){"object"==typeof t?(t.allowProps&&this.classToAllowedProps.set(e,t.allowProps),super.register(e,t.identifier)):super.register(e,t)}getAllowedProps(e){return this.classToAllowedProps.get(e)}}function n(e,t){const r=function(e){if("values"in Object)return Object.values(e);const t=[];for(const r in e)e.hasOwnProperty(r)&&t.push(e[r]);return t}(e);if("find"in r)return r.find(t);const n=r;for(let e=0;e<n.length;e++){const r=n[e];if(t(r))return r}}function s(e,t){Object.entries(e).forEach(([e,r])=>t(r,e))}function o(e,t){return-1!==e.indexOf(t)}function i(e,t){for(let r=0;r<e.length;r++){const n=e[r];if(t(n))return n}}class a{constructor(){this.transfomers={}}register(e){this.transfomers[e.name]=e}findApplicable(e){return n(this.transfomers,t=>t.isApplicable(e))}findByName(e){return this.transfomers[e]}}const l=e=>void 0===e,u=e=>"object"==typeof e&&null!==e&&(e!==Object.prototype&&(null===Object.getPrototypeOf(e)||Object.getPrototypeOf(e)===Object.prototype)),c=e=>u(e)&&0===Object.keys(e).length,f=e=>Array.isArray(e),p=e=>e instanceof Map,d=e=>e instanceof Set,y=e=>"Symbol"===(e=>Object.prototype.toString.call(e).slice(8,-1))(e),g=e=>e instanceof Error,m=e=>"number"==typeof e&&isNaN(e),h=e=>(e=>"boolean"==typeof e)(e)||(e=>null===e)(e)||l(e)||(e=>"number"==typeof e&&!isNaN(e))(e)||(e=>"string"==typeof e)(e)||y(e),b=e=>e.replace(/\\/g,"\\\\").replace(/\./g,"\\."),w=e=>e.map(String).map(b).join("."),v=(e,t)=>{const r=[];let n="";for(let s=0;s<e.length;s++){let o=e.charAt(s);if(!t&&"\\"===o){const t=e.charAt(s+1);if("\\"===t){n+="\\",s++;continue}if("."!==t)throw Error("invalid path")}if("\\"===o&&"."===e.charAt(s+1)){n+=".",s++;continue}"."===o?(r.push(n),n=""):n+=o}const s=n;return r.push(s),r};function E(e,t,r,n){return{isApplicable:e,annotation:t,transform:r,untransform:n}}const I=[E(l,"undefined",()=>null,()=>{}),E(e=>"bigint"==typeof e,"bigint",e=>e.toString(),e=>"undefined"!=typeof BigInt?BigInt(e):(console.error("Please add a BigInt polyfill."),e)),E(e=>e instanceof Date&&!isNaN(e.valueOf()),"Date",e=>e.toISOString(),e=>new Date(e)),E(g,"Error",(e,t)=>{const r={name:e.name,message:e.message};return"cause"in e&&(r.cause=e.cause),t.allowedErrorProps.forEach(t=>{r[t]=e[t]}),r},(e,t)=>{const r=new Error(e.message,{cause:e.cause});return r.name=e.name,r.stack=e.stack,t.allowedErrorProps.forEach(t=>{r[t]=e[t]}),r}),E(e=>e instanceof RegExp,"regexp",e=>""+e,e=>{const t=e.slice(1,e.lastIndexOf("/")),r=e.slice(e.lastIndexOf("/")+1);return new RegExp(t,r)}),E(d,"set",e=>[...e.values()],e=>new Set(e)),E(p,"map",e=>[...e.entries()],e=>new Map(e)),E(e=>{return m(e)||((t=e)===1/0||t===-1/0);var t},"number",e=>m(e)?"NaN":e>0?"Infinity":"-Infinity",Number),E(e=>0===e&&1/e==-1/0,"number",()=>"-0",Number),E(e=>e instanceof URL,"URL",e=>e.toString(),e=>new URL(e))];function k(e,t,r,n){return{isApplicable:e,annotation:t,transform:r,untransform:n}}const O=k((e,t)=>{if(y(e)){return!!t.symbolRegistry.getIdentifier(e)}return!1},(e,t)=>["symbol",t.symbolRegistry.getIdentifier(e)],e=>e.description,(e,t,r)=>{const n=r.symbolRegistry.getValue(t[1]);if(!n)throw new Error("Trying to deserialize unknown symbol");return n}),j=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,Uint8ClampedArray].reduce((e,t)=>(e[t.name]=t,e),{}),A=k(e=>ArrayBuffer.isView(e)&&!(e instanceof DataView),e=>["typed-array",e.constructor.name],e=>[...e],(e,t)=>{const r=j[t[1]];if(!r)throw new Error("Trying to deserialize unknown typed array");return new r(e)});function T(e,t){if(e?.constructor){return!!t.classRegistry.getIdentifier(e.constructor)}return!1}const P=k(T,(e,t)=>["class",t.classRegistry.getIdentifier(e.constructor)],(e,t)=>{const r=t.classRegistry.getAllowedProps(e.constructor);if(!r)return{...e};const n={};return r.forEach(t=>{n[t]=e[t]}),n},(e,t,r)=>{const n=r.classRegistry.getValue(t[1]);if(!n)throw new Error(`Trying to deserialize unknown class '${t[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);return Object.assign(Object.create(n.prototype),e)}),V=k((e,t)=>!!t.customTransformerRegistry.findApplicable(e),(e,t)=>["custom",t.customTransformerRegistry.findApplicable(e).name],(e,t)=>t.customTransformerRegistry.findApplicable(e).serialize(e),(e,t,r)=>{const n=r.customTransformerRegistry.findByName(t[1]);if(!n)throw new Error("Trying to deserialize unknown custom value");return n.deserialize(e)}),R=[P,O,V,A],S=(e,t)=>{const r=i(R,r=>r.isApplicable(e,t));if(r)return{value:r.transform(e,t),type:r.annotation(e,t)};const n=i(I,r=>r.isApplicable(e,t));return n?{value:n.transform(e,t),type:n.annotation}:void 0},N={};I.forEach(e=>{N[e.annotation]=e});const z=(e,t)=>{if(t>e.size)throw new Error("index out of bounds");const r=e.keys();for(;t>0;)r.next(),t--;return r.next().value};function _(e){if(o(e,"__proto__"))throw new Error("__proto__ is not allowed as a property");if(o(e,"prototype"))throw new Error("prototype is not allowed as a property");if(o(e,"constructor"))throw new Error("constructor is not allowed as a property")}const x=(e,t,r)=>{if(_(t),0===t.length)return r(e);let n=e;for(let e=0;e<t.length-1;e++){const r=t[e];if(f(n)){n=n[+r]}else if(u(n))n=n[r];else if(d(n)){n=z(n,+r)}else if(p(n)){if(e===t.length-2)break;const s=+r,o=0===+t[++e]?"key":"value",i=z(n,s);switch(o){case"key":n=i;break;case"value":n=n.get(i)}}}const s=t[t.length-1];if(f(n)?n[+s]=r(n[+s]):u(n)&&(n[s]=r(n[s])),d(n)){const e=z(n,+s),t=r(e);e!==t&&(n.delete(e),n.add(t))}if(p(n)){const e=+t[t.length-2],o=z(n,e);switch(0===+s?"key":"value"){case"key":{const e=r(o);n.set(e,n.get(o)),e!==o&&n.delete(o);break}case"value":n.set(o,r(n.get(o)))}}return e},F=e=>e<1;function B(e,t,r,n=[]){if(!e)return;const o=F(r);if(!f(e))return void s(e,(e,s)=>B(e,t,r,[...n,...v(s,o)]));const[i,a]=e;a&&s(a,(e,s)=>{B(e,t,r,[...n,...v(s,o)])}),t(i,n)}function C(e,t,r,n){return B(t,(t,r)=>{e=x(e,r,e=>((e,t,r)=>{if(!f(t)){const n=N[t];if(!n)throw new Error("Unknown transformation: "+t);return n.untransform(e,r)}switch(t[0]){case"symbol":return O.untransform(e,t,r);case"class":return P.untransform(e,t,r);case"custom":return V.untransform(e,t,r);case"typed-array":return A.untransform(e,t,r);default:throw new Error("Unknown transformation: "+t)}})(e,t,n))},r),e}function U(e,t,r){const n=F(r);function o(t,r){const s=((e,t)=>{_(t);for(let r=0;r<t.length;r++){const n=t[r];if(d(e))e=z(e,+n);else if(p(e)){const s=+n,o=0===+t[++r]?"key":"value",i=z(e,s);switch(o){case"key":e=i;break;case"value":e=e.get(i)}}else e=e[n]}return e})(e,v(r,n));t.map(e=>v(e,n)).forEach(t=>{e=x(e,t,()=>s)})}if(f(t)){const[r,i]=t;r.forEach(t=>{e=x(e,v(t,n),()=>e)}),i&&s(i,o)}else s(t,o);return e}const M=(e,t,r,n,i=[],a=[],l=new Map)=>{const y=h(e);if(!y){!function(e,t,r){const n=r.get(e);n?n.push(t):r.set(e,[t])}(e,i,t);const r=l.get(e);if(r)return n?{transformedValue:null}:r}if(!((e,t)=>u(e)||f(e)||p(e)||d(e)||g(e)||T(e,t))(e,r)){const t=S(e,r),n=t?{transformedValue:t.value,annotations:[t.type]}:{transformedValue:e};return y||l.set(e,n),n}if(o(a,e))return{transformedValue:null};const m=S(e,r),w=m?.value??e,v=f(w)?[]:{},E={};s(w,(o,c)=>{if("__proto__"===c||"constructor"===c||"prototype"===c)throw new Error(`Detected property ${c}. This is a prototype pollution risk, please remove it from your object.`);const p=M(o,t,r,n,[...i,c],[...a,e],l);v[c]=p.transformedValue,f(p.annotations)?E[b(c)]=p.annotations:u(p.annotations)&&s(p.annotations,(e,t)=>{E[b(c)+"."+t]=e})});const I=c(E)?{transformedValue:v,annotations:m?[m.type]:void 0}:{transformedValue:v,annotations:m?[m.type,E]:E};return y||l.set(e,I),I};function D(e){return Object.prototype.toString.call(e).slice(8,-1)}function K(e){return"Array"===D(e)}function q(e,t={}){if(K(e))return e.map(e=>q(e,t));if(!function(e){if("Object"!==D(e))return!1;const t=Object.getPrototypeOf(e);return!!t&&t.constructor===Object&&t===Object.prototype}(e))return e;return[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)].reduce((r,n)=>{if("__proto__"===n)return r;if(K(t.props)&&!t.props.includes(n))return r;return function(e,t,r,n,s){const o={}.propertyIsEnumerable.call(n,t)?"enumerable":"nonenumerable";"enumerable"===o&&(e[t]=r),s&&"nonenumerable"===o&&Object.defineProperty(e,t,{value:r,enumerable:!1,writable:!0,configurable:!0})}(r,n,q(e[n],t),e,t.nonenumerable),r},{})}class L{constructor({dedupe:e=!1}={}){this.classRegistry=new r,this.symbolRegistry=new t(e=>e.description??""),this.customTransformerRegistry=new a,this.allowedErrorProps=[],this.dedupe=e}serialize(e){const t=new Map,r=M(e,t,this,this.dedupe),n={json:r.transformedValue};r.annotations&&(n.meta={...n.meta,values:r.annotations});const s=function(e,t){const r={};let n;return e.forEach(e=>{if(e.length<=1)return;t||(e=e.map(e=>e.map(String)).sort((e,t)=>e.length-t.length));const[s,...o]=e;0===s.length?n=o.map(w):r[w(s)]=o.map(w)}),n?c(r)?[n]:[n,r]:c(r)?void 0:r}(t,this.dedupe);return s&&(n.meta={...n.meta,referentialEqualities:s}),n.meta&&(n.meta.v=1),n}deserialize(e,t){const{json:r,meta:n}=e;let s=t?.inPlace?r:q(r);return n?.values&&(s=C(s,n.values,n.v??0,this)),n?.referentialEqualities&&(s=U(s,n.referentialEqualities,n.v??0)),s}stringify(e){return JSON.stringify(this.serialize(e))}parse(e){return this.deserialize(JSON.parse(e),{inPlace:!0})}registerClass(e,t){this.classRegistry.register(e,t)}registerSymbol(e,t){this.symbolRegistry.register(e,t)}registerCustom(e,t){this.customTransformerRegistry.register({name:t,...e})}allowErrorProps(...e){this.allowedErrorProps.push(...e)}}L.defaultInstance=new L,L.serialize=L.defaultInstance.serialize.bind(L.defaultInstance),L.deserialize=L.defaultInstance.deserialize.bind(L.defaultInstance),L.stringify=L.defaultInstance.stringify.bind(L.defaultInstance),L.parse=L.defaultInstance.parse.bind(L.defaultInstance),L.registerClass=L.defaultInstance.registerClass.bind(L.defaultInstance),L.registerSymbol=L.defaultInstance.registerSymbol.bind(L.defaultInstance),L.registerCustom=L.defaultInstance.registerCustom.bind(L.defaultInstance),L.allowErrorProps=L.defaultInstance.allowErrorProps.bind(L.defaultInstance),L.serialize,L.deserialize,L.stringify,L.parse,L.registerClass,L.registerCustom,L.registerSymbol,L.allowErrorProps;class J{static version="6.0.4";static superjson=L;itemName;defaultValue;#e;#t;storage;#r;constructor(e,t,r={}){const{encodeFn:n,decodeFn:s,storage:o=window.localStorage}=r;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.#e=n||(e=>J.superjson.stringify(e)),s&&"function"!=typeof s)throw new TypeError("decodeFn is defined but is not a function");if(this.#t=s||(e=>J.superjson.parse(e)),!(o instanceof Storage))throw new TypeError("storage must be an instance of Storage");this.storage=o,this.sync()}set value(e){this.#r=e,this.storage.setItem(this.itemName,this.#e(e))}get value(){return this.#r}set(e,t){return this.value="function"==typeof e?e(this.#r):{...this.#r,[e]:t}}sync(e=this.#t){let t=this.storage.getItem(this.itemName);if("string"!=typeof t)return this.reset();try{return this.value=e(t)}catch(e){return console.error(e),this.reset(),e}}reset(){return this.value=this.defaultValue}isDefault(){return this.#r===this.defaultValue}}return J});
|
|
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";class DoubleIndexedKV{constructor(){this.keyToValue=new Map,this.valueToKey=new Map}set(e,t){this.keyToValue.set(e,t),this.valueToKey.set(t,e)}getByKey(e){return this.keyToValue.get(e)}getByValue(e){return this.valueToKey.get(e)}clear(){this.keyToValue.clear(),this.valueToKey.clear()}}class Registry{constructor(e){this.generateIdentifier=e,this.kv=new DoubleIndexedKV}register(e,t){this.kv.getByValue(e)||(t||(t=this.generateIdentifier(e)),this.kv.set(t,e))}clear(){this.kv.clear()}getIdentifier(e){return this.kv.getByValue(e)}getValue(e){return this.kv.getByKey(e)}}class ClassRegistry extends Registry{constructor(){super(e=>e.name),this.classToAllowedProps=new Map}register(e,t){"object"==typeof t?(t.allowProps&&this.classToAllowedProps.set(e,t.allowProps),super.register(e,t.identifier)):super.register(e,t)}getAllowedProps(e){return this.classToAllowedProps.get(e)}}function e(e,t){const r=function(e){if("values"in Object)return Object.values(e);const t=[];for(const r in e)e.hasOwnProperty(r)&&t.push(e[r]);return t}(e);if("find"in r)return r.find(t);const n=r;for(let e=0;e<n.length;e++){const r=n[e];if(t(r))return r}}function t(e,t){Object.entries(e).forEach(([e,r])=>t(r,e))}function r(e,t){return-1!==e.indexOf(t)}function n(e,t){for(let r=0;r<e.length;r++){const n=e[r];if(t(n))return n}}class CustomTransformerRegistry{constructor(){this.transfomers={}}register(e){this.transfomers[e.name]=e}findApplicable(t){return e(this.transfomers,e=>e.isApplicable(t))}findByName(e){return this.transfomers[e]}}const s=e=>void 0===e,o=e=>"object"==typeof e&&null!==e&&(e!==Object.prototype&&(null===Object.getPrototypeOf(e)||Object.getPrototypeOf(e)===Object.prototype)),i=e=>o(e)&&0===Object.keys(e).length,a=e=>Array.isArray(e),u=e=>e instanceof Map,l=e=>e instanceof Set,c=e=>"Symbol"===(e=>Object.prototype.toString.call(e).slice(8,-1))(e),f=e=>e instanceof Error,p=e=>"number"==typeof e&&isNaN(e),d=e=>(e=>"boolean"==typeof e)(e)||(e=>null===e)(e)||s(e)||(e=>"number"==typeof e&&!isNaN(e))(e)||(e=>"string"==typeof e)(e)||c(e),y=e=>e.replace(/\\/g,"\\\\").replace(/\./g,"\\."),g=e=>e.map(String).map(y).join("."),m=(e,t)=>{const r=[];let n="";for(let s=0;s<e.length;s++){let o=e.charAt(s);if(!t&&"\\"===o){const t=e.charAt(s+1);if("\\"===t){n+="\\",s++;continue}if("."!==t)throw Error("invalid path")}if("\\"===o&&"."===e.charAt(s+1)){n+=".",s++;continue}"."===o?(r.push(n),n=""):n+=o}const s=n;return r.push(s),r};function h(e,t,r,n){return{isApplicable:e,annotation:t,transform:r,untransform:n}}const S=[h(s,"undefined",()=>null,()=>{}),h(e=>"bigint"==typeof e,"bigint",e=>e.toString(),e=>"undefined"!=typeof BigInt?BigInt(e):(console.error("Please add a BigInt polyfill."),e)),h(e=>e instanceof Date&&!isNaN(e.valueOf()),"Date",e=>e.toISOString(),e=>new Date(e)),h(f,"Error",(e,t)=>{const r={name:e.name,message:e.message};return"cause"in e&&(r.cause=e.cause),t.allowedErrorProps.forEach(t=>{r[t]=e[t]}),r},(e,t)=>{const r=new Error(e.message,{cause:e.cause});return r.name=e.name,r.stack=e.stack,t.allowedErrorProps.forEach(t=>{r[t]=e[t]}),r}),h(e=>e instanceof RegExp,"regexp",e=>""+e,e=>{const t=e.slice(1,e.lastIndexOf("/")),r=e.slice(e.lastIndexOf("/")+1);return new RegExp(t,r)}),h(l,"set",e=>[...e.values()],e=>new Set(e)),h(u,"map",e=>[...e.entries()],e=>new Map(e)),h(e=>{return p(e)||((t=e)===1/0||t===-1/0);var t},"number",e=>p(e)?"NaN":e>0?"Infinity":"-Infinity",Number),h(e=>0===e&&1/e==-1/0,"number",()=>"-0",Number),h(e=>e instanceof URL,"URL",e=>e.toString(),e=>new URL(e))];function b(e,t,r,n){return{isApplicable:e,annotation:t,transform:r,untransform:n}}const w=b((e,t)=>{if(c(e)){return!!t.symbolRegistry.getIdentifier(e)}return!1},(e,t)=>["symbol",t.symbolRegistry.getIdentifier(e)],e=>e.description,(e,t,r)=>{const n=r.symbolRegistry.getValue(t[1]);if(!n)throw new Error("Trying to deserialize unknown symbol");return n}),O=[Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,Uint8ClampedArray].reduce((e,t)=>(e[t.name]=t,e),{}),N=b(e=>ArrayBuffer.isView(e)&&!(e instanceof DataView),e=>["typed-array",e.constructor.name],e=>[...e],(e,t)=>{const r=O[t[1]];if(!r)throw new Error("Trying to deserialize unknown typed array");return new r(e)});function v(e,t){if(e?.constructor){return!!t.classRegistry.getIdentifier(e.constructor)}return!1}const E=b(v,(e,t)=>["class",t.classRegistry.getIdentifier(e.constructor)],(e,t)=>{const r=t.classRegistry.getAllowedProps(e.constructor);if(!r)return{...e};const n={};return r.forEach(t=>{n[t]=e[t]}),n},(e,t,r)=>{const n=r.classRegistry.getValue(t[1]);if(!n)throw new Error(`Trying to deserialize unknown class '${t[1]}' - check https://github.com/blitz-js/superjson/issues/116#issuecomment-773996564`);return Object.assign(Object.create(n.prototype),e)}),I=b((e,t)=>!!t.customTransformerRegistry.findApplicable(e),(e,t)=>["custom",t.customTransformerRegistry.findApplicable(e).name],(e,t)=>t.customTransformerRegistry.findApplicable(e).serialize(e),(e,t,r)=>{const n=r.customTransformerRegistry.findByName(t[1]);if(!n)throw new Error("Trying to deserialize unknown custom value");return n.deserialize(e)}),J=[E,w,I,N],k=(e,t)=>{const r=n(J,r=>r.isApplicable(e,t));if(r)return{value:r.transform(e,t),type:r.annotation(e,t)};const s=n(S,r=>r.isApplicable(e,t));return s?{value:s.transform(e,t),type:s.annotation}:void 0},j={};S.forEach(e=>{j[e.annotation]=e});const A=(e,t)=>{if(t>e.size)throw new Error("index out of bounds");const r=e.keys();for(;t>0;)r.next(),t--;return r.next().value};function T(e){if(r(e,"__proto__"))throw new Error("__proto__ is not allowed as a property");if(r(e,"prototype"))throw new Error("prototype is not allowed as a property");if(r(e,"constructor"))throw new Error("constructor is not allowed as a property")}const R=(e,t,r)=>{if(T(t),0===t.length)return r(e);let n=e;for(let e=0;e<t.length-1;e++){const r=t[e];if(a(n)){n=n[+r]}else if(o(n))n=n[r];else if(l(n)){n=A(n,+r)}else if(u(n)){if(e===t.length-2)break;const s=+r,o=0===+t[++e]?"key":"value",i=A(n,s);switch(o){case"key":n=i;break;case"value":n=n.get(i)}}}const s=t[t.length-1];if(a(n)?n[+s]=r(n[+s]):o(n)&&(n[s]=r(n[s])),l(n)){const e=A(n,+s),t=r(e);e!==t&&(n.delete(e),n.add(t))}if(u(n)){const e=+t[t.length-2],o=A(n,e);switch(0===+s?"key":"value"){case"key":{const e=r(o);n.set(e,n.get(o)),e!==o&&n.delete(o);break}case"value":n.set(o,r(n.get(o)))}}return e},V=e=>e<1;function P(e,r,n,s=[]){if(!e)return;const o=V(n);if(!a(e))return void t(e,(e,t)=>P(e,r,n,[...s,...m(t,o)]));const[i,u]=e;u&&t(u,(e,t)=>{P(e,r,n,[...s,...m(t,o)])}),r(i,s)}function z(e,t,r,n){return P(t,(t,r)=>{e=R(e,r,e=>((e,t,r)=>{if(!a(t)){const n=j[t];if(!n)throw new Error("Unknown transformation: "+t);return n.untransform(e,r)}switch(t[0]){case"symbol":return w.untransform(e,t,r);case"class":return E.untransform(e,t,r);case"custom":return I.untransform(e,t,r);case"typed-array":return N.untransform(e,t,r);default:throw new Error("Unknown transformation: "+t)}})(e,t,n))},r),e}function _(e,r,n){const s=V(n);function o(t,r){const n=((e,t)=>{T(t);for(let r=0;r<t.length;r++){const n=t[r];if(l(e))e=A(e,+n);else if(u(e)){const s=+n,o=0===+t[++r]?"key":"value",i=A(e,s);switch(o){case"key":e=i;break;case"value":e=e.get(i)}}else e=e[n]}return e})(e,m(r,s));t.map(e=>m(e,s)).forEach(t=>{e=R(e,t,()=>n)})}if(a(r)){const[n,i]=r;n.forEach(t=>{e=R(e,m(t,s),()=>e)}),i&&t(i,o)}else t(r,o);return e}const x=(e,n,s,c,p=[],g=[],m=new Map)=>{const h=d(e);if(!h){!function(e,t,r){const n=r.get(e);n?n.push(t):r.set(e,[t])}(e,p,n);const t=m.get(e);if(t)return c?{transformedValue:null}:t}if(!((e,t)=>o(e)||a(e)||u(e)||l(e)||f(e)||v(e,t))(e,s)){const t=k(e,s),r=t?{transformedValue:t.value,annotations:[t.type]}:{transformedValue:e};return h||m.set(e,r),r}if(r(g,e))return{transformedValue:null};const S=k(e,s),b=S?.value??e,w=a(b)?[]:{},O={};t(b,(r,i)=>{if("__proto__"===i||"constructor"===i||"prototype"===i)throw new Error(`Detected property ${i}. This is a prototype pollution risk, please remove it from your object.`);const u=x(r,n,s,c,[...p,i],[...g,e],m);w[i]=u.transformedValue,a(u.annotations)?O[y(i)]=u.annotations:o(u.annotations)&&t(u.annotations,(e,t)=>{O[y(i)+"."+t]=e})});const N=i(O)?{transformedValue:w,annotations:S?[S.type]:void 0}:{transformedValue:w,annotations:S?[S.type,O]:O};return h||m.set(e,N),N};function C(e){return Object.prototype.toString.call(e).slice(8,-1)}function F(e){return"Array"===C(e)}function B(e,t={}){if(F(e))return e.map(e=>B(e,t));if(!function(e){if("Object"!==C(e))return!1;const t=Object.getPrototypeOf(e);return!!t&&t.constructor===Object&&t===Object.prototype}(e))return e;return[...Object.getOwnPropertyNames(e),...Object.getOwnPropertySymbols(e)].reduce((r,n)=>{if("__proto__"===n)return r;if(F(t.props)&&!t.props.includes(n))return r;return function(e,t,r,n,s){const o={}.propertyIsEnumerable.call(n,t)?"enumerable":"nonenumerable";"enumerable"===o&&(e[t]=r),s&&"nonenumerable"===o&&Object.defineProperty(e,t,{value:r,enumerable:!1,writable:!0,configurable:!0})}(r,n,B(e[n],t),e,t.nonenumerable),r},{})}class SuperJSON{constructor({dedupe:e=!1}={}){this.classRegistry=new ClassRegistry,this.symbolRegistry=new Registry(e=>e.description??""),this.customTransformerRegistry=new CustomTransformerRegistry,this.allowedErrorProps=[],this.dedupe=e}serialize(e){const t=new Map,r=x(e,t,this,this.dedupe),n={json:r.transformedValue};r.annotations&&(n.meta={...n.meta,values:r.annotations});const s=function(e,t){const r={};let n;return e.forEach(e=>{if(e.length<=1)return;t||(e=e.map(e=>e.map(String)).sort((e,t)=>e.length-t.length));const[s,...o]=e;0===s.length?n=o.map(g):r[g(s)]=o.map(g)}),n?i(r)?[n]:[n,r]:i(r)?void 0:r}(t,this.dedupe);return s&&(n.meta={...n.meta,referentialEqualities:s}),n.meta&&(n.meta.v=1),n}deserialize(e,t){const{json:r,meta:n}=e;let s=t?.inPlace?r:B(r);return n?.values&&(s=z(s,n.values,n.v??0,this)),n?.referentialEqualities&&(s=_(s,n.referentialEqualities,n.v??0)),s}stringify(e){return JSON.stringify(this.serialize(e))}parse(e){return this.deserialize(JSON.parse(e),{inPlace:!0})}registerClass(e,t){this.classRegistry.register(e,t)}registerSymbol(e,t){this.symbolRegistry.register(e,t)}registerCustom(e,t){this.customTransformerRegistry.register({name:t,...e})}allowErrorProps(...e){this.allowedErrorProps.push(...e)}}SuperJSON.defaultInstance=new SuperJSON,SuperJSON.serialize=SuperJSON.defaultInstance.serialize.bind(SuperJSON.defaultInstance),SuperJSON.deserialize=SuperJSON.defaultInstance.deserialize.bind(SuperJSON.defaultInstance),SuperJSON.stringify=SuperJSON.defaultInstance.stringify.bind(SuperJSON.defaultInstance),SuperJSON.parse=SuperJSON.defaultInstance.parse.bind(SuperJSON.defaultInstance),SuperJSON.registerClass=SuperJSON.defaultInstance.registerClass.bind(SuperJSON.defaultInstance),SuperJSON.registerSymbol=SuperJSON.defaultInstance.registerSymbol.bind(SuperJSON.defaultInstance),SuperJSON.registerCustom=SuperJSON.defaultInstance.registerCustom.bind(SuperJSON.defaultInstance),SuperJSON.allowErrorProps=SuperJSON.defaultInstance.allowErrorProps.bind(SuperJSON.defaultInstance),SuperJSON.serialize,SuperJSON.deserialize,SuperJSON.stringify,SuperJSON.parse,SuperJSON.registerClass,SuperJSON.registerCustom,SuperJSON.registerSymbol,SuperJSON.allowErrorProps;class HyperStorage{static version="6.0.6";static superjson=SuperJSON;itemName;defaultValue;#e;#t;storage;#r;constructor(e,t,r={}){const{encodeFn:n,decodeFn:s,storage:o=window.localStorage}=r;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.#e=n||(e=>HyperStorage.superjson.stringify(e)),s&&"function"!=typeof s)throw new TypeError("decodeFn is defined but is not a function");if(this.#t=s||(e=>HyperStorage.superjson.parse(e)),!(o instanceof Storage))throw new TypeError("storage must be an instance of Storage");this.storage=o,this.sync()}set value(e){this.#r=e,this.storage.setItem(this.itemName,this.#e(e))}get value(){return this.#r}set(e,t){return this.value="function"==typeof e?e(this.#r):{...this.#r,[e]:t}}sync(e=this.#t){const t=this.storage.getItem(this.itemName);if(null===t)return this.reset();try{return this.value=e(t)}catch(e){return console.error(e),this.reset(),e}}reset(){return this.value=this.defaultValue}isDefault(){return this.#r===this.defaultValue}}return HyperStorage});
|
package/package.json
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "hyperstorage-js",
|
|
3
|
-
"version": "6.0.
|
|
4
|
-
"description": "A lightweight wrapper for localStorage/sessionStorage with efficient caching and type-preserving serialization.",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"author": "Khoeckman",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"main": "dist/index.umd.js",
|
|
9
|
-
"module": "dist/index.mjs",
|
|
10
|
-
"types": "dist/index.d.ts",
|
|
11
|
-
"exports": {
|
|
12
|
-
".": {
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
|
-
"import": "./dist/index.mjs",
|
|
15
|
-
"require": "./dist/index.cjs",
|
|
16
|
-
"default": "./dist/index.umd.js"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"files": [
|
|
20
|
-
"src/",
|
|
21
|
-
"dist/"
|
|
22
|
-
],
|
|
23
|
-
"scripts": {
|
|
24
|
-
"test": "exit 0",
|
|
25
|
-
"build": "rollup -c rollup.config.js",
|
|
26
|
-
"prepack": "npm run build"
|
|
27
|
-
},
|
|
28
|
-
"dependencies": {
|
|
29
|
-
"superjson": "^2.2.6"
|
|
30
|
-
},
|
|
31
|
-
"devDependencies": {
|
|
32
|
-
"@rollup/plugin-commonjs": "^29.0.
|
|
33
|
-
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
34
|
-
"@rollup/plugin-replace": "^6.0.3",
|
|
35
|
-
"@rollup/plugin-terser": "^0.
|
|
36
|
-
"@rollup/plugin-typescript": "^12.3.0",
|
|
37
|
-
"@types/node": "^
|
|
38
|
-
"pnpm": "^10.
|
|
39
|
-
"prettier": "^3.
|
|
40
|
-
"rollup": "^4.
|
|
41
|
-
"rollup-plugin-delete": "^3.0.2",
|
|
42
|
-
"rollup-plugin-prettier": "^4.1.
|
|
43
|
-
"tslib": "^2.8.1",
|
|
44
|
-
"typescript": "^5.9.3"
|
|
45
|
-
},
|
|
46
|
-
"publishConfig": {
|
|
47
|
-
"access": "public"
|
|
48
|
-
},
|
|
49
|
-
"homepage": "https://github.com/Khoeckman/HyperStorage#readme",
|
|
50
|
-
"bugs": {
|
|
51
|
-
"url": "https://github.com/Khoeckman/HyperStorage/issues"
|
|
52
|
-
},
|
|
53
|
-
"repository": {
|
|
54
|
-
"type": "git",
|
|
55
|
-
"url": "git+https://github.com/Khoeckman/HyperStorage.git"
|
|
56
|
-
},
|
|
57
|
-
"keywords": [
|
|
58
|
-
"localStorage",
|
|
59
|
-
"sessionStorage",
|
|
60
|
-
"storage",
|
|
61
|
-
"utility",
|
|
62
|
-
"javascript",
|
|
63
|
-
"js",
|
|
64
|
-
"typescript",
|
|
65
|
-
"ts",
|
|
66
|
-
"ecmascript",
|
|
67
|
-
"es",
|
|
68
|
-
"umd",
|
|
69
|
-
"browser",
|
|
70
|
-
"client",
|
|
71
|
-
"module",
|
|
72
|
-
"commonjs"
|
|
73
|
-
]
|
|
74
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "hyperstorage-js",
|
|
3
|
+
"version": "6.0.6",
|
|
4
|
+
"description": "A lightweight wrapper for localStorage/sessionStorage with efficient caching and type-preserving serialization.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Khoeckman",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.umd.js",
|
|
9
|
+
"module": "dist/index.mjs",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.mjs",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"default": "./dist/index.umd.js"
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"files": [
|
|
20
|
+
"src/",
|
|
21
|
+
"dist/"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "exit 0",
|
|
25
|
+
"build": "rollup -c rollup.config.js",
|
|
26
|
+
"prepack": "npm run build"
|
|
27
|
+
},
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"superjson": "^2.2.6"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@rollup/plugin-commonjs": "^29.0.2",
|
|
33
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
34
|
+
"@rollup/plugin-replace": "^6.0.3",
|
|
35
|
+
"@rollup/plugin-terser": "^1.0.0",
|
|
36
|
+
"@rollup/plugin-typescript": "^12.3.0",
|
|
37
|
+
"@types/node": "^25.3.5",
|
|
38
|
+
"pnpm": "^10.30.3",
|
|
39
|
+
"prettier": "^3.8.1",
|
|
40
|
+
"rollup": "^4.59.0",
|
|
41
|
+
"rollup-plugin-delete": "^3.0.2",
|
|
42
|
+
"rollup-plugin-prettier": "^4.1.2",
|
|
43
|
+
"tslib": "^2.8.1",
|
|
44
|
+
"typescript": "^5.9.3"
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/Khoeckman/HyperStorage#readme",
|
|
50
|
+
"bugs": {
|
|
51
|
+
"url": "https://github.com/Khoeckman/HyperStorage/issues"
|
|
52
|
+
},
|
|
53
|
+
"repository": {
|
|
54
|
+
"type": "git",
|
|
55
|
+
"url": "git+https://github.com/Khoeckman/HyperStorage.git"
|
|
56
|
+
},
|
|
57
|
+
"keywords": [
|
|
58
|
+
"localStorage",
|
|
59
|
+
"sessionStorage",
|
|
60
|
+
"storage",
|
|
61
|
+
"utility",
|
|
62
|
+
"javascript",
|
|
63
|
+
"js",
|
|
64
|
+
"typescript",
|
|
65
|
+
"ts",
|
|
66
|
+
"ecmascript",
|
|
67
|
+
"es",
|
|
68
|
+
"umd",
|
|
69
|
+
"browser",
|
|
70
|
+
"client",
|
|
71
|
+
"module",
|
|
72
|
+
"commonjs"
|
|
73
|
+
]
|
|
74
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -68,7 +68,7 @@ class HyperStorage<T> {
|
|
|
68
68
|
this.#encodeFn = encodeFn || ((v) => HyperStorage.superjson.stringify(v))
|
|
69
69
|
|
|
70
70
|
if (decodeFn && typeof decodeFn !== 'function') throw new TypeError('decodeFn is defined but is not a function')
|
|
71
|
-
this.#decodeFn = decodeFn || ((v) => HyperStorage.superjson.parse
|
|
71
|
+
this.#decodeFn = decodeFn || ((v) => HyperStorage.superjson.parse(v))
|
|
72
72
|
|
|
73
73
|
if (!(storage instanceof Storage)) throw new TypeError('storage must be an instance of Storage')
|
|
74
74
|
this.storage = storage
|
|
@@ -113,15 +113,15 @@ class HyperStorage<T> {
|
|
|
113
113
|
* Using this function should be avoided when possible and is not type safe.
|
|
114
114
|
*/
|
|
115
115
|
sync(decodeFn = this.#decodeFn): unknown {
|
|
116
|
-
|
|
116
|
+
const json = this.storage.getItem(this.itemName)
|
|
117
117
|
|
|
118
118
|
// Reset value to defaultValue if it does not exist in storage
|
|
119
|
-
if (
|
|
119
|
+
if (json === null) return this.reset()
|
|
120
120
|
|
|
121
|
-
// Reset value to defaultValue if the incoming value is not properly encoded
|
|
122
121
|
try {
|
|
123
122
|
return (this.value = decodeFn(json))
|
|
124
123
|
} catch (err) {
|
|
124
|
+
// Reset value to defaultValue if the incoming value is not properly encoded
|
|
125
125
|
console.error(err)
|
|
126
126
|
this.reset()
|
|
127
127
|
return err
|