@naturalcycles/js-lib 14.149.2 → 14.150.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.
package/dist/index.d.ts CHANGED
@@ -78,6 +78,7 @@ export * from './env';
78
78
  export * from './http/http.model';
79
79
  export * from './http/fetcher';
80
80
  export * from './http/fetcher.model';
81
+ export * from './string/hash.util';
81
82
  export * from './zod/zod.util';
82
83
  export * from './zod/zod.shared.schemas';
83
84
  import { z, ZodSchema, ZodError, ZodIssue } from 'zod';
package/dist/index.js CHANGED
@@ -82,6 +82,7 @@ tslib_1.__exportStar(require("./env"), exports);
82
82
  tslib_1.__exportStar(require("./http/http.model"), exports);
83
83
  tslib_1.__exportStar(require("./http/fetcher"), exports);
84
84
  tslib_1.__exportStar(require("./http/fetcher.model"), exports);
85
+ tslib_1.__exportStar(require("./string/hash.util"), exports);
85
86
  tslib_1.__exportStar(require("./zod/zod.util"), exports);
86
87
  tslib_1.__exportStar(require("./zod/zod.shared.schemas"), exports);
87
88
  const zod_1 = require("zod");
@@ -44,7 +44,7 @@ export declare function _filterObject<T extends AnyObject>(obj: T, predicate: Ob
44
44
  * 'pebbles': { 'user': 'pebbles', 'age': 1 }
45
45
  * }
46
46
  *
47
- * _.mapValues(users, function(o) { return o.age; });
47
+ * _.mapValues(users, function(_key, value) { return value.age; });
48
48
  * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
49
49
  *
50
50
  * // The `_.property` iteratee shorthand.
@@ -93,7 +93,7 @@ exports._filterObject = _filterObject;
93
93
  * 'pebbles': { 'user': 'pebbles', 'age': 1 }
94
94
  * }
95
95
  *
96
- * _.mapValues(users, function(o) { return o.age; });
96
+ * _.mapValues(users, function(_key, value) { return value.age; });
97
97
  * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
98
98
  *
99
99
  * // The `_.property` iteratee shorthand.
@@ -297,7 +297,7 @@ exports._invertMap = _invertMap;
297
297
  */
298
298
  function _get(obj = {}, path = '') {
299
299
  return path
300
- .replace(/\[([^\]]+)]/g, '.$1')
300
+ .replaceAll(/\[([^\]]+)]/g, '.$1')
301
301
  .split('.')
302
302
  .reduce((o, p) => o?.[p], obj);
303
303
  }
@@ -5,17 +5,17 @@ const words_1 = require("./lodash/words");
5
5
  const string_util_1 = require("./string.util");
6
6
  function _camelCase(s) {
7
7
  // return s.replace(/(_\w)/g, m => m[1]!.toUpperCase())
8
- return (0, words_1.words)(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => {
8
+ return (0, words_1.words)(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => {
9
9
  word = word.toLowerCase();
10
10
  return result + (index ? (0, string_util_1._upperFirst)(word) : word);
11
11
  }, '');
12
12
  }
13
13
  exports._camelCase = _camelCase;
14
14
  function _snakeCase(s) {
15
- return (0, words_1.words)(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '_' : '') + word.toLowerCase(), '');
15
+ return (0, words_1.words)(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '_' : '') + word.toLowerCase(), '');
16
16
  }
17
17
  exports._snakeCase = _snakeCase;
18
18
  function _kebabCase(s) {
19
- return (0, words_1.words)(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '-' : '') + word.toLowerCase(), '');
19
+ return (0, words_1.words)(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '-' : '') + word.toLowerCase(), '');
20
20
  }
21
21
  exports._kebabCase = _kebabCase;
@@ -16,19 +16,19 @@ exports.htmlUnescape = exports.htmlEscape = void 0;
16
16
  // Multiple `.replace()` calls are actually faster than using replacer functions
