deox
Version:
Functional Type-safe Flux Standard Utilities
158 lines (148 loc) • 5.46 kB
JavaScript
export { configureStore } from 'redux-starter-kit';
import { __assign } from 'tslib';
import { filter } from 'rxjs/operators';
/**
* Flux standard action factory
* @example
* const clearTodos = action('[Todo] truncate');
* @example
* const fetchTodosRejected = (payload: Error) => action('[Todo] fetch rejected', payload);
* @example
* const addTodo = ({ name, completed = false }: Todo) => action('[Todo] add', { name, completed });
* @example
* const fetchTodosRejected = (payload: Error, meta?: Meta) => action('[Todo] fetch rejected', payload, meta);
* @example
* const addTodo = ({ name, completed = false }: Todo, meta?: Meta) => action('[Todo] add', { name, completed }, meta);
*/
function createAction(type, payload, meta) {
return __assign({ type: type }, (payload !== undefined ? { payload: payload } : {}), (meta !== undefined ? { meta: meta } : {}), (payload instanceof Error ? { error: true } : {}));
}
function createActionCreator(type, executor) {
if (executor === void 0) { executor = function (resolve) { return (function () { return resolve(); }); }; }
var callable = executor(function (payload, meta) {
return createAction(type, payload, meta);
});
return Object.assign(callable, {
type: type,
toString: function () {
return type;
},
});
}
/**
* Map action creator to it's contained action type
* @description it gets an object with at least a type property or overridden toString method and returns it.
* @example
* const increment = createActionCreator('[Counter] increment')
* getType(increment) //=> '[Counter] increment'
* @example
* getType({ type: 'TEST' }) //=> 'TEST'
* @example
* getType({
* toString() { return 'TEST' }
* }) //=> 'TEST'
*/
function getType(actionCreator) {
if (!actionCreator.type && !actionCreator.hasOwnProperty('toString')) {
throw new Error("Action creator that has been passed to getType() does not provide any API to expose action type. You can use createAction() to create an action creator without any unsense errors.");
}
return (actionCreator.type || actionCreator.toString());
}
var othersHandlerKey = Symbol('others');
/**
* Handler map factory
* @description create an action(s) to reducer map
* @example
* createHandlerMap(increment, (state: number) => state + 1)
* @example
* createHandlerMap([increment, increase], (state: number) => state + 1)
* @example
* createHandlerMap.others((state: number) => state + 1)
*/
function createHandlerMap(actionCreators, handler) {
return (Array.isArray(actionCreators) ? actionCreators : [actionCreators])
.map(getType)
.reduce(function (acc, type) {
acc[type] = handler;
return acc;
}, {});
}
createHandlerMap.others = createOthersHandlerMap;
function createOthersHandlerMap(handler) {
var _a;
return _a = {}, _a[othersHandlerKey] = handler, _a;
}
var merge = function () {
var objs = [];
for (var _i = 0; _i < arguments.length; _i++) {
objs[_i] = arguments[_i];
}
return Object.assign.apply(Object, [{}].concat(objs));
};
function castArray(value) {
return [].concat(value);
}
/**
* Reducer factory
* @description combines multiple handler map into single reducer
* @example
* const counter = createReducer(0, handleAction => [
* handleAction(increment, state => state + 1),
* handleAction(decrement, state => state - 1),
* ])
*/
function createReducer(defaultState, handlerMapsCreator) {
var handlerMap = merge.apply(void 0, handlerMapsCreator(createHandlerMap));
return function (state, action) {
if (state === void 0) { state = defaultState; }
var handler = handlerMap[action.type] || handlerMap[othersHandlerKey];
return handler ? handler(state, action) : state;
};
}
/**
* Action type assertion helper
* @description Check if action type property is equal given action(s) type property or action creator(s) type property or action type(s).
* @example
* const increment = createActionCreator('[Counter] increment')
* const decrement = createActionCreator('[Counter] decrement')
* const reset = createActionCreator(
* '[Counter] reset',
* resolve => (value: number) => resolve(value)
* )
* isOfType([decrement(), reset(0)], increment()) //=> false
* @example
* isOfType(reset, increment()) //=> false
* @example
* isOfType([reset, increment], increment()) //=> true
* @example
* isOfType(['[Counter] increment', decrement(), reset], increment()) //=> true
*/
function isOfType(keys, action) {
var types = castArray(keys).map(function (key) { return (typeof key === 'string' ? key : getType(key)); });
var assertFn = function (action) { return types.includes(action.type); };
// 1 arg case => return curried version
if (action === undefined) {
return assertFn;
}
// 2 args case => invoke assertFn and return the result
return assertFn(action);
}
/**
* Filter actions emitted by the source Observable by only emitting those that
* are compatible with specified action(s) or action creator(s) or action type(s).
*
* @example
* action$.pipe(
* ofType(foo),
* ...
* )
* @example
* action$.pipe(
* ofType([foo, bar]),
* ...
* )
*/
function ofType(keys) {
return filter(isOfType(keys));
}
export { createAction as action, createActionCreator as createAction, createActionCreator, createReducer, getType, isOfType, ofType };