feat(06-08): the ?drive= fixture-replay knob — the D-16 browser leg's fix source
- ?drive=<name> replaces navigator.geolocation with a replay source
streaming a committed drive fixture (web/test/fixtures/drives/*.json)
through the LIVE shell: the real pump arms the real
createGuidanceWatch against it — no test-only bypass of validation
- the replay clock starts lazily at the first watchPosition arm (the
route draw — nothing emitted before a consumer exists) and plays
REAL-TIME: the machine's grace/storm clocks read wall time
- window.__ARAHARAH_DRIVE = { pause(), resume(), name }: pause freezes
emission mid-stream (the capture legs' freeze for scripted taps and
the REROUTING window), resume continues; the machine's wall clock
keeps running across a pause (documented harness semantics)
- unknown drive name -> one honest token (ARAHARAH-DRIVE
state=unknown) + the default geolocation stays — never a crash,
never arbitrary data (T-06-08 Tampering mitigation); slug guard on
the name, defensive fixture shape check
- import.meta.glob pattern is ROOT-ABSOLUTE (/test/fixtures/drives/*.json
— Vite 8's importGlob matches no relative pattern escaping src/,
verified by the dev-server probe); fixtures inline into the harness
bundle only — production tree-shake verified clean in BOTH directions
- BA5 posture held: production scan clean, harness bundle carries the
replay; 147/147 web tests, typecheck clean
This commit is contained in:
+1
-1
@@ -9,7 +9,7 @@
|
||||
<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 + ?dest=<id> + ?fixpair=<pair-id>
|
||||
/* D-11 Mock-Telegram profile: ?mock=android|ios|tdesktop|macos|web + ?scheme=light|dark + ?pin=1 + ?locale=en|ms + ?fixdelay + ?sessiondelay + ?dest=<id> + ?fixpair=<pair-id> + ?drive=<fixture-name> (06-08: the drive replay is read inside the mock module — installMockWebApp replaces navigator.geolocation with the committed fixture's stream)
|
||||
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
|
||||
|
||||
@@ -29,6 +29,25 @@
|
||||
* tests read (the `__events` / `__backButtonClicks` exposure precedent).
|
||||
* The mock's initData is HMAC-unvalidated by design — the runner page
|
||||
* stubs `/api/session`.
|
||||
*
|
||||
* The ?drive= replay knob (06-08, D-16's browser leg): `?drive=<name>` makes
|
||||
* THIS mock replace navigator.geolocation with a replay source that streams
|
||||
* a committed drive fixture (web/test/fixtures/drives/*.json — the same JSON
|
||||
* the Node replays prove the brain with) through the LIVE shell — the real
|
||||
* pump, real builders, real camera consume the fixes via the real
|
||||
* createGuidanceWatch. The replay clock starts lazily at the FIRST
|
||||
* watchPosition arm (the route draw — the pump's ensureWatch), so no fix is
|
||||
* ever emitted before something consumes it, and plays REAL-TIME: the
|
||||
* machine's grace/storm clocks read wall time (route.ts's now()), so a
|
||||
* compressed playback would desync them. window.__ARAHARAH_DRIVE.pause()/
|
||||
* resume() freeze/resume emission mid-stream — the capture script's freeze
|
||||
* for stable scripted taps (confirm/tripend legs) and the REROUTING leg's
|
||||
* window. A pause stops EMISSION only; the shell's wall clock keeps running
|
||||
* (grace windows inflate across a pause — legs that pause close before any
|
||||
* machine timing matters). Console emits NOTHING per fix; an unknown drive
|
||||
* name is the knob's one honest token (`ARAHARAH-DRIVE state=unknown`) and
|
||||
* falls back to the default geolocation — never a crash, never replayed
|
||||
* arbitrary data (the fixture/kernel mismatch mitigation, T-06-08).
|
||||
*/
|
||||
import type { TelegramWebAppLike, HomeScreenStatus } from "../telegram/webapp.ts";
|
||||
|
||||
@@ -162,6 +181,189 @@ function buildMockInitData(languageCode?: string, startParam?: string | null): s
|
||||
return startParam ? `${base}&start_param=${encodeURIComponent(startParam)}` : base;
|
||||
}
|
||||
|
||||
/* ————— the ?drive= fixture-replay source (06-08, D-16's browser leg) ————— */
|
||||
|
||||
/** A committed drive fixture's structural minimum (the 06-02 replay shape:
|
||||
* { _notice, pairId, maneuverIdx, kinds, fixes[] } with Fix-shaped fixes). */
|
||||
interface DriveFixture {
|
||||
fixes: Array<{ lat: number; lng: number; accuracyM?: number | null; headingDeg?: number | null; ts: number; source?: string }>;
|
||||
}
|
||||
|
||||
/** The pause/resume surface the capture script drives (window.__ARAHARAH_DRIVE). */
|
||||
export interface DriveReplayControls {
|
||||
/** Freeze the replay clock mid-stream — emission stops until resume(). */
|
||||
pause(): void;
|
||||
/** Continue mid-stream: unpaused wall time resumes driving the schedule. */
|
||||
resume(): void;
|
||||
readonly name: string;
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
/** Installed ONLY when ?drive= names a committed fixture — the capture
|
||||
* script's freeze surface (and its diagnostics: .name). */
|
||||
__ARAHARAH_DRIVE?: DriveReplayControls;
|
||||
}
|
||||
}
|
||||
|
||||
/** Drive names are slugs (the pairId/destinationId pattern — bounds the
|
||||
* lookup against hostile query strings before the fixture record is read). */
|
||||
const DRIVE_NAME_PATTERN = /^[a-z0-9-]{1,64}$/;
|
||||
|
||||
/** The committed D-16 drive replays, inlined into the HARNESS bundle at build
|
||||
* time (eager glob, the route-profiles BA-exclusion posture: this module is
|
||||
* imported by main.ts's dead harness branch and tree-shaken from production —
|
||||
* BA5 asserts the absence). The pattern is ROOT-ABSOLUTE (/test/... — Vite 8's
|
||||
* importGlob matches no relative pattern that escapes src/; verified by the
|
||||
* 06-08 dev-server probe). */
|
||||
const DRIVE_FIXTURES = import.meta.glob<unknown>("/test/fixtures/drives/*.json", {
|
||||
eager: true,
|
||||
import: "default",
|
||||
});
|
||||
|
||||
/** Defensive shape check (the fixture-source isUsablePair posture): a
|
||||
* malformed drive degrades to the honest unknown-fallback, never a crash
|
||||
* and never a partial replay (T-06-08's Tampering mitigation). */
|
||||
function isDriveFixture(value: unknown): value is DriveFixture {
|
||||
if (value === null || typeof value !== "object") return false;
|
||||
const fixes = (value as { fixes?: unknown }).fixes;
|
||||
if (!Array.isArray(fixes) || fixes.length < 2) return false;
|
||||
return fixes.every((f) => {
|
||||
if (f === null || typeof f !== "object") return false;
|
||||
const p = f as { lat?: unknown; lng?: unknown; ts?: unknown };
|
||||
return (
|
||||
typeof p.lat === "number" &&
|
||||
Number.isFinite(p.lat) &&
|
||||
typeof p.lng === "number" &&
|
||||
Number.isFinite(p.lng) &&
|
||||
typeof p.ts === "number" &&
|
||||
Number.isFinite(p.ts)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function driveFixtureByName(name: string): DriveFixture | null {
|
||||
if (!DRIVE_NAME_PATTERN.test(name)) return null;
|
||||
const hit = DRIVE_FIXTURES[`/test/fixtures/drives/${name}.json`];
|
||||
return isDriveFixture(hit) ? hit : null;
|
||||
}
|
||||
|
||||
/** Position assembled from a fixture fix at a replayed wall timestamp. The
|
||||
* watch (createGuidanceWatch) normalizes exactly the four consumed fields;
|
||||
* the GeolocationPosition type's remaining members are cast — the harness
|
||||
* source is the fixture itself, not a lying platform. */
|
||||
function replayedPosition(f: DriveFixture["fixes"][number], timestamp: number): GeolocationPosition {
|
||||
return {
|
||||
coords: {
|
||||
latitude: f.lat,
|
||||
longitude: f.lng,
|
||||
accuracy: f.accuracyM ?? undefined,
|
||||
altitude: null,
|
||||
altitudeAccuracy: null,
|
||||
altitudeAngle: null,
|
||||
heading: f.headingDeg ?? undefined,
|
||||
speed: null,
|
||||
},
|
||||
timestamp,
|
||||
} as unknown as GeolocationPosition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replace navigator.geolocation with the drive replay (harness-only): the
|
||||
* REAL pump arms its REAL watch against this object, so the shell consumes
|
||||
* the committed stream through the unmodified D-15 throttle — replay proves
|
||||
* the shell, never bypassing it (the trust boundary's law).
|
||||
*
|
||||
* Clock semantics: the schedule is real-time (offset = fix.ts − fixes[0].ts;
|
||||
* a 200ms tick delivers every fix whose offset has elapsed). pos.timestamp
|
||||
* is t0 + offset so the machine's now()-anchored windows stay aligned with
|
||||
* the fixes that drive them. Exhaustion stops the timer; a later re-arm
|
||||
* delivers nothing (the drive is over).
|
||||
*/
|
||||
function installDriveReplay(name: string, fixture: DriveFixture): DriveReplayControls {
|
||||
const fixes = fixture.fixes;
|
||||
const first = fixes[0]!;
|
||||
const offsets = fixes.map((f) => Math.max(0, f.ts - first.ts));
|
||||
let clockStart: number | null = null; // performance.now() at the first watch arm
|
||||
let wallT0 = 0; // Date.now() at the same instant — pos.timestamp = wallT0 + offset
|
||||
let pausedAt: number | null = null;
|
||||
let pausedTotal = 0;
|
||||
let delivered = 0;
|
||||
let nextId = 0;
|
||||
let timer: ReturnType<typeof setInterval> | null = null;
|
||||
const sinks = new Map<number, (pos: GeolocationPosition) => void>();
|
||||
|
||||
const elapsedMs = (): number => {
|
||||
if (clockStart === null) return -1;
|
||||
const nowP = performance.now();
|
||||
return (nowP - clockStart) - pausedTotal - (pausedAt !== null ? nowP - pausedAt : 0);
|
||||
};
|
||||
const stopTimer = (): void => {
|
||||
if (timer !== null) {
|
||||
clearInterval(timer);
|
||||
timer = null;
|
||||
}
|
||||
};
|
||||
const tick = (): void => {
|
||||
const e = elapsedMs();
|
||||
if (e < 0) return;
|
||||
while (delivered < fixes.length) {
|
||||
const offset = offsets[delivered]!;
|
||||
if (offset > e) break;
|
||||
const fix = fixes[delivered]!;
|
||||
const pos = replayedPosition(fix, wallT0 + offset);
|
||||
for (const sink of sinks.values()) sink(pos);
|
||||
delivered += 1;
|
||||
}
|
||||
if (delivered >= fixes.length) stopTimer(); // fully replayed — the drive is over
|
||||
};
|
||||
const ensureTimer = (): void => {
|
||||
if (timer === null && delivered < fixes.length) timer = setInterval(tick, 200);
|
||||
};
|
||||
|
||||
const fakeGeo = {
|
||||
getCurrentPosition(success: PositionCallback, _error?: PositionErrorCallback | null, _opts?: PositionOptions): void {
|
||||
// the chain's LINK-2 shape (unused while the Telegram mock serves LINK 1):
|
||||
// the drive's FIRST fix, at wall-now, like a real one-shot read
|
||||
const f = first;
|
||||
setTimeout(() => success(replayedPosition(f, Date.now())), 0);
|
||||
},
|
||||
watchPosition(success: PositionCallback, _error?: PositionErrorCallback | null, _opts?: PositionOptions): number {
|
||||
const id = ++nextId;
|
||||
sinks.set(id, success);
|
||||
if (clockStart === null) {
|
||||
clockStart = performance.now();
|
||||
wallT0 = Date.now();
|
||||
}
|
||||
ensureTimer(); // the first arm starts the clock; re-arms resume delivery
|
||||
return id;
|
||||
},
|
||||
clearWatch(id: number): void {
|
||||
sinks.delete(id);
|
||||
if (sinks.size === 0) stopTimer(); // the pump stopped the watch — the replay stops with it
|
||||
},
|
||||
};
|
||||
Object.defineProperty(navigator, "geolocation", { value: fakeGeo, configurable: true });
|
||||
|
||||
const controls: DriveReplayControls = {
|
||||
get name(): string {
|
||||
return name;
|
||||
},
|
||||
pause(): void {
|
||||
if (pausedAt === null) pausedAt = performance.now();
|
||||
},
|
||||
resume(): void {
|
||||
if (pausedAt !== null) {
|
||||
pausedTotal += performance.now() - pausedAt;
|
||||
pausedAt = null;
|
||||
}
|
||||
ensureTimer();
|
||||
},
|
||||
};
|
||||
window.__ARAHARAH_DRIVE = controls;
|
||||
return controls;
|
||||
}
|
||||
|
||||
export function installMockWebApp(options: MockOptions = {}): TelegramWebAppLike {
|
||||
const version = options.version ?? "8.0";
|
||||
const telegramFix = options.telegramFix ?? null;
|
||||
@@ -250,6 +452,22 @@ export function installMockWebApp(options: MockOptions = {}): TelegramWebAppLike
|
||||
installed = fake;
|
||||
const w = window as unknown as { Telegram?: unknown };
|
||||
w.Telegram = { WebApp: fake };
|
||||
// The ?drive= replay knob (06-08): read from location.search HERE — the
|
||||
// mock module is the harness surface, so the knob rides every install path
|
||||
// (the runner page's profile install AND main.ts's plain-dev fallback)
|
||||
// without a new carrier. Unknown name → one honest token + the default
|
||||
// geolocation stays; never a crash, never replayed arbitrary data.
|
||||
let driveName: string | null = null;
|
||||
try {
|
||||
driveName = new URLSearchParams(window.location?.search ?? "").get("drive");
|
||||
} catch {
|
||||
driveName = null;
|
||||
}
|
||||
if (driveName !== null) {
|
||||
const fixture = driveFixtureByName(driveName);
|
||||
if (fixture === null) console.log("ARAHARAH-DRIVE state=unknown");
|
||||
else installDriveReplay(driveName, fixture);
|
||||
}
|
||||
return fake;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user