effector-storage 5.0.1 → 6.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 +95 -21
- package/async-storage/index.cjs +1 -1
- package/async-storage/index.cjs.d.ts +12 -5
- package/async-storage/index.cjs.map +1 -1
- package/async-storage/index.d.ts +12 -5
- package/async-storage/index.js +1 -1
- package/async-storage/index.js.flow +21 -4
- package/async-storage/index.js.map +1 -1
- package/async-storage/package.json +1 -0
- package/core/index.cjs +2 -0
- package/core/index.cjs.d.ts +90 -0
- package/core/index.cjs.map +1 -0
- package/core/index.d.ts +90 -0
- package/core/index.js +2 -0
- package/core/index.js.flow +113 -0
- package/core/index.js.map +1 -0
- package/core/package.json +8 -0
- package/index.cjs +1 -1
- package/index.cjs.d.ts +54 -12
- package/index.cjs.map +1 -1
- package/index.d.ts +54 -12
- package/index.js +1 -1
- package/index.js.flow +61 -10
- package/index.js.map +1 -1
- package/local/index.cjs +1 -1
- package/local/index.cjs.d.ts +45 -15
- package/local/index.cjs.map +1 -1
- package/local/index.d.ts +45 -15
- package/local/index.js +1 -1
- package/local/index.js.flow +53 -13
- package/local/index.js.map +1 -1
- package/local/package.json +1 -0
- package/log/index.cjs +2 -0
- package/log/index.cjs.d.ts +19 -0
- package/log/index.cjs.map +1 -0
- package/log/index.d.ts +19 -0
- package/log/index.js +2 -0
- package/log/index.js.flow +31 -0
- package/log/index.js.map +1 -0
- package/log/package.json +8 -0
- package/memory/index.cjs +1 -1
- package/memory/index.cjs.d.ts +42 -9
- package/memory/index.cjs.map +1 -1
- package/memory/index.d.ts +42 -9
- package/memory/index.js +1 -1
- package/memory/index.js.flow +45 -6
- package/memory/index.js.map +1 -1
- package/memory/package.json +1 -0
- package/nil/index.cjs +1 -1
- package/nil/index.cjs.d.ts +16 -6
- package/nil/index.cjs.map +1 -1
- package/nil/index.d.ts +16 -6
- package/nil/index.js +1 -1
- package/nil/index.js.flow +24 -4
- package/nil/index.js.map +1 -1
- package/nil/package.json +1 -0
- package/package.json +20 -4
- package/query/index.cjs +1 -1
- package/query/index.cjs.d.ts +50 -22
- package/query/index.cjs.map +1 -1
- package/query/index.d.ts +50 -22
- package/query/index.js +1 -1
- package/query/index.js.flow +54 -15
- package/query/index.js.map +1 -1
- package/query/package.json +1 -0
- package/rn/async/index.cjs +1 -1
- package/rn/async/index.cjs.d.ts +40 -13
- package/rn/async/index.cjs.map +1 -1
- package/rn/async/index.d.ts +40 -13
- package/rn/async/index.js +1 -1
- package/rn/async/index.js.flow +48 -11
- package/rn/async/index.js.map +1 -1
- package/rn/async/package.json +1 -0
- package/rn/encrypted/index.cjs +1 -1
- package/rn/encrypted/index.cjs.d.ts +40 -13
- package/rn/encrypted/index.cjs.map +1 -1
- package/rn/encrypted/index.d.ts +40 -13
- package/rn/encrypted/index.js +1 -1
- package/rn/encrypted/index.js.flow +50 -11
- package/rn/encrypted/index.js.map +1 -1
- package/rn/encrypted/package.json +1 -0
- package/session/index.cjs +1 -1
- package/session/index.cjs.d.ts +45 -15
- package/session/index.cjs.map +1 -1
- package/session/index.d.ts +45 -15
- package/session/index.js +1 -1
- package/session/index.js.flow +53 -13
- package/session/index.js.map +1 -1
- package/session/package.json +1 -0
- package/storage/index.cjs +1 -1
- package/storage/index.cjs.d.ts +17 -6
- package/storage/index.cjs.map +1 -1
- package/storage/index.d.ts +17 -6
- package/storage/index.js +1 -1
- package/storage/index.js.flow +24 -5
- package/storage/index.js.map +1 -1
- package/storage/package.json +1 -0
- package/tools/index.cjs +2 -0
- package/tools/index.cjs.d.ts +125 -0
- package/tools/index.cjs.map +1 -0
- package/tools/index.d.ts +125 -0
- package/tools/index.js +2 -0
- package/tools/index.js.flow +101 -0
- package/tools/index.js.map +1 -0
- package/tools/package.json +8 -0
package/README.md
CHANGED
|
@@ -19,12 +19,14 @@ Small module for [Effector](https://github.com/effector/effector) ☄️ to sync
|
|
|
19
19
|
- [with query string](#with-query-string)
|
|
20
20
|
- [with React Native AsyncStorage](#with-react-native-asyncstorage)
|
|
21
21
|
- [with React Native EncryptedStorage](#with-react-native-encryptedstorage)
|
|
22
|
+
- [extra adapters](#extra-adapters)
|
|
22
23
|
- [Usage with domains](#usage-with-domains)
|
|
23
|
-
- [Functional helpers](#functional-helpers)
|
|
24
24
|
- [Formulae](#formulae)
|
|
25
25
|
- [Units](#units)
|
|
26
26
|
- [Options](#options)
|
|
27
27
|
- [Returns](#returns)
|
|
28
|
+
- [Contracts](#contracts)
|
|
29
|
+
- [Notes](#notes)
|
|
28
30
|
- [`createPersist` factory](#createpersist-factory)
|
|
29
31
|
- [Options](#options-1)
|
|
30
32
|
- [Returns](#returns-1)
|
|
@@ -62,7 +64,7 @@ $ npm install --save effector-storage
|
|
|
62
64
|
|
|
63
65
|
### with `localStorage`
|
|
64
66
|
|
|
65
|
-
Docs: [effector-storage/local](https://github.com/yumauri/effector-storage/tree/
|
|
67
|
+
Docs: [effector-storage/local](https://github.com/yumauri/effector-storage/tree/main/src/local/README.md)
|
|
66
68
|
|
|
67
69
|
```javascript
|
|
68
70
|
import { persist } from 'effector-storage/local'
|
|
@@ -78,7 +80,7 @@ Stores, persisted in `localStorage`, are automatically synced between two (or mo
|
|
|
78
80
|
|
|
79
81
|
### with `sessionStorage`
|
|
80
82
|
|
|
81
|
-
Docs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/
|
|
83
|
+
Docs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/main/src/session/README.md)
|
|
82
84
|
|
|
83
85
|
Same as above, just import `persist` from `'effector-storage/session'`:
|
|
84
86
|
|
|
@@ -90,7 +92,7 @@ Stores, persisted in `sessionStorage`, are synced between instances, but not bet
|
|
|
90
92
|
|
|
91
93
|
### with query string
|
|
92
94
|
|
|
93
|
-
Docs: [effector-storage/query](https://github.com/yumauri/effector-storage/tree/
|
|
95
|
+
Docs: [effector-storage/query](https://github.com/yumauri/effector-storage/tree/main/src/query/README.md)
|
|
94
96
|
|
|
95
97
|
You can _reflect_ plain string store value in query string parameter, using this adapter. Think of it like about synchronizing store value and query string parameter.
|
|
96
98
|
|
|
@@ -108,7 +110,9 @@ Use this only with plain string stores (`Store<string | null>`) to avoid strange
|
|
|
108
110
|
|
|
109
111
|
### with React Native AsyncStorage
|
|
110
112
|
|
|
111
|
-
|
|
113
|
+
❗️ Will be deprecated in next minor release and removed in next major release, in favor of [`@effector-storage/react-native-async-storage`](https://github.com/yumauri/effector-storage-extras/tree/main/packages/react-native-async-storage).
|
|
114
|
+
|
|
115
|
+
Docs: [effector-storage/rn/async](https://github.com/yumauri/effector-storage/tree/main/src/rn/async/README.md)
|
|
112
116
|
|
|
113
117
|
```javascript
|
|
114
118
|
import { persist } from 'effector-storage/rn/async'
|
|
@@ -124,7 +128,9 @@ persist({ store: $counter })
|
|
|
124
128
|
|
|
125
129
|
### with React Native EncryptedStorage
|
|
126
130
|
|
|
127
|
-
|
|
131
|
+
❗️ Will be deprecated in next minor release and removed in next major release, in favor of [`@effector-storage/react-native-encrypted-storage`](https://github.com/yumauri/effector-storage-extras/tree/main/packages/react-native-encrypted-storage).
|
|
132
|
+
|
|
133
|
+
Docs: [effector-storage/rn/encrypted](https://github.com/yumauri/effector-storage/tree/main/src/rn/encrypted/README.md)
|
|
128
134
|
|
|
129
135
|
```javascript
|
|
130
136
|
import { persist } from 'effector-storage/rn/encrypted'
|
|
@@ -138,6 +144,10 @@ persist({ store: $counter })
|
|
|
138
144
|
|
|
139
145
|
⚠️ Note, that [EncryptedStorage] is asynchronous (it is based on [AsyncStorage] actually).
|
|
140
146
|
|
|
147
|
+
### extra adapters
|
|
148
|
+
|
|
149
|
+
You can find a collection of useful adapters in [effector-storage-extras](https://github.com/yumauri/effector-storage-extras). That side repository was created in order to not bloat `effector-storage` with dependencies and adapters, which depends on other libraries.
|
|
150
|
+
|
|
141
151
|
## Usage with domains
|
|
142
152
|
|
|
143
153
|
You can use `persist` inside Domain's `onCreateStore` hook:
|
|
@@ -177,7 +187,9 @@ In order to synchronize _something_, you need to specify effector units. Dependi
|
|
|
177
187
|
- `key`? ([_string_]): Key for local/session storage, to store value in. If omitted — `store` name is used. **Note!** If `key` is not specified, `store` _must_ have a `name`! You can use `'effector/babel-plugin'` to have those names automatically.
|
|
178
188
|
- `keyPrefix`? ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.
|
|
179
189
|
- `clock`? ([_Event_] | [_Effect_] | [_Store_]): Unit, if passed – then value from `store`/`source` will be stored in the storage only upon its trigger.
|
|
180
|
-
- `pickup`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which you can specify to update `store` value from storage. **Note!** When you add `pickup`, `persist` _will not_ get initial value from storage automatically!
|
|
190
|
+
- `pickup`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which you can specify to update `store` value from storage. This unit can also set a special context for adapter. **Note!** When you add `pickup`, `persist` _will not_ get initial value from storage automatically!
|
|
191
|
+
- `context`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which can set a special context for adapter.
|
|
192
|
+
- `contract`? ([_Contract_]): Rule to statically validate data from storage.
|
|
181
193
|
- `done`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which will be triggered on each successful read or write from/to storage.<br>
|
|
182
194
|
Payload structure:
|
|
183
195
|
- `key` ([_string_]): Same `key` as above.
|
|
@@ -188,14 +200,14 @@ In order to synchronize _something_, you need to specify effector units. Dependi
|
|
|
188
200
|
Payload structure:
|
|
189
201
|
- `key` ([_string_]): Same `key` as above.
|
|
190
202
|
- `keyPrefix` ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.
|
|
191
|
-
- `operation` (_`'set'`_ | _`'get'`_): Type of operation, read (get)
|
|
203
|
+
- `operation` (_`'set'`_ | _`'get'`_ | _`'validate'`_): Type of operation, read (get), write (set) or validation against contract (validate).
|
|
192
204
|
- `error` ([_Error_]): Error instance
|
|
193
205
|
- `value`? (_any_): In case of _'set'_ operation — value from `store`. In case of _'get'_ operation could contain raw value from storage or could be empty.
|
|
194
206
|
- `finally`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which will be triggered either in case of success or error.<br>
|
|
195
207
|
Payload structure:
|
|
196
208
|
- `key` ([_string_]): Same `key` as above.
|
|
197
209
|
- `keyPrefix` ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.
|
|
198
|
-
- `operation` (_`'set'`_ | _`'get'`_): Type of operation, read (get)
|
|
210
|
+
- `operation` (_`'set'`_ | _`'get'`_ | _`'validate'`_): Type of operation, read (get), write (set) or validation against contract (validate).
|
|
199
211
|
- `status` (_`'done'`_ | _`'fail'`_): Operation status.
|
|
200
212
|
- `error`? ([_Error_]): Error instance, in case of error.
|
|
201
213
|
- `value`? (_any_): Value, in case it is exists (look above).
|
|
@@ -204,6 +216,58 @@ In order to synchronize _something_, you need to specify effector units. Dependi
|
|
|
204
216
|
|
|
205
217
|
- ([_Subscription_]): You can use this subscription to remove store association with storage, if you don't need them to be synced anymore. It is a function.
|
|
206
218
|
|
|
219
|
+
### Contracts
|
|
220
|
+
|
|
221
|
+
You can use `contract` option to validate data from storage. Contract has the following type definition:
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
export type Contract<Data> =
|
|
225
|
+
| ((raw: unknown) => raw is Data)
|
|
226
|
+
| {
|
|
227
|
+
isData: (raw: unknown) => raw is Data
|
|
228
|
+
getErrorMessages: (raw: unknown) => string[]
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
So, it could be simple type guard function in trivial use cases, or more complex object with `isData` type guard and `getErrorMessages` function, which returns array of error messages. This format is fully compatible with [Farfetched contracts](https://farfetched.pages.dev/api/primitives/contract.html), so you can use any adapter from Farfetched ([runtypes](https://farfetched.pages.dev/api/contracts/runtypes.html), [zod](https://farfetched.pages.dev/api/contracts/zod.html), [io-ts](https://farfetched.pages.dev/api/contracts/io-ts.html), [superstruct](https://farfetched.pages.dev/api/contracts/superstruct.html), [typed-contracts](https://farfetched.pages.dev/api/contracts/typed-contracts.html)) with `persist` and `contract` option:
|
|
233
|
+
|
|
234
|
+
```typescript
|
|
235
|
+
// simple type guard
|
|
236
|
+
persist({
|
|
237
|
+
store: $counter,
|
|
238
|
+
key: 'counter',
|
|
239
|
+
contract: (raw): raw is number => typeof raw === 'number',
|
|
240
|
+
})
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// complex contract with Farfetched adapter
|
|
245
|
+
import { Record, Literal, Number } from 'runtypes'
|
|
246
|
+
import { runtypeContract } from '@farfetched/runtypes'
|
|
247
|
+
|
|
248
|
+
const Asteroid = Record({
|
|
249
|
+
type: Literal('asteroid'),
|
|
250
|
+
mass: Number,
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
persist({
|
|
254
|
+
store: $asteroid,
|
|
255
|
+
key: 'asteroid',
|
|
256
|
+
contract: runtypeContract(Asteroid),
|
|
257
|
+
})
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
There are two gotchas with contracts:
|
|
261
|
+
|
|
262
|
+
1. From `effector-storage` point of view it is absolutely normal, when there is no persisted value in the storage yet. So, `undefined` value is _always valid_, even if contract does not explicitly allow it.
|
|
263
|
+
2. `effector-storage` does not prevent persisting invalid data to the storage, but it will validate it nonetheless, after persisting, so, if you write invalid data to the storage, `fail` will be triggered, but data will be persisted.
|
|
264
|
+
|
|
265
|
+
### Notes
|
|
266
|
+
|
|
267
|
+
Without specifying `pickup` property, calling `persist` will immediately call adapter to get initial value. In case of synchronous storage (like `localStorage` or `sessionStorage`) this action will synchronously set store value, and call `done`/`fail`/`finally` right away. You should take that into account, if you adds some logic on `done`, for example — place `persist` after that logic (see issue [#38](https://github.com/yumauri/effector-storage/issues/38) for more details).
|
|
268
|
+
|
|
269
|
+
You can modify adapter to be asynchronous to mitigate this behavior with [`async`](https://github.com/yumauri/effector-storage/tree/main/src/tools/README.md#async) function.
|
|
270
|
+
|
|
207
271
|
## `createPersist` factory
|
|
208
272
|
|
|
209
273
|
In rare cases you might want to use `createPersist` factory. It allows you to specify some adapter options, like `keyPrefix`.
|
|
@@ -229,7 +293,10 @@ persist({
|
|
|
229
293
|
|
|
230
294
|
### Options
|
|
231
295
|
|
|
296
|
+
- `pickup`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which you can specify to update `store` value from storage. This unit can also set a special context for adapter. **Note!** When you add `pickup`, `persist` _will not_ get initial value from storage automatically!
|
|
297
|
+
- `context`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which can set a special context for adapter.
|
|
232
298
|
- `keyPrefix`? ([_string_]): Key prefix for adapter. It will be concatenated with any `key`, given to returned `persist` function.
|
|
299
|
+
- `contract`? ([_Contract_]): Rule to statically validate data from storage.
|
|
233
300
|
|
|
234
301
|
### Returns
|
|
235
302
|
|
|
@@ -239,7 +306,7 @@ persist({
|
|
|
239
306
|
|
|
240
307
|
`effector-storage` consists of a _core_ module and _adapter_ modules.
|
|
241
308
|
|
|
242
|
-
The core module itself does nothing with actual storage, it just connects effector units to the storage adapter, using
|
|
309
|
+
The core module itself does nothing with actual storage, it just connects effector units to the storage adapter, using couple of _Effects_ and bunch of connections.
|
|
243
310
|
|
|
244
311
|
The storage adapter _gets_ and _sets_ values, and also can asynchronously emit values on storage updates.
|
|
245
312
|
|
|
@@ -258,10 +325,11 @@ Adapter is a function, which is called by the core `persist` function, and has f
|
|
|
258
325
|
```typescript
|
|
259
326
|
interface StorageAdapter {
|
|
260
327
|
<State>(key: string, update: (raw?: any) => any): {
|
|
261
|
-
get(raw?: any): State | Promise<State>
|
|
262
|
-
set(value: State): void
|
|
328
|
+
get(raw?: any, ctx?: any): State | Promise<State | undefined> | undefined
|
|
329
|
+
set(value: State, ctx?: any): void
|
|
263
330
|
}
|
|
264
331
|
keyArea?: any
|
|
332
|
+
noop?: boolean
|
|
265
333
|
}
|
|
266
334
|
```
|
|
267
335
|
|
|
@@ -273,13 +341,18 @@ interface StorageAdapter {
|
|
|
273
341
|
#### Returns
|
|
274
342
|
|
|
275
343
|
- `{ get, set }` (_{ Function, Function }_): Getter from and setter to storage. These functions are used as Effects handlers, and could be sync or async. Also, you don't have to catch exceptions and errors inside those functions — Effects will do that for you.<br>
|
|
276
|
-
As mentioned above, call of `update` function will trigger `get` function with the same argument. So you can handle cases, when `get` function is called during initial `persist` execution (without arguments), or after external update. Check out [example below](#storage-with-external-updates-example)
|
|
344
|
+
As mentioned above, call of `update` function will trigger `get` function with the same argument. So you can handle cases, when `get` function is called during initial `persist` execution (without arguments), or after external update. Check out [example below](#storage-with-external-updates-example).<br>
|
|
345
|
+
Also getter and setter both accepts optional _context_ as a second argument — it can be any value. This context could be useful, if adapter depends on some external environment, for example, it can contain _Request_ and _Response_ from Express middleware, to get/set cookies from/to. (TODO: isomorphic cookies adapter example).
|
|
277
346
|
|
|
278
347
|
#### keyArea
|
|
279
348
|
|
|
280
349
|
Adapter function can have static field `keyArea` — this could be any value of any type, which should be unique for _keys namespace_. For example, two local storage adapters could have different settings, but both of them uses same _storage area_ — `localStorage`. So, different stores, persisted in local storage with the same key (but possibly with different adapters), should be synced. That is what `keyArea` is responsible for. Value of that field is used as a key in cache `Map`.<br>
|
|
281
350
|
In case it is omitted — adapter instances is used instead.
|
|
282
351
|
|
|
352
|
+
#### noop
|
|
353
|
+
|
|
354
|
+
Marks adapter as "no-op" for [`either`](https://github.com/yumauri/effector-storage/tree/main/src/tools/README.md#either) function.
|
|
355
|
+
|
|
283
356
|
### Synchronous storage adapter example
|
|
284
357
|
|
|
285
358
|
For example, simplified _localStorage_ adapter might looks like this. This is over-simplified example, don't do that in real code, there are no serialization and deserialization, no checks for edge cases. This is just to show an idea.
|
|
@@ -378,10 +451,10 @@ import { persist } from 'effector-storage'
|
|
|
378
451
|
const pickup = createEvent()
|
|
379
452
|
|
|
380
453
|
const adapter = (key, update) => {
|
|
381
|
-
// if `pickup` event was triggered ->
|
|
454
|
+
// if `pickup` event was triggered -> call an `update` function
|
|
382
455
|
// this will call `get` function from below ↓
|
|
383
456
|
// wrapped in Effect, to handle any errors
|
|
384
|
-
|
|
457
|
+
pickup.watch(update)
|
|
385
458
|
return {
|
|
386
459
|
get: () => localStorage.getItem(key),
|
|
387
460
|
set: (value) => localStorage.setItem(key, value),
|
|
@@ -443,7 +516,7 @@ adapter = storage(options)
|
|
|
443
516
|
#### Options
|
|
444
517
|
|
|
445
518
|
- `storage` (_Storage_): Storage to communicate with.
|
|
446
|
-
- `sync`? ([_boolean_]): Add [`'storage'`] event listener or no. Default = `false`.
|
|
519
|
+
- `sync`? ([_boolean_] | 'force'): Add [`'storage'`] event listener or no. Default = `false`. In case of `'force'` value adapter will always read new value from _Storage_, instead of event.
|
|
447
520
|
- `serialize`? (_(value: any) => string_): Custom serialize function. Default = `JSON.stringify`
|
|
448
521
|
- `deserialize`? (_(value: string) => any_): Custom deserialize function. Default = `JSON.parse`
|
|
449
522
|
|
|
@@ -486,11 +559,11 @@ Use this approach with caution, beware of infinite circular updates. To avoid th
|
|
|
486
559
|
|
|
487
560
|
## TODO
|
|
488
561
|
|
|
489
|
-
- [x] [localStorage] support (docs: [effector-storage/local](https://github.com/yumauri/effector-storage/tree/
|
|
490
|
-
- [x] [sessionStorage] support (docs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/
|
|
491
|
-
- [x] [query string](https://developer.mozilla.org/en-US/docs/Web/API/Location/search) support (docs: [effector-storage/query](https://github.com/yumauri/effector-storage/tree/
|
|
492
|
-
- [x] [AsyncStorage] support (docs: [effector-storage/rn/async](https://github.com/yumauri/effector-storage/tree/
|
|
493
|
-
- [x] [EncryptedStorage] support (docs: [effector-storage/rn/encrypted](https://github.com/yumauri/effector-storage/tree/
|
|
562
|
+
- [x] [localStorage] support (docs: [effector-storage/local](https://github.com/yumauri/effector-storage/tree/main/src/local/README.md))
|
|
563
|
+
- [x] [sessionStorage] support (docs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/main/src/session/README.md))
|
|
564
|
+
- [x] [query string](https://developer.mozilla.org/en-US/docs/Web/API/Location/search) support (docs: [effector-storage/query](https://github.com/yumauri/effector-storage/tree/main/src/query/README.md))
|
|
565
|
+
- [x] [AsyncStorage] support (docs: [effector-storage/rn/async](https://github.com/yumauri/effector-storage/tree/main/src/rn/async/README.md))
|
|
566
|
+
- [x] [EncryptedStorage] support (docs: [effector-storage/rn/encrypted](https://github.com/yumauri/effector-storage/tree/main/src/rn/encrypted/README.md))
|
|
494
567
|
- [ ] [IndexedDB] support
|
|
495
568
|
- [ ] [Cookies] support
|
|
496
569
|
- [ ] you name it support
|
|
@@ -516,3 +589,4 @@ Use this approach with caution, beware of infinite circular updates. To avoid th
|
|
|
516
589
|
[_function_]: https://developer.mozilla.org/en-US/docs/Glossary/Function
|
|
517
590
|
[_boolean_]: https://developer.mozilla.org/en-US/docs/Glossary/Boolean
|
|
518
591
|
[_error_]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
|
|
592
|
+
[_Contract_]: #contracts
|
package/async-storage/index.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";
|
|
1
|
+
"use strict";function e({storage:e,serialize:t=JSON.stringify,deserialize:a=JSON.parse}){var r=r=>({async get(){var t=await e().getItem(r);return null===t?void 0:a(t)},async set(a){await e().setItem(r,t(a))}});try{r.keyArea=e()}catch(e){}return r}e.factory=!0,exports.asyncStorage=e;
|
|
2
2
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
interface StorageAdapter {
|
|
2
|
+
<State>(key: string, update: (raw?: any) => any): {
|
|
3
|
+
get(raw?: any, ctx?: any): State | Promise<State | undefined> | undefined
|
|
4
|
+
set(value: State, ctx?: any): void
|
|
5
|
+
}
|
|
6
|
+
keyArea?: any
|
|
7
|
+
noop?: boolean
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
interface AsyncStorage {
|
|
4
11
|
getItem: (key: string) => Promise<string | null>
|
|
5
12
|
setItem: (key: string, value: string) => Promise<void>
|
|
6
13
|
}
|
|
7
14
|
interface AsyncStorageConfig {
|
|
8
|
-
storage: AsyncStorage
|
|
15
|
+
storage: () => AsyncStorage
|
|
9
16
|
serialize?: (value: any) => string
|
|
10
17
|
deserialize?: (value: string) => any
|
|
11
18
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Generic `AsyncStorage` adapter factory
|
|
14
|
-
*/
|
|
15
19
|
declare function asyncStorage({
|
|
16
20
|
storage,
|
|
17
21
|
serialize,
|
|
18
22
|
deserialize,
|
|
19
23
|
}: AsyncStorageConfig): StorageAdapter
|
|
24
|
+
declare namespace asyncStorage {
|
|
25
|
+
var factory: true
|
|
26
|
+
}
|
|
20
27
|
|
|
21
28
|
export { AsyncStorage, AsyncStorageConfig, asyncStorage }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../../src/async-storage/index.ts"],"sourcesContent":["import type { StorageAdapter } from '
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/async-storage/index.ts"],"sourcesContent":["import type { StorageAdapter } from '../types'\n\nexport interface AsyncStorage {\n getItem: (key: string) => Promise<string | null>\n setItem: (key: string, value: string) => Promise<void>\n}\n\nexport interface AsyncStorageConfig {\n storage: () => AsyncStorage\n serialize?: (value: any) => string\n deserialize?: (value: string) => any\n}\n\n/**\n * Creates generic `AsyncStorage` adapter\n */\nasyncStorage.factory = true as const\nexport function asyncStorage({\n storage,\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n}: AsyncStorageConfig): StorageAdapter {\n const adapter: StorageAdapter = <State>(key: string) => ({\n async get() {\n const item = await storage().getItem(key)\n return item === null ? undefined : deserialize(item)\n },\n\n async set(value: State) {\n await storage().setItem(key, serialize(value))\n },\n })\n\n try {\n adapter.keyArea = storage()\n } catch (error) {\n // do nothing\n }\n\n return adapter\n}\n"],"names":["asyncStorage","storage","serialize","JSON","stringify","deserialize","parse","adapter","key","async","item","getItem","undefined","value","setItem","keyArea","error","factory"],"mappings":"aAiBO,SAASA,GAAaC,QAC3BA,EAAOC,UACPA,EAAYC,KAAKC,UAASC,YAC1BA,EAAcF,KAAKG,QAEnB,IAAMC,EAAkCC,IAAiB,CACvDC,YACE,IAAMC,QAAaT,IAAUU,QAAQH,GACrC,OAAgB,OAATE,OAAgBE,EAAYP,EAAYK,EAChD,EAEDD,UAAUI,SACFZ,IAAUa,QAAQN,EAAKN,EAAUW,GACzC,IAGF,IACEN,EAAQQ,QAAUd,GACnB,CAAC,MAAOe,GACP,CAGF,OAAOT,CACT,CAxBAP,EAAaiB,SAAU"}
|
package/async-storage/index.d.ts
CHANGED
|
@@ -1,21 +1,28 @@
|
|
|
1
|
-
|
|
1
|
+
interface StorageAdapter {
|
|
2
|
+
<State>(key: string, update: (raw?: any) => any): {
|
|
3
|
+
get(raw?: any, ctx?: any): State | Promise<State | undefined> | undefined
|
|
4
|
+
set(value: State, ctx?: any): void
|
|
5
|
+
}
|
|
6
|
+
keyArea?: any
|
|
7
|
+
noop?: boolean
|
|
8
|
+
}
|
|
2
9
|
|
|
3
10
|
interface AsyncStorage {
|
|
4
11
|
getItem: (key: string) => Promise<string | null>
|
|
5
12
|
setItem: (key: string, value: string) => Promise<void>
|
|
6
13
|
}
|
|
7
14
|
interface AsyncStorageConfig {
|
|
8
|
-
storage: AsyncStorage
|
|
15
|
+
storage: () => AsyncStorage
|
|
9
16
|
serialize?: (value: any) => string
|
|
10
17
|
deserialize?: (value: string) => any
|
|
11
18
|
}
|
|
12
|
-
/**
|
|
13
|
-
* Generic `AsyncStorage` adapter factory
|
|
14
|
-
*/
|
|
15
19
|
declare function asyncStorage({
|
|
16
20
|
storage,
|
|
17
21
|
serialize,
|
|
18
22
|
deserialize,
|
|
19
23
|
}: AsyncStorageConfig): StorageAdapter
|
|
24
|
+
declare namespace asyncStorage {
|
|
25
|
+
var factory: true
|
|
26
|
+
}
|
|
20
27
|
|
|
21
28
|
export { AsyncStorage, AsyncStorageConfig, asyncStorage }
|
package/async-storage/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
function e({storage:e,serialize:t=JSON.stringify,deserialize:a=JSON.parse}){var r=r=>({async get(){var t=await e.getItem(r);return null===t?void 0:a(t)},async set(a){await e.setItem(r,t(a))}});
|
|
1
|
+
function e({storage:e,serialize:t=JSON.stringify,deserialize:a=JSON.parse}){var r=r=>({async get(){var t=await e().getItem(r);return null===t?void 0:a(t)},async set(a){await e().setItem(r,t(a))}});try{r.keyArea=e()}catch(e){}return r}e.factory=!0;export{e as asyncStorage};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|
|
@@ -1,19 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Flowtype definitions for index
|
|
3
3
|
* Generated by Flowgen from a Typescript Definition
|
|
4
|
-
* Flowgen v1.
|
|
4
|
+
* Flowgen v1.21.0
|
|
5
5
|
* @flow
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
declare interface StorageAdapter {
|
|
9
|
+
<State>(
|
|
10
|
+
key: string,
|
|
11
|
+
update: (raw?: any) => any
|
|
12
|
+
): {
|
|
13
|
+
get(raw?: any, ctx?: any): State | Promise<State | void> | void,
|
|
14
|
+
set(value: State, ctx?: any): void,
|
|
15
|
+
...
|
|
16
|
+
};
|
|
17
|
+
keyArea?: any;
|
|
18
|
+
noop?: boolean;
|
|
19
|
+
}
|
|
9
20
|
declare interface AsyncStorage {
|
|
10
21
|
getItem: (key: string) => Promise<string | null>;
|
|
11
22
|
setItem: (key: string, value: string) => Promise<void>;
|
|
12
23
|
}
|
|
13
24
|
declare interface AsyncStorageConfig {
|
|
14
|
-
storage: AsyncStorage;
|
|
25
|
+
storage: () => AsyncStorage;
|
|
15
26
|
serialize?: (value: any) => string;
|
|
16
27
|
deserialize?: (value: string) => any;
|
|
17
28
|
}
|
|
18
|
-
declare
|
|
29
|
+
declare var asyncStorage: typeof npm$namespace$asyncStorage
|
|
30
|
+
|
|
31
|
+
declare var npm$namespace$asyncStorage: {|
|
|
32
|
+
(x: AsyncStorageConfig): StorageAdapter,
|
|
33
|
+
factory: typeof asyncStorage$factory,
|
|
34
|
+
|}
|
|
35
|
+
declare var asyncStorage$factory: true
|
|
19
36
|
declare export { AsyncStorage, AsyncStorageConfig, asyncStorage }
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../src/async-storage/index.ts"],"sourcesContent":["import type { StorageAdapter } from '
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../src/async-storage/index.ts"],"sourcesContent":["import type { StorageAdapter } from '../types'\n\nexport interface AsyncStorage {\n getItem: (key: string) => Promise<string | null>\n setItem: (key: string, value: string) => Promise<void>\n}\n\nexport interface AsyncStorageConfig {\n storage: () => AsyncStorage\n serialize?: (value: any) => string\n deserialize?: (value: string) => any\n}\n\n/**\n * Creates generic `AsyncStorage` adapter\n */\nasyncStorage.factory = true as const\nexport function asyncStorage({\n storage,\n serialize = JSON.stringify,\n deserialize = JSON.parse,\n}: AsyncStorageConfig): StorageAdapter {\n const adapter: StorageAdapter = <State>(key: string) => ({\n async get() {\n const item = await storage().getItem(key)\n return item === null ? undefined : deserialize(item)\n },\n\n async set(value: State) {\n await storage().setItem(key, serialize(value))\n },\n })\n\n try {\n adapter.keyArea = storage()\n } catch (error) {\n // do nothing\n }\n\n return adapter\n}\n"],"names":["asyncStorage","storage","serialize","JSON","stringify","deserialize","parse","adapter","key","async","item","getItem","undefined","value","setItem","keyArea","error","factory"],"mappings":"AAiBO,SAASA,GAAaC,QAC3BA,EAAOC,UACPA,EAAYC,KAAKC,UAASC,YAC1BA,EAAcF,KAAKG,QAEnB,IAAMC,EAAkCC,IAAiB,CACvDC,YACE,IAAMC,QAAaT,IAAUU,QAAQH,GACrC,OAAgB,OAATE,OAAgBE,EAAYP,EAAYK,EAChD,EAEDD,UAAUI,SACFZ,IAAUa,QAAQN,EAAKN,EAAUW,GACzC,IAGF,IACEN,EAAQQ,QAAUd,GACnB,CAAC,MAAOe,GACP,CAGF,OAAOT,CACT,CAxBAP,EAAaiB,SAAU"}
|
package/core/index.cjs
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";var e=require("effector"),r=new Map,t=e=>(r,t)=>e(t,r.ref),o=e.createEvent();o.watch((e=>console.error(e.error))),exports.persist=function(a){var{adapter:i,store:f,source:s=f,target:n=f,clock:d=s,done:c,fail:u=o,finally:l,pickup:p,context:v,key:y,keyPrefix:g="",contract:m}=a;if(!i)throw Error("Adapter is not defined");if(!s)throw Error("Store or source is not defined");if(!n)throw Error("Target is not defined");if(!y&&s.shortName===s.id)throw Error("Key or name is not defined");if(s===n&&!e.is.store(s))throw Error("Source must be different from target");void 0===a.def&&e.is.store(s)&&(a.def=s.defaultState);var k="factory"in i?i(a):i,w=y||s.shortName,h=function(t,o){var a=r.get(t);void 0===a&&(a=new Map,r.set(t,a));var i=a.get(o);return void 0!==i||(i=e.createStore(null,{serialize:"ignore"}),a.set(o,i)),i}(k.keyArea||k,g+w),E=e.createNode(),x=()=>e.clearNode(E),P=e=>({status:r="fail",params:t,result:o,error:a})=>"done"===r?{status:r,key:w,keyPrefix:g,operation:e,value:"get"===e?o:t}:{status:r,key:w,keyPrefix:g,operation:e,value:t,error:a};return e.withRegion(E,(()=>{var r=e.createStore({ref:void 0},{serialize:"ignore"}),o=k(g+w,(e=>D(e))),a=e.attach({source:r,effect:t(o.get)}),i=e.attach({source:r,effect:t(o.set)}),f=e.createEffect((e=>r=>!e||void 0===r||("isData"in e?e.isData(r):e(r))?r:(()=>{throw"getErrorMessages"in e?e.getErrorMessages(r):void 0})())(m)),y=e.createEvent(),E=y.filterMap((({status:e,key:r,keyPrefix:t,operation:o,value:a})=>"done"===e?{key:r,keyPrefix:t,operation:o,value:a}:void 0)),x=y.filterMap((({status:e,key:r,keyPrefix:t,operation:o,error:a,value:i})=>"fail"===e?{key:r,keyPrefix:t,operation:o,error:a,value:i}:void 0)),M=e.createEvent(),D=a;r.updates.watch((()=>{D=(r=>{try{return e.scopeBind(r,{safe:!0})}catch(e){return r}})(a)})),e.sample({source:s,clock:d,target:M}),e.guard({source:e.sample(h,M,((e,r)=>[r,e])),filter:([e,r])=>e!==r,target:i.prepend((([e])=>e))}),e.forward({from:[a.doneData,i],to:h}),e.sample({source:e.merge([a.doneData,h]),target:f}),e.forward({from:f.doneData,to:n}),e.forward({from:[a.finally.map(P("get")),i.finally.map(P("set")),f.fail.map(P("validate"))],to:y}),e.forward({from:x,to:u}),c&&e.forward({from:E,to:c}),l&&e.forward({from:y,to:l}),v&&r.on(v,(({ref:e},r)=>({ref:void 0===r?e:r}))),p?(e.forward({from:p,to:a.prepend((()=>{}))}),r.on(p,(({ref:e},r)=>({ref:void 0===r?e:r})))):a()})),x.unsubscribe=x};
|
|
2
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Unit, Store, Event, Effect, Subscription } from 'effector'
|
|
2
|
+
|
|
3
|
+
interface StorageAdapter {
|
|
4
|
+
<State>(key: string, update: (raw?: any) => any): {
|
|
5
|
+
get(raw?: any, ctx?: any): State | Promise<State | undefined> | undefined
|
|
6
|
+
set(value: State, ctx?: any): void
|
|
7
|
+
}
|
|
8
|
+
keyArea?: any
|
|
9
|
+
noop?: boolean
|
|
10
|
+
}
|
|
11
|
+
interface StorageAdapterFactory<AdapterConfig> {
|
|
12
|
+
(config?: AdapterConfig): StorageAdapter
|
|
13
|
+
factory: true
|
|
14
|
+
}
|
|
15
|
+
type Contract<Data> =
|
|
16
|
+
| ((raw: unknown) => raw is Data)
|
|
17
|
+
| {
|
|
18
|
+
isData: (raw: unknown) => raw is Data
|
|
19
|
+
getErrorMessages: (raw: unknown) => string[]
|
|
20
|
+
}
|
|
21
|
+
type Done<State> = {
|
|
22
|
+
key: string
|
|
23
|
+
keyPrefix: string
|
|
24
|
+
operation: 'set' | 'get'
|
|
25
|
+
value: State
|
|
26
|
+
}
|
|
27
|
+
type Fail<Err> = {
|
|
28
|
+
key: string
|
|
29
|
+
keyPrefix: string
|
|
30
|
+
operation: 'set' | 'get'
|
|
31
|
+
error: Err
|
|
32
|
+
value?: any
|
|
33
|
+
}
|
|
34
|
+
type Finally<State, Err> =
|
|
35
|
+
| (Done<State> & {
|
|
36
|
+
status: 'done'
|
|
37
|
+
})
|
|
38
|
+
| (Fail<Err> & {
|
|
39
|
+
status: 'fail'
|
|
40
|
+
})
|
|
41
|
+
interface ConfigPersist {
|
|
42
|
+
pickup?: Unit<any>
|
|
43
|
+
context?: Unit<any>
|
|
44
|
+
keyPrefix?: string
|
|
45
|
+
contract?: Contract<any>
|
|
46
|
+
}
|
|
47
|
+
interface ConfigAdapter {
|
|
48
|
+
adapter: StorageAdapter
|
|
49
|
+
}
|
|
50
|
+
interface ConfigAdapterFactory<AdapterConfig> {
|
|
51
|
+
adapter: StorageAdapterFactory<AdapterConfig>
|
|
52
|
+
}
|
|
53
|
+
interface ConfigCommon<State, Err = Error> {
|
|
54
|
+
clock?: Unit<any>
|
|
55
|
+
done?: Unit<Done<State>>
|
|
56
|
+
fail?: Unit<Fail<Err>>
|
|
57
|
+
finally?: Unit<Finally<State, Err>>
|
|
58
|
+
pickup?: Unit<any>
|
|
59
|
+
context?: Unit<any>
|
|
60
|
+
key?: string
|
|
61
|
+
keyPrefix?: string
|
|
62
|
+
contract?: Contract<State | undefined>
|
|
63
|
+
}
|
|
64
|
+
interface ConfigJustStore<State> {
|
|
65
|
+
store: Store<State>
|
|
66
|
+
}
|
|
67
|
+
interface ConfigJustSourceTarget<State> {
|
|
68
|
+
source: Store<State> | Event<State> | Effect<State, any, any>
|
|
69
|
+
target: Store<State> | Event<State> | Effect<State, any, any>
|
|
70
|
+
}
|
|
71
|
+
interface ConfigStore<State, Err = Error>
|
|
72
|
+
extends ConfigCommon<State, Err>,
|
|
73
|
+
ConfigJustStore<State> {}
|
|
74
|
+
interface ConfigSourceTarget<State, Err = Error>
|
|
75
|
+
extends ConfigCommon<State, Err>,
|
|
76
|
+
ConfigJustSourceTarget<State> {}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Main `persist` function
|
|
80
|
+
*/
|
|
81
|
+
declare function persist<State, Err = Error>(
|
|
82
|
+
config: Partial<
|
|
83
|
+
(ConfigAdapter | ConfigAdapterFactory<any>) &
|
|
84
|
+
ConfigPersist &
|
|
85
|
+
ConfigStore<State, Err> &
|
|
86
|
+
ConfigSourceTarget<State, Err>
|
|
87
|
+
>
|
|
88
|
+
): Subscription
|
|
89
|
+
|
|
90
|
+
export { persist }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../../src/core/area.ts","../../src/core/index.ts"],"sourcesContent":["import type { Store } from 'effector'\nimport { createStore } from 'effector'\n\n/**\n * Keys areas / namespaces cache\n */\nconst areas = new Map<any, Map<string, Store<any>>>()\n\n/**\n * Get store, responsible for the key in key area / namespace\n */\nexport function getAreaStorage<State>(keyArea: any, key: string): Store<State> {\n let area = areas.get(keyArea)\n if (area === undefined) {\n area = new Map()\n areas.set(keyArea, area)\n }\n\n let store = area.get(key)\n if (store !== undefined) {\n return store\n }\n\n store = createStore(null, { serialize: 'ignore' })\n area.set(key, store)\n\n return store\n}\n","import type { Effect, Subscription } from 'effector'\nimport type {\n ConfigAdapter,\n ConfigAdapterFactory,\n ConfigPersist,\n ConfigSourceTarget,\n ConfigStore,\n Contract,\n Done,\n Fail,\n Finally,\n} from '../types'\nimport {\n attach,\n clearNode,\n createEvent,\n createEffect,\n createNode,\n createStore,\n forward,\n guard,\n is,\n merge,\n sample,\n scopeBind,\n withRegion,\n} from 'effector'\nimport { getAreaStorage } from './area'\n\n// helper function to swap two function arguments\n// end extract current context from ref-box\nconst contextual =\n <T, C, R>(fn: (value: T, ctx?: C) => R) =>\n (ctx: { ref?: C }, value: T) =>\n fn(value, ctx.ref)\n\n// helper function to validate data with contract\nconst contracted =\n <T>(contract?: Contract<T>) =>\n (raw: unknown) =>\n !contract || // no contract -> data is valid\n raw === undefined || // `undefined` is always valid\n ('isData' in contract ? contract.isData(raw) : contract(raw))\n ? (raw as T)\n : (() => {\n throw 'getErrorMessages' in contract\n ? contract.getErrorMessages(raw)\n : undefined\n })()\n\n// helper function for safe bind effects to scope\n// since version 22.4.0 there is `safe` option in `scopeBind`,\n// but as long as effector-storage supports 22.0 this helper is required\nconst safeBind = (fx: Effect<any, any, any>) => {\n try {\n // @ts-expect-error due to old typings in import\n return scopeBind(fx, { safe: true })\n } catch (e) {\n return fx\n }\n}\n\n/**\n * Default sink for unhandled errors\n */\nconst sink = createEvent<Fail<any>>()\nsink.watch((payload) => console.error(payload.error))\n\n/**\n * Main `persist` function\n */\nexport function persist<State, Err = Error>(\n config: Partial<\n (ConfigAdapter | ConfigAdapterFactory<any>) &\n ConfigPersist &\n ConfigStore<State, Err> &\n ConfigSourceTarget<State, Err>\n >\n): Subscription {\n const {\n adapter: adapterOrFactory,\n store,\n source = store,\n target = store,\n clock = source,\n done,\n fail = sink,\n finally: anyway,\n pickup,\n context,\n key: keyName,\n keyPrefix = '',\n contract,\n } = config\n\n if (!adapterOrFactory) {\n throw Error('Adapter is not defined')\n }\n if (!source) {\n throw Error('Store or source is not defined')\n }\n if (!target) {\n throw Error('Target is not defined')\n }\n if (!keyName && source.shortName === (source as any).id) {\n throw Error('Key or name is not defined')\n }\n if (source === target && !is.store(source)) {\n throw Error('Source must be different from target')\n }\n\n // get default value from store, if given\n // this is used in adapter factory\n if ((config as any).def === undefined && is.store(source)) {\n ;(config as any).def = source.defaultState\n }\n\n const adapter =\n 'factory' in adapterOrFactory ? adapterOrFactory(config) : adapterOrFactory\n\n const key = keyName || source.shortName\n const storage = getAreaStorage<State>(\n adapter.keyArea || adapter,\n keyPrefix + key\n )\n const region = createNode()\n const desist = () => clearNode(region)\n\n const op =\n (operation: 'get' | 'set' | 'validate') =>\n ({ status = 'fail', params, result, error }: any): any =>\n status === 'done'\n ? {\n status,\n key,\n keyPrefix,\n operation,\n value: operation === 'get' ? result : params,\n }\n : {\n status,\n key,\n keyPrefix,\n operation,\n value: params,\n error,\n }\n\n // create all auxiliary units and nodes within the region,\n // to be able to remove them all at once on unsubscription\n withRegion(region, () => {\n const ctx = createStore<{ ref: any }>(\n { ref: undefined },\n { serialize: 'ignore' }\n )\n\n const value = adapter<State>(keyPrefix + key, (x) => bindedGet(x))\n\n const getFx = attach({\n source: ctx,\n effect: contextual(value.get),\n }) as any as Effect<void, State, Err>\n\n const setFx = attach({\n source: ctx,\n effect: contextual(value.set),\n }) as any as Effect<State, void, Err>\n\n const validateFx = createEffect<unknown, State>(contracted(contract))\n\n const localAnyway = createEvent<Finally<State, Err>>()\n const localDone = localAnyway.filterMap<Done<State>>(\n ({ status, key, keyPrefix, operation, value }) =>\n status === 'done' ? { key, keyPrefix, operation, value } : undefined\n )\n const localFail = localAnyway.filterMap<Fail<Err>>(\n ({ status, key, keyPrefix, operation, error, value }: any) =>\n status === 'fail'\n ? { key, keyPrefix, operation, error, value }\n : undefined\n )\n\n const trigger = createEvent<State>()\n\n let bindedGet: (raw?: any) => any = getFx\n ctx.updates.watch(() => {\n bindedGet = safeBind(getFx)\n })\n\n sample({\n source,\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n clock: clock!, // `clock` is always defined, as long as `source` is defined\n target: trigger,\n })\n\n guard({\n source: sample(storage, trigger, (current, proposed) => [\n proposed,\n current,\n ]),\n filter: ([proposed, current]) => proposed !== current,\n target: setFx.prepend(([proposed]: State[]) => proposed),\n })\n forward({ from: [getFx.doneData, setFx], to: storage })\n sample({\n source: merge([getFx.doneData, storage]),\n target: validateFx as any,\n })\n forward({ from: validateFx.doneData, to: target })\n\n forward({\n from: [\n getFx.finally.map(op('get')),\n setFx.finally.map(op('set')),\n validateFx.fail.map(op('validate')),\n ],\n to: localAnyway,\n })\n\n forward({ from: localFail, to: fail })\n if (done) forward({ from: localDone, to: done })\n if (anyway) forward({ from: localAnyway, to: anyway })\n\n if (context) {\n ctx.on(context, ({ ref }, payload) => ({\n ref: payload === undefined ? ref : payload,\n }))\n }\n\n if (pickup) {\n // pick up value from storage ONLY on `pickup` update\n forward({ from: pickup, to: getFx.prepend(() => undefined) })\n ctx.on(pickup, ({ ref }, payload) => ({\n ref: payload === undefined ? ref : payload,\n }))\n } else {\n // kick getter to pick up initial value from storage\n getFx()\n }\n })\n\n return (desist.unsubscribe = desist)\n}\n"],"names":["areas","Map","contextual","fn","ctx","value","ref","sink","createEvent","watch","payload","console","error","config","adapter","adapterOrFactory","store","source","target","clock","done","fail","finally","anyway","pickup","context","key","keyName","keyPrefix","contract","Error","shortName","id","is","undefined","def","defaultState","storage","keyArea","area","get","set","createStore","serialize","getAreaStorage","region","createNode","desist","clearNode","op","operation","status","params","result","withRegion","x","bindedGet","getFx","attach","effect","setFx","validateFx","createEffect","raw","isData","getErrorMessages","contracted","localAnyway","localDone","filterMap","localFail","trigger","updates","fx","scopeBind","safe","e","safeBind","sample","guard","current","proposed","filter","prepend","forward","from","doneData","to","merge","map","on","unsubscribe"],"mappings":"uCAMMA,EAAQ,IAAIC,ICyBZC,EACMC,GACV,CAACC,EAAkBC,IACjBF,EAAGE,EAAOD,EAAIE,KA+BZC,EAAOC,EAAWA,cACxBD,EAAKE,OAAOC,GAAYC,QAAQC,MAAMF,EAAQE,yBAKvC,SACLC,GAOA,IACEC,QAASC,EAAgBC,MACzBA,EAAKC,OACLA,EAASD,EAAKE,OACdA,EAASF,EAAKG,MACdA,EAAQF,EAAMG,KACdA,EAAIC,KACJA,EAAOd,EACPe,QAASC,EAAMC,OACfA,EAAMC,QACNA,EACAC,IAAKC,EAAOC,UACZA,EAAY,GAAEC,SACdA,GACEhB,EAEJ,IAAKE,EACH,MAAMe,MAAM,0BAEd,IAAKb,EACH,MAAMa,MAAM,kCAEd,IAAKZ,EACH,MAAMY,MAAM,yBAEd,IAAKH,GAAWV,EAAOc,YAAed,EAAee,GACnD,MAAMF,MAAM,8BAEd,GAAIb,IAAWC,IAAWe,EAAEA,GAACjB,MAAMC,GACjC,MAAMa,MAAM,6CAKcI,IAAvBrB,EAAesB,KAAqBF,EAAAA,GAAGjB,MAAMC,KAC9CJ,EAAesB,IAAMlB,EAAOmB,cAGhC,IAAMtB,EACJ,YAAaC,EAAmBA,EAAiBF,GAAUE,EAEvDW,EAAMC,GAAWV,EAAOc,UACxBM,ED9GD,SAA+BC,EAAcZ,GAClD,IAAIa,EAAOvC,EAAMwC,IAAIF,QACRJ,IAATK,IACFA,EAAO,IAAItC,IACXD,EAAMyC,IAAIH,EAASC,IAGrB,IAAIvB,EAAQuB,EAAKC,IAAId,GACrB,YAAcQ,IAAVlB,IAIJA,EAAQ0B,EAAWA,YAAC,KAAM,CAAEC,UAAW,WACvCJ,EAAKE,IAAIf,EAAKV,IAJLA,CAOX,CC8FkB4B,CACd9B,EAAQwB,SAAWxB,EACnBc,EAAYF,GAERmB,EAASC,EAAAA,aACTC,EAASA,IAAMC,YAAUH,GAEzBI,EACHC,GACD,EAAGC,SAAS,OAAQC,SAAQC,SAAQzC,WACvB,SAAXuC,EACI,CACEA,SACAzB,MACAE,YACAsB,YACA7C,MAAqB,QAAd6C,EAAsBG,EAASD,GAExC,CACED,SACAzB,MACAE,YACAsB,YACA7C,MAAO+C,EACPxC,SAiGV,OA5FA0C,EAAUA,WAACT,GAAQ,KACjB,IAAMzC,EAAMsC,EAAAA,YACV,CAAEpC,SAAK4B,GACP,CAAES,UAAW,WAGTtC,EAAQS,EAAec,EAAYF,GAAM6B,GAAMC,EAAUD,KAEzDE,EAAQC,EAAAA,OAAO,CACnBzC,OAAQb,EACRuD,OAAQzD,EAAWG,EAAMmC,OAGrBoB,EAAQF,EAAAA,OAAO,CACnBzC,OAAQb,EACRuD,OAAQzD,EAAWG,EAAMoC,OAGrBoB,EAAaC,EAAYA,aAlI7BjC,IACHkC,IACElC,QACOK,IAAR6B,IACC,WAAYlC,EAAWA,EAASmC,OAAOD,GAAOlC,EAASkC,IACnDA,EACD,MACE,KAAM,qBAAsBlC,EACxBA,EAASoC,iBAAiBF,QAC1B7B,CACL,EAJD,GA4H4CgC,CAAWrC,IAErDsC,EAAc3D,EAAAA,cACd4D,EAAYD,EAAYE,WAC5B,EAAGlB,SAAQzB,MAAKE,YAAWsB,YAAW7C,WACzB,SAAX8C,EAAoB,CAAEzB,MAAKE,YAAWsB,YAAW7C,cAAU6B,IAEzDoC,EAAYH,EAAYE,WAC5B,EAAGlB,SAAQzB,MAAKE,YAAWsB,YAAWtC,QAAOP,WAChC,SAAX8C,EACI,CAAEzB,MAAKE,YAAWsB,YAAWtC,QAAOP,cACpC6B,IAGFqC,EAAU/D,EAAAA,cAEZgD,EAAgCC,EACpCrD,EAAIoE,QAAQ/D,OAAM,KAChB+C,EArIYiB,KAChB,IAEE,OAAOC,EAAAA,UAAUD,EAAI,CAAEE,MAAM,GAC9B,CAAC,MAAOC,GACP,OAAOH,CACT,GA+HgBI,CAASpB,EAAM,IAG7BqB,SAAO,CACL7D,SAEAE,MAAOA,EACPD,OAAQqD,IAGVQ,QAAM,CACJ9D,OAAQ6D,EAAAA,OAAOzC,EAASkC,GAAS,CAACS,EAASC,IAAa,CACtDA,EACAD,KAEFE,OAAQA,EAAED,EAAUD,KAAaC,IAAaD,EAC9C9D,OAAQ0C,EAAMuB,SAAQ,EAAEF,KAAuBA,MAEjDG,UAAQ,CAAEC,KAAM,CAAC5B,EAAM6B,SAAU1B,GAAQ2B,GAAIlD,IAC7CyC,SAAO,CACL7D,OAAQuE,EAAAA,MAAM,CAAC/B,EAAM6B,SAAUjD,IAC/BnB,OAAQ2C,IAEVuB,UAAQ,CAAEC,KAAMxB,EAAWyB,SAAUC,GAAIrE,IAEzCkE,UAAQ,CACNC,KAAM,CACJ5B,EAAMnC,QAAQmE,IAAIxC,EAAG,QACrBW,EAAMtC,QAAQmE,IAAIxC,EAAG,QACrBY,EAAWxC,KAAKoE,IAAIxC,EAAG,cAEzBsC,GAAIpB,IAGNiB,UAAQ,CAAEC,KAAMf,EAAWiB,GAAIlE,IAC3BD,GAAMgE,EAAAA,QAAQ,CAAEC,KAAMjB,EAAWmB,GAAInE,IACrCG,GAAQ6D,EAAAA,QAAQ,CAAEC,KAAMlB,EAAaoB,GAAIhE,IAEzCE,GACFrB,EAAIsF,GAAGjE,GAAS,EAAGnB,OAAOI,KAAa,CACrCJ,SAAiB4B,IAAZxB,EAAwBJ,EAAMI,MAInCc,GAEF4D,UAAQ,CAAEC,KAAM7D,EAAQ+D,GAAI9B,EAAM0B,SAAQ,KAAe,MACzD/E,EAAIsF,GAAGlE,GAAQ,EAAGlB,OAAOI,KAAa,CACpCJ,SAAiB4B,IAAZxB,EAAwBJ,EAAMI,OAIrC+C,GACF,IAGMV,EAAO4C,YAAc5C,CAC/B"}
|
package/core/index.d.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { Unit, Store, Event, Effect, Subscription } from 'effector'
|
|
2
|
+
|
|
3
|
+
interface StorageAdapter {
|
|
4
|
+
<State>(key: string, update: (raw?: any) => any): {
|
|
5
|
+
get(raw?: any, ctx?: any): State | Promise<State | undefined> | undefined
|
|
6
|
+
set(value: State, ctx?: any): void
|
|
7
|
+
}
|
|
8
|
+
keyArea?: any
|
|
9
|
+
noop?: boolean
|
|
10
|
+
}
|
|
11
|
+
interface StorageAdapterFactory<AdapterConfig> {
|
|
12
|
+
(config?: AdapterConfig): StorageAdapter
|
|
13
|
+
factory: true
|
|
14
|
+
}
|
|
15
|
+
type Contract<Data> =
|
|
16
|
+
| ((raw: unknown) => raw is Data)
|
|
17
|
+
| {
|
|
18
|
+
isData: (raw: unknown) => raw is Data
|
|
19
|
+
getErrorMessages: (raw: unknown) => string[]
|
|
20
|
+
}
|
|
21
|
+
type Done<State> = {
|
|
22
|
+
key: string
|
|
23
|
+
keyPrefix: string
|
|
24
|
+
operation: 'set' | 'get'
|
|
25
|
+
value: State
|
|
26
|
+
}
|
|
27
|
+
type Fail<Err> = {
|
|
28
|
+
key: string
|
|
29
|
+
keyPrefix: string
|
|
30
|
+
operation: 'set' | 'get'
|
|
31
|
+
error: Err
|
|
32
|
+
value?: any
|
|
33
|
+
}
|
|
34
|
+
type Finally<State, Err> =
|
|
35
|
+
| (Done<State> & {
|
|
36
|
+
status: 'done'
|
|
37
|
+
})
|
|
38
|
+
| (Fail<Err> & {
|
|
39
|
+
status: 'fail'
|
|
40
|
+
})
|
|
41
|
+
interface ConfigPersist {
|
|
42
|
+
pickup?: Unit<any>
|
|
43
|
+
context?: Unit<any>
|
|
44
|
+
keyPrefix?: string
|
|
45
|
+
contract?: Contract<any>
|
|
46
|
+
}
|
|
47
|
+
interface ConfigAdapter {
|
|
48
|
+
adapter: StorageAdapter
|
|
49
|
+
}
|
|
50
|
+
interface ConfigAdapterFactory<AdapterConfig> {
|
|
51
|
+
adapter: StorageAdapterFactory<AdapterConfig>
|
|
52
|
+
}
|
|
53
|
+
interface ConfigCommon<State, Err = Error> {
|
|
54
|
+
clock?: Unit<any>
|
|
55
|
+
done?: Unit<Done<State>>
|
|
56
|
+
fail?: Unit<Fail<Err>>
|
|
57
|
+
finally?: Unit<Finally<State, Err>>
|
|
58
|
+
pickup?: Unit<any>
|
|
59
|
+
context?: Unit<any>
|
|
60
|
+
key?: string
|
|
61
|
+
keyPrefix?: string
|
|
62
|
+
contract?: Contract<State | undefined>
|
|
63
|
+
}
|
|
64
|
+
interface ConfigJustStore<State> {
|
|
65
|
+
store: Store<State>
|
|
66
|
+
}
|
|
67
|
+
interface ConfigJustSourceTarget<State> {
|
|
68
|
+
source: Store<State> | Event<State> | Effect<State, any, any>
|
|
69
|
+
target: Store<State> | Event<State> | Effect<State, any, any>
|
|
70
|
+
}
|
|
71
|
+
interface ConfigStore<State, Err = Error>
|
|
72
|
+
extends ConfigCommon<State, Err>,
|
|
73
|
+
ConfigJustStore<State> {}
|
|
74
|
+
interface ConfigSourceTarget<State, Err = Error>
|
|
75
|
+
extends ConfigCommon<State, Err>,
|
|
76
|
+
ConfigJustSourceTarget<State> {}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Main `persist` function
|
|
80
|
+
*/
|
|
81
|
+
declare function persist<State, Err = Error>(
|
|
82
|
+
config: Partial<
|
|
83
|
+
(ConfigAdapter | ConfigAdapterFactory<any>) &
|
|
84
|
+
ConfigPersist &
|
|
85
|
+
ConfigStore<State, Err> &
|
|
86
|
+
ConfigSourceTarget<State, Err>
|
|
87
|
+
>
|
|
88
|
+
): Subscription
|
|
89
|
+
|
|
90
|
+
export { persist }
|
package/core/index.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{createStore as e,createEvent as r,is as o,createNode as t,withRegion as a,attach as i,createEffect as f,sample as n,guard as s,forward as d,merge as u,scopeBind as l,clearNode as c}from"effector";var p=new Map,v=e=>(r,o)=>e(o,r.ref),y=r();function k(k){var{adapter:m,store:g,source:h=g,target:w=g,clock:x=h,done:E,fail:P=y,finally:M,pickup:D,context:b,key:S,keyPrefix:z="",contract:A}=k;if(!m)throw Error("Adapter is not defined");if(!h)throw Error("Store or source is not defined");if(!w)throw Error("Target is not defined");if(!S&&h.shortName===h.id)throw Error("Key or name is not defined");if(h===w&&!o.store(h))throw Error("Source must be different from target");void 0===k.def&&o.store(h)&&(k.def=h.defaultState);var N="factory"in m?m(k):m,K=S||h.shortName,T=function(r,o){var t=p.get(r);void 0===t&&(t=new Map,p.set(r,t));var a=t.get(o);return void 0!==a||(a=e(null,{serialize:"ignore"}),t.set(o,a)),a}(N.keyArea||N,z+K),j=t(),q=()=>c(j),B=e=>({status:r="fail",params:o,result:t,error:a})=>"done"===r?{status:r,key:K,keyPrefix:z,operation:e,value:"get"===e?t:o}:{status:r,key:K,keyPrefix:z,operation:e,value:o,error:a};return a(j,(()=>{var o=e({ref:void 0},{serialize:"ignore"}),t=N(z+K,(e=>S(e))),a=i({source:o,effect:v(t.get)}),c=i({source:o,effect:v(t.set)}),p=f((e=>r=>!e||void 0===r||("isData"in e?e.isData(r):e(r))?r:(()=>{throw"getErrorMessages"in e?e.getErrorMessages(r):void 0})())(A)),y=r(),k=y.filterMap((({status:e,key:r,keyPrefix:o,operation:t,value:a})=>"done"===e?{key:r,keyPrefix:o,operation:t,value:a}:void 0)),m=y.filterMap((({status:e,key:r,keyPrefix:o,operation:t,error:a,value:i})=>"fail"===e?{key:r,keyPrefix:o,operation:t,error:a,value:i}:void 0)),g=r(),S=a;o.updates.watch((()=>{S=(e=>{try{return l(e,{safe:!0})}catch(r){return e}})(a)})),n({source:h,clock:x,target:g}),s({source:n(T,g,((e,r)=>[r,e])),filter:([e,r])=>e!==r,target:c.prepend((([e])=>e))}),d({from:[a.doneData,c],to:T}),n({source:u([a.doneData,T]),target:p}),d({from:p.doneData,to:w}),d({from:[a.finally.map(B("get")),c.finally.map(B("set")),p.fail.map(B("validate"))],to:y}),d({from:m,to:P}),E&&d({from:k,to:E}),M&&d({from:y,to:M}),b&&o.on(b,(({ref:e},r)=>({ref:void 0===r?e:r}))),D?(d({from:D,to:a.prepend((()=>{}))}),o.on(D,(({ref:e},r)=>({ref:void 0===r?e:r})))):a()})),q.unsubscribe=q}y.watch((e=>console.error(e.error)));export{k as persist};
|
|
2
|
+
//# sourceMappingURL=index.js.map
|