Skip to content

Hosting

An Engine and its JsValue instances are thread-affine. A transport thread carries protocol text, while commands that inspect or execute JavaScript run on the engine thread. Choose that thread with EngineTargetOptions.ThreadMode.

HostOwned

HostOwned is the default. Use it when your application already owns the engine loop:

csharp
var engine = new Engine(options => options.UseDevTools());
await using var target = new EngineTarget(engine);

while (!stopping)
{
    target.Pump(); // must run on the engine's owning thread
    engine.Tasks.WaitForScheduledWork(TimeSpan.FromMilliseconds(50));
}

Keep pumping after the initial script finishes. Otherwise inspection, timers, promise reactions, and debugger commands cannot progress.

If a JavaScriptException escapes host-owned scheduled work, call target.ReportUncaughtException(exception) on the engine thread if attached clients should receive it. Reporting does not handle or suppress the exception.

LibraryOwned

LibraryOwned creates one background thread for the target. Submit all host work through the target; do not access or pump the engine from another thread.

csharp
var engine = new Engine(options => options.UseDevTools());
await using var target = new EngineTarget(engine, new EngineTargetOptions
{
    ThreadMode = ThreadMode.LibraryOwned
});

await target.PostAsync(engine =>
{
    engine.Execute("globalThis.answer = 6 * 7", "startup.js");
});

Convert results to CLR values inside a PostAsync<T> callback. Never return a JsValue to the awaiting thread.

Wait for a debugger

WaitForDebuggerOnStart holds host work until a client sends Runtime.runIfWaitingForDebugger:

csharp
await using var target = new EngineTarget(engine, new EngineTargetOptions
{
    WaitForDebuggerOnStart = true
});

server.AddTarget(target);
server.Start();

if (!target.WaitForDebugger(TimeSpan.FromMinutes(2)))
{
    throw new TimeoutException("No debugger attached.");
}

A host-owned target must call WaitForDebugger; it pumps while waiting so the release command can run. A library-owned target pumps itself.

Lifetimes and timeouts

The host owns the Engine lifetime. Disposing an EngineTarget stops its library-owned thread and protocol access but does not dispose its engine.

  • CommandTimeout bounds the client's wait, not queued execution. A timed-out command can still run later.
  • PauseTimeout resumes a debugger pause whose client stopped responding.
  • MaxMessageBytes limits accepted WebSocket message size.