checkBios/src/bios.ts
2025-06-28 20:54:51 +02:00

123 lines
3.5 KiB
TypeScript

import { HTMLElement, parse } from "node-html-parser";
/**
* Represents a BIOS entry with version, date, changelog, and optional download URL.
*/
export class Bios {
/** BIOS release date (YYYY/MM/DD) */
readonly date: string;
/** BIOS version string */
readonly version: string;
/** BIOS changelog/description */
readonly changelog: string;
/** Optional download URL for the BIOS file */
readonly url?: string;
/**
* @param date - BIOS release date
* @param version - BIOS version string
* @param changelog - BIOS changelog/description
* @param url - Optional download URL
* @throws Error if required fields are missing
*/
constructor(
date: string,
version: string,
changelog: string,
url?: string,
) {
if (!date?.trim() || !version?.trim() || !changelog?.trim()) {
throw new Error(
"Required BIOS information missing: date, version, or changelog",
);
}
this.date = date.trim();
this.version = version.trim();
this.changelog = changelog.trim();
this.url = url?.trim();
}
/**
* Returns a formatted string representation of the BIOS entry.
*/
toString(): string {
return `${this.version} (${this.date}):\n ${this.changelog}\n ${this.url}`;
}
}
/**
* Fetches and parses BIOS information from a motherboard support page.
* @param mobo - URL of the motherboard support page
* @returns Promise containing array of Bios objects
* @throws Error if the URL is invalid or parsing fails
*/
export async function getBios(mobo: string): Promise<Bios[]> {
if (!mobo || !/^https?:\/\//.test(mobo)) {
throw new Error("Invalid motherboard URL: must start with http or https");
}
try {
const response = await fetch(mobo);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const body = await response.text();
const root = parse(body);
const biosList: Bios[] = [];
const rows = root.querySelectorAll("table>tbody>tr");
if (!rows.length) {
console.warn("No BIOS entries found in the table");
return biosList;
}
for (const tr of rows) {
try {
biosList.push(parseBiosRow(tr));
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
console.error("Failed to parse BIOS row:", message);
}
}
return biosList;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Failed to fetch BIOS information: ${message}`);
}
}
/**
* Parses a table row into a Bios object.
* @param tr - HTML table row element
* @returns Bios object
* @throws Error if the row format is invalid
*/
function parseBiosRow(tr: HTMLElement): Bios {
try {
const tds = tr.querySelectorAll("td");
if (tds.length < 6) {
throw new Error(
`Invalid table row format: expected >=6 columns, got ${tds.length}`,
);
}
// Try to resolve the link to an absolute URL if possible
const link = tds[5].querySelector("a")?.getAttribute("href");
let url: string | undefined = undefined;
if (link) {
try {
url = encodeURI(link).replace(/[(]/g, "%28").replace(/[)]/g, "%29");
} catch {
url = link;
}
}
return new Bios(
tds[1].text, // date
tds[0].text, // version
tds[4].text, // changelog
url,
);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
throw new Error(`Error parsing BIOS row: ${message}`);
}
}