@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,9 +1,9 @@
1
+ import { mkdir, writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
1
4
  import type { CdpClient } from './cdp.js';
2
- import type { ElementRef } from './types.js';
3
5
  import { getObjectIdForRef } from './snapshot.js';
4
- import { writeFile, mkdir } from 'fs/promises';
5
- import { join, dirname } from 'path';
6
- import { tmpdir } from 'os';
6
+ import type { ElementRef } from './types.js';
7
7
 
8
8
  export interface ScreenshotOptions {
9
9
  path?: string;
@@ -17,16 +17,21 @@ export interface ScreenshotOptions {
17
17
  async function getViewportTopLeft(
18
18
  client: CdpClient,
19
19
  sessionId: string,
20
- ref: ElementRef
20
+ ref: ElementRef,
21
21
  ): Promise<{ x: number; y: number } | null> {
22
22
  const objectId = await getObjectIdForRef(client, sessionId, ref).catch(() => null);
23
23
  if (!objectId) return null;
24
24
  try {
25
- const r = await client.send<{ result: { value?: string } }>('Runtime.callFunctionOn', {
26
- functionDeclaration: 'function() { var r=this.getBoundingClientRect(); if(r.width===0&&r.height===0)return null; return JSON.stringify({x:Math.round(r.left),y:Math.round(r.top)}); }',
27
- objectId,
28
- returnByValue: true,
29
- }, sessionId);
25
+ const r = await client.send<{ result: { value?: string } }>(
26
+ 'Runtime.callFunctionOn',
27
+ {
28
+ functionDeclaration:
29
+ 'function() { var r=this.getBoundingClientRect(); if(r.width===0&&r.height===0)return null; return JSON.stringify({x:Math.round(r.left),y:Math.round(r.top)}); }',
30
+ objectId,
31
+ returnByValue: true,
32
+ },
33
+ sessionId,
34
+ );
30
35
  if (!r.result?.value) return null;
31
36
  return JSON.parse(r.result.value) as { x: number; y: number };
32
37
  } catch {
@@ -37,7 +42,7 @@ async function getViewportTopLeft(
37
42
  async function injectAnnotationOverlay(
38
43
  client: CdpClient,
39
44
  sessionId: string,
40
- refs: Map<string, ElementRef>
45
+ refs: Map<string, ElementRef>,
41
46
  ): Promise<void> {
42
47
  const badges: Array<{ num: number; x: number; y: number }> = [];
43
48
  for (const [key, ref] of refs) {
@@ -49,8 +54,11 @@ async function injectAnnotationOverlay(
49
54
  if (badges.length === 0) return;
50
55
 
51
56
  const badgesJson = JSON.stringify(badges);
52
- await client.send('Runtime.evaluate', {
53
- expression: `(function(){
57
+ await client
58
+ .send(
59
+ 'Runtime.evaluate',
60
+ {
61
+ expression: `(function(){
54
62
  var p=document.getElementById('__mm_ann__');if(p)p.remove();
55
63
  var c=document.createElement('div');
56
64
  c.id='__mm_ann__';
@@ -64,21 +72,30 @@ async function injectAnnotationOverlay(
64
72
  });
65
73
  document.body.appendChild(c);
66
74
  })()`,
67
- returnByValue: false,
68
- }, sessionId).catch(() => {});
75
+ returnByValue: false,
76
+ },
77
+ sessionId,
78
+ )
79
+ .catch(() => {});
69
80
  }
70
81
 
71
82
  async function removeAnnotationOverlay(client: CdpClient, sessionId: string): Promise<void> {
72
- await client.send('Runtime.evaluate', {
73
- expression: `(function(){var e=document.getElementById('__mm_ann__');if(e)e.remove();})()`,
74
- returnByValue: false,
75
- }, sessionId).catch(() => {});
83
+ await client
84
+ .send(
85
+ 'Runtime.evaluate',
86
+ {
87
+ expression: `(function(){var e=document.getElementById('__mm_ann__');if(e)e.remove();})()`,
88
+ returnByValue: false,
89
+ },
90
+ sessionId,
91
+ )
92
+ .catch(() => {});
76
93
  }
77
94
 
78
95
  export async function captureScreenshot(
79
96
  client: CdpClient,
80
97
  sessionId: string,
81
- options: ScreenshotOptions = {}
98
+ options: ScreenshotOptions = {},
82
99
  ): Promise<{ path: string; dataUrl: string }> {
83
100
  const format = options.format ?? 'png';
84
101
  const params: Record<string, unknown> = { format };
@@ -88,10 +105,15 @@ export async function captureScreenshot(
88
105
  }
89
106
 
90
107
  if (options.fullPage) {
91
- const dims = await client.send<{ result: { value: string } }>('Runtime.evaluate', {
92
- expression: 'JSON.stringify({w: document.documentElement.scrollWidth, h: document.documentElement.scrollHeight})',
93
- returnByValue: true,
94
- }, sessionId);
108
+ const dims = await client.send<{ result: { value: string } }>(
109
+ 'Runtime.evaluate',
110
+ {
111
+ expression:
112
+ 'JSON.stringify({w: document.documentElement.scrollWidth, h: document.documentElement.scrollHeight})',
113
+ returnByValue: true,
114
+ },
115
+ sessionId,
116
+ );
95
117
  const { w, h } = JSON.parse(dims.result?.value ?? '{"w":1280,"h":720}');
96
118
  params.clip = { x: 0, y: 0, width: w, height: h, scale: 1 };
97
119
  params.captureBeyondViewport = true;
@@ -123,20 +145,24 @@ export async function setViewport(
123
145
  client: CdpClient,
124
146
  sessionId: string,
125
147
  width: number,
126
- height: number
148
+ height: number,
127
149
  ): Promise<void> {
128
- await client.send('Emulation.setDeviceMetricsOverride', {
129
- width,
130
- height,
131
- deviceScaleFactor: 1,
132
- mobile: false,
133
- }, sessionId);
150
+ await client.send(
151
+ 'Emulation.setDeviceMetricsOverride',
152
+ {
153
+ width,
154
+ height,
155
+ deviceScaleFactor: 1,
156
+ mobile: false,
157
+ },
158
+ sessionId,
159
+ );
134
160
  }
135
161
 
136
162
  export async function setUserAgent(
137
163
  client: CdpClient,
138
164
  sessionId: string,
139
- userAgent: string
165
+ userAgent: string,
140
166
  ): Promise<void> {
141
167
  await client.send('Emulation.setUserAgentOverride', { userAgent }, sessionId);
142
168
  }
@@ -1,9 +1,16 @@
1
- import { readFile, writeFile, mkdir } from 'fs/promises';
2
- import { join, dirname } from 'path';
3
- import { homedir } from 'os';
4
- import type { SessionState, CdpCookie } from './types.js';
1
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
5
4
  import type { CdpClient } from './cdp.js';
6
- import { getCookiesForUrls, setCookies, getLocalStorage, setLocalStorage, getSessionStorage, setSessionStorage } from './network.js';
5
+ import {
6
+ getCookiesForUrls,
7
+ getLocalStorage,
8
+ getSessionStorage,
9
+ setCookies,
10
+ setLocalStorage,
11
+ setSessionStorage,
12
+ } from './network.js';
13
+ import type { SessionState } from './types.js';
7
14
 
8
15
  // Session state files contain cookies/localStorage — treat as secrets:
9
16
  // owner-only read/write on the file (0600) and owner-only access on the
@@ -23,7 +30,12 @@ function validateFilePath(filePath: string): void {
23
30
  if (!filePath || filePath.includes('\x00')) {
24
31
  throw new Error(`Invalid file path: ${JSON.stringify(filePath)}`);
25
32
  }
26
- if (filePath.includes('/../') || filePath.startsWith('../') || filePath.endsWith('/..') || filePath.includes('\\')) {
33
+ if (
34
+ filePath.includes('/../') ||
35
+ filePath.startsWith('../') ||
36
+ filePath.endsWith('/..') ||
37
+ filePath.includes('\\')
38
+ ) {
27
39
  throw new Error(`Invalid file path (traversal not allowed): ${JSON.stringify(filePath)}`);
28
40
  }
29
41
  }
@@ -34,7 +46,7 @@ export async function saveSession(
34
46
  targetId: string,
35
47
  name: string,
36
48
  url: string,
37
- title: string
49
+ title: string,
38
50
  ): Promise<string> {
39
51
  validateSessionName(name);
40
52
  await mkdir(SESSION_DIR, { recursive: true, mode: SESSION_DIR_MODE });
@@ -43,7 +55,15 @@ export async function saveSession(
43
55
  const localStorage = await getLocalStorage(client, sessionId);
44
56
  const sessionStorage = await getSessionStorage(client, sessionId);
45
57
 
46
- const state: SessionState = { targetId, sessionId, url, title, cookies, localStorage, sessionStorage };
58
+ const state: SessionState = {
59
+ targetId,
60
+ sessionId,
61
+ url,
62
+ title,
63
+ cookies,
64
+ localStorage,
65
+ sessionStorage,
66
+ };
47
67
  const filePath = join(SESSION_DIR, `${name}.json`);
48
68
  await writeFile(filePath, JSON.stringify(state, null, 2), { mode: SESSION_FILE_MODE });
49
69
  return filePath;
@@ -52,7 +72,7 @@ export async function saveSession(
52
72
  export async function loadSession(
53
73
  client: CdpClient,
54
74
  sessionId: string,
55
- name: string
75
+ name: string,
56
76
  ): Promise<SessionState> {
57
77
  validateSessionName(name);
58
78
  const filePath = join(SESSION_DIR, `${name}.json`);
@@ -61,15 +81,18 @@ export async function loadSession(
61
81
  throw err;
62
82
  });
63
83
  const state: SessionState = JSON.parse(raw);
64
- if (!Array.isArray(state.cookies)) throw new Error(`Invalid session file: cookies is not an array`);
84
+ if (!Array.isArray(state.cookies))
85
+ throw new Error(`Invalid session file: cookies is not an array`);
65
86
 
66
87
  await setCookies(client, sessionId, state.cookies);
67
88
  if (state.localStorage) {
68
- if (typeof state.localStorage !== 'object' || Array.isArray(state.localStorage)) throw new Error('Invalid session file: localStorage is not a plain object');
89
+ if (typeof state.localStorage !== 'object' || Array.isArray(state.localStorage))
90
+ throw new Error('Invalid session file: localStorage is not a plain object');
69
91
  await setLocalStorage(client, sessionId, state.localStorage);
70
92
  }
71
93
  if (state.sessionStorage) {
72
- if (typeof state.sessionStorage !== 'object' || Array.isArray(state.sessionStorage)) throw new Error('Invalid session file: sessionStorage is not a plain object');
94
+ if (typeof state.sessionStorage !== 'object' || Array.isArray(state.sessionStorage))
95
+ throw new Error('Invalid session file: sessionStorage is not a plain object');
73
96
  await setSessionStorage(client, sessionId, state.sessionStorage);
74
97
  }
75
98
 
@@ -82,21 +105,29 @@ export async function saveStateFile(
82
105
  targetId: string,
83
106
  filePath: string,
84
107
  url: string,
85
- title: string
108
+ title: string,
86
109
  ): Promise<void> {
87
110
  validateFilePath(filePath);
88
111
  await mkdir(dirname(filePath), { recursive: true, mode: SESSION_DIR_MODE });
89
112
  const cookies = await getCookiesForUrls(client, sessionId, [url]);
90
113
  const localStorage = await getLocalStorage(client, sessionId);
91
114
  const sessionStorage = await getSessionStorage(client, sessionId);
92
- const state: SessionState = { targetId, sessionId, url, title, cookies, localStorage, sessionStorage };
115
+ const state: SessionState = {
116
+ targetId,
117
+ sessionId,
118
+ url,
119
+ title,
120
+ cookies,
121
+ localStorage,
122
+ sessionStorage,
123
+ };
93
124
  await writeFile(filePath, JSON.stringify(state, null, 2), { mode: SESSION_FILE_MODE });
94
125
  }
95
126
 
96
127
  export async function loadStateFile(
97
128
  client: CdpClient,
98
129
  sessionId: string,
99
- filePath: string
130
+ filePath: string,
100
131
  ): Promise<SessionState> {
101
132
  validateFilePath(filePath);
102
133
  const raw = await readFile(filePath, 'utf8');
@@ -104,18 +135,20 @@ export async function loadStateFile(
104
135
  if (!Array.isArray(state.cookies)) throw new Error(`Invalid state file: cookies is not an array`);
105
136
  await setCookies(client, sessionId, state.cookies);
106
137
  if (state.localStorage) {
107
- if (typeof state.localStorage !== 'object' || Array.isArray(state.localStorage)) throw new Error('Invalid state file: localStorage is not a plain object');
138
+ if (typeof state.localStorage !== 'object' || Array.isArray(state.localStorage))
139
+ throw new Error('Invalid state file: localStorage is not a plain object');
108
140
  await setLocalStorage(client, sessionId, state.localStorage);
109
141
  }
110
142
  if (state.sessionStorage) {
111
- if (typeof state.sessionStorage !== 'object' || Array.isArray(state.sessionStorage)) throw new Error('Invalid state file: sessionStorage is not a plain object');
143
+ if (typeof state.sessionStorage !== 'object' || Array.isArray(state.sessionStorage))
144
+ throw new Error('Invalid state file: sessionStorage is not a plain object');
112
145
  await setSessionStorage(client, sessionId, state.sessionStorage);
113
146
  }
114
147
  return state;
115
148
  }
116
149
 
117
150
  export async function listSessions(): Promise<string[]> {
118
- const { readdir } = await import('fs/promises');
151
+ const { readdir } = await import('node:fs/promises');
119
152
  const files = await readdir(SESSION_DIR).catch((err: NodeJS.ErrnoException) => {
120
153
  if (err.code === 'ENOENT') return [] as string[];
121
154
  throw err;
@@ -1,7 +1,7 @@
1
+ import { getCurrentTitle, getCurrentUrl } from './browser.js';
1
2
  import type { CdpClient } from './cdp.js';
2
3
  import type { ElementRef, SnapshotOptions, SnapshotResult } from './types.js';
3
4
  import { INTERACTIVE_ROLES } from './types.js';
4
- import { getCurrentUrl, getCurrentTitle } from './browser.js';
5
5
 
6
6
  interface AXNode {
7
7
  nodeId: number;
@@ -20,7 +20,7 @@ interface AXNode {
20
20
  export async function captureSnapshot(
21
21
  client: CdpClient,
22
22
  sessionId: string,
23
- options: SnapshotOptions = {}
23
+ options: SnapshotOptions = {},
24
24
  ): Promise<SnapshotResult> {
25
25
  const { interactiveOnly = false, compact = false, maxDepth, selector } = options;
26
26
 
@@ -28,25 +28,39 @@ export async function captureSnapshot(
28
28
  let nodes: AXNode[];
29
29
  if (selector) {
30
30
  const doc = await client.send<{ root: { nodeId: number } }>('DOM.getDocument', {}, sessionId);
31
- const found = await client.send<{ nodeId: number }>(
32
- 'DOM.querySelector',
33
- { nodeId: doc.root.nodeId, selector },
34
- sessionId
35
- ).catch(() => ({ nodeId: 0 }));
31
+ const found = await client
32
+ .send<{ nodeId: number }>(
33
+ 'DOM.querySelector',
34
+ { nodeId: doc.root.nodeId, selector },
35
+ sessionId,
36
+ )
37
+ .catch(() => ({ nodeId: 0 }));
36
38
 
37
39
  if (found.nodeId) {
38
- const partial = await client.send<{ nodes: AXNode[] }>(
39
- 'Accessibility.getPartialAXTree',
40
- { nodeId: found.nodeId, fetchRelatives: false },
41
- sessionId
42
- ).catch(async () => client.send<{ nodes: AXNode[] }>('Accessibility.getFullAXTree', {}, sessionId));
40
+ const partial = await client
41
+ .send<{ nodes: AXNode[] }>(
42
+ 'Accessibility.getPartialAXTree',
43
+ { nodeId: found.nodeId, fetchRelatives: false },
44
+ sessionId,
45
+ )
46
+ .catch(async () =>
47
+ client.send<{ nodes: AXNode[] }>('Accessibility.getFullAXTree', {}, sessionId),
48
+ );
43
49
  nodes = partial.nodes;
44
50
  } else {
45
- const full = await client.send<{ nodes: AXNode[] }>('Accessibility.getFullAXTree', {}, sessionId);
51
+ const full = await client.send<{ nodes: AXNode[] }>(
52
+ 'Accessibility.getFullAXTree',
53
+ {},
54
+ sessionId,
55
+ );
46
56
  nodes = full.nodes;
47
57
  }
48
58
  } else {
49
- const full = await client.send<{ nodes: AXNode[] }>('Accessibility.getFullAXTree', {}, sessionId);
59
+ const full = await client.send<{ nodes: AXNode[] }>(
60
+ 'Accessibility.getFullAXTree',
61
+ {},
62
+ sessionId,
63
+ );
50
64
  nodes = full.nodes;
51
65
  }
52
66
 
@@ -192,10 +206,10 @@ function extractProperties(node: AXNode): {
192
206
  }
193
207
 
194
208
  export async function resolveRef(
195
- client: CdpClient,
196
- sessionId: string,
209
+ _client: CdpClient,
210
+ _sessionId: string,
197
211
  refs: Map<string, ElementRef>,
198
- refKey: string
212
+ refKey: string,
199
213
  ): Promise<ElementRef> {
200
214
  const ref = refs.get(refKey);
201
215
  if (!ref) throw new Error(`Element ref @${refKey} not found. Run snapshot first.`);
@@ -205,14 +219,14 @@ export async function resolveRef(
205
219
  export async function getObjectIdForRef(
206
220
  client: CdpClient,
207
221
  sessionId: string,
208
- ref: ElementRef
222
+ ref: ElementRef,
209
223
  ): Promise<string | null> {
210
224
  if (!ref.backendDOMNodeId) return null;
211
225
 
212
226
  const result = await client.send<{ object: { objectId?: string } }>(
213
227
  'DOM.resolveNode',
214
228
  { backendNodeId: ref.backendDOMNodeId },
215
- sessionId
229
+ sessionId,
216
230
  );
217
231
  return result.object?.objectId ?? null;
218
232
  }
@@ -231,7 +245,7 @@ export async function getObjectIdForRef(
231
245
  export async function getElementBox(
232
246
  client: CdpClient,
233
247
  sessionId: string,
234
- ref: ElementRef
248
+ ref: ElementRef,
235
249
  ): Promise<{ x: number; y: number; width: number; height: number } | null> {
236
250
  if (!ref.backendDOMNodeId) return null;
237
251
 
@@ -1,16 +1,37 @@
1
- import type { CdpClient } from './cdp.js';
2
1
  import { evaluateJs } from './actions.js';
2
+ import type { CdpClient } from './cdp.js';
3
3
 
4
- export async function getLocalStorageKey(client: CdpClient, sessionId: string, key: string): Promise<string | null> {
5
- const result = await evaluateJs(client, sessionId, `localStorage.getItem(${JSON.stringify(key)})`);
4
+ export async function getLocalStorageKey(
5
+ client: CdpClient,
6
+ sessionId: string,
7
+ key: string,
8
+ ): Promise<string | null> {
9
+ const result = await evaluateJs(
10
+ client,
11
+ sessionId,
12
+ `localStorage.getItem(${JSON.stringify(key)})`,
13
+ );
6
14
  return result as string | null;
7
15
  }
8
16
 
9
- export async function setLocalStorageKey(client: CdpClient, sessionId: string, key: string, value: string): Promise<void> {
10
- await evaluateJs(client, sessionId, `localStorage.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)})`);
17
+ export async function setLocalStorageKey(
18
+ client: CdpClient,
19
+ sessionId: string,
20
+ key: string,
21
+ value: string,
22
+ ): Promise<void> {
23
+ await evaluateJs(
24
+ client,
25
+ sessionId,
26
+ `localStorage.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)})`,
27
+ );
11
28
  }
12
29
 
13
- export async function removeLocalStorageKey(client: CdpClient, sessionId: string, key: string): Promise<void> {
30
+ export async function removeLocalStorageKey(
31
+ client: CdpClient,
32
+ sessionId: string,
33
+ key: string,
34
+ ): Promise<void> {
14
35
  await evaluateJs(client, sessionId, `localStorage.removeItem(${JSON.stringify(key)})`);
15
36
  }
16
37
 
@@ -18,25 +39,53 @@ export async function clearLocalStorage(client: CdpClient, sessionId: string): P
18
39
  await evaluateJs(client, sessionId, 'localStorage.clear()');
19
40
  }
20
41
 
21
- export async function getAllLocalStorage(client: CdpClient, sessionId: string): Promise<Record<string, string>> {
42
+ export async function getAllLocalStorage(
43
+ client: CdpClient,
44
+ sessionId: string,
45
+ ): Promise<Record<string, string>> {
22
46
  const result = await evaluateJs(
23
47
  client,
24
48
  sessionId,
25
- 'JSON.stringify(Object.fromEntries(Object.entries(localStorage)))'
49
+ 'JSON.stringify(Object.fromEntries(Object.entries(localStorage)))',
26
50
  );
27
- try { return JSON.parse(result as string); } catch { return {}; }
51
+ try {
52
+ return JSON.parse(result as string);
53
+ } catch {
54
+ return {};
55
+ }
28
56
  }
29
57
 
30
- export async function getSessionStorageKey(client: CdpClient, sessionId: string, key: string): Promise<string | null> {
31
- const result = await evaluateJs(client, sessionId, `sessionStorage.getItem(${JSON.stringify(key)})`);
58
+ export async function getSessionStorageKey(
59
+ client: CdpClient,
60
+ sessionId: string,
61
+ key: string,
62
+ ): Promise<string | null> {
63
+ const result = await evaluateJs(
64
+ client,
65
+ sessionId,
66
+ `sessionStorage.getItem(${JSON.stringify(key)})`,
67
+ );
32
68
  return result as string | null;
33
69
  }
34
70
 
35
- export async function setSessionStorageKey(client: CdpClient, sessionId: string, key: string, value: string): Promise<void> {
36
- await evaluateJs(client, sessionId, `sessionStorage.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)})`);
71
+ export async function setSessionStorageKey(
72
+ client: CdpClient,
73
+ sessionId: string,
74
+ key: string,
75
+ value: string,
76
+ ): Promise<void> {
77
+ await evaluateJs(
78
+ client,
79
+ sessionId,
80
+ `sessionStorage.setItem(${JSON.stringify(key)}, ${JSON.stringify(value)})`,
81
+ );
37
82
  }
38
83
 
39
- export async function removeSessionStorageKey(client: CdpClient, sessionId: string, key: string): Promise<void> {
84
+ export async function removeSessionStorageKey(
85
+ client: CdpClient,
86
+ sessionId: string,
87
+ key: string,
88
+ ): Promise<void> {
40
89
  await evaluateJs(client, sessionId, `sessionStorage.removeItem(${JSON.stringify(key)})`);
41
90
  }
42
91
 
@@ -44,11 +93,18 @@ export async function clearSessionStorage(client: CdpClient, sessionId: string):
44
93
  await evaluateJs(client, sessionId, 'sessionStorage.clear()');
45
94
  }
46
95
 
47
- export async function getAllSessionStorage(client: CdpClient, sessionId: string): Promise<Record<string, string>> {
96
+ export async function getAllSessionStorage(
97
+ client: CdpClient,
98
+ sessionId: string,
99
+ ): Promise<Record<string, string>> {
48
100
  const result = await evaluateJs(
49
101
  client,
50
102
  sessionId,
51
- 'JSON.stringify(Object.fromEntries(Object.entries(sessionStorage)))'
103
+ 'JSON.stringify(Object.fromEntries(Object.entries(sessionStorage)))',
52
104
  );
53
- try { return JSON.parse(result as string); } catch { return {}; }
105
+ try {
106
+ return JSON.parse(result as string);
107
+ } catch {
108
+ return {};
109
+ }
54
110
  }
@@ -1,6 +1,6 @@
1
1
  import type { CdpClient } from './cdp.js';
2
+ import { fetchNewTarget, fetchTargets } from './cdp.js';
2
3
  import type { CdpTarget } from './types.js';
3
- import { fetchTargets, fetchNewTarget } from './cdp.js';
4
4
 
5
5
  export async function listTabs(port: number): Promise<CdpTarget[]> {
6
6
  const targets = await fetchTargets(port);
@@ -11,16 +11,27 @@ export async function newTab(port: number, url = 'about:blank'): Promise<CdpTarg
11
11
  return fetchNewTarget(port, url);
12
12
  }
13
13
 
14
- export async function closeTab(client: CdpClient, _sessionId: string, targetId: string): Promise<void> {
14
+ export async function closeTab(
15
+ client: CdpClient,
16
+ _sessionId: string,
17
+ targetId: string,
18
+ ): Promise<void> {
15
19
  await client.send('Target.closeTarget', { targetId });
16
20
  }
17
21
 
18
- export async function activateTab(client: CdpClient, oldSessionId: string, targetId: string): Promise<string> {
22
+ export async function activateTab(
23
+ client: CdpClient,
24
+ oldSessionId: string,
25
+ targetId: string,
26
+ ): Promise<string> {
19
27
  if (oldSessionId) {
20
28
  await client.send('Target.detachFromTarget', { sessionId: oldSessionId }).catch(() => {});
21
29
  }
22
30
  await client.send('Target.activateTarget', { targetId });
23
- const result = await client.send<{ sessionId: string }>('Target.attachToTarget', { targetId, flatten: true });
31
+ const result = await client.send<{ sessionId: string }>('Target.attachToTarget', {
32
+ targetId,
33
+ flatten: true,
34
+ });
24
35
  return result.sessionId;
25
36
  }
26
37
 
@@ -32,7 +43,7 @@ export interface FrameSwitchResult {
32
43
  export async function switchToFrame(
33
44
  client: CdpClient,
34
45
  sessionId: string,
35
- frameSelector: string
46
+ frameSelector: string,
36
47
  ): Promise<FrameSwitchResult> {
37
48
  const { result } = await client.send<{ result: { result: { value: string | null } } }>(
38
49
  'Runtime.evaluate',
@@ -44,29 +55,30 @@ export async function switchToFrame(
44
55
  })()`,
45
56
  returnByValue: true,
46
57
  },
