@andrew_l/toolkit 0.2.4 → 0.2.5

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.mjs CHANGED
@@ -1,9 +1,9 @@
1
- import _get from 'lodash/get';
2
- import _cloneDeep from 'lodash/cloneDeep';
3
- import _cloneDeepWith from 'lodash/cloneDeepWith';
4
- import _defaultsDeep from 'lodash/defaultsDeep';
5
- import _set from 'lodash/set';
6
- import _unset from 'lodash/unset';
1
+ import _get from 'lodash/get.js';
2
+ import _cloneDeep from 'lodash/cloneDeep.js';
3
+ import _cloneDeepWith from 'lodash/cloneDeepWith.js';
4
+ import _defaultsDeep from 'lodash/defaultsDeep.js';
5
+ import _set from 'lodash/set.js';
6
+ import _unset from 'lodash/unset.js';
7
7
 
8
8
  const isClient = typeof globalThis?.window !== "undefined";
9
9
  const isDef = (val) => typeof val !== "undefined";
@@ -237,6 +237,30 @@ function difference(...arrays) {
237
237
  return Array.from(set);
238
238
  }
239
239
 
240
+ function groupBy(array, keyBy, objectMode) {
241
+ const getItemKey = isFunction(keyBy) ? keyBy : (item) => item?.[keyBy];
242
+ let key;
243
+ if (objectMode === true) {
244
+ const result2 = {};
245
+ for (const item of array) {
246
+ key = getItemKey(item);
247
+ result2[key] = result2[key] || [];
248
+ result2[key].push(item);
249
+ }
250
+ return result2;
251
+ }
252
+ const result = /* @__PURE__ */ new Map();
253
+ for (const item of array) {
254
+ key = getItemKey(item);
255
+ if (!result.has(key)) {
256
+ result.set(key, [item]);
257
+ } else {
258
+ result.get(key).push(item);
259
+ }
260
+ }
261
+ return result;
262
+ }
263
+
240
264
  function intersection(...arrays) {
241
265
  if (arrays.length === 0) return [];
242
266
  if (arrays.length === 1) return arrays[0];
@@ -347,6 +371,7 @@ let AssertionError;
347
371
  (r) => r.BrowserAssertionError
348
372
  ) : await import('node:assert').then((r) => r.AssertionError);
349
373
  })();
374
+
350
375
  function ok(value, message) {
351
376
  if (!value) {
352
377
  throw toError$1(
@@ -368,6 +393,11 @@ function equal(actual, expected, message) {
368
393
  });
369
394
  }
370
395
  }
