@andrew_l/toolkit 0.2.4 → 0.2.7

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.
package/dist/index.cjs CHANGED
@@ -1,11 +1,11 @@
1
1
  'use strict';
2
2
 
3
- const _get = require('lodash/get');
4
- const _cloneDeep = require('lodash/cloneDeep');
5
- const _cloneDeepWith = require('lodash/cloneDeepWith');
6
- const _defaultsDeep = require('lodash/defaultsDeep');
7
- const _set = require('lodash/set');
8
- const _unset = require('lodash/unset');
3
+ const _get = require('lodash/get.js');
4
+ const _cloneDeep = require('lodash/cloneDeep.js');
5
+ const _cloneDeepWith = require('lodash/cloneDeepWith.js');
6
+ const _defaultsDeep = require('lodash/defaultsDeep.js');
7
+ const _set = require('lodash/set.js');
8
+ const _unset = require('lodash/unset.js');
9
9
 
10
10
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
11
11
 
@@ -248,6 +248,30 @@ function difference(...arrays) {
248
248
  return Array.from(set);
249
249
  }
250
250
 
251
+ function groupBy(array, keyBy, objectMode) {
252
+ const getItemKey = isFunction(keyBy) ? keyBy : (item) => item?.[keyBy];
253
+ let key;
254
+ if (objectMode === true) {
255
+ const result2 = {};
256
+ for (const item of array) {
257
+ key = getItemKey(item);
258
+ result2[key] = result2[key] || [];
259
+ result2[key].push(item);
260
+ }
261
+ return result2;
262
+ }
263
+ const result = /* @__PURE__ */ new Map();
264
+ for (const item of array) {
265
+ key = getItemKey(item);
266
+ if (!result.has(key)) {
267
+ result.set(key, [item]);
268
+ } else {
269
+ result.get(key).push(item);
270
+ }
271
+ }
272
+ return result;
273
+ }
274
+
251
275
  function intersection(...arrays) {
252
276
  if (arrays.length === 0) return [];
253
277
  if (arrays.length === 1) return arrays[0];
@@ -358,6 +382,7 @@ let AssertionError;
358
382
  (r) => r.BrowserAssertionError
359
383
  ) : await import('node:assert').then((r) => r.AssertionError);
360
384
  })();
385
+
361
386
  function ok(value, message) {
362
387
  if (!value) {
363
388
  throw toError$1(
@@ -379,6 +404,11 @@ function equal(actual, expected, message) {
379
404
  });
380
405
  }
381
406
  }
