cafe-utility 3.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/index.d.ts +491 -0
  2. package/index.js +735 -499
  3. package/module.mjs +1 -0
  4. package/package.json +3 -2
package/index.js CHANGED
@@ -1,21 +1,17 @@
1
- function nodeModuleRequire(module) {
2
- if (require) {
3
- try {
4
- return require(module)
5
- } catch {}
6
- }
7
- }
1
+ const ChildProcess = require('child_process')
2
+ const NodeCrypto = require('crypto')
3
+ const Fs = require('fs')
4
+ const Path = require('path')
8
5
 
9
- const ChildProcess = nodeModuleRequire('child_process')
10
- const Crypto = nodeModuleRequire('crypto')
11
- const Fs = nodeModuleRequire('fs')
12
- const Path = nodeModuleRequire('path')
13
-
14
- const raceFulfilled = promises => invertPromise(Promise.all(promises.map(invertPromise)))
6
+ async function invertPromise(promise) {
7
+ return new Promise((resolve, reject) => promise.then(reject, resolve))
8
+ }
15
9
 
16
- const invertPromise = promise => new Promise((resolve, reject) => promise.then(reject, resolve))
10
+ async function raceFulfilled(promises) {
11
+ invertPromise(Promise.all(promises.map(invertPromise)))
12
+ }
17
13
 
