json-storage-formatter 3.0.0 β 3.0.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/README.md +213 -84
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,131 +1,260 @@
|
|
|
1
|
-
|
|
1
|
+
const README = `
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# json-storage-formatter π
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+

|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
A **lightweight solution** to store complex JavaScript values as JSON β **without losing their types**. π
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
---
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## π€ The Problem
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
When working with tools like **localStorage**, **sessionStorage**, **AsyncStorage**, or even databases like **Redis** or **PostgreSQL (JSON columns)**,
|
|
14
|
+
we often need to store application state or configuration objects as strings using `JSON.stringify`.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
But thereβs a catch:
|
|
17
|
+
`JSON.stringify` has no idea what to do with values like `Date`, `Map`, or `Set`... It just flattens them into empty objects or strings, and when you parse them back with `JSON.parse`, itβs too late.
|
|
18
|
+
|
|
19
|
+
Letβs see an example.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
### π Example: Losing Real Data
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
const userProfile = {
|
|
27
|
+
id: 42,
|
|
28
|
+
createdAt: new Date('2024-10-01T10:30:00Z'),
|
|
29
|
+
preferences: new Map([
|
|
30
|
+
['theme', 'dark'],
|
|
31
|
+
['languages', new Set(['en', 'es'])],
|
|
32
|
+
]),
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
console.log(JSON.stringify(userProfile, null, 2));
|
|
36
|
+
```
|
|
17
37
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
38
|
+
**Console Output:**
|
|
39
|
+
|
|
40
|
+
```json
|
|
21
41
|
{
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
'key1',
|
|
26
|
-
{
|
|
27
|
-
$t: 'date',
|
|
28
|
-
$v: '2021-05-08T13:30:00.000Z',
|
|
29
|
-
},
|
|
30
|
-
],
|
|
31
|
-
[
|
|
32
|
-
'key2',
|
|
33
|
-
{
|
|
34
|
-
$t: 'set',
|
|
35
|
-
$v: [
|
|
36
|
-
{
|
|
37
|
-
$t: 'map',
|
|
38
|
-
$v: [
|
|
39
|
-
[
|
|
40
|
-
'key1',
|
|
41
|
-
{
|
|
42
|
-
$t: 'date',
|
|
43
|
-
$v: '2021-05-08T13:30:00.000Z',
|
|
44
|
-
},
|
|
45
|
-
],
|
|
46
|
-
],
|
|
47
|
-
},
|
|
48
|
-
],
|
|
49
|
-
},
|
|
50
|
-
],
|
|
51
|
-
],
|
|
42
|
+
"id": 42,
|
|
43
|
+
"createdAt": "2024-10-01T10:30:00.000Z",
|
|
44
|
+
"preferences": {}
|
|
52
45
|
}
|
|
53
|
-
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
What happened?
|
|
54
49
|
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
- The **Date** turned into a string.
|
|
51
|
+
- The **Map** disappeared.
|
|
52
|
+
- The **Set** vanished inside the Map.
|
|
57
53
|
|
|
58
|
-
|
|
54
|
+
If we try to restore it:
|
|
59
55
|
|
|
56
|
+
```ts
|
|
57
|
+
const parsed = JSON.parse(JSON.stringify(userProfile));
|
|
58
|
+
console.log(parsed.createdAt instanceof Date); // β false
|
|
59
|
+
console.log(parsed.preferences instanceof Map); // β false
|
|
60
60
|
```
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
When serializing JavaScript objects, we lose important value types β
|
|
63
|
+
Dates, Sets, Maps, RegExps, and even nested structures.
|
|
64
|
+
This forces us to write endless **custom parsers and validators** to recover data
|
|
65
|
+
that could have been automatically restored.
|
|
63
66
|
|
|
64
|
-
|
|
67
|
+
---
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
const object = formatFromStore<Map<string, unknown>>(objectWithMetadata);
|
|
69
|
+
## π‘ What json-storage-formatter Does
|
|
68
70
|
|
|
69
|
-
|
|
70
|
-
console.log(object);
|
|
71
|
+
Exposes **two simple functions**:
|
|
71
72
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
[
|
|
77
|
-
'key2',
|
|
78
|
-
new Set([new Map([['key1', new Date('2021-05-08T13:30:00.000Z')]])]),
|
|
79
|
-
],
|
|
80
|
-
]);
|
|
81
|
-
*/
|
|
82
|
-
```
|
|
73
|
+
1. `formatToStore(value)` β safely prepares your data for storage returning the JSON string representation.
|
|
74
|
+
2. `formatFromStore(value)` β restores it to the **exact original shape and types**.
|
|
75
|
+
|
|
76
|
+
As simple as that.
|
|
83
77
|
|
|
84
|
-
|
|
78
|
+
# Letβs see it a couple of examples
|
|
85
79
|
|
|
86
|
-
|
|
87
|
-
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## βοΈ Example: useDashboardConfig hook
|
|
83
|
+
|
|
84
|
+
Letβs create a hook that sync localStorage with react state:
|
|
88
85
|
|
|
89
86
|
```ts
|
|
90
|
-
|
|
87
|
+
import { formatToStore, formatFromStore } from 'json-storage-formatter';
|
|
88
|
+
|
|
89
|
+
type DashboardConfig = {
|
|
90
|
+
theme: 'light' | 'dark' | 'system';
|
|
91
|
+
widgets: Map<string, WidgetConfig>;
|
|
92
|
+
hiddenWidgets: Set<string>;
|
|
93
|
+
lastCustomizedAt: Date;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const useDashboardConfig = () => {
|
|
97
|
+
const [config, setConfig] = useState<DashboardConfig>(() => {
|
|
98
|
+
const json = localStorage.getItem('dashboard-config');
|
|
99
|
+
|
|
100
|
+
if (json) return formatFromStore<DashboardConfig>(json);
|
|
101
|
+
|
|
102
|
+
return getDefaultDashboardConfig();
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const saveConfig = (newConfig: DashboardConfig) => {
|
|
106
|
+
localStorage.setItem('dashboard-config', formatToStore(newConfig));
|
|
107
|
+
|
|
108
|
+
setConfig(newConfig);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
return { config, saveConfig };
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
// Even if the config contains Maps, Sets, or Dates, they will be preserved.
|
|
115
|
+
const example: DashboardConfig = {
|
|
116
|
+
theme: 'dark',
|
|
117
|
+
widgets: new Map([
|
|
118
|
+
['weather', { location: 'New York', units: 'metric' }],
|
|
119
|
+
['stocks', { symbols: ['AAPL', 'GOOGL'] }],
|
|
120
|
+
]),
|
|
121
|
+
hiddenWidgets: new Set(['news']),
|
|
122
|
+
lastCustomizedAt: new Date(),
|
|
123
|
+
};
|
|
91
124
|
```
|
|
92
125
|
|
|
93
|
-
|
|
126
|
+
---
|
|
94
127
|
|
|
95
|
-
|
|
128
|
+
## π Example: Sync dashboard queries through URL (Dashboards / Reports)
|
|
96
129
|
|
|
97
|
-
|
|
98
|
-
|
|
130
|
+
This pattern is common in dashboards, where filters are shared via URL.
|
|
131
|
+
You can safely serialize complex filters (with Dates, Sets, Maps, etc.) and encode them for sharing.
|
|
132
|
+
|
|
133
|
+
```tsx
|
|
134
|
+
import { formatToStore, formatFromStore } from 'json-storage-formatter';
|
|
135
|
+
|
|
136
|
+
type DashboardQuery = {
|
|
137
|
+
dateRange: { from: Date; to: Date };
|
|
138
|
+
selectedCategories: Set<string>;
|
|
139
|
+
additionalSettings: Map<string, unknown>;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const useUrlQuery = () => {
|
|
143
|
+
const [query, setQuery] = useState<DashboardQuery>(() => {
|
|
144
|
+
const params = new URLSearchParams(location.search);
|
|
145
|
+
const storedQuery = params.get('query');
|
|
146
|
+
|
|
147
|
+
if (storedQuery) {
|
|
148
|
+
// decode from base64 and restore types
|
|
149
|
+
return formatFromStore<DashboardQuery>(atob(storedQuery));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return getDefaultDashboardQuery();
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const updateQuery = (newQuery: DashboardQuery) => {
|
|
156
|
+
const encoded = btoa(formatToStore(newQuery));
|
|
157
|
+
|
|
158
|
+
// encode the json as base64 to make it URL-safe
|
|
159
|
+
window.history.replaceState(null, '', `${location.pathname}?query=${encoded}`);
|
|
160
|
+
|
|
161
|
+
setQuery(newQuery);
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
return { query, updateQuery };
|
|
165
|
+
};
|
|
99
166
|
```
|
|
100
167
|
|
|
101
|
-
|
|
168
|
+
The examples above are in react but the library is framework-agnostic and can be used anywhere in JavaScript or TypeScript projects.
|
|
102
169
|
|
|
103
|
-
|
|
170
|
+
## π§© Why Itβs Useful
|
|
104
171
|
|
|
105
|
-
|
|
106
|
-
|
|
172
|
+
This becomes incredibly powerful when your app needs to **sync complex state**
|
|
173
|
+
to a string-based storage layer β like when syncing to **localStorage**, **Redis**, or **a shared dashboard URL**.
|
|
174
|
+
|
|
175
|
+
Instead of being limited by what JSON can handle,
|
|
176
|
+
you can now serialize **any structure** β Maps, Sets, nested objects, or Dates β
|
|
177
|
+
and restore it back without losing context or meaning.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## π§ How It Works
|
|
182
|
+
|
|
183
|
+
Under the hood, `formatToStore` adds small metadata markers to every special value.
|
|
184
|
+
Each piece of data gets a structure like this:
|
|
185
|
+
|
|
186
|
+
```json
|
|
187
|
+
{
|
|
188
|
+
"$t": "map",
|
|
189
|
+
"$v": [["key", { "$t": "date", "$v": "2024-10-01T10:30:00.000Z" }]]
|
|
190
|
+
}
|
|
107
191
|
```
|
|
108
192
|
|
|
109
|
-
|
|
193
|
+
The `$t` field stores the **type**, and `$v` holds the actual value.
|
|
194
|
+
When using `formatFromStore`, it reads that metadata and recreates your data structure
|
|
195
|
+
exactly as it was before β even if itβs deeply nested.
|
|
110
196
|
|
|
111
|
-
|
|
197
|
+
Resulting on:
|
|
112
198
|
|
|
113
199
|
```ts
|
|
114
|
-
|
|
200
|
+
new Map([['key', new Date('2024-10-01T10:30:00.000Z')]]);
|
|
115
201
|
```
|
|
116
202
|
|
|
117
|
-
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## βοΈ API Reference
|
|
118
206
|
|
|
119
|
-
|
|
207
|
+
### π£ formatToStore
|
|
208
|
+
|
|
209
|
+
Converts any value into a JSON-safe structure with internal type metadata.
|
|
120
210
|
|
|
121
211
|
```ts
|
|
122
|
-
const
|
|
212
|
+
const objectWithMetadata = formatToStore(object);
|
|
123
213
|
```
|
|
124
214
|
|
|
125
|
-
|
|
215
|
+
### π΅ formatFromStore<T>
|
|
126
216
|
|
|
127
|
-
|
|
217
|
+
Restores the serialized object back to its original types.
|
|
128
218
|
|
|
129
219
|
```ts
|
|
130
|
-
const
|
|
220
|
+
const result = formatFromStore<Map<string, unknown>>(objectWithMetadata);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## π§° Utility Functions
|
|
226
|
+
|
|
227
|
+
| Function | Description | Example |
|
|
228
|
+
| ------------- | ---------------------------------------- | ----------------------------- |
|
|
229
|
+
| **isNil** | Checks if value is `null` or `undefined` | `isNil(null); // true` |
|
|
230
|
+
| **isNumber** | Checks if value is a number | `isNumber(42); // true` |
|
|
231
|
+
| **isBoolean** | Checks if value is a boolean | `isBoolean(false); // true` |
|
|
232
|
+
| **isString** | Checks if value is a string | `isString('hi'); // true` |
|
|
233
|
+
| **isDate** | Checks if value is a Date | `isDate(new Date()); // true` |
|
|
234
|
+
|
|
235
|
+
---
|
|
236
|
+
|
|
237
|
+
## βοΈ Comparison
|
|
238
|
+
|
|
239
|
+
| Behavior | JSON.stringify | json-storage-formatter |
|
|
240
|
+
| ---------------- | --------------- | ---------------------- |
|
|
241
|
+
| **Date** | Becomes string | β
Restored as Date |
|
|
242
|
+
| **Set** | Lost completely | β
Restored as Set |
|
|
243
|
+
| **Map** | Lost completely | β
Restored as Map |
|
|
244
|
+
| **Nested types** | Broken | β
Preserved |
|
|
245
|
+
| **Performance** | Fast | β‘ Nearly identical |
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## π¦ Installation
|
|
250
|
+
|
|
251
|
+
```bash
|
|
252
|
+
npm install json-storage-formatter
|
|
131
253
|
```
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## π― Ready to Try It?
|
|
258
|
+
|
|
259
|
+
π **NPM:** [json-storage-formatter](https://www.npmjs.com/package/json-storage-formatter)
|
|
260
|
+
Serialize, store, and restore **any JavaScript data type** without losing its identity β lightweight, fast, and fully type-safe. β¨
|