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.
Files changed (105) hide show
  1. package/README.md +95 -21
  2. package/async-storage/index.cjs +1 -1
  3. package/async-storage/index.cjs.d.ts +12 -5
  4. package/async-storage/index.cjs.map +1 -1
  5. package/async-storage/index.d.ts +12 -5
  6. package/async-storage/index.js +1 -1
  7. package/async-storage/index.js.flow +21 -4
  8. package/async-storage/index.js.map +1 -1
  9. package/async-storage/package.json +1 -0
  10. package/core/index.cjs +2 -0
  11. package/core/index.cjs.d.ts +90 -0
  12. package/core/index.cjs.map +1 -0
  13. package/core/index.d.ts +90 -0
  14. package/core/index.js +2 -0
  15. package/core/index.js.flow +113 -0
  16. package/core/index.js.map +1 -0
  17. package/core/package.json +8 -0
  18. package/index.cjs +1 -1
  19. package/index.cjs.d.ts +54 -12
  20. package/index.cjs.map +1 -1
  21. package/index.d.ts +54 -12
  22. package/index.js +1 -1
  23. package/index.js.flow +61 -10
  24. package/index.js.map +1 -1
  25. package/local/index.cjs +1 -1
  26. package/local/index.cjs.d.ts +45 -15
  27. package/local/index.cjs.map +1 -1
  28. package/local/index.d.ts +45 -15
  29. package/local/index.js +1 -1
  30. package/local/index.js.flow +53 -13
  31. package/local/index.js.map +1 -1
  32. package/local/package.json +1 -0
  33. package/log/index.cjs +2 -0
  34. package/log/index.cjs.d.ts +19 -0
  35. package/log/index.cjs.map +1 -0
  36. package/log/index.d.ts +19 -0
  37. package/log/index.js +2 -0
  38. package/log/index.js.flow +31 -0
  39. package/log/index.js.map +1 -0
  40. package/log/package.json +8 -0
  41. package/memory/index.cjs +1 -1
  42. package/memory/index.cjs.d.ts +42 -9
  43. package/memory/index.cjs.map +1 -1
  44. package/memory/index.d.ts +42 -9
  45. package/memory/index.js +1 -1
  46. package/memory/index.js.flow +45 -6
  47. package/memory/index.js.map +1 -1
  48. package/memory/package.json +1 -0
  49. package/nil/index.cjs +1 -1
  50. package/nil/index.cjs.d.ts +16 -6
  51. package/nil/index.cjs.map +1 -1
  52. package/nil/index.d.ts +16 -6
  53. package/nil/index.js +1 -1
  54. package/nil/index.js.flow +24 -4
  55. package/nil/index.js.map +1 -1
  56. package/nil/package.json +1 -0
  57. package/package.json +20 -4
  58. package/query/index.cjs +1 -1
  59. package/query/index.cjs.d.ts +50 -22
  60. package/query/index.cjs.map +1 -1
  61. package/query/index.d.ts +50 -22
  62. package/query/index.js +1 -1
  63. package/query/index.js.flow +54 -15
  64. package/query/index.js.map +1 -1
  65. package/query/package.json +1 -0
  66. package/rn/async/index.cjs +1 -1
  67. package/rn/async/index.cjs.d.ts +40 -13
  68. package/rn/async/index.cjs.map +1 -1
  69. package/rn/async/index.d.ts +40 -13
  70. package/rn/async/index.js +1 -1
  71. package/rn/async/index.js.flow +48 -11
  72. package/rn/async/index.js.map +1 -1
  73. package/rn/async/package.json +1 -0
  74. package/rn/encrypted/index.cjs +1 -1
  75. package/rn/encrypted/index.cjs.d.ts +40 -13
  76. package/rn/encrypted/index.cjs.map +1 -1
  77. package/rn/encrypted/index.d.ts +40 -13
  78. package/rn/encrypted/index.js +1 -1
  79. package/rn/encrypted/index.js.flow +50 -11
  80. package/rn/encrypted/index.js.map +1 -1
  81. package/rn/encrypted/package.json +1 -0
  82. package/session/index.cjs +1 -1
  83. package/session/index.cjs.d.ts +45 -15
  84. package/session/index.cjs.map +1 -1
  85. package/session/index.d.ts +45 -15
  86. package/session/index.js +1 -1
  87. package/session/index.js.flow +53 -13
  88. package/session/index.js.map +1 -1
  89. package/session/package.json +1 -0
  90. package/storage/index.cjs +1 -1
  91. package/storage/index.cjs.d.ts +17 -6
  92. package/storage/index.cjs.map +1 -1
  93. package/storage/index.d.ts +17 -6
  94. package/storage/index.js +1 -1
  95. package/storage/index.js.flow +24 -5
  96. package/storage/index.js.map +1 -1
  97. package/storage/package.json +1 -0
  98. package/tools/index.cjs +2 -0
  99. package/tools/index.cjs.d.ts +125 -0
  100. package/tools/index.cjs.map +1 -0
  101. package/tools/index.d.ts +125 -0
  102. package/tools/index.js +2 -0
  103. package/tools/index.js.flow +101 -0
  104. package/tools/index.js.map +1 -0
  105. 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/master/src/local/README.md)
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/master/src/session/README.md)
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/master/src/query/README.md)
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
- Docs: [effector-storage/rn/async](https://github.com/yumauri/effector-storage/tree/master/src/rn/async/README.md)
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
- Docs: [effector-storage/rn/encrypted](https://github.com/yumauri/effector-storage/tree/master/src/rn/encrypted/README.md)
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) or write (set).
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) or write (set).
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 two _Effects_ and bunch of _forwards_.
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 -> trigger `update`
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
- forward({ from: pickup, to: update })
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/master/src/local/README.md))
490
- - [x] [sessionStorage] support (docs: [effector-storage/session](https://github.com/yumauri/effector-storage/tree/master/src/session/README.md))
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/master/src/query/README.md))
492
- - [x] [AsyncStorage] support (docs: [effector-storage/rn/async](https://github.com/yumauri/effector-storage/tree/master/src/rn/async/README.md))
493
- - [x] [EncryptedStorage] support (docs: [effector-storage/rn/encrypted](https://github.com/yumauri/effector-storage/tree/master/src/rn/encrypted/README.md))
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
@@ -1,2 +1,2 @@
1
- "use strict";exports.asyncStorage=function({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))}});return r.keyArea=e,r};
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
- import { StorageAdapter } from '..'
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 '..'\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 * Generic `AsyncStorage` adapter factory\n */\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 adapter.keyArea = storage\n return adapter\n}\n"],"names":["storage","serialize","JSON","stringify","deserialize","parse","adapter","key","async","item","getItem","undefined","value","setItem","keyArea"],"mappings":"kCAgBO,UAAsBA,QAC3BA,EAD2BC,UAE3BA,EAAYC,KAAKC,UAFUC,YAG3BA,EAAcF,KAAKG,QAEnB,IAAMC,EAAkCC,IAAiB,CACvDC,YACE,IAAMC,QAAaT,EAAQU,QAAQH,GACnC,OAAgB,OAATE,OAAgBE,EAAYP,EAAYK,EAHM,EAMvDD,UAAUI,SACFZ,EAAQa,QAAQN,EAAKN,EAAUW,GACtC,IAIH,OADAN,EAAQQ,QAAUd,EACXM,CACR"}
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"}
@@ -1,21 +1,28 @@
1
- import { StorageAdapter } from '..'
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,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))}});return r.keyArea=e,r}export{e as asyncStorage};
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.20.1
4
+ * Flowgen v1.21.0
5
5
  * @flow
