first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
# `protocol`
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines the Zod-validated wire format for log frames travelling from a
worker-isolated plugin to the main-process `@suaveplan/logger`. Frames
are a small discriminated union — `log-batch`, `goodbye`, and `control`
— so receivers branch on `kind` and reject malformed input cleanly via
Zod parsing rather than ad-hoc shape checks.
The protocol is bidirectional: workers send `log-batch` and `goodbye`
frames to the main, the main sends `control` frames (`disable` /
`enable`) back to the worker so it can stop emitting after the
main-side logger has been disposed (design doc §5.5 dispose-in-reverse).
## Features
- `LOGGER_PROXY_PROTOCOL_VERSION` — `"1.0.0"`. Embedded in every frame
via the `v` field; servers reject on mismatch.
- `LOG_LEVELS` / `LogLevel` — `trace | debug | info | warn | error |
fatal`. Wider than `LoggerLike` to allow forward-compatibility; the
main-side server narrows down before dispatch.
- `PluginTag` / `pluginTagSchema` — `{ name, version }` attached to
every batch.
- `WireLogRecord` / `logRecordSchema` — single record: `level`, `msg`,
`ts`, optional `ctx` map, optional `trace` (`trace_id` / `span_id`).
- `LogBatchFrame` / `logBatchFrameSchema` — `{ kind: "log-batch", v,
plugin, records: WireLogRecord[] }` with `min(1)` records.
- `ControlFrame` / `controlFrameSchema` — `{ kind: "control", v,
action: "disable" | "enable" }`.
- `parseLoggerProxyFrame(input)` — validates and returns the
discriminated union; throws `ZodError` on malformed input.
## Usage: Basic
```ts
import {
LOGGER_PROXY_PROTOCOL_VERSION,
parseLoggerProxyFrame,
} from "@suaveplan/api-logger-worker-proxy/protocol";
const frame = parseLoggerProxyFrame({
v: LOGGER_PROXY_PROTOCOL_VERSION,
kind: "log-batch",
plugin: { name: "demo", version: "0.1.0" },
records: [
{
level: "info",
msg: "hello",
ts: new Date().toISOString(),
ctx: { userId: 42 },
},
],
});
```
## Usage: Advanced
Switch over `kind` with exhaustive narrowing:
```ts
import { parseLoggerProxyFrame } from "@suaveplan/api-logger-worker-proxy/protocol";
function handle(input: unknown): void {
const frame = parseLoggerProxyFrame(input);
switch (frame.kind) {
case "log-batch":
for (const r of frame.records) dispatch(r, frame.plugin);
break;
case "control":
applyControl(frame.action);
break;
case "goodbye":
shutdown(frame.reason);
break;
}
}
```
## API Reference
- `LOG_LEVELS: readonly LogLevel[]` — exported tuple for enumeration.
- `logLevelSchema: ZodEnum<LogLevel>` — re-use in downstream schemas.
- `logRecordSchema`, `logBatchFrameSchema`, `controlFrameSchema`,
`logGoodbyeFrameSchema`, `loggerProxyFrameSchema` — Zod schemas.
- `parseLoggerProxyFrame(input: unknown): LoggerProxyFrame` — entry
point for receivers. Throws on malformed input.
## Implementation Notes
The schema accepts `ctx` as `Record<string, unknown>` to keep the
proxy transparent to the application. Main-side redaction normalises
sensitive fields before they reach the sink. `ts` is required so the
main-side stays a pure forwarder; the worker stamps wall-clock time at
record creation. Frames flow over `MessagePort` and survive
structured-clone — keeping records as plain data (no `Date`, no
classes) is intentional.