# `server` (main-side) _(no submodules)_ ## Purpose The main-side server. Consumes inbound `log-batch` frames from a worker-isolated plugin, applies redaction at the boundary, narrows the wire-format levels (`trace` / `fatal`) down to the `LoggerLike` levels (`debug` / `error`), and forwards each record to the host's main-side `@suaveplan/logger` instance. Plugin pre-tagging is honoured by routing through `logger.child(plugin)` when available, so every record reaches the sink with structured `plugin` / `plugin_version` fields without the worker having to know about the host logger's child-binding semantics. The server is best-effort, just like the worker-side client. Malformed frames increment a counter, fire an `onInvalidFrame` hook, and surface via `onError` — but they NEVER throw out of the listener. A single bad record cannot bring down the port. ## Features - Wire-frame validation via Zod (`parseLoggerProxyFrame`). - Level narrowing (`trace → debug`, `fatal → error`) so downstream sinks need not understand the broader level set. - Optional redaction passthrough at the boundary. - Child-logger caching per `PluginTag` (Map keyed by `name@version` so caching survives structured-clone of the tag object across the port boundary). - Hard `maxBatchSize` cap (default 10_000 records/batch) that protects the main logger from a hostile or buggy worker dumping unbounded inbound traffic. - Observation hooks: `onRecord` (post-redaction), `onError`, and `onInvalidFrame`. - `forwardedCount`, `invalidFrameCount`, `overflowDropCount` counters. ## Usage: Basic ```ts import { createLoggerProxyServer } from "@suaveplan/api-logger-worker-proxy/main-side"; import { createLogger } from "@suaveplan/logger"; const mainLogger = createLogger({ level: "info" }); const server = createLoggerProxyServer(mainPort, mainLogger); // ... worker emits log records over workerPort ... server.close(); ``` ## Usage: Advanced Pipe through host redaction and observe every record: ```ts import { createLoggerProxyServer } from "@suaveplan/api-logger-worker-proxy/main-side"; import { maskKeys } from "@suaveplan/api-logger-worker-proxy/redaction-passthrough"; const server = createLoggerProxyServer(mainPort, mainLogger, { redaction: { ctxRedactor: maskKeys(["password", "token", "secret"]), }, onRecord: (rec, plug) => audit.write(rec, plug), onError: (err) => mainLogger.warn("logger-proxy error", { code: err.code }), onInvalidFrame: (raw, cause) => mainLogger.warn("logger-proxy invalid frame", { raw, cause }), maxBatchSize: 5_000, }); ``` ## API Reference - `createLoggerProxyServer(port, logger, options?): LoggerProxyServer` — primary entry. - `LoggerProxyServer.close()` — detach. Idempotent. - `OnRecord(record, plugin)` — post-redaction, pre-dispatch hook. - `OnInvalidFrame(input, error)` — fires on Zod parse failure. ## Implementation Notes `logger.child` is optional on `LoggerLike`; when absent the server merges `plugin` / `plugin_version` into the call-site `ctx` instead. The child-logger cache is a `Map` keyed by `${name}@${version}` so that workers reusing the same identity (the common case) hit the cache even after structured-clone has produced a fresh object on the receive side of the port. Sink failures (the host logger throws while writing) are caught per-record so a single bad record cannot stall the listener; the error surfaces via `onError`.