@valkyriestudios/utils 12.17.1 → 12.18.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/README.md CHANGED
@@ -633,6 +633,9 @@ equal(new RegExp(/ab+c/, 'i'), /ab+c/i); // TRUE
633
633
  ```
634
634
 
635
635
  ### function
636
+ - **debounce(val:Fn, wait:number)**
637
+ Wrap a function in a debounce proxy that waits for X uninterrupted milliseconds before running callback function
638
+
636
639
  - **isFunction(val:unknown)**
637
640
  Check if a variable is a Function
638
641
 
@@ -923,13 +926,16 @@ isNotEmptyString(' ', false); // TRUE
923
926
  isNotEmptyString('Hi'); // TRUE
924
927
  ```
925
928
 
926
- - **shorten(val:any, length:integer, postfix:string=...)**
929
+ - **shorten(val:any, length:integer, postfix:string=..., truncate_words=true)**
927
930
  Shorten a string and add a postfix if string went over length
928
931
  ```typescript
929
932
  shorten('To the moon and beyond', 11, '..'); // 'To the moon..'
930
933
  shorten('Hi', 250); // 'Hi'
931
934
  shorten('To the moon and beyond'); // 'To the moon...'
932
935
  shorten('To the moon and beyond', 11, ' '); // 'To the moon '
936
+
937
+ /* For when you don't want words to be truncated mid-word */
938
+ shorten('To the moon and beyond', 11, '...', false);
933
939
  ```
934
940
 
935
941
  - **humanizeBytes(val:number|string)**
package/array/dedupe.js CHANGED
@@ -2,35 +2,53 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.dedupe = dedupe;
4
4
  exports.default = dedupe;
