Files
2026-05-27 23:29:53 +01:00

3.0 KiB

control-channel

(no submodules)

Purpose

Bidirectional control plane: the main process tells a worker-isolated plugin to stop or resume emitting log records. The host calls disable during plugin shutdown so a worker that is still draining its work queue does not keep producing records after the main-side sink has been torn down (design doc §5.5 — components dispose in reverse dependency order). Without this channel the main-side sink would either log to a zombie sink (silent loss with no diagnostic) or panic on a closed port.

Frames are simple: { kind: "control", action: "disable" | "enable" }. There is no acknowledgement frame; the channel is fire-and-forget. The worker maintains a shared ControlState object the proxy client checks synchronously on every log call, so a disable received between two calls correctly suppresses the second.

Features

  • createControlChannelSender(port) — returns { disable, enable } thunks usable from the main process.
  • mountControlChannelReceiver(port, options?) — installs a listener on the worker-side port; returns the shared ControlState plus a dispose thunk. Tolerates malformed frames (silent drop) so a hostile or buggy host cannot crash the worker.
  • onChange callback fires only on real transitions, not on repeated same-value frames.

Usage: Basic

// Main side
import { createControlChannelSender } from "@suaveplan/api-logger-worker-proxy/control-channel";
const ctl = createControlChannelSender(mainPort);
ctl.disable();
// ... main-side sink torn down ...
worker.terminate();
// Worker side
import { mountControlChannelReceiver } from "@suaveplan/api-logger-worker-proxy/control-channel";
const ctl = mountControlChannelReceiver(workerPort);
// log.info() etc. read ctl.state.enabled synchronously

Usage: Advanced

Observe transitions to flush in-flight batches and surface a counter:

import { mountControlChannelReceiver } from "@suaveplan/api-logger-worker-proxy/control-channel";

const ctl = mountControlChannelReceiver(port, {
    onChange: (enabled) => {
        if (!enabled) batcher.flush();
        meter.add(`logger_proxy.control_state.${enabled ? "on" : "off"}`, 1);
    },
});

API Reference

  • createControlChannelSender(port: PortLike): ControlChannelSender — emits { v, kind: "control", action } frames.
  • mountControlChannelReceiver(port, options?) — returns { state: Readonly<ControlState>, dispose }.
  • ControlState — { enabled: boolean }. Defaults to true so workers emit normally until told otherwise.

Implementation Notes

The receiver shares the port with goodbye and (incidentally) with the worker-side log emit path — frames whose kind is not "control" are ignored so the receiver composes cleanly with other listeners on the same port. The state object is mutated in place (not replaced) so the proxy client can capture a stable reference at construction time and not need to re-resolve a getter on the per-call hot path.