cidr-tools 11.0.10 → 11.0.12

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 (3) hide show
  1. package/dist/index.d.ts +26 -24
  2. package/dist/index.js +351 -332
  3. package/package.json +12 -11
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ //#region index.d.ts
1
2
  type IPv4Address = string;
2
3
  type IPv6Address = string;
3
4
  type IPv4CIDR = string;
@@ -6,38 +7,39 @@ type Network = IPv4Address | IPv4CIDR | IPv6Address | IPv6CIDR;
6
7
  type Networks = Network | Array<Network>;
7
8
  type ValidIpVersion = 4 | 6;
8
9
  type ParsedCidr = {
9
- cidr: string;
10
- ip: string;
11
- version: ValidIpVersion;
12
- prefix: string;
13
- prefixPresent: boolean;
14
- start: bigint;
15
- end: bigint;
10
+ cidr: string;
11
+ ip: string;
12
+ version: ValidIpVersion;
13
+ prefix: string;
14
+ prefixPresent: boolean;
15
+ start: bigint;
16
+ end: bigint;
16
17
  };
17
18
  type NormalizeOpts = {
18
- compress?: boolean;
19
- hexify?: boolean;
19
+ compress?: boolean;
20
+ hexify?: boolean;
20
21
  };
21
22
  /** 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. */
22
- export declare function normalizeCidr<T extends Network | Array<Network>>(cidr: T, opts?: NormalizeOpts): T;
23
+ declare function normalizeCidr<T extends Network | Array<Network>>(cidr: T, opts?: NormalizeOpts): T;
23
24
  /** 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. */
24
- export declare function parseCidr(str: Network): ParsedCidr;
25
+ declare function parseCidr(str: Network): ParsedCidr;
25
26
  /** Returns an array of merged networks */
26
- export declare function mergeCidr(nets: Networks): Array<Network>;
27
+ declare function mergeCidr(nets: Networks): Array<Network>;
27
28
  /** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
28
- export declare function excludeCidr(base: Networks, excl: Networks): Array<Network>;
29
- export declare function expandCidr(nets: Networks): Generator<Network>;
29
+ declare function excludeCidr(base: Networks, excl: Networks): Array<Network>;
30
+ declare function expandCidr(nets: Networks): Generator<Network>;
30
31
  /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
31
- export declare function overlapCidr(a: Networks, b: Networks): boolean;
32
+ declare function overlapCidr(a: Networks, b: Networks): boolean;
32
33
  /** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
33
- export declare function containsCidr(a: Networks, b: Networks): boolean;
34
+ declare function containsCidr(a: Networks, b: Networks): boolean;
34
35
  declare const _default: {
35
- mergeCidr: typeof mergeCidr;
36
- excludeCidr: typeof excludeCidr;
37
- expandCidr: typeof expandCidr;
38
- overlapCidr: typeof overlapCidr;
39
- containsCidr: typeof containsCidr;
40
- normalizeCidr: typeof normalizeCidr;
41
- parseCidr: typeof parseCidr;
36
+ mergeCidr: typeof mergeCidr;
37
+ excludeCidr: typeof excludeCidr;
38
+ expandCidr: typeof expandCidr;
39
+ overlapCidr: typeof overlapCidr;
40
+ containsCidr: typeof containsCidr;
41
+ normalizeCidr: typeof normalizeCidr;
42
+ parseCidr: typeof parseCidr;
42
43
  };
43
- export default _default;
44
+ //#endregion
45
+ export { containsCidr, _default as default, excludeCidr, expandCidr, mergeCidr, normalizeCidr, overlapCidr, parseCidr };
package/dist/index.js CHANGED
@@ -1,362 +1,381 @@
1
- import { parseIp, stringifyIp, normalizeIp } from "ip-bigint";
2
- const bits = { 4: 32, 6: 128 };
3
- function uniq(arr) {
4
- const set = new Set(arr);
5
- return set.size === arr.length ? arr : Array.from(set);
1
+ import { normalizeIp, parseIp, stringifyIp } from "ip-bigint";
2
+
3
+ //#region index.ts
4
+ const bits = {
5
+ 4: 32,
6
+ 6: 128
7
+ };
8
+ function parseIPv4Fast(s) {
9
+ let num = 0;
10
+ let octet = 0;
11
+ let dots = 0;
12
+ let digits = 0;
13
+ for (let i = 0; i < s.length; i++) {
14
+ const c = s.charCodeAt(i);
15
+ if (c === 46) {
16
+ if (digits === 0 || octet > 255) return -1;
17
+ num = (num << 8 | octet) >>> 0;
18
+ octet = 0;
19
+ dots++;
20
+ digits = 0;
21
+ } else if (c >= 48 && c <= 57) {
22
+ octet = octet * 10 + (c - 48);
23
+ digits++;
24
+ } else return -1;
25
+ }
26
+ if (dots !== 3 || digits === 0 || octet > 255) return -1;
27
+ return (num << 8 | octet) >>> 0;
28
+ }
29
+ function formatIPv4Fast(n) {
30
+ return `${n >>> 24 & 255}.${n >>> 16 & 255}.${n >>> 8 & 255}.${n & 255}`;
6
31
  }
7
32
  function doNormalize(cidr, { compress = true, hexify = false } = {}) {
8
- const { start, end, prefix, version, prefixPresent } = parseCidr(cidr);
9
- if (start !== end || prefixPresent) {
10
- const ip = normalizeIp(stringifyIp({ number: start, version }), { compress, hexify });
11
- return `${ip}/${prefix}`;
12
- } else {
13
- return normalizeIp(cidr, { compress, hexify });
14
- }
33
+ const { start, end, prefix, version, prefixPresent } = parseCidr(cidr);
34
+ if (start !== end || prefixPresent) return `${normalizeIp(stringifyIp({
35
+ number: start,
36
+ version
37
+ }), {
38
+ compress,
39
+ hexify
40
+ })}/${prefix}`;
41
+ else return normalizeIp(cidr, {
42
+ compress,
43
+ hexify
44
+ });
15
45
  }
46
+ /** 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. */
16
47
  function normalizeCidr(cidr, opts) {
17
- if (Array.isArray(cidr)) {
18
- return cidr.map((entry) => normalizeCidr(entry, opts));
19
- } else {
20
- return doNormalize(cidr, opts);
21
- }
48
+ if (Array.isArray(cidr)) return cidr.map((entry) => normalizeCidr(entry, opts));
49
+ else return doNormalize(cidr, opts);
22
50
  }
