cidr-tools 12.1.3 → 13.0.1

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
@@ -33,24 +33,27 @@ parseCidr("::/64");
33
33
 
34
34
  All functions take CIDR addresses or single IP addresses. On single addresses, a prefix of `/32` or `/128` is assumed. Function that return networks will return a merged and sorted set of networks with IPv4 sorted before IPv6.
35
35
 
36
- It is expected that the passed CIDRs and IPs are validated as the module's own input validation is rudimentary. You are encouraged to use modules like [is-cidr](https://github.com/silverwind/is-cidr) and [is-ip](https://github.com/sindresorhus/is-ip) to validate before passing to this module.
36
+ Networks are validated with [cidr-regex](https://github.com/silverwind/cidr-regex) and anything that is not a CIDR or IP address throws, including out-of-family prefixes like `1.2.3.4/33` and ambiguous zero-padded octets like `010.0.0.1`.
37
+
38
+ Every function takes a trailing `[opts]` *Object*:
39
+ - `validate`: Whether to reject networks that are not a CIDR or IP address. `false` skips the check for input already known to be valid, and then malformed input is parsed on a best-effort basis. Default: `true`.
37
40
 
38
41
  This module requires [BigInt](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt#browser_compatibility) support in your environment.
39
42
 
40
- ### mergeCidr(networks)
43
+ ### mergeCidr(networks, [opts])
41
44
 
42
45
  - `networks` *String* or *Array*: One or more CIDR or IP addresses.
43
46
 
44
47
  Returns an array of merged networks.
45
48
 
46
- ### excludeCidr(baseNetworks, excludeNetworks)
49
+ ### excludeCidr(baseNetworks, excludeNetworks, [opts])
47
50
 
48
51
  - `baseNetworks` *String* or *Array*: One or more CIDR or IP addresses.
49
52
  - `excludeNetworks` *String* or *Array*: One or more CIDR or IP addresses to exclude from `baseNetworks`.
50
53
 
51
54
  Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`.
52
55
 
53
- ### expandCidr(networks)
56
+ ### expandCidr(networks, [opts])
54
57
 
55
58
  - `networks` *String* or *Array*: One or more CIDR or IP addresses.
56
59
 
@@ -67,14 +70,14 @@ if (end - start >= 1000000n) {
67
70
  }
68
71
  ```
69
72
 
70
- ### overlapCidr(networksA, networksB)
73
+ ### overlapCidr(networksA, networksB, [opts])
71
74
 
72
75
  - `networksA` *String* or *Array*: One or more CIDR or IP address.
73
76
  - `networksB` *String* or *Array*: One or more CIDR or IP address.
74
77
 
75
78
  Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`.
76
79
 
77
- ### containsCidr(networksA, networksB)
80
+ ### containsCidr(networksA, networksB, [opts])
78
81
 
79
82
  - `networksA` *String* or *Array*: One or more CIDR or IP address.
80
83
  - `networksB` *String* or *Array*: One or more CIDR or IP address.
@@ -87,11 +90,11 @@ Returns a boolean that indicates whether `networksA` fully contain all `networks
87
90
 
88
91
  Returns a string or array (depending on input) with a normalized representation. Will not include a prefix on single IPs. Will set network address to the start of the network.
89
92
 
90
- `opts`: *Object*
93
+ `opts`: *Object*, additionally to `validate`:
91
94
  - `compress`: Whether to compress the IP. For IPv6, this means the "best representation" all-lowercase shortest possible form. Default: `true`.
92
95
  - `hexify`: Whether to convert IPv4-Mapped IPv6 addresses to hex. Default: `false`.
93
96
 
94
- ### parseCidr(network)
97
+ ### parseCidr(network, [opts])
95
98
 
96
99
  - `network` *String*: A CIDR or IP address.
97
100
 
@@ -109,9 +112,7 @@ Returns a `parsed` Object which is used internally by this module. It can be use
109
112
  ## Related
110
113
 
111
114
  - [ip-bigint](https://github.com/silverwind/ip-bigint) - Convert IPv4 and IPv6 addresses to native BigInt and vice-versa
112
- - [ip-regex](https://github.com/sindresorhus/ip-regex) - Regular expression for matching IP addresses
113
115
  - [is-cidr](https://github.com/silverwind/is-cidr) - Check if a string is an IP address in CIDR notation
114
- - [is-ip](https://github.com/sindresorhus/is-ip) - Check if a string is an IP address
115
- - [cidr-regex](https://github.com/silverwind/cidr-regex) - Check if a string is an IP address in CIDR notation
116
+ - [cidr-regex](https://github.com/silverwind/cidr-regex) - Regular expression for matching IP addresses in CIDR notation and bare IP addresses
116
117
 
117
118
  © [silverwind](https://github.com/silverwind), distributed under BSD licence.
package/dist/index.d.ts CHANGED
@@ -11,24 +11,30 @@ type ParsedCidr = {
11
11
  start: bigint;
12
12
  end: bigint;
13
13
  };
14
+ type CidrOpts = {
15
+ /** Whether to reject networks that are not a CIDR or IP address. Default: `true` */
16
+ validate?: boolean;
17
+ };
14
18
  type NormalizeOpts = {
19
+ /** Whether to reject networks that are not a CIDR or IP address. Default: `true` */
20
+ validate?: boolean;
15
21
  compress?: boolean;
16
22
  hexify?: boolean;
17
23
  };
18
24
  /** Returns a string or array (depending on input) with a normalized representation. Will not include a prefix on single IPs. Will set network address to the start of the network. */
19
25
  declare function normalizeCidr<T extends Networks>(cidr: T, opts?: NormalizeOpts): T;
20
26
  /** Returns a `parsed` Object which is used internally by this module. It can be used to test whether the passed network is IPv4 or IPv6 or to work with the BigInts directly. */
21
- declare function parseCidr(str: Network): ParsedCidr;
27
+ declare function parseCidr(str: Network, opts?: CidrOpts): ParsedCidr;
22
28
  /** Returns an array of merged networks */
23
- declare function mergeCidr(nets: Networks): Array<Network>;
29
+ declare function mergeCidr(nets: Networks, opts?: CidrOpts): Array<Network>;
24
30
  /** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
25
- declare function excludeCidr(base: Networks, excl: Networks): Array<Network>;
31
+ declare function excludeCidr(base: Networks, excl: Networks, opts?: CidrOpts): Array<Network>;
26
32
  /** Returns a generator for individual IPs contained in the networks. */
27
- declare function expandCidr(nets: Networks): Generator<Network>;
33
+ declare function expandCidr(nets: Networks, opts?: CidrOpts): Generator<Network>;
28
34
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
29
- declare function overlapCidr(a: Networks, b: Networks): boolean;
35
+ declare function overlapCidr(a: Networks, b: Networks, opts?: CidrOpts): boolean;
30
36
  /** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
31
- declare function containsCidr(a: Networks, b: Networks): boolean;
37
+ declare function containsCidr(a: Networks, b: Networks, opts?: CidrOpts): boolean;
32
38
  declare const _default: {
33
39
  mergeCidr: typeof mergeCidr;
34
40
  excludeCidr: typeof excludeCidr;
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { normalizeIp, parseIp, stringifyIp } from "ip-bigint";
1
+ import cidrRegex from "cidr-regex";
2
+ import { parseIp, stringifyIp } from "ip-bigint";
2
3
 
3
4
  //#region index.ts
4
5
  const bits = {
@@ -13,10 +14,30 @@ const hexStrings = Array.from({ length: 256 }, (_, i) => i.toString(16));
13
14
  const hexPadStrings = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
14
15
  const hostMasks = Array.from({ length: 129 }, (_, i) => (1n << BigInt(i)) - 1n);
15
16
  const hostNotMasks = hostMasks.map((mask) => ~mask);
17
+ const networkRe = cidrRegex({
18
+ exact: true,
19
+ prefix: "optional"
20
+ });
21
+ const unchecked = { validate: false };
22
+ function invalidNetwork(net) {
23
+ return /* @__PURE__ */ new Error(`Network is not a CIDR or IP: "${net}"`);
24
+ }
25
+ function checkNetwork(net) {
26
+ if (!networkRe.test(net)) throw invalidNetwork(net);
27
+ }
28
+ function checkNetworks(nets, opts) {
29
+ if (opts?.validate === false) return;
30
+ if (typeof nets === "string") checkNetwork(nets);
31
+ else for (const net of nets) checkNetwork(net);
32
+ }
16
33
  const cmpV4StartEnd = (a, b) => a.start - b.start || a.end - b.end;
17
34
  const cmpV4Start = (a, b) => a.start - b.start;
18
35
  const cmpV6StartEnd = (a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : a.end > b.end ? 1 : a.end < b.end ? -1 : 0;
19
36
  const cmpV6Start = (a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : 0;
37
+ const hostMasks4 = Int32Array.from({ length: 33 }, (_, i) => i === 32 ? -1 : (1 << i) - 1);
38
+ function formatIPv4Fast(n) {
39
+ return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
40
+ }
20
41
  function parseIPv4Fast(s, end) {
21
42
  let num = 0;
22
43
  let octet = 0;
@@ -24,31 +45,26 @@ function parseIPv4Fast(s, end) {
24
45
  let digits = 0;
25
46
  for (let i = 0; i < end; i++) {
26
47
  const c = s.charCodeAt(i);
27
- if (c === 46) {
48
+ if (c >= 48 && c <= 57) {
49
+ octet = octet * 10 + (c - 48);
50
+ digits++;
51
+ } else if (c === 46) {
28
52
  if (digits === 0 || octet > 255) return -1;
29
53
  num = num << 8 | octet;
30
54
  octet = 0;
31
55
  dots++;
32
56
  digits = 0;
33
- } else if (c >= 48 && c <= 57) {
34
- octet = octet * 10 + (c - 48);
35
- digits++;
36
57
  } else return -1;
37
58
  }
38
59
  if (dots !== 3 || digits === 0 || octet > 255) return -1;
39
60
  return (num << 8 | octet) >>> 0;
40
61
  }
41
- const hostMasks4 = Int32Array.from({ length: 33 }, (_, i) => i === 32 ? -1 : (1 << i) - 1);
42
- function formatIPv4Fast(n) {
43
- return octetDotStrings[n >>> 24 & 255] + octetDotStrings[n >>> 16 & 255] + octetDotStrings[n >>> 8 & 255] + octetStrings[n & 255];
44
- }
45
62
  function parsePrefixNum(str, slashIndex) {
46
- if (slashIndex === -1) return -1;
47
- if (slashIndex + 1 >= str.length) throw new Error(`Network is not a CIDR or IP: "${str}"`);
63
+ if (slashIndex + 1 >= str.length) throw invalidNetwork(str);
48
64
  let prefixNum = 0;
49
65
  for (let i = slashIndex + 1; i < str.length; i++) {
50
66
  const c = str.charCodeAt(i);
51
- if (c < 48 || c > 57) throw new Error(`Network is not a CIDR or IP: "${str}"`);
67
+ if (c < 48 || c > 57) throw invalidNetwork(str);
52
68
  prefixNum = prefixNum * 10 + (c - 48);
53
69
  }
54
70
  return prefixNum;
@@ -58,9 +74,32 @@ let rangeV4End = 0;
58
74
  let rangeV4Prefix = 0;
59
75
  let rangeSlashIndex = -1;
60
76
  function parseIPv4Range(str) {
61
- rangeSlashIndex = str.indexOf("/");
62
- const v4num = parseIPv4Fast(str, rangeSlashIndex !== -1 ? rangeSlashIndex : str.length);
63
- if (v4num === -1) return false;
77
+ const len = str.length;
78
+ let num = 0;
79
+ let octet = 0;
80
+ let dots = 0;
81
+ let digits = 0;
82
+ let i = 0;
83
+ for (; i < len; i++) {
84
+ const c = str.charCodeAt(i);
85
+ if (c >= 48 && c <= 57) {
86
+ octet = octet * 10 + (c - 48);
87
+ digits++;
88
+ } else if (c === 46) {
89
+ if (digits === 0 || octet > 255) break;
90
+ num = num << 8 | octet;
91
+ octet = 0;
92
+ dots++;
93
+ digits = 0;
94
+ } else break;
95
+ }
96
+ if (i < len && str.charCodeAt(i) === 47) rangeSlashIndex = i;
97
+ else {
98
+ rangeSlashIndex = str.indexOf("/", i);
99
+ if (i < len) return false;
100
+ }
101
+ if (dots !== 3 || digits === 0 || octet > 255) return false;
102
+ const v4num = (num << 8 | octet) >>> 0;
64
103
  rangeV4Prefix = rangeSlashIndex !== -1 ? parsePrefixNum(str, rangeSlashIndex) : 32;
65
104
  const mask = hostMasks4[Math.max(32 - rangeV4Prefix, 0)];
66
105
  rangeV4Start = (v4num & ~mask) >>> 0;
@@ -75,7 +114,7 @@ function doNormalize(cidr, opts) {
75
114
  const slashIndex = rangeSlashIndex;
76
115
  const prefixPresent = slashIndex !== -1;
77
116
  let prefixNum = prefixPresent ? parsePrefixNum(cidr, slashIndex) : -1;
78
- const { number, version, ipv4mapped, scopeid } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr);
117
+ const { number, version, ipv4mapped, scopeid } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr, unchecked);
79
118
  if (prefixNum === -1) prefixNum = bits[version];
80
119
  if (version === 4) {
81
120
  const hostBits = 32 - prefixNum;
@@ -83,44 +122,49 @@ function doNormalize(cidr, opts) {
83
122
  const ip = formatIPv4Fast((Number(number) & ~mask) >>> 0);
84
123
  return hostBits > 0 || prefixPresent ? ip + prefixStrings[prefixNum] : ip;
85
124
  }
86
- const { compress = true, hexify = false } = opts || {};
125
+ const compress = opts?.compress ?? true;
126
+ const hexify = opts?.hexify ?? false;
87
127
  const hostBits = 128 - prefixNum;
88
- if (hostBits <= 0 && !prefixPresent) return normalizeIp(cidr, {
128
+ if (hostBits <= 0 && !prefixPresent) return stringifyIp({
129
+ number,
130
+ version,
131
+ ipv4mapped,
132
+ scopeid
133
+ }, {
89
134
  compress,
90
135
  hexify
91
136
  });
92
137
  const start = hostBits > 0 ? number & hostNotMasks[hostBits] : number;
93
- const ip = stringifyIp({
138
+ return stringifyIp({
94
139
  number: start,
95
140
  version,
96
141
  ipv4mapped: ipv4mapped && start >> 32n === 65535n,
97
142
  scopeid
98
- }, { hexify });
99
- return (compress ? ip : normalizeIp(ip, {
143
+ }, {
100
144
  compress,
101
145
  hexify
102
- })) + prefixStrings[prefixNum];
146
+ }) + prefixStrings[prefixNum];
103
147
  }
104
148
  /** Returns a string or array (depending on input) with a normalized representation. Will not include a prefix on single IPs. Will set network address to the start of the network. */
105
149
  function normalizeCidr(cidr, opts) {
150
+ checkNetworks(cidr, opts);
106
151
  return typeof cidr === "string" ? doNormalize(cidr, opts) : cidr.map((entry) => doNormalize(entry, opts));
107
152
  }
108
153
  /** Returns a `parsed` Object which is used internally by this module. It can be used to test whether the passed network is IPv4 or IPv6 or to work with the BigInts directly. */
109
- function parseCidr(str) {
154
+ function parseCidr(str, opts) {
155
+ if (opts?.validate !== false) checkNetwork(str);
110
156
  const slashIndex = str.indexOf("/");
111
- const ipEnd = slashIndex !== -1 ? slashIndex : str.length;
112
157
  const prefixPresent = slashIndex !== -1;
113
- const v4num = parseIPv4Fast(str, ipEnd);
158
+ const v4num = parseIPv4Fast(str, prefixPresent ? slashIndex : str.length);
114
159
  if (v4num !== -1) {
115
160
  const prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : 32;
116
161
  const ip = formatIPv4Fast(v4num);
117
- const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
118
162
  const mask = hostMasks4[Math.max(32 - prefixNum, 0)];
119
163
  return {
120
164
  cidr: ip + prefixStrings[prefixNum],
121
165
  ip,
122
166
  version: 4,
123
- prefix,
167
+ prefix: prefixNumStrings[prefixNum] ?? String(prefixNum),
124
168
  prefixPresent,
125
169
  start: BigInt((v4num & ~mask) >>> 0),
126
170
  end: BigInt((v4num | mask) >>> 0)
@@ -128,8 +172,7 @@ function parseCidr(str) {
128
172
  }
129
173
  const ipPart = prefixPresent ? str.substring(0, slashIndex) : str;
130
174
  let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
131
- const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
132
- if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
175
+ const { number, version, ipv4mapped, scopeid } = parseIp(ipPart, unchecked);
133
176
  const numBits = bits[version];
134
177
  if (prefixNum === -1) prefixNum = numBits;
135
178
  const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
@@ -156,17 +199,11 @@ function parseCidr(str) {
156
199
  end
157
200
  };
158
201
  }
159
- function parseCidrLean(str) {
160
- if (parseIPv4Range(str)) return {
161
- start: rangeV4Start,
162
- end: rangeV4End,
163
- version: 4
164
- };
202
+ function parseCidrLeanSlow(str) {
165
203
  const slashIndex = rangeSlashIndex;
166
204
  const ipPart = slashIndex !== -1 ? str.substring(0, slashIndex) : str;
167
205
  let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
168
- const { number, version } = parseIp(ipPart);
169
- if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
206
+ const { number, version } = parseIp(ipPart, unchecked);
170
207
  const numBits = bits[version];
171
208
  if (prefixNum === -1) prefixNum = numBits;
172
209
  const hostBits = numBits - prefixNum;
@@ -190,6 +227,14 @@ function parseCidrLean(str) {
190
227
  version: 6
191
228
  };
192
229
  }
230
+ function parseCidrLean(str) {
231
+ if (parseIPv4Range(str)) return {
232
+ start: rangeV4Start,
233
+ end: rangeV4End,
234
+ version: 4
235
+ };
236
+ return parseCidrLeanSlow(str);
237
+ }
193
238
  function bigintBitLength(n) {
194
239
  if (n === 0n) return 0;
195
240
  let len = 0;
@@ -203,10 +248,6 @@ function bigintBitLength(n) {
203
248
  }
204
249
  return len + 32 - Math.clz32(Number(n));
205
250
  }
206
- function biggestPowerOfTwo(num) {
207
- if (num === 0n) return 0n;
208
- return 1n << BigInt(bigintBitLength(num) - 1);
209
- }
210
251
  function biggestPowerOfTwo4(num) {
211
252
  if (num === 0) return 0;
212
253
  if (num >= 4294967296) return 4294967296;
@@ -236,63 +277,50 @@ function subparts6(pStart, pEnd, output) {
236
277
  while (start <= pEnd) {
237
278
  const size = pEnd - start + 1n;
238
279
  const lowBit = start & -start;
239
- const blockSize = lowBit !== 0n && lowBit <= size ? lowBit : biggestPowerOfTwo(size);
280
+ const alignedFully = lowBit !== 0n && lowBit <= size;
281
+ const blockBits = bigintBitLength(alignedFully ? lowBit : size);
240
282
  output.push(stringifyIp({
241
283
  number: start,
242
284
  version: 6
243
- }) + prefixStrings[129 - bigintBitLength(blockSize)]);
244
- start += blockSize;
285
+ }) + prefixStrings[129 - blockBits]);
286
+ start += alignedFully ? lowBit : 1n << BigInt(blockBits - 1);
245
287
  }
246
288
  }
247
289
  function mergeIntervalsRaw4(nets) {
248
- if (nets.length === 0) return [];
290
+ if (nets.length < 2) return nets;
249
291
  nets.sort(cmpV4StartEnd);
250
- const merged = [];
251
- let curStart = nets[0].start;
252
- let curEnd = nets[0].end;
292
+ let last = 0;
293
+ let cur = nets[0];
253
294
  for (let i = 1; i < nets.length; i++) {
254
295
  const { start, end } = nets[i];
255
- if (start <= curEnd + 1) {
256
- if (end > curEnd) curEnd = end;
296
+ if (start <= cur.end + 1) {
297
+ if (end > cur.end) cur.end = end;
257
298
  } else {
258
- merged.push({
259
- start: curStart,
260
- end: curEnd
261
- });
262
- curStart = start;
263
- curEnd = end;
299
+ cur = nets[++last];
300
+ cur.start = start;
301
+ cur.end = end;
264
302
  }
265
303
  }
266
- merged.push({
267
- start: curStart,
268
- end: curEnd
269
- });
270
- return merged;
304
+ nets.length = last + 1;
305
+ return nets;
271
306
  }
272
307
  function mergeIntervalsRaw6(nets) {
273
- if (nets.length === 0) return [];
308
+ if (nets.length < 2) return nets;
274
309
  nets.sort(cmpV6StartEnd);
275
- const merged = [];
276
- let curStart = nets[0].start;
277
- let curEnd = nets[0].end;
310
+ let last = 0;
311
+ let cur = nets[0];
278
312
  for (let i = 1; i < nets.length; i++) {
279
313
  const { start, end } = nets[i];
280
- if (start <= curEnd + 1n) {
281
- if (end > curEnd) curEnd = end;
314
+ if (start <= cur.end + 1n) {
315
+ if (end > cur.end) cur.end = end;
282
316
  } else {
283
- merged.push({
284
- start: curStart,
285
- end: curEnd
286
- });
287
- curStart = start;
288
- curEnd = end;
317
+ cur = nets[++last];
318
+ cur.start = start;
319
+ cur.end = end;
289
320
  }
290
321
  }
291
- merged.push({
292
- start: curStart,
293
- end: curEnd
294
- });
295
- return merged;
322
+ nets.length = last + 1;
323
+ return nets;
296
324
  }
297
325
  function subtractSorted4(bases, excls) {
298
326
  if (excls.length === 0) return bases;
@@ -345,7 +373,8 @@ function subtractSorted6(bases, excls) {
345
373
  return result;
346
374
  }
347
375
  /** Returns an array of merged networks */
348
- function mergeCidr(nets) {
376
+ function mergeCidr(nets, opts) {
377
+ checkNetworks(nets, opts);
349
378
  const v4 = [], v6 = [];
350
379
  for (const str of typeof nets === "string" ? [nets] : nets) {
351
380
  const net = parseCidrLean(str);
@@ -358,7 +387,9 @@ function mergeCidr(nets) {
358
387
  return merged;
359
388
  }
360
389
  /** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
361
- function excludeCidr(base, excl) {
390
+ function excludeCidr(base, excl, opts) {
391
+ checkNetworks(base, opts);
392
+ checkNetworks(excl, opts);
362
393
  const v4base = [], v6base = [];
363
394
  const v4excl = [], v6excl = [];
364
395
  for (const str of typeof base === "string" ? [base] : base) {
@@ -377,7 +408,11 @@ function excludeCidr(base, excl) {
377
408
  return result;
378
409
  }
379
410
  /** Returns a generator for individual IPs contained in the networks. */
380
- function* expandCidr(nets) {
411
+ function expandCidr(nets, opts) {
412
+ checkNetworks(nets, opts);
413
+ return expandChecked(nets);
414
+ }
415
+ function* expandChecked(nets) {
381
416
  const v4 = [], v6 = [];
382
417
  for (const str of typeof nets === "string" ? [nets] : nets) {
383
418
  const net = parseCidrLean(str);
@@ -424,16 +459,18 @@ function* expandCidr(nets) {
424
459
  }
425
460
  }
426
461
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
427
- function overlapCidr(a, b) {
462
+ function overlapCidr(a, b, opts) {
463
+ checkNetworks(a, opts);
464
+ checkNetworks(b, opts);
428
465
  if (typeof a === "string" && typeof b === "string") {
429
466
  if (parseIPv4Range(a)) {
430
467
  const startA = rangeV4Start, endA = rangeV4End;
431
468
  if (parseIPv4Range(b)) return startA <= rangeV4End && rangeV4Start <= endA;
432
- const pb = parseCidrLean(b);
469
+ const pb = parseCidrLeanSlow(b);
433
470
  if (pb.version !== 4) return false;
434
471
  return startA <= pb.end && pb.start <= endA;
435
472
  }
436
- const pa = parseCidrLean(a);
473
+ const pa = parseCidrLeanSlow(a);
437
474
  const pb = parseCidrLean(b);
438
475
  if (pa.version !== pb.version) return false;
439
476
  return pa.start <= pb.end && pb.start <= pa.end;
@@ -481,16 +518,18 @@ function overlapCidr(a, b) {
481
518
  return false;
482
519
  }
483
520
  /** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
484
- function containsCidr(a, b) {
521
+ function containsCidr(a, b, opts) {
522
+ checkNetworks(a, opts);
523
+ checkNetworks(b, opts);
485
524
  if (typeof a === "string" && typeof b === "string") {
486
525
  if (parseIPv4Range(a)) {
487
526
  const startA = rangeV4Start, endA = rangeV4End;
488
527
  if (parseIPv4Range(b)) return startA <= rangeV4Start && endA >= rangeV4End;
489
- const pb = parseCidrLean(b);
528
+ const pb = parseCidrLeanSlow(b);
490
529
  if (pb.version !== 4) return false;
491
530
  return startA <= pb.start && endA >= pb.end;
492
531
  }
493
- const pa = parseCidrLean(a);
532
+ const pa = parseCidrLeanSlow(a);
494
533
  const pb = parseCidrLean(b);
495
534
  if (pa.version !== pb.version) return false;
496
535
  return pa.start <= pb.start && pa.end >= pb.end;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "12.1.3",
3
+ "version": "13.0.1",
4
4
  "author": "silverwind <me@silverwind.io>",
5
5
  "description": "Tools to work with IPv4 and IPv6 CIDR",
6
6
  "keywords": [
@@ -29,7 +29,8 @@
29
29
  "bun": "*"
30
30
  },
31
31
  "dependencies": {
32
- "ip-bigint": "^9.0.7"
32
+ "cidr-regex": "^7.1.0",
33
+ "ip-bigint": "^10.0.1"
33
34
  },
34
35
  "devDependencies": {
35
36
  "@types/node": "26.1.2",