@magic/types 0.1.38 → 0.1.39

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -140,6 +140,10 @@ isIterable([]) // true
140
140
  isEmail('a@b.c') // true
141
141
  // alias isMail, is.email, is.mail
142
142
 
143
+ isIp('192.168.1.1') // true
144
+ isIp('::1') // true
145
+ // alias ip, is.ip, ipV4, ipV6, isIPv4, isIPv6
146
+
143
147
  isNull(null) // true
144
148
  // alias isNil, is.nil, is.null
145
149
 
@@ -407,6 +411,11 @@ update dependencies
407
411
  - remove recursive dependencies
408
412
  - update dependencies
409
413
 
410
- ##### 0.1.39 - unreleased
414
+ ##### 0.1.39
415
+
416
+ - add isIp and variants to allow ip v4 and v6 checks
417
+ - update dependencies
418
+
419
+ ##### 0.1.40 - unreleased
411
420
 
412
421
  ...
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magic/types",
3
- "version": "0.1.38",
3
+ "version": "0.1.39",
4
4
  "author": "Wizards & Witches",
5
5
  "homepage": "https://github.com/magic/types",
6
6
  "license": "AGPL-3.0",
@@ -51,8 +51,8 @@
51
51
  "@magic-modules/pre": "0.0.12",
52
52
  "@magic-themes/docs": "0.0.15",
53
53
  "@magic/core": "0.0.159",
54
- "@magic/format": "0.0.74",
55
- "@magic/test": "0.3.21",
54
+ "@magic/format": "0.0.75",
55
+ "@magic/test": "0.3.23",
56
56
  "typescript": "6.0.3"
57
57
  },
58
58
  "keywords": [
@@ -67,7 +67,7 @@
67
67
  }
68
68
  ],
69
69
  "dependencies": {
70
- "@types/node": "26.0.1"
70
+ "@types/node": "26.1.0"
71
71
  },