396
+ function empty$1(value, message) {
397
+ if (isEmpty(value)) {
398
+ throw toError$1(empty$1, value, message, "Expected not empty value.");
399
+ }
400
+ }
371
401
  function object(value, message) {
372
402
  if (!isObject(value)) {
373
403
  throw toError$1(object, value, message, "Expected object value.");
@@ -398,6 +428,36 @@ function number$1(value, message) {
398
428
  throw toError$1(number$1, value, message, "Expected number value.");
399
429
  }
400
430
  }
431
+ function date(value, message) {
432
+ if (!isDate(value)) {
433
+ throw toError$1(date, value, message, "Expected date value.");
434
+ }
435
+ }
436
+ function fn(value, message) {
437
+ if (!isNumber(value)) {
438
+ throw toError$1(fn, value, message, "Expected function value.");
439
+ }
440
+ }
441
+ function greaterThan(value, target, message) {
442
+ if (!isNumber(value) || value < target) {
443
+ throw toError$1(
444
+ greaterThan,
445
+ value,
446
+ message,
447
+ "Expected number value greater then " + target + "."
448
+ );
449
+ }
450
+ }
451
+ function lessThan(value, target, message) {
452
+ if (!isNumber(value) || value > target) {
453
+ throw toError$1(
454
+ lessThan,
455
+ value,
456
+ message,
457
+ "Expected number value less then " + target + "."
458
+ );
459
+ }
460
+ }
401
461
  function array(value, message) {
402
462
  if (!Array.isArray(value)) {
403
463
  throw toError$1(array, value, message, "Expected array value.");
@@ -431,7 +491,12 @@ const assert = {
431
491
  arrayNumbers: arrayNumbers,
432
492
  arrayStrings: arrayStrings,
433
493
  boolean: boolean,
494
+ date: date,
495
+ empty: empty$1,
434
496
  equal: equal,
497
+ fn: fn,
498
+ greaterThan: greaterThan,
499
+ lessThan: lessThan,
435
500
  notEmptyString: notEmptyString,
436
501
  number: number$1,
437
502
  object: object,
@@ -439,6 +504,53 @@ const assert = {
439
504
  string: string
440
505
  };
441
506
 
507
+ function weightedRoundRobin(arr) {
508
+ ok(arr.length > 0, "Array must contain at least one item.");
509
+ const instance = new WeightedRoundRobin(
510
+ arr.map((v) => ({
511
+ item: v.item,
512
+ weight: Math.min(v.weight ?? 1, 1)
513
+ }))
514
+ );
515
+ return () => instance.nextItem();
516
+ }
517
+ const gcd = (a, b) => !b ? a : gcd(b, a % b);
518
+ class WeightedRoundRobin {
519
+ constructor(items) {
520
+ this.items = items;
521
+ this.maxWeight = this._calculateMaxWeight();
522
+ this.gcdWeight = this._calculateGCD();
523
+ }
524
+ currentIndex = -1;
525
+ currentWeight = 0;
526
+ maxWeight;
527
+ gcdWeight;
528
+ _calculateGCD() {
529
+ return this.items.reduce(
530
+ (acc, curr) => gcd(acc, curr.weight),
531
+ this.items[0].weight
532
+ );
533
+ }
534
+ _calculateMaxWeight() {
535
+ return Math.max(...this.items.map((s) => s.weight));
536
+ }
537
+ nextItem() {
538
+ const n = this.items.length;
539
+ while (true) {
540
+ this.currentIndex = (this.currentIndex + 1) % n;
541
+ if (this.currentIndex === 0) {
542
+ this.currentWeight -= this.gcdWeight;
543
+ if (this.currentWeight <= 0) {
544
+ this.currentWeight = this.maxWeight;
545
+ }
546
+ }
547
+ if (this.items[this.currentIndex].weight >= this.currentWeight) {
548
+ return this.items[this.currentIndex].item;
549
+ }
550
+ }
551
+ }
552
+ }
553
+
442
554
  class Base64Encoding {
443
555
  alphabet;
444
556
  padding;
@@ -647,13 +759,12 @@ const def = (obj, key, value, writable = false) => {
647
759
  const rndCharacters = "abcdefghijklmnopqrstuvwxyz0123456789";
648
760
  const charactersLength = rndCharacters.length;
649
761
  function randomString(length) {
650
- let result = "";
651
- for (let i = 0; i < length; i++) {
652
- result += rndCharacters.charAt(
653
- Math.floor(Math.random() * charactersLength)
654
- );
762
+ let str = "";
763
+ let num = isNumber(length) ? Math.max(0, length) : 0;
764
+ while (num--) {
765
+ str += rndCharacters[charactersLength * Math.random() | 0];
655
766
  }
656
- return result;
767
+ return str;
657
768
  }
658
769
 
659
770
  const objectKeys = /* @__PURE__ */ new WeakMap();
@@ -1446,27 +1557,57 @@ function flatten(obj, {
1446
1557
  isObjectCompare = isObject
1447
1558
  } = {}) {
1448
1559
  const result = {};
1449
- const processed = /* @__PURE__ */ new WeakSet();
1450
- const handle = (node, prefix, initial) => {
1451
- if (processed.has(node)) return;
1452
- if (isObjectCompare(node)) {
1453
- processed.add(node);
1454
- prefix = prefix && !initial ? `${prefix}${separator}` : prefix;
1455
- for (const [key, value] of Object.entries(node)) {
1456
- handle(value, `${prefix}${key}`);
1457
- }
1560
+ const seen = /* @__PURE__ */ new WeakSet();
1561
+ if (isObjectCompare(obj)) {
1562
+ iter(
1563
+ result,
1564
+ seen,
1565
+ isObjectCompare,
1566
+ separator,
1567
+ withArrays,
1568
+ obj,
1569
+ initialPrefix,
1570
+ true
1571
+ );
1572
+ }
1573
+ return result;
1574
+ }
1575
+ function iter(output, seen, isObjectCompare, separator, withArrays, val, key, initial) {
1576
+ if (seen.has(val)) return;
1577
+ let k, pfx = key && !initial ? key + separator : key;
1578
+ if (Array.isArray(val)) {
1579
+ seen.add(val);
1580
+ if (!withArrays) {
1581
+ output[key] = val;
1458
1582
  return;
1459
1583
  }
1460
- if (Array.isArray(node) && withArrays) {
1461
- processed.add(node);
1462
- prefix = prefix && !initial ? `${prefix}${separator}` : prefix;
1463
- node.forEach((value, idx) => handle(value, `${prefix}${idx}`));
1464
- return;
1584
+ for (k = 0; k < val.length; k++) {
1585
+ iter(
1586
+ output,
1587
+ seen,
1588
+ isObjectCompare,
1589
+ separator,
1590
+ withArrays,
1591
+ val[k],
1592
+ pfx + k
1593
+ );
1465
1594
  }
1466
- result[prefix] = node;
1467
- };
1468
- handle(obj, initialPrefix, true);
1469
- return result;
1595
+ } else if (isObjectCompare(val)) {
1596
+ seen.add(val);
1597
+ for (k in val) {
1598
+ iter(
1599
+ output,
1600
+ seen,
1601
+ isObjectCompare,
1602
+ separator,
1603
+ withArrays,
1604
+ val[k],
1605
+ pfx + k
1606
+ );
1607
+ }
1608
+ } else {
1609
+ output[key] = val;
1610
+ }
1470
1611
  }
1471
1612
 
1472
1613
  function has(value, keys) {
@@ -1547,12 +1688,40 @@ const toMap = (obj) => {
1547
1688
  return map;
1548
1689
  };
1549
1690
 
1550
- function unflatten(obj, separator = "_") {
1551
- const result = {};
1552
- Object.keys(obj).forEach((path) => {
1553
- set(result, path.split(separator), obj[path]);
1554
- });
1555
- return result;
1691
+ const badKeys = Object.freeze(
1692
+ /* @__PURE__ */ new Set(["constructor", "__proto__", "prototype"])
1693
+ );
1694
+ function unflatten(input, separator = "_") {
1695
+ if (!isObject(input)) {
1696
+ return {};
1697
+ }
1698
+ let arr, tmp, output;
1699
+ let i = 0, k, key;
1700
+ for (k in input) {
1701
+ tmp = output;
1702
+ arr = k.split(separator);
1703
+ for (i = 0; i < arr.length; ) {
1704
+ key = arr[i++];
1705
+ if (tmp == null) {
1706
+ tmp = empty(+key);
1707
+ output = output || tmp;
1708
+ }
1709
+ if (badKeys.has(key)) break;
1710
+ if (i < arr.length) {
1711
+ if (key in tmp) {
1712
+ tmp = tmp[key];
1713
+ } else {
1714
+ tmp = tmp[key] = empty(+arr[i]);
1715
+ }
1716
+ } else {
1717
+ tmp[key] = input[k];
1718
+ }
1719
+ }
1720
+ }
1721
+ return output;
1722
+ }
1723
+ function empty(key) {
1724
+ return key === key ? [] : {};
1556
1725
  }
1557
1726
 
1558
1727
  const unset = _unset;
@@ -3177,7 +3346,7 @@ function getFileExtension(name, withDot = true) {
3177
3346
  return null;
3178
3347
  }
3179
3348
  const ext = name.split(".").at(-1)?.split("?")?.at(0);
3180
- return ext ? withDot ? ext : `.${ext}` : null;
3349
+ return ext ? withDot ? `.${ext}` : ext : null;
3181
3350
  }
3182
3351
 
3183
3352
  const DEF_PROTOCOLS = ["http://", "https://"];
@@ -3195,7 +3364,21 @@ function getFileName(value) {
3195
3364
  if (!ext) {
3196
3365
  return value;
3197
3366
  }
3198
- return value.slice(0, -ext.length - 1);
3367
+ return value.slice(0, -ext.length);
3368
+ }
3369
+
3370
+ function getMostSpecificPaths(keys) {
3371
+ keys = [...keys].sort();
3372
+ const result = [];
3373
+ for (let i = 0; i < keys.length; i++) {
3374
+ const currentPath = keys[i];
3375
+ const nextPath = keys[i + 1];
3376
+ if (currentPath === nextPath) continue;
3377
+ if (!nextPath || !nextPath.startsWith(currentPath + ".")) {
3378
+ result.push(currentPath);
3379
+ }
3380
+ }
3381
+ return result;
3199
3382
  }
3200
3383
 
3201
3384
  function humanFileSize(bytes, digits = 1, withSpace = true) {
@@ -4220,5 +4403,5 @@ function wrapText(value, maxLength = 30) {
4220
4403
  return value;
4221
4404
  }
4222
4405
 
4223
- export { AppError, AsyncIterableQueue, BrowserAssertionError, CancellablePromise, ColorParser, instance as EJSON, EJSONStream, FindMean, FixedMap, FixedWeakMap, LruCache, Queue, SimpleEventEmitter, TWEMOJI_REGEX, TimeBucket, TimeSpan, alpha, arrayable, assert, asyncFilter, asyncFind, asyncForEach, asyncMap, avg, base64ToBytes, bigIntBytes, bigIntFromBytes, blendColors, buildCssColor, bytesToBase64, cache, cacheBucket, cacheFixed, cacheLRU, camelCase, capitalize, captureStackTrace, catchError, channelsToHSL, channelsToHex, channelsToRGB, checkBitmask, chunk, chunkSeries, clamp, cleanEmpty, cleanObject, colorToChannels, compareBytes, concatenateBytes, contrastRatio, convertToUnit, crc32, createCustomizer, createCustomizerFactory, createDateObject, createDeepCloneWith, createEJSON, createEJSONStream, createEnvParser, createRandomizer, createSecureCustomizer, createTimeObject, createTimeSpan, createWithCache, cssVariable, dateInDays, dateInSeconds, deepAssign, deepClone, deepCloneWith, deepDefaults, deepFreeze, def, defer, delay, difference, dropCache, env, escapeHtml, escapeNumeric, escapeRegExp, fastIdle, fastIdlePromise, fastRaf, findMean, flagsToMap, flatten, formatMoney, formatNumber, get, getFileExtension, getFileName, getInitials, getRandomInt, getRandomTime, getWords, has, hasOwn, hasProtocol, hex, hexToChannels, hmToSeconds, hslToChannels, humanFileSize, humanize, interpolateColor, intersection, isBigInt, isBoolean, isCached, isClient, isColorChannels, isCustomizerFactory, isDate, isDateObject, isDef, isEmpty, isEqual, isError, isFunction, isInfinity, isMap, isNullOrUndefined, isNumber, isObject, isOneEmoji, isPlainObject, isPrimitive, isPromise, isSet, isSkip, isString, isSuccess, isSymbol, isTimeObject, isTimeString, isTimeValue, isValidWeekDay, isWeakMap, isWeakSet, isWithCache, isoToFlagEmoji, kebabCase, keyBy, logger, lowerCase, luminance, maskingEmail, maskingPhone, maskingWords, nextTickIteration, noop, objectId, omit, omitPrefixed, orderBy, parseAllNumbers, parseAlpha, parsePercentage, percentOf, pick, pickPrefixed, qs, rafPromise, randomString, removeVS16s, retryOnError, rgbToChannels, round2digits, secondsToHm, set, shuffle, snakeCase, sprintf, startCase, strAssign, sum, timeFromMinutes, timeStringify, timeToMinutes, timeout, timestamp, timestampMs, timestampToDate, tintedTextColor, toError, toMap, truncate, typeOf, uint16ToUint8, uint32ToUint8, uint8ToUint16, uint8ToUint32, unflatten, union, uniq, uniqBy, unset, weeksInYear, withCache, withCacheBucket, withCacheBucketBatch, withCacheFixed, withCacheLRU, withDeepClone, withPointerCache, wrapText };
4406
+ export { AppError, AsyncIterableQueue, BrowserAssertionError, CancellablePromise, ColorParser, instance as EJSON, EJSONStream, FindMean, FixedMap, FixedWeakMap, LruCache, Queue, SimpleEventEmitter, TWEMOJI_REGEX, TimeBucket, TimeSpan, alpha, arrayable, assert, asyncFilter, asyncFind, asyncForEach, asyncMap, avg, base64ToBytes, bigIntBytes, bigIntFromBytes, blendColors, buildCssColor, bytesToBase64, cache, cacheBucket, cacheFixed, cacheLRU, camelCase, capitalize, captureStackTrace, catchError, channelsToHSL, channelsToHex, channelsToRGB, checkBitmask, chunk, chunkSeries, clamp, cleanEmpty, cleanObject, colorToChannels, compareBytes, concatenateBytes, contrastRatio, convertToUnit, crc32, createCustomizer, createCustomizerFactory, createDateObject, createDeepCloneWith, createEJSON, createEJSONStream, createEnvParser, createRandomizer, createSecureCustomizer, createTimeObject, createTimeSpan, createWithCache, cssVariable, dateInDays, dateInSeconds, deepAssign, deepClone, deepCloneWith, deepDefaults, deepFreeze, def, defer, delay, difference, dropCache, env, escapeHtml, escapeNumeric, escapeRegExp, fastIdle, fastIdlePromise, fastRaf, findMean, flagsToMap, flatten, formatMoney, formatNumber, get, getFileExtension, getFileName, getInitials, getMostSpecificPaths, getRandomInt, getRandomTime, getWords, groupBy, has, hasOwn, hasProtocol, hex, hexToChannels, hmToSeconds, hslToChannels, humanFileSize, humanize, interpolateColor, intersection, isBigInt, isBoolean, isCached, isClient, isColorChannels, isCustomizerFactory, isDate, isDateObject, isDef, isEmpty, isEqual, isError, isFunction, isInfinity, isMap, isNullOrUndefined, isNumber, isObject, isOneEmoji, isPlainObject, isPrimitive, isPromise, isSet, isSkip, isString, isSuccess, isSymbol, isTimeObject, isTimeString, isTimeValue, isValidWeekDay, isWeakMap, isWeakSet, isWithCache, isoToFlagEmoji, kebabCase, keyBy, logger, lowerCase, luminance, maskingEmail, maskingPhone, maskingWords, nextTickIteration, noop, objectId, omit, omitPrefixed, orderBy, parseAllNumbers, parseAlpha, parsePercentage, percentOf, pick, pickPrefixed, qs, rafPromise, randomString, removeVS16s, retryOnError, rgbToChannels, round2digits, secondsToHm, set, shuffle, snakeCase, sprintf, startCase, strAssign, sum, timeFromMinutes, timeStringify, timeToMinutes, timeout, timestamp, timestampMs, timestampToDate, tintedTextColor, toError, toMap, truncate, typeOf, uint16ToUint8, uint32ToUint8, uint8ToUint16, uint8ToUint32, unflatten, union, uniq, uniqBy, unset, weeksInYear, weightedRoundRobin, withCache, withCacheBucket, withCacheBucketBatch, withCacheFixed, withCacheLRU, withDeepClone, withPointerCache, wrapText };
4224
4407
  //# sourceMappingURL=index.mjs.map