@wistia/utils 0.0.0-stage → 0.17.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/LICENSE +22 -0
- package/README.md +237 -2
- package/dist/index.cjs +1921 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1560 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +1560 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +1833 -0
- package/dist/index.mjs.map +1 -0
- package/llms-full.txt +1195 -0
- package/llms.txt +158 -0
- package/package.json +81 -4
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,1833 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
* @license @wistia/utils v0.17.0
|
|
4
|
+
*
|
|
5
|
+
* Copyright (c) 2023-2026, Wistia, Inc. and its affiliates.
|
|
6
|
+
*
|
|
7
|
+
* This source code is unlicensed, all rights reserved.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
//#region src/guards/isNull.ts
|
|
11
|
+
/**
|
|
12
|
+
* Checks if the provided value is null.
|
|
13
|
+
*
|
|
14
|
+
* @param value - The value to check.
|
|
15
|
+
* @returns True if the value is null, false otherwise.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* isNull(null); // true
|
|
19
|
+
* isNull(undefined); // false
|
|
20
|
+
*
|
|
21
|
+
* @category Primitives
|
|
22
|
+
*/
|
|
23
|
+
const isNull = (value) => value === null;
|
|
24
|
+
//#endregion
|
|
25
|
+
//#region src/guards/isUndefined.ts
|
|
26
|
+
/**
|
|
27
|
+
* Checks if the provided value is undefined.
|
|
28
|
+
*
|
|
29
|
+
* @param value - The value to check.
|
|
30
|
+
* @returns True if the value is undefined, false otherwise.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* isUndefined(undefined); // true
|
|
34
|
+
* isUndefined(null); // false
|
|
35
|
+
*
|
|
36
|
+
* @category Primitives
|
|
37
|
+
*/
|
|
38
|
+
const isUndefined = (value) => value === void 0;
|
|
39
|
+
//#endregion
|
|
40
|
+
//#region src/guards/isNil.ts
|
|
41
|
+
/**
|
|
42
|
+
* Checks if the provided value is null or undefined.
|
|
43
|
+
*
|
|
44
|
+
* @param value - The value to check.
|
|
45
|
+
* @returns True if the value is null or undefined, false otherwise.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* isNil(null); // true
|
|
49
|
+
* isNil(0); // false
|
|
50
|
+
*
|
|
51
|
+
* @category Primitives
|
|
52
|
+
*/
|
|
53
|
+
const isNil = (value) => isNull(value) || isUndefined(value);
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/guards/isNotNil.ts
|
|
56
|
+
/**
|
|
57
|
+
* Checks if the provided value is neither null nor undefined.
|
|
58
|
+
*
|
|
59
|
+
* @param value - The value to check.
|
|
60
|
+
* @returns True if the value is not null or undefined, false otherwise.
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* Common in `.filter(isNotNil)` to drop `null`/`undefined` from arrays
|
|
64
|
+
* with narrowing.
|
|
65
|
+
*
|
|
66
|
+
* @example
|
|
67
|
+
* isNotNil(0); // true
|
|
68
|
+
* isNotNil(null); // false
|
|
69
|
+
*
|
|
70
|
+
* const items: (string | null)[] = ['a', null, 'b'];
|
|
71
|
+
* const clean: string[] = items.filter(isNotNil); // ['a', 'b']
|
|
72
|
+
*
|
|
73
|
+
* @category Negated
|
|
74
|
+
*/
|
|
75
|
+
const isNotNil = (value) => !isNil(value);
|
|
76
|
+
//#endregion
|
|
77
|
+
//#region src/guards/isRecord.ts
|
|
78
|
+
/**
|
|
79
|
+
* Checks if the provided value is a non-null object that is not an array.
|
|
80
|
+
*
|
|
81
|
+
* @param value - The value to check.
|
|
82
|
+
* @returns True if the value is an object (but not an array), false otherwise.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* isRecord({}); // true
|
|
86
|
+
* isRecord([]); // false
|
|
87
|
+
*
|
|
88
|
+
* @category Records
|
|
89
|
+
*/
|
|
90
|
+
const isRecord = (value) => isNotNil(value) && typeof value === "object" && !Array.isArray(value);
|
|
91
|
+
//#endregion
|
|
92
|
+
//#region src/guards/isNonEmptyRecord.ts
|
|
93
|
+
/**
|
|
94
|
+
* Checks if the provided value is an object with at least one own property.
|
|
95
|
+
*
|
|
96
|
+
* @param value - The value to check.
|
|
97
|
+
* @returns True if the value is a non-empty object, false otherwise.
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* isNonEmptyRecord({ a: 1 }); // true
|
|
101
|
+
* isNonEmptyRecord({}); // false
|
|
102
|
+
*
|
|
103
|
+
* @category Records
|
|
104
|
+
*/
|
|
105
|
+
const isNonEmptyRecord = (value) => isRecord(value) && Object.keys(value).length > 0;
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/guards/hasKey.ts
|
|
108
|
+
/**
|
|
109
|
+
* Curried function that tests if an input is an object
|
|
110
|
+
* _and_ that a key that was passed in is a property of that object.
|
|
111
|
+
*
|
|
112
|
+
* @param value - The input object to test.
|
|
113
|
+
* @param key - The key to test if it is a property of the input record.
|
|
114
|
+
* @returns A function that takes an unknown input and then tests if the
|
|
115
|
+
* input is an object and whether the key is a property of that object or not.
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* hasKey({ a: 1 }, 'a'); // true
|
|
119
|
+
* hasKey({ a: 1 }, 'b'); // false
|
|
120
|
+
*
|
|
121
|
+
* @category Records
|
|
122
|
+
*/
|
|
123
|
+
const hasKey = (value, key) => isNonEmptyRecord(value) && key in value;
|
|
124
|
+
//#endregion
|
|
125
|
+
//#region src/guards/isArray.ts
|
|
126
|
+
/**
|
|
127
|
+
* Checks if the provided value is an array.
|
|
128
|
+
*
|
|
129
|
+
* @param value - The value to check.
|
|
130
|
+
* @returns True if the value is an array, false otherwise.
|
|
131
|
+
*
|
|
132
|
+
* @example
|
|
133
|
+
* isArray([1, 2]); // true
|
|
134
|
+
* isArray('abc'); // false
|
|
135
|
+
*
|
|
136
|
+
* @category Arrays
|
|
137
|
+
*/
|
|
138
|
+
const isArray = (value) => isNotNil(value) && typeof value === "object" && Array.isArray(value);
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region src/guards/isFunction.ts
|
|
141
|
+
/**
|
|
142
|
+
* Checks if the provided value is a function.
|
|
143
|
+
*
|
|
144
|
+
* @param value - The value to check.
|
|
145
|
+
* @returns True if the value is a function, false otherwise.
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* isFunction(() => {}); // true
|
|
149
|
+
* isFunction({}); // false
|
|
150
|
+
*
|
|
151
|
+
* @category Primitives
|
|
152
|
+
*/
|
|
153
|
+
const isFunction = (value) => isNotNil(value) && typeof value === "function";
|
|
154
|
+
//#endregion
|
|
155
|
+
//#region src/guards/isAsyncFunction.ts
|
|
156
|
+
/**
|
|
157
|
+
* Checks if the provided value is an async function.
|
|
158
|
+
*
|
|
159
|
+
* @param value - The value to check.
|
|
160
|
+
* @returns True if the value is an async function, false otherwise.
|
|
161
|
+
*
|
|
162
|
+
* @remarks
|
|
163
|
+
* Detection relies on `constructor.name === 'AsyncFunction'` and is a
|
|
164
|
+
* heuristic: async functions transpiled to regular functions return false,
|
|
165
|
+
* async generator functions return false, and a forged `constructor`
|
|
166
|
+
* property can fool the check. Bound async functions return true, since
|
|
167
|
+
* `bind` preserves the prototype chain. Synchronous functions that return
|
|
168
|
+
* promises also return false.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* isAsyncFunction(async () => {}); // true
|
|
172
|
+
* isAsyncFunction(() => {}); // false
|
|
173
|
+
*
|
|
174
|
+
* @category Primitives
|
|
175
|
+
*/
|
|
176
|
+
const isAsyncFunction = (value) => {
|
|
177
|
+
return isFunction(value) && value.constructor?.name === "AsyncFunction";
|
|
178
|
+
};
|
|
179
|
+
//#endregion
|
|
180
|
+
//#region src/guards/isBigInt.ts
|
|
181
|
+
/**
|
|
182
|
+
* Checks if the provided value is a bigint.
|
|
183
|
+
*
|
|
184
|
+
* @param value - The value to check.
|
|
185
|
+
* @returns True if the value is a bigint, false otherwise.
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* isBigInt(10n); // true
|
|
189
|
+
* isBigInt(10); // false
|
|
190
|
+
*
|
|
191
|
+
* @category Primitives
|
|
192
|
+
*/
|
|
193
|
+
const isBigInt = (value) => typeof value === "bigint";
|
|
194
|
+
//#endregion
|
|
195
|
+
//#region src/guards/isBoolean.ts
|
|
196
|
+
/**
|
|
197
|
+
* Checks if the provided value is a boolean.
|
|
198
|
+
*
|
|
199
|
+
* @param value - The value to check.
|
|
200
|
+
* @returns True if the value is a boolean, false otherwise.
|
|
201
|
+
*
|
|
202
|
+
* @example
|
|
203
|
+
* isBoolean(true); // true
|
|
204
|
+
* isBoolean(1); // false
|
|
205
|
+
*
|
|
206
|
+
* @category Primitives
|
|
207
|
+
*/
|
|
208
|
+
const isBoolean = (value) => isNotNil(value) && typeof value === "boolean";
|
|
209
|
+
//#endregion
|
|
210
|
+
//#region src/guards/isDate.ts
|
|
211
|
+
/**
|
|
212
|
+
* Checks if the provided value is an instance of Date.
|
|
213
|
+
*
|
|
214
|
+
* @param value - The value to check.
|
|
215
|
+
* @returns True if the value is a Date instance, false otherwise.
|
|
216
|
+
*
|
|
217
|
+
* @remarks
|
|
218
|
+
* Matches `Date` instances only. Does not parse date strings or
|
|
219
|
+
* accept numeric timestamps.
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* isDate(new Date()); // true
|
|
223
|
+
* isDate('2024-01-01'); // false
|
|
224
|
+
*
|
|
225
|
+
* @category Primitives
|
|
226
|
+
*/
|
|
227
|
+
const isDate = (value) => {
|
|
228
|
+
return isNotNil(value) && value instanceof Date;
|
|
229
|
+
};
|
|
230
|
+
//#endregion
|
|
231
|
+
//#region src/guards/isEmptyArray.ts
|
|
232
|
+
/**
|
|
233
|
+
* Checks if the provided value is an empty array.
|
|
234
|
+
*
|
|
235
|
+
* @param value - The value to check.
|
|
236
|
+
* @returns True if the value is an empty array, false otherwise.
|
|
237
|
+
*
|
|
238
|
+
* @example
|
|
239
|
+
* isEmptyArray([]); // true
|
|
240
|
+
* isEmptyArray([1]); // false
|
|
241
|
+
*
|
|
242
|
+
* @category Arrays
|
|
243
|
+
*/
|
|
244
|
+
const isEmptyArray = (value) => isArray(value) && value.length === 0;
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/guards/isEmptyRecord.ts
|
|
247
|
+
/**
|
|
248
|
+
* Checks if the provided value is an object with no own properties.
|
|
249
|
+
*
|
|
250
|
+
* @param value - The value to check.
|
|
251
|
+
* @returns True if the value is an empty object, false otherwise.
|
|
252
|
+
*
|
|
253
|
+
* @example
|
|
254
|
+
* isEmptyRecord({}); // true
|
|
255
|
+
* isEmptyRecord({ a: 1 }); // false
|
|
256
|
+
*
|
|
257
|
+
* @category Records
|
|
258
|
+
*/
|
|
259
|
+
const isEmptyRecord = (value) => isRecord(value) && Object.keys(value).length === 0;
|
|
260
|
+
//#endregion
|
|
261
|
+
//#region src/guards/isString.ts
|
|
262
|
+
/**
|
|
263
|
+
* Checks if the provided value is a string.
|
|
264
|
+
*
|
|
265
|
+
* @param value - The value to check.
|
|
266
|
+
* @returns True if the value is a string, false otherwise.
|
|
267
|
+
*
|
|
268
|
+
* @example
|
|
269
|
+
* isString('hi'); // true
|
|
270
|
+
* isString(42); // false
|
|
271
|
+
*
|
|
272
|
+
* @category Primitives
|
|
273
|
+
*/
|
|
274
|
+
const isString = (value) => typeof value === "string";
|
|
275
|
+
//#endregion
|
|
276
|
+
//#region src/guards/isEmptyString.ts
|
|
277
|
+
/**
|
|
278
|
+
* Checks if the provided value is an empty string.
|
|
279
|
+
*
|
|
280
|
+
* @param value - The value to check.
|
|
281
|
+
* @returns True if the value is an empty string, false otherwise.
|
|
282
|
+
*
|
|
283
|
+
* @example
|
|
284
|
+
* isEmptyString(''); // true
|
|
285
|
+
* isEmptyString('a'); // false
|
|
286
|
+
*
|
|
287
|
+
* @category Strings
|
|
288
|
+
*/
|
|
289
|
+
const isEmptyString = (value) => isString(value) && value === "";
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/guards/isError.ts
|
|
292
|
+
/**
|
|
293
|
+
* Checks if the provided value is an instance of Error.
|
|
294
|
+
*
|
|
295
|
+
* @param value - The value to check.
|
|
296
|
+
* @returns True if the value is an Error instance, false otherwise.
|
|
297
|
+
*
|
|
298
|
+
* @remarks
|
|
299
|
+
* Matches subclasses of `Error` too (e.g. `TypeError`, custom errors).
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* isError(new Error('boom')); // true
|
|
303
|
+
* isError('boom'); // false
|
|
304
|
+
*
|
|
305
|
+
* @category Primitives
|
|
306
|
+
*/
|
|
307
|
+
const isError = (value) => {
|
|
308
|
+
return isNotNil(value) && value instanceof Error;
|
|
309
|
+
};
|
|
310
|
+
//#endregion
|
|
311
|
+
//#region src/guards/_falsyValues.ts
|
|
312
|
+
const FALSY_VALUES = [
|
|
313
|
+
false,
|
|
314
|
+
0,
|
|
315
|
+
-0,
|
|
316
|
+
0n,
|
|
317
|
+
"",
|
|
318
|
+
null,
|
|
319
|
+
void 0,
|
|
320
|
+
NaN
|
|
321
|
+
];
|
|
322
|
+
//#endregion
|
|
323
|
+
//#region src/guards/isFalsy.ts
|
|
324
|
+
/**
|
|
325
|
+
* Checks if the provided value is falsy.
|
|
326
|
+
*
|
|
327
|
+
* @param value - The value to check.
|
|
328
|
+
* @returns True if the value is falsy, false otherwise.
|
|
329
|
+
*
|
|
330
|
+
* @remarks
|
|
331
|
+
* Returns true for `false`, `0`, `-0`, `0n`, `''`, `null`, `undefined`,
|
|
332
|
+
* and `NaN`.
|
|
333
|
+
*
|
|
334
|
+
* @example
|
|
335
|
+
* isFalsy(0); // true
|
|
336
|
+
* isFalsy(1); // false
|
|
337
|
+
*
|
|
338
|
+
* @category Truthy / falsy
|
|
339
|
+
*/
|
|
340
|
+
const isFalsy = (value) => FALSY_VALUES.includes(value) || Number.isNaN(value);
|
|
341
|
+
//#endregion
|
|
342
|
+
//#region src/guards/isNumber.ts
|
|
343
|
+
/**
|
|
344
|
+
* Checks if the provided value is a number.
|
|
345
|
+
*
|
|
346
|
+
* @param value - The value to check.
|
|
347
|
+
* @returns True if the value is a number, false otherwise.
|
|
348
|
+
*
|
|
349
|
+
* @remarks
|
|
350
|
+
* `NaN` is a `number`, so `isNumber(NaN)` is true. Use `isInteger` or
|
|
351
|
+
* combine with `!isNaN(value)` if you need a finite number.
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* isNumber(1); // true
|
|
355
|
+
* isNumber('1'); // false
|
|
356
|
+
*
|
|
357
|
+
* @category Primitives
|
|
358
|
+
*/
|
|
359
|
+
const isNumber = (value) => typeof value === "number";
|
|
360
|
+
//#endregion
|
|
361
|
+
//#region src/guards/isFiniteNumber.ts
|
|
362
|
+
/**
|
|
363
|
+
* Checks if the provided value is a finite number.
|
|
364
|
+
*
|
|
365
|
+
* @param value - The value to check.
|
|
366
|
+
* @returns True if the value is a finite number, false otherwise.
|
|
367
|
+
*
|
|
368
|
+
* @example
|
|
369
|
+
* isFiniteNumber(42); // true
|
|
370
|
+
* isFiniteNumber(Infinity); // false
|
|
371
|
+
*
|
|
372
|
+
* @category Primitives
|
|
373
|
+
*/
|
|
374
|
+
const isFiniteNumber = (value) => isNumber(value) && Number.isFinite(value);
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/guards/isHtmlButtonElement.ts
|
|
377
|
+
/**
|
|
378
|
+
* Checks if the provided value is an HTMLButtonElement.
|
|
379
|
+
*
|
|
380
|
+
* @param value - The value to check.
|
|
381
|
+
* @returns True if the value is an HTMLButtonElement, false otherwise.
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* isHtmlButtonElement(document.createElement('button')); // true
|
|
385
|
+
* isHtmlButtonElement(document.createElement('div')); // false
|
|
386
|
+
*
|
|
387
|
+
* @category DOM
|
|
388
|
+
*/
|
|
389
|
+
const isHtmlButtonElement = (value) => value instanceof HTMLButtonElement;
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/guards/isHtmlElement.ts
|
|
392
|
+
/**
|
|
393
|
+
* Checks if the provided value is an HTMLElement.
|
|
394
|
+
*
|
|
395
|
+
* @param value - The value to check.
|
|
396
|
+
* @returns True if the value is an HTMLElement, false otherwise.
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* isHtmlElement(document.body); // true
|
|
400
|
+
* isHtmlElement('div'); // false
|
|
401
|
+
*
|
|
402
|
+
* @category DOM
|
|
403
|
+
*/
|
|
404
|
+
const isHtmlElement = (value) => value instanceof HTMLElement;
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region src/guards/isHtmlInputElement.ts
|
|
407
|
+
/**
|
|
408
|
+
* Checks if the provided value is an HTMLInputElement.
|
|
409
|
+
*
|
|
410
|
+
* @param value - The value to check.
|
|
411
|
+
* @returns True if the value is an HTMLInputElement, false otherwise.
|
|
412
|
+
*
|
|
413
|
+
* @example
|
|
414
|
+
* isHtmlInputElement(document.createElement('input')); // true
|
|
415
|
+
* isHtmlInputElement(document.createElement('div')); // false
|
|
416
|
+
*
|
|
417
|
+
* @category DOM
|
|
418
|
+
*/
|
|
419
|
+
const isHtmlInputElement = (value) => value instanceof HTMLInputElement;
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/guards/isHtmlVideoElement.ts
|
|
422
|
+
/**
|
|
423
|
+
* Checks if the provided value is an HTMLVideoElement.
|
|
424
|
+
*
|
|
425
|
+
* @param value - The value to check.
|
|
426
|
+
* @returns True if the value is an HTMLVideoElement, false otherwise.
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* isHtmlVideoElement(document.createElement('video')); // true
|
|
430
|
+
* isHtmlVideoElement(document.createElement('div')); // false
|
|
431
|
+
*
|
|
432
|
+
* @category DOM
|
|
433
|
+
*/
|
|
434
|
+
const isHtmlVideoElement = (value) => value instanceof HTMLVideoElement;
|
|
435
|
+
//#endregion
|
|
436
|
+
//#region src/guards/isInteger.ts
|
|
437
|
+
/**
|
|
438
|
+
* Checks if the provided value is an integer.
|
|
439
|
+
*
|
|
440
|
+
* @param value - The value to check.
|
|
441
|
+
* @returns True if the value is an integer, false otherwise.
|
|
442
|
+
*
|
|
443
|
+
* @remarks
|
|
444
|
+
* Uses `Number.isInteger`, so non-finite values (`NaN`, `Infinity`)
|
|
445
|
+
* return false.
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* isInteger(5); // true
|
|
449
|
+
* isInteger(5.5); // false
|
|
450
|
+
*
|
|
451
|
+
* @category Primitives
|
|
452
|
+
*/
|
|
453
|
+
const isInteger = (value) => Number.isInteger(value);
|
|
454
|
+
//#endregion
|
|
455
|
+
//#region src/guards/isIterable.ts
|
|
456
|
+
/**
|
|
457
|
+
* Checks if the provided value implements the iterable protocol.
|
|
458
|
+
*
|
|
459
|
+
* @param value - The value to check.
|
|
460
|
+
* @returns True if the value is iterable, false otherwise.
|
|
461
|
+
*
|
|
462
|
+
* @remarks
|
|
463
|
+
* Strings are iterable and return true. Plain objects without
|
|
464
|
+
* `Symbol.iterator` return false.
|
|
465
|
+
*
|
|
466
|
+
* @example
|
|
467
|
+
* isIterable([1, 2, 3]); // true
|
|
468
|
+
* isIterable({}); // false
|
|
469
|
+
*
|
|
470
|
+
* @category Primitives
|
|
471
|
+
*/
|
|
472
|
+
const isIterable = (value) => {
|
|
473
|
+
if (!isNotNil(value)) return false;
|
|
474
|
+
return typeof value[Symbol.iterator] === "function";
|
|
475
|
+
};
|
|
476
|
+
//#endregion
|
|
477
|
+
//#region src/guards/isMap.ts
|
|
478
|
+
/**
|
|
479
|
+
* Checks if the provided value is an instance of Map.
|
|
480
|
+
*
|
|
481
|
+
* @param value - The value to check.
|
|
482
|
+
* @returns True if the value is a Map instance, false otherwise.
|
|
483
|
+
*
|
|
484
|
+
* @remarks
|
|
485
|
+
* WeakMap instances return false.
|
|
486
|
+
*
|
|
487
|
+
* @example
|
|
488
|
+
* isMap(new Map()); // true
|
|
489
|
+
* isMap({}); // false
|
|
490
|
+
*
|
|
491
|
+
* @category Primitives
|
|
492
|
+
*/
|
|
493
|
+
const isMap = (value) => {
|
|
494
|
+
return isNotNil(value) && value instanceof Map;
|
|
495
|
+
};
|
|
496
|
+
//#endregion
|
|
497
|
+
//#region src/guards/isMouseEvent.ts
|
|
498
|
+
/**
|
|
499
|
+
* Checks if the provided value is a MouseEvent.
|
|
500
|
+
*
|
|
501
|
+
* @param value - The value to check.
|
|
502
|
+
* @returns True if the value is a MouseEvent, false otherwise.
|
|
503
|
+
*
|
|
504
|
+
* @example
|
|
505
|
+
* isMouseEvent(new MouseEvent('click')); // true
|
|
506
|
+
* isMouseEvent(new Event('change')); // false
|
|
507
|
+
*
|
|
508
|
+
* @category DOM
|
|
509
|
+
*/
|
|
510
|
+
const isMouseEvent = (value) => value instanceof MouseEvent;
|
|
511
|
+
//#endregion
|
|
512
|
+
//#region src/guards/isNaN.ts
|
|
513
|
+
/**
|
|
514
|
+
* Checks if the provided value is NaN (Not a Number).
|
|
515
|
+
*
|
|
516
|
+
* @param value - The value to check.
|
|
517
|
+
* @returns True if the value is NaN, false otherwise.
|
|
518
|
+
*
|
|
519
|
+
* @remarks
|
|
520
|
+
* Uses `Number.isNaN` (not the coercive global `isNaN`), so only the
|
|
521
|
+
* literal `NaN` value returns true.
|
|
522
|
+
*
|
|
523
|
+
* @example
|
|
524
|
+
* isNaN(Number.NaN); // true
|
|
525
|
+
* isNaN(0); // false
|
|
526
|
+
*
|
|
527
|
+
* @category Primitives
|
|
528
|
+
*/
|
|
529
|
+
const isNaN = (value) => Number.isNaN(value);
|
|
530
|
+
//#endregion
|
|
531
|
+
//#region src/guards/isNonBlankString.ts
|
|
532
|
+
/**
|
|
533
|
+
* Checks if the provided value is a non-blank string (not empty and not whitespace-only).
|
|
534
|
+
* Use {@link isNonEmptyString} when whitespace-only strings are acceptable (e.g. preserving
|
|
535
|
+
* intentional formatting).
|
|
536
|
+
*
|
|
537
|
+
* @param value - The value to check.
|
|
538
|
+
* @returns True if the value is a string with at least one non-whitespace character, false otherwise.
|
|
539
|
+
*
|
|
540
|
+
* @example
|
|
541
|
+
* isNonBlankString('a'); // true
|
|
542
|
+
* isNonBlankString(' '); // false
|
|
543
|
+
*
|
|
544
|
+
* @category Strings
|
|
545
|
+
*/
|
|
546
|
+
const isNonBlankString = (value) => isString(value) && value.trim() !== "";
|
|
547
|
+
//#endregion
|
|
548
|
+
//#region src/guards/isNonEmptyArray.ts
|
|
549
|
+
/**
|
|
550
|
+
* Checks if the provided value is a non-empty array.
|
|
551
|
+
*
|
|
552
|
+
* @param value - The value to check. Can be null or undefined.
|
|
553
|
+
* @returns True if the value is a non-empty array, false otherwise.
|
|
554
|
+
*
|
|
555
|
+
* @example
|
|
556
|
+
* isNonEmptyArray([1]); // true
|
|
557
|
+
* isNonEmptyArray([]); // false
|
|
558
|
+
*
|
|
559
|
+
* @category Arrays
|
|
560
|
+
*/
|
|
561
|
+
const isNonEmptyArray = (value) => isArray(value) && value.length > 0;
|
|
562
|
+
//#endregion
|
|
563
|
+
//#region src/guards/isNonEmptyString.ts
|
|
564
|
+
/**
|
|
565
|
+
* Checks if the provided value is a non-empty string. Whitespace-only strings like `" "` pass.
|
|
566
|
+
* Use {@link isNonBlankString} when whitespace-only strings should be rejected (e.g. user input
|
|
567
|
+
* validation).
|
|
568
|
+
*
|
|
569
|
+
* @param value - The value to check.
|
|
570
|
+
* @returns True if the value is a non-empty string, false otherwise.
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* isNonEmptyString('a'); // true
|
|
574
|
+
* isNonEmptyString(''); // false
|
|
575
|
+
*
|
|
576
|
+
* @category Strings
|
|
577
|
+
*/
|
|
578
|
+
const isNonEmptyString = (value) => isString(value) && !isEmptyString(value);
|
|
579
|
+
//#endregion
|
|
580
|
+
//#region src/guards/isNonNaNNumber.ts
|
|
581
|
+
/**
|
|
582
|
+
* Checks if the provided value is a number that is not NaN.
|
|
583
|
+
*
|
|
584
|
+
* @param value - The value to check.
|
|
585
|
+
* @returns True if the value is a number and not NaN, false otherwise.
|
|
586
|
+
*
|
|
587
|
+
* @example
|
|
588
|
+
* isNonNaNNumber(42); // true
|
|
589
|
+
* isNonNaNNumber(Number.NaN); // false
|
|
590
|
+
*
|
|
591
|
+
* @category Primitives
|
|
592
|
+
*/
|
|
593
|
+
const isNonNaNNumber = (value) => isNumber(value) && !isNaN(value);
|
|
594
|
+
//#endregion
|
|
595
|
+
//#region src/guards/isNotArray.ts
|
|
596
|
+
/**
|
|
597
|
+
* Checks if the provided value is not an array.
|
|
598
|
+
*
|
|
599
|
+
* @param value - The value to check.
|
|
600
|
+
* @returns True if the value is not an array, false otherwise.
|
|
601
|
+
*
|
|
602
|
+
* @example
|
|
603
|
+
* isNotArray('x'); // true
|
|
604
|
+
* isNotArray([]); // false
|
|
605
|
+
*
|
|
606
|
+
* @category Negated
|
|
607
|
+
*/
|
|
608
|
+
const isNotArray = (value) => !isArray(value);
|
|
609
|
+
//#endregion
|
|
610
|
+
//#region src/guards/isNotBoolean.ts
|
|
611
|
+
/**
|
|
612
|
+
* Checks if the provided value is not a boolean.
|
|
613
|
+
*
|
|
614
|
+
* @param value - The value to check.
|
|
615
|
+
* @returns True if the value is not a boolean, false otherwise.
|
|
616
|
+
*
|
|
617
|
+
* @example
|
|
618
|
+
* isNotBoolean(1); // true
|
|
619
|
+
* isNotBoolean(true); // false
|
|
620
|
+
*
|
|
621
|
+
* @category Negated
|
|
622
|
+
*/
|
|
623
|
+
const isNotBoolean = (value) => !isBoolean(value);
|
|
624
|
+
//#endregion
|
|
625
|
+
//#region src/guards/isNotFunction.ts
|
|
626
|
+
/**
|
|
627
|
+
* Checks if the provided value is not a function.
|
|
628
|
+
*
|
|
629
|
+
* @param value - The value to check.
|
|
630
|
+
* @returns True if the value is not a function, false otherwise.
|
|
631
|
+
*
|
|
632
|
+
* @example
|
|
633
|
+
* isNotFunction(1); // true
|
|
634
|
+
* isNotFunction(() => {}); // false
|
|
635
|
+
*
|
|
636
|
+
* @category Negated
|
|
637
|
+
*/
|
|
638
|
+
const isNotFunction = (value) => !isFunction(value);
|
|
639
|
+
//#endregion
|
|
640
|
+
//#region src/guards/isNotNull.ts
|
|
641
|
+
/**
|
|
642
|
+
* Checks if the provided value is not null.
|
|
643
|
+
*
|
|
644
|
+
* @param value - The value to check.
|
|
645
|
+
* @returns True if the value is not null, false otherwise.
|
|
646
|
+
*
|
|
647
|
+
* @example
|
|
648
|
+
* isNotNull(undefined); // true
|
|
649
|
+
* isNotNull(null); // false
|
|
650
|
+
*
|
|
651
|
+
* @category Negated
|
|
652
|
+
*/
|
|
653
|
+
const isNotNull = (value) => !isNull(value);
|
|
654
|
+
//#endregion
|
|
655
|
+
//#region src/guards/isNotNumber.ts
|
|
656
|
+
/**
|
|
657
|
+
* Checks if the provided value is not a number.
|
|
658
|
+
*
|
|
659
|
+
* @param value - The value to check.
|
|
660
|
+
* @returns True if the value is not a number, false otherwise.
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* isNotNumber('1'); // true
|
|
664
|
+
* isNotNumber(1); // false
|
|
665
|
+
*
|
|
666
|
+
* @category Negated
|
|
667
|
+
*/
|
|
668
|
+
const isNotNumber = (value) => !isNumber(value);
|
|
669
|
+
//#endregion
|
|
670
|
+
//#region src/guards/isNotRecord.ts
|
|
671
|
+
/**
|
|
672
|
+
* Checks if the provided value is not an object.
|
|
673
|
+
*
|
|
674
|
+
* @param value - The value to check.
|
|
675
|
+
* @returns True if the value is not a record (object), false otherwise.
|
|
676
|
+
*
|
|
677
|
+
* @example
|
|
678
|
+
* isNotRecord([]); // true
|
|
679
|
+
* isNotRecord({}); // false
|
|
680
|
+
*
|
|
681
|
+
* @category Negated
|
|
682
|
+
*/
|
|
683
|
+
const isNotRecord = (value) => !isRecord(value);
|
|
684
|
+
//#endregion
|
|
685
|
+
//#region src/guards/isNotString.ts
|
|
686
|
+
/**
|
|
687
|
+
* Checks if the provided value is not a string.
|
|
688
|
+
*
|
|
689
|
+
* @param value - The value to check.
|
|
690
|
+
* @returns True if the value is not a string, false otherwise.
|
|
691
|
+
*
|
|
692
|
+
* @example
|
|
693
|
+
* isNotString(1); // true
|
|
694
|
+
* isNotString('a'); // false
|
|
695
|
+
*
|
|
696
|
+
* @category Negated
|
|
697
|
+
*/
|
|
698
|
+
const isNotString = (value) => !isString(value);
|
|
699
|
+
//#endregion
|
|
700
|
+
//#region src/guards/isNotUndefined.ts
|
|
701
|
+
/**
|
|
702
|
+
* Checks if the provided value is not undefined.
|
|
703
|
+
*
|
|
704
|
+
* @param value - The value to check.
|
|
705
|
+
* @returns True if the value is not undefined, false otherwise.
|
|
706
|
+
*
|
|
707
|
+
* @example
|
|
708
|
+
* isNotUndefined(null); // true
|
|
709
|
+
* isNotUndefined(undefined); // false
|
|
710
|
+
*
|
|
711
|
+
* @category Negated
|
|
712
|
+
*/
|
|
713
|
+
const isNotUndefined = (value) => !isUndefined(value);
|
|
714
|
+
//#endregion
|
|
715
|
+
//#region src/guards/isVoid.ts
|
|
716
|
+
/**
|
|
717
|
+
* Checks if the provided value is void.
|
|
718
|
+
*
|
|
719
|
+
* @param value - The value to check.
|
|
720
|
+
* @returns True if the value is void (undefined), false otherwise.
|
|
721
|
+
*
|
|
722
|
+
* @example
|
|
723
|
+
* isVoid(undefined); // true
|
|
724
|
+
* isVoid(0); // false
|
|
725
|
+
*
|
|
726
|
+
* @category Primitives
|
|
727
|
+
*/
|
|
728
|
+
const isVoid = (value) => value === void 0;
|
|
729
|
+
//#endregion
|
|
730
|
+
//#region src/guards/isNotVoid.ts
|
|
731
|
+
/**
|
|
732
|
+
* Checks if the provided value is not void.
|
|
733
|
+
*
|
|
734
|
+
* @param value - The value to check.
|
|
735
|
+
* @returns True if the value is not void (defined), false otherwise.
|
|
736
|
+
*
|
|
737
|
+
* @example
|
|
738
|
+
* isNotVoid(0); // true
|
|
739
|
+
* isNotVoid(undefined); // false
|
|
740
|
+
*
|
|
741
|
+
* @category Negated
|
|
742
|
+
*/
|
|
743
|
+
const isNotVoid = (value) => !isVoid(value);
|
|
744
|
+
//#endregion
|
|
745
|
+
//#region src/guards/isPlainObject.ts
|
|
746
|
+
/**
|
|
747
|
+
* Checks if the provided value is a plain object created with `{}` or `Object.create(null)`.
|
|
748
|
+
* Returns false for arrays, `Date`, `Map`, `RegExp`, boxed primitives, and class instances.
|
|
749
|
+
*
|
|
750
|
+
* @param value - The value to check.
|
|
751
|
+
* @returns True if the value is a plain object, false otherwise.
|
|
752
|
+
*
|
|
753
|
+
* @example
|
|
754
|
+
* isPlainObject({}); // true
|
|
755
|
+
* isPlainObject(new Date()); // false
|
|
756
|
+
*
|
|
757
|
+
* @category Records
|
|
758
|
+
*/
|
|
759
|
+
const isPlainObject = (value) => {
|
|
760
|
+
if (isNil(value) || typeof value !== "object") return false;
|
|
761
|
+
if (isArray(value)) return false;
|
|
762
|
+
const proto = Object.getPrototypeOf(value);
|
|
763
|
+
return proto === Object.prototype || proto === null;
|
|
764
|
+
};
|
|
765
|
+
//#endregion
|
|
766
|
+
//#region src/guards/isPromise.ts
|
|
767
|
+
/**
|
|
768
|
+
* Checks if the provided value is a Promise, including promise-like objects
|
|
769
|
+
* that implement `then`, `catch`, and `finally`.
|
|
770
|
+
*
|
|
771
|
+
* @param value - The value to check.
|
|
772
|
+
* @returns True if the value is a Promise or promise-like object, false otherwise.
|
|
773
|
+
*
|
|
774
|
+
* @remarks
|
|
775
|
+
* The full `Promise` method surface is required so the narrowed type is
|
|
776
|
+
* accurate: `then`, `catch`, and `finally` must all be functions. Thenables
|
|
777
|
+
* that only implement a subset are rejected.
|
|
778
|
+
*
|
|
779
|
+
* @example
|
|
780
|
+
* isPromise(Promise.resolve()); // true
|
|
781
|
+
* isPromise({ then: () => 1 }); // false
|
|
782
|
+
*
|
|
783
|
+
* @category Primitives
|
|
784
|
+
*/
|
|
785
|
+
const isPromise = (value) => {
|
|
786
|
+
if (!isNotNil(value)) return false;
|
|
787
|
+
const candidate = value;
|
|
788
|
+
return typeof candidate.then === "function" && typeof candidate.catch === "function" && typeof candidate.finally === "function";
|
|
789
|
+
};
|
|
790
|
+
//#endregion
|
|
791
|
+
//#region src/guards/isRegExp.ts
|
|
792
|
+
/**
|
|
793
|
+
* Checks if the provided value is an instance of RegExp.
|
|
794
|
+
*
|
|
795
|
+
* @param value - The value to check.
|
|
796
|
+
* @returns True if the value is a RegExp instance, false otherwise.
|
|
797
|
+
*
|
|
798
|
+
* @example
|
|
799
|
+
* isRegExp(/abc/); // true
|
|
800
|
+
* isRegExp('/abc/'); // false
|
|
801
|
+
*
|
|
802
|
+
* @category Primitives
|
|
803
|
+
*/
|
|
804
|
+
const isRegExp = (value) => {
|
|
805
|
+
return isNotNil(value) && value instanceof RegExp;
|
|
806
|
+
};
|
|
807
|
+
//#endregion
|
|
808
|
+
//#region src/guards/isSet.ts
|
|
809
|
+
/**
|
|
810
|
+
* Checks if the provided value is an instance of Set.
|
|
811
|
+
*
|
|
812
|
+
* @param value - The value to check.
|
|
813
|
+
* @returns True if the value is a Set instance, false otherwise.
|
|
814
|
+
*
|
|
815
|
+
* @remarks
|
|
816
|
+
* WeakSet instances return false.
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* isSet(new Set()); // true
|
|
820
|
+
* isSet([]); // false
|
|
821
|
+
*
|
|
822
|
+
* @category Primitives
|
|
823
|
+
*/
|
|
824
|
+
const isSet = (value) => {
|
|
825
|
+
return isNotNil(value) && value instanceof Set;
|
|
826
|
+
};
|
|
827
|
+
//#endregion
|
|
828
|
+
//#region src/guards/isSvgElement.ts
|
|
829
|
+
/**
|
|
830
|
+
* Checks if the provided value is an SVGElement.
|
|
831
|
+
*
|
|
832
|
+
* @param value - The value to check.
|
|
833
|
+
* @returns True if the value is an SVGElement, false otherwise.
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* isSvgElement(document.createElementNS('http://www.w3.org/2000/svg', 'svg')); // true
|
|
837
|
+
* isSvgElement(document.createElement('div')); // false
|
|
838
|
+
*
|
|
839
|
+
* @category DOM
|
|
840
|
+
*/
|
|
841
|
+
const isSvgElement = (value) => value instanceof SVGElement;
|
|
842
|
+
//#endregion
|
|
843
|
+
//#region src/guards/isSymbol.ts
|
|
844
|
+
/**
|
|
845
|
+
* Checks if the provided value is a symbol.
|
|
846
|
+
*
|
|
847
|
+
* @param value - The value to check.
|
|
848
|
+
* @returns True if the value is a symbol, false otherwise.
|
|
849
|
+
*
|
|
850
|
+
* @example
|
|
851
|
+
* isSymbol(Symbol('a')); // true
|
|
852
|
+
* isSymbol('symbol'); // false
|
|
853
|
+
*
|
|
854
|
+
* @category Primitives
|
|
855
|
+
*/
|
|
856
|
+
const isSymbol = (value) => typeof value === "symbol";
|
|
857
|
+
//#endregion
|
|
858
|
+
//#region src/guards/isTextNode.ts
|
|
859
|
+
/**
|
|
860
|
+
* Checks if the provided value is a Text node.
|
|
861
|
+
*
|
|
862
|
+
* @param value - The value to check.
|
|
863
|
+
* @returns True if the value is a Text node, false otherwise.
|
|
864
|
+
*
|
|
865
|
+
* @example
|
|
866
|
+
* isTextNode(document.createTextNode('hi')); // true
|
|
867
|
+
* isTextNode(document.createElement('div')); // false
|
|
868
|
+
*
|
|
869
|
+
* @category DOM
|
|
870
|
+
*/
|
|
871
|
+
const isTextNode = (value) => value.nodeName === "#text";
|
|
872
|
+
//#endregion
|
|
873
|
+
//#region src/guards/isTruthy.ts
|
|
874
|
+
/**
|
|
875
|
+
* Checks if the provided value is truthy.
|
|
876
|
+
*
|
|
877
|
+
* @param value - The value to check.
|
|
878
|
+
* @returns True if the value is truthy, false otherwise.
|
|
879
|
+
*
|
|
880
|
+
* @remarks
|
|
881
|
+
* The complement of `isFalsy`. Does not provide narrowing on its own;
|
|
882
|
+
* use `isNotNil` or a specific positive guard when narrowing matters.
|
|
883
|
+
*
|
|
884
|
+
* @example
|
|
885
|
+
* isTruthy(1); // true
|
|
886
|
+
* isTruthy(0); // false
|
|
887
|
+
*
|
|
888
|
+
* @category Truthy / falsy
|
|
889
|
+
*/
|
|
890
|
+
const isTruthy = (value) => !FALSY_VALUES.includes(value) && !Number.isNaN(value);
|
|
891
|
+
//#endregion
|
|
892
|
+
//#region src/guards/isWeakMap.ts
|
|
893
|
+
/**
|
|
894
|
+
* Checks if the provided value is an instance of WeakMap.
|
|
895
|
+
*
|
|
896
|
+
* @param value - The value to check.
|
|
897
|
+
* @returns True if the value is a WeakMap instance, false otherwise.
|
|
898
|
+
*
|
|
899
|
+
* @remarks
|
|
900
|
+
* Map instances return false. Narrows to `WeakMap<WeakKey, unknown>`, since
|
|
901
|
+
* weak collections accept non-registered symbols as keys (ES2023).
|
|
902
|
+
*
|
|
903
|
+
* @example
|
|
904
|
+
* isWeakMap(new WeakMap()); // true
|
|
905
|
+
* isWeakMap(new Map()); // false
|
|
906
|
+
*
|
|
907
|
+
* @category Primitives
|
|
908
|
+
*/
|
|
909
|
+
const isWeakMap = (value) => {
|
|
910
|
+
return isNotNil(value) && value instanceof WeakMap;
|
|
911
|
+
};
|
|
912
|
+
//#endregion
|
|
913
|
+
//#region src/guards/isWeakSet.ts
|
|
914
|
+
/**
|
|
915
|
+
* Checks if the provided value is an instance of WeakSet.
|
|
916
|
+
*
|
|
917
|
+
* @param value - The value to check.
|
|
918
|
+
* @returns True if the value is a WeakSet instance, false otherwise.
|
|
919
|
+
*
|
|
920
|
+
* @remarks
|
|
921
|
+
* Set instances return false. Narrows to `WeakSet<WeakKey>`, since weak
|
|
922
|
+
* collections accept non-registered symbols as members (ES2023).
|
|
923
|
+
*
|
|
924
|
+
* @example
|
|
925
|
+
* isWeakSet(new WeakSet()); // true
|
|
926
|
+
* isWeakSet(new Set()); // false
|
|
927
|
+
*
|
|
928
|
+
* @category Primitives
|
|
929
|
+
*/
|
|
930
|
+
const isWeakSet = (value) => {
|
|
931
|
+
return isNotNil(value) && value instanceof WeakSet;
|
|
932
|
+
};
|
|
933
|
+
//#endregion
|
|
934
|
+
//#region src/guards/readonlyArrayIncludes.ts
|
|
935
|
+
/**
|
|
936
|
+
* Checks if a readonly array includes the given element, with type narrowing.
|
|
937
|
+
*
|
|
938
|
+
* @param arr - The readonly array to search.
|
|
939
|
+
* @param elem - The element to search for.
|
|
940
|
+
* @returns True if the array includes the element, false otherwise.
|
|
941
|
+
*
|
|
942
|
+
* @example
|
|
943
|
+
* const arr = ['a', 'b'] as const;
|
|
944
|
+
* readonlyArrayIncludes(arr, 'a'); // true
|
|
945
|
+
* readonlyArrayIncludes(arr, 'z'); // false
|
|
946
|
+
*
|
|
947
|
+
* @category Arrays
|
|
948
|
+
*/
|
|
949
|
+
const readonlyArrayIncludes = (arr, elem) => arr.includes(elem);
|
|
950
|
+
//#endregion
|
|
951
|
+
//#region src/utilities/asyncNoOpFn.ts
|
|
952
|
+
/**
|
|
953
|
+
* An async function that does nothing. Useful as a default or placeholder
|
|
954
|
+
* callback where a promise is expected.
|
|
955
|
+
*
|
|
956
|
+
* @returns A promise that resolves to `undefined`.
|
|
957
|
+
*
|
|
958
|
+
* @example
|
|
959
|
+
* await asyncNoOpFn(); // undefined
|
|
960
|
+
*/
|
|
961
|
+
const asyncNoOpFn = async () => {};
|
|
962
|
+
//#endregion
|
|
963
|
+
//#region src/utilities/_constants.ts
|
|
964
|
+
const defaultLocales = ["en-US"];
|
|
965
|
+
const defaultTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
|
|
966
|
+
const milisecondsInSecond = 1e3;
|
|
967
|
+
const millisecondsInDay = 86400 * milisecondsInSecond;
|
|
968
|
+
//#endregion
|
|
969
|
+
//#region src/utilities/buildTimeDuration.ts
|
|
970
|
+
/**
|
|
971
|
+
* Returns an object representing a duration in seconds, minutes and hours.
|
|
972
|
+
*
|
|
973
|
+
* @param numberOfMilliseconds - The duration in milliseconds.
|
|
974
|
+
* @returns The duration split into hours, minutes and seconds.
|
|
975
|
+
*
|
|
976
|
+
* @example
|
|
977
|
+
* buildTimeDuration(3723000); // { hours: 1, minutes: 2, seconds: 3 }
|
|
978
|
+
*/
|
|
979
|
+
const buildTimeDuration = (numberOfMilliseconds) => {
|
|
980
|
+
const numberOfSeconds = Math.floor(numberOfMilliseconds / milisecondsInSecond);
|
|
981
|
+
const numberOfMinutes = Math.floor(numberOfSeconds / 60);
|
|
982
|
+
const numberOfHours = Math.floor(numberOfMinutes / 60);
|
|
983
|
+
return {
|
|
984
|
+
seconds: numberOfSeconds - numberOfMinutes * 60,
|
|
985
|
+
minutes: numberOfMinutes - numberOfHours * 60,
|
|
986
|
+
hours: numberOfHours
|
|
987
|
+
};
|
|
988
|
+
};
|
|
989
|
+
//#endregion
|
|
990
|
+
//#region src/utilities/coerceToBoolean.ts
|
|
991
|
+
/**
|
|
992
|
+
* Converts the boolean string values 'true' and 'false' into the
|
|
993
|
+
* corresponding boolean. Any other value is coerced with `Boolean()`.
|
|
994
|
+
*
|
|
995
|
+
* @param value - The value to coerce.
|
|
996
|
+
* @returns The value as a boolean.
|
|
997
|
+
*
|
|
998
|
+
* @example
|
|
999
|
+
* coerceToBoolean('true'); // true
|
|
1000
|
+
* coerceToBoolean('false'); // false
|
|
1001
|
+
* coerceToBoolean(0); // false
|
|
1002
|
+
*/
|
|
1003
|
+
const coerceToBoolean = (value) => {
|
|
1004
|
+
if (value === "true" || value === "false") return value === "true";
|
|
1005
|
+
return Boolean(value);
|
|
1006
|
+
};
|
|
1007
|
+
//#endregion
|
|
1008
|
+
//#region src/utilities/_isInvalidDate.ts
|
|
1009
|
+
/**
|
|
1010
|
+
* Checks whether a value is not a usable Date. Note that the Unix epoch
|
|
1011
|
+
* (getTime() === 0) is treated as invalid.
|
|
1012
|
+
*
|
|
1013
|
+
* @param date - The value to check.
|
|
1014
|
+
* @returns True if the value is not a Date, is an invalid Date, or is the Unix epoch.
|
|
1015
|
+
*/
|
|
1016
|
+
const isInvalidDate = (date) => {
|
|
1017
|
+
if (!isDate(date)) return true;
|
|
1018
|
+
const time = date.getTime();
|
|
1019
|
+
return time === 0 || Number.isNaN(time);
|
|
1020
|
+
};
|
|
1021
|
+
//#endregion
|
|
1022
|
+
//#region src/utilities/dateOnlyISOString.ts
|
|
1023
|
+
/**
|
|
1024
|
+
* Converts a Date object into a date only string formatted to ISO 8601,
|
|
1025
|
+
* e.g. '2021-06-03'.
|
|
1026
|
+
*
|
|
1027
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1028
|
+
* @param options - Formatting options.
|
|
1029
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1030
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1031
|
+
*
|
|
1032
|
+
* @example
|
|
1033
|
+
* dateOnlyISOString(new Date(2011, 11, 15)); // '2011-12-15'
|
|
1034
|
+
*/
|
|
1035
|
+
const dateOnlyISOString = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1036
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1037
|
+
try {
|
|
1038
|
+
const partsAsObject = new Intl.DateTimeFormat(defaultLocales, {
|
|
1039
|
+
year: "numeric",
|
|
1040
|
+
month: "2-digit",
|
|
1041
|
+
day: "2-digit",
|
|
1042
|
+
timeZone
|
|
1043
|
+
}).formatToParts(date).reduce((map, obj) => {
|
|
1044
|
+
map[obj.type] = obj.value;
|
|
1045
|
+
return map;
|
|
1046
|
+
}, {});
|
|
1047
|
+
return `${partsAsObject.year}-${partsAsObject.month}-${partsAsObject.day}`;
|
|
1048
|
+
} catch (error) {
|
|
1049
|
+
console.error(error);
|
|
1050
|
+
return "";
|
|
1051
|
+
}
|
|
1052
|
+
};
|
|
1053
|
+
//#endregion
|
|
1054
|
+
//#region src/utilities/dateOnlyString.ts
|
|
1055
|
+
/**
|
|
1056
|
+
* Converts a Date object into a date only string, e.g. 'Jun 3, 2021',
|
|
1057
|
+
* or 'Jun 3' if omitYear is true. Formats with the en-US locale.
|
|
1058
|
+
*
|
|
1059
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1060
|
+
* @param options - Formatting options.
|
|
1061
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1062
|
+
* @param options.omitYear - If true, display the date without the year.
|
|
1063
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1064
|
+
*
|
|
1065
|
+
* @example
|
|
1066
|
+
* dateOnlyString(new Date(2011, 11, 15)); // 'Dec 15, 2011'
|
|
1067
|
+
*/
|
|
1068
|
+
const dateOnlyString = (date, { timeZone = defaultTimeZone, omitYear = false } = {}) => {
|
|
1069
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1070
|
+
const formatOptions = omitYear ? {
|
|
1071
|
+
month: "short",
|
|
1072
|
+
day: "numeric",
|
|
1073
|
+
timeZone
|
|
1074
|
+
} : {
|
|
1075
|
+
dateStyle: "medium",
|
|
1076
|
+
timeZone
|
|
1077
|
+
};
|
|
1078
|
+
try {
|
|
1079
|
+
return new Intl.DateTimeFormat(defaultLocales, formatOptions).format(date);
|
|
1080
|
+
} catch (error) {
|
|
1081
|
+
console.error(error);
|
|
1082
|
+
return "";
|
|
1083
|
+
}
|
|
1084
|
+
};
|
|
1085
|
+
//#endregion
|
|
1086
|
+
//#region src/utilities/dateOnlyStringForSentence.ts
|
|
1087
|
+
/**
|
|
1088
|
+
* Converts a Date object into a date only string for use in a sentence,
|
|
1089
|
+
* e.g. 'June 3, 2021'. Formats with the en-US locale.
|
|
1090
|
+
*
|
|
1091
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1092
|
+
* @param options - Formatting options.
|
|
1093
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1094
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1095
|
+
*
|
|
1096
|
+
* @example
|
|
1097
|
+
* dateOnlyStringForSentence(new Date(2011, 11, 15)); // 'December 15, 2011'
|
|
1098
|
+
*/
|
|
1099
|
+
const dateOnlyStringForSentence = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1100
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1101
|
+
try {
|
|
1102
|
+
return new Intl.DateTimeFormat(defaultLocales, {
|
|
1103
|
+
dateStyle: "long",
|
|
1104
|
+
timeZone
|
|
1105
|
+
}).format(date);
|
|
1106
|
+
} catch (error) {
|
|
1107
|
+
console.error(error);
|
|
1108
|
+
return "";
|
|
1109
|
+
}
|
|
1110
|
+
};
|
|
1111
|
+
//#endregion
|
|
1112
|
+
//#region src/utilities/dateOnlyStringNumeric.ts
|
|
1113
|
+
/**
|
|
1114
|
+
* Converts a Date object into a date only string formatted numerically,
|
|
1115
|
+
* e.g. '06/03/2021'. Formats with the en-US locale.
|
|
1116
|
+
*
|
|
1117
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1118
|
+
* @param options - Formatting options.
|
|
1119
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1120
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1121
|
+
*
|
|
1122
|
+
* @example
|
|
1123
|
+
* dateOnlyStringNumeric(new Date(2011, 11, 15)); // '12/15/2011'
|
|
1124
|
+
*/
|
|
1125
|
+
const dateOnlyStringNumeric = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1126
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1127
|
+
try {
|
|
1128
|
+
return new Intl.DateTimeFormat(defaultLocales, {
|
|
1129
|
+
year: "numeric",
|
|
1130
|
+
month: "2-digit",
|
|
1131
|
+
day: "2-digit",
|
|
1132
|
+
timeZone
|
|
1133
|
+
}).format(date);
|
|
1134
|
+
} catch (error) {
|
|
1135
|
+
console.error(error);
|
|
1136
|
+
return "";
|
|
1137
|
+
}
|
|
1138
|
+
};
|
|
1139
|
+
//#endregion
|
|
1140
|
+
//#region src/utilities/dateTimeToDate.ts
|
|
1141
|
+
/**
|
|
1142
|
+
* Converts a plain object of date and time parts into a Date object.
|
|
1143
|
+
*
|
|
1144
|
+
* @param dateTime - The date parts to convert.
|
|
1145
|
+
* @returns The converted Date, or null if year, month, or dayOfMonth is missing.
|
|
1146
|
+
*
|
|
1147
|
+
* @example
|
|
1148
|
+
* dateTimeToDate({ year: 2015, month: 10, dayOfMonth: 7, hours: 4, minutes: 15 });
|
|
1149
|
+
* // new Date(2015, 10, 7, 4, 15)
|
|
1150
|
+
*/
|
|
1151
|
+
const dateTimeToDate = (dateTime) => {
|
|
1152
|
+
const { year, month, dayOfMonth, hours, minutes } = dateTime ?? {};
|
|
1153
|
+
if (year !== void 0 && month !== void 0 && dayOfMonth !== void 0) return new Date(year, month, dayOfMonth, hours ?? 0, minutes ?? 0);
|
|
1154
|
+
return null;
|
|
1155
|
+
};
|
|
1156
|
+
//#endregion
|
|
1157
|
+
//#region src/utilities/dateTimeToISO.ts
|
|
1158
|
+
/**
|
|
1159
|
+
* Converts a plain object of date and time parts into an ISO-8601 string.
|
|
1160
|
+
*
|
|
1161
|
+
* @param dateTime - The date parts to convert.
|
|
1162
|
+
* @returns The ISO-8601 string, or null if year, month, or dayOfMonth is missing.
|
|
1163
|
+
*
|
|
1164
|
+
* @example
|
|
1165
|
+
* dateTimeToISO({ year: 2015, month: 10, dayOfMonth: 7, hours: 4, minutes: 15 });
|
|
1166
|
+
* // '2015-11-07T04:15:00.000Z' (in UTC)
|
|
1167
|
+
*/
|
|
1168
|
+
const dateTimeToISO = (dateTime) => dateTimeToDate(dateTime)?.toISOString() ?? null;
|
|
1169
|
+
//#endregion
|
|
1170
|
+
//#region src/utilities/dateToDateTime.ts
|
|
1171
|
+
/**
|
|
1172
|
+
* Converts a Date object into a plain object of its local date and time parts.
|
|
1173
|
+
*
|
|
1174
|
+
* @param date - The Date object to convert.
|
|
1175
|
+
* @returns The date parts, or null if the value is not a Date.
|
|
1176
|
+
*
|
|
1177
|
+
* @example
|
|
1178
|
+
* dateToDateTime(new Date(2011, 11, 15, 3, 10));
|
|
1179
|
+
* // { year: 2011, month: 11, dayOfMonth: 15, hours: 3, minutes: 10 }
|
|
1180
|
+
*/
|
|
1181
|
+
const dateToDateTime = (date) => {
|
|
1182
|
+
if (!isDate(date)) return null;
|
|
1183
|
+
return {
|
|
1184
|
+
year: date.getFullYear(),
|
|
1185
|
+
month: date.getMonth(),
|
|
1186
|
+
dayOfMonth: date.getDate(),
|
|
1187
|
+
hours: date.getHours(),
|
|
1188
|
+
minutes: date.getMinutes()
|
|
1189
|
+
};
|
|
1190
|
+
};
|
|
1191
|
+
//#endregion
|
|
1192
|
+
//#region src/utilities/dateTimeRounded.ts
|
|
1193
|
+
/**
|
|
1194
|
+
* Rounds a Date up to the next half hour boundary.
|
|
1195
|
+
*
|
|
1196
|
+
* @param dateTime - The Date to round.
|
|
1197
|
+
* @param toISOString - Whether to return an ISO-8601 string instead of a Date.
|
|
1198
|
+
* @returns The rounded value as a string or Date depending on toISOString.
|
|
1199
|
+
*
|
|
1200
|
+
* @example
|
|
1201
|
+
* dateTimeRounded(new Date(2015, 10, 7, 0, 10)); // '2015-11-07T00:30:00.000Z' (in UTC)
|
|
1202
|
+
* dateTimeRounded(new Date(2015, 10, 7, 0, 40), false); // new Date(2015, 10, 7, 1, 0)
|
|
1203
|
+
*/
|
|
1204
|
+
const dateTimeRounded = (dateTime, toISOString = true) => {
|
|
1205
|
+
const first30Min = dateTime.getMinutes() <= 30;
|
|
1206
|
+
const hours = first30Min ? dateTime.getHours() : dateTime.getHours() + 1;
|
|
1207
|
+
const minutes = first30Min ? 30 : 0;
|
|
1208
|
+
const dateWithTime = {
|
|
1209
|
+
...dateToDateTime(dateTime),
|
|
1210
|
+
hours,
|
|
1211
|
+
minutes
|
|
1212
|
+
};
|
|
1213
|
+
if (toISOString) return dateTimeToISO(dateWithTime);
|
|
1214
|
+
return dateTimeToDate(dateWithTime);
|
|
1215
|
+
};
|
|
1216
|
+
//#endregion
|
|
1217
|
+
//#region src/utilities/dateTimeString.ts
|
|
1218
|
+
/**
|
|
1219
|
+
* Converts a Date, timestamp, or date string into a date and time string,
|
|
1220
|
+
* e.g. 'Jun 3, 2021, 11:52 AM'. Formats with the en-US locale.
|
|
1221
|
+
*
|
|
1222
|
+
* NOTE: you should probably use timeAgoString when trying to display
|
|
1223
|
+
* an updated/created timestamp.
|
|
1224
|
+
*
|
|
1225
|
+
* @param date - The Date, timestamp, or date string to format; gracefully handles any value.
|
|
1226
|
+
* @param options - Formatting options.
|
|
1227
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1228
|
+
* @param options.omitYear - If true, display the date without the year.
|
|
1229
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1230
|
+
*
|
|
1231
|
+
* @example
|
|
1232
|
+
* dateTimeString(new Date(2011, 11, 15, 7, 30)); // 'Dec 15, 2011, 7:30 AM'
|
|
1233
|
+
*/
|
|
1234
|
+
const dateTimeString = (date, { timeZone = defaultTimeZone, omitYear = false } = {}) => {
|
|
1235
|
+
if (isNil(date)) return "";
|
|
1236
|
+
const formattedDate = new Date(date);
|
|
1237
|
+
if (isInvalidDate(formattedDate)) return "";
|
|
1238
|
+
try {
|
|
1239
|
+
const baseOptions = {
|
|
1240
|
+
month: "short",
|
|
1241
|
+
day: "numeric",
|
|
1242
|
+
hour: "numeric",
|
|
1243
|
+
minute: "2-digit",
|
|
1244
|
+
timeZone
|
|
1245
|
+
};
|
|
1246
|
+
const options = omitYear ? baseOptions : {
|
|
1247
|
+
...baseOptions,
|
|
1248
|
+
year: "numeric"
|
|
1249
|
+
};
|
|
1250
|
+
return formattedDate.toLocaleDateString(defaultLocales, options);
|
|
1251
|
+
} catch (error) {
|
|
1252
|
+
console.error(error);
|
|
1253
|
+
return "";
|
|
1254
|
+
}
|
|
1255
|
+
};
|
|
1256
|
+
//#endregion
|
|
1257
|
+
//#region src/utilities/dateTimeStringForSentence.ts
|
|
1258
|
+
/**
|
|
1259
|
+
* Converts a Date object into a date and time string for use in a sentence,
|
|
1260
|
+
* e.g. 'June 3, 2021, 11:52 AM'. Formats with the en-US locale.
|
|
1261
|
+
*
|
|
1262
|
+
* NOTE: you should probably use timeAgoString when trying to display
|
|
1263
|
+
* an updated/created timestamp.
|
|
1264
|
+
*
|
|
1265
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1266
|
+
* @param options - Formatting options.
|
|
1267
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1268
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1269
|
+
*
|
|
1270
|
+
* @example
|
|
1271
|
+
* dateTimeStringForSentence(new Date(2011, 11, 15, 7, 30)); // 'December 15, 2011, 7:30 AM'
|
|
1272
|
+
*/
|
|
1273
|
+
const dateTimeStringForSentence = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1274
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1275
|
+
try {
|
|
1276
|
+
const { year, month, day, hour, minute, dayPeriod } = new Intl.DateTimeFormat(defaultLocales, {
|
|
1277
|
+
month: "long",
|
|
1278
|
+
day: "numeric",
|
|
1279
|
+
year: "numeric",
|
|
1280
|
+
hour: "numeric",
|
|
1281
|
+
minute: "2-digit",
|
|
1282
|
+
timeZone
|
|
1283
|
+
}).formatToParts(date).reduce((acc, part) => {
|
|
1284
|
+
if (part.type !== "literal") acc[part.type] = part.value;
|
|
1285
|
+
return acc;
|
|
1286
|
+
}, {});
|
|
1287
|
+
return `${month} ${day}, ${year}, ${hour}:${minute} ${dayPeriod}`;
|
|
1288
|
+
} catch (error) {
|
|
1289
|
+
console.error(error);
|
|
1290
|
+
return "";
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
//#endregion
|
|
1294
|
+
//#region src/utilities/dateUTCOffset.ts
|
|
1295
|
+
/**
|
|
1296
|
+
* Given a date, shows the UTC offset of the current time zone, e.g. '+02:00'.
|
|
1297
|
+
*
|
|
1298
|
+
* @param date - The Date to calculate the offset for.
|
|
1299
|
+
* @returns The UTC offset string.
|
|
1300
|
+
*
|
|
1301
|
+
* @example
|
|
1302
|
+
* dateUTCOffset(new Date(2011, 11, 15)); // '+00:00' (when run in UTC)
|
|
1303
|
+
*/
|
|
1304
|
+
const dateUTCOffset = (date) => {
|
|
1305
|
+
const offsetInHours = date.getTimezoneOffset() / 60 * -1;
|
|
1306
|
+
const hours = Math.round(offsetInHours);
|
|
1307
|
+
const minutes = (offsetInHours - hours) * 60;
|
|
1308
|
+
return `${hours >= 0 ? "+" : ""}${`${hours}`.padStart(2, "0")}:${`${minutes}`.padStart(2, "0")}`;
|
|
1309
|
+
};
|
|
1310
|
+
//#endregion
|
|
1311
|
+
//#region src/utilities/dayOfWeekString.ts
|
|
1312
|
+
/**
|
|
1313
|
+
* Converts a Date object into a day of the week string, e.g. 'Thursday'.
|
|
1314
|
+
* Formats with the en-US locale.
|
|
1315
|
+
*
|
|
1316
|
+
* @param date - The Date to format.
|
|
1317
|
+
* @param options - Formatting options.
|
|
1318
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1319
|
+
* @returns The formatted day of the week, or '' for null or invalid input.
|
|
1320
|
+
*
|
|
1321
|
+
* @example
|
|
1322
|
+
* dayOfWeekString(new Date(2011, 11, 15)); // 'Thursday'
|
|
1323
|
+
*/
|
|
1324
|
+
const dayOfWeekString = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1325
|
+
if (date === null) return "";
|
|
1326
|
+
try {
|
|
1327
|
+
return new Intl.DateTimeFormat(defaultLocales, {
|
|
1328
|
+
weekday: "long",
|
|
1329
|
+
timeZone
|
|
1330
|
+
}).format(date);
|
|
1331
|
+
} catch (error) {
|
|
1332
|
+
console.error(error);
|
|
1333
|
+
return "";
|
|
1334
|
+
}
|
|
1335
|
+
};
|
|
1336
|
+
//#endregion
|
|
1337
|
+
//#region src/utilities/deepMerge.ts
|
|
1338
|
+
const UNSAFE_KEYS = /* @__PURE__ */ new Set([
|
|
1339
|
+
"__proto__",
|
|
1340
|
+
"constructor",
|
|
1341
|
+
"prototype"
|
|
1342
|
+
]);
|
|
1343
|
+
const cloneIfArray = (value) => isArray(value) ? [...value] : value;
|
|
1344
|
+
/**
|
|
1345
|
+
* Deeply merges objects without mutating them, inferring the return type from
|
|
1346
|
+
* the inputs. Plain objects are merged recursively, arrays are concatenated
|
|
1347
|
+
* with duplicate items removed (by reference for object items), and all other
|
|
1348
|
+
* values (including class instances) are overwritten in the order of the
|
|
1349
|
+
* provided arguments. The prototype-polluting keys `__proto__`, `constructor`,
|
|
1350
|
+
* and `prototype` are dropped. Symbol-keyed properties are not merged.
|
|
1351
|
+
*
|
|
1352
|
+
* @param objects - The objects to merge.
|
|
1353
|
+
* @returns A new object containing the merged result.
|
|
1354
|
+
* @throws TypeError when an argument is an array.
|
|
1355
|
+
*
|
|
1356
|
+
* @example
|
|
1357
|
+
* deepMerge({ a: { b: 1 }, c: [1] }, { a: { d: 2 }, c: [2] });
|
|
1358
|
+
* // { a: { b: 1, d: 2 }, c: [1, 2] }
|
|
1359
|
+
*/
|
|
1360
|
+
const deepMerge = (...objects) => {
|
|
1361
|
+
const result = {};
|
|
1362
|
+
for (const current of objects) {
|
|
1363
|
+
if (isArray(current)) throw new TypeError("Arguments provided to deepMerge must be objects, not arrays.");
|
|
1364
|
+
const safeKeys = Object.keys(current).filter((key) => !UNSAFE_KEYS.has(key));
|
|
1365
|
+
for (const key of safeKeys) {
|
|
1366
|
+
const existing = result[key];
|
|
1367
|
+
const incoming = current[key];
|
|
1368
|
+
if (isArray(existing) && isArray(incoming)) result[key] = [.../* @__PURE__ */ new Set([...existing, ...incoming])];
|
|
1369
|
+
else if (isPlainObject(existing) && isPlainObject(incoming)) result[key] = deepMerge(existing, incoming);
|
|
1370
|
+
else if (isPlainObject(incoming)) result[key] = deepMerge(incoming);
|
|
1371
|
+
else result[key] = cloneIfArray(incoming);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
return result;
|
|
1375
|
+
};
|
|
1376
|
+
//#endregion
|
|
1377
|
+
//#region src/utilities/getObjectEntries.ts
|
|
1378
|
+
/**
|
|
1379
|
+
* Returns the entries of an object with strongly typed keys and values.
|
|
1380
|
+
*
|
|
1381
|
+
* @param obj - The object to get entries from.
|
|
1382
|
+
* @returns An array of [key, value] tuples.
|
|
1383
|
+
*
|
|
1384
|
+
* @example
|
|
1385
|
+
* getObjectEntries({ a: 1, b: 2 }); // [['a', 1], ['b', 2]]
|
|
1386
|
+
*/
|
|
1387
|
+
const getObjectEntries = (obj) => Object.keys(obj).map((key) => [key, obj[key]]);
|
|
1388
|
+
//#endregion
|
|
1389
|
+
//#region src/utilities/getObjectKeys.ts
|
|
1390
|
+
/**
|
|
1391
|
+
* Returns the keys of an object with strongly typed key type.
|
|
1392
|
+
*
|
|
1393
|
+
* @param obj - The object to get keys from.
|
|
1394
|
+
* @returns An array of the object's keys.
|
|
1395
|
+
*
|
|
1396
|
+
* @example
|
|
1397
|
+
* getObjectKeys({ a: 1, b: 2 }); // ['a', 'b']
|
|
1398
|
+
*/
|
|
1399
|
+
const getObjectKeys = (obj) => Object.keys(obj);
|
|
1400
|
+
//#endregion
|
|
1401
|
+
//#region src/utilities/getObjectValues.ts
|
|
1402
|
+
/**
|
|
1403
|
+
* Returns the values of an object with strongly typed value type.
|
|
1404
|
+
*
|
|
1405
|
+
* @param obj - The object to get values from.
|
|
1406
|
+
* @returns An array of the object's values.
|
|
1407
|
+
*
|
|
1408
|
+
* @example
|
|
1409
|
+
* getObjectValues({ a: 1, b: 2 }); // [1, 2]
|
|
1410
|
+
*/
|
|
1411
|
+
const getObjectValues = (obj) => Object.values(obj);
|
|
1412
|
+
//#endregion
|
|
1413
|
+
//#region src/utilities/isEmail.ts
|
|
1414
|
+
/**
|
|
1415
|
+
* Loosely validates an email address string.
|
|
1416
|
+
*
|
|
1417
|
+
* @param value - The string to validate.
|
|
1418
|
+
* @returns True if the string appears to be an email address, false otherwise.
|
|
1419
|
+
*
|
|
1420
|
+
* @example
|
|
1421
|
+
* isEmail('user@example.com'); // true
|
|
1422
|
+
* isEmail('user@example'); // false
|
|
1423
|
+
*/
|
|
1424
|
+
const isEmail = (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value);
|
|
1425
|
+
//#endregion
|
|
1426
|
+
//#region src/utilities/isUrl.ts
|
|
1427
|
+
/**
|
|
1428
|
+
* Loosely validates a URL string.
|
|
1429
|
+
*
|
|
1430
|
+
* @param value - The value to validate.
|
|
1431
|
+
* @returns True if the value appears to be a URL, false otherwise.
|
|
1432
|
+
*
|
|
1433
|
+
* @example
|
|
1434
|
+
* isUrl('https://wistia.com'); // true
|
|
1435
|
+
* isUrl('not a url'); // false
|
|
1436
|
+
*/
|
|
1437
|
+
const isUrl = (value) => {
|
|
1438
|
+
const protocolAndDomain = /^(?:\w+:)?\/\/(\S+)$/;
|
|
1439
|
+
const localhostDomain = /^localhost[:?\d]*(?:[^:?\d]\S*)?$/;
|
|
1440
|
+
const nonLocalhostDomain = /^[^\s.]+\.\S{2,}$/;
|
|
1441
|
+
if (!isString(value)) return false;
|
|
1442
|
+
const remaining = protocolAndDomain.exec(value)?.[1];
|
|
1443
|
+
if (remaining === void 0) return false;
|
|
1444
|
+
return localhostDomain.test(remaining) || nonLocalhostDomain.test(remaining);
|
|
1445
|
+
};
|
|
1446
|
+
//#endregion
|
|
1447
|
+
//#region src/utilities/_padTimeInteger.ts
|
|
1448
|
+
/**
|
|
1449
|
+
* Pads an integer with a leading zero.
|
|
1450
|
+
*
|
|
1451
|
+
* @param num - The integer to pad.
|
|
1452
|
+
* @returns The integer as a string with padding.
|
|
1453
|
+
*/
|
|
1454
|
+
const padTimeInteger = (num) => num.toString().padStart(2, "0");
|
|
1455
|
+
//#endregion
|
|
1456
|
+
//#region src/utilities/mediaDurationString.ts
|
|
1457
|
+
/**
|
|
1458
|
+
* A string representation of a duration for a media. Assumes most medias
|
|
1459
|
+
* are under an hour so only shows hours if media is over an hour.
|
|
1460
|
+
*
|
|
1461
|
+
* @param numberOfMilliseconds - The duration in milliseconds.
|
|
1462
|
+
* @returns The formatted duration string.
|
|
1463
|
+
*
|
|
1464
|
+
* @example
|
|
1465
|
+
* mediaDurationString(234230); // '3:54'
|
|
1466
|
+
* mediaDurationString(23423000); // '6:30:23'
|
|
1467
|
+
*/
|
|
1468
|
+
const mediaDurationString = (numberOfMilliseconds) => {
|
|
1469
|
+
const { hours, minutes, seconds } = buildTimeDuration(numberOfMilliseconds);
|
|
1470
|
+
if (hours < 1) return `${minutes}:${padTimeInteger(seconds)}`;
|
|
1471
|
+
return `${hours}:${padTimeInteger(minutes)}:${padTimeInteger(seconds)}`;
|
|
1472
|
+
};
|
|
1473
|
+
//#endregion
|
|
1474
|
+
//#region src/utilities/millisecondsToDurationISOString.ts
|
|
1475
|
+
/**
|
|
1476
|
+
* Given a number of milliseconds, returns the ISO 8601 duration string
|
|
1477
|
+
* rounded down from the number of seconds.
|
|
1478
|
+
*
|
|
1479
|
+
* @param numberOfMilliseconds - The duration in milliseconds.
|
|
1480
|
+
* @returns The ISO 8601 duration string.
|
|
1481
|
+
*
|
|
1482
|
+
* @example
|
|
1483
|
+
* millisecondsToDurationISOString(33000); // 'PT33S'
|
|
1484
|
+
* millisecondsToDurationISOString(3605000); // 'PT1H5S'
|
|
1485
|
+
*/
|
|
1486
|
+
const millisecondsToDurationISOString = (numberOfMilliseconds) => {
|
|
1487
|
+
const { seconds, minutes, hours } = buildTimeDuration(numberOfMilliseconds);
|
|
1488
|
+
let string = "PT";
|
|
1489
|
+
if (hours) string += `${hours}H`;
|
|
1490
|
+
if (minutes) string += `${minutes}M`;
|
|
1491
|
+
if (seconds) string += `${seconds}S`;
|
|
1492
|
+
if (!(seconds || minutes || hours)) string += "0S";
|
|
1493
|
+
return string;
|
|
1494
|
+
};
|
|
1495
|
+
//#endregion
|
|
1496
|
+
//#region src/utilities/monthDayStringNumeric.ts
|
|
1497
|
+
/**
|
|
1498
|
+
* Converts a Date object into a month and day string formatted numerically,
|
|
1499
|
+
* e.g. '06/03'. Formats with the en-US locale.
|
|
1500
|
+
*
|
|
1501
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1502
|
+
* @param options - Formatting options.
|
|
1503
|
+
* @param options.timeZone - The time zone to display the date in. Defaults to the current time zone.
|
|
1504
|
+
* @returns The formatted date string, or '' for invalid input.
|
|
1505
|
+
*
|
|
1506
|
+
* @example
|
|
1507
|
+
* monthDayStringNumeric(new Date(2011, 11, 15)); // '12/15'
|
|
1508
|
+
*/
|
|
1509
|
+
const monthDayStringNumeric = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1510
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1511
|
+
try {
|
|
1512
|
+
return new Intl.DateTimeFormat(defaultLocales, {
|
|
1513
|
+
month: "2-digit",
|
|
1514
|
+
day: "2-digit",
|
|
1515
|
+
timeZone
|
|
1516
|
+
}).format(date);
|
|
1517
|
+
} catch (error) {
|
|
1518
|
+
console.error(error);
|
|
1519
|
+
return "";
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
//#endregion
|
|
1523
|
+
//#region src/utilities/noOpFn.ts
|
|
1524
|
+
/**
|
|
1525
|
+
* A function that does nothing. Useful as a default or placeholder callback.
|
|
1526
|
+
*
|
|
1527
|
+
* @returns Nothing.
|
|
1528
|
+
*
|
|
1529
|
+
* @example
|
|
1530
|
+
* noOpFn(); // undefined
|
|
1531
|
+
*/
|
|
1532
|
+
const noOpFn = () => {};
|
|
1533
|
+
//#endregion
|
|
1534
|
+
//#region src/utilities/noOpFnReturningFalse.ts
|
|
1535
|
+
/**
|
|
1536
|
+
* A function that does nothing and returns `false`. Useful as a default or
|
|
1537
|
+
* placeholder callback where `false` is expected.
|
|
1538
|
+
*
|
|
1539
|
+
* @returns `false`.
|
|
1540
|
+
*
|
|
1541
|
+
* @example
|
|
1542
|
+
* noOpFnReturningFalse(); // false
|
|
1543
|
+
*/
|
|
1544
|
+
const noOpFnReturningFalse = () => false;
|
|
1545
|
+
//#endregion
|
|
1546
|
+
//#region src/utilities/noOpFnReturningNull.ts
|
|
1547
|
+
/**
|
|
1548
|
+
* A function that does nothing and returns `null`. Useful as a default or
|
|
1549
|
+
* placeholder callback where `null` is expected.
|
|
1550
|
+
*
|
|
1551
|
+
* @returns `null`.
|
|
1552
|
+
*
|
|
1553
|
+
* @example
|
|
1554
|
+
* noOpFnReturningNull(); // null
|
|
1555
|
+
*/
|
|
1556
|
+
const noOpFnReturningNull = () => null;
|
|
1557
|
+
//#endregion
|
|
1558
|
+
//#region src/utilities/parseDateString.ts
|
|
1559
|
+
const MONTH_NAMES = [
|
|
1560
|
+
"January",
|
|
1561
|
+
"February",
|
|
1562
|
+
"March",
|
|
1563
|
+
"April",
|
|
1564
|
+
"May",
|
|
1565
|
+
"June",
|
|
1566
|
+
"July",
|
|
1567
|
+
"August",
|
|
1568
|
+
"September",
|
|
1569
|
+
"October",
|
|
1570
|
+
"November",
|
|
1571
|
+
"December"
|
|
1572
|
+
];
|
|
1573
|
+
const MONTH_ABBREVIATIONS = MONTH_NAMES.map((name) => name.slice(0, 3));
|
|
1574
|
+
const startOfDay = (date) => {
|
|
1575
|
+
const result = new Date(date);
|
|
1576
|
+
result.setHours(0, 0, 0, 0);
|
|
1577
|
+
return result;
|
|
1578
|
+
};
|
|
1579
|
+
const startOfToday = () => startOfDay(/* @__PURE__ */ new Date());
|
|
1580
|
+
const startOfTomorrow = () => {
|
|
1581
|
+
const tomorrow = startOfToday();
|
|
1582
|
+
tomorrow.setDate(tomorrow.getDate() + 1);
|
|
1583
|
+
return tomorrow;
|
|
1584
|
+
};
|
|
1585
|
+
const monthIndexIn = (names, value) => names.findIndex((name) => name.toLowerCase() === value.toLowerCase());
|
|
1586
|
+
const toNumberOrUndefined = (value) => value === void 0 ? void 0 : Number(value);
|
|
1587
|
+
const DATE_PATTERNS = [
|
|
1588
|
+
{
|
|
1589
|
+
regex: new RegExp(`^(${MONTH_ABBREVIATIONS.join("|")}) (\\d{1,2})(?:, (\\d{1,4}))?$`, "i"),
|
|
1590
|
+
toParts: ([, month, day, year]) => ({
|
|
1591
|
+
monthIndex: monthIndexIn(MONTH_ABBREVIATIONS, String(month)),
|
|
1592
|
+
day: Number(day),
|
|
1593
|
+
year: toNumberOrUndefined(year)
|
|
1594
|
+
})
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
regex: new RegExp(`^(${MONTH_NAMES.join("|")}) (\\d{1,2})(?:, (\\d{1,4}))?$`, "i"),
|
|
1598
|
+
toParts: ([, month, day, year]) => ({
|
|
1599
|
+
monthIndex: monthIndexIn(MONTH_NAMES, String(month)),
|
|
1600
|
+
day: Number(day),
|
|
1601
|
+
year: toNumberOrUndefined(year)
|
|
1602
|
+
})
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
regex: /^(\d{1,2})\/(\d{1,2})(?:\/(\d{1,4}))?$/,
|
|
1606
|
+
toParts: ([, month, day, year]) => ({
|
|
1607
|
+
monthIndex: Number(month) - 1,
|
|
1608
|
+
day: Number(day),
|
|
1609
|
+
year: toNumberOrUndefined(year)
|
|
1610
|
+
})
|
|
1611
|
+
},
|
|
1612
|
+
{
|
|
1613
|
+
regex: /^(\d{1,2})-(\d{1,2})(?:-(\d{1,4}))?$/,
|
|
1614
|
+
toParts: ([, month, day, year]) => ({
|
|
1615
|
+
monthIndex: Number(month) - 1,
|
|
1616
|
+
day: Number(day),
|
|
1617
|
+
year: toNumberOrUndefined(year)
|
|
1618
|
+
})
|
|
1619
|
+
},
|
|
1620
|
+
{
|
|
1621
|
+
regex: /^(\d{1,4})-(\d{1,2})-(\d{1,2})$/,
|
|
1622
|
+
toParts: ([, year, month, day]) => ({
|
|
1623
|
+
monthIndex: Number(month) - 1,
|
|
1624
|
+
day: Number(day),
|
|
1625
|
+
year: Number(year)
|
|
1626
|
+
})
|
|
1627
|
+
}
|
|
1628
|
+
];
|
|
1629
|
+
const toValidDate = ({ monthIndex, day, year }, referenceDate) => {
|
|
1630
|
+
if (year === 0) return null;
|
|
1631
|
+
const resolvedYear = year ?? referenceDate.getFullYear();
|
|
1632
|
+
const date = new Date(referenceDate.getFullYear(), 0, 1);
|
|
1633
|
+
date.setFullYear(resolvedYear, monthIndex, day);
|
|
1634
|
+
return date.getFullYear() === resolvedYear && date.getMonth() === monthIndex && date.getDate() === day ? date : null;
|
|
1635
|
+
};
|
|
1636
|
+
/**
|
|
1637
|
+
* Loosely parses a human-entered date string into a Date at local midnight.
|
|
1638
|
+
* Accepts 'today' and 'tomorrow', month name prefixes (resolved to the next
|
|
1639
|
+
* 1st of that month), and common numeric and month-name formats, with a
|
|
1640
|
+
* native Date fallback for anything else.
|
|
1641
|
+
*
|
|
1642
|
+
* @param str - The date string to parse.
|
|
1643
|
+
* @returns The parsed Date at local midnight, or null if unparseable.
|
|
1644
|
+
*
|
|
1645
|
+
* @example
|
|
1646
|
+
* parseDateString('Jan 5, 2025'); // Date for 2025-01-05 at local midnight
|
|
1647
|
+
* parseDateString('garbage'); // null
|
|
1648
|
+
*/
|
|
1649
|
+
const parseDateString = (str) => {
|
|
1650
|
+
const trimmed = str.trim();
|
|
1651
|
+
if (!trimmed) return null;
|
|
1652
|
+
const lower = trimmed.toLowerCase();
|
|
1653
|
+
if (lower === "today") return startOfToday();
|
|
1654
|
+
if (lower === "tomorrow") return startOfTomorrow();
|
|
1655
|
+
const monthIndex = MONTH_NAMES.findIndex((name) => name.toLowerCase().startsWith(lower));
|
|
1656
|
+
if (monthIndex !== -1) {
|
|
1657
|
+
const today = startOfToday();
|
|
1658
|
+
let date = new Date(today.getFullYear(), monthIndex, 1);
|
|
1659
|
+
if (date.getTime() < Date.now()) date = new Date(today.getFullYear() + 1, monthIndex, 1);
|
|
1660
|
+
return date;
|
|
1661
|
+
}
|
|
1662
|
+
const referenceDate = /* @__PURE__ */ new Date();
|
|
1663
|
+
for (const { regex, toParts } of DATE_PATTERNS) {
|
|
1664
|
+
const match = trimmed.match(regex);
|
|
1665
|
+
if (match) {
|
|
1666
|
+
const parsed = toValidDate(toParts(match), referenceDate);
|
|
1667
|
+
if (parsed !== null) return parsed;
|
|
1668
|
+
}
|
|
1669
|
+
}
|
|
1670
|
+
const native = new Date(trimmed);
|
|
1671
|
+
if (!Number.isNaN(native.getTime())) return startOfDay(native);
|
|
1672
|
+
return null;
|
|
1673
|
+
};
|
|
1674
|
+
//#endregion
|
|
1675
|
+
//#region src/utilities/sessionDurationString.ts
|
|
1676
|
+
/**
|
|
1677
|
+
* A string representation of a duration for a user session. Assumes that
|
|
1678
|
+
* sessions may or may not be more than an hour. To prevent confusion all
|
|
1679
|
+
* times show hours, even those that are less than an hour.
|
|
1680
|
+
*
|
|
1681
|
+
* @param numberOfMilliseconds - The duration in milliseconds.
|
|
1682
|
+
* @returns The formatted duration string.
|
|
1683
|
+
*
|
|
1684
|
+
* @example
|
|
1685
|
+
* sessionDurationString(2342300); // '0:39:02'
|
|
1686
|
+
*/
|
|
1687
|
+
const sessionDurationString = (numberOfMilliseconds) => {
|
|
1688
|
+
const { hours, minutes, seconds } = buildTimeDuration(numberOfMilliseconds);
|
|
1689
|
+
return `${hours}:${padTimeInteger(minutes)}:${padTimeInteger(seconds)}`;
|
|
1690
|
+
};
|
|
1691
|
+
//#endregion
|
|
1692
|
+
//#region src/utilities/stripExtension.ts
|
|
1693
|
+
/**
|
|
1694
|
+
* Removes the file extension from a string.
|
|
1695
|
+
*
|
|
1696
|
+
* @param str - The string to remove the file extension from.
|
|
1697
|
+
* @returns The string without its file extension.
|
|
1698
|
+
*
|
|
1699
|
+
* @example
|
|
1700
|
+
* stripExtension('config.yml'); // 'config'
|
|
1701
|
+
* stripExtension('example.test.ts'); // 'example.test'
|
|
1702
|
+
*/
|
|
1703
|
+
const stripExtension = (str) => (str || "").replace(/\.[^/.]{1,4}$/, "");
|
|
1704
|
+
//#endregion
|
|
1705
|
+
//#region src/utilities/_getLocalDateParts.ts
|
|
1706
|
+
/**
|
|
1707
|
+
* Get local year, month, day parts from a Date using Intl.DateTimeFormat.
|
|
1708
|
+
* This ignores the time of day (hours, minutes, etc.).
|
|
1709
|
+
*
|
|
1710
|
+
* @param date - The Date to extract parts from.
|
|
1711
|
+
* @returns The numeric year, month (1-based) and day.
|
|
1712
|
+
*/
|
|
1713
|
+
const getLocalDateParts = (date) => {
|
|
1714
|
+
const partsAsObject = new Intl.DateTimeFormat(defaultLocales, {
|
|
1715
|
+
year: "numeric",
|
|
1716
|
+
month: "numeric",
|
|
1717
|
+
day: "numeric",
|
|
1718
|
+
timeZone: defaultTimeZone
|
|
1719
|
+
}).formatToParts(date).reduce((map, part) => {
|
|
1720
|
+
map[part.type] = part.value;
|
|
1721
|
+
return map;
|
|
1722
|
+
}, {});
|
|
1723
|
+
return {
|
|
1724
|
+
year: Math.trunc(Number(partsAsObject.year)),
|
|
1725
|
+
month: Math.trunc(Number(partsAsObject.month)),
|
|
1726
|
+
day: Math.trunc(Number(partsAsObject.day))
|
|
1727
|
+
};
|
|
1728
|
+
};
|
|
1729
|
+
//#endregion
|
|
1730
|
+
//#region src/utilities/_differenceInCalendarDays.ts
|
|
1731
|
+
/**
|
|
1732
|
+
* Returns the difference in calendar days between two dates in local time,
|
|
1733
|
+
* ignoring the time of day.
|
|
1734
|
+
*
|
|
1735
|
+
* @param dateLeft - The later date (or timestamp).
|
|
1736
|
+
* @param dateRight - The earlier date (or timestamp).
|
|
1737
|
+
* @returns The number of calendar days (can be negative).
|
|
1738
|
+
*/
|
|
1739
|
+
const differenceInCalendarDays = (dateLeft, dateRight) => {
|
|
1740
|
+
const leftDate = isDate(dateLeft) ? dateLeft : new Date(dateLeft);
|
|
1741
|
+
const rightDate = isDate(dateRight) ? dateRight : new Date(dateRight);
|
|
1742
|
+
if (Number.isNaN(leftDate.getTime()) || Number.isNaN(rightDate.getTime())) return NaN;
|
|
1743
|
+
const { year: y1, month: m1, day: d1 } = getLocalDateParts(leftDate);
|
|
1744
|
+
const { year: y2, month: m2, day: d2 } = getLocalDateParts(rightDate);
|
|
1745
|
+
const msDiff = new Date(y1, m1 - 1, d1).getTime() - new Date(y2, m2 - 1, d2).getTime();
|
|
1746
|
+
return Math.round(msDiff / millisecondsInDay);
|
|
1747
|
+
};
|
|
1748
|
+
//#endregion
|
|
1749
|
+
//#region src/utilities/timeAgoString.ts
|
|
1750
|
+
/**
|
|
1751
|
+
* Shows time ago relative to current time, e.g. '< 1 minute ago',
|
|
1752
|
+
* '33 minutes ago', 'Today, 3:30 PM', 'Yesterday, 6:22 AM',
|
|
1753
|
+
* 'Nov 11, 11:32 AM', or 'Feb 23, 2020, 1:55 PM'. When includeTime is
|
|
1754
|
+
* false: 'Today', 'Yesterday', 'on Thursday', 'Nov 11', 'Feb 23, 2020'.
|
|
1755
|
+
*
|
|
1756
|
+
* NOTE: timeAgoString doesn't support multiple time zones since doing so
|
|
1757
|
+
* would complicate calculations for whether to use "Today" or "Yesterday".
|
|
1758
|
+
*
|
|
1759
|
+
* @param date - The date relative to now.
|
|
1760
|
+
* @param options - Formatting options.
|
|
1761
|
+
* @param options.nowAnchor - The date used to calculate relative to now. Defaults to the current date but can be overwritten for tests.
|
|
1762
|
+
* @param options.includeTime - Whether to include the time in the output. Defaults to true.
|
|
1763
|
+
* @returns The relative date string, or '' for invalid input.
|
|
1764
|
+
*
|
|
1765
|
+
* @example
|
|
1766
|
+
* timeAgoString(new Date(Date.now() - 30_000)); // '< 1 minute ago'
|
|
1767
|
+
*/
|
|
1768
|
+
const timeAgoString = (date, { nowAnchor = /* @__PURE__ */ new Date(), includeTime = true } = {}) => {
|
|
1769
|
+
if (isInvalidDate(date)) return "";
|
|
1770
|
+
const minutesAgo = (nowAnchor.valueOf() - date.valueOf()) / (60 * milisecondsInSecond);
|
|
1771
|
+
const minutesAgoRounded = Math.round(minutesAgo);
|
|
1772
|
+
const differenceInDays = differenceInCalendarDays(nowAnchor, date);
|
|
1773
|
+
const isSameYear = date.getFullYear() === nowAnchor.getFullYear();
|
|
1774
|
+
if (minutesAgo < 0) return dateTimeString(date);
|
|
1775
|
+
if (minutesAgo < 1) return "< 1 minute ago";
|
|
1776
|
+
if (minutesAgoRounded < 2) return "1 minute ago";
|
|
1777
|
+
if (minutesAgoRounded <= 60) return `${minutesAgoRounded} minutes ago`;
|
|
1778
|
+
if (differenceInDays === 0) return includeTime ? `Today, ${Intl.DateTimeFormat(defaultLocales, {
|
|
1779
|
+
hour: "numeric",
|
|
1780
|
+
minute: "2-digit"
|
|
1781
|
+
}).format(date)}` : "Today";
|
|
1782
|
+
if (differenceInDays === 1) return includeTime ? `Yesterday, ${Intl.DateTimeFormat(defaultLocales, {
|
|
1783
|
+
hour: "numeric",
|
|
1784
|
+
minute: "2-digit"
|
|
1785
|
+
}).format(date)}` : "Yesterday";
|
|
1786
|
+
if (differenceInDays < 7 && !includeTime) return `on ${dayOfWeekString(date)}`;
|
|
1787
|
+
return includeTime ? dateTimeString(date, { omitYear: isSameYear }) : dateOnlyString(date, { omitYear: false });
|
|
1788
|
+
};
|
|
1789
|
+
//#endregion
|
|
1790
|
+
//#region src/utilities/timeOnlyString.ts
|
|
1791
|
+
/**
|
|
1792
|
+
* Converts a Date object into a time only string, e.g. '7:30 AM'.
|
|
1793
|
+
* Formats with the en-US locale.
|
|
1794
|
+
*
|
|
1795
|
+
* @param date - The Date to format; gracefully handles any value.
|
|
1796
|
+
* @param options - Formatting options.
|
|
1797
|
+
* @param options.timeZone - The time zone to display the time in. Defaults to the current time zone.
|
|
1798
|
+
* @returns The formatted time string, or '' for invalid input.
|
|
1799
|
+
*
|
|
1800
|
+
* @example
|
|
1801
|
+
* timeOnlyString(new Date(2011, 11, 15, 7, 30)); // '7:30 AM'
|
|
1802
|
+
*/
|
|
1803
|
+
const timeOnlyString = (date, { timeZone = defaultTimeZone } = {}) => {
|
|
1804
|
+
if (!isDate(date) || isInvalidDate(date)) return "";
|
|
1805
|
+
try {
|
|
1806
|
+
return new Intl.DateTimeFormat(defaultLocales, {
|
|
1807
|
+
timeStyle: "short",
|
|
1808
|
+
timeZone
|
|
1809
|
+
}).format(date);
|
|
1810
|
+
} catch (error) {
|
|
1811
|
+
console.error(error);
|
|
1812
|
+
return "";
|
|
1813
|
+
}
|
|
1814
|
+
};
|
|
1815
|
+
//#endregion
|
|
1816
|
+
//#region src/utilities/tupleIncludes.ts
|
|
1817
|
+
/**
|
|
1818
|
+
* Checks if a tuple includes the given item, with type narrowing.
|
|
1819
|
+
*
|
|
1820
|
+
* @param tuple - The tuple to search.
|
|
1821
|
+
* @param item - The item to search for.
|
|
1822
|
+
* @returns True if the tuple includes the item, false otherwise.
|
|
1823
|
+
*
|
|
1824
|
+
* @example
|
|
1825
|
+
* const tuple = ['x', 'y'] as const;
|
|
1826
|
+
* tupleIncludes(tuple, 'x'); // true
|
|
1827
|
+
* tupleIncludes(tuple, 'z'); // false
|
|
1828
|
+
*/
|
|
1829
|
+
const tupleIncludes = (tuple, item) => tuple.includes(item);
|
|
1830
|
+
//#endregion
|
|
1831
|
+
export { asyncNoOpFn, buildTimeDuration, coerceToBoolean, dateOnlyISOString, dateOnlyString, dateOnlyStringForSentence, dateOnlyStringNumeric, dateTimeRounded, dateTimeString, dateTimeStringForSentence, dateTimeToDate, dateTimeToISO, dateToDateTime, dateUTCOffset, dayOfWeekString, deepMerge, getObjectEntries, getObjectKeys, getObjectValues, hasKey, isArray, isAsyncFunction, isBigInt, isBoolean, isDate, isEmail, isEmptyArray, isEmptyRecord, isEmptyString, isError, isFalsy, isFiniteNumber, isFunction, isHtmlButtonElement, isHtmlElement, isHtmlInputElement, isHtmlVideoElement, isInteger, isIterable, isMap, isMouseEvent, isNaN, isNil, isNonBlankString, isNonEmptyArray, isNonEmptyRecord, isNonEmptyString, isNonNaNNumber, isNotArray, isNotBoolean, isNotFunction, isNotNil, isNotNull, isNotNumber, isNotRecord, isNotString, isNotUndefined, isNotVoid, isNull, isNumber, isPlainObject, isPromise, isRecord, isRegExp, isSet, isString, isSvgElement, isSymbol, isTextNode, isTruthy, isUndefined, isUrl, isVoid, isWeakMap, isWeakSet, mediaDurationString, millisecondsToDurationISOString, monthDayStringNumeric, noOpFn, noOpFnReturningFalse, noOpFnReturningNull, parseDateString, readonlyArrayIncludes, sessionDurationString, stripExtension, timeAgoString, timeOnlyString, tupleIncludes };
|
|
1832
|
+
|
|
1833
|
+
//# sourceMappingURL=index.mjs.map
|