Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate adapter effects #217

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 22 additions & 22 deletions .size-limit.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ module.exports = [
{
name: 'root persist, es module',
path: 'build/index.js',
limit: '985 B',
limit: '989 B',
import: '{ persist }',
ignore: ['effector'],
gzip: true,
},
{
name: 'root persist, cjs module',
path: 'build/index.cjs',
limit: '3250 B',
limit: '3869 B',
// import: '{ persist }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -21,15 +21,15 @@ module.exports = [
{
name: 'core persist, es module',
path: 'build/core/index.js',
limit: '980 B',
limit: '984 B',
import: '{ persist }',
ignore: ['effector'],
gzip: true,
},
{
name: 'core persist, cjs module',
path: 'build/core/index.cjs',
limit: '1166 B',
limit: '1643 B',
// import: '{ persist }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -39,15 +39,15 @@ module.exports = [
{
name: 'tools, es module',
path: 'build/tools/index.js',
limit: '289 B',
limit: '292 B',
import: '{ async, either, farcached }',
ignore: ['effector'],
gzip: true,
},
{
name: 'tools, cjs module',
path: 'build/tools/index.cjs',
limit: '482 B',
limit: '485 B',
// import: '{ async, either, farcached }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -65,7 +65,7 @@ module.exports = [
{
name: 'nil adapter, cjs module',
path: 'build/nil/index.cjs',
limit: '139 B',
limit: '140 B',
// import: '{ nil }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -75,7 +75,7 @@ module.exports = [
{
name: 'log adapter, es module',
path: 'build/log/index.js',
limit: '139 B',
limit: '140 B',
import: '{ log }',
ignore: ['effector'],
gzip: true,
Expand All @@ -93,7 +93,7 @@ module.exports = [
{
name: 'storage adapter, es module',
path: 'build/storage/index.js',
limit: '399 B',
limit: '401 B',
import: '{ storage }',
ignore: ['effector'],
gzip: true,
Expand All @@ -111,15 +111,15 @@ module.exports = [
{
name: '`localStorage` persist, es module',
path: 'build/local/index.js',
limit: '1453 B',
limit: '1464 B',
import: '{ persist }',
ignore: ['effector'],
gzip: true,
},
{
name: '`localStorage` persist, cjs module',
path: 'build/local/index.cjs',
limit: '1732 B',
limit: '2238 B',
// import: '{ persist }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -130,15 +130,15 @@ module.exports = [
{
name: 'core adapter, es module',
path: 'build/index.js',
limit: '1459 B',
limit: '1444 B',
import: '{ persist, local }',
ignore: ['effector'],
gzip: true,
},
{
name: 'core adapter factory, es module',
path: ['build/index.js', 'build/local/index.js'],
limit: '1462 B',
limit: '1446 B',
import: {
'build/index.js': '{ persist }',
'build/local/index.js': '{ local }',
Expand All @@ -151,15 +151,15 @@ module.exports = [
{
name: '`sessionStorage` persist, es module',
path: 'build/session/index.js',
limit: '1451 B',
limit: '1463 B',
import: '{ persist }',
ignore: ['effector'],
gzip: true,
},
{
name: '`sessionStorage` persist, cjs module',
path: 'build/session/index.cjs',
limit: '1729 B',
limit: '2234 B',
// import: '{ persist }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -169,15 +169,15 @@ module.exports = [
{
name: 'query string persist, es module',
path: 'build/query/index.js',
limit: '1509 B',
limit: '1514 B',
import: '{ persist }',
ignore: ['effector'],
gzip: true,
},
{
name: 'query string persist, cjs module',
path: 'build/query/index.cjs',
limit: '1802 B',
limit: '2295 B',
// import: '{ persist }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -187,15 +187,15 @@ module.exports = [
{
name: 'memory adapter, es module',
path: 'build/memory/index.js',
limit: '1073 B',
limit: '1078 B',
import: '{ persist }',
ignore: ['effector'],
gzip: true,
},
{
name: 'memory adapter, cjs module',
path: 'build/memory/index.cjs',
limit: '1304 B',
limit: '1816 B',
// import: '{ persist }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand All @@ -205,7 +205,7 @@ module.exports = [
{
name: 'generic async storage adapter, es module',
path: 'build/async-storage/index.js',
limit: '162 B',
limit: '165 B',
import: '{ asyncStorage }',
ignore: ['effector'],
gzip: true,
Expand All @@ -223,15 +223,15 @@ module.exports = [
{
name: 'broadcast channel adapter, es module',
path: 'build/broadcast/index.js',
limit: '1317 B',
limit: '371 B',
import: '{ broadcast }',
ignore: ['effector'],
gzip: true,
},
{
name: 'broadcast channel adapter, cjs module',
path: 'build/broadcast/index.cjs',
limit: '1568 B',
limit: '2074 B',
// import: '{ broadcast }', // tree-shaking is not working with cjs
ignore: ['effector'],
gzip: true,
Expand Down
57 changes: 54 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ In order to synchronize _something_, you need to specify effector units. Dependi
- `keyPrefix` ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.
- `operation` (_`'set'`_ | _`'get'`_ | _`'validate'`_): Type of operation, read (get), write (set) or validation against contract (validate).
- `error` ([_Error_]): Error instance
- `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.
- `value`? (_any_): In case of _'set'_ operation — value from `store`. In case of _'get'_ and _'validate'_ operations could contain raw value from storage or could be empty.
- `finally`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which will be triggered either in case of success or error.<br>
Payload structure:
- `key` ([_string_]): Same `key` as above.
Expand Down Expand Up @@ -274,6 +274,57 @@ persist({

- Custom `persist` function, with predefined adapter options.

## `createStorage` factory

In rare cases you might want to get a granular control over a storage and manually set or get values from it. You can use `createStorage` factory for that.

```javascript
import { sample, createEvent } from 'effector'
import { createStorage } from 'effector-storage/local'

const persist = createStorage('my-storage')

const userWantToSave = createEvent()
const userWantToLoad = createEvent()

// ---8<---

sample({
clock: userWantToSave,
fn: () => 'some data'
target: persist.setFx,
})

sample({
source: userWantToLoad,
target: persist.getFx,
})
```

### Options

- `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.
- `keyPrefix`? ([_string_]): Prefix, used in adapter, to be concatenated to `key`. By default = `''`.
- `context`? ([_Event_] | [_Effect_] | [_Store_]): Unit, which can set a special context for adapter.
- `contract`? ([_Contract_]): Rule to statically validate data from storage.

### Returns

An object with fields:

- `getFx` (_Effect_): to get value from storage.
- `setFx` (_Effect_): to set value to storage.
- `removeFx` (_Effect_): to remove value from storage.

All fields of returned object are _Effects_ units, so you can use them in `sample` as any other _Effects_. For example, you can add logging on failed storage operations:

```javascript
sample({
clock: getFx.fail,
target: sendLogToSentry,
})
```

## Advanced usage

`effector-storage` consists of a _core_ module and _adapter_ modules.
Expand All @@ -300,8 +351,8 @@ interface StorageAdapter {
key: string,
update: (raw?: any) => void
): {
get(raw?: any, ctx?: any): State | Promise<State | undefined> | undefined
set(value: State, ctx?: any): void
get(raw?: any, ctx?: any): State | undefined | Promise<State | undefined>
set(value: State, ctx?: any): void | Promise<void>
}
keyArea?: any
noop?: boolean
Expand Down
5 changes: 1 addition & 4 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,7 @@ export default tseslint.config(
'@typescript-eslint/no-empty-object-type': 'off',
'@typescript-eslint/unified-signatures': 'off',
'@typescript-eslint/consistent-type-definitions': 'off',
'@typescript-eslint/no-invalid-void-type': [
'error',
{ allowAsThisParameter: true },
],
'@typescript-eslint/no-invalid-void-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': [
'error',
{ allowArgumentsExplicitlyTypedAsAny: true },
Expand Down
1 change: 1 addition & 0 deletions rollup.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const src = (name) => ({
},
format: {
comments: false,
preserve_annotations: true,
},
}),

Expand Down
37 changes: 34 additions & 3 deletions src/broadcast/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import type {
ConfigPersist as BaseConfigPersist,
ConfigStore as BaseConfigStore,
ConfigSourceTarget as BaseConfigSourceTarget,
ConfigCreateStorage as BaseConfigCreateStorage,
StorageAdapter,
StorageHandles,
} from '../types'
import type { BroadcastConfig } from './adapter'
import { persist as base } from '../core'
import { persist as base, createStorage as baseCreateStorage } from '../core'
import { nil } from '../nil'
import { adapter } from './adapter'

Expand Down Expand Up @@ -37,6 +39,19 @@ export interface Persist {
<State, Err = Error>(config: ConfigStore<State, Err>): Subscription
}

export interface ConfigCreateStorage<State>
extends BaseConfigCreateStorage<State> {}

export interface CreateStorage {
<State, Err = Error>(
key: string,
config?: BroadcastConfig & BaseConfigCreateStorage<State>
): StorageHandles<State, Err>
<State, Err = Error>(
config: BroadcastConfig & BaseConfigCreateStorage<State> & { key: string }
): StorageHandles<State, Err>
}

/**
* Function, checking if `BroadcastChannel` exists and accessible
*/
Expand All @@ -45,7 +60,7 @@ function supports() {
}

/**
* Creates BroadcastChannel string adapter
* Creates BroadcastChannel adapter
*/
broadcast.factory = true as const
export function broadcast(config?: BroadcastConfig): StorageAdapter {
Expand All @@ -72,4 +87,20 @@ export function createPersist(defaults?: ConfigPersist): Persist {
/**
* Default partially applied `persist`
*/
export const persist = createPersist()
export const persist = /*#__PURE__*/ createPersist()

/**
* Creates custom partially applied `createStorage`
* with predefined BroadcastChannel adapter
*/
export function createStorageFactory(
defaults?: ConfigCreateStorage<any>
): CreateStorage {
return (...configs: any[]) =>
baseCreateStorage({ adapter: broadcast }, defaults, ...configs)
}

/**
* Default partially applied `createStorage`
*/
export const createStorage = /*#__PURE__*/ createStorageFactory()
9 changes: 6 additions & 3 deletions src/core/area.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import type { Store } from 'effector'
import type { StoreWritable } from 'effector'
import { createStore } from 'effector'

/**
* Keys areas / namespaces cache
*/
const areas = new Map<any, Map<string, Store<any>>>()
const areas = new Map<any, Map<string, StoreWritable<any>>>()

/**
* Get store, responsible for the key in key area / namespace
*/
export function getAreaStorage<State>(keyArea: any, key: string): Store<State> {
export function getAreaStorage<State>(
keyArea: any,
key: string
): StoreWritable<State> {
let area = areas.get(keyArea)
if (area === undefined) {
area = new Map()
Expand Down
Loading