export interface PlayerCodeGridBounds { low: { x: bigint; y: bigint; z: bigint; }; high: { x: bigint; y: bigint; z: bigint; }; } export interface PlayerCodeHostCall { fn: string; args: Record; } export interface PlayerCodeWorkerLike { postMessage(message: unknown, transfer?: Transferable[]): void; addEventListener(type: 'message', listener: (event: MessageEvent) => void): void; removeEventListener(type: 'message', listener: (event: MessageEvent) => void): void; terminate(): void; } /** * A HUD/overlay presentation message the running mod asked the host game to * render. The broker forwards these to the game-declared channel; the mod * never touches the DOM (04 §4 presentation hooks). */ export interface PlayerCodePresentation { channel: 'hud' | 'overlay'; payload: unknown; } export interface PlayerCodeBrokerOptions { /** Platform-owned glue worker URL; the worker never receives auth tokens. */ workerUrl: string | URL; grid: PlayerCodeGridBounds; /** * Content hash of the platform-fetched artifact. When set, start() refuses * any artifact whose hash does not match — a side-loaded module cannot be * run (09 T7). Compute it from the same bytes the game-api served. */ artifactHash?: string; /** * Informational server-authored fuel metadata. The browser does not enforce * this value; metering must be injected into the platform artifact. */ fuelPerDispatch?: bigint; onHostCall: (call: PlayerCodeHostCall) => Promise; /** Optional sink for HUD/overlay presentation the mod emits (BWF wires this). */ onPresentation?: (presentation: PlayerCodePresentation) => void; /** Called when the local circuit breaker trips (repeated traps / rate abuse). */ onCircuitOpen?: (reason: string) => void; workerFactory?: (url: string | URL) => PlayerCodeWorkerLike; /** Override the hash function for tests; defaults to SubtleCrypto SHA-256. */ hashArtifact?: (artifact: ArrayBuffer) => Promise; /** Wall-clock ms allowed per dispatch before the broker recycles the worker. */ dispatchWatchdogMs?: number; /** Wall-clock ms allowed for worker creation + WASM instantiation. */ startupWatchdogMs?: number; /** Wall-clock ms allowed while an async SDK host call is outstanding. */ hostCallTimeoutMs?: number; /** * Local tick cadence (ms) for a client mod: the worker self-drives `tick` * at this interval. Omit/0 for invoke-only mods (no periodic tick). A HUD * mod typically ticks ~1 Hz; the per-dispatch watchdog still bounds each. */ tickIntervalMs?: number; } /** * Page-side security broker for browser-target player WASM (production shape, * player compute P3). * * The untrusted glue worker runs the player module; the broker is the trusted * boundary on the page: * - artifact bytes come only from the platform (hash-verified; side-load * refused), * - a deny-by-default, capability-grouped host-call allowlist crosses the * bridge, each call re-validated (confused-deputy safe) and grid-AABB * filtered on both reads and effects, * - per-call-family rate caps bound a runaway mod, * - a local circuit breaker + per-dispatch watchdog recover the page from a * hung or abusive worker. * * Tokens, DOM, admin/authoring domains, and the network are never reachable * from the worker: the broker only ever calls the injected onHostCall (which * routes to the ordinary server-authorized SDK path) and the presentation * sink the host game opted into. */ export declare class PlayerCodeBroker { private readonly options; private worker; private workerListener; private artifact; private circuitOpen; private consecutiveTraps; private hardTimeouts; private readonly rateBuckets; private globalCallBucket; private generation; private lifecycleVersion; private starting; private workerReady; private startupTimer; private dispatchTimer; private activeDispatch; constructor(options: PlayerCodeBrokerOptions); /** * Start the worker on a platform-fetched artifact. Verifies the artifact * hash (side-load refusal, T7) before handing bytes to the worker. A retained * copy allows the page-side hard watchdog to replace a wedged worker. */ start(artifact: ArrayBuffer): Promise; /** Terminate + respawn on a fresh artifact — the client hot-reload path. */ restart(artifact: ArrayBuffer): Promise; stop(): void; /** Clear a tripped circuit so the caller can start again after a fix. */ resetCircuit(): void; private handleMessage; /** * Deliver a host-call reply. Always posts the message (the offline test * shape + any async-transport consumer), and — when the worker shared a * SharedArrayBuffer for this call — ALSO writes the SDK Response envelope * into it and wakes the worker blocked in Atomics.wait. The synchronous * guest can only receive the reply through the SAB (a blocked worker never * runs its message handler), so the SAB write is the load-bearing path in * the browser; the postMessage is harmless there. */ private reply; private spawnWorker; private stopWorker; private armStartupTimer; private clearStartupTimer; private armDispatchTimer; private armHostCallTimer; private resumeDispatchWatchdog; private finishDispatch; private clearDispatchTimer; private recycleWorker; private openCircuit; private enforceGlobalRate; private enforceRate; private recordTrap; private assertGridScope; private assertChunk; private hash; } //# sourceMappingURL=player-code-broker.d.ts.map