Files
arah2/web/index.html
T
araharah-executor 5c155e393d feat(03-04): Mock-Telegram runner page + Playwright capture harness — 7/7 captures + GEO-03 probe
- runner page: ?mock/?scheme/?pin/?locale profile block wholly inside if (__ARAHARAH_HARNESS__) — installer + /api/session + /api/location + /api/location-fallback fetch stubs dead-code-eliminated from production; main.ts import stays outside the guard
- harness-only ?sessiondelay/?fixdelay boot-stage photograph knobs (localhost resolves the chain in ms — staged BOOT-01/BOOT-02 cards need a hold)
- capture.mjs extends theme/scripts/screenshot.mjs: MIN_BYTES, WR-05 docroot guard, READY-signal race, dist-harness docroot, DENIED_LEG_TIMEOUT_MS=90000 for LINK 3's 60s pre-denial poll, timeout = hard failure with a signal diagnostic
- 7 captures: boot-1/boot-2 stages, live light+dark (theme-slot attribute asserted), Desktop denied card w/ ASK VIA CHAT, chat-pin relay LIVE, DSGN-02 side-by-side vs navigate_map_overview
- GEO-03 runtime probe: localStorage/sessionStorage/cookies/IndexedDB asserted empty after boot-to-live
- mock fixes (Rule 1): LocationManager delivery now uses the Telegram wire shape {latitude,longitude,horizontal_accuracy,course} (authoring shape mapped to undefined → NaN LngLat pageerror); languageCode→initData so localeFromInitData parses the real wire format

CAPTURE OK — 7/7 non-blank captures; GEO-03 probe PASSED
2026-10-23 00:37:58 +08:00

76 lines
4.0 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
<title>araharah</title>
</head>
<body>
<div id="app"></div>
<script type="module">
/* D-11 Mock-Telegram profile: ?mock=android|ios|tdesktop|macos|web + ?scheme=light|dark + ?pin=1 + ?locale=en|ms + ?fixdelay + ?sessiondelay
Harness-only: served from web/dist-harness, never deployed.
The WHOLE block below is inside `if (__ARAHARAH_HARNESS__)` — Vite's `define`
replaces that identifier with `false` in the production build, so the branch is
dead-code-eliminated and web/dist/index.html carries neither installMockWebApp
nor the fetch stubs (BA5 scans ALL of web/dist HTML as well as its JS assets).
No storage writes may appear here — the harness profile lives in a
module-scope global (window.__ARAHARAH_MOCK__), never in browser storage (D-07). */
if (__ARAHARAH_HARNESS__) {
const q = new URLSearchParams(location.search);
const profile = q.get("mock") || "android";
const scheme = q.get("scheme") === "dark" ? "dark" : "light";
const locale = q.get("locale") === "ms" ? "ms" : "en";
/* Boot-stage photography knobs (harness-only): at localhost speeds the
whole session→chain→fix sequence resolves in a few ms, so the staged
BOOT-01 (validating) and BOOT-02 (acquiring) cards cannot hold on
screen. ?sessiondelay=<ms> holds the validating card; ?fixdelay=<ms>
holds the acquiring card (a delayed LocationManager delivery).
Production and the plain dev fallback never pass either. */
const sessionDelayMs = Number.parseInt(q.get("sessiondelay") || "0", 10);
const fixDelayMs = Number.parseInt(q.get("fixdelay") || "0", 10);
// Set BEFORE main.ts is imported — this is the marker main.ts's install guard checks.
window.__ARAHARAH_MOCK__ = { profile, scheme, locale };
// Stub the endpoints the app calls, OUTSIDE any dead-code-eliminated branch. These
// live in the runner page (not in main.ts) precisely so no import.meta.env guard can
// strip them: the harness bundle must reach LIVE without a Worker in the loop.
const realFetch = window.fetch.bind(window);
const fakeToken = "00000000-0000-4000-8000-000000000001";
window.fetch = async (input, init) => {
const url = typeof input === "string" ? input : (input && input.url) || "";
if (url.includes("/api/session") && (!init || init.method === "POST")) {
// The session gate is HMAC-validated server-side; offline the harness
// short-circuits it with a fake token the stubbed /api/location also accepts.
if (sessionDelayMs > 0) await new Promise((r) => setTimeout(r, sessionDelayMs));
return new Response(JSON.stringify({ token: fakeToken }), { status: 200, headers: { "content-type": "application/json" } });
}
if (url.includes("/api/location") && (!init || init.method === "GET")) {
const body = q.get("pin") === "1"
? { pin: { latitude: 3.1513, longitude: 101.6958, horizontal_accuracy: 8 } }
: { pin: null };
return new Response(JSON.stringify(body), { status: 200, headers: { "content-type": "application/json" } });
}
if (url.includes("/api/location-fallback")) {
return new Response(JSON.stringify({ ok: true }), { status: 200, headers: { "content-type": "application/json" } });
}
return realFetch(input, init);
};
const { installMockWebApp } = await import("/src/mock/telegram-mock.ts");
installMockWebApp({
platform: profile,
colorScheme: scheme,
languageCode: locale,
grantLocation: profile !== "tdesktop",
deliveryDelayMs: fixDelayMs > 0 ? fixDelayMs : undefined,
telegramFix: { lat: 3.1513, lng: 101.6958, accuracyM: 12, headingDeg: null },
});
}
await import("/src/main.ts");
</script>
</body>
</html>