51
+ /** 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. */
23
52
  function parseCidr(str) {
24
- const parsed = /* @__PURE__ */ Object.create(null);
25
- const slashIndex = str.indexOf("/");
26
- let ipPart;
27
- let prefix;
28
- if (slashIndex !== -1) {
29
- ipPart = str.substring(0, slashIndex);
30
- prefix = str.substring(slashIndex + 1);
31
- if (!/^[0-9]+$/.test(prefix)) {
32
- throw new Error(`Network is not a CIDR or IP: "${str}"`);
33
- }
34
- parsed.prefixPresent = true;
35
- } else {
36
- ipPart = str;
37
- parsed.prefixPresent = false;
38
- prefix = "";
39
- }
40
- const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
41
- if (!version) {
42
- throw new Error(`Network is not a CIDR or IP: "${str}"`);
43
- }
44
- if (!parsed.prefixPresent) {
45
- prefix = String(bits[version]);
46
- }
47
- parsed.version = version;
48
- parsed.ip = stringifyIp({ number, version, ipv4mapped, scopeid });
49
- parsed.cidr = `${parsed.ip}/${prefix}`;
50
- parsed.prefix = prefix;
51
- const numBits = bits[version];
52
- const hostBits = numBits - Number(prefix);
53
- const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
54
- parsed.start = number & ~mask;
55
- parsed.end = number | mask;
56
- return parsed;
53
+ const parsed = Object.create(null);
54
+ const slashIndex = str.indexOf("/");
55
+ let ipPart;
56
+ let prefix;
57
+ if (slashIndex !== -1) {
58
+ ipPart = str.substring(0, slashIndex);
59
+ prefix = str.substring(slashIndex + 1);
60
+ if (!/^[0-9]+$/.test(prefix)) throw new Error(`Network is not a CIDR or IP: "${str}"`);
61
+ parsed.prefixPresent = true;
62
+ } else {
63
+ ipPart = str;
64
+ parsed.prefixPresent = false;
65
+ prefix = "";
66
+ }
67
+ const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
68
+ if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
69
+ if (!parsed.prefixPresent) prefix = String(bits[version]);
70
+ parsed.version = version;
71
+ parsed.ip = stringifyIp({
72
+ number,
73
+ version,
74
+ ipv4mapped,
75
+ scopeid
76
+ });
77
+ parsed.cidr = `${parsed.ip}/${prefix}`;
78
+ parsed.prefix = prefix;
79
+ const hostBits = bits[version] - Number(prefix);
80
+ const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
81
+ parsed.start = number & ~mask;
82
+ parsed.end = number | mask;
83
+ return parsed;
57
84
  }
