@monoes/monobrowse 1.0.11 → 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 (220) hide show
  1. package/dist/src/__tests__/adapters.test.js +5 -2
  2. package/dist/src/__tests__/adapters.test.js.map +1 -1
  3. package/dist/src/__tests__/batch-eval-parsing.test.js +4 -2
  4. package/dist/src/__tests__/batch-eval-parsing.test.js.map +1 -1
  5. package/dist/src/__tests__/box-model.test.js +9 -5
  6. package/dist/src/__tests__/box-model.test.js.map +1 -1
  7. package/dist/src/__tests__/browser-launch.test.d.ts +10 -0
  8. package/dist/src/__tests__/browser-launch.test.d.ts.map +1 -1
  9. package/dist/src/__tests__/browser-launch.test.js +17 -10
  10. package/dist/src/__tests__/browser-launch.test.js.map +1 -1
  11. package/dist/src/__tests__/cdp-client.test.js +9 -3
  12. package/dist/src/__tests__/cdp-client.test.js.map +1 -1
  13. package/dist/src/__tests__/close-browser.test.d.ts +13 -0
  14. package/dist/src/__tests__/close-browser.test.d.ts.map +1 -1
  15. package/dist/src/__tests__/close-browser.test.js +5 -5
  16. package/dist/src/__tests__/close-browser.test.js.map +1 -1
  17. package/dist/src/__tests__/console-log.test.js +17 -6
  18. package/dist/src/__tests__/console-log.test.js.map +1 -1
  19. package/dist/src/__tests__/find.test.js +26 -6
  20. package/dist/src/__tests__/find.test.js.map +1 -1
  21. package/dist/src/__tests__/frame-command.test.js +9 -3
  22. package/dist/src/__tests__/frame-command.test.js.map +1 -1
  23. package/dist/src/__tests__/open-url-navigate-error.test.js +1 -3
  24. package/dist/src/__tests__/open-url-navigate-error.test.js.map +1 -1
  25. package/dist/src/__tests__/output.test.js +1 -1
  26. package/dist/src/__tests__/output.test.js.map +1 -1
  27. package/dist/src/__tests__/reap-idle-browser.test.d.ts +15 -0
  28. package/dist/src/__tests__/reap-idle-browser.test.d.ts.map +1 -0
  29. package/dist/src/__tests__/reap-idle-browser.test.js +238 -0
  30. package/dist/src/__tests__/reap-idle-browser.test.js.map +1 -0
  31. package/dist/src/__tests__/ref-cache.test.js +18 -10
  32. package/dist/src/__tests__/ref-cache.test.js.map +1 -1
  33. package/dist/src/__tests__/session-state.test.d.ts +8 -0
  34. package/dist/src/__tests__/session-state.test.d.ts.map +1 -1
  35. package/dist/src/__tests__/session-state.test.js +10 -10
  36. package/dist/src/__tests__/session-state.test.js.map +1 -1
  37. package/dist/src/__tests__/snapshot.test.js +44 -14
  38. package/dist/src/__tests__/snapshot.test.js.map +1 -1
  39. package/dist/src/__tests__/storage-eval.test.js +14 -6
  40. package/dist/src/__tests__/storage-eval.test.js.map +1 -1
  41. package/dist/src/__tests__/tabs.test.js +25 -9
  42. package/dist/src/__tests__/tabs.test.js.map +1 -1
  43. package/dist/src/browser/action-builder/analyzer.d.ts +1 -1
  44. package/dist/src/browser/action-builder/analyzer.d.ts.map +1 -1
  45. package/dist/src/browser/action-builder/analyzer.js +25 -9
  46. package/dist/src/browser/action-builder/analyzer.js.map +1 -1
  47. package/dist/src/browser/action-builder/types.d.ts.map +1 -1
  48. package/dist/src/browser/actions.d.ts +1 -1
  49. package/dist/src/browser/actions.d.ts.map +1 -1
  50. package/dist/src/browser/actions.js +16 -6
  51. package/dist/src/browser/actions.js.map +1 -1
  52. package/dist/src/browser/adapters/gemini.d.ts.map +1 -1
  53. package/dist/src/browser/adapters/gemini.js.map +1 -1
  54. package/dist/src/browser/adapters/google.d.ts.map +1 -1
  55. package/dist/src/browser/adapters/google.js.map +1 -1
  56. package/dist/src/browser/adapters/index.d.ts.map +1 -1
  57. package/dist/src/browser/adapters/index.js +3 -3
  58. package/dist/src/browser/adapters/index.js.map +1 -1
  59. package/dist/src/browser/adapters/instagram.d.ts.map +1 -1
  60. package/dist/src/browser/adapters/instagram.js.map +1 -1
  61. package/dist/src/browser/adapters/linkedin.d.ts.map +1 -1
  62. package/dist/src/browser/adapters/linkedin.js.map +1 -1
  63. package/dist/src/browser/adapters/microsoft.d.ts.map +1 -1
  64. package/dist/src/browser/adapters/microsoft.js.map +1 -1
  65. package/dist/src/browser/adapters/x.d.ts.map +1 -1
  66. package/dist/src/browser/adapters/x.js.map +1 -1
  67. package/dist/src/browser/browser.d.ts +30 -0
  68. package/dist/src/browser/browser.d.ts.map +1 -1
  69. package/dist/src/browser/browser.js +122 -15
  70. package/dist/src/browser/browser.js.map +1 -1
  71. package/dist/src/browser/cdp.d.ts.map +1 -1
  72. package/dist/src/browser/cdp.js +21 -8
  73. package/dist/src/browser/cdp.js.map +1 -1
  74. package/dist/src/browser/console-log.d.ts.map +1 -1
  75. package/dist/src/browser/console-log.js +2 -2
  76. package/dist/src/browser/console-log.js.map +1 -1
  77. package/dist/src/browser/dashboard/server.d.ts.map +1 -1
  78. package/dist/src/browser/dashboard/server.js +29 -19
  79. package/dist/src/browser/dashboard/server.js.map +1 -1
  80. package/dist/src/browser/dashboard/ui.html +153 -165
  81. package/dist/src/browser/dialog.d.ts.map +1 -1
  82. package/dist/src/browser/dialog.js +3 -1
  83. package/dist/src/browser/dialog.js.map +1 -1
  84. package/dist/src/browser/emulation.d.ts.map +1 -1
  85. package/dist/src/browser/emulation.js +21 -6
  86. package/dist/src/browser/emulation.js.map +1 -1
  87. package/dist/src/browser/find.d.ts +4 -4
  88. package/dist/src/browser/find.d.ts.map +1 -1
  89. package/dist/src/browser/find.js +14 -6
  90. package/dist/src/browser/find.js.map +1 -1
  91. package/dist/src/browser/har.d.ts.map +1 -1
  92. package/dist/src/browser/har.js +14 -7
  93. package/dist/src/browser/har.js.map +1 -1
  94. package/dist/src/browser/index.d.ts +16 -16
  95. package/dist/src/browser/index.d.ts.map +1 -1
  96. package/dist/src/browser/index.js +16 -16
  97. package/dist/src/browser/index.js.map +1 -1
  98. package/dist/src/browser/network.d.ts.map +1 -1
  99. package/dist/src/browser/network.js +14 -4
  100. package/dist/src/browser/network.js.map +1 -1
  101. package/dist/src/browser/pdf.d.ts.map +1 -1
  102. package/dist/src/browser/pdf.js +3 -3
  103. package/dist/src/browser/pdf.js.map +1 -1
  104. package/dist/src/browser/profiler.d.ts.map +1 -1
  105. package/dist/src/browser/profiler.js +10 -5
  106. package/dist/src/browser/profiler.js.map +1 -1
  107. package/dist/src/browser/record.d.ts +1 -1
  108. package/dist/src/browser/record.d.ts.map +1 -1
  109. package/dist/src/browser/record.js +18 -7
  110. package/dist/src/browser/record.js.map +1 -1
  111. package/dist/src/browser/ref-cache.d.ts.map +1 -1
  112. package/dist/src/browser/ref-cache.js +9 -4
  113. package/dist/src/browser/ref-cache.js.map +1 -1
  114. package/dist/src/browser/screenshot.d.ts.map +1 -1
  115. package/dist/src/browser/screenshot.js +11 -7
  116. package/dist/src/browser/screenshot.js.map +1 -1
  117. package/dist/src/browser/session.d.ts +1 -1
  118. package/dist/src/browser/session.d.ts.map +1 -1
  119. package/dist/src/browser/session.js +27 -8
  120. package/dist/src/browser/session.js.map +1 -1
  121. package/dist/src/browser/snapshot.d.ts +1 -1
  122. package/dist/src/browser/snapshot.d.ts.map +1 -1
  123. package/dist/src/browser/snapshot.js +8 -4
  124. package/dist/src/browser/snapshot.js.map +1 -1
  125. package/dist/src/browser/storage.d.ts.map +1 -1
  126. package/dist/src/browser/storage.js.map +1 -1
  127. package/dist/src/browser/tabs.d.ts.map +1 -1
  128. package/dist/src/browser/tabs.js +9 -3
  129. package/dist/src/browser/tabs.js.map +1 -1
  130. package/dist/src/browser/trace.d.ts.map +1 -1
  131. package/dist/src/browser/trace.js +3 -3
  132. package/dist/src/browser/trace.js.map +1 -1
  133. package/dist/src/browser/types.d.ts.map +1 -1
  134. package/dist/src/browser/types.js +23 -4
  135. package/dist/src/browser/types.js.map +1 -1
  136. package/dist/src/browser/vitals.d.ts.map +1 -1
  137. package/dist/src/browser/vitals.js +1 -1
  138. package/dist/src/browser/vitals.js.map +1 -1
  139. package/dist/src/browser/wait.d.ts.map +1 -1
  140. package/dist/src/browser/wait.js +11 -5
  141. package/dist/src/browser/wait.js.map +1 -1
  142. package/dist/src/cli/action.d.ts.map +1 -1
  143. package/dist/src/cli/action.js +14 -11
  144. package/dist/src/cli/action.js.map +1 -1
  145. package/dist/src/cli/commands.d.ts.map +1 -1
  146. package/dist/src/cli/commands.js +453 -159
  147. package/dist/src/cli/commands.js.map +1 -1
  148. package/dist/src/cli/output.js +5 -5
  149. package/dist/src/cli/output.js.map +1 -1
  150. package/dist/src/cli/platform.d.ts.map +1 -1
  151. package/dist/src/cli/platform.js +15 -11
  152. package/dist/src/cli/platform.js.map +1 -1
  153. package/dist/src/cli/types.d.ts +1 -1
  154. package/dist/src/cli/types.d.ts.map +1 -1
  155. package/dist/src/cli.js +21 -9
  156. package/dist/src/cli.js.map +1 -1
  157. package/dist/src/index.d.ts +2 -2
  158. package/dist/src/index.d.ts.map +1 -1
  159. package/dist/src/index.js +2 -2
  160. package/dist/src/index.js.map +1 -1
  161. package/dist/tsconfig.tsbuildinfo +1 -1
  162. package/package.json +1 -1
  163. package/src/__tests__/adapters.test.ts +15 -8
  164. package/src/__tests__/batch-eval-parsing.test.ts +4 -2
  165. package/src/__tests__/box-model.test.ts +9 -5
  166. package/src/__tests__/browser-launch.test.ts +26 -15
  167. package/src/__tests__/cdp-client.test.ts +25 -9
  168. package/src/__tests__/close-browser.test.ts +11 -6
  169. package/src/__tests__/console-log.test.ts +36 -13
  170. package/src/__tests__/find.test.ts +58 -20
  171. package/src/__tests__/frame-command.test.ts +12 -6
  172. package/src/__tests__/open-url-navigate-error.test.ts +2 -4
  173. package/src/__tests__/output.test.ts +1 -1
  174. package/src/__tests__/reap-idle-browser.test.ts +276 -0
  175. package/src/__tests__/ref-cache.test.ts +25 -12
  176. package/src/__tests__/session-state.test.ts +40 -28
  177. package/src/__tests__/snapshot.test.ts +60 -21
  178. package/src/__tests__/storage-eval.test.ts +30 -16
  179. package/src/__tests__/tabs.test.ts +26 -10
  180. package/src/browser/action-builder/analyzer.ts +39 -15
  181. package/src/browser/action-builder/types.ts +45 -8
  182. package/src/browser/actions.ts +288 -123
  183. package/src/browser/adapters/gemini.ts +7 -3
  184. package/src/browser/adapters/google.ts +5 -3
  185. package/src/browser/adapters/index.ts +5 -4
  186. package/src/browser/adapters/instagram.ts +4 -2
  187. package/src/browser/adapters/linkedin.ts +4 -2
  188. package/src/browser/adapters/microsoft.ts +9 -5
  189. package/src/browser/adapters/x.ts +7 -3
  190. package/src/browser/browser.ts +188 -42
  191. package/src/browser/cdp.ts +43 -11
  192. package/src/browser/console-log.ts +24 -7
  193. package/src/browser/dashboard/server.ts +44 -25
  194. package/src/browser/dashboard/ui.html +153 -165
  195. package/src/browser/dialog.ts +25 -8
  196. package/src/browser/emulation.ts +86 -36
  197. package/src/browser/find.ts +110 -47
  198. package/src/browser/har.ts +58 -26
  199. package/src/browser/index.ts +16 -16
  200. package/src/browser/network.ts +147 -46
  201. package/src/browser/pdf.ts +4 -4
  202. package/src/browser/profiler.ts +23 -10
  203. package/src/browser/record.ts +38 -19
  204. package/src/browser/ref-cache.ts +38 -17
  205. package/src/browser/screenshot.ts +58 -32
  206. package/src/browser/session.ts +51 -18
  207. package/src/browser/snapshot.ts +34 -20
  208. package/src/browser/storage.ts +73 -17
  209. package/src/browser/tabs.ts +29 -17
  210. package/src/browser/trace.ts +14 -10
  211. package/src/browser/types.ts +23 -4
  212. package/src/browser/vitals.ts +33 -16
  213. package/src/browser/wait.ts +47 -21
  214. package/src/cli/action.ts +52 -21
  215. package/src/cli/commands.ts +1093 -385
  216. package/src/cli/output.ts +5 -5
  217. package/src/cli/platform.ts +44 -23
  218. package/src/cli/types.ts +1 -1
  219. package/src/cli.ts +34 -18
  220. package/src/index.ts +2 -2
