cidr-tools 11.0.9 → 11.0.11

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