Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
A tiny event-based Redux-like state manager for web.
Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | None × |
npm install storefy
Using unpkg CDN:
<script src="https://unpkg.com/storefy/dist/storefy.min.js"></script>
NodeJs:
const Storefy = Storefy
const store = Storefy(function (store) {
store.on('@init', { count: 0 }) // Initial state
store.on('increment', ({ count }) => ({ count: count + 1 }))
})
store.dispath('increment') // count = 1
store.dispath('increment') // count = 2
In a browser you can use the global Storefy function:
var store = Storefy(function (store) {
store.on('@init', { count: 0 }) // Initial state
store.on('increment', ({ count }) => ({ count: count + 1 }))
})
store.dispath('increment') // count = 1
store.dispath('increment') // count = 2
var store = Storefy({ count: 10 }) // Initial state
store.on('increment', ({ count }) => ({ count: count + 1 }))
store.dispath('increment') // count = 11
store.dispath('increment') // count = 12
Multiple modules: Initial state, reducers and business logic are packed in independent modules
// Users module
var users = function (store) {
store.on('@init', { users: [] })
store.on('users:add', function (state, data) {
state.users.push(data)
})
}
// Cart module
var cart = function (store) {
store.on('@init', {
cart: {
products: [],
budget: 0
}
})
store.on('cart:add', function (state, data) {
state.cart.products.push(data)
state.cart.budget += data.cost
})
}
var store = Storefy(users, cart, /* etc... */)
Vue:
Vue.prototype.$store = Storefy(function (store) {
store.on('vue:init', function () { console.log('Vue has started!') })
})
var app = new Vue({
mounted () {
this.$store.dispath('vue:init')
}
})
Typescript:
import { Storefy } from 'storefy'
// Initial state, reducers and business logic are packed in independent modules
let increment = (store) => {
// Initial state
store.on('@init', () => ({ count: 0 }))
// Reducers returns only changed part of the state
store.on('inc', ({ count }) => ({ count: count + 1 }))
}
export const store = Storefy(increment)
React:
import { useStorefy } from 'storefy/react' // or storefy/preact
export const Counter = () => {
// Counter will be re-render only on `state.count` changes
const { dispatch, count } = useStorefy('count')
return <button onClick={() => dispatch('inc')}>{count}</button>
}
import { StoreContext } from 'storefy/react'
render(
<StoreContext.Provider value={store}>
<Counter />
</StoreContext.Provider>,
document.body
)
The store should be created with Storefy()
function. It accepts mutiple arguments as modules.
Each module is just a function, which will accept a store
and bind their event listeners.
// store/index.js
import { Storefy } from 'storefy'
import { projects } from './projects'
import { users } from './users'
export const store = Storefy(projects, users)
// store/projects.js
export function projects (store) {
store.on('@init', { projects: [] })
store.on('projects/add', ({ projects }, project) => {
return { projects: projects.concat([project]) }
})
}
The store has 3 methods:
store.get()
will return current state. The state is always an object.store.on(event, callback)
will add an event listener.store.dispatch(event, data)
will emit an event with optional data.There are three built-in events:
@init
will be fired in Storefy
. The best moment to set
an initial state.@dispatch
will be fired on every new action (on store.dispatch()
calls
and @changed
event). It receives an array with the event name
and the event’s data. Can be useful for debugging.@changed
will be fired when any event changes the state.
It receives object with state changes.To add an event listener, call store.on()
with event name and callback.
store.on('@dispatch', (state, [event, data]) => {
console.log(`Storefy: ${ event } with `, data)
})
store.on()
will return cleanup function. This function will remove
the event listener.
const unbind = store.on('@changed', …)
unbind()
You can dispatch any other events. Just do not start event names with @
.
If the event listener returns an object, this object will update the state. You do not need to return the whole state, return an object with changed keys.
// users: [] will be added to state on initialization
store.on('@init', { users: [] })
It could be a function too:
// users: [] will be added to state on initialization
store.on('@init', () => {
{ users: [] }
})
Event listener accepts the current state as a first argument and optional event object as a second.
So event listeners can be a reducer as well. As in Redux’s reducers, you should change immutable.
store.on('users/save', ({ users }, user) => {
return {
users: { ...users, [user.id]: user }
}
})
store.dispatch('users/save', { id: 1, name: 'Ivan' })
You can dispatch other events in event listeners. It can be useful for async operations.
store.on('users/add', async (state, user) => {
try {
await api.addUser(user)
store.dispatch('users/save', user)
} catch (e) {
store.dispatch('errors/server-error')
}
})
For functional components, useStorefy
hook will be the best option:
import { useStorefy } from 'storefy/react' // Use 'storefy/preact' for Preact
const Users = () => {
const { dispatch, users, projects } = useStorefy('users', 'projects')
const onAdd = useCallback(user => {
dispatch('users/add', user)
})
return <div>
{users.map(user => <User key={user.id} user={user} projects={projects} />)}
<NewUser onAdd={onAdd} />
</div>
}
For class components, you can use connectStorefy()
decorator.
import { connectStorefy } from 'storefy/react' // Use 'storefy/preact' for Preact
class Users extends React.Component {
onAdd = () => {
this.props.dispatch('users/add', user)
}
render () {
return <div>
{this.props.users.map(user => <User key={user.id} user={user} />)}
<NewUser onAdd={this.onAdd} />
</div>
}
}
export default connectStorefy('users', 'anotherStateKey', Users)
useStorefy
hook and connectStorefy()
accept the list of state keys to pass
into props
. It will re-render only if this keys will be changed.
Storefy supports debugging with Redux DevTools Extension.
import { storefyDevtools } from 'storefy/devtools';
const store = Storefy([
…
process.env.NODE_ENV !== 'production' && storefyDevtools
])
DevTools will also warn you about typo in event name. It will throw an error if you are dispatching event, but nobody subscribed to it.
Or if you want to print events to console
you can use built-in logger.
It could be useful for simple cases or to investigate issue in error trackers.
import { storefyLogger } from 'storefy/devtools';
const store = Storefy([
…
process.env.NODE_ENV !== 'production' && storefyLogger
])
Storefy delivers TypeScript declaration which allows to declare type of state and optionally declare types of events and parameter.
If Storefy store has to be full type safe the event types declaration
interface has to be delivered as second type to createStore
function.
import { Storefy, StoreonModufy } from 'storefy'
import { useStorefy } from 'storefy/react' // or storefy/preact
// State structure
interface State {
counter: number
}
// Events declaration: map of event names to type of event data
interface Events {
// `inc` event which do not goes with any data
'inc': undefined
// `set` event which goes with number as data
'set': number
}
const counterModule: StoreonModufy<State, Events> = store => {
store.on('@init', () => ({ counter: 0}))
store.on('inc', state => ({ counter: state.counter + 1}))
store.on('set', (state, event) => ({ counter: event}))
}
const store = Storefy<State, Events>([counterModule])
const Counter = () => {
const { dispatch, count } = useStorefy<State, Events>('count')
// Correct call
dispatch('set', 100)
// Compilation error: `set` event do not expect string data
dispatch('set', "100")
…
}
// Correct calls:
store.dispatch('set', 100)
store.dispatch('inc')
// Compilation errors:
store.dispatch('inc', 100) // `inc` doesn’t have data
store.dispatch('set', "100") // `set` event do not expect string data
store.dispatch('dec') // Unknown event
In order to work properly for imports, it is considering adding
allowSyntheticDefaultImports: true
to tsconfig.json
.
In order to preload data for server-side rendering, Storefy provide
customContext
function to create your own useStorefy
hooks that it will
depends on your custom context.
// store.jsx
import { createContext, render } from 'react' // or preact
import { Storefy, StoreonModufy } from 'storefy'
import { customContext } from 'storefy/react' // or storefy/preact
const store = …
const CustomContext = createContext(store)
// useStorefy will automatically recognize your storefy store and event types
export const useStorefy = customContext(CustomContext)
render(
<CustomContext.Provider value={store}>
<Counter />
</CustomContext.Provider>,
document.body
)
// children.jsx
import { useStorefy } from '../store'
const Counter = () => {
const { dispatch, count } = useStorefy('count')
dispatch('set', 100)
…
}
Tests for store can be written in this way:
it('creates users', () => {
let addUserResolve
jest.spyOn(api, 'addUser').mockImplementation(() => new Promise(resolve => {
addUserResolve = resolve
}))
let store = Storefy([usersModule])
store.dispatch('users/add', { name: 'User' })
expect(api.addUser).toHaveBeenCalledWith({ name: 'User' })
expect(store.get().users).toEqual([])
addUserResolve()
expect(store.get().users).toEqual([{ name: 'User' }])
})
We recommend to keep business logic away from the components. In this case, UI kit (special page with all your components in all states) will be the best way to test components.
For instance, with UIBook you can mock store and show notification
on any dispatch
call.
FAQs
A tiny event-based Redux-like state manager for JavaScript.
The npm package storefy receives a total of 0 weekly downloads. As such, storefy popularity was classified as not popular.
We found that storefy demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.