@naturalcycles/js-lib 14.249.0 → 14.251.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 (59) hide show
  1. package/dist/array/array.util.js +52 -42
  2. package/dist/datetime/localDate.js +30 -17
  3. package/dist/datetime/localTime.js +31 -18
  4. package/dist/error/assert.js +2 -1
  5. package/dist/http/fetcher.js +5 -5
  6. package/dist/json-schema/jsonSchemaBuilder.js +2 -2
  7. package/dist/math/math.util.js +14 -4
  8. package/dist/math/sma.js +4 -1
  9. package/dist/number/createDeterministicRandom.js +1 -1
  10. package/dist/number/number.util.js +2 -2
  11. package/dist/object/object.util.d.ts +11 -2
  12. package/dist/object/object.util.js +89 -38
  13. package/dist/object/sortObject.js +10 -7
  14. package/dist/object/sortObjectDeep.js +6 -9
  15. package/dist/promise/pDelay.d.ts +3 -3
  16. package/dist/promise/pDelay.js +1 -1
  17. package/dist/semver.js +22 -13
  18. package/dist/string/case.js +15 -6
  19. package/dist/string/lodash/words.js +1 -0
  20. package/dist/string/safeJsonStringify.js +1 -1
  21. package/dist-esm/array/array.util.js +52 -42
  22. package/dist-esm/datetime/localDate.js +30 -17
  23. package/dist-esm/datetime/localTime.js +31 -18
  24. package/dist-esm/error/assert.js +2 -1
  25. package/dist-esm/http/fetcher.js +5 -5
  26. package/dist-esm/json-schema/jsonSchemaBuilder.js +2 -2
  27. package/dist-esm/math/math.util.js +14 -4
  28. package/dist-esm/math/sma.js +4 -1
  29. package/dist-esm/number/createDeterministicRandom.js +1 -1
  30. package/dist-esm/number/number.util.js +2 -2
  31. package/dist-esm/object/object.util.js +88 -39
  32. package/dist-esm/object/sortObject.js +10 -7
  33. package/dist-esm/object/sortObjectDeep.js +6 -9
  34. package/dist-esm/promise/pDelay.js +1 -1
  35. package/dist-esm/semver.js +22 -13
  36. package/dist-esm/string/case.js +15 -6
  37. package/dist-esm/string/lodash/words.js +1 -0
  38. package/dist-esm/string/safeJsonStringify.js +1 -1
  39. package/package.json +1 -1
  40. package/src/array/array.util.ts +48 -40
  41. package/src/datetime/localDate.ts +34 -19
  42. package/src/datetime/localTime.ts +34 -21
  43. package/src/error/assert.ts +2 -1
  44. package/src/http/fetcher.ts +8 -6
  45. package/src/json-schema/jsonSchemaBuilder.ts +2 -2
  46. package/src/math/math.util.ts +13 -6
  47. package/src/math/sma.ts +3 -1
  48. package/src/number/createDeterministicRandom.ts +1 -1
  49. package/src/number/number.util.ts +4 -2
  50. package/src/object/object.util.ts +112 -53
  51. package/src/object/sortObject.ts +9 -7
  52. package/src/object/sortObjectDeep.ts +6 -12
  53. package/src/promise/pDelay.ts +6 -3
  54. package/src/semver.ts +22 -13
  55. package/src/string/case.ts +15 -12
  56. package/src/string/leven.ts +1 -1
  57. package/src/string/lodash/words.ts +1 -0
  58. package/src/string/safeJsonStringify.ts +1 -1
  59. package/src/string/string.util.ts +2 -2
@@ -1,5 +1,4 @@
1
1
  import { _assert } from '../error/assert';
2
- import { _isTruthy } from '../is.util';
3
2
  import { _ms } from '../time/time.util';
4
3
  import { localDate } from './localDate';
5
4
  import { WallTime } from './wallTime';