47
- sessionId
58
+ sessionId,
48
59
  );
49
60
  const frameSrc = result?.result?.value ?? null;
50
61
 
51
62
  const { frameTree } = await client.send<{
52
- frameTree: { childFrames?: Array<{ frame: { id: string; url: string; securityOrigin: string } }> };
63
+ frameTree: {
64
+ childFrames?: Array<{ frame: { id: string; url: string; securityOrigin: string } }>;
65
+ };
53
66
  }>('Page.getFrameTree', {}, sessionId);
54
67
 
55
68
  const match = frameTree.childFrames?.find(
56
- (cf) => cf.frame.url === frameSrc || cf.frame.id === frameSelector
69
+ (cf) => cf.frame.url === frameSrc || cf.frame.id === frameSelector,
57
70
  );
58
71
  if (!match) return { url: frameSrc, sessionId: null };
59
72
 
60
- const targets = await client.send<{ targetInfos: Array<{ targetId: string; type: string; url: string }> }>(
61
- 'Target.getTargets', {}
62
- );
63
- const oopif = targets.targetInfos.find(
64
- (t) => t.type === 'iframe' && t.url === match.frame.url
65
- );
73
+ const targets = await client.send<{
74
+ targetInfos: Array<{ targetId: string; type: string; url: string }>;
75
+ }>('Target.getTargets', {});
76
+ const oopif = targets.targetInfos.find((t) => t.type === 'iframe' && t.url === match.frame.url);
66
77
  if (oopif) {
67
- const attached = await client.send<{ sessionId: string }>(
68
- 'Target.attachToTarget', { targetId: oopif.targetId, flatten: true }
69
- );
78
+ const attached = await client.send<{ sessionId: string }>('Target.attachToTarget', {
79
+ targetId: oopif.targetId,
80
+ flatten: true,
81
+ });
70
82
  return { url: frameSrc, sessionId: attached.sessionId };
71
83
  }
