Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
const {
BaseKonnector,
log,
errors,
addData,
hydrateAndFilter,
cozyClient,
} = __webpack_require__(1)
const rp = __webpack_require__(1307)
const moment = __webpack_require__(1339)
__webpack_require__(1476)
moment.locale('fr') // set the language
moment.tz.setDefault('Europe/Paris') // set the timezone
// eslint-disable-next-line
const Tracing = __webpack_require__(1579) // Needed for tracking performance in Sentry
const { version } = __webpack_require__(1615)
const { isDev } = __webpack_require__(1616)
process.env.COZY_JOB_MANUAL_EXECUTION === 'true' ? true : false
.subtract(1, 'year')
.format('MM/DD/YYYY')
.subtract(3, 'year')
.format('MM/DD/YYYY')
const endDate = moment().format('MM/DD/YYYY')
doctype: 'com.grandlyon.egl.day',
keys: ['year', 'month', 'day'],
doctype: 'com.grandlyon.egl.month',
keys: ['year', 'month'],
doctype: 'com.grandlyon.egl.year',
keys: ['year'],
},
}
module.exports = new BaseKonnector(start)
/**
* Sentry
*/
Sentry.init({
dsn:
'https://3f97baf46c2b44c2bd9e0c371abe3e05@grandlyon.errors.cozycloud.cc/2',
// Set tracesSampleRate to 1.0 to capture 100%
// of transactions for performance monitoring.
// We recommend adjusting this value in production
tracesSampleRate: 1.0,
release: version,
environment: isDev() ? 'development' : 'production',
debug: isDev(),
integrations: [
// enable HTTP calls tracing
new Sentry.Integrations.Http({ tracing: true }),
],
})
// The start function is run by the BaseKonnector instance only when it got all the account
// information (fields). When you run this connector yourself in 'standalone' mode or 'dev' mode,
// the account information come from ./konnector-dev-config.json file
async function start(fields, cozyParameters) {
const transaction = Sentry.startTransaction({
op: 'konnector',
name: 'EGL Konnector',
})
transaction.startChild({ op: 'Konnector starting' })
// const baseUrl = fields.eglBaseURL
// const apiAuthKey = fields.eglAPIAuthKey
const baseUrl = cozyParameters.secret.eglBaseURL
const apiAuthKey = cozyParameters.secret.eglAPIAuthKey
log('info', 'Authenticating ...')
const response = await authenticate(
fields.login,
fields.password,
baseUrl,
apiAuthKey
)
log('info', 'Successfully logged in')
const eglData = await getData(response, baseUrl, apiAuthKey)
log('debug', 'Process egl daily data')
const processedLoadData = await processData(
eglData,
rangeDate.day.doctype,
rangeDate.day.keys
)
log('debug', 'Aggregate egl load data for month and year')
await aggregateMonthAndYearData(processedLoadData)
log('debug', 'No data found')
transaction.setStatus(Tracing.SpanStatus.Ok)
transaction.finish()
log('error', error)
Sentry.captureException(error)
transaction.setStatus(Tracing.SpanStatus.Aborted)
transaction.finish()
await Sentry.flush()
throw error
/**
* Parse data
* Remove existing data from DB using hydrateAndFilter
* Store filtered data
* Return the list of filtered data
*/
async function processData(data, doctype, filterKeys) {
log('debug', 'processData - data formatted')
// Remove data for existing days into the DB
const filteredData = await hydrateAndFilter(data, doctype, {
keys: filterKeys,
})
log('debug', 'processData - data filtered')
await storeData(filteredData, doctype, filterKeys)
return filteredData
* Aggregate data from daily data to monthly and yearly data
async function aggregateMonthAndYearData(data) {
// Sum year and month values into object with year or year-month as keys
if (data && data.length !== 0) {
let monthData = {}
let yearData = {}
const monthDataKey = element.year + '-' + element.month
if (monthDataKey in monthData) {
monthData[monthDataKey] += element.load
} else {
monthData[monthDataKey] = element.load
}
const yearDataKey = element.year
if (yearDataKey in yearData) {
yearData[yearDataKey] += element.load
} else {
yearData[yearDataKey] = element.load
}
})
// Aggregation for Month data
const aggregatedMonthData = await buildAggregatedData(
'com.grandlyon.egl.month'
)
await storeData(aggregatedMonthData, 'com.grandlyon.egl.month', [
'year',
'month',
])
// Aggregation for Year data
const aggregatedYearData = await buildAggregatedData(
'com.grandlyon.egl.year'
)
await storeData(aggregatedYearData, 'com.grandlyon.egl.year', ['year'])
}
}
/**
* Retrieve and remove old data for a specific doctype
* Return an Array of aggregated data
async function buildAggregatedData(data, doctype) {
log('info', 'entering buildAggregatedData')
let aggregatedData = []
for (let [key, value] of Object.entries(data)) {
const data = await buildDataFromKey(doctype, key, value)
const oldValue = await resetInProgressAggregatedData(data, doctype)
log('info', 'Data load + old value is ' + data.load + ' + ' + oldValue)
data.load += oldValue
aggregatedData.push(data)
return aggregatedData
async function authenticate(login, password, baseUrl, apiAuthKey) {
const authRequest = {
method: 'POST',
uri: baseUrl + '/connect.aspx',
'Content-Type': 'application/x-www-form-urlencoded',
pass: password,
json: true,
}
response = await rp(authRequest)
if (response.codeRetour === 100) {
return response
} else {
Sentry.captureException(JSON.stringify(response))
throw new Error()
}
} catch (error) {
throw new Error(errors.LOGIN_FAILED)
}
}
async function getData(response, baseUrl, apiAuthKey) {
log('debug', 'Start date : ' + startDate)
log('debug', 'End date : ' + endDate)
method: 'POST',
uri: baseUrl + '/getAllAgregatsByAbonnement.aspx',
'Content-Type': 'application/x-www-form-urlencoded',
token: response.resultatRetour.token,
num_abt: response.resultatRetour.num_abt,
date_debut: startDate,
date_fin: endDate,
json: true,
}
const responseEgl = await rp(dataRequest).then(eglRawData => {
eglRawData.resultatRetour.sort(function(a, b) {
return new Date(a.DateReleve) - new Date(b.DateReleve)
})
return eglRawData
})
return format(responseEgl)
throw errors.LOGIN_FAILED
throw errors.VENDOR_DOWN
throw errors.UNKNOWN_ERROR
log('debug', 'Error from getAllAgregatsByAbonnement')
throw new Error(errors.VENDOR_DOWN)
log('info', 'origin response size is : ' + response.resultatRetour.length)
// Store first value as reference for index processing
let refValue = response.resultatRetour[0]
// Create copy of data without first value
.filter(value => value.ValeurIndex)
log('info', 'filtered size is : ' + data.length)
return data.map(value => {
const time = moment(value.DateReleve, moment.ISO_8601)
const processedLoad = value.ValeurIndex - refValue.ValeurIndex
if (processedLoad < 0) {
'error',
`processing load for day ${parseInt(time.format('D'))}/${parseInt(
time.format('M')
)}/${parseInt(time.format('YYYY'))}, value is : ${processedLoad}`
)
throw errors.VENDOR_DOWN
}
// Change index ref value
refValue = value
load: processedLoad,
year: parseInt(time.format('YYYY')),
month: parseInt(time.format('M')),
day: parseInt(time.format('D')),
type: value.TypeAgregat,
}
})
/**
* Save data in the right doctype db and prevent duplicated keys
*/
async function storeData(data, doctype, filterKeys) {
log('debug', 'Store into ' + doctype)
log('debug', 'Store into keys : ' + filterKeys)
// log('info', 'Saving data ' + v.load + ' for ' + v.day + '/' + v.month + '/' + v.year)
// })
const filteredDocuments = await hydrateAndFilter(data, doctype, {
keys: filterKeys,
})
return await addData(filteredDocuments, doctype)
}
/**
* Format an entry for DB storage
* using key and value
* For year doctype: key = 'YYYY'
* For month doctype: key = 'YYYY-MM'
*/
async function buildDataFromKey(doctype, key, value) {
let year, month, day, hour
if (doctype === 'com.grandlyon.egl.year') {
year = key
month = 1
day = 0
hour = 0
} else if (doctype === 'com.grandlyon.egl.month') {
const split = key.split('-')
year = split[0]
month = split[1]
day = 0
hour = 0
const split = key.split('-')
year = split[0]
month = split[1]
day = split[2]
hour = split[3]
return {
load: Math.round(value * 10000) / 10000,
year: parseInt(year),
month: parseInt(month),
day: parseInt(day),
hour: parseInt(hour),
minute: 0,
}
/**
* Function handling special case.
* The temporary aggregated data need to be remove in order for the most recent one te be saved.
* { load: 76.712, month: 2020, ... } need to be replace by
* { load: 82.212, month: 2020, ... } after egl data reprocess
async function resetInProgressAggregatedData(data, doctype) {
// /!\ Warning: cannot use mongo queries because not supported for dev by cozy-konnectors-libs
log('debug', 'Remove aggregated data for ' + doctype)
const result = await cozyClient.data.findAll(doctype)
let filtered = []
if (doctype === 'com.grandlyon.egl.year') {
// Yearly case
filtered = result.filter(function(el) {
return el.year == data.year
})
} else if (doctype === 'com.grandlyon.egl.month') {
return el.year == data.year && el.month == data.month
})
filtered = result.filter(function(el) {
return (
el.year == data.year &&
el.month == data.month &&
el.day == data.day &&
el.hour == data.hour
)
})
let sum = 0.0
sum += doc.load
log('debug', 'Removing this entry for ' + doc.load)
await cozyClient.data.delete(doctype, doc)
return sum
return 0.0
}
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
const log = __webpack_require__(2).namespace('cozy-konnector-libs');
const requestFactory = __webpack_require__(22);
const hydrateAndFilter = __webpack_require__(369);
const categorization = __webpack_require__(1162);
BaseKonnector: __webpack_require__(1222),
CookieKonnector: __webpack_require__(1300),
cozyClient: __webpack_require__(485),
errors: __webpack_require__(1228),
saveFiles: __webpack_require__(1224),
saveBills: __webpack_require__(1223),
saveIdentity: __webpack_require__(1266),
linkBankOperations: __webpack_require__(1245),
addData: __webpack_require__(1244),
htmlToPDF: __webpack_require__(1301).htmlToPDF,
createCozyPDFDocument: __webpack_require__(1301).createCozyPDFDocument,
filterData: deprecate(hydrateAndFilter, 'Use hydrateAndFilter now. filterData will be removed in cozy-konnector-libs@4'),
request: deprecate(requestFactory, 'Use requestFactory instead of request. It will be removed in cozy-konnector-libs@4'),
requestFactory,
retry: __webpack_require__(1225),
wrapIfSentrySetUp: __webpack_require__(1267).wrapIfSentrySetUp,
Document: __webpack_require__(1302),
signin: __webpack_require__(1262),
submitForm: __webpack_require__(1262),
scrape: __webpack_require__(1304),
mkdirp: __webpack_require__(1227),
normalizeFilename: __webpack_require__(1305),
utils: __webpack_require__(484),
solveCaptcha: __webpack_require__(1306),
createCategorizer: categorization.createCategorizer,
categorize: categorization.categorize,
};
function deprecate(wrapped, message) {
return function () {
log('warn', message);
return wrapped.apply(this, arguments);
};
}
/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {
const { filterLevel, filterSecrets } = __webpack_require__(3)
const Secret = __webpack_require__(4)
const { LOG_LEVEL } = process.env
let level = LOG_LEVEL || 'debug'
const format = __webpack_require__(5)
const filters = [filterLevel, filterSecrets]
const filterOut = function() {
for (const filter of filters) {
if (filter.apply(null, arguments) === false) {
return true
}
}
return false
}
* Use it to log messages in your konnector. Typical types are
* - `debug`
* - `warning`
* - `info`
* - `error`
* - `ok`
* They will be colored in development mode. In production mode, those logs are formatted in JSON to be interpreted by the stack and possibly sent to the client. `error` will stop the konnector.
* ```js
* logger = log('my-namespace')
* logger('debug', '365 bills')
* // my-namespace : debug : 365 bills
* logger('info', 'Page fetched')
* // my-namespace : info : Page fetched
* @param {string} type
* @param {string} message
* @param {string} label
* @param {string} namespace
function log(type, message, label, namespace) {
if (filterOut(level, type, message, label, namespace)) {
return
}
// eslint-disable-next-line no-console
console.log(format(type, message, label, namespace))
}
log.addFilter = function(filter) {
return filters.push(filter)
log.setLevel = function(lvl) {
level = lvl
}
// Short-hands
const methods = ['debug', 'info', 'warn', 'error', 'ok', 'critical']
methods.forEach(level => {
log[level] = function(message, label, namespace) {
return log(level, message, label, namespace)
}
})
log.setNoRetry = obj => {
if (obj) obj.no_retry = true
else obj = { no_retry: true }
return obj.no_retry
}
log.Secret = Secret
log.namespace = function(namespace) {
return function(type, message, label, ns = namespace) {
log(type, message, label, ns)
/***/ }),
/* 3 */
/***/ (function(module, exports, __webpack_require__) {
const levels = {
secret: 0,
debug: 10,
info: 20,
warn: 30,
error: 40,
ok: 50,
critical: 50
const filterSecrets = function(level, type, message) {
if (type !== 'secret' && message instanceof Secret) {
throw new Error('You should log a secret with log.secret')
const filterLevel = function(level, type) {
return levels[type] >= levels[level]
module.exports = {
filterSecrets,
filterLevel
}
/***/ }),
/* 4 */
/***/ (function(module, exports) {
const Secret = function(data) {
Object.assign(this, data)
return this
Secret.prototype.toString = function() {
throw new Error('Cannot convert Secret to string')
/***/ (function(module, exports, __webpack_require__) {
const prodFormat = __webpack_require__(6)
const devFormat = __webpack_require__(8)
switch ("none") {
case 'production':
module.exports = prodFormat
break
case 'development':
module.exports = devFormat
break
case 'standalone':
module.exports = devFormat
break
case 'test':
module.exports = devFormat
break
default:
module.exports = prodFormat
}
/***/ }),
/* 6 */
/***/ (function(module, exports, __webpack_require__) {
function prodFormat(type, message, label, namespace) {
const log = { time: new Date(), type, label, namespace }
if (typeof message === 'object') {
if (message && message.no_retry) {
log.no_retry = message.no_retry
if (message && message.message) {
log.message = message.message
}
} else {
log.message = message
}
// properly display error messages
if (log.message && log.message.stack) {
log.message = log.message.stack
}
// cut the string to avoid a fail in the stack
let result = log
try {
result = stringify(log).substr(0, LOG_LENGTH_LIMIT)
} catch (err) {
// eslint-disable-next-line no-console
console.log(err.message, 'cozy-logger: Failed to convert message to JSON')
}
return result
}
exports = module.exports = stringify
exports.getSerialize = serializer
function stringify(obj, replacer, spaces, cycleReplacer) {
return JSON.stringify(obj, serializer(replacer, cycleReplacer), spaces)
}
function serializer(replacer, cycleReplacer) {
var stack = [], keys = []
if (cycleReplacer == null) cycleReplacer = function(key, value) {
if (stack[0] === value) return "[Circular ~]"
return "[Circular ~." + keys.slice(0, stack.indexOf(value)).join(".") + "]"
}
return function(key, value) {
if (stack.length > 0) {
var thisPos = stack.indexOf(this)
~thisPos ? stack.splice(thisPos + 1) : stack.push(this)
~thisPos ? keys.splice(thisPos, Infinity, key) : keys.push(key)
if (~stack.indexOf(value)) value = cycleReplacer.call(this, key, value)
}
else stack.push(value)
return replacer == null ? value : replacer.call(this, key, value)
}
/***/ (function(module, exports, __webpack_require__) {
const util = __webpack_require__(9)
const chalk = __webpack_require__(10)
if (util && util.inspect && util.inspect.defaultOptions) {
util.inspect.defaultOptions.maxArrayLength = null
util.inspect.defaultOptions.depth = 2
util.inspect.defaultOptions.colors = true
}
const type2color = {
debug: 'cyan',
warn: 'yellow',
info: 'blue',
error: 'red',
ok: 'green',
secret: 'red',
critical: 'red'
}
function devFormat(type, message, label, namespace) {
let formatmessage = message
if (typeof formatmessage !== 'string') {
formatmessage = util.inspect(formatmessage)
}
let formatlabel = label ? ` : "${label}" ` : ''
let formatnamespace = namespace ? chalk.magenta(`${namespace}: `) : ''
let color = type2color[type]
let formattype = color ? chalk[color](type) : type
return `${formatnamespace}${formattype}${formatlabel} : ${formatmessage}`
/***/ }),
/* 9 */
/***/ (function(module, exports) {
/***/ }),
/* 10 */
/***/ (function(module, exports, __webpack_require__) {
const escapeStringRegexp = __webpack_require__(11);
const ansiStyles = __webpack_require__(12);
const stdoutColor = __webpack_require__(18).stdout;
const isSimpleWindowsTerm = process.platform === 'win32' && !(process.env.TERM || '').toLowerCase().startsWith('xterm');
// `supportsColor.level` → `ansiStyles.color[name]` mapping
const levelMapping = ['ansi', 'ansi', 'ansi256', 'ansi16m'];
// `color-convert` models to exclude from the Chalk API due to conflicts and such
const skipModels = new Set(['gray']);
function applyOptions(obj, options) {
options = options || {};
// Detect level if not set manually
const scLevel = stdoutColor ? stdoutColor.level : 0;
obj.level = options.level === undefined ? scLevel : options.level;
obj.enabled = 'enabled' in options ? options.enabled : obj.level > 0;
}
function Chalk(options) {
// We check for this.template here since calling `chalk.constructor()`
// by itself will have a `this` of a previously constructed chalk object
if (!this || !(this instanceof Chalk) || this.template) {
const chalk = {};
applyOptions(chalk, options);
chalk.template = function () {
const args = [].slice.call(arguments);
return chalkTag.apply(null, [chalk.template].concat(args));
};
Object.setPrototypeOf(chalk, Chalk.prototype);
Object.setPrototypeOf(chalk.template, chalk);
// Use bright blue on Windows as the normal blue color is illegible
if (isSimpleWindowsTerm) {
ansiStyles.blue.open = '\u001B[94m';
}
for (const key of Object.keys(ansiStyles)) {
ansiStyles[key].closeRe = new RegExp(escapeStringRegexp(ansiStyles[key].close), 'g');
styles[key] = {
get() {
const codes = ansiStyles[key];
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, key);
}
};
}
styles.visible = {
get() {
return build.call(this, this._styles || [], true, 'visible');
}
ansiStyles.color.closeRe = new RegExp(escapeStringRegexp(ansiStyles.color.close), 'g');
for (const model of Object.keys(ansiStyles.color.ansi)) {
if (skipModels.has(model)) {
continue;
}
styles[model] = {
get() {
const level = this.level;
return function () {
const open = ansiStyles.color[levelMapping[level]][model].apply(null, arguments);
const codes = {
open,
close: ansiStyles.color.close,
closeRe: ansiStyles.color.closeRe
};
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model);
};
}
};
}
ansiStyles.bgColor.closeRe = new RegExp(escapeStringRegexp(ansiStyles.bgColor.close), 'g');
for (const model of Object.keys(ansiStyles.bgColor.ansi)) {
if (skipModels.has(model)) {
continue;
}
const bgModel = 'bg' + model[0].toUpperCase() + model.slice(1);
styles[bgModel] = {
get() {
const level = this.level;
return function () {
const open = ansiStyles.bgColor[levelMapping[level]][model].apply(null, arguments);
const codes = {
open,
close: ansiStyles.bgColor.close,
closeRe: ansiStyles.bgColor.closeRe
};
return build.call(this, this._styles ? this._styles.concat(codes) : [codes], this._empty, model);
};
}
};
}
const proto = Object.defineProperties(() => {}, styles);
function build(_styles, _empty, key) {
const builder = function () {
return applyStyle.apply(builder, arguments);
};
builder._styles = _styles;
builder._empty = _empty;
Object.defineProperty(builder, 'level', {
enumerable: true,
get() {
return self.level;
},
set(level) {
self.level = level;
}
});
Object.defineProperty(builder, 'enabled', {
enumerable: true,
get() {
return self.enabled;
},
set(enabled) {
self.enabled = enabled;
}
});
// See below for fix regarding invisible grey/dim combination on Windows
builder.hasGrey = this.hasGrey || key === 'gray' || key === 'grey';
// `__proto__` is used because we must return a function, but there is
// no way to create a function with a different prototype
builder.__proto__ = proto; // eslint-disable-line no-proto
function applyStyle() {
// Support varags, but simply cast to string in case there's only one arg
const args = arguments;
const argsLen = args.length;
let str = String(arguments[0]);
if (argsLen > 1) {
// Don't slice `arguments`, it prevents V8 optimizations