@@ -673,7 +672,7 @@ class LocalTimeFactory {
673
672
  if (input instanceof LocalTime)
674
673
  return true;
675
674
  if (input instanceof Date)
676
- return !isNaN(input.getDate());
675
+ return !Number.isNaN(input.getDate());
677
676
  // We currently don't validate Unixtimestamp input, treat it as always valid
678
677
  if (typeof input === 'number')
679
678
  return true;
@@ -694,7 +693,7 @@ class LocalTimeFactory {
694
693
  if (input instanceof LocalTime)
695
694
  return input;
696
695
  if (input instanceof Date) {
697
- if (isNaN(input.getDate()))
696
+ if (Number.isNaN(input.getDate()))
698
697
  return;
699
698
  return new LocalTime(input);
700
699
  }
@@ -758,7 +757,7 @@ class LocalTimeFactory {
758
757
  return;
759
758
  // Attempt to parse with Date constructor
760
759
  const d = new Date(s);
761
- return isNaN(d.getDate()) ? undefined : d;
760
+ return Number.isNaN(d.getDate()) ? undefined : d;
762
761
  }
763
762
  const o = {
764
763
  year: Number(m[1]),
@@ -786,7 +785,7 @@ class LocalTimeFactory {
786
785
  return hour >= 0 && hour <= 23 && minute >= 0 && minute <= 59 && second >= 0 && second <= 59;
787
786
  }
788
787
  fromDate(date) {
789
- _assert(!isNaN(date.getDate()), `localTime.fromDate is called on Date object that is invalid`);
788
+ _assert(!Number.isNaN(date.getDate()), 'localTime.fromDate is called on Date object that is invalid');
790
789
  return new LocalTime(date);
791
790
  }
792
791
  fromUnix(ts) {
@@ -841,24 +840,38 @@ class LocalTimeFactory {
841
840
  });
842
841
  }
843
842
  minOrUndefined(items) {
844
- return items.length ? this.min(items) : undefined;
843
+ let min;
844
+ for (const item of items) {
845
+ if (!item)
846
+ continue;
847
+ const lt = this.fromInput(item);
848
+ if (!min || lt.$date.valueOf() < min.$date.valueOf()) {
849
+ min = lt;
850
+ }
851
+ }
852
+ return min;
845
853
  }
846
854
  min(items) {
847
- const items2 = items.filter(_isTruthy);
848
- _assert(items2.length, 'localTime.min called on empty array');
849
- return items2
850
- .map(i => this.fromInput(i))
851
- .reduce((min, item) => (min.$date.valueOf() <= item.$date.valueOf() ? min : item));
855
+ const min = this.minOrUndefined(items);
856
+ _assert(min, 'localTime.min called on empty array');
857
+ return min;
852
858
  }
853
859
  maxOrUndefined(items) {
854
- return items.length ? this.max(items) : undefined;
860
+ let max;
861
+ for (const item of items) {
862
+ if (!item)
863
+ continue;
864
+ const lt = this.fromInput(item);
865
+ if (!max || lt.$date.valueOf() > max.$date.valueOf()) {
866
+ max = lt;
867
+ }
868
+ }
869
+ return max;
855
870
  }
856
871
  max(items) {
857
- const items2 = items.filter(_isTruthy);
858
- _assert(items2.length, 'localTime.max called on empty array');
859
- return items2
860
- .map(i => this.fromInput(i))
861
- .reduce((max, item) => (max.$date.valueOf() >= item.$date.valueOf() ? max : item));
872
+ const max = this.maxOrUndefined(items);
873
+ _assert(max, 'localTime.max called on empty array');
874
+ return max;
862
875
  }
863
876
  }
864
877
  // based on: https://github.com/date-fns/date-fns/blob/master/src/getISOWeek/index.ts
@@ -894,7 +907,7 @@ function getWeekYear(date) {
894
907
  if (date.getTime() >= startOfNextYear.getTime()) {
895
908
  return year + 1;
896
909
  }
897
- else if (date.getTime() >= startOfThisYear.getTime()) {
910
+ if (date.getTime() >= startOfThisYear.getTime()) {
898
911
  return year;
899
912
  }
900
913
  return year - 1;
@@ -49,7 +49,7 @@ export function _assertEquals(actual, expected, message, errorData) {
49
49
  export function _assertDeepEquals(actual, expected, message, errorData) {
50
50
  if (!_deepEquals(actual, expected)) {
51
51
  const msg = message ||
52
- [`not deeply equal`, `expected: ${_stringify(expected)}`, `got : ${_stringify(actual)}`]
52
+ ['not deeply equal', `expected: ${_stringify(expected)}`, `got : ${_stringify(actual)}`]
53
53
  .filter(Boolean)
54
54
  .join('\n');
55
55
  throw new AssertionError(msg, {
@@ -85,6 +85,7 @@ export function _assertIsNumber(v, message) {
85
85
  _assertTypeOf(v, 'number', message);
86
86
  }
87
87
  export function _assertTypeOf(v, expectedType, message) {
88
+ // biome-ignore lint/suspicious/useValidTypeof: ok
88
89
  if (typeof v !== expectedType) {
89
90
  const msg = message || `Expected typeof ${expectedType}, actual typeof: ${typeof v}`;
90
91
  throw new AssertionError(msg);
@@ -34,7 +34,7 @@ const defRetryOptions = {
34
34
  export class Fetcher {
35
35
  constructor(cfg = {}) {
36
36
  if (typeof globalThis.fetch !== 'function') {
37
- throw new TypeError(`globalThis.fetch is not available`);
37
+ throw new TypeError('globalThis.fetch is not available');
38
38
  }
39
39
  this.cfg = this.normalizeCfg(cfg);
40
40
  // Dynamically create all helper methods
@@ -281,7 +281,7 @@ export class Fetcher {
281
281
  res.body = res.fetchResponse.body;
282
282
  if (res.body === null) {
283
283
  // Error is to be handled upstream
284
- throw new Error(`fetchResponse.body is null`);
284
+ throw new Error('fetchResponse.body is null');
285
285
  }
286
286
  }
287
287
  res.retryStatus.retryStopped = true;
@@ -410,13 +410,13 @@ export class Fetcher {
410
410
  }
411
411
  else {
412
412
  const date = new Date(retryAfterStr);
413
- if (!isNaN(date)) {
413
+ if (!Number.isNaN(date)) {
414
414
  timeout = Number(date) - Date.now();
415
415
  }
416
416
  }
417
417
  this.cfg.logger.log(`retry-after: ${retryAfterStr}`);
418
418
  if (!timeout) {
419
- this.cfg.logger.warn(`retry-after could not be parsed`);
419
+ this.cfg.logger.warn('retry-after could not be parsed');
420
420
  }
421
421
  }
422
422
  }
@@ -570,7 +570,7 @@ export class Fetcher {
570
570
  const baseUrl = opt.baseUrl || this.cfg.baseUrl;
571
571
  if (baseUrl) {
572
572
  if (req.fullUrl.startsWith('/')) {
573
- console.warn(`Fetcher: url should not start with / when baseUrl is specified`);
573
+ console.warn('Fetcher: url should not start with / when baseUrl is specified');
574
574
  req.fullUrl = req.fullUrl.slice(1);
575
575
  }
576
576
  req.fullUrl = `${baseUrl}/${req.inputUrl}`;
@@ -141,7 +141,7 @@ export class JsonSchemaAnyBuilder {
141
141
  this.schema.optionalField = true;
142
142
  }
143
143
  else {
144
- delete this.schema.optionalField;
144
+ this.schema.optionalField = undefined;
145
145
  }
146
146
  return this;
147
147
  }
@@ -275,7 +275,7 @@ export class JsonSchemaObjectBuilder extends JsonSchemaAnyBuilder {
275
275
  this.schema.required.push(k);
276
276
  }
277
277
  else {
278
- delete schema.optionalField;
278
+ schema.optionalField = undefined;
279
279
  }
280
280
  this.schema.properties[k] = schema;
281
281
  });
@@ -1,3 +1,4 @@
1
+ import { _assert } from '../error/assert';
1
2
  import { _sortNumbers } from '../number/number.util';
2
3
  /**
3
4
  * @returns Average of the array of numbers
@@ -8,20 +9,29 @@ import { _sortNumbers } from '../number/number.util';
8
9
  * // 2.5
9
10
  */
10
11
  export function _average(values) {
11
- return values.reduce((a, b) => a + b) / values.length;
12
+ _assert(values.length, '_average is called on empty array');
13
+ let total = 0;
14
+ for (const n of values)
15
+ total += n;
16
+ return total / values.length;
12
17
  }
13
18
  /**
14
19
  * Same as _average, but safely returns null if input array is empty or nullish.
15
20
  */
16
21
  export function _averageOrNull(values) {
17
- return values?.length ? values.reduce((a, b) => a + b) / values.length : null;
22
+ return values?.length ? _average(values) : null;
18
23
  }
19
24
  /**
20
25
  * valuesArray and weightsArray length is expected to be the same.
21
26
  */
22
27
  export function _averageWeighted(values, weights) {
23
- const numerator = values.map((value, i) => value * weights[i]).reduce((a, b) => a + b);
24
- const denominator = weights.reduce((a, b) => a + b);
28
+ let numerator = 0;
29
+ let denominator = 0;
30
+ // eslint-disable-next-line unicorn/no-for-loop
31
+ for (let i = 0; i < values.length; i++) {
32
+ numerator += values[i] * weights[i];
33
+ denominator += weights[i];
34
+ }
25
35
  return numerator / denominator;
26
36
  }
27
37
  /**
@@ -17,7 +17,10 @@ export class SimpleMovingAverage {
17
17
  get avg() {
18
18
  if (this.data.length === 0)
19
19
  return 0;
20
- return this.data.reduce((total, n) => total + n, 0) / this.data.length;
20
+ let total = 0;
21
+ for (const n of this.data)
22
+ total += n;
23
+ return total / this.data.length;
21
24
  }
22
25
  /**
23
26
  * Push new value.
@@ -6,7 +6,7 @@
6
6
  */
7
7
  export function _createDeterministicRandom() {
8
8
  let seed = 0x2f6e2b1;
9
- return function () {
9
+ return () => {
10
10
  // Robert Jenkins’ 32 bit integer hash function
11
11
  seed = (seed + 0x7ed55d16 + (seed << 12)) & 0xffffffff;
12
12
  seed = (seed ^ 0xc761c23c ^ (seed >>> 19)) & 0xffffffff;
@@ -36,10 +36,10 @@ export function _isBetween(x, min, max, incl = '[)') {
36
36
  if (incl === '[)') {
37
37
  return x >= min && x < max;
38
38
  }
39
- else if (incl === '[]') {
39
+ if (incl === '[]') {
40
40
  return x >= min && x <= max;
41
41
  }
42
- else if (incl === '(]') {
42
+ if (incl === '(]') {
43
43
  return x > min && x <= max;
44
44
  }
45
45
  return x > min && x < max;
@@ -1,5 +1,5 @@
1
1
  import { _isEmpty, _isObject } from '../is.util';
2
- import { _objectEntries, SKIP } from '../types';
2
+ import { _objectEntries, SKIP, } from '../types';
3
3
  /**
4
4
  * Returns clone of `obj` with only `props` preserved.
5
5
  * Opposite of Omit.
@@ -7,28 +7,61 @@ import { _objectEntries, SKIP } from '../types';
7
7
  export function _pick(obj, props, mutate = false) {
8
8
  if (mutate) {
9
9
  // Start as original object (mutable), DELETE properties that are not whitelisted
10
- return Object.keys(obj).reduce((r, prop) => {
11
- if (!props.includes(prop))
12
- delete r[prop];
13
- return r;
14
- }, obj);
10
+ for (const k of Object.keys(obj)) {
11
+ if (!props.includes(k))
12
+ delete obj[k];
13
+ }
14
+ return obj;
15
15
  }
16
16
  // Start as empty object, pick/add needed properties
17
- return props.reduce((r, prop) => {
18
- if (prop in obj)
19
- r[prop] = obj[prop];
20
- return r;
21
- }, {});
17
+ const r = {};
18
+ for (const k of props) {
19
+ if (k in obj)
20
+ r[k] = obj[k];
21
+ }
22
+ return r;
23
+ }
24
+ /**
25
+ * Sets all properties of an object except passed ones to `undefined`.
26
+ * This is a more performant alternative to `_pick` that does picking/deleting.
27
+ */
28
+ export function _pickWithUndefined(obj, props, mutate = false) {
29
+ const r = mutate ? obj : { ...obj };
30
+ for (const k of Object.keys(r)) {
31
+ if (!props.includes(k)) {
32
+ r[k] = undefined;
33
+ }
34
+ }
35
+ return r;
22
36
  }
23
37
  /**
24
38
  * Returns clone of `obj` with `props` omitted.
25
39
  * Opposite of Pick.
26
40
  */
27
41
  export function _omit(obj, props, mutate = false) {
28
- return props.reduce((r, prop) => {
29
- delete r[prop];
30
- return r;
31
- }, mutate ? obj : { ...obj });
42
+ if (mutate) {
43
+ for (const k of props) {
44
+ delete obj[k];
45
+ }
46
+ return obj;
47
+ }
48
+ const r = {};
49
+ for (const k of Object.keys(obj)) {
50
+ if (!props.includes(k))
51
+ r[k] = obj[k];
52
+ }
53
+ return r;
54
+ }
55
+ /**
56
+ * Sets all passed properties of an object to `undefined`.
57
+ * This is a more performant alternative to `_omit` that does picking/deleting.
58
+ */
59
+ export function _omitWithUndefined(obj, props, mutate = false) {
60
+ const r = mutate ? obj : { ...obj };
61
+ for (const k of props) {
62
+ r[k] = undefined;
63
+ }
64
+ return r;
32
65
  }
33
66
  /**
34
67
  * Returns object with filtered keys from `props` array.
@@ -39,10 +72,11 @@ export function _omit(obj, props, mutate = false) {
39
72
  * ])
40
73
  */
41
74
  export function _mask(obj, props, mutate = false) {
42
- return props.reduce((r, prop) => {
43
- _unset(r, prop);
44
- return r;
45
- }, mutate ? obj : _deepCopy(obj));
75
+ const r = mutate ? obj : _deepCopy(obj);
76
+ for (const k of props) {
77
+ _unset(r, k);
78
+ }
79
+ return r;
46
80
  }
47
81
  /**
48
82
  * Removes "falsy" values from the object.
@@ -71,11 +105,21 @@ export function _filterEmptyArrays(obj, mutate = false) {
71
105
  * Allows filtering by both key and value.
72
106
  */
73
107
  export function _filterObject(obj, predicate, mutate = false) {
74
- return Object.keys(obj).reduce((r, k) => {
75
- if (!predicate(k, r[k], obj))
76
- delete r[k];
77
- return r;
78
- }, mutate ? obj : { ...obj });
108
+ if (mutate) {
109
+ for (const [k, v] of _objectEntries(obj)) {
110
+ if (!predicate(k, v, obj)) {
111
+ delete obj[k];
112
+ }
113
+ }
114
+ return obj;
115
+ }
116
+ const r = {};
117
+ for (const [k, v] of _objectEntries(obj)) {
118
+ if (predicate(k, v, obj)) {
119
+ r[k] = v;
120
+ }
121
+ }
122
+ return r;
79
123
  }
80
124
  /**
81
125
  * var users = {
@@ -89,10 +133,11 @@ export function _filterObject(obj, predicate, mutate = false) {
89
133
  * To skip some key-value pairs - use _mapObject instead.
90
134
  */
91
135
  export function _mapValues(obj, mapper, mutate = false) {
92
- return _objectEntries(obj).reduce((map, [k, v]) => {
136
+ const map = mutate ? obj : {};
137
+ for (const [k, v] of Object.entries(obj)) {
93
138
  map[k] = mapper(k, v, obj);
94
- return map;
95
- }, mutate ? obj : {});
139
+ }
140
+ return map;
96
141
  }
97
142
  /**
98
143
  * _.mapKeys({ 'a': 1, 'b': 2 }, (key, value) => key + value)
@@ -103,10 +148,11 @@ export function _mapValues(obj, mapper, mutate = false) {
103
148
  * To skip some key-value pairs - use _mapObject instead.
104
149
  */
105
150
  export function _mapKeys(obj, mapper) {
106
- return _objectEntries(obj).reduce((map, [k, v]) => {
151
+ const map = {};
152
+ for (const [k, v] of Object.entries(obj)) {
107
153
  map[mapper(k, v, obj)] = v;
108
- return map;
109
- }, {});
154
+ }
155
+ return map;
110
156
  }
111
157
  /**
112
158
  * Maps object through predicate - a function that receives (k, v, obj)
@@ -125,13 +171,14 @@ export function _mapKeys(obj, mapper) {
125
171
  * Non-string keys are passed via String(...)
126
172
  */
127
173
  export function _mapObject(obj, mapper) {
128
- return Object.entries(obj).reduce((map, [k, v]) => {
174
+ const map = {};
175
+ for (const [k, v] of Object.entries(obj)) {
129
176
  const r = mapper(k, v, obj);
130
- if (r !== SKIP) {
131
- map[r[0]] = r[1];
132
- }
133
- return map;
134
- }, {});
177
+ if (r === SKIP)
178
+ continue;
179
+ map[r[0]] = r[1];
180
+ }
181
+ return map;
135
182
  }
136
183
  export function _findKeyByValue(obj, v) {
137
184
  return Object.entries(obj).find(([_, value]) => value === v)?.[0];
@@ -219,7 +266,7 @@ export function _deepTrim(o) {
219
266
  if (typeof o === 'string') {
220
267
  return o.trim();
221
268
  }
222
- else if (typeof o === 'object') {
269
+ if (typeof o === 'object') {
223
270
  Object.keys(o).forEach(k => {
224
271
  o[k] = _deepTrim(o[k]);
225
272
  });
@@ -273,10 +320,11 @@ export function _invertMap(m) {
273
320
  * _get(obj, 'unknown.path') // undefined
274
321
  */
275
322
  export function _get(obj = {}, path = '') {
276
- return path
323
+ return (path
277
324
  .replaceAll(/\[([^\]]+)]/g, '.$1')
278
325
  .split('.')
279
- .reduce((o, p) => o?.[p], obj);
326
+ // eslint-disable-next-line unicorn/no-array-reduce
327
+ .reduce((o, p) => o?.[p], obj));
280
328
  }
281
329
  /**
282
330
  * Sets the value at path of object. If a portion of path doesn’t exist it’s created. Arrays are created for
@@ -299,6 +347,7 @@ export function _set(obj, path, value) {
299
347
  else if (!path.length) {
300
348
  return obj;
301
349
  }
350
+ // eslint-disable-next-line unicorn/no-array-reduce
302
351
  ;
303
352
  path.slice(0, -1).reduce((a, c, i) => Object(a[c]) === a[c] // Does the key exist and is its value an object?
304
353
  ? // Yes: then follow that path
@@ -1,4 +1,3 @@
1
- import { _omit } from '../index';
2
1
  /**
3
2
  * Returns new object with keys sorder in the given order.
4
3
  * All keys that are not listed in `keyOrder` go last.
@@ -6,13 +5,17 @@ import { _omit } from '../index';
6
5
  */
7
6
  export function _sortObject(obj, keyOrder) {
8
7
  const r = {};
9
- keyOrder.forEach(key => {
10
- if (key in obj) {
11
- r[key] = obj[key];
8
+ // First, go over ordered keys
9
+ for (const k of keyOrder) {
10
+ if (k in obj) {
11
+ r[k] = obj[k];
12
12
  }
13
- });
14
- Object.entries(_omit(obj, keyOrder)).forEach(([k, v]) => {
13
+ }
14
+ // Second, go over all other keys
15
+ for (const [k, v] of Object.entries(obj)) {
16
+ if (keyOrder.includes(k))
17
+ continue;
15
18
  r[k] = v;
16
- });
19
+ }
17
20
  return r;
18
21
  }
@@ -1,4 +1,3 @@
1
- import { _isObject } from '..';
2
1
  /**
3
2
  * based on: https://github.com/IndigoUnited/js-deep-sort-object
4
3
  */
@@ -7,14 +6,12 @@ export function _sortObjectDeep(o) {
7
6
  if (Array.isArray(o)) {
8
7
  return o.map(_sortObjectDeep);
9
8
  }
10
- if (_isObject(o)) {
11
- const out = {};
12
- Object.keys(o)
13
- .sort((a, b) => a.localeCompare(b))
14
- .forEach(k => {
15
- out[k] = _sortObjectDeep(o[k]);
16
- });
17
- return out;
9
+ if (o && typeof o === 'object') {
10
+ const r = {};
11
+ for (const k of Object.keys(o).sort((a, b) => a.localeCompare(b))) {
12
+ r[k] = _sortObjectDeep(o[k]);
13
+ }
14
+ return r;
18
15
  }
19
16
  return o;
20
17
  }
@@ -18,7 +18,7 @@ export async function pDelay(ms = 0, value) {
18
18
  *
19
19
  * On abort() - clears the Timeout and immediately resolves the Promise with void.
20
20
  */
21
- export function pDelayFn(ms = 0, fn) {
21
+ export function pDelayFn(ms, fn) {
22
22
  const p = pDefer();
23
23
  const timer = setTimeout(async () => {
24
24
  try {
@@ -1,6 +1,5 @@
1
1
  import { _range } from './array/range';
2
2
  import { _assert } from './error/assert';
3
- import { _isTruthy } from './is.util';
4
3
  /**
5
4
  * Simple Semver implementation.
6
5
  *
@@ -75,35 +74,45 @@ class SemverFactory {
75
74
  * Returns the highest (max) Semver from the array, or undefined if the array is empty.
76
75
  */
77
76
  maxOrUndefined(items) {
78
- return items.length ? this.max(items) : undefined;
77
+ let max;
78
+ for (const item of items) {
79
+ const input = this.fromInputOrUndefined(item);
80
+ if (!max || input?.isAfter(max)) {
81
+ max = input;
82
+ }
83
+ }
84
+ return max;
79
85
  }
80
86
  /**
81
87
  * Returns the highest Semver from the array.
82
88
  * Throws if the array is empty.
83
89
  */
84
90
  max(items) {
85
- const items2 = items.filter(_isTruthy);
86
- _assert(items2.length, 'semver.max called on empty array');
87
- return items2
88
- .map(i => this.fromInput(i))
89
- .reduce((max, item) => (max.isSameOrAfter(item) ? max : item));
91
+ const max = this.maxOrUndefined(items);
92
+ _assert(max, 'semver.max called on empty array');
93
+ return max;
90
94
  }
91
95
  /**
92
96
  * Returns the lowest (min) Semver from the array, or undefined if the array is empty.
93
97
  */
94
98
  minOrUndefined(items) {
95
- return items.length ? this.min(items) : undefined;
99
+ let min;
100
+ for (const item of items) {
101
+ const input = this.fromInputOrUndefined(item);
102
+ if (!min || input?.isBefore(min)) {
103
+ min = input;
104
+ }
105
+ }
106
+ return min;
96
107
  }
97
108
  /**
98
109
  * Returns the lowest Semver from the array.
99
110
  * Throws if the array is empty.
100
111
  */
101
112
  min(items) {
102
- const items2 = items.filter(_isTruthy);
103
- _assert(items2.length, 'semver.min called on empty array');
104
- return items2
105
- .map(i => this.fromInput(i))
106
- .reduce((min, item) => (min.isSameOrBefore(item) ? min : item));
113
+ const min = this.minOrUndefined(items);
114
+ _assert(min, 'semver.min called on empty array');
115
+ return min;
107
116
  }
108
117
  /**
109
118
  * Sorts an array of Semvers in `dir` order (ascending by default).
@@ -1,15 +1,24 @@
1
1
  import { words } from './lodash/words';
2
2
  import { _upperFirst } from './string.util';
3
3
  export function _camelCase(s) {
4
- // return s.replace(/(_\w)/g, m => m[1]!.toUpperCase())
5
- return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => {
4
+ let r = '';
5
+ for (let word of words(s.replaceAll(/['\u2019]/g, ''))) {
6
6
  word = word.toLowerCase();
7
- return result + (index ? _upperFirst(word) : word);
8
- }, '');
7
+ r += r ? _upperFirst(word) : word;
8
+ }
9
+ return r;
9
10
  }
10
11
  export function _snakeCase(s) {
11
- return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '_' : '') + word.toLowerCase(), '');
12
+ let r = '';
13
+ for (const word of words(s.replaceAll(/['\u2019]/g, ''))) {
14
+ r += (r ? '_' : '') + word.toLowerCase();
15
+ }
16
+ return r;
12
17
  }
13
18
  export function _kebabCase(s) {
14
- return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '-' : '') + word.toLowerCase(), '');
19
+ let r = '';
20
+ for (const word of words(s.replaceAll(/['\u2019]/g, ''))) {
21
+ r += (r ? '-' : '') + word.toLowerCase();
22
+ }
23
+ return r;
15
24
  }
@@ -3,6 +3,7 @@
3
3
  import { unicodeWords } from './unicodeWords';
4
4
  const hasUnicodeWord = RegExp.prototype.test.bind(/[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/);
5
5
  /** Used to match words composed of alphanumeric characters. */
6
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: ok
6
7
  const reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
7
8
  function asciiWords(s) {
8
9
  return s.match(reAsciiWord);
@@ -17,7 +17,7 @@ export function _safeJsonStringify(obj, replacer, spaces, cycleReplacer) {
17
17
  function serializer(replacer, cycleReplacer) {
18
18
  const stack = [];
19
19
  const keys = [];
20
- cycleReplacer ?? (cycleReplacer = function (key, value) {
20
+ cycleReplacer ?? (cycleReplacer = (key, value) => {
21
21
  if (stack[0] === value)
22
22
  return '[Circular ~]';
23
23
  return '[Circular ~.' + keys.slice(0, stack.indexOf(value)).join('.') + ']';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.249.0",
3
+ "version": "14.251.0",
4
4
  "scripts": {
5
5
  "prepare": "husky",
6
6
  "build": "dev-lib build-esm-cjs",