407
+ function empty$1(value, message) {
408
+ if (isEmpty(value)) {
409
+ throw toError$1(empty$1, value, message, "Expected not empty value.");
410
+ }
411
+ }
382
412
  function object(value, message) {
383
413
  if (!isObject(value)) {
384
414
  throw toError$1(object, value, message, "Expected object value.");
@@ -409,6 +439,36 @@ function number$1(value, message) {
409
439
  throw toError$1(number$1, value, message, "Expected number value.");
410
440
  }
411
441
  }
442
+ function date(value, message) {
443
+ if (!isDate(value)) {
444
+ throw toError$1(date, value, message, "Expected date value.");
445
+ }
446
+ }
447
+ function fn(value, message) {
448
+ if (!isNumber(value)) {
449
+ throw toError$1(fn, value, message, "Expected function value.");
450
+ }
451
+ }
452
+ function greaterThan(value, target, message) {
453
+ if (!isNumber(value) || value < target) {
454
+ throw toError$1(
455
+ greaterThan,
456
+ value,
457
+ message,
458
+ "Expected number value greater then " + target + "."
459
+ );
460
+ }
461
+ }
462
+ function lessThan(value, target, message) {
463
+ if (!isNumber(value) || value > target) {
464
+ throw toError$1(
465
+ lessThan,
466
+ value,
467
+ message,
468
+ "Expected number value less then " + target + "."
469
+ );
470
+ }
471
+ }
412
472
  function array(value, message) {
413
473
  if (!Array.isArray(value)) {
414
474
  throw toError$1(array, value, message, "Expected array value.");
@@ -442,7 +502,12 @@ const assert = {
442
502
  arrayNumbers: arrayNumbers,
443
503
  arrayStrings: arrayStrings,
444
504
  boolean: boolean,
505
+ date: date,
506
+ empty: empty$1,
445
507
  equal: equal,
508
+ fn: fn,
509
+ greaterThan: greaterThan,
510
+ lessThan: lessThan,
446
511
  notEmptyString: notEmptyString,
447
512
  number: number$1,
448
513
  object: object,
@@ -450,6 +515,53 @@ const assert = {
450
515
  string: string
451
516
  };
452
517
 
518
+ function weightedRoundRobin(arr) {
519
+ ok(arr.length > 0, "Array must contain at least one item.");
520
+ const instance = new WeightedRoundRobin(
521
+ arr.map((v) => ({
522
+ item: v.item,
523
+ weight: Math.min(v.weight ?? 1, 1)
524
+ }))
525
+ );
526
+ return () => instance.nextItem();
527
+ }
528
+ const gcd = (a, b) => !b ? a : gcd(b, a % b);
529
+ class WeightedRoundRobin {
530
+ constructor(items) {
531
+ this.items = items;
532
+ this.maxWeight = this._calculateMaxWeight();
533
+ this.gcdWeight = this._calculateGCD();
534
+ }
535
+ currentIndex = -1;
536
+ currentWeight = 0;
537
+ maxWeight;
538
+ gcdWeight;
539
+ _calculateGCD() {
540
+ return this.items.reduce(
541
+ (acc, curr) => gcd(acc, curr.weight),
542
+ this.items[0].weight
543
+ );
544
+ }
545
+ _calculateMaxWeight() {
546
+ return Math.max(...this.items.map((s) => s.weight));
547
+ }
548
+ nextItem() {
549
+ const n = this.items.length;
550
+ while (true) {
551
+ this.currentIndex = (this.currentIndex + 1) % n;
552
+ if (this.currentIndex === 0) {
553
+ this.currentWeight -= this.gcdWeight;
554
+ if (this.currentWeight <= 0) {
555
+ this.currentWeight = this.maxWeight;
556
+ }
557
+ }
558
+ if (this.items[this.currentIndex].weight >= this.currentWeight) {
559
+ return this.items[this.currentIndex].item;
560
+ }
561
+ }
562
+ }
563
+ }
564
+
453
565
  class Base64Encoding {
454
566
  alphabet;
455
567
  padding;
@@ -658,13 +770,12 @@ const def = (obj, key, value, writable = false) => {
658
770
  const rndCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
659
771
  const charactersLength = rndCharacters.length;
660
772
  function randomString(length) {
661
- let result = "";
662
- for (let i = 0; i < length; i++) {
663
- result += rndCharacters.charAt(
664
- Math.floor(Math.random() * charactersLength)
665
- );
773
+ let str = "";
774
+ let num = isNumber(length) ? Math.max(0, length) : 0;
775
+ while (num--) {
776
+ str += rndCharacters[charactersLength * Math.random() | 0];
666
777
  }
667
- return result;
778
+ return str;
668
779
  }
669
780
 
670
781
  const objectKeys = /* @__PURE__ */ new WeakMap();
@@ -1457,27 +1568,57 @@ function flatten(obj, {
1457
1568
  isObjectCompare = isObject
1458
1569
  } = {}) {
1459
1570
  const result = {};
1460
- const processed = /* @__PURE__ */ new WeakSet();
1461
- const handle = (node, prefix, initial) => {
1462
- if (processed.has(node)) return;
1463
- if (isObjectCompare(node)) {
1464
- processed.add(node);
1465
- prefix = prefix && !initial ? `${prefix}${separator}` : prefix;
1466
- for (const [key, value] of Object.entries(node)) {
1467
- handle(value, `${prefix}${key}`);
1468
- }
1571
+ const seen = /* @__PURE__ */ new WeakSet();
1572
+ if (isObjectCompare(obj)) {
1573
+ iter(
1574
+ result,
1575
+ seen,
1576
+ isObjectCompare,
1577
+ separator,
1578
+ withArrays,
1579
+ obj,
1580
+ initialPrefix,
1581
+ true
1582
+ );
1583
+ }
1584
+ return result;
1585
+ }
1586
+ function iter(output, seen, isObjectCompare, separator, withArrays, val, key, initial) {
1587
+ if (seen.has(val)) return;
1588
+ let k, pfx = key && !initial ? key + separator : key;
1589
+ if (Array.isArray(val)) {
1590
+ seen.add(val);
1591
+ if (!withArrays) {
1592
+ output[key] = val;
1469
1593
  return;
1470
1594
  }
1471
- if (Array.isArray(node) && withArrays) {
1472
- processed.add(node);
1473
- prefix = prefix && !initial ? `${prefix}${separator}` : prefix;
1474
- node.forEach((value, idx) => handle(value, `${prefix}${idx}`));
1475
- return;
1595
+ for (k = 0; k < val.length; k++) {
1596
+ iter(
1597
+ output,
1598
+ seen,
1599
+ isObjectCompare,
1600
+ separator,
1601
+ withArrays,
1602
+ val[k],
1603
+ pfx + k
1604
+ );
1476
1605
  }
1477
- result[prefix] = node;
1478
- };
1479
- handle(obj, initialPrefix, true);
1480
- return result;
1606
+ } else if (isObjectCompare(val)) {
1607
+ seen.add(val);
1608
+ for (k in val) {
1609
+ iter(
1610
+ output,
1611
+ seen,
1612
+ isObjectCompare,
1613
+ separator,
1614
+ withArrays,
1615
+ val[k],
1616
+ pfx + k
1617
+ );
1618
+ }
1619
+ } else {
1620
+ output[key] = val;
1621
+ }
1481
1622
  }
1482
1623
 
1483
1624
  function has(value, keys) {
@@ -1558,12 +1699,40 @@ const toMap = (obj) => {
1558
1699
  return map;
1559
1700
  };
1560
1701
 
1561
- function unflatten(obj, separator = "_") {
1562
- const result = {};
1563
- Object.keys(obj).forEach((path) => {
1564
- set(result, path.split(separator), obj[path]);
1565
- });
1566
- return result;
1702
+ const badKeys = Object.freeze(
1703
+ /* @__PURE__ */ new Set(["constructor", "__proto__", "prototype"])
1704
+ );
1705
+ function unflatten(input, separator = "_") {
1706
+ if (!isObject(input)) {
1707
+ return {};
1708
+ }
1709
+ let arr, tmp, output;
1710
+ let i = 0, k, key;
1711
+ for (k in input) {
1712
+ tmp = output;
1713
+ arr = k.split(separator);
1714
+ for (i = 0; i < arr.length; ) {
1715
+ key = arr[i++];
1716
+ if (tmp == null) {
1717
+ tmp = empty(+key);
1718
+ output = output || tmp;
1719
+ }
1720
+ if (badKeys.has(key)) break;
1721
+ if (i < arr.length) {
1722
+ if (key in tmp) {
1723
+ tmp = tmp[key];
1724
+ } else {
1725
+ tmp = tmp[key] = empty(+arr[i]);
1726
+ }
1727
+ } else {
1728
+ tmp[key] = input[k];
1729
+ }
1730
+ }
1731
+ }
1732
+ return output;
1733
+ }
1734
+ function empty(key) {
1735
+ return key === key ? [] : {};
1567
1736
  }
1568
1737
 
1569
1738
  const unset = _unset__default;
@@ -3188,7 +3357,7 @@ function getFileExtension(name, withDot = true) {
3188
3357
  return null;
3189
3358
  }
3190
3359
  const ext = name.split(".").at(-1)?.split("?")?.at(0);
3191
- return ext ? withDot ? ext : `.${ext}` : null;
3360
+ return ext ? withDot ? `.${ext}` : ext : null;
3192
3361
  }
3193
3362
 
3194
3363
  const DEF_PROTOCOLS = ["http://", "https://"];
@@ -3206,7 +3375,21 @@ function getFileName(value) {
3206
3375
  if (!ext) {
3207
3376
  return value;
3208
3377
  }
3209
- return value.slice(0, -ext.length - 1);
3378
+ return value.slice(0, -ext.length);
3379
+ }
3380
+
3381
+ function getMostSpecificPaths(keys) {
3382
+ keys = [...keys].sort();
3383
+ const result = [];
3384
+ for (let i = 0; i < keys.length; i++) {
3385
+ const currentPath = keys[i];
3386
+ const nextPath = keys[i + 1];
3387
+ if (currentPath === nextPath) continue;
3388
+ if (!nextPath || !nextPath.startsWith(currentPath + ".")) {
3389
+ result.push(currentPath);
3390
+ }
3391
+ }
3392
+ return result;
3210
3393
  }
3211
3394
 
3212
3395
  function humanFileSize(bytes, digits = 1, withSpace = true) {
@@ -3308,19 +3491,37 @@ function tryStringify(o) {
3308
3491
  }
3309
3492
  }
3310
3493
 
3311
- const IS_DEV = env.isDevelopment || env.bool("DEV", false);
3494
+ const LEVEL_NUM = {
3495
+ debug: 0,
3496
+ log: 1,
3497
+ info: 2,
3498
+ warn: 3,
3499
+ error: 4
3500
+ };
3501
+ let currentLogLevel = LEVEL_NUM.log;
3502
+ const loggerSetLevel = (level) => {
3503
+ number$1(LEVEL_NUM[level], `Invalid log level: ${level}`);
3504
+ currentLogLevel = LEVEL_NUM[level];
3505
+ };
3312
3506
  const logger = (...baseArgs) => {
3507
+ if (isString(baseArgs[0]?.url)) {
3508
+ baseArgs[0] = baseArgs[0]?.url;
3509
+ }
3313
3510
  if (typeof baseArgs[0] === "string" && baseArgs[0][0] !== "[") {
3314
3511
  baseArgs[0] = `[${baseArgs[0].split("/").at(-1).split("?", 1)[0]}]`;
3315
3512
  }
3316
3513
  const writeLog = (level, ...[pattern, ...args]) => {
3514
+ const levelNum = LEVEL_NUM[level];
3515
+ if (levelNum < currentLogLevel) {
3516
+ return;
3517
+ }
3317
3518
  if (!isString(pattern)) {
3318
3519
  console[level](...baseArgs, pattern, ...args);
3319
3520
  return;
3320
3521
  }
3321
3522
  const unusedArgs = [];
3322
3523
  const formatted = sprintf(pattern, args, unusedArgs);
3323
- console[level](...baseArgs, ...formatted, ...unusedArgs);
3524
+ console[level](...baseArgs, formatted, ...unusedArgs);
3324
3525
  };
3325
3526
  const log = writeLog.bind(null, "log");
3326
3527
  const info = writeLog.bind(null, "info");
@@ -3338,9 +3539,6 @@ const logger = (...baseArgs) => {
3338
3539
  debug,
3339
3540
  extend
3340
3541
  };
3341
- Object.defineProperty(instance, "debug", {
3342
- get: () => IS_DEV ? debug : noop
3343
- });
3344
3542
  return instance;
3345
3543
  };
3346
3544
 
@@ -4325,9 +4523,11 @@ exports.get = get;
4325
4523
  exports.getFileExtension = getFileExtension;
4326
4524
  exports.getFileName = getFileName;
4327
4525
  exports.getInitials = getInitials;
4526
+ exports.getMostSpecificPaths = getMostSpecificPaths;
4328
4527
  exports.getRandomInt = getRandomInt;
4329
4528
  exports.getRandomTime = getRandomTime;
4330
4529
  exports.getWords = getWords;
4530
+ exports.groupBy = groupBy;
4331
4531
  exports.has = has;
4332
4532
  exports.hasOwn = hasOwn;
4333
4533
  exports.hasProtocol = hasProtocol;
@@ -4377,6 +4577,7 @@ exports.isoToFlagEmoji = isoToFlagEmoji;
4377
4577
  exports.kebabCase = kebabCase;
4378
4578
  exports.keyBy = keyBy;
4379
4579
  exports.logger = logger;
4580
+ exports.loggerSetLevel = loggerSetLevel;
4380
4581
  exports.lowerCase = lowerCase;
4381
4582
  exports.luminance = luminance;
4382
4583
  exports.maskingEmail = maskingEmail;
@@ -4431,6 +4632,7 @@ exports.uniq = uniq;
4431
4632
  exports.uniqBy = uniqBy;
4432
4633
  exports.unset = unset;
4433
4634
  exports.weeksInYear = weeksInYear;
4635
+ exports.weightedRoundRobin = weightedRoundRobin;
4434
4636
  exports.withCache = withCache;
4435
4637
  exports.withCacheBucket = withCacheBucket;
4436
4638
  exports.withCacheBucketBatch = withCacheBucketBatch;