cidr-tools 12.0.2 → 12.1.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.
- package/dist/index.d.ts +2 -2
- package/dist/index.js +92 -75
- package/package.json +19 -16
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
//#region index.d.ts
|
|
2
2
|
type Network = string;
|
|
3
|
-
type Networks = Network |
|
|
3
|
+
type Networks = Network | ReadonlyArray<Network>;
|
|
4
4
|
type ValidIpVersion = 4 | 6;
|
|
5
5
|
type ParsedCidr = {
|
|
6
6
|
cidr: string;
|
|
@@ -16,7 +16,7 @@ type NormalizeOpts = {
|
|
|
16
16
|
hexify?: boolean;
|
|
17
17
|
};
|
|
18
18
|
/** 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
|
-
declare function normalizeCidr<T extends
|
|
19
|
+
declare function normalizeCidr<T extends Networks>(cidr: T, opts?: NormalizeOpts): T;
|
|
20
20
|
/** 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
21
|
declare function parseCidr(str: Network): ParsedCidr;
|
|
22
22
|
/** Returns an array of merged networks */
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,10 @@ const octetStrings = Array.from({ length: 256 }, (_, i) => String(i));
|
|
|
9
9
|
const octetDotStrings = Array.from({ length: 256 }, (_, i) => `${i}.`);
|
|
10
10
|
const prefixStrings = Array.from({ length: 129 }, (_, i) => `/${i}`);
|
|
11
11
|
const prefixNumStrings = Array.from({ length: 129 }, (_, i) => String(i));
|
|
12
|
+
const hexStrings = Array.from({ length: 256 }, (_, i) => i.toString(16));
|
|
13
|
+
const hexPadStrings = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, "0"));
|
|
14
|
+
const hostMasks = Array.from({ length: 129 }, (_, i) => (1n << BigInt(i)) - 1n);
|
|
15
|
+
const hostNotMasks = hostMasks.map((mask) => ~mask);
|
|
12
16
|
const cmpV4StartEnd = (a, b) => a.start - b.start || a.end - b.end;
|
|
13
17
|
const cmpV4Start = (a, b) => a.start - b.start;
|
|
14
18
|
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;
|
|
@@ -73,27 +77,37 @@ function doNormalize(cidr, opts) {
|
|
|
73
77
|
const ip = formatIPv4Fast(rangeV4Start);
|
|
74
78
|
return rangeSlashIndex !== -1 ? ip + prefixStrings[rangeV4Prefix] : ip;
|
|
75
79
|
}
|
|
76
|
-
const
|
|
80
|
+
const slashIndex = rangeSlashIndex;
|
|
81
|
+
const prefixPresent = slashIndex !== -1;
|
|
82
|
+
let prefixNum = prefixPresent ? parsePrefixNum(cidr, slashIndex) : -1;
|
|
83
|
+
const { number, version } = parseIp(prefixPresent ? cidr.substring(0, slashIndex) : cidr);
|
|
84
|
+
if (prefixNum === -1) prefixNum = bits[version];
|
|
77
85
|
if (version === 4) {
|
|
78
|
-
const
|
|
79
|
-
|
|
86
|
+
const hostBits = 32 - prefixNum;
|
|
87
|
+
let startNum = Number(number);
|
|
88
|
+
if (hostBits >= 32) startNum = 0;
|
|
89
|
+
else if (hostBits > 0) startNum = (startNum & ~((1 << hostBits >>> 0) - 1)) >>> 0;
|
|
90
|
+
const ip = formatIPv4Fast(startNum);
|
|
91
|
+
return hostBits > 0 || prefixPresent ? ip + prefixStrings[prefixNum] : ip;
|
|
80
92
|
}
|
|
81
93
|
const { compress = true, hexify = false } = opts || {};
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
version
|
|
85
|
-
}), {
|
|
94
|
+
const hostBits = 128 - prefixNum;
|
|
95
|
+
if (hostBits <= 0 && !prefixPresent) return normalizeIp(cidr, {
|
|
86
96
|
compress,
|
|
87
97
|
hexify
|
|
88
|
-
})
|
|
89
|
-
|
|
98
|
+
});
|
|
99
|
+
const ip = stringifyIp({
|
|
100
|
+
number: hostBits > 0 ? number & hostNotMasks[hostBits] : number,
|
|
101
|
+
version
|
|
102
|
+
});
|
|
103
|
+
return (compress ? ip : normalizeIp(ip, {
|
|
90
104
|
compress,
|
|
91
105
|
hexify
|
|
92
|
-
});
|
|
106
|
+
})) + prefixStrings[prefixNum];
|
|
93
107
|
}
|
|
94
108
|
/** 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. */
|
|
95
109
|
function normalizeCidr(cidr, opts) {
|
|
96
|
-
return
|
|
110
|
+
return typeof cidr === "string" ? doNormalize(cidr, opts) : cidr.map((entry) => doNormalize(entry, opts));
|
|
97
111
|
}
|
|
98
112
|
/** 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. */
|
|
99
113
|
function parseCidr(str) {
|
|
@@ -129,7 +143,8 @@ function parseCidr(str) {
|
|
|
129
143
|
let prefixNum = prefixPresent ? parsePrefixNum(str, slashIndex) : -1;
|
|
130
144
|
const { number, version, ipv4mapped, scopeid } = parseIp(ipPart);
|
|
131
145
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
132
|
-
|
|
146
|
+
const numBits = bits[version];
|
|
147
|
+
if (prefixNum === -1) prefixNum = numBits;
|
|
133
148
|
const prefix = prefixNumStrings[prefixNum] ?? String(prefixNum);
|
|
134
149
|
const ip = stringifyIp({
|
|
135
150
|
number,
|
|
@@ -137,16 +152,21 @@ function parseCidr(str) {
|
|
|
137
152
|
ipv4mapped,
|
|
138
153
|
scopeid
|
|
139
154
|
});
|
|
140
|
-
const hostBits =
|
|
141
|
-
|
|
155
|
+
const hostBits = numBits - prefixNum;
|
|
156
|
+
let start = number;
|
|
157
|
+
let end = number;
|
|
158
|
+
if (hostBits > 0) {
|
|
159
|
+
start = number & hostNotMasks[hostBits];
|
|
160
|
+
end = number | hostMasks[hostBits];
|
|
161
|
+
}
|
|
142
162
|
return {
|
|
143
163
|
cidr: ip + prefixStrings[prefixNum],
|
|
144
164
|
ip,
|
|
145
165
|
version,
|
|
146
166
|
prefix,
|
|
147
167
|
prefixPresent,
|
|
148
|
-
start
|
|
149
|
-
end
|
|
168
|
+
start,
|
|
169
|
+
end
|
|
150
170
|
};
|
|
151
171
|
}
|
|
152
172
|
function parseCidrLean(str) {
|
|
@@ -160,8 +180,9 @@ function parseCidrLean(str) {
|
|
|
160
180
|
let prefixNum = slashIndex !== -1 ? parsePrefixNum(str, slashIndex) : -1;
|
|
161
181
|
const { number, version } = parseIp(ipPart);
|
|
162
182
|
if (!version) throw new Error(`Network is not a CIDR or IP: "${str}"`);
|
|
163
|
-
|
|
164
|
-
|
|
183
|
+
const numBits = bits[version];
|
|
184
|
+
if (prefixNum === -1) prefixNum = numBits;
|
|
185
|
+
const hostBits = numBits - prefixNum;
|
|
165
186
|
if (version === 4) {
|
|
166
187
|
const num = Number(number);
|
|
167
188
|
if (hostBits >= 32) return {
|
|
@@ -176,10 +197,14 @@ function parseCidrLean(str) {
|
|
|
176
197
|
version: 4
|
|
177
198
|
};
|
|
178
199
|
}
|
|
179
|
-
|
|
200
|
+
if (hostBits <= 0) return {
|
|
201
|
+
start: number,
|
|
202
|
+
end: number,
|
|
203
|
+
version: 6
|
|
204
|
+
};
|
|
180
205
|
return {
|
|
181
|
-
start: number &
|
|
182
|
-
end: number |
|
|
206
|
+
start: number & hostNotMasks[hostBits],
|
|
207
|
+
end: number | hostMasks[hostBits],
|
|
183
208
|
version: 6
|
|
184
209
|
};
|
|
185
210
|
}
|
|
@@ -211,10 +236,7 @@ function subparts4(pStart, pEnd, output) {
|
|
|
211
236
|
const size = pEnd - start + 1;
|
|
212
237
|
const lowBit = (start & -start) >>> 0;
|
|
213
238
|
const blockSize = lowBit !== 0 && lowBit <= size ? lowBit : biggestPowerOfTwo4(size);
|
|
214
|
-
output.push(
|
|
215
|
-
start,
|
|
216
|
-
end: start + blockSize - 1
|
|
217
|
-
});
|
|
239
|
+
output.push(formatIPv4Fast(start) + prefixStrings[Math.clz32(blockSize - 1)]);
|
|
218
240
|
start += blockSize;
|
|
219
241
|
}
|
|
220
242
|
}
|
|
@@ -224,29 +246,20 @@ function subparts6(pStart, pEnd, output) {
|
|
|
224
246
|
const size = pEnd - start + 1n;
|
|
225
247
|
const lowBit = start & -start;
|
|
226
248
|
if ((size & size - 1n) === 0n && (lowBit === 0n || lowBit >= size)) {
|
|
227
|
-
output.push({
|
|
228
|
-
start,
|
|
229
|
-
|
|
230
|
-
});
|
|
249
|
+
output.push(stringifyIp({
|
|
250
|
+
number: start,
|
|
251
|
+
version: 6
|
|
252
|
+
}) + prefixStrings[129 - bigintBitLength(size)]);
|
|
231
253
|
return;
|
|
232
254
|
}
|
|
233
255
|
const blockSize = lowBit !== 0n && lowBit <= size ? lowBit : biggestPowerOfTwo(size);
|
|
234
|
-
output.push({
|
|
235
|
-
start,
|
|
236
|
-
|
|
237
|
-
});
|
|
256
|
+
output.push(stringifyIp({
|
|
257
|
+
number: start,
|
|
258
|
+
version: 6
|
|
259
|
+
}) + prefixStrings[129 - bigintBitLength(blockSize)]);
|
|
238
260
|
start += blockSize;
|
|
239
261
|
}
|
|
240
262
|
}
|
|
241
|
-
function formatPart4(part) {
|
|
242
|
-
return formatIPv4Fast(part.start) + prefixStrings[Math.clz32(part.end - part.start)];
|
|
243
|
-
}
|
|
244
|
-
function formatPart6(part) {
|
|
245
|
-
return stringifyIp({
|
|
246
|
-
number: part.start,
|
|
247
|
-
version: 6
|
|
248
|
-
}) + prefixStrings[128 - bigintBitLength(part.end - part.start)];
|
|
249
|
-
}
|
|
250
263
|
function mergeIntervalsRaw4(nets) {
|
|
251
264
|
if (nets.length === 0) return [];
|
|
252
265
|
nets.sort(cmpV4StartEnd);
|
|
@@ -297,16 +310,6 @@ function mergeIntervalsRaw6(nets) {
|
|
|
297
310
|
});
|
|
298
311
|
return merged;
|
|
299
312
|
}
|
|
300
|
-
function mergeIntervals4(nets) {
|
|
301
|
-
const merged = [];
|
|
302
|
-
for (const part of mergeIntervalsRaw4(nets)) subparts4(part.start, part.end, merged);
|
|
303
|
-
return merged;
|
|
304
|
-
}
|
|
305
|
-
function mergeIntervals6(nets) {
|
|
306
|
-
const merged = [];
|
|
307
|
-
for (const part of mergeIntervalsRaw6(nets)) subparts6(part.start, part.end, merged);
|
|
308
|
-
return merged;
|
|
309
|
-
}
|
|
310
313
|
function subtractSorted4(bases, excls) {
|
|
311
314
|
if (excls.length === 0) return bases;
|
|
312
315
|
if (bases.length === 0) return [];
|
|
@@ -359,7 +362,7 @@ function subtractSorted6(bases, excls) {
|
|
|
359
362
|
}
|
|
360
363
|
/** Returns an array of merged networks */
|
|
361
364
|
function mergeCidr(nets) {
|
|
362
|
-
const arr =
|
|
365
|
+
const arr = typeof nets === "string" ? [nets] : nets;
|
|
363
366
|
const v4 = [];
|
|
364
367
|
const v6 = [];
|
|
365
368
|
for (const s of arr) {
|
|
@@ -368,14 +371,14 @@ function mergeCidr(nets) {
|
|
|
368
371
|
else v6.push(n);
|
|
369
372
|
}
|
|
370
373
|
const merged = [];
|
|
371
|
-
for (const part of
|
|
372
|
-
for (const part of
|
|
374
|
+
for (const part of mergeIntervalsRaw4(v4)) subparts4(part.start, part.end, merged);
|
|
375
|
+
for (const part of mergeIntervalsRaw6(v6)) subparts6(part.start, part.end, merged);
|
|
373
376
|
return merged;
|
|
374
377
|
}
|
|
375
378
|
/** Returns an array of merged remaining networks of the subtraction of `excludeNetworks` from `baseNetworks`. */
|
|
376
379
|
function excludeCidr(base, excl) {
|
|
377
|
-
const baseArr =
|
|
378
|
-
const exclArr =
|
|
380
|
+
const baseArr = typeof base === "string" ? [base] : base;
|
|
381
|
+
const exclArr = typeof excl === "string" ? [excl] : excl;
|
|
379
382
|
const v4base = [], v6base = [];
|
|
380
383
|
const v4excl = [], v6excl = [];
|
|
381
384
|
for (const s of baseArr) {
|
|
@@ -390,22 +393,20 @@ function excludeCidr(base, excl) {
|
|
|
390
393
|
}
|
|
391
394
|
const result = [];
|
|
392
395
|
{
|
|
393
|
-
const
|
|
394
|
-
const
|
|
395
|
-
for (const part of
|
|
396
|
-
for (const p of aligned) result.push(formatPart4(p));
|
|
396
|
+
const baseParts = mergeIntervalsRaw4(v4base);
|
|
397
|
+
const exclParts = mergeIntervalsRaw4(v4excl);
|
|
398
|
+
for (const part of subtractSorted4(baseParts, exclParts)) subparts4(part.start, part.end, result);
|
|
397
399
|
}
|
|
398
400
|
{
|
|
399
|
-
const
|
|
400
|
-
const
|
|
401
|
-
for (const part of
|
|
402
|
-
for (const p of aligned) result.push(formatPart6(p));
|
|
401
|
+
const baseParts = mergeIntervalsRaw6(v6base);
|
|
402
|
+
const exclParts = mergeIntervalsRaw6(v6excl);
|
|
403
|
+
for (const part of subtractSorted6(baseParts, exclParts)) subparts6(part.start, part.end, result);
|
|
403
404
|
}
|
|
404
405
|
return result;
|
|
405
406
|
}
|
|
406
407
|
/** Returns a generator for individual IPs contained in the networks. */
|
|
407
408
|
function* expandCidr(nets) {
|
|
408
|
-
const arr =
|
|
409
|
+
const arr = typeof nets === "string" ? [nets] : nets;
|
|
409
410
|
const v4 = [];
|
|
410
411
|
const v6 = [];
|
|
411
412
|
for (const s of arr) {
|
|
@@ -430,15 +431,31 @@ function* expandCidr(nets) {
|
|
|
430
431
|
number: 0n,
|
|
431
432
|
version: 6
|
|
432
433
|
};
|
|
433
|
-
for (const part of mergeIntervalsRaw6(v6))
|
|
434
|
-
|
|
435
|
-
|
|
434
|
+
for (const part of mergeIntervalsRaw6(v6)) {
|
|
435
|
+
let num = part.start;
|
|
436
|
+
while (num <= part.end) {
|
|
437
|
+
const blockStart = num & -65536n;
|
|
438
|
+
const blockEnd = blockStart | 65535n;
|
|
439
|
+
let group = Number(num & 65535n);
|
|
440
|
+
const groupEnd = part.end < blockEnd ? Number(part.end & 65535n) : 65535;
|
|
441
|
+
if (group === 0) {
|
|
442
|
+
ipObj.number = blockStart;
|
|
443
|
+
yield stringifyIp(ipObj);
|
|
444
|
+
group = 1;
|
|
445
|
+
}
|
|
446
|
+
if (group <= groupEnd) {
|
|
447
|
+
ipObj.number = blockStart | 1n;
|
|
448
|
+
const prefix = stringifyIp(ipObj).slice(0, -1);
|
|
449
|
+
for (; group <= groupEnd; group++) yield group < 256 ? prefix + hexStrings[group] : prefix + hexStrings[group >>> 8] + hexPadStrings[group & 255];
|
|
450
|
+
}
|
|
451
|
+
num = blockEnd + 1n;
|
|
452
|
+
}
|
|
436
453
|
}
|
|
437
454
|
}
|
|
438
455
|
}
|
|
439
456
|
/** Returns a boolean that indicates if `networksA` overlap (intersect) with `networksB`. */
|
|
440
457
|
function overlapCidr(a, b) {
|
|
441
|
-
if (
|
|
458
|
+
if (typeof a === "string" && typeof b === "string") {
|
|
442
459
|
if (parseIPv4Range(a)) {
|
|
443
460
|
const startA = rangeV4Start, endA = rangeV4End;
|
|
444
461
|
if (parseIPv4Range(b)) return startA <= rangeV4End && rangeV4Start <= endA;
|
|
@@ -451,8 +468,8 @@ function overlapCidr(a, b) {
|
|
|
451
468
|
if (pa.version !== pb.version) return false;
|
|
452
469
|
return pa.start <= pb.end && pb.start <= pa.end;
|
|
453
470
|
}
|
|
454
|
-
const aArr =
|
|
455
|
-
const bArr =
|
|
471
|
+
const aArr = typeof a === "string" ? [a] : a;
|
|
472
|
+
const bArr = typeof b === "string" ? [b] : b;
|
|
456
473
|
const v4a = [], v6a = [];
|
|
457
474
|
const v4b = [], v6b = [];
|
|
458
475
|
for (const s of aArr) {
|
|
@@ -501,7 +518,7 @@ function overlapCidr(a, b) {
|
|
|
501
518
|
}
|
|
502
519
|
/** Returns a boolean that indicates whether `networksA` fully contain all `networksB`. */
|
|
503
520
|
function containsCidr(a, b) {
|
|
504
|
-
if (
|
|
521
|
+
if (typeof a === "string" && typeof b === "string") {
|
|
505
522
|
if (parseIPv4Range(a)) {
|
|
506
523
|
const startA = rangeV4Start, endA = rangeV4End;
|
|
507
524
|
if (parseIPv4Range(b)) return startA <= rangeV4Start && endA >= rangeV4End;
|
|
@@ -514,8 +531,8 @@ function containsCidr(a, b) {
|
|
|
514
531
|
if (pa.version !== pb.version) return false;
|
|
515
532
|
return pa.start <= pb.start && pa.end >= pb.end;
|
|
516
533
|
}
|
|
517
|
-
const aArr =
|
|
518
|
-
const bArr =
|
|
534
|
+
const aArr = typeof a === "string" ? [a] : a;
|
|
535
|
+
const bArr = typeof b === "string" ? [b] : b;
|
|
519
536
|
const v4a = [], v6a = [];
|
|
520
537
|
const v4b = [], v6b = [];
|
|
521
538
|
for (const s of aArr) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cidr-tools",
|
|
3
|
-
"version": "12.0
|
|
3
|
+
"version": "12.1.0",
|
|
4
4
|
"author": "silverwind <me@silverwind.io>",
|
|
5
5
|
"description": "Tools to work with IPv4 and IPv6 CIDR",
|
|
6
6
|
"keywords": [
|
|
@@ -11,7 +11,10 @@
|
|
|
11
11
|
"subnet",
|
|
12
12
|
"network"
|
|
13
13
|
],
|
|
14
|
-
"repository":
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "silverwind/cidr-tools"
|
|
17
|
+
},
|
|
15
18
|
"license": "BSD-2-Clause",
|
|
16
19
|
"type": "module",
|
|
17
20
|
"sideEffects": false,
|
|
@@ -26,23 +29,23 @@
|
|
|
26
29
|
"bun": "*"
|
|
27
30
|
},
|
|
28
31
|
"dependencies": {
|
|
29
|
-
"ip-bigint": "^9.0.
|
|
32
|
+
"ip-bigint": "^9.0.6"
|
|
30
33
|
},
|
|
31
34
|
"devDependencies": {
|
|
32
|
-
"@types/node": "25.9.
|
|
33
|
-
"@typescript/native-preview": "7.0.0-dev.
|
|
34
|
-
"@vitest/coverage-v8": "4.1.
|
|
35
|
-
"eslint": "10.
|
|
36
|
-
"eslint-config-silverwind": "
|
|
35
|
+
"@types/node": "25.9.3",
|
|
36
|
+
"@typescript/native-preview": "7.0.0-dev.20260616.1",
|
|
37
|
+
"@vitest/coverage-v8": "4.1.9",
|
|
38
|
+
"eslint": "10.5.0",
|
|
39
|
+
"eslint-config-silverwind": "136.0.10",
|
|
37
40
|
"jest-extended": "7.0.0",
|
|
38
|
-
"tsdown": "0.22.
|
|
39
|
-
"tsdown-config-silverwind": "3.0.
|
|
41
|
+
"tsdown": "0.22.3",
|
|
42
|
+
"tsdown-config-silverwind": "3.0.3",
|
|
40
43
|
"typescript": "6.0.3",
|
|
41
|
-
"typescript-config-silverwind": "
|
|
42
|
-
"updates": "17.
|
|
43
|
-
"updates-config-silverwind": "
|
|
44
|
-
"versions": "15.1.
|
|
45
|
-
"vitest": "4.1.
|
|
46
|
-
"vitest-config-silverwind": "11.3.
|
|
44
|
+
"typescript-config-silverwind": "20.0.0",
|
|
45
|
+
"updates": "17.18.0",
|
|
46
|
+
"updates-config-silverwind": "4.0.0",
|
|
47
|
+
"versions": "15.1.1",
|
|
48
|
+
"vitest": "4.1.9",
|
|
49
|
+
"vitest-config-silverwind": "11.3.6"
|
|
47
50
|
}
|
|
48
51
|
}
|