{"version":3,"file":"index.cjs","sources":["../src/index.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nexport interface PackageData {\n  name?: string;\n  [key: string]: unknown;\n}\n\nexport interface ImportMap {\n  imports: Record<string, string>;\n  [key: string]: unknown;\n}\n\nexport interface PortCollision {\n  port: number;\n  packages: string[];\n}\n\nexport interface ImportMapAnalysis {\n  ports: Record<number, string[]>;\n  entries: Record<string, number>;\n}\n\nfunction primeHash(\n  name: string,\n  primes: [number, ...number[]],\n  initial?: number,\n): number {\n  let hash = initial ?? name.length;\n\n  for (let i = 0; i < name.length; i++) {\n    // biome-ignore lint/style/noNonNullAssertion: using the modulo operator on the length guarantees a valid index\n    const prime = primes[i % primes.length]!;\n    hash = (Math.imul(hash, prime) + name.charCodeAt(i)) >>> 0;\n  }\n\n  return hash;\n}\n\nexport const HASH = {\n  sdbm: (name: string): number => primeHash(name, [65599]),\n  twin: (name: string): number => primeHash(name, [31, 37]),\n  double: (name: string): number => {\n    const hash1 = primeHash(name, [31]) % 2147483647;\n    const hash2 = primeHash(name, [37], 1) % 2147483647;\n    const combined = (hash1 ^ (hash2 << 1)) >>> 0;\n    const phi = 2654435761;\n    return Math.imul(combined, phi) >>> 0;\n  },\n} as const;\n\nexport const REDUCERS = {\n  modulo: (hash: number, range: number): number => hash % range,\n  knuth: (hash: number, range: number): number => {\n    const constant = 2654435769;\n    const multiplied = Math.imul(hash, constant) >>> 0;\n    return (multiplied >>> (32 - Math.ceil(Math.log2(range)))) % range;\n  },\n  lcg: (hash: number, range: number): number => {\n    const a = 1664525;\n    const c = 1013904223;\n    const result = (Math.imul(a, hash) + c) >>> 0;\n    return Math.floor((result / 0x100000000) * range);\n  },\n} as const;\n\n// Extract types for hash functions and reducers\nexport type HashFunction = keyof typeof HASH;\nexport type ReducerFunction = keyof typeof REDUCERS;\n\n/**\n * Generate a stable, unique development port based on a package name\n */\nexport function getPort(\n  packageName: string,\n  basePort = 3001,\n  range = 1997,\n  hash: HashFunction = 'twin',\n  reducer: ReducerFunction = 'knuth',\n): number {\n  if (!packageName || typeof packageName !== 'string') {\n    throw new Error('Package name must be a non-empty string');\n  }\n\n  if (basePort < 1024 || basePort > 65535) {\n    throw new Error('Base port must be between 1024 and 65535');\n  }\n\n  if (range < 1 || range > 10000) {\n    throw new Error('Port range must be between 1 and 10000');\n  }\n\n  // Get functions with fallbacks\n  const hashFunction = HASH[hash] ?? HASH.twin;\n  const reducerFunction = REDUCERS[reducer] ?? REDUCERS.knuth;\n\n  // Generate and return port\n  const hashValue = hashFunction(packageName);\n  const offset = reducerFunction(hashValue, range);\n  const port = basePort + offset;\n\n  // Safety check: ensure the generated port is within the expected range\n  if (port < basePort || port >= basePort + range) {\n    throw new Error(\n      `Hash '${hash}' + Reducer '${reducer}' generated invalid port ${port}. ` +\n        `Expected range: ${basePort} to ${basePort + range - 1}. ` +\n        `Offset: ${offset}, Range: ${range}. ` +\n        `This indicates a bug in the implementation.`,\n    );\n  }\n\n  return port;\n}\n\nexport async function getPackageName(packageJsonPath?: string) {\n  const pkgPath = packageJsonPath || path.join(process.cwd(), 'package.json');\n\n  const packageContent = await fs.readFile(pkgPath, 'utf8');\n  const packageData = JSON.parse(packageContent) as PackageData;\n\n  if (!packageData.name) {\n    throw new Error('package.json must have a \"name\" field');\n  }\n\n  return packageData.name;\n}\n\n/**\n * Determine if a string is a URL\n */\nfunction isUrl(source: string): boolean {\n  return source.startsWith('http://') || source.startsWith('https://');\n}\n\nexport async function parseImportMap(source: string): Promise<ImportMap> {\n  let importMapData: ImportMap;\n\n  if (isUrl(source)) {\n    const response = await fetch(source);\n    importMapData = await response.json();\n  } else {\n    const content = await fs.readFile(source, 'utf8');\n    importMapData = JSON.parse(content);\n  }\n\n  if (!importMapData.imports || typeof importMapData.imports !== 'object') {\n    throw new Error('Import map must have an \"imports\" object');\n  }\n\n  return importMapData;\n}\n\n/**\n * Analyze an import map file and generate ports for all entries\n * @param importMap - The import map object or path to the JSON file\n * @param basePort - The base port to start from (default: 3001)\n * @param range - The range of ports to use (default: 1997)\n * @param hash - The hash function to use (default: 'twin')\n * @param reducer - The reducer function to use (default: 'knuth')\n * @returns Analysis results including ports, collisions, and distribution\n */\nexport async function analyzeImportMap(\n  importMap: string | ImportMap,\n  basePort = 3001,\n  range = 1997,\n  hash: HashFunction = 'twin',\n  reducer: ReducerFunction = 'knuth',\n): Promise<ImportMapAnalysis> {\n  const importMapBody =\n    typeof importMap === 'string' ? await parseImportMap(importMap) : importMap;\n\n  // Generate port entries for all packages\n  const entries: Record<string, number> = {};\n  for (const packageName of Object.keys(importMapBody.imports)) {\n    entries[packageName] = getPort(packageName, basePort, range, hash, reducer);\n  }\n\n  // Group packages by port for collision detection\n  const ports: Record<number, string[]> = {};\n  for (const [packageName, port] of Object.entries(entries)) {\n    ports[port] = ports[port] ?? [];\n    ports[port].push(packageName);\n  }\n\n  return { entries, ports };\n}\n"],"names":[],"mappings":"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"}