@@ -9,7 +9,11 @@ export interface DialogInfo {
9
9
  const _pendingDialogs = new Map<string, DialogInfo | null>();
10
10
  const _dialogListeners = new Map<string, Array<() => void>>();
11
11
 
12
- export function setupDialogAutoHandling(client: CdpClient, sessionId: string, autoAccept = true): void {
12
+ export function setupDialogAutoHandling(
13
+ client: CdpClient,
14
+ sessionId: string,
15
+ autoAccept = true,
16
+ ): void {
13
17
  if (_pendingDialogs.has(sessionId)) return;
14
18
  _pendingDialogs.set(sessionId, null);
15
19
 
@@ -27,7 +31,9 @@ export function setupDialogAutoHandling(client: CdpClient, sessionId: string, au
27
31
  if (autoAccept) {
28
32
  try {
29
33
  await client.send('Page.handleJavaScriptDialog', { accept: true }, sessionId);
30
- } catch { /* dialog may have already been dismissed */ }
34
+ } catch {
35
+ /* dialog may have already been dismissed */
36
+ }
31
37
  _pendingDialogs.set(sessionId, null);
32
38
  }
33
39
  });
@@ -41,15 +47,26 @@ export function setupDialogAutoHandling(client: CdpClient, sessionId: string, au
41
47
 
42
48
  export function teardownDialogHandling(sessionId: string): void {
43
49
  const offs = _dialogListeners.get(sessionId);
44
- if (offs) { for (const off of offs) off(); _dialogListeners.delete(sessionId); }
50
+ if (offs) {
51
+ for (const off of offs) off();
52
+ _dialogListeners.delete(sessionId);
53
+ }
45
54
  _pendingDialogs.delete(sessionId);
46
55
  }
47
56
 
48
- export async function acceptDialog(client: CdpClient, sessionId: string, text?: string): Promise<void> {
49
- await client.send('Page.handleJavaScriptDialog', {
50
- accept: true,
51
- promptText: text,
52
- }, sessionId);
57
+ export async function acceptDialog(
58
+ client: CdpClient,
59
+ sessionId: string,
60
+ text?: string,
61
+ ): Promise<void> {
62
+ await client.send(
63
+ 'Page.handleJavaScriptDialog',
64
+ {
65
+ accept: true,
66
+ promptText: text,
67
+ },
68
+ sessionId,
69
+ );
53
70
  _pendingDialogs.set(sessionId, null);
54
71
  }
55
72
 
@@ -10,39 +10,69 @@ export interface DeviceProfile {
10
10
 
11
11
  const DEVICES: Record<string, DeviceProfile> = {
12
12
  'iPhone 14': {
13
- width: 390, height: 844, deviceScaleFactor: 3, mobile: true,
14
- 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',
13
+ width: 390,
14
+ height: 844,
15
+ deviceScaleFactor: 3,
16
+ mobile: true,
17
+ userAgent:
18
+ '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',
15
19
  },
16
20
  'iPhone SE': {
17
- width: 375, height: 667, deviceScaleFactor: 2, mobile: true,
18
- userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
21
+ width: 375,
22
+ height: 667,
23
+ deviceScaleFactor: 2,
24
+ mobile: true,
25
+ userAgent:
26
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
19
27
  },
20
- 'iPad': {
21
- width: 768, height: 1024, deviceScaleFactor: 2, mobile: true,
22
- userAgent: 'Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
28
+ iPad: {
29
+ width: 768,
30
+ height: 1024,
31
+ deviceScaleFactor: 2,
32
+ mobile: true,
33
+ userAgent:
34
+ 'Mozilla/5.0 (iPad; CPU OS 15_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/15.0 Mobile/15E148 Safari/604.1',
23
35
  },
24
36
  'Galaxy S21': {
25
- width: 360, height: 800, deviceScaleFactor: 3, mobile: true,
26
- userAgent: 'Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
37
+ width: 360,
38
+ height: 800,
39
+ deviceScaleFactor: 3,
40
+ mobile: true,
41
+ userAgent:
42
+ 'Mozilla/5.0 (Linux; Android 11; SM-G991B) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
27
43
  },
28
44
  'Pixel 5': {
29
- width: 393, height: 851, deviceScaleFactor: 2.75, mobile: true,
30
- userAgent: 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
45
+ width: 393,
46
+ height: 851,
47
+ deviceScaleFactor: 2.75,
48
+ mobile: true,
49
+ userAgent:
50
+ 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.101 Mobile Safari/537.36',
31
51
  },
32
52
  };
33
53
 
34
- export async function emulateDevice(client: CdpClient, sessionId: string, deviceName: string): Promise<void> {
54
+ export async function emulateDevice(
55
+ client: CdpClient,
56
+ sessionId: string,
57
+ deviceName: string,
58
+ ): Promise<void> {
35
59
  const device = DEVICES[deviceName];
36
60
  if (!device) {
37
- throw new Error(`Unknown device: "${deviceName}". Available: ${Object.keys(DEVICES).join(', ')}`);
61
+ throw new Error(
62
+ `Unknown device: "${deviceName}". Available: ${Object.keys(DEVICES).join(', ')}`,
63
+ );
38
64
  }
39
65
 
40
- await client.send('Emulation.setDeviceMetricsOverride', {
41
- width: device.width,
42
- height: device.height,
43
- deviceScaleFactor: device.deviceScaleFactor,
44
- mobile: device.mobile,
45
- }, sessionId);
66
+ await client.send(
67
+ 'Emulation.setDeviceMetricsOverride',
68
+ {
69
+ width: device.width,
70
+ height: device.height,
71
+ deviceScaleFactor: device.deviceScaleFactor,
72
+ mobile: device.mobile,
73
+ },
74
+ sessionId,
75
+ );
46
76
 
47
77
  await client.send('Emulation.setUserAgentOverride', { userAgent: device.userAgent }, sessionId);
48
78
  await client.send('Emulation.setTouchEmulationEnabled', { enabled: device.mobile }, sessionId);
@@ -53,40 +83,60 @@ export async function setGeolocation(
53
83
  sessionId: string,
54
84
  latitude: number,
55
85
  longitude: number,
56
- accuracy = 100
86
+ accuracy = 100,
57
87
  ): Promise<void> {
58
- await client.send('Emulation.setGeolocationOverride', { latitude, longitude, accuracy }, sessionId);
88
+ await client.send(
89
+ 'Emulation.setGeolocationOverride',
90
+ { latitude, longitude, accuracy },
91
+ sessionId,
92
+ );
59
93
  }
60
94
 
61
- export async function setOfflineMode(client: CdpClient, sessionId: string, offline: boolean): Promise<void> {
62
- await client.send('Network.emulateNetworkConditions', {
63
- offline,
64
- latency: 0,
65
- downloadThroughput: -1,
66
- uploadThroughput: -1,
67
- }, sessionId);
95
+ export async function setOfflineMode(
96
+ client: CdpClient,
97
+ sessionId: string,
98
+ offline: boolean,
99
+ ): Promise<void> {
100
+ await client.send(
101
+ 'Network.emulateNetworkConditions',
102
+ {
103
+ offline,
104
+ latency: 0,
105
+ downloadThroughput: -1,
106
+ uploadThroughput: -1,
107
+ },
108
+ sessionId,
109
+ );
68
110
  }
69
111
 
70
112
  export async function setColorScheme(
71
113
  client: CdpClient,
72
114
  sessionId: string,
73
- scheme: 'dark' | 'light' | 'no-preference'
115
+ scheme: 'dark' | 'light' | 'no-preference',
74
116
  ): Promise<void> {
75
- await client.send('Emulation.setEmulatedMedia', {
76
- features: [{ name: 'prefers-color-scheme', value: scheme }],
77
- }, sessionId);
117
+ await client.send(
118
+ 'Emulation.setEmulatedMedia',
119
+ {
120
+ features: [{ name: 'prefers-color-scheme', value: scheme }],
121
+ },
122
+ sessionId,
123
+ );
78
124
  }
79
125
 
80
126
  export async function setBasicAuth(
81
127
  client: CdpClient,
82
128
  sessionId: string,
83
129
  username: string,
84
- password: string
130
+ password: string,
85
131
  ): Promise<void> {
86
132
  const encoded = Buffer.from(`${username}:${password}`).toString('base64');
87
- await client.send('Network.setExtraHTTPHeaders', {
88
- headers: { Authorization: `Basic ${encoded}` },
89
- }, sessionId);
133
+ await client.send(
134
+ 'Network.setExtraHTTPHeaders',
135
+ {
136
+ headers: { Authorization: `Basic ${encoded}` },
137
+ },
138
+ sessionId,
139
+ );
90
140
  }
91
141
 
92
142
  export function listDevices(): string[] {
@@ -1,9 +1,17 @@
1
- import type { CdpClient } from './cdp.js';
2
- import type { ElementRef } from './types.js';
3
1
  import { evaluateJs } from './actions.js';
2
+ import type { CdpClient } from './cdp.js';
4
3
  import { getObjectIdForRef } from './snapshot.js';
4
+ import type { ElementRef } from './types.js';
5
5
 
6
- export type FindAction = 'click' | 'fill' | 'type' | 'hover' | 'focus' | 'check' | 'uncheck' | 'text';
6
+ export type FindAction =
7
+ | 'click'
8
+ | 'fill'
9
+ | 'type'
10
+ | 'hover'
11
+ | 'focus'
12
+ | 'check'
13
+ | 'uncheck'
14
+ | 'text';
7
15
 
8
16
  export interface FindOptions {
9
17
  name?: string;
@@ -18,7 +26,7 @@ export async function findBySelector(
18
26
  sessionId: string,
19
27
  refs: Map<string, ElementRef>,
20
28
  selector: string,
21
- options: FindOptions = {}
29
+ options: FindOptions = {},
22
30
  ): Promise<ElementRef | null> {
23
31
  if (options.nth !== undefined && options.nth < 1) {
24
32
  throw new Error(`nth must be >= 1 (received ${options.nth})`);
@@ -29,14 +37,20 @@ export async function findBySelector(
29
37
  let targetNodeId: number;
30
38
  if (options.nth !== undefined || options.last) {
31
39
  const result = await client.send<{ nodeIds: number[] }>(
32
- 'DOM.querySelectorAll', { nodeId: doc.root.nodeId, selector }, sessionId
40
+ 'DOM.querySelectorAll',
41
+ { nodeId: doc.root.nodeId, selector },
42
+ sessionId,
33
43
  );
34
44
  const nodeIds = result.nodeIds ?? [];
35
45
  if (nodeIds.length === 0) return null;
36
- targetNodeId = options.last ? nodeIds[nodeIds.length - 1] : (nodeIds[(options.nth ?? 1) - 1] ?? 0);
46
+ targetNodeId = options.last
47
+ ? nodeIds[nodeIds.length - 1]
48
+ : (nodeIds[(options.nth ?? 1) - 1] ?? 0);
37
49
  } else {
38
50
  const result = await client.send<{ nodeId: number }>(
39
- 'DOM.querySelector', { nodeId: doc.root.nodeId, selector }, sessionId
51
+ 'DOM.querySelector',
52
+ { nodeId: doc.root.nodeId, selector },
53
+ sessionId,
40
54
  );
41
55
  targetNodeId = result.nodeId ?? 0;
42
56
  }
@@ -44,7 +58,9 @@ export async function findBySelector(
44
58
  if (!targetNodeId) return null;
45
59
 
46
60
  const desc = await client.send<{ node: { backendNodeId: number } }>(
47
- 'DOM.describeNode', { nodeId: targetNodeId }, sessionId
61
+ 'DOM.describeNode',
62
+ { nodeId: targetNodeId },
63
+ sessionId,
48
64
  );
49
65
  const backendDOMNodeId = desc.node?.backendNodeId;
50
66
  if (!backendDOMNodeId) return null;
@@ -54,7 +70,13 @@ export async function findBySelector(
54
70
  if (existing) return existing;
55
71
 
56
72
  // Synthetic ref for elements not represented in the AX tree — insert into refs so it can be used in subsequent commands
57
- const syntheticRef: ElementRef = { ref: `sel-${backendDOMNodeId}`, role: 'generic', name: selector, nodeId: targetNodeId, backendDOMNodeId };
73
+ const syntheticRef: ElementRef = {
74
+ ref: `sel-${backendDOMNodeId}`,
75
+ role: 'generic',
76
+ name: selector,
77
+ nodeId: targetNodeId,
78
+ backendDOMNodeId,
79
+ };
58
80
  refs.set(syntheticRef.ref, syntheticRef);
59
81
  return syntheticRef;
60
82
  } catch (err: unknown) {
@@ -65,11 +87,11 @@ export async function findBySelector(
65
87
  }
66
88
 
67
89
  export async function findByRole(
68
- client: CdpClient,
69
- sessionId: string,
90
+ _client: CdpClient,
91
+ _sessionId: string,
70
92
  refs: Map<string, ElementRef>,
71
93
  role: string,
72
- options: FindOptions = {}
94
+ options: FindOptions = {},
73
95
  ): Promise<ElementRef | null> {
74
96
  if (options.nth !== undefined && options.nth < 1) {
75
97
  throw new Error(`nth must be >= 1 (received ${options.nth})`);
@@ -92,18 +114,18 @@ export async function findByRole(
92
114
  }
93
115
 
94
116
  export async function findByText(
95
- client: CdpClient,
96
- sessionId: string,
117
+ _client: CdpClient,
118
+ _sessionId: string,
97
119
  refs: Map<string, ElementRef>,
98
120
  text: string,
99
- options: FindOptions = {}
121
+ options: FindOptions = {},
100
122
  ): Promise<ElementRef | null> {
101
123
  if (options.nth !== undefined && options.nth < 1) {
102
124
  throw new Error(`nth must be >= 1 (received ${options.nth})`);
103
125
  }
104
126
  const lower = text.toLowerCase();
105
127
  const candidates = [...refs.values()].filter((r) =>
106
- options.exact ? r.name.toLowerCase() === lower : r.name.toLowerCase().includes(lower)
128
+ options.exact ? r.name.toLowerCase() === lower : r.name.toLowerCase().includes(lower),
107
129
  );
108
130
  if (options.nth !== undefined) return candidates[options.nth - 1] ?? null;
109
131
  if (options.last) return candidates[candidates.length - 1] ?? null;
@@ -115,17 +137,17 @@ export async function findByLabel(
115
137
  sessionId: string,
116
138
  refs: Map<string, ElementRef>,
117
139
  label: string,
118
- options: FindOptions = {}
140
+ options: FindOptions = {},
119
141
  ): Promise<ElementRef | null> {
120
142
  return findByText(client, sessionId, refs, label, options);
121
143
  }
122
144
 
123
145
  export async function findByPlaceholder(
124
- client: CdpClient,
125
- sessionId: string,
146
+ _client: CdpClient,
147
+ _sessionId: string,
126
148
  refs: Map<string, ElementRef>,
127
149
  placeholder: string,
128
- options: FindOptions = {}
150
+ options: FindOptions = {},
129
151
  ): Promise<ElementRef | null> {
130
152
  if (options.nth !== undefined && options.nth < 1) {
131
153
  throw new Error(`nth must be >= 1 (received ${options.nth})`);
@@ -143,7 +165,7 @@ export async function findByPlaceholder(
143
165
  export async function findByTestId(
144
166
  client: CdpClient,
145
167
  sessionId: string,
146
- testId: string
168
+ testId: string,
147
169
  ): Promise<string | null> {
148
170
  const escapedId = testId.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
149
171
  const selectors = [
@@ -152,18 +174,28 @@ export async function findByTestId(
152
174
  `[data-test="${escapedId}"]`,
153
175
  ];
154
176
  for (const sel of selectors) {
155
- const result = await evaluateJs(client, sessionId, `!!document.querySelector(${JSON.stringify(sel)})`);
177
+ const result = await evaluateJs(
178
+ client,
179
+ sessionId,
180
+ `!!document.querySelector(${JSON.stringify(sel)})`,
181
+ );
156
182
  if (result) return sel;
157
183
  }
158
184
  return null;
159
185
  }
160
186
 
161
- export async function isVisible(client: CdpClient, sessionId: string, ref: ElementRef): Promise<boolean> {
187
+ export async function isVisible(
188
+ client: CdpClient,
189
+ sessionId: string,
190
+ ref: ElementRef,
191
+ ): Promise<boolean> {
162
192
  const objectId = await getObjectIdForRef(client, sessionId, ref);
163
193
  if (!objectId) return false;
164
194
 
165
- const result = await client.send<{ result: { value?: boolean } }>('Runtime.callFunctionOn', {
166
- functionDeclaration: `function() {
195
+ const result = await client.send<{ result: { value?: boolean } }>(
196
+ 'Runtime.callFunctionOn',
197
+ {
198
+ functionDeclaration: `function() {
167
199
  const rect = this.getBoundingClientRect();
168
200
  const style = window.getComputedStyle(this);
169
201
  return rect.width > 0 && rect.height > 0 &&
@@ -171,45 +203,74 @@ export async function isVisible(client: CdpClient, sessionId: string, ref: Eleme
171
203
  style.visibility !== 'hidden' &&
172
204
  style.opacity !== '0';
173
205
  }`,
174
- objectId,
175
- returnByValue: true,
176
- }, sessionId);
206
+ objectId,
207
+ returnByValue: true,
208
+ },
209
+ sessionId,
210
+ );
177
211
  return result.result?.value ?? false;
178
212
  }
179
213
 
180
- export async function isEnabled(client: CdpClient, sessionId: string, ref: ElementRef): Promise<boolean> {
214
+ export async function isEnabled(
215
+ _client: CdpClient,
216
+ _sessionId: string,
217
+ ref: ElementRef,
218
+ ): Promise<boolean> {
181
219
  return !ref.disabled;
182
220
  }
183
221
 
184
- export async function isChecked(client: CdpClient, sessionId: string, ref: ElementRef): Promise<boolean> {
222
+ export async function isChecked(
223
+ client: CdpClient,
224
+ sessionId: string,
225
+ ref: ElementRef,
226
+ ): Promise<boolean> {
185
227
  const objectId = await getObjectIdForRef(client, sessionId, ref);
186
228
  if (!objectId) return false;
187
229
 
188
- const result = await client.send<{ result: { value?: boolean } }>('Runtime.callFunctionOn', {
189
- functionDeclaration: 'function() { return !!this.checked; }',
190
- objectId,
191
- returnByValue: true,
192
- }, sessionId);
230
+ const result = await client.send<{ result: { value?: boolean } }>(
231
+ 'Runtime.callFunctionOn',
232
+ {
233
+ functionDeclaration: 'function() { return !!this.checked; }',
234
+ objectId,
235
+ returnByValue: true,
236
+ },
237
+ sessionId,
238
+ );
193
239
  return result.result?.value ?? false;
194
240
  }
195
241
 
196
- export async function scrollIntoView(client: CdpClient, sessionId: string, ref: ElementRef): Promise<void> {
242
+ export async function scrollIntoView(
243
+ client: CdpClient,
244
+ sessionId: string,
245
+ ref: ElementRef,
246
+ ): Promise<void> {
197
247
  const objectId = await getObjectIdForRef(client, sessionId, ref);
198
248
  if (!objectId) throw new Error(`Cannot scroll: ref @${ref.ref} not found in DOM`);
199
249
 
200
- await client.send('Runtime.callFunctionOn', {
201
- functionDeclaration: 'function() { this.scrollIntoView({ behavior: "smooth", block: "center" }); }',
202
- objectId,
203
- returnByValue: true,
204
- }, sessionId);
250
+ await client.send(
251
+ 'Runtime.callFunctionOn',
252
+ {
253
+ functionDeclaration:
254
+ 'function() { this.scrollIntoView({ behavior: "smooth", block: "center" }); }',
255
+ objectId,
256
+ returnByValue: true,
257
+ },
258
+ sessionId,
259
+ );
205
260
  }
206
261
 
207
- export async function highlightElement(client: CdpClient, sessionId: string, ref: ElementRef): Promise<void> {
262
+ export async function highlightElement(
263
+ client: CdpClient,
264
+ sessionId: string,
265
+ ref: ElementRef,
266
+ ): Promise<void> {
208
267
  const objectId = await getObjectIdForRef(client, sessionId, ref);
209
268
  if (!objectId) return;
210
269
 
211
- await client.send('Runtime.callFunctionOn', {
212
- functionDeclaration: `function() {
270
+ await client.send(
271
+ 'Runtime.callFunctionOn',
272
+ {
273
+ functionDeclaration: `function() {
213
274
  const prev = this.style.outline;
214
275
  const prevBg = this.style.backgroundColor;
215
276
  this.style.outline = '3px solid #ff5722';
@@ -219,7 +280,9 @@ export async function highlightElement(client: CdpClient, sessionId: string, ref
219
280
  this.style.backgroundColor = prevBg;
220
281
  }, 2000);
221
282
  }`,
222
- objectId,
223
- returnByValue: true,
224
- }, sessionId);
283
+ objectId,
284
+ returnByValue: true,
285
+ },
286
+ sessionId,
287
+ );
225
288
  }
@@ -1,7 +1,7 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { homedir, tmpdir } from 'node:os';
3
+ import { isAbsolute, join, relative, resolve } from 'node:path';
1
4
  import type { CdpClient } from './cdp.js';
2
- import { writeFile } from 'fs/promises';
3
- import { join, resolve, relative, isAbsolute } from 'path';
4
- import { tmpdir, homedir } from 'os';
5
5
 
6
6
  interface HarRequest {
7
7
  id: string;
@@ -24,14 +24,17 @@ interface HarRequest {
24
24
  const MAX_CONCURRENT_SESSIONS = 100;
25
25
  const MAX_REQUESTS_PER_SESSION = 5_000;
26
26
 
27
- const _sessions = new Map<string, {
28
- requests: Map<string, Partial<HarRequest>>;
29
- offReq: () => void;
30
- offResp: () => void;
31
- offFinished: () => void;
32
- startTime: number;
33
- startWallMs: number;
34
- }>();
27
+ const _sessions = new Map<
28
+ string,
29
+ {
30
+ requests: Map<string, Partial<HarRequest>>;
31
+ offReq: () => void;
32
+ offResp: () => void;
33
+ offFinished: () => void;
34
+ startTime: number;
35
+ startWallMs: number;
36
+ }
37
+ >();
35
38
 
36
39
  /** Validate output path is within cwd or home dir to prevent path traversal. */
37
40
  function safeOutputPath(p: string): string {
@@ -40,8 +43,10 @@ function safeOutputPath(p: string): string {
40
43
  const home = homedir();
41
44
  const relCwd = relative(cwd, resolved);
42
45
  const relHome = relative(home, resolved);
43
- if ((!relCwd.startsWith('..') && !isAbsolute(relCwd)) ||
44
- (!relHome.startsWith('..') && !isAbsolute(relHome))) {
46
+ if (
47
+ (!relCwd.startsWith('..') && !isAbsolute(relCwd)) ||
48
+ (!relHome.startsWith('..') && !isAbsolute(relHome))
49
+ ) {
45
50
  return resolved;
46
51
  }
47
52
  // Reject out-of-scope paths — fall back to tmpdir
@@ -51,7 +56,9 @@ function safeOutputPath(p: string): string {
51
56
  export async function startHarRecording(client: CdpClient, sessionId: string): Promise<void> {
52
57
  if (_sessions.has(sessionId)) throw new Error('HAR recording already in progress');
53
58
  if (_sessions.size >= MAX_CONCURRENT_SESSIONS) {
54
- throw new Error(`HAR recording limit reached (max ${MAX_CONCURRENT_SESSIONS} concurrent sessions)`);
59
+ throw new Error(
60
+ `HAR recording limit reached (max ${MAX_CONCURRENT_SESSIONS} concurrent sessions)`,
61
+ );
55
62
  }
56
63
 
57
64
  const requests = new Map<string, Partial<HarRequest>>();
@@ -68,7 +75,11 @@ export async function startHarRecording(client: CdpClient, sessionId: string): P
68
75
  if (sid !== sessionId) return;
69
76
  // Cap to prevent unbounded memory growth on high-traffic pages
70
77
  if (requests.size >= MAX_REQUESTS_PER_SESSION) return;
71
- const p = params as { requestId: string; request: { url: string; method: string; headers: Record<string, string> }; timestamp: number };
78
+ const p = params as {
79
+ requestId: string;
80
+ request: { url: string; method: string; headers: Record<string, string> };
81
+ timestamp: number;
82
+ };
72
83
  requests.set(p.requestId, {
73
84
  id: p.requestId,
74
85
  url: p.request.url,
@@ -81,7 +92,19 @@ export async function startHarRecording(client: CdpClient, sessionId: string): P
81
92
 
82
93
  const offResp = client.on('Network.responseReceived', (params, sid) => {
83
94
  if (sid !== sessionId) return;
84
- const p = params as { requestId: string; response: { url: string; status: number; statusText: string; mimeType: string; headers: Record<string, string>; fromDiskCache: boolean; fromServiceWorker: boolean }; timestamp: number };
95
+ const p = params as {
96
+ requestId: string;
97
+ response: {
98
+ url: string;
99
+ status: number;
100
+ statusText: string;
101
+ mimeType: string;
102
+ headers: Record<string, string>;
103
+ fromDiskCache: boolean;
104
+ fromServiceWorker: boolean;
105
+ };
106
+ timestamp: number;
107
+ };
85
108
  const entry = requests.get(p.requestId);
86
109
  if (entry) {
87
110
  entry.status = p.response.status;
@@ -110,7 +133,7 @@ export async function stopHarRecording(
110
133
  client: CdpClient,
111
134
  sessionId: string,
112
135
  outputPath?: string,
113
- captureResponseBodies = false
136
+ captureResponseBodies = false,
114
137
  ): Promise<string> {
115
138
  const state = _sessions.get(sessionId);
116
139
  if (!state) throw new Error('No active HAR recording for this session');
@@ -125,7 +148,9 @@ export async function stopHarRecording(
125
148
  for (const [reqId, entry] of state.requests.entries()) {
126
149
  try {
127
150
  const body = await client.send<{ body: string; base64Encoded: boolean }>(
128
- 'Network.getResponseBody', { requestId: reqId }, sessionId
151
+ 'Network.getResponseBody',
152
+ { requestId: reqId },
153
+ sessionId,
129
154
  );
130
155
  entry.responseBody = body.body;
131
156
  entry.bodyEncoding = body.base64Encoded ? 'base64' : undefined;
@@ -139,7 +164,9 @@ export async function stopHarRecording(
139
164
  }
140
165
 
141
166
  const har = buildHar(Array.from(state.requests.values()), state.startTime);
142
- const safePath = outputPath ? safeOutputPath(outputPath) : join(tmpdir(), `monomind-har-${Date.now()}.har`);
167
+ const safePath = outputPath
168
+ ? safeOutputPath(outputPath)
169
+ : join(tmpdir(), `monomind-har-${Date.now()}.har`);
143
170
  await writeFile(safePath, JSON.stringify(har, null, 2));
144
171
  return safePath;
145
172
  }
@@ -159,12 +186,14 @@ function buildHar(entries: Partial<HarRequest>[], startTime: number) {
159
186
  log: {
160
187
  version: '1.2',
161
188
  creator: { name: 'monomind browse', version: '1.0.0' },
162
- pages: [{
163
- startedDateTime: new Date(startTime).toISOString(),
164
- id: 'page_1',
165
- title: '',
166
- pageTimings: {},
167
- }],
189
+ pages: [
190
+ {
191
+ startedDateTime: new Date(startTime).toISOString(),
192
+ id: 'page_1',
193
+ title: '',
194
+ pageTimings: {},
195
+ },
196
+ ],
168
197
  entries: entries.map((e) => ({
169
198
  startedDateTime: new Date(e.startTime ?? startTime).toISOString(),
170
199
  time: (e.endTime ?? e.startTime ?? startTime) - (e.startTime ?? startTime),
@@ -183,7 +212,10 @@ function buildHar(entries: Partial<HarRequest>[], startTime: number) {
183
212
  statusText: e.statusText ?? '',
184
213
  httpVersion: 'HTTP/1.1',
185
214
  cookies: [],
186
- headers: Object.entries(e.responseHeaders ?? {}).map(([name, value]) => ({ name, value })),
215
+ headers: Object.entries(e.responseHeaders ?? {}).map(([name, value]) => ({
216
+ name,
217
+ value,
218
+ })),
187
219
  content: {
188
220
  size: e.size ?? -1,
189
221
  mimeType: e.mimeType ?? 'application/octet-stream',