@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.
@@ -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.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.cts","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"}