@ait-co/debug-console 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/LICENSE +28 -0
- package/README.en.md +75 -0
- package/README.md +75 -0
- package/dist/auto.cjs +1491 -0
- package/dist/auto.cjs.map +1 -0
- package/dist/auto.d.cts +139 -0
- package/dist/auto.d.cts.map +1 -0
- package/dist/auto.d.ts +139 -0
- package/dist/auto.d.ts.map +1 -0
- package/dist/auto.js +1489 -0
- package/dist/auto.js.map +1 -0
- package/dist/index.cjs +1353 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +470 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.ts +470 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1339 -0
- package/dist/index.js.map +1 -0
- package/package.json +85 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
//#region ../internal-protocol/src/debug-host.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Host allowlist predicates — the one part of the activation gate that BOTH
|
|
4
|
+
* sides of the device↔host protocol evaluate.
|
|
5
|
+
*
|
|
6
|
+
* The device evaluates them inside the runtime gate (`@ait-co/debug-console`'s
|
|
7
|
+
* `gate.ts`, Layer B1). The daemon evaluates the same predicate when it decides
|
|
8
|
+
* whether a CDP target's URL is allowed to be driven (`@ait-co/debugger`'s
|
|
9
|
+
* `debug-server.ts`). Before the split those two call sites shared one module
|
|
10
|
+
* by reaching from the daemon into the in-app tree — a reverse edge that would
|
|
11
|
+
* have dragged the browser bundle into the daemon's graph once the two surfaces
|
|
12
|
+
* became separate packages. Extracting the 5 predicates here removes the edge
|
|
13
|
+
* without duplicating the rule.
|
|
14
|
+
*
|
|
15
|
+
* This module is intentionally dependency-free (no Node, no DOM — only
|
|
16
|
+
* `String#endsWith` and one regex) so it inlines into both bundles. There is no
|
|
17
|
+
* barrel index in this package: each consumer imports the one subpath it needs,
|
|
18
|
+
* and its bundler inlines the handful of statements at build time. Nothing here
|
|
19
|
+
* survives as a runtime cross-package import.
|
|
20
|
+
*
|
|
21
|
+
* SECRET-HANDLING: a hostname is an input, never an output. These functions
|
|
22
|
+
* return booleans only — no predicate may grow to log or return the hostname,
|
|
23
|
+
* a relay URL, or a tunnel host.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Returns whether `hostname` is a `*.private-apps.tossmini.com` subdomain —
|
|
27
|
+
* the host reserved for dogfood / private mini-app entries.
|
|
28
|
+
*
|
|
29
|
+
* The match is an exact suffix check, not a substring `.includes()`: a
|
|
30
|
+
* substring test would also accept an attacker-controlled host like
|
|
31
|
+
* `private-apps.tossmini.com.evil.example`, which ends in `.example`, not in
|
|
32
|
+
* `.tossmini.com`. Requiring the string to END with the suffix closes that.
|
|
33
|
+
* The leading `.` in the suffix also forces a real subdomain label, so a bare
|
|
34
|
+
* `private-apps.tossmini.com` (no mini-app subdomain) does not match.
|
|
35
|
+
*/
|
|
36
|
+
declare function isPrivateAppsHost(hostname: string): boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Returns whether `hostname` is any `*.tossmini.com` subdomain — the host
|
|
39
|
+
* family mini-app pages are served from. Includes the 2.x
|
|
40
|
+
* `*.private-apps.tossmini.com` dogfood hosts and the 3.0 unified serving
|
|
41
|
+
* hosts.
|
|
42
|
+
*
|
|
43
|
+
* Same exact-suffix `endsWith` check as {@link isPrivateAppsHost} — never a
|
|
44
|
+
* substring `.includes()`, which would accept `x.tossmini.com.evil.example`.
|
|
45
|
+
*/
|
|
46
|
+
declare function isTossminiHost(hostname: string): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Returns whether `hostname` is a Cloudflare quick-tunnel host — the env 2
|
|
49
|
+
* (PWA) entry.
|
|
50
|
+
*
|
|
51
|
+
* Env 2 serves the local dev server through a `*.trycloudflare.com` quick
|
|
52
|
+
* tunnel. It has no production runtime: the SDK is the devtools mock and the
|
|
53
|
+
* page is the developer's own dev build. The host safety net (which stops a
|
|
54
|
+
* dogfood build that lands on a production host from attaching) has nothing to
|
|
55
|
+
* protect against here — but the remaining gate layers (opt-in, relay URL,
|
|
56
|
+
* TOTP) still apply, so a leaked tunnel URL is still blocked.
|
|
57
|
+
*
|
|
58
|
+
* Same exact-suffix `endsWith` check as {@link isPrivateAppsHost} — never a
|
|
59
|
+
* substring `.includes()`, which would accept
|
|
60
|
+
* `evil.trycloudflare.com.example.com`.
|
|
61
|
+
*/
|
|
62
|
+
declare function isTrycloudflareHost(hostname: string): boolean;
|
|
63
|
+
//#endregion
|
|
64
|
+
//#region src/gate.d.ts
|
|
65
|
+
/** Shape returned when the gate allows attachment. */
|
|
66
|
+
interface GateResultAttach {
|
|
67
|
+
readonly attach: true;
|
|
68
|
+
/** The validated `wss:` relay URL from the `relay` query param. */
|
|
69
|
+
readonly relayUrl: string;
|
|
70
|
+
/** The deployment ID extracted from the `_deploymentId` query param. */
|
|
71
|
+
readonly deploymentId: string;
|
|
72
|
+
}
|
|
73
|
+
/** Shape returned when the gate blocks attachment, with a reason code. */
|
|
74
|
+
interface GateResultBlocked {
|
|
75
|
+
readonly attach: false;
|
|
76
|
+
/**
|
|
77
|
+
* - `'host'` Layer B1: `hostname` is not a `*.private-apps.tossmini.com` host.
|
|
78
|
+
* - `'entry'` Layer B2: `_deploymentId` param is absent or empty.
|
|
79
|
+
* - `'opt-in'` Layer C1: `debug=1` param is absent.
|
|
80
|
+
* - `'invalid-relay'` Layer C2: `relay` param is absent, empty, or not a `wss:` URL.
|
|
81
|
+
* - `'auth'` Layer C3: TOTP `at=` code is absent, invalid, or expired
|
|
82
|
+
* (only when a `verifyTotpCode` predicate is injected).
|
|
83
|
+
*
|
|
84
|
+
* There is no `'build'` reason: Layer A is enforced by the consumer's
|
|
85
|
+
* `if (__DEBUG_BUILD__)` guard, not by this function.
|
|
86
|
+
*
|
|
87
|
+
* SECRET-HANDLING: `'auth'` is the only value surfaced for auth failures —
|
|
88
|
+
* no code value, expected value, or secret fragment is ever exposed.
|
|
89
|
+
*/
|
|
90
|
+
readonly reason: 'host' | 'entry' | 'opt-in' | 'invalid-relay' | 'auth';
|
|
91
|
+
}
|
|
92
|
+
type GateResult = GateResultAttach | GateResultBlocked;
|
|
93
|
+
/**
|
|
94
|
+
* Input for {@link evaluateDebugGate}.
|
|
95
|
+
*
|
|
96
|
+
* All fields are explicit so the function is trivially testable without
|
|
97
|
+
* touching `window`.
|
|
98
|
+
*/
|
|
99
|
+
interface GateInput {
|
|
100
|
+
/**
|
|
101
|
+
* The host the page is served from — `window.location.hostname`.
|
|
102
|
+
*
|
|
103
|
+
* This is the Layer B1 security signal. Why hostname and not the entry
|
|
104
|
+
* scheme: the Toss SDK normalises `intoss-private://` to `intoss://` in
|
|
105
|
+
* `getSchemeUri()`, and `getOperationalEnvironment()` / `getWebViewType()`
|
|
106
|
+
* return the same value (`"toss"` / `"partner"`) for both dogfood and
|
|
107
|
+
* production entries — none of them distinguish a dogfood entry. The host
|
|
108
|
+
* does: a dogfood / private-apps entry is served from
|
|
109
|
+
* `*.private-apps.tossmini.com`, a production entry is not. This was
|
|
110
|
+
* confirmed live over CDP against mini-app 31146 (see spec open question 2).
|
|
111
|
+
*/
|
|
112
|
+
readonly hostname: string;
|
|
113
|
+
/**
|
|
114
|
+
* The URL search params to inspect for gate signals (Layers B2 and C).
|
|
115
|
+
*
|
|
116
|
+
* Prefer `URLSearchParams` so callers can pass `new URLSearchParams(location.search)`
|
|
117
|
+
* without coupling the pure function to `window`.
|
|
118
|
+
*/
|
|
119
|
+
readonly searchParams: URLSearchParams;
|
|
120
|
+
/**
|
|
121
|
+
* Optional TOTP code verifier for Layer C3 auth gate.
|
|
122
|
+
*
|
|
123
|
+
* When provided, `evaluateDebugGate` reads the `at` query param and passes
|
|
124
|
+
* it to this predicate. Return `true` to allow, `false` to block with
|
|
125
|
+
* `reason: 'auth'`.
|
|
126
|
+
*
|
|
127
|
+
* Inject via the consumer's build define, e.g.:
|
|
128
|
+
* ```ts
|
|
129
|
+
* // dogfood build entry — consumer's build injects __DEBUG_TOTP_SECRET__
|
|
130
|
+
* declare const __DEBUG_TOTP_SECRET__: string | undefined;
|
|
131
|
+
* const verifyTotpCode = typeof __DEBUG_TOTP_SECRET__ !== 'undefined'
|
|
132
|
+
* ? (code: string) => verifyTotp(__DEBUG_TOTP_SECRET__, code)
|
|
133
|
+
* : undefined;
|
|
134
|
+
* maybeAttach(evaluateDebugGate({ ...params, verifyTotpCode }));
|
|
135
|
+
* ```
|
|
136
|
+
*
|
|
137
|
+
* Security note: this predicate is a black-box from the gate's perspective.
|
|
138
|
+
* The gate only surfaces pass/fail and the `'auth'` reason code — no code
|
|
139
|
+
* value or secret fragment is ever logged or returned.
|
|
140
|
+
*
|
|
141
|
+
* When `undefined` (TOTP disabled), `at=` is silently ignored and the gate
|
|
142
|
+
* proceeds to ATTACH if all other layers pass.
|
|
143
|
+
*/
|
|
144
|
+
readonly verifyTotpCode?: (code: string) => boolean;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Pure function that evaluates the runtime debug activation layers (B and C).
|
|
148
|
+
*
|
|
149
|
+
* Has no side effects. The input is explicit. Returns a discriminated union
|
|
150
|
+
* so callers can pattern-match on `result.attach`.
|
|
151
|
+
*
|
|
152
|
+
* Layer A (build-time) is intentionally not evaluated here — see the file-level
|
|
153
|
+
* comment. By the time this function runs, the consumer's `if (__DEBUG_BUILD__)`
|
|
154
|
+
* guard has already passed; this function only decides B and C.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* const result = evaluateDebugGate({
|
|
159
|
+
* hostname: window.location.hostname,
|
|
160
|
+
* searchParams: new URLSearchParams(window.location.search),
|
|
161
|
+
* });
|
|
162
|
+
* if (result.attach) {
|
|
163
|
+
* // Proceed to load Chii client
|
|
164
|
+
* }
|
|
165
|
+
* ```
|
|
166
|
+
*/
|
|
167
|
+
declare function evaluateDebugGate(input: GateInput): GateResult;
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/attach.d.ts
|
|
170
|
+
/**
|
|
171
|
+
* Converts a validated `wss:` relay URL into the Chii `target.js` script URL.
|
|
172
|
+
*
|
|
173
|
+
* Scheme is mapped `wss:` → `https:`. Host and port are preserved.
|
|
174
|
+
* Pathname is set to `/target.js` (or `/at/<code>/target.js` when a TOTP code
|
|
175
|
+
* is given) regardless of the relay path. Query params and hash from the
|
|
176
|
+
* relay URL are dropped — the target script URL is a static asset path on the
|
|
177
|
+
* same host.
|
|
178
|
+
*
|
|
179
|
+
* TOTP path-prefix transport (issue #466): chii's stock `target.js` derives
|
|
180
|
+
* its WS endpoint from the script `src` (`scriptEl.src.replace('target.js',
|
|
181
|
+
* '')`), so embedding the current TOTP code in the script URL *path* is the
|
|
182
|
+
* only way the phone-side WS upgrade can carry it — both the script fetch and
|
|
183
|
+
* the derived `wss://<host>/at/<code>/target/<id>` dial inherit the prefix,
|
|
184
|
+
* and the relay verifies + strips it before chii parses the URL. The
|
|
185
|
+
* `window.ChiiServerUrl` + query alternative does NOT work: chii appends
|
|
186
|
+
* `target/<id>` to the serverUrl string, which would land after a `?`.
|
|
187
|
+
*
|
|
188
|
+
* SECRET-HANDLING: `atCode` rides only inside the returned URL (the intended
|
|
189
|
+
* transport — same exposure grade as the daemon client's `at=` query). It is
|
|
190
|
+
* never logged here.
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* deriveTargetScriptUrl('wss://abc.trycloudflare.com/relay')
|
|
194
|
+
* // → 'https://abc.trycloudflare.com/target.js'
|
|
195
|
+
*
|
|
196
|
+
* deriveTargetScriptUrl('wss://h.example.com:9100/', '123456')
|
|
197
|
+
* // → 'https://h.example.com:9100/at/123456/target.js'
|
|
198
|
+
*
|
|
199
|
+
* @param relayUrl - Validated `wss:` relay URL from the gate result.
|
|
200
|
+
* @param atCode - Current TOTP code from the page URL's `at` query param, or
|
|
201
|
+
* `null`/`undefined`/`''` to keep the legacy un-prefixed URL.
|
|
202
|
+
*/
|
|
203
|
+
declare function deriveTargetScriptUrl(relayUrl: string, atCode?: string | null): string;
|
|
204
|
+
declare global {
|
|
205
|
+
interface Window {
|
|
206
|
+
/**
|
|
207
|
+
* Set once {@link installRelayWsObserver} has wrapped `window.WebSocket`
|
|
208
|
+
* (#730). The bare CDP-injected indicator (`buildIndicatorExpression`)
|
|
209
|
+
* checks this flag to decide whether it can piggy-back on the
|
|
210
|
+
* `ait:relay-ws-state` CustomEvent this module broadcasts, instead of
|
|
211
|
+
* installing a second competing `Proxy` on `window.WebSocket`.
|
|
212
|
+
*/
|
|
213
|
+
__ait_relay_ws_observed?: boolean;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Idempotent, non-throwing teardown of the in-app debug surface (issue #748).
|
|
218
|
+
*
|
|
219
|
+
* Removes every debug-surface element WE injected and restores the one side
|
|
220
|
+
* effect WE applied:
|
|
221
|
+
* 1. The CDP-injected `#__ait_debug_indicator` badge (the persistent
|
|
222
|
+
* "Debugger Disconnected" element). Its live heartbeat/pending-call timer
|
|
223
|
+
* (#749) is stopped first via the badge controller's `stop()` so no 1 Hz
|
|
224
|
+
* interval leaks past detach; `buildIndicatorExpression` also
|
|
225
|
+
* self-dismisses the node — this is the in-app hard guarantee, idempotent,
|
|
226
|
+
* a no-op if it is already gone.
|
|
227
|
+
* 2. The eruda in-page console (floating button + any open panel).
|
|
228
|
+
* 3. The native-bridge call observer (#749) — its `callAsyncMethod` /
|
|
229
|
+
* `postMessage` / emitter wraps are restored and `window.__ait_bridge` is
|
|
230
|
+
* removed, so nothing we wrapped survives the run's end.
|
|
231
|
+
* 4. keepAwake — forced on at attach; restored here so a run that ends
|
|
232
|
+
* WITHOUT a page unload does not leave the screen pinned awake (the
|
|
233
|
+
* existing `beforeunload` restore only covers the unload path).
|
|
234
|
+
*
|
|
235
|
+
* Deliberately NOT touched: the `window.WebSocket` observer proxy (kept so the
|
|
236
|
+
* #478 post-4401 fail-fast survives; it is non-blocking and never absorbs
|
|
237
|
+
* input), and any NATIVE overlay (out of our layer — see the block comment
|
|
238
|
+
* above and issue #748 hypothesis (a)).
|
|
239
|
+
*
|
|
240
|
+
* Never throws into the host app — every step is individually guarded.
|
|
241
|
+
* Exported for unit tests and for a consumer that wants to force a clean detach.
|
|
242
|
+
*/
|
|
243
|
+
declare function detachDebugSurface(): void;
|
|
244
|
+
/**
|
|
245
|
+
* Self-report the mini-app's webViewType to the parent launcher shell, ONCE
|
|
246
|
+
* (#580).
|
|
247
|
+
*
|
|
248
|
+
* The mini-app's type is the build constant `__WEB_VIEW_TYPE__`, injected by
|
|
249
|
+
* the devtools unplugin from `granite.config.ts`'s `webViewProps.type`. The
|
|
250
|
+
* launcher (env-2 PWA) is cross-origin and cannot read it directly, so the
|
|
251
|
+
* framed page posts it to `window.parent`; the launcher switches to game mode
|
|
252
|
+
* automatically (no manual `?navBarType=game` URL edit).
|
|
253
|
+
*
|
|
254
|
+
* Defensive by construction — must NEVER break attach:
|
|
255
|
+
* - `__WEB_VIEW_TYPE__` is a CONSUMER-build define; it does not exist in
|
|
256
|
+
* devtools' own build or where the unplugin did not inject it. The `typeof`
|
|
257
|
+
* guard avoids a ReferenceError; an absent constant is a silent no-op.
|
|
258
|
+
* - Only posts when inside an iframe (`window.parent !== window`) — a
|
|
259
|
+
* top-level load has no launcher shell to receive the message.
|
|
260
|
+
* - The SDK's deprecated `'external'` alias of `partner` (web-framework 2.6.1)
|
|
261
|
+
* is mapped to `'partner'`; the launcher only emulates `partner` | `game`.
|
|
262
|
+
* - Wrapped in try/catch so any postMessage/iframe edge case is swallowed.
|
|
263
|
+
*
|
|
264
|
+
* SECRET-HANDLING: the payload carries ONLY the webViewType enum — no host,
|
|
265
|
+
* relay URL, code, or secret.
|
|
266
|
+
*/
|
|
267
|
+
declare function reportWebViewType(): void;
|
|
268
|
+
/**
|
|
269
|
+
* Evaluates the 3-layer debug gate and, if the gate passes, injects the Chii
|
|
270
|
+
* `target.js` script into `document.head`.
|
|
271
|
+
*
|
|
272
|
+
* Idempotent — calling more than once is safe. The second call is a no-op if
|
|
273
|
+
* a script with the same `src` is already present in the document, and the
|
|
274
|
+
* module-level `attached` flag prevents redundant DOM queries after the first
|
|
275
|
+
* successful injection.
|
|
276
|
+
*
|
|
277
|
+
* Safe to call even if `document` is somehow unavailable (defensive boundary
|
|
278
|
+
* guard — in practice this always runs in a real WebView).
|
|
279
|
+
*
|
|
280
|
+
* **keepAwake side effect**: on a successful attach, `setScreenAwakeMode({
|
|
281
|
+
* enabled: true })` is called so the phone screen stays awake during the debug
|
|
282
|
+
* session. A `beforeunload` handler restores normal sleep on page unload.
|
|
283
|
+
* Opt out by adding `noKeepAwake=1` to the page URL query string — the check
|
|
284
|
+
* reads `window.location.search` directly, consistent with other guards in
|
|
285
|
+
* this file.
|
|
286
|
+
*
|
|
287
|
+
* @param gateResult - Optional pre-evaluated gate result for testability.
|
|
288
|
+
* Defaults to `checkDebugGate()` which reads the current page URL. Passing a
|
|
289
|
+
* custom value avoids the need to manipulate `window.location` in tests.
|
|
290
|
+
*/
|
|
291
|
+
declare function maybeAttach(gateResult?: GateResult): void;
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region ../internal-protocol/src/bridge-observer-state.d.ts
|
|
294
|
+
/**
|
|
295
|
+
* The `window.__ait_bridge` snapshot shape and the event/global names the
|
|
296
|
+
* device publishes it under.
|
|
297
|
+
*
|
|
298
|
+
* The device (`@ait-co/debug-console`) writes the snapshot; the daemon
|
|
299
|
+
* (`@ait-co/debugger`) reads it back field-by-field from inside a CDP
|
|
300
|
+
* `Runtime.evaluate` source string it assembles as text. There is therefore no
|
|
301
|
+
* compile-time link between writer and reader — the daemon's use of the
|
|
302
|
+
* interface is type-only (erased entirely) and its use of the name constants is
|
|
303
|
+
* string interpolation. Keeping both in one module is what makes a rename a
|
|
304
|
+
* compile error on the writing side and a single edit on the reading side,
|
|
305
|
+
* instead of a silent no-op badge.
|
|
306
|
+
*
|
|
307
|
+
* SECRET-HANDLING: the snapshot holds API METHOD NAMES + timestamps + a
|
|
308
|
+
* correlation id ONLY. It must never grow fields for call arguments, results,
|
|
309
|
+
* relay URLs, or auth codes.
|
|
310
|
+
*/
|
|
311
|
+
/** Name of the API being called (never its arguments). */
|
|
312
|
+
interface BridgePendingCall {
|
|
313
|
+
method: string;
|
|
314
|
+
/** `Date.now()` epoch ms when the call was dispatched. */
|
|
315
|
+
startedAt: number;
|
|
316
|
+
}
|
|
317
|
+
/** The most-recent bridge activity — API name + wall-clock + settle status. */
|
|
318
|
+
interface BridgeLastCall {
|
|
319
|
+
method: string;
|
|
320
|
+
/** `Date.now()` epoch ms of the start or settle that produced this record. */
|
|
321
|
+
at: number;
|
|
322
|
+
status: 'pending' | 'resolved' | 'rejected';
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* The snapshot exposed on `window.__ait_bridge`. `pending` is keyed by
|
|
326
|
+
* correlation id so a settle is an O(1) delete; the indicator reads
|
|
327
|
+
* `Object.values(pending)` and computes each call's live elapsed itself.
|
|
328
|
+
*/
|
|
329
|
+
interface BridgeObserverState {
|
|
330
|
+
pending: Record<string, BridgePendingCall>;
|
|
331
|
+
last: BridgeLastCall | null;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* CustomEvent fired (no detail) on every bridge-call start/settle so the
|
|
335
|
+
* indicator badge re-renders promptly. SECRET-HANDLING: carries no detail
|
|
336
|
+
* payload at all — the badge reads the enum-only snapshot on receipt.
|
|
337
|
+
*/
|
|
338
|
+
declare const BRIDGE_CALL_EVENT = "ait:bridge-call";
|
|
339
|
+
//#endregion
|
|
340
|
+
//#region src/bridge-observer.d.ts
|
|
341
|
+
declare global {
|
|
342
|
+
interface Window {
|
|
343
|
+
/**
|
|
344
|
+
* Read-only bridge-call snapshot published by {@link installBridgeObserver}
|
|
345
|
+
* (#749). The CDP-injected indicator badge reads this to render the pending
|
|
346
|
+
* native-call list + last-call stamp. Absent when no debug attach ran or in
|
|
347
|
+
* a context with no observable bridge (env 2 mock). SECRET-HANDLING: holds
|
|
348
|
+
* API names + timings only — never arguments or results.
|
|
349
|
+
*/
|
|
350
|
+
__ait_bridge?: BridgeObserverState | undefined;
|
|
351
|
+
/**
|
|
352
|
+
* 3.0-line native bridge (`@apps-in-toss/webview-bridge`
|
|
353
|
+
* `injectNativeBridge`). `callAsyncMethod` is the single async dispatcher
|
|
354
|
+
* the observer wraps.
|
|
355
|
+
*/
|
|
356
|
+
__appsInTossNativeBridge?: {
|
|
357
|
+
callAsyncMethod?: (name: string, params?: unknown) => unknown;
|
|
358
|
+
[key: string]: unknown;
|
|
359
|
+
};
|
|
360
|
+
/** 2.x-line native call transport (`ReactNativeWebView.postMessage`). */
|
|
361
|
+
ReactNativeWebView?: {
|
|
362
|
+
postMessage?: (message: string) => void;
|
|
363
|
+
[key: string]: unknown;
|
|
364
|
+
};
|
|
365
|
+
/** 2.x-line native→JS settle emitter (`@apps-in-toss/bridge-core`). */
|
|
366
|
+
__GRANITE_NATIVE_EMITTER?: {
|
|
367
|
+
emit?: (event: string, args?: unknown) => void;
|
|
368
|
+
[key: string]: unknown;
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Installs the native-bridge call observer (#749). Idempotent per page
|
|
374
|
+
* lifecycle. Called by {@link maybeAttach} after the gate passes (debug builds
|
|
375
|
+
* only). Prefers the 3.0 single-dispatcher wrap; falls back to the universal
|
|
376
|
+
* 2.x postMessage-start + emitter-settle pair. A context with no observable
|
|
377
|
+
* bridge (env 2 mock) leaves `window.__ait_bridge` as an empty snapshot — the
|
|
378
|
+
* badge then shows the heartbeat only, which is correct.
|
|
379
|
+
*
|
|
380
|
+
* Never throws into the host app — a wrapped hook that somehow fails is caught
|
|
381
|
+
* and the original behavior is always preserved.
|
|
382
|
+
*/
|
|
383
|
+
declare function installBridgeObserver(): void;
|
|
384
|
+
/**
|
|
385
|
+
* Restores every wrapped bridge hook and removes `window.__ait_bridge` (#749).
|
|
386
|
+
* Idempotent; safe to call when nothing was installed. Wired into
|
|
387
|
+
* {@link detachDebugSurface} (#748) so no bridge wrap survives a run's end.
|
|
388
|
+
*/
|
|
389
|
+
declare function uninstallBridgeObserver(): void;
|
|
390
|
+
//#endregion
|
|
391
|
+
//#region src/eruda-overlay.d.ts
|
|
392
|
+
/**
|
|
393
|
+
* In-app eruda console overlay for the debug attach flow.
|
|
394
|
+
*
|
|
395
|
+
* Spec: docs/superpowers/specs/2026-05-18-in-app-debug-mcp.md
|
|
396
|
+
*
|
|
397
|
+
* This module mounts the eruda in-page console (https://github.com/liriliri/eruda)
|
|
398
|
+
* on the phone screen when a debug session attaches. It is the mobile-only
|
|
399
|
+
* counterpart to the Chii `target.js` injection in {@link attach.ts}: Chii is a
|
|
400
|
+
* REMOTE CDP transport (phone → relay → PC DevTools frontend), whereas eruda is
|
|
401
|
+
* a LOCAL in-page view — a floating button + console/network/DOM/storage panels
|
|
402
|
+
* rendered directly on the phone, with no relay or second device. The two are
|
|
403
|
+
* orthogonal and coexist (eruda opens no WebSocket, mounts into its own
|
|
404
|
+
* `#eruda` shadow host — it cannot collide with the relay WS or the Chii DOM).
|
|
405
|
+
*
|
|
406
|
+
* Build-time absence (the security contract): this module lives in the
|
|
407
|
+
* `@ait-co/debug-console` graph. A consumer wraps its
|
|
408
|
+
* `import('@ait-co/debug-console')` call site in `if (__DEBUG_BUILD__) { … }`;
|
|
409
|
+
* a release build folds that constant to `false` and dead-code-eliminates the
|
|
410
|
+
* whole module — so eruda (and its dynamic `import('eruda')` chunk) is simply
|
|
411
|
+
* absent from release bundles, exactly like the Chii target.js injection. The
|
|
412
|
+
* `import('eruda')` here is a dynamic import precisely so the bundler emits it
|
|
413
|
+
* as a separate chunk that the dead branch never pulls in.
|
|
414
|
+
*
|
|
415
|
+
* Runtime gate: `mountEruda()` is called only from `maybeAttach()` AFTER the
|
|
416
|
+
* full Layer B/C gate has passed (`gateResult.attach === true`) — host
|
|
417
|
+
* allowlist, `debug=1`, relay URL, and TOTP. So eruda inherits the same
|
|
418
|
+
* four-layer defence as the Chii injection, byte-for-byte, with no eruda-
|
|
419
|
+
* specific gate of its own.
|
|
420
|
+
*
|
|
421
|
+
* SECRET-HANDLING: this module reads no secret, TOTP code, relay URL, or host
|
|
422
|
+
* value, and logs none. eruda observes only the page it is mounted on.
|
|
423
|
+
*/
|
|
424
|
+
/**
|
|
425
|
+
* Mounts the eruda in-page console once.
|
|
426
|
+
*
|
|
427
|
+
* Idempotent: repeated calls after a successful mount are no-ops, mirroring the
|
|
428
|
+
* `attached` guard in {@link attach.ts}. Fail-silent: if the dynamic import or
|
|
429
|
+
* `eruda.init()` throws (eruda absent, or a runtime that rejects it), the Chii
|
|
430
|
+
* debug session is unaffected — eruda is an additive convenience, not a
|
|
431
|
+
* dependency of the relay path.
|
|
432
|
+
*
|
|
433
|
+
* `eruda.init()` mounts eruda's own floating entry button on the phone screen;
|
|
434
|
+
* tapping it opens the console. We do not add a separate button.
|
|
435
|
+
*/
|
|
436
|
+
declare function mountEruda(): Promise<void>;
|
|
437
|
+
/**
|
|
438
|
+
* Unmounts the eruda in-page console (#748 graceful detach).
|
|
439
|
+
*
|
|
440
|
+
* Calls `eruda.destroy()`, which removes eruda's floating entry button, any
|
|
441
|
+
* open panel, and its `#eruda` shadow host — returning the phone screen to a
|
|
442
|
+
* clean, non-debug state when a debug session ends. After a successful unmount
|
|
443
|
+
* the guard is reset so a later {@link mountEruda} (a fresh attach) can
|
|
444
|
+
* re-mount.
|
|
445
|
+
*
|
|
446
|
+
* Idempotent: a call when eruda was never mounted (or already unmounted) is a
|
|
447
|
+
* no-op. Fail-silent: a `destroy()` throw is swallowed — teardown must never
|
|
448
|
+
* throw into the host app.
|
|
449
|
+
*/
|
|
450
|
+
declare function unmountEruda(): void;
|
|
451
|
+
//#endregion
|
|
452
|
+
//#region src/index.d.ts
|
|
453
|
+
/**
|
|
454
|
+
* Evaluates the runtime debug activation layers (B and C) against the current
|
|
455
|
+
* page URL.
|
|
456
|
+
*
|
|
457
|
+
* Returns the gate result. Callers can check `result.attach` to decide whether
|
|
458
|
+
* to proceed with debug surface attachment.
|
|
459
|
+
*
|
|
460
|
+
* This function reads `window.location` only — both the hostname (Layer B1
|
|
461
|
+
* host allowlist) and the search params (Layers B2 and C). Layer A
|
|
462
|
+
* (build-time) is enforced by the consumer's `if (__DEBUG_BUILD__)` guard
|
|
463
|
+
* around the import site, not here — see the file-level comment. Consumers
|
|
464
|
+
* call this with no arguments, so the Layer B1 host check is picked up with
|
|
465
|
+
* no change at the call site.
|
|
466
|
+
*/
|
|
467
|
+
declare function checkDebugGate(): GateResult;
|
|
468
|
+
//#endregion
|
|
469
|
+
export { BRIDGE_CALL_EVENT, type BridgeLastCall, type BridgeObserverState, type BridgePendingCall, type GateInput, type GateResult, type GateResultAttach, type GateResultBlocked, checkDebugGate, deriveTargetScriptUrl, detachDebugSurface, evaluateDebugGate, installBridgeObserver, isPrivateAppsHost, isTossminiHost, isTrycloudflareHost, maybeAttach, mountEruda, reportWebViewType, uninstallBridgeObserver, unmountEruda };
|
|
470
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../internal-protocol/src/debug-host.ts","../src/gate.ts","../src/attach.ts","../../internal-protocol/src/bridge-observer-state.ts","../src/bridge-observer.ts","../src/eruda-overlay.ts","../src/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4DgB,kBAAkB;;;;;;;;;;iBAalB,eAAe;;;;;;;;;;;;;;;;iBAmBf,oBAAoB;;;;UCkBnB;WACN;;WAEA;;WAEA;;;UAIM;WACN;;;;;;;;;;;;;;;WAeA;;KAGC,aAAa,mBAAmB;;;;;;;UAQ3B;;;;;;;;;;;;;WAaN;;;;;;;WAQA,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;WA0Bd,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;iBAwBb,kBAAkB,OAAO,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBC9JrC,sBAAsB,kBAAkB;QA6FhD;YACI;;;;;;;;IAQR;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBA4MY;;;;;;;;;;;;;;;;;;;;;;;;iBAwKA;;;;;;;;;;;;;;;;;;;;;;;;iBA4CA,YAAY,aAAY;;;;;;;;;;;;;;;;;;;;;UC9iBvB;EACf;;EAEA;;;UAIe;EACf;;EAEA;EACA;;;;;;;UAQe;EACf,SAAS,eAAe;EACxB,MAAM;;;;;;;cAQK;;;QCoBL;YACI;;;;;;;;IAQR,eAAe;;;;;;IAMf;MACE,mBAAmB,cAAc;OAChC;;;IAGH;MACE,eAAe;OACd;;;IAGH;MACE,QAAQ,eAAe;OACtB;;;;;;;;;;;;;;;iBAsHS;;;;;;iBAoFA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBClPM,cAAc;;;;;;;;;;;;;;iBAgCpB;;;;;;;;;;;;;;;;;iBCxBA,kBAAkB"}
|