cf-speedtest
Version:
CLI tool for testing internet speed using Cloudflare's network
181 lines (168 loc) • 5.11 kB
JavaScript
// src/constants/index.ts
var CLOUDFLARE_SPEEDTEST_HOSTNAME = "speed.cloudflare.com";
var FILE_SIZE = 1e8;
// src/logger/logger.ts
import chalk from "chalk";
var logServerLocation = (city, ip) => {
console.log(chalk.bold.green("\u{1F310} Server Location:"));
console.log(`${chalk.blue("City :")} ${chalk.green(city)}`);
console.log(`${chalk.blue("Public IP:")} ${chalk.green(ip)}`);
};
var logLatency = (stats) => {
const { average, packetLoss } = stats;
console.log(
chalk.bold.cyan("\u{1F3D3} Latency:"),
`${chalk.green(`${average.toFixed(2)}ms`)}`
);
console.log(
chalk.bold.red("\u{1F6AB} Packet Loss:"),
`${chalk.red(`${packetLoss.toFixed(2)}%`)}`
);
};
var logDownload = ({ average }) => {
console.log(
chalk.bold.yellow(
"\u2B07\uFE0F Download:",
`${chalk.green(average.toFixed(2), "Mbps")}`
)
);
};
var logUpload = ({ average }) => {
console.log(
chalk.bold.magenta("\u2B06\uFE0F Upload:"),
`${chalk.green(average.toFixed(2), "Mbps")}`
);
};
// src/measurements/measure-download.ts
import https from "https";
// src/utils/convert-to-mbps.ts
var convertToMbps = (bytes, duration) => bytes * 8 / (duration / 1e3) / 1e6;
// src/utils/measure-bandwidth.ts
var measureBandwidth = async (handler, bytes) => {
const start = performance.now();
const transferred = await handler(bytes);
const duration = performance.now() - start;
const average = convertToMbps(transferred, duration);
return { average };
};
// src/measurements/measure-transfer.ts
var measureTransfer = async (handler, bytes, logger) => {
const stats = await measureBandwidth(handler, bytes);
logger(stats);
return stats;
};
// src/measurements/measure-download.ts
var download = (bytes) => new Promise((resolve, reject) => {
const options = {
hostname: CLOUDFLARE_SPEEDTEST_HOSTNAME,
path: `/__down?bytes=${bytes}`,
method: "GET"
};
const req = https.request(options, (res) => {
if (res.statusCode !== 200) {
reject(new Error(`Download failed with status ${res.statusCode}`));
return;
}
let total = 0;
res.on("data", (chunk) => {
total += chunk.length;
});
res.on("end", () => {
resolve(total);
});
});
req.on("error", (error) => reject(new Error(error.message)));
req.end();
});
var measureDownload = (bytes) => measureTransfer(download, bytes, logDownload);
// src/measurements/measure-latency.ts
var measureLatency = async (runs = 20) => {
const url = "https://speed.cloudflare.com/cdn-cgi/trace";
const measurements = [];
for (let i = 0; i < runs; i++) {
try {
const start = performance.now();
await fetch(url, { method: "HEAD" });
const duration = performance.now() - start;
measurements.push(duration);
} catch (error) {
console.error("Error measuring latency:", error);
}
}
const latencies = measurements;
const sum = latencies.reduce((acc, latency) => acc + latency, 0);
const average = sum / latencies.length;
const diffs = latencies.slice(1).map((v, i) => Math.abs(v - (latencies[i] ?? 0)));
const jitter = diffs.length ? diffs.reduce((acc, diff) => acc + diff, 0) / diffs.length : 0;
const lost = runs - measurements.length;
const packetLoss = lost / runs * 100;
return { average, jitter, packetLoss, measurements: latencies };
};
// src/measurements/measure-upload.ts
import { Buffer } from "buffer";
var measureUpload = (bytes) => measureTransfer(
async (bytes2) => {
const body = Buffer.allocUnsafe(bytes2);
const res = await fetch(
`https://${CLOUDFLARE_SPEEDTEST_HOSTNAME}/__up?bytes=${bytes2}`,
{
method: "POST",
body
}
);
if (!res.ok) {
throw new Error(`Upload failed with status ${res.status}`);
}
return bytes2;
},
bytes,
logUpload
);
// src/utils/connection-metadata.ts
var parseToJson = (text) => {
const lines = text.split("\n");
const result = {};
for (const line of lines) {
const [key, value] = line.split("=");
if (key && value) {
result[key] = value;
}
}
return result;
};
var connectionMetadata = async () => {
const res = await fetch(
`https://${CLOUDFLARE_SPEEDTEST_HOSTNAME}/cdn-cgi/trace`
).then((res2) => res2.text());
return parseToJson(res);
};
// src/utils/get-city.ts
var getCity = async (iata) => {
const res = await fetch(`https://${CLOUDFLARE_SPEEDTEST_HOSTNAME}/locations`);
if (!res.ok) {
throw new Error(res.statusText);
}
const serverLocation = await res.json();
const matchedLocation = serverLocation.find(
(location) => location.iata === iata
);
if (matchedLocation) {
return matchedLocation.city;
}
return "Unknown Location";
};
// src/index.ts
var runCLI = async () => {
const [latencyStats, metadata] = await Promise.all([
measureLatency(),
connectionMetadata()
]);
const { colo, ip } = metadata;
const city = await getCity(colo);
logServerLocation(city, ip);
logLatency(latencyStats);
await Promise.all([measureDownload(FILE_SIZE), measureUpload(FILE_SIZE)]);
};
export {
runCLI
};