58
85
  function parseCidrLean(str) {
59
- const slashIndex = str.indexOf("/");
60
- let ipPart;
61
- let prefixNum;
62
- if (slashIndex !== -1) {
63
- ipPart = str.substring(0, slashIndex);
64
- const prefixStr = str.substring(slashIndex + 1);
65
- if (!/^[0-9]+$/.test(prefixStr)) {
66
- throw new Error(`Network is not a CIDR or IP: "${str}"`);
67
- }
68
- prefixNum = Number(prefixStr);
69
- } else {
70
- ipPart = str;
71
- prefixNum = -1;
72
- }
73
- const { number, version } = parseIp(ipPart);
74
- if (!version) {
75
- throw new Error(`Network is not a CIDR or IP: "${str}"`);
76
- }
77
- if (prefixNum === -1) {
78
- prefixNum = bits[version];
79
- }
80
- const numBits = bits[version];
81
- const hostBits = numBits - prefixNum;
82
- const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
83
- return {
84
- start: number & ~mask,
85
- end: number | mask,
86
- version
87
- };
88
- }
89
- function excludeNetsParts(a, b) {
90
- if (a.start > b.end || a.end < b.start) {
91
- return [a];
92
- }
93
- if (a.start === b.start && a.end === b.end) {
94
- return [];
95
- }
96
- if (a.start > b.start && a.end < b.end) {
97
- return [];
98
- }
99
- const parts = [];
100
- if (a.start < b.start && a.end <= b.end) {
101
- parts.push({ start: a.start, end: b.start - 1n });
102
- }
103
- if (a.start >= b.start && a.end > b.end) {
104
- parts.push({ start: b.end + 1n, end: a.end });
105
- }
106
- if (a.start < b.start && a.end > b.end) {
107
- parts.push(
108
- { start: a.start, end: b.start - 1n },
109
- { start: b.end + 1n, end: a.end }
110
- );
111
- }
112
- const remaining = [];
113
- for (const part of parts) {
114
- subparts(part, remaining);
115
- }
116
- return remaining;
86
+ const slashIndex = str.indexOf("/");
87
+ let ipPart;
88
+ let prefixNum;
89
+ if (slashIndex !== -1) {
90
+ ipPart = str.substring(0, slashIndex);
91
+ const prefixStr = str.substring(slashIndex + 1);
92
+ if (!/^[0-9]+$/.test(prefixStr)) throw new Error(`Network is not a CIDR or IP: "${str}"`);
93
+ prefixNum = Number(prefixStr);
94
+ } else {
95
+ ipPart = str;
96
+ prefixNum = -1;
97
+ }
98
+ if (!ipPart.includes(":")) {
99
+ const v4num = parseIPv4Fast(ipPart);
100
+ if (v4num !== -1) {
101
+ if (prefixNum === -1) prefixNum = 32;
102
+ const bigNum = BigInt(v4num);
103
+ const hostBits = 32 - prefixNum;
104
+ const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
105
+ return {
106
+ start: bigNum & ~mask,
107
+ end: bigNum | mask,
108
+ version: 4
109
+ };
110
+ }
111
+ }
112
+ const { number, version } = parseIp(ipPart);
113
+ if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
114
+ if (prefixNum === -1) prefixNum = bits[version];
115
+ const hostBits = bits[version] - prefixNum;
116
+ const mask = hostBits > 0 ? (1n << BigInt(hostBits)) - 1n : 0n;
117
+ return {
118
+ start: number & ~mask,
119
+ end: number | mask,
120
+ version
121
+ };
117
122
  }