18
- const runInParallelBatches = async (promises, concurrency = 1) => {
14
+ async function runInParallelBatches(promises, concurrency = 1) {
19
15
  const batches = splitByCount(promises, concurrency)
20
16
  const results = []
21
17
  const jobs = batches.map(async batch => {
@@ -27,14 +23,15 @@ const runInParallelBatches = async (promises, concurrency = 1) => {
27
23
  return results
28
24
  }
29
25
 
30
- const sleepMillis = millis =>
31
- new Promise(resolve =>
26
+ async function sleepMillis(millis) {
27
+ return new Promise(resolve =>
32
28
  setTimeout(() => {
33
29
  resolve(true)
34
30
  }, millis)
35
31
  )
32
+ }
36
33
 
37
- const shuffle = array => {
34
+ function shuffle(array) {
38
35
  for (let i = array.length - 1; i > 0; i--) {
39
36
  const j = Math.floor(Math.random() * (i + 1))
40
37
  const swap = array[i]
@@ -44,7 +41,7 @@ const shuffle = array => {
44
41
  return array
45
42
  }
46
43
 
47
- const onlyOrThrow = array => {
44
+ function onlyOrThrow(array) {
48
45
  if (array && array.length === 1) {
49
46
  return array[0]
50
47
  }
@@ -54,16 +51,18 @@ const onlyOrThrow = array => {
54
51
  throw Error('Expected array, got: ' + array)
55
52
  }
56
53
 
57
- const onlyOrNull = array => {
54
+ function onlyOrNull(array) {
58
55
  if (array && array.length === 1) {
59
56
  return array[0]
60
57
  }
61
58
  return null
62
59
  }
63
60
 
64
- const firstOrNull = array => (array.length > 0 ? array[0] : null)
61
+ function firstOrNull(array) {
62
+ return array.length > 0 ? array[0] : null
63
+ }
65
64
 
66
- const initializeArray = (count, initializer) => {
65
+ function initializeArray(count, initializer) {
67
66
  const results = []
68
67
  for (let i = 0; i < count; i++) {
69
68
  results.push(initializer(i))
@@ -71,23 +70,43 @@ const initializeArray = (count, initializer) => {
71
70
  return results
72
71
  }
73
72
 
74
- const takeRandomly = (array, count) => shuffle(array).slice(0, count)
73
+ function takeRandomly(array, count) {
74
+ return shuffle(array).slice(0, count)
75
+ }
75
76
 
76
- const pluck = (array, key) => array.map(element => element[key])
77
+ function pickRandomIndices(array, count) {
78
+ return shuffle(range(0, array.length - 1)).slice(0, count)
79
+ }
77
80
 
78
- const randomIntInclusive = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min
81
+ function pluck(array, key) {
82
+ return array.map(element => element[key])
83
+ }
79
84
 
80
- const randomBetween = (min, max) => Math.random() * (max - min) + min
85
+ function randomIntInclusive(min, max) {
86
+ return Math.floor(Math.random() * (max - min + 1)) + min
87
+ }
81
88
 
82
- const signedRandom = () => Math.random() * 2 - 1
89
+ function randomBetween(min, max) {
90
+ return Math.random() * (max - min) + min
91
+ }
83
92
 
84
- const chance = threshold => Math.random() < threshold
93
+ function signedRandom() {
94
+ return Math.random() * 2 - 1
95
+ }
96
+
97
+ function chance(threshold) {
98
+ return Math.random() < threshold
99
+ }
85
100
 
86
- const pick = array => array[Math.floor(array.length * Math.random())]
101
+ function pick(array) {
102
+ return array[Math.floor(array.length * Math.random())]
103
+ }
87
104
 
88
- const last = array => array[array.length - 1]
105
+ function last(array) {
106
+ return array[array.length - 1]
107
+ }
89
108
 
90
- const pickWeighted = (array, weights, randomNumber) => {
109
+ function pickWeighted(array, weights, randomNumber) {
91
110
  if (array.length !== weights.length) {
92
111
  throw new Error('Array length mismatch')
93
112
  }
@@ -99,18 +118,19 @@ const pickWeighted = (array, weights, randomNumber) => {
99
118
  return array[i]
100
119
  }
101
120
  }
121
+ throw new Error('Weight out of range')
102
122
  }
103
123
 
104
- const sortWeighted = (array, weights) => {
124
+ function sortWeighted(array, weights) {
105
125
  const rolls = weights.map(weight => Math.random() * weight)
106
126
  const results = []
107
- for (let i = 0; i < array; i++) {
127
+ for (let i = 0; i < array.length; i++) {
108
128
  results.push([array[i], rolls[i]])
109
129
  }
110
130
  return results.sort((a, b) => a[1] - b[1]).map(a => a[0])
111
131
  }
112
132
 
113
- const getDeep = (object, path) => {
133
+ function getDeep(object, path) {
114
134
  const parts = path.split('.')
115
135
  let buffer = object
116
136
  for (const part of parts) {
@@ -122,9 +142,11 @@ const getDeep = (object, path) => {
122
142
  return buffer
123
143
  }
124
144
 
125
- const getDeepOrElse = (object, path, fallback) => getDeep(object, path) || fallback
145
+ function getDeepOrElse(object, path, fallback) {
146
+ return getDeep(object, path) || fallback
147
+ }
126
148
 
127
- const setDeep = (object, path, value) => {
149
+ function setDeep(object, path, value) {
128
150
  const parts = path.split(/\.|\[/)
129
151
  let buffer = object
130
152
  for (let i = 0; i < parts.length; i++) {
@@ -148,16 +170,24 @@ const setDeep = (object, path, value) => {
148
170
  return value
149
171
  }
150
172
 
151
- const ensureDeep = (object, path, value) => getDeep(object, path) || setDeep(object, path, value)
173
+ function incrementDeep(object, path, amount = 1) {
174
+ const existing = getDeep(object, path) || 0
175
+ setDeep(object, path, existing + amount)
176
+ return existing
177
+ }
178
+
179
+ function ensureDeep(object, path, value) {
180
+ return getDeep(object, path) || setDeep(object, path, value)
181
+ }
152
182
 
153
- const deleteDeep = (object, path) => {
183
+ function deleteDeep(object, path) {
154
184
  const location = beforeLast(path, '.')
155
185
  const toDelete = afterLast(path, '.')
156
186
  const segment = getDeep(object, location)
157
187
  delete segment[toDelete]
158
188
  }
159
189
 
160
- const replaceDeep = (object, path, value) => {
190
+ function replaceDeep(object, path, value) {
161
191
  const existing = getDeep(object, path)
162
192
  if (!existing) {
163
193
  throw new Error("Key '" + path + "' does not exist.")
@@ -166,7 +196,7 @@ const replaceDeep = (object, path, value) => {
166
196
  return existing
167
197
  }
168
198
 
169
- const getFirstDeep = (object, paths, fallbackToAnyKey) => {
199
+ function getFirstDeep(object, paths, fallbackToAnyKey) {
170
200
  for (const path of paths) {
171
201
  const value = getDeep(object, path)
172
202
  if (value) {
@@ -182,7 +212,7 @@ const getFirstDeep = (object, paths, fallbackToAnyKey) => {
182
212
  return null
183
213
  }
184
214
 
185
- const forever = async (callable, millis) => {
215
+ async function forever(callable, millis) {
186
216
  while (true) {
187
217
  try {
188
218
  await callable()
@@ -193,11 +223,15 @@ const forever = async (callable, millis) => {
193
223
  }
194
224
  }
195
225
 
196
- const readUtf8FileAsync = async path => Fs.promises.readFile(path, 'utf8')
226
+ async function readUtf8FileAsync(path) {
227
+ return Fs.promises.readFile(path, 'utf8')
228
+ }
197
229
 
198
- const readJsonAsync = async path => JSON.parse(await readUtf8FileAsync(path))
230
+ async function readJsonAsync(path) {
231
+ return JSON.parse(await readUtf8FileAsync(path))
232
+ }
199
233
 
200
- const writeJsonAsync = async (path, object, prettify) => {
234
+ async function writeJsonAsync(path, object, prettify) {
201
235
  if (prettify) {
202
236
  await Fs.promises.writeFile(path, JSON.stringify(object, null, 4))
203
237
  } else {
@@ -205,16 +239,23 @@ const writeJsonAsync = async (path, object, prettify) => {
205
239
  }
206
240
  }
207
241
 
208
- const readLinesAsync = async path => (await readUtf8FileAsync(path)).split(/\r?\n/)
242
+ async function readLinesAsync(path) {
243
+ return (await readUtf8FileAsync(path)).split(/\r?\n/)
244
+ }
209
245
 
210
- const readMatchingLines = async (path, filterFn) => (await readLinesAsync(path)).filter(filterFn)
246
+ async function readMatchingLines(path, filterFn) {
247
+ return (await readLinesAsync(path)).filter(filterFn)
248
+ }
211
249
 
212
- const readNonEmptyLines = async path => readMatchingLines(path, x => x)
250
+ async function readNonEmptyLines(path) {
251
+ return readMatchingLines(path, x => !!x)
252
+ }
213
253
 
214
- const readCsv = async (path, skip = 0, delimiter = ',', quote = '"') =>
215
- (skip ? (await readNonEmptyLines(path)).slice(skip) : await readNonEmptyLines(path)).map(x =>
254
+ async function readCsv(path, skip = 0, delimiter = ',', quote = '"') {
255
+ return (skip ? (await readNonEmptyLines(path)).slice(skip) : await readNonEmptyLines(path)).map(x =>
216
256
  parseCsv(x, delimiter, quote)
217
257
  )
258
+ }
218
259
 
219
260
  async function* walkTreeAsync(path) {
220
261
  for await (const directory of await Fs.promises.opendir(path)) {
@@ -227,13 +268,13 @@ async function* walkTreeAsync(path) {
227
268
  }
228
269
  }
229
270
 
230
- const removeLeadingDirectory = (path, directory) => {
271
+ function removeLeadingDirectory(path, directory) {
231
272
  directory = directory.startsWith('./') ? directory.slice(2) : directory
232
273
  directory = directory.endsWith('/') ? directory : directory + '/'
233
274
  return path.replace(directory, '')
234
275
  }
235
276
 
236
- const readdirDeepAsync = async (path, cwd) => {
277
+ async function readdirDeepAsync(path, cwd) {
237
278
  const entries = []
238
279
  for await (const entry of walkTreeAsync(path)) {
239
280
  entries.push(cwd ? removeLeadingDirectory(entry, cwd) : entry)
@@ -241,7 +282,7 @@ const readdirDeepAsync = async (path, cwd) => {
241
282
  return entries
242
283
  }
243
284
 
244
- const existsAsync = async path => {
285
+ async function existsAsync(path) {
245
286
  try {
246
287
  await Fs.promises.stat(path)
247
288
  return true
@@ -250,14 +291,16 @@ const existsAsync = async path => {
250
291
  }
251
292
  }
252
293
 
253
- const getFileSize = async path => {
294
+ async function getFileSize(path) {
254
295
  const stats = await Fs.promises.stat(path)
255
296
  return stats.size
256
297
  }
257
298
 
258
- const asMegabytes = number => number / 1024 / 1024
299
+ function asMegabytes(number) {
300
+ return number / 1024 / 1024
301
+ }
259
302
 
260
- const getDirectorySize = async path => {
303
+ async function getDirectorySize(path) {
261
304
  let size = 0
262
305
  for await (const file of walkTreeAsync(path)) {
263
306
  size += await getFileSize(file)
@@ -265,52 +308,86 @@ const getDirectorySize = async path => {
265
308
  return size
266
309
  }
267
310
 
268
- const convertBytes = bytes => {
311
+ function convertBytes(bytes) {
269
312
  if (bytes > 1000000) {
270
313
  return (bytes / 1000000).toFixed(3) + 'MB'
271
314
  }
272
315
  if (bytes > 1000) {
273
316
  return (bytes / 1000).toFixed(3) + 'KB'
274
317
  }
275
- return bytes
318
+ return bytes + ''
276
319
  }
277
320
 
278
- const getChecksum = data => {
279
- const hash = Crypto.createHash('sha1')
321
+ function getChecksum(data) {
322
+ const hash = NodeCrypto.createHash('sha1')
280
323
  hash.update(data)
281
324
  return hash.digest('hex')
282
325
  }
283
326
 
284
- const getChecksumOfFile = async path =>
285
- new Promise((resolve, reject) => {
286
- const hash = Crypto.createHash('sha1')
327
+ async function getChecksumOfFile(path) {
328
+ return new Promise((resolve, reject) => {
329
+ const hash = NodeCrypto.createHash('sha1')
287
330
  const readStream = Fs.createReadStream(path)
288
331
  readStream.on('error', reject)
289
332
  readStream.on('data', chunk => hash.update(chunk))
290
333
  readStream.on('end', () => resolve(hash.digest('hex')))
291
334
  })
335
+ }
292
336
 
293
- const isObject = value => value !== null && typeof value === 'object'
337
+ function isObject(value) {
338
+ return value !== null && typeof value === 'object'
339
+ }
294
340
 
295
- const isStrictlyObject = value => isObject(value) && !Array.isArray(value)
341
+ function isStrictlyObject(value) {
342
+ return isObject(value) && !Array.isArray(value)
343
+ }
296
344
 
297
- const isUndefined = value => typeof value === 'undefined'
345
+ function isEmptyArray(value) {
346
+ return Array.isArray(value) && value.length === 0
347
+ }
298
348
 
299
- const isFunction = value => Object.prototype.toString.call(value) === '[object Function]'
349
+ function isUndefined(value) {
350
+ return typeof value === 'undefined'
351
+ }
300
352
 
301
- const isString = value => Object.prototype.toString.call(value) === '[object String]'
353
+ function isFunction(value) {
354
+ return Object.prototype.toString.call(value) === '[object Function]'
355
+ }
302
356
 
303
- const isNumber = value => !isNaN(value) && String(value) === String(parseFloat(value))
357
+ function isString(value) {
358
+ return Object.prototype.toString.call(value) === '[object String]'
359
+ }
304
360
 
305
- const isDate = value => Object.prototype.toString.call(value) === '[object Date]'
361
+ function isPromise(value) {
362
+ return value && typeof value.then === 'function'
363
+ }
306
364
 
307
- const isBlank = value => !isString(value) || value.trim().length === 0
365
+ function isNumber(value) {
366
+ return !isNaN(value) && String(value) === String(parseFloat(value))
367
+ }
308
368
 
309
- const alphanumericAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
369
+ function isDate(value) {
370
+ return Object.prototype.toString.call(value) === '[object Date]'
371
+ }
372
+
373
+ function isBlank(value) {
374
+ return !isString(value) || value.trim().length === 0
375
+ }
310
376
 
377
+ const alphabet = 'abcdefghijklmnopqrstuvwxyz'
378
+ const alphanumericAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890'
379
+ const richAsciiAlphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*()_+-=[]{}|;:<>?,./'
380
+ const unicodeTestingAlphabet = ['—', '\\', '東', '京', '都', '𝖆', '𝖇', '𝖈', '👾', '🙇', '💁', '🙅']
311
381
  const hexAlphabet = '0123456789abcdef'
382
+ function randomLetterString(length) {
383
+ let buffer = ''
384
+ for (let i = 0; i < length; i++) {
385
+ buffer += alphabet[Math.floor(Math.random() * alphabet.length)]
386
+ }
387
+ return buffer
388
+ }
312
389
 
313
- const randomAlphanumericString = length => {
390
+ function randomAlphanumericString(length) {
314
391
  let buffer = ''
315
392
  for (let i = 0; i < length; i++) {
316
393
  buffer += alphanumericAlphabet[Math.floor(Math.random() * alphanumericAlphabet.length)]
@@ -318,7 +395,23 @@ const randomAlphanumericString = length => {
318
395
  return buffer
319
396
  }
320
397
 
321
- const randomHexString = length => {
398
+ function randomRichAsciiString(length) {
399
+ let buffer = ''
400
+ for (let i = 0; i < length; i++) {
401
+ buffer += richAsciiAlphabet[Math.floor(Math.random() * richAsciiAlphabet.length)]
402
+ }
403
+ return buffer
404
+ }
405
+
406
+ function randomUnicodeString(length) {
407
+ let buffer = ''
408
+ for (let i = 0; i < length; i++) {
409
+ buffer += unicodeTestingAlphabet[Math.floor(Math.random() * unicodeTestingAlphabet.length)]
410
+ }
411
+ return buffer
412
+ }
413
+
414
+ function randomHexString(length) {
322
415
  let buffer = ''
323
416
  for (let i = 0; i < length; i++) {
324
417
  buffer += hexAlphabet[Math.floor(Math.random() * hexAlphabet.length)]
@@ -326,47 +419,35 @@ const randomHexString = length => {
326
419
  return buffer
327
420
  }
328
421
 
329
- /**
330
- * @param {*} string
331
- * @returns {string}
332
- */
333
- const asString = string => {
422
+ function asString(string) {
334
423
  if (isBlank(string)) {
335
424
  throw new TypeError('Expected string, got: ' + string)
336
425
  }
337
426
  return string
338
427
  }
339
428
 
340
- /**
341
- * @param {*} number
342
- * @returns {number}
343
- */
344
- const asNumber = number => {
429
+ function asNumber(number) {
345
430
  if (!isNumber(number)) {
346
431
  throw new TypeError('Expected number, got: ' + number)
347
432
  }
348
433
  return number
349
434
  }
350
435
 
351
- /**
352
- * @param {*} date
353
- * @returns {Date}
354
- */
355
- const asDate = date => {
356
- if (Object.prototype.toString.call(date) === '[object Date]') {
357
- return date
436
+ function asDate(date) {
437
+ if (!isDate(date)) {
438
+ throw new TypeError('Expected date, got: ' + date)
358
439
  }
359
- throw new TypeError('Expected date, got: ' + date)
440
+ return date
360
441
  }
361
442
 
362
- const asNullableString = string => {
443
+ function asNullableString(string) {
363
444
  if (isBlank(string)) {
364
445
  return null
365
446
  }
366
447
  return string
367
448
  }
368
449
 
369
- const represent = value => {
450
+ function represent(value) {
370
451
  if (isObject(value)) {
371
452
  return JSON.stringify(value, null, 4)
372
453
  }
@@ -383,8 +464,8 @@ const loggerGlobalState = {
383
464
  fileStream: null
384
465
  }
385
466
 
386
- const log = (level, module, pieces) => {
387
- const timestamp = new Date().toISOString().replace('T', ' ').substr(0, 19)
467
+ function log(level, module, pieces) {
468
+ const timestamp = new Date().toISOString().replace('T', ' ').slice(0, 19)
388
469
  const message = `${timestamp} ${level} ${module} ${pieces.map(represent).join(' ')}\n`
389
470
  process.stdout.write(message)
390
471
  if (level === 'ERROR') {
@@ -395,7 +476,7 @@ const log = (level, module, pieces) => {
395
476
  }
396
477
  }
397
478
 
398
- const createLogger = module => {
479
+ function createLogger(module) {
399
480
  module = last(module.split(/\\|\//))
400
481
  return {
401
482
  trace: (...pieces) => {
@@ -416,11 +497,11 @@ const createLogger = module => {
416
497
  }
417
498
  }
418
499
 
419
- const enableFileLogging = path => {
500
+ function enableFileLogging(path) {
420
501
  loggerGlobalState.fileStream = Fs.createWriteStream(path, { flags: 'a' })
421
502
  }
422
503
 
423
- const expandError = (error, stackTrace) => {
504
+ function expandError(error, stackTrace) {
424
505
  if (isString(error)) {
425
506
  return error
426
507
  }
@@ -432,7 +513,7 @@ const expandError = (error, stackTrace) => {
432
513
  return stackTrace && error.stack ? joined + '\n' + error.stack : joined
433
514
  }
434
515
 
435
- const mergeDeep = (target, source) => {
516
+ function mergeDeep(target, source) {
436
517
  if (isStrictlyObject(target) && isStrictlyObject(source)) {
437
518
  for (const key in source) {
438
519
  if (isStrictlyObject(source[key])) {
@@ -447,9 +528,10 @@ const mergeDeep = (target, source) => {
447
528
  }
448
529
  }
449
530
  }
531
+ return target
450
532
  }
451
533
 
452
- const zip = (objects, reducer) => {
534
+ function zip(objects, reducer) {
453
535
  const result = {}
454
536
  for (const object of objects) {
455
537
  for (const key of Object.keys(object)) {
@@ -463,13 +545,11 @@ const zip = (objects, reducer) => {
463
545
  return result
464
546
  }
465
547
 
466
- const zipSum = objects => zip(objects, (x, y) => x + y)
548
+ function zipSum(objects) {
549
+ return zip(objects, (x, y) => x + y)
550
+ }
467
551
 
468
- /**
469
- * @param {*} value
470
- * @returns {number}
471
- */
472
- const asPageNumber = value => {
552
+ function asPageNumber(value) {
473
553
  let number
474
554
  try {
475
555
  number = parseInt(value, 10)
@@ -488,52 +568,37 @@ const asPageNumber = value => {
488
568
  return number
489
569
  }
490
570
 
491
- const pushToBucket = (object, bucket, item) => {
571
+ function pushToBucket(object, bucket, item) {
492
572
  if (!object[bucket]) {
493
573
  object[bucket] = []
494
574
  }
495
575
  object[bucket].push(item)
496
576
  }
497
577
 
498
- const unshiftAndLimit = (array, item, limit) => {
578
+ function unshiftAndLimit(array, item, limit) {
499
579
  array.unshift(item)
500
580
  while (array.length > limit) {
501
581
  array.pop()
502
582
  }
503
583
  }
504
584
 
505
- const atRolling = (array, index) => {
506
- const realIndex = index % array.length
585
+ function atRolling(array, index) {
586
+ let realIndex = index % array.length
587
+ if (realIndex < 0) {
588
+ realIndex += array.length
589
+ }
507
590
  return array[realIndex]
508
591
  }
509
592
 
510
- const pushAll = (array, elements) => Array.prototype.push.apply(array, elements)
511
-
512
- const unshiftAll = (array, elements) => Array.prototype.unshift.apply(array, elements)
513
-
514
- const mapWithIndex = (array, callback) => {
515
- const results = new Array(array.length)
516
- for (let i = 0; i < array.length; i++) {
517
- results[i] = callback(array[i], i)
518
- }
519
- return results
593
+ function pushAll(array, elements) {
594
+ Array.prototype.push.apply(array, elements)
520
595
  }
521
596
 
522
- const mapWithGetters = (object, keys) => {
523
- const mapped = {}
524
- for (const key of keys) {
525
- if (Array.isArray(key)) {
526
- mapped[key[1]] = object.get(key[0])
527
- } else {
528
- mapped[key] = object.get(key)
529
- }
530
- }
531
- return mapped
597
+ function unshiftAll(array, elements) {
598
+ Array.prototype.unshift.apply(array, elements)
532
599
  }
533
600
 
534
- const mapAllWithGetters = (array, keys) => array.map(object => mapWithGetters(object, keys))
535
-
536
- const mapAllAsync = async (array, fn) => {
601
+ async function mapAllAsync(array, fn) {
537
602
  const mapped = []
538
603
  for (const object of array) {
539
604
  mapped.push(await fn(object))
@@ -541,7 +606,7 @@ const mapAllAsync = async (array, fn) => {
541
606
  return mapped
542
607
  }
543
608
 
544
- const glue = (array, glueElement) => {
609
+ function glue(array, glueElement) {
545
610
  const results = []
546
611
  for (let i = 0; i < array.length; i++) {
547
612
  results.push(array[i])
@@ -556,7 +621,7 @@ const glue = (array, glueElement) => {
556
621
  return results
557
622
  }
558
623
 
559
- const pageify = (data, totalElements, pageSize, currentPage) => {
624
+ function pageify(data, totalElements, pageSize, currentPage) {
560
625
  const totalPages = Math.floor(totalElements / pageSize) + 1
561
626
  return {
562
627
  data,
@@ -567,49 +632,49 @@ const pageify = (data, totalElements, pageSize, currentPage) => {
567
632
  }
568
633
  }
569
634
 
570
- const asEqual = (a, b) => {
635
+ function asEqual(a, b) {
571
636
  if (a !== b) {
572
637
  throw Error(`Expected [${a}] to equal [${b}]`)
573
638
  }
574
639
  return [a, b]
575
640
  }
576
641
 
577
- const asTrue = data => {
642
+ function asTrue(data) {
578
643
  if (data !== true) {
579
644
  throw Error(`Expected [true], got [${data}]`)
580
645
  }
581
646
  return data
582
647
  }
583
648
 
584
- const asTruthy = data => {
649
+ function asTruthy(data) {
585
650
  if (!data) {
586
651
  throw Error(`Expected truthy value, got [${data}]`)
587
652
  }
588
653
  return data
589
654
  }
590
655
 
591
- const asFalse = data => {
656
+ function asFalse(data) {
592
657
  if (data !== false) {
593
658
  throw Error(`Expected [false], got [${data}]`)
594
659
  }
595
660
  return data
596
661
  }
597
662
 
598
- const asFalsy = data => {
663
+ function asFalsy(data) {
599
664
  if (data) {
600
665
  throw Error(`Expected falsy value, got [${data}]`)
601
666
  }
602
667
  return data
603
668
  }
604
669
 
605
- const asEither = (data, values) => {
670
+ function asEither(data, values) {
606
671
  if (!values.includes(data)) {
607
672
  throw Error(`Expected any of [${values.join(', ')}], got [${data}]`)
608
673
  }
609
674
  return data
610
675
  }
611
676
 
612
- const scheduleMany = (handlers, dates) => {
677
+ function scheduleMany(handlers, dates) {
613
678
  for (let i = 0; i < handlers.length; i++) {
614
679
  const handler = handlers[i]
615
680
  const date = dates[i]
@@ -618,13 +683,19 @@ const scheduleMany = (handlers, dates) => {
618
683
  }
619
684
  }
620
685
 
621
- const interpolate = (a, b, t) => a + (b - a) * t
686
+ function interpolate(a, b, t) {
687
+ return a + (b - a) * t
688
+ }
622
689
 
623
- const sum = array => array.reduce((a, b) => a + b, 0)
690
+ function sum(array) {
691
+ return array.reduce((a, b) => a + b, 0)
692
+ }
624
693
 
625
- const average = array => array.reduce((a, b) => a + b, 0) / array.length
694
+ function average(array) {
695
+ return array.reduce((a, b) => a + b, 0) / array.length
696
+ }
626
697
 
627
- const range = (start, end) => {
698
+ function range(start, end) {
628
699
  const array = []
629
700
  for (let i = start; i <= end; i++) {
630
701
  array.push(i)
@@ -632,7 +703,7 @@ const range = (start, end) => {
632
703
  return array
633
704
  }
634
705
 
635
- const includesAny = (string, substrings) => {
706
+ function includesAny(string, substrings) {
636
707
  for (const substring of substrings) {
637
708
  if (string.includes(substring)) {
638
709
  return true
@@ -641,8 +712,8 @@ const includesAny = (string, substrings) => {
641
712
  return false
642
713
  }
643
714
 
644
- const slugify = string =>
645
- string
715
+ function slugify(string) {
716
+ return string
646
717
  .toLowerCase()
647
718
  .normalize('NFD')
648
719
  .replace(/[\u0300-\u036f]/g, '')
@@ -651,14 +722,21 @@ const slugify = string =>
651
722
  .join('')
652
723
  .replace(/-+/g, '-')
653
724
  .replace(/^-|-$/g, '')
725
+ }
654
726
 
655
- const camelToTitle = string => capitalize(string.replace(/([A-Z])/g, ' $1'))
727
+ function camelToTitle(string) {
728
+ return capitalize(string.replace(/([A-Z])/g, ' $1'))
729
+ }
656
730
 
657
- const slugToTitle = string => string.split('-').map(capitalize).join(' ')
731
+ function slugToTitle(string) {
732
+ return string.split('-').map(capitalize).join(' ')
733
+ }
658
734
 
659
- const slugToCamel = string => decapitalize(string.split('-').map(capitalize).join(''))
735
+ function slugToCamel(string) {
736
+ return decapitalize(string.split('-').map(capitalize).join(''))
737
+ }
660
738
 
661
- const joinHumanly = (parts, separator = ', ', lastSeparator = ' and ') => {
739
+ function joinHumanly(parts, separator = ', ', lastSeparator = ' and ') {
662
740
  if (!parts || !parts.length) {
663
741
  return null
664
742
  }
@@ -671,16 +749,23 @@ const joinHumanly = (parts, separator = ', ', lastSeparator = ' and ') => {
671
749
  return `${parts.slice(0, parts.length - 1).join(separator)}${lastSeparator}${parts[parts.length - 1]}`
672
750
  }
673
751
 
674
- const surroundInOut = (string, filler) => filler + string.split('').join(filler) + filler
752
+ function surroundInOut(string, filler) {
753
+ return filler + string.split('').join(filler) + filler
754
+ }
675
755
 
676
- const enumify = string => slugify(string).replace(/-/g, '_').toUpperCase()
756
+ function enumify(string) {
757
+ return slugify(string).replace(/-/g, '_').toUpperCase()
758
+ }
677
759
 
678
- const getFuzzyMatchScore = (string, input) => {
760
+ function getFuzzyMatchScore(string, input) {
679
761
  if (input.length === 0) {
680
762
  return 0
681
763
  }
682
764
  const lowercaseString = string.toLowerCase()
683
765
  const lowercaseInput = input.toLowerCase()
766
+ if (string === input) {
767
+ return 10000
768
+ }
684
769
  if (lowercaseString.startsWith(lowercaseInput)) {
685
770
  return 10000 - string.length
686
771
  }
@@ -691,12 +776,15 @@ const getFuzzyMatchScore = (string, input) => {
691
776
  return regex.test(lowercaseString) ? 1000 - string.length : 0
692
777
  }
693
778
 
694
- const sortByFuzzyScore = (strings, input) =>
695
- strings
779
+ function sortByFuzzyScore(strings, input) {
780
+ return strings
696
781
  .filter(a => getFuzzyMatchScore(a, input))
697
782
  .sort((a, b) => getFuzzyMatchScore(b, input) - getFuzzyMatchScore(a, input))
783
+ }
698
784
 
699
- const escapeHtml = string => string.replace(/\</g, '&lt;').replace('/>/g', '&gt;')
785
+ function escapeHtml(string) {
786
+ return string.replace(/\</g, '&lt;').replace('/>/g', '&gt;')
787
+ }
700
788
 
701
789
  const htmlEntityMap = {
702
790
  '&quot;': '"',
@@ -704,82 +792,104 @@ const htmlEntityMap = {
704
792
  '&lt': '<'
705
793
  }
706
794
 
707
- const decodeHtmlEntities = string => {
708
- let buffer = string.replace(/&#(\d+);/g, (match, dec) => String.fromCharCode(dec))
795
+ function decodeHtmlEntities(string) {
796
+ let buffer = string.replace(/&#(\d+);/g, (_, dec) => String.fromCharCode(dec))
709
797
  for (const key of Object.keys(htmlEntityMap)) {
710
798
  buffer = buffer.split(key).join(htmlEntityMap[key])
711
799
  }
712
800
  return buffer
713
801
  }
714
802
 
715
- const before = (string, searchString) => {
803
+ function before(string, searchString) {
716
804
  const position = string.indexOf(searchString)
717
805
  return position === -1 ? string : string.slice(0, position)
718
806
  }
719
807
 
720
- const after = (string, searchString) => {
808
+ function after(string, searchString) {
721
809
  const position = string.indexOf(searchString)
722
810
  return position === -1 ? string : string.slice(position + searchString.length)
723
811
  }
724
812
 
725
- const beforeLast = (string, searchString) => {
813
+ function beforeLast(string, searchString) {
726
814
  const position = string.lastIndexOf(searchString)
727
815
  return position === -1 ? string : string.slice(0, position)
728
816
  }
729
817
 
730
- const afterLast = (string, searchString) => {
818
+ function afterLast(string, searchString) {
731
819
  const position = string.lastIndexOf(searchString)
732
820
  return position === -1 ? string : string.slice(position + searchString.length)
733
821
  }
734
822
 
735
- const between = (string, start, end) => before(after(string, start), end)
823
+ function between(string, start, end) {
824
+ return before(after(string, start), end)
825
+ }
736
826
 
737
- const betweenWide = (string, start, end) => beforeLast(after(string, start), end)
827
+ function betweenWide(string, start, end) {
828
+ return after(beforeLast(string, end), start)
829
+ }
738
830
 
739
- const betweenNarrow = (string, start, end) => before(afterLast(string, start), end)
831
+ function betweenNarrow(string, start, end) {
832
+ return before(after(string, start), end)
833
+ }
740
834
 
741
- const splitOnce = (string, separator) =>
742
- string.includes(separator) ? [before(string, separator), after(string, separator)] : [string, '']
835
+ function splitOnce(string, separator) {
836
+ return string.includes(separator) ? [before(string, separator), after(string, separator)] : [string, '']
837
+ }
743
838
 
744
- const getExtension = string => {
745
- const name = last(string.split(/\\|\//g))
746
- const lastIndex = name.lastIndexOf('.')
839
+ function getExtension(path) {
840
+ const name = last(path.split(/\\|\//g))
841
+ const lastIndex = name.lastIndexOf('.', name.length - 1)
747
842
  return lastIndex <= 0 ? '' : name.slice(lastIndex + 1)
748
843
  }
749
844
 
750
- const getBasename = string => {
751
- const name = last(string.split(/\\|\//g))
752
- const index = name.indexOf('.', 1)
753
- return index <= 0 ? string : name.slice(0, index)
845
+ function getBasename(path) {
846
+ const name = last(path.split(/\\|\//g))
847
+ const lastIndex = name.lastIndexOf('.', name.length - 1)
848
+ return lastIndex <= 0 ? name : name.slice(0, lastIndex)
754
849
  }
755
850
 
756
- const normalizeFilename = string => `${getBasename(string)}.${getExtension(string)}`
851
+ function normalizeFilename(path) {
852
+ const basename = getBasename(path)
853
+ const extension = getExtension(path)
854
+ return extension ? `${basename}.${extension}` : basename
855
+ }
757
856
 
758
- const parseFilename = string => {
857
+ function parseFilename(string) {
759
858
  const basename = getBasename(string)
760
859
  const extension = getExtension(string)
761
860
  return {
762
861
  basename,
763
862
  extension,
764
- filename: `${basename}.${extension}`
863
+ filename: extension ? `${basename}.${extension}` : basename
765
864
  }
766
865
  }
767
866
 
768
- const randomize = string => string.replace(/\{(.+?)\}/g, (_, group) => pick(group.split('|')))
867
+ function randomize(string) {
868
+ return string.replace(/\{(.+?)\}/g, (_, group) => pick(group.split('|')))
869
+ }
769
870
 
770
- const shrinkTrim = string => string.replace(/\s+/g, ' ').replace(/\s$|^\s/g, '')
871
+ function shrinkTrim(string) {
872
+ return string.replace(/\s+/g, ' ').replace(/\s$|^\s/g, '')
873
+ }
771
874
 
772
- const capitalize = string => string.charAt(0).toUpperCase() + string.slice(1)
875
+ function capitalize(string) {
876
+ return string.charAt(0).toUpperCase() + string.slice(1)
877
+ }
773
878
 
774
- const decapitalize = string => string.charAt(0).toLowerCase() + string.slice(1)
879
+ function decapitalize(string) {
880
+ return string.charAt(0).toLowerCase() + string.slice(1)
881
+ }
775
882
 
776
- const csvEscape = string => (string.match(/"|,/) ? `"${string.replace(/"/g, '""')}"` : string)
883
+ function csvEscape(string) {
884
+ return string.match(/"|,/) ? `"${string.replace(/"/g, '""')}"` : string
885
+ }
777
886
 
778
- const parseCsv = (string, delimiter = ',', quote = '"') => {
887
+ function parseCsv(string, delimiter = ',', quote = '"') {
779
888
  const items = []
780
889
  let buffer = ''
781
890
  let escaped = false
782
- for (const character of string) {
891
+ const characters = string.split('')
892
+ for (const character of characters) {
783
893
  if (character === delimiter && !escaped) {
784
894
  items.push(buffer)
785
895
  buffer = ''
@@ -793,23 +903,13 @@ const parseCsv = (string, delimiter = ',', quote = '"') => {
793
903
  return items
794
904
  }
795
905
 
796
- const humanizeProgress = state =>
797
- `[${Math.floor(state.progress * 100)}%] ${humanizeTime(state.deltaMs)} out of ${humanizeTime(
906
+ function humanizeProgress(state) {
907
+ return `[${Math.floor(state.progress * 100)}%] ${humanizeTime(state.deltaMs)} out of ${humanizeTime(
798
908
  state.totalTimeMs
799
909
  )} (${humanizeTime(state.remainingTimeMs)} left) [${Math.round(state.baseTimeMs)} ms each]`
800
-
801
- const expectThrow = async (name, message, callable) => {
802
- try {
803
- await callable()
804
- throw new Error('Expected ' + name + ' ' + message + ' but everything went fine')
805
- } catch (error) {
806
- if (error.name !== name || error.message !== message) {
807
- throw new Error('Expected ' + name + ' ' + message + ' but caught ' + error.name + ' ' + error.message)
808
- }
809
- }
810
910
  }
811
911
 
812
- const waitFor = async (predicate, waitLength, maxWaits) => {
912
+ async function waitFor(predicate, waitLength, maxWaits) {
813
913
  for (let i = 0; i < maxWaits; i++) {
814
914
  try {
815
915
  if (await predicate()) {
@@ -823,7 +923,7 @@ const waitFor = async (predicate, waitLength, maxWaits) => {
823
923
  return false
824
924
  }
825
925
 
826
- const mkdirp = async path => {
926
+ async function mkdirp(path) {
827
927
  const segments = path.split('/')
828
928
  let buffer = ''
829
929
  for (const segment of segments) {
@@ -840,7 +940,7 @@ const mkdirp = async path => {
840
940
  }
841
941
  }
842
942
 
843
- const filterAndRemove = (array, predicate) => {
943
+ function filterAndRemove(array, predicate) {
844
944
  const results = []
845
945
  for (let i = array.length - 1; i >= 0; i--) {
846
946
  if (predicate(array[i])) {
@@ -850,8 +950,8 @@ const filterAndRemove = (array, predicate) => {
850
950
  return results
851
951
  }
852
952
 
853
- const execAsync = async (command, resolveWithErrors, inherit, options) =>
854
- new Promise((resolve, reject) => {
953
+ async function execAsync(command, resolveWithErrors, inherit, options) {
954
+ return new Promise((resolve, reject) => {
855
955
  const childProcess = ChildProcess.exec(command, options, (error, stdout, stderr) => {
856
956
  if (error) {
857
957
  if (resolveWithErrors) {
@@ -864,16 +964,17 @@ const execAsync = async (command, resolveWithErrors, inherit, options) =>
864
964
  }
865
965
  })
866
966
  if (inherit) {
867
- childProcess.stdout.pipe(process.stdout)
868
- childProcess.stderr.pipe(process.stderr)
967
+ childProcess.stdout && childProcess.stdout.pipe(process.stdout)
968
+ childProcess.stderr && childProcess.stderr.pipe(process.stderr)
869
969
  }
870
970
  })
971
+ }
871
972
 
872
- const runProcess = async (command, args, onStdout, onStderr) =>
873
- new Promise((resolve, reject) => {
874
- const subprocess = ChildProcess.spawn(command, args)
875
- subprocess.stdout.on('data', onStdout)
876
- subprocess.stderr.on('data', onStderr)
973
+ async function runProcess(command, args, options, onStdout, onStderr) {
974
+ return new Promise((resolve, reject) => {
975
+ const subprocess = ChildProcess.spawn(command, args, options)
976
+ subprocess?.stdout?.on('data', onStdout)
977
+ subprocess?.stderr?.on('data', onStderr)
877
978
  subprocess.on('close', code => {
878
979
  if (code === 0) {
879
980
  resolve(code)
@@ -882,23 +983,34 @@ const runProcess = async (command, args, onStdout, onStderr) =>
882
983
  }
883
984
  })
884
985
  })
986
+ }
885
987
 
886
- const cloneWithJson = a => JSON.parse(JSON.stringify(a))
988
+ function cloneWithJson(a) {
989
+ return JSON.parse(JSON.stringify(a))
990
+ }
887
991
 
888
- const unixTimestamp = optionalTimestamp => Math.ceil((optionalTimestamp || Date.now()) / 1000)
992
+ function unixTimestamp(optionalTimestamp) {
993
+ return Math.ceil((optionalTimestamp || Date.now()) / 1000)
994
+ }
889
995
 
890
- const isoDate = optionalDate => (optionalDate || new Date()).toISOString().slice(0, 10)
996
+ function isoDate(optionalDate) {
997
+ return (optionalDate || new Date()).toISOString().slice(0, 10)
998
+ }
891
999
 
892
- const dateTimeSlug = optionalDate => (optionalDate || new Date()).toISOString().slice(0, 19).replace(/T|:/g, '-')
1000
+ function dateTimeSlug(optionalDate) {
1001
+ return (optionalDate || new Date()).toISOString().slice(0, 19).replace(/T|:/g, '-')
1002
+ }
893
1003
 
894
- const fromUtcString = string => {
1004
+ function fromUtcString(string) {
895
1005
  const date = new Date(string)
896
1006
  return new Date(date.getTime() - date.getTimezoneOffset() * 60000)
897
1007
  }
898
1008
 
899
- const createTimeDigits = value => String(Math.floor(value)).padStart(2, '0')
1009
+ function createTimeDigits(value) {
1010
+ return String(Math.floor(value)).padStart(2, '0')
1011
+ }
900
1012
 
901
- const humanizeTime = millis => {
1013
+ function humanizeTime(millis) {
902
1014
  let seconds = Math.floor(millis / 1000)
903
1015
  if (seconds < 60) {
904
1016
  return `${seconds}s`
@@ -913,8 +1025,10 @@ const humanizeTime = millis => {
913
1025
  return `${createTimeDigits(hours)}:${createTimeDigits(minutes)}:${createTimeDigits(seconds)}`
914
1026
  }
915
1027
 
916
- const getAgo = date => {
917
- const now = Date.now()
1028
+ function getAgo(date, now) {
1029
+ if (!now) {
1030
+ now = Date.now()
1031
+ }
918
1032
  const then = date.getTime()
919
1033
  let delta = (now - then) / 1000
920
1034
  if (delta < 10) {
@@ -935,7 +1049,7 @@ const getAgo = date => {
935
1049
  return delta.toFixed(0) + ' days ago'
936
1050
  }
937
1051
 
938
- const debounce = (longWrapper, millis) => {
1052
+ function debounce(longWrapper, millis) {
939
1053
  if (Date.now() > longWrapper.value) {
940
1054
  longWrapper.value = Date.now() + millis
941
1055
  return true
@@ -950,15 +1064,18 @@ const timeUnits = {
950
1064
  d: 86400000
951
1065
  }
952
1066
 
953
- const timeSince = (unit, a, optionalB) => {
1067
+ function timeSince(unit, a, optionalB) {
954
1068
  a = isDate(a) ? a.getTime() : a
955
1069
  optionalB = optionalB ? (isDate(optionalB) ? optionalB.getTime() : optionalB) : Date.now()
956
1070
  return (optionalB - a) / timeUnits[unit]
957
1071
  }
958
1072
 
959
- const getProgress = (startedAt, current, total) => {
1073
+ function getProgress(startedAt, current, total, now) {
1074
+ if (!now) {
1075
+ now = Date.now()
1076
+ }
960
1077
  const progress = current / total
961
- const deltaMs = Date.now() - startedAt
1078
+ const deltaMs = now - startedAt
962
1079
  const baseTimeMs = deltaMs / current
963
1080
  const totalTimeMs = baseTimeMs * total
964
1081
  const remainingTimeMs = totalTimeMs - deltaMs
@@ -981,23 +1098,43 @@ const dayNumberIndex = {
981
1098
  6: 'saturday'
982
1099
  }
983
1100
 
984
- const mapDayNumber = zeroBasedIndex => ({
985
- zeroBasedIndex,
986
- day: dayNumberIndex[zeroBasedIndex]
987
- })
1101
+ function mapDayNumber(zeroBasedIndex) {
1102
+ return {
1103
+ zeroBasedIndex,
1104
+ day: dayNumberIndex[zeroBasedIndex]
1105
+ }
1106
+ }
1107
+
1108
+ function getDayInfoFromDate(date) {
1109
+ return mapDayNumber(date.getDay())
1110
+ }
1111
+
1112
+ function getDayInfoFromDateTimeString(dateTimeString) {
1113
+ return getDayInfoFromDate(new Date(dateTimeString))
1114
+ }
1115
+
1116
+ function seconds(value) {
1117
+ return value * 1000
1118
+ }
988
1119
 
989
- const getDayInfoFromDate = date => mapDayNumber(date.getDay())
1120
+ function minutes(value) {
1121
+ return value * 60000
1122
+ }
990
1123
 
991
- const getDayInfoFromDateTimeString = dateTimeString => getDayInfoFromDate(new Date(dateTimeString))
1124
+ function hours(value) {
1125
+ return value * 3600000
1126
+ }
992
1127
 
993
- const getPreLine = string => string.replace(/ +/g, ' ').replace(/^ /gm, '')
1128
+ function getPreLine(string) {
1129
+ return string.replace(/ +/g, ' ').replace(/^ /gm, '')
1130
+ }
994
1131
 
995
- const containsWord = (string, word) => {
1132
+ function containsWord(string, word) {
996
1133
  const slug = slugify(string)
997
1134
  return slug.startsWith(word + '-') || slug.endsWith('-' + word) || slug.includes('-' + word + '-') || slug === word
998
1135
  }
999
1136
 
1000
- const containsWords = (string, words) => {
1137
+ function containsWords(string, words) {
1001
1138
  for (const word of words) {
1002
1139
  if (containsWord(string, word)) {
1003
1140
  return true
@@ -1007,8 +1144,7 @@ const containsWords = (string, words) => {
1007
1144
  }
1008
1145
 
1009
1146
  const tinyCache = {}
1010
-
1011
- const getCached = async (key, ttlMillis, handler) => {
1147
+ async function getCached(key, ttlMillis, handler) {
1012
1148
  const now = Date.now()
1013
1149
  const existing = tinyCache[key]
1014
1150
  if (existing && existing.validUntil > now) {
@@ -1023,7 +1159,7 @@ const getCached = async (key, ttlMillis, handler) => {
1023
1159
  return value
1024
1160
  }
1025
1161
 
1026
- const joinUrl = (...parts) => {
1162
+ function joinUrl(...parts) {
1027
1163
  let url = parts[0][parts[0].length - 1] === '/' ? parts[0].slice(0, -1) : parts[0]
1028
1164
  for (let i = 1; i < parts.length; i++) {
1029
1165
  const part = parts[i]
@@ -1037,7 +1173,7 @@ const joinUrl = (...parts) => {
1037
1173
  return url
1038
1174
  }
1039
1175
 
1040
- const sortObject = object => {
1176
+ function sortObject(object) {
1041
1177
  const keys = Object.keys(object)
1042
1178
  const orderedKeys = keys.sort((a, b) => a.localeCompare(b))
1043
1179
  const sorted = {}
@@ -1047,7 +1183,7 @@ const sortObject = object => {
1047
1183
  return sorted
1048
1184
  }
1049
1185
 
1050
- const sortArray = array => {
1186
+ function sortArray(array) {
1051
1187
  const result = []
1052
1188
  array
1053
1189
  .sort((a, b) => JSON.stringify(sortAny(a)).localeCompare(JSON.stringify(sortAny(b))))
@@ -1055,7 +1191,7 @@ const sortArray = array => {
1055
1191
  return result
1056
1192
  }
1057
1193
 
1058
- const sortAny = any => {
1194
+ function sortAny(any) {
1059
1195
  if (Array.isArray(any)) {
1060
1196
  return sortArray(any)
1061
1197
  }
@@ -1065,9 +1201,11 @@ const sortAny = any => {
1065
1201
  return any
1066
1202
  }
1067
1203
 
1068
- const deepEquals = (a, b) => JSON.stringify(sortAny(a)) === JSON.stringify(sortAny(b))
1204
+ function deepEquals(a, b) {
1205
+ return JSON.stringify(sortAny(a)) === JSON.stringify(sortAny(b))
1206
+ }
1069
1207
 
1070
- const safeParse = stringable => {
1208
+ function safeParse(stringable) {
1071
1209
  try {
1072
1210
  return JSON.parse(stringable)
1073
1211
  } catch (error) {
@@ -1095,23 +1233,25 @@ const longNumberUnits = [
1095
1233
  'decillion'
1096
1234
  ]
1097
1235
  const shortNumberUnits = ['K', 'M', 'B', 't', 'q', 'Q', 's', 'S', 'o', 'n', 'd']
1098
-
1099
- const prettifyNumber = (number, precision = 1, long = false) => {
1100
- const table = long ? longNumberUnits : shortNumberUnits
1236
+ function formatNumber(number, options) {
1237
+ const longFormat = options?.longForm ?? false
1238
+ const unitString = options?.unit ? ` ${options.unit}` : ''
1239
+ const table = longFormat ? longNumberUnits : shortNumberUnits
1240
+ const precision = options?.precision ?? 1
1101
1241
  if (number < thresholds[0]) {
1102
- return number
1242
+ return `${number}${unitString}`
1103
1243
  }
1104
1244
  for (let i = 0; i < thresholds.length - 1; i++) {
1105
1245
  if (number < thresholds[i + 1]) {
1106
- return `${(number / thresholds[i]).toFixed(precision)}${long ? ' ' : ''}${table[i]}`
1246
+ return `${(number / thresholds[i]).toFixed(precision)}${longFormat ? ' ' : ''}${table[i]}${unitString}`
1107
1247
  }
1108
1248
  }
1109
- return `${(number / thresholds[thresholds.length - 1]).toFixed(precision)}${long ? ' ' : ''}${
1249
+ return `${(number / thresholds[thresholds.length - 1]).toFixed(precision)}${longFormat ? ' ' : ''}${
1110
1250
  table[thresholds.length - 1]
1111
- }`
1251
+ }${unitString}`
1112
1252
  }
1113
1253
 
1114
- const parseIntOrThrow = numberOrString => {
1254
+ function parseIntOrThrow(numberOrString) {
1115
1255
  if (typeof numberOrString === 'number') {
1116
1256
  if (isNaN(numberOrString)) {
1117
1257
  throw Error('parseIntOrThrow got NaN for input')
@@ -1131,19 +1271,21 @@ const parseIntOrThrow = numberOrString => {
1131
1271
  throw Error('parseIntOrThrow got unsupported input type: ' + typeof numberOrString)
1132
1272
  }
1133
1273
 
1134
- const clamp = (value, lower, upper) => (value < lower ? lower : value > upper ? upper : value)
1274
+ function clamp(value, lower, upper) {
1275
+ return value < lower ? lower : value > upper ? upper : value
1276
+ }
1135
1277
 
1136
- const increment = (value, change, maximum) => {
1278
+ function increment(value, change, maximum) {
1137
1279
  const result = value + change
1138
1280
  return result > maximum ? maximum : result
1139
1281
  }
1140
1282
 
1141
- const decrement = (value, change, minimum) => {
1283
+ function decrement(value, change, minimum) {
1142
1284
  const result = value - change
1143
1285
  return result < minimum ? minimum : result
1144
1286
  }
1145
1287
 
1146
- const getHeapMegabytes = () => {
1288
+ function getHeapMegabytes() {
1147
1289
  const memory = process.memoryUsage()
1148
1290
  return {
1149
1291
  used: (memory.heapUsed / 1024 / 1024).toFixed(3),
@@ -1152,18 +1294,18 @@ const getHeapMegabytes = () => {
1152
1294
  }
1153
1295
  }
1154
1296
 
1155
- const runOn = (object, callable) => {
1297
+ function runOn(object, callable) {
1156
1298
  callable(object)
1157
1299
  return object
1158
1300
  }
1159
1301
 
1160
- const ifPresent = (object, callable) => {
1302
+ function ifPresent(object, callable) {
1161
1303
  if (object) {
1162
1304
  callable(object)
1163
1305
  }
1164
1306
  }
1165
1307
 
1166
- const mergeArrays = (target, source) => {
1308
+ function mergeArrays(target, source) {
1167
1309
  const keys = Object.keys(source)
1168
1310
  for (const key of keys) {
1169
1311
  if (Array.isArray(source[key]) && Array.isArray(target[key])) {
@@ -1172,28 +1314,30 @@ const mergeArrays = (target, source) => {
1172
1314
  }
1173
1315
  }
1174
1316
 
1175
- const empty = array => {
1317
+ function empty(array) {
1176
1318
  array.splice(0, array.length)
1177
1319
  return array
1178
1320
  }
1179
1321
 
1180
- const removeEmptyArrays = object => {
1322
+ function removeEmptyArrays(object) {
1181
1323
  for (const key of Object.keys(object)) {
1182
- if (Array.isArray(object[key]) && object[key].length === 0) {
1324
+ if (isEmptyArray(object[key])) {
1183
1325
  delete object[key]
1184
1326
  }
1185
1327
  }
1328
+ return object
1186
1329
  }
1187
1330
 
1188
- const removeEmptyValues = object => {
1331
+ function removeEmptyValues(object) {
1189
1332
  for (const entry of Object.entries(object)) {
1190
1333
  if (isUndefined(entry[1]) || entry[1] === null || (isString(entry[1]) && isBlank(entry[1]))) {
1191
1334
  delete object[entry[0]]
1192
1335
  }
1193
1336
  }
1337
+ return object
1194
1338
  }
1195
1339
 
1196
- const mapObject = (object, mapper) => {
1340
+ function mapObject(object, mapper) {
1197
1341
  const output = {}
1198
1342
  for (const entry of Object.entries(object)) {
1199
1343
  output[entry[0]] = mapper(entry[1])
@@ -1201,7 +1345,7 @@ const mapObject = (object, mapper) => {
1201
1345
  return output
1202
1346
  }
1203
1347
 
1204
- const rethrow = async (asyncFn, throwable) => {
1348
+ async function rethrow(asyncFn, throwable) {
1205
1349
  try {
1206
1350
  const returnValue = await asyncFn()
1207
1351
  return returnValue
@@ -1210,13 +1354,13 @@ const rethrow = async (asyncFn, throwable) => {
1210
1354
  }
1211
1355
  }
1212
1356
 
1213
- const setSomeOnObject = (object, key, value) => {
1357
+ function setSomeOnObject(object, key, value) {
1214
1358
  if (typeof value !== 'undefined' && value !== null) {
1215
1359
  object[key] = value
1216
1360
  }
1217
1361
  }
1218
1362
 
1219
- const flip = object => {
1363
+ function flip(object) {
1220
1364
  const result = {}
1221
1365
  for (const [key, value] of Object.entries(object)) {
1222
1366
  result[value] = key
@@ -1224,7 +1368,7 @@ const flip = object => {
1224
1368
  return result
1225
1369
  }
1226
1370
 
1227
- const crossJoin = object => {
1371
+ function getAllPermutations(object) {
1228
1372
  const keys = Object.keys(object)
1229
1373
  const lengths = keys.map(key => object[key].length)
1230
1374
  const count = lengths.reduce((previous, current) => (previous *= current))
@@ -1247,14 +1391,15 @@ const crossJoin = object => {
1247
1391
  return results
1248
1392
  }
1249
1393
 
1250
- const countTruthyValues = object => {
1394
+ function countTruthyValues(object) {
1251
1395
  return Object.values(object).filter(x => x).length
1252
1396
  }
1253
1397
 
1254
- const getFlatNotation = (prefix, key, bracket) =>
1255
- prefix + (bracket ? '[' + key + ']' : (prefix.length ? '.' : '') + key)
1398
+ function getFlatNotation(prefix, key, bracket) {
1399
+ return prefix + (bracket ? '[' + key + ']' : (prefix.length ? '.' : '') + key)
1400
+ }
1256
1401
 
1257
- const flattenInner = (target, object, prefix, bracket, arrays) => {
1402
+ function flattenInner(target, object, prefix, bracket, arrays) {
1258
1403
  if (!isObject(object)) {
1259
1404
  return object
1260
1405
  }
@@ -1277,11 +1422,11 @@ const flattenInner = (target, object, prefix, bracket, arrays) => {
1277
1422
  return target
1278
1423
  }
1279
1424
 
1280
- const flatten = (object, arrays, prefix) => {
1425
+ function flatten(object, arrays = false, prefix) {
1281
1426
  return flattenInner({}, object, prefix || '', false, arrays)
1282
1427
  }
1283
1428
 
1284
- const unflatten = object => {
1429
+ function unflatten(object) {
1285
1430
  if (!isObject(object)) {
1286
1431
  return object
1287
1432
  }
@@ -1300,9 +1445,11 @@ const unflatten = object => {
1300
1445
  return target
1301
1446
  }
1302
1447
 
1303
- const match = (value, options, fallback) => (options[value] ? options[value] : fallback)
1448
+ function match(value, options, fallback) {
1449
+ return options[value] ? options[value] : fallback
1450
+ }
1304
1451
 
1305
- const indexArray = (array, keyFn, useArrays) => {
1452
+ function indexArray(array, keyFn, useArrays) {
1306
1453
  const target = {}
1307
1454
  for (const element of array) {
1308
1455
  const key = keyFn(element)
@@ -1318,7 +1465,7 @@ const indexArray = (array, keyFn, useArrays) => {
1318
1465
  return target
1319
1466
  }
1320
1467
 
1321
- const splitBySize = (array, size) => {
1468
+ function splitBySize(array, size) {
1322
1469
  const batches = []
1323
1470
  for (let i = 0; i < array.length; i += size) {
1324
1471
  batches.push(array.slice(i, i + size))
@@ -1326,7 +1473,7 @@ const splitBySize = (array, size) => {
1326
1473
  return batches
1327
1474
  }
1328
1475
 
1329
- const splitByCount = (array, count) => {
1476
+ function splitByCount(array, count) {
1330
1477
  const size = Math.ceil(array.length / count)
1331
1478
  const batches = []
1332
1479
  for (let i = 0; i < array.length; i += size) {
@@ -1335,7 +1482,7 @@ const splitByCount = (array, count) => {
1335
1482
  return batches
1336
1483
  }
1337
1484
 
1338
- const tokenizeByLength = (string, length) => {
1485
+ function tokenizeByLength(string, length) {
1339
1486
  const parts = []
1340
1487
  const count = Math.ceil(string.length / length)
1341
1488
  for (let i = 0; i < count; i++) {
@@ -1344,14 +1491,16 @@ const tokenizeByLength = (string, length) => {
1344
1491
  return parts
1345
1492
  }
1346
1493
 
1347
- const tokenizeByCount = (string, count) => {
1494
+ function tokenizeByCount(string, count) {
1348
1495
  const length = Math.ceil(string.length / count)
1349
1496
  return tokenizeByLength(string, length)
1350
1497
  }
1351
1498
 
1352
- const makeUnique = (array, fn) => Object.values(indexArray(array, fn, false))
1499
+ function makeUnique(array, fn) {
1500
+ return Object.values(indexArray(array, fn, false))
1501
+ }
1353
1502
 
1354
- const countUnique = (array, mapper, plain, sort, reverse) => {
1503
+ function countUnique(array, mapper, plain, sort, reverse) {
1355
1504
  const mapped = mapper ? array.map(mapper) : array
1356
1505
  const object = {}
1357
1506
  for (const item of mapped) {
@@ -1364,9 +1513,11 @@ const countUnique = (array, mapper, plain, sort, reverse) => {
1364
1513
  return sorted
1365
1514
  }
1366
1515
 
1367
- const sortObjectValues = (object, compareFn) => Object.fromEntries(Object.entries(object).sort(compareFn))
1516
+ function sortObjectValues(object, compareFn) {
1517
+ return Object.fromEntries(Object.entries(object).sort(compareFn))
1518
+ }
1368
1519
 
1369
- const transformToArray = objectOfArrays => {
1520
+ function transformToArray(objectOfArrays) {
1370
1521
  const array = []
1371
1522
  const keys = Object.keys(objectOfArrays)
1372
1523
  const length = objectOfArrays[keys[0]].length
@@ -1380,21 +1531,23 @@ const transformToArray = objectOfArrays => {
1380
1531
  return array
1381
1532
  }
1382
1533
 
1383
- const incrementMulti = (objects, key, step = 1) => {
1534
+ function incrementMulti(objects, key, step = 1) {
1384
1535
  for (const object of objects) {
1385
1536
  object[key] += step
1386
1537
  }
1387
1538
  }
1388
1539
 
1389
- const setMulti = (objects, key, value) => {
1540
+ function setMulti(objects, key, value) {
1390
1541
  for (const object of objects) {
1391
1542
  object[key] = value
1392
1543
  }
1393
1544
  }
1394
1545
 
1395
- const createFastIndex = () => ({ index: {}, keys: [] })
1546
+ function createFastIndex() {
1547
+ return { index: {}, keys: [] }
1548
+ }
1396
1549
 
1397
- const pushToFastIndex = (object, key, item, limit = 100) => {
1550
+ function pushToFastIndex(object, key, item, limit = 100) {
1398
1551
  if (object.index[key]) {
1399
1552
  const index = object.keys.indexOf(key)
1400
1553
  object.keys.splice(index, 1)
@@ -1403,15 +1556,15 @@ const pushToFastIndex = (object, key, item, limit = 100) => {
1403
1556
  object.keys.push(key)
1404
1557
  if (object.keys.length > limit) {
1405
1558
  const oldKey = object.keys.shift()
1406
- delete object.index[oldKey]
1559
+ oldKey && delete object.index[oldKey]
1407
1560
  }
1408
1561
  }
1409
1562
 
1410
- const pushToFastIndexWithExpiracy = (object, key, item, expiration, limit = 100) => {
1563
+ function pushToFastIndexWithExpiracy(object, key, item, expiration, limit = 100) {
1411
1564
  pushToFastIndex(object, key, { validUntil: Date.now() + expiration, data: item }, limit)
1412
1565
  }
1413
1566
 
1414
- const getFromFastIndexWithExpiracy = (object, key) => {
1567
+ function getFromFastIndexWithExpiracy(object, key) {
1415
1568
  const item = object.index[key]
1416
1569
  if (item && item.validUntil > Date.now()) {
1417
1570
  return item.data
@@ -1419,216 +1572,299 @@ const getFromFastIndexWithExpiracy = (object, key) => {
1419
1572
  return null
1420
1573
  }
1421
1574
 
1422
- module.exports = {
1423
- Random: {
1424
- inclusiveInt: randomIntInclusive,
1425
- between: randomBetween,
1426
- chance,
1427
- signed: signedRandom
1428
- },
1429
- Arrays: {
1430
- countUnique,
1431
- makeUnique,
1432
- splitBySize,
1433
- splitByCount,
1434
- index: indexArray,
1435
- onlyOrThrow,
1436
- onlyOrNull,
1437
- firstOrNull,
1438
- shuffle,
1439
- takeRandomly,
1440
- initialize: initializeArray,
1441
- mapWithIndex,
1442
- glue,
1443
- pluck,
1444
- pick,
1445
- last,
1446
- pickWeighted,
1447
- sortWeighted,
1448
- pushAll,
1449
- unshiftAll,
1450
- filterAndRemove,
1451
- merge: mergeArrays,
1452
- empty,
1453
- pushToBucket,
1454
- unshiftAndLimit,
1455
- atRolling
1456
- },
1457
- System: {
1458
- sleepMillis,
1459
- forever,
1460
- scheduleMany,
1461
- waitFor,
1462
- execAsync,
1463
- getHeapMegabytes,
1464
- expandError,
1465
- runProcess
1466
- },
1467
- Numbers: {
1468
- sum,
1469
- average,
1470
- clamp,
1471
- range,
1472
- interpolate,
1473
- createSequence,
1474
- increment,
1475
- decrement,
1476
- prettify: prettifyNumber,
1477
- parseIntOrThrow
1478
- },
1479
- Promises: {
1480
- raceFulfilled,
1481
- invert: invertPromise,
1482
- runInParallelBatches
1483
- },
1484
- Dates: {
1485
- getAgo,
1486
- isoDate,
1487
- debounce,
1488
- timeSince,
1489
- dateTimeSlug,
1490
- unixTimestamp,
1491
- fromUtcString,
1492
- getProgress,
1493
- humanizeTime,
1494
- humanizeProgress,
1495
- createTimeDigits,
1496
- mapDayNumber,
1497
- getDayInfoFromDate,
1498
- getDayInfoFromDateTimeString
1499
- },
1500
- Objects: {
1501
- safeParse,
1502
- deleteDeep,
1503
- getDeep,
1504
- getDeepOrElse,
1505
- setDeep,
1506
- ensureDeep,
1507
- replaceDeep,
1508
- getFirstDeep,
1509
- mergeDeep,
1510
- mapWithGetters,
1511
- mapAllWithGetters,
1512
- mapAllAsync,
1513
- cloneWithJson,
1514
- sortObject,
1515
- sortArray,
1516
- sortAny,
1517
- deepEquals,
1518
- runOn,
1519
- ifPresent,
1520
- zip,
1521
- zipSum,
1522
- removeEmptyArrays,
1523
- removeEmptyValues,
1524
- flatten,
1525
- unflatten,
1526
- match,
1527
- sort: sortObjectValues,
1528
- map: mapObject,
1529
- rethrow,
1530
- setSomeOnObject,
1531
- flip,
1532
- crossJoin,
1533
- countTruthyValues,
1534
- transformToArray,
1535
- setMulti,
1536
- incrementMulti,
1537
- createFastIndex,
1538
- pushToFastIndex,
1539
- pushToFastIndexWithExpiracy,
1540
- getFromFastIndexWithExpiracy
1541
- },
1542
- Pagination: {
1543
- asPageNumber,
1544
- pageify
1545
- },
1546
- Files: {
1547
- existsAsync,
1548
- writeJsonAsync,
1549
- readdirDeepAsync,
1550
- readUtf8FileAsync,
1551
- readJsonAsync,
1552
- readLinesAsync,
1553
- readMatchingLines,
1554
- readNonEmptyLines,
1555
- readCsv,
1556
- walkTreeAsync,
1557
- getFileSize,
1558
- asMegabytes,
1559
- getDirectorySize,
1560
- convertBytes,
1561
- getChecksum: getChecksumOfFile,
1562
- mkdirp
1563
- },
1564
- Types: {
1565
- isFunction,
1566
- isObject,
1567
- isStrictlyObject,
1568
- isUndefined,
1569
- isString,
1570
- isNumber,
1571
- isDate,
1572
- isBlank,
1573
- asString,
1574
- asNumber,
1575
- asDate,
1576
- asNullableString
1577
- },
1578
- Strings: {
1579
- tokenizeByCount,
1580
- tokenizeByLength,
1581
- randomAlphanumeric: randomAlphanumericString,
1582
- randomHex: randomHexString,
1583
- includesAny,
1584
- slugify,
1585
- enumify,
1586
- escapeHtml,
1587
- decodeHtmlEntities,
1588
- after,
1589
- afterLast,
1590
- before,
1591
- beforeLast,
1592
- between,
1593
- betweenWide,
1594
- betweenNarrow,
1595
- getPreLine,
1596
- containsWord,
1597
- containsWords,
1598
- joinUrl,
1599
- getFuzzyMatchScore,
1600
- sortByFuzzyScore,
1601
- getChecksum,
1602
- splitOnce,
1603
- randomize,
1604
- shrinkTrim,
1605
- capitalize,
1606
- decapitalize,
1607
- csvEscape,
1608
- parseCsv,
1609
- surroundInOut,
1610
- getExtension,
1611
- getBasename,
1612
- normalizeFilename,
1613
- parseFilename,
1614
- camelToTitle,
1615
- slugToTitle,
1616
- slugToCamel,
1617
- joinHumanly
1618
- },
1619
- Assertions: {
1620
- asEqual,
1621
- asTrue,
1622
- asTruthy,
1623
- asFalse,
1624
- asFalsy,
1625
- asEither
1626
- },
1627
- Cache: {
1628
- get: getCached
1629
- },
1630
- Logger: {
1631
- create: createLogger,
1632
- enableFileLogging
1575
+ function makeAsyncQueue(concurrency = 1) {
1576
+ const queue = []
1577
+ const listeners = []
1578
+ let running = 0
1579
+ async function runOneTask() {
1580
+ if (queue.length > 0 && running < concurrency) {
1581
+ running++
1582
+ const task = queue.shift()
1583
+ try {
1584
+ task && (await task())
1585
+ } finally {
1586
+ if (--running === 0) {
1587
+ while (listeners.length > 0) {
1588
+ listeners.shift()()
1589
+ }
1590
+ }
1591
+ runOneTask()
1592
+ }
1593
+ }
1594
+ }
1595
+ async function drain() {
1596
+ if (!running) {
1597
+ return Promise.resolve()
1598
+ }
1599
+ return new Promise(resolve => {
1600
+ listeners.push(resolve)
1601
+ })
1602
+ }
1603
+ return {
1604
+ enqueue(fn) {
1605
+ queue.push(fn)
1606
+ runOneTask()
1607
+ },
1608
+ drain
1609
+ }
1610
+ }
1611
+
1612
+ class Maybe {
1613
+ constructor(value) {
1614
+ this.value = value
1615
+ }
1616
+ bind(fn) {
1617
+ if (this.value === null || this.value === undefined) {
1618
+ return new Maybe(null)
1619
+ }
1620
+ if (isPromise(this.value)) {
1621
+ return new Maybe(this.value.then(x => (x !== null && x !== undefined ? fn(x) : null)).catch(() => null))
1622
+ }
1623
+ try {
1624
+ const result = fn(this.value)
1625
+ return new Maybe(result)
1626
+ } catch (error) {
1627
+ return new Maybe(null)
1628
+ }
1633
1629
  }
1630
+ async valueOf() {
1631
+ try {
1632
+ return await this.value
1633
+ } catch {
1634
+ return null
1635
+ }
1636
+ }
1637
+ }
1638
+
1639
+ exports.Maybe = Maybe
1640
+ exports.Random = {
1641
+ inclusiveInt: randomIntInclusive,
1642
+ between: randomBetween,
1643
+ chance,
1644
+ signed: signedRandom
1645
+ }
1646
+
1647
+ exports.Arrays = {
1648
+ countUnique,
1649
+ makeUnique,
1650
+ splitBySize,
1651
+ splitByCount,
1652
+ index: indexArray,
1653
+ onlyOrThrow,
1654
+ onlyOrNull,
1655
+ firstOrNull,
1656
+ shuffle,
1657
+ takeRandomly,
1658
+ pickRandomIndices,
1659
+ initialize: initializeArray,
1660
+ glue,
1661
+ pluck,
1662
+ pick,
1663
+ last,
1664
+ pickWeighted,
1665
+ sortWeighted,
1666
+ pushAll,
1667
+ unshiftAll,
1668
+ filterAndRemove,
1669
+ merge: mergeArrays,
1670
+ empty,
1671
+ pushToBucket,
1672
+ unshiftAndLimit,
1673
+ atRolling
1674
+ }
1675
+
1676
+ exports.System = {
1677
+ sleepMillis,
1678
+ forever,
1679
+ scheduleMany,
1680
+ waitFor,
1681
+ execAsync,
1682
+ getHeapMegabytes,
1683
+ expandError,
1684
+ runProcess
1685
+ }
1686
+
1687
+ exports.Numbers = {
1688
+ sum,
1689
+ average,
1690
+ clamp,
1691
+ range,
1692
+ interpolate,
1693
+ createSequence,
1694
+ increment,
1695
+ decrement,
1696
+ format: formatNumber,
1697
+ parseIntOrThrow
1698
+ }
1699
+
1700
+ exports.Promises = {
1701
+ raceFulfilled,
1702
+ invert: invertPromise,
1703
+ runInParallelBatches,
1704
+ makeAsyncQueue
1705
+ }
1706
+
1707
+ exports.Dates = {
1708
+ getAgo,
1709
+ isoDate,
1710
+ debounce,
1711
+ timeSince,
1712
+ dateTimeSlug,
1713
+ unixTimestamp,
1714
+ fromUtcString,
1715
+ getProgress,
1716
+ humanizeTime,
1717
+ humanizeProgress,
1718
+ createTimeDigits,
1719
+ mapDayNumber,
1720
+ getDayInfoFromDate,
1721
+ getDayInfoFromDateTimeString,
1722
+ seconds,
1723
+ minutes,
1724
+ hours
1725
+ }
1726
+
1727
+ exports.Objects = {
1728
+ safeParse,
1729
+ deleteDeep,
1730
+ getDeep,
1731
+ getDeepOrElse,
1732
+ setDeep,
1733
+ incrementDeep,
1734
+ ensureDeep,
1735
+ replaceDeep,
1736
+ getFirstDeep,
1737
+ mergeDeep,
1738
+ mapAllAsync,
1739
+ cloneWithJson,
1740
+ sortObject,
1741
+ sortArray,
1742
+ sortAny,
1743
+ deepEquals,
1744
+ runOn,
1745
+ ifPresent,
1746
+ zip,
1747
+ zipSum,
1748
+ removeEmptyArrays,
1749
+ removeEmptyValues,
1750
+ flatten,
1751
+ unflatten,
1752
+ match,
1753
+ sort: sortObjectValues,
1754
+ map: mapObject,
1755
+ rethrow,
1756
+ setSomeOnObject,
1757
+ flip,
1758
+ getAllPermutations,
1759
+ countTruthyValues,
1760
+ transformToArray,
1761
+ setMulti,
1762
+ incrementMulti,
1763
+ createFastIndex,
1764
+ pushToFastIndex,
1765
+ pushToFastIndexWithExpiracy,
1766
+ getFromFastIndexWithExpiracy
1767
+ }
1768
+
1769
+ exports.Pagination = {
1770
+ asPageNumber,
1771
+ pageify
1772
+ }
1773
+
1774
+ exports.Files = {
1775
+ existsAsync,
1776
+ writeJsonAsync,
1777
+ readdirDeepAsync,
1778
+ readUtf8FileAsync,
1779
+ readJsonAsync,
1780
+ readLinesAsync,
1781
+ readMatchingLines,
1782
+ readNonEmptyLines,
1783
+ readCsv,
1784
+ walkTreeAsync,
1785
+ getFileSize,
1786
+ asMegabytes,
1787
+ getDirectorySize,
1788
+ convertBytes,
1789
+ getChecksum: getChecksumOfFile,
1790
+ mkdirp
1791
+ }
1792
+
1793
+ exports.Types = {
1794
+ isFunction,
1795
+ isObject,
1796
+ isStrictlyObject,
1797
+ isEmptyArray,
1798
+ isUndefined,
1799
+ isString,
1800
+ isNumber,
1801
+ isDate,
1802
+ isBlank,
1803
+ asString,
1804
+ asNumber,
1805
+ asDate,
1806
+ asNullableString
1807
+ }
1808
+
1809
+ exports.Strings = {
1810
+ tokenizeByCount,
1811
+ tokenizeByLength,
1812
+ randomHex: randomHexString,
1813
+ randomLetter: randomLetterString,
1814
+ randomAlphanumeric: randomAlphanumericString,
1815
+ randomRichAscii: randomRichAsciiString,
1816
+ randomUnicode: randomUnicodeString,
1817
+ includesAny,
1818
+ slugify,
1819
+ enumify,
1820
+ escapeHtml,
1821
+ decodeHtmlEntities,
1822
+ after,
1823
+ afterLast,
1824
+ before,
1825
+ beforeLast,
1826
+ between,
1827
+ betweenWide,
1828
+ betweenNarrow,
1829
+ getPreLine,
1830
+ containsWord,
1831
+ containsWords,
1832
+ joinUrl,
1833
+ getFuzzyMatchScore,
1834
+ sortByFuzzyScore,
1835
+ getChecksum,
1836
+ splitOnce,
1837
+ randomize,
1838
+ shrinkTrim,
1839
+ capitalize,
1840
+ decapitalize,
1841
+ csvEscape,
1842
+ parseCsv,
1843
+ surroundInOut,
1844
+ getExtension,
1845
+ getBasename,
1846
+ normalizeFilename,
1847
+ parseFilename,
1848
+ camelToTitle,
1849
+ slugToTitle,
1850
+ slugToCamel,
1851
+ joinHumanly
1852
+ }
1853
+
1854
+ exports.Assertions = {
1855
+ asEqual,
1856
+ asTrue,
1857
+ asTruthy,
1858
+ asFalse,
1859
+ asFalsy,
1860
+ asEither
1861
+ }
1862
+
1863
+ exports.Cache = {
1864
+ get: getCached
1865
+ }
1866
+
1867
+ exports.Logger = {
1868
+ create: createLogger,
1869
+ enableFileLogging
1634
1870
  }