json-storage-formatter 3.0.0 β 3.0.2
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 +201 -87
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,131 +1,245 @@
|
|
|
1
|
-
# json-storage-formatter
|
|
1
|
+
# json-storage-formatter π
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+

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