5
- const fnv1A_1 = require("../hash/fnv1A");
5
+ const REPL_NAN = 'nan';
6
+ const REPL_TRUE = 'true';
7
+ const REPL_FALSE = 'false';
8
+ const REPL_UNDEF = 'undefined';
9
+ const REPL_NULL = 'null';
10
+ function getTypeString(el) {
11
+ switch (typeof el) {
12
+ case 'string':
13
+ return el;
14
+ case 'number':
15
+ return Number.isNaN(el) || !Number.isFinite(el) ? REPL_NAN : String(el);
16
+ case 'boolean':
17
+ return el ? REPL_TRUE : REPL_FALSE;
18
+ case 'undefined':
19
+ return REPL_UNDEF;
20
+ case 'object':
21
+ if (el === null) {
22
+ return REPL_NULL;
23
+ }
24
+ else if (Array.isArray(el) || el.toString() === '[object Object]') {
25
+ return JSON.stringify(el);
26
+ }
27
+ else if (el instanceof RegExp) {
28
+ return el.toString();
29
+ }
30
+ else if (el instanceof Date) {
31
+ return String(el.getTime());
32
+ }
33
+ }
34
+ return '';
35
+ }
6
36
  function dedupe(val, opts) {
7
37
  if (!Array.isArray(val))
8
38
  return [];
9
- const FILTER_FN = typeof opts?.filter_fn === 'function' ? opts?.filter_fn : false;
39
+ const FILTER_FN = opts?.filter_fn;
10
40
  const set = new Set();
11
41
  const acc = [];
12
42
  let hash;
13
43
  const len = val.length;
14
- if (FILTER_FN) {
15
- for (let i = 0; i < len; i++) {
16
- const el = val[i];
17
- if (FILTER_FN && !FILTER_FN(el))
18
- continue;
19
- hash = (0, fnv1A_1.fnv1A)(el);
20
- if (!set.has(hash)) {
21
- set.add(hash);
22
- acc.push(el);
23
- }
24
- }
25
- }
26
- else {
27
- for (let i = 0; i < len; i++) {
28
- const el = val[i];
29
- hash = (0, fnv1A_1.fnv1A)(el);
30
- if (!set.has(hash)) {
31
- set.add(hash);
32
- acc.push(el);
33
- }
44
+ for (let i = 0; i < len; i++) {
45
+ const el = val[i];
46
+ if (FILTER_FN && !FILTER_FN(el))
47
+ continue;
48
+ hash = getTypeString(el);
49
+ if (!set.has(hash)) {
50
+ set.add(hash);
51
+ acc.push(el);
34
52
  }
35
53
  }
36
54
  return acc;
package/array/groupBy.js CHANGED
@@ -23,11 +23,12 @@ function groupBy(arr, handler) {
23
23
  key = n_handler(el);
24
24
  if (key === undefined || (typeof key === 'string' && !key.length))
25
25
  key = FALLBACK;
26
- if (Array.isArray(acc[key])) {
26
+ if (!acc[key]) {
27
+ acc[key] = [el];
28
+ }
29
+ else {
27
30
  acc[key].push(el);
28
- continue;
29
31
  }
30
- acc[key] = [el];
31
32
  }
32
33
  return acc;
33
34
  }
package/array/mapKey.js CHANGED
@@ -3,19 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mapKey = mapKey;
4
4
  exports.default = mapKey;
5
5
  function mapKey(arr, key, opts) {
6
- if ((!Array.isArray(arr) || !arr.length) ||
7
- typeof key !== 'string')
6
+ if (!Array.isArray(arr) || typeof key !== 'string')
8
7
  return {};
9
8
  const key_s = key.trim();
10
9
  if (!key_s.length)
11
10
  return {};
11
+ const len = arr.length;
12
+ if (!len)
13
+ return {};
12
14
  const MERGE = opts?.merge === true;
13
15
  const map = {};
14
- for (let i = 0; i < arr.length; i++) {
16
+ for (let i = 0; i < len; i++) {
15
17
  const el = arr[i];
16
- if (el?.[key_s] === undefined)
18
+ const el_key = el?.[key_s];
19
+ if (el_key === undefined)
17
20
  continue;
18
- map[el[key_s]] = MERGE && map.hasOwnProperty(el[key_s]) ? { ...map[el[key_s]], ...el } : el;
21
+ map[el_key] = MERGE && el_key in map ? { ...map[el_key], ...el } : el;
19
22
  }
20
23
  return map;
21
24
  }
package/array/sort.d.ts CHANGED
@@ -60,5 +60,5 @@ type sortByFunction = (el: Record<string, any>) => string;
60
60
  */
61
61
  declare function sort<T extends {
62
62
  [key: string]: any;
63
- }[]>(arr: T, by: string | sortByFunction, dir?: 'asc' | 'desc', opts?: sortOptions<T>): T;
63
+ }>(arr: T[], by: string | sortByFunction, dir?: 'asc' | 'desc', opts?: sortOptions<T>): T[];
64
64
  export { sort, sort as default };
package/array/sort.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sort = sort;
4
4
  exports.default = sort;
5
- const isNotEmpty_1 = require("../object/isNotEmpty");
5
+ const is_1 = require("../object/is");
6
6
  const INSERTION_SORT_THRESHOLD = 10;
7
7
  function partition(arr, low, high) {
8
8
  const pivot = arr[(low + high) >> 1][0];
@@ -24,9 +24,9 @@ function partition(arr, low, high) {
24
24
  return i;
25
25
  }
26
26
  function quickSort(arr) {
27
- const stack = [{ low: 0, high: arr.length - 1 }];
27
+ const stack = [[0, arr.length - 1]];
28
28
  while (stack.length) {
29
- const { low, high } = stack.pop();
29
+ const [low, high] = stack.pop();
30
30
  if (high - low <= INSERTION_SORT_THRESHOLD) {
31
31
  for (let i = low + 1; i <= high; i++) {
32
32
  const key = arr[i];
@@ -41,22 +41,24 @@ function quickSort(arr) {
41
41
  else {
42
42
  const p = partition(arr, low, high);
43
43
  if (p - 1 > low)
44
- stack.push({ low, high: p - 1 });
44
+ stack.push([low, p - 1]);
45
45
  if (p < high)
46
- stack.push({ low: p, high });
46
+ stack.push([p, high]);
47
47
  }
48
48
  }
49
- return arr;
50
49
  }
51
50
  function sort(arr, by, dir = 'asc', opts) {
52
- if (!Array.isArray(arr) || !arr.length)
51
+ if (!Array.isArray(arr))
52
+ return [];
53
+ const len = arr.length;
54
+ if (!len)
53
55
  return [];
54
56
  const NOKEY_HIDE = opts?.nokey_hide === true;
55
57
  const NOKEY_AT_END = opts?.nokey_atend !== false;
56
- let FILTER_FN = isNotEmpty_1.isNotEmptyObject;
58
+ let FILTER_FN = is_1.isObject;
57
59
  if (typeof opts?.filter_fn === 'function') {
58
60
  const fn = opts.filter_fn;
59
- FILTER_FN = (el => (0, isNotEmpty_1.isNotEmptyObject)(el) && fn(el));
61
+ FILTER_FN = (el => (0, is_1.isObject)(el) && fn(el));
60
62
  }
61
63
  const prepared_arr = [];
62
64
  const nokey_arr = [];
@@ -64,19 +66,22 @@ function sort(arr, by, dir = 'asc', opts) {
64
66
  const by_s = by.trim();
65
67
  if (!by_s.length)
66
68
  throw new Error('Sort by as string should contain content');
67
- for (const el of arr) {
69
+ for (let i = 0; i < len; i++) {
70
+ const el = arr[i];
68
71
  if (!FILTER_FN(el))
69
72
  continue;
70
- if (el?.[by_s] === undefined) {
73
+ const key = el?.[by_s];
74
+ if (key === undefined) {
71
75
  nokey_arr.push(el);
72
76
  }
73
77
  else {
74
- prepared_arr.push([el[by_s], el]);
78
+ prepared_arr.push([key, el]);
75
79
  }
76
80
  }
77
81
  }
78
82
  else if (typeof by === 'function') {
79
- for (const el of arr) {
83
+ for (let i = 0; i < len; i++) {
84
+ const el = arr[i];
80
85
  if (!FILTER_FN(el))
81
86
  continue;
82
87
  const key = by(el);
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Debounce a function
3
+ *
4
+ * Take Note, the debounced function also contains a "cancel" and "flush" method
5
+ * "cancel": Allows clearing the timeout
6
+ * "flush": Allows immediately executing the debounced function if pending
7
+ *
8
+ * Example Usage:
9
+ *
10
+ * const log = (message: string) => console.log(message);
11
+ *
12
+ * const debouncedLog = debounce(log, 2000);
13
+ *
14
+ * debouncedLog("Hello, World!");
15
+ * debouncedLog.cancel();
16
+ * debouncedLog.flush();
17
+ *
18
+ * @param {Function} fn - Function to debounce
19
+ * @param {number} wait - Amount of time to debounce the function for
20
+ */
21
+ declare function debounce<T extends (...args: any[]) => any>(fn: T, wait: number): T & {
22
+ cancel: () => void;
23
+ flush: () => void;
24
+ };
25
+ export { debounce, debounce as default };
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.debounce = debounce;
4
+ exports.default = debounce;
5
+ const is_1 = require("./is");
6
+ const isIntegerAbove_1 = require("../number/isIntegerAbove");
7
+ function debounce(fn, wait) {
8
+ if (!(0, is_1.isFunction)(fn))
9
+ throw new Error('functions/debounce: Expected a function');
10
+ if (!(0, isIntegerAbove_1.isIntegerAbove)(wait, 0))
11
+ throw new Error('functions/debounce: Wait should be an integer above 0');
12
+ let timeout;
13
+ let self;
14
+ let current_args;
15
+ let current_rslt;
16
+ const clear = () => {
17
+ if (!timeout)
18
+ return;
19
+ clearTimeout(timeout);
20
+ timeout = null;
21
+ };
22
+ const flush = () => {
23
+ if (!timeout)
24
+ return;
25
+ current_rslt = fn.apply(self, current_args);
26
+ clearTimeout(timeout);
27
+ timeout = null;
28
+ };
29
+ const debouncedFn = function (...args) {
30
+ self = this;
31
+ current_args = args;
32
+ clear();
33
+ timeout = setTimeout(() => {
34
+ timeout = null;
35
+ current_rslt = fn.apply(self, current_args);
36
+ }, wait);
37
+ return current_rslt;
38
+ };
39
+ debouncedFn.cancel = clear;
40
+ debouncedFn.flush = flush;
41
+ return debouncedFn;
42
+ }
@@ -1,7 +1,8 @@
1
+ import { debounce } from './debounce';
1
2
  import { isFunction } from './is';
2
3
  import { isAsyncFunction } from './isAsync';
3
4
  import { noop } from './noop';
4
5
  import { noopresolve } from './noopresolve';
5
6
  import { noopreturn } from './noopreturn';
6
7
  import { sleep } from './sleep';
7
- export { isFunction, isFunction as is, isFunction as isFn, isAsyncFunction, isAsyncFunction as isAsync, isAsyncFunction as isAsyncFn, noop, noopresolve, noopreturn, sleep };
8
+ export { debounce, isFunction, isFunction as is, isFunction as isFn, isAsyncFunction, isAsyncFunction as isAsync, isAsyncFunction as isAsyncFn, noop, noopresolve, noopreturn, sleep };
package/function/index.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sleep = exports.noopreturn = exports.noopresolve = exports.noop = exports.isAsyncFn = exports.isAsync = exports.isAsyncFunction = exports.isFn = exports.is = exports.isFunction = void 0;
3
+ exports.sleep = exports.noopreturn = exports.noopresolve = exports.noop = exports.isAsyncFn = exports.isAsync = exports.isAsyncFunction = exports.isFn = exports.is = exports.isFunction = exports.debounce = void 0;
4
+ const debounce_1 = require("./debounce");
5
+ Object.defineProperty(exports, "debounce", { enumerable: true, get: function () { return debounce_1.debounce; } });
4
6
  const is_1 = require("./is");
5
7
  Object.defineProperty(exports, "isFunction", { enumerable: true, get: function () { return is_1.isFunction; } });
6
8
  Object.defineProperty(exports, "is", { enumerable: true, get: function () { return is_1.isFunction; } });
package/index.d.ts CHANGED
@@ -6,10 +6,6 @@ declare module "equal" {
6
6
  function equal(a: any, b: any): boolean;
7
7
  export { equal, equal as default };
8
8
  }
9
- declare module "hash/fnv1A" {
10
- function fnv1A(data: unknown, offset?: number): number;
11
- export { fnv1A, fnv1A as default };
12
- }
13
9
  declare module "array/dedupe" {
14
10
  type DedupeOptions<T> = {
15
11
  filter_fn?: (el: T) => boolean;
@@ -91,6 +87,12 @@ declare module "array/split" {
91
87
  function split<T>(arr: T[], size: number, opts?: SplitOptions<T>): T[][];
92
88
  export { split, split as default };
93
89
  }
90
+ declare module "object/is" {
91
+ function isObject(val: unknown): val is {
92
+ [key: string]: any;
93
+ };
94
+ export { isObject, isObject as default };
95
+ }
94
96
  declare module "array/sort" {
95
97
  type sortOptions<T> = {
96
98
  filter_fn?: (el: T) => boolean;
@@ -100,7 +102,7 @@ declare module "array/sort" {
100
102
  type sortByFunction = (el: Record<string, any>) => string;
101
103
  function sort<T extends {
102
104
  [key: string]: any;
103
- }[]>(arr: T, by: string | sortByFunction, dir?: 'asc' | 'desc', opts?: sortOptions<T>): T;
105
+ }>(arr: T[], by: string | sortByFunction, dir?: 'asc' | 'desc', opts?: sortOptions<T>): T[];
104
106
  export { sort, sort as default };
105
107
  }
106
108
  declare module "array/index" {
@@ -205,6 +207,17 @@ declare module "function/is" {
205
207
  function isFunction(val: unknown): val is (...args: unknown[]) => unknown;
206
208
  export { isFunction, isFunction as default };
207
209
  }
210
+ declare module "number/isIntegerAbove" {
211
+ function isIntegerAbove(val: unknown, ref: number): val is number;
212
+ export { isIntegerAbove, isIntegerAbove as default };
213
+ }
214
+ declare module "function/debounce" {
215
+ function debounce<T extends (...args: any[]) => any>(fn: T, wait: number): T & {
216
+ cancel: () => void;
217
+ flush: () => void;
218
+ };
219
+ export { debounce, debounce as default };
220
+ }
208
221
  declare module "function/isAsync" {
209
222
  function isAsyncFunction(val: unknown): val is (...args: unknown[]) => Promise<unknown>;
210
223
  export { isAsyncFunction, isAsyncFunction as default };
@@ -226,13 +239,14 @@ declare module "function/sleep" {
226
239
  export { sleep, sleep as default };
227
240
  }
228
241
  declare module "function/index" {
242
+ import { debounce } from "function/debounce";
229
243
  import { isFunction } from "function/is";
230
244
  import { isAsyncFunction } from "function/isAsync";
231
245
  import { noop } from "function/noop";
232
246
  import { noopresolve } from "function/noopresolve";
233
247
  import { noopreturn } from "function/noopreturn";
234
248
  import { sleep } from "function/sleep";
235
- export { isFunction, isFunction as is, isFunction as isFn, isAsyncFunction, isAsyncFunction as isAsync, isAsyncFunction as isAsyncFn, noop, noopresolve, noopreturn, sleep };
249
+ export { debounce, isFunction, isFunction as is, isFunction as isFn, isAsyncFunction, isAsyncFunction as isAsync, isAsyncFunction as isAsyncFn, noop, noopresolve, noopreturn, sleep };
236
250
  }
237
251
  declare module "regexp/is" {
238
252
  function isRegExp(val: unknown): val is RegExp;
@@ -257,12 +271,6 @@ declare module "object/define" {
257
271
  };
258
272
  export { define, define as default };
259
273
  }
260
- declare module "object/is" {
261
- function isObject(val: unknown): val is {
262
- [key: string]: any;
263
- };
264
- export { isObject, isObject as default };
265
- }
266
274
  declare module "object/merge" {
267
275
  function merge(target: {
268
276
  [key: string]: any;
@@ -333,10 +341,6 @@ declare module "number/isInteger" {
333
341
  function isInteger(val: unknown): val is number;
334
342
  export { isInteger, isInteger as default };
335
343
  }
336
- declare module "number/isIntegerAbove" {
337
- function isIntegerAbove(val: unknown, ref: number): val is number;
338
- export { isIntegerAbove, isIntegerAbove as default };
339
- }
340
344
  declare module "number/isIntegerBelow" {
341
345
  function isIntegerBelow(val: unknown, ref: number): val is number;
342
346
  export { isIntegerBelow, isIntegerBelow as default };
@@ -412,7 +416,7 @@ declare module "string/isBetween" {
412
416
  export { isStringBetween, isStringBetween as default };
413
417
  }
414
418
  declare module "string/shorten" {
415
- function shorten(val: string, length: number, postfix?: string): string;
419
+ function shorten(val: string, length: number, postfix?: string, truncate_words?: boolean): string;
416
420
  export { shorten, shorten as default };
417
421
  }
418
422
  declare module "string/index" {
@@ -528,6 +532,10 @@ declare module "deep/index" {
528
532
  import { deepSet } from "deep/set";
529
533
  export { deepFreeze as freeze, deepFreeze, deepGet as get, deepGet, deepSeal as seal, deepSeal, deepSet as set, deepSet };
530
534
  }
535
+ declare module "hash/fnv1A" {
536
+ function fnv1A(data: unknown, offset?: number): number;
537
+ export { fnv1A, fnv1A as default };
538
+ }
531
539
  declare module "hash/guid" {
532
540
  function guid(): string;
533
541
  export { guid, guid as default };
package/package.json CHANGED
@@ -1 +1 @@
1
- { "name": "@valkyriestudios/utils", "version": "12.17.1", "description": "A collection of single-function utilities for common tasks", "author": { "name": "Peter Vermeulen", "url": "https://www.linkedin.com/in/petervermeulen1/" }, "keywords": [ "utility", "library", "javascript", "js", "node", "bun" ], "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/ValkyrieStudios/utils.git" }, "bugs": { "url": "https://github.com/ValkyrieStudios/utils/issues" }, "homepage": "https://github.com/ValkyrieStudios/utils#readme", "types": "index.d.ts" }
1
+ { "name": "@valkyriestudios/utils", "version": "12.18.0", "description": "A collection of single-function utilities for common tasks", "author": { "name": "Peter Vermeulen", "url": "https://www.linkedin.com/in/petervermeulen1/" }, "keywords": [ "utility", "library", "javascript", "js", "node", "bun" ], "license": "MIT", "repository": { "type": "git", "url": "git+https://github.com/ValkyrieStudios/utils.git" }, "bugs": { "url": "https://github.com/ValkyrieStudios/utils/issues" }, "homepage": "https://github.com/ValkyrieStudios/utils#readme", "types": "index.d.ts" }
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Shorten a string and add a postfix if it goes over a specific length, will autotrim value.
3
3
  *
4
- * @param val - String value to shorten
5
- * @param length - Length to shorten it to
6
- * @param postfix - (default='...') Postfix to use in case the string got shortened
4
+ * @param {string} val - String value to shorten
5
+ * @param {number} length - Length to shorten it to
6
+ * @param {string} postfix - (default:'...') Postfix to use in case the string got shortened
7
+ * @param {boolean?} truncate_words - (default:true) Truncate words or not
7
8
  *
8
9
  * @returns Shortened string
9
10
  */
10
- declare function shorten(val: string, length: number, postfix?: string): string;
11
+ declare function shorten(val: string, length: number, postfix?: string, truncate_words?: boolean): string;
11
12
  export { shorten, shorten as default };
package/string/shorten.js CHANGED
@@ -2,11 +2,23 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.shorten = shorten;
4
4
  exports.default = shorten;
5
- function shorten(val, length, postfix = '...') {
5
+ function shorten(val, length, postfix = '...', truncate_words = true) {
6
6
  if (typeof val !== 'string')
7
7
  return '';
8
8
  if (typeof postfix !== 'string' || !Number.isInteger(length) || length <= 0)
9
9
  return val;
10
10
  const sanitized = val.trim();
11
- return sanitized.length <= length ? sanitized : `${sanitized.substring(0, length)}${postfix}`;
11
+ if (sanitized.length <= length)
12
+ return sanitized;
13
+ const truncated = sanitized.substring(0, length);
14
+ if (truncate_words)
15
+ return truncated + postfix;
16
+ let end = length;
17
+ while (end > 0 && sanitized[end] !== ' ' && sanitized[end - 1] !== ' ') {
18
+ end--;
19
+ }
20
+ if (end === 0) {
21
+ return sanitized.substring(0, length) + postfix;
22
+ }
23
+ return sanitized.substring(0, end).trim() + postfix;
12
24
  }