UNPKG

90.3 kBJavaScriptView Raw
1'use strict';
2
3var functions = require('./functions-b6202a08.js');
4var task = require('folktale/concurrency/task');
5var R = require('ramda');
6var Result = require('folktale/result');
7var util = require('util');
8var throwingFunctions = require('./throwingFunctions.js');
9var maybe = require('folktale/maybe');
10
11/**
12 * Created by Andy Likuski on 2020.02.21
13 * Copyright (c) 2020 Andy Likuski
14 *
15 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21/**
22 * Stringify an error with a stack trace
23 * https://stackoverflow.com/questions/18391212/is-it-not-possible-to-stringify-an-error-using-json-stringify
24 * @param {Object} err Error
25 * @return {string} The json stringified error
26 */
27
28var stringifyError = function stringifyError(err) {
29 // If the error isn't an Error object, wrap it
30 var wrappedError = wrapError(err);
31 var obj = R.fromPairs(R.map(function (key) {
32 return [key, wrappedError[key]];
33 }, Object.getOwnPropertyNames(wrappedError))); // Use replace to convert escaped in stack \\n to \n
34
35 return R.replace(/\\n/g, '\n', JSON.stringify( // Put message and stack first
36 R.merge(R.pick(['message', 'stack'], obj), R.omit(['message', 'stack'])), null, 2));
37};
38/**
39 * Wraps an error in Error unless it already is an Error. Useful for Result.Error strings that need to be
40 * converted to errors
41 * @param {*} error The value to wrap if needed
42 * @return {Error} The wrapped error
43 */
44
45var wrapError = function wrapError(error) {
46 return R.unless(R.is(Error), function (e) {
47 return new Error(e);
48 })(error);
49};
50
51/**
52 * Default handler for Task rejections when an error is unexpected and should halt execution
53 * with a useful error message
54 * @param {[Object]} Errors that are accumulated
55 * @param {*} reject Rejection value from a Task
56 * @returns {void} No return
57 */
58
59var defaultOnRejected = R.curry(function (errors, reject) {
60 // Combine reject and errors
61 var errorsAsArray = functions.toArrayIfNot(errors);
62 var allErrors = R.uniq(R.concat(errorsAsArray, [reject])); // Wrap each error in an Error object if it isn't already one
63
64 console.error('Accumulated task errors:\n', // eslint-disable-line no-console
65 R.join('\n', R.map(function (error) {
66 return stringifyError(error);
67 }, allErrors)));
68});
69var _onRejected = defaultOnRejected;
70/**
71 * Default behavior for task listener defaultOnCancelled, which simply logs
72 * @returns {void} No return
73 */
74
75var defaultOnCancelled = function defaultOnCancelled() {
76 console.log('The task was cancelled. This is the default action'); // eslint-disable-line no-console
77};
78var _onCanceled = defaultOnCancelled;
79
80var whenDone = function whenDone(errors, done) {
81 if (done) {
82 done(R.when(R.identity, function (errs) {
83 return R.map(stringifyError, errs || []);
84 })(errors));
85 }
86};
87/**
88 * Defaults the defaultOnRejected and defaultOnCancelled to throw or log, respectively, when neither is expected to occur.
89 * Pass the onResolved function with the key onResolved pointing to a unary function with the result. Example:
90 * task.run().listen(defaultRunConfig({
91 * onResolved: value => ... do something with value ...
92 * }))
93 * @param {Object} obj Object of callbacks
94 * @param {Function} obj.onResolved Unary function expecting the resolved value
95 * @param {Function} obj.onCancelled optional cancelled handler. Default is to log
96 * @param {Function} obj.onRejected optional rejected handler. Default is to throw. This function is first
97 * passed the errors that have accumulated and then the final error. You should make a curried function
98 * or similarly that expects two arguments, error and error
99 * @param {[Object]} errors Optional list of errors that accumulated
100 * @param {Function} done Optional or tests. Will be called after rejecting, canceling or resolving
101 * @returns {Object} Run config with defaultOnCancelled, defaultOnRejected, and onReolved handlers
102 */
103
104
105var defaultRunConfig = function defaultRunConfig(_ref, errors, done) {
106 var _onResolved = _ref.onResolved,
107 _onCancelled = _ref.onCancelled,
108 _onRejected2 = _ref.onRejected,
109 _whenDone = _ref._whenDone;
110 return {
111 onCancelled: function onCancelled() {
112 (_onCancelled || _onCanceled)();
113
114 whenDone(null, done);
115 },
116 onRejected: function onRejected(error) {
117 _handleReject(_onRejected2, done, errors, error);
118 },
119 onResolved: function onResolved(value) {
120 var errs = null;
121
122 try {
123 // Wrap in case anything goes wrong with the assertions
124 _onResolved(value);
125 } catch (e) {
126 // I can't import this but we don't want to process assertion errors
127 if (e.constructor.name === 'JestAssertionError') {
128 errs = [e];
129 throw e;
130 }
131
132 var error = new Error('Assertion threw error');
133 errs = [e, error];
134 var reject = _onRejected2 || _onRejected;
135 reject(errs, error);
136 } finally {
137 (_whenDone || whenDone)(errs, done);
138 }
139 }
140 };
141};
142/**
143 * Given a rejection in runDefaultConfig or runDefaultToResultConfig, calls the given rejected or the default.
144 * if no onRejected is given or it throws an error when called, then done is called with the errors to make the
145 * test fail
146 * @param {Function} onRejected Expects errors and error
147 * @param {Function} done Done function
148 * @param {[Object]} errs Accumulated error
149 * @param {Object} error Error that caused the oReject
150 * @returns {void} No return
151 * @private
152 */
153
154var _handleReject = function _handleReject(onRejected, done, errs, error) {
155 var noThrow = true;
156 var caughtError = null;
157
158 try {
159 (onRejected || _onRejected)(errs, error);
160 } catch (e) {
161 noThrow = false;
162 caughtError = e;
163 } finally {
164 // If we didn't define onRejected or our onRejected threw, pass errors so jest fails
165 whenDone(onRejected && noThrow ? null : R.concat(errs, functions.compact([error, caughtError])), done);
166 }
167};
168/**
169 * For a task that returns a Result.
170 * Defaults the defaultOnRejected and defaultOnCancelled to fail the test or log, respectively, when neither is expected to occur.
171 * Pass the onResolved function with the key onResolved pointing to a unary function with the result.
172 * If the task resolves to an Result.Ok, resolves the underlying value and passes it to the onResolved function
173 * that you define. If the task resolves ot an Result.Error, the underlying value is passed to on Rejected.
174 * rejection and cancellation resolves the underlying value of the Result.Ok or Result.Error. In practice defaultOnRejected shouldn't
175 * get called directly. Rather a Result.Error should be resolved and then this function calls defaultOnRejected. cancellation
176 * should probably ignores the value.
177 * If you don't define onRejected an a rejection occurs or your onRejected throws an exception, the test will fail
178 * Example:
179 * task.run().listen(defaultRunConfig({
180 * onResolved: value => ... do something with value ...
181 * }))
182 * @param {Object} obj Object of callbacks
183 * @param {Function} obj.onResolved Unary function expecting the resolved value
184 * @param {Function} [obj.onRejected] Optional expects a list of accumulated errors and the final error. This function
185 * will be called for normal task rejection and also if the result of the task is a result.Error, in which
186 * case errors will be R.concat(errors || [], [result.Error]) and error will be result.Error
187 * @param {Function} [obj.onCancelled] Optional cancelled function
188 * @param {[Object]} errors Empty array to collect errors
189 * @param {Function} done Done function from test definition
190 * @returns {Object} Run config with defaultOnCancelled, defaultOnRejected, and onReolved handlers
191 */
192
193
194var defaultRunToResultConfig = function defaultRunToResultConfig(_ref2, errors, done) {
195 var _onResolved2 = _ref2.onResolved,
196 onCancelled = _ref2.onCancelled,
197 onRejected = _ref2.onRejected;
198
199 // We have to do this here instead of using defaultRunConfig's version
200 var reject = function reject(errs, error) {
201 _handleReject(onRejected, done, errs, error);
202 };
203
204 return defaultRunConfig({
205 onResolved: function onResolved(result) {
206 return result.map(function (value) {
207 try {
208 // Wrap in case anything goes wrong with the assertions
209 _onResolved2(value);
210 } catch (error) {
211 reject(R.concat(onCancelled || [], [error]), error);
212 } // don't finalize here, defaultRunConfig.onResolved does that
213
214 }).mapError(function (error) {
215 return reject(onCancelled || [], error);
216 });
217 },
218 onRejected: function onRejected(error) {
219 return reject([], error);
220 },
221 onCancelled: onCancelled
222 }, errors, done);
223};
224/**
225 * Wraps a Task in a Promise.
226 * @param {Task} tsk The Task
227 * @returns {Promise} The Task as a Promise
228 */
229
230var taskToPromise = function taskToPromise(tsk) {
231 if (!tsk.run) {
232 throw new TypeError("Expected a Task, got ".concat(functions._typeof(tsk)));
233 }
234
235 return tsk.run().promise();
236};
237/**
238 * @deprecated Use fromPromised from folktale/concurrency/task
239 * Wraps a Promise in a Task
240 * @param {Promise} promise The promise
241 * @param {boolean} expectReject default false. Set true for testing to avoid logging rejects
242 * @returns {Task} The promise as a Task
243 */
244
245var promiseToTask = function promiseToTask(promise) {
246 return task.fromPromised(function () {
247 return promise;
248 })();
249};
250/**
251 * Natural transformation of a Result to a Task. This is useful for chained tasks that return Results.
252 * If the Result is a Result.Error, a Task.reject is called with the value. If the Result is a Result.Ok,
253 * a Task.of is created with the value
254 * @param {Result} result A Result.Ok or Result.Error
255 * @returns {Task} The Task.of or Task.reject
256 */
257
258var resultToTask = function resultToTask(result) {
259 return result.matchWith({
260 Ok: function Ok(_ref3) {
261 var value = _ref3.value;
262 return task.of(value);
263 },
264 Error: function Error(_ref4) {
265 var value = _ref4.value;
266 return task.rejected(value);
267 }
268 });
269};
270/**
271 * Passes a result to a function that returns a Task an maps the successful Task value to a Result.Ok
272 * and erroneous task to a Result.Error. If result is an error it is wrapped in a Task.Of
273 * @param {Function} f Function that receives a Result.Ok value and returns a Task. This should not return a task
274 * with a result. If it does then you don't need resultToTaskNeedingResult.
275 * @param {Object} result A Result.Ok or Result.Error
276 * @returns {Object} Task with Result.Ok or Result.Error inside.
277 * @sig resultToTaskNeedingResult:: Result r, Task t => (r -> t) -> r -> t r
278 */
279
280var resultToTaskNeedingResult = R.curry(function (f, result) {
281 return result.matchWith({
282 Ok: function Ok(_ref5) {
283 var value = _ref5.value;
284 return f(value).map(Result.Ok).mapRejected(Result.Error);
285 },
286 Error: task.of
287 });
288});
289/**
290 * Passes a result to a function that returns a Task containing a Result
291 * and erroneous task maps converts a Result.Ok to a Result.Error. If result is an error it is wrapped in a Task.Of
292 * @param {Function} f Function that receives result and returns a Task with a Result in it.
293 * @param {Object} result A Result.Ok or Result.Error
294 * @returns {Object} Task with Result.Ok or Result.Error inside.
295 * @sig resultToTaskNeedingResult:: Result r, Task t => (r -> t r) -> r -> t r
296 */
297
298var resultToTaskWithResult = R.curry(function (f, result) {
299 return result.matchWith({
300 Ok: function Ok(_ref6) {
301 var value = _ref6.value;
302 return f(value).mapRejected(function (r) {
303 return R.cond([// Chain Result.Ok to Result.Error
304 [Result.Ok.hasInstance, R.chain(function (v) {
305 return Result.Error(v);
306 })], // Leave Result.Error alone
307 [Result.Error.hasInstance, R.identity], // If the rejected function didn't produce a Result then wrap it in a Result.Error
308 [R.T, Result.Error]])(r);
309 });
310 },
311 Error: task.of
312 });
313});
314/**
315 * Wraps the value of a successful task in a Result.Ok if it isn't already a Result
316 * Converts a rejected task to a resolved task and
317 * wraps the value of a rejected task in a Result.Error if it isn't already a Result.Error or converts
318 * Result.Ok to Result.Error.
319 * @param {Task} tsk The task to map
320 * @returns {Task} The task whose resolved or rejected value is wrapped in a Result and is always resolved
321 */
322
323var taskToResultTask = function taskToResultTask(tsk) {
324 return tsk.map(function (v) {
325 return R.cond([// Leave Result.Ok alone
326 [Result.Ok.hasInstance, R.identity], // Leave Result.Error alone
327 [Result.Error.hasInstance, R.identity], // If the rejected function didn't produce a Result then wrap it in a Result.Ok
328 [R.T, Result.Ok]])(v);
329 }).orElse(function (v) {
330 return task.of(R.cond([// Chain Result.Ok to Result.Error
331 [Result.Ok.hasInstance, R.chain(function (e) {
332 return Result.Error(e);
333 })], // Leave Result.Error alone
334 [Result.Error.hasInstance, R.identity], // If the rejected function didn't produce a Result then wrap it in a Result.Error
335 [R.T, Result.Error]])(v));
336 });
337};
338/**
339 * A version of traverse that also reduces. I'm sure there's something in Ramda for this, but I can't find it.
340 * Same arguments as reduce, but the initialValue must be an applicative, like task.of({}) or Result.of({})
341 * f is called with the underlying value of accumulated applicative and the underlying value of each list item,
342 * which must be an applicative
343 * @param {Function} accumulator Accepts the value of the reduced container and each result of sequencer,
344 * then returns a value that will be wrapped in a container for the subsequent interation
345 * Container C v => v -> v -> v
346 * @param {Object} initialValue A container to be the initial reduced value of accumulator
347 * @param {[Object]} list List of contianer
348 * @returns {Object} The value resulting from traversing and reducing
349 * @sig traverseReduce:: Container C v => (v -> v -> v) -> C v -> [C v] -> C v
350 */
351
352var traverseReduce = function traverseReduce(accumulator, initialValue, list) {
353 return R.reduce(function (containerResult, container) {
354 return R.chain(function (res) {
355 return R.map(function (v) {
356 return accumulator(res, v);
357 }, container);
358 }, containerResult);
359 }, initialValue, list);
360};
361/**
362 * Same as traverseReduce but uses mapError to handle Result.Error or anything else that implements mapError
363 * @param {function} join It's necessary to pass a join function to instruct how to extract the embedded value,
364 * since Result.Error and similar don't implement chain or join. For Result.Error, join would be:
365 * const join = error => error.matchWith({Error: ({value}) => value}) or simply error => error.value
366 * @param {function} accumulator Accumulates the values of the monads
367 * @param {object} initialValue The initial value should match an empty error monad
368 * @param {[object]} list The list of error monads
369 * @returns {Object} The reduced error monad
370 */
371
372var traverseReduceError = function traverseReduceError(join, accumulator, initialValue, list) {
373 return R.reduce(function (containerResult, container) {
374 return join(containerResult.mapError(function (res) {
375 return container.mapError(function (v) {
376 return accumulator(res, v);
377 });
378 }));
379 }, initialValue, list);
380};
381/**
382 * traverseReduceError specifically for Result.Error
383 * @param {function} accumulator Accumulates the values of the monads
384 * @param {object} initialValue The initial value should match an empty error monad
385 * @param {[object]} list The list of error monads
386 * @returns {Object} The reduced error monad
387 */
388
389var traverseReduceResultError = function traverseReduceResultError(accumulator, initialValue, list) {
390 return traverseReduceError(function (error) {
391 return error.matchWith({
392 Error: function Error(_ref7) {
393 var value = _ref7.value;
394 return value;
395 },
396 Ok: function Ok(_ref8) {
397 var value = _ref8.value;
398 return value;
399 }
400 });
401 }, accumulator, initialValue, list);
402};
403/**
404 * A version of traverse that also reduces. I'm sure there's something in Ramda for this, but I can't find it.
405 * The first argument specify the depth of the container (monad). So a container of R.compose(Task.of Result.Ok(1)) needs
406 * a depth or 2. A container of R.compose(Task.of, Result.Ok)([1,2,3]) needs a depth of 3, where the array is the 3rd container
407 * if you are operating on individual items. If you're treating the array as an singular entity then it remains level 2.
408 * After that Same arguments as reduce, but the initialValue must be an applicative,
409 * like task.of({}) or Result.of({}) (both level 1) or R.compose(Task.of, Result.Ok(0)) if adding values (level 2)
410 * or R.compose(Task.of, Result.Ok, Array.of)() (level 3) if combining each array item somehow.
411 * @param {Function} accumulator Accepts a reduced applicative and each result of sequencer, then returns the new reduced applicative
412 * Container C v => v -> v -> v
413 * @param {Object} initialValue A conatiner to be the initial reduced value of accumulator. This must match the
414 * expected container type
415 * @param {[Object]} list List of containers. The list does not itself count as a container toward containerDepth. So a list of Tasks of Results is still level containerDepth: 2
416 * @returns {Object} The value resulting from traversing and reducing
417 * @sig traverseReduceDeep:: Number N, N-Depth-Container C v => N -> (v -> v -> v) -> C v -> [C v] -> C v
418 */
419
420var traverseReduceDeep = R.curry(function (containerDepth, accumulator, initialValue, deepContainers) {
421 return R.reduce(function (applicatorRes, applicator) {
422 return R.compose.apply(R, functions._toConsumableArray(R.times(R.always(R.liftN(2)), containerDepth)))(accumulator)(applicatorRes, applicator);
423 }, initialValue, deepContainers);
424});
425/**
426 * Export chains a monad to a reducedMonad with the given function
427 * @param {Function} f Expects the reducedMonad and the monad, returns a new reducedMonad
428 * @param {Object} reducedMonad THe monad that is already reduced
429 * @param {Object} monad The monad to reduce
430 * @param {Number} index The index of the monad
431 * @return {Object} The monad result of calling f
432 */
433
434var _chainTogetherWith = function _chainTogetherWith(f, reducedMonad, monad, index) {
435 return f(reducedMonad, monad, index);
436};
437/**
438 * Version of _chainTogetherWith for task that composes a timeout every 100 calls into the chain to prevent stack overflow
439 * @param {Function} f Expects the reducedMonad and the monad and an optional index, returns a new reducedMonad
440 * @param {Object} reducedMonad THe monad that is already reduced
441 * @param {Object} monad The monad to reduce
442 * @param {Object} index So we don't break the chain all the time
443 * @return {Object} The monad result of calling f
444 * @private
445 */
446
447
448var _chainTogetherWithTaskDelay = function _chainTogetherWithTaskDelay(f, reducedMonad, monad, index) {
449 var n = 100; // console.log(`${index} trace: ${stackTrace.get().length}`);
450
451 return composeWithChainMDeep(1, [function (i) {
452 return f(reducedMonad, monad, i);
453 }, // Timeout every n calls
454 R.ifElse(function (i) {
455 return R.not(R.modulo(i, n));
456 }, timeoutTask, task.of)])(index);
457};
458
459var timeoutTask = function timeoutTask() {
460 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
461 args[_key] = arguments[_key];
462 }
463
464 return task.task(function (resolver) {
465 var timerId = setTimeout(function () {
466 return resolver.resolve.apply(resolver, args);
467 }, 0);
468 resolver.cleanup(function () {
469 clearTimeout(timerId);
470 });
471 });
472};
473/**
474 * Reduces a list of monads using buckets to prevent stack overflow
475 * @param {Object} config
476 * @param {Object} [config.buckets] Defaults to Math.max(100, R.length(monads) / 100). Divides the chained reductions into
477 * @param {function} f Reduce function, expects the reducedMonad and the monad and chains them togther
478 * buckets to prevent stack overflow.
479 * @param {Object} initialValue The monad with the empty value, e.g. Maybe.Just([]) or Task.of(Result.Ok({}))
480 * @param {[Object]} monads The list of monads
481 */
482
483var _reduceMonadsChainedBucketed = R.curry(function (_ref9, f, initialValue, monads) {
484 var buckets = _ref9.buckets;
485 // Bucket the tasks so each task set has up to bucketSize monads If these are still too big we'll recursively
486 // break them up later. Minimum 100 per bucket
487 var bucketSize = buckets || Math.max(100, Math.floor(R.length(monads) / 100));
488 var monadSets = bucketedMonadSets(bucketSize, monads); // Process each bucket of monads with traverseReduceWhileBucketed (end case) or
489 // recurse with _reduceMonadsChainedBucketed with smaller bucket size
490 // The first item of each set expects the accumulation of the previous set.
491 // This means we can't actually evaluate the sets yet, rather return functions
492 // Monad m:: [m[a]] -> m[b]
493
494 var reducedMonadSetFuncs = R.map(function (mSet) {
495 return function (previousSetAccumulationMonad) {
496 return R.ifElse( // If we have more than 100 monads recurse, limiting the bucket size to 1 / 10 the current bucket size
497 function (mmSet) {
498 return R.compose(R.lt(100), R.length)(mmSet);
499 }, // Take the bucketSize number of monads and recurse, this will divide into more buckets
500 function (mmSet) {
501 return _reduceMonadsChainedBucketed({
502 buckets: buckets
503 }, f, previousSetAccumulationMonad, mmSet);
504 }, // Small enough, do a normal R.reduce for each bucket of tasks
505 // Monad m:: [[m a]] -> [b -> m [c]]
506 function (mmSet) {
507 return R.reduce(f, previousSetAccumulationMonad, mmSet);
508 })(mSet);
509 };
510 }, monadSets); // Reduce the buckets. We pass the previous bucket result to the next monadSetFunc to kick off the processing
511 // of each monadSet
512
513 return R.reduce( // Chain the resultSets together
514 // Monad m:: m[[a]] -> m[a]
515 function (accumulatedMonadSets, monadSetFunc) {
516 return monadSetFunc(accumulatedMonadSets);
517 }, // Initial value
518 initialValue, reducedMonadSetFuncs);
519});
520/**
521 * A version of traverseReduce that also reduces until a boolean condition is met.
522 * Same arguments as reduceWhile, but the initialValue must be an applicative, like task.of({}) or Result.of({})
523 * f is called with the underlying value of accumulated applicative and the underlying value of each list item,
524 * which must be an applicative
525 * @param {Object|Function} predicateOrObj Like ramda's reduceWhile predicate. Accepts the accumulated value and next value.
526 * These are the values of the container. If false is returned the accumulated value is returned without processing
527 * more values. Be aware that for Tasks the task must run to predicate on the result, so plan to check the previous
528 * task to prevent a certain task from running
529 @param {Boolean} [predicateOrObj.accumulateAfterPredicateFail] Default false. Because of Tasks, we have a boolean here to allow accumulation after
530 * the predicate fails. The default behavior is to not accumulate the value of a failed predicate. This makes
531 * sense for things like Result where there is no consequence of evaluating them. But we have to run a Task to
532 * evaluate it so we might want to quit after the previous task but also add that task result to the accumulation.
533 * In that case set this true
534 * @param {Function} [predicateOrObj.mappingFunction] Defaults to R.map. The function used to each monad result from list.
535 * If the accumulator does not create a new monad then R.map is sufficient. However if the accumulator does create
536 * a new monad this should be set to R.chain so that the resulting monad isn't put inside the monad result
537 * @param {Function} [predicateOrObj.chainTogetherWith] Defaults to _chainTogetherWith. Only needs to be overridden
538 * for high stack count chaining that needs to be broken up to avoid max stack trace
539 * @param {Function} [predicateOrObj.monadConstructor] Default to R.identity. Function to create a monad if mappingFunction uses R.chain. This
540 * would be a task of function for task monads, an Result,Ok monad for Results, etc.
541 * @param {Function} [predicateOrObj.reducer] Default R.Reduce. An alternative reducer function to use, for
542 * instnace for stack handling by traverseReduceWhileBucketed
543 * @param {Function} accumulator Accepts a reduced applicative and each result of sequencer, then returns the new reduced applicative
544 * false it "short-circuits" the iteration and returns teh current value of the accumulator
545 * @param {Object} initialValueMonad An applicative to be the intial reduced value of accumulator
546 * @param {[Object]} list List of applicatives
547 * @returns {Object} The value resulting from traversing and reducing
548 */
549
550
551var traverseReduceWhile = function traverseReduceWhile(predicateOrObj, accumulator, initialValueMonad, list) {
552 // Configure the reduce function. It returns a reduce function expecting the two monads, the accumulated monad
553 // and each in list
554 var _reduceMonadsWithWhilst = _reduceMonadsWithWhile({
555 predicateOrObj: predicateOrObj,
556 accumulator: accumulator,
557 initialValueMonad: initialValueMonad
558 }); // Use R.reduce for processing each monad unless an alternative is specified.
559
560
561 var reduceFunction = R.ifElse(R.both(functions.isObject, R.prop('reducer')), R.prop('reducer'), function () {
562 return R.reduce;
563 })(predicateOrObj); // By default we call
564
565 var chainWith = R.propOr(_chainTogetherWith, 'chainTogetherWith', predicateOrObj); // Call the reducer. After it finishes strip out @@transducer/reduced if we aborted with it at some point
566
567 return composeWithChain([function (reducedMonadValue) {
568 // Using the initial value to get the right monad type, strip reduced if if was returned on the last iteration
569 return R.map(function () {
570 return R.ifElse(R.prop('@@transducer/reduced'), function (res) {
571 return R.prop('@@transducer/value', res);
572 }, R.identity)(reducedMonadValue);
573 }, initialValueMonad);
574 }, // Reduce each monad. This reducer operate on the monad level.
575 // The reducer function _reduceMonadsWithWhilst. It is called with the two monads and either chains
576 // them together if if the predicate passes or returns the accMonad unchanged each time once the predicate
577 // fails.
578 function () {
579 return R.addIndex(reduceFunction)(function (accumulatedMonad, currentMonad, index) {
580 return chainWith(function (accMonad, app, i) {
581 return _reduceMonadsWithWhilst(accMonad, app, i);
582 }, accumulatedMonad, currentMonad, index);
583 }, // The monad with the empty value, e.g. Maybe.Just([]) or Task.of(Result.Ok({}))
584 initialValueMonad, // The list of monads
585 list);
586 }])();
587};
588/**
589 * A version of traverseReduceWhile that prevents maximum call stack exceeded by breaking chains into buckets
590 * Normally long lists of chained tasks keep calling a new function. We need to break this up after some number
591 * of calls to prevent the maximum call stack
592 * @param {Object} config The config
593 * @param {Object} config.predicateOrObj Like ramda's reduceWhile predicate. Accepts the accumulated value and next value.
594 * These are the values of the container. If false is returned the accumulated value is returned without processing
595 * more values. Be aware that for Tasks the task must run to predicate on the result, so plan to check the previous
596 * task to prevent a certain task from running
597 * @param {Boolean} [config.accumulateAfterPredicateFail] Default false. Because of Tasks, we have a boolean here to allow accumulation after
598 * the predicate fails. The default behavior is to not accumulate the value of a failed predicate. This makes
599 * sense for things like Result where there is no consequence of evaluating them. But we have to run a Task to
600 * evaluate it so we might want to quit after the previous task but also add that task result to the accumulation.
601 * In that case set this true
602 * @param {Function} [config.mappingFunction] Defaults to R.map. The function used to each monad result from list.
603 * If the accumulator does not create a new monad then R.map is sufficient. However if the accumulator does create
604 * a new monad this should be set to R.chain so that the resulting monad isn't put inside the monad result
605 * @param {Function} [config.chainTogetherWith] Defaults to _chainTogetherWith. Only needs to be overridden
606 * for high stack count chaining that needs to be broken up to avoid max stack trace
607 * @param {Function} accumulator The accumulator function expecting the reduced monad and nonad
608 * @param {Object} initialValue The initial value monad
609 * @param {[Object]} list The list of monads
610 * @return {Object} The reduced monad
611 */
612
613var traverseReduceWhileBucketed = function traverseReduceWhileBucketed(config, accumulator, initialValue, list) {
614 return traverseReduceWhile(R.merge(config, {
615 reducer: _reduceMonadsChainedBucketed({})
616 }), accumulator, initialValue, list);
617};
618/**
619 * Version of traverseReduceWhileBucketed that breaks tasks chaining with timeouts to prevent max stack trace errors
620 * @param {Object} config The config
621 * @param {Boolean} [config.accumulateAfterPredicateFail] Default false. Because of Tasks, we have a boolean here to allow accumulation after
622 * the predicate fails. The default behavior is to not accumulate the value of a failed predicate. This makes
623 * sense for things like Result where there is no consequence of evaluating them. But we have to run a Task to
624 * evaluate it so we might want to quit after the previous task but also add that task result to the accumulation.
625 * In that case set this true
626 * @param {Function} [config.mappingFunction] Defaults to R.map. The function used to each monad result from list.
627 * If the accumulator does not create a new monad then R.map is sufficient. However if the accumulator does create
628 * a new monad this should be set to R.chain so that the resulting monad isn't put inside the monad result
629 * @param {Function} [config.chainTogetherWith] Defaults to _chainTogetherWithTaskDelay
630 * @param {Function} accumulator The accumulator function expecting the reduced task and task
631 * @param {Object} initialValue The initial value task
632 * @param {[Object]} list The list of tasks
633 * @return {Object} The reduced task
634 * @return {Object} The reduced monad
635 */
636
637var traverseReduceWhileBucketedTasks = function traverseReduceWhileBucketedTasks(config, accumulator, initialValue, list) {
638 // If config.mappingFunction is already R.chain, we can compose with chain since the accumulator is returning
639 // a monad. If not the use composeWithMapMDeep so that the given accumulator can returns its value but our
640 // composed accumulator returns a task
641 var accumulatorComposeChainOrMap = R.ifElse(R.equals(R.chain), function () {
642 return composeWithChainMDeep;
643 }, function () {
644 return composeWithMapMDeep;
645 })(R.propOr(null, 'mappingFunction', config));
646 return traverseReduceWhileBucketed(R.merge(config, {
647 monadConstructor: task.of,
648 // This has to be chain so we can return a task in our accumulator
649 mappingFunction: R.chain,
650 // This adds a timeout in the chaining process to avoid max stack trace problems
651 chainTogetherWith: _chainTogetherWithTaskDelay
652 }), // Call the timeout task to break the stacktrace chain. Then call the accumulator with the normal inputs.
653 // Always returns a task no matter if the accumulator does or not
654 function (accum, current) {
655 return accumulatorComposeChainOrMap(1, [function (_ref10) {
656 var _ref11 = functions._slicedToArray(_ref10, 2),
657 a = _ref11[0],
658 c = _ref11[1];
659
660 return accumulator(a, c);
661 }, function (_ref12) {
662 var _ref13 = functions._slicedToArray(_ref12, 2),
663 a = _ref13[0],
664 c = _ref13[1];
665
666 return timeoutTask([a, c]);
667 }])([accum, current]);
668 }, initialValue, list);
669};
670/**
671 * Used by traverseReduceWhile to chain the accumulated monad with each subsequent monad.
672 * If the value of the accumulated monad is @@transducer/reduced. The chaining is short-circuited so that
673 * all subsequent values are ignored
674 * @param {Object} config The config
675 * @param {Function|Object} config.predicateOrObj See traverseReduceWhile
676 * @param {Function} config.accumulator The accumulator
677 * @returns {Function} A function expecting (accumulatedMonad, applicator, index) This function is called with
678 * each accumulatedMonad and applicator by traverseReduceWhile. The index is the monad index
679 * @private
680 */
681
682var _reduceMonadsWithWhile = function _reduceMonadsWithWhile(_ref14) {
683 var predicateOrObj = _ref14.predicateOrObj,
684 accumulator = _ref14.accumulator,
685 initialValueMonad = _ref14.initialValueMonad;
686
687 // Determine if predicateOrObj is just a function or also an object
688 var _R$ifElse = R.ifElse(R.is(Function), function () {
689 return {
690 predicate: predicateOrObj,
691 accumulateAfterPredicateFail: false
692 };
693 }, R.identity)(predicateOrObj),
694 predicate = _R$ifElse.predicate,
695 accumulateAfterPredicateFail = _R$ifElse.accumulateAfterPredicateFail; // Map the applicator below with R.map unless an override like R.chain is specified
696
697
698 var mappingFunction = R.propOr(R.map, 'mappingFunction', predicateOrObj);
699 var monadConstructor = R.propOr(R.identity, 'monadConstructor', predicateOrObj);
700 var chainTogetherWith = R.propOr(_chainTogetherWith, 'chainTogetherWith', predicateOrObj);
701 return function (accumulatedMonad, applicator, index) {
702 return R.chain(function (accumulatedValue) {
703 return R.ifElse(R.prop('@@transducer/reduced'), // Done, we can't quit reducing since we're chaining monads. Instead we keep chaining the initialValueMonad
704 // and always return the same accumulatedMonad, meaning the @@transducer/reduced valued monad
705 // We use chainWith to allow breaks in chains for tasks that would otherwise cause a stack overflow
706 function (accValue) {
707 // Always returns the same thing, but break the chain occasionally to prevent stack overflow
708 return chainTogetherWith(function () {
709 return initialValueMonad.map(function () {
710 return accValue;
711 });
712 }, null, null, index);
713 }, function (accValue) {
714 return mappingFunction(function (value) {
715 // If the applicator's value passes the predicate, accumulate it and process the next item
716 // Otherwise we stop reducing by returning R.reduced()
717 return R.ifElse(function (v) {
718 return predicate(accValue, v);
719 }, function (v) {
720 return accumulator(accValue, v);
721 }, // We have to detect this above ourselves. R.reduce can't see it for deferred types like Task
722 // IF the user wants to add v to the accumulation after predicate failure, do it.
723 function (v) {
724 // Use monadConstructor if is false so we return the right monad type if specified
725 return (accumulateAfterPredicateFail ? R.identity : monadConstructor)(R.reduced(accumulateAfterPredicateFail ? accumulator(accValue, v) : accValue));
726 })(value);
727 }, // map or chain this
728 applicator);
729 })(accumulatedValue);
730 }, // Chain this
731 accumulatedMonad);
732 };
733};
734/**
735 * Like traverseReduceDeep but also accepts an accumulate to deal with Result.Error objects.
736 * Like traverseReduceDeep accumulator is called on Result.Ok values, but Result.Error values are passed to
737 * accumulatorError. The returned value is within the outer container(s): {Ok: accumulator result, Error: errorAccumulator result}
738 *
739 * Example:
740 * traverseReduceDeepResults(2, R.flip(R.append), R.concat, Task.of({Ok: Result.Ok([]), Error: Result.Error('')}), [Task.of(Result.Ok(1))), Task.of(Result.Error('a')),
741 * Task.of(Result.Ok(2)), Task.of(Result.Error('b')])
742 * returns Task.of({Ok: Result.Ok([1, 2]), Error: Result.Error('ab')})
743 *
744 * @param {Function} accumulator Accepts a reduced applicative and each result that is a Result.Ok of reducer, then returns the new reduced applicative
745 * Container C v => v -> v -> v where the Container at containerDepth must be a Result
746 * @param {Function} accumulatorForErrors Accepts a reduced applicative and each result that is a Result.Error of reducer, then returns the new reduced applicative
747 * Container C v => v -> v -> v where the Container at containerDepth must be a Result
748 * @param {Object} initialValue A container to be the initial reduced values of accumulators. This must match the
749 * expected container type up to the Result and have an object with two initial Results: {Ok: initial Result.Ok, Error: initial Result.Error}
750 * @param {[Object]} list List of containers. The list does not itself count as a container toward containerDepth. So a list of Tasks of Results is still level containerDepth: 2
751 * @returns {Object} The value resulting from traversing and reducing
752 * @sig traverseReduceDeep:: Number N, N-Depth-Container C v => N -> (v -> v -> v) -> C v -> [C v] -> C v
753 */
754
755
756var traverseReduceDeepResults = R.curry(function (containerDepth, accumulator, accumulatorForErrors, initialValue, deepContainers) {
757 return R.reduce(function (applicatorRes, applicator) {
758 var f = R.ifElse(function (d) {
759 return R.gt(d, 1);
760 }, // Compose levels
761 function () {
762 return R.compose;
763 }, // Just 1 level, no need to lift
764 function () {
765 return function () {
766 return R.identity;
767 };
768 })(containerDepth);
769 var composed = f.apply(void 0, functions._toConsumableArray(R.times(R.always(R.liftN(2)), containerDepth - 1)));
770 return composed(function (accumulatedObj, result) {
771 return result.matchWith({
772 Ok: function Ok(_ref15) {
773 var value = _ref15.value;
774 return {
775 Ok: accumulator(accumulatedObj.Ok, value),
776 Error: accumulatedObj.Error
777 };
778 },
779 Error: function Error(_ref16) {
780 var value = _ref16.value;
781 return {
782 Error: accumulatorForErrors(accumulatedObj.Error, value),
783 Ok: accumulatedObj.Ok
784 };
785 }
786 });
787 })(applicatorRes, applicator);
788 }, initialValue, deepContainers);
789});
790/**
791 * Converts objects with monad values into list of [M [k,v]]
792 * @param {Function} monadConstructor Constructs the one-level monad, e.g. Result.Ok
793 * @param {Object} objOfMonads Object with String keys and value that are monads matching that of the constructor
794 * e.g. {a: Result.Ok(1), b: Result.Ok(2)}
795 * @returns {[Object]} A list of the same type of Monads but containing an array with one key value pair
796 * e.g. [Result.Ok([['a',1]]), Result.Ok(['b', 2])]
797 * @sig objOfMLevelDeepMonadsToListWithPairs:: Monad M, String k => <k, M v> -> [M [k, v] ]
798 */
799
800var objOfMLevelDeepMonadsToListWithPairs = R.curry(function (monadDepth, monadConstructor, objOfMonads) {
801 // Lifts k, which is not a monad, to the level of the monad v, then combines them into a single pair array,
802 // which is returned wrapped with the monad constructor
803 var liftKeyIntoMonad = lift1stOf2ForMDeepMonad(monadDepth, monadConstructor, function (k, v) {
804 return [k, v];
805 }); // Here we map each key into a monad with its value, converting the k, v to an array with one pair
806 // Object <k, (Result (Maybe v))> -> [Result (Maybe [[k, v]]) ]
807
808 return R.map(function (_ref17) {
809 var _ref18 = functions._slicedToArray(_ref17, 2),
810 k = _ref18[0],
811 v = _ref18[1];
812
813 return liftKeyIntoMonad(k, v);
814 }, R.toPairs(objOfMonads));
815});
816/**
817 * Handles objects whose values are lists of monads by sequencing each list of monads into a single monad
818 * and then packaging the keys into the monad as well
819 * @param {Number} monadDepth The depth of the monad for each item of the array for each value.
820 * @param {Function} monadConstructor Constructs the monad so that the key can be combined with the values monad
821 * @param {Function} objOfMonads Objects whose values are list of monads
822 * @returns {[Monad]} A list of monads each containing and origianl key value in the form monad [[k, values]]].
823 * This is thus an array with one pair, where the pair contains a key and values
824 * @sig objOfMLevelDeepListOfMonadsToListWithPairs:: Monad M, String k => [<k, [M<v>]>] -> [M [k, [v]]]
825 * Example {a: [Maybe.Just(1), Maybe.Just(2)], b: [Maybe.Just(3), Maybe.Just(4)]} becomes
826 * [Maybe.Just(['a', [1, 2]]], Maybe.Just(['b', [3, 4]])]
827 */
828
829var objOfMLevelDeepListOfMonadsToListWithPairs = R.curry(function (monadDepth, monadConstructor, objOfMonads) {
830 // Here String k:: k -> [v] -> monadConstructor [k, [v]]
831 // So we lift k to the monad level and create a pair with the array of values
832 var liftKeyIntoMonad = lift1stOf2ForMDeepMonad(monadDepth, monadConstructor, function (k, values) {
833 return R.prepend(k, [values]);
834 });
835 return R.compose(R.map(function (_ref19) {
836 var _ref20 = functions._slicedToArray(_ref19, 2),
837 k = _ref20[0],
838 v = _ref20[1];
839
840 return liftKeyIntoMonad(k, v);
841 }), R.toPairs, // Map each value and then sequence each monad of the value into a single monad containing an array of values
842 // Monad m:: <k, [m v]> -> <k, m [v]>
843 R.map(function (monadValues) {
844 return traverseReduceDeep(monadDepth, // Prev is an array of previous monad values. Next is a value from monadValues
845 function (prev, next) {
846 return R.append(next, prev);
847 }, monadConstructor([]), monadValues);
848 }))(objOfMonads);
849});
850/**
851 * Like objOfMLevelDeepListOfMonadsToListWithPairs but where the input is already pairs
852 * The monad depth should be the depth of each monad in each list + 1 where 1 accounts for each list, which we
853 * want to treat as a monad layer.
854 */
855
856var pairsOfMLevelDeepListOfMonadsToListWithPairs = R.curry(function (monadDepth, monadConstructor, pairsOfMonads) {
857 // Here String k:: k -> [v] -> monadConstructor [k, [v]]
858 // So we lift k to the monad level and create a pair with the array of values
859 var liftKeyIntoMonad = lift1stOf2ForMDeepMonad(monadDepth, monadConstructor, function (k, values) {
860 return R.prepend(k, [values]);
861 });
862 return R.compose(R.map(function (_ref21) {
863 var _ref22 = functions._slicedToArray(_ref21, 2),
864 k = _ref22[0],
865 v = _ref22[1];
866
867 return liftKeyIntoMonad(k, v);
868 }), // Map each value and then sequence each monad of the value into a single monad containing an array of values
869 // Monad m:: [k, [m v]> -> [k, m [v]]
870 function (pairs) {
871 return R.map(function (_ref23) {
872 var _ref24 = functions._slicedToArray(_ref23, 2),
873 k = _ref24[0],
874 monadValues = _ref24[1];
875
876 return [k, traverseReduceDeep(monadDepth, // Prev is an array of previous monad values. Next is a value from monadValues
877 function (prev, next) {
878 return R.append(next, prev);
879 }, monadConstructor(), monadValues)];
880 }, pairs);
881 })(pairsOfMonads);
882});
883/**
884 * Lifts an M level deep monad using the given M-level monad constructor and applies a 2 argument function f
885 * The given value is called as the first argument of f, the second argument if the unwrapped value of the monad
886 * The value returned by f is converted back to a monad. So this is essentially monad.chain(v => f(value, v)
887 * but the chaining works on the given depth. This is useful for key value pairs when the key and value
888 * need to be packaged into the monad that is the value.
889 *
890 * Inspiration: https://github.com/MostlyAdequate/mostly-adequate-guide (Ch 10)
891 * const tOfM = compose(Task.of, Maybe.of);
892 liftA2(liftA2(concat), tOfM('Rainy Days and Mondays'), tOfM(' always get me down'));
893 * Task(Maybe(Rainy Days and Mondays always get me down))
894 *
895 * @param {Number} The monad depth to process values at. Note that the given monads can be deeper than
896 * this number but the processing will occur at the depth given here
897 * @param {Function} constructor M-level deep monad constructor
898 * @param {Function} 2-arity function to combine value with the value of the last argument
899 * @param {*} value Unwrapped value as the first argument of the function
900 * @param {Object} monad Monad matching that of the constructor to apply the function(value) to
901 * @returns {Object} the mapped monad
902 * Example:
903 * const constructor = R.compose(Result.Ok, Result.Just)
904 * const myLittleResultWithMaybeAdder = lift1stOf2ForMDeepMonad(2, constructor, R.add);
905 * myLittleResultWithMaybeAdder(5)(constructor(1))) -> constructor(6);
906 * f -> Result (Just (a) ) -> Result (Just (f (value, a)))
907 */
908
909var lift1stOf2ForMDeepMonad = R.curry(function (monadDepth, constructor, f, value, monad) {
910 return R.compose.apply(R, functions._toConsumableArray(R.times(R.always(R.liftN(2)), monadDepth)))(f)(constructor(value))(monad);
911});
912/**
913 * Map based on the depth of the monad
914 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
915 * @param {Function} Mapping function that operates at the given depth.
916 * @param {Object} Monad of a least the given depth
917 * @returns {Object} The mapped monad value
918 */
919
920var mapMDeep = R.curry(function (monadDepth, f, monad) {
921 return doMDeep(monadDepth, // Wrapping for debugging visibility
922 R.curry(function (fn, functor) {
923 return R.map(fn, functor);
924 }), f, monad);
925});
926/**
927 * composeWith using mapMDeep Each function of compose will receive the object monadDepth levels deep.
928 * The function should transform the value without wrapping in monads
929 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
930 * @param {*} list List of functions that expects the unwrapped value and returns an unwrapped value
931 * @returns {Object} A function expecting the input value(s), which is/are passed to the last function of list
932 * The value returned by the first function of list wrapped in the monadDepth levels of monads
933 */
934
935var composeWithMapMDeep = function composeWithMapMDeep(monadDepth, list) {
936 // Each function, last to first, in list receives an unwrapped object and returns an unwrapped object.
937 // mapMDeep is called with each of these functions. The result of each mapMDeep is given to the next function
938 return R.composeWith(mapMDeep(monadDepth))(list);
939};
940/**
941 * composeWith using map. The final function in list (first run) must return a monad that can be mapped to the
942 * subsequent functions. The subsequent functions map the incoming value but do not return a monad.
943 * @param {*} list List of functions that expects the unwrapped value and returns an unwrapped value
944 * @returns {Object} A function expecting the input value(s), which is/are passed to the last function of list
945 */
946
947var composeWithMap = function composeWithMap(list) {
948 return composeWithMapMDeep(1, list);
949};
950/**
951 * Chain based on the depth of the monad
952 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
953 * @param {Function} Mapping function that operates at the given depth.
954 * @param {Object} Monad of a least the given depth
955 * @returns {Object} The mapped monad value
956 */
957
958var chainMDeep = R.curry(function (monadDepth, f, monad) {
959 // This prevents common error types from continuing to chain to prevent a partially chained result
960 // Add more as needed
961 var errorPredicate = function errorPredicate(mm) {
962 return R.anyPass([Result.Error.hasInstance])(mm);
963 };
964
965 return doMDeep(monadDepth, // Wrapping for debugging visibility
966 R.curry(function (fn, m) {
967 // If the error predicate returns true revert to the monad for the remaining
968 return R.ifElse(errorPredicate, function () {
969 return monad;
970 }, function (mm) {
971 return R.chain(fn, mm);
972 })(m);
973 }), f, monad);
974});
975/**
976 * chains at each level excepts maps the deepest level
977 * @param monadDepth
978 * @param f
979 * @param monad
980 * @return {*}
981 */
982
983var chainExceptMapDeepestMDeep = R.curry(function (monadDepth, f, monad) {
984 return doMDeepExceptDeepest(monadDepth, [R.chain, R.map], f, monad);
985});
986/**
987 * Map based on the depth of the monad-1 and chain the deepest level monad
988 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
989 * @param {Function} Chaining function that operates at the given depth.
990 * @param {Object} Monad of a least the given depth
991 * @returns {Object} The mapped then chained monad value
992 */
993
994var mapExceptChainDeepestMDeep = R.curry(function (monadDepth, f, monad) {
995 return doMDeepExceptDeepest(monadDepth, [R.map, R.chain], f, monad);
996});
997/**
998 * composeWith using chainMDeep Each function of compose will receive the object monadDepth levels deep.
999 * The function should transform the value without wrapping in monads
1000 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
1001 * @param {*} list List of functions that expects the unwrapped value and returns an unwrapped value
1002 * @returns {Object} A function expecting the input value(s), which is/are passed to the last function of list
1003 * The value returned by the first function of list wrapped in the monadDepth levels of monads
1004 */
1005
1006var composeWithChainMDeep = function composeWithChainMDeep(monadDepth, list) {
1007 // Each function, last to first, in list receives an unwrapped object and returns the monadDepth level deep monad
1008 // chainMDeep is called with each of these functions. The result of each chainMDeep is given to the next function
1009 return R.composeWith(function (f, res) {
1010 return chainMDeep(monadDepth, f, res);
1011 })(list);
1012};
1013/**
1014 * Composes with chain
1015 * The function should transform the value without wrapping in monads
1016 * @param {*} list List of functions that expects the unwrapped value and returns an unwrapped value
1017 * @returns {Object} A function expecting the input value(s), which is/are passed to the last function of list
1018 * The value returned by the first function of list wrapped in a monad
1019 */
1020
1021var composeWithChain = function composeWithChain(list) {
1022 return composeWithChainMDeep(1, list);
1023};
1024/**
1025 * composeWith using mapMDeep but chain the lowest level so that each function of list must return the deepest monad.
1026 * Each function of compose will receive the object monadDepth levels deep.
1027 * The last function (first called) must returned the monadDepth deep wrapped monad but subsequent ones must only
1028 * return a type of the deepest monad.
1029 * For example:
1030 * const test = composeWithMapExceptChainDeepestMDeep(2, [
1031 * // Subsequent function will only process Result.Ok
1032 * deliciousFruitOnly => Result.Ok(R.concat('still ', deliciousFruitOnly)),
1033 * // Subsequent function returns the deepest monad
1034 * testFruit => R.ifElse(R.contains('apple'), f => Result.Ok(R.concat('delicious ', f)), f => Result.Error(R.concat('disgusting ', f)))(testFruit),
1035 * // Initial function returns 2-levels deep
1036 * fruit => task.of(Result.Ok(R.concat('test ', fruit)))
1037 *])
1038 * test('apple') => task.of(Result.Ok('still delicious test apple'))
1039 * test('kumquat') => task.of(Result.Error('disgusting test kumquat'))
1040 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
1041 * @param {*} list List of functions that expects the unwrapped value and returns an unwrapped value
1042 * @returns {Object} A function expecting the input value(s), which is/are passed to the last function of list
1043 * The value returned by the first function of list wrapped in the monadDepth levels of monads
1044 */
1045
1046var composeWithMapExceptChainDeepestMDeep = function composeWithMapExceptChainDeepestMDeep(monadDepth, list) {
1047 return R.composeWith(mapExceptChainDeepestMDeep(monadDepth))(list);
1048};
1049/**
1050 * Map/Chain/Filter etc based on the depth of the monad and the iterFunction
1051 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
1052 * @param {Function} func R.map, R.chain, R.filter or similar
1053 * @param {Function} f Mapping function that operates at the given depth.
1054 * @param {Object} Monad of a least the given depth
1055 * @returns {Object} The mapped monad value
1056 */
1057
1058var doMDeep = R.curry(function (monadDepth, func, f, monad) {
1059 return R.compose.apply(R, functions._toConsumableArray(R.times(R.always(func), monadDepth)))(f)(monad);
1060});
1061/**
1062 * Map/Chain/Filter etc based on the depth of the monad and the funcPair. The deepest monad is processed
1063 * with funcPair[1] and all higher level monads are processed with funcPair[0]. This allows for a combination
1064 * such as [R.map, R.chain] to chain the deepest value but map the higher values so that the caller can
1065 * change the deepest monad type without having to wrap the unchanging outer monad types. For instance
1066 * the caller might have a monad task.of(Result.Ok) and want to convert it conditionally to task.of(Result.Error):
1067 * const test = doMDeepExceptDeepest(2, [R.map, R.chain], R.ifElse(R.equals('pear'), Result.Error, Result.Ok)(of(result)))
1068 * test(of(Result.Ok('apple'))) => of(Result.Ok('apple'))
1069 * test(of(Result.Ok('pear'))) => of(Result.Error('pear'))
1070 * Note that in this example task.of doesn't have to be used again to wrap the result
1071 * @param {Number} monadDepth 1 or greater. [1] is 1, [[1]] is 2, Result.Ok(Maybe.Just(1)) is 2
1072 * @param {[Function]} funcPair Two functions R.map, R.chain, R.filter or similar.
1073 * The first function is composed monadDepth-1 times and the last function is composed once after the others
1074 * @param {Function} f Mapping function that operates at the given depth.
1075 * @param {Object} Monad of a least the given depth
1076 * @returns {Object} The mapped monad value
1077 */
1078
1079var doMDeepExceptDeepest = R.curry(function (monadDepth, funcPair, f, monad) {
1080 return R.compose.apply(R, functions._toConsumableArray(R.times(R.always(function (func) {
1081 return function (value) {
1082 return funcPair[0](func, value);
1083 };
1084 }), monadDepth - 1)).concat([// funcPair[1] gets called with the deepest level of monad
1085 function (func) {
1086 return function (value) {
1087 return funcPair[1](func, value);
1088 };
1089 }]))(f)(monad);
1090});
1091/**
1092 * Given a monad whose return value can be mapped and a single input object,
1093 * map the monad return value to return an obj with the value at 'value', merged with input object in its original form
1094 * of the function. Example: mapToResponseAndInputs(({a, b, c}) => task.of(someValue))({a, b, c}) -> task.of({a, b, c, value: someValue})
1095 * @param {Function} f Function expecting an object and returning a monad that can be mapped
1096 * @param {Object} arg The object containing the incoming named arguments that f is called with. If null defaults to {}.
1097 * @return {Object} The value of the monad at the value key merged with the input args
1098 */
1099
1100var mapToResponseAndInputs = function mapToResponseAndInputs(f) {
1101 return function (arg) {
1102 return mapMonadByConfig({
1103 name: 'value'
1104 }, f)(arg);
1105 };
1106};
1107/**
1108 * Applies f to arg returning a monad that is at least level deep. mapMDeep(level) is then used to map the value
1109 * of the monad at that level
1110 * @param {Number} level Monadic level of 1 or greater. For instance 1 would map the 'apple' of task.of('apple'),
1111 * 2 would map the 'apple' of task.of(Result.Ok('apple')) etc. The mapped value is merged with arg and returned
1112 * @param {Function} f The function applied to arg
1113 * @param {*} arg Argument passed to f
1114 * @return {Object} The monad that result from the deep mapping
1115 */
1116
1117var mapToMergedResponseAndInputsMDeep = function mapToMergedResponseAndInputsMDeep(level, f) {
1118 return function (arg) {
1119 return mapMonadByConfig({
1120 mappingFunction: mapMDeep(level)
1121 }, f)(arg);
1122 };
1123};
1124/**
1125 * Given a monad whose return value can be mapped and a single input object,
1126 * map the monad return value to return an obj merged with input object in its original form
1127 * of the function. Example: mapToResponseAndInputs(({a, b, c}) => task.of({d: true, e: true}))({a, b, c}) -> task.of({a, b, c, d, e})
1128 * @param {Function} f Function expecting an object and returning a monad that can be mapped to an object
1129 * @param {Object} arg The object containing the incoming named arguments that f is called with. If null defaults to {}.
1130 * @return {Object} The value of the monad merged with the input args
1131 */
1132
1133var mapToMergedResponseAndInputs = function mapToMergedResponseAndInputs(f) {
1134 return function (arg) {
1135 return mapToMergedResponseAndInputsMDeep(1, f)(arg);
1136 };
1137};
1138/**
1139 * Given a monad whose return value can be mapped and a single input object,
1140 * map the monad return value to return an obj with the value at 'value', merged with input object in its original form
1141 * of the function. Example: mapToNamedResponseAndInputs('foo', ({a, b, c}) => task.of(someValue))({a, b, c}) -> task.of({a, b, c, foo: someValue})
1142 * @param {String} name The key name for the output
1143 * @param {Function} f Function expecting an object and returning a monad that can be mapped
1144 * @param {Object} arg The object containing the incoming named arguments that f is called with. If null defaults to {}.
1145 * @return {Object} The value of the monad at the value key merged with the input args
1146 */
1147
1148var mapToNamedResponseAndInputs = function mapToNamedResponseAndInputs(name, f) {
1149 return function (arg) {
1150 return mapMonadByConfig({
1151 name: name
1152 }, f)(arg);
1153 };
1154};
1155/**
1156 * Given a monad the specified levels deep whose return value can be mapped and a single input object,
1157 * map the monad return value to return an obj with the value at 'value', merged with input object in its original form
1158 * of the function. Example: mapToNamedResponseAndInputs(2, 'foo', ({a, b, c}) => task.of(Result.Ok(someValue)))({a, b, c}) -> task.of(Result.Ok({a, b, c, foo: someValue}))
1159 * @param {String} name The key name for the output
1160 * @param {Function} f Function expecting an object and returning a monad that can be mapped
1161 * @param {Object} arg The object containing the incoming named arguments that f is called with. If null defaults to {}.
1162 * @return {Object} The value of the monad at the value key merged with the input args
1163 */
1164
1165var mapToNamedResponseAndInputsMDeep = R.curry(function (level, name, f, arg) {
1166 return mapMonadByConfig({
1167 mappingFunction: mapMDeep(level),
1168 name: name
1169 }, f)(arg);
1170});
1171/**
1172 * Same as mapToNamedResponseAndInputs but works with a non-monad
1173 * @param {String} name The key name for the output
1174 * @param {Function} f Function expecting an object and returning an value that is directly merged with the other args
1175 * @param {Object} arg The object containing the incoming named arguments that f is called with. If null defaults to {}.
1176 * @return {Object} The output of f named named and merged with arg
1177 */
1178
1179var toNamedResponseAndInputs = function toNamedResponseAndInputs(name, f) {
1180 return function (arg) {
1181 var monadF = function monadF(_arg) {
1182 return maybe.Just(f(_arg));
1183 };
1184
1185 var just = mapToNamedResponseAndInputs(name, monadF)(R.when(R.isNil, function () {
1186 return {};
1187 })(arg));
1188 return just.unsafeGet();
1189 };
1190};
1191/**
1192 * Hybrid version of mapToNamedResponseAndInputs and toNamedResponseAndInputs.
1193 * Handles mapping a monad containing an object or straight object
1194 * @param {String} name The name for the key of the output value
1195 * @param {Function} f mapping function
1196 * @return {*} The monad if f(_arg) is a monad, other wise an object
1197 */
1198
1199var mapOrObjToNamedResponseAndInputs = function mapOrObjToNamedResponseAndInputs(name, f) {
1200 return function (arg) {
1201 // Wrap in a monad unless there is a map property, meaning it's already a monad
1202 var isMonad = null;
1203
1204 var monadF = function monadF(_arg) {
1205 var maybeMonad = f(_arg);
1206 isMonad = R.hasIn('map', maybeMonad); // Wrap if it wasn't a monad
1207
1208 return R.unless(function () {
1209 return isMonad;
1210 }, maybe.Just)(f(_arg));
1211 };
1212
1213 var just = mapToNamedResponseAndInputs(name, monadF)(R.when(R.isNil, function () {
1214 return {};
1215 })(arg)); // Unwrap if it wasn't a monad
1216
1217 return R.unless(function () {
1218 return isMonad;
1219 }, function (j) {
1220 return j.unsafeGet();
1221 })(just);
1222 };
1223};
1224/**
1225 * Same as toMergedResponseAndInputs but works with a non-monad
1226 * @param {Function} f Function expecting an object and returning an value that is directly merged with the other args
1227 * @param {Object} arg The object containing the incoming named arguments that f is called with. If null defaults to {}.
1228 * @return {Object} The output of f named named and merged with arg
1229 */
1230
1231var toMergedResponseAndInputs = function toMergedResponseAndInputs(f) {
1232 return function (arg) {
1233 var monadF = function monadF(_arg) {
1234 return maybe.Just(f(_arg));
1235 };
1236
1237 var just = mapToMergedResponseAndInputs(monadF)(arg);
1238 return just.unsafeGet();
1239 };
1240};
1241/**
1242 * Internal method to place a Result instance at the key designated by resultOutputKey merged with an
1243 * object remainingInputObj. This is used by mapResultMonadWithOtherInputs and for task error handling by
1244 * mapResultTaskWithOtherInputs
1245 * @param {String} [resultOutputKey] The key to use for the output Result instance. If not specified,
1246 * The result is instead merged with the remainingInputObj
1247 * @param {Object} remainingInputObj Input object that does not include the resultInputKey Result
1248 * @param {Object} result The Result instance to output
1249 * @returns {Object} remainingInputObject merged with {resultOutputKey: result} or result
1250 * @private
1251 */
1252
1253var _mapResultToOutputKey = function _mapResultToOutputKey(resultOutputKey, remainingInputObj, result) {
1254 return R.ifElse(R.always(resultOutputKey), // Leave the remainingInputObj out
1255 function (_result) {
1256 return R.merge(remainingInputObj, functions._defineProperty({}, resultOutputKey, _result));
1257 }, // If there is anything in the remainingInputObj, merge it with the result.value
1258 function (_result) {
1259 return R.map(R.merge(remainingInputObj), _result);
1260 })(result);
1261};
1262/**
1263 * For mapResultMonadWithOtherInputs separates the resultInputKey value from the other input values in inputObj
1264 * @param {String} [resultInputKey] Key indicating a Result instance in inputObj. If null then the entire inputObj
1265 * is assumed to be a Result
1266 * @param {Object} inputObj Input object containing a Result at inputObj
1267 * @returns {{remainingInputObj: *, inputResult: *}|{remainingInputObj: {}, inputResult: *}}
1268 * remainingInputObj is inputObject without resultInputKey, inputResult is the value of resultInputKey or simply
1269 * inputObject if resultInputKey is not specified
1270 * @private
1271 */
1272
1273
1274var _separateResultInputFromRemaining = function _separateResultInputFromRemaining(resultInputKey, inputObj) {
1275 if (resultInputKey) {
1276 // Omit resultInputKey since we need to process it separately
1277 return {
1278 remainingInputObj: R.omit([resultInputKey], inputObj),
1279 // inputObj[resultInputKey] must exist
1280 inputResult: throwingFunctions.reqStrPathThrowing(resultInputKey, inputObj)
1281 };
1282 } // No resultInputKey, so the the entire input is an inputObj
1283
1284
1285 return {
1286 remainingInputObj: {},
1287 inputResult: inputObj
1288 };
1289};
1290/**
1291 * Like mapToNamedResponseAndInputs but operates on one incoming Result.Ok|Error and outputs a monad with it's internal
1292 * value containing a Result along with the other unaltered input keys. If the incoming instance is a Result.Ok, it's
1293 * value is passed to f. Otherwise f is skipped.
1294 * The f function must produce monad whose internal value may or may not be a Result
1295 * If the f does not produce its own Result.Ok/Result.Error, use the flag needsFunctionOutputWrapped=true.
1296 * The logic for this function is that often we are composing a monad like a Task whose returned value might or
1297 * might not be a Result. But for the sake of composition, we always want to get a Result wrapped in a monad back
1298 * from each call of the composition. We also want to keep passing unaltered input parameters to each call in the composition
1299 *
1300 * @param {Object} inputOutputConfig
1301 * @param {String} [inputOutputConfig.resultInputKey] A key of arg that is a Result.Ok.
1302 * The value of this is passed to f with the key inputKey. If not specified all of inputObj is expected to be a result
1303 * and it's value is passed to f
1304 * @param {String} [inputOutputConfig.inputKey] A key name to use to pass arg[resultInputKey]'s value in to f. Only
1305 * specify if resultInputKey is
1306 * @param {String} [inputOutputConfig.resultOutputKey] The key name for the output,
1307 * it should have a suffix 'Result' since the output is always a Result. If not specified then the result of f
1308 * is returned instead of assigning it to resultOutputKey
1309 * @param {String} inputOutputConfig.monad Specify the outer monad, such as Task.of, in case the incoming
1310 * Result is a Result.Error and we therefore can't run f on its mapped value. This is not used on the Result that is
1311 * returned by f, since even if that is a Result.Error f will have it wrapped in the monad.
1312 * @param {Boolean} [inputOutputConfig.needsFunctionOutputWrapped] Default false. Set true if the value of the monad produced
1313 * by f is not a Result. This will map the monad's value to a Result.Ok producing a Result within a Monad
1314 * @param {Function} f Function expecting arg merged with the underlying value of result at and returning a monad that can be mapped
1315 * @param {Object} inputObj The object containing the incoming named arguments that f is called with in addition to the Result
1316 * at obj[resultInputKey] that has been mapped to its underlying value at key inputKey. obj[resultInputKey] is omitted from the
1317 * object passed to f since it's underlying value is being passed. The output of f must be a monad such as a Task but it's underlying
1318 * value must NOT be a Result, because the value will be mapped automatically to a result. If you want f to produce a
1319 * Monad<Result> instead of a Monad<value>, use chainResultToNamedResponseAndInputs
1320 * @return {Object} The value produced by f mapped to a result and assigned to resultOutputKey and the rest of the key/values
1321 * from inputObj unchanged. Note that only the value at resultOutputKey is a Result.Ok|Error
1322 * Example: See unit test
1323 *
1324 */
1325
1326
1327var mapResultMonadWithOtherInputs = R.curry( // Map Result inputObj[resultInputKey] to a merge of its value at key inputKey with inputObj (inputObj omits resultInputKey)
1328// Monad M, Result R: R a -> R M b
1329function (_ref25, f, inputObj) {
1330 var resultInputKey = _ref25.resultInputKey,
1331 inputKey = _ref25.inputKey,
1332 resultOutputKey = _ref25.resultOutputKey,
1333 wrapFunctionOutputInResult = _ref25.wrapFunctionOutputInResult,
1334 monad = _ref25.monad;
1335
1336 var _separateResultInputF = _separateResultInputFromRemaining(resultInputKey, inputObj),
1337 remainingInputObj = _separateResultInputF.remainingInputObj,
1338 inputResult = _separateResultInputF.inputResult; // If our incoming Result is a Result.Error, just wrap it in the monad with the expected resultOutputKey
1339 // This is the same resulting structure if the f produces a Result.Error
1340
1341
1342 if (Result.Error.hasInstance(inputResult)) {
1343 return inputResult.orElse(function (error) {
1344 return monad(_mapResultToOutputKey(resultOutputKey, remainingInputObj, inputResult));
1345 });
1346 }
1347
1348 return R.chain(function (value) {
1349 return R.compose(function (resultMonad) {
1350 return R.map(function (result) {
1351 return _mapResultToOutputKey(resultOutputKey, remainingInputObj, result);
1352 }, resultMonad);
1353 }, // Wrap the monad value from f in a Result if needed (if f didn't produce one)
1354 // Monad M: Result R: M b | M R b -> M R b
1355 function (outputMonad) {
1356 return R.when(R.always(wrapFunctionOutputInResult), function (mon) {
1357 return R.map(function (m) {
1358 return Result.Ok(m);
1359 }, mon);
1360 })(outputMonad);
1361 }, // Call f on the merged object
1362 // Monad M: Result R: R <k, v> -> M b | M R b
1363 function (obj) {
1364 return f(obj);
1365 }, // Merge the inputObj with the valued value
1366 // Assign the value to inputKey if it's specified
1367 // Result R: R a -> <k, v>
1368 function (v) {
1369 return R.merge(remainingInputObj, R.when(R.always(inputKey), function (vv) {
1370 return functions._defineProperty({}, inputKey, vv);
1371 })(v));
1372 })(value);
1373 }, inputResult);
1374});
1375/**
1376 * Version of mapResultMonadWithOtherInputs for Tasks as the monad and expects
1377 * resultInputKey to end in the word 'Result' so inputKey can be the same key without that ending.
1378 * If a task error occurs then a Result.Error is returned in a task with the error
1379 */
1380
1381var mapResultTaskWithOtherInputs = R.curry(function (_ref27, f, inputObj) {
1382 var resultInputKey = _ref27.resultInputKey,
1383 resultOutputKey = _ref27.resultOutputKey,
1384 wrapFunctionOutputInResult = _ref27.wrapFunctionOutputInResult;
1385 // assign inputKey to resultInputKey value minus Result if resultInputKey is specified
1386 var inputKey = R.when(R.identity, function (rik) {
1387 var key = R.replace(/Result$/, '', rik);
1388
1389 if (R.concat(key, 'Result') !== rik) {
1390 throw new Error("Expected resultInputKey to end with 'Result' but got ".concat(resultInputKey));
1391 }
1392
1393 return key;
1394 })(resultInputKey);
1395 return mapResultMonadWithOtherInputs({
1396 resultInputKey: resultInputKey,
1397 inputKey: inputKey,
1398 resultOutputKey: resultOutputKey,
1399 wrapFunctionOutputInResult: wrapFunctionOutputInResult,
1400 monad: task.of
1401 }, f, inputObj).orElse( // If the task itself fails, put the error in the resultOutputKey
1402 function (error) {
1403 // Separate the inputResult from the other input values
1404 var _separateResultInputF2 = _separateResultInputFromRemaining(resultInputKey, inputObj),
1405 remainingInputObj = _separateResultInputF2.remainingInputObj,
1406 inputResult = _separateResultInputF2.inputResult; // Create a Result.Error at resultOutputKey and wrap the object in a task. This matches the successful
1407 // outcome but with a Result.Error
1408
1409
1410 return task.of(_mapResultToOutputKey(resultOutputKey, remainingInputObj, Result.Error(error)));
1411 });
1412});
1413/**
1414 * Like mapToResponseAndInputs, but resolves the value of the monad to a certain path and gives it a name .
1415 * Given a monad whose return value can be mapped and a single input object,
1416 * map the monad return value, getting its value at strPath and giving it the key name, them merge it with the input object in its original form
1417 * of the function.
1418 * Example: mapToResponseAndInputs('billy', 'is.1.goat', ({a, b, c}) => task.of({is: [{cow: 'grass'}, {goat: 'can'}]}))({a, b, c}) ->
1419 * task.of({a, b, c, billy: 'can'})
1420 * @param {String} name The key name for the output
1421 * @param {String} strPath dot-separated path with strings and indexes
1422 * @param {Function} f Function that returns a monad
1423 * @returns {Object} The resulting monad containing the strPath value of the monad at the named key merged with the input args
1424 */
1425
1426var mapToNamedPathAndInputs = R.curry(function (name, strPath, f) {
1427 return function (arg) {
1428 return mapMonadByConfig({
1429 name: name,
1430 strPath: strPath
1431 }, f)(arg);
1432 };
1433});
1434/**
1435 * A generalized form of mapToNamedPathAndInputs and mapToNamedResponse
1436 * @param {Object} config The configuration
1437 * @param {Function} [config.mappingFunction]. Defaults to R.map, the mapping function to use to map the monad
1438 * returned by f(arg). For example R.mapMDeep(2) to map 2-level monad
1439 * @param {String} [config.name] The name to assign the result of applying the monadic function f to arg. This
1440 * name/value is merged with the incoming object arg. If omitted
1441 * @param {String} [config.strPath] Optional string path to extract a value with the value that the monad that f(arg) returns
1442 * @param {Function} [config.isMonadType] Optionaly accepts f(arg) and tests if it matches the desired monad, such
1443 * as task.of, Result.of, Array.of. Returns true or false accordingly
1444 * f(arg).
1445 * @param {Function} [config.errorMonad] Optional. If the monad returned by f(arg) doesn't match the monad,
1446 * then the errorMonad is returned containing an {f, arg, value, message} where value is the return value and message
1447 * is an error message. If config.successMonad isn't specified, this value is used if the the return value of f(arg)
1448 * lacks a .map function
1449 * @param {Function} f The monadic function to apply to arg
1450 * @param {Object} arg The argument to pass to f. No that this argument must be called on the result of such as:
1451 * mapMonadByConfig(config, f)(arg)
1452 * @return {Object} The monad or error monad value or throws
1453 */
1454
1455var mapMonadByConfig = function mapMonadByConfig(_ref28, f) {
1456 var mappingFunction = _ref28.mappingFunction,
1457 name = _ref28.name,
1458 strPath = _ref28.strPath,
1459 isMonadType = _ref28.isMonadType,
1460 errorMonad = _ref28.errorMonad;
1461 return function (arg) {
1462 return R.defaultTo(R.map, mappingFunction)( // Container value
1463 function (value) {
1464 // If name is not specified, value must be an object
1465 // If strPath is specified, value must be an object
1466 if (R.both(function (v) {
1467 return R.not(R.is(Object, v));
1468 }, function () {
1469 return R.either(function (_ref29) {
1470 var n = _ref29.name;
1471 return R.isNil(n);
1472 }, function (_ref30) {
1473 var s = _ref30.strPath;
1474 return s;
1475 })({
1476 name: name,
1477 strPath: strPath
1478 });
1479 })(value)) {
1480 var message;
1481 message = "value ".concat(util.inspect(value), " is not an object. arg: ").concat(util.inspect(arg), ", f: ").concat(f);
1482
1483 if (errorMonad) {
1484 // return the errorMonad if defined
1485 return errorMonad({
1486 f: f,
1487 arg: arg,
1488 value: value,
1489 message: message
1490 });
1491 }
1492
1493 throw new Error(message);
1494 } // Merge the current args with the value object, or the value at name,
1495 // first optionally extracting what is at strPath
1496
1497
1498 var resolvedValue = R.when(function () {
1499 return strPath;
1500 }, function (v) {
1501 try {
1502 return throwingFunctions.reqStrPathThrowing(strPath, v);
1503 } catch (e) {
1504 console.error("Function ".concat(f, " did not produce a value at ").concat(strPath)); // eslint-disable-line no-console
1505
1506 throw e;
1507 }
1508 })(value);
1509 return R.merge(arg, R.when(function () {
1510 return name;
1511 }, function (v) {
1512 return functions._defineProperty({}, name, v);
1513 })(resolvedValue));
1514 }, // Call f(arg), raising an exception if it doesn't return a monad
1515 applyMonadicFunction({
1516 isMonadType: isMonadType,
1517 errorMonad: errorMonad
1518 }, f, arg));
1519 };
1520};
1521/**
1522 *
1523 * @param {Object} config The configuration
1524 * @param {Function} [config.isMonadType] if specified the result of f(arg) is applied to it to see if f(arg) returns
1525 * the right type. Returns a boolean
1526 * @param {Object} [config.errorMonad] if f(arg) doesn't match the type of config.successMonad or if config.successMonad
1527 * is not specified but the returned value of f(arg) lacks a map method, this type is called with the given values:
1528 * {f, arg, value, message} where value is the return value of f(arg) and message is an error message
1529 * @param {Function} f Expects a single argument and returns a monad
1530 * @param {*} arg The argument. If this is mistakenly null it will be made {}
1531 * @return {*} The monad
1532 */
1533
1534var applyMonadicFunction = function applyMonadicFunction(_ref32, f, arg) {
1535 var isMonadType = _ref32.isMonadType,
1536 errorMonad = _ref32.errorMonad;
1537 return R.unless(function (value) {
1538 return R.both(function (v) {
1539 return R.is(Object, v);
1540 }, function (v) {
1541 return R.ifElse(function () {
1542 return isMonadType;
1543 }, // If successMonad is specified check that the value matches its type
1544 function (vv) {
1545 return isMonadType(vv);
1546 }, // Otherwise just check that it has a map function
1547 function (vv) {
1548 return R.hasIn('map', vv);
1549 })(v);
1550 })(value);
1551 }, function (value) {
1552 var message = "mapToNamedPathAndInputs: function f with args: ".concat(util.inspect(arg), " returned value ").concat(util.inspect(value), ", which lacks a .map() function, meaning it is not a monad. Make sure the return value is the desired monad type: task, array, etc");
1553
1554 if (errorMonad) {
1555 return errorMonad({
1556 f: f,
1557 arg: arg,
1558 value: value,
1559 message: message
1560 });
1561 }
1562
1563 throw new TypeError(message);
1564 } // Default arg to {} if null
1565 )(f(R.when(R.isNil, function () {
1566 return {};
1567 })(arg)));
1568};
1569/**
1570 * Calls a function that returns a monad and maps the result to the given string path
1571 * @param {String} strPath dot-separated path with strings and indexes
1572 * @param {Function} f Function that returns a monad
1573 * @returns {*} The monad containing the value at the string path
1574 */
1575
1576var mapToPath = R.curry(function (strPath, monad) {
1577 return R.map( // Container value
1578 function (value) {
1579 // Find the path in the returned value
1580 return throwingFunctions.reqStrPathThrowing(strPath, value);
1581 }, monad);
1582});
1583/**
1584 * Calls a function with arg that returns a monad and maps the result to the given string path.
1585 * The input values are not returned, just the mapped value
1586 * @param {String} strPath dot-separated path with strings and indexes
1587 * @param {Function} f Function that returns a monad
1588 * @param {*} arg Passed to f
1589 * @returns {*} The monad containing the value at the string path
1590 */
1591
1592var mapWithArgToPath = R.curry(function (strPath, f) {
1593 return function (arg) {
1594 return R.map( // Container value
1595 function (value) {
1596 // Find the path in the returned value
1597 return throwingFunctions.reqStrPathThrowing(strPath, value);
1598 }, // Call f(arg), raising an exception if it doesn't return a monad
1599 applyMonadicFunction({}, f, arg));
1600 };
1601});
1602/**
1603 * Versions of task.waitAll that divides tasks into 100 buckets to prevent stack overflow since waitAll
1604 * chains all tasks together
1605 * @param {Task} tasks A list of tasks
1606 * @param {Number} [buckets] Default to 100. If there are 1 million tasks we probably need 100,000 buckets to
1607 * keep stacks to 100 lines
1608 * @returns {*} The list of tasks to be processed without blowing the stack limit
1609 */
1610
1611var waitAllBucketed = function waitAllBucketed(tasks) {
1612 var buckets = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 100;
1613 var taskSets = R.reduceBy(function (acc, _ref33) {
1614 var _ref34 = functions._slicedToArray(_ref33, 2),
1615 tsk = _ref34[0],
1616 i = _ref34[1];
1617
1618 return R.concat(acc, [tsk]);
1619 }, [], function (_ref35) {
1620 var _ref36 = functions._slicedToArray(_ref35, 2),
1621 _ = _ref36[0],
1622 i = _ref36[1];
1623
1624 return i.toString();
1625 }, R.addIndex(R.map)(function (tsk, i) {
1626 return [tsk, i % buckets];
1627 }, tasks));
1628 return R.map( // Chain the resultSets together
1629 // Task t:: t[[a]] -> t[a]
1630 function (resultSets) {
1631 return R.chain(R.identity, resultSets);
1632 }, // Task t:: [t[a]] -> t[[a]]
1633 R.traverse(task.of, // Do a normal waitAll for each bucket of tasks
1634 // to run them all in parallel
1635 // Task t:: [t] -> t [a]
1636 R.ifElse( // If we have more than 100 buckets recurse on a tenth
1637 function (ts) {
1638 return R.compose(R.lt(100), R.length)(ts);
1639 }, function (ts) {
1640 return waitAllBucketed(ts, buckets / 10);
1641 }, function (ts) {
1642 return task.waitAll(ts);
1643 }), // Remove the bucket keys
1644 // Task t:: <k, [t]> -> [[t]]
1645 R.values(taskSets)));
1646};
1647/**
1648 *
1649 * Buckets the given monads into bucketCount number buckets
1650 * @param {Number} bucketSize The number of moands in each bucket
1651 * @param {[Object]} monads The monads to bucket
1652 * @return {[[Object]]} Lists of monads
1653 */
1654
1655var bucketedMonadSets = function bucketedMonadSets(bucketSize, monads) {
1656 return R.compose( // Remove the keys
1657 R.values, function (ms) {
1658 return R.reduceBy(function (acc, _ref37) {
1659 var _ref38 = functions._slicedToArray(_ref37, 2),
1660 mms = _ref38[0],
1661 i = _ref38[1];
1662
1663 return R.concat(acc, [mms]);
1664 }, [], function (_ref39) {
1665 var _ref40 = functions._slicedToArray(_ref39, 2),
1666 _ = _ref40[0],
1667 i = _ref40[1];
1668
1669 return i.toString();
1670 }, // Create pairs where the second value is the index / bucketCount
1671 // This lets us dived the first bucketCount monads into once bucket, the next bucketCounts into the next
1672 // bucket, etc
1673 R.addIndex(R.map)(function (monad, monadIndex) {
1674 return [monad, Math.floor(monadIndex / bucketSize)];
1675 }, ms));
1676 })(monads);
1677};
1678/**
1679 * Versions of R.sequence that divides tasks into 100 buckets to prevent stack overflow since waitAll
1680 * chains all tasks together. waitAllSequentiallyBucketed runs tasks sequentially not concurrently
1681 * @param {Object} config The configuration
1682 * @param {Number} [config.buckets] Default to R.length(monads) / 100 The number of buckets to divide monads into
1683 * @param {Object} [config.monadType] The monad type to pass to R.traverse. E.g. Task.of, Result.Ok, Maybe.Just
1684 * @param {[Object]} monads A list of monads
1685 * @returns {*} The list of monads to be processed without blowing the stack limit
1686 */
1687
1688
1689var sequenceBucketed = function sequenceBucketed(_ref41, monads) {
1690 var buckets = _ref41.buckets,
1691 monadType = _ref41.monadType;
1692 var bucketSize = buckets || Math.floor(R.length(monads) / 100);
1693 var monadSets = bucketedMonadSets(bucketSize, monads);
1694
1695 if (!monadType) {
1696 throw new Error('config.monadType is not specified. It is required for sequencing');
1697 }
1698
1699 return R.map( // Chain the resultSets together
1700 // Monad m:: m[[a]] -> m[a]
1701 function (resultSets) {
1702 return R.chain(R.identity, resultSets);
1703 }, // Process each set of monads with R.sequence (end case) or recurse with sequenceBucket with smaller bucket size
1704 // Monad m:: [m[a]] -> m[[a]]
1705 R.traverse(monadType, // Monad m:: [m] -> m [a]
1706 R.ifElse( // If we have more than 100 monads recurse, limiting the bucket size to 1 / 10 the current bucket size
1707 function (m) {
1708 return R.compose(R.lt(100), R.length)(m);
1709 }, function (m) {
1710 return sequenceBucketed({
1711 monadType: monadType,
1712 buckets: bucketSize / 10
1713 }, m);
1714 }, // Do a normal R.sequence for each bucket of monads
1715 // to run them all in sequence
1716 function (m) {
1717 return R.sequence(monadType, m);
1718 }), monadSets));
1719}; // given an array of something and a transform function that takes an item from
1720// the array and turns it into a task, run all the tasks in sequence.
1721// inSequence :: (a -> Task) -> Array<a> -> Task
1722
1723/* export const chainInSequence = R.curry((chainedTasks, task) => {
1724 let log = [];
1725
1726 R.chain(
1727 reducedValue => task
1728 chainedTasks;
1729)
1730
1731 return items.reduce((pipe, item, i) => {
1732 // build up a chain of tasks
1733 return (
1734 pipe
1735 // transform this item to a task
1736 .chain(() => transform(item))
1737 // after it's done, push the result to and return the log
1738 .map(result => {
1739 log.push(result);
1740 return log;
1741 })
1742 );
1743 }, Task.of("start"));
1744})*/
1745
1746/*
1747export function liftObjDeep(obj, keys = []) {
1748 if (R.anyPass([Array.isArray, R.complement(R.is)(Object)])(obj)) {
1749 return R.cond([
1750 [Array.isArray,
1751 a => R.liftN(R.length(keys) + 1, (...args) => args)(R.addIndex(R.map)(
1752 (v, k) => v,
1753 a
1754 ))
1755 ],
1756 [R.T,
1757 o => R.liftN(R.length(keys) + 1, (...args) => args)([o])
1758 ]
1759 ])(obj);
1760 }
1761
1762 // Get all combinations at this level. To do this we look at array values and scalars
1763 // We put the scalar in a single array
1764 return R.compose(
1765 R.when(
1766 pairs => {
1767 return R.compose(R.lt(1), R.length)(pairs);
1768 },
1769 pairs => R.liftN(R.length(pairs), (...args) => [...args])(...R.map(R.compose(Array.of, R.last), pairs))
1770 ),
1771 //R.toPairs,
1772 //R.map(R.unless(Array.isArray, Array.of)),
1773 o => chainObjToValues(
1774 (v, k) => liftObjDeep(v, R.concat(keys, [k])),
1775 o
1776 )
1777 )(obj);
1778};
1779*/
1780
1781/**
1782 * Converts a list of result tasks to a single task containing {Ok: objects, Error: objects}
1783 * @param {[Task<Result<Object>>]} resultTasks List of Tasks resolving to a Result.Ok or Result.Error
1784 * @return {Task<Object>} The Task that resolves to {Ok: objects, Error: objects}
1785 */
1786
1787var resultTasksToResultObjTask = function resultTasksToResultObjTask(resultTasks) {
1788 return traverseReduceWhileBucketedTasks({
1789 predicate: R.always(true)
1790 }, // The accumulator
1791 function (_ref42, result) {
1792 var oks = _ref42.Ok,
1793 errors = _ref42.Error;
1794 return result.matchWith({
1795 Ok: function Ok(_ref43) {
1796 var value = _ref43.value;
1797 return {
1798 Ok: R.concat(oks, [value]),
1799 Error: errors
1800 };
1801 },
1802 Error: function Error(_ref44) {
1803 var value = _ref44.value;
1804 return {
1805 Ok: oks,
1806 Error: R.concat(errors, [value])
1807 };
1808 }
1809 });
1810 }, task.of({
1811 Ok: [],
1812 Error: []
1813 }), resultTasks);
1814};
1815/**
1816 * Converts a list of results to a single result containing {Ok: objects, Error: objects}
1817 * @param {[Result<Object>]} results List of Tasks resolving to a Result.Ok or Result.Error
1818 * @return {Object} The Task that resolves to {Ok: objects, Error: objects}
1819 */
1820
1821var resultsToResultObj = function resultsToResultObj(results) {
1822 return traverseReduceDeepResults(1, // The accumulator
1823 function (res, location) {
1824 return R.concat(res, [location]);
1825 }, // The accumulator of errors
1826 function (res, errorObj) {
1827 return R.concat(res, [errorObj]);
1828 }, {
1829 Ok: [],
1830 Error: []
1831 }, results);
1832};
1833/**
1834 * Run the given task the given number of times until it succeeds. If after the give times it still rejects then
1835 * reject with the accumulated errors of each failed run
1836 * @param {Object} tsk Task to run multiply times
1837 * @param {Number} times The number of times to run the tasks
1838 * @param {[Object]} [errors] Optional place to push errors
1839 * @return {Task <Object>} Returns a task that resolves task or rejects
1840 */
1841
1842var retryTask = function retryTask(tsk, times, errors) {
1843 var errs = errors || [];
1844
1845 var _retryTask = function _retryTask(_times) {
1846 return tsk.orElse(function (reason) {
1847 errs.push(reason);
1848
1849 if (_times > 1) {
1850 return _retryTask(_times - 1);
1851 }
1852
1853 return task.rejected("Task failed after ".concat(_times, " tries ").concat(R.join('\n', R.map(function (error) {
1854 return stringifyError(error);
1855 }, errs))));
1856 });
1857 };
1858
1859 return _retryTask(times);
1860};
1861
1862exports.applyMonadicFunction = applyMonadicFunction;
1863exports.chainExceptMapDeepestMDeep = chainExceptMapDeepestMDeep;
1864exports.chainMDeep = chainMDeep;
1865exports.composeWithChain = composeWithChain;
1866exports.composeWithChainMDeep = composeWithChainMDeep;
1867exports.composeWithMap = composeWithMap;
1868exports.composeWithMapExceptChainDeepestMDeep = composeWithMapExceptChainDeepestMDeep;
1869exports.composeWithMapMDeep = composeWithMapMDeep;
1870exports.defaultOnCancelled = defaultOnCancelled;
1871exports.defaultOnRejected = defaultOnRejected;
1872exports.defaultRunConfig = defaultRunConfig;
1873exports.defaultRunToResultConfig = defaultRunToResultConfig;
1874exports.doMDeep = doMDeep;
1875exports.doMDeepExceptDeepest = doMDeepExceptDeepest;
1876exports.lift1stOf2ForMDeepMonad = lift1stOf2ForMDeepMonad;
1877exports.mapExceptChainDeepestMDeep = mapExceptChainDeepestMDeep;
1878exports.mapMDeep = mapMDeep;
1879exports.mapMonadByConfig = mapMonadByConfig;
1880exports.mapOrObjToNamedResponseAndInputs = mapOrObjToNamedResponseAndInputs;
1881exports.mapResultMonadWithOtherInputs = mapResultMonadWithOtherInputs;
1882exports.mapResultTaskWithOtherInputs = mapResultTaskWithOtherInputs;
1883exports.mapToMergedResponseAndInputs = mapToMergedResponseAndInputs;
1884exports.mapToMergedResponseAndInputsMDeep = mapToMergedResponseAndInputsMDeep;
1885exports.mapToNamedPathAndInputs = mapToNamedPathAndInputs;
1886exports.mapToNamedResponseAndInputs = mapToNamedResponseAndInputs;
1887exports.mapToNamedResponseAndInputsMDeep = mapToNamedResponseAndInputsMDeep;
1888exports.mapToPath = mapToPath;
1889exports.mapToResponseAndInputs = mapToResponseAndInputs;
1890exports.mapWithArgToPath = mapWithArgToPath;
1891exports.objOfMLevelDeepListOfMonadsToListWithPairs = objOfMLevelDeepListOfMonadsToListWithPairs;
1892exports.objOfMLevelDeepMonadsToListWithPairs = objOfMLevelDeepMonadsToListWithPairs;
1893exports.pairsOfMLevelDeepListOfMonadsToListWithPairs = pairsOfMLevelDeepListOfMonadsToListWithPairs;
1894exports.promiseToTask = promiseToTask;
1895exports.resultTasksToResultObjTask = resultTasksToResultObjTask;
1896exports.resultToTask = resultToTask;
1897exports.resultToTaskNeedingResult = resultToTaskNeedingResult;
1898exports.resultToTaskWithResult = resultToTaskWithResult;
1899exports.resultsToResultObj = resultsToResultObj;
1900exports.retryTask = retryTask;
1901exports.sequenceBucketed = sequenceBucketed;
1902exports.stringifyError = stringifyError;
1903exports.taskToPromise = taskToPromise;
1904exports.taskToResultTask = taskToResultTask;
1905exports.timeoutTask = timeoutTask;
1906exports.toMergedResponseAndInputs = toMergedResponseAndInputs;
1907exports.toNamedResponseAndInputs = toNamedResponseAndInputs;
1908exports.traverseReduce = traverseReduce;
1909exports.traverseReduceDeep = traverseReduceDeep;
1910exports.traverseReduceDeepResults = traverseReduceDeepResults;
1911exports.traverseReduceError = traverseReduceError;
1912exports.traverseReduceResultError = traverseReduceResultError;
1913exports.traverseReduceWhile = traverseReduceWhile;
1914exports.traverseReduceWhileBucketed = traverseReduceWhileBucketed;
1915exports.traverseReduceWhileBucketedTasks = traverseReduceWhileBucketedTasks;
1916exports.waitAllBucketed = waitAllBucketed;
1917//# sourceMappingURL=monadHelpers-e0c6950d.js.map