UNPKG

redux-logic

Version:

Redux middleware for organizing all your business logic. Intercept actions and perform async processing.

245 lines (233 loc) 11.7 kB
import "core-js/modules/es.error.cause.js"; import "core-js/modules/es.array.filter.js"; import "core-js/modules/es.array.map.js"; import "core-js/modules/es.function.name.js"; import "core-js/modules/es.object.keys.js"; import "core-js/modules/es.object.to-string.js"; import "core-js/modules/es.regexp.to-string.js"; import "core-js/modules/esnext.iterator.constructor.js"; import "core-js/modules/esnext.iterator.filter.js"; import "core-js/modules/esnext.iterator.for-each.js"; import "core-js/modules/esnext.iterator.map.js"; import "core-js/modules/web.dom-collections.for-each.js"; import { stringifyType } from './utils'; var allowedOptions = ['name', 'type', 'cancelType', 'latest', 'debounce', 'throttle', 'validate', 'transform', 'process', 'processOptions', 'warnTimeout']; var allowedProcessOptions = ['dispatchReturn', 'dispatchMultiple', 'successType', 'failType']; var NODE_ENV = typeof window === 'undefined' && process && process.env && process.env.NODE_ENV ? process.env.NODE_ENV : ''; var defaultOptions = { warnTimeout: 60000, latest: false, debounce: 0, throttle: 0 }; var globallyConfigurableOptions = ['warnTimeout']; /** Configure the default `createLogic` options. Note that changing these values will not affect `Logic` instances that have already been instantiated. @param {object} options object defining default values to be used when creating `Logic` instances. The following options may be set globally: - warnTimeout See the `createLogic` API documentation for a description of these options. @returns {undefined} @example ``` import { configureLogic, createLogic } from 'redux-logic'; configureLogic({ warnTimeout: 10000 }) // These will both timeout after 10 seconds instead of the library default of // 1 minute. const logicOne = createLogic({ type: 'ACTION_ONE', }) const logicTwo = createLogic({ type: 'ACTION_TWO', }) ``` */ export var configureLogic = function configureLogic() { var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var invalidOptions = getInvalidOptions(options, globallyConfigurableOptions); if (invalidOptions.length) { throw new Error("".concat(invalidOptions, " are not globally configurable options.")); } Object.keys(options).forEach(function (option) { defaultOptions[option] = options[option]; }); }; /** Validate and augment logic object to be used in logicMiddleware. The returned object has the same structure as the supplied logicOptions argument but it will have been validated and defaults will be applied @param {object} logicOptions object defining logic operation @param {string} logicOptions.name optional string name, defaults to generated name from type and idx @param {string | regex | function | array} logicOptions.type action type(s) that this logic is used for. A string '*' indicates that it applies to all types, otherwise strings are used for exact match. A regex can also be used to match. If a function is supplied like a redux-actions action function, then it will use call its toString() method to get the associated action type. An array of any of these can be supplied to extend match to more types. @param {string | regex | function | array} logicOptions.cancelType action type(s) that will cause a cancellation. String, regex, fn, array are used similar to how the logicOptions.type works. Cancellation will automatically prevent dispatches from being used regardless of when the original logic finishes. Additionally a cancellation$ observable is available to logic to detect cancellation and perform any manual cleanup. @param {boolean} logicOptions.latest enables takeLatest which cancels previous when a newer one comes in, default false @param {number} logicOptions.debounce milliseconds to perform debouncing, cannot be used with latest, default 0 (disabled) @param {number} logicOptions.throttle milliseconds to perform throttling, cannot be used with latest, default 0 (disabled) @param {function} logicOptions.validate hook that will be executed before an action has been sent to other logic, middleware, and the reducers. Must call one of the provided callback functions allow or reject with an action to signal completion. Expected to be called exactly once. Pass undefined as an object to forward nothing. Calling reject prevents process hook from being run. Defaults to an identity fn which allows the original action. @param {function} logicOptions.transform hook that will be executed before an action has been sent to other logic, middleware, and the reducers. This is an alias for the validate hook. Call the provided callback function `next` (or `reject`) to signal completion. Expected to be called exactly once. Pass undefined as an object to forward nothing. Defaults to an identity transform which forwards the original action. @param {function} logicOptions.process hook that will be invoked after the original action (or that returned by validate/transform step) has been forwarded to other logic, middleware, and reducers. This hook will not be run if the validate/transform hook called reject. This hook is ideal for any additional processing or async fetching. The fn signature is `process(deps, ?dispatch, ?done)` where dispatch and done are optional and if included in the the signature will change the dispatch mode: 1. Neither dispatch, nor done - dispatches the returned/resolved val 2. Only dispatch - single dispatch mode, call dispatch exactly once (deprecated) 3. Both dispatch and done - multi-dispatch mode, call done when finished Dispatch may be called with undefined when nothing needs to be dispatched. Multiple dispatches may be made if including the done or simply by dispatching an observable. More details on dispatching modes are in the advanced API docs @param {object} logicOptions.processOptions options influencing process hook, default {} @param {boolean} logicOptions.processOptions.dispatchReturn dispatch the return value or resolved/next promise/observable, default is false when dispatch is included in process fn signature @param {boolean} logicOptions.processOptions.dispatchMultiple multi-dispatch mode is enabled and continues until done is called or cancelled. The default is false unless the done cb is included in the process fn signature. @param {string|function} logicOptions.processOptions.successType action type or action creator fn, use value as payload @param {string|function} logicOptions.processOptions.failType action type or action creator fn, use value as payload @param {number} logicOptions.warnTimeout In non-production environment a console.error message will be logged if logic doesn't complete before this timeout in ms fires. Set to 0 to disable. Defaults to 60000 (one minute) @returns {object} validated logic object which can be used in logicMiddleware contains the same properties as logicOptions but has defaults applied. */ export default function createLogic() { var logicOptions = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var invalidOptions = getInvalidOptions(logicOptions, allowedOptions); if (invalidOptions.length) { throw new Error("unknown or misspelled option(s): ".concat(invalidOptions)); } var name = logicOptions.name, type = logicOptions.type, cancelType = logicOptions.cancelType, _logicOptions$warnTim = logicOptions.warnTimeout, warnTimeout = _logicOptions$warnTim === void 0 ? defaultOptions.warnTimeout : _logicOptions$warnTim, _logicOptions$latest = logicOptions.latest, latest = _logicOptions$latest === void 0 ? defaultOptions.latest : _logicOptions$latest, _logicOptions$debounc = logicOptions.debounce, debounce = _logicOptions$debounc === void 0 ? defaultOptions.debounce : _logicOptions$debounc, _logicOptions$throttl = logicOptions.throttle, throttle = _logicOptions$throttl === void 0 ? defaultOptions.throttle : _logicOptions$throttl, validate = logicOptions.validate, transform = logicOptions.transform, process = logicOptions.process, _logicOptions$process = logicOptions.processOptions, processOptions = _logicOptions$process === void 0 ? {} : _logicOptions$process; if (!type) { throw new Error('type is required, use \'*\' to match all actions'); } if (type === 'undefined') { throw new Error('type is a string "undefined", check the logicOptions type field for a stringified undefined value'); } if (validate && transform) { throw new Error('logic cannot define both the validate and transform hooks they are aliases'); } if (typeof processOptions.warnTimeout !== 'undefined') { throw new Error('warnTimeout is a top level createLogic option, not a processOptions option'); } var invalidProcessOptions = getInvalidOptions(processOptions, allowedProcessOptions); if (invalidProcessOptions.length) { throw new Error("unknown or misspelled processOption(s): ".concat(invalidProcessOptions)); } if (NODE_ENV !== 'production' && typeof processOptions.dispatchMultiple !== 'undefined' && warnTimeout !== 0) { // eslint-disable-next-line no-console console.error("warning: in logic for type(s): ".concat(stringifyType(type), " - dispatchMultiple is always true in next version. For non-ending logic, set warnTimeout to 0")); } var processLength = process ? process.length : 0; // use process fn signature to determine some processOption defaults // for dispatchReturn and dispatchMultiple switch (processLength) { case 0: // process() - dispatchReturn case 1: // process(deps) - dispatchReturn setIfUndefined(processOptions, 'dispatchReturn', true); break; case 2: // process(deps, dispatch) - single dispatch (deprecated) if (NODE_ENV !== 'production' && !processOptions.dispatchMultiple && warnTimeout !== 0) { // eslint-disable-next-line no-console console.error("warning: in logic for type(s): ".concat(stringifyType(type), " - single-dispatch mode is deprecated, call done when finished dispatching. For non-ending logic, set warnTimeout: 0")); } // nothing to do, defaults are fine break; case 3: // process(deps, dispatch, done) - multi-dispatch default: // allow for additional params to come later setIfUndefined(processOptions, 'dispatchMultiple', true); break; } return { name: typeToStrFns(name), type: typeToStrFns(type), cancelType: typeToStrFns(cancelType), latest: latest, debounce: debounce, throttle: throttle, validate: validate, transform: transform, process: process, processOptions: processOptions, warnTimeout: warnTimeout }; } function getInvalidOptions(options, validOptions) { return Object.keys(options).filter(function (k) { return validOptions.indexOf(k) === -1; }); } /* if type is a fn call toString() to get type, redux-actions if array, then check members */ function typeToStrFns(type) { if (Array.isArray(type)) { return type.map(function (x) { return typeToStrFns(x); }); } return typeof type === 'function' ? type.toString() : type; } function setIfUndefined(obj, propName, propValue) { if (typeof obj[propName] === 'undefined') { // eslint-disable-next-line no-param-reassign obj[propName] = propValue; } }