@magic/types 0.1.23 → 0.1.25

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/src/fns.js ADDED
@@ -0,0 +1,630 @@
1
+ /**
2
+ * Check if a value is an Array
3
+ * @param {unknown} e - value to test
4
+ * @returns {e is unknown[]} - true if value is an array
5
+ */
6
+ export const isArray = e => Array.isArray(e)
7
+
8
+ /**
9
+ * Check if a value is a boolean
10
+ * @param {unknown} e - value to test
11
+ * @returns {e is boolean}
12
+ */
13
+ export const isBoolean = e => typeof e === 'boolean'
14
+
15
+ /**
16
+ * Check if a value is defined (not undefined)
17
+ * @param {unknown} e - value to test
18
+ * @returns {e is NonNullable<unknown>}
19
+ */
20
+ export const isDefined = e => typeof e !== 'undefined'
21
+
22
+ /**
23
+ * Check if a value is undefined
24
+ * @param {unknown} e - value to test
25
+ * @returns {e is undefined}
26
+ */
27
+ export const isUndefined = e => !isDefined(e)
28
+
29
+ /**
30
+ * Check if a value is a function
31
+ * @param {unknown} e - value to test
32
+ * @returns {e is Function}
33
+ */
34
+ export const isFunction = e => typeof e === 'function'
35
+
36
+ /**
37
+ * Check if a value is an async function
38
+ * @param {unknown} e - value to test
39
+ * @returns {e is (...args: unknown[]) => Promise<unknown>}
40
+ */
41
+ export const isAsyncFunction = e => Object.prototype.toString.call(e) === '[object AsyncFunction]'
42
+
43
+ /**
44
+ * Check if a value is a generator function
45
+ * @param {unknown} e - value to test
46
+ * @returns {e is GeneratorFunction}
47
+ */
48
+ export const isGeneratorFunction = e =>
49
+ Object.prototype.toString.call(e) === '[object GeneratorFunction]'
50
+
51
+ /**
52
+ * Check if a value is a number
53
+ * @param {unknown} e - value to test
54
+ * @returns {e is number}
55
+ */
56
+ export const isNumber = e => !isObject(e) && !isNull(e) && !isUndefined(e) && e === +e
57
+
58
+ /**
59
+ * Check if a value is an integer
60
+ * @param {unknown} e - value to test
61
+ * @returns {e is number}
62
+ */
63
+ export const isInteger = e => isNumber(e) && e === (e | 0)
64
+
65
+ /**
66
+ * Check if a value is a float (any number)
67
+ * @param {unknown} e - value to test
68
+ * @returns {e is number}
69
+ */
70
+ export const isFloat = e => isNumber(e)
71
+
72
+ /**
73
+ * Check if a value is an object (but not null)
74
+ * @param {unknown} e - value to test
75
+ * @returns {e is object}
76
+ */
77
+ export const isObject = e => typeof e === 'object' && !isNull(e)
78
+
79
+ /**
80
+ * Check if a value is a plain object (`{}`)
81
+ * @param {unknown} e - value to test
82
+ * @returns {e is Record<string, unknown>}
83
+ */
84
+ export const isObjectNative = e => Object.prototype.toString.call(e) === '[object Object]'
85
+
86
+ /**
87
+ * Check if a value is mergeable (plain object, not Date or RegExp)
88
+ * @param {unknown} e - value to test
89
+ * @returns {e is Record<string, unknown>}
90
+ */
91
+ export const isMergeableObject = e => isObject(e) && !isDate(e) && !isRegExp(e)
92
+
93
+ /**
94
+ * Alias for isMergeableObject
95
+ * @type {(e: unknown) => e is Record<string, unknown>}
96
+ */
97
+ export const isMergeable = isMergeableObject
98
+
99
+ /**
100
+ * Check if a value is a string
101
+ * @param {unknown} e - value to test
102
+ * @returns {e is string}
103
+ */
104
+ export const isString = e => typeof e === 'string'
105
+
106
+ /**
107
+ * Check if a value is an RGB object {r,g,b}
108
+ * @param {unknown} e - value to test
109
+ * @returns {e is { r: number, g: number, b: number }}
110
+ */
111
+ export const isRGBValue = e => isObjectNative(e) && [e.r, e.g, e.b].every(n => isNumber(n))
112
+ export const isRGBObject = isRGBValue
113
+
114
+ /**
115
+ * Check if a value is an RGBA object {r,g,b,a}
116
+ * @param {unknown} e - value to test
117
+ * @returns {e is { r: number, g: number, b: number, a: number }}
118
+ */
119
+ export const isRGBAValue = e => isObjectNative(e) && [e.r, e.g, e.b, e.a].every(n => isNumber(n))
120
+ export const isRGBAObject = isRGBAValue
121
+
122
+ export const hexRegex = /#\b([a-f0-9]{3}|[a-f0-9]{4}|[a-f0-9]{6}|[a-f0-9]{8})\b/i
123
+
124
+ /**
125
+ * Check if a string is a valid hex color (#rgb, #rgba, #rrggbb, #rrggbbaa)
126
+ * @param {unknown} e - value to test
127
+ * @returns {boolean}
128
+ */
129
+ export const isHexColor = e => isString(e) && hexRegex.test(e)
130
+
131
+ /**
132
+ * Check if a string is a 3-digit hex color
133
+ * @param {unknown} e
134
+ * @returns {boolean}
135
+ */
136
+ export const isHexColor3 = e => isString(e) && /#\b([a-f0-9]{3})\b/i.test(e)
137
+
138
+ /**
139
+ * Check if a string is a 4-digit hex color (#rgba)
140
+ * @param {unknown} e
141
+ * @returns {boolean}
142
+ */
143
+ export const isHexColor4 = e => isString(e) && /#\b([a-f0-9]{4})\b/i.test(e)
144
+
145
+ /**
146
+ * Check if a string is a 6-digit hex color (#rrggbb)
147
+ * @param {unknown} e
148
+ * @returns {boolean}
149
+ */
150
+ export const isHexColor6 = e => isString(e) && /#\b([a-f0-9]{6})\b/i.test(e)
151
+
152
+ /**
153
+ * Check if a string is an 8-digit hex color (#rrggbbaa)
154
+ * @param {unknown} e
155
+ * @returns {boolean}
156
+ */
157
+ export const isHexColor8 = e => isString(e) && /#\b([a-f0-9]{8})\b/i.test(e)
158
+
159
+ /**
160
+ * Check if a string is an alpha hex color (#rgba or #rrggbbaa)
161
+ * @param {unknown} e
162
+ * @returns {boolean}
163
+ */
164
+ export const isHexAlphaColor = e => isString(e) && /#\b([a-f0-9]{4}|[a-f0-9]{8})\b/i.test(e)
165
+
166
+ /**
167
+ * Check if a string is a 4-digit alpha hex color (#rgba)
168
+ * @param {unknown} e
169
+ * @returns {boolean}
170
+ */
171
+ export const isHexAlphaColor4 = e => isString(e) && /#\b([a-f0-9]{4})\b/i.test(e)
172
+
173
+ /**
174
+ * Check if a string is an 8-digit alpha hex color (#rrggbbaa)
175
+ * @param {unknown} e
176
+ * @returns {boolean}
177
+ */
178
+ export const isHexAlphaColor8 = e => isString(e) && /#\b([a-f0-9]{8})\b/i.test(e)
179
+
180
+ /**
181
+ * Check if a value is a color (RGB, RGBA, or hex)
182
+ * @param {unknown} e
183
+ * @returns {boolean}
184
+ */
185
+ export const isColor = e => isRGBAObject(e) || isRGBObject(e) || isHexColor(e) || isHexAlphaColor(e)
186
+
187
+ /**
188
+ * Check if a value is truthy
189
+ * @param {unknown} e
190
+ * @returns {boolean}
191
+ */
192
+ export const isTruthy = e => !!e
193
+
194
+ /**
195
+ * Check if a value is falsy or empty
196
+ * @param {unknown} e
197
+ * @returns {boolean}
198
+ */
199
+ export const isFalsy = e => !e || isEmpty(e)
200
+
201
+ /**
202
+ * Get the length/size of a value
203
+ * - Arrays, strings, buffers → `.length`
204
+ * - Numbers → numeric value
205
+ * - Maps, Sets → `.size`
206
+ * - RegExp → pattern length
207
+ * - Objects → number of keys
208
+ * @param {unknown} arg
209
+ * @returns {number} - length/size or -1 if unknown
210
+ */
211
+ export const getLength = arg => {
212
+ if (Array.isArray(arg) || typeof arg === 'string' || isBuffer(arg)) {
213
+ return arg.length
214
+ }
215
+ if (isInteger(arg)) {
216
+ return arg
217
+ }
218
+ if (arg instanceof Map || arg instanceof Set) {
219
+ return arg.size
220
+ }
221
+ if (arg instanceof RegExp) {
222
+ const str = arg.toString()
223
+ return str === '/(?:)/' ? 0 : str.length - 2 // length of pattern without slashes
224
+ }
225
+ if (isObjectNative(arg)) {
226
+ return Object.keys(arg).length
227
+ }
228
+ return -1
229
+ }
230
+
231
+ /**
232
+ * Compare two values by their length/size
233
+ * @param {unknown} len
234
+ * @param {unknown} e
235
+ * @returns {boolean}
236
+ */
237
+ export const compareCount = (len, e) => getLength(len) === getLength(e)
238
+
239
+ /**
240
+ * @overload
241
+ * @param {unknown} a
242
+ * @param {unknown} b
243
+ * @returns {boolean}
244
+ */
245
+ /**
246
+ * @overload
247
+ * @param {unknown} a
248
+ * @returns {(c: unknown) => boolean}
249
+ */
250
+ /**
251
+ * Check if length of `a` equals `b` (or return curried fn)
252
+ * @param {unknown} a
253
+ * @param {unknown} [b]
254
+ * @returns {boolean | ((c: unknown) => boolean)}
255
+ */
256
+ export const isLengthEqual = (a, b) => (isDefined(b) ? compareCount(a, b) : b => compareCount(a, b))
257
+
258
+ /**
259
+ * @overload
260
+ * @param {unknown} a
261
+ * @param {unknown} b
262
+ * @returns {boolean}
263
+ */
264
+ /**
265
+ * @overload
266
+ * @param {unknown} a
267
+ * @returns {(c: unknown) => boolean}
268
+ */
269
+ /**
270
+ * Check if length of `a` > `b` (or return curried fn)
271
+ * @param {unknown} a
272
+ * @param {unknown} [b]
273
+ * @returns {boolean | ((c: unknown) => boolean)}
274
+ */
275
+ export const isLengthGreater = (a, b) =>
276
+ isDefined(b) ? getLength(a) > getLength(b) : c => getLength(a) > getLength(c)
277
+
278
+ /**
279
+ * @overload
280
+ * @param {unknown} a
281
+ * @param {unknown} b
282
+ * @returns {boolean}
283
+ */
284
+ /**
285
+ * @overload
286
+ * @param {unknown} a
287
+ * @returns {(c: unknown) => boolean}
288
+ */
289
+ /**
290
+ * Check if length of `a` >= `b` (or return curried fn)
291
+ * @param {unknown} a
292
+ * @param {unknown} [b]
293
+ * @returns {boolean | ((c: unknown) => boolean)}
294
+ */
295
+ export const isLengthGreaterOrEqual = (a, b) =>
296
+ isDefined(b) ? getLength(a) >= getLength(b) : c => getLength(a) >= getLength(c)
297
+
298
+ /**
299
+ * @overload
300
+ * @param {unknown} a
301
+ * @param {unknown} b
302
+ * @returns {boolean}
303
+ */
304
+ /**
305
+ * @overload
306
+ * @param {unknown} a
307
+ * @returns {(c: unknown) => boolean}
308
+ */
309
+ /**
310
+ * Check if length of `a` < `b` (or return curried fn)
311
+ * @param {unknown} a
312
+ * @param {unknown} [b]
313
+ * @returns {boolean | ((c: unknown) => boolean)}
314
+ */
315
+ export const isLengthSmaller = (a, b) =>
316
+ isDefined(b) ? getLength(a) < getLength(b) : c => getLength(a) < getLength(c)
317
+
318
+ /**
319
+ * @overload
320
+ * @param {unknown} a
321
+ * @param {unknown} b
322
+ * @returns {boolean}
323
+ */
324
+ /**
325
+ * @overload
326
+ * @param {unknown} a
327
+ * @returns {(c: unknown) => boolean}
328
+ */
329
+ /**
330
+ * Check if length of `a` <= `b` (or return curried fn)
331
+ * @param {unknown} a
332
+ * @param {unknown} [b]
333
+ * @returns {boolean | ((c: unknown) => boolean)}
334
+ */
335
+ export const isLengthSmallerOrEqual = (a, b) =>
336
+ isDefined(b) ? getLength(a) <= getLength(b) : c => getLength(a) <= getLength(c)
337
+
338
+ /**
339
+ * Check if a value is empty
340
+ * - null/undefined → true
341
+ * - arrays, objects, regex → empty if size < 1
342
+ * - errors/dates → never empty
343
+ * - numbers, strings → falsy or 0/""
344
+ * @param {unknown} e
345
+ * @returns {boolean}
346
+ */
347
+ export const isEmpty = e => {
348
+ if (isError(e) || isDate(e)) {
349
+ return false
350
+ }
351
+ if (isNull(e) || isUndefined(e)) {
352
+ return true
353
+ }
354
+ if (isArray(e)) {
355
+ return isLengthSmaller(e, 1)
356
+ }
357
+ if (isRegExp(e)) {
358
+ return isLengthSmaller(e, 1)
359
+ }
360
+ if (isObject(e)) {
361
+ return isLengthSmaller(e, 1)
362
+ }
363
+
364
+ return e === 0 || e === '' || !e
365
+ }
366
+
367
+ /**
368
+ * Check if a value is iterable (Array-like with forEach or implements Symbol.iterator)
369
+ * @param {unknown} e
370
+ * @returns {e is Iterable<unknown>}
371
+ */
372
+ export const isIterable = e =>
373
+ isDefined(e) &&
374
+ ((isObject(e) && e !== null && isFunction(/** @type {{ forEach?: unknown }} */ (e).forEach)) ||
375
+ (isObject(e) &&
376
+ !isNull(e) &&
377
+ isFunction(/** @type {{ [Symbol.iterator]?: unknown }} */ (e)[Symbol.iterator])))
378
+
379
+ /**
380
+ * Check if a string contains an `@` (naive email test)
381
+ * @param {unknown} e
382
+ * @returns {e is string}
383
+ */
384
+ export const isEmail = e => isString(e) && e.indexOf('@') > -1
385
+
386
+ /**
387
+ * Check if a value is null
388
+ * @param {unknown} e
389
+ * @returns {e is null}
390
+ */
391
+ export const isNull = e => e === null
392
+
393
+ /**
394
+ * Check if a value is undefined or null
395
+ * @param {unknown} e
396
+ * @returns {e is null | undefined}
397
+ */
398
+ export const isUndefinedOrNull = e => isNull(e) || !isDefined(e)
399
+
400
+ /**
401
+ * Check if a value is a Node.js Buffer
402
+ * @param {unknown} e
403
+ * @returns {e is Buffer}
404
+ */
405
+ export const isBuffer = e => Buffer.isBuffer(e)
406
+
407
+ /**
408
+ * Check if a value is a Promise
409
+ * @param {unknown} e
410
+ * @returns {e is Promise<unknown>}
411
+ */
412
+ export const isPromise = e =>
413
+ Object.prototype.toString.call(e) === '[object Promise]' ||
414
+ (isObjectNative(e) && isFunction(e?.then))
415
+
416
+ /**
417
+ * Alias for isPromise
418
+ * @type {(e: unknown) => e is Promise<unknown>}
419
+ */
420
+ export const isThenable = isPromise
421
+
422
+ /**
423
+ * Check if a value is the special `arguments` object
424
+ * @param {unknown} e
425
+ * @returns {e is IArguments}
426
+ */
427
+ export const isArguments = e => Object.prototype.toString.call(e) === '[object Arguments]'
428
+
429
+ /**
430
+ * Check if a string looks like a UUID v4 (basic structural test)
431
+ * @param {unknown} e
432
+ * @returns {boolean}
433
+ */
434
+ export const isUUID = e => {
435
+ if (!isDefined(e)) return false
436
+ if (!isString(e)) return false
437
+
438
+ const split = e.split('-')
439
+ if (!isLengthEqual(split, 5)) return false
440
+
441
+ const lengths = [8, 4, 4, 4, 12]
442
+ if (lengths.some((l, i) => split[i].length !== l)) return false
443
+
444
+ if (Object.values(e).some(s => s > 'f')) return false
445
+
446
+ return true
447
+ }
448
+
449
+ /**
450
+ * Check if a value matches one of the given JS types
451
+ * @param {unknown} e
452
+ * @param {...string} types - typeof strings (e.g. "string", "number")
453
+ * @returns {boolean}
454
+ */
455
+ export const isType = (e, ...types) =>
456
+ types.some(type => typeof e === type || Object.prototype.toString.call(e) === type)
457
+
458
+ export const isTypes = isType
459
+
460
+ /**
461
+ * Negation of isTypes
462
+ * @param {unknown} e
463
+ * @param {...string} types
464
+ * @returns {boolean}
465
+ */
466
+ export const isNot = (e, ...types) => !isType(e, ...types)
467
+
468
+ /**
469
+ * Check if two values have the same type
470
+ * @param {unknown} a - first value to compare
471
+ * @param {unknown} b - second value to compare
472
+ * @returns {boolean} - true if both values are of the same type
473
+ */
474
+ export const isSameType = (a, b) => typeof a === typeof b
475
+
476
+ /**
477
+ * Check if a value matches a type or passes a function check
478
+ * @template T
479
+ * @param {unknown} e - value to check
480
+ * @param {((v: unknown) => boolean) | T} t - function to validate value or a typeof string
481
+ * @returns {e is T} - true if value matches the type or passes the function check
482
+ */
483
+ const isElementCheck = (e, t) => (isFunction(t) ? t(e) : typeof e === t)
484
+
485
+ /**
486
+ * Check if a value is a string, number or a boolean
487
+ * @param {unknown} a
488
+ * @returns {a is string | number | boolean}
489
+ */
490
+ export const isComparable = a => isBoolean(a) || isString(a) || isNumber(a)
491
+
492
+ /**
493
+ * Check if every element of an array matches a predicate
494
+ * @template T
495
+ * @param {T | T[]} arr - value or array of values to check
496
+ * @param {((v: T) => boolean) | string} t - function or typeof string to validate elements
497
+ * @returns {boolean} - true if all elements match the predicate
498
+ */
499
+ export const isEvery = (arr, t) =>
500
+ !isArray(arr) ? isElementCheck(arr, t) : arr.every(e => isElementCheck(e, t))
501
+
502
+ /**
503
+ * Check if some elements of an array match a predicate
504
+ * @template T
505
+ * @param {T | T[]} arr - value or array of values to check
506
+ * @param {((v: T) => boolean) | string} t - function or typeof string to validate elements
507
+ * @returns {boolean} - true if at least one element matches the predicate
508
+ */
509
+ export const isSome = (arr, t) =>
510
+ !isArray(arr) ? isElementCheck(arr, t) : arr.some(e => isElementCheck(e, t))
511
+
512
+ /**
513
+ * Check if no elements of an array match a predicate
514
+ * @template T
515
+ * @param {T | T[]} arr - value or array of values to check
516
+ * @param {((v: T) => boolean) | string} t - function or typeof string to validate elements
517
+ * @returns {boolean} - true if no elements match the predicate
518
+ */
519
+ export const isNone = (arr, t) => !isSome(arr, t)
520
+
521
+ /**
522
+ * Check if a value is an instance of a given constructor
523
+ * @param {unknown} e - value to check
524
+ * @param {new (...args: any[]) => any} t - constructor function
525
+ * @returns {boolean} - true if value is an instance of constructor
526
+ */
527
+ export const isInstanceOf = (e, t) => (!t ? false : e instanceof t)
528
+
529
+ /**
530
+ * Check if a value is an Error
531
+ * @param {unknown} e - value to check
532
+ * @returns {e is Error} - true if value is an Error
533
+ */
534
+ export const isError = e => isInstanceOf(e, Error)
535
+
536
+ /**
537
+ * Check if a value is a Date
538
+ * @param {unknown} e - value to check
539
+ * @returns {e is Date} - true if value is a Date
540
+ */
541
+ export const isDate = e => isInstanceOf(e, Date)
542
+
543
+ /**
544
+ * Check if a value is a RegExp
545
+ * @param {unknown} e - value to check
546
+ * @returns {e is RegExp} - true if value is a RegExp
547
+ */
548
+ export const isRegExp = e => isInstanceOf(e, RegExp)
549
+
550
+ /**
551
+ * Check if a value is a Map
552
+ * @param {unknown} a - value to check
553
+ * @returns {a is Map<unknown, unknown>} - true if value is a Map
554
+ */
555
+ export const isMap = a => isInstanceOf(a, Map)
556
+
557
+ /**
558
+ * Check if a value is a Set
559
+ * @param {unknown} a - value to check
560
+ * @returns {a is Set<unknown>} - true if value is a Set
561
+ */
562
+ export const isSet = a => isInstanceOf(a, Set)
563
+
564
+ /**
565
+ * Check if a value is a WeakMap
566
+ * @param {unknown} a - value to check
567
+ * @returns {a is WeakMap<any, unknown>} - true if value is a WeakMap
568
+ */
569
+ export const isWeakMap = a => isInstanceOf(a, WeakMap)
570
+
571
+ /**
572
+ * Check if a value is a WeakSet
573
+ * @param {unknown} a - value to check
574
+ * @returns {a is WeakSet<any>} - true if value is a WeakSet
575
+ */
576
+ export const isWeakSet = a => isInstanceOf(a, WeakSet)
577
+
578
+ /**
579
+ * Check if a string is upper case
580
+ * @param {unknown} s - value to check
581
+ * @returns {s is string} - true if string is all upper case
582
+ */
583
+ export const isUpperCase = s => (isString(s) ? s === s.toUpperCase() : false)
584
+
585
+ /**
586
+ * Check if a string is lower case
587
+ * @param {unknown} s - value to check
588
+ * @returns {s is string} - true if string is all lower case
589
+ */
590
+ export const isLowerCase = s => (isString(s) ? s === s.toLowerCase() : false)
591
+
592
+ /**
593
+ * Check if an object has an own property key
594
+ * @template {string | number | symbol} K
595
+ * @param {unknown} o - object to check
596
+ * @param {K} k - key to check
597
+ * @returns {o is Record<K, unknown>} - true if object has own property key
598
+ */
599
+ export const isOwnProp = (o, k) => isObjectNative(o) && k in o
600
+
601
+ /**
602
+ * Check if an object has an own property key (alias of isOwnProp)
603
+ * @param {unknown} o - object to check
604
+ * @param {string} k - key to check
605
+ * @returns {boolean} - true if object has own property key
606
+ */
607
+ export const isOwnProperty = isOwnProp
608
+
609
+ /**
610
+ * A type representing a Module-like object
611
+ * @typedef {object & { [Symbol.toStringTag]: 'Module' }} ModuleLike
612
+ */
613
+
614
+ /**
615
+ * Check if a value is a Module-like object
616
+ * @param {unknown} s - value to check
617
+ * @returns {s is ModuleLike} - true if value is a Module-like object
618
+ */
619
+ export const isModule = s => Object.prototype.toString.call(s) === '[object Module]'
620
+
621
+ /**
622
+ * Check if a string is upper or lower case depending on mode
623
+ * @type {((s: unknown, c?: 'up' | 'down') => boolean) & {
624
+ * upper: (s: unknown) => boolean,
625
+ * lower: (s: unknown) => boolean
626
+ * }}
627
+ */
628
+ export const isCase = (s, c = 'up') => (c === 'up' ? isUpperCase(s) : isLowerCase(s))
629
+ isCase.upper = isUpperCase
630
+ isCase.lower = isLowerCase
package/src/index.js ADDED
@@ -0,0 +1,8 @@
1
+ import def from './lib.js'
2
+
3
+ export * from './fns.js'
4
+ export * as deep from './deep/index.js'
5
+
6
+ export const is = def
7
+
8
+ export default is