@verifyhash/ip-cidr 0.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.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +143 -0
  3. package/index.js +359 -0
  4. package/package.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 <OWNER-NAME PLACEHOLDER>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # ip-cidr
2
+
3
+ A tiny, **zero-dependency** IPv4 (and basic IPv6) address/CIDR toolkit for
4
+ Node.js. It does the boring arithmetic you always end up rewriting: validate an
5
+ address, parse a CIDR block, ask whether a block contains an IP, and compute the
6
+ network/broadcast/first/last host and host count of a subnet — plus lossless
7
+ ip↔integer conversion.
8
+
9
+ No network access, no runtime dependencies, no servers. Just math on the bits.
10
+
11
+ > **Package name is a placeholder.** The manifest name is
12
+ > `PLACEHOLDER-ip-cidr`. TODO: the owner picks the final npm name/scope before
13
+ > any publish. This library is **not published** — graduation to npm is a
14
+ > human-gated step.
15
+
16
+ ## Who it's for
17
+
18
+ - Anyone writing allow/deny lists, firewall-ish rules, or geo/ASN buckets who
19
+ needs a correct `contains(cidr, ip)` without pulling a dependency tree.
20
+ - Tools and CLIs that display subnet facts (network, broadcast, usable range,
21
+ host count) for an operator.
22
+ - Code that stores IPs as integers (databases, bitmaps) and needs a reliable
23
+ round-trip to/from dotted-quad.
24
+
25
+ ## Install & usage
26
+
27
+ There is nothing to install beyond Node (uses only built-in `assert` for
28
+ tests). Once vendored/published under its final name you would:
29
+
30
+ ```js
31
+ const ip = require('ip-cidr'); // during development: require('./index.js')
32
+
33
+ // Containment
34
+ ip.contains('10.0.0.0/8', '10.1.2.3'); // => true
35
+ ip.contains('192.168.1.0/24', '192.168.2.9'); // => false
36
+
37
+ // Subnet facts
38
+ const net = ip.parseCidr('192.168.1.10/24');
39
+ net.network; // '192.168.1.0'
40
+ net.broadcast; // '192.168.1.255'
41
+ net.firstHost; // '192.168.1.1'
42
+ net.lastHost; // '192.168.1.254'
43
+ net.hostCount; // 254 (usable hosts)
44
+ net.size; // 256 (total addresses)
45
+
46
+ // Address <-> integer (lossless, unsigned 32-bit)
47
+ ip.ipv4ToInt('192.168.1.1'); // 3232235777
48
+ ip.intToIpv4(3232235777); // '192.168.1.1'
49
+
50
+ // Validation / dispatch
51
+ ip.isValidIPv4('1.2.3.256'); // false
52
+ ip.ipVersion('::1'); // 6
53
+ ip.parse('8.8.8.8'); // { version: 4, address: '8.8.8.8', int: 134744072 }
54
+ ```
55
+
56
+ ## API
57
+
58
+ ### IPv4 core
59
+ - `isValidIPv4(str) → boolean` — strict dotted-quad. Rejects leading zeros
60
+ (`01.2.3.4`), out-of-range octets (`1.2.3.256`), and wrong segment counts.
61
+ - `ipv4ToInt(str) → number` — unsigned 32-bit integer (throws on invalid).
62
+ - `intToIpv4(int) → string` — inverse (throws if outside `0 … 4294967295`).
63
+
64
+ ### IPv6 core
65
+ - `isValidIPv6(str) → boolean` — supports `::` compression and an embedded
66
+ IPv4 tail (`::ffff:192.168.0.1`). Zone ids (`%eth0`) are rejected.
67
+ - `ipv6ToBigInt(str) → bigint` / `bigIntToIpv6(bigint) → string` — lossless
68
+ round-trip via `BigInt`. `bigIntToIpv6` returns the **fully expanded**
69
+ (uncompressed) form, e.g. `0:0:0:0:0:0:0:1`, not `::1`.
70
+
71
+ ### Unified
72
+ - `ipVersion(str) → 4 | 6 | null`
73
+ - `parse(str) → { version, address, int }` for v4 or
74
+ `{ version, address, big }` for v6 (throws on invalid).
75
+ - `parseCidr(str) → object` — see fields below (throws on invalid).
76
+ - `contains(cidr, ip) → boolean` — throws only if `cidr`/`ip` can't be parsed;
77
+ returns `false` (never throws) when the two are different address families.
78
+
79
+ ### `parseCidr` result
80
+
81
+ IPv4 blocks return numeric and string forms:
82
+
83
+ | field | example (`192.168.1.10/24`) | notes |
84
+ |-------|------------------------------|-------|
85
+ | `network` / `networkInt` | `192.168.1.0` / `3232235776` | masked base address |
86
+ | `broadcast` / `broadcastInt` | `192.168.1.255` / … | all-ones host |
87
+ | `firstHost` / `lastHost` | `192.168.1.1` / `192.168.1.254` | usable range |
88
+ | `size` | `256` | total addresses in the block |
89
+ | `hostCount` | `254` | usable hosts (`size − 2`, except `/31`,`/32`) |
90
+ | `prefix` | `24` | |
91
+
92
+ IPv6 blocks return `network`, `firstHost`, `lastHost` (strings) plus
93
+ `networkBig`, `firstHostBig`, `lastHostBig`, `size`, and `hostCount` as
94
+ `BigInt`. There is **no** `broadcast` for IPv6 — broadcast is not an IPv6
95
+ concept.
96
+
97
+ ### `/31` and `/32` edge cases
98
+
99
+ `/32` is treated as a single usable host (`hostCount: 1`). `/31` follows
100
+ RFC 3021: both addresses are usable point-to-point endpoints, so
101
+ `hostCount: 2` with no network/broadcast reservation. Wider IPv4 blocks use the
102
+ conventional `size − 2` usable-host count.
103
+
104
+ ## IPv6 support scope (honest limits)
105
+
106
+ IPv6 is **partial by design**. Fully supported:
107
+
108
+ - **Validation & parsing** — `isValidIPv6`, `ipv6ToBigInt`, including `::`
109
+ compression and embedded-IPv4 tails.
110
+ - **Containment** — `contains(cidr, ip)` and `parseCidr` work for v6 using
111
+ `BigInt`, so there is no precision loss across the full 128-bit space.
112
+
113
+ Intentionally **not** provided for IPv6:
114
+
115
+ - No `broadcast`/first-usable/last-usable *reservation* semantics (v6 has no
116
+ broadcast). `firstHost`/`lastHost` are just the block's low/high addresses.
117
+ - `bigIntToIpv6` emits the expanded form only — it does **not** produce the
118
+ RFC 5952 shortest/canonical `::` form.
119
+ - No zone identifiers (`fe80::1%eth0`), no scoped/link-local special handling,
120
+ no CIDR aggregation/summarization, and no IPv4-mapped normalization beyond
121
+ parsing the embedded-IPv4 syntax.
122
+
123
+ If you need full IPv6 canonicalization or aggregation, use a dedicated library.
124
+ IPv4 is the first-class, fully-featured path here.
125
+
126
+ ## Running the tests
127
+
128
+ One command, no framework (Node's built-in `assert`):
129
+
130
+ ```sh
131
+ node test/index.test.js
132
+ # or
133
+ npm test
134
+ ```
135
+
136
+ The suite covers: containment true/false cases, `/31` and `/32` edges,
137
+ network/broadcast math for known blocks, ip↔int round-trips, invalid-input
138
+ rejection (bad octets, out-of-range masks, malformed strings), and the IPv6
139
+ validate/parse/contain paths.
140
+
141
+ ## License
142
+
143
+ MIT — see `LICENSE` (owner name is a placeholder until graduation).
package/index.js ADDED
@@ -0,0 +1,359 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * ip-cidr — zero-dependency IPv4 (+ basic IPv6) address/CIDR toolkit.
5
+ *
6
+ * Design notes:
7
+ * - IPv4 is fully supported: validate/parse, CIDR math (network, broadcast,
8
+ * first/last host, host count), ip<->integer conversion, and containment.
9
+ * - IPv6 support is intentionally limited to validate/parse and containment
10
+ * (see README "IPv6 support scope"). We use BigInt for IPv6 so no
11
+ * precision is lost, but we do not expose broadcast/first/last helpers for
12
+ * v6 because "broadcast" is not an IPv6 concept.
13
+ * - No network access, no dependencies, no I/O.
14
+ */
15
+
16
+ // ---------------------------------------------------------------------------
17
+ // IPv4 address <-> integer
18
+ // ---------------------------------------------------------------------------
19
+
20
+ /**
21
+ * Validate a dotted-quad IPv4 string. Requires exactly four octets, each an
22
+ * integer 0-255 with no leading zeros (so "01.2.3.4" and "1.2.3.256" are
23
+ * rejected). Returns true/false and never throws.
24
+ * @param {string} str
25
+ * @returns {boolean}
26
+ */
27
+ function isValidIPv4(str) {
28
+ if (typeof str !== 'string') return false;
29
+ const parts = str.split('.');
30
+ if (parts.length !== 4) return false;
31
+ for (const part of parts) {
32
+ // Must be 1-3 digits, no leading zero (except the single char "0").
33
+ if (!/^\d{1,3}$/.test(part)) return false;
34
+ if (part.length > 1 && part[0] === '0') return false;
35
+ const n = Number(part);
36
+ if (n < 0 || n > 255) return false;
37
+ }
38
+ return true;
39
+ }
40
+
41
+ /**
42
+ * Convert a valid IPv4 string to its unsigned 32-bit integer value.
43
+ * @param {string} str
44
+ * @returns {number} 0 .. 4294967295
45
+ * @throws {Error} on invalid input
46
+ */
47
+ function ipv4ToInt(str) {
48
+ if (!isValidIPv4(str)) throw new Error('invalid IPv4 address: ' + str);
49
+ const [a, b, c, d] = str.split('.').map(Number);
50
+ // >>> 0 forces the result to an unsigned 32-bit integer.
51
+ return ((a << 24) | (b << 16) | (c << 8) | d) >>> 0;
52
+ }
53
+
54
+ /**
55
+ * Convert an unsigned 32-bit integer to a dotted-quad IPv4 string.
56
+ * @param {number} int 0 .. 4294967295
57
+ * @returns {string}
58
+ * @throws {Error} on out-of-range / non-integer input
59
+ */
60
+ function intToIpv4(int) {
61
+ if (!Number.isInteger(int) || int < 0 || int > 0xffffffff) {
62
+ throw new Error('integer out of IPv4 range: ' + int);
63
+ }
64
+ return [
65
+ (int >>> 24) & 0xff,
66
+ (int >>> 16) & 0xff,
67
+ (int >>> 8) & 0xff,
68
+ int & 0xff,
69
+ ].join('.');
70
+ }
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // IPv6 address <-> BigInt (validate/parse + containment only)
74
+ // ---------------------------------------------------------------------------
75
+
76
+ /**
77
+ * Validate an IPv6 string, supporting "::" compression and an optional
78
+ * embedded IPv4 tail (e.g. "::ffff:192.168.0.1"). Returns true/false, never
79
+ * throws. Zone identifiers ("%eth0") are not accepted.
80
+ * @param {string} str
81
+ * @returns {boolean}
82
+ */
83
+ function isValidIPv6(str) {
84
+ try {
85
+ ipv6ToBigInt(str);
86
+ return true;
87
+ } catch (_e) {
88
+ return false;
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Convert an IPv6 string to a BigInt (0 .. 2**128-1). Supports "::"
94
+ * compression and an embedded IPv4 tail.
95
+ * @param {string} str
96
+ * @returns {bigint}
97
+ * @throws {Error} on invalid input
98
+ */
99
+ function ipv6ToBigInt(str) {
100
+ if (typeof str !== 'string' || str.length === 0) {
101
+ throw new Error('invalid IPv6 address: not a string');
102
+ }
103
+ if (str.indexOf('%') !== -1) {
104
+ throw new Error('invalid IPv6 address: zone id not supported');
105
+ }
106
+
107
+ // At most one "::".
108
+ const doubleColonCount = str.split('::').length - 1;
109
+ if (doubleColonCount > 1) throw new Error('invalid IPv6 address: multiple "::"');
110
+
111
+ let head;
112
+ let tail;
113
+ if (doubleColonCount === 1) {
114
+ const [left, right] = str.split('::');
115
+ head = left.length ? left.split(':') : [];
116
+ tail = right.length ? right.split(':') : [];
117
+ } else {
118
+ head = str.split(':');
119
+ tail = [];
120
+ }
121
+
122
+ // Expand a trailing embedded IPv4 group (must be the last group) into two
123
+ // 16-bit hextets.
124
+ function expandGroups(groups) {
125
+ if (groups.length === 0) return [];
126
+ const out = [];
127
+ for (let i = 0; i < groups.length; i++) {
128
+ const g = groups[i];
129
+ if (g.indexOf('.') !== -1) {
130
+ // Only allowed as the very last group.
131
+ if (i !== groups.length - 1) {
132
+ throw new Error('invalid IPv6 address: embedded IPv4 not at end');
133
+ }
134
+ if (!isValidIPv4(g)) throw new Error('invalid IPv6 address: bad embedded IPv4');
135
+ const v = ipv4ToInt(g);
136
+ out.push(((v >>> 16) & 0xffff).toString(16));
137
+ out.push((v & 0xffff).toString(16));
138
+ } else {
139
+ if (!/^[0-9a-fA-F]{1,4}$/.test(g)) {
140
+ throw new Error('invalid IPv6 address: bad hextet "' + g + '"');
141
+ }
142
+ out.push(g);
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+
148
+ const headExp = expandGroups(head);
149
+ const tailExp = expandGroups(tail);
150
+
151
+ let groups;
152
+ if (doubleColonCount === 1) {
153
+ const missing = 8 - (headExp.length + tailExp.length);
154
+ if (missing < 1) {
155
+ // "::" must stand for at least one zero group.
156
+ throw new Error('invalid IPv6 address: "::" compresses nothing');
157
+ }
158
+ groups = headExp.concat(new Array(missing).fill('0'), tailExp);
159
+ } else {
160
+ groups = headExp;
161
+ }
162
+
163
+ if (groups.length !== 8) {
164
+ throw new Error('invalid IPv6 address: expected 8 groups, got ' + groups.length);
165
+ }
166
+
167
+ let result = 0n;
168
+ for (const g of groups) {
169
+ result = (result << 16n) | BigInt(parseInt(g, 16));
170
+ }
171
+ return result;
172
+ }
173
+
174
+ /**
175
+ * Convert a BigInt (0 .. 2**128-1) to a fully-expanded (uncompressed) IPv6
176
+ * string. This is a canonical form, not the shortest form.
177
+ * @param {bigint} value
178
+ * @returns {string}
179
+ * @throws {Error} on out-of-range input
180
+ */
181
+ function bigIntToIpv6(value) {
182
+ if (typeof value !== 'bigint' || value < 0n || value > (1n << 128n) - 1n) {
183
+ throw new Error('value out of IPv6 range');
184
+ }
185
+ const groups = [];
186
+ for (let i = 7; i >= 0; i--) {
187
+ const hextet = (value >> BigInt(i * 16)) & 0xffffn;
188
+ groups.push(hextet.toString(16));
189
+ }
190
+ return groups.join(':');
191
+ }
192
+
193
+ // ---------------------------------------------------------------------------
194
+ // Address family detection + unified parse
195
+ // ---------------------------------------------------------------------------
196
+
197
+ /**
198
+ * Return 4 if str is a valid IPv4 address, 6 if a valid IPv6 address, else
199
+ * null. Never throws.
200
+ * @param {string} str
201
+ * @returns {4|6|null}
202
+ */
203
+ function ipVersion(str) {
204
+ if (isValidIPv4(str)) return 4;
205
+ if (isValidIPv6(str)) return 6;
206
+ return null;
207
+ }
208
+
209
+ /**
210
+ * Parse any IPv4 or IPv6 address into a descriptor object, or throw.
211
+ * @param {string} str
212
+ * @returns {{version:4, address:string, int:number}|{version:6, address:string, big:bigint}}
213
+ * @throws {Error} on invalid input
214
+ */
215
+ function parse(str) {
216
+ const v = ipVersion(str);
217
+ if (v === 4) return { version: 4, address: str, int: ipv4ToInt(str) };
218
+ if (v === 6) return { version: 6, address: str, big: ipv6ToBigInt(str) };
219
+ throw new Error('invalid IP address: ' + str);
220
+ }
221
+
222
+ // ---------------------------------------------------------------------------
223
+ // CIDR parsing
224
+ // ---------------------------------------------------------------------------
225
+
226
+ /**
227
+ * Parse a CIDR string ("10.0.0.0/24" or "2001:db8::/32").
228
+ *
229
+ * For IPv4 returns a descriptor with numeric network/broadcast/first/last host
230
+ * and host count. For IPv6 returns network/first/last/count as BigInt strings
231
+ * plus a numeric prefix (no broadcast — not an IPv6 concept).
232
+ *
233
+ * @param {string} str
234
+ * @returns {object} see README for the full shape
235
+ * @throws {Error} on invalid input
236
+ */
237
+ function parseCidr(str) {
238
+ if (typeof str !== 'string') throw new Error('invalid CIDR: not a string');
239
+ const slash = str.indexOf('/');
240
+ if (slash === -1) throw new Error('invalid CIDR: missing "/" in ' + str);
241
+ const addr = str.slice(0, slash);
242
+ const prefixStr = str.slice(slash + 1);
243
+ if (!/^\d{1,3}$/.test(prefixStr)) {
244
+ throw new Error('invalid CIDR: bad prefix in ' + str);
245
+ }
246
+ const prefix = Number(prefixStr);
247
+ const v = ipVersion(addr);
248
+ if (v === 4) return _parseCidr4(addr, prefix);
249
+ if (v === 6) return _parseCidr6(addr, prefix);
250
+ throw new Error('invalid CIDR: bad address in ' + str);
251
+ }
252
+
253
+ function _parseCidr4(addr, prefix) {
254
+ if (prefix < 0 || prefix > 32) {
255
+ throw new Error('invalid CIDR: IPv4 prefix must be 0-32, got ' + prefix);
256
+ }
257
+ const ipInt = ipv4ToInt(addr);
258
+ // Mask as unsigned 32-bit. prefix 0 => 0 mask (avoid <<32 UB).
259
+ const mask = prefix === 0 ? 0 : (0xffffffff << (32 - prefix)) >>> 0;
260
+ const network = (ipInt & mask) >>> 0;
261
+ const broadcast = (network | (~mask >>> 0)) >>> 0;
262
+ const size = prefix === 0 ? 0x100000000 : Math.pow(2, 32 - prefix);
263
+
264
+ let firstHost;
265
+ let lastHost;
266
+ let usableHosts;
267
+ if (prefix >= 31) {
268
+ // /31 (RFC 3021 point-to-point) and /32 have no "network/broadcast"
269
+ // reservation, so every address is usable.
270
+ firstHost = network;
271
+ lastHost = broadcast;
272
+ usableHosts = size; // /32 => 1, /31 => 2
273
+ } else {
274
+ firstHost = (network + 1) >>> 0;
275
+ lastHost = (broadcast - 1) >>> 0;
276
+ usableHosts = size - 2;
277
+ }
278
+
279
+ return {
280
+ version: 4,
281
+ cidr: intToIpv4(network) + '/' + prefix,
282
+ prefix: prefix,
283
+ network: intToIpv4(network),
284
+ networkInt: network,
285
+ broadcast: intToIpv4(broadcast),
286
+ broadcastInt: broadcast,
287
+ firstHost: intToIpv4(firstHost),
288
+ firstHostInt: firstHost,
289
+ lastHost: intToIpv4(lastHost),
290
+ lastHostInt: lastHost,
291
+ size: size, // total addresses in the block
292
+ hostCount: usableHosts, // usable host count
293
+ };
294
+ }
295
+
296
+ function _parseCidr6(addr, prefix) {
297
+ if (prefix < 0 || prefix > 128) {
298
+ throw new Error('invalid CIDR: IPv6 prefix must be 0-128, got ' + prefix);
299
+ }
300
+ const ipBig = ipv6ToBigInt(addr);
301
+ const full = (1n << 128n) - 1n;
302
+ const mask = prefix === 0 ? 0n : (full << BigInt(128 - prefix)) & full;
303
+ const network = ipBig & mask;
304
+ const last = network | (~mask & full);
305
+ const size = 1n << BigInt(128 - prefix);
306
+
307
+ return {
308
+ version: 6,
309
+ cidr: bigIntToIpv6(network) + '/' + prefix,
310
+ prefix: prefix,
311
+ network: bigIntToIpv6(network),
312
+ networkBig: network,
313
+ firstHost: bigIntToIpv6(network),
314
+ firstHostBig: network,
315
+ lastHost: bigIntToIpv6(last),
316
+ lastHostBig: last,
317
+ size: size, // BigInt total addresses
318
+ hostCount: size, // IPv6 has no broadcast reservation
319
+ };
320
+ }
321
+
322
+ // ---------------------------------------------------------------------------
323
+ // Containment
324
+ // ---------------------------------------------------------------------------
325
+
326
+ /**
327
+ * Does the CIDR block contain the given IP? Both must be the same family.
328
+ * Returns a boolean; never throws for a well-formed cidr/ip of matching
329
+ * family. Throws only if either argument is not parseable.
330
+ * @param {string} cidr e.g. "10.0.0.0/24"
331
+ * @param {string} ip e.g. "10.0.0.5"
332
+ * @returns {boolean}
333
+ * @throws {Error} if cidr or ip cannot be parsed
334
+ */
335
+ function contains(cidr, ip) {
336
+ const block = parseCidr(cidr);
337
+ const addr = parse(ip);
338
+ if (block.version !== addr.version) return false; // cross-family never matches
339
+ if (block.version === 4) {
340
+ return addr.int >= block.networkInt && addr.int <= block.broadcastInt;
341
+ }
342
+ return addr.big >= block.networkBig && addr.big <= block.lastHostBig;
343
+ }
344
+
345
+ module.exports = {
346
+ // IPv4 core
347
+ isValidIPv4,
348
+ ipv4ToInt,
349
+ intToIpv4,
350
+ // IPv6 core
351
+ isValidIPv6,
352
+ ipv6ToBigInt,
353
+ bigIntToIpv6,
354
+ // unified
355
+ ipVersion,
356
+ parse,
357
+ parseCidr,
358
+ contains,
359
+ };
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@verifyhash/ip-cidr",
3
+ "version": "0.1.0",
4
+ "description": "Zero-dependency IPv4 (and basic IPv6) address/CIDR toolkit for Node.js: validate/parse, CIDR math (network, broadcast, first/last host, host count), ip<->integer, and containment.",
5
+ "keywords": [
6
+ "ip",
7
+ "ipv4",
8
+ "ipv6",
9
+ "cidr",
10
+ "subnet",
11
+ "netmask",
12
+ "network",
13
+ "broadcast",
14
+ "contains",
15
+ "ip-to-int"
16
+ ],
17
+ "main": "index.js",
18
+ "scripts": {
19
+ "test": "node test/index.test.js"
20
+ },
21
+ "license": "MIT",
22
+ "files": [
23
+ "index.js",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/verifyhash/libs.git",
33
+ "directory": "ip-cidr"
34
+ },
35
+ "homepage": "https://github.com/verifyhash/libs/tree/main/ip-cidr#readme",
36
+ "bugs": {
37
+ "url": "https://github.com/verifyhash/libs/issues"
38
+ }
39
+ }