ip-utilities 1.0.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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +238 -0
  3. package/dist/cidr.d.ts +2 -0
  4. package/dist/cidr.d.ts.map +1 -0
  5. package/dist/cidr.js +1 -0
  6. package/dist/index.d.ts +54 -0
  7. package/dist/index.d.ts.map +1 -0
  8. package/dist/index.js +89 -0
  9. package/dist/internal/cidr.d.ts +112 -0
  10. package/dist/internal/cidr.d.ts.map +1 -0
  11. package/dist/internal/cidr.js +297 -0
  12. package/dist/internal/ipv4-ranges.d.ts +26 -0
  13. package/dist/internal/ipv4-ranges.d.ts.map +1 -0
  14. package/dist/internal/ipv4-ranges.js +73 -0
  15. package/dist/internal/ipv4.d.ts +44 -0
  16. package/dist/internal/ipv4.d.ts.map +1 -0
  17. package/dist/internal/ipv4.js +183 -0
  18. package/dist/internal/ipv6-ranges.d.ts +47 -0
  19. package/dist/internal/ipv6-ranges.d.ts.map +1 -0
  20. package/dist/internal/ipv6-ranges.js +159 -0
  21. package/dist/internal/ipv6.d.ts +67 -0
  22. package/dist/internal/ipv6.d.ts.map +1 -0
  23. package/dist/internal/ipv6.js +385 -0
  24. package/dist/internal/regex.d.ts +37 -0
  25. package/dist/internal/regex.d.ts.map +1 -0
  26. package/dist/internal/regex.js +118 -0
  27. package/dist/internal/sort.d.ts +22 -0
  28. package/dist/internal/sort.d.ts.map +1 -0
  29. package/dist/internal/sort.js +207 -0
  30. package/dist/ipv4.d.ts +2 -0
  31. package/dist/ipv4.d.ts.map +1 -0
  32. package/dist/ipv4.js +1 -0
  33. package/dist/ipv6.d.ts +2 -0
  34. package/dist/ipv6.d.ts.map +1 -0
  35. package/dist/ipv6.js +1 -0
  36. package/dist/ranges.d.ts +3 -0
  37. package/dist/ranges.d.ts.map +1 -0
  38. package/dist/ranges.js +2 -0
  39. package/dist/regex.d.ts +3 -0
  40. package/dist/regex.d.ts.map +1 -0
  41. package/dist/regex.js +1 -0
  42. package/dist/sort.d.ts +2 -0
  43. package/dist/sort.d.ts.map +1 -0
  44. package/dist/sort.js +1 -0
  45. package/dist/types.d.ts +26 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +1 -0
  48. package/package.json +78 -0
