import { mkdirSync } from 'node:fs' import { getNetworkTargets } from './network' const CERT_PATH = './certs/cert.pem' const KEY_PATH = './certs/key.pem' export interface LocalServerUrls { primaryUrl: string urls: string[] } export function getLocalServerUrls(port: number): LocalServerUrls { const scheme = isLocalTlsEnabled() ? 'https' : 'http' const { primaryHost, urls } = getNetworkTargets(port, scheme) return { primaryUrl: `${scheme}://${primaryHost}:${port}`, urls, } } export async function getLocalTlsOptions(_port: number) { if (!isLocalTlsEnabled()) return await ensureLocalTlsFiles() return { cert: Bun.file(CERT_PATH), key: Bun.file(KEY_PATH), } } export function isLocalTlsEnabled() { return process.platform === 'darwin' } async function ensureLocalTlsFiles() { const certExists = await Bun.file(CERT_PATH).exists() const keyExists = await Bun.file(KEY_PATH).exists() if (certExists && keyExists) return const opensslCheck = Bun.spawnSync(['which', 'openssl'], { stdout: 'ignore', stderr: 'ignore', }) if (opensslCheck.exitCode !== 0) { throw new Error('openssl is required for local macOS HTTPS mode') } mkdirSync('./certs', { recursive: true }) const proc = Bun.spawnSync([ 'openssl', 'req', '-x509', '-newkey', 'rsa:2048', '-keyout', KEY_PATH, '-out', CERT_PATH, '-days', '365', '-nodes', '-subj', '/CN=localhost', ], { stdout: 'ignore', stderr: 'pipe', }) if (proc.exitCode !== 0) { throw new Error(`failed to generate local TLS certificate: ${proc.stderr.toString().trim()}`) } }