Comparing version 0.9.8 to 1.0.0
# Change Log | ||
This project adheres to [Semantic Versioning](http://semver.org/). | ||
## 1.0 | ||
* Add ES modules support. | ||
* Use ES2016 syntax. | ||
* Use universal `devtools/logger` for Node and bundlers. | ||
* Reduce size. | ||
## 0.9.8 | ||
@@ -5,0 +11,0 @@ * Fix TypeScript definitions (by @majo44). |
@@ -1,8 +0,8 @@ | ||
import {Store} from '..'; | ||
import { Store } from '..' | ||
declare const devtools: { | ||
<State>(store: Store<State>): void; | ||
(options?: object): <State>(store: Store<State>) => void; | ||
}; | ||
<State>(store: Store<State>): void | ||
(options?: object): <State>(store: Store<State>) => void | ||
} | ||
export = devtools; | ||
export = devtools |
@@ -1,20 +0,6 @@ | ||
/** | ||
* Create Redux DevTools module for Storeon. | ||
* | ||
* @param {object} options Redux DevTools option. | ||
* @returns {devtools} Redux DevTools module | ||
* | ||
* @example | ||
* const store = createStore([ | ||
* process.env.NODE_ENV !== 'production' && require('storeon/devtools')() | ||
* ]) | ||
* | ||
* @name devtools | ||
* @function | ||
*/ | ||
function devtools (options) { | ||
var isStore = options && options.on && options.dispatch && options.get | ||
export default options => { | ||
let isStore = options && options.on && options.dispatch && options.get | ||
function module (store) { | ||
var extension | ||
let module = store => { | ||
let extension | ||
try { | ||
@@ -34,5 +20,5 @@ extension = window.__REDUX_DEVTOOLS_EXTENSION__ || | ||
var ReduxTool = extension.connect(isStore ? {} : options) | ||
store.on('@init', function () { | ||
ReduxTool.subscribe(function (message) { | ||
let ReduxTool = extension.connect(isStore ? { } : options) | ||
store.on('@init', () => { | ||
ReduxTool.subscribe(message => { | ||
if (message.type === 'DISPATCH' && message.state) { | ||
@@ -45,5 +31,5 @@ store.dispatch('UPDATE_FROM_DEVTOOLS', JSON.parse(message.state)) | ||
var prev = '' | ||
store.on('@dispatch', function (state, data) { | ||
var event = String(data[0]) | ||
let prev = '' | ||
store.on('@dispatch', (state, data) => { | ||
let event = String(data[0]) | ||
if (event !== 'UPDATE_FROM_DEVTOOLS' && prev !== 'UPDATE_FROM_DEVTOOLS') { | ||
@@ -60,5 +46,5 @@ if (event[0] !== '@' && (!data[2] || data[2].length === 0)) { | ||
store.on('UPDATE_FROM_DEVTOOLS', function (state, data) { | ||
var newState = {} | ||
var key | ||
store.on('UPDATE_FROM_DEVTOOLS', (state, data) => { | ||
let newState = { } | ||
let key | ||
for (key in state) { | ||
@@ -76,9 +62,1 @@ newState[key] = undefined | ||
} | ||
module.exports = devtools | ||
/** | ||
* @callback devtools | ||
* @param {Store} store The store. | ||
* @returns {devtools} | ||
*/ |
type DataTypes<Map, Key extends keyof Map> = | ||
Map extends never ? [any?] : (Map[Key] extends (never | undefined) ? [never?] : [Map[Key]]) | ||
Map extends never | ||
? [any?] | ||
: (Map[Key] extends (never | undefined) ? [never?] : [Map[Key]]) | ||
declare namespace createStore { | ||
export type Dispatch<Events> = (<Event extends keyof Events>( | ||
event: Event, ...data: DataTypes<Partial<Events>, Event>) => void) & {___events: Events}; | ||
event: Event, ...data: DataTypes<Partial<Events>, Event> | ||
) => void) & {___events: Events} | ||
export type DispatchEvent<State, Events, Event extends keyof Events = keyof Events> = | ||
[Event, Events[Event], Array<StoreonEventHandler<State, Events, Event>>]; | ||
export type DispatchEvent< | ||
State, Events, Event extends keyof Events = keyof Events | ||
> = [Event, Events[Event], Array<StoreonEventHandler<State, Events, Event>>] | ||
export type StoreonEventHandler<State, Events, Event extends keyof (Events & StoreonEvents<State, Events>)> = | ||
(state: Readonly<State>, data: (Events & StoreonEvents<State, Events>)[Event]) => Partial<State> | Promise<void> | null | void; | ||
export type StoreonEventHandler< | ||
State, Events, Event extends keyof (Events & StoreonEvents<State, Events>) | ||
> = ( | ||
state: Readonly<State>, | ||
data: (Events & StoreonEvents<State, Events>)[Event] | ||
) => Partial<State> | Promise<void> | null | void | ||
export interface Store<State = unknown, Events = any> { | ||
readonly on: <Event extends keyof (Events & StoreonEvents<State, Events>)>( | ||
/** | ||
* Store with application state and event listeners. | ||
*/ | ||
export class Store<State = unknown, Events = any> { | ||
/** | ||
* Add event listener. | ||
* | ||
* @param event The event name. | ||
* @param handler The event listener. | ||
* @returns The function to remove listener. | ||
*/ | ||
on <Event extends keyof (Events & StoreonEvents<State, Events>)>( | ||
event: Event, | ||
handler: StoreonEventHandler<State, Events, Event> | ||
) => () => void; | ||
readonly dispatch: Dispatch<Events & DispatchableStoreonEvents<State>>; | ||
readonly get: () => State; | ||
): () => void | ||
/** | ||
* Return current state. You can use this method only to read state. | ||
* Any state changes should be in event listeners. | ||
* | ||
* @returns The current state. | ||
*/ | ||
get(): State | ||
/** | ||
* Emit event. | ||
* | ||
* @param event The event name. | ||
* @param data Any additional data for the event. | ||
* @returns The current state. | ||
*/ | ||
dispatch: Dispatch<Events & DispatchableEvents<State>> | ||
} | ||
export type Module<State, Events = any> = (store: Store<State, Events>) => void; | ||
export type Module<State, Events = any> = | ||
(store: Store<State, Events>) => void | ||
export interface DispatchableStoreonEvents<State> { | ||
'@init': never; | ||
'@changed': State; | ||
export interface DispatchableEvents<State> { | ||
'@init': never | ||
'@changed': State | ||
} | ||
export interface StoreonEvents<State, Events = any> extends DispatchableStoreonEvents<State>{ | ||
'@dispatch': DispatchEvent<State, Events & DispatchableStoreonEvents<State>>; | ||
export interface StoreonEvents<State, Events = any> extends DispatchableEvents<State>{ | ||
'@dispatch': DispatchEvent<State, Events & DispatchableEvents<State>> | ||
} | ||
} | ||
/** | ||
* Initialize new store and apply all modules to the store. | ||
* | ||
* ```js | ||
* import createStore from 'storeon' | ||
* let increment = store => { | ||
* store.on('@init', () => ({ count: 0 })) | ||
* store.on('inc', ({ count }) => ({ count: count + 1 })) | ||
* } | ||
* const store = createStore([increment]) | ||
* store.get().count //=> 0 | ||
* store.dispatch('inc') | ||
* store.get().count //=> 1 | ||
* ``` | ||
* | ||
* @param modules Functions which will set initial state define reducer | ||
* and subscribe to all system events. | ||
* @returns The new store. | ||
*/ | ||
declare function createStore<State, Events = any>( | ||
modules: Array<createStore.Module<State, Events> | false> | ||
): createStore.Store<State, Events>; | ||
modules: Array<createStore.Module<State, Events> | false> | ||
): createStore.Store<State, Events> | ||
export = createStore; | ||
export = createStore |
139
index.js
@@ -1,121 +0,42 @@ | ||
/** | ||
* Initialize new store and apply all modules to the store. | ||
* | ||
* @param {moduleInitializer[]} modules Functions which will set initial state | ||
* define reducer and subscribe | ||
* to all system events. | ||
* | ||
* @return {Store} The new store. | ||
* | ||
* @example | ||
* import createStore from 'storeon' | ||
* let increment = store => { | ||
* store.on('@init', () => ({ count: 0 })) | ||
* store.on('inc', ({ count }) => ({ count: count + 1 })) | ||
* } | ||
* const store = createStore([increment]) | ||
* store.get().count //=> 0 | ||
* store.dispatch('inc') | ||
* store.get().count //=> 1 | ||
*/ | ||
var createStore = function (modules) { | ||
var events = { } | ||
var state = { } | ||
export default modules => { | ||
let events = { } | ||
let state = { } | ||
var on = function (event, cb) { | ||
(events[event] || (events[event] = [])).push(cb) | ||
let store = { | ||
dispatch (event, data) { | ||
if (event !== '@dispatch') { | ||
store.dispatch('@dispatch', [event, data, events[event]]) | ||
} | ||
return function () { | ||
events[event] = events[event].filter(function (i) { | ||
return i !== cb | ||
}) | ||
} | ||
} | ||
if (events[event]) { | ||
let changes = { } | ||
let changed | ||
events[event].forEach(i => { | ||
let diff = i(state, data) | ||
if (diff && typeof diff.then !== 'function') { | ||
changed = state = { ...state, ...diff } | ||
changes = { ...changes, ...diff } | ||
} | ||
}) | ||
if (changed) store.dispatch('@changed', changes) | ||
} | ||
}, | ||
var dispatch = function (event, data) { | ||
if (event !== '@dispatch') { | ||
dispatch('@dispatch', [event, data, events[event]]) | ||
} | ||
get: () => state, | ||
if (events[event]) { | ||
var changes = { } | ||
var changed | ||
events[event].forEach(function (i) { | ||
var diff = i(state, data) | ||
if (diff && typeof diff.then !== 'function') { | ||
changed = state = Object.assign({ }, state, diff) | ||
Object.assign(changes, diff) | ||
} | ||
}) | ||
if (changed) dispatch('@changed', changes) | ||
on (event, cb) { | ||
(events[event] || (events[event] = [])).push(cb) | ||
return () => { | ||
events[event] = events[event].filter(i => i !== cb) | ||
} | ||
} | ||
} | ||
var get = function () { | ||
return state | ||
} | ||
var store = { dispatch: dispatch, get: get, on: on } | ||
modules.forEach(function (i) { | ||
modules.forEach(i => { | ||
if (i) i(store) | ||
}) | ||
dispatch('@init') | ||
store.dispatch('@init') | ||
return store | ||
} | ||
module.exports = createStore | ||
/** | ||
* Store with application state and event listeners. | ||
* | ||
* @name Store | ||
* @class | ||
*/ | ||
/** | ||
* Return current state. You can use this method only to read state. | ||
* Any state changes should be in event listeners. | ||
* | ||
* @return {object} The current state. | ||
* | ||
* @name get | ||
* @function | ||
* @memberof Store# | ||
*/ | ||
/** | ||
* Emit event. | ||
* | ||
* @param {string} event The event name. | ||
* @param {*} [data] Any additional data for the event. | ||
* | ||
* @return {object} The current state. | ||
* | ||
* @name dispatch | ||
* @function | ||
* @memberof Store# | ||
*/ | ||
/** | ||
* Add event listener. | ||
* | ||
* @param {string} event The event name. | ||
* @param {listener} listener The event listener. | ||
* | ||
* @return {function} The function to remove listener. | ||
* | ||
* @name on | ||
* @function | ||
* @memberof Store# | ||
*/ | ||
/** | ||
* @callback moduleInitializer | ||
* @param {Store} store Store to define initial state and reducers. | ||
*/ | ||
/** | ||
* @callback listener | ||
* @param {object} state The current state of the store. | ||
* @param {*} [data] The event data if it was passed. | ||
* @return {object|undefined} Changes for next state. | ||
*/ |
{ | ||
"name": "storeon", | ||
"version": "0.9.8", | ||
"version": "1.0.0", | ||
"description": "Tiny (175 bytes) event-based Redux-like state manager for React and Preact", | ||
@@ -15,13 +15,11 @@ "keywords": [ | ||
"repository": "storeon/storeon", | ||
"browser": { | ||
"./devtools/logger.js": "./devtools/logger.browser.js" | ||
"engines": { | ||
"node": ">=8.3.0" | ||
}, | ||
"eslintIgnore": [ | ||
"node_modules", | ||
"test/demo/dist" | ||
], | ||
"sharec": { | ||
"config": "@logux/sharec-config", | ||
"version": "0.5.6" | ||
} | ||
} | ||
"type": "module", | ||
"main": "index.cjs", | ||
"module": "index.js" | ||
} |
@@ -1,13 +0,30 @@ | ||
import { Dispatch } from ".."; | ||
import { Dispatch } from '..' | ||
declare namespace useStoreon { | ||
export type StoreData<State extends object = {}, EventsDataTypesMap = any> = { | ||
dispatch: Dispatch<EventsDataTypesMap>; | ||
export type StoreData<State extends object = {}, EventsMap = any> = { | ||
dispatch: Dispatch<EventsMap> | ||
} & State | ||
} | ||
declare function useStoreon<State extends object = {}, EventsDataTypesMap = any>( | ||
/** | ||
* Hook to use Storeon in functional React component. | ||
* | ||
* ```js | ||
* import useStoreon from 'storeon/preact' | ||
* const Counter = () => { | ||
* const { dispatch, count } = useStoreon('count') | ||
* return <div> | ||
* {count} | ||
* <button onClick={() => dispatch('inc')} | ||
* </div> | ||
* } | ||
* ``` | ||
* | ||
* @param keys List of state’s field. | ||
* @returns The selected part of the state. | ||
*/ | ||
declare function useStoreon<State extends object = {}, EventsMap = any>( | ||
...keys: (keyof State)[] | ||
): useStoreon.StoreData<State, EventsDataTypesMap>; | ||
): useStoreon.StoreData<State, EventsMap> | ||
export = useStoreon; | ||
export = useStoreon |
@@ -1,12 +0,10 @@ | ||
var hooks = require('preact/hooks') | ||
import hooks from 'preact/hooks' | ||
var StoreContext = require('./context') | ||
import StoreContext from './context/index.js' | ||
var useIsomorphicLayoutEffect = | ||
let useIsomorphicLayoutEffect = | ||
typeof window !== 'undefined' ? hooks.useLayoutEffect : hooks.useEffect | ||
module.exports = function () { | ||
var keys = [].slice.call(arguments) | ||
var store = hooks.useContext(StoreContext) | ||
export default (...keys) => { | ||
let store = hooks.useContext(StoreContext) | ||
if (process.env.NODE_ENV !== 'production' && !store) { | ||
@@ -19,9 +17,7 @@ throw new Error( | ||
var rerender = hooks.useState({ }) | ||
let rerender = hooks.useState({ }) | ||
useIsomorphicLayoutEffect(function () { | ||
return store.on('@changed', function (_, changed) { | ||
var changesInKeys = keys.some(function (key) { | ||
return key in changed | ||
}) | ||
useIsomorphicLayoutEffect(() => { | ||
return store.on('@changed', (_, changed) => { | ||
let changesInKeys = keys.some(key => key in changed) | ||
if (changesInKeys) rerender[1]({ }) | ||
@@ -31,6 +27,6 @@ }) | ||
return hooks.useMemo(function () { | ||
var state = store.get() | ||
var data = { } | ||
keys.forEach(function (key) { | ||
return hooks.useMemo(() => { | ||
let state = store.get() | ||
let data = { } | ||
keys.forEach(key => { | ||
data[key] = state[key] | ||
@@ -37,0 +33,0 @@ }) |
@@ -1,13 +0,30 @@ | ||
import {Dispatch} from '..' | ||
import { Dispatch } from '..' | ||
declare namespace useStoreon { | ||
export type StoreData<State extends object = {}, EventsDataTypesMap = any> = { | ||
dispatch: Dispatch<EventsDataTypesMap>; | ||
export type StoreData<State extends object = {}, EventsMap = any> = { | ||
dispatch: Dispatch<EventsMap> | ||
} & State | ||
} | ||
declare function useStoreon<State extends object = {}, EventsDataTypesMap = any>( | ||
/** | ||
* Hook to use Storeon in functional React component. | ||
* | ||
* ```js | ||
* import useStoreon from 'storeon/react' | ||
* const Counter = () => { | ||
* const { dispatch, count } = useStoreon('count') | ||
* return <div> | ||
* {count} | ||
* <button onClick={() => dispatch('inc')} | ||
* </div> | ||
* } | ||
* ``` | ||
* | ||
* @param keys List of state’s field. | ||
* @returns The selected part of the state. | ||
*/ | ||
declare function useStoreon<State extends object = {}, EventsMap = any>( | ||
...keys: (keyof State)[] | ||
): useStoreon.StoreData<State, EventsDataTypesMap>; | ||
): useStoreon.StoreData<State, EventsMap> | ||
export = useStoreon; | ||
export = useStoreon |
@@ -1,31 +0,10 @@ | ||
var React = require('react') | ||
import React from 'react' | ||
var StoreContext = require('./context') | ||
import StoreContext from './context/index.js' | ||
var useIsomorphicLayoutEffect = | ||
let useIsomorphicLayoutEffect = | ||
typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect | ||
/** | ||
* Hook to use Storeon in functional React component. | ||
* | ||
* @param {...string} fields List of state’s field. | ||
* | ||
* @return {object} The selected part of the state. | ||
* | ||
* @example | ||
* import useStoreon from 'storeon/react' | ||
* const Counter = () => { | ||
* const { dispatch, count } = useStoreon('count') | ||
* return <div> | ||
* {count} | ||
* <button onClick={() => dispatch('inc')} | ||
* </div> | ||
* } | ||
* | ||
* @name useStoreon | ||
*/ | ||
module.exports = function () { | ||
var keys = [].slice.call(arguments) | ||
var store = React.useContext(StoreContext) | ||
export default (...keys) => { | ||
let store = React.useContext(StoreContext) | ||
if (process.env.NODE_ENV !== 'production' && !store) { | ||
@@ -38,9 +17,7 @@ throw new Error( | ||
var rerender = React.useState({ }) | ||
let rerender = React.useState({ }) | ||
useIsomorphicLayoutEffect(function () { | ||
return store.on('@changed', function (_, changed) { | ||
var changesInKeys = keys.some(function (key) { | ||
return key in changed | ||
}) | ||
useIsomorphicLayoutEffect(() => { | ||
return store.on('@changed', (_, changed) => { | ||
let changesInKeys = keys.some(key => key in changed) | ||
if (changesInKeys) rerender[1]({ }) | ||
@@ -50,6 +27,6 @@ }) | ||
return React.useMemo(function () { | ||
var state = store.get() | ||
var data = { } | ||
keys.forEach(function (key) { | ||
return React.useMemo(() => { | ||
let state = store.get() | ||
let data = { } | ||
keys.forEach(key => { | ||
data[key] = state[key] | ||
@@ -56,0 +33,0 @@ }) |
@@ -9,3 +9,3 @@ # Storeon | ||
* **Small.** 175 bytes (minified and gzipped). No dependencies. | ||
* **Small.** 172 bytes (minified and gzipped). No dependencies. | ||
It uses [Size Limit] to control size. | ||
@@ -94,4 +94,5 @@ * **Fast.** It tracks what parts of state were changed and re-renders | ||
If you need to support IE, add [`Object.assign`] polyfill to your bundle. | ||
You should have this polyfill already if you are using React. | ||
If you need to support IE, you need to [compile `node_modules`] with Babel and | ||
add [`Object.assign`] polyfill to your bundle. You should have this polyfill | ||
already if you are using React. | ||
@@ -102,4 +103,6 @@ ```js | ||
[compile `node_modules`]: https://developer.epages.com/blog/coding/how-to-transpile-node-modules-with-babel-and-webpack-in-a-monorepo/ | ||
[`Object.assign`]: https://www.npmjs.com/package/object-assign | ||
## Store | ||
@@ -211,2 +214,3 @@ | ||
## Components | ||
@@ -337,2 +341,14 @@ | ||
## ES Modules | ||
Storeon supports ES modules. You do not need to do anything for bundlers. | ||
For quick hacks you can load Storeon from CDN. Do not use it in production | ||
because of low performance. | ||
```js | ||
import createStore from 'https://cdn.jsdelivr.net/npm/storeon/index.js' | ||
``` | ||
## Testing | ||
@@ -339,0 +355,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
33804
39
638
1
378
Yes
5
1