overmind
Version:
Frictionless state management
228 lines (226 loc) • 7.63 kB
JavaScript
import { EventType } from "./internalTypes.js";
import { IS_OPERATOR, MODE_TEST, ORIGINAL_ACTIONS, createActionsProxy, getFunctionName, isPromise } from "./utils.js";
//#region src/operator.ts
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);
}
});
}
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
});
}
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 : void 0,
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(() => {});
else {
context.execution.isRunning = false;
context.execution.emit(EventType.OPERATOR_END, {
...context.execution,
result: value,
isAsync: false,
...evaluatedDetails
});
}
}
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$1) => {
return (value$1) => action$1(value$1, 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;
}
};
}
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);
};
}
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$1, value, options = {}) => {
function run(err$2, value$1) {
if (options.path) {
const newContext = createContext(context, value$1, context.execution.path && context.execution.path.concat(options.path.name));
const nextWithPath = createNextPath(next);
(options.path.operator[IS_OPERATOR] ? options.path.operator : action(options.path.operator))(err$2, newContext, (...args) => {
operatorStopped(context, args[1].value);
nextWithPath(...args);
});
} else {
operatorStopped(context, err$2 || value$1, { isSkipped: err$2 ? true : options.isSkipped });
next(err$2, createContext(context, value$1));
}
}
if (value && value instanceof Promise) value.then((promiseValue) => run(err$1, promiseValue)).catch((promiseError) => run(promiseError, promiseError));
else {
nextIsCalled = true;
run(err$1, value);
}
}, (err$1, value) => {
nextIsCalled = true;
operatorStopped(context, err$1 || value, {
isSkipped: Boolean(err$1),
isIntercepted: !err$1
});
final(err$1, 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;
}
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$1, value, options = {}) => {
function run(err$2, value$1) {
operatorStopped(context, err$2 || value$1, { isSkipped: err$2 ? true : options.isSkipped });
mutationTree.dispose();
next(err$2, createContext(context, value$1));
}
if (value && value instanceof Promise) value.then((promiseValue) => run(err$1, promiseValue)).catch((promiseError) => run(promiseError, promiseError));
else {
nextIsCalled = true;
run(err$1, value);
}
}, (err$1, value) => {
nextIsCalled = true;
operatorStopped(context, err$1 || value, {
isSkipped: Boolean(err$1),
isIntercepted: !err$1
});
final(err$1, 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;
}
//#endregion
export { action, createContext, createMutationOperator, createNextPath, createOperator, operatorStarted, operatorStopped };
//# sourceMappingURL=operator.js.map