@monoes/monobrowse 1.0.9 → 1.0.12

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.
Files changed (221) hide show
  1. package/README.md +1 -1
  2. package/dist/src/__tests__/adapters.test.js +5 -2
  3. package/dist/src/__tests__/adapters.test.js.map +1 -1
  4. package/dist/src/__tests__/batch-eval-parsing.test.js +4 -2
  5. package/dist/src/__tests__/batch-eval-parsing.test.js.map +1 -1
  6. package/dist/src/__tests__/box-model.test.js +9 -5
  7. package/dist/src/__tests__/box-model.test.js.map +1 -1
  8. package/dist/src/__tests__/browser-launch.test.d.ts +10 -0
  9. package/dist/src/__tests__/browser-launch.test.d.ts.map +1 -1
  10. package/dist/src/__tests__/browser-launch.test.js +17 -10
  11. package/dist/src/__tests__/browser-launch.test.js.map +1 -1
  12. package/dist/src/__tests__/cdp-client.test.js +9 -3
  13. package/dist/src/__tests__/cdp-client.test.js.map +1 -1
  14. package/dist/src/__tests__/close-browser.test.d.ts +13 -0
  15. package/dist/src/__tests__/close-browser.test.d.ts.map +1 -1
  16. package/dist/src/__tests__/close-browser.test.js +40 -26
  17. package/dist/src/__tests__/close-browser.test.js.map +1 -1
  18. package/dist/src/__tests__/console-log.test.js +17 -6
  19. package/dist/src/__tests__/console-log.test.js.map +1 -1
  20. package/dist/src/__tests__/find.test.js +26 -6
  21. package/dist/src/__tests__/find.test.js.map +1 -1
  22. package/dist/src/__tests__/frame-command.test.js +9 -3
  23. package/dist/src/__tests__/frame-command.test.js.map +1 -1
  24. package/dist/src/__tests__/open-url-navigate-error.test.js +1 -3
  25. package/dist/src/__tests__/open-url-navigate-error.test.js.map +1 -1
  26. package/dist/src/__tests__/output.test.js +1 -1
  27. package/dist/src/__tests__/output.test.js.map +1 -1
  28. package/dist/src/__tests__/reap-idle-browser.test.d.ts +15 -0
  29. package/dist/src/__tests__/reap-idle-browser.test.d.ts.map +1 -0
  30. package/dist/src/__tests__/reap-idle-browser.test.js +238 -0
  31. package/dist/src/__tests__/reap-idle-browser.test.js.map +1 -0
  32. package/dist/src/__tests__/ref-cache.test.js +18 -10
  33. package/dist/src/__tests__/ref-cache.test.js.map +1 -1
  34. package/dist/src/__tests__/session-state.test.d.ts +8 -0
  35. package/dist/src/__tests__/session-state.test.d.ts.map +1 -1
  36. package/dist/src/__tests__/session-state.test.js +10 -10
  37. package/dist/src/__tests__/session-state.test.js.map +1 -1
  38. package/dist/src/__tests__/snapshot.test.js +45 -15
  39. package/dist/src/__tests__/snapshot.test.js.map +1 -1
  40. package/dist/src/__tests__/storage-eval.test.js +14 -6
  41. package/dist/src/__tests__/storage-eval.test.js.map +1 -1
  42. package/dist/src/__tests__/tabs.test.js +25 -9
  43. package/dist/src/__tests__/tabs.test.js.map +1 -1
  44. package/dist/src/browser/action-builder/analyzer.d.ts +1 -1
  45. package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -1
  46. package/dist/src/browser/action-builder/analyzer.js +25 -9
  47. package/dist/src/browser/action-builder/analyzer.js.map +1 -1
  48. package/dist/src/browser/action-builder/types.d.ts.map +1 -1
  49. package/dist/src/browser/actions.d.ts +1 -1
  50. package/dist/src/browser/actions.d.ts.map +1 -1
  51. package/dist/src/browser/actions.js +16 -6
  52. package/dist/src/browser/actions.js.map +1 -1
  53. package/dist/src/browser/adapters/gemini.d.ts.map +1 -1
  54. package/dist/src/browser/adapters/gemini.js.map +1 -1
  55. package/dist/src/browser/adapters/google.d.ts.map +1 -1
  56. package/dist/src/browser/adapters/google.js.map +1 -1
  57. package/dist/src/browser/adapters/index.d.ts.map +1 -1
  58. package/dist/src/browser/adapters/index.js +3 -3
  59. package/dist/src/browser/adapters/index.js.map +1 -1
  60. package/dist/src/browser/adapters/instagram.d.ts.map +1 -1
  61. package/dist/src/browser/adapters/instagram.js.map +1 -1
  62. package/dist/src/browser/adapters/linkedin.d.ts.map +1 -1
  63. package/dist/src/browser/adapters/linkedin.js.map +1 -1
  64. package/dist/src/browser/adapters/microsoft.d.ts.map +1 -1
  65. package/dist/src/browser/adapters/microsoft.js.map +1 -1
  66. package/dist/src/browser/adapters/x.d.ts.map +1 -1
  67. package/dist/src/browser/adapters/x.js.map +1 -1
  68. package/dist/src/browser/browser.d.ts +30 -0
  69. package/dist/src/browser/browser.d.ts.map +1 -1
  70. package/dist/src/browser/browser.js +122 -15
  71. package/dist/src/browser/browser.js.map +1 -1
  72. package/dist/src/browser/cdp.d.ts.map +1 -1
  73. package/dist/src/browser/cdp.js +21 -8
  74. package/dist/src/browser/cdp.js.map +1 -1
  75. package/dist/src/browser/console-log.d.ts.map +1 -1
  76. package/dist/src/browser/console-log.js +2 -2
  77. package/dist/src/browser/console-log.js.map +1 -1
  78. package/dist/src/browser/dashboard/server.d.ts.map +1 -1
  79. package/dist/src/browser/dashboard/server.js +29 -19
  80. package/dist/src/browser/dashboard/server.js.map +1 -1
  81. package/dist/src/browser/dashboard/ui.html +153 -165
  82. package/dist/src/browser/dialog.d.ts.map +1 -1
  83. package/dist/src/browser/dialog.js +3 -1
  84. package/dist/src/browser/dialog.js.map +1 -1
  85. package/dist/src/browser/emulation.d.ts.map +1 -1
  86. package/dist/src/browser/emulation.js +21 -6
  87. package/dist/src/browser/emulation.js.map +1 -1
  88. package/dist/src/browser/find.d.ts +4 -4
  89. package/dist/src/browser/find.d.ts.map +1 -1
  90. package/dist/src/browser/find.js +14 -6
  91. package/dist/src/browser/find.js.map +1 -1
  92. package/dist/src/browser/har.d.ts.map +1 -1
  93. package/dist/src/browser/har.js +14 -7
  94. package/dist/src/browser/har.js.map +1 -1
  95. package/dist/src/browser/index.d.ts +16 -16
  96. package/dist/src/browser/index.d.ts.map +1 -1
  97. package/dist/src/browser/index.js +16 -16
  98. package/dist/src/browser/index.js.map +1 -1
  99. package/dist/src/browser/network.d.ts.map +1 -1
  100. package/dist/src/browser/network.js +14 -4
  101. package/dist/src/browser/network.js.map +1 -1
  102. package/dist/src/browser/pdf.d.ts.map +1 -1
  103. package/dist/src/browser/pdf.js +3 -3
  104. package/dist/src/browser/pdf.js.map +1 -1
  105. package/dist/src/browser/profiler.d.ts.map +1 -1
  106. package/dist/src/browser/profiler.js +10 -5
  107. package/dist/src/browser/profiler.js.map +1 -1
  108. package/dist/src/browser/record.d.ts +1 -1
  109. package/dist/src/browser/record.d.ts.map +1 -1
  110. package/dist/src/browser/record.js +18 -7
  111. package/dist/src/browser/record.js.map +1 -1
  112. package/dist/src/browser/ref-cache.d.ts.map +1 -1
  113. package/dist/src/browser/ref-cache.js +9 -4
  114. package/dist/src/browser/ref-cache.js.map +1 -1
  115. package/dist/src/browser/screenshot.d.ts.map +1 -1
  116. package/dist/src/browser/screenshot.js +11 -7
  117. package/dist/src/browser/screenshot.js.map +1 -1
  118. package/dist/src/browser/session.d.ts +1 -1
  119. package/dist/src/browser/session.d.ts.map +1 -1
  120. package/dist/src/browser/session.js +27 -8
  121. package/dist/src/browser/session.js.map +1 -1
  122. package/dist/src/browser/snapshot.d.ts +1 -1
  123. package/dist/src/browser/snapshot.d.ts.map +1 -1
  124. package/dist/src/browser/snapshot.js +8 -4
  125. package/dist/src/browser/snapshot.js.map +1 -1
  126. package/dist/src/browser/storage.d.ts.map +1 -1
  127. package/dist/src/browser/storage.js.map +1 -1
  128. package/dist/src/browser/tabs.d.ts.map +1 -1
  129. package/dist/src/browser/tabs.js +9 -3
  130. package/dist/src/browser/tabs.js.map +1 -1
  131. package/dist/src/browser/trace.d.ts.map +1 -1
  132. package/dist/src/browser/trace.js +3 -3
  133. package/dist/src/browser/trace.js.map +1 -1
  134. package/dist/src/browser/types.d.ts.map +1 -1
  135. package/dist/src/browser/types.js +23 -4
  136. package/dist/src/browser/types.js.map +1 -1
  137. package/dist/src/browser/vitals.d.ts.map +1 -1
  138. package/dist/src/browser/vitals.js +1 -1
  139. package/dist/src/browser/vitals.js.map +1 -1
  140. package/dist/src/browser/wait.d.ts.map +1 -1
  141. package/dist/src/browser/wait.js +11 -5
  142. package/dist/src/browser/wait.js.map +1 -1
  143. package/dist/src/cli/action.d.ts.map +1 -1
  144. package/dist/src/cli/action.js +14 -11
  145. package/dist/src/cli/action.js.map +1 -1
  146. package/dist/src/cli/commands.d.ts.map +1 -1
  147. package/dist/src/cli/commands.js +453 -159
  148. package/dist/src/cli/commands.js.map +1 -1
  149. package/dist/src/cli/output.js +5 -5
  150. package/dist/src/cli/output.js.map +1 -1
  151. package/dist/src/cli/platform.d.ts.map +1 -1
  152. package/dist/src/cli/platform.js +15 -11
  153. package/dist/src/cli/platform.js.map +1 -1
  154. package/dist/src/cli/types.d.ts +1 -1
  155. package/dist/src/cli/types.d.ts.map +1 -1
  156. package/dist/src/cli.js +21 -9
  157. package/dist/src/cli.js.map +1 -1
  158. package/dist/src/index.d.ts +2 -2
  159. package/dist/src/index.d.ts.map +1 -1
  160. package/dist/src/index.js +2 -2
  161. package/dist/src/index.js.map +1 -1
  162. package/dist/tsconfig.tsbuildinfo +1 -1
  163. package/package.json +2 -2
  164. package/src/__tests__/adapters.test.ts +15 -8
  165. package/src/__tests__/batch-eval-parsing.test.ts +4 -2
  166. package/src/__tests__/box-model.test.ts +9 -5
  167. package/src/__tests__/browser-launch.test.ts +26 -15
  168. package/src/__tests__/cdp-client.test.ts +25 -9
  169. package/src/__tests__/close-browser.test.ts +49 -26
  170. package/src/__tests__/console-log.test.ts +36 -13
  171. package/src/__tests__/find.test.ts +58 -20
  172. package/src/__tests__/frame-command.test.ts +12 -6
  173. package/src/__tests__/open-url-navigate-error.test.ts +2 -4
  174. package/src/__tests__/output.test.ts +1 -1
  175. package/src/__tests__/reap-idle-browser.test.ts +276 -0
  176. package/src/__tests__/ref-cache.test.ts +25 -12
  177. package/src/__tests__/session-state.test.ts +40 -28
  178. package/src/__tests__/snapshot.test.ts +61 -22
  179. package/src/__tests__/storage-eval.test.ts +30 -16
  180. package/src/__tests__/tabs.test.ts +26 -10
  181. package/src/browser/action-builder/analyzer.ts +39 -15
  182. package/src/browser/action-builder/types.ts +45 -8
  183. package/src/browser/actions.ts +288 -123
  184. package/src/browser/adapters/gemini.ts +7 -3
  185. package/src/browser/adapters/google.ts +5 -3
  186. package/src/browser/adapters/index.ts +5 -4
  187. package/src/browser/adapters/instagram.ts +4 -2
  188. package/src/browser/adapters/linkedin.ts +4 -2
  189. package/src/browser/adapters/microsoft.ts +9 -5
  190. package/src/browser/adapters/x.ts +7 -3
  191. package/src/browser/browser.ts +188 -42
  192. package/src/browser/cdp.ts +43 -11
  193. package/src/browser/console-log.ts +24 -7
  194. package/src/browser/dashboard/server.ts +44 -25
  195. package/src/browser/dashboard/ui.html +153 -165
  196. package/src/browser/dialog.ts +25 -8
  197. package/src/browser/emulation.ts +86 -36
  198. package/src/browser/find.ts +110 -47
  199. package/src/browser/har.ts +58 -26
  200. package/src/browser/index.ts +16 -16
  201. package/src/browser/network.ts +147 -46
  202. package/src/browser/pdf.ts +4 -4
  203. package/src/browser/profiler.ts +23 -10
  204. package/src/browser/record.ts +38 -19
  205. package/src/browser/ref-cache.ts +38 -17
  206. package/src/browser/screenshot.ts +58 -32
  207. package/src/browser/session.ts +51 -18
  208. package/src/browser/snapshot.ts +34 -20
  209. package/src/browser/storage.ts +73 -17
  210. package/src/browser/tabs.ts +29 -17
  211. package/src/browser/trace.ts +14 -10
  212. package/src/browser/types.ts +23 -4
  213. package/src/browser/vitals.ts +33 -16
  214. package/src/browser/wait.ts +47 -21
  215. package/src/cli/action.ts +52 -21
  216. package/src/cli/commands.ts +1093 -385
  217. package/src/cli/output.ts +5 -5
  218. package/src/cli/platform.ts +44 -23
  219. package/src/cli/types.ts +1 -1
  220. package/src/cli.ts +34 -18
  221. package/src/index.ts +2 -2
