├── .travis.yml ├── .gitignore ├── index.js ├── test.js ├── LICENSE ├── package.json └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | node_js: 2 | - "2" 3 | - "3" 4 | sudo: false 5 | language: node_js 6 | script: "npm run test:cov" 7 | after_script: "npm i -g codecov.io && cat ./coverage/lcov.info | codecov" 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # tmp files 2 | lib-cov 3 | *.seed 4 | *.log 5 | *.csv 6 | *.dat 7 | *.out 8 | *.pid 9 | *.gz 10 | 11 | # tmp folders 12 | pids/ 13 | logs/ 14 | results/ 15 | coverage/ 16 | 17 | # node.js 18 | node_modules/ 19 | npm-debug.log 20 | 21 | # osx 22 | .DS_Store 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const varhash = require('observ-varhash') 2 | const struct = require('observ-struct') 3 | const array = require('observ-array') 4 | const assert = require('assert') 5 | const value = require('observ') 6 | const xtend = require('xtend') 7 | 8 | stateAtom.varhash = varhash 9 | stateAtom.struct = struct 10 | stateAtom.array = array 11 | stateAtom.value = value 12 | 13 | module.exports = stateAtom 14 | 15 | // create a state atom 16 | // obj -> obj 17 | function stateAtom (obj) { 18 | assert.equal(typeof obj, 'object') 19 | const copy = xtend(obj) 20 | return struct(copy) 21 | } 22 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const atom = require('./') 3 | 4 | test('should assert input types', function (t) { 5 | t.plan(1) 6 | t.throws(atom, 'object') 7 | }) 8 | 9 | test('should create state from an object', function (t) { 10 | t.plan(2) 11 | const state = atom({ 12 | foo: 'bar' 13 | }) 14 | t.equal(typeof state, 'function') 15 | t.equal(state.foo, 'bar') 16 | }) 17 | 18 | test('should create updateable collections', function (t) { 19 | t.plan(2) 20 | const state = atom({ 21 | todos: atom.array([todo('foo'), todo('bar')]) 22 | }) 23 | t.equal(state.todos.get(0)(), 'foo') 24 | 25 | state.todos.push(todo('baz')) 26 | t.equal(state.todos.get(2)(), 'baz') 27 | 28 | function todo (title) { 29 | return atom.value(title) 30 | } 31 | }) 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "state-atom", 3 | "version": "2.1.1", 4 | "description": "Create an immutable state atom", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "standard && NODE_ENV=test node test", 8 | "test:cov": "standard && NODE_ENV=test istanbul cover test.js" 9 | }, 10 | "repository": "yoshuawuyts/state-atom", 11 | "keywords": [ 12 | "immutable", 13 | "state", 14 | "atom", 15 | "observ", 16 | "application", 17 | "flux", 18 | "react", 19 | "reactive", 20 | "frp", 21 | "functional", 22 | "container", 23 | "core", 24 | "heart", 25 | "data", 26 | "sync", 27 | "time travel", 28 | "hot reloading", 29 | "micro framework" 30 | ], 31 | "license": "MIT", 32 | "dependencies": { 33 | "observ": "^0.2.0", 34 | "observ-array": "^3.2.1", 35 | "observ-struct": "^6.0.0", 36 | "observ-varhash": "^1.0.6", 37 | "xtend": "^4.0.0" 38 | }, 39 | "devDependencies": { 40 | "istanbul": "^0.3.17", 41 | "standard": "^4.5.4", 42 | "tape": "^4.0.1" 43 | }, 44 | "files": [ 45 | "LICENSE", 46 | "README.md", 47 | "index.js" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # state-atom 2 | [![NPM version][npm-image]][npm-url] 3 | [![build status][travis-image]][travis-url] 4 | [![Test coverage][codecov-image]][codecov-url] 5 | [![Downloads][downloads-image]][downloads-url] 6 | [![js-standard-style][standard-image]][standard-url] 7 | 8 | Create an immutable state atom. Forms the basis for hot reloading, infinite 9 | undo / redo (time-travel) and more. 10 | 11 | ## Installation 12 | ```sh 13 | $ npm install state-atom 14 | ``` 15 | 16 | ## Usage 17 | ```js 18 | const atom = require('state-atom') 19 | 20 | const array = atom.array 21 | const value = atom.value 22 | 23 | const state = atom({ 24 | foo: array([ value('bin'), value('baz') ]), 25 | bar: array([ value('beep'), value('boop') ]) 26 | }) 27 | 28 | state((curr) => { 29 | console.log(curr.foo) 30 | // => [ 'bin', 'baz' ] 31 | console.log(curr.bar) 32 | // => [ 'beep', 'boop' ] 33 | }) 34 | ``` 35 | 36 | ## API 37 | ### state = atom(obj) 38 | Create a new immutable state atom from an object. 39 | 40 | ### state(cb(curr)) 41 | Register a handler function that is called whenever state changes. 42 | 43 | ### struct = atom.struct 44 | Access [`observ-struct`](https://github.com/Raynos/observ-struct). 45 | 46 | ### array = atom.array 47 | Access [`observ-array`](https://github.com/Raynos/observ-array). 48 | 49 | ### array = atom.varhash 50 | Access [`observ-varhash`](https://github.com/Raynos/observ-varhash). 51 | 52 | ### value = atom.value 53 | Access [`observ`](https://github.com/Raynos/observ). 54 | 55 | ## FAQ 56 | ### What is a "state atom"? 57 | A state atom holds all the state of your application, generally implemented as 58 | an object that holds several arrays. Think of it as the in-memory database of 59 | your application from where your ui components can query data. 60 | 61 | ### Why did you write this module? 62 | I've always been annoyed by managing state; when refactoring it's a great 63 | slowdown. By having all state live in a single location you can do interesting 64 | things such as persist application state to local storage, dump full 65 | application state to debug, hot reload code and more! 66 | 67 | ### How does this module reduce application complexity? 68 | Backend applications have both persistant state (database) and application 69 | state (memory). `state-atom` takes this analogy and applies it to the 70 | frontend. Changes saved to the atom are immutable, returning mutable copies 71 | when read. Only when actively persisting the state back to the atom will 72 | listeners fire and changes propagate throughout the application. 73 | 74 | ## Thanks 75 | Shout out to [Raynos](https://github.com/raynos) for creating Mercury and its 76 | dependencies of which this package makes heavy use. The original version of 77 | this package was extracted from Mercury. 78 | 79 | ## See Also 80 | - [barracks](https://github.com/yoshuawuyts/barracks) - action dispatcher for unidirectional data flows 81 | - [wayfarer](https://github.com/yoshuawuyts/wayfarer) - composable trie based router 82 | - [vel](https://github.com/yoshuawuyts/vel) - create and render virtual-dom elements 83 | 84 | ## License 85 | [MIT](https://tldrlegal.com/license/mit-license) 86 | 87 | [npm-image]: https://img.shields.io/npm/v/state-atom.svg?style=flat-square 88 | [npm-url]: https://npmjs.org/package/state-atom 89 | [travis-image]: https://img.shields.io/travis/yoshuawuyts/state-atom/master.svg?style=flat-square 90 | [travis-url]: https://travis-ci.org/yoshuawuyts/state-atom 91 | [codecov-image]: https://img.shields.io/codecov/c/github/yoshuawuyts/state-atom/master.svg?style=flat-square 92 | [codecov-url]: https://codecov.io/github/yoshuawuyts/state-atom 93 | [downloads-image]: http://img.shields.io/npm/dm/state-atom.svg?style=flat-square 94 | [downloads-url]: https://npmjs.org/package/state-atom 95 | [standard-image]: https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square 96 | [standard-url]: https://github.com/feross/standard 97 | --------------------------------------------------------------------------------