@monoes/monobrowse 1.0.26 → 1.0.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/__tests__/disconnect-session.test.d.ts +13 -0
- package/dist/src/__tests__/disconnect-session.test.d.ts.map +1 -0
- package/dist/src/__tests__/disconnect-session.test.js +37 -0
- package/dist/src/__tests__/disconnect-session.test.js.map +1 -0
- package/dist/src/__tests__/profile-cleanup-close.test.d.ts +16 -0
- package/dist/src/__tests__/profile-cleanup-close.test.d.ts.map +1 -0
- package/dist/src/__tests__/profile-cleanup-close.test.js +250 -0
- package/dist/src/__tests__/profile-cleanup-close.test.js.map +1 -0
- package/dist/src/__tests__/profile-dir.test.d.ts +10 -0
- package/dist/src/__tests__/profile-dir.test.d.ts.map +1 -0
- package/dist/src/__tests__/profile-dir.test.js +160 -0
- package/dist/src/__tests__/profile-dir.test.js.map +1 -0
- package/dist/src/__tests__/setup/isolated-tmpdir.global.d.ts +2 -0
- package/dist/src/__tests__/setup/isolated-tmpdir.global.d.ts.map +1 -0
- package/dist/src/__tests__/setup/isolated-tmpdir.global.js +30 -0
- package/dist/src/__tests__/setup/isolated-tmpdir.global.js.map +1 -0
- package/dist/src/browser/actions-eval.d.ts +8 -0
- package/dist/src/browser/actions-eval.d.ts.map +1 -0
- package/dist/src/browser/actions-eval.js +72 -0
- package/dist/src/browser/actions-eval.js.map +1 -0
- package/dist/src/browser/actions-form.d.ts +10 -0
- package/dist/src/browser/actions-form.d.ts.map +1 -0
- package/dist/src/browser/actions-form.js +133 -0
- package/dist/src/browser/actions-form.js.map +1 -0
- package/dist/src/browser/actions-keyboard.d.ts +7 -0
- package/dist/src/browser/actions-keyboard.d.ts.map +1 -0
- package/dist/src/browser/actions-keyboard.js +131 -0
- package/dist/src/browser/actions-keyboard.js.map +1 -0
- package/dist/src/browser/actions-mouse.d.ts +11 -0
- package/dist/src/browser/actions-mouse.d.ts.map +1 -0
- package/dist/src/browser/actions-mouse.js +105 -0
- package/dist/src/browser/actions-mouse.js.map +1 -0
- package/dist/src/browser/actions.d.ts +12 -28
- package/dist/src/browser/actions.d.ts.map +1 -1
- package/dist/src/browser/actions.js +12 -434
- package/dist/src/browser/actions.js.map +1 -1
- package/dist/src/browser/browser-discovery.d.ts +26 -0
- package/dist/src/browser/browser-discovery.d.ts.map +1 -0
- package/dist/src/browser/browser-discovery.js +113 -0
- package/dist/src/browser/browser-discovery.js.map +1 -0
- package/dist/src/browser/browser-launch.d.ts +3 -0
- package/dist/src/browser/browser-launch.d.ts.map +1 -0
- package/dist/src/browser/browser-launch.js +341 -0
- package/dist/src/browser/browser-launch.js.map +1 -0
- package/dist/src/browser/browser-lifecycle.d.ts +57 -0
- package/dist/src/browser/browser-lifecycle.d.ts.map +1 -0
- package/dist/src/browser/browser-lifecycle.js +245 -0
- package/dist/src/browser/browser-lifecycle.js.map +1 -0
- package/dist/src/browser/browser-page.d.ts +13 -0
- package/dist/src/browser/browser-page.d.ts.map +1 -0
- package/dist/src/browser/browser-page.js +179 -0
- package/dist/src/browser/browser-page.js.map +1 -0
- package/dist/src/browser/browser-state.d.ts +11 -0
- package/dist/src/browser/browser-state.d.ts.map +1 -0
- package/dist/src/browser/browser-state.js +27 -0
- package/dist/src/browser/browser-state.js.map +1 -0
- package/dist/src/browser/browser.d.ts +5 -68
- package/dist/src/browser/browser.d.ts.map +1 -1
- package/dist/src/browser/browser.js +9 -849
- package/dist/src/browser/browser.js.map +1 -1
- package/dist/src/browser/profile-dir.d.ts +88 -0
- package/dist/src/browser/profile-dir.d.ts.map +1 -0
- package/dist/src/browser/profile-dir.js +242 -0
- package/dist/src/browser/profile-dir.js.map +1 -0
- package/dist/src/browser/ref-cache.d.ts +4 -0
- package/dist/src/browser/ref-cache.d.ts.map +1 -1
- package/dist/src/browser/ref-cache.js +2 -0
- package/dist/src/browser/ref-cache.js.map +1 -1
- package/dist/src/browser/types.d.ts +6 -0
- package/dist/src/browser/types.d.ts.map +1 -1
- package/dist/src/browser/types.js.map +1 -1
- package/dist/src/cli/commands.d.ts +1 -0
- package/dist/src/cli/commands.d.ts.map +1 -1
- package/dist/src/cli/commands.js +2 -0
- package/dist/src/cli/commands.js.map +1 -1
- package/dist/src/cli/session.d.ts +9 -0
- package/dist/src/cli/session.d.ts.map +1 -1
- package/dist/src/cli/session.js +21 -4
- package/dist/src/cli/session.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/__tests__/disconnect-session.test.ts +42 -0
- package/src/__tests__/profile-cleanup-close.test.ts +285 -0
- package/src/__tests__/profile-dir.test.ts +196 -0
- package/src/__tests__/setup/isolated-tmpdir.global.ts +28 -0
- package/src/browser/actions-eval.ts +118 -0
- package/src/browser/actions-form.ts +218 -0
- package/src/browser/actions-keyboard.ts +171 -0
- package/src/browser/actions-mouse.ts +212 -0
- package/src/browser/actions.ts +37 -712
- package/src/browser/browser-discovery.ts +116 -0
- package/src/browser/browser-launch.ts +375 -0
- package/src/browser/browser-lifecycle.ts +248 -0
- package/src/browser/browser-page.ts +213 -0
- package/src/browser/browser-state.ts +30 -0
- package/src/browser/browser.ts +21 -917
- package/src/browser/profile-dir.ts +249 -0
- package/src/browser/ref-cache.ts +7 -0
- package/src/browser/types.ts +6 -0
- package/src/cli/commands.ts +2 -0
- package/src/cli/session.ts +22 -4
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
// Split out of browser.ts (file-size sweep). Pure move: no behaviour change.
|
|
2
|
+
import { fetchBrowserWebSocketUrl } from './browser-discovery.js';
|
|
3
|
+
import { launchedPids, launchedUserDataDirs, ownedUserDataDirPorts } from './browser-state.js';
|
|
4
|
+
import { CdpClient, fetchTargets } from './cdp.js';
|
|
5
|
+
import { removeOwnedProfileDir } from './profile-dir.js';
|
|
6
|
+
import { listSessionRecords, loadSessionRecord, removeSessionRecord, } from './ref-cache.js';
|
|
7
|
+
const BROWSER_CLOSE_TIMEOUT_MS = 3000;
|
|
8
|
+
// How long closeBrowser() waits for the process to actually disappear before
|
|
9
|
+
// forcing it, and again after forcing. Bounded so a wedged Chrome cannot hang
|
|
10
|
+
// a caller indefinitely.
|
|
11
|
+
const PROCESS_EXIT_TIMEOUT_MS = 5000;
|
|
12
|
+
const PROCESS_EXIT_POLL_MS = 50;
|
|
13
|
+
// A cross-process persisted PID older than this is not trusted for the
|
|
14
|
+
// SIGKILL fallback in closeBrowser() — see the call site for why.
|
|
15
|
+
const PERSISTED_PID_MAX_AGE_MS = 24 * 60 * 60 * 1000;
|
|
16
|
+
// A browser WE launched that has had no targets for longer than this is
|
|
17
|
+
// reaped at the next launch/attach — see reapIdleLaunchedBrowser().
|
|
18
|
+
const IDLE_REAP_AFTER_MS = 30 * 60 * 1000;
|
|
19
|
+
const REAP_CONNECT_TIMEOUT_MS = 3000;
|
|
20
|
+
/**
|
|
21
|
+
* Cleanly terminate a Chrome/Chromium instance we launched on `port`.
|
|
22
|
+
* Sends the `Browser.close` CDP command (the correct graceful shutdown —
|
|
23
|
+
* closes all tabs and exits the process cleanly) over `client`'s connection.
|
|
24
|
+
* If that command fails, times out, or the connection is already gone,
|
|
25
|
+
* falls back to killing the tracked PID directly so a headed/interactive
|
|
26
|
+
* browser window (e.g. one spawned for a login/CAPTCHA flow) never lingers
|
|
27
|
+
* as a visible, authenticated, still-debuggable orphan process.
|
|
28
|
+
*
|
|
29
|
+
* On the graceful path this resolves only once the process is actually gone,
|
|
30
|
+
* or after force-killing it when it outlasts PROCESS_EXIT_TIMEOUT_MS. Chrome
|
|
31
|
+
* acknowledges `Browser.close` well before it exits, and this used to return
|
|
32
|
+
* on the ack with an unref'd 1s timer as the only force-kill — so it resolved
|
|
33
|
+
* while Chrome was still running and holding its port and its profile's
|
|
34
|
+
* singleton lock, and if the caller's process exited first the kill never
|
|
35
|
+
* happened at all. Callers reasonably read a resolved close() as "that
|
|
36
|
+
* browser is gone"; on the graceful path it now means that.
|
|
37
|
+
*
|
|
38
|
+
* A profile directory monobrowse created for that browser (the default temp
|
|
39
|
+
* profile, or a CLI session's — never a caller-supplied one) is deleted once
|
|
40
|
+
* the process is gone, on both paths (#395). In a fresh process the dir and
|
|
41
|
+
* its ownership come from the persisted session record.
|
|
42
|
+
*/
|
|
43
|
+
export async function closeBrowser(client, port) {
|
|
44
|
+
let gracefullyClosed = false;
|
|
45
|
+
let closeTimer;
|
|
46
|
+
try {
|
|
47
|
+
await Promise.race([
|
|
48
|
+
client.send('Browser.close', {}),
|
|
49
|
+
// Deliberately NOT unref'd, for the same reason waitForProcessExit()'s
|
|
50
|
+
// poll below is not. This timer is the only thing that settles the race
|
|
51
|
+
// when Chrome's socket dies without ever acknowledging the close, and
|
|
52
|
+
// closeBrowser() is actively awaited by callers. An unref'd timer does
|
|
53
|
+
// not hold the event loop open, so Node would consider the loop drained
|
|
54
|
+
// and exit — status 0, before the caller's verdict was ever printed.
|
|
55
|
+
// Cleared in the finally so a close that IS acknowledged does not pin
|
|
56
|
+
// the loop for the rest of BROWSER_CLOSE_TIMEOUT_MS.
|
|
57
|
+
new Promise((_, reject) => {
|
|
58
|
+
closeTimer = setTimeout(() => reject(new Error('Browser.close timed out')), BROWSER_CLOSE_TIMEOUT_MS);
|
|
59
|
+
}),
|
|
60
|
+
]);
|
|
61
|
+
gracefullyClosed = true;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// fall through to PID-kill fallback below
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
clearTimeout(closeTimer);
|
|
68
|
+
}
|
|
69
|
+
let pid = launchedPids.get(port);
|
|
70
|
+
let profileDir = ownedUserDataDirPorts.has(port) ? launchedUserDataDirs.get(port) : undefined;
|
|
71
|
+
if (pid === undefined) {
|
|
72
|
+
// Fresh process (each CLI invocation is its own node process — see
|
|
73
|
+
// module header) — launchedPids is per-process and empty here even
|
|
74
|
+
// though a prior `open` in a DIFFERENT process launched this Chrome.
|
|
75
|
+
// Fall back to the PID that process persisted via saveSessionRecord().
|
|
76
|
+
try {
|
|
77
|
+
const persisted = await loadSessionRecord(port);
|
|
78
|
+
if (persisted?.launched && persisted.pid) {
|
|
79
|
+
// Bound how far we trust a cross-process PID: if the Chrome we
|
|
80
|
+
// launched died outside this flow (OOM-killed, host slept, etc.)
|
|
81
|
+
// without its session record ever being cleared, the OS can recycle
|
|
82
|
+
// that PID for an unrelated process. There is no cmdline/start-time
|
|
83
|
+
// check available portably, so a coarse age bound is the practical
|
|
84
|
+
// guard — a PID persisted more than a day ago is far more likely to
|
|
85
|
+
// be stale/reused than to still be the same live Chrome.
|
|
86
|
+
const age = persisted.savedAt !== undefined ? Date.now() - persisted.savedAt : undefined;
|
|
87
|
+
if (age === undefined || age <= PERSISTED_PID_MAX_AGE_MS) {
|
|
88
|
+
pid = persisted.pid;
|
|
89
|
+
if (persisted.ownsUserDataDir)
|
|
90
|
+
profileDir = persisted.userDataDir;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
// best-effort — fall through to "nothing to kill" below
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (pid === undefined)
|
|
99
|
+
return; // not a process we launched, or too stale to trust — nothing to kill
|
|
100
|
+
launchedPids.delete(port);
|
|
101
|
+
ownedUserDataDirPorts.delete(port);
|
|
102
|
+
// Liveness-check before every SIGKILL (not just the safety-net path below)
|
|
103
|
+
// — process.kill(pid, 0) throws ESRCH if the PID is no longer running,
|
|
104
|
+
// which also catches the case where the PID has already been recycled by
|
|
105
|
+
// an unrelated process that happens to have exited too.
|
|
106
|
+
if (!gracefullyClosed) {
|
|
107
|
+
try {
|
|
108
|
+
process.kill(pid, 0);
|
|
109
|
+
process.kill(pid, 'SIGKILL');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* already exited, or never was — nothing to do */
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
else if (!(await waitForProcessExit(pid))) {
|
|
116
|
+
// Browser.close was acknowledged — wait for the process to actually go
|
|
117
|
+
// away, then force-kill as a safety net in case it hung.
|
|
118
|
+
try {
|
|
119
|
+
process.kill(pid, 'SIGKILL');
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
/* raced us to exit — expected */
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
// Waits (bounded) for the killed process to be gone before deleting.
|
|
126
|
+
if (profileDir)
|
|
127
|
+
await removeOwnedProfileDir(profileDir, { pid });
|
|
128
|
+
}
|
|
129
|
+
/** Poll until `pid` is gone or PROCESS_EXIT_TIMEOUT_MS elapses. Returns
|
|
130
|
+
* whether it exited. `process.kill(pid, 0)` throws once the process is no
|
|
131
|
+
* longer there, on Windows as well as POSIX.
|
|
132
|
+
*
|
|
133
|
+
* This timer is deliberately NOT unref'd. closeBrowser() awaits this poll
|
|
134
|
+
* as part of its own return value — a caller that awaits closeBrowser() is
|
|
135
|
+
* actively blocked on it, not doing other work in the background. Once
|
|
136
|
+
* Chrome's CDP websocket closes (which happens right around here, as part
|
|
137
|
+
* of it shutting down), nothing else may be left holding the event loop
|
|
138
|
+
* open; an unref'd timer at that point never fires because Node considers
|
|
139
|
+
* the loop drained and exits without running it, leaving this promise (and
|
|
140
|
+
* closeBrowser()'s) pending forever — reproduced locally by closing a real
|
|
141
|
+
* launched browser with nothing else scheduled. Bounded by
|
|
142
|
+
* PROCESS_EXIT_TIMEOUT_MS (5s) either way, so keeping it ref'd only ever
|
|
143
|
+
* costs a caller a few seconds, never a hang. */
|
|
144
|
+
async function waitForProcessExit(pid) {
|
|
145
|
+
const deadline = Date.now() + PROCESS_EXIT_TIMEOUT_MS;
|
|
146
|
+
for (;;) {
|
|
147
|
+
try {
|
|
148
|
+
process.kill(pid, 0);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (Date.now() >= deadline)
|
|
154
|
+
return false;
|
|
155
|
+
await new Promise((resolve) => {
|
|
156
|
+
setTimeout(resolve, PROCESS_EXIT_POLL_MS);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Give back the CDP port of a browser we launched that nothing is using any
|
|
162
|
+
* more. A launched Chrome is deliberately detached and unref'd so it outlives
|
|
163
|
+
* the command that spawned it and later commands can reattach — but nothing
|
|
164
|
+
* reaped it afterwards, so an abandoned session kept listening on its port
|
|
165
|
+
* (9222 by convention, which other local tools want) indefinitely. A foreign
|
|
166
|
+
* CDP server *answers* there rather than refusing the connection, so those
|
|
167
|
+
* tools retry the wrong port and report misleading errors instead of moving on.
|
|
168
|
+
*
|
|
169
|
+
* Nothing long-lived exists to run this on a timer — every CLI command is its
|
|
170
|
+
* own short-lived process — so it runs at the next launch/attach instead
|
|
171
|
+
* (see launchBrowser). A browser is reaped only when ALL of these hold:
|
|
172
|
+
* - the persisted session says WE launched it — `connect` records
|
|
173
|
+
* launched:false for a browser that belongs to the user, never touched;
|
|
174
|
+
* - that session is older than IDLE_REAP_AFTER_MS;
|
|
175
|
+
* - the CDP endpoint still answers and reports ZERO targets. An open tab,
|
|
176
|
+
* or a client attached to one (Chrome drops webSocketDebuggerUrl from an
|
|
177
|
+
* attached target but still lists it), means the session is live and
|
|
178
|
+
* reattach-across-commands must keep working.
|
|
179
|
+
*
|
|
180
|
+
* Shutdown goes through closeBrowser() on the browser-level websocket — the
|
|
181
|
+
* only connection left once every target is gone — so a recycled PID can
|
|
182
|
+
* never be signaled by mistake: graceful `Browser.close` reaches whatever is
|
|
183
|
+
* actually listening on that port, at any age, and the persisted PID is only
|
|
184
|
+
* ever used for closeBrowser's own age-bounded force-kill.
|
|
185
|
+
*
|
|
186
|
+
* Never throws: a failed reap must not stop a launch. Returns the reaped
|
|
187
|
+
* port, or null when there was nothing to reap. Sessions are per-port
|
|
188
|
+
* (#318), so every recorded one is considered, not just the newest.
|
|
189
|
+
*/
|
|
190
|
+
export async function reapIdleLaunchedBrowser() {
|
|
191
|
+
let reaped = null;
|
|
192
|
+
for (const session of await listSessionRecords().catch(() => [])) {
|
|
193
|
+
const port = await reapSession(session);
|
|
194
|
+
reaped ??= port;
|
|
195
|
+
}
|
|
196
|
+
return reaped;
|
|
197
|
+
}
|
|
198
|
+
async function reapSession(session) {
|
|
199
|
+
try {
|
|
200
|
+
if (!session.launched || session.savedAt === undefined)
|
|
201
|
+
return null;
|
|
202
|
+
if (Date.now() - session.savedAt < IDLE_REAP_AFTER_MS)
|
|
203
|
+
return null;
|
|
204
|
+
const wsUrl = await fetchBrowserWebSocketUrl(session.port);
|
|
205
|
+
if (!wsUrl)
|
|
206
|
+
return null;
|
|
207
|
+
if ((await fetchTargets(session.port)).length > 0)
|
|
208
|
+
return null;
|
|
209
|
+
const client = new CdpClient();
|
|
210
|
+
let reapTimer;
|
|
211
|
+
try {
|
|
212
|
+
// CdpClient.connect() has no timeout of its own — a socket that never
|
|
213
|
+
// opens (and never errors) would hang the launch this reap is running
|
|
214
|
+
// inside of, which is strictly worse than leaving the port squatted.
|
|
215
|
+
// Not unref'd (see closeBrowser): a socket that never opens and never
|
|
216
|
+
// errors leaves this timer as the only thing that can settle the race,
|
|
217
|
+
// and the reap is awaited inside launch/attach. Cleared in the finally
|
|
218
|
+
// below so a connect that succeeds does not pin the event loop.
|
|
219
|
+
await Promise.race([
|
|
220
|
+
client.connect(wsUrl),
|
|
221
|
+
new Promise((_, reject) => {
|
|
222
|
+
reapTimer = setTimeout(() => reject(new Error('Reap: CDP connect timed out')), REAP_CONNECT_TIMEOUT_MS);
|
|
223
|
+
}),
|
|
224
|
+
]);
|
|
225
|
+
await closeBrowser(client, session.port);
|
|
226
|
+
}
|
|
227
|
+
finally {
|
|
228
|
+
clearTimeout(reapTimer);
|
|
229
|
+
try {
|
|
230
|
+
client.close();
|
|
231
|
+
}
|
|
232
|
+
catch {
|
|
233
|
+
/* already gone */
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
launchedPids.delete(session.port);
|
|
237
|
+
launchedUserDataDirs.delete(session.port);
|
|
238
|
+
await removeSessionRecord(session.port);
|
|
239
|
+
return session.port;
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
//# sourceMappingURL=browser-lifecycle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-lifecycle.js","sourceRoot":"","sources":["../../../src/browser/browser-lifecycle.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AACzD,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,mBAAmB,GAEpB,MAAM,gBAAgB,CAAC;AAExB,MAAM,wBAAwB,GAAG,IAAI,CAAC;AACtC,6EAA6E;AAC7E,8EAA8E;AAC9E,yBAAyB;AACzB,MAAM,uBAAuB,GAAG,IAAI,CAAC;AACrC,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAChC,uEAAuE;AACvE,kEAAkE;AAClE,MAAM,wBAAwB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AACrD,wEAAwE;AACxE,oEAAoE;AACpE,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAC1C,MAAM,uBAAuB,GAAG,IAAI,CAAC;AAErC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAiB,EAAE,IAAY;IAChE,IAAI,gBAAgB,GAAG,KAAK,CAAC;IAC7B,IAAI,UAAqD,CAAC;IAC1D,IAAI,CAAC;QACH,MAAM,OAAO,CAAC,IAAI,CAAC;YACjB,MAAM,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YAChC,uEAAuE;YACvE,wEAAwE;YACxE,sEAAsE;YACtE,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,sEAAsE;YACtE,qDAAqD;YACrD,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,UAAU,GAAG,UAAU,CACrB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC,EAClD,wBAAwB,CACzB,CAAC;YACJ,CAAC,CAAC;SACH,CAAC,CAAC;QACH,gBAAgB,GAAG,IAAI,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,0CAA0C;IAC5C,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,UAAU,CAAC,CAAC;IAC3B,CAAC;IAED,IAAI,GAAG,GAAG,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,UAAU,GAAG,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9F,IAAI,GAAG,KAAK,SAAS,EAAE,CAAC;QACtB,mEAAmE;QACnE,mEAAmE;QACnE,qEAAqE;QACrE,uEAAuE;QACvE,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,MAAM,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,SAAS,EAAE,QAAQ,IAAI,SAAS,CAAC,GAAG,EAAE,CAAC;gBACzC,+DAA+D;gBAC/D,iEAAiE;gBACjE,oEAAoE;gBACpE,oEAAoE;gBACpE,mEAAmE;gBACnE,oEAAoE;gBACpE,yDAAyD;gBACzD,MAAM,GAAG,GAAG,SAAS,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;gBACzF,IAAI,GAAG,KAAK,SAAS,IAAI,GAAG,IAAI,wBAAwB,EAAE,CAAC;oBACzD,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC;oBACpB,IAAI,SAAS,CAAC,eAAe;wBAAE,UAAU,GAAG,SAAS,CAAC,WAAW,CAAC;gBACpE,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wDAAwD;QAC1D,CAAC;IACH,CAAC;IACD,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,CAAC,qEAAqE;IACpG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,qBAAqB,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEnC,2EAA2E;IAC3E,uEAAuE;IACvE,yEAAyE;IACzE,wDAAwD;IACxD,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACrB,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;IACH,CAAC;SAAM,IAAI,CAAC,CAAC,MAAM,kBAAkB,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC5C,uEAAuE;QACvE,yDAAyD;QACzD,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC/B,CAAC;QAAC,MAAM,CAAC;YACP,iCAAiC;QACnC,CAAC;IACH,CAAC;IAED,qEAAqE;IACrE,IAAI,UAAU;QAAE,MAAM,qBAAqB,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;AACnE,CAAC;AAED;;;;;;;;;;;;;;kDAckD;AAClD,KAAK,UAAU,kBAAkB,CAAC,GAAW;IAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,uBAAuB,CAAC;IACtD,SAAS,CAAC;QACR,IAAI,CAAC;YACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;QACD,IAAI,IAAI,CAAC,GAAG,EAAE,IAAI,QAAQ;YAAE,OAAO,KAAK,CAAC;QACzC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC5B,UAAU,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QAC5C,CAAC,CAAC,CAAC;IACL,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,CAAC,KAAK,UAAU,uBAAuB;IAC3C,IAAI,MAAM,GAAkB,IAAI,CAAC;IACjC,KAAK,MAAM,OAAO,IAAI,MAAM,kBAAkB,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;QACjE,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,KAAK,IAAI,CAAC;IAClB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,OAAsB;IAC/C,IAAI,CAAC;QACH,IAAI,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC;QACpE,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,OAAO,GAAG,kBAAkB;YAAE,OAAO,IAAI,CAAC;QAEnE,MAAM,KAAK,GAAG,MAAM,wBAAwB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3D,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QACxB,IAAI,CAAC,MAAM,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAE/D,MAAM,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC/B,IAAI,SAAoD,CAAC;QACzD,IAAI,CAAC;YACH,sEAAsE;YACtE,sEAAsE;YACtE,qEAAqE;YACrE,sEAAsE;YACtE,uEAAuE;YACvE,uEAAuE;YACvE,gEAAgE;YAChE,MAAM,OAAO,CAAC,IAAI,CAAC;gBACjB,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;gBACrB,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;oBAC/B,SAAS,GAAG,UAAU,CACpB,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC,EACtD,uBAAuB,CACxB,CAAC;gBACJ,CAAC,CAAC;aACH,CAAC,CAAC;YACH,MAAM,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,SAAS,CAAC,CAAC;YACxB,IAAI,CAAC;gBACH,MAAM,CAAC,KAAK,EAAE,CAAC;YACjB,CAAC;YAAC,MAAM,CAAC;gBACP,kBAAkB;YACpB,CAAC;QACH,CAAC;QACD,YAAY,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAClC,oBAAoB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,mBAAmB,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,OAAO,CAAC,IAAI,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { CdpClient } from './cdp.js';
|
|
2
|
+
import type { CdpTarget } from './types.js';
|
|
3
|
+
export declare function enableSessionDomains(client: CdpClient, sessionId: string): Promise<void>;
|
|
4
|
+
export declare function connectToTarget(port: number, targetId?: string): Promise<{
|
|
5
|
+
client: CdpClient;
|
|
6
|
+
target: CdpTarget;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
}>;
|
|
9
|
+
export declare function openUrl(client: CdpClient, sessionId: string, url: string): Promise<void>;
|
|
10
|
+
export declare function waitForLoad(client: CdpClient, sessionId: string, condition?: 'load' | 'networkidle' | 'domcontentloaded', timeout?: number): Promise<void>;
|
|
11
|
+
export declare function getCurrentUrl(client: CdpClient, sessionId: string): Promise<string>;
|
|
12
|
+
export declare function getCurrentTitle(client: CdpClient, sessionId: string): Promise<string>;
|
|
13
|
+
//# sourceMappingURL=browser-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-page.d.ts","sourceRoot":"","sources":["../../../src/browser/browser-page.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAgC,MAAM,UAAU,CAAC;AAGnE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,wBAAsB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAW9F;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,MAAM,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BtE;AAED,wBAAsB,OAAO,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAU9F;AAED,wBAAsB,WAAW,CAC/B,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,MAAM,EACjB,SAAS,GAAE,MAAM,GAAG,aAAa,GAAG,kBAA2B,EAC/D,OAAO,SAAS,GACf,OAAO,CAAC,IAAI,CAAC,CAyCf;AAiFD,wBAAsB,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUzF;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAU3F"}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// Split out of browser.ts (file-size sweep). Pure move: no behaviour change.
|
|
2
|
+
import { CdpClient, fetchNewTarget, fetchTargets } from './cdp.js';
|
|
3
|
+
import { enableConsoleCapture, setupConsoleCapture } from './console-log.js';
|
|
4
|
+
import { setupDialogAutoHandling } from './dialog.js';
|
|
5
|
+
export async function enableSessionDomains(client, sessionId) {
|
|
6
|
+
await Promise.all([
|
|
7
|
+
client.send('Page.enable', {}, sessionId),
|
|
8
|
+
client.send('Runtime.enable', {}, sessionId),
|
|
9
|
+
client.send('Network.enable', {}, sessionId),
|
|
10
|
+
client.send('DOM.enable', {}, sessionId),
|
|
11
|
+
client.send('Accessibility.enable', {}, sessionId),
|
|
12
|
+
]);
|
|
13
|
+
setupConsoleCapture(client, sessionId);
|
|
14
|
+
await enableConsoleCapture(client, sessionId);
|
|
15
|
+
setupDialogAutoHandling(client, sessionId);
|
|
16
|
+
}
|
|
17
|
+
export async function connectToTarget(port, targetId) {
|
|
18
|
+
const targets = await fetchTargets(port);
|
|
19
|
+
const pageTargets = targets.filter((t) => t.type === 'page');
|
|
20
|
+
let target;
|
|
21
|
+
if (targetId) {
|
|
22
|
+
const found = pageTargets.find((t) => t.id === targetId);
|
|
23
|
+
if (!found)
|
|
24
|
+
throw new Error(`Target ${targetId} not found`);
|
|
25
|
+
target = found;
|
|
26
|
+
}
|
|
27
|
+
else if (pageTargets.length > 0) {
|
|
28
|
+
target = pageTargets[0];
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
target = await fetchNewTarget(port, 'about:blank');
|
|
32
|
+
}
|
|
33
|
+
const wsUrl = target.webSocketDebuggerUrl ?? `ws://127.0.0.1:${port}/devtools/page/${target.id}`;
|
|
34
|
+
const client = new CdpClient();
|
|
35
|
+
await client.connect(wsUrl);
|
|
36
|
+
const { sessionId } = await client.send('Target.attachToTarget', {
|
|
37
|
+
targetId: target.id,
|
|
38
|
+
flatten: true,
|
|
39
|
+
});
|
|
40
|
+
await enableSessionDomains(client, sessionId);
|
|
41
|
+
return { client, target, sessionId };
|
|
42
|
+
}
|
|
43
|
+
export async function openUrl(client, sessionId, url) {
|
|
44
|
+
// Cap to 2 MB to prevent OOM in CDP message serializer (e.g. data: URI attacks)
|
|
45
|
+
if (url.length > 2_097_152)
|
|
46
|
+
throw new Error('URL exceeds 2 MB limit');
|
|
47
|
+
// Page.navigate reports a protocol-level failure (e.g. a refused connection)
|
|
48
|
+
// via errorText in its own response, before Chrome ever settles on the
|
|
49
|
+
// chrome-error://chromewebdata/ page — without this check a refused
|
|
50
|
+
// connection previously reported success against that error page.
|
|
51
|
+
const nav = await client.send('Page.navigate', { url }, sessionId);
|
|
52
|
+
if (nav.errorText)
|
|
53
|
+
throw new Error(`Navigation to ${url} failed: ${nav.errorText}`);
|
|
54
|
+
await waitForNetworkIdle(client, sessionId, 500, 30_000);
|
|
55
|
+
}
|
|
56
|
+
export async function waitForLoad(client, sessionId, condition = 'load', timeout = 30_000) {
|
|
57
|
+
if (condition === 'load' || condition === 'domcontentloaded') {
|
|
58
|
+
// Guard against race where the page loads before the listener is registered
|
|
59
|
+
const readyExpr = condition === 'load'
|
|
60
|
+
? 'document.readyState === "complete"'
|
|
61
|
+
: 'document.readyState !== "loading"';
|
|
62
|
+
const readyCheck = await client
|
|
63
|
+
.send('Runtime.evaluate', {
|
|
64
|
+
expression: readyExpr,
|
|
65
|
+
returnByValue: true,
|
|
66
|
+
}, sessionId)
|
|
67
|
+
.catch(() => ({ result: { value: false } }));
|
|
68
|
+
if (readyCheck.result?.value)
|
|
69
|
+
return;
|
|
70
|
+
const event = condition === 'load' ? 'Page.loadEventFired' : 'Page.domContentEventFired';
|
|
71
|
+
const [eventPromise, cancelOnce] = client.onceWithOff(event, sessionId);
|
|
72
|
+
let timedOut = false;
|
|
73
|
+
let timeoutHandle;
|
|
74
|
+
const timeoutPromise = new Promise((resolve) => {
|
|
75
|
+
timeoutHandle = setTimeout(() => {
|
|
76
|
+
timedOut = true;
|
|
77
|
+
resolve();
|
|
78
|
+
}, timeout);
|
|
79
|
+
});
|
|
80
|
+
try {
|
|
81
|
+
await Promise.race([eventPromise, timeoutPromise]);
|
|
82
|
+
if (timedOut)
|
|
83
|
+
throw new Error(`Timeout waiting for ${condition}`);
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
cancelOnce();
|
|
87
|
+
clearTimeout(timeoutHandle);
|
|
88
|
+
}
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
// networkidle: no network requests for 500ms
|
|
92
|
+
await waitForNetworkIdle(client, sessionId, 500, timeout);
|
|
93
|
+
}
|
|
94
|
+
async function waitForNetworkIdle(client, sessionId, idleMs, timeout) {
|
|
95
|
+
return new Promise((resolve, reject) => {
|
|
96
|
+
let pending = 0;
|
|
97
|
+
const inflight = new Set();
|
|
98
|
+
let idleTimer = null;
|
|
99
|
+
const killTimer = setTimeout(() => {
|
|
100
|
+
cleanup();
|
|
101
|
+
reject(new Error('Timeout waiting for networkidle'));
|
|
102
|
+
}, timeout);
|
|
103
|
+
const cleanup = () => {
|
|
104
|
+
if (idleTimer) {
|
|
105
|
+
clearTimeout(idleTimer);
|
|
106
|
+
idleTimer = null;
|
|
107
|
+
}
|
|
108
|
+
clearTimeout(killTimer);
|
|
109
|
+
offReq();
|
|
110
|
+
offResp();
|
|
111
|
+
offFail();
|
|
112
|
+
offCache();
|
|
113
|
+
offResp2();
|
|
114
|
+
};
|
|
115
|
+
const settle = () => {
|
|
116
|
+
cleanup();
|
|
117
|
+
resolve();
|
|
118
|
+
};
|
|
119
|
+
const check = () => {
|
|
120
|
+
if (pending === 0) {
|
|
121
|
+
if (idleTimer)
|
|
122
|
+
clearTimeout(idleTimer);
|
|
123
|
+
idleTimer = setTimeout(settle, idleMs);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (idleTimer) {
|
|
127
|
+
clearTimeout(idleTimer);
|
|
128
|
+
idleTimer = null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
const offReq = client.on('Network.requestWillBeSent', (params, sid) => {
|
|
133
|
+
if (sid !== sessionId)
|
|
134
|
+
return;
|
|
135
|
+
const id = params.requestId;
|
|
136
|
+
if (!inflight.has(id)) {
|
|
137
|
+
inflight.add(id);
|
|
138
|
+
pending++;
|
|
139
|
+
check();
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
const decrement = (params, sid) => {
|
|
143
|
+
if (sid !== sessionId)
|
|
144
|
+
return;
|
|
145
|
+
const id = params.requestId;
|
|
146
|
+
if (inflight.delete(id)) {
|
|
147
|
+
pending = Math.max(0, pending - 1);
|
|
148
|
+
check();
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
const offResp = client.on('Network.loadingFinished', decrement);
|
|
152
|
+
const offFail = client.on('Network.loadingFailed', decrement);
|
|
153
|
+
const offCache = client.on('Network.requestServedFromCache', decrement);
|
|
154
|
+
// Guard against requests that never fire loadingFinished/loadingFailed (e.g. data: URLs)
|
|
155
|
+
// Skip 3xx redirect responses — the request continues under the same requestId
|
|
156
|
+
const offResp2 = client.on('Network.responseReceived', (params, sid) => {
|
|
157
|
+
const p = params;
|
|
158
|
+
if (p.response.status >= 300 && p.response.status < 400)
|
|
159
|
+
return;
|
|
160
|
+
decrement(params, sid);
|
|
161
|
+
});
|
|
162
|
+
check();
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
export async function getCurrentUrl(client, sessionId) {
|
|
166
|
+
const result = await client.send('Runtime.evaluate', {
|
|
167
|
+
expression: 'location.href',
|
|
168
|
+
returnByValue: true,
|
|
169
|
+
}, sessionId);
|
|
170
|
+
return result.result?.value ?? '';
|
|
171
|
+
}
|
|
172
|
+
export async function getCurrentTitle(client, sessionId) {
|
|
173
|
+
const result = await client.send('Runtime.evaluate', {
|
|
174
|
+
expression: 'document.title',
|
|
175
|
+
returnByValue: true,
|
|
176
|
+
}, sessionId);
|
|
177
|
+
return result.result?.value ?? '';
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=browser-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-page.js","sourceRoot":"","sources":["../../../src/browser/browser-page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const launchedPids: Map<number, number>;
|
|
2
|
+
export declare const launchedUserDataDirs: Map<number, string>;
|
|
3
|
+
export declare const ownedUserDataDirPorts: Set<number>;
|
|
4
|
+
/** PID of the Chrome instance *this process* launched on `port`, if any. */
|
|
5
|
+
export declare function getLaunchedPid(port: number): number | undefined;
|
|
6
|
+
/** userDataDir of the Chrome instance *this process* launched on `port`, if any. */
|
|
7
|
+
export declare function getLaunchedUserDataDir(port: number): string | undefined;
|
|
8
|
+
/** Whether the userDataDir of the Chrome *this process* launched on `port`
|
|
9
|
+
* was created by monobrowse, and so is deleted when that browser closes. */
|
|
10
|
+
export declare function ownsLaunchedUserDataDir(port: number): boolean;
|
|
11
|
+
//# sourceMappingURL=browser-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-state.d.ts","sourceRoot":"","sources":["../../../src/browser/browser-state.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,YAAY,qBAA4B,CAAC;AAGtD,eAAO,MAAM,oBAAoB,qBAA4B,CAAC;AAG9D,eAAO,MAAM,qBAAqB,aAAoB,CAAC;AAEvD,4EAA4E;AAC5E,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE/D;AAED,oFAAoF;AACpF,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAEvE;AAED;6EAC6E;AAC7E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE7D"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// Split out of browser.ts (file-size sweep). Pure move: no behaviour change.
|
|
2
|
+
// Tracks the PID of Chrome instances *we* spawned, keyed by CDP port, so
|
|
3
|
+
// closeBrowser() has a kill fallback when the graceful `Browser.close` CDP
|
|
4
|
+
// command fails or times out (e.g. a hung renderer). Ports we merely attached
|
|
5
|
+
// to (already-running browser) are never recorded here — we only ever kill
|
|
6
|
+
// processes we launched ourselves.
|
|
7
|
+
export const launchedPids = new Map();
|
|
8
|
+
// userDataDir per launched port, persisted alongside the pid so a later
|
|
9
|
+
// process can clean up the temp profile dir too (#395).
|
|
10
|
+
export const launchedUserDataDirs = new Map();
|
|
11
|
+
// Launched ports whose userDataDir monobrowse created itself (not one the
|
|
12
|
+
// caller supplied). Only these profile dirs are ever deleted on close.
|
|
13
|
+
export const ownedUserDataDirPorts = new Set();
|
|
14
|
+
/** PID of the Chrome instance *this process* launched on `port`, if any. */
|
|
15
|
+
export function getLaunchedPid(port) {
|
|
16
|
+
return launchedPids.get(port);
|
|
17
|
+
}
|
|
18
|
+
/** userDataDir of the Chrome instance *this process* launched on `port`, if any. */
|
|
19
|
+
export function getLaunchedUserDataDir(port) {
|
|
20
|
+
return launchedUserDataDirs.get(port);
|
|
21
|
+
}
|
|
22
|
+
/** Whether the userDataDir of the Chrome *this process* launched on `port`
|
|
23
|
+
* was created by monobrowse, and so is deleted when that browser closes. */
|
|
24
|
+
export function ownsLaunchedUserDataDir(port) {
|
|
25
|
+
return ownedUserDataDirPorts.has(port);
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=browser-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-state.js","sourceRoot":"","sources":["../../../src/browser/browser-state.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAE7E,yEAAyE;AACzE,2EAA2E;AAC3E,8EAA8E;AAC9E,2EAA2E;AAC3E,mCAAmC;AACnC,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,GAAG,EAAkB,CAAC;AACtD,wEAAwE;AACxE,wDAAwD;AACxD,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAC9D,0EAA0E;AAC1E,uEAAuE;AACvE,MAAM,CAAC,MAAM,qBAAqB,GAAG,IAAI,GAAG,EAAU,CAAC;AAEvD,4EAA4E;AAC5E,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AAChC,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,sBAAsB,CAAC,IAAY;IACjD,OAAO,oBAAoB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACxC,CAAC;AAED;6EAC6E;AAC7E,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,qBAAqB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
|
|
@@ -1,69 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export
|
|
5
|
-
|
|
6
|
-
export declare function getLaunchedUserDataDir(port: number): string | undefined;
|
|
7
|
-
export declare function isPortOpen(port: number): Promise<boolean>;
|
|
8
|
-
export declare function launchBrowser(config?: BrowserConfig): Promise<number>;
|
|
9
|
-
export declare function enableSessionDomains(client: CdpClient, sessionId: string): Promise<void>;
|
|
10
|
-
export declare function connectToTarget(port: number, targetId?: string): Promise<{
|
|
11
|
-
client: CdpClient;
|
|
12
|
-
target: CdpTarget;
|
|
13
|
-
sessionId: string;
|
|
14
|
-
}>;
|
|
15
|
-
/**
|
|
16
|
-
* Cleanly terminate a Chrome/Chromium instance we launched on `port`.
|
|
17
|
-
* Sends the `Browser.close` CDP command (the correct graceful shutdown —
|
|
18
|
-
* closes all tabs and exits the process cleanly) over `client`'s connection.
|
|
19
|
-
* If that command fails, times out, or the connection is already gone,
|
|
20
|
-
* falls back to killing the tracked PID directly so a headed/interactive
|
|
21
|
-
* browser window (e.g. one spawned for a login/CAPTCHA flow) never lingers
|
|
22
|
-
* as a visible, authenticated, still-debuggable orphan process.
|
|
23
|
-
*
|
|
24
|
-
* On the graceful path this resolves only once the process is actually gone,
|
|
25
|
-
* or after force-killing it when it outlasts PROCESS_EXIT_TIMEOUT_MS. Chrome
|
|
26
|
-
* acknowledges `Browser.close` well before it exits, and this used to return
|
|
27
|
-
* on the ack with an unref'd 1s timer as the only force-kill — so it resolved
|
|
28
|
-
* while Chrome was still running and holding its port and its profile's
|
|
29
|
-
* singleton lock, and if the caller's process exited first the kill never
|
|
30
|
-
* happened at all. Callers reasonably read a resolved close() as "that
|
|
31
|
-
* browser is gone"; on the graceful path it now means that.
|
|
32
|
-
*/
|
|
33
|
-
export declare function closeBrowser(client: CdpClient, port: number): Promise<void>;
|
|
34
|
-
/**
|
|
35
|
-
* Give back the CDP port of a browser we launched that nothing is using any
|
|
36
|
-
* more. A launched Chrome is deliberately detached and unref'd so it outlives
|
|
37
|
-
* the command that spawned it and later commands can reattach — but nothing
|
|
38
|
-
* reaped it afterwards, so an abandoned session kept listening on its port
|
|
39
|
-
* (9222 by convention, which other local tools want) indefinitely. A foreign
|
|
40
|
-
* CDP server *answers* there rather than refusing the connection, so those
|
|
41
|
-
* tools retry the wrong port and report misleading errors instead of moving on.
|
|
42
|
-
*
|
|
43
|
-
* Nothing long-lived exists to run this on a timer — every CLI command is its
|
|
44
|
-
* own short-lived process — so it runs at the next launch/attach instead
|
|
45
|
-
* (see launchBrowser). A browser is reaped only when ALL of these hold:
|
|
46
|
-
* - the persisted session says WE launched it — `connect` records
|
|
47
|
-
* launched:false for a browser that belongs to the user, never touched;
|
|
48
|
-
* - that session is older than IDLE_REAP_AFTER_MS;
|
|
49
|
-
* - the CDP endpoint still answers and reports ZERO targets. An open tab,
|
|
50
|
-
* or a client attached to one (Chrome drops webSocketDebuggerUrl from an
|
|
51
|
-
* attached target but still lists it), means the session is live and
|
|
52
|
-
* reattach-across-commands must keep working.
|
|
53
|
-
*
|
|
54
|
-
* Shutdown goes through closeBrowser() on the browser-level websocket — the
|
|
55
|
-
* only connection left once every target is gone — so a recycled PID can
|
|
56
|
-
* never be signaled by mistake: graceful `Browser.close` reaches whatever is
|
|
57
|
-
* actually listening on that port, at any age, and the persisted PID is only
|
|
58
|
-
* ever used for closeBrowser's own age-bounded force-kill.
|
|
59
|
-
*
|
|
60
|
-
* Never throws: a failed reap must not stop a launch. Returns the reaped
|
|
61
|
-
* port, or null when there was nothing to reap. Sessions are per-port
|
|
62
|
-
* (#318), so every recorded one is considered, not just the newest.
|
|
63
|
-
*/
|
|
64
|
-
export declare function reapIdleLaunchedBrowser(): Promise<number | null>;
|
|
65
|
-
export declare function openUrl(client: CdpClient, sessionId: string, url: string): Promise<void>;
|
|
66
|
-
export declare function waitForLoad(client: CdpClient, sessionId: string, condition?: 'load' | 'networkidle' | 'domcontentloaded', timeout?: number): Promise<void>;
|
|
67
|
-
export declare function getCurrentUrl(client: CdpClient, sessionId: string): Promise<string>;
|
|
68
|
-
export declare function getCurrentTitle(client: CdpClient, sessionId: string): Promise<string>;
|
|
1
|
+
export { isPortOpen } from './browser-discovery.js';
|
|
2
|
+
export { launchBrowser } from './browser-launch.js';
|
|
3
|
+
export { closeBrowser, reapIdleLaunchedBrowser } from './browser-lifecycle.js';
|
|
4
|
+
export { connectToTarget, enableSessionDomains, getCurrentTitle, getCurrentUrl, openUrl, waitForLoad, } from './browser-page.js';
|
|
5
|
+
export { getLaunchedPid, getLaunchedUserDataDir, ownsLaunchedUserDataDir, } from './browser-state.js';
|
|
69
6
|
//# sourceMappingURL=browser.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/browser/browser.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../../src/browser/browser.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,aAAa,EACb,OAAO,EACP,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,cAAc,EACd,sBAAsB,EACtB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC"}
|