72
72
  "allowScripts": {
73
73
  "@swc/core@1.3.41": true,
package/src/fns.js CHANGED
@@ -631,3 +631,86 @@ export const isModule = s => Object.prototype.toString.call(s) === '[object Modu
631
631
  export const isCase = (s, c = 'up') => (c === 'up' ? isUpperCase(s) : isLowerCase(s))
632
632
  isCase.upper = isUpperCase
633
633
  isCase.lower = isLowerCase
634
+
635
+ /**
636
+ * Check if a value is a valid IPv4 address
637
+ * @param {unknown} a - value to check
638
+ * @returns {boolean} - true if value is an IPv4 address
639
+ */
640
+ export const isIPv4 = a => {
641
+ if (!isString(a)) return false
642
+ const parts = a.split('.')
643
+ if (parts.length !== 4) return false
644
+ return parts.every(part => {
645
+ const num = Number(part)
646
+ return /^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])$/.test(part) && String(num) === part
647
+ })
648
+ }
649
+
650
+ /**
651
+ * Check if a value is a valid IPv6 address
652
+ * @param {unknown} a - value to check
653
+ * @returns {boolean} - true if value is an IPv6 address
654
+ */
655
+ export const isIPv6 = a => {
656
+ if (!isString(a)) {
657
+ return false
658
+ }
659
+
660
+ const str = a.toLowerCase()
661
+
662
+ // Handle IPv4-mapped IPv6 (::ffff:192.168.1.1)
663
+ if (str.includes('.')) {
664
+ const lastColon = str.lastIndexOf(':')
665
+ const ipv4Part = str.slice(lastColon + 1)
666
+ if (!isIPv4(ipv4Part)) {
667
+ return false
668
+ }
669
+ const beforeIpv4 = str.slice(0, lastColon)
670
+ // Should be ::ffff: or similar, and rest should be empty or valid hex
671
+ const colonCount = beforeIpv4.split(':').length - 1
672
+ return colonCount <= 2 && (beforeIpv4 === '::ffff' || beforeIpv4 === ':' || beforeIpv4 === '')
673
+ }
674
+
675
+ // Handle :: compression
676
+ const doubleColon = str.indexOf('::')
677
+ if (doubleColon !== -1) {
678
+ const parts = str.split('::')
679
+ if (parts.length !== 2) {
680
+ return false
681
+ }
682
+ const leftParts = parts[0] ? parts[0].split(':') : []
683
+ const rightParts = parts[1] ? parts[1].split(':') : []
684
+ const totalGroups = leftParts.length + rightParts.length
685
+ if (totalGroups > 7) {
686
+ return false
687
+ }
688
+ const emptyParts =
689
+ leftParts.filter(p => p === '').length + rightParts.filter(p => p === '').length
690
+ if (emptyParts > 1) {
691
+ return false
692
+ }
693
+ const allValid = [...leftParts, ...rightParts].every(p => p === '' || /^[0-9a-f]{1,4}$/.test(p))
694
+ return allValid
695
+ }
696
+
697
+ // Full form: 8 groups of 1-4 hex digits
698
+ const parts = str.split(':')
699
+ if (parts.length !== 8) {
700
+ return false
701
+ }
702
+ return parts.every(part => /^[0-9a-f]{1,4}$/.test(part))
703
+ }
704
+
705
+ /**
706
+ * Check if a value is a valid IP address (v4 or v6)
707
+ * @param {unknown} a - value to check
708
+ * @returns {boolean} - true if value is an IP address
709
+ */
710
+ export const isIp = a => isIPv4(a) || isIPv6(a)
711
+ isIp.v4 = isIPv4
712
+ isIp.v6 = isIPv6
713
+
714
+ export const ipV4 = isIPv4
715
+
716
+ export const ipV6 = isIPv6
package/src/lib.js CHANGED
@@ -315,6 +315,18 @@ export const is = /** @type {const} */ {
315
315
  isLowerCase: fns.isLowerCase,
316
316
  lowerCase: fns.isLowerCase,
317
317
 
318
+ isIp: fns.isIp,
319
+ ip: fns.isIp,
320
+ isIPv4: fns.isIPv4,
321
+ ipv4: fns.isIPv4,
322
+ v4: fns.isIPv4,
323
+ isIPv6: fns.isIPv6,
324
+ ipv6: fns.isIPv6,
325
+ v6: fns.isIPv6,
326
+
327
+ ipV4: fns.ipV4,
328
+ ipV6: fns.ipV6,
329
+
318
330
  same: fns.isSameType,
319
331
  sameType: fns.isSameType,
320
332
  isSameType: fns.isSameType,
package/types/fns.d.ts CHANGED
@@ -173,3 +173,12 @@ export const isCase: ((s: unknown, c?: 'up' | 'down') => boolean) & {
173
173
  upper: (s: unknown) => boolean
174
174
  lower: (s: unknown) => boolean
175
175
  }
176
+ export function isIPv4(a: unknown): boolean
177
+ export function isIPv6(a: unknown): boolean
178
+ export function isIp(a: unknown): boolean
179
+ export namespace isIp {
180
+ export { isIPv4 as v4 }
181
+ export { isIPv6 as v6 }
182
+ }
183
+ export function ipV4(a: unknown): boolean
184
+ export function ipV6(a: unknown): boolean
package/types/index.d.ts CHANGED
@@ -495,6 +495,24 @@ export const is: {
495
495
  upperCase: (s: unknown) => s is string
496
496
  isLowerCase: (s: unknown) => s is string
497
497
  lowerCase: (s: unknown) => s is string
498
+ isIp: {
499
+ (a: unknown): boolean
500
+ v4: (a: unknown) => boolean
501
+ v6: (a: unknown) => boolean
502
+ }
503
+ ip: {
504
+ (a: unknown): boolean
505
+ v4: (a: unknown) => boolean
506
+ v6: (a: unknown) => boolean
507
+ }
508
+ isIPv4: (a: unknown) => boolean
509
+ ipv4: (a: unknown) => boolean
510
+ v4: (a: unknown) => boolean
511
+ isIPv6: (a: unknown) => boolean
512
+ ipv6: (a: unknown) => boolean
513
+ v6: (a: unknown) => boolean
514
+ ipV4: (a: unknown) => boolean
515
+ ipV6: (a: unknown) => boolean
498
516
  same: (a: unknown, b: unknown) => boolean
499
517
  sameType: (a: unknown, b: unknown) => boolean
500
518
  isSameType: (a: unknown, b: unknown) => boolean
package/types/lib.d.ts CHANGED
@@ -493,6 +493,24 @@ export const is: {
493
493
  upperCase: (s: unknown) => s is string
494
494
  isLowerCase: (s: unknown) => s is string
495
495
  lowerCase: (s: unknown) => s is string
496
+ isIp: {
497
+ (a: unknown): boolean
498
+ v4: (a: unknown) => boolean
499
+ v6: (a: unknown) => boolean
500
+ }
501
+ ip: {
502
+ (a: unknown): boolean
503
+ v4: (a: unknown) => boolean
504
+ v6: (a: unknown) => boolean
505
+ }
506
+ isIPv4: (a: unknown) => boolean
507
+ ipv4: (a: unknown) => boolean
508
+ v4: (a: unknown) => boolean
509
+ isIPv6: (a: unknown) => boolean
510
+ ipv6: (a: unknown) => boolean
511
+ v6: (a: unknown) => boolean
512
+ ipV4: (a: unknown) => boolean
513
+ ipV6: (a: unknown) => boolean
496
514
  same: (a: unknown, b: unknown) => boolean
497
515
  sameType: (a: unknown, b: unknown) => boolean
498
516
  isSameType: (a: unknown, b: unknown) => boolean