@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/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