storion 0.8.3 → 0.9.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/CHANGELOG.md +68 -20
- package/dist/persist/index.js +48 -26
- package/dist/persist/persist.d.ts +111 -54
- package/dist/persist/persist.d.ts.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,31 +8,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
|
+
|
|
11
12
|
- `MetaEntry.fields` now supports arrays for applying meta to multiple fields at once
|
|
12
13
|
```ts
|
|
13
|
-
meta: [notPersisted.for(["password", "token"])]
|
|
14
|
+
meta: [notPersisted.for(["password", "token"])];
|
|
14
15
|
```
|
|
15
16
|
- `MetaQuery.fields(type, predicate?)` method to get field names with a specific meta type
|
|
16
17
|
```ts
|
|
17
|
-
const sessionFields = ctx.meta.fields(sessionStore);
|
|
18
|
-
const highPriority = ctx.meta.fields(priority, v => v > 5);
|
|
19
|
-
```
|
|
20
|
-
- `persistMiddleware` `fields` option for multi-storage patterns
|
|
21
|
-
```ts
|
|
22
|
-
// Split fields between session and local storage
|
|
23
|
-
persistMiddleware({
|
|
24
|
-
filter: ({ meta }) => meta.any(sessionStore),
|
|
25
|
-
fields: ({ meta }) => meta.fields(sessionStore),
|
|
26
|
-
save: (ctx, state) => sessionStorage.setItem(ctx.displayName, JSON.stringify(state)),
|
|
27
|
-
})
|
|
18
|
+
const sessionFields = ctx.meta.fields(sessionStore); // ['token', 'userId']
|
|
19
|
+
const highPriority = ctx.meta.fields(priority, (v) => v > 5);
|
|
28
20
|
```
|
|
29
21
|
- `applyFor` now supports object form to map patterns to different middleware
|
|
30
22
|
```ts
|
|
31
23
|
applyFor({
|
|
32
|
-
|
|
24
|
+
userStore: loggingMiddleware,
|
|
33
25
|
"auth*": [authMiddleware, securityMiddleware],
|
|
34
26
|
"*Cache": cacheMiddleware,
|
|
35
|
-
})
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
|
|
32
|
+
- **BREAKING**: `persistMiddleware` API refactored for better encapsulation
|
|
33
|
+
|
|
34
|
+
- New `handler` option replaces `load`/`save` callbacks
|
|
35
|
+
- Handler receives `PersistContext` (extends `StoreMiddlewareContext` with `store` instance)
|
|
36
|
+
- Handler returns `{ load, save }` object (can be sync or async)
|
|
37
|
+
- `onError` signature changed to `(error, operation)` where operation is `"init" | "load" | "save"`
|
|
38
|
+
- Enables encapsulated async initialization (e.g., IndexedDB)
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
// Before (old API)
|
|
42
|
+
persistMiddleware({
|
|
43
|
+
load: (ctx) => localStorage.getItem(ctx.displayName),
|
|
44
|
+
save: (ctx, state) =>
|
|
45
|
+
localStorage.setItem(ctx.displayName, JSON.stringify(state)),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
// After (new API)
|
|
49
|
+
persistMiddleware({
|
|
50
|
+
handler: (ctx) => {
|
|
51
|
+
const key = `app:${ctx.displayName}`;
|
|
52
|
+
return {
|
|
53
|
+
load: () => JSON.parse(localStorage.getItem(key) || "null"),
|
|
54
|
+
save: (state) => localStorage.setItem(key, JSON.stringify(state)),
|
|
55
|
+
};
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Async handler (IndexedDB)
|
|
60
|
+
persistMiddleware({
|
|
61
|
+
handler: async (ctx) => {
|
|
62
|
+
const db = await openDB("app-db");
|
|
63
|
+
return {
|
|
64
|
+
load: () => db.get("stores", ctx.displayName),
|
|
65
|
+
save: (state) => db.put("stores", state, ctx.displayName),
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
36
69
|
```
|
|
37
70
|
|
|
38
71
|
---
|
|
@@ -40,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
40
73
|
## [0.8.0] - 2024-12-21
|
|
41
74
|
|
|
42
75
|
### Added
|
|
76
|
+
|
|
43
77
|
- **Persist Module** (`storion/persist`)
|
|
44
78
|
- `persistMiddleware(options)` for automatic state persistence
|
|
45
79
|
- `notPersisted` meta for excluding stores or fields from persistence
|
|
@@ -58,6 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
58
92
|
- `store.hydrate(state, { force })` - force option to override dirty properties
|
|
59
93
|
|
|
60
94
|
### Changed
|
|
95
|
+
|
|
61
96
|
- `StoreMiddlewareContext` now includes `meta` property for querying store metadata
|
|
62
97
|
- `FactoryMiddlewareContext` now includes `meta` property for querying factory metadata
|
|
63
98
|
|
|
@@ -66,6 +101,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
66
101
|
## [0.7.0] - 2024-12-15
|
|
67
102
|
|
|
68
103
|
### Added
|
|
104
|
+
|
|
69
105
|
- **DevTools Module** (`storion/devtools`)
|
|
70
106
|
- `devtoolsMiddleware()` for state inspection
|
|
71
107
|
- `__revertState` and `__takeSnapshot` injected actions
|
|
@@ -79,6 +115,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
79
115
|
- `create()` shorthand for single-store apps returning `[instance, useHook, withStore]`
|
|
80
116
|
|
|
81
117
|
### Changed
|
|
118
|
+
|
|
82
119
|
- Improved TypeScript inference for store actions
|
|
83
120
|
|
|
84
121
|
---
|
|
@@ -86,6 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
86
123
|
## [0.6.0] - 2024-12-01
|
|
87
124
|
|
|
88
125
|
### Added
|
|
126
|
+
|
|
89
127
|
- **Async Module** (`storion/async`)
|
|
90
128
|
- `async.fresh<T>()` - throws during loading (Suspense-compatible)
|
|
91
129
|
- `async.stale<T>(initialData)` - returns stale data during loading
|
|
@@ -95,6 +133,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
95
133
|
- `trigger(action, deps, ...args)` for declarative data fetching in components
|
|
96
134
|
|
|
97
135
|
### Changed
|
|
136
|
+
|
|
98
137
|
- Effects now require synchronous functions (use `ctx.safe()` for async)
|
|
99
138
|
|
|
100
139
|
---
|
|
@@ -102,6 +141,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
102
141
|
## [0.5.0] - 2024-11-15
|
|
103
142
|
|
|
104
143
|
### Added
|
|
144
|
+
|
|
105
145
|
- **Focus (Lens-like Access)**
|
|
106
146
|
- `focus(path)` for nested state access
|
|
107
147
|
- Returns `[getter, setter]` tuple
|
|
@@ -119,6 +159,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
119
159
|
## [0.4.0] - 2024-11-01
|
|
120
160
|
|
|
121
161
|
### Added
|
|
162
|
+
|
|
122
163
|
- **Middleware System**
|
|
123
164
|
- `container({ middleware: [...] })` for middleware injection
|
|
124
165
|
- Middleware receives `MiddlewareContext` with `type`, `next`, `resolver`
|
|
@@ -127,6 +168,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
127
168
|
- `createValidationMiddleware()` built-in middleware
|
|
128
169
|
|
|
129
170
|
### Changed
|
|
171
|
+
|
|
130
172
|
- Container now uses middleware chain pattern
|
|
131
173
|
|
|
132
174
|
---
|
|
@@ -134,6 +176,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
134
176
|
## [0.3.0] - 2024-10-15
|
|
135
177
|
|
|
136
178
|
### Added
|
|
179
|
+
|
|
137
180
|
- **Store Lifecycle**
|
|
138
181
|
- `lifetime: "keepAlive"` (default) - persists until container disposal
|
|
139
182
|
- `lifetime: "autoDispose"` - disposes when no subscribers
|
|
@@ -145,6 +188,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
145
188
|
- `store.reset()` to restore initial state
|
|
146
189
|
|
|
147
190
|
### Changed
|
|
191
|
+
|
|
148
192
|
- Stores now track dirty state automatically
|
|
149
193
|
|
|
150
194
|
---
|
|
@@ -152,6 +196,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
152
196
|
## [0.2.0] - 2024-10-01
|
|
153
197
|
|
|
154
198
|
### Added
|
|
199
|
+
|
|
155
200
|
- **Dependency Injection**
|
|
156
201
|
- `container()` for managing store instances
|
|
157
202
|
- `get(factory)` for resolving dependencies
|
|
@@ -161,6 +206,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
161
206
|
- `useContainer()` hook
|
|
162
207
|
|
|
163
208
|
### Changed
|
|
209
|
+
|
|
164
210
|
- Stores are now lazily instantiated via container
|
|
165
211
|
|
|
166
212
|
---
|
|
@@ -168,6 +214,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
168
214
|
## [0.1.0] - 2024-09-15
|
|
169
215
|
|
|
170
216
|
### Added
|
|
217
|
+
|
|
171
218
|
- **Core Store**
|
|
172
219
|
- `store(options)` factory function
|
|
173
220
|
- `state` - reactive state object
|
|
@@ -195,22 +242,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
195
242
|
### Migrating to 0.8.0
|
|
196
243
|
|
|
197
244
|
#### Meta System Changes
|
|
245
|
+
|
|
198
246
|
If you were using internal meta APIs, update to the new public API:
|
|
199
247
|
|
|
200
248
|
```ts
|
|
201
249
|
// Before (internal)
|
|
202
|
-
spec.meta // was MetaEntry[]
|
|
250
|
+
spec.meta; // was MetaEntry[]
|
|
203
251
|
|
|
204
252
|
// After (0.8.0)
|
|
205
|
-
ctx.meta(persistMeta).store
|
|
206
|
-
ctx.meta(persistMeta).fields
|
|
207
|
-
ctx.meta.all(type)
|
|
208
|
-
ctx.meta.any(type1, type2)
|
|
253
|
+
ctx.meta(persistMeta).store; // query store-level
|
|
254
|
+
ctx.meta(persistMeta).fields; // query field-level
|
|
255
|
+
ctx.meta.all(type); // get all values
|
|
256
|
+
ctx.meta.any(type1, type2); // check existence
|
|
209
257
|
```
|
|
210
258
|
|
|
211
259
|
### Migrating to 0.6.0
|
|
212
260
|
|
|
213
261
|
#### Async Effects
|
|
262
|
+
|
|
214
263
|
Effects must now be synchronous:
|
|
215
264
|
|
|
216
265
|
```ts
|
|
@@ -239,4 +288,3 @@ effect((ctx) => {
|
|
|
239
288
|
[0.3.0]: https://github.com/linq2js/storion/compare/v0.2.0...v0.3.0
|
|
240
289
|
[0.2.0]: https://github.com/linq2js/storion/compare/v0.1.0...v0.2.0
|
|
241
290
|
[0.1.0]: https://github.com/linq2js/storion/releases/tag/v0.1.0
|
|
242
|
-
|
package/dist/persist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import { m as meta } from "../meta-40r-AZfe.js";
|
|
|
2
2
|
import { i as isPromiseLike } from "../isPromiseLike-bFkfHAbm.js";
|
|
3
3
|
const notPersisted = meta();
|
|
4
4
|
function persistMiddleware(options) {
|
|
5
|
-
const { filter, fields,
|
|
5
|
+
const { filter, fields, handler, onError, force = false } = options;
|
|
6
6
|
return (context) => {
|
|
7
7
|
const { next, meta: meta2 } = context;
|
|
8
8
|
const instance = next();
|
|
@@ -38,39 +38,61 @@ function persistMiddleware(options) {
|
|
|
38
38
|
}
|
|
39
39
|
return filtered;
|
|
40
40
|
};
|
|
41
|
-
const
|
|
42
|
-
|
|
41
|
+
const persistContext = {
|
|
42
|
+
...context,
|
|
43
|
+
store: instance
|
|
44
|
+
};
|
|
45
|
+
const setupPersistence = (persistHandler) => {
|
|
46
|
+
const { load, save } = persistHandler;
|
|
47
|
+
const hydrateWithState = (state) => {
|
|
48
|
+
if (state != null) {
|
|
49
|
+
try {
|
|
50
|
+
instance.hydrate(filterState(state), { force });
|
|
51
|
+
} catch (error) {
|
|
52
|
+
onError == null ? void 0 : onError(error, "load");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
if (load) {
|
|
43
57
|
try {
|
|
44
|
-
|
|
58
|
+
const loadResult = load();
|
|
59
|
+
if (loadResult) {
|
|
60
|
+
if (isPromiseLike(loadResult)) {
|
|
61
|
+
loadResult.then(
|
|
62
|
+
(state) => hydrateWithState(state),
|
|
63
|
+
(error) => onError == null ? void 0 : onError(error, "load")
|
|
64
|
+
);
|
|
65
|
+
} else {
|
|
66
|
+
hydrateWithState(loadResult);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
45
69
|
} catch (error) {
|
|
46
|
-
onError == null ? void 0 : onError(
|
|
70
|
+
onError == null ? void 0 : onError(error, "load");
|
|
47
71
|
}
|
|
48
72
|
}
|
|
73
|
+
if (save) {
|
|
74
|
+
instance.subscribe(() => {
|
|
75
|
+
try {
|
|
76
|
+
const state = instance.dehydrate();
|
|
77
|
+
save(filterState(state));
|
|
78
|
+
} catch (error) {
|
|
79
|
+
onError == null ? void 0 : onError(error, "save");
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
49
83
|
};
|
|
50
84
|
try {
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
hydrateWithState(loadResult);
|
|
60
|
-
}
|
|
85
|
+
const handlerResult = handler(persistContext);
|
|
86
|
+
if (isPromiseLike(handlerResult)) {
|
|
87
|
+
handlerResult.then(
|
|
88
|
+
(persistHandler) => setupPersistence(persistHandler),
|
|
89
|
+
(error) => onError == null ? void 0 : onError(error, "init")
|
|
90
|
+
);
|
|
91
|
+
} else {
|
|
92
|
+
setupPersistence(handlerResult);
|
|
61
93
|
}
|
|
62
94
|
} catch (error) {
|
|
63
|
-
onError == null ? void 0 : onError(
|
|
64
|
-
}
|
|
65
|
-
if (save) {
|
|
66
|
-
instance.subscribe(() => {
|
|
67
|
-
try {
|
|
68
|
-
const state = instance.dehydrate();
|
|
69
|
-
save(context, filterState(state));
|
|
70
|
-
} catch (error) {
|
|
71
|
-
onError == null ? void 0 : onError(context, error, "save");
|
|
72
|
-
}
|
|
73
|
-
});
|
|
95
|
+
onError == null ? void 0 : onError(error, "init");
|
|
74
96
|
}
|
|
75
97
|
return instance;
|
|
76
98
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { StoreMiddleware, StoreMiddlewareContext } from '../types';
|
|
1
|
+
import { StoreMiddleware, StoreMiddlewareContext, StoreInstance } from '../types';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Mark stores or fields as not persisted.
|
|
@@ -27,8 +27,8 @@ import { StoreMiddleware, StoreMiddlewareContext } from '../types';
|
|
|
27
27
|
* state: { name: '', password: '', token: '' },
|
|
28
28
|
* setup: () => ({}),
|
|
29
29
|
* meta: [
|
|
30
|
-
* notPersisted('password'),
|
|
31
|
-
* notPersisted('token'),
|
|
30
|
+
* notPersisted.for('password'),
|
|
31
|
+
* notPersisted.for('token'),
|
|
32
32
|
* ],
|
|
33
33
|
* });
|
|
34
34
|
* ```
|
|
@@ -38,6 +38,33 @@ export declare const notPersisted: import('..').MetaType<any, [], true>;
|
|
|
38
38
|
* Result from load function - can be sync or async
|
|
39
39
|
*/
|
|
40
40
|
export type PersistLoadResult = Record<string, unknown> | null | undefined | Promise<Record<string, unknown> | null | undefined>;
|
|
41
|
+
/**
|
|
42
|
+
* Context passed to the handler function.
|
|
43
|
+
* Extends StoreMiddlewareContext with the created store instance.
|
|
44
|
+
*/
|
|
45
|
+
export interface PersistContext extends StoreMiddlewareContext {
|
|
46
|
+
/** The store instance being persisted */
|
|
47
|
+
store: StoreInstance;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Handler returned by the handler function.
|
|
51
|
+
* Contains the load and save operations for a specific store.
|
|
52
|
+
*/
|
|
53
|
+
export interface PersistHandler {
|
|
54
|
+
/**
|
|
55
|
+
* Load persisted state for the store.
|
|
56
|
+
* Can return sync or async result.
|
|
57
|
+
*
|
|
58
|
+
* @returns The persisted state, null/undefined if not found, or a Promise
|
|
59
|
+
*/
|
|
60
|
+
load?: () => PersistLoadResult;
|
|
61
|
+
/**
|
|
62
|
+
* Save state to persistent storage.
|
|
63
|
+
*
|
|
64
|
+
* @param state - The dehydrated state to save
|
|
65
|
+
*/
|
|
66
|
+
save?: (state: Record<string, unknown>) => void;
|
|
67
|
+
}
|
|
41
68
|
/**
|
|
42
69
|
* Options for persist middleware
|
|
43
70
|
*/
|
|
@@ -59,28 +86,43 @@ export interface PersistOptions {
|
|
|
59
86
|
*/
|
|
60
87
|
fields?: (context: StoreMiddlewareContext) => string[];
|
|
61
88
|
/**
|
|
62
|
-
*
|
|
63
|
-
*
|
|
89
|
+
* Handler factory that creates load/save operations for each store.
|
|
90
|
+
* Receives context with store instance, returns handler with load/save.
|
|
91
|
+
* Can be sync or async (e.g., for IndexedDB initialization).
|
|
64
92
|
*
|
|
65
|
-
* @param context - The
|
|
66
|
-
* @returns
|
|
67
|
-
*/
|
|
68
|
-
load?: (context: StoreMiddlewareContext) => PersistLoadResult;
|
|
69
|
-
/**
|
|
70
|
-
* Save state to persistent storage.
|
|
93
|
+
* @param context - The persist context with store instance
|
|
94
|
+
* @returns Handler with load/save operations, or Promise of handler
|
|
71
95
|
*
|
|
72
|
-
* @
|
|
73
|
-
*
|
|
96
|
+
* @example Sync handler (localStorage)
|
|
97
|
+
* ```ts
|
|
98
|
+
* handler: (ctx) => {
|
|
99
|
+
* const key = `app:${ctx.displayName}`;
|
|
100
|
+
* return {
|
|
101
|
+
* load: () => JSON.parse(localStorage.getItem(key) || 'null'),
|
|
102
|
+
* save: (state) => localStorage.setItem(key, JSON.stringify(state)),
|
|
103
|
+
* };
|
|
104
|
+
* }
|
|
105
|
+
* ```
|
|
106
|
+
*
|
|
107
|
+
* @example Async handler (IndexedDB)
|
|
108
|
+
* ```ts
|
|
109
|
+
* handler: async (ctx) => {
|
|
110
|
+
* const db = await openDB('app-db');
|
|
111
|
+
* return {
|
|
112
|
+
* load: () => db.get('stores', ctx.displayName),
|
|
113
|
+
* save: (state) => db.put('stores', state, ctx.displayName),
|
|
114
|
+
* };
|
|
115
|
+
* }
|
|
116
|
+
* ```
|
|
74
117
|
*/
|
|
75
|
-
|
|
118
|
+
handler: (context: PersistContext) => PersistHandler | PromiseLike<PersistHandler>;
|
|
76
119
|
/**
|
|
77
|
-
* Called when an error occurs during load or save.
|
|
120
|
+
* Called when an error occurs during init, load, or save.
|
|
78
121
|
*
|
|
79
|
-
* @param context - The middleware context
|
|
80
122
|
* @param error - The error that occurred
|
|
81
|
-
* @param operation - Whether the error occurred during 'load' or 'save'
|
|
123
|
+
* @param operation - Whether the error occurred during 'init', 'load', or 'save'
|
|
82
124
|
*/
|
|
83
|
-
onError?: (
|
|
125
|
+
onError?: (error: unknown, operation: "init" | "load" | "save") => void;
|
|
84
126
|
/**
|
|
85
127
|
* Force hydration to overwrite dirty (modified) state properties.
|
|
86
128
|
*
|
|
@@ -96,59 +138,74 @@ export interface PersistOptions {
|
|
|
96
138
|
/**
|
|
97
139
|
* Creates a persist middleware that automatically saves and restores store state.
|
|
98
140
|
*
|
|
99
|
-
* @example
|
|
141
|
+
* @example localStorage (sync handler)
|
|
100
142
|
* ```ts
|
|
101
|
-
* import { container } from "storion";
|
|
143
|
+
* import { container, forStores } from "storion";
|
|
102
144
|
* import { persistMiddleware } from "storion/persist";
|
|
103
145
|
*
|
|
104
146
|
* const app = container({
|
|
105
|
-
* middleware: [
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* },
|
|
118
|
-
* })],
|
|
147
|
+
* middleware: forStores([
|
|
148
|
+
* persistMiddleware({
|
|
149
|
+
* handler: (ctx) => {
|
|
150
|
+
* const key = `app:${ctx.displayName}`;
|
|
151
|
+
* return {
|
|
152
|
+
* load: () => JSON.parse(localStorage.getItem(key) || 'null'),
|
|
153
|
+
* save: (state) => localStorage.setItem(key, JSON.stringify(state)),
|
|
154
|
+
* };
|
|
155
|
+
* },
|
|
156
|
+
* onError: (error, op) => console.error(`Persist ${op} failed:`, error),
|
|
157
|
+
* }),
|
|
158
|
+
* ]),
|
|
119
159
|
* });
|
|
120
160
|
* ```
|
|
121
161
|
*
|
|
122
|
-
* @example
|
|
162
|
+
* @example IndexedDB (async handler)
|
|
123
163
|
* ```ts
|
|
124
164
|
* persistMiddleware({
|
|
125
|
-
*
|
|
126
|
-
* const db = await openDB(
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
165
|
+
* handler: async (ctx) => {
|
|
166
|
+
* const db = await openDB('app-db', 1, {
|
|
167
|
+
* upgrade(db) { db.createObjectStore('stores'); },
|
|
168
|
+
* });
|
|
169
|
+
* return {
|
|
170
|
+
* load: () => db.get('stores', ctx.displayName),
|
|
171
|
+
* save: (state) => db.put('stores', state, ctx.displayName),
|
|
172
|
+
* };
|
|
131
173
|
* },
|
|
132
174
|
* });
|
|
133
175
|
* ```
|
|
134
176
|
*
|
|
135
|
-
* @example
|
|
177
|
+
* @example With shared debounce
|
|
136
178
|
* ```ts
|
|
137
|
-
*
|
|
179
|
+
* persistMiddleware({
|
|
180
|
+
* handler: (ctx) => {
|
|
181
|
+
* const key = `app:${ctx.displayName}`;
|
|
182
|
+
* const debouncedSave = debounce(
|
|
183
|
+
* (s) => localStorage.setItem(key, JSON.stringify(s)),
|
|
184
|
+
* 300
|
|
185
|
+
* );
|
|
186
|
+
* return {
|
|
187
|
+
* load: () => JSON.parse(localStorage.getItem(key) || 'null'),
|
|
188
|
+
* save: debouncedSave,
|
|
189
|
+
* };
|
|
190
|
+
* },
|
|
191
|
+
* });
|
|
192
|
+
* ```
|
|
138
193
|
*
|
|
139
|
-
*
|
|
194
|
+
* @example Multi-storage with meta
|
|
195
|
+
* ```ts
|
|
196
|
+
* const sessionStore = meta();
|
|
197
|
+
* const localStore = meta();
|
|
140
198
|
*
|
|
199
|
+
* // Session storage middleware
|
|
141
200
|
* persistMiddleware({
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
*
|
|
145
|
-
* const key =
|
|
146
|
-
* return
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
* const key = customKey ?? ctx.spec.displayName;
|
|
151
|
-
* localStorage.setItem(key, JSON.stringify(state));
|
|
201
|
+
* filter: ({ meta }) => meta.any(sessionStore),
|
|
202
|
+
* fields: ({ meta }) => meta.fields(sessionStore),
|
|
203
|
+
* handler: (ctx) => {
|
|
204
|
+
* const key = `session:${ctx.displayName}`;
|
|
205
|
+
* return {
|
|
206
|
+
* load: () => JSON.parse(sessionStorage.getItem(key) || 'null'),
|
|
207
|
+
* save: (state) => sessionStorage.setItem(key, JSON.stringify(state)),
|
|
208
|
+
* };
|
|
152
209
|
* },
|
|
153
210
|
* });
|
|
154
211
|
* ```
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist/persist.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"persist.d.ts","sourceRoot":"","sources":["../../src/persist/persist.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,aAAa,EACd,MAAM,UAAU,CAAC;AAIlB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,eAAO,MAAM,YAAY,sCAAS,CAAC;AAEnC;;GAEG;AACH,MAAM,MAAM,iBAAiB,GACzB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACvB,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;AAExD;;;GAGG;AACH,MAAM,WAAW,cAAe,SAAQ,sBAAsB;IAC5D,yCAAyC;IACzC,KAAK,EAAE,aAAa,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;OAKG;IACH,IAAI,CAAC,EAAE,MAAM,iBAAiB,CAAC;IAE/B;;;;OAIG;IACH,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;CACjD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,OAAO,CAAC;IAEtD;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,sBAAsB,KAAK,MAAM,EAAE,CAAC;IAEvD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,OAAO,EAAE,CACP,OAAO,EAAE,cAAc,KACpB,cAAc,GAAG,WAAW,CAAC,cAAc,CAAC,CAAC;IAElD;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,KAAK,IAAI,CAAC;IAExE;;;;;;;;;OASG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0EG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,CAkJ1E"}
|