theseus/dev/probe-site.mjs

66 lines
3 KiB
JavaScript
Raw Normal View History

// Dev harness — exercise Theseus's `s3` content path (serveBns) WITHOUT Electron.
// Faithfully replicates what the browser triggers for a Sia site: resolve the
// name, fetch the index through the public gateway exactly as serveBns does,
// strip the gateway's <base>, then fetch every referenced same-origin asset the
// same way and report which load. This is the headless version of manual test
// #2 (Sia-via-gateway rendering).
//
// Usage: node dev/probe-site.mjs <name> e.g. coinspectrum.deviant.bch
import WebSocket from "ws";
import { resolveName } from "../../Argus/src/lib/resolver-web.js";
const GATEWAY = "https://navigate.st"; // must match main.js
const host = (process.argv[2] || "coinspectrum.deviant.bch").toLowerCase();
// mirror of serveBns's s3 fetch for one request path
async function gw(reqPath) {
const r = await fetch(`${GATEWAY}/bns/${host}${reqPath}`);
const ct = r.headers.get("content-type") || "";
const buf = Buffer.from(await r.arrayBuffer());
return { status: r.status, ct, buf };
}
const stripBase = (html) => html.replace(/<base\s+href="\/bns\/[^"]*">/i, "");
// same-origin STATIC asset refs a browser would request from bns://host/.
// Strip <script>/<style> bodies first so we don't match template literals or
// URLs built at runtime (those need a real render to verify — see selftest.js).
function assetPaths(html) {
const stripped = html
.replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, "")
.replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, "");
const out = new Set();
const re = /(?:href|src)\s*=\s*["']([^"']+)["']/gi;
let m;
while ((m = re.exec(stripped))) {
const u = m[1].trim();
if (!u || u.startsWith("#") || u.startsWith("data:") || u.startsWith("mailto:")) continue;
if (/^[a-z]+:\/\//i.test(u) || u.startsWith("//")) continue; // external
if (u.includes("${") || u.includes("{{")) continue; // runtime-built
out.add(u.startsWith("/") ? u : "/" + u.replace(/^\.?\//, ""));
}
return [...out];
}
console.log(`\n=== probe ${host} ===`);
const entry = await resolveName(host, { WebSocket });
if (!entry) { console.log("NXDOMAIN — not registered"); process.exit(1); }
console.log("records:", JSON.stringify(entry.records));
if (!entry.records.s3) { console.log("(not an s3 site — this harness targets s3)"); process.exit(0); }
const idx = await gw("/");
const wasBase = /<base\s+href="\/bns\//i.test(idx.buf.toString("utf8"));
const html = stripBase(idx.buf.toString("utf8"));
console.log(`index: HTTP ${idx.status} ${idx.ct} base-injected-by-gateway=${wasBase} (stripped)`);
const assets = assetPaths(html);
console.log(`\nassets referenced: ${assets.length}`);
let fail = 0;
for (const p of assets) {
const a = await gw(p);
const ok = a.status >= 200 && a.status < 300;
if (!ok) fail++;
console.log(` ${ok ? "OK " : "FAIL"} ${String(a.status).padEnd(3)} ${a.ct.split(";")[0].padEnd(24)} ${p}`);
}
console.log(`\nresult: ${assets.length - fail}/${assets.length} assets loaded, ${fail} failed`);
process.exit(fail ? 2 : 0);