first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user