storion 0.8.2 → 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 +71 -15
- package/dist/core/middleware.d.ts +13 -0
- package/dist/core/middleware.d.ts.map +1 -1
- package/dist/{index-BsdNVe8q.js → index-C8B6Mo8r.js} +17 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- 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/dist/react/index.js +2 -2
- package/dist/storion.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,23 +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);
|
|
18
|
+
const sessionFields = ctx.meta.fields(sessionStore); // ['token', 'userId']
|
|
19
|
+
const highPriority = ctx.meta.fields(priority, (v) => v > 5);
|
|
19
20
|
```
|
|
20
|
-
- `
|
|
21
|
+
- `applyFor` now supports object form to map patterns to different middleware
|
|
22
|
+
```ts
|
|
23
|
+
applyFor({
|
|
24
|
+
userStore: loggingMiddleware,
|
|
25
|
+
"auth*": [authMiddleware, securityMiddleware],
|
|
26
|
+
"*Cache": cacheMiddleware,
|
|
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
|
+
|
|
21
40
|
```ts
|
|
22
|
-
//
|
|
41
|
+
// Before (old API)
|
|
23
42
|
persistMiddleware({
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
})
|
|
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
|
+
});
|
|
28
69
|
```
|
|
29
70
|
|
|
30
71
|
---
|
|
@@ -32,6 +73,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
32
73
|
## [0.8.0] - 2024-12-21
|
|
33
74
|
|
|
34
75
|
### Added
|
|
76
|
+
|
|
35
77
|
- **Persist Module** (`storion/persist`)
|
|
36
78
|
- `persistMiddleware(options)` for automatic state persistence
|
|
37
79
|
- `notPersisted` meta for excluding stores or fields from persistence
|
|
@@ -50,6 +92,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
50
92
|
- `store.hydrate(state, { force })` - force option to override dirty properties
|
|
51
93
|
|
|
52
94
|
### Changed
|
|
95
|
+
|
|
53
96
|
- `StoreMiddlewareContext` now includes `meta` property for querying store metadata
|
|
54
97
|
- `FactoryMiddlewareContext` now includes `meta` property for querying factory metadata
|
|
55
98
|
|
|
@@ -58,6 +101,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
58
101
|
## [0.7.0] - 2024-12-15
|
|
59
102
|
|
|
60
103
|
### Added
|
|
104
|
+
|
|
61
105
|
- **DevTools Module** (`storion/devtools`)
|
|
62
106
|
- `devtoolsMiddleware()` for state inspection
|
|
63
107
|
- `__revertState` and `__takeSnapshot` injected actions
|
|
@@ -71,6 +115,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
71
115
|
- `create()` shorthand for single-store apps returning `[instance, useHook, withStore]`
|
|
72
116
|
|
|
73
117
|
### Changed
|
|
118
|
+
|
|
74
119
|
- Improved TypeScript inference for store actions
|
|
75
120
|
|
|
76
121
|
---
|
|
@@ -78,6 +123,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
78
123
|
## [0.6.0] - 2024-12-01
|
|
79
124
|
|
|
80
125
|
### Added
|
|
126
|
+
|
|
81
127
|
- **Async Module** (`storion/async`)
|
|
82
128
|
- `async.fresh<T>()` - throws during loading (Suspense-compatible)
|
|
83
129
|
- `async.stale<T>(initialData)` - returns stale data during loading
|
|
@@ -87,6 +133,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
87
133
|
- `trigger(action, deps, ...args)` for declarative data fetching in components
|
|
88
134
|
|
|
89
135
|
### Changed
|
|
136
|
+
|
|
90
137
|
- Effects now require synchronous functions (use `ctx.safe()` for async)
|
|
91
138
|
|
|
92
139
|
---
|
|
@@ -94,6 +141,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
94
141
|
## [0.5.0] - 2024-11-15
|
|
95
142
|
|
|
96
143
|
### Added
|
|
144
|
+
|
|
97
145
|
- **Focus (Lens-like Access)**
|
|
98
146
|
- `focus(path)` for nested state access
|
|
99
147
|
- Returns `[getter, setter]` tuple
|
|
@@ -111,6 +159,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
111
159
|
## [0.4.0] - 2024-11-01
|
|
112
160
|
|
|
113
161
|
### Added
|
|
162
|
+
|
|
114
163
|
- **Middleware System**
|
|
115
164
|
- `container({ middleware: [...] })` for middleware injection
|
|
116
165
|
- Middleware receives `MiddlewareContext` with `type`, `next`, `resolver`
|
|
@@ -119,6 +168,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
119
168
|
- `createValidationMiddleware()` built-in middleware
|
|
120
169
|
|
|
121
170
|
### Changed
|
|
171
|
+
|
|
122
172
|
- Container now uses middleware chain pattern
|
|
123
173
|
|
|
124
174
|
---
|
|
@@ -126,6 +176,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
126
176
|
## [0.3.0] - 2024-10-15
|
|
127
177
|
|
|
128
178
|
### Added
|
|
179
|
+
|
|
129
180
|
- **Store Lifecycle**
|
|
130
181
|
- `lifetime: "keepAlive"` (default) - persists until container disposal
|
|
131
182
|
- `lifetime: "autoDispose"` - disposes when no subscribers
|
|
@@ -137,6 +188,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
137
188
|
- `store.reset()` to restore initial state
|
|
138
189
|
|
|
139
190
|
### Changed
|
|
191
|
+
|
|
140
192
|
- Stores now track dirty state automatically
|
|
141
193
|
|
|
142
194
|
---
|
|
@@ -144,6 +196,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
144
196
|
## [0.2.0] - 2024-10-01
|
|
145
197
|
|
|
146
198
|
### Added
|
|
199
|
+
|
|
147
200
|
- **Dependency Injection**
|
|
148
201
|
- `container()` for managing store instances
|
|
149
202
|
- `get(factory)` for resolving dependencies
|
|
@@ -153,6 +206,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
153
206
|
- `useContainer()` hook
|
|
154
207
|
|
|
155
208
|
### Changed
|
|
209
|
+
|
|
156
210
|
- Stores are now lazily instantiated via container
|
|
157
211
|
|
|
158
212
|
---
|
|
@@ -160,6 +214,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
160
214
|
## [0.1.0] - 2024-09-15
|
|
161
215
|
|
|
162
216
|
### Added
|
|
217
|
+
|
|
163
218
|
- **Core Store**
|
|
164
219
|
- `store(options)` factory function
|
|
165
220
|
- `state` - reactive state object
|
|
@@ -187,22 +242,24 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
187
242
|
### Migrating to 0.8.0
|
|
188
243
|
|
|
189
244
|
#### Meta System Changes
|
|
245
|
+
|
|
190
246
|
If you were using internal meta APIs, update to the new public API:
|
|
191
247
|
|
|
192
248
|
```ts
|
|
193
249
|
// Before (internal)
|
|
194
|
-
spec.meta // was MetaEntry[]
|
|
250
|
+
spec.meta; // was MetaEntry[]
|
|
195
251
|
|
|
196
252
|
// After (0.8.0)
|
|
197
|
-
ctx.meta(persistMeta).store
|
|
198
|
-
ctx.meta(persistMeta).fields
|
|
199
|
-
ctx.meta.all(type)
|
|
200
|
-
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
|
|
201
257
|
```
|
|
202
258
|
|
|
203
259
|
### Migrating to 0.6.0
|
|
204
260
|
|
|
205
261
|
#### Async Effects
|
|
262
|
+
|
|
206
263
|
Effects must now be synchronous:
|
|
207
264
|
|
|
208
265
|
```ts
|
|
@@ -231,4 +288,3 @@ effect((ctx) => {
|
|
|
231
288
|
[0.3.0]: https://github.com/linq2js/storion/compare/v0.2.0...v0.3.0
|
|
232
289
|
[0.2.0]: https://github.com/linq2js/storion/compare/v0.1.0...v0.2.0
|
|
233
290
|
[0.1.0]: https://github.com/linq2js/storion/releases/tag/v0.1.0
|
|
234
|
-
|
|
@@ -2,6 +2,8 @@ import { Middleware, MiddlewareContext, StoreMiddleware } from '../types';
|
|
|
2
2
|
|
|
3
3
|
/** Pattern type for matching displayName */
|
|
4
4
|
export type SpecPattern = string | RegExp;
|
|
5
|
+
/** Mapping of patterns to middleware */
|
|
6
|
+
export type MiddlewareMap = Record<string, StoreMiddleware | StoreMiddleware[]>;
|
|
5
7
|
/**
|
|
6
8
|
* Conditionally apply middleware based on a predicate or pattern(s).
|
|
7
9
|
*
|
|
@@ -13,6 +15,9 @@ export type SpecPattern = string | RegExp;
|
|
|
13
15
|
* @param patterns - Pattern or array of patterns to match displayName
|
|
14
16
|
* @param middleware - Middleware or array of middleware to apply
|
|
15
17
|
*
|
|
18
|
+
* @overload Apply different middleware for different patterns (object form)
|
|
19
|
+
* @param middlewareMap - Object mapping patterns to middleware
|
|
20
|
+
*
|
|
16
21
|
* Pattern types:
|
|
17
22
|
* - `"userStore"` - exact match
|
|
18
23
|
* - `"user*"` - startsWith
|
|
@@ -44,10 +49,18 @@ export type SpecPattern = string | RegExp;
|
|
|
44
49
|
*
|
|
45
50
|
* // Multiple middleware
|
|
46
51
|
* applyFor("counterStore", [loggingMiddleware, devtoolsMiddleware]);
|
|
52
|
+
*
|
|
53
|
+
* // Object form - map patterns to middleware
|
|
54
|
+
* applyFor({
|
|
55
|
+
* "userStore": loggingMiddleware,
|
|
56
|
+
* "auth*": [authMiddleware, securityMiddleware],
|
|
57
|
+
* "*Cache": cacheMiddleware,
|
|
58
|
+
* });
|
|
47
59
|
* ```
|
|
48
60
|
*/
|
|
49
61
|
export declare function applyFor(predicate: (ctx: MiddlewareContext) => boolean, middleware: Middleware | Middleware[]): Middleware;
|
|
50
62
|
export declare function applyFor(patterns: SpecPattern | SpecPattern[], middleware: StoreMiddleware | StoreMiddleware[]): Middleware;
|
|
63
|
+
export declare function applyFor(middlewareMap: MiddlewareMap): Middleware;
|
|
51
64
|
/**
|
|
52
65
|
* Apply middleware to all except those matching predicate or pattern(s).
|
|
53
66
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/core/middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EACjB,eAAe,
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/core/middleware.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAEV,UAAU,EACV,iBAAiB,EACjB,eAAe,EAEhB,MAAM,UAAU,CAAC;AAElB,4CAA4C;AAC5C,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,CAAC;AA8F1C,wCAAwC;AACxC,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,GAAG,eAAe,EAAE,CAAC,CAAC;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,QAAQ,CACtB,SAAS,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,EAC9C,UAAU,EAAE,UAAU,GAAG,UAAU,EAAE,GACpC,UAAU,CAAC;AACd,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,WAAW,GAAG,WAAW,EAAE,EACrC,UAAU,EAAE,eAAe,GAAG,eAAe,EAAE,GAC9C,UAAU,CAAC;AACd,wBAAgB,QAAQ,CAAC,aAAa,EAAE,aAAa,GAAG,UAAU,CAAC;AAwDnE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,WAAW,CACzB,SAAS,EAAE,CAAC,GAAG,EAAE,iBAAiB,KAAK,OAAO,EAC9C,UAAU,EAAE,UAAU,GAAG,UAAU,EAAE,GACpC,UAAU,CAAC;AACd,wBAAgB,WAAW,CACzB,QAAQ,EAAE,WAAW,GAAG,WAAW,EAAE,EACrC,UAAU,EAAE,UAAU,GAAG,UAAU,EAAE,GACpC,UAAU,CAAC;AAmBd;;;;;;;;;;;GAWG;AACH,wBAAgB,SAAS,CACvB,eAAe,EAAE,eAAe,GAAG,eAAe,EAAE,GACnD,UAAU,CAWZ"}
|
|
@@ -2868,8 +2868,23 @@ function compose(...middlewares) {
|
|
|
2868
2868
|
return executeNext();
|
|
2869
2869
|
};
|
|
2870
2870
|
}
|
|
2871
|
-
function applyFor(
|
|
2872
|
-
|
|
2871
|
+
function applyFor(predicateOrPatternsOrMap, middleware) {
|
|
2872
|
+
if (typeof predicateOrPatternsOrMap === "object" && !Array.isArray(predicateOrPatternsOrMap) && !(predicateOrPatternsOrMap instanceof RegExp)) {
|
|
2873
|
+
const entries = Object.entries(predicateOrPatternsOrMap);
|
|
2874
|
+
const pairs = entries.map(([pattern, mw]) => ({
|
|
2875
|
+
predicate: patternsToPredicate(pattern),
|
|
2876
|
+
middleware: Array.isArray(mw) ? compose(...mw) : mw
|
|
2877
|
+
}));
|
|
2878
|
+
return (ctx) => {
|
|
2879
|
+
for (const { predicate: predicate2, middleware: middleware2 } of pairs) {
|
|
2880
|
+
if (predicate2(ctx)) {
|
|
2881
|
+
return middleware2(ctx);
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
return ctx.next();
|
|
2885
|
+
};
|
|
2886
|
+
}
|
|
2887
|
+
const predicate = typeof predicateOrPatternsOrMap === "function" ? predicateOrPatternsOrMap : patternsToPredicate(predicateOrPatternsOrMap);
|
|
2873
2888
|
const composedMiddleware = Array.isArray(middleware) ? compose(...middleware) : middleware;
|
|
2874
2889
|
return (ctx) => {
|
|
2875
2890
|
if (predicate(ctx)) {
|
package/dist/index.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export { batch, untrack } from './core/tracking';
|
|
|
11
11
|
export { createResolver } from './core/createResolver';
|
|
12
12
|
export { pick } from './core/pick';
|
|
13
13
|
export { effect, type EffectFn, type EffectContext, type EffectOptions, type EffectErrorStrategy, type EffectErrorContext, type EffectRetryConfig, } from './core/effect';
|
|
14
|
-
export { applyFor, applyExcept, forStores, type SpecPattern, } from './core/middleware';
|
|
14
|
+
export { applyFor, applyExcept, forStores, type SpecPattern, type MiddlewareMap, } from './core/middleware';
|
|
15
15
|
export { equality, shallowEqual, deepEqual, strictEqual, } from './core/equality';
|
|
16
16
|
export { trigger, type TriggerOptions } from './trigger';
|
|
17
17
|
export { wrapFn as wrapFn, unwrapFn, isWrappedFn } from './core/fnWrapper';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EAEjB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEf,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,eAAe,EAEpB,KAAK,QAAQ,EACb,KAAK,QAAQ,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,EAAE,EACF,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,iBAAiB,GAClB,MAAM,MAAM,CAAC;AAGd,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,EACL,MAAM,EACN,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,KAAK,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,YAAY,EACZ,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EAEjB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,KAAK,EACV,KAAK,YAAY,EACjB,KAAK,gBAAgB,EACrB,KAAK,UAAU,EAEf,KAAK,OAAO,EACZ,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,QAAQ,EACb,KAAK,eAAe,EAEpB,KAAK,QAAQ,EACb,KAAK,QAAQ,GACd,MAAM,SAAS,CAAC;AAGjB,OAAO,EACL,EAAE,EACF,SAAS,EACT,OAAO,EACP,MAAM,EACN,WAAW,EACX,OAAO,EACP,OAAO,EACP,QAAQ,EACR,cAAc,EACd,iBAAiB,GAClB,MAAM,MAAM,CAAC;AAGd,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGjD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAGvD,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAEnC,OAAO,EACL,MAAM,EACN,KAAK,QAAQ,EACb,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,iBAAiB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,KAAK,WAAW,EAChB,KAAK,aAAa,GACnB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAGzD,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG3E,OAAO,EACL,YAAY,EACZ,eAAe,EACf,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,kBAAkB,GACnB,MAAM,UAAU,CAAC;AAGlB,OAAO,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AACnC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC"}
|
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"}
|
package/dist/react/index.js
CHANGED
|
@@ -2,8 +2,8 @@ var __defProp = Object.defineProperty;
|
|
|
2
2
|
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import { memo, useRef, useMemo, createElement, createContext, useContext, useReducer, useEffect, useLayoutEffect, useState, forwardRef } from "react";
|
|
5
|
-
import { c as container, i as isSpec, S as STORION_TYPE, r as resolveEquality, s as store } from "../index-
|
|
6
|
-
import { n, m, l, u, q, o, g, a, h, d, f, k, e, j, b, z, p, t, v, w, y, A, x } from "../index-
|
|
5
|
+
import { c as container, i as isSpec, S as STORION_TYPE, r as resolveEquality, s as store } from "../index-C8B6Mo8r.js";
|
|
6
|
+
import { n, m, l, u, q, o, g, a, h, d, f, k, e, j, b, z, p, t, v, w, y, A, x } from "../index-C8B6Mo8r.js";
|
|
7
7
|
import { P as ProviderMissingError, L as LocalStoreDependencyError, w as withHooks, A as AsyncFunctionError } from "../effect-C6h0PDDI.js";
|
|
8
8
|
import { E, H, I, c, a as a2, d as d2, S, b as b2, e as e2, u as u2 } from "../effect-C6h0PDDI.js";
|
|
9
9
|
import { jsx } from "react/jsx-runtime";
|
package/dist/storion.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S, n, m, c, l, u, q, o, g, a, h, d, f, k, i, e, j, b, z, p, t, s, v, w, y, A, x } from "./index-
|
|
1
|
+
import { S, n, m, c, l, u, q, o, g, a, h, d, f, k, i, e, j, b, z, p, t, s, v, w, y, A, x } from "./index-C8B6Mo8r.js";
|
|
2
2
|
import { A as A2, E, H, I, c as c2, L, P, a as a2, d as d2, S as S2, b as b2, e as e2, u as u2 } from "./effect-C6h0PDDI.js";
|
|
3
3
|
import { m as m2 } from "./meta-40r-AZfe.js";
|
|
4
4
|
export {
|