72
84
 
@@ -1,7 +1,7 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
1
4
  import type { CdpClient } from './cdp.js';
2
- import { writeFile } from 'fs/promises';
3
- import { join } from 'path';
4
- import { tmpdir } from 'os';
5
5
 
6
6
  export interface TraceOptions {
7
7
  path?: string;
@@ -27,7 +27,7 @@ const _sessions = new Map<string, { events: unknown[]; offData: () => void }>();
27
27
  export async function startTrace(
28
28
  client: CdpClient,
29
29
  sessionId: string,
30
- options: TraceOptions = {}
30
+ options: TraceOptions = {},
31
31
  ): Promise<void> {
32
32
  if (_sessions.has(sessionId)) {
33
33
  throw new Error('Trace already in progress for this session');
@@ -47,12 +47,16 @@ export async function startTrace(
47
47
  if (options.screenshots) cats.push('disabled-by-default-devtools.screenshot');
48
48
 
49
49
  try {
50
- await client.send('Tracing.start', {
51
- traceConfig: {
52
- includedCategories: cats.filter((c) => !c.startsWith('-')),
53
- excludedCategories: cats.filter((c) => c.startsWith('-')).map((c) => c.slice(1)),
50
+ await client.send(
51
+ 'Tracing.start',
52
+ {
53
+ traceConfig: {
54
+ includedCategories: cats.filter((c) => !c.startsWith('-')),
55
+ excludedCategories: cats.filter((c) => c.startsWith('-')).map((c) => c.slice(1)),
56
+ },
54
57
  },
55
- }, sessionId);
58
+ sessionId,
59
+ );
56
60
  } catch (err) {
57
61
  offData();
58
62
  _sessions.delete(sessionId);
@@ -63,7 +67,7 @@ export async function startTrace(
63
67
  export async function stopTrace(
64
68
  client: CdpClient,
65
69
  sessionId: string,
66
- outputPath?: string
70
+ outputPath?: string,
67
71
  ): Promise<string> {
68
72
  const state = _sessions.get(sessionId);
69
73
  if (!state) throw new Error('No active trace for this session');