UNPKG

overmind

Version:
285 lines • 10.6 kB
import { EventType } from './internalTypes'; import { IS_OPERATOR, MODE_TEST, ORIGINAL_ACTIONS, createActionsProxy, getFunctionName, isPromise, } from './utils'; export function action(operation) { return createMutationOperator('action', getFunctionName(operation), (err, context, value, next) => { if (err) next(err, value); else { const result = operation(context, value); if (isPromise(result)) { next(null, result.then((resolvedValue) => resolvedValue)); } else { next(null, result); } } }); } export function operatorStarted(type, arg, context) { if (process.env.NODE_ENV === 'production') { return; } const name = typeof arg === 'function' ? arg.displayName || arg.name : String(arg); context.execution.isRunning = true; context.execution.emit(EventType.OPERATOR_START, { ...context.execution, name, type, }); } export function operatorStopped(context, value, details = {}) { if (process.env.NODE_ENV === 'production') { if (value instanceof Promise) { context.execution.emit(EventType.OPERATOR_ASYNC, { ...context.execution, isAsync: true, }); } return; } const evaluatedDetails = { error: details.error ? details.error.message : undefined, isIntercepted: Boolean(details.isIntercepted), isSkipped: Boolean(details.isSkipped), }; if (value instanceof Promise) { value .then((promiseValue) => { context.execution.isRunning = false; context.execution.emit(EventType.OPERATOR_END, { ...context.execution, result: promiseValue, isAsync: true, ...evaluatedDetails, }); }) .catch(() => { // Make sure an error does not cause uncaught }); } else { context.execution.isRunning = false; context.execution.emit(EventType.OPERATOR_END, { ...context.execution, result: value, isAsync: false, ...evaluatedDetails, }); } } export function createContext(context, value, path) { if (process.env.NODE_ENV === 'production') { return { ...context, value, }; } const newExecution = { ...context.execution, operatorId: context.execution.getNextOperatorId(), path: path || context.execution.path, }; const mutationTrees = []; return { ...context, actions: createActionsProxy(context.actions[ORIGINAL_ACTIONS] || context.actions, (action) => { return (value) => action(value, newExecution.isRunning ? newExecution : null); }), value, execution: newExecution, effects: context.execution.trackEffects(newExecution), flush: context.parentExecution ? context.parentExecution.flush : (isAsync) => { return this.proxyStateTree.flush(mutationTrees, isAsync); }, getMutationTree: context.parentExecution ? context.parentExecution.getMutationTree : () => { const mutationTree = this.proxyStateTree.getMutationTree(); mutationTrees.push(mutationTree); if (this.mode.mode === MODE_TEST) { mutationTree.onMutation((mutation) => { this.addExecutionMutation(mutation); }); } return mutationTree; }, }; } export function createNextPath(next) { if (process.env.NODE_ENV === 'production') { return next; } return (err, context) => { const newContext = { ...context, execution: { ...context.execution, path: context.execution.path.slice(0, context.execution.path.length - 1), }, }; if (err) next(err, newContext); else next(null, newContext); }; } export function createOperator(type, name, cb) { const operator = (err, context, next, final) => { operatorStarted(type, name, context); let nextIsCalled = false; try { cb(err, { state: context.state, effects: context.effects, actions: context.actions, execution: context.execution, addFlushListener: context.addFlushListener, addMutationListener: context.addMutationListener, reaction: context.reaction, }, context.value, (err, value, options = {}) => { function run(err, value) { if (options.path) { const newContext = createContext(context, value, context.execution.path && context.execution.path.concat(options.path.name)); const nextWithPath = createNextPath(next); const operatorToRun = options.path.operator[IS_OPERATOR] ? options.path.operator : action(options.path.operator); operatorToRun(err, newContext, (...args) => { operatorStopped(context, args[1].value); nextWithPath(...args); }); } else { operatorStopped(context, err || value, { isSkipped: err ? true : options.isSkipped, }); next(err, createContext(context, value)); } } if (value && value instanceof Promise) { value .then((promiseValue) => run(err, promiseValue)) .catch((promiseError) => run(promiseError, promiseError)); } else { nextIsCalled = true; run(err, value); } }, (err, value) => { nextIsCalled = true; operatorStopped(context, err || value, { isSkipped: Boolean(err), isIntercepted: !err, }); final(err, createContext(context, value)); }); } catch (error) { nextIsCalled = true; operatorStopped(context, context.value, { error, }); next(error, createContext(context, context.value)); } if (!nextIsCalled) { context.execution.emit(EventType.OPERATOR_ASYNC, { ...context.execution, isAsync: true, }); } }; operator[IS_OPERATOR] = true; return operator; } export function createMutationOperator(type, name, cb) { const operator = (err, context, next, final) => { operatorStarted(type, name, context); const mutationTree = context.execution.getMutationTree(); if (!(process.env.NODE_ENV === 'production')) { mutationTree.onMutation((mutation) => { context.execution.emit(EventType.MUTATIONS, { ...context.execution, mutations: [mutation], }); }); } let nextIsCalled = false; try { cb(err, { state: mutationTree.state, effects: context.effects, actions: context.actions, execution: context.execution, addFlushListener: context.addFlushListener, addMutationListener: context.addMutationListener, reaction: context.reaction, }, process.env.NODE_ENV === 'production' ? context.value : context.execution.scopeValue(context.value, mutationTree), (err, value, options = {}) => { function run(err, value) { operatorStopped(context, err || value, { isSkipped: err ? true : options.isSkipped, }); mutationTree.dispose(); next(err, createContext(context, value)); } if (value && value instanceof Promise) { value .then((promiseValue) => run(err, promiseValue)) .catch((promiseError) => run(promiseError, promiseError)); } else { nextIsCalled = true; run(err, value); } }, (err, value) => { nextIsCalled = true; operatorStopped(context, err || value, { isSkipped: Boolean(err), isIntercepted: !err, }); final(err, createContext(context, value)); }); if (!(process.env.NODE_ENV === 'production')) { let pendingFlush; mutationTree.onMutation(() => { if (pendingFlush) { clearTimeout(pendingFlush); } pendingFlush = setTimeout(() => { const flushData = context.execution.flush(true); if (flushData.mutations.length) { context.execution.send({ type: 'flush', data: { ...context.execution, ...flushData, mutations: flushData.mutations, }, }); } }); }); } } catch (error) { nextIsCalled = true; operatorStopped(context, context.value, { error, }); next(error, createContext(context, context.value)); } if (!nextIsCalled) { context.execution.emit(EventType.OPERATOR_ASYNC, { ...context.execution, isAsync: true, }); } }; operator[IS_OPERATOR] = true; return operator; } //# sourceMappingURL=operator.js.map