├── .babelrc ├── .github └── FUNDING.yml ├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── src └── middleware.js └── test └── middleware.spec.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "stage": 1 3 | } 4 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ['buunguyen'] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | lib 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | test 3 | .babelrc 4 | .travis.yml 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "iojs" 5 | - "4.0" 6 | - "4.1" 7 | - "4.2" 8 | - "5.0" 9 | - "5.1" 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Buu Nguyen 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. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## redux-validate-fsa 2 | 3 | [![NPM](https://nodei.co/npm/redux-validate-fsa.png?compact=true)](https://www.npmjs.com/package/redux-validate-fsa) 4 | 5 | [![Build Status](https://travis-ci.org/buunguyen/redux-validate-fsa.svg?branch=master)](https://travis-ci.org/buunguyen/redux-validate-fsa) 6 | 7 | Redux middleware that validates if an action is a [Flux Standard Action](https://github.com/acdlite/flux-standard-action) (FSA). 8 | 9 | ### Usage 10 | 11 | ```js 12 | const middleware = [reduxThunk] 13 | 14 | // Only use in DEV mode 15 | if (__DEV__) { 16 | const fsaMiddleware = require('redux-validate-fsa')(ignore /* optional */) 17 | middleware.push(fsaMiddleware) 18 | } 19 | ``` 20 | 21 | The `ignore` argument specify actions that should be skipped from the FSA check. This is useful when dealing with non-compliant actions from third-party libraries. 22 | 23 | * If an array is given, it is the action types that should be skipped from the FSA check. 24 | * If a function is given, it must return true for actions that should be skipped from the FSA check. 25 | 26 | For example, if you use [react-router-redux](https://github.com/reactjs/react-router-redux), you should ignore its update-path actions, which are not FSA compliant. 27 | 28 | ```js 29 | import {LOCATION_CHANGE} from 'react-router-redux' 30 | const fsaMiddleware = require('redux-validate-fsa')([LOCATION_CHANGE]) 31 | ``` 32 | 33 | Notes: 34 | * This middleware is only useful in dev mode. Therefore, it should be conditionally imported. 35 | * If you use [redux-thunk](https://github.com/gaearon/redux-thunk), make sure the thunk middleware is added before this middleware. Alternatively, you can use the `ignore` predicate to filter out actions that are thunks. 36 | 37 | ### Test 38 | 39 | ```bash 40 | npm install 41 | npm test 42 | ``` 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "redux-validate-fsa", 3 | "version": "0.1.2", 4 | "description": "Redux middleware that validates if an action is a Flux Standard Action (FSA)", 5 | "main": "lib/middleware.js", 6 | "scripts": { 7 | "prepublish": "rimraf lib && babel src --out-dir lib", 8 | "test": "mocha --compilers js:babel/register 'test/*.js'" 9 | }, 10 | "repository": { 11 | "type": "git", 12 | "url": "git+https://github.com/buunguyen/redux-validate-fsa.git" 13 | }, 14 | "keywords": [ 15 | "react", 16 | "redux", 17 | "flux", 18 | "action", 19 | "fsa", 20 | "flux-standard-action", 21 | "middleware", 22 | "redux-middleware" 23 | ], 24 | "author": "Buu Nguyen (https://github.com/buunguyen)", 25 | "license": "MIT", 26 | "bugs": { 27 | "url": "https://github.com/buunguyen/redux-validate-fsa/issues" 28 | }, 29 | "homepage": "https://github.com/buunguyen/redux-validate-fsa#readme", 30 | "devDependencies": { 31 | "babel": "^5.8.34", 32 | "chai": "^3.4.1", 33 | "mocha": "^2.3.4", 34 | "rimraf": "^2.4.4", 35 | "sinon": "^1.17.2" 36 | }, 37 | "dependencies": { 38 | "flux-standard-action": "^0.6.0" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/middleware.js: -------------------------------------------------------------------------------- 1 | import {isFSA} from 'flux-standard-action' 2 | 3 | /** 4 | * Creates a Redux middleware that validates if supplied actions are FSA-compliant. 5 | * 6 | * @param {Array|Function} ignore (optional) - an array of action types 7 | * that should be skipped from the FSA check or a predicate of type (Action) => bool 8 | * that should return true for actions that should be skipped from the FSA check. 9 | * 10 | * @returns {Function} A Redux middleware. 11 | */ 12 | export default function fsa(ignore) { 13 | const _ignore = (function () { 14 | if (!ignore) return () => false 15 | if (typeof ignore === 'function') return ignore 16 | if (Array.isArray(ignore)) return (action) => action && ~ignore.indexOf(action.type) 17 | throw new Error(`'ignore' must be an array or function`) 18 | })() 19 | 20 | return store => next => action => { 21 | if (_ignore(action) || (action && isFSA(action))) { 22 | return next(action) 23 | } 24 | throw new Error(`'action' must be an object and FSA compliant`) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /test/middleware.spec.js: -------------------------------------------------------------------------------- 1 | import {assert} from 'chai' 2 | import sinon from 'sinon' 3 | import fsa from '../src/middleware' 4 | 5 | describe('fsa', () => { 6 | const dispatch = () => {} 7 | const getState = () => {} 8 | 9 | describe('handle action', () => { 10 | const executeMiddleware = fsa()({dispatch, getState}) 11 | 12 | it('should throw if action is undefined', () => { 13 | const next = (action) => {} 14 | assert.throws(() => { 15 | executeMiddleware(next)(undefined) 16 | }, Error, `'action' must be an object and FSA compliant`) 17 | }) 18 | 19 | it('should throw if action is not an FSA', () => { 20 | const next = (action) => {} 21 | assert.throws(() => { 22 | executeMiddleware(next)({type: 'action', nonFsaProperty: 0}) 23 | }, Error, `'action' must be an object and FSA compliant`) 24 | }) 25 | 26 | it('should call next and return its result if action is an FSA', () => { 27 | const action = {type: 'fsa'} 28 | const next = sinon.stub() 29 | next.withArgs(action).returns(42) 30 | 31 | const result = executeMiddleware(next)(action) 32 | assert.equal(result, 42) 33 | }) 34 | }) 35 | 36 | describe('handle ignore', () => { 37 | it('should skip FSA check if an array whitelists the action type', () => { 38 | const whitelist = ['ignore_me'] 39 | const executeMiddleware = fsa(whitelist)({dispatch, getState}) 40 | const next = sinon.spy() 41 | executeMiddleware(next)({type: 'ignore_me', nonFsaProperty: 0}) 42 | assert.isTrue(next.calledOnce) 43 | }) 44 | 45 | it('should skip FSA check if a predicate filters the action', () => { 46 | const predicate = (action) => action.type === 'ignore_me' 47 | const executeMiddleware = fsa(predicate)({dispatch, getState}) 48 | const next = sinon.spy() 49 | executeMiddleware(next)({type: 'ignore_me', nonFsaProperty: 0}) 50 | assert.isTrue(next.calledOnce) 51 | }) 52 | 53 | it('should skip FSA check if a predicate filters undefined action', () => { 54 | const predicate = (action) => action === undefined 55 | const executeMiddleware = fsa(predicate)({dispatch, getState}) 56 | const next = sinon.spy() 57 | executeMiddleware(next)(undefined) 58 | assert.isTrue(next.calledOnce) 59 | }) 60 | }) 61 | }) 62 | --------------------------------------------------------------------------------