diff --git a/src/IPAddress.ts b/src/IPAddress.ts index 1c0d46d..0ec69df 100644 --- a/src/IPAddress.ts +++ b/src/IPAddress.ts @@ -17,23 +17,27 @@ import {IPv4, IPv6} from "./index.js"; /** - * An IP address + * An IP address. */ export abstract class IPAddress { /** - * The integer representation of the IP address + * The integer representation of the IP address. */ public readonly value: bigint; /** - * Create new IP address instance + * Creates a new IP address instance. + * + * @param value The integer representation of the IP address. + * @deprecated This class will be sealed in v2.0.0 and should not be extended by public API users. */ protected constructor(value: bigint) { this.value = value; } /** - * Create IP address from string + * Creates an IP address from a string. + * * @throws {@link !RangeError} If provided string is not a valid IPv4 or IPv6 address */ public static fromString(str: string): IPv4 | IPv6 { @@ -42,19 +46,19 @@ export abstract class IPAddress { } /** - * Get IP address binary representation + * Gets the IP address binary representation. */ public abstract binary(): ArrayBufferView; /** - * Check if the given addresses are equal + * Checks if the given addresses are equal. */ public equals(other: IPAddress): boolean { return other instanceof this.constructor && other.value === this.value; } /** - * Format IP address as string + * Formats the IP address as string. */ public abstract toString(): string; } diff --git a/src/IPv4.ts b/src/IPv4.ts index b6e5bd2..f9ff16c 100644 --- a/src/IPv4.ts +++ b/src/IPv4.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Cloudnode OÜ + * Copyright © 2024–2025 Cloudnode OÜ * * This file is part of @cldn/ip. * @@ -17,18 +17,33 @@ import {IPAddress} from "./index.js"; /** - * An IPv4 address + * An IPv4 address. */ export class IPv4 extends IPAddress { + /** + * Regular expression for testing IPv4 addresses in string form. + */ public static regex = /^(?:(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d{2}|[1-9]\d|\d)$/; + + /** + * Bit length of IPv4 addresses. + */ public static bitLength = 32; /** - * Create new IPv4 address instance - * @throws {@link !TypeError} If + * Creates a new IPv4 address instance. + * + * @param value A 32-bit unsigned big integer. + * @throws {@link !TypeError} If provided value is not a 32-bit unsigned integer. */ public constructor(value: bigint); + /** + * Creates a new IPv4 address instance. + * + * @param value A 32-bit unsigned number. + * @throws {@link !TypeError} If provided value is not a 32-bit unsigned integer. + */ public constructor(value: number); public constructor(value: number | bigint) { @@ -39,8 +54,10 @@ export class IPv4 extends IPAddress { } /** - * Create an IPv4 address instance from octets - * @throws {@link !RangeError} If provided octets are not 4 + * Creates an IPv4 address instance from octets. + * + * @param octets A typed array of 4 octets. + * @throws {@link !RangeError} If provided octets are not 4. */ public static fromBinary(octets: Uint8Array): IPv4 { if (octets.length !== 4) throw new RangeError("Expected 4 octets, got " + octets.length); @@ -56,8 +73,10 @@ export class IPv4 extends IPAddress { } /** - * Create an IPv4 address instance from string - * @throws {@link !RangeError} If provided string is not a valid IPv4 address + * Creates an IPv4 address instance from a string. + * + * @param str A string representation of an IPv4 address. + * @throws {@link !RangeError} If provided string is not a valid IPv4 address. */ public static override fromString(str: string): IPv4 { const octets = str.split(".", 4).map(octet => Number.parseInt(octet, 10)); @@ -68,7 +87,7 @@ export class IPv4 extends IPAddress { } /** - * Get the 4 octets of the IPv4 address + * Gets the 4 octets of the IPv4 address. */ public override binary(): Uint8Array { return new Uint8Array([ diff --git a/src/IPv6.ts b/src/IPv6.ts index eb590d4..a9a1c18 100644 --- a/src/IPv6.ts +++ b/src/IPv6.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Cloudnode OÜ + * Copyright © 2024–2025 Cloudnode OÜ * * This file is part of @cldn/ip. * @@ -17,13 +17,16 @@ import {IPAddress, IPv4, Subnet} from "./index.js"; /** - * An IPv6 address + * An IPv6 address. */ export class IPv6 extends IPAddress { public static bitLength = 128; /** - * Create new IPv6 address instance + * Creates a new IPv6 address instance. + * + * @param value A 128-bit unsigned big integer. + * @throws {@link !TypeError} If provided value is not a 128-bit unsigned integer. */ public constructor(value: bigint) { if (value < 0n || value > ((1n << BigInt(IPv6.bitLength)) - 1n)) @@ -32,8 +35,10 @@ export class IPv6 extends IPAddress { } /** - * Create an IPv6 address instance from hextets - * @throws {@link !RangeError} If provided hextets are not 8 + * Creates an IPv6 address instance from hextets. + * + * @param hextets A typed array of 8 hextets. + * @throws {@link !RangeError} If provided hextets are not 8. */ public static fromBinary(hextets: Uint16Array): IPv6 { if (hextets.length !== 8) throw new RangeError("Expected 8 hextets, got " + hextets.length); @@ -51,8 +56,10 @@ export class IPv6 extends IPAddress { } /** - * Create an IPv6 address instance from string - * @throws {@link !RangeError} If provided string is not a valid IPv6 address + * Creates an IPv6 address instance from a string. + * + * @param str A string representation of an IPv6 address. + * @throws {@link !RangeError} If provided string is not a valid IPv6 address. */ public static override fromString(str: string): IPv6 { const parts = str.split("::", 2); @@ -77,7 +84,7 @@ export class IPv6 extends IPAddress { } /** - * Parse string hextet into unsigned 16-bit integer + * Parses a string hextet into unsigned 16-bit integer. * @internal */ private static parseHextet(hextet: string): number | number[] { @@ -92,7 +99,7 @@ export class IPv6 extends IPAddress { } /** - * Get the 8 hextets of the IPv6 address + * Gets the 8 hextets of the IPv6 address */ public binary(): Uint16Array { return new Uint16Array([ @@ -108,16 +115,16 @@ export class IPv6 extends IPAddress { } /** - * Check whether this is an IPv4-mapped IPv6 address. - * Only works for `::ffff:0:0/96` + * Checks whether this is an IPv4-mapped IPv6 address. Only works for `::ffff:0:0/96`. */ public hasMappedIPv4(): boolean { return Subnet.IPV4_MAPPED_IPV6.has(this); } /** - * Get the IPv4-mapped IPv6 address. - * Returns the last 32 bits as an IPv4 address. + * Gets the IPv4-mapped IPv6 address. + * + * @returns An IPv4 address from the least significant 32 bits of this IPv6 address. * @see {@link IPv6#hasMappedIPv4} */ public getMappedIPv4(): IPv4 { diff --git a/src/Network.ts b/src/Network.ts index 6684b9d..5c6cff6 100644 --- a/src/Network.ts +++ b/src/Network.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Cloudnode OÜ + * Copyright © 2024–2025 Cloudnode OÜ * * This file is part of @cldn/ip. * @@ -17,11 +17,11 @@ import {IPv4, IPv6, Subnet} from "./index.js" /** - * A network that can contain multiple subnets + * A network that can contain multiple subnets. */ export class Network { /** - * Reserved subnets + * A network of reserved subnets. This network does not contain publicly routable IP addresses. */ public static readonly BOGON = new Network([ // IPv4 @@ -58,45 +58,50 @@ export class Network { readonly #subnets: Map> = new Map(); /** - * Create new network - * @param [subnets] Initial subnets to add to this network + * Creates a new network. + * + * @param [subnets] Initial subnets to add to this network. */ public constructor(subnets?: Iterable>) { if (subnets) for (const subnet of subnets) this.add(subnet); } /** - * Add a subnet to this network + * Adds a subnet to this network. */ public add(subnet: Subnet): void { this.#subnets.set(subnet.toString(), subnet); } /** - * Remove a subnet from this network - * @param cidr CIDR notation of the subnet to remove + * Removes a subnet from this network. + * + * @param cidr CIDR notation of the subnet to remove. */ public remove(cidr: string): void { this.#subnets.delete(Subnet.fromCIDR(cidr).toString()); } /** - * Check if subnet is in this network - * @param cidr CIDR notation of the subnet to check + * Checks if a subnet is in this network. + * + * @param cidr CIDR notation of the subnet to check. */ public hasSubnet(cidr: string): boolean { return this.#subnets.has(Subnet.fromCIDR(cidr).toString()); } /** - * Get all subnets in this network mapped to their CIDR notation + * Gets all subnets in this network mapped to their CIDR notation. */ public subnets(): ReadonlyMap> { return this.#subnets; } /** - * Check if an IP address is in this network + * Checks if an IP address is in this network. + * + * @param address IP address to check. */ public has(address: IPv4 | IPv6): boolean { for (const subnet of this.#subnets.values()) if (subnet.has(address)) return true; @@ -104,7 +109,7 @@ export class Network { } /** - * Get the number of addresses in this network + * Gets the number of addresses in this network. */ public size(): bigint { let size = 0n; diff --git a/src/Subnet.ts b/src/Subnet.ts index ed5aa05..a344bcc 100644 --- a/src/Subnet.ts +++ b/src/Subnet.ts @@ -1,5 +1,5 @@ /* - * Copyright © 2024 Cloudnode OÜ + * Copyright © 2024–2025 Cloudnode OÜ * * This file is part of @cldn/ip. * @@ -17,30 +17,32 @@ import {IPAddress, IPv4, IPv6} from "./index.js"; /** - * A subnet of IP addresses - * @typeParam T IP address family + * An address range subnetwork of IP addresses. + * + * @typeParam T IP address family. */ export class Subnet implements Iterable { /** - * IPv4-mapped IPv6 subnet + * IPv4-mapped IPv6 subnet. */ public static readonly IPV4_MAPPED_IPV6 = Subnet.fromCIDR("::ffff:0.0.0.0/96"); /** - * Subnet address (i.e. the first/gateway IP address of the network) + * The subnet address (i.e. the first/gateway IP address of the network). */ public readonly address: T; /** - * IP address constructor + * IP address constructor. */ readonly #AddressClass: typeof IPv4 | typeof IPv6; /** - * Create new subnet instance - * @param address An IP address - * @param suffix Subnet suffix bits - * @throws {@link !RangeError} If the suffix is greater than the IP address family bit-length + * Creates a new subnet instance. + * + * @param address An IP address. + * @param suffix Subnet suffix bits. + * @throws {@link !RangeError} If the suffix is greater than the IP address family bit-length. */ public constructor(address: T, public readonly suffix: number) { this.#AddressClass = address.constructor as typeof IPv4 | typeof IPv6; @@ -49,7 +51,9 @@ export class Subnet implements Iterable { } /** - * Create subnet from string in CIDR notation + * Creates a subnet from a string in CIDR notation. + * + * @param cidr A string in CIDR notation. * @throws {@link !RangeError} If the address or suffix is invalid */ public static fromCIDR(cidr: string): Subnet { @@ -60,28 +64,30 @@ export class Subnet implements Iterable { } /** - * Get the network mask + * Gets the network mask. */ public netmask(): bigint { return ((1n << BigInt(this.suffix)) - 1n) << BigInt(this.#AddressClass.bitLength - this.suffix); } /** - * Get the network wildcard mask + * Gets the network wildcard mask. */ public wildcard(): bigint { return ((1n << BigInt(this.#AddressClass.bitLength - this.suffix)) - 1n); } /** - * Get the last IP address in the subnet (i.e. broadcast address) + * Gets the last IP address in the subnet (i.e. broadcast address). */ public broadcast(): T { return new this.#AddressClass(this.address.value | this.wildcard()) as T; } /** - * Check if the provided IP address is in this subnet + * Checks if the provided IP address is in this subnet. + * + * @param address IP address to check. */ public has(address: T): boolean { if (!(address instanceof this.#AddressClass)) return false; @@ -89,8 +95,7 @@ export class Subnet implements Iterable { } /** - * Get the number of addresses in this subnet. - * This number includes the broadcast and gateway addresses, so for the + * Gets the number of addresses in this subnet. This number includes the broadcast and gateway addresses, so for the * number of *usable* addresses, subtract 2. */ public size(): bigint { @@ -98,37 +103,38 @@ export class Subnet implements Iterable { } /** - * Iterate all IP addresses in this subnet + * Iterates all IP addresses in this subnet. * * **NOTE**: This can be slow for large subnets. If you need to check if an - * IP address is in the subnet, use {@link Subnet#contains}. + * IP address is in the subnet, use {@link Subnet#has}. */ public* iterate(): IterableIterator { for (let i = this.address.value; i <= this.broadcast().value; ++i) yield new this.#AddressClass(i) as T; } /** - * Iterate all IP addresses in this subnet + * Iterates all IP addresses in this subnet. * * **NOTE**: This can be slow for large subnets. If you need to check if an - * IP address is in the subnet, use {@link Subnet#contains}. + * IP address is in the subnet, use {@link Subnet#has}. */ public [Symbol.iterator](): IterableIterator { return this.iterate(); } /** - * A set containing all IP addresses in this subnet + * Creates a set containing all IP addresses in this subnet. * * **NOTE**: This can be slow for large subnets. If you need to check if an - * IP address is in the subnet, use {@link Subnet#contains}. + * IP address is in the subnet, use {@link Subnet#has}. */ public set(): Set { return new Set(this.iterate()); } /** - * String representation of this subnet in CIDR notation + * Creates a string representation of this subnet in CIDR notation. + * * @example "203.0.113.0/24" */ public toString(): string {