browser-pilot 0.0.8 → 0.0.10
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 +62 -1
- package/dist/actions.cjs +465 -6
- package/dist/actions.d.cts +22 -3
- package/dist/actions.d.ts +22 -3
- package/dist/actions.mjs +5 -3
- package/dist/browser.cjs +1350 -14
- package/dist/browser.d.cts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.mjs +2 -2
- package/dist/{chunk-JN44FHTK.mjs → chunk-7OSR2CAE.mjs} +1429 -14
- package/dist/chunk-KKW2SZLV.mjs +741 -0
- package/dist/cli.mjs +6554 -152
- package/dist/index.cjs +2026 -23
- package/dist/index.d.cts +142 -6
- package/dist/index.d.ts +142 -6
- package/dist/index.mjs +357 -10
- package/dist/providers.d.cts +2 -2
- package/dist/providers.d.ts +2 -2
- package/dist/{types-D_uDqh0Z.d.cts → types--wXNHUwt.d.cts} +1 -1
- package/dist/{types-D_uDqh0Z.d.ts → types--wXNHUwt.d.ts} +1 -1
- package/dist/{types-DklIxnbO.d.cts → types-CYw-7vx1.d.cts} +244 -1
- package/dist/{types-Pv8KzZ6l.d.ts → types-DOGsEYQa.d.ts} +244 -1
- package/package.json +3 -3
- package/dist/chunk-ZIQA4JOT.mjs +0 -226
- package/dist/chunk-ZTQ37YQT.mjs +0 -283
- package/dist/cli.cjs +0 -6377
- package/dist/cli.d.cts +0 -25
- package/dist/cli.d.ts +0 -25
|
@@ -1,5 +1,209 @@
|
|
|
1
1
|
import { C as CDPClient } from './client-7Nqka5MV.js';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Audio I/O type definitions
|
|
5
|
+
*/
|
|
6
|
+
/** A chunk of captured audio data */
|
|
7
|
+
interface AudioChunk {
|
|
8
|
+
/** Left channel PCM data (Float32) */
|
|
9
|
+
left: Float32Array;
|
|
10
|
+
/** Right channel PCM data (Float32) */
|
|
11
|
+
right: Float32Array;
|
|
12
|
+
/** Sample rate */
|
|
13
|
+
sampleRate: number;
|
|
14
|
+
/** Number of samples in this chunk */
|
|
15
|
+
samples: number;
|
|
16
|
+
/** Timestamp when this chunk was received */
|
|
17
|
+
timestamp: number;
|
|
18
|
+
}
|
|
19
|
+
/** Result of an audio capture session */
|
|
20
|
+
interface CaptureResult {
|
|
21
|
+
/** All captured PCM data, left channel */
|
|
22
|
+
left: Float32Array;
|
|
23
|
+
/** All captured PCM data, right channel */
|
|
24
|
+
right: Float32Array;
|
|
25
|
+
/** Sample rate */
|
|
26
|
+
sampleRate: number;
|
|
27
|
+
/** Total duration in milliseconds */
|
|
28
|
+
durationMs: number;
|
|
29
|
+
/** Number of chunks received */
|
|
30
|
+
chunkCount: number;
|
|
31
|
+
}
|
|
32
|
+
/** Options for audio output capture */
|
|
33
|
+
interface CaptureOptions {
|
|
34
|
+
/** Stop after N ms of silence (RMS below threshold). Default: 1500 */
|
|
35
|
+
silenceTimeout?: number;
|
|
36
|
+
/** RMS threshold to consider as silence. Default: 0.01 */
|
|
37
|
+
silenceThreshold?: number;
|
|
38
|
+
/** Maximum capture duration in ms. Default: 300000 (5 min) */
|
|
39
|
+
maxDuration?: number;
|
|
40
|
+
/** Stop early if no non-silent audio arrives within this many ms. Default: 15000 */
|
|
41
|
+
noAudioTimeout?: number;
|
|
42
|
+
}
|
|
43
|
+
/** Options for audio input playback */
|
|
44
|
+
interface PlayOptions {
|
|
45
|
+
/** Wait for playback to complete before resolving (default: true) */
|
|
46
|
+
waitForEnd?: boolean;
|
|
47
|
+
/** Timeout in ms (default: 60000) */
|
|
48
|
+
timeout?: number;
|
|
49
|
+
}
|
|
50
|
+
/** State of the injected audio input system */
|
|
51
|
+
interface AudioInputState {
|
|
52
|
+
/** AudioContext state */
|
|
53
|
+
contextState: 'not-created' | 'running' | 'suspended' | 'closed';
|
|
54
|
+
/** Whether audio is currently playing */
|
|
55
|
+
isPlaying: boolean;
|
|
56
|
+
/** Sample rate of the AudioContext */
|
|
57
|
+
sampleRate: number;
|
|
58
|
+
}
|
|
59
|
+
/** Options for a full audio round-trip */
|
|
60
|
+
interface RoundTripOptions {
|
|
61
|
+
/** Audio data to send as microphone input (WAV, MP3, OGG, etc.) */
|
|
62
|
+
input: ArrayBuffer | Uint8Array;
|
|
63
|
+
/** Ms of silence before considering the agent "done talking". Default: 1500 */
|
|
64
|
+
silenceTimeout?: number;
|
|
65
|
+
/** RMS threshold for silence. Default: 0.01 */
|
|
66
|
+
silenceThreshold?: number;
|
|
67
|
+
/** Max total time for the round trip in ms. Default: 120000 (2 min) */
|
|
68
|
+
timeout?: number;
|
|
69
|
+
/** Delay before starting input playback (let page initialize) in ms. Default: 0 */
|
|
70
|
+
preDelay?: number;
|
|
71
|
+
/** Selector to click after audio input finishes (for push-to-talk UIs). */
|
|
72
|
+
sendSelector?: string | string[];
|
|
73
|
+
}
|
|
74
|
+
/** Result of a full audio round-trip */
|
|
75
|
+
interface RoundTripResult {
|
|
76
|
+
/** Captured audio response */
|
|
77
|
+
audio: CaptureResult;
|
|
78
|
+
/** Time from input start to first non-silent output chunk (ms), -1 if no audio received */
|
|
79
|
+
latencyMs: number;
|
|
80
|
+
/** Total round-trip time in ms */
|
|
81
|
+
totalMs: number;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* AudioInput — inject audio into a page's fake microphone
|
|
86
|
+
*
|
|
87
|
+
* Monkey-patches navigator.mediaDevices.getUserMedia to return a
|
|
88
|
+
* controlled MediaStream fed by an AudioContext. Supports playing
|
|
89
|
+
* arbitrary audio (WAV, MP3, OGG) and changing audio mid-session.
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
declare class AudioInput {
|
|
93
|
+
private cdp;
|
|
94
|
+
private injected;
|
|
95
|
+
private bindingRegistered;
|
|
96
|
+
private bindingHandler;
|
|
97
|
+
constructor(cdp: CDPClient);
|
|
98
|
+
/** Whether the audio input system has been set up */
|
|
99
|
+
get isSetup(): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Set up audio input injection.
|
|
102
|
+
* Must be called before navigating to the page that will use getUserMedia.
|
|
103
|
+
* Grants permissions and injects the getUserMedia override.
|
|
104
|
+
*/
|
|
105
|
+
setup(): Promise<void>;
|
|
106
|
+
/**
|
|
107
|
+
* Play audio bytes into the page's fake microphone.
|
|
108
|
+
* Accepts any format that Web Audio API can decode (WAV, MP3, OGG, etc.).
|
|
109
|
+
*
|
|
110
|
+
* @param audioData - Raw audio file bytes
|
|
111
|
+
* @param options - Playback options
|
|
112
|
+
*/
|
|
113
|
+
play(audioData: ArrayBuffer | Uint8Array, options?: PlayOptions): Promise<void>;
|
|
114
|
+
/**
|
|
115
|
+
* Stop any currently playing audio.
|
|
116
|
+
*/
|
|
117
|
+
stop(): Promise<void>;
|
|
118
|
+
/**
|
|
119
|
+
* Get current state of the injected audio input system.
|
|
120
|
+
*/
|
|
121
|
+
getState(): Promise<AudioInputState>;
|
|
122
|
+
/**
|
|
123
|
+
* Clean up: remove binding handler.
|
|
124
|
+
*/
|
|
125
|
+
teardown(): Promise<void>;
|
|
126
|
+
/**
|
|
127
|
+
* Wait for the playback-complete binding to fire.
|
|
128
|
+
*/
|
|
129
|
+
private waitForBinding;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* AudioOutput — capture audio that a page plays
|
|
134
|
+
*
|
|
135
|
+
* Intercepts audio at multiple levels:
|
|
136
|
+
* 1. AudioContext constructor tracking + per-context ScriptProcessorNode taps
|
|
137
|
+
* 2. AudioNode.connect override to tap connections to AudioDestinationNode
|
|
138
|
+
* 3. HTMLMediaElement.play interception via captureStream
|
|
139
|
+
* 3b. HTMLMediaElement.srcObject setter override for WebRTC MediaStreams
|
|
140
|
+
* 4. RTCPeerConnection override to capture WebRTC audio tracks
|
|
141
|
+
* 5. MutationObserver for dynamically created media elements
|
|
142
|
+
*
|
|
143
|
+
* Uses ScriptProcessorNode to tap PCM data, transfers to Node.js
|
|
144
|
+
* via Runtime.addBinding.
|
|
145
|
+
*/
|
|
146
|
+
|
|
147
|
+
declare class AudioOutput {
|
|
148
|
+
private cdp;
|
|
149
|
+
private chunks;
|
|
150
|
+
private injected;
|
|
151
|
+
private capturing;
|
|
152
|
+
private bindingHandler;
|
|
153
|
+
private onChunkHandler?;
|
|
154
|
+
private onDiagHandler?;
|
|
155
|
+
/** Timestamp of the first non-silent chunk received */
|
|
156
|
+
firstChunkTime: number | null;
|
|
157
|
+
constructor(cdp: CDPClient);
|
|
158
|
+
/** Whether the audio output system has been set up */
|
|
159
|
+
get isSetup(): boolean;
|
|
160
|
+
/** Whether audio is currently being captured */
|
|
161
|
+
get isCapturing(): boolean;
|
|
162
|
+
/**
|
|
163
|
+
* Set up audio output capture.
|
|
164
|
+
* Registers bindings and injects the capture script.
|
|
165
|
+
*/
|
|
166
|
+
setup(): Promise<void>;
|
|
167
|
+
/**
|
|
168
|
+
* Start capturing audio output.
|
|
169
|
+
*/
|
|
170
|
+
start(): Promise<void>;
|
|
171
|
+
/**
|
|
172
|
+
* Stop capturing and return all collected audio.
|
|
173
|
+
*/
|
|
174
|
+
stop(): Promise<CaptureResult>;
|
|
175
|
+
/**
|
|
176
|
+
* Capture audio until silence is detected.
|
|
177
|
+
*
|
|
178
|
+
* Two-phase approach:
|
|
179
|
+
* 1. **Wait phase**: Wait up to `maxDuration` for the first non-silent chunk.
|
|
180
|
+
* The silence countdown does NOT tick during this phase, so slow voice agents
|
|
181
|
+
* (STT → LLM → TTS can take 5-15s) don't cause premature timeout.
|
|
182
|
+
* 2. **Capture phase**: Once audio is detected, capture until `silenceTimeout` ms
|
|
183
|
+
* of consecutive silence pass, then stop.
|
|
184
|
+
*/
|
|
185
|
+
captureUntilSilence(options?: CaptureOptions): Promise<CaptureResult>;
|
|
186
|
+
/**
|
|
187
|
+
* Subscribe to real-time audio chunks as they arrive.
|
|
188
|
+
*/
|
|
189
|
+
onData(handler: (chunk: AudioChunk) => void): void;
|
|
190
|
+
/**
|
|
191
|
+
* Subscribe to diagnostic messages (for --verbose).
|
|
192
|
+
*/
|
|
193
|
+
onDiag(handler: (msg: string) => void): void;
|
|
194
|
+
/**
|
|
195
|
+
* Clean up: remove binding handler.
|
|
196
|
+
*/
|
|
197
|
+
teardown(): Promise<void>;
|
|
198
|
+
/**
|
|
199
|
+
* Use CDP Runtime.queryObjects to find RTCPeerConnection instances
|
|
200
|
+
* that were created before our override was injected, and tap their audio tracks.
|
|
201
|
+
*/
|
|
202
|
+
private discoverExistingPeerConnections;
|
|
203
|
+
private handleAudioData;
|
|
204
|
+
private mergeChunks;
|
|
205
|
+
}
|
|
206
|
+
|
|
3
207
|
/**
|
|
4
208
|
* Wait strategy implementations
|
|
5
209
|
*/
|
|
@@ -503,6 +707,10 @@ declare class Page {
|
|
|
503
707
|
private currentFrameContextId;
|
|
504
708
|
/** Last matched selector from findElement (for selectorUsed tracking) */
|
|
505
709
|
private _lastMatchedSelector;
|
|
710
|
+
/** Audio input controller (lazy-initialized) */
|
|
711
|
+
private _audioInput?;
|
|
712
|
+
/** Audio output controller (lazy-initialized) */
|
|
713
|
+
private _audioOutput?;
|
|
506
714
|
constructor(cdp: CDPClient, targetId: string);
|
|
507
715
|
/**
|
|
508
716
|
* Get the CDP target ID for this page
|
|
@@ -877,6 +1085,41 @@ declare class Page {
|
|
|
877
1085
|
* Click an element by node ID
|
|
878
1086
|
*/
|
|
879
1087
|
private clickElement;
|
|
1088
|
+
/**
|
|
1089
|
+
* Audio input controller (fake microphone).
|
|
1090
|
+
* Lazy-initialized on first access.
|
|
1091
|
+
*/
|
|
1092
|
+
get audioInput(): AudioInput;
|
|
1093
|
+
/**
|
|
1094
|
+
* Audio output capture controller.
|
|
1095
|
+
* Lazy-initialized on first access.
|
|
1096
|
+
*/
|
|
1097
|
+
get audioOutput(): AudioOutput;
|
|
1098
|
+
/**
|
|
1099
|
+
* Set up both audio input (fake microphone) and output (capture).
|
|
1100
|
+
* Must be called before navigating to the page that will use audio.
|
|
1101
|
+
*/
|
|
1102
|
+
setupAudio(): Promise<void>;
|
|
1103
|
+
/**
|
|
1104
|
+
* Full audio round-trip: feed input audio, capture the response.
|
|
1105
|
+
*
|
|
1106
|
+
* 1. Starts capturing output
|
|
1107
|
+
* 2. Feeds input audio as microphone data
|
|
1108
|
+
* 3. Waits for the page to respond and then go silent
|
|
1109
|
+
* 4. Returns the captured response audio with latency metrics
|
|
1110
|
+
*
|
|
1111
|
+
* @example
|
|
1112
|
+
* ```typescript
|
|
1113
|
+
* await page.setupAudio();
|
|
1114
|
+
* await page.goto('https://voice-agent.example.com');
|
|
1115
|
+
* const result = await page.audioRoundTrip({
|
|
1116
|
+
* input: wavFileBytes,
|
|
1117
|
+
* silenceTimeout: 3000,
|
|
1118
|
+
* });
|
|
1119
|
+
* console.log(`Response: ${result.audio.durationMs}ms, latency: ${result.latencyMs}ms`);
|
|
1120
|
+
* ```
|
|
1121
|
+
*/
|
|
1122
|
+
audioRoundTrip(options: RoundTripOptions): Promise<RoundTripResult>;
|
|
880
1123
|
}
|
|
881
1124
|
|
|
882
1125
|
/**
|
|
@@ -973,4 +1216,4 @@ interface BatchResult {
|
|
|
973
1216
|
totalDurationMs: number;
|
|
974
1217
|
}
|
|
975
1218
|
|
|
976
|
-
export { type
|
|
1219
|
+
export { type InterceptedRequest as $, type ActionType as A, type BatchOptions as B, type CaptureResult as C, type Dialog as D, type ElementInfo as E, type FileInput as F, type GeolocationOptions as G, type PageSnapshot as H, type InteractiveElement as I, type SnapshotNode as J, type SubmitOptions as K, type TypeOptions as L, type UserAgentOptions as M, NavigationError as N, type DeviceDescriptor as O, type PlayOptions as P, type DeviceName as Q, type RequestPattern as R, type Step as S, TimeoutError as T, type UserAgentMetadata as U, type ViewportOptions as V, type WaitForOptions as W, devices as X, type ContinueRequestOptions as Y, type FailRequestOptions as Z, type FulfillRequestOptions as _, type RequestHandler as a, type RequestActions as a0, type ResourceType as a1, type RouteOptions as a2, type ClearCookiesOptions as a3, type Cookie as a4, type DeleteCookieOptions as a5, type SetCookieOptions as a6, type WaitOptions as a7, type WaitResult as a8, type WaitState as a9, waitForAnyElement as aa, waitForElement as ab, waitForNavigation as ac, waitForNetworkIdle as ad, type FailureHint as ae, type BatchResult as b, type StepResult as c, type AudioChunk as d, AudioInput as e, type AudioInputState as f, AudioOutput as g, type CaptureOptions as h, type RoundTripOptions as i, type RoundTripResult as j, type ActionOptions as k, type ActionResult as l, type ConsoleHandler as m, type ConsoleMessage as n, type ConsoleMessageType as o, type CustomSelectConfig as p, type DialogHandler as q, type DialogType as r, type Download as s, ElementNotFoundError as t, type EmulationState as u, type ErrorHandler as v, type FillOptions as w, type NetworkIdleOptions as x, Page as y, type PageError as z };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-pilot",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.10",
|
|
4
4
|
"description": "Lightweight CDP-based browser automation for Node.js, Bun, and Cloudflare Workers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -34,8 +34,7 @@
|
|
|
34
34
|
},
|
|
35
35
|
"./cli": {
|
|
36
36
|
"types": "./dist/cli.d.ts",
|
|
37
|
-
"import": "./dist/cli.mjs"
|
|
38
|
-
"require": "./dist/cli.cjs"
|
|
37
|
+
"import": "./dist/cli.mjs"
|
|
39
38
|
}
|
|
40
39
|
},
|
|
41
40
|
"bin": {
|
|
@@ -62,6 +61,7 @@
|
|
|
62
61
|
"check": "bun run typecheck && bun run lint",
|
|
63
62
|
"prepublishOnly": "bun run build",
|
|
64
63
|
"bp": "bun ./src/cli/index.ts",
|
|
64
|
+
"dev:bp": "bun ./src/cli/index.ts",
|
|
65
65
|
"browser-pilot": "bun ./src/cli/index.ts",
|
|
66
66
|
"fixtures:serve": "bun tests/fixtures/server.ts"
|
|
67
67
|
},
|
package/dist/chunk-ZIQA4JOT.mjs
DELETED
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
// src/emulation/devices.ts
|
|
2
|
-
var devices = {
|
|
3
|
-
"iPhone 14": {
|
|
4
|
-
name: "iPhone 14",
|
|
5
|
-
viewport: {
|
|
6
|
-
width: 390,
|
|
7
|
-
height: 844,
|
|
8
|
-
deviceScaleFactor: 3,
|
|
9
|
-
isMobile: true,
|
|
10
|
-
hasTouch: true
|
|
11
|
-
},
|
|
12
|
-
userAgent: {
|
|
13
|
-
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
|
14
|
-
platform: "iPhone",
|
|
15
|
-
userAgentMetadata: {
|
|
16
|
-
mobile: true,
|
|
17
|
-
platform: "iOS",
|
|
18
|
-
platformVersion: "16.0"
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
},
|
|
22
|
-
"iPhone 14 Pro Max": {
|
|
23
|
-
name: "iPhone 14 Pro Max",
|
|
24
|
-
viewport: {
|
|
25
|
-
width: 430,
|
|
26
|
-
height: 932,
|
|
27
|
-
deviceScaleFactor: 3,
|
|
28
|
-
isMobile: true,
|
|
29
|
-
hasTouch: true
|
|
30
|
-
},
|
|
31
|
-
userAgent: {
|
|
32
|
-
userAgent: "Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
|
33
|
-
platform: "iPhone"
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
"Pixel 7": {
|
|
37
|
-
name: "Pixel 7",
|
|
38
|
-
viewport: {
|
|
39
|
-
width: 412,
|
|
40
|
-
height: 915,
|
|
41
|
-
deviceScaleFactor: 2.625,
|
|
42
|
-
isMobile: true,
|
|
43
|
-
hasTouch: true
|
|
44
|
-
},
|
|
45
|
-
userAgent: {
|
|
46
|
-
userAgent: "Mozilla/5.0 (Linux; Android 13; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36",
|
|
47
|
-
platform: "Linux armv8l",
|
|
48
|
-
userAgentMetadata: {
|
|
49
|
-
mobile: true,
|
|
50
|
-
platform: "Android",
|
|
51
|
-
platformVersion: "13",
|
|
52
|
-
model: "Pixel 7"
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
"iPad Pro 11": {
|
|
57
|
-
name: "iPad Pro 11",
|
|
58
|
-
viewport: {
|
|
59
|
-
width: 834,
|
|
60
|
-
height: 1194,
|
|
61
|
-
deviceScaleFactor: 2,
|
|
62
|
-
isMobile: true,
|
|
63
|
-
hasTouch: true
|
|
64
|
-
},
|
|
65
|
-
userAgent: {
|
|
66
|
-
userAgent: "Mozilla/5.0 (iPad; CPU OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/16.0 Mobile/15E148 Safari/604.1",
|
|
67
|
-
platform: "iPad"
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
"Desktop Chrome": {
|
|
71
|
-
name: "Desktop Chrome",
|
|
72
|
-
viewport: {
|
|
73
|
-
width: 1920,
|
|
74
|
-
height: 1080,
|
|
75
|
-
deviceScaleFactor: 1,
|
|
76
|
-
isMobile: false,
|
|
77
|
-
hasTouch: false
|
|
78
|
-
},
|
|
79
|
-
userAgent: {
|
|
80
|
-
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
81
|
-
platform: "Win32",
|
|
82
|
-
userAgentMetadata: {
|
|
83
|
-
brands: [
|
|
84
|
-
{ brand: "Not_A Brand", version: "8" },
|
|
85
|
-
{ brand: "Chromium", version: "120" },
|
|
86
|
-
{ brand: "Google Chrome", version: "120" }
|
|
87
|
-
],
|
|
88
|
-
platform: "Windows",
|
|
89
|
-
platformVersion: "10.0.0",
|
|
90
|
-
architecture: "x86",
|
|
91
|
-
bitness: "64",
|
|
92
|
-
mobile: false
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
"Desktop Firefox": {
|
|
97
|
-
name: "Desktop Firefox",
|
|
98
|
-
viewport: {
|
|
99
|
-
width: 1920,
|
|
100
|
-
height: 1080,
|
|
101
|
-
deviceScaleFactor: 1,
|
|
102
|
-
isMobile: false,
|
|
103
|
-
hasTouch: false
|
|
104
|
-
},
|
|
105
|
-
userAgent: {
|
|
106
|
-
userAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0",
|
|
107
|
-
platform: "Win32"
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// src/trace/tracer.ts
|
|
113
|
-
var LEVEL_ORDER = {
|
|
114
|
-
debug: 0,
|
|
115
|
-
info: 1,
|
|
116
|
-
warn: 2,
|
|
117
|
-
error: 3
|
|
118
|
-
};
|
|
119
|
-
var Tracer = class _Tracer {
|
|
120
|
-
options;
|
|
121
|
-
constructor(options = {}) {
|
|
122
|
-
this.options = {
|
|
123
|
-
enabled: options.enabled ?? false,
|
|
124
|
-
output: options.output ?? "console",
|
|
125
|
-
callback: options.callback,
|
|
126
|
-
level: options.level ?? "info",
|
|
127
|
-
includeTimings: options.includeTimings ?? true
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Emit a trace event
|
|
132
|
-
*/
|
|
133
|
-
emit(event) {
|
|
134
|
-
if (!this.options.enabled) return;
|
|
135
|
-
if (LEVEL_ORDER[event.level] < LEVEL_ORDER[this.options.level]) {
|
|
136
|
-
return;
|
|
137
|
-
}
|
|
138
|
-
const fullEvent = {
|
|
139
|
-
...event,
|
|
140
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
141
|
-
};
|
|
142
|
-
switch (this.options.output) {
|
|
143
|
-
case "console":
|
|
144
|
-
this.logToConsole(fullEvent);
|
|
145
|
-
break;
|
|
146
|
-
case "callback":
|
|
147
|
-
this.options.callback?.(fullEvent);
|
|
148
|
-
break;
|
|
149
|
-
case "silent":
|
|
150
|
-
break;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Log event to console
|
|
155
|
-
*/
|
|
156
|
-
logToConsole(event) {
|
|
157
|
-
const { level, category, action, selectorUsed, success, durationMs, error } = event;
|
|
158
|
-
const icon = success === true ? "\u2713" : success === false ? "\u2717" : "\u25CB";
|
|
159
|
-
const timing = this.options.includeTimings && durationMs !== void 0 ? ` (${durationMs}ms)` : "";
|
|
160
|
-
const selector = selectorUsed ? ` ${selectorUsed}` : "";
|
|
161
|
-
const errorStr = error ? ` - ${error}` : "";
|
|
162
|
-
const message = `[${level.toUpperCase()}] [${category}] ${icon} ${action}${selector}${timing}${errorStr}`;
|
|
163
|
-
switch (level) {
|
|
164
|
-
case "debug":
|
|
165
|
-
console.debug(message);
|
|
166
|
-
break;
|
|
167
|
-
case "info":
|
|
168
|
-
console.info(message);
|
|
169
|
-
break;
|
|
170
|
-
case "warn":
|
|
171
|
-
console.warn(message);
|
|
172
|
-
break;
|
|
173
|
-
case "error":
|
|
174
|
-
console.error(message);
|
|
175
|
-
break;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Create a child tracer with modified options
|
|
180
|
-
*/
|
|
181
|
-
child(options) {
|
|
182
|
-
return new _Tracer({ ...this.options, ...options });
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Enable tracing
|
|
186
|
-
*/
|
|
187
|
-
enable() {
|
|
188
|
-
this.options.enabled = true;
|
|
189
|
-
}
|
|
190
|
-
/**
|
|
191
|
-
* Disable tracing
|
|
192
|
-
*/
|
|
193
|
-
disable() {
|
|
194
|
-
this.options.enabled = false;
|
|
195
|
-
}
|
|
196
|
-
/**
|
|
197
|
-
* Check if tracing is enabled
|
|
198
|
-
*/
|
|
199
|
-
get isEnabled() {
|
|
200
|
-
return this.options.enabled;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
var globalTracer = null;
|
|
204
|
-
function getTracer() {
|
|
205
|
-
if (!globalTracer) {
|
|
206
|
-
globalTracer = new Tracer({ enabled: false });
|
|
207
|
-
}
|
|
208
|
-
return globalTracer;
|
|
209
|
-
}
|
|
210
|
-
function enableTracing(options = {}) {
|
|
211
|
-
globalTracer = new Tracer({ ...options, enabled: true });
|
|
212
|
-
return globalTracer;
|
|
213
|
-
}
|
|
214
|
-
function disableTracing() {
|
|
215
|
-
if (globalTracer) {
|
|
216
|
-
globalTracer.disable();
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
export {
|
|
221
|
-
devices,
|
|
222
|
-
Tracer,
|
|
223
|
-
getTracer,
|
|
224
|
-
enableTracing,
|
|
225
|
-
disableTracing
|
|
226
|
-
};
|