17
17
  function _htmlEscape(s) {
18
18
  return s
19
- .replace(/&/g, '&amp;') // Must happen first or else it will escape other just-escaped characters.
20
- .replace(/"/g, '&quot;')
21
- .replace(/'/g, '&#39;')
22
- .replace(/</g, '&lt;')
23
- .replace(/>/g, '&gt;');
19
+ .replaceAll('&', '&amp;') // Must happen first or else it will escape other just-escaped characters.
20
+ .replaceAll('"', '&quot;')
21
+ .replaceAll("'", '&#39;')
22
+ .replaceAll('<', '&lt;')
23
+ .replaceAll('>', '&gt;');
24
24
  }
25
25
  function _htmlUnescape(html) {
26
26
  return html
27
- .replace(/&gt;/g, '>')
28
- .replace(/&lt;/g, '<')
29
- .replace(/&#0?39;/g, "'")
30
- .replace(/&quot;/g, '"')
31
- .replace(/&amp;/g, '&'); // Must happen last or else it will unescape other characters in the wrong order.
27
+ .replaceAll('&gt;', '>')
28
+ .replaceAll('&lt;', '<')
29
+ .replaceAll(/&#0?39;/g, "'")
30
+ .replaceAll('&quot;', '"')
31
+ .replaceAll('&amp;', '&'); // Must happen last or else it will unescape other characters in the wrong order.
32
32
  }
33
33
  function htmlEscape(strings, ...values) {
34
34
  if (typeof strings === 'string') {
@@ -0,0 +1,38 @@
1
+ import { Integer } from '../types';
2
+ /**
3
+ * Returns hashCode as hex (radix 16).
4
+ *
5
+ * All hash functions here are optimized for:
6
+ *
7
+ * 1. Performance
8
+ * 2. For non-cryptographic use (where accidental collision is not the end-of-the-world)
9
+ * 3. Compact size (32 bits max, versus 128 in md5; presented in less string json-safe characters)
10
+ *
11
+ * Basically, these functions are as simple as they can be, but still "random enough" for
12
+ * normal non-cryptographic use cases.
13
+ *
14
+ * To be run on the ClientSide, as in Node there are plenty of other hash options in `node:crypto`.
15
+ *
16
+ * Perf: it runs ~10 times faster than CryptoJS md5, and ~3 times smaller String hash size (for
17
+ * hashCode64).
18
+ */
19
+ export declare function hashCode16(s: string): string;
20
+ /**
21
+ * Returns hashCode as "radix 36", using "Base36 alphabet".
22
+ * 36 is used, because it's the maximum "radix" that Number.toString() can take.
23
+ *
24
+ * See the hashCode16 for full description.
25
+ */
26
+ export declare function hashCode36(s: string): string;
27
+ /**
28
+ * Returns hashCode as "radix 64", using Base64url alphabet.
29
+ * See the hashCode16 for full description.
30
+ */
31
+ export declare function hashCode64(s: string): string;
32
+ /**
33
+ * Generates a stable integer hashCode for a given String.
34
+ * Matches Java implementation (they say), except it ensures a positive Integer
35
+ * by adding 2147483647 + 1 to the end result.
36
+ * Source: https://stackoverflow.com/a/33647870/4919972
37
+ */
38
+ export declare function hashCode(s: string): Integer;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hashCode = exports.hashCode64 = exports.hashCode36 = exports.hashCode16 = void 0;
4
+ const BASE62 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
5
+ // const BASE64 = BASE62 + '+/'
6
+ const BASE64URL = BASE62 + '-_';
7
+ /**
8
+ * Returns hashCode as hex (radix 16).
9
+ *
10
+ * All hash functions here are optimized for:
11
+ *
12
+ * 1. Performance
13
+ * 2. For non-cryptographic use (where accidental collision is not the end-of-the-world)
14
+ * 3. Compact size (32 bits max, versus 128 in md5; presented in less string json-safe characters)
15
+ *
16
+ * Basically, these functions are as simple as they can be, but still "random enough" for
17
+ * normal non-cryptographic use cases.
18
+ *
19
+ * To be run on the ClientSide, as in Node there are plenty of other hash options in `node:crypto`.
20
+ *
21
+ * Perf: it runs ~10 times faster than CryptoJS md5, and ~3 times smaller String hash size (for
22
+ * hashCode64).
23
+ */
24
+ function hashCode16(s) {
25
+ return hashCode(s).toString(16);
26
+ }
27
+ exports.hashCode16 = hashCode16;
28
+ /**
29
+ * Returns hashCode as "radix 36", using "Base36 alphabet".
30
+ * 36 is used, because it's the maximum "radix" that Number.toString() can take.
31
+ *
32
+ * See the hashCode16 for full description.
33
+ */
34
+ function hashCode36(s) {
35
+ return hashCode(s).toString(36);
36
+ }
37
+ exports.hashCode36 = hashCode36;
38
+ /**
39
+ * Returns hashCode as "radix 64", using Base64url alphabet.
40
+ * See the hashCode16 for full description.
41
+ */
42
+ function hashCode64(s) {
43
+ return numberToBase(hashCode(s), BASE64URL);
44
+ }
45
+ exports.hashCode64 = hashCode64;
46
+ /**
47
+ * Generates a stable integer hashCode for a given String.
48
+ * Matches Java implementation (they say), except it ensures a positive Integer
49
+ * by adding 2147483647 + 1 to the end result.
50
+ * Source: https://stackoverflow.com/a/33647870/4919972
51
+ */
52
+ function hashCode(s) {
53
+ let hash = 0;
54
+ let i = 0;
55
+ const len = s.length;
56
+ while (i < len) {
57
+ // eslint-disable-next-line no-bitwise, unicorn/prefer-math-trunc, unicorn/prefer-code-point
58
+ hash = ((hash << 5) - hash + s.charCodeAt(i++)) << 0;
59
+ }
60
+ return hash + 2147483647 + 1;
61
+ }
62
+ exports.hashCode = hashCode;
63
+ /**
64
+ * Source: https://gist.github.com/alkaruno/b84162bae5115f4ca99b
65
+ */
66
+ function numberToBase(n, alphabet) {
67
+ const alen = alphabet.length;
68
+ let result = '';
69
+ do {
70
+ result = alphabet.charAt(n % alen) + result;
71
+ n = Math.floor(n / alen) - 1;
72
+ } while (n > -1);
73
+ return result;
74
+ }
@@ -52,9 +52,9 @@ function pupa(template, data, opt = {}) {
52
52
  // The regex tries to match either a number inside `{{ }}` or a valid JS identifier or key path.
53
53
  const doubleBraceRegex = /{{(\d+|[a-z$_][\w\-$]*?(?:\.[\w\-$]*?)*?)}}/gi;
54
54
  if (doubleBraceRegex.test(template)) {
55
- template = template.replace(doubleBraceRegex, composeHtmlEscape(replace));
55
+ template = template.replaceAll(doubleBraceRegex, composeHtmlEscape(replace));
56
56
  }
57
57
  const braceRegex = /{(\d+|[a-z$_][\w\-$]*?(?:\.[\w\-$]*?)*?)}/gi;
58
- return template.replace(braceRegex, replace);
58
+ return template.replaceAll(braceRegex, replace);
59
59
  }
60
60
  exports.pupa = pupa;
@@ -35,13 +35,6 @@ export declare function _substringAfterLast(s: string, delimiter: string): strin
35
35
  * // `someFile`
36
36
  */
37
37
  export declare function _substringBetweenLast(s: string, leftDelimiter: string, rightDelimiter: string): string;
38
- /**
39
- * Polyfill for es2021 `String.prototype.replaceAll`.
40
- * Uses regex implementation that's a bit faster than another popular "split/join" implementation.
41
- *
42
- * Based on: https://stackoverflow.com/a/1144788/4919972
43
- */
44
- export declare function _replaceAll(s: string, find: string, replaceWith: string): string;
45
38
  /**
46
39
  * Converts `\n` (aka new-line) to `<br>`, to be presented in HTML.
47
40
  * Keeps `\n`, so if it's printed in non-HTML environment it still looks ok-ish.
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._nl2br = exports._replaceAll = exports._substringBetweenLast = exports._substringAfterLast = exports._substringAfter = exports._substringBeforeLast = exports._substringBefore = exports._truncateMiddle = exports._truncate = exports._removeWhitespace = exports._split = exports._lowerFirst = exports._upperFirst = exports._capitalize = void 0;
3
+ exports._nl2br = exports._substringBetweenLast = exports._substringAfterLast = exports._substringAfter = exports._substringBeforeLast = exports._substringBefore = exports._truncateMiddle = exports._truncate = exports._removeWhitespace = exports._split = exports._lowerFirst = exports._upperFirst = exports._capitalize = void 0;
4
4
  /**
5
5
  * Converts the first character of string to upper case and the remaining to lower case.
6
6
  */
@@ -29,7 +29,7 @@ function _split(str, separator, limit) {
29
29
  }
30
30
  exports._split = _split;
31
31
  function _removeWhitespace(s) {
32
- return s.replace(/\s/g, '');
32
+ return s.replaceAll(/\s/g, '');
33
33
  }
34
34
  exports._removeWhitespace = _removeWhitespace;
35
35
  /**
@@ -92,21 +92,11 @@ function _substringBetweenLast(s, leftDelimiter, rightDelimiter) {
92
92
  return _substringBefore(_substringAfterLast(s, leftDelimiter), rightDelimiter);
93
93
  }
94
94
  exports._substringBetweenLast = _substringBetweenLast;
95
- /**
96
- * Polyfill for es2021 `String.prototype.replaceAll`.
97
- * Uses regex implementation that's a bit faster than another popular "split/join" implementation.
98
- *
99
- * Based on: https://stackoverflow.com/a/1144788/4919972
100
- */
101
- function _replaceAll(s, find, replaceWith) {
102
- return s.replace(new RegExp(find, 'g'), replaceWith);
103
- }
104
- exports._replaceAll = _replaceAll;
105
95
  /**
106
96
  * Converts `\n` (aka new-line) to `<br>`, to be presented in HTML.
107
97
  * Keeps `\n`, so if it's printed in non-HTML environment it still looks ok-ish.
108
98
  */
109
99
  function _nl2br(s) {
110
- return s.replace(/\n/g, '<br>\n');
100
+ return s.replaceAll('\n', '<br>\n');
111
101
  }
112
102
  exports._nl2br = _nl2br;
package/dist-esm/index.js CHANGED
@@ -78,6 +78,7 @@ export * from './env';
78
78
  export * from './http/http.model';
79
79
  export * from './http/fetcher';
80
80
  export * from './http/fetcher.model';
81
+ export * from './string/hash.util';
81
82
  export * from './zod/zod.util';
82
83
  export * from './zod/zod.shared.schemas';
83
84
  import { z, ZodSchema, ZodError } from 'zod';
@@ -82,7 +82,7 @@ export function _filterObject(obj, predicate, mutate = false) {
82
82
  * 'pebbles': { 'user': 'pebbles', 'age': 1 }
83
83
  * }
84
84
  *
85
- * _.mapValues(users, function(o) { return o.age; });
85
+ * _.mapValues(users, function(_key, value) { return value.age; });
86
86
  * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
87
87
  *
88
88
  * // The `_.property` iteratee shorthand.
@@ -275,7 +275,7 @@ export function _invertMap(m) {
275
275
  */
276
276
  export function _get(obj = {}, path = '') {
277
277
  return path
278
- .replace(/\[([^\]]+)]/g, '.$1')
278
+ .replaceAll(/\[([^\]]+)]/g, '.$1')
279
279
  .split('.')
280
280
  .reduce((o, p) => o === null || o === void 0 ? void 0 : o[p], obj);
281
281
  }
@@ -2,14 +2,14 @@ import { words } from './lodash/words';
2
2
  import { _upperFirst } from './string.util';
3
3
  export function _camelCase(s) {
4
4
  // return s.replace(/(_\w)/g, m => m[1]!.toUpperCase())
5
- return words(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => {
5
+ return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => {
6
6
  word = word.toLowerCase();
7
7
  return result + (index ? _upperFirst(word) : word);
8
8
  }, '');
9
9
  }
10
10
  export function _snakeCase(s) {
11
- return words(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '_' : '') + word.toLowerCase(), '');
11
+ return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '_' : '') + word.toLowerCase(), '');
12
12
  }
13
13
  export function _kebabCase(s) {
14
- return words(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '-' : '') + word.toLowerCase(), '');
14
+ return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => result + (index ? '-' : '') + word.toLowerCase(), '');
15
15
  }
@@ -13,19 +13,19 @@ Reasons:
13
13
  // Multiple `.replace()` calls are actually faster than using replacer functions
14
14
  function _htmlEscape(s) {
15
15
  return s
16
- .replace(/&/g, '&amp;') // Must happen first or else it will escape other just-escaped characters.
17
- .replace(/"/g, '&quot;')
18
- .replace(/'/g, '&#39;')
19
- .replace(/</g, '&lt;')
20
- .replace(/>/g, '&gt;');
16
+ .replaceAll('&', '&amp;') // Must happen first or else it will escape other just-escaped characters.
17
+ .replaceAll('"', '&quot;')
18
+ .replaceAll("'", '&#39;')
19
+ .replaceAll('<', '&lt;')
20
+ .replaceAll('>', '&gt;');
21
21
  }
22
22
  function _htmlUnescape(html) {
23
23
  return html
24
- .replace(/&gt;/g, '>')
25
- .replace(/&lt;/g, '<')
26
- .replace(/&#0?39;/g, "'")
27
- .replace(/&quot;/g, '"')
28
- .replace(/&amp;/g, '&'); // Must happen last or else it will unescape other characters in the wrong order.
24
+ .replaceAll('&gt;', '>')
25
+ .replaceAll('&lt;', '<')
26
+ .replaceAll(/&#0?39;/g, "'")
27
+ .replaceAll('&quot;', '"')
28
+ .replaceAll('&amp;', '&'); // Must happen last or else it will unescape other characters in the wrong order.
29
29
  }
30
30
  export function htmlEscape(strings, ...values) {
31
31
  if (typeof strings === 'string') {
@@ -0,0 +1,67 @@
1
+ const BASE62 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
2
+ // const BASE64 = BASE62 + '+/'
3
+ const BASE64URL = BASE62 + '-_';
4
+ /**
5
+ * Returns hashCode as hex (radix 16).
6
+ *
7
+ * All hash functions here are optimized for:
8
+ *
9
+ * 1. Performance
10
+ * 2. For non-cryptographic use (where accidental collision is not the end-of-the-world)
11
+ * 3. Compact size (32 bits max, versus 128 in md5; presented in less string json-safe characters)
12
+ *
13
+ * Basically, these functions are as simple as they can be, but still "random enough" for
14
+ * normal non-cryptographic use cases.
15
+ *
16
+ * To be run on the ClientSide, as in Node there are plenty of other hash options in `node:crypto`.
17
+ *
18
+ * Perf: it runs ~10 times faster than CryptoJS md5, and ~3 times smaller String hash size (for
19
+ * hashCode64).
20
+ */
21
+ export function hashCode16(s) {
22
+ return hashCode(s).toString(16);
23
+ }
24
+ /**
25
+ * Returns hashCode as "radix 36", using "Base36 alphabet".
26
+ * 36 is used, because it's the maximum "radix" that Number.toString() can take.
27
+ *
28
+ * See the hashCode16 for full description.
29
+ */
30
+ export function hashCode36(s) {
31
+ return hashCode(s).toString(36);
32
+ }
33
+ /**
34
+ * Returns hashCode as "radix 64", using Base64url alphabet.
35
+ * See the hashCode16 for full description.
36
+ */
37
+ export function hashCode64(s) {
38
+ return numberToBase(hashCode(s), BASE64URL);
39
+ }
40
+ /**
41
+ * Generates a stable integer hashCode for a given String.
42
+ * Matches Java implementation (they say), except it ensures a positive Integer
43
+ * by adding 2147483647 + 1 to the end result.
44
+ * Source: https://stackoverflow.com/a/33647870/4919972
45
+ */
46
+ export function hashCode(s) {
47
+ let hash = 0;
48
+ let i = 0;
49
+ const len = s.length;
50
+ while (i < len) {
51
+ // eslint-disable-next-line no-bitwise, unicorn/prefer-math-trunc, unicorn/prefer-code-point
52
+ hash = ((hash << 5) - hash + s.charCodeAt(i++)) << 0;
53
+ }
54
+ return hash + 2147483647 + 1;
55
+ }
56
+ /**
57
+ * Source: https://gist.github.com/alkaruno/b84162bae5115f4ca99b
58
+ */
59
+ function numberToBase(n, alphabet) {
60
+ const alen = alphabet.length;
61
+ let result = '';
62
+ do {
63
+ result = alphabet.charAt(n % alen) + result;
64
+ n = Math.floor(n / alen) - 1;
65
+ } while (n > -1);
66
+ return result;
67
+ }
@@ -48,8 +48,8 @@ export function pupa(template, data, opt = {}) {
48
48
  // The regex tries to match either a number inside `{{ }}` or a valid JS identifier or key path.
49
49
  const doubleBraceRegex = /{{(\d+|[a-z$_][\w\-$]*?(?:\.[\w\-$]*?)*?)}}/gi;
50
50
  if (doubleBraceRegex.test(template)) {
51
- template = template.replace(doubleBraceRegex, composeHtmlEscape(replace));
51
+ template = template.replaceAll(doubleBraceRegex, composeHtmlEscape(replace));
52
52
  }
53
53
  const braceRegex = /{(\d+|[a-z$_][\w\-$]*?(?:\.[\w\-$]*?)*?)}/gi;
54
- return template.replace(braceRegex, replace);
54
+ return template.replaceAll(braceRegex, replace);
55
55
  }
@@ -22,7 +22,7 @@ export function _split(str, separator, limit) {
22
22
  return [...parts.slice(0, limit - 1), parts.slice(limit - 1).join(separator)];
23
23
  }
24
24
  export function _removeWhitespace(s) {
25
- return s.replace(/\s/g, '');
25
+ return s.replaceAll(/\s/g, '');
26
26
  }
27
27
  /**
28
28
  * _.truncate('hi-diddly-ho there, neighborino')
@@ -77,19 +77,10 @@ export function _substringAfterLast(s, delimiter) {
77
77
  export function _substringBetweenLast(s, leftDelimiter, rightDelimiter) {
78
78
  return _substringBefore(_substringAfterLast(s, leftDelimiter), rightDelimiter);
79
79
  }
80
- /**
81
- * Polyfill for es2021 `String.prototype.replaceAll`.
82
- * Uses regex implementation that's a bit faster than another popular "split/join" implementation.
83
- *
84
- * Based on: https://stackoverflow.com/a/1144788/4919972
85
- */
86
- export function _replaceAll(s, find, replaceWith) {
87
- return s.replace(new RegExp(find, 'g'), replaceWith);
88
- }
89
80
  /**
90
81
  * Converts `\n` (aka new-line) to `<br>`, to be presented in HTML.
91
82
  * Keeps `\n`, so if it's printed in non-HTML environment it still looks ok-ish.
92
83
  */
93
84
  export function _nl2br(s) {
94
- return s.replace(/\n/g, '<br>\n');
85
+ return s.replaceAll('\n', '<br>\n');
95
86
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/js-lib",
3
- "version": "14.149.2",
3
+ "version": "14.150.0",
4
4
  "scripts": {
5
5
  "prepare": "husky install",
6
6
  "build-prod": "build-prod-esm-cjs",
@@ -16,7 +16,9 @@
16
16
  "@naturalcycles/dev-lib": "^13.0.1",
17
17
  "@naturalcycles/nodejs-lib": "^12.33.4",
18
18
  "@naturalcycles/time-lib": "^3.5.1",
19
- "@types/node": "^18.0.0",
19
+ "@types/crypto-js": "^4.1.1",
20
+ "@types/node": "^20.1.0",
21
+ "crypto-js": "^4.1.1",
20
22
  "jest": "^29.0.0",
21
23
  "prettier": "^2.1.2",
22
24
  "rxjs": "^7.0.1",
package/src/index.ts CHANGED
@@ -78,6 +78,7 @@ export * from './env'
78
78
  export * from './http/http.model'
79
79
  export * from './http/fetcher'
80
80
  export * from './http/fetcher.model'
81
+ export * from './string/hash.util'
81
82
  export * from './zod/zod.util'
82
83
  export * from './zod/zod.shared.schemas'
83
84
  import { z, ZodSchema, ZodError, ZodIssue } from 'zod'
@@ -111,7 +111,7 @@ export function _filterObject<T extends AnyObject>(
111
111
  * 'pebbles': { 'user': 'pebbles', 'age': 1 }
112
112
  * }
113
113
  *
114
- * _.mapValues(users, function(o) { return o.age; });
114
+ * _.mapValues(users, function(_key, value) { return value.age; });
115
115
  * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed)
116
116
  *
117
117
  * // The `_.property` iteratee shorthand.
@@ -322,7 +322,7 @@ export function _invertMap<K, V>(m: ReadonlyMap<K, V>): Map<V, K> {
322
322
  */
323
323
  export function _get<T extends AnyObject>(obj = {} as T, path = ''): unknown {
324
324
  return path
325
- .replace(/\[([^\]]+)]/g, '.$1')
325
+ .replaceAll(/\[([^\]]+)]/g, '.$1')
326
326
  .split('.')
327
327
  .reduce((o, p) => o?.[p], obj)
328
328
  }
@@ -3,21 +3,21 @@ import { _upperFirst } from './string.util'
3
3
 
4
4
  export function _camelCase(s: string): string {
5
5
  // return s.replace(/(_\w)/g, m => m[1]!.toUpperCase())
6
- return words(s.replace(/['\u2019]/g, '')).reduce((result, word, index) => {
6
+ return words(s.replaceAll(/['\u2019]/g, '')).reduce((result, word, index) => {
7
7
  word = word.toLowerCase()
8
8
  return result + (index ? _upperFirst(word) : word)
9
9
  }, '')
10
10
  }
11
11
 
12
12
  export function _snakeCase(s: string): string {
13
- return words(s.replace(/['\u2019]/g, '')).reduce(
13
+ return words(s.replaceAll(/['\u2019]/g, '')).reduce(
14
14
  (result, word, index) => result + (index ? '_' : '') + word.toLowerCase(),
15
15
  '',
16
16
  )
17
17
  }
18
18
 
19
19
  export function _kebabCase(s: string): string {
20
- return words(s.replace(/['\u2019]/g, '')).reduce(
20
+ return words(s.replaceAll(/['\u2019]/g, '')).reduce(
21
21
  (result, word, index) => result + (index ? '-' : '') + word.toLowerCase(),
22
22
  '',
23
23
  )
@@ -14,20 +14,20 @@ Reasons:
14
14
  // Multiple `.replace()` calls are actually faster than using replacer functions
15
15
  function _htmlEscape(s: string): string {
16
16
  return s
17
- .replace(/&/g, '&amp;') // Must happen first or else it will escape other just-escaped characters.
18
- .replace(/"/g, '&quot;')
19
- .replace(/'/g, '&#39;')
20
- .replace(/</g, '&lt;')
21
- .replace(/>/g, '&gt;')
17
+ .replaceAll('&', '&amp;') // Must happen first or else it will escape other just-escaped characters.
18
+ .replaceAll('"', '&quot;')
19
+ .replaceAll("'", '&#39;')
20
+ .replaceAll('<', '&lt;')
21
+ .replaceAll('>', '&gt;')
22
22
  }
23
23
 
24
24
  function _htmlUnescape(html: string): string {
25
25
  return html
26
- .replace(/&gt;/g, '>')
27
- .replace(/&lt;/g, '<')
28
- .replace(/&#0?39;/g, "'")
29
- .replace(/&quot;/g, '"')
30
- .replace(/&amp;/g, '&') // Must happen last or else it will unescape other characters in the wrong order.
26
+ .replaceAll('&gt;', '>')
27
+ .replaceAll('&lt;', '<')
28
+ .replaceAll(/&#0?39;/g, "'")
29
+ .replaceAll('&quot;', '"')
30
+ .replaceAll('&amp;', '&') // Must happen last or else it will unescape other characters in the wrong order.
31
31
  }
32
32
 
33
33
  export function htmlEscape(strings: string | TemplateStringsArray, ...values: any[]): string {
@@ -0,0 +1,76 @@
1
+ import { Integer } from '../types'
2
+
3
+ const BASE62 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
4
+ // const BASE64 = BASE62 + '+/'
5
+ const BASE64URL = BASE62 + '-_'
6
+
7
+ /**
8
+ * Returns hashCode as hex (radix 16).
9
+ *
10
+ * All hash functions here are optimized for:
11
+ *
12
+ * 1. Performance
13
+ * 2. For non-cryptographic use (where accidental collision is not the end-of-the-world)
14
+ * 3. Compact size (32 bits max, versus 128 in md5; presented in less string json-safe characters)
15
+ *
16
+ * Basically, these functions are as simple as they can be, but still "random enough" for
17
+ * normal non-cryptographic use cases.
18
+ *
19
+ * To be run on the ClientSide, as in Node there are plenty of other hash options in `node:crypto`.
20
+ *
21
+ * Perf: it runs ~10 times faster than CryptoJS md5, and ~3 times smaller String hash size (for
22
+ * hashCode64).
23
+ */
24
+ export function hashCode16(s: string): string {
25
+ return hashCode(s).toString(16)
26
+ }
27
+
28
+ /**
29
+ * Returns hashCode as "radix 36", using "Base36 alphabet".
30
+ * 36 is used, because it's the maximum "radix" that Number.toString() can take.
31
+ *
32
+ * See the hashCode16 for full description.
33
+ */
34
+ export function hashCode36(s: string): string {
35
+ return hashCode(s).toString(36)
36
+ }
37
+
38
+ /**
39
+ * Returns hashCode as "radix 64", using Base64url alphabet.
40
+ * See the hashCode16 for full description.
41
+ */
42
+ export function hashCode64(s: string): string {
43
+ return numberToBase(hashCode(s), BASE64URL)
44
+ }
45
+
46
+ /**
47
+ * Generates a stable integer hashCode for a given String.
48
+ * Matches Java implementation (they say), except it ensures a positive Integer
49
+ * by adding 2147483647 + 1 to the end result.
50
+ * Source: https://stackoverflow.com/a/33647870/4919972
51
+ */
52
+ export function hashCode(s: string): Integer {
53
+ let hash = 0
54
+ let i = 0
55
+ const len = s.length
56
+ while (i < len) {
57
+ // eslint-disable-next-line no-bitwise, unicorn/prefer-math-trunc, unicorn/prefer-code-point
58
+ hash = ((hash << 5) - hash + s.charCodeAt(i++)) << 0
59
+ }
60
+ return hash + 2147483647 + 1
61
+ }
62
+
63
+ /**
64
+ * Source: https://gist.github.com/alkaruno/b84162bae5115f4ca99b
65
+ */
66
+ function numberToBase(n: number, alphabet: string): string {
67
+ const alen = alphabet.length
68
+ let result = ''
69
+
70
+ do {
71
+ result = alphabet.charAt(n % alen) + result
72
+ n = Math.floor(n / alen) - 1
73
+ } while (n > -1)
74
+
75
+ return result
76
+ }
@@ -77,10 +77,10 @@ export function pupa(template: string, data: any[] | AnyObject, opt: PupaOptions
77
77
  const doubleBraceRegex = /{{(\d+|[a-z$_][\w\-$]*?(?:\.[\w\-$]*?)*?)}}/gi
78
78
 
79
79
  if (doubleBraceRegex.test(template)) {
80
- template = template.replace(doubleBraceRegex, composeHtmlEscape(replace))
80
+ template = template.replaceAll(doubleBraceRegex, composeHtmlEscape(replace))
81
81
  }
82
82
 
83
83
  const braceRegex = /{(\d+|[a-z$_][\w\-$]*?(?:\.[\w\-$]*?)*?)}/gi
84
84
 
85
- return template.replace(braceRegex, replace)
85
+ return template.replaceAll(braceRegex, replace)
86
86
  }
@@ -25,7 +25,7 @@ export function _split(str: string, separator: string, limit: number): string[]
25
25
  }
26
26
 
27
27
  export function _removeWhitespace(s: string): string {
28
- return s.replace(/\s/g, '')
28
+ return s.replaceAll(/\s/g, '')
29
29
  }
30
30
 
31
31
  /**
@@ -90,20 +90,10 @@ export function _substringBetweenLast(
90
90
  return _substringBefore(_substringAfterLast(s, leftDelimiter), rightDelimiter)
91
91
  }
92
92
 
93
- /**
94
- * Polyfill for es2021 `String.prototype.replaceAll`.
95
- * Uses regex implementation that's a bit faster than another popular "split/join" implementation.
96
- *
97
- * Based on: https://stackoverflow.com/a/1144788/4919972
98
- */
99
- export function _replaceAll(s: string, find: string, replaceWith: string): string {
100
- return s.replace(new RegExp(find, 'g'), replaceWith)
101
- }
102
-
103
93
  /**
104
94
  * Converts `\n` (aka new-line) to `<br>`, to be presented in HTML.
105
95
  * Keeps `\n`, so if it's printed in non-HTML environment it still looks ok-ish.
106
96
  */
107
97
  export function _nl2br(s: string): string {
108
- return s.replace(/\n/g, '<br>\n')
98
+ return s.replaceAll('\n', '<br>\n')
109
99
  }