{"version":3,"file":"calculate-subnets.cjs","sources":["../../../src/validate.ts","../../../src/uniformly-distributed-subnets.ts","../../../bin/cli.ts"],"sourcesContent":["import assert from 'node:assert';\nimport { Cidr, IpAddress } from 'cidr-calc';\nimport { UniformlyDistributedSubnetsArguments } from './types.js';\n\nexport function validate({\n  neededSubnets,\n  cidr,\n}: UniformlyDistributedSubnetsArguments): {\n  parentCidr: Cidr;\n} {\n  let parentCidr: Cidr;\n  let availableSpace: number;\n  try {\n    const [ipRaw, cidrNumberRaw] = cidr.split('/');\n    assert(typeof ipRaw === 'string');\n    assert(\n      typeof cidrNumberRaw === 'string' &&\n        Number.isInteger(Number(cidrNumberRaw)),\n    );\n    parentCidr = new Cidr(IpAddress.of(ipRaw), Number(cidrNumberRaw));\n    availableSpace = 32 - parentCidr.prefixLen;\n  } catch {\n    throw new TypeError(`Invalid CIDR provided: ${cidr}`);\n  }\n\n  if (\n    typeof availableSpace !== 'number' ||\n    Number.isNaN(availableSpace) ||\n    !Number.isInteger(availableSpace) ||\n    availableSpace < 0 ||\n    availableSpace > 32\n  ) {\n    throw new TypeError(\n      'Expected `cidr` number to be a positive integer between 1 and 32, representing the number of available bits in the parent CIDR block',\n    );\n  }\n\n  if (\n    typeof neededSubnets !== 'number' ||\n    Number.isNaN(neededSubnets) ||\n    !Number.isInteger(neededSubnets) ||\n    neededSubnets < 1\n  ) {\n    throw new TypeError(`Expected \"neededSubnets\" to be a positive integer.`);\n  }\n\n  if (neededSubnets > 2 ** availableSpace) {\n    throw new TypeError(\n      `The number of subnets needed (${neededSubnets.toLocaleString()}) exceeds the available space in the parent CIDR block (${(2 ** availableSpace).toLocaleString()})`,\n    );\n  }\n  return {\n    parentCidr,\n  };\n}\n","import { Cidr, IpRange } from 'cidr-calc';\nimport { UniformlyDistributedSubnetsArguments } from './types.js';\nimport { validate } from './validate.js';\n\n/**\n * Allocate subnet CIDRs as uniformly as possible across the available space.\n */\nexport function uniformlyDistributedSubnets({\n  neededSubnets,\n  cidr,\n}: UniformlyDistributedSubnetsArguments): {\n  subnetCidrs: Cidr[];\n  optimalSubnetCidrPrefixLength: number;\n  maxIpsPerSubnet: number;\n  parentCidr: Cidr;\n} {\n  const { parentCidr } = validate({ neededSubnets, cidr });\n\n  /**\n   * Where x is the optimal subnet CIDR number\n   *       y is parentCidrNumber\n   *       z is neededSubnets\n   *\n   * Theoretical max IPs = 2^(32-y) / z\n   * Optimal CIDR number IP space = 2^(32-x)\n   *\n   * 2^(32-x) = 2^(32-y) / z\n   *        x = 32 - log2(2^(32-y) / z)\n   *\n   * but we can only do the closest power of 2, so we need to round up `x` to get the CIDR number closest to the theoretical max.\n   *\n   * x = ceil(32 - log2(2^(32-y) / z))\n   */\n  const optimalSubnetCidrPrefixLength = Math.ceil(\n    32 - Math.log2(2 ** (32 - parentCidr.prefixLen) / neededSubnets),\n  );\n\n  // Get the actual subnet CIDR blocks needed\n  const subnetCidrs: Cidr[] = [];\n  let currentCidr: Cidr;\n  let currentRange: IpRange;\n  let nextStartIpAddr = parentCidr.toIpRange().startIpAddr;\n  for (let index = 0; index < neededSubnets; index++) {\n    currentCidr = new Cidr(nextStartIpAddr, optimalSubnetCidrPrefixLength);\n    currentRange = currentCidr.toIpRange();\n    subnetCidrs.push(currentCidr);\n    nextStartIpAddr = currentRange.endIpAddr.next();\n  }\n\n  const maxIpsPerSubnet = 2 ** (32 - optimalSubnetCidrPrefixLength);\n\n  return {\n    subnetCidrs,\n    optimalSubnetCidrPrefixLength,\n    maxIpsPerSubnet,\n    parentCidr,\n  };\n}\n","import assert from 'node:assert';\nimport { parseArgs } from 'node:util';\nimport { IpRange } from 'cidr-calc';\nimport { uniformlyDistributedSubnets } from '../src/uniformly-distributed-subnets.js';\nimport { validate } from '../src/validate.js';\n\n/**\n * @example\n * ```sh\n * pnpm start -c '10.113.0.0/16' -n 9\n * ```\n */\nfunction main() {\n  const {\n    values: { ['needed-subnets']: neededSubnetsRaw, cidr: cidrRaw },\n  } = parseArgs({\n    options: {\n      /** How many subnets you want in the network */\n      ['needed-subnets']: {\n        type: 'string',\n        short: 'n',\n      },\n      /** The CIDR of the parent network the subnets are going into */\n      cidr: {\n        type: 'string',\n        short: 'c',\n        description:\n          'The CIDR of the parent network the subnets are going into, e.g., 16',\n      },\n    },\n  });\n\n  assert(\n    neededSubnetsRaw !== undefined,\n    'Missing argument: --neededSubnets (or -n for short)',\n  );\n  const neededSubnets = Number(neededSubnetsRaw);\n  const cidr = cidrRaw ?? '0.0.0.0/16';\n  if (!cidrRaw) {\n    console.warn('WARNING: No CIDR provided, defaulting to', cidr, '\\n');\n  }\n\n  try {\n    validate({ neededSubnets, cidr });\n  } catch (error) {\n    if (error instanceof TypeError) {\n      console.error('Input error:', error.message);\n      return;\n    }\n    throw error;\n  }\n\n  const {\n    subnetCidrs,\n    optimalSubnetCidrPrefixLength,\n    maxIpsPerSubnet,\n    parentCidr,\n  } = uniformlyDistributedSubnets({\n    neededSubnets,\n    cidr,\n  });\n\n  const availableSpace = 32 - parentCidr.prefixLen;\n  const ipsInAvailableSpace = 2 ** availableSpace;\n  console.info(`Network IP range: ${parentCidr.toIpRange().toString()}`);\n  console.debug(\n    `Total IPs in network: ${ipsInAvailableSpace.toLocaleString()}`,\n  );\n  console.debug(\n    `Theoretical max IPs per subnet: ${Math.floor(2 ** availableSpace / neededSubnets).toLocaleString()}`,\n  );\n  console.info('');\n  console.info(\n    `Subnets' optimal CIDR number: /${optimalSubnetCidrPrefixLength.toString()}`,\n  );\n  console.info(`Max IPs per subnet: ${maxIpsPerSubnet.toLocaleString()}`);\n  console.info(`\\nSubnet CIDR blocks:`);\n  for (const [index, cidr] of subnetCidrs.entries()) {\n    const range = cidr.toIpRange();\n    console.info(\n      `Subnet ${(index + 1).toString()}: ${cidr.toString()}`.padEnd(28) +\n        ` (IP range: ${range.toString()})`,\n    );\n  }\n  console.info('');\n\n  const usedIps = maxIpsPerSubnet * neededSubnets;\n  const unusedIps = ipsInAvailableSpace - usedIps;\n  const percentageUsed = (usedIps / ipsInAvailableSpace) * 100;\n  const lastCidr = subnetCidrs.at(-1);\n  if (lastCidr && unusedIps) {\n    console.info(\n      `Unused IP range: ${new IpRange(lastCidr.toIpRange().endIpAddr.next(), parentCidr.toIpRange().endIpAddr).toString()}`,\n    );\n  }\n  console.info(`Number of unused IPs: ${unusedIps.toLocaleString()}`);\n  console.info(\n    `Number of used IPs:   ${usedIps.toLocaleString()} (${percentageUsed.toFixed(2)}%)`,\n  );\n}\n\nmain();\n"],"names":["Cidr","IpAddress","parseArgs","IpRange"],"mappings":"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"}