feat(03-02): D-11 Mock-Telegram harness + the four requirement gates (17 cases green)

- telegram-mock.ts: fake window.Telegram.WebApp injected into the SAME window
  (never an iframe — Bot API 10.2 origin restriction, Pitfall 6); Web-Crypto
  dev signer mirroring bot/src/security/initdata.ts's two-step HMAC (async
  makeDevInitData — node:crypto stays out of the browser bundle path);
  controllable LocationManager stream, themeChanged emitter, BackButton clicks
- location-chain.test.mjs (10 cases): GEO-01 version gate / null-WebApp guard
  / null-denial / hard timeout / stale-detector + one-read nudge; GEO-02 pin
  pickup / bounded 60s poll / Desktop no-geolocation leg ' same denial keys
- no-storage.test.mjs (3 cases): GEO-03 comment-stripped grep gate over
  web/src/**/*.{ts,html}, no-SW invariant, store.ts storage-assignment probe
- theme.test.mjs (4 cases): PX-02 colorScheme + luminance fallback + setStyle
  source-introspection (Pitfall 7) + themeChanged re-apply; document stub
  installed and restored per case
- Rule 1 fix in telegram.ts: looksStale/nudgeAndRetryTelegram accept an
  omitted lastAccepted (undefined normalized to null) ' acquireFix(deps)
  first-call contract
This commit is contained in:
araharah-executor
2026-10-22 23:46:17 +08:00
parent 0a35bce256
commit 2659b7f352
5 changed files with 687 additions and 3 deletions
+5 -3
View File
@@ -70,8 +70,10 @@ export async function requestTelegramFix(
* of the never-refreshing native cache. Only telegram-vs-telegram pairs
* qualify — different sources bounding identically is coincidence, not stale.
*/
export function looksStale(prev: Fix | null, next: Fix): boolean {
if (prev === null || prev.source !== "telegram" || next.source !== "telegram") return false;
export function looksStale(prev: Fix | null | undefined, next: Fix): boolean {
// undefined is normalized to null: acquireFix(deps) with no second
// argument is the documented "first fix of the session" call.
if (prev === null || prev === undefined || prev.source !== "telegram" || next.source !== "telegram") return false;
return haversineMeters(prev, next) < 0.01;
}
@@ -97,7 +99,7 @@ export function haversineMeters(a: Fix, b: Fix): number {
export async function nudgeAndRetryTelegram(
wa: TelegramWebAppLike,
readWebviewFix: (o?: { timeoutMs?: number }) => Promise<Fix | null>,
lastAccepted: Fix | null,
lastAccepted: Fix | null | undefined,
options: { timeoutMs?: number; nowMs?: () => number } = {},
): Promise<Fix | null> {
const first = await requestTelegramFix(wa, options);