(home: string)
| 264 | }; |
| 265 | |
| 266 | const launchPackaged = async (home: string): Promise<PackagedApp> => { |
| 267 | let output = ""; |
| 268 | let settled = false; |
| 269 | const child = spawn(appExe as string, ["--remote-debugging-port=0"], { |
| 270 | env: { ...process.env, HOME: home }, |
| 271 | stdio: ["ignore", "pipe", "pipe"], |
| 272 | }); |
| 273 | |
| 274 | const browserCdpUrl = await new Promise<string>((resolve, reject) => { |
| 275 | const timer = setTimeout(() => { |
| 276 | // oxlint-disable-next-line executor/no-promise-reject, executor/no-error-constructor -- boundary: packaged-app launch promise adapter |
| 277 | reject(new Error(`Timed out waiting for packaged app CDP URL\n${output}`)); |
| 278 | }, 120_000); |
| 279 | const settle = (fn: () => void) => { |
| 280 | if (settled) return; |
| 281 | settled = true; |
| 282 | clearTimeout(timer); |
| 283 | fn(); |
| 284 | }; |
| 285 | const collectOutput = (chunk: Buffer) => { |
| 286 | const text = chunk.toString(); |
| 287 | output = (output + text).slice(-16_384); |
| 288 | const match = output.match(/DevTools listening on (ws:\/\/[^\s]+)/); |
| 289 | if (match) settle(() => resolve(match[1]!)); |
| 290 | }; |
| 291 | child.stdout?.on("data", collectOutput); |
| 292 | child.stderr?.on("data", collectOutput); |
| 293 | // oxlint-disable-next-line executor/no-promise-reject -- boundary: packaged-app launch promise adapter |
| 294 | child.once("error", (error) => settle(() => reject(error))); |
| 295 | child.once("exit", (code, signal) => |
| 296 | settle(() => |
| 297 | // oxlint-disable-next-line executor/no-promise-reject, executor/no-error-constructor -- boundary: packaged-app launch promise adapter |
| 298 | reject( |
| 299 | new Error(`Packaged app exited before CDP (code=${code} signal=${signal})\n${output}`), |
| 300 | ), |
| 301 | ), |
| 302 | ); |
| 303 | }); |
| 304 | |
| 305 | const debugPort = new URL(browserCdpUrl).port; |
| 306 | const pageCdpUrl = await waitForPageWebSocket(debugPort); |
| 307 | const cdp = await CdpPage.connect(pageCdpUrl); |
| 308 | await cdp.command("Runtime.enable"); |
| 309 | await cdp.command("Page.enable"); |
| 310 | return { child, cdp, debugPort }; |
| 311 | }; |
| 312 | |
| 313 | const stopProcess = async (child: ChildProcess | undefined): Promise<void> => { |
| 314 | if (!child || child.exitCode !== null || child.signalCode !== null) return; |
no test coverage detected