118
123
  function biggestPowerOfTwo(num) {
119
- if (num === 0n) return 0n;
120
- let b = 0n;
121
- let n = num >> 1n;
122
- while (n > 0n) {
123
- b++;
124
- n >>= 1n;
125
- }
126
- return 1n << b;
124
+ if (num === 0n) return 0n;
125
+ return 1n << BigInt(num.toString(2).length - 1);
127
126
  }
128
127
  function subparts(part, output) {
129
- if (!output) output = [];
130
- if (part.end < part.start) {
131
- return output;
132
- }
133
- if (part.end === part.start) {
134
- output.push(part);
135
- return output;
136
- }
137
- if (part.end - part.start === 1n) {
138
- if (part.end % 2n === 0n) {
139
- output.push({ start: part.start, end: part.start }, { start: part.end, end: part.end });
140
- } else {
141
- output.push({ start: part.start, end: part.end });
142
- }
143
- return output;
144
- }
145
- const size = diff(part.end, part.start);
146
- let biggest = biggestPowerOfTwo(size);
147
- let start;
148
- let end;
149
- if (size === biggest && part.start % biggest === 0n) {
150
- output.push(part);
151
- return output;
152
- } else if (part.start % biggest === 0n) {
153
- start = part.start;
154
- end = start + biggest - 1n;
155
- } else {
156
- start = part.end / biggest * biggest;
157
- if (start + biggest - 1n > part.end) {
158
- start = (part.end / biggest - 1n) * biggest;
159
- while (start < part.start) {
160
- biggest /= 2n;
161
- start = (part.end / biggest - 1n) * biggest;
162
- }
163
- end = start + biggest - 1n;
164
- } else {
165
- start = part.end / biggest * biggest;
166
- end = start + biggest - 1n;
167
- }
168
- }
169
- if (start !== part.start) {
170
- subparts({ start: part.start, end: start - 1n }, output);
171
- }
172
- output.push({ start, end });
173
- if (end !== part.end) {
174
- subparts({ start: end + 1n, end: part.end }, output);
175
- }
176
- return output;
128
+ if (!output) output = [];
129
+ if (part.end < part.start) return output;
130
+ if (part.end === part.start) {
131
+ output.push(part);
132
+ return output;
133
+ }
134
+ if (part.end - part.start === 1n) {
135
+ if (part.end % 2n === 0n) output.push({
136
+ start: part.start,
137
+ end: part.start
138
+ }, {
139
+ start: part.end,
140
+ end: part.end
141
+ });
142
+ else output.push({
143
+ start: part.start,
144
+ end: part.end
145
+ });
146
+ return output;
147
+ }
148
+ const size = diff(part.end, part.start);
149
+ let biggest = biggestPowerOfTwo(size);
150
+ let start;
151
+ let end;
152
+ if (size === biggest && part.start % biggest === 0n) {
153
+ output.push(part);
154
+ return output;
155
+ } else if (part.start % biggest === 0n) {
156
+ start = part.start;
157
+ end = start + biggest - 1n;
158
+ } else {
159
+ start = part.end / biggest * biggest;
160
+ if (start + biggest - 1n > part.end) {
161
+ start = (part.end / biggest - 1n) * biggest;
162
+ while (start < part.start) {
163
+ biggest /= 2n;
164
+ start = (part.end / biggest - 1n) * biggest;
165
+ }
166
+ end = start + biggest - 1n;
167
+ } else {
168
+ start = part.end / biggest * biggest;
169
+ end = start + biggest - 1n;
170
+ }
171
+ }
172
+ if (start !== part.start) subparts({
173
+ start: part.start,
174
+ end: start - 1n
175
+ }, output);
176
+ output.push({
177
+ start,
178
+ end
179
+ });
180
+ if (end !== part.end) subparts({
181
+ start: end + 1n,
182
+ end: part.end
183
+ }, output);
184
+ return output;
177
185
  }