6
6
  */
7
7
 
8
- import { StorageAdapter } from '..'
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 function asyncStorage(x: AsyncStorageConfig): StorageAdapter
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 '..'\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 * Generic `AsyncStorage` adapter factory\n */\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 adapter.keyArea = storage\n return adapter\n}\n"],"names":["asyncStorage","storage","serialize","JSON","stringify","deserialize","parse","adapter","key","async","item","getItem","undefined","value","setItem","keyArea"],"mappings":"AAgBO,SAASA,GAAaC,QAC3BA,EAD2BC,UAE3BA,EAAYC,KAAKC,UAFUC,YAG3BA,EAAcF,KAAKG,QAEnB,IAAMC,EAAkCC,IAAiB,CACvDC,YACE,IAAMC,QAAaT,EAAQU,QAAQH,GACnC,OAAgB,OAATE,OAAgBE,EAAYP,EAAYK,EAHM,EAMvDD,UAAUI,SACFZ,EAAQa,QAAQN,EAAKN,EAAUW,GACtC,IAIH,OADAN,EAAQQ,QAAUd,EACXM,CACR"}
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"}
@@ -1,5 +1,6 @@
1
1
  {
2
2
  "type": "module",
3
+ "sideEffects": false,
3
4
  "main": "index.cjs",
4
5
  "module": "index.js",
5
6
  "react-native": "index.js",
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":"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"}
@@ -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