import { readFileSync } from "node:fs"; import { createServer } from "node:http"; import { isAbsolute, join, resolve } from "node:path"; import { chromium } from "@playwright/test"; function option(name) { const index = process.argv.indexOf(name); return index >= 0 ? process.argv[index + 1] : undefined; } function defaultConfigPath() { if (!process.env.LOCALAPPDATA || !isAbsolute(process.env.LOCALAPPDATA)) { throw new Error("local_app_data_unavailable"); } return join(process.env.LOCALAPPDATA, "Dada", "P0A", "config", "instance.json"); } const manifestPath = resolve(option("--manifest") ?? "config/runtime-assets-manifest.json"); const configPath = resolve(option("--config") ?? process.env.DADA_INSTANCE_CONFIG_PATH ?? defaultConfigPath()); const manifest = JSON.parse(readFileSync(manifestPath, "utf8")); const configuration = JSON.parse(readFileSync(configPath, "utf8")); if (typeof configuration.asset_root !== "string" || !isAbsolute(configuration.asset_root)) { throw new Error("asset_root_configuration_invalid"); } const fontEntries = manifest.entries .filter((entry) => entry.assetId.startsWith("TEXT-FONT-") && entry.mimeType.startsWith("font/")) .toSorted((left, right) => left.assetId.localeCompare(right.assetId)); const uniqueEntries = [...new Map(fontEntries.map((entry) => [entry.sha256, entry])).values()]; const fontPaths = new Map( uniqueEntries.map((entry) => [ `/${encodeURIComponent(entry.assetId)}`, { mimeType: entry.mimeType, path: join(configuration.asset_root, entry.relativePath) }, ]), ); const server = createServer((request, response) => { const font = fontPaths.get(request.url ?? ""); if (!font) { response.writeHead(200, { "Content-Type": "text/html; charset=utf-8" }); response.end("Dada browser font validation"); return; } const bytes = readFileSync(font.path); response.writeHead(200, { "Content-Length": bytes.length, "Content-Type": font.mimeType }); response.end(bytes); }); await new Promise((resolveReady) => server.listen(0, "127.0.0.1", resolveReady)); const address = server.address(); if (!address || typeof address === "string") throw new Error("browser_font_probe_server_unavailable"); const origin = `http://127.0.0.1:${address.port}`; const browser = await chromium.launch({ channel: option("--channel") ?? "msedge", headless: true }); const failures = []; try { let page = await browser.newPage(); await page.goto(origin); for (let index = 0; index < uniqueEntries.length; index += 1) { if (index > 0 && index % 25 === 0) { await page.close(); page = await browser.newPage(); await page.goto(origin); } const entry = uniqueEntries[index]; const result = await page.evaluate(async ({ family, url }) => { try { const response = await fetch(url); if (!response.ok) return `http_${response.status}`; await new FontFace(family, await response.arrayBuffer()).load(); return "loaded"; } catch (error) { return error instanceof Error ? `${error.name}: ${error.message}` : String(error); } }, { family: `DadaFontProbe${index}`, url: `${origin}/${encodeURIComponent(entry.assetId)}` }); if (result !== "loaded") failures.push({ asset_id: entry.assetId, error: result }); } } finally { await browser.close(); await new Promise((resolveClosed) => server.close(resolveClosed)); } const result = { checked_entries: fontEntries.length, failed_fonts: failures, status: failures.length === 0 ? "passed" : "failed", unique_fonts: uniqueEntries.length, }; process.stdout.write(`${JSON.stringify(result)}\n`); if (failures.length > 0) process.exitCode = 1;