@valkyriestudios/utils 12.11.0 → 12.13.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
@@ -404,6 +404,10 @@ Available tokens for usage in spec:
404
404
  | `SSS` | Milliseconds as 3-digit | 000 001 ... 998 999 |
405
405
  | `A` | Uppercase AM/PM | AM ... PM |
406
406
  | `a` | Lowercase AM/PM | am ... pm |
407
+ | `l` | Locale-specific short Date | 15/07/2024 |
408
+ | `L` | Locale-Specific date | 15 jul 2024 |
409
+ | `t` | Locale-specific short time | 10:28 AM |
410
+ | `T` | Locale-specific time with seconds | 10:28:30 AM |
407
411
 
408
412
  ```typescript
409
413
  format(new Date('2023-01-10T14:30:00Z'), '[Today is] dddd, MMMM D, YYYY [at] h:mm A', 'en', 'Europe/Brussels');
@@ -431,6 +435,19 @@ Returns the current unix timestamp in seconds
431
435
  - **nowUnixMs()**
432
436
  Returns the current unix timestamp in milliseconds
433
437
 
438
+ - **setTimeUTC(val:Date, props:{hour?:number;minute?:number;second?:number;millisecond?:number})**
439
+ Take the incoming date and return a date where the time portion is set to the values in the provided props
440
+
441
+ Note: Does not touch the date object passed
442
+ ```typescript
443
+ setTimeUTC(new Date("2023-05-04T12:04:27.432Z"), {hour: 5}); // new Date("2023-05-04T05:04:27.432Z")
444
+ setTimeUTC(new Date("2023-05-04T12:04:27.432Z"), {hour: 5, minute: 30}); // new Date("2023-05-04T05:30:27.432Z")
445
+ setTimeUTC(new Date("2023-05-04T12:04:27.432Z"), {hour: 5, minute: 30, second: 0}); // new Date("2023-05-04T05:30:00.432Z")
446
+ setTimeUTC(new Date("2023-05-04T12:04:27.432Z"), {hour: 5, minute: 30, second: 0, millisecond: 0}); // new Date("2023-05-04T05:30:00.000Z")
447
+ setTimeUTC(new Date("2023-05-04T12:04:27.432Z"), {minute: 30, second: 0, millisecond: 0}); // new Date("2023-05-04T12:30:00.000Z")
448
+ setTimeUTC(new Date("2023-05-04T12:04:27.432Z"), {second: 9, millisecond: 0}); // new Date("2023-05-04T12:04:09.000Z")
449
+ ```
450
+
434
451
  - **startOfUTC(val:Date, key:string)**
435
452
  Take the incoming date and return a date set to the start of passed key. Possible key options(year,quarter,month,week,week_sun,week_mon,week_tue,week_wed,week_thu,week_fri,week_sat,day,hour,minute,second).
436
453
 
package/array/sort.d.ts CHANGED
@@ -17,7 +17,14 @@ interface sortOptions {
17
17
  }
18
18
  type sortByFunction = (el: Record<string, any>) => string;
19
19
  /**
20
- * Sort an array of objects, uses an implementation of Tony Hoare's quicksort
20
+ * Sort an array of objects.
21
+ *
22
+ * The internals of this function swap between insertion and quicksort depending on the use-case.
23
+ * Insertion sort is used for smaller arrays and quicksort is used for larger arrays.
24
+ *
25
+ * The threshold for insertion sort is 10 elements.
26
+ *
27
+ * The quicksort implementation is based on Tony Hoare's quicksort
21
28
  * (https://cs.stanford.edu/people/eroberts/courses/soco/projects/2008-09/tony-hoare/quicksort.html)
22
29
  *
23
30
  * Example:
@@ -43,10 +50,10 @@ type sortByFunction = (el: Record<string, any>) => string;
43
50
  * Output:
44
51
  * [{test: 'Pony'}, {test: 'Peter'}, {test: 'JOHn'}, {test: 'Joe'}]
45
52
  *
46
- * @param val - Array to sort
47
- * @param by - Either a string (key) or a function
48
- * @param dir - (default='asc') Direction to sort in (asc or desc)
49
- * @param opts - Sort options
53
+ * @param {Array} val - Array to sort
54
+ * @param {string|sortByFunction} by - Either a string (key) or a function
55
+ * @param {'desc'|'asc'} dir - (default='asc') Direction to sort in (asc or desc)
56
+ * @param {sortOptions} opts - Sort options
50
57
  *
51
58
  * @returns Sorted array
52
59
  * @throws {Error}
package/array/sort.js CHANGED
@@ -3,100 +3,97 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sort = sort;
4
4
  exports.default = sort;
5
5
  const isNotEmpty_1 = require("../object/isNotEmpty");
6
- function partition(arr, start_ix, end_ix) {
7
- const pivot_val = arr[Math.floor((start_ix + end_ix) / 2)].t;
8
- while (start_ix <= end_ix) {
9
- while (arr[start_ix].t < pivot_val) {
10
- start_ix++;
6
+ const INSERTION_SORT_THRESHOLD = 10;
7
+ function partition(arr, low, high) {
8
+ const pivot = arr[Math.floor((low + high) / 2)][0];
9
+ let i = low;
10
+ let j = high;
11
+ while (i <= j) {
12
+ while (arr[i][0] < pivot) {
13
+ i++;
11
14
  }
12
- while (arr[end_ix].t > pivot_val) {
13
- end_ix--;
15
+ while (arr[j][0] > pivot) {
16
+ j--;
14
17
  }
15
- if (start_ix <= end_ix) {
16
- const temp = arr[start_ix];
17
- arr[start_ix] = arr[end_ix];
18
- arr[end_ix] = temp;
19
- start_ix++;
20
- end_ix--;
18
+ if (i <= j) {
19
+ [arr[i], arr[j]] = [arr[j], arr[i]];
20
+ i++;
21
+ j--;
21
22
  }
22
23
  }
23
- return start_ix;
24
+ return i;
24
25
  }
25
- function quickSort(arr, start_ix = 0, end_ix = arr.length - 1) {
26
- if (start_ix < end_ix) {
27
- const ix = partition(arr, start_ix, end_ix);
28
- quickSort(arr, start_ix, ix - 1);
29
- quickSort(arr, ix, end_ix);
26
+ function quickSort(arr) {
27
+ const stack = [{ low: 0, high: arr.length - 1 }];
28
+ while (stack.length) {
29
+ const { low, high } = stack.pop();
30
+ if (high - low <= INSERTION_SORT_THRESHOLD) {
31
+ for (let i = low + 1; i <= high; i++) {
32
+ const key = arr[i];
33
+ let j = i - 1;
34
+ while (j >= low && arr[j][0] > key[0]) {
35
+ arr[j + 1] = arr[j];
36
+ j--;
37
+ }
38
+ arr[j + 1] = key;
39
+ }
40
+ }
41
+ else {
42
+ const p = partition(arr, low, high);
43
+ if (p - 1 > low)
44
+ stack.push({ low, high: p - 1 });
45
+ if (p < high)
46
+ stack.push({ low: p, high });
47
+ }
30
48
  }
31
49
  return arr;
32
50
  }
33
51
  function sort(arr, by, dir = 'asc', opts) {
34
52
  if (!Array.isArray(arr) || !arr.length)
35
53
  return [];
36
- if (dir !== 'asc' && dir !== 'desc')
37
- throw new Error('Direction should be either asc or desc');
38
- let NOKEY_HIDE = false;
39
- let NOKEY_AT_END = true;
54
+ const NOKEY_HIDE = opts?.nokey_hide === true;
55
+ const NOKEY_AT_END = opts?.nokey_atend !== false;
40
56
  let FILTER_FN = isNotEmpty_1.isNotEmptyObject;
41
- if (opts && Object.prototype.toString.call(opts) === '[object Object]') {
42
- if (opts.nokey_hide === true)
43
- NOKEY_HIDE = true;
44
- if (opts.nokey_atend === false)
45
- NOKEY_AT_END = false;
46
- if (typeof opts.filter_fn === 'function') {
47
- const fn = opts.filter_fn;
48
- FILTER_FN = (el => (0, isNotEmpty_1.isNotEmptyObject)(el) && fn(el));
49
- }
57
+ if (typeof opts?.filter_fn === 'function') {
58
+ const fn = opts.filter_fn;
59
+ FILTER_FN = (el => (0, isNotEmpty_1.isNotEmptyObject)(el) && fn(el));
50
60
  }
51
- const prepared_arr = [];
52
- const nokey_arr = [];
61
+ let getKey;
53
62
  if (typeof by === 'string') {
54
63
  const by_s = by.trim();
55
64
  if (!by_s.length)
56
65
  throw new Error('Sort by as string should contain content');
57
- for (let i = 0; i < arr.length; i++) {
58
- const el = arr[i];
59
- if (!FILTER_FN(el))
60
- continue;
61
- if (el?.[by_s] === undefined) {
62
- nokey_arr.push(el);
63
- }
64
- else {
65
- prepared_arr.push({ t: el[by_s], el });
66
- }
67
- }
66
+ getKey = (el) => el[by_s];
68
67
  }
69
68
  else if (typeof by === 'function') {
70
- let key;
71
- for (let i = 0; i < arr.length; i++) {
72
- const el = arr[i];
73
- if (!FILTER_FN(el))
74
- continue;
75
- key = by(el);
76
- if (key === undefined) {
77
- nokey_arr.push(el);
78
- }
79
- else {
80
- prepared_arr.push({ t: key, el });
81
- }
82
- }
69
+ getKey = by;
83
70
  }
84
71
  else {
85
72
  throw new Error('Sort by should either be a string with content or a function');
86
73
  }
74
+ const prepared_arr = [];
75
+ const nokey_arr = [];
76
+ for (let i = 0; i < arr.length; i++) {
77
+ const el = arr[i];
78
+ if (!FILTER_FN(el))
79
+ continue;
80
+ const key = getKey(el);
81
+ if (key === undefined) {
82
+ nokey_arr.push(el);
83
+ }
84
+ else {
85
+ prepared_arr.push([key, el]);
86
+ }
87
+ }
87
88
  quickSort(prepared_arr);
88
89
  if (dir === 'desc')
89
90
  prepared_arr.reverse();
90
91
  const rslt = [];
91
- if (!NOKEY_HIDE && !NOKEY_AT_END) {
92
- for (let i = 0; i < nokey_arr.length; i++)
93
- rslt.push(nokey_arr[i]);
94
- }
92
+ if (!NOKEY_HIDE && !NOKEY_AT_END)
93
+ rslt.push(...nokey_arr);
95
94
  for (let i = 0; i < prepared_arr.length; i++)
96
- rslt.push(prepared_arr[i].el);
97
- if (!NOKEY_HIDE && NOKEY_AT_END) {
98
- for (let i = 0; i < nokey_arr.length; i++)
99
- rslt.push(nokey_arr[i]);
100
- }
95
+ rslt.push(prepared_arr[i][1]);
96
+ if (!NOKEY_HIDE && NOKEY_AT_END)
97
+ rslt.push(...nokey_arr);
101
98
  return rslt;
102
99
  }
package/date/format.js CHANGED
@@ -36,21 +36,21 @@ function toZone(date, zone) {
36
36
  throw new Error(`format: Invalid zone passed - ${zone}`);
37
37
  const offset = zone_time - client_time;
38
38
  zone_offset_cache.set(ckey, offset);
39
- return new Date(date.getTime() + offset);
39
+ return new Date(client_time + offset);
40
40
  }
41
41
  function runIntl(loc, token, props, val) {
42
42
  const hash = `${loc}:${token}`;
43
43
  let formatter = intl_formatters.get(hash);
44
- if (formatter)
45
- return formatter.format(val);
46
- try {
47
- formatter = new Intl.DateTimeFormat(loc, props);
48
- intl_formatters.set(hash, formatter);
49
- return formatter.format(val);
50
- }
51
- catch (err) {
52
- throw new Error(`format: Failed to run conversion for ${token} with locale ${loc}`);
44
+ if (!formatter) {
45
+ try {
46
+ formatter = new Intl.DateTimeFormat(loc, props);
47
+ intl_formatters.set(hash, formatter);
48
+ }
49
+ catch (err) {
50
+ throw new Error(`format: Failed to run conversion for ${token} with locale ${loc}`);
51
+ }
53
52
  }
53
+ return formatter.format(val);
54
54
  }
55
55
  const Tokens = [
56
56
  ['YYYY', d => d.getFullYear()],
@@ -74,6 +74,10 @@ const Tokens = [
74
74
  ['SSS', d => `${d.getMilliseconds()}`.padStart(3, '0')],
75
75
  ['A', d => d.getHours() < 12 ? 'AM' : 'PM'],
76
76
  ['a', d => d.getHours() < 12 ? 'am' : 'pm'],
77
+ ['l', (d, loc) => runIntl(loc, 'l', { dateStyle: 'short' }, d)],
78
+ ['L', (d, loc) => runIntl(loc, 'L', { dateStyle: 'medium' }, d)],
79
+ ['t', (d, loc) => runIntl(loc, 't', { timeStyle: 'short' }, d)],
80
+ ['T', (d, loc) => runIntl(loc, 'T', { timeStyle: 'medium' }, d)],
77
81
  ]
78
82
  .sort((a, b) => a[0].length > b[0].length ? -1 : 1)
79
83
  .map((el) => [el[0], new RegExp(el[0], 'g'), el[1]]);
@@ -83,11 +87,13 @@ function getSpecChain(spec) {
83
87
  return spec_chain;
84
88
  spec_chain = [];
85
89
  let cursor;
90
+ let spec_cursor = spec;
86
91
  for (let i = 0; i < Tokens.length; i++) {
87
92
  cursor = Tokens[i];
88
- if (spec.indexOf(cursor[0]) < 0)
93
+ if (spec_cursor.indexOf(cursor[0]) < 0)
89
94
  continue;
90
95
  spec_chain.push(cursor);
96
+ spec_cursor = spec_cursor.replace(cursor[1], '');
91
97
  }
92
98
  if (spec_chain.length === 0)
93
99
  return false;
package/date/index.d.ts CHANGED
@@ -5,7 +5,8 @@ import { format } from './format';
5
5
  import { isDate } from './is';
6
6
  import { nowUnix } from './nowUnix';
7
7
  import { nowUnixMs } from './nowUnixMs';
8
+ import { setTimeUTC } from './setTimeUTC';
8
9
  import { startOfUTC } from './startOfUTC';
9
10
  import { toUnix } from './toUnix';
10
11
  import { toUTC } from './toUTC';
11
- export { addUTC, diff, endOfUTC, format, isDate, isDate as is, nowUnix, nowUnixMs, startOfUTC, toUnix, toUTC };
12
+ export { addUTC, diff, endOfUTC, format, isDate, isDate as is, nowUnix, nowUnixMs, setTimeUTC, startOfUTC, toUnix, toUTC };
package/date/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toUTC = exports.toUnix = exports.startOfUTC = exports.nowUnixMs = exports.nowUnix = exports.is = exports.isDate = exports.format = exports.endOfUTC = exports.diff = exports.addUTC = void 0;
3
+ exports.toUTC = exports.toUnix = exports.startOfUTC = exports.setTimeUTC = exports.nowUnixMs = exports.nowUnix = exports.is = exports.isDate = exports.format = exports.endOfUTC = exports.diff = exports.addUTC = void 0;
4
4
  const addUTC_1 = require("./addUTC");
5
5
  Object.defineProperty(exports, "addUTC", { enumerable: true, get: function () { return addUTC_1.addUTC; } });
6
6
  const diff_1 = require("./diff");
@@ -16,6 +16,8 @@ const nowUnix_1 = require("./nowUnix");
16
16
  Object.defineProperty(exports, "nowUnix", { enumerable: true, get: function () { return nowUnix_1.nowUnix; } });
17
17
  const nowUnixMs_1 = require("./nowUnixMs");
18
18
  Object.defineProperty(exports, "nowUnixMs", { enumerable: true, get: function () { return nowUnixMs_1.nowUnixMs; } });
19
+ const setTimeUTC_1 = require("./setTimeUTC");
20
+ Object.defineProperty(exports, "setTimeUTC", { enumerable: true, get: function () { return setTimeUTC_1.setTimeUTC; } });
19
21
  const startOfUTC_1 = require("./startOfUTC");
20
22
  Object.defineProperty(exports, "startOfUTC", { enumerable: true, get: function () { return startOfUTC_1.startOfUTC; } });
21
23
  const toUnix_1 = require("./toUnix");
@@ -0,0 +1,15 @@
1
+ export type TimeProps = {
2
+ hour?: number;
3
+ minute?: number;
4
+ second?: number;
5
+ millisecond?: number;
6
+ };
7
+ /**
8
+ * Sets the time on a provided date object and returns it
9
+ *
10
+ * @param {Date} val - Date to set the time for
11
+ * @param {Time} props - Time props to set the time to
12
+ * @returns {Date} New date with provided amount of key added
13
+ */
14
+ declare function setTimeUTC(val: Date, props: TimeProps): Date;
15
+ export { setTimeUTC, setTimeUTC as default };
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setTimeUTC = setTimeUTC;
4
+ exports.default = setTimeUTC;
5
+ const is_1 = require("./is");
6
+ const isIntegerBetween_1 = require("../number/isIntegerBetween");
7
+ function setTimeUTC(val, props) {
8
+ if (!(0, is_1.isDate)(val))
9
+ throw new TypeError('setTimeUTC requires a date object');
10
+ return new Date(Date.UTC(val.getUTCFullYear(), val.getUTCMonth(), val.getUTCDate(), (0, isIntegerBetween_1.isIntegerBetween)(props?.hour, 0, 23) ? props.hour : val.getUTCHours(), (0, isIntegerBetween_1.isIntegerBetween)(props?.minute, 0, 59) ? props?.minute : val.getUTCMinutes(), (0, isIntegerBetween_1.isIntegerBetween)(props?.second, 0, 59) ? props?.second : val.getUTCSeconds(), (0, isIntegerBetween_1.isIntegerBetween)(props?.millisecond, 0, 999) ? props?.millisecond : val.getUTCMilliseconds()));
11
+ }
package/index.d.ts CHANGED
@@ -146,6 +146,20 @@ declare module "date/nowUnixMs" {
146
146
  function nowUnixMs(): number;
147
147
  export { nowUnixMs, nowUnixMs as default };
148
148
  }
149
+ declare module "number/isIntegerBetween" {
150
+ function isIntegerBetween(val: unknown, min: number, max: number): val is number;
151
+ export { isIntegerBetween, isIntegerBetween as default };
152
+ }
153
+ declare module "date/setTimeUTC" {
154
+ export type TimeProps = {
155
+ hour?: number;
156
+ minute?: number;
157
+ second?: number;
158
+ millisecond?: number;
159
+ };
160
+ function setTimeUTC(val: Date, props: TimeProps): Date;
161
+ export { setTimeUTC, setTimeUTC as default };
162
+ }
149
163
  declare module "date/startOfUTC" {
150
164
  function startOfUTC(val: Date, key?: 'year' | 'quarter' | 'month' | 'week' | 'week_sun' | 'week_mon' | 'week_tue' | 'week_wed' | 'week_thu' | 'week_fri' | 'week_sat' | 'day' | 'hour' | 'minute' | 'second' | 'millisecond'): Date;
151
165
  export { startOfUTC, startOfUTC as default };
@@ -166,10 +180,11 @@ declare module "date/index" {
166
180
  import { isDate } from "date/is";
167
181
  import { nowUnix } from "date/nowUnix";
168
182
  import { nowUnixMs } from "date/nowUnixMs";
183
+ import { setTimeUTC } from "date/setTimeUTC";
169
184
  import { startOfUTC } from "date/startOfUTC";
170
185
  import { toUnix } from "date/toUnix";
171
186
  import { toUTC } from "date/toUTC";
172
- export { addUTC, diff, endOfUTC, format, isDate, isDate as is, nowUnix, nowUnixMs, startOfUTC, toUnix, toUTC };
187
+ export { addUTC, diff, endOfUTC, format, isDate, isDate as is, nowUnix, nowUnixMs, setTimeUTC, startOfUTC, toUnix, toUTC };
173
188
  }
174
189
  declare module "formdata/is" {
175
190
  function isFormData(val: unknown): val is FormData;
@@ -319,10 +334,6 @@ declare module "number/isIntegerBelowOrEqual" {
319
334
  function isIntegerBelowOrEqual(val: unknown, ref: number): val is number;
320
335
  export { isIntegerBelowOrEqual, isIntegerBelowOrEqual as default };
321
336
  }
322
- declare module "number/isIntegerBetween" {
323
- function isIntegerBetween(val: unknown, min: number, max: number): val is number;
324
- export { isIntegerBetween, isIntegerBetween as default };
325
- }
326
337
  declare module "number/randomBetween" {
327
338
  function randomBetween(min?: number, max?: number): number;
328
339
  export { randomBetween, randomBetween as default };
package/package.json CHANGED
@@ -1 +1 @@
1
- { "name": "@valkyriestudios/utils", "version": "12.11.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
+ { "name": "@valkyriestudios/utils", "version": "12.13.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" }