Where: extractors/common.mjs:82-106 (cdp() spawns node bin/cdp.mjs per command), bin/cdp.mjs daemon client (~856-975)
The per-tab daemon architecture correctly reuses one CDP WebSocket per tab, but every command still pays a full Node CLI startup (~80-150ms on Windows). An all-engine search issues dozens of cdp(...) calls (list/nav/eval per engine, plus per-tick polls that remain node-side), so this is seconds of pure spawn overhead per search.
Fix: extract the daemon client (socket connect + sendCommand) into an importable module (e.g. src/search/cdp-client.mjs); have search.mjs, extractors, and fetch paths send commands over the existing named-pipe socket in-process, keeping bin/cdp.mjs as a thin CLI wrapper for manual use. Related micro-item: captureMainContext's unconditional 100ms settle sleep (bin/cdp.mjs ~362) could resolve early on the root frame's Runtime.executionContextCreated event once the client is in-process.
Where:
extractors/common.mjs:82-106(cdp()spawnsnode bin/cdp.mjsper command),bin/cdp.mjsdaemon client (~856-975)The per-tab daemon architecture correctly reuses one CDP WebSocket per tab, but every command still pays a full Node CLI startup (~80-150ms on Windows). An all-engine search issues dozens of
cdp(...)calls (list/nav/eval per engine, plus per-tick polls that remain node-side), so this is seconds of pure spawn overhead per search.Fix: extract the daemon client (socket connect +
sendCommand) into an importable module (e.g.src/search/cdp-client.mjs); havesearch.mjs, extractors, and fetch paths send commands over the existing named-pipe socket in-process, keepingbin/cdp.mjsas a thin CLI wrapper for manual use. Related micro-item:captureMainContext's unconditional 100ms settle sleep (bin/cdp.mjs ~362) could resolve early on the root frame'sRuntime.executionContextCreatedevent once the client is in-process.