aquameta
Version:
Node implementation of the Aquameta server
1,799 lines (1,674 loc) • 117 kB
JavaScript
/*
aquameta
v0.1.20
https://github.com/mickburks/node-aquameta
MIT License
*/
import nodeFetch from 'node-fetch';
import pg from '@micburks/pg';
import url from 'url';
import Router from 'koa-router';
import bodyParser from 'koa-bodyparser';
import Koa from 'koa';
import mount from 'koa-mount';
const fetch = nodeFetch;
/**
* Fetch query results client-side
* @returns {Promise}
*/
async function executeEndpoint(client, query) {
const requestUrl = `/${client.url}/${client.version}/${query.url}`.replace(/\/+/g, '/') // remove duplicate slashes
.replace(/\/$/, ''); // remove tail slash
const queryString = getQueryString(query.args);
const options = {
method: query.method,
credentials: 'same-origin',
headers: {
'Content-Type': 'application/json'
}
};
if (query.data) {
options.body = JSON.stringify(query.data);
}
try {
console.log(`endpoint: trying to fetch ${requestUrl}`, options);
const response = await fetch(queryString ? `${requestUrl}?${queryString}` : requestUrl, options); // Let client deal with status codes
if (client.rawResponse) {
return response;
} else {
return response.json().then(r => {
return r.result.map(({
row
}) => row);
});
}
} catch (error) {
// Log error in collapsed group
console.groupCollapsed(query.method, error.statusCode, error.title);
if ('message' in error) {
console.error(error.message);
}
console.groupEnd();
throw error.title;
}
} // Map the keys of the args object to an array of encoded url components
function getQueryString(args) {
return Object.keys(args).sort().map(argName => argName in methods && methods[argName](args[argName])).filter(Boolean).join('&').replace(/&&/g, '&');
}
const methods = {
where(arg) {
// where: [{ name: 'column_name', op: '=', value: 'value' }]
return asArray(arg).map(w => `where=${urlEncode(w)}`).join('&');
},
order(arg) {
// order_by: [{ column: 'column_name', direction: 'asc|desc' }]
// TODO: random()?
const columnList = asArray(arg).map(col => {
const {
column,
direction = 'asc'
} = col;
return direction !== 'asc' ? `-${column}` : `${column}`;
});
return `order_by=${encodeURIComponent(columnList.join(','))}`;
},
limit(arg) {
// limit: number
const parsedNum = parseInt(arg);
if (!isNaN(parsedNum)) {
return `limit=${parsedNum}`;
} else {
return null;
}
},
offset(arg) {
// offset: number
const parsedNum = parseInt(arg);
if (!isNaN(parsedNum)) {
return `order=${parsedNum}`;
} else {
return null;
}
},
evented() {
return 'session_id';
},
metaData(arg) {
return `meta_data=${urlEncode(arg)}`;
},
args(arg) {
return `args=${urlEncode(arg)}`;
},
exclude(arg) {
return `exclude=${urlEncode(arg)}`;
},
include(arg) {
return `include=${urlEncode(arg)}`;
}
};
function urlEncode(str) {
return encodeURIComponent(JSON.stringify(str));
}
function asArray(arg) {
return arg instanceof Array ? arg : [arg];
}
const anonConfig = {
user: 'anonymous',
database: 'aquameta',
host: 'localhost',
port: 5432,
max: 4,
idleTimeoutMilliseconds: 30000
};
async function getConnection(config) {
const mergedConfig = { ...anonConfig,
...(config && config.connection || {})
}; // Don't allow user to be overridden
mergedConfig.user = anonConfig.user;
const anonClient = new pg.Client(mergedConfig);
await anonClient.connect();
if (!mergedConfig.sessionId) {
return anonClient;
}
let result;
try {
result = await anonClient.query('select (role_id).name as role_name from endpoint.session where id = $1::uuid', [mergedConfig.sessionId]);
} catch (e) {
await anonClient.end();
throw e;
} // If login fails - continue request as anonymous
if (!result || !result.rows || result.rows.length === 0) {
return anonClient;
} // Release Client - TODO: release back to pool
await anonClient.end(); // Logged in
const userResult = new Result(result.rows[0]);
const user = await userResult.json();
console.log(`connection: logged in as ${user.role_name}`);
const userClient = new pg.Client({ ...mergedConfig,
user: user.role_name
});
await userClient.connect();
return userClient;
}
/**
* Execute query server-side
* @returns {Promise}
*/
async function executeConnection(client, query) {
let connection;
try {
connection = await getConnection(client); // let result;
/*
if (query.args && query.args.source) {
const {schemaName, relationName, column, name} = parseSourceUrl(
query.url,
);
const queryResult = await connection.query(
`
select content, mimetype
from (select $1 as content, '$1' as extension from $3.$4 where name='$2') as c
join endpoint.mimetype_extension me on c.extension=me.extension
join endpoint.mimetype m on me.mimetype_id=m.id;
`,
[column, name, schemaName, relationName],
);
result = {...queryResult};
if (result && result.rows && result.rows.length !== 0) {
result.status = 200;
result.message = 'OK';
} else {
result.status = 404;
result.status = 'NOT FOUND';
}
} else {
*/
console.log('trying connection', query.version || client.version, query.method, query.url, JSON.stringify(query.args), JSON.stringify(query.data));
const result = await connection.query('select status, message, response, mimetype ' + 'from endpoint.request($1, $2, $3, $4::json, $5::json)', [query.version || client.version, query.method, query.url, JSON.stringify(query.args), JSON.stringify(query.data)]); //}
// TODO: end connection if userClient, but release if anonClient?
await connection.end();
const res = new Result(result.rows[0]);
if (client.rawResponse) {
return res;
} else {
return res.json().then(r => {
if (r.result) {
return r.result.map(({
row
}) => row);
} else {
return [];
}
});
}
} catch (e) {
// Problem with connecting to database
console.error(`connection: error trying to connect to database`);
console.error(e);
if (connection) {
await connection.end();
}
return null;
}
}
// Mimic response from fetch
class Result {
constructor({
status,
message,
response,
mimetype
}) {
this.status = status;
this.statusText = message;
this.response = response;
this.mimetype = mimetype;
}
async json() {
return JSON.parse(this.response);
}
}
/**
* TODO I want to keep track of how many pools are open and when they connect
* pg-pool has some great events
* pool.on('connect', client => {
* client.count = count++
* })
*/
/**
* TODO in order to do this, I would have to keep track of the open pools,
* instead of doing it with pg.connect()
*
* var pool = new pg.Pool(config)
* pool.connect(callback)
*
* is the same as
*
* pg.connect(config, callback)
*
* and this way, pg will keep track of the pools and not create a new one when
* the same config has been passed in twice
*/
function parseSourceUrl(pathname) {
const [,, schemaName, relationName, ...rest] = pathname.split('/');
const fileName = rest.join('/');
const lastPeriod = fileName.lastIndexOf('.');
const name = fileName.slice(0, lastPeriod);
const column = fileName.slice(lastPeriod + 1);
return {
schemaName,
relationName,
column,
name
};
}
/**
* A function that always returns `true`. Any passed in parameters are ignored.
*
* @func
* @memberOf R
* @since v0.9.0
* @category Function
* @sig * -> Boolean
* @param {*}
* @return {Boolean}
* @see R.F
* @example
*
* R.T(); //=> true
*/
var T = function () {
return true;
};
/**
* A special placeholder value used to specify "gaps" within curried functions,
* allowing partial application of any combination of arguments, regardless of
* their positions.
*
* If `g` is a curried ternary function and `_` is `R.__`, the following are
* equivalent:
*
* - `g(1, 2, 3)`
* - `g(_, 2, 3)(1)`
* - `g(_, _, 3)(1)(2)`
* - `g(_, _, 3)(1, 2)`
* - `g(_, 2, _)(1, 3)`
* - `g(_, 2)(1)(3)`
* - `g(_, 2)(1, 3)`
* - `g(_, 2)(_, 3)(1)`
*
* @name __
* @constant
* @memberOf R
* @since v0.6.0
* @category Function
* @example
*
* const greet = R.replace('{name}', R.__, 'Hello, {name}!');
* greet('Alice'); //=> 'Hello, Alice!'
*/
var __ = { '@@functional/placeholder': true };
function _isPlaceholder(a) {
return a != null && typeof a === 'object' && a['@@functional/placeholder'] === true;
}
/**
* Optimized internal one-arity curry function.
*
* @private
* @category Function
* @param {Function} fn The function to curry.
* @return {Function} The curried function.
*/
function _curry1(fn) {
return function f1(a) {
if (arguments.length === 0 || _isPlaceholder(a)) {
return f1;
} else {
return fn.apply(this, arguments);
}
};
}
/**
* Optimized internal two-arity curry function.
*
* @private
* @category Function
* @param {Function} fn The function to curry.
* @return {Function} The curried function.
*/
function _curry2(fn) {
return function f2(a, b) {
switch (arguments.length) {
case 0:
return f2;
case 1:
return _isPlaceholder(a) ? f2 : _curry1(function (_b) {
return fn(a, _b);
});
default:
return _isPlaceholder(a) && _isPlaceholder(b) ? f2 : _isPlaceholder(a) ? _curry1(function (_a) {
return fn(_a, b);
}) : _isPlaceholder(b) ? _curry1(function (_b) {
return fn(a, _b);
}) : fn(a, b);
}
};
}
/**
* Adds two values.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Math
* @sig Number -> Number -> Number
* @param {Number} a
* @param {Number} b
* @return {Number}
* @see R.subtract
* @example
*
* R.add(2, 3); //=> 5
* R.add(7)(10); //=> 17
*/
var add = /*#__PURE__*/_curry2(function add(a, b) {
return Number(a) + Number(b);
});
/**
* Private `concat` function to merge two array-like objects.
*
* @private
* @param {Array|Arguments} [set1=[]] An array-like object.
* @param {Array|Arguments} [set2=[]] An array-like object.
* @return {Array} A new, merged array.
* @example
*
* _concat([4, 5, 6], [1, 2, 3]); //=> [4, 5, 6, 1, 2, 3]
*/
function _concat(set1, set2) {
set1 = set1 || [];
set2 = set2 || [];
var idx;
var len1 = set1.length;
var len2 = set2.length;
var result = [];
idx = 0;
while (idx < len1) {
result[result.length] = set1[idx];
idx += 1;
}
idx = 0;
while (idx < len2) {
result[result.length] = set2[idx];
idx += 1;
}
return result;
}
function _arity(n, fn) {
/* eslint-disable no-unused-vars */
switch (n) {
case 0:
return function () {
return fn.apply(this, arguments);
};
case 1:
return function (a0) {
return fn.apply(this, arguments);
};
case 2:
return function (a0, a1) {
return fn.apply(this, arguments);
};
case 3:
return function (a0, a1, a2) {
return fn.apply(this, arguments);
};
case 4:
return function (a0, a1, a2, a3) {
return fn.apply(this, arguments);
};
case 5:
return function (a0, a1, a2, a3, a4) {
return fn.apply(this, arguments);
};
case 6:
return function (a0, a1, a2, a3, a4, a5) {
return fn.apply(this, arguments);
};
case 7:
return function (a0, a1, a2, a3, a4, a5, a6) {
return fn.apply(this, arguments);
};
case 8:
return function (a0, a1, a2, a3, a4, a5, a6, a7) {
return fn.apply(this, arguments);
};
case 9:
return function (a0, a1, a2, a3, a4, a5, a6, a7, a8) {
return fn.apply(this, arguments);
};
case 10:
return function (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9) {
return fn.apply(this, arguments);
};
default:
throw new Error('First argument to _arity must be a non-negative integer no greater than ten');
}
}
/**
* Internal curryN function.
*
* @private
* @category Function
* @param {Number} length The arity of the curried function.
* @param {Array} received An array of arguments received thus far.
* @param {Function} fn The function to curry.
* @return {Function} The curried function.
*/
function _curryN(length, received, fn) {
return function () {
var combined = [];
var argsIdx = 0;
var left = length;
var combinedIdx = 0;
while (combinedIdx < received.length || argsIdx < arguments.length) {
var result;
if (combinedIdx < received.length && (!_isPlaceholder(received[combinedIdx]) || argsIdx >= arguments.length)) {
result = received[combinedIdx];
} else {
result = arguments[argsIdx];
argsIdx += 1;
}
combined[combinedIdx] = result;
if (!_isPlaceholder(result)) {
left -= 1;
}
combinedIdx += 1;
}
return left <= 0 ? fn.apply(this, combined) : _arity(left, _curryN(length, combined, fn));
};
}
/**
* Returns a curried equivalent of the provided function, with the specified
* arity. The curried function has two unusual capabilities. First, its
* arguments needn't be provided one at a time. If `g` is `R.curryN(3, f)`, the
* following are equivalent:
*
* - `g(1)(2)(3)`
* - `g(1)(2, 3)`
* - `g(1, 2)(3)`
* - `g(1, 2, 3)`
*
* Secondly, the special placeholder value [`R.__`](#__) may be used to specify
* "gaps", allowing partial application of any combination of arguments,
* regardless of their positions. If `g` is as above and `_` is [`R.__`](#__),
* the following are equivalent:
*
* - `g(1, 2, 3)`
* - `g(_, 2, 3)(1)`
* - `g(_, _, 3)(1)(2)`
* - `g(_, _, 3)(1, 2)`
* - `g(_, 2)(1)(3)`
* - `g(_, 2)(1, 3)`
* - `g(_, 2)(_, 3)(1)`
*
* @func
* @memberOf R
* @since v0.5.0
* @category Function
* @sig Number -> (* -> a) -> (* -> a)
* @param {Number} length The arity for the returned function.
* @param {Function} fn The function to curry.
* @return {Function} A new, curried function.
* @see R.curry
* @example
*
* const sumArgs = (...args) => R.sum(args);
*
* const curriedAddFourNumbers = R.curryN(4, sumArgs);
* const f = curriedAddFourNumbers(1, 2);
* const g = f(3);
* g(4); //=> 10
*/
var curryN = /*#__PURE__*/_curry2(function curryN(length, fn) {
if (length === 1) {
return _curry1(fn);
}
return _arity(length, _curryN(length, [], fn));
});
/**
* Optimized internal three-arity curry function.
*
* @private
* @category Function
* @param {Function} fn The function to curry.
* @return {Function} The curried function.
*/
function _curry3(fn) {
return function f3(a, b, c) {
switch (arguments.length) {
case 0:
return f3;
case 1:
return _isPlaceholder(a) ? f3 : _curry2(function (_b, _c) {
return fn(a, _b, _c);
});
case 2:
return _isPlaceholder(a) && _isPlaceholder(b) ? f3 : _isPlaceholder(a) ? _curry2(function (_a, _c) {
return fn(_a, b, _c);
}) : _isPlaceholder(b) ? _curry2(function (_b, _c) {
return fn(a, _b, _c);
}) : _curry1(function (_c) {
return fn(a, b, _c);
});
default:
return _isPlaceholder(a) && _isPlaceholder(b) && _isPlaceholder(c) ? f3 : _isPlaceholder(a) && _isPlaceholder(b) ? _curry2(function (_a, _b) {
return fn(_a, _b, c);
}) : _isPlaceholder(a) && _isPlaceholder(c) ? _curry2(function (_a, _c) {
return fn(_a, b, _c);
}) : _isPlaceholder(b) && _isPlaceholder(c) ? _curry2(function (_b, _c) {
return fn(a, _b, _c);
}) : _isPlaceholder(a) ? _curry1(function (_a) {
return fn(_a, b, c);
}) : _isPlaceholder(b) ? _curry1(function (_b) {
return fn(a, _b, c);
}) : _isPlaceholder(c) ? _curry1(function (_c) {
return fn(a, b, _c);
}) : fn(a, b, c);
}
};
}
/**
* Tests whether or not an object is an array.
*
* @private
* @param {*} val The object to test.
* @return {Boolean} `true` if `val` is an array, `false` otherwise.
* @example
*
* _isArray([]); //=> true
* _isArray(null); //=> false
* _isArray({}); //=> false
*/
var _isArray = Array.isArray || function _isArray(val) {
return val != null && val.length >= 0 && Object.prototype.toString.call(val) === '[object Array]';
};
function _isTransformer(obj) {
return obj != null && typeof obj['@@transducer/step'] === 'function';
}
/**
* Returns a function that dispatches with different strategies based on the
* object in list position (last argument). If it is an array, executes [fn].
* Otherwise, if it has a function with one of the given method names, it will
* execute that function (functor case). Otherwise, if it is a transformer,
* uses transducer [xf] to return a new transformer (transducer case).
* Otherwise, it will default to executing [fn].
*
* @private
* @param {Array} methodNames properties to check for a custom implementation
* @param {Function} xf transducer to initialize if object is transformer
* @param {Function} fn default ramda implementation
* @return {Function} A function that dispatches on object in list position
*/
function _dispatchable(methodNames, xf, fn) {
return function () {
if (arguments.length === 0) {
return fn();
}
var args = Array.prototype.slice.call(arguments, 0);
var obj = args.pop();
if (!_isArray(obj)) {
var idx = 0;
while (idx < methodNames.length) {
if (typeof obj[methodNames[idx]] === 'function') {
return obj[methodNames[idx]].apply(obj, args);
}
idx += 1;
}
if (_isTransformer(obj)) {
var transducer = xf.apply(null, args);
return transducer(obj);
}
}
return fn.apply(this, arguments);
};
}
var _xfBase = {
init: function () {
return this.xf['@@transducer/init']();
},
result: function (result) {
return this.xf['@@transducer/result'](result);
}
};
/**
* Returns the larger of its two arguments.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Relation
* @sig Ord a => a -> a -> a
* @param {*} a
* @param {*} b
* @return {*}
* @see R.maxBy, R.min
* @example
*
* R.max(789, 123); //=> 789
* R.max('a', 'b'); //=> 'b'
*/
var max = /*#__PURE__*/_curry2(function max(a, b) {
return b > a ? b : a;
});
function _map(fn, functor) {
var idx = 0;
var len = functor.length;
var result = Array(len);
while (idx < len) {
result[idx] = fn(functor[idx]);
idx += 1;
}
return result;
}
function _isString(x) {
return Object.prototype.toString.call(x) === '[object String]';
}
/**
* Tests whether or not an object is similar to an array.
*
* @private
* @category Type
* @category List
* @sig * -> Boolean
* @param {*} x The object to test.
* @return {Boolean} `true` if `x` has a numeric length property and extreme indices defined; `false` otherwise.
* @example
*
* _isArrayLike([]); //=> true
* _isArrayLike(true); //=> false
* _isArrayLike({}); //=> false
* _isArrayLike({length: 10}); //=> false
* _isArrayLike({0: 'zero', 9: 'nine', length: 10}); //=> true
*/
var _isArrayLike = /*#__PURE__*/_curry1(function isArrayLike(x) {
if (_isArray(x)) {
return true;
}
if (!x) {
return false;
}
if (typeof x !== 'object') {
return false;
}
if (_isString(x)) {
return false;
}
if (x.nodeType === 1) {
return !!x.length;
}
if (x.length === 0) {
return true;
}
if (x.length > 0) {
return x.hasOwnProperty(0) && x.hasOwnProperty(x.length - 1);
}
return false;
});
var XWrap = /*#__PURE__*/function () {
function XWrap(fn) {
this.f = fn;
}
XWrap.prototype['@@transducer/init'] = function () {
throw new Error('init not implemented on XWrap');
};
XWrap.prototype['@@transducer/result'] = function (acc) {
return acc;
};
XWrap.prototype['@@transducer/step'] = function (acc, x) {
return this.f(acc, x);
};
return XWrap;
}();
function _xwrap(fn) {
return new XWrap(fn);
}
/**
* Creates a function that is bound to a context.
* Note: `R.bind` does not provide the additional argument-binding capabilities of
* [Function.prototype.bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind).
*
* @func
* @memberOf R
* @since v0.6.0
* @category Function
* @category Object
* @sig (* -> *) -> {*} -> (* -> *)
* @param {Function} fn The function to bind to context
* @param {Object} thisObj The context to bind `fn` to
* @return {Function} A function that will execute in the context of `thisObj`.
* @see R.partial
* @example
*
* const log = R.bind(console.log, console);
* R.pipe(R.assoc('a', 2), R.tap(log), R.assoc('a', 3))({a: 1}); //=> {a: 3}
* // logs {a: 2}
* @symb R.bind(f, o)(a, b) = f.call(o, a, b)
*/
var bind = /*#__PURE__*/_curry2(function bind(fn, thisObj) {
return _arity(fn.length, function () {
return fn.apply(thisObj, arguments);
});
});
function _arrayReduce(xf, acc, list) {
var idx = 0;
var len = list.length;
while (idx < len) {
acc = xf['@@transducer/step'](acc, list[idx]);
if (acc && acc['@@transducer/reduced']) {
acc = acc['@@transducer/value'];
break;
}
idx += 1;
}
return xf['@@transducer/result'](acc);
}
function _iterableReduce(xf, acc, iter) {
var step = iter.next();
while (!step.done) {
acc = xf['@@transducer/step'](acc, step.value);
if (acc && acc['@@transducer/reduced']) {
acc = acc['@@transducer/value'];
break;
}
step = iter.next();
}
return xf['@@transducer/result'](acc);
}
function _methodReduce(xf, acc, obj, methodName) {
return xf['@@transducer/result'](obj[methodName](bind(xf['@@transducer/step'], xf), acc));
}
var symIterator = typeof Symbol !== 'undefined' ? Symbol.iterator : '@@iterator';
function _reduce(fn, acc, list) {
if (typeof fn === 'function') {
fn = _xwrap(fn);
}
if (_isArrayLike(list)) {
return _arrayReduce(fn, acc, list);
}
if (typeof list['fantasy-land/reduce'] === 'function') {
return _methodReduce(fn, acc, list, 'fantasy-land/reduce');
}
if (list[symIterator] != null) {
return _iterableReduce(fn, acc, list[symIterator]());
}
if (typeof list.next === 'function') {
return _iterableReduce(fn, acc, list);
}
if (typeof list.reduce === 'function') {
return _methodReduce(fn, acc, list, 'reduce');
}
throw new TypeError('reduce: list must be array or iterable');
}
var XMap = /*#__PURE__*/function () {
function XMap(f, xf) {
this.xf = xf;
this.f = f;
}
XMap.prototype['@@transducer/init'] = _xfBase.init;
XMap.prototype['@@transducer/result'] = _xfBase.result;
XMap.prototype['@@transducer/step'] = function (result, input) {
return this.xf['@@transducer/step'](result, this.f(input));
};
return XMap;
}();
var _xmap = /*#__PURE__*/_curry2(function _xmap(f, xf) {
return new XMap(f, xf);
});
function _has(prop, obj) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
var toString = Object.prototype.toString;
var _isArguments = /*#__PURE__*/function () {
return toString.call(arguments) === '[object Arguments]' ? function _isArguments(x) {
return toString.call(x) === '[object Arguments]';
} : function _isArguments(x) {
return _has('callee', x);
};
}();
// cover IE < 9 keys issues
var hasEnumBug = ! /*#__PURE__*/{ toString: null }.propertyIsEnumerable('toString');
var nonEnumerableProps = ['constructor', 'valueOf', 'isPrototypeOf', 'toString', 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
// Safari bug
var hasArgsEnumBug = /*#__PURE__*/function () {
return arguments.propertyIsEnumerable('length');
}();
var contains = function contains(list, item) {
var idx = 0;
while (idx < list.length) {
if (list[idx] === item) {
return true;
}
idx += 1;
}
return false;
};
/**
* Returns a list containing the names of all the enumerable own properties of
* the supplied object.
* Note that the order of the output array is not guaranteed to be consistent
* across different JS platforms.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Object
* @sig {k: v} -> [k]
* @param {Object} obj The object to extract properties from
* @return {Array} An array of the object's own properties.
* @see R.keysIn, R.values
* @example
*
* R.keys({a: 1, b: 2, c: 3}); //=> ['a', 'b', 'c']
*/
var keys = typeof Object.keys === 'function' && !hasArgsEnumBug ? /*#__PURE__*/_curry1(function keys(obj) {
return Object(obj) !== obj ? [] : Object.keys(obj);
}) : /*#__PURE__*/_curry1(function keys(obj) {
if (Object(obj) !== obj) {
return [];
}
var prop, nIdx;
var ks = [];
var checkArgsLength = hasArgsEnumBug && _isArguments(obj);
for (prop in obj) {
if (_has(prop, obj) && (!checkArgsLength || prop !== 'length')) {
ks[ks.length] = prop;
}
}
if (hasEnumBug) {
nIdx = nonEnumerableProps.length - 1;
while (nIdx >= 0) {
prop = nonEnumerableProps[nIdx];
if (_has(prop, obj) && !contains(ks, prop)) {
ks[ks.length] = prop;
}
nIdx -= 1;
}
}
return ks;
});
/**
* Takes a function and
* a [functor](https://github.com/fantasyland/fantasy-land#functor),
* applies the function to each of the functor's values, and returns
* a functor of the same shape.
*
* Ramda provides suitable `map` implementations for `Array` and `Object`,
* so this function may be applied to `[1, 2, 3]` or `{x: 1, y: 2, z: 3}`.
*
* Dispatches to the `map` method of the second argument, if present.
*
* Acts as a transducer if a transformer is given in list position.
*
* Also treats functions as functors and will compose them together.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig Functor f => (a -> b) -> f a -> f b
* @param {Function} fn The function to be called on every element of the input `list`.
* @param {Array} list The list to be iterated over.
* @return {Array} The new list.
* @see R.transduce, R.addIndex
* @example
*
* const double = x => x * 2;
*
* R.map(double, [1, 2, 3]); //=> [2, 4, 6]
*
* R.map(double, {x: 1, y: 2, z: 3}); //=> {x: 2, y: 4, z: 6}
* @symb R.map(f, [a, b]) = [f(a), f(b)]
* @symb R.map(f, { x: a, y: b }) = { x: f(a), y: f(b) }
* @symb R.map(f, functor_o) = functor_o.map(f)
*/
var map = /*#__PURE__*/_curry2( /*#__PURE__*/_dispatchable(['fantasy-land/map', 'map'], _xmap, function map(fn, functor) {
switch (Object.prototype.toString.call(functor)) {
case '[object Function]':
return curryN(functor.length, function () {
return fn.call(this, functor.apply(this, arguments));
});
case '[object Object]':
return _reduce(function (acc, key) {
acc[key] = fn(functor[key]);
return acc;
}, {}, keys(functor));
default:
return _map(fn, functor);
}
}));
/**
* Retrieve the value at a given path.
*
* @func
* @memberOf R
* @since v0.2.0
* @category Object
* @typedefn Idx = String | Int
* @sig [Idx] -> {a} -> a | Undefined
* @param {Array} path The path to use.
* @param {Object} obj The object to retrieve the nested property from.
* @return {*} The data at `path`.
* @see R.prop
* @example
*
* R.path(['a', 'b'], {a: {b: 2}}); //=> 2
* R.path(['a', 'b'], {c: {b: 2}}); //=> undefined
*/
var path = /*#__PURE__*/_curry2(function path(paths, obj) {
var val = obj;
var idx = 0;
while (idx < paths.length) {
if (val == null) {
return;
}
val = val[paths[idx]];
idx += 1;
}
return val;
});
/**
* Returns a function that when supplied an object returns the indicated
* property of that object, if it exists.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Object
* @sig s -> {s: a} -> a | Undefined
* @param {String} p The property name
* @param {Object} obj The object to query
* @return {*} The value at `obj.p`.
* @see R.path
* @example
*
* R.prop('x', {x: 100}); //=> 100
* R.prop('x', {}); //=> undefined
* R.compose(R.inc, R.prop('x'))({ x: 3 }) //=> 4
*/
var prop = /*#__PURE__*/_curry2(function prop(p, obj) {
return path([p], obj);
});
/**
* Returns a new list by plucking the same named property off all objects in
* the list supplied.
*
* `pluck` will work on
* any [functor](https://github.com/fantasyland/fantasy-land#functor) in
* addition to arrays, as it is equivalent to `R.map(R.prop(k), f)`.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig Functor f => k -> f {k: v} -> f v
* @param {Number|String} key The key name to pluck off of each object.
* @param {Array} f The array or functor to consider.
* @return {Array} The list of values for the given key.
* @see R.props
* @example
*
* var getAges = R.pluck('age');
* getAges([{name: 'fred', age: 29}, {name: 'wilma', age: 27}]); //=> [29, 27]
*
* R.pluck(0, [[1, 2], [3, 4]]); //=> [1, 3]
* R.pluck('val', {a: {val: 3}, b: {val: 5}}); //=> {a: 3, b: 5}
* @symb R.pluck('x', [{x: 1, y: 2}, {x: 3, y: 4}, {x: 5, y: 6}]) = [1, 3, 5]
* @symb R.pluck(0, [[1, 2], [3, 4], [5, 6]]) = [1, 3, 5]
*/
var pluck = /*#__PURE__*/_curry2(function pluck(p, list) {
return map(prop(p), list);
});
/**
* Returns a single item by iterating through the list, successively calling
* the iterator function and passing it an accumulator value and the current
* value from the array, and then passing the result to the next call.
*
* The iterator function receives two values: *(acc, value)*. It may use
* [`R.reduced`](#reduced) to shortcut the iteration.
*
* The arguments' order of [`reduceRight`](#reduceRight)'s iterator function
* is *(value, acc)*.
*
* Note: `R.reduce` does not skip deleted or unassigned indices (sparse
* arrays), unlike the native `Array.prototype.reduce` method. For more details
* on this behavior, see:
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce#Description
*
* Dispatches to the `reduce` method of the third argument, if present. When
* doing so, it is up to the user to handle the [`R.reduced`](#reduced)
* shortcuting, as this is not implemented by `reduce`.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig ((a, b) -> a) -> a -> [b] -> a
* @param {Function} fn The iterator function. Receives two values, the accumulator and the
* current element from the array.
* @param {*} acc The accumulator value.
* @param {Array} list The list to iterate over.
* @return {*} The final, accumulated value.
* @see R.reduced, R.addIndex, R.reduceRight
* @example
*
* R.reduce(R.subtract, 0, [1, 2, 3, 4]) // => ((((0 - 1) - 2) - 3) - 4) = -10
* // - -10
* // / \ / \
* // - 4 -6 4
* // / \ / \
* // - 3 ==> -3 3
* // / \ / \
* // - 2 -1 2
* // / \ / \
* // 0 1 0 1
*
* @symb R.reduce(f, a, [b, c, d]) = f(f(f(a, b), c), d)
*/
var reduce = /*#__PURE__*/_curry3(_reduce);
/**
* Returns a function that always returns the given value. Note that for
* non-primitives the value returned is a reference to the original value.
*
* This function is known as `const`, `constant`, or `K` (for K combinator) in
* other languages and libraries.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Function
* @sig a -> (* -> a)
* @param {*} val The value to wrap in a function
* @return {Function} A Function :: * -> val.
* @example
*
* const t = R.always('Tee');
* t(); //=> 'Tee'
*/
var always = /*#__PURE__*/_curry1(function always(val) {
return function () {
return val;
};
});
/**
* ap applies a list of functions to a list of values.
*
* Dispatches to the `ap` method of the second argument, if present. Also
* treats curried functions as applicatives.
*
* @func
* @memberOf R
* @since v0.3.0
* @category Function
* @sig [a -> b] -> [a] -> [b]
* @sig Apply f => f (a -> b) -> f a -> f b
* @sig (r -> a -> b) -> (r -> a) -> (r -> b)
* @param {*} applyF
* @param {*} applyX
* @return {*}
* @example
*
* R.ap([R.multiply(2), R.add(3)], [1,2,3]); //=> [2, 4, 6, 4, 5, 6]
* R.ap([R.concat('tasty '), R.toUpper], ['pizza', 'salad']); //=> ["tasty pizza", "tasty salad", "PIZZA", "SALAD"]
*
* // R.ap can also be used as S combinator
* // when only two functions are passed
* R.ap(R.concat, R.toUpper)('Ramda') //=> 'RamdaRAMDA'
* @symb R.ap([f, g], [a, b]) = [f(a), f(b), g(a), g(b)]
*/
var ap = /*#__PURE__*/_curry2(function ap(applyF, applyX) {
return typeof applyX['fantasy-land/ap'] === 'function' ? applyX['fantasy-land/ap'](applyF) : typeof applyF.ap === 'function' ? applyF.ap(applyX) : typeof applyF === 'function' ? function (x) {
return applyF(x)(applyX(x));
} : _reduce(function (acc, f) {
return _concat(acc, map(f, applyX));
}, [], applyF);
});
/**
* Determine if the passed argument is an integer.
*
* @private
* @param {*} n
* @category Type
* @return {Boolean}
*/
function _isFunction(x) {
return Object.prototype.toString.call(x) === '[object Function]';
}
/**
* "lifts" a function to be the specified arity, so that it may "map over" that
* many lists, Functions or other objects that satisfy the [FantasyLand Apply spec](https://github.com/fantasyland/fantasy-land#apply).
*
* @func
* @memberOf R
* @since v0.7.0
* @category Function
* @sig Number -> (*... -> *) -> ([*]... -> [*])
* @param {Function} fn The function to lift into higher context
* @return {Function} The lifted function.
* @see R.lift, R.ap
* @example
*
* const madd3 = R.liftN(3, (...args) => R.sum(args));
* madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
*/
var liftN = /*#__PURE__*/_curry2(function liftN(arity, fn) {
var lifted = curryN(arity, fn);
return curryN(arity, function () {
return _reduce(ap, map(lifted, arguments[0]), Array.prototype.slice.call(arguments, 1));
});
});
/**
* "lifts" a function of arity > 1 so that it may "map over" a list, Function or other
* object that satisfies the [FantasyLand Apply spec](https://github.com/fantasyland/fantasy-land#apply).
*
* @func
* @memberOf R
* @since v0.7.0
* @category Function
* @sig (*... -> *) -> ([*]... -> [*])
* @param {Function} fn The function to lift into higher context
* @return {Function} The lifted function.
* @see R.liftN
* @example
*
* const madd3 = R.lift((a, b, c) => a + b + c);
*
* madd3([1,2,3], [1,2,3], [1]); //=> [3, 4, 5, 4, 5, 6, 5, 6, 7]
*
* const madd5 = R.lift((a, b, c, d, e) => a + b + c + d + e);
*
* madd5([1,2], [3], [4, 5], [6], [7, 8]); //=> [21, 22, 22, 23, 22, 23, 23, 24]
*/
var lift = /*#__PURE__*/_curry1(function lift(fn) {
return liftN(fn.length, fn);
});
/**
* Returns a curried equivalent of the provided function. The curried function
* has two unusual capabilities. First, its arguments needn't be provided one
* at a time. If `f` is a ternary function and `g` is `R.curry(f)`, the
* following are equivalent:
*
* - `g(1)(2)(3)`
* - `g(1)(2, 3)`
* - `g(1, 2)(3)`
* - `g(1, 2, 3)`
*
* Secondly, the special placeholder value [`R.__`](#__) may be used to specify
* "gaps", allowing partial application of any combination of arguments,
* regardless of their positions. If `g` is as above and `_` is [`R.__`](#__),
* the following are equivalent:
*
* - `g(1, 2, 3)`
* - `g(_, 2, 3)(1)`
* - `g(_, _, 3)(1)(2)`
* - `g(_, _, 3)(1, 2)`
* - `g(_, 2)(1)(3)`
* - `g(_, 2)(1, 3)`
* - `g(_, 2)(_, 3)(1)`
*
* @func
* @memberOf R
* @since v0.1.0
* @category Function
* @sig (* -> a) -> (* -> a)
* @param {Function} fn The function to curry.
* @return {Function} A new, curried function.
* @see R.curryN, R.partial
* @example
*
* const addFourNumbers = (a, b, c, d) => a + b + c + d;
*
* const curriedAddFourNumbers = R.curry(addFourNumbers);
* const f = curriedAddFourNumbers(1, 2);
* const g = f(3);
* g(4); //=> 10
*/
var curry = /*#__PURE__*/_curry1(function curry(fn) {
return curryN(fn.length, fn);
});
/**
* Returns the result of calling its first argument with the remaining
* arguments. This is occasionally useful as a converging function for
* [`R.converge`](#converge): the first branch can produce a function while the
* remaining branches produce values to be passed to that function as its
* arguments.
*
* @func
* @memberOf R
* @since v0.9.0
* @category Function
* @sig (*... -> a),*... -> a
* @param {Function} fn The function to apply to the remaining arguments.
* @param {...*} args Any number of positional arguments.
* @return {*}
* @see R.apply
* @example
*
* R.call(R.add, 1, 2); //=> 3
*
* const indentN = R.pipe(R.repeat(' '),
* R.join(''),
* R.replace(/^(?!$)/gm));
*
* const format = R.converge(R.call, [
* R.pipe(R.prop('indent'), indentN),
* R.prop('value')
* ]);
*
* format({indent: 2, value: 'foo\nbar\nbaz\n'}); //=> ' foo\n bar\n baz\n'
* @symb R.call(f, a, b) = f(a, b)
*/
var call = /*#__PURE__*/curry(function call(fn) {
return fn.apply(this, Array.prototype.slice.call(arguments, 1));
});
/**
* `_makeFlat` is a helper function that returns a one-level or fully recursive
* function based on the flag passed in.
*
* @private
*/
function _makeFlat(recursive) {
return function flatt(list) {
var value, jlen, j;
var result = [];
var idx = 0;
var ilen = list.length;
while (idx < ilen) {
if (_isArrayLike(list[idx])) {
value = recursive ? flatt(list[idx]) : list[idx];
j = 0;
jlen = value.length;
while (j < jlen) {
result[result.length] = value[j];
j += 1;
}
} else {
result[result.length] = list[idx];
}
idx += 1;
}
return result;
};
}
function _forceReduced(x) {
return {
'@@transducer/value': x,
'@@transducer/reduced': true
};
}
var preservingReduced = function (xf) {
return {
'@@transducer/init': _xfBase.init,
'@@transducer/result': function (result) {
return xf['@@transducer/result'](result);
},
'@@transducer/step': function (result, input) {
var ret = xf['@@transducer/step'](result, input);
return ret['@@transducer/reduced'] ? _forceReduced(ret) : ret;
}
};
};
var _flatCat = function _xcat(xf) {
var rxf = preservingReduced(xf);
return {
'@@transducer/init': _xfBase.init,
'@@transducer/result': function (result) {
return rxf['@@transducer/result'](result);
},
'@@transducer/step': function (result, input) {
return !_isArrayLike(input) ? _reduce(rxf, result, [input]) : _reduce(rxf, result, input);
}
};
};
var _xchain = /*#__PURE__*/_curry2(function _xchain(f, xf) {
return map(f, _flatCat(xf));
});
/**
* `chain` maps a function over a list and concatenates the results. `chain`
* is also known as `flatMap` in some libraries.
*
* Dispatches to the `chain` method of the second argument, if present,
* according to the [FantasyLand Chain spec](https://github.com/fantasyland/fantasy-land#chain).
*
* If second argument is a function, `chain(f, g)(x)` is equivalent to `f(g(x), x)`.
*
* Acts as a transducer if a transformer is given in list position.
*
* @func
* @memberOf R
* @since v0.3.0
* @category List
* @sig Chain m => (a -> m b) -> m a -> m b
* @param {Function} fn The function to map with
* @param {Array} list The list to map over
* @return {Array} The result of flat-mapping `list` with `fn`
* @example
*
* const duplicate = n => [n, n];
* R.chain(duplicate, [1, 2, 3]); //=> [1, 1, 2, 2, 3, 3]
*
* R.chain(R.append, R.head)([1, 2, 3]); //=> [1, 2, 3, 1]
*/
var chain = /*#__PURE__*/_curry2( /*#__PURE__*/_dispatchable(['fantasy-land/chain', 'chain'], _xchain, function chain(fn, monad) {
if (typeof monad === 'function') {
return function (x) {
return fn(monad(x))(x);
};
}
return _makeFlat(false)(map(fn, monad));
}));
/**
* Gives a single-word string description of the (native) type of a value,
* returning such answers as 'Object', 'Number', 'Array', or 'Null'. Does not
* attempt to distinguish user Object types any further, reporting them all as
* 'Object'.
*
* @func
* @memberOf R
* @since v0.8.0
* @category Type
* @sig (* -> {*}) -> String
* @param {*} val The value to test
* @return {String}
* @example
*
* R.type({}); //=> "Object"
* R.type(1); //=> "Number"
* R.type(false); //=> "Boolean"
* R.type('s'); //=> "String"
* R.type(null); //=> "Null"
* R.type([]); //=> "Array"
* R.type(/[A-z]/); //=> "RegExp"
* R.type(() => {}); //=> "Function"
* R.type(undefined); //=> "Undefined"
*/
var type = /*#__PURE__*/_curry1(function type(val) {
return val === null ? 'Null' : val === undefined ? 'Undefined' : Object.prototype.toString.call(val).slice(8, -1);
});
/**
* A function that returns the `!` of its argument. It will return `true` when
* passed false-y value, and `false` when passed a truth-y one.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Logic
* @sig * -> Boolean
* @param {*} a any value
* @return {Boolean} the logical inverse of passed argument.
* @see R.complement
* @example
*
* R.not(true); //=> false
* R.not(false); //=> true
* R.not(0); //=> true
* R.not(1); //=> false
*/
var not = /*#__PURE__*/_curry1(function not(a) {
return !a;
});
/**
* Takes a function `f` and returns a function `g` such that if called with the same arguments
* when `f` returns a "truthy" value, `g` returns `false` and when `f` returns a "falsy" value `g` returns `true`.
*
* `R.complement` may be applied to any functor
*
* @func
* @memberOf R
* @since v0.12.0
* @category Logic
* @sig (*... -> *) -> (*... -> Boolean)
* @param {Function} f
* @return {Function}
* @see R.not
* @example
*
* const isNotNil = R.complement(R.isNil);
* isNil(null); //=> true
* isNotNil(null); //=> false
* isNil(7); //=> false
* isNotNil(7); //=> true
*/
var complement = /*#__PURE__*/lift(not);
function _pipe(f, g) {
return function () {
return g.call(this, f.apply(this, arguments));
};
}
/**
* This checks whether a function has a [methodname] function. If it isn't an
* array it will execute that function otherwise it will default to the ramda
* implementation.
*
* @private
* @param {Function} fn ramda implemtation
* @param {String} methodname property to check for a custom implementation
* @return {Object} Whatever the return value of the method is.
*/
function _checkForMethod(methodname, fn) {
return function () {
var length = arguments.length;
if (length === 0) {
return fn();
}
var obj = arguments[length - 1];
return _isArray(obj) || typeof obj[methodname] !== 'function' ? fn.apply(this, arguments) : obj[methodname].apply(obj, Array.prototype.slice.call(arguments, 0, length - 1));
};
}
/**
* Returns the elements of the given list or string (or object with a `slice`
* method) from `fromIndex` (inclusive) to `toIndex` (exclusive).
*
* Dispatches to the `slice` method of the third argument, if present.
*
* @func
* @memberOf R
* @since v0.1.4
* @category List
* @sig Number -> Number -> [a] -> [a]
* @sig Number -> Number -> String -> String
* @param {Number} fromIndex The start index (inclusive).
* @param {Number} toIndex The end index (exclusive).
* @param {*} list
* @return {*}
* @example
*
* R.slice(1, 3, ['a', 'b', 'c', 'd']); //=> ['b', 'c']
* R.slice(1, Infinity, ['a', 'b', 'c', 'd']); //=> ['b', 'c', 'd']
* R.slice(0, -1, ['a', 'b', 'c', 'd']); //=> ['a', 'b', 'c']
* R.slice(-3, -1, ['a', 'b', 'c', 'd']); //=> ['b', 'c']
* R.slice(0, 3, 'ramda'); //=> 'ram'
*/
var slice = /*#__PURE__*/_curry3( /*#__PURE__*/_checkForMethod('slice', function slice(fromIndex, toIndex, list) {
return Array.prototype.slice.call(list, fromIndex, toIndex);
}));
/**
* Returns all but the first element of the given list or string (or object
* with a `tail` method).
*
* Dispatches to the `slice` method of the first argument, if present.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig [a] -> [a]
* @sig String -> String
* @param {*} list
* @return {*}
* @see R.head, R.init, R.last
* @example
*
* R.tail([1, 2, 3]); //=> [2, 3]
* R.tail([1, 2]); //=> [2]
* R.tail([1]); //=> []
* R.tail([]); //=> []
*
* R.tail('abc'); //=> 'bc'
* R.tail('ab'); //=> 'b'
* R.tail('a'); //=> ''
* R.tail(''); //=> ''
*/
var tail = /*#__PURE__*/_curry1( /*#__PURE__*/_checkForMethod('tail', /*#__PURE__*/slice(1, Infinity)));
/**
* Performs left-to-right function composition. The leftmost function may have
* any arity; the remaining functions must be unary.
*
* In some libraries this function is named `sequence`.
*
* **Note:** The result of pipe is not automatically curried.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Function
* @sig (((a, b, ..., n) -> o), (o -> p), ..., (x -> y), (y -> z)) -> ((a, b, ..., n) -> z)
* @param {...Function} functions
* @return {Function}
* @see R.compose
* @example
*
* const f = R.pipe(Math.pow, R.negate, R.inc);
*
* f(3, 4); // -(3^4) + 1
* @symb R.pipe(f, g, h)(a, b) = h(g(f(a, b)))
*/
function pipe() {
if (arguments.length === 0) {
throw new Error('pipe requires at least one argument');
}
return _arity(arguments[0].length, reduce(_pipe, arguments[0], tail(arguments)));
}
/**
* Returns a new list or string with the elements or characters in reverse
* order.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig [a] -> [a]
* @sig String -> String
* @param {Array|String} list
* @return {Array|String}
* @example
*
* R.reverse([1, 2, 3]); //=> [3, 2, 1]
* R.reverse([1, 2]); //=> [2, 1]
* R.reverse([1]); //=> [1]
* R.reverse([]); //=> []
*
* R.reverse('abc'); //=> 'cba'
* R.reverse('ab'); //=> 'ba'
* R.reverse('a'); //=> 'a'
* R.reverse(''); //=> ''
*/
var reverse = /*#__PURE__*/_curry1(function reverse(list) {
return _isString(list) ? list.split('').reverse().join('') : Array.prototype.slice.call(list, 0).reverse();
});
/**
* Performs right-to-left function composition. The rightmost function may have
* any arity; the remaining functions must be unary.
*
* **Note:** The result of compose is not automatically curried.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Function
* @sig ((y -> z), (x -> y), ..., (o -> p), ((a, b, ..., n) -> o)) -> ((a, b, ..., n) -> z)
* @param {...Function} ...functions The functions to compose
* @return {Function}
* @see R.pipe
* @example
*
* const classyGreeting = (firstName, lastName) => "The name's " + lastName + ", " + firstName + " " + lastName
* const yellGreeting = R.compose(R.toUpper, classyGreeting);
* yellGreeting('James', 'Bond'); //=> "THE NAME'S BOND, JAMES BOND"
*
* R.compose(Math.abs, R.add(1), R.multiply(2))(-4) //=> 7
*
* @symb R.compose(f, g, h)(a, b) = f(g(h(a, b)))
*/
function compose() {
if (arguments.length === 0) {
throw new Error('compose requires at least one argument');
}
return pipe.apply(this, reverse(arguments));
}
/**
* Returns the nth element of the given list or string. If n is negative the
* element at index length + n is returned.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig Number -> [a] -> a | Undefined
* @sig Number -> String -> String
* @param {Number} offset
* @param {*} list
* @return {*}
* @example
*
* const list = ['foo', 'bar', 'baz', 'quux'];
* R.nth(1, list); //=> 'bar'
* R.nth(-1, list); //=> 'quux'
* R.nth(-99, list); //=> undefined
*
* R.nth(2, 'abc'); //=> 'c'
* R.nth(3, 'abc'); //=> ''
* @symb R.nth(-1, [a, b, c]) = c
* @symb R.nth(0, [a, b, c]) = a
* @symb R.nth(1, [a, b, c]) = b
*/
var nth = /*#__PURE__*/_curry2(function nth(offset, list) {
var idx = offset < 0 ? list.length + offset : offset;
return _isString(list) ? list.charAt(idx) : list[idx];
});
/**
* Returns the first element of the given list or string. In some libraries
* this function is named `first`.
*
* @func
* @memberOf R
* @since v0.1.0
* @category List
* @sig [a] -> a | Undefined
* @sig String -> String
* @param {Array|String} list
* @return {*}
* @see R.tail, R.init, R.last
* @example
*
* R.head(['fi', 'fo', 'fum']); //=> 'fi'
* R.head([]); //=> undefined
*
* R.head('abc'); //=> 'a'
* R.head(''); //=> ''
*/
var head = /*#__PURE__*/nth(0);
function _identity(x) {
return x;
}
/**
* A function that does nothing but return the parameter supplied to it. Good
* as a default or placeholder function.
*
* @func
* @memberOf R
* @since v0.1.0
* @category Function
* @sig a -> a
* @param {*} x The value to return.
* @return {*} The input value, `x`.
* @example