@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 +5 -8
- package/bridge.d.ts +6 -51
- package/collector.d.ts +2 -20
- package/diagnostics.d.ts +57 -109
- package/index.d.ts +5 -5
- package/index.js +928 -863
- package/package.json +2 -3
- package/perf.d.ts +0 -18
- package/protocol.d.ts +3 -41
- package/seek-game.js +928 -863
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
|
|
7
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3
|
-
|
|
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:
|
|
46
|
-
removeEventListener(type:
|
|
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<
|
|
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?:
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
19
|
-
|
|
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
|
-
|
|
28
|
-
|
|
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
|
-
|
|
31
|
-
|
|
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:
|
|
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:
|
|
65
|
+
kind: "log";
|
|
74
66
|
at: number;
|
|
75
67
|
level: LogLevel;
|
|
76
68
|
message: string;
|
|
77
69
|
data?: unknown;
|
|
78
|
-
|
|
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:
|
|
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:
|
|
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 [
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
132
|
-
os:
|
|
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[
|
|
139
|
-
export declare const OS_NAMES: readonly LaunchContext[
|
|
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:
|
|
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
|
-
|
|
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
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
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";
|