{"id":19070856,"url":"https://github.com/ohager/nanoflux-fusion","last_synced_at":"2025-04-28T14:47:44.203Z","repository":{"id":57307953,"uuid":"60844087","full_name":"ohager/nanoflux-fusion","owner":"ohager","description":"Redux-like extension for Nanoflux","archived":false,"fork":false,"pushed_at":"2017-02-12T14:35:13.000Z","size":65,"stargazers_count":15,"open_issues_count":4,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-11T19:55:00.698Z","etag":null,"topics":["flux","flux-architecture","nanoflux","redux"],"latest_commit_sha":null,"homepage":"https://ohager.github.io/nanoflux","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ohager.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-06-10T11:50:07.000Z","updated_at":"2018-09-30T21:24:32.000Z","dependencies_parsed_at":"2022-09-09T01:22:40.611Z","dependency_job_id":null,"html_url":"https://github.com/ohager/nanoflux-fusion","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohager%2Fnanoflux-fusion","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohager%2Fnanoflux-fusion/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohager%2Fnanoflux-fusion/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ohager%2Fnanoflux-fusion/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ohager","download_url":"https://codeload.github.com/ohager/nanoflux-fusion/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249436751,"owners_count":21271932,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flux","flux-architecture","nanoflux","redux"],"created_at":"2024-11-09T01:21:02.188Z","updated_at":"2025-04-18T05:31:58.808Z","avatar_url":"https://github.com/ohager.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/ohager/nanoflux-fusion.svg?branch=master)](https://travis-ci.org/ohager/nanoflux-fusion)\r\n[![npm version](https://img.shields.io/npm/v/nanoflux-fusion.svg?style=flat-square)](https://www.npmjs.com/package/nanoflux-fusion)\r\n[![GetBadges Game](http://ohager-nanoflux-fusion.getbadges.io/shield/company/ohager-nanoflux-fusion/user/8773)](https://ohager-nanoflux-fusion.getbadges.io/?ref=shield-player)\r\n\r\n# nanoflux-fusion\r\n\r\n[__PROJECT SITE__](http://ohager.github.io/nanoflux/)\r\n\r\n\u003e Note: Fusion is still in beta, being tested on some real world examples. You may use it already. If you encounter issues feel free to report them! \r\n\r\nA Redux-like extension for Nanoflux.\r\n\r\n*nanoflux-fusion* is built on top of [nanoflux](https://github.com/ohager/nanoflux), a quite efficient Flux implementation,  \r\nand adopts the concept of reducer functions for application state management, making Flux even more comfortable.\r\n\r\n## Install\r\n\r\nEasy peasy lemon squeezy!\r\n\r\n```npm install nanoflux-fusion --save```\r\n\r\n\r\n## Concept\r\n\r\n[Dan Abramov](https://github.com/gaearon) pushed the Flux evolution with [Redux](http://redux.js.org/) and proved that application \r\nstate management is possible with a very minimalistic approach. The idea of using reducer functions to alter the state\r\nis very elegant. *nanoflux-fusion* adopts the idea of reducer functions (called *Fusionators*, because naming is fun) to make Flux even more comfortable.\r\nThat way, it's not necessary to define any Store. The user just focus on *how* he wants to alter the state using\r\nfunctions the simply return the new state.\r\n\r\n### Example\r\n\r\n```javascript\r\n\r\n\tvar NanoFlux = require('nanoflux-fusion');\r\n\t\r\n\t// NanoFlux provides a dedicated store\r\n\tvar fusionStore = NanoFlux.getFusionStore();\r\n\t\r\n\t// subscription is the same as in NanoFlux, note that the function passes a state (which is immutable)\r\n\tvar subscription = fusionStore.subscribe(this, function(state){\r\n\t\t// ... do something with the state\r\n\t\t// state is also available via fusionStore.getState()\r\n\t\tconsole.log(\"Items:\", state.items);\r\n\t});\r\n\t\r\n\t// the 'fusionator' is responsible for the state manipulation\r\n\t// it is called with two arguments, the previous state\r\n\t// and an arguments array containing the arguments passed on actor call.\r\n\tNanoFlux.createFusionator({\r\n\t\t\r\n\t\taddItem : function(previousState, args){\r\n\t\t\t// the previous state is frozen/immutable, so we need to copy the items to update the list\r\n\t\t\tvar currentItems = previousState.items.slice(); \r\n\t\t\tcurrentItems.push(args[0]);\r\n\t\t\t// we don't need to return the entire state. \r\n\t\t\t// A partial state will be merged\r\n\t\t\t// As builtin feature, the new state is being frozen (deeply)\r\n\t\t\treturn { items : currentItems };\r\n\t\t},\r\n\t\tremoveItem : function(previousState, args){\r\n\t\t\tif (previousState.items.length == 0) return {};\r\n\r\n\t\t\t// note: filter creates a copy already \r\n\t\t\tvar items = previousState.items.filter(function (item) {\r\n\t\t\t\treturn item.name !== args[0];\r\n\t\t\t});\r\n\t\t\treturn {items: items}\r\n\t\t}\r\n\t}, \r\n\t// define an initial state!\r\n\t{\r\n\t items: []\t\r\n\t});\r\n\t\r\n\t// gets the fusion actors, i.e. have the same name as defined above\r\n\tvar addItem = NanoFlux.getFusionActor(\"addItem\");\r\n\tvar removeItem = NanoFlux.getFusionActor(\"removeItem\");\r\n\t\r\n\t// use the actors as simple action functions\r\n\taddItem({ name: \"item1\", value : 1 });\r\n\taddItem({ name: \"item2\", value : 2 });\r\n\t\r\n\tremoveItem(\"item1\");\r\n\r\n```\r\n\r\n## Immutability\r\nThe state passed on notification and/or `getState()` is immutable. Internally, [Object.freeze](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze) \r\nis used to (deeply) freeze the state making it immutable. For large nested states this will have an performance impact (tests will be made to quantify). \r\nDue to the use of `Object.freeze` the overall performance is not directly comparable with, e.g. nanoflux (which does not guarantee immutability), but should be still fast enough \r\nfor most scenarios.\r\n\r\n## Asynchronous Actions\r\n\r\n*nanoflux-fusion* supports asynchronous actions out-of-the-box. If a Fusionator returns a promise instead of a state object,\r\nthe promise will be executed. The state shall be passed as argument of the resolver. Chaining is also possible. \r\n*nanoflux-fusion* aims to support all [A+ compliant](https://promisesaplus.com/) implementations. \r\nIt is currently tested with the \r\n\r\n - [native Promise-API](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise/)\r\n - [Q](https://github.com/kriskowal/q/)\r\n - [RSVP](https://github.com/tildeio/rsvp.js/)\r\n - [Bluebird](https://github.com/petkaantonov/bluebird/)\r\n \r\n ### Example\r\n \r\n```javascript\r\n\r\nfunction asyncA(arg1){\r\n\treturn new Promise(function(resolve,reject){\r\n\t\tsetTimeout(function(){\r\n\t\t\t// returns state to be merged\r\n\t\t\tresolve({a: arg1});\r\n\t\t}, 500)\r\n\t})\r\n}\r\n \r\nfunction asyncB(arg1){\r\n\treturn new Promise(function(resolve,reject){\r\n\t\tsetTimeout(function(){\r\n\t\t\tresolve( { b: 5 + arg1.a });\r\n\t\t}, 500)\r\n\t})\r\n}\r\n\r\nvar asyncFusionator = NanoFlux.createFusionator({\r\n\t\r\n\tsimplePromise: function(prevState, args){\r\n\t\t\treturn asyncA(args[0]); \r\n\t},\t\r\n\tchainedPromises: function(prevState, args){\r\n\t\treturn asyncA(args[0]).then(function(data){\r\n\t\t\tconsole.log(data); // data = {a: 5} \r\n\t\t\treturn asyncB(data);  \r\n\t\t});\r\n\t}\r\n}, \r\n// initial state\r\n{\r\n\ta: 0,\r\n\tb: 0\r\n});\r\n\r\nvar simplePromise = NanoFlux.getFusionActor(\"simplePromise\");\r\nvar chainedPromises = NanoFlux.getFusionActor(\"chainedPromises\");\r\n\r\n// call the actions\r\nsimplePromise(5); // state will be { a: 5 }\r\nchainedPromises(5); // state will be { a: 5, b: 10 }\r\n\r\n```\r\n\r\n## Middleware Interface\r\n\r\nSince version 0.5 a middleware interface is provided. Simply pass a function object with signature ``function(newState, oldState, actionName)``\r\nto the ``FusionStore.use``. The middleware function __must__ return a new state, typically the received ``newState`` argument, or a modified version of it (see below). \r\n\r\n\u003e Note: Nanoflux also provides a middleware interface (``Nanoflux.use()``), but that interface applies for the dispatcher. In case of Fusion, the dispatcher middleware\r\n isn't that useful, as it dispatches always to the same method (i.e. _on__fuse()_) with internally maintained arguments.\r\n\r\n```javascript\r\n\r\nfunction LoggerMiddleware(){\r\n\tvar logData = [];\r\n\r\n\tthis.log = function(newState, oldState, actionName){\r\n\t\tlogData.push({\r\n\t\t    action: actionName,\r\n\t\t    timestamp: Date.now(),\r\n\t\t\tstate: _.cloneDeep(oldState)\r\n\t\t});\r\n\r\n\t\treturn newState; // must return a state \r\n\t};\r\n\r\n\tthis.countLogEntries = function(){ return logData.length };\r\n\tthis.getLogEntry = function(t){\r\n\t\treturn logData[t];\r\n\t};\r\n}\r\n\r\nvar fusionStore = NanoFlux.getFusionStore();\r\nvar logger = new LoggerMiddleware();\r\nfusionStore.use( logger.log );\r\n// use more if needed ... \r\n```\r\n\r\n\r\n#### No async support yet\r\n\r\nThe current middleware implementation is purely synchronous. Of course, you could execute async operations, e.g. server requests, but the middleware won't wait for the request being finished.  \r\n\r\n\r\n### New State argument is mutable\r\n\r\nThe middleware function receives two versions of a state, the new state and the prior/old state *before* the new state is being merged into the application state.\r\nWhile the old state is immutable, the new state can be modified. That way, the middleware can be used as transformation pipeline. \r\n\r\n```javascript\r\nfunction addTimestamp(newState, oldState){\r\n\r\n\t\tvar modifiedState = {};\r\n\t\tmodifiedState.modified = Date.now();\r\n\r\n\t\tObject.assign(newState, modifiedState);\r\n\t\treturn newState;\r\n}\r\n\r\nfusionStore.use( addTimestamp );\r\n\r\n```\r\n\r\n## Compatibility\r\n\r\nIt should be compatible with all major modern browsers. *nanoflux-fusion* uses internally `Object.assign()` and `Object.freeze()`. While `Object.freeze` is supported since IE9, `Object.assign` was only introduced in EDGE.\r\nTherefore, you need a [polyfill](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#Polyfill) for unsupported browsers, i.e. IE9+. \r\n \r\n\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fohager%2Fnanoflux-fusion","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fohager%2Fnanoflux-fusion","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fohager%2Fnanoflux-fusion/lists"}