@@ -1,13 +1,13 @@
1
- import { resolve } from 'path';
1
+ import { resolve } from 'node:path';
2
2
  import type { CdpClient } from './cdp.js';
3
- import type { ElementRef, ClickOptions } from './types.js';
4
- import { getObjectIdForRef, getElementBox } from './snapshot.js';
3
+ import { getElementBox, getObjectIdForRef } from './snapshot.js';
4
+ import type { ClickOptions, ElementRef } from './types.js';
5
5
 
6
6
  export async function clickElement(
7
7
  client: CdpClient,
8
8
  sessionId: string,
9
9
  ref: ElementRef,
10
- options: ClickOptions = {}
10
+ options: ClickOptions = {},
11
11
  ): Promise<void> {
12
12
  const box = await getElementBox(client, sessionId, ref);
13
13
 
@@ -19,11 +19,15 @@ export async function clickElement(
19
19
  // Fallback: use JS click via objectId
20
20
  const objectId = await getObjectIdForRef(client, sessionId, ref);
21
21
  if (objectId) {
22
- await client.send('Runtime.callFunctionOn', {
23
- functionDeclaration: 'function() { this.click(); }',
24
- objectId,
25
- returnByValue: true,
26
- }, sessionId);
22
+ await client.send(
23
+ 'Runtime.callFunctionOn',
24
+ {
25
+ functionDeclaration: 'function() { this.click(); }',
26
+ objectId,
27
+ returnByValue: true,
28
+ },
29
+ sessionId,
30
+ );
27
31
  return;
28
32
  }
29
33
 
@@ -35,7 +39,7 @@ export async function clickPoint(
35
39
  sessionId: string,
36
40
  x: number,
37
41
  y: number,
38
- options: ClickOptions = {}
42
+ options: ClickOptions = {},
39
43
  ): Promise<void> {
40
44
  const button = options.button ?? 'left';
41
45
  // Cap clickCount to prevent unbounded loop DoS
@@ -47,8 +51,21 @@ export async function clickPoint(
47
51
 
48
52
  for (let i = 0; i < clickCount; i++) {
49
53
  const count = i + 1;
50
- await client.send('Input.dispatchMouseEvent', { ...shared, type: 'mousePressed', buttons: buttonsMask, clickCount: count }, sessionId);
51
- await client.send('Input.dispatchMouseEvent', { ...shared, type: 'mouseReleased', buttons: i < clickCount - 1 ? buttonsMask : 0, clickCount: count }, sessionId);
54
+ await client.send(
55
+ 'Input.dispatchMouseEvent',
56
+ { ...shared, type: 'mousePressed', buttons: buttonsMask, clickCount: count },
57
+ sessionId,
58
+ );
59
+ await client.send(
60
+ 'Input.dispatchMouseEvent',
61
+ {
62
+ ...shared,
63
+ type: 'mouseReleased',
64
+ buttons: i < clickCount - 1 ? buttonsMask : 0,
65
+ clickCount: count,
66
+ },
67
+ sessionId,
68
+ );
52
69
  }
53
70
  }
54
71
 
@@ -56,7 +73,7 @@ export async function fillElement(
56
73
  client: CdpClient,
57
74
  sessionId: string,
58
75
  ref: ElementRef,
59
- value: string
76
+ value: string,
60
77
  ): Promise<void> {
61
78
  const box = await getElementBox(client, sessionId, ref);
62
79
 
@@ -68,8 +85,13 @@ export async function fillElement(
68
85
  // Select all and replace
69
86
  const objectId = await getObjectIdForRef(client, sessionId, ref);
70
87
  if (objectId) {
71
- const fillSelectResult = await client.send<{ result: unknown; exceptionDetails?: { text: string; exception?: { description?: string } } }>('Runtime.callFunctionOn', {
72
- functionDeclaration: `function() {
88
+ const fillSelectResult = await client.send<{
89
+ result: unknown;
90
+ exceptionDetails?: { text: string; exception?: { description?: string } };
91
+ }>(
92
+ 'Runtime.callFunctionOn',
93
+ {
94
+ functionDeclaration: `function() {
73
95
  this.focus();
74
96
  if (this.tagName === 'INPUT' || this.tagName === 'TEXTAREA') {
75
97
  this.select();
@@ -80,11 +102,15 @@ export async function fillElement(
80
102
  if (sel) { sel.removeAllRanges(); sel.addRange(range); }
81
103
  }
82
104
  }`,
83
- objectId,
84
- returnByValue: true,
85
- }, sessionId);
105
+ objectId,
106
+ returnByValue: true,
107
+ },
108
+ sessionId,
109
+ );
86
110
  if (fillSelectResult.exceptionDetails) {
87
- throw new Error(`fillElement select-all failed: ${fillSelectResult.exceptionDetails.exception?.description ?? fillSelectResult.exceptionDetails.text}`);
111
+ throw new Error(
112
+ `fillElement select-all failed: ${fillSelectResult.exceptionDetails.exception?.description ?? fillSelectResult.exceptionDetails.text}`,
113
+ );
88
114
  }
89
115
  } else if (box) {
90
116
  // Fallback for elements without a resolvable objectId: keyboard select-all clears existing content
@@ -108,42 +134,62 @@ export async function pressKeyCombo(
108
134
  client: CdpClient,
109
135
  sessionId: string,
110
136
  key: string,
111
- modifiers: number
137
+ modifiers: number,
112
138
  ): Promise<void> {
113
139
  const { text: _text, ...keyInfo } = resolveKey(key);
114
140
  // rawKeyDown prevents Chrome from inserting the character text; only the shortcut fires
115
- await client.send('Input.dispatchKeyEvent', { type: 'rawKeyDown', ...keyInfo, modifiers }, sessionId);
141
+ await client.send(
142
+ 'Input.dispatchKeyEvent',
143
+ { type: 'rawKeyDown', ...keyInfo, modifiers },
144
+ sessionId,
145
+ );
116
146
  await client.send('Input.dispatchKeyEvent', { type: 'keyUp', ...keyInfo, modifiers }, sessionId);
117
147
  }
118
148
 
119
- export async function pressKey(
120
- client: CdpClient,
121
- sessionId: string,
122
- key: string
123
- ): Promise<void> {
149
+ export async function pressKey(client: CdpClient, sessionId: string, key: string): Promise<void> {
124
150
  const { text, ...keyInfo } = resolveKey(key);
125
151
 
126
152
  // rawKeyDown does not insert text; the explicit char event handles insertion
127
- await client.send('Input.dispatchKeyEvent', {
128
- type: 'rawKeyDown',
129
- ...keyInfo,
130
- }, sessionId);
153
+ await client.send(
154
+ 'Input.dispatchKeyEvent',
155
+ {
156
+ type: 'rawKeyDown',
157
+ ...keyInfo,
158
+ },
159
+ sessionId,
160
+ );
131
161
 
132
162
  if (text) {
133
- await client.send('Input.dispatchKeyEvent', {
134
- type: 'char',
135
- text,
136
- }, sessionId);
163
+ await client.send(
164
+ 'Input.dispatchKeyEvent',
165
+ {
166
+ type: 'char',
167
+ text,
168
+ },
169
+ sessionId,
170
+ );
137
171
  }
138
172
 
139
- await client.send('Input.dispatchKeyEvent', {
140
- type: 'keyUp',
141
- ...keyInfo,
142
- }, sessionId);
173
+ await client.send(
174
+ 'Input.dispatchKeyEvent',
175
+ {
176
+ type: 'keyUp',
177
+ ...keyInfo,
178
+ },
179
+ sessionId,
180
+ );
143
181
  }
144
182
 
145
- function resolveKey(key: string): { key: string; code: string; text?: string; windowsVirtualKeyCode?: number } {
146
- const keyMap: Record<string, { key: string; code: string; text?: string; windowsVirtualKeyCode?: number }> = {
183
+ function resolveKey(key: string): {
184
+ key: string;
185
+ code: string;
186
+ text?: string;
187
+ windowsVirtualKeyCode?: number;
188
+ } {
189
+ const keyMap: Record<
190
+ string,
191
+ { key: string; code: string; text?: string; windowsVirtualKeyCode?: number }
192
+ > = {
147
193
  Enter: { key: 'Enter', code: 'Enter', text: '\r', windowsVirtualKeyCode: 13 },
148
194
  Tab: { key: 'Tab', code: 'Tab', windowsVirtualKeyCode: 9 },
149
195
  Escape: { key: 'Escape', code: 'Escape', windowsVirtualKeyCode: 27 },
@@ -186,14 +232,19 @@ function resolveKey(key: string): { key: string; code: string; text?: string; wi
186
232
  return { key, code: `Digit${key}`, text: key, windowsVirtualKeyCode: charCode };
187
233
  }
188
234
  if ((charCode >= 65 && charCode <= 90) || (charCode >= 97 && charCode <= 122)) {
189
- return { key, code: `Key${key.toUpperCase()}`, text: key, windowsVirtualKeyCode: key.toUpperCase().charCodeAt(0) };
235
+ return {
236
+ key,
237
+ code: `Key${key.toUpperCase()}`,
238
+ text: key,
239
+ windowsVirtualKeyCode: key.toUpperCase().charCodeAt(0),
240
+ };
190
241
  }
191
242
  // Symbol keys: map to the physical key's code and Windows virtual key code
192
243
  const symbolMap: Record<string, { code: string; windowsVirtualKeyCode: number }> = {
193
244
  '!': { code: 'Digit1', windowsVirtualKeyCode: 49 },
194
245
  '@': { code: 'Digit2', windowsVirtualKeyCode: 50 },
195
246
  '#': { code: 'Digit3', windowsVirtualKeyCode: 51 },
196
- '$': { code: 'Digit4', windowsVirtualKeyCode: 52 },
247
+ $: { code: 'Digit4', windowsVirtualKeyCode: 52 },
197
248
  '%': { code: 'Digit5', windowsVirtualKeyCode: 53 },
198
249
  '^': { code: 'Digit6', windowsVirtualKeyCode: 54 },
199
250
  '&': { code: 'Digit7', windowsVirtualKeyCode: 55 },
@@ -201,7 +252,7 @@ function resolveKey(key: string): { key: string; code: string; text?: string; wi
201
252
  '(': { code: 'Digit9', windowsVirtualKeyCode: 57 },
202
253
  ')': { code: 'Digit0', windowsVirtualKeyCode: 48 },
203
254
  '-': { code: 'Minus', windowsVirtualKeyCode: 189 },
204
- '_': { code: 'Minus', windowsVirtualKeyCode: 189 },
255
+ _: { code: 'Minus', windowsVirtualKeyCode: 189 },
205
256
  '=': { code: 'Equal', windowsVirtualKeyCode: 187 },
206
257
  '+': { code: 'Equal', windowsVirtualKeyCode: 187 },
207
258
  '[': { code: 'BracketLeft', windowsVirtualKeyCode: 219 },
@@ -224,7 +275,8 @@ function resolveKey(key: string): { key: string; code: string; text?: string; wi
224
275
  '?': { code: 'Slash', windowsVirtualKeyCode: 191 },
225
276
  };
226
277
  const sym = symbolMap[key];
227
- if (sym) return { key, code: sym.code, text: key, windowsVirtualKeyCode: sym.windowsVirtualKeyCode };
278
+ if (sym)
279
+ return { key, code: sym.code, text: key, windowsVirtualKeyCode: sym.windowsVirtualKeyCode };
228
280
  // Unknown/non-ASCII character — omit code since no valid DOM KeyboardEvent.code exists
229
281
  return { key, code: '', text: key };
230
282
  }
@@ -237,7 +289,7 @@ export async function scrollElement(
237
289
  sessionId: string,
238
290
  direction: 'up' | 'down' | 'left' | 'right',
239
291
  amount = 300,
240
- ref?: ElementRef
292
+ ref?: ElementRef,
241
293
  ): Promise<void> {
242
294
  // Cap scroll amount to prevent extreme delta values
243
295
  amount = Math.min(Math.max(1, Math.floor(amount)), 100_000);
@@ -249,60 +301,86 @@ export async function scrollElement(
249
301
  if (ref) {
250
302
  const box = await getElementBox(client, sessionId, ref);
251
303
  if (!box) throw new Error(`Cannot scroll: ref @${ref.ref} not found in DOM`);
252
- x = box.x; y = box.y;
304
+ x = box.x;
305
+ y = box.y;
253
306
  } else {
254
307
  // Center of viewport
255
- const vp = await client.send<{ result: { value: string } }>('Runtime.evaluate', {
256
- expression: 'JSON.stringify({w: window.innerWidth, h: window.innerHeight})',
257
- returnByValue: true,
258
- }, sessionId);
308
+ const vp = await client.send<{ result: { value: string } }>(
309
+ 'Runtime.evaluate',
310
+ {
311
+ expression: 'JSON.stringify({w: window.innerWidth, h: window.innerHeight})',
312
+ returnByValue: true,
313
+ },
314
+ sessionId,
315
+ );
259
316
  const dims = JSON.parse(vp.result?.value ?? '{"w":1280,"h":720}');
260
317
  x = dims.w / 2;
261
318
  y = dims.h / 2;
262
319
  }
263
320
 
264
321
  switch (direction) {
265
- case 'down': deltaY = amount; break;
266
- case 'up': deltaY = -amount; break;
267
- case 'right': deltaX = amount; break;
268
- case 'left': deltaX = -amount; break;
322
+ case 'down':
323
+ deltaY = amount;
324
+ break;
325
+ case 'up':
326
+ deltaY = -amount;
327
+ break;
328
+ case 'right':
329
+ deltaX = amount;
330
+ break;
331
+ case 'left':
332
+ deltaX = -amount;
333
+ break;
269
334
  }
270
335
 
271
- await client.send('Input.dispatchMouseEvent', {
272
- type: 'mouseWheel',
273
- x,
274
- y,
275
- deltaX,
276
- deltaY,
277
- }, sessionId);
336
+ await client.send(
337
+ 'Input.dispatchMouseEvent',
338
+ {
339
+ type: 'mouseWheel',
340
+ x,
341
+ y,
342
+ deltaX,
343
+ deltaY,
344
+ },
345
+ sessionId,
346
+ );
278
347
  }
279
348
 
280
349
  export async function hoverElement(
281
350
  client: CdpClient,
282
351
  sessionId: string,
283
- ref: ElementRef
352
+ ref: ElementRef,
284
353
  ): Promise<void> {
285
354
  const box = await getElementBox(client, sessionId, ref);
286
355
  if (!box) throw new Error(`Cannot hover ref @${ref.ref}: element not found in DOM`);
287
356
 
288
- await client.send('Input.dispatchMouseEvent', {
289
- type: 'mouseMoved',
290
- x: box.x,
291
- y: box.y,
292
- }, sessionId);
357
+ await client.send(
358
+ 'Input.dispatchMouseEvent',
359
+ {
360
+ type: 'mouseMoved',
361
+ x: box.x,
362
+ y: box.y,
363
+ },
364
+ sessionId,
365
+ );
293
366
  }
294
367
 
295
368
  export async function selectOption(
296
369
  client: CdpClient,
297
370
  sessionId: string,
298
371
  ref: ElementRef,
299
- value: string
372
+ value: string,
300
373
  ): Promise<void> {
301
374
  const objectId = await getObjectIdForRef(client, sessionId, ref);
302
375
  if (!objectId) throw new Error(`Cannot select: ref @${ref.ref} not found in DOM`);
303
376
 
304
- const selectResult = await client.send<{ result: unknown; exceptionDetails?: { text: string; exception?: { description?: string } } }>('Runtime.callFunctionOn', {
305
- functionDeclaration: `function(value) {
377
+ const selectResult = await client.send<{
378
+ result: unknown;
379
+ exceptionDetails?: { text: string; exception?: { description?: string } };
380
+ }>(
381
+ 'Runtime.callFunctionOn',
382
+ {
383
+ functionDeclaration: `function(value) {
306
384
  if (this.tagName !== 'SELECT') throw new Error('Not a select element');
307
385
  for (const opt of this.options) {
308
386
  if (opt.value === value || opt.textContent.trim() === value) {
@@ -313,12 +391,16 @@ export async function selectOption(
313
391
  }
314
392
  throw new Error('Option not found: ' + value);
315
393
  }`,
316
- objectId,
317
- arguments: [{ value }],
318
- returnByValue: true,
319
- }, sessionId);
394
+ objectId,
395
+ arguments: [{ value }],
396
+ returnByValue: true,
397
+ },
398
+ sessionId,
399
+ );
320
400
  if (selectResult.exceptionDetails) {
321
- throw new Error(`selectOption failed: ${selectResult.exceptionDetails.exception?.description ?? selectResult.exceptionDetails.text}`);
401
+ throw new Error(
402
+ `selectOption failed: ${selectResult.exceptionDetails.exception?.description ?? selectResult.exceptionDetails.text}`,
403
+ );
322
404
  }
323
405
  }
324
406
 
@@ -326,34 +408,51 @@ export async function checkElement(
326
408
  client: CdpClient,
327
409
  sessionId: string,
328
410
  ref: ElementRef,
329
- checked = true
411
+ checked = true,
330
412
  ): Promise<void> {
331
413
  const objectId = await getObjectIdForRef(client, sessionId, ref);
332
414
  if (!objectId) throw new Error(`Cannot check: ref @${ref.ref} not found in DOM`);
333
415
 
334
- const checkResult = await client.send<{ result: unknown; exceptionDetails?: { text: string; exception?: { description?: string } } }>('Runtime.callFunctionOn', {
335
- functionDeclaration: `function(checked) {
416
+ const checkResult = await client.send<{
417
+ result: unknown;
418
+ exceptionDetails?: { text: string; exception?: { description?: string } };
419
+ }>(
420
+ 'Runtime.callFunctionOn',
421
+ {
422
+ functionDeclaration: `function(checked) {
336
423
  if (this.checked !== checked) {
337
424
  this.click();
338
425
  }
339
426
  }`,
340
- objectId,
341
- arguments: [{ value: checked }],
342
- returnByValue: true,
343
- }, sessionId);
427
+ objectId,
428
+ arguments: [{ value: checked }],
429
+ returnByValue: true,
430
+ },
431
+ sessionId,
432
+ );
344
433
  if (checkResult.exceptionDetails) {
345
- throw new Error(`checkElement failed: ${checkResult.exceptionDetails.exception?.description ?? checkResult.exceptionDetails.text}`);
434
+ throw new Error(
435
+ `checkElement failed: ${checkResult.exceptionDetails.exception?.description ?? checkResult.exceptionDetails.text}`,
436
+ );
346
437
  }
347
438
  }
348
439
 
349
- export async function focusElement(client: CdpClient, sessionId: string, ref: ElementRef): Promise<void> {
440
+ export async function focusElement(
441
+ client: CdpClient,
442
+ sessionId: string,
443
+ ref: ElementRef,
444
+ ): Promise<void> {
350
445
  const objectId = await getObjectIdForRef(client, sessionId, ref);
351
446
  if (objectId) {
352
- await client.send('Runtime.callFunctionOn', {
353
- functionDeclaration: 'function() { this.focus(); }',
354
- objectId,
355
- returnByValue: true,
356
- }, sessionId);
447
+ await client.send(
448
+ 'Runtime.callFunctionOn',
449
+ {
450
+ functionDeclaration: 'function() { this.focus(); }',
451
+ objectId,
452
+ returnByValue: true,
453
+ },
454
+ sessionId,
455
+ );
357
456
  return;
358
457
  }
359
458
  const box = await getElementBox(client, sessionId, ref);
@@ -368,7 +467,7 @@ export async function typeIntoElement(
368
467
  client: CdpClient,
369
468
  sessionId: string,
370
469
  ref: ElementRef,
371
- text: string
470
+ text: string,
372
471
  ): Promise<void> {
373
472
  await focusElement(client, sessionId, ref);
374
473
  await typeText(client, sessionId, text);
@@ -384,7 +483,12 @@ export async function keyUp(client: CdpClient, sessionId: string, key: string):
384
483
  await client.send('Input.dispatchKeyEvent', { type: 'keyUp', ...keyInfo }, sessionId);
385
484
  }
386
485
 
387
- export async function mouseMove(client: CdpClient, sessionId: string, x: number, y: number): Promise<void> {
486
+ export async function mouseMove(
487
+ client: CdpClient,
488
+ sessionId: string,
489
+ x: number,
490
+ y: number,
491
+ ): Promise<void> {
388
492
  await client.send('Input.dispatchMouseEvent', { type: 'mouseMoved', x, y }, sessionId);
389
493
  }
390
494
 
@@ -393,10 +497,14 @@ export async function mouseDown(
393
497
  sessionId: string,
394
498
  x: number,
395
499
  y: number,
396
- button: 'left' | 'right' | 'middle' = 'left'
500
+ button: 'left' | 'right' | 'middle' = 'left',
397
501
  ): Promise<void> {
398
502
  const buttonsMask = button === 'right' ? 2 : button === 'middle' ? 4 : 1;
399
- await client.send('Input.dispatchMouseEvent', { type: 'mousePressed', x, y, button, buttons: buttonsMask, clickCount: 1 }, sessionId);
503
+ await client.send(
504
+ 'Input.dispatchMouseEvent',
505
+ { type: 'mousePressed', x, y, button, buttons: buttonsMask, clickCount: 1 },
506
+ sessionId,
507
+ );
400
508
  }
401
509
 
402
510
  export async function mouseUp(
@@ -404,9 +512,13 @@ export async function mouseUp(
404
512
  sessionId: string,
405
513
  x: number,
406
514
  y: number,
407
- button: 'left' | 'right' | 'middle' = 'left'
515
+ button: 'left' | 'right' | 'middle' = 'left',
408
516
  ): Promise<void> {
409
- await client.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x, y, button, buttons: 0, clickCount: 1 }, sessionId);
517
+ await client.send(
518
+ 'Input.dispatchMouseEvent',
519
+ { type: 'mouseReleased', x, y, button, buttons: 0, clickCount: 1 },
520
+ sessionId,
521
+ );
410
522
  }
411
523
 
412
524
  export async function mouseWheel(
@@ -415,47 +527,71 @@ export async function mouseWheel(
415
527
  x: number,
416
528
  y: number,
417
529
  deltaY: number,
418
- deltaX = 0
530
+ deltaX = 0,
419
531
  ): Promise<void> {
420
- await client.send('Input.dispatchMouseEvent', { type: 'mouseWheel', x, y, deltaX, deltaY }, sessionId);
532
+ await client.send(
533
+ 'Input.dispatchMouseEvent',
534
+ { type: 'mouseWheel', x, y, deltaX, deltaY },
535
+ sessionId,
536
+ );
421
537
  }
422
538
 
423
539
  export async function dragAndDrop(
424
540
  client: CdpClient,
425
541
  sessionId: string,
426
542
  src: ElementRef,
427
- tgt: ElementRef
543
+ tgt: ElementRef,
428
544
  ): Promise<void> {
429
545
  const srcBox = await getElementBox(client, sessionId, src);
430
546
  const tgtBox = await getElementBox(client, sessionId, tgt);
431
547
  if (!srcBox || !tgtBox) throw new Error('Cannot drag: one or both elements not found in DOM');
432
548
 
433
- await client.send('Input.dispatchMouseEvent', { type: 'mousePressed', x: srcBox.x, y: srcBox.y, button: 'left', buttons: 1, clickCount: 1 }, sessionId);
434
- await client.send('Input.dispatchMouseEvent', { type: 'mouseMoved', x: srcBox.x, y: srcBox.y, button: 'left', buttons: 1 }, sessionId);
549
+ await client.send(
550
+ 'Input.dispatchMouseEvent',
551
+ { type: 'mousePressed', x: srcBox.x, y: srcBox.y, button: 'left', buttons: 1, clickCount: 1 },
552
+ sessionId,
553
+ );
554
+ await client.send(
555
+ 'Input.dispatchMouseEvent',
556
+ { type: 'mouseMoved', x: srcBox.x, y: srcBox.y, button: 'left', buttons: 1 },
557
+ sessionId,
558
+ );
435
559
 
436
560
  // Move in steps for smooth drag
437
561
  const steps = 10;
438
562
  for (let i = 1; i <= steps; i++) {
439
563
  const x = srcBox.x + (tgtBox.x - srcBox.x) * (i / steps);
440
564
  const y = srcBox.y + (tgtBox.y - srcBox.y) * (i / steps);
441
- await client.send('Input.dispatchMouseEvent', { type: 'mouseMoved', x, y, button: 'left', buttons: 1 }, sessionId);
565
+ await client.send(
566
+ 'Input.dispatchMouseEvent',
567
+ { type: 'mouseMoved', x, y, button: 'left', buttons: 1 },
568
+ sessionId,
569
+ );
442
570
  }
443
571
 
444
- await client.send('Input.dispatchMouseEvent', { type: 'mouseReleased', x: tgtBox.x, y: tgtBox.y, button: 'left', buttons: 0, clickCount: 1 }, sessionId);
572
+ await client.send(
573
+ 'Input.dispatchMouseEvent',
574
+ { type: 'mouseReleased', x: tgtBox.x, y: tgtBox.y, button: 'left', buttons: 0, clickCount: 1 },
575
+ sessionId,
576
+ );
445
577
  }
446
578
 
447
579
  export async function uploadFile(
448
580
  client: CdpClient,
449
581
  sessionId: string,
450
582
  ref: ElementRef,
451
- filePaths: string[]
583
+ filePaths: string[],
452
584
  ): Promise<void> {
453
585
  if (!ref.backendDOMNodeId) throw new Error(`Cannot upload: ref @${ref.ref} has no DOM node`);
454
586
 
455
- await client.send('DOM.setFileInputFiles', {
456
- files: filePaths.map((f) => resolve(f)),
457
- backendNodeId: ref.backendDOMNodeId,
458
- }, sessionId);
587
+ await client.send(
588
+ 'DOM.setFileInputFiles',
589
+ {
590
+ files: filePaths.map((f) => resolve(f)),
591
+ backendNodeId: ref.backendDOMNodeId,
592
+ },
593
+ sessionId,
594
+ );
459
595
  }
460
596
 
461
597
  export async function readClipboard(client: CdpClient, sessionId: string): Promise<string> {
@@ -463,7 +599,11 @@ export async function readClipboard(client: CdpClient, sessionId: string): Promi
463
599
  return result as string;
464
600
  }
465
601
 
466
- export async function writeClipboard(client: CdpClient, sessionId: string, text: string): Promise<void> {
602
+ export async function writeClipboard(
603
+ client: CdpClient,
604
+ sessionId: string,
605
+ text: string,
606
+ ): Promise<void> {
467
607
  // Cap to 100 KB to prevent OOM when serializing the CDP expression
468
608
  const safeText = text.length > 102_400 ? text.slice(0, 102_400) : text;
469
609
  await evaluateJs(client, sessionId, `navigator.clipboard.writeText(${JSON.stringify(safeText)})`);
@@ -471,7 +611,10 @@ export async function writeClipboard(client: CdpClient, sessionId: string, text:
471
611
 
472
612
  export async function pushState(client: CdpClient, sessionId: string, url: string): Promise<void> {
473
613
  // Try Next.js router first, then fallback to history.pushState
474
- await evaluateJs(client, sessionId, `
614
+ await evaluateJs(
615
+ client,
616
+ sessionId,
617
+ `
475
618
  (function() {
476
619
  const url = ${JSON.stringify(url)};
477
620
  if (window.next && window.next.router) {
@@ -482,17 +625,30 @@ export async function pushState(client: CdpClient, sessionId: string, url: strin
482
625
  window.dispatchEvent(new PopStateEvent('popstate'));
483
626
  }
484
627
  })()
485
- `);
628
+ `,
629
+ );
486
630
  }
487
631
 
488
- export async function addInitScript(client: CdpClient, sessionId: string, script: string): Promise<string> {
489
- const result = await client.send<{ identifier: string }>('Page.addScriptToEvaluateOnNewDocument', {
490
- source: script,
491
- }, sessionId);
632
+ export async function addInitScript(
633
+ client: CdpClient,
634
+ sessionId: string,
635
+ script: string,
636
+ ): Promise<string> {
637
+ const result = await client.send<{ identifier: string }>(
638
+ 'Page.addScriptToEvaluateOnNewDocument',
639
+ {
640
+ source: script,
641
+ },
642
+ sessionId,
643
+ );
492
644
  return result.identifier;
493
645
  }
494
646
 
495
- export async function removeInitScript(client: CdpClient, sessionId: string, identifier: string): Promise<void> {
647
+ export async function removeInitScript(
648
+ client: CdpClient,
649
+ sessionId: string,
650
+ identifier: string,
651
+ ): Promise<void> {
496
652
  await client.send('Page.removeScriptToEvaluateOnNewDocument', { identifier }, sessionId);
497
653
  }
498
654
 
@@ -509,29 +665,38 @@ export async function evaluateJs(
509
665
  client: CdpClient,
510
666
  sessionId: string,
511
667
  expression: string,
512
- timeoutMs: number = DEFAULT_EVAL_TIMEOUT_MS
668
+ timeoutMs: number = DEFAULT_EVAL_TIMEOUT_MS,
513
669
  ): Promise<unknown> {
514
670
  const evalPromise = client.send<{
515
671
  result: { value?: unknown; type: string; description?: string };
516
672
  exceptionDetails?: { text: string; exception?: { description?: string } };
517
- }>('Runtime.evaluate', {
518
- expression,
519
- returnByValue: true,
520
- awaitPromise: true,
521
- }, sessionId);
673
+ }>(
674
+ 'Runtime.evaluate',
675
+ {
676
+ expression,
677
+ returnByValue: true,
678
+ awaitPromise: true,
679
+ },
680
+ sessionId,
681
+ );
522
682
 
523
683
  const result = await (timeoutMs > 0
524
684
  ? Promise.race([
525
685
  evalPromise,
526
686
  new Promise<never>((_, reject) => {
527
- const t = setTimeout(() => reject(new Error(`JS evaluation timed out after ${timeoutMs}ms`)), timeoutMs);
687
+ const t = setTimeout(
688
+ () => reject(new Error(`JS evaluation timed out after ${timeoutMs}ms`)),
689
+ timeoutMs,
690
+ );
528
691
  t.unref?.();
529
692
  }),
530
693
  ])
531
694
  : evalPromise);
532
695
 
533
696
  if (result.exceptionDetails) {
534
- throw new Error(`JS evaluation error: ${result.exceptionDetails.exception?.description ?? result.exceptionDetails.text}`);
697
+ throw new Error(
698
+ `JS evaluation error: ${result.exceptionDetails.exception?.description ?? result.exceptionDetails.text}`,
699
+ );
535
700
  }
536
701
 
537
702
  return result.result?.value;