* Expands an IPv6 string to exactly 8 groups of 16-bit hex, handling :: and IPv4-mapped form.
(ip: string)
| 46 | * Expands an IPv6 string to exactly 8 groups of 16-bit hex, handling :: and IPv4-mapped form. |
| 47 | */ |
| 48 | function expandIPv6ToGroups(ip: string): string[] { |
| 49 | let parts = ip.split(':') |
| 50 | |
| 51 | // IPv4-mapped: last segment can be dotted decimal (e.g. 192.168.1.1) |
| 52 | const last = parts[parts.length - 1] |
| 53 | if (last?.includes('.')) { |
| 54 | const octets = last.split('.').map(Number) |
| 55 | if (octets.length === 4 && octets.every((n) => n >= 0 && n <= 255)) { |
| 56 | const high = (octets[0] << 8) | octets[1] |
| 57 | const low = (octets[2] << 8) | octets[3] |
| 58 | parts = [...parts.slice(0, -1), high.toString(16), low.toString(16)] |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | if (parts.includes('')) { |
| 63 | const emptyIdx = parts.indexOf('') |
| 64 | const zeroCount = 8 - (parts.length - 1) |
| 65 | const zeros = Array(zeroCount).fill('0000') |
| 66 | parts = [...parts.slice(0, emptyIdx), ...zeros, ...parts.slice(emptyIdx + 1)] |
| 67 | } |
| 68 | |
| 69 | return parts.map((p) => p.padStart(4, '0').toLowerCase()) |
| 70 | } |
| 71 | |
| 72 | /** |
| 73 | * Masks the last octet of an IP address for privacy (GDPR compliance). |