178
186
  function diff(a, b) {
179
- return a + 1n - b;
187
+ return a + 1n - b;
180
188
  }
181
189
  function formatPart(part, version) {
182
- const ip = normalizeIp(stringifyIp({ number: part.start, version }));
183
- const size = diff(part.end, part.start);
184
- let hostBits = 0;
185
- let s = size >> 1n;
186
- while (s > 0n) {
187
- s >>= 1n;
188
- hostBits++;
189
- }
190
- const prefix = bits[version] - hostBits;
191
- return `${ip}/${prefix}`;
190
+ if (version === 4) {
191
+ const ip = formatIPv4Fast(Number(part.start));
192
+ const sizeNum = Number(part.end - part.start) + 1;
193
+ return `${ip}/${32 - (sizeNum <= 1 ? 0 : sizeNum >= 4294967296 ? 32 : 31 - Math.clz32(sizeNum))}`;
194
+ }
195
+ const ip = stringifyIp({
196
+ number: part.start,
197
+ version
198
+ });
199
+ const size = diff(part.end, part.start);
200
+ const hostBits = size <= 1n ? 0 : size.toString(2).length - 1;
201
+ return `${ip}/${bits[version] - hostBits}`;
192
202
  }
193
- function mapNets(nets) {
194
- const maps = { 4: /* @__PURE__ */ new Map(), 6: /* @__PURE__ */ new Map() };
195
- for (const { start, end, version } of nets) {
196
- let startEntry = maps[version].get(start);
197
- if (!startEntry) {
198
- startEntry = { start: 0, end: 0 };
199
- maps[version].set(start, startEntry);
200
- }
201
- let endEntry = maps[version].get(end);
202
- if (!endEntry) {
203
- endEntry = { start: 0, end: 0 };
204
- maps[version].set(end, endEntry);
205
- }
206
- startEntry.start += 1;
207
- endEntry.end += 1;
208
- }
209
- return maps;
203
+ function mergeIntervalsRaw(nets) {
204
+ if (nets.length === 0) return [];
205
+ const sorted = nets.slice().sort((a, b) => a.start > b.start ? 1 : a.start < b.start ? -1 : a.end > b.end ? 1 : a.end < b.end ? -1 : 0);
206
+ const merged = [];
207
+ let curStart = sorted[0].start;
208
+ let curEnd = sorted[0].end;
209
+ for (let i = 1; i < sorted.length; i++) {
210
+ const { start, end } = sorted[i];
211
+ if (start <= curEnd + 1n) {
212
+ if (end > curEnd) curEnd = end;
213
+ } else {
214
+ merged.push({
215
+ start: curStart,
216
+ end: curEnd
217
+ });
218
+ curStart = start;
219
+ curEnd = end;
220
+ }
221
+ }
222
+ merged.push({
223
+ start: curStart,
224
+ end: curEnd
225
+ });
226
+ return merged;
210
227
  }
211
- function doMerge(maps) {
212
- let start = null;
213
- let end = null;
214
- const numbers = Array.from(maps.keys()).sort((a, b) => a > b ? 1 : a < b ? -1 : 0);
215
- let depth = 0;
216
- const merged = [];
217
- for (const [index2, number] of numbers.entries()) {
218
- const marker = maps.get(number);
219
- if (start === null && marker.start) start = number;
220
- if (marker.end) end = number;
221
- if (start === null) continue;
222
- if (marker.start) depth += marker.start;
223
- if (marker.end) depth -= marker.end;
224
- const next = numbers[index2 + 1];
225
- if (marker.end && depth === 0 && next !== void 0 && next - number > 1n) {
226
- subparts({ start, end }, merged);
227
- start = null;
228
- end = null;
229
- } else if (index2 === numbers.length - 1) {
230
- subparts({ start, end }, merged);
231
- }
232
- }
233
- return merged;
228
+ function mergeIntervals(nets) {
229
+ const merged = [];
230
+ for (const part of mergeIntervalsRaw(nets)) subparts(part, merged);
231
+ return merged;
234
232
  }
233
+ function subtractSorted(bases, excls) {
234
+ if (excls.length === 0) return bases;
235
+ if (bases.length === 0) return [];
236
+ const result = [];
237
+ let j = 0;
238
+ for (const base of bases) {
239
+ let start = base.start;
240
+ const end = base.end;
241
+ while (j < excls.length && excls[j].end < start) j++;
242
+ let k = j;
243
+ while (k < excls.length && excls[k].start <= end && start <= end) {
244
+ if (excls[k].start > start) result.push({
245
+ start,
246
+ end: excls[k].start - 1n
247
+ });
248
+ start = excls[k].end + 1n;
249
+ k++;
250
+ }
251
+ if (start <= end) result.push({
252
+ start,
253
+ end
254
+ });
255
+ }
256
+ return result;
257
+ }
258
+ /** Returns an array of merged networks */
235
259
  function mergeCidr(nets) {
236
- const arr = uniq(Array.isArray(nets) ? nets : [nets]).map(parseCidrLean);
237
- const maps = mapNets(arr);
238
- const merged = [];
239
- for (const v of [4, 6]) {
240
- for (const part of doMerge(maps[v])) {
241
- merged.push(formatPart(part, v));
242
- }
243
- }
244
- return merged;
260
+ const arr = (Array.isArray(nets) ? nets : [nets]).map(parseCidrLean);
261
+ const byVersion = {
262
+ 4: [],
263
+ 6: []
264
+ };
265
+ for (const n of arr) byVersion[n.version].push(n);
266
+ const merged = [];
267
+ for (const v of [4, 6]) for (const part of mergeIntervals(byVersion[v])) merged.push(formatPart(part, v));
268
+ return merged;
245
269
  }
270
+ /** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
246
271
  function excludeCidr(base, excl) {
247
- const baseArr = uniq(Array.isArray(base) ? base : [base]).map(parseCidrLean);
248
- const exclArr = uniq(Array.isArray(excl) ? excl : [excl]).map(parseCidrLean);
249
- const baseMaps = mapNets(baseArr);
250
- const exclMaps = mapNets(exclArr);
251
- const bases = { 4: [], 6: [] };
252
- const excls = { 4: [], 6: [] };
253
- for (const v of [4, 6]) {
254
- bases[v] = doMerge(baseMaps[v]);
255
- excls[v] = doMerge(exclMaps[v]);
256
- }
257
- for (const v of [4, 6]) {
258
- for (const exclPart of excls[v]) {
259
- const newBases = [];
260
- for (const basePart of bases[v]) {
261
- for (const part of excludeNetsParts(basePart, exclPart)) {
262
- newBases.push(part);
263
- }
264
- }
265
- bases[v] = newBases;
266
- }
267
- }
268
- const result = [];
269
- for (const v of [4, 6]) {
270
- for (const part of bases[v]) {
271
- result.push(formatPart(part, v));
272
- }
273
- }
274
- return result;
272
+ const baseArr = (Array.isArray(base) ? base : [base]).map(parseCidrLean);
273
+ const exclArr = (Array.isArray(excl) ? excl : [excl]).map(parseCidrLean);
274
+ const baseByVersion = {
275
+ 4: [],
276
+ 6: []
277
+ };
278
+ const exclByVersion = {
279
+ 4: [],
280
+ 6: []
281
+ };
282
+ for (const n of baseArr) baseByVersion[n.version].push(n);
283
+ for (const n of exclArr) exclByVersion[n.version].push(n);
284
+ const result = [];
285
+ for (const v of [4, 6]) {
286
+ const remaining = subtractSorted(mergeIntervalsRaw(baseByVersion[v]), mergeIntervalsRaw(exclByVersion[v]));
287
+ for (const part of remaining) {
288
+ const aligned = subparts(part);
289
+ for (const p of aligned) result.push(formatPart(p, v));
290
+ }
291
+ }
292
+ return result;
275
293
  }
276
294
  function* expandCidr(nets) {
277
- const arr = uniq(Array.isArray(nets) ? nets : [nets]);
278
- for (const net of mergeCidr(arr)) {
279
- const { start, end, version } = parseCidrLean(net);
280
- for (let number = start; number <= end; number++) {
281
- yield normalizeIp(stringifyIp({ number, version }));
282
- }
283
- }
295
+ const arr = Array.isArray(nets) ? nets : [nets];
296
+ for (const net of mergeCidr(arr)) {
297
+ const { start, end, version } = parseCidrLean(net);
298
+ for (let number = start; number <= end; number++) yield normalizeIp(stringifyIp({
299
+ number,
300
+ version
301
+ }));
302
+ }
284
303
  }
304
+ /** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
285
305
  function overlapCidr(a, b) {
286
- const aNets = uniq(Array.isArray(a) ? a : [a]).map(parseCidrLean);
287
- const bNets = uniq(Array.isArray(b) ? b : [b]).map(parseCidrLean);
288
- const aByVersion = { 4: [], 6: [] };
289
- const bByVersion = { 4: [], 6: [] };
290
- for (const n of aNets) aByVersion[n.version].push(n);
291
- for (const n of bNets) bByVersion[n.version].push(n);
292
- for (const v of [4, 6]) {
293
- const aVer = aByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
294
- const bVer = bByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
295
- let i = 0, j = 0;
296
- while (i < aVer.length && j < bVer.length) {
297
- const aNet = aVer[i];
298
- const bNet = bVer[j];
299
- if (aNet.start <= bNet.end && bNet.start <= aNet.end) return true;
300
- if (aNet.end < bNet.end) i++;
301
- else j++;
302
- }
303
- }
304
- return false;
306
+ const aNets = (Array.isArray(a) ? a : [a]).map(parseCidrLean);
307
+ const bNets = (Array.isArray(b) ? b : [b]).map(parseCidrLean);
308
+ const aByVersion = {
309
+ 4: [],
310
+ 6: []
311
+ };
312
+ const bByVersion = {
313
+ 4: [],
314
+ 6: []
315
+ };
316
+ for (const n of aNets) aByVersion[n.version].push(n);
317
+ for (const n of bNets) bByVersion[n.version].push(n);
318
+ for (const v of [4, 6]) {
319
+ const aVer = aByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
320
+ const bVer = bByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
321
+ let i = 0, j = 0;
322
+ while (i < aVer.length && j < bVer.length) {
323
+ const aNet = aVer[i];
324
+ const bNet = bVer[j];
325
+ if (aNet.start <= bNet.end && bNet.start <= aNet.end) return true;
326
+ if (aNet.end < bNet.end) i++;
327
+ else j++;
328
+ }
329
+ }
330
+ return false;
305
331
  }
332
+ /** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
306
333
  function containsCidr(a, b) {
307
- const aNets = uniq(Array.isArray(a) ? a : [a]).map(parseCidrLean);
308
- const bNets = uniq(Array.isArray(b) ? b : [b]).map(parseCidrLean);
309
- const aByVersion = { 4: [], 6: [] };
310
- const bByVersion = { 4: [], 6: [] };
311
- for (const n of aNets) aByVersion[n.version].push(n);
312
- for (const n of bNets) bByVersion[n.version].push(n);
313
- for (const v of [4, 6]) {
314
- const containers = aByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
315
- const targets = bByVersion[v];
316
- if (targets.length === 0) continue;
317
- if (containers.length === 0) return false;
318
- const maxEnd = new Array(containers.length);
319
- maxEnd[0] = containers[0].end;
320
- for (let i = 1; i < containers.length; i++) {
321
- if (containers[i].end > maxEnd[i - 1]) {
322
- maxEnd[i] = containers[i].end;
323
- } else {
324
- maxEnd[i] = maxEnd[i - 1];
325
- }
326
- }
327
- for (const target of targets) {
328
- let lo = 0, hi = containers.length - 1;
329
- let idx = -1;
330
- while (lo <= hi) {
331
- const mid = lo + hi >> 1;
332
- if (containers[mid].start <= target.start) {
333
- idx = mid;
334
- lo = mid + 1;
335
- } else {
336
- hi = mid - 1;
337
- }
338
- }
339
- if (idx < 0 || maxEnd[idx] < target.end) return false;
340
- }
341
- }
342
- return true;
334
+ const aNets = (Array.isArray(a) ? a : [a]).map(parseCidrLean);
335
+ const bNets = (Array.isArray(b) ? b : [b]).map(parseCidrLean);
336
+ const aByVersion = {
337
+ 4: [],
338
+ 6: []
339
+ };
340
+ const bByVersion = {
341
+ 4: [],
342
+ 6: []
343
+ };
344
+ for (const n of aNets) aByVersion[n.version].push(n);
345
+ for (const n of bNets) bByVersion[n.version].push(n);
346
+ for (const v of [4, 6]) {
347
+ const containers = aByVersion[v].sort((x, y) => x.start > y.start ? 1 : x.start < y.start ? -1 : 0);
348
+ const targets = bByVersion[v];
349
+ if (targets.length === 0) continue;
350
+ if (containers.length === 0) return false;
351
+ const maxEnd = new Array(containers.length);
352
+ maxEnd[0] = containers[0].end;
353
+ for (let i = 1; i < containers.length; i++) if (containers[i].end > maxEnd[i - 1]) maxEnd[i] = containers[i].end;
354
+ else maxEnd[i] = maxEnd[i - 1];
355
+ for (const target of targets) {
356
+ let lo = 0, hi = containers.length - 1;
357
+ let idx = -1;
358
+ while (lo <= hi) {
359
+ const mid = lo + hi >> 1;
360
+ if (containers[mid].start <= target.start) {
361
+ idx = mid;
362
+ lo = mid + 1;
363
+ } else hi = mid - 1;
364
+ }
365
+ if (idx < 0 || maxEnd[idx] < target.end) return false;
366
+ }
367
+ }
368
+ return true;
343
369
  }
344
- const index = {
345
- mergeCidr,
346
- excludeCidr,
347
- expandCidr,
348
- overlapCidr,
349
- containsCidr,
350
- normalizeCidr,
351
- parseCidr
352
- };
353
- export {
354
- containsCidr,
355
- index as default,
356
- excludeCidr,
357
- expandCidr,
358
- mergeCidr,
359
- normalizeCidr,
360
- overlapCidr,
361
- parseCidr
370
+ var cidr_tools_default = {
371
+ mergeCidr,
372
+ excludeCidr,
373
+ expandCidr,
374
+ overlapCidr,
375
+ containsCidr,
376
+ normalizeCidr,
377
+ parseCidr
362
378
  };
379
+
380
+ //#endregion
381
+ export { containsCidr, cidr_tools_default as default, excludeCidr, expandCidr, mergeCidr, normalizeCidr, overlapCidr, parseCidr };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cidr-tools",
3
- "version": "11.0.10",
3
+ "version": "11.0.12",
4
4
  "author": "silverwind <me@silverwind.io>",
5
5
  "description": "Tools to work with IPv4 and IPv6 CIDR",
6
6
  "repository": "silverwind/cidr-tools",
@@ -17,21 +17,22 @@
17
17
  "node": ">=18"
18
18
  },
19
19
  "dependencies": {
20
- "ip-bigint": "^8.2.6"
20
+ "ip-bigint": "^8.2.8"
21
21
  },
22
22
  "devDependencies": {
23
- "@typescript/native-preview": "7.0.0-dev.20260202.1",
24
- "eslint": "9.39.2",
25
- "eslint-config-silverwind": "120.1.2",
23
+ "@types/node": "25.3.3",
24
+ "@typescript/native-preview": "7.0.0-dev.20260305.1",
25
+ "eslint": "9.39.3",
26
+ "eslint-config-silverwind": "121.2.0",
26
27
  "jest-extended": "7.0.0",
28
+ "tsdown": "0.21.0",
29
+ "tsdown-config-silverwind": "2.0.0",
27
30
  "typescript": "5.9.3",
28
- "typescript-config-silverwind": "14.0.0",
29
- "updates": "17.1.0",
31
+ "typescript-config-silverwind": "15.0.0",
32
+ "updates": "17.8.1",
30
33
  "updates-config-silverwind": "1.0.3",
31
- "versions": "14.1.0",
32
- "vite": "7.3.1",
33
- "vite-config-silverwind": "6.0.9",
34
+ "versions": "14.2.1",
34
35
  "vitest": "4.0.18",
35
- "vitest-config-silverwind": "10.6.1"
36
+ "vitest-config-silverwind": "10.6.3"
36
37
  }
37
38
  }