@@ -0,0 +1,159 @@
1
+ const _V6_W0 = [0x00000000, 0x00000000, 0x0064FF9B, 0x00000000, 0x00000000, 0x01000000, 0x20010004, 0x2620004F, 0x20010002, 0x20010DB8, 0x20010003, 0x20010000, 0x20010010, 0x20010020, 0x20010030, 0x20010000, 0x20020000, 0xFE800000, 0xFF000000, 0xFC000000];
2
+ const _V6_W1 = [0, 0, 0, 0, 0, 0, 0x01120000, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
3
+ const _V6_W2 = [0, 0, 0, 0x0000FFFF, 0xFFFF0000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
4
+ const _V6_W3 = [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
5
+ const _V6_PREFIX = [128, 128, 96, 96, 96, 64, 48, 48, 48, 32, 32, 32, 28, 28, 28, 23, 16, 10, 8, 7];
6
+ const _V6_NAMES = [
7
+ 'loopback', 'unspecified', 'rfc6052', 'ipv4Mapped', 'rfc6145',
8
+ 'discard', 'as112v6', 'as112v6', 'benchmarking', 'reserved',
9
+ 'amt', 'teredo', 'deprecated', 'orchid2', 'droneRemoteIdProtocolEntityTags',
10
+ 'reserved', '6to4', 'linkLocal', 'multicast', 'uniqueLocal',
11
+ ];
12
+ // Pre-masked range tables: masks and expected values for each of the 4 uint32 words
13
+ const V6_COUNT = _V6_W0.length;
14
+ const V6R_MW0 = new Uint32Array(V6_COUNT);
15
+ const V6R_MW1 = new Uint32Array(V6_COUNT);
16
+ const V6R_MW2 = new Uint32Array(V6_COUNT);
17
+ const V6R_MW3 = new Uint32Array(V6_COUNT);
18
+ const V6R_KM0 = new Uint32Array(V6_COUNT);
19
+ const V6R_KM1 = new Uint32Array(V6_COUNT);
20
+ const V6R_KM2 = new Uint32Array(V6_COUNT);
21
+ const V6R_KM3 = new Uint32Array(V6_COUNT);
22
+ const V6R_NAMES = new Array(V6_COUNT);
23
+ for (let i = 0; i < V6_COUNT; i++) {
24
+ const p = _V6_PREFIX[i];
25
+ const m0 = prefixMaskV6(p, 0);
26
+ const m1 = prefixMaskV6(p, 1);
27
+ const m2 = prefixMaskV6(p, 2);
28
+ const m3 = prefixMaskV6(p, 3);
29
+ V6R_MW0[i] = (_V6_W0[i] & m0) >>> 0;
30
+ V6R_MW1[i] = (_V6_W1[i] & m1) >>> 0;
31
+ V6R_MW2[i] = (_V6_W2[i] & m2) >>> 0;
32
+ V6R_MW3[i] = (_V6_W3[i] & m3) >>> 0;
33
+ V6R_KM0[i] = m0;
34
+ V6R_KM1[i] = m1;
35
+ V6R_KM2[i] = m2;
36
+ V6R_KM3[i] = m3;
37
+ V6R_NAMES[i] = _V6_NAMES[i];
38
+ }
39
+ /**
40
+ * Tests whether an IPv6 address matches a CIDR prefix using raw uint32 words.
41
+ * @param aw0 - Address word 0 (bits 0-31)
42
+ * @param aw1 - Address word 1 (bits 32-63)
43
+ * @param aw2 - Address word 2 (bits 64-95)
44
+ * @param aw3 - Address word 3 (bits 96-127)
45
+ * @param nw0 - Network word 0
46
+ * @param nw1 - Network word 1
47
+ * @param nw2 - Network word 2
48
+ * @param nw3 - Network word 3
49
+ * @param prefix - Prefix length (0-128)
50
+ * @returns True if the address is within the network prefix
51
+ */
52
+ export function matchesCidr(aw0, aw1, aw2, aw3, nw0, nw1, nw2, nw3, prefix) {
53
+ if (prefix === 0)
54
+ return true;
55
+ if (prefix <= 32) {
56
+ const mask = (0xFFFFFFFF << (32 - prefix)) >>> 0;
57
+ return (aw0 & mask) === (nw0 & mask);
58
+ }
59
+ if (aw0 !== nw0)
60
+ return false;
61
+ if (prefix <= 64) {
62
+ if (prefix === 32)
63
+ return true;
64
+ const mask = (0xFFFFFFFF << (64 - prefix)) >>> 0;
65
+ return (aw1 & mask) === (nw1 & mask);
66
+ }
67
+ if (aw1 !== nw1)
68
+ return false;
69
+ if (prefix <= 96) {
70
+ if (prefix === 64)
71
+ return true;
72
+ const mask = (0xFFFFFFFF << (96 - prefix)) >>> 0;
73
+ return (aw2 & mask) === (nw2 & mask);
74
+ }
75
+ if (aw2 !== nw2)
76
+ return false;
77
+ if (prefix === 128)
78
+ return aw3 === nw3;
79
+ const mask = (0xFFFFFFFF << (128 - prefix)) >>> 0;
80
+ return (aw3 & mask) === (nw3 & mask);
81
+ }
82
+ /**
83
+ * Computes the bitmask for a specific 32-bit word of an IPv6 prefix.
84
+ * @param prefix - Prefix length (0-128)
85
+ * @param wordOffset - Which 32-bit word (0-3)
86
+ * @returns The uint32 mask for that word
87
+ */
88
+ export function prefixMaskV6(prefix, wordOffset) {
89
+ const lo = wordOffset * 32;
90
+ const hi = lo + 32;
91
+ if (prefix >= hi)
92
+ return 0xFFFFFFFF;
93
+ if (prefix <= lo)
94
+ return 0;
95
+ return (0xFFFFFFFF << (hi - prefix)) >>> 0;
96
+ }
97
+ /**
98
+ * Classifies an IPv6 address into its IANA-assigned range.
99
+ * @param addr - The IPv6 address
100
+ * @returns The range name (e.g. "uniqueLocal", "loopback", "unicast")
101
+ */
102
+ export function ipv6Range(addr) {
103
+ const w0 = addr.w0;
104
+ const top3 = w0 >>> 29;
105
+ if (top3 === 1) {
106
+ const top16 = w0 >>> 16;
107
+ if (top16 !== 0x2001 && top16 !== 0x2002 && top16 !== 0x2620)
108
+ return 'unicast';
109
+ }
110
+ else if ((top3 - 2) >>> 0 <= 3) {
111
+ return 'unicast';
112
+ }
113
+ const w1 = addr.w1, w2 = addr.w2, w3 = addr.w3;
114
+ for (let i = 0; i < V6_COUNT; i++) {
115
+ if (((w0 & V6R_KM0[i]) >>> 0) !== V6R_MW0[i])
116
+ continue;
117
+ if (((w1 & V6R_KM1[i]) >>> 0) !== V6R_MW1[i])
118
+ continue;
119
+ if (((w2 & V6R_KM2[i]) >>> 0) !== V6R_MW2[i])
120
+ continue;
121
+ if (((w3 & V6R_KM3[i]) >>> 0) !== V6R_MW3[i])
122
+ continue;
123
+ return V6R_NAMES[i];
124
+ }
125
+ return 'unicast';
126
+ }
127
+ /**
128
+ * Checks whether an IPv6 address is private (unique-local, multicast, link-local, or loopback).
129
+ * @param addr - The IPv6 address
130
+ * @returns True if the address is non-globally-routable
131
+ */
132
+ export function isPrivateIPv6(addr) {
133
+ const w0 = addr.w0;
134
+ if ((w0 >>> 25) === 0x7E)
135
+ return true;
136
+ if ((w0 >>> 24) === 0xFF)
137
+ return true;
138
+ if ((w0 >>> 22) === 0x3FA)
139
+ return true;
140
+ if (w0 === 0 && addr.w1 === 0 && addr.w2 === 0 && addr.w3 === 1)
141
+ return true;
142
+ return false;
143
+ }
144
+ /**
145
+ * Checks whether an IPv6 address is the loopback address (::1).
146
+ * @param addr - The IPv6 address
147
+ * @returns True if the address is ::1
148
+ */
149
+ export function isLoopbackIPv6(addr) {
150
+ return addr.w0 === 0 && addr.w1 === 0 && addr.w2 === 0 && addr.w3 === 1;
151
+ }
152
+ /**
153
+ * Checks whether an IPv6 address is link-local (fe80::/10).
154
+ * @param addr - The IPv6 address
155
+ * @returns True if the address is in the link-local range
156
+ */
157
+ export function isLinkLocalIPv6(addr) {
158
+ return (addr.w0 >>> 22) === 0x3FA;
159
+ }
@@ -0,0 +1,67 @@
1
+ import type { IPv6Addr } from '../types.ts';
2
+ export declare const ipv6RawBuf: Uint32Array<ArrayBuffer>;
3
+ /**
4
+ * Retrieves the zone ID (e.g. "eth0") associated with a parsed IPv6 address.
5
+ * @param addr - The IPv6 address
6
+ * @returns The zone ID string, or empty string if none
7
+ */
8
+ export declare function ipv6GetZoneId(addr: IPv6Addr): string;
9
+ /**
10
+ * Parses an IPv6 string into raw uint32 words written to {@link ipv6RawBuf}.
11
+ * @param s - Source string
12
+ * @param end - End index (exclusive) within the string
13
+ * @returns True if valid, with results in ipv6RawBuf[0..3]
14
+ */
15
+ export declare function parseIPv6Raw(s: string, end: number): boolean;
16
+ /**
17
+ * Parses an IPv6 string into an IPv6Addr. Supports :: compression,
18
+ * embedded IPv4 suffixes, and zone IDs (e.g. "fe80::1%eth0").
19
+ * @param s - The IPv6 string
20
+ * @param end - Optional end index for parsing a substring
21
+ * @returns The parsed address, or null if invalid
22
+ */
23
+ export declare function parseIPv6(s: string, end?: number): IPv6Addr | null;
24
+ /**
25
+ * Checks whether a string is a valid IPv6 address.
26
+ * @param s - The string to validate
27
+ * @returns True if the string is a valid IPv6 address
28
+ */
29
+ export declare function isIPv6(s: string): boolean;
30
+ /**
31
+ * Checks whether an IPv6 address is an IPv4-mapped address (::ffff:0:0/96).
32
+ * @param addr - The IPv6 address
33
+ * @returns True if the address is IPv4-mapped
34
+ */
35
+ export declare function isIPv4Mapped(addr: IPv6Addr): boolean;
36
+ /**
37
+ * Expands an IPv6 address into eight 16-bit words.
38
+ * @param addr - The IPv6 address
39
+ * @returns Tuple of eight 16-bit group values
40
+ */
41
+ export declare function ipv6ToWords(addr: IPv6Addr): [number, number, number, number, number, number, number, number];
42
+ /**
43
+ * Converts an IPv6 address to zero-padded colon notation (e.g. "0000:0000:...:0001").
44
+ * @param addr - The IPv6 address
45
+ * @returns The zero-padded string representation
46
+ */
47
+ export declare function ipv6ToFixedString(addr: IPv6Addr): string;
48
+ /**
49
+ * Converts an IPv6 address to uncompressed colon notation without zero-padding (e.g. "0:0:...:1").
50
+ * @param addr - The IPv6 address
51
+ * @returns The uncompressed string representation
52
+ */
53
+ export declare function ipv6ToNormalizedString(addr: IPv6Addr): string;
54
+ /**
55
+ * Converts an IPv6 address to its shortest RFC 5952 representation
56
+ * with :: compression for the longest zero-group run.
57
+ * @param addr - The IPv6 address
58
+ * @returns The compressed string (e.g. "::1", "fe80::1%eth0")
59
+ */
60
+ export declare function ipv6ToString(addr: IPv6Addr): string;
61
+ /**
62
+ * Extracts the embedded IPv4 address from the lower 32 bits of an IPv6 address.
63
+ * @param addr - The IPv6 address (typically an IPv4-mapped or IPv4-compatible address)
64
+ * @returns The packed uint32 IPv4 address
65
+ */
66
+ export declare function ipv6ToIPv4(addr: IPv6Addr): number;
67
+ //# sourceMappingURL=ipv6.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ipv6.d.ts","sourceRoot":"","sources":["../../src/internal/ipv6.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAoC3C,eAAO,MAAM,UAAU,0BAAqB,CAAA;AAgC5C;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEpD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAuG5D;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAElE;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAO5G;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAExD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAE7D;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CA4CnD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAEjD"}
@@ -0,0 +1,385 @@
1
+ import { parseIPv4Raw } from "./ipv4.js";
2
+ // --- Constants & lookup tables ---
3
+ // Hex digit lookup: maps charCode to nibble value (0-15), or 0xFF for non-hex
4
+ const HEX = new Uint8Array(256);
5
+ HEX.fill(0xFF);
6
+ for (let i = 0; i < 10; i++)
7
+ HEX[0x30 + i] = i;
8
+ for (let i = 0; i < 6; i++) {
9
+ HEX[0x41 + i] = 10 + i;
10
+ HEX[0x61 + i] = 10 + i;
11
+ }
12
+ // Hex string tables for all 16-bit values, used by toString functions
13
+ const HEX4_CACHE = new Array(65536);
14
+ for (let i = 0; i < 65536; i++)
15
+ HEX4_CACHE[i] = i.toString(16);
16
+ const HEX4_COLON = new Array(65536);
17
+ for (let i = 0; i < 65536; i++)
18
+ HEX4_COLON[i] = HEX4_CACHE[i] + ':';
19
+ const HEX4PAD_CACHE = new Array(65536);
20
+ for (let i = 0; i < 65536; i++)
21
+ HEX4PAD_CACHE[i] = i.toString(16).padStart(4, '0');
22
+ const HEX4PAD_COLON = new Array(65536);
23
+ for (let i = 0; i < 65536; i++)
24
+ HEX4PAD_COLON[i] = HEX4PAD_CACHE[i] + ':';
25
+ // --- Mutable state ---
26
+ // Zone IDs stored separately to keep IPv6Addr object property count minimal
27
+ const _zoneIds = new WeakMap();
28
+ // Scratch buffer for group accumulation during parsing
29
+ const _parts = new Uint16Array(8);
30
+ // Scratch buffer for toString compression path
31
+ const _toStrParts = new Uint16Array(8);
32
+ // Shared parse result buffer written by parseIPv6Raw: [w0, w1, w2, w3]
33
+ export const ipv6RawBuf = new Uint32Array(4);
34
+ let _rawZS = -1;
35
+ let _rawEnd = 0;
36
+ // --- Non-exported helpers ---
37
+ function _parseIPv6(s, end) {
38
+ if (!parseIPv6Raw(s, end))
39
+ return null;
40
+ const addr = {
41
+ w0: ipv6RawBuf[0],
42
+ w1: ipv6RawBuf[1],
43
+ w2: ipv6RawBuf[2],
44
+ w3: ipv6RawBuf[3],
45
+ kind: 6,
46
+ };
47
+ if (_rawZS !== -1)
48
+ _zoneIds.set(addr, s.slice(_rawZS, _rawEnd));
49
+ return addr;
50
+ }
51
+ function _ipv6FullStr(addr, colonLut, lastLut) {
52
+ const w0 = addr.w0, w1 = addr.w1, w2 = addr.w2, w3 = addr.w3;
53
+ let s = colonLut[(w0 >>> 16) & 0xFFFF] + colonLut[w0 & 0xFFFF] +
54
+ colonLut[(w1 >>> 16) & 0xFFFF] + colonLut[w1 & 0xFFFF] +
55
+ colonLut[(w2 >>> 16) & 0xFFFF] + colonLut[w2 & 0xFFFF] +
56
+ colonLut[(w3 >>> 16) & 0xFFFF] + lastLut[w3 & 0xFFFF];
57
+ const z = _zoneIds.get(addr);
58
+ if (z)
59
+ s += '%' + z;
60
+ return s;
61
+ }
62
+ // --- Exports ---
63
+ /**
64
+ * Retrieves the zone ID (e.g. "eth0") associated with a parsed IPv6 address.
65
+ * @param addr - The IPv6 address
66
+ * @returns The zone ID string, or empty string if none
67
+ */
68
+ export function ipv6GetZoneId(addr) {
69
+ return _zoneIds.get(addr) ?? '';
70
+ }
71
+ /**
72
+ * Parses an IPv6 string into raw uint32 words written to {@link ipv6RawBuf}.
73
+ * @param s - Source string
74
+ * @param end - End index (exclusive) within the string
75
+ * @returns True if valid, with results in ipv6RawBuf[0..3]
76
+ */
77
+ export function parseIPv6Raw(s, end) {
78
+ if (end < 2 || end > 70)
79
+ return false;
80
+ let zoneStart = -1;
81
+ let bodyEnd = end;
82
+ for (let i = end - 1; i >= 0; i--) {
83
+ const ch = s.charCodeAt(i);
84
+ if (ch === 37) {
85
+ if (i === end - 1)
86
+ return false;
87
+ zoneStart = i + 1;
88
+ bodyEnd = i;
89
+ break;
90
+ }
91
+ if (ch === 58)
92
+ break;
93
+ }
94
+ const c0 = s.charCodeAt(0);
95
+ if (c0 === 58) {
96
+ if (bodyEnd < 2 || s.charCodeAt(1) !== 58)
97
+ return false;
98
+ }
99
+ const parts = _parts;
100
+ let pIdx = 0;
101
+ let dcPart = -1;
102
+ let pos = 0;
103
+ if (c0 === 58) {
104
+ pos = 2;
105
+ dcPart = 0;
106
+ }
107
+ let gVal = 0;
108
+ let gLen = 0;
109
+ while (pos < bodyEnd) {
110
+ const ch = s.charCodeAt(pos);
111
+ if (ch === 58) {
112
+ if (gLen > 0) {
113
+ if (pIdx >= 8)
114
+ return false;
115
+ parts[pIdx++] = gVal;
116
+ gVal = 0;
117
+ gLen = 0;
118
+ if (pos + 1 < bodyEnd && s.charCodeAt(pos + 1) === 58) {
119
+ if (dcPart !== -1)
120
+ return false;
121
+ dcPart = pIdx;
122
+ pos += 2;
123
+ continue;
124
+ }
125
+ }
126
+ else {
127
+ return false;
128
+ }
129
+ pos++;
130
+ continue;
131
+ }
132
+ if (ch === 46) {
133
+ const v4Start = pos - gLen;
134
+ const packed = parseIPv4Raw(s, v4Start, bodyEnd);
135
+ if (packed === -1)
136
+ return false;
137
+ if (pIdx >= 7)
138
+ return false;
139
+ parts[pIdx++] = (packed >>> 16) & 0xFFFF;
140
+ parts[pIdx++] = packed & 0xFFFF;
141
+ gVal = 0;
142
+ gLen = 0;
143
+ pos = bodyEnd;
144
+ continue;
145
+ }
146
+ if (ch >= 128)
147
+ return false;
148
+ const nibble = HEX[ch];
149
+ if (nibble === 0xFF)
150
+ return false;
151
+ gVal = (gVal << 4) | nibble;
152
+ if (++gLen > 4)
153
+ return false;
154
+ pos++;
155
+ }
156
+ if (gLen > 0) {
157
+ if (pIdx >= 8)
158
+ return false;
159
+ parts[pIdx++] = gVal;
160
+ }
161
+ else if (bodyEnd > 0 && s.charCodeAt(bodyEnd - 1) === 58 && !(bodyEnd >= 2 && s.charCodeAt(bodyEnd - 2) === 58)) {
162
+ return false;
163
+ }
164
+ if (dcPart !== -1) {
165
+ if (pIdx > 7)
166
+ return false;
167
+ const zeros = 8 - pIdx;
168
+ if (zeros <= 0)
169
+ return false;
170
+ for (let j = pIdx - 1; j >= dcPart; j--)
171
+ parts[j + zeros] = parts[j];
172
+ for (let j = dcPart; j < dcPart + zeros; j++)
173
+ parts[j] = 0;
174
+ pIdx = 8;
175
+ }
176
+ if (pIdx !== 8)
177
+ return false;
178
+ ipv6RawBuf[0] = ((parts[0] << 16) | parts[1]) >>> 0;
179
+ ipv6RawBuf[1] = ((parts[2] << 16) | parts[3]) >>> 0;
180
+ ipv6RawBuf[2] = ((parts[4] << 16) | parts[5]) >>> 0;
181
+ ipv6RawBuf[3] = ((parts[6] << 16) | parts[7]) >>> 0;
182
+ _rawZS = zoneStart;
183
+ _rawEnd = end;
184
+ return true;
185
+ }
186
+ /**
187
+ * Parses an IPv6 string into an IPv6Addr. Supports :: compression,
188
+ * embedded IPv4 suffixes, and zone IDs (e.g. "fe80::1%eth0").
189
+ * @param s - The IPv6 string
190
+ * @param end - Optional end index for parsing a substring
191
+ * @returns The parsed address, or null if invalid
192
+ */
193
+ export function parseIPv6(s, end) {
194
+ return _parseIPv6(s, end !== undefined ? end : s.length);
195
+ }
196
+ /**
197
+ * Checks whether a string is a valid IPv6 address.
198
+ * @param s - The string to validate
199
+ * @returns True if the string is a valid IPv6 address
200
+ */
201
+ export function isIPv6(s) {
202
+ return parseIPv6Raw(s, s.length);
203
+ }
204
+ /**
205
+ * Checks whether an IPv6 address is an IPv4-mapped address (::ffff:0:0/96).
206
+ * @param addr - The IPv6 address
207
+ * @returns True if the address is IPv4-mapped
208
+ */
209
+ export function isIPv4Mapped(addr) {
210
+ return addr.w0 === 0 && addr.w1 === 0 && addr.w2 === 0x0000FFFF;
211
+ }
212
+ /**
213
+ * Expands an IPv6 address into eight 16-bit words.
214
+ * @param addr - The IPv6 address
215
+ * @returns Tuple of eight 16-bit group values
216
+ */
217
+ export function ipv6ToWords(addr) {
218
+ return [
219
+ (addr.w0 >>> 16) & 0xFFFF, addr.w0 & 0xFFFF,
220
+ (addr.w1 >>> 16) & 0xFFFF, addr.w1 & 0xFFFF,
221
+ (addr.w2 >>> 16) & 0xFFFF, addr.w2 & 0xFFFF,
222
+ (addr.w3 >>> 16) & 0xFFFF, addr.w3 & 0xFFFF,
223
+ ];
224
+ }
225
+ /**
226
+ * Converts an IPv6 address to zero-padded colon notation (e.g. "0000:0000:...:0001").
227
+ * @param addr - The IPv6 address
228
+ * @returns The zero-padded string representation
229
+ */
230
+ export function ipv6ToFixedString(addr) {
231
+ return _ipv6FullStr(addr, HEX4PAD_COLON, HEX4PAD_CACHE);
232
+ }
233
+ /**
234
+ * Converts an IPv6 address to uncompressed colon notation without zero-padding (e.g. "0:0:...:1").
235
+ * @param addr - The IPv6 address
236
+ * @returns The uncompressed string representation
237
+ */
238
+ export function ipv6ToNormalizedString(addr) {
239
+ return _ipv6FullStr(addr, HEX4_COLON, HEX4_CACHE);
240
+ }
241
+ /**
242
+ * Converts an IPv6 address to its shortest RFC 5952 representation
243
+ * with :: compression for the longest zero-group run.
244
+ * @param addr - The IPv6 address
245
+ * @returns The compressed string (e.g. "::1", "fe80::1%eth0")
246
+ */
247
+ export function ipv6ToString(addr) {
248
+ const w0 = addr.w0, w1 = addr.w1, w2 = addr.w2, w3 = addr.w3;
249
+ const p0 = (w0 >>> 16) & 0xFFFF, p1 = w0 & 0xFFFF;
250
+ const p2 = (w1 >>> 16) & 0xFFFF, p3 = w1 & 0xFFFF;
251
+ const p4 = (w2 >>> 16) & 0xFFFF, p5 = w2 & 0xFFFF;
252
+ const p6 = (w3 >>> 16) & 0xFFFF, p7 = w3 & 0xFFFF;
253
+ // Find longest run of consecutive zero groups (minimum length 2)
254
+ let bestStart = -1, bestLen = 0, curStart = -1, curLen = 0;
255
+ if (p0 === 0) {
256
+ curStart = 0;
257
+ curLen = 1;
258
+ }
259
+ if (p1 === 0) {
260
+ if (curStart === -1)
261
+ curStart = 1;
262
+ curLen++;
263
+ if (curLen > bestLen && curLen >= 2) {
264
+ bestStart = curStart;
265
+ bestLen = curLen;
266
+ }
267
+ }
268
+ else {
269
+ curStart = -1;
270
+ curLen = 0;
271
+ }
272
+ if (p2 === 0) {
273
+ if (curStart === -1)
274
+ curStart = 2;
275
+ curLen++;
276
+ if (curLen > bestLen && curLen >= 2) {
277
+ bestStart = curStart;
278
+ bestLen = curLen;
279
+ }
280
+ }
281
+ else {
282
+ curStart = -1;
283
+ curLen = 0;
284
+ }
285
+ if (p3 === 0) {
286
+ if (curStart === -1)
287
+ curStart = 3;
288
+ curLen++;
289
+ if (curLen > bestLen && curLen >= 2) {
290
+ bestStart = curStart;
291
+ bestLen = curLen;
292
+ }
293
+ }
294
+ else {
295
+ curStart = -1;
296
+ curLen = 0;
297
+ }
298
+ if (p4 === 0) {
299
+ if (curStart === -1)
300
+ curStart = 4;
301
+ curLen++;
302
+ if (curLen > bestLen && curLen >= 2) {
303
+ bestStart = curStart;
304
+ bestLen = curLen;
305
+ }
306
+ }
307
+ else {
308
+ curStart = -1;
309
+ curLen = 0;
310
+ }
311
+ if (p5 === 0) {
312
+ if (curStart === -1)
313
+ curStart = 5;
314
+ curLen++;
315
+ if (curLen > bestLen && curLen >= 2) {
316
+ bestStart = curStart;
317
+ bestLen = curLen;
318
+ }
319
+ }
320
+ else {
321
+ curStart = -1;
322
+ curLen = 0;
323
+ }
324
+ if (p6 === 0) {
325
+ if (curStart === -1)
326
+ curStart = 6;
327
+ curLen++;
328
+ if (curLen > bestLen && curLen >= 2) {
329
+ bestStart = curStart;
330
+ bestLen = curLen;
331
+ }
332
+ }
333
+ else {
334
+ curStart = -1;
335
+ curLen = 0;
336
+ }
337
+ if (p7 === 0) {
338
+ if (curStart === -1)
339
+ curStart = 7;
340
+ curLen++;
341
+ if (curLen > bestLen && curLen >= 2) {
342
+ bestStart = curStart;
343
+ bestLen = curLen;
344
+ }
345
+ }
346
+ if (bestLen === 0)
347
+ return _ipv6FullStr(addr, HEX4_COLON, HEX4_CACHE);
348
+ const z = _zoneIds.get(addr);
349
+ _toStrParts[0] = p0;
350
+ _toStrParts[1] = p1;
351
+ _toStrParts[2] = p2;
352
+ _toStrParts[3] = p3;
353
+ _toStrParts[4] = p4;
354
+ _toStrParts[5] = p5;
355
+ _toStrParts[6] = p6;
356
+ _toStrParts[7] = p7;
357
+ let s = '';
358
+ if (bestStart === 0) {
359
+ s = '::';
360
+ }
361
+ else {
362
+ for (let i = 0; i < bestStart - 1; i++) {
363
+ s += HEX4_COLON[_toStrParts[i]];
364
+ }
365
+ s += HEX4_CACHE[_toStrParts[bestStart - 1]];
366
+ s += '::';
367
+ }
368
+ const afterIdx = bestStart + bestLen;
369
+ for (let i = afterIdx; i < 8; i++) {
370
+ if (i > afterIdx)
371
+ s += ':';
372
+ s += HEX4_CACHE[_toStrParts[i]];
373
+ }
374
+ if (z)
375
+ s += '%' + z;
376
+ return s;
377
+ }
378
+ /**
379
+ * Extracts the embedded IPv4 address from the lower 32 bits of an IPv6 address.
380
+ * @param addr - The IPv6 address (typically an IPv4-mapped or IPv4-compatible address)
381
+ * @returns The packed uint32 IPv4 address
382
+ */
383
+ export function ipv6ToIPv4(addr) {
384
+ return addr.w3;
385
+ }
@@ -0,0 +1,37 @@
1
+ export interface Options {
2
+ readonly exact?: boolean;
3
+ readonly includeBoundaries?: boolean;
4
+ }
5
+ export interface ScanResult {
6
+ readonly value: string;
7
+ readonly index: number;
8
+ }
9
+ export interface IpRegex {
10
+ (options?: Options): RegExp;
11
+ v4(options?: Options): RegExp;
12
+ v6(options?: Options): RegExp;
13
+ scanAll(input: string, options?: Options): ScanResult[];
14
+ scanAllV4(input: string, options?: Options): ScanResult[];
15
+ scanAllV6(input: string, options?: Options): ScanResult[];
16
+ }
17
+ /**
18
+ * Tests whether a string is a valid IPv4 address using regex.
19
+ * @param s - The string to test
20
+ * @returns True if the string matches IPv4 format
21
+ */
22
+ export declare function testV4(s: string): boolean;
23
+ /**
24
+ * Tests whether a string is a valid IPv6 address using regex.
25
+ * @param s - The string to test
26
+ * @returns True if the string matches IPv6 format
27
+ */
28
+ export declare function testV6(s: string): boolean;
29
+ /**
30
+ * Tests whether a string is a valid IPv4 or IPv6 address using regex.
31
+ * @param s - The string to test
32
+ * @returns True if the string matches either format
33
+ */
34
+ export declare function testV46(s: string): boolean;
35
+ declare const ipRegex: IpRegex;
36
+ export default ipRegex;
37
+ //# sourceMappingURL=regex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"regex.d.ts","sourceRoot":"","sources":["../../src/internal/regex.ts"],"names":[],"mappings":"AA6CA,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,OAAO,CAAA;CACrC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,OAAO;IACtB,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC3B,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,GAAG,MAAM,CAAA;IAC7B,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;IACvD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;IACzD,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,UAAU,EAAE,CAAA;CAC1D;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAEzC;AAED;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAE1C;AAwCD,QAAA,MAAM,OAAO,EAKP,OAAO,CAAA;AAoBb,eAAe,OAAO,CAAA"}