@seekprotocol/game 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,19 +3,16 @@
3
3
  The GameKit bridge between a minigame and the Seek app. Made by
4
4
  [Seek Protocol](https://www.seekprotocol.ai/en).
5
5
 
6
- A minigame in a quest is a web page that Seek shows inside the app. This bridge
7
- tells the app when the game is ready, reports the result and sends Seekprotocol
8
- Vitals (start time, frame rate, errors) so makers see how their game runs on
9
- real phones.
6
+ A minigame in a quest is a web page that the Seek app shows. The bridge tells
7
+ the app when the game is ready and how it ended.
10
8
 
11
9
  ```js
12
10
  import { createBridge } from '@seekprotocol/game';
13
11
  ```
14
12
 
15
- `seek-game.js` is the same bridge as one file for a game without a bundler.
16
- The bridge is readable on purpose: it ships inside every game and App Review
17
- reads it.
13
+ `seek-game.js` is the same bridge as one file, for a game without a bundler.
18
14
 
19
- Seek Protocol: https://www.seekprotocol.ai/en
15
+ - Store Connect: https://storeconnect.seekprotocol.ai
16
+ - Seek Protocol: https://www.seekprotocol.ai/en
20
17
 
21
18
  Copyright (c) 2026 Seek Protocol. MIT licensed, see `LICENSE.md`: ship the bridge with your game freely.
package/bridge.d.ts CHANGED
@@ -1,26 +1,6 @@
1
- /**
2
- * The bridge a minigame imports: `ready()`, `launch()`, `finish()`,
3
- * `storage` and `track`.
4
- *
5
- * Two hosts are possible. In the Seek app the game is the top page of a
6
- * hardened WebView: the bridge posts to `window.ReactNativeWebView` and the app
7
- * answers by dispatching `message` events on the page (no source window). In
8
- * Store Connect's simulator the game sits in an iframe: the bridge posts to
9
- * its parent, only to the allowed host origins, and accepts answers only from
10
- * the parent window at the origin that answered `ready`.
11
- *
12
- * The bridge never sees who the player is. The launch token names a
13
- * pseudonymous `sub`, the run, the seed and the language; it is what the
14
- * proxy accepts for storage, track and Vitals, and nothing else.
15
- *
16
- * Seekprotocol Vitals: once ready, the bridge reports uncaught errors and
17
- * unhandled rejections, the game's own `log()` lines (and, if the game opts
18
- * in, console output), a heartbeat and the session's end. Everything is
19
- * scrubbed, batched and limited first (see `collector.ts` and `diagnostics.ts`).
20
- */
21
- import { type HostEventType, type LaunchInfo, type TrackProps } from './protocol.js';
22
- import { type CollectorTimers } from './collector.js';
23
- import { type LogLevel } from './diagnostics.js';
1
+ import { type HostEventType, type LaunchInfo, type TrackProps } from "./protocol.js";
2
+ import { type CollectorTimers } from "./collector.js";
3
+ import { type LogLevel } from "./diagnostics.js";
24
4
  export declare class BridgeError extends Error {
25
5
  readonly code: string;
26
6
  constructor(code: string, message: string);
@@ -31,7 +11,6 @@ interface MessageEventLike {
31
11
  source: unknown;
32
12
  }
33
13
  type Listener = (e: MessageEventLike) => void;
34
- /** What the bridge needs of `window`; tests pass a fake. Besides `message` it listens for `error`, `unhandledrejection` and `pagehide`. */
35
14
  export interface WindowLike {
36
15
  addEventListener(type: string, fn: (e: any) => void): void;
37
16
  removeEventListener(type: string, fn: (e: any) => void): void;
@@ -42,47 +21,34 @@ export interface WindowLike {
42
21
  postMessage(message: string): void;
43
22
  };
44
23
  document?: {
45
- addEventListener(type: 'message', fn: Listener): void;
46
- removeEventListener(type: 'message', fn: Listener): void;
24
+ addEventListener(type: "message", fn: Listener): void;
25
+ removeEventListener(type: "message", fn: Listener): void;
47
26
  };
48
27
  }
49
28
  export interface BridgeOptions {
50
29
  window?: WindowLike;
51
- /** Frame hosts allowed to answer. Default: Store Connect. Add `http://localhost:<port>` for a local simulator. */
52
30
  hostOrigins?: readonly string[];
53
31
  fetch?: typeof fetch;
54
- /** How long a request waits for the host, in ms. */
55
32
  timeoutMs?: number;
56
- /** Seekprotocol Vitals. On by default; `false` turns off error capture, heartbeats and log(). */
57
33
  vitals?: VitalsOptions | false;
58
34
  }
59
- type ConsoleLike = Partial<Record<'debug' | 'log' | 'info' | 'warn' | 'error', (...args: unknown[]) => void>>;
35
+ type ConsoleLike = Partial<Record<"debug" | "log" | "info" | "warn" | "error", (...args: unknown[]) => void>>;
60
36
  export interface VitalsOptions {
61
- /** Capture uncaught errors and unhandled promise rejections. Default true. */
62
37
  errors?: boolean;
63
- /** Also send console output: true for every level, or a list such as `['warn', 'error']`. Default off. */
64
38
  console?: boolean | readonly LogLevel[];
65
- /** Seconds between heartbeats, 5 to 25. Default 10. */
66
39
  heartbeatSeconds?: number;
67
- /** Seconds between sends of a non-empty queue. Default 5. */
68
40
  flushSeconds?: number;
69
- /** Measure frames per second, jank, the heap and the time to ready(). Default true. */
70
41
  performance?: boolean;
71
- /** For tests. */
72
42
  consoleObject?: ConsoleLike;
73
43
  timers?: CollectorTimers;
74
44
  now?: () => number;
75
45
  }
76
46
  export interface FinishResult {
77
- /** Whether the host took the score to Seek. Whether it counts is Seek's decision. */
78
47
  accepted: boolean;
79
48
  }
80
49
  export interface Bridge {
81
- /** The handshake. Resolves with the launch once the host answered. Safe to call twice. */
82
50
  ready(): Promise<LaunchInfo>;
83
- /** The run, after `ready()`. */
84
51
  launch(): LaunchInfo;
85
- /** Report the run, once. The score is advisory: Seek checks it against the step and the run ticket. */
86
52
  finish(result: {
87
53
  score: number;
88
54
  seconds: number;
@@ -92,24 +58,13 @@ export interface Bridge {
92
58
  set(key: string, value: unknown): Promise<void>;
93
59
  remove(key: string): Promise<void>;
94
60
  };
95
- /** Count an event. Never throws; returns false when the event was dropped. */
96
61
  track(event: string, props?: TrackProps): boolean;
97
- /**
98
- * A line for Seekprotocol Vitals, shown to you in Store Connect. Never throws;
99
- * false when it was dropped (not ready yet beyond 20 lines, over the limits, or Vitals are off).
100
- * Do not log personal data: emails, positions, long numbers and tokens are scrubbed, but the rest is kept for 30 days.
101
- */
102
62
  log(level: LogLevel, message: string, data?: unknown): boolean;
103
- /** End the session cleanly (the player left the game) and send what is left. The bridge keeps working for storage and finish. */
104
63
  exit(): Promise<void>;
105
- /** The Vitals session id of this launch, after ready(): search for it in Store Connect's logs. */
106
64
  sessionId(): string | null;
107
- /** Host events: the app went to the background (`pause`), came back (`resume`), or is closing the game (`exit`). */
108
65
  on(event: HostEventType, fn: () => void): () => void;
109
- /** Stop listening. */
110
66
  close(): void;
111
67
  }
112
- /** Events per launch the bridge sends at most; the proxy counts too. */
113
68
  export declare const TRACK_PER_LAUNCH = 200;
114
69
  export declare function createBridge(opts?: BridgeOptions): Bridge;
115
70
  export {};
package/collector.d.ts CHANGED
@@ -1,34 +1,20 @@
1
- /**
2
- * The bridge's side of Seekprotocol Vitals: a queue of scrubbed entries that
3
- * goes out in small batches, within per-launch limits and a rate limit.
4
- *
5
- * It knows nothing about windows or transports: the bridge feeds it errors
6
- * and logs and gives it a `send` that posts to the proxy or the host.
7
- */
8
- import { type DiagnosticsBatch, type EndReason, type ErrorSource, type LogLevel } from './diagnostics.js';
1
+ import { type DiagnosticsBatch, type EndReason, type ErrorSource, type LogLevel } from "./diagnostics.js";
9
2
  export interface CollectorTimers {
10
3
  setInterval(fn: () => void, ms: number): unknown;
11
4
  clearInterval(t: unknown): void;
12
5
  }
13
6
  export interface CollectorOptions {
14
- /** Deliver one batch. `final` is the last one of the session (send it with keepalive). Errors are swallowed. */
15
7
  send: (batch: DiagnosticsBatch, final: boolean) => Promise<unknown> | unknown;
16
- /** A heartbeat for the host's hang check, every `heartbeatMs`, and once with `ended` at the end. */
17
8
  beat?: (ended: boolean) => void;
18
- /** Exact texts never to send (the launch token). */
19
9
  secrets?: () => readonly string[];
20
10
  now?: () => number;
21
11
  timers?: CollectorTimers;
22
- /** How often a non-empty queue is sent. Default 5 s. */
23
12
  flushMs?: number;
24
- /** How often a heartbeat goes out. Default 10 s, at least 5 s, at most 25 s (the host's hang limit is 30 s). */
25
13
  heartbeatMs?: number;
26
- /** Entries per second on average, and the burst. */
27
14
  rate?: {
28
15
  perSecond: number;
29
16
  burst: number;
30
17
  };
31
- /** What each heartbeat carries about how smooth the game runs (perf.ts). */
32
18
  sample?: () => {
33
19
  fps?: number;
34
20
  jank?: number;
@@ -44,14 +30,11 @@ export interface ErrorInput {
44
30
  col?: number;
45
31
  }
46
32
  export interface Collector {
47
- /** The session started (after ready): sends what was buffered before, then runs the timers. `readyMs`: navigation to ready(). */
48
33
  start(session: string, readyMs?: number): void;
49
34
  error(e: ErrorInput): boolean;
50
- log(level: LogLevel, message: string, data?: unknown, source?: 'log' | 'console'): boolean;
51
- /** End the session once and send what is left. */
35
+ log(level: LogLevel, message: string, data?: unknown, source?: "log" | "console"): boolean;
52
36
  end(reason: EndReason): Promise<void>;
53
37
  flush(final?: boolean): Promise<void>;
54
- /** For tests and the docs: what the collector counted. */
55
38
  stats(): {
56
39
  sent: number;
57
40
  dropped: number;
@@ -62,6 +45,5 @@ export interface Collector {
62
45
  readonly session: string | null;
63
46
  readonly ended: boolean;
64
47
  }
65
- /** Entries kept before ready(); the rest are dropped and counted. */
66
48
  export declare const PRE_READY_ENTRIES = 20;
67
49
  export declare function createCollector(opts: CollectorOptions): Collector;
package/diagnostics.d.ts CHANGED
@@ -1,64 +1,57 @@
1
- /**
2
- * Seekprotocol Vitals: the shapes, limits, scrubbing and grouping of a
3
- * minigame's crash and log reports.
4
- *
5
- * The bridge collects errors, logs, heartbeats and the session's start and
6
- * end, scrubs them and sends them in small batches: to the proxy
7
- * (`POST /v1/diagnostics`, with the launch token) or, in the simulator, to the
8
- * host. A host (the Seek app, the simulator) adds what only it can see: a game
9
- * that never became ready, stopped answering, lost its WebView process or did
10
- * not load (`HostEvent`). The proxy runs every check here again and scrubs
11
- * again: nothing the game sends is trusted.
12
- *
13
- * No dependencies and no DOM, so the Workers and the hosts import this file too.
14
- */
15
- export declare const LOG_LEVELS: readonly ["debug", "info", "warn", "error"];
1
+ export declare const LOG_LEVELS: readonly [
2
+ "debug",
3
+ "info",
4
+ "warn",
5
+ "error"
6
+ ];
16
7
  export type LogLevel = (typeof LOG_LEVELS)[number];
17
8
  export declare const isLogLevel: (v: unknown) => v is LogLevel;
18
- /** What the host reports for a game it runs. Each one ends the session as a crash. */
19
- export declare const HOST_EVENTS: readonly ["no_ready", "hang", "render_gone", "load_failed"];
9
+ export declare const HOST_EVENTS: readonly [
10
+ "no_ready",
11
+ "hang",
12
+ "render_gone",
13
+ "load_failed"
14
+ ];
20
15
  export type HostCrashEvent = (typeof HOST_EVENTS)[number];
21
16
  export declare const isHostEvent: (v: unknown) => v is HostCrashEvent;
22
- /** A host reports `no_ready` when the game has not called ready() this long after it started loading. */
23
17
  export declare const HOST_READY_TIMEOUT_MS = 10000;
24
- /** A host reports `hang` when a ready game sent no heartbeat for this long while in the foreground. */
25
18
  export declare const HOST_HANG_MS = 30000;
26
19
  export declare const HOST_EVENT_TEXT: Record<HostCrashEvent, string>;
27
- /** How a session ended. `exit`, `pagehide` and `closed` come from the bridge; the rest from the host. */
28
- export declare const END_REASONS: readonly ["exit", "pagehide", "closed", "host_exit", "no_ready", "hang", "render_gone", "load_failed"];
20
+ export declare const END_REASONS: readonly [
21
+ "exit",
22
+ "pagehide",
23
+ "closed",
24
+ "host_exit",
25
+ "no_ready",
26
+ "hang",
27
+ "render_gone",
28
+ "load_failed"
29
+ ];
29
30
  export type EndReason = (typeof END_REASONS)[number];
30
- /** Where an error came from. `error` and `unhandledrejection` are uncaught and count as crashes. */
31
- export declare const ERROR_SOURCES: readonly ["error", "unhandledrejection", "console", "manual"];
31
+ export declare const ERROR_SOURCES: readonly [
32
+ "error",
33
+ "unhandledrejection",
34
+ "console",
35
+ "manual"
36
+ ];
32
37
  export type ErrorSource = (typeof ERROR_SOURCES)[number];
33
38
  export declare const isFatalSource: (s: ErrorSource) => s is "error" | "unhandledrejection";
34
39
  export declare const DIAG_LIMITS: {
35
- /** Characters of a message (log or error). */
36
40
  readonly message: 500;
37
- /** Characters of a stack, after scrubbing. */
38
41
  readonly stack: 4000;
39
- /** Stack frames kept. */
40
42
  readonly frames: 20;
41
- /** Characters of a source file path. */
42
43
  readonly file: 200;
43
- /** UTF-8 bytes of a log's data as JSON. */
44
44
  readonly data: 2048;
45
- /** Characters of a host event's detail. */
46
45
  readonly detail: 200;
47
- /** Entries in one batch. */
48
46
  readonly batchEntries: 50;
49
- /** UTF-8 bytes of one batch as JSON (the proxy reads at most 64 KB). */
50
47
  readonly batchBytes: number;
51
- /** Error entries per session; the proxy keeps the same limit. */
52
48
  readonly sessionErrors: 100;
53
- /** Log entries per session. */
54
49
  readonly sessionLogs: 1000;
55
- /** One error entry stands for at most this many identical errors. */
56
50
  readonly repeat: 1000;
57
51
  };
58
52
  export declare const DIAG_BODY_MAX_BYTES: number;
59
53
  export interface ErrorEntry {
60
- kind: 'error';
61
- /** ms since 1970, the device's clock. The proxy clamps it. */
54
+ kind: "error";
62
55
  at: number;
63
56
  source: ErrorSource;
64
57
  message: string;
@@ -66,96 +59,82 @@ export interface ErrorEntry {
66
59
  file?: string;
67
60
  line?: number;
68
61
  col?: number;
69
- /** How many identical errors this entry stands for. */
70
62
  count?: number;
71
63
  }
72
64
  export interface LogEntry {
73
- kind: 'log';
65
+ kind: "log";
74
66
  at: number;
75
67
  level: LogLevel;
76
68
  message: string;
77
69
  data?: unknown;
78
- /** `console` when captured from console.*, else the game called log(). */
79
- source?: 'log' | 'console';
70
+ source?: "log" | "console";
80
71
  }
81
- /** A heartbeat, with how the game ran since the last one (perf.ts): frames per second, frames over 50 ms, the JS heap in MB. */
82
72
  export interface HeartbeatEntry {
83
- kind: 'heartbeat';
73
+ kind: "heartbeat";
84
74
  at: number;
85
75
  fps?: number;
86
76
  jank?: number;
87
77
  heap_mb?: number;
88
78
  }
89
- /** The session's start; `ready_ms` is the time from navigation to ready(). */
90
79
  export interface StartEntry {
91
- kind: 'start';
80
+ kind: "start";
92
81
  at: number;
93
82
  ready_ms?: number;
94
83
  }
95
- /** The ranges a measurement must fall in; anything else is left out. */
96
84
  export declare const PERF_RANGES: {
97
- readonly fps: readonly [0, 240];
98
- readonly jank: readonly [0, 100000];
99
- readonly heap_mb: readonly [0, 65536];
100
- readonly ready_ms: readonly [0, 600000];
85
+ readonly fps: readonly [
86
+ 0,
87
+ 240
88
+ ];
89
+ readonly jank: readonly [
90
+ 0,
91
+ 100000
92
+ ];
93
+ readonly heap_mb: readonly [
94
+ 0,
95
+ 65536
96
+ ];
97
+ readonly ready_ms: readonly [
98
+ 0,
99
+ 600000
100
+ ];
101
101
  };
102
102
  export interface EndEntry {
103
- kind: 'end';
103
+ kind: "end";
104
104
  at: number;
105
105
  reason: EndReason;
106
106
  }
107
- /** Only a host may send these, with a host token. */
108
107
  export interface HostEntry {
109
- kind: 'host';
108
+ kind: "host";
110
109
  at: number;
111
110
  event: HostCrashEvent;
112
111
  detail?: string;
113
112
  }
114
113
  export type DiagnosticEntry = ErrorEntry | LogEntry | HeartbeatEntry | StartEntry | EndEntry | HostEntry;
115
- /** What the bridge sends, to the proxy or to the host. Who and what game come from the token, never from here. */
116
114
  export interface DiagnosticsBatch {
117
- /** The session id the bridge derived (`sessionIdOf`); the proxy derives its own and ignores this. */
118
115
  session?: string;
119
- /** Batch number within the session, from 1. */
120
116
  seq?: number;
121
117
  entries: DiagnosticEntry[];
122
- /** Entries the bridge left out because of its own limits since the last batch. */
123
118
  dropped?: number;
124
119
  }
125
- /**
126
- * The launch context a host knows about the device: never free text from the
127
- * game. platform-quest signs it into the launch token (`ctx`) from what the
128
- * app sent to `run_start`.
129
- */
130
120
  export interface LaunchContext {
131
- device: 'phone' | 'tablet' | 'desktop' | 'unknown';
132
- os: 'ios' | 'android' | 'web' | 'unknown';
133
- /** `17.4`, `14`: digits and dots only. */
121
+ device: "phone" | "tablet" | "desktop" | "unknown";
122
+ os: "ios" | "android" | "web" | "unknown";
134
123
  os_version?: string;
135
- /** The Seek app's version, `3.2.0`, or `simulator`. */
136
124
  app_version?: string;
137
125
  }
138
- export declare const DEVICE_CLASSES: readonly LaunchContext['device'][];
139
- export declare const OS_NAMES: readonly LaunchContext['os'][];
140
- /** Why a launch context is not one, or null. */
126
+ export declare const DEVICE_CLASSES: readonly LaunchContext["device"][];
127
+ export declare const OS_NAMES: readonly LaunchContext["os"][];
141
128
  export declare function launchContextProblem(c: unknown): string | null;
142
- /** The context as stored: anything missing or odd becomes `unknown` / null. */
143
129
  export declare function normaliseContext(c: unknown): {
144
130
  device: string;
145
131
  os: string;
146
132
  os_version: string | null;
147
133
  app_version: string | null;
148
134
  };
149
- /** A text with emails, tokens, keys, positions, long numbers and URL queries taken out. */
150
135
  export declare function scrubText(s: string, extra?: readonly string[]): string;
151
- /** Plain JSON with every text scrubbed and keys that name personal or secret things emptied. Null when it is not JSON. */
152
136
  export declare function scrubData(value: unknown, extra?: readonly string[], depth?: number): unknown;
153
- /** Data as stored: scrubbed, and replaced by a marker when it is larger than the limit. */
154
137
  export declare function boundData(value: unknown, extra?: readonly string[]): unknown;
155
- /**
156
- * A game's own URLs, shortened to their path: `https://<app>.play.seekprotocol.ai/@<hash>/game.js`
157
- * becomes `/game.js`, so stacks read well and group across versions.
158
- */
159
138
  export declare function shortenGameUrls(s: string): string;
160
139
  export interface StackFrame {
161
140
  fn: string;
@@ -163,49 +142,27 @@ export interface StackFrame {
163
142
  line: number | null;
164
143
  col: number | null;
165
144
  }
166
- /** The frames of a V8, JavaScriptCore or Gecko stack, top first. */
167
145
  export declare function parseStack(stack: string | undefined): StackFrame[];
168
- /**
169
- * A message with what changes between occurrences taken out: numbers, quoted
170
- * texts, hex ids and placeholders the scrubber left. Two errors that differ
171
- * only there are one group.
172
- */
173
146
  export declare function normaliseMessage(message: string): string;
174
- /** How many frames name a group. */
175
147
  export declare const FINGERPRINT_FRAMES = 3;
176
- /** The part of a frame that stays the same across builds: the file and the function, or the line when it has no name. */
177
148
  export declare const frameKey: (f: StackFrame) => string;
178
149
  export declare function sha256HexOf(s: string): Promise<string>;
179
- /** The text a fingerprint hashes: the kind, the normalised message and the top frames. */
180
150
  export declare function fingerprintInput(kind: string, message: string, stack?: string, file?: string, line?: number): string;
181
- /** A group id: the first 32 hex of SHA-256 over `fingerprintInput`. */
182
151
  export declare function fingerprint(kind: string, message: string, stack?: string, file?: string, line?: number): Promise<string>;
183
- /** The session id of a launch: the same in the bridge, the proxy and Store Connect, and not the run id itself. */
184
152
  export declare function sessionIdOf(appId: string, runId: string): Promise<string>;
185
153
  export interface ParsedBatch {
186
154
  entries: DiagnosticEntry[];
187
- /** What the bridge says it dropped before sending (its own limits). */
188
155
  dropped: number;
189
- /** Entries that were not valid and were left out. */
190
156
  invalid: number;
191
- /** Host entries in a batch that did not come from a host. */
192
157
  hostOnly: number;
193
158
  }
194
- /**
195
- * Check a batch and clean every entry: unknown kinds and broken shapes are
196
- * dropped (counted in `invalid`), texts are clipped and scrubbed again, stacks
197
- * are shortened. `host` entries are only kept when `from` is `host`.
198
- */
199
159
  export declare function parseDiagnosticsBatch(body: unknown, opts?: {
200
- from: 'game' | 'host';
160
+ from: "game" | "host";
201
161
  secrets?: readonly string[];
202
162
  }): ParsedBatch | null;
203
- /** One entry, checked, clipped and scrubbed, or null. The bridge runs it before sending, the proxy again on receipt. */
204
163
  export declare function cleanDiagnosticEntry(e: unknown, secrets?: readonly string[]): DiagnosticEntry | null;
205
- /** Whether an entry ends its session as a crash. */
206
164
  export declare const isCrashEntry: (e: DiagnosticEntry) => boolean;
207
165
  export interface WatchdogOptions {
208
- /** Called once with the crash the host saw; the host then sends it (app) or shows it (simulator). */
209
166
  onCrash: (entry: HostEntry) => void;
210
167
  readyTimeoutMs?: number;
211
168
  hangMs?: number;
@@ -214,20 +171,11 @@ export interface WatchdogOptions {
214
171
  clearTimer?: (t: unknown) => void;
215
172
  }
216
173
  export interface HostWatchdog {
217
- /** The game answered ready. */
218
174
  ready(): void;
219
- /** A heartbeat (or any message) arrived from the game. */
220
175
  beat(): void;
221
- /** The app went to the background (no hang while paused) or came back. */
222
176
  pause(): void;
223
177
  resume(): void;
224
- /** The WebView reported a failure: `render_gone` or `load_failed`. */
225
- fail(event: 'render_gone' | 'load_failed', detail?: string): void;
226
- /** The game ended cleanly, or the host closed it: stop watching. */
178
+ fail(event: "render_gone" | "load_failed", detail?: string): void;
227
179
  stop(): void;
228
180
  }
229
- /**
230
- * The timers a host needs for `no_ready` and `hang`. Start it when the game
231
- * page starts loading; it reports at most one crash, then stops.
232
- */
233
181
  export declare function createHostWatchdog(opts: WatchdogOptions): HostWatchdog;
package/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export * from './protocol.js';
2
- export * from './bridge.js';
3
- export * from './diagnostics.js';
4
- export * from './collector.js';
5
- export * from './perf.js';
1
+ export * from "./protocol.js";
2
+ export * from "./bridge.js";
3
+ export * from "./diagnostics.js";
4
+ export * from "./collector.js";
5
+ export * from "./perf.js";