ip-utilties 1.0.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/LICENSE ADDED
@@ -0,0 +1,12 @@
1
+ Copyright (C) 2025 by arlac77
2
+
3
+ Permission to use, copy, modify, and/or distribute this software for any
4
+ purpose with or without fee is hereby granted.
5
+
6
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
7
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
8
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
9
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
10
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
11
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
12
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ [![npm](https://img.shields.io/npm/v/ip-utilties.svg)](https://www.npmjs.com/package/ip-utilties)
2
+ [![License](https://img.shields.io/badge/License-0BSD-blue.svg)](https://spdx.org/licenses/0BSD.html)
3
+ [![Typed with TypeScript](https://flat.badgen.net/badge/icon/Typed?icon=typescript\&label\&labelColor=blue\&color=555555)](https://typescriptlang.org)
4
+ [![bundlejs](https://deno.bundlejs.com/?q=ip-utilties\&badge=detailed)](https://bundlejs.com/?q=ip-utilties)
5
+ [![downloads](http://img.shields.io/npm/dm/ip-utilties.svg?style=flat-square)](https://npmjs.org/package/ip-utilties)
6
+ [![GitHub Issues](https://img.shields.io/github/issues/arlac77/ip-utilties.svg?style=flat-square)](https://github.com/arlac77/ip-utilties/issues)
7
+ [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Farlac77%2Fip-utilties%2Fbadge\&style=flat)](https://actions-badge.atrox.dev/arlac77/ip-utilties/goto)
8
+ [![Styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg)](https://github.com/prettier/prettier)
9
+ [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
10
+ [![Known Vulnerabilities](https://snyk.io/test/github/arlac77/ip-utilties/badge.svg)](https://snyk.io/test/github/arlac77/ip-utilties)
11
+ [![Coverage Status](https://coveralls.io/repos/arlac77/ip-utilties/badge.svg)](https://coveralls.io/github/arlac77/ip-utilties)
12
+
13
+ ip v4/v6 utility functions
14
+
15
+ # API
16
+
17
+ # install
18
+
19
+ With [npm](http://npmjs.org) do:
20
+
21
+ ```shell
22
+ npm install ip-utilties
23
+ ```
24
+
25
+ # license
26
+
27
+ BSD-2-Clause
package/package.json ADDED
@@ -0,0 +1,68 @@
1
+ {
2
+ "name": "ip-utilties",
3
+ "version": "1.0.0",
4
+ "publishConfig": {
5
+ "access": "public",
6
+ "provenance": true
7
+ },
8
+ "exports": {
9
+ ".": {
10
+ "default": "./src/ip.mjs"
11
+ }
12
+ },
13
+ "description": "ip v4/v6 utility functions",
14
+ "keywords": [
15
+ "ip",
16
+ "ipv4",
17
+ "ipv6",
18
+ "network",
19
+ "subnet"
20
+ ],
21
+ "contributors": [
22
+ {
23
+ "name": "Markus Felten",
24
+ "email": "markus.felten@gmx.de"
25
+ }
26
+ ],
27
+ "license": "0BSD",
28
+ "scripts": {
29
+ "prepare": "node --run prepare:typescript",
30
+ "prepare:typescript": "tsc --allowJs --declaration --emitDeclarationOnly --declarationDir types --resolveJsonModule --target es2024 --lib esnext -m esnext --module nodenext --moduleResolution nodenext --rootDir src ./src**/*.mjs",
31
+ "test": "node --run test:browser-ava && node --run test:ava",
32
+ "test:browser-ava": "browser-ava --headless --no-keep-open tests/*-ava.mjs tests/*-ava-browser.mjs",
33
+ "test:ava": "ava --timeout 4m tests/*-ava.mjs tests/*-ava-node.mjs",
34
+ "cover": "c8 -x 'tests/**/*' --temp-directory build/tmp ava --timeout 4m tests/*-ava.mjs tests/*-ava-node.mjs && c8 report -r lcov -o build/coverage --temp-directory build/tmp",
35
+ "docs": "documentation readme --section=API ./src**/*.mjs",
36
+ "lint": "node --run lint:docs && node --run lint:typescript",
37
+ "lint:docs": "documentation lint ./src**/*.mjs",
38
+ "lint:typescript": "tsc --allowJs --checkJs --noEmit --resolveJsonModule --target es2024 --lib esnext -m esnext --module nodenext --moduleResolution nodenext ./src**/*.mjs"
39
+ },
40
+ "devDependencies": {
41
+ "ava": "^6.2.0",
42
+ "browser-ava": "^2.3.16",
43
+ "c8": "^10.1.3",
44
+ "documentation": "^14.0.3",
45
+ "semantic-release": "^24.2.3",
46
+ "typescript": "^5.8.3"
47
+ },
48
+ "engines": {
49
+ "node": ">=22.14.0"
50
+ },
51
+ "repository": {
52
+ "type": "git",
53
+ "url": "git+https://github.com/arlac77/ip-utilties.git"
54
+ },
55
+ "bugs": {
56
+ "url": "https://github.com/arlac77/ip-utilties/issues"
57
+ },
58
+ "homepage": "https://github.com/arlac77/ip-utilties#readme",
59
+ "template": {
60
+ "inheritFrom": [
61
+ "arlac77/template-arlac77-github",
62
+ "arlac77/template-browser-ava",
63
+ "arlac77/template-javascript-component",
64
+ "arlac77/template-node-component",
65
+ "arlac77/template-typescript"
66
+ ]
67
+ }
68
+ }
package/src/ip.mjs ADDED
@@ -0,0 +1,323 @@
1
+ const ipv4 = {
2
+ factory: Uint8Array,
3
+ normalize(address) {
4
+ return address;
5
+ },
6
+ separator: ".",
7
+ bitLength: 32,
8
+ byteLength: 4,
9
+ segments: 4,
10
+ segmentLength: 8,
11
+ segmentMask: 0xffn,
12
+ mask: 0xffffffffn,
13
+ base: 10
14
+ };
15
+
16
+ const ipv6 = {
17
+ factory: Uint16Array,
18
+ normalize(address) {
19
+ const parts = address.split(":");
20
+ const i = parts.indexOf("");
21
+ if (i >= 0) {
22
+ parts.splice(i, 1, ..."0".repeat(9 - parts.length));
23
+ }
24
+ return parts.join(":");
25
+ },
26
+ separator: ":",
27
+ compressor: "::",
28
+ bitLength: 128,
29
+ byteLength: 8,
30
+ segments: 8,
31
+ segmentLength: 16,
32
+ segmentMask: 0xffffn,
33
+ mask: 0xffffffffffffffffffffffffffffffffn,
34
+ base: 16
35
+ };
36
+
37
+ export function IPV4(...args) {
38
+ return _create(ipv4, ...args);
39
+ }
40
+
41
+ export function IPV6(...args) {
42
+ return _create(ipv6, ...args);
43
+ }
44
+
45
+ function _create(definition, ...args) {
46
+ if (args.length === 1) {
47
+ return _encode(definition, args[0]);
48
+ }
49
+ return new definition.factory(args);
50
+ }
51
+
52
+ export function encodeIP(address) {
53
+ const d = _definition(address);
54
+ return d && _encode(d, address);
55
+ }
56
+
57
+ export function encodeIPv6(address) {
58
+ return _encode(ipv6, address);
59
+ }
60
+
61
+ export function encodeIPv4(address) {
62
+ return _encode(ipv4, address);
63
+ }
64
+
65
+ export function _encode(definition, address) {
66
+ switch (typeof address) {
67
+ case "string":
68
+ const res = new definition.factory(definition.segments);
69
+
70
+ let i = 0;
71
+ for (const segment of definition
72
+ .normalize(address)
73
+ .split(definition.separator)) {
74
+ res[i++] = parseInt(segment, definition.base);
75
+ }
76
+
77
+ return res;
78
+
79
+ case "bigint":
80
+ return _encodeBigInt(definition, address);
81
+
82
+ case "object":
83
+ if (
84
+ address instanceof definition.factory &&
85
+ address.length === definition.byteLength
86
+ ) {
87
+ return address;
88
+ }
89
+ }
90
+ }
91
+
92
+ function _decode(definition, address, length) {
93
+ switch (typeof address) {
94
+ case "string":
95
+ if (length === undefined) {
96
+ return address;
97
+ }
98
+ address = _encode(definition, address);
99
+ break;
100
+ case "bigint":
101
+ address = _encodeBigInt(definition, address);
102
+ }
103
+
104
+ let result = "";
105
+ let compressed = 0;
106
+ let word;
107
+ let last = address?.length;
108
+
109
+ if (length !== undefined) {
110
+ length /= definition.segmentLength;
111
+
112
+ if (length < last) {
113
+ last = length;
114
+ }
115
+ }
116
+ for (let i = 0, j = 0; i < last; j = j + 1, i = j) {
117
+ for (; j < last; j++) {
118
+ word = address[j];
119
+
120
+ if (word !== 0 || !definition.compressor || compressed > 0) {
121
+ break;
122
+ }
123
+ }
124
+
125
+ if (j > i + 1) {
126
+ compressed++;
127
+ result += definition.compressor;
128
+ } else {
129
+ if (result.length > 0) {
130
+ result += definition.separator;
131
+ }
132
+ }
133
+
134
+ if (j < last) {
135
+ result += word.toString(definition.base);
136
+ }
137
+ }
138
+
139
+ return result;
140
+ }
141
+
142
+ export function decodeIPv6(address, length) {
143
+ return _decode(ipv6, address, length);
144
+ }
145
+
146
+ export function decodeIPv4(address, length) {
147
+ return _decode(ipv4, address, length);
148
+ }
149
+
150
+ export function decodeIP(address, length) {
151
+ return _decode(isIPv4(address) ? ipv4 : ipv6, address, length);
152
+ }
153
+
154
+ export function isIPv4(address) {
155
+ return _is(ipv4, address);
156
+ }
157
+
158
+ export function isIPv6(address) {
159
+ return _is(ipv6, address);
160
+ }
161
+
162
+ function _definition(address) {
163
+ for (const defintion of [ipv4, ipv6]) {
164
+ if (_is(defintion, address)) {
165
+ return defintion;
166
+ }
167
+ }
168
+ }
169
+
170
+ export function _is(definition, address) {
171
+ switch (typeof address) {
172
+ case "string":
173
+ return address.indexOf(definition.separator) >= 0;
174
+
175
+ case "object":
176
+ return (
177
+ address instanceof definition.factory &&
178
+ address.length === definition.byteLength
179
+ );
180
+ }
181
+
182
+ return false;
183
+ }
184
+
185
+ export function asBigInt(address) {
186
+ return _asBigInt(isIPv4(address) ? ipv4 : ipv6, address);
187
+ }
188
+
189
+ function _asBigInt(definition, address) {
190
+ if (typeof address === "bigint") {
191
+ return address;
192
+ }
193
+
194
+ const ea = _encode(definition, address);
195
+
196
+ let result = 0n;
197
+
198
+ for (let i = 0; i < ea.length; i++) {
199
+ result = result << BigInt(definition.segmentLength);
200
+ result += BigInt(ea[i]);
201
+ }
202
+
203
+ return result;
204
+ }
205
+
206
+ function _encodeBigInt(definition, address) {
207
+ const segments = [];
208
+
209
+ for (let i = 0; i < definition.segments; i++) {
210
+ segments.push(Number(address & definition.segmentMask));
211
+ address >>= BigInt(definition.segmentLength);
212
+ }
213
+
214
+ return new definition.factory(segments.reverse());
215
+ }
216
+
217
+ export function prefixIP(address, length) {
218
+ const definition = isIPv4(address) ? ipv4 : ipv6;
219
+ return _decode(definition, _prefix(definition, address, length));
220
+ }
221
+
222
+ export function _prefix(definition, address, length) {
223
+ return (
224
+ _asBigInt(definition, address) &
225
+ (definition.mask << BigInt(definition.bitLength - length))
226
+ );
227
+ }
228
+
229
+ export function rangeIP(address, prefix, lowerAdd = 0, upperReduce = 0) {
230
+ const definition = isIPv4(address) ? ipv4 : ipv6;
231
+
232
+ const from = _prefix(definition, address, prefix);
233
+ const to = from | ((1n << BigInt(definition.bitLength - prefix)) - 1n);
234
+
235
+ return [_encode(definition, from + BigInt(lowerAdd)), _encode(definition, to - BigInt(upperReduce))];
236
+ }
237
+
238
+ export function normalizeCIDR(address) {
239
+ let [prefix, prefixLength] = address.split(/\//);
240
+ let longPrefix;
241
+
242
+ if (!prefixLength && isLinkLocal(address)) {
243
+ prefix = "fe80::";
244
+ longPrefix = prefix;
245
+ prefixLength = 64;
246
+ } else {
247
+ prefixLength = parseInt(prefixLength);
248
+
249
+ const definition = isIPv6(prefix) ? ipv6 : ipv4;
250
+ let n;
251
+
252
+ if (prefixLength) {
253
+ n = _prefix(definition, prefix, prefixLength);
254
+ } else {
255
+ n = _encode(definition, prefix);
256
+
257
+ if (isLocalhost(n)) {
258
+ prefixLength = definition === ipv6 ? 128 : 8;
259
+ } else {
260
+ return {};
261
+ }
262
+ }
263
+ prefix = _decode(definition, n, prefixLength);
264
+ longPrefix = _decode(definition, n);
265
+ }
266
+
267
+ return {
268
+ longPrefix,
269
+ prefix,
270
+ prefixLength,
271
+ cidr: `${prefix}/${prefixLength}`
272
+ };
273
+ }
274
+
275
+ export function formatCIDR(address, subnet) {
276
+ return subnet ? `${address}/${subnet.prefixLength}` : address;
277
+ }
278
+
279
+ export function normalizeIP(address) {
280
+ return decodeIP(encodeIP(address));
281
+ }
282
+
283
+ export function reverseArpa(address) {
284
+ if (isIPv6(address)) {
285
+ const ea = encodeIPv6(address);
286
+ let result = [];
287
+ for (let i = 0; i < ea.length; i++) {
288
+ const v = ea[i];
289
+ for (let i = 0; i < 4; i++) {
290
+ result.push(((v >> (12 - 4 * i)) & 0x000f).toString(16));
291
+ }
292
+ }
293
+ return result.reverse().join(".") + ".ip6.arpa";
294
+ }
295
+
296
+ return address.split(".").reverse().join(".") + ".in-addr.arpa";
297
+ }
298
+
299
+ export function isLocalhost(address) {
300
+ const eaddr = encodeIP(address);
301
+
302
+ if (!eaddr) {
303
+ return false;
304
+ }
305
+
306
+ const str = eaddr.toString();
307
+
308
+ return str === IPV4_LOCALHOST.toString() || str === IPV6_LOCALHOST.toString();
309
+ }
310
+
311
+ export function isLinkLocal(address) {
312
+ const eaddr = encodeIP(address);
313
+ return eaddr?.[0] === 0xfe80;
314
+ }
315
+
316
+ export function hasWellKnownSubnet(address) {
317
+ return isLocalhost(address) || isLinkLocal(address);
318
+ }
319
+
320
+ export const IPV6_LINK_LOCAL_BROADCAST = _encode(ipv6, "ff02::1");
321
+ export const IPV6_ROUTER_BROADCAST = _encode(ipv6, "ff02::2");
322
+ export const IPV4_LOCALHOST = _encode(ipv4, "127.0.0.1");
323
+ export const IPV6_LOCALHOST = _encode(ipv6, "::1");
package/types/ip.d.mts ADDED
@@ -0,0 +1,37 @@
1
+ export function IPV4(...args: any[]): any;
2
+ export function IPV6(...args: any[]): any;
3
+ export function encodeIP(address: any): any;
4
+ export function encodeIPv6(address: any): any;
5
+ export function encodeIPv4(address: any): any;
6
+ export function _encode(definition: any, address: any): any;
7
+ export function decodeIPv6(address: any, length: any): string;
8
+ export function decodeIPv4(address: any, length: any): string;
9
+ export function decodeIP(address: any, length: any): string;
10
+ export function isIPv4(address: any): boolean;
11
+ export function isIPv6(address: any): boolean;
12
+ export function _is(definition: any, address: any): boolean;
13
+ export function asBigInt(address: any): bigint;
14
+ export function prefixIP(address: any, length: any): string;
15
+ export function _prefix(definition: any, address: any, length: any): bigint;
16
+ export function rangeIP(address: any, prefix: any, lowerAdd?: number, upperReduce?: number): any[];
17
+ export function normalizeCIDR(address: any): {
18
+ longPrefix?: undefined;
19
+ prefix?: undefined;
20
+ prefixLength?: undefined;
21
+ cidr?: undefined;
22
+ } | {
23
+ longPrefix: any;
24
+ prefix: any;
25
+ prefixLength: any;
26
+ cidr: string;
27
+ };
28
+ export function formatCIDR(address: any, subnet: any): any;
29
+ export function normalizeIP(address: any): string;
30
+ export function reverseArpa(address: any): string;
31
+ export function isLocalhost(address: any): boolean;
32
+ export function isLinkLocal(address: any): boolean;
33
+ export function hasWellKnownSubnet(address: any): boolean;
34
+ export const IPV6_LINK_LOCAL_BROADCAST: any;
35
+ export const IPV6_ROUTER_BROADCAST: any;
36
+ export const IPV4_LOCALHOST: any;
37
+ export const IPV6_LOCALHOST: any;