UNPKG

csf

Version:

Generator based flow control with context providing

211 lines (174 loc) 6.73 kB
"use strict"; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var isGenerator = require("is-generator"); var isFunction = require("is-function"); var isPromise = require("is-promise"); var isObject = require("is-object"); var isObjectLike = require("is-object-like"); var isError = require("is-error"); var _require = require("./constants"), PAYLOAD = _require.PAYLOAD, CANCEL = _require.CANCEL, CANCELLED = _require.CANCELLED; function isGeneratorResultLike(next) { return next.hasOwnProperty("value") && next.hasOwnProperty("done") && typeof next.done === "boolean"; } function extractPayload(value) { if (isObjectLike(value) && value[PAYLOAD]) { if (value[PAYLOAD] === "invoke") { return value(); } Reflect.deleteProperty(value, PAYLOAD); return value; } return value; } module.exports = function run(next, args, previousNext) { if (typeof args === "undefined") { args = []; } // TODO: add object like if (_typeof(this) !== "object" && typeof this !== "function" && typeof this !== "undefined") { throw new Error("Context should be an object like or undefined. " + _typeof(this) + " given"); } /* Reduce context */ var context = this || null; /* * Handles situation when result is a function as is, not a next part of the * flow. Thus if you prefer to return function as final paylaod, you must to * define [PAYLOAD] static property, to prevent execution in the flow */ if (next && next[PAYLOAD]) { return Promise.resolve(next); } /* * Is next unit is a functiom. it must be called with current props * and store and result will go to the flow again */ if (isFunction(next)) { try { var result = Reflect.apply(next, context, args); /* That function was just a final result factory */ /* * if (next[RETURN_PAYLOAD]) { * return Promise.resolve(result); * } */ /* No arguments should be passed the text sequence */ return Reflect.apply(run, context, [result]); } catch (e) { return Promise.reject(e); } /* Handle case when next unit is an object */ } else if (isObject(next)) { /* If next unit is a generator result */ if (isObject(previousNext) && isGeneratorResultLike(next)) { /* * Generator still in work, current value should be executed * and after it next value should be getted */ if (!next.done) { return Reflect.apply(run, context, [next.value, args]).then(function (rawValue) { /* Extract payload */ var value = extractPayload(rawValue); try { /* In this case next is just a generator result, ??? */ if (previousNext.hasOwnProperty(CANCELLED)) { return Promise.resolve(typeof previousNext[CANCELLED] === "undefined" ? previousNext[CANCELLED] : previousNext.return(value).value); } var nextValue = previousNext.next(value); /** * If generator has no closing `return` statement, it will * return `undefined` values with done===true. * Anyway we have to return last yielded value. * * I must to decide about necessary to return undefined * with there is no return statement */ if (nextValue.done && typeof nextValue.value === "undefined") { return Promise.resolve(rawValue); } return Reflect.apply(run, context, [nextValue, /* No arguments should be passed to the next sequence */ [], previousNext]); } catch (e) { return Promise.reject(e); } }) /* If error hapends while execution generator next value */ .catch(function (error) { try { /* Handle case when generator work have been cancelled */ if (previousNext.hasOwnProperty(CANCELLED)) { return Promise.resolve(typeof previousNext[CANCELLED] === "undefined" ? previousNext[CANCELLED] /* TODO: What I must to return here? */ : previousNext.return(null).value); } /* Return error back to generator */ var nextValue = previousNext.throw(error); /* Run next unit, provided by catch block */ return Reflect.apply(run, context, [nextValue, /* No arguments should be passed to the next sequence */ [], previousNext]); /* * Handles error which may be throwed while handling * sub-level error */ } catch (e) { /* Hm. may be we shoudl handle it in a normal way? .... */ return Promise.reject(e); } }); /* If generator done */ } return Reflect.apply(run, context, [next.value, /* No arguments should be passed to the next sequence */ []]); } else if (isGenerator(next)) { // Means it is generator try { var value = next.next(); var wrap = Reflect.apply(run, context, [value, args, next]); wrap[CANCEL] = function (val) { next[CANCELLED] = val; }; return wrap; } catch (e) { return Promise.reject(e); } } else if (isPromise(next)) { var cancel = void 0; var _wrap = new Promise(function (resolve, reject) { cancel = resolve; next.then(function (next) { Reflect.apply(run, context, [next, /* No arguments should be passed to the next sequence */ []]).then(resolve).catch(reject); }).catch(function (err) { Reflect.apply(run, context, [err instanceof Error ? err : new Error(err), /* No arguments should be passed to the next sequence */ []]).then(resolve).catch(reject); }); }); _wrap[CANCEL] = function (val) { return cancel(val); }; return _wrap; } else if (isError(next)) { return Promise.reject(next); /* Unexecutable payload */ } else if (next[PAYLOAD]) { return Promise.resolve(next.value); } else { // Returns plain object return Promise.resolve(next); } } else { // Returns plain object return Promise.resolve(next); } }; module.exports.PAYLOAD = PAYLOAD; // module.exports.RETURN_PAYLOAD = RETURN_PAYLOAD; module.exports.CANCELLED = CANCELLED; module.exports.CANCELLED = CANCELLED;