first commit
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# `payload-codec`
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
|
||||
## Purpose
|
||||
|
||||
Encodes and decodes the wire envelope used to ship a single event from the main-process `EventBus` across a `MessagePort` to a worker-tier subscriber. Includes a depth-bounded structured-clone safety probe so payloads that cannot survive the transfer are rejected at the source rather than producing opaque `DataCloneError` exceptions at runtime.
|
||||
|
||||
The codec is intentionally conservative: anything beyond plain JSON-shaped data plus a small list of well-known cloneable host types (`Date`, `RegExp`, `Map`, `Set`, `ArrayBuffer`, typed arrays) is rejected with `EVENTS_PROXY.UNSERIALISABLE_PAYLOAD`. This matches the structured-clone algorithm but is checked synchronously and surfaces the offending path.
|
||||
|
||||
## Features
|
||||
|
||||
- `EventEnvelopeSchema` Zod schema for the wire frame (`{ topic, payload, traceId? }`).
|
||||
- `encodeEnvelope(topic, payload, traceId?)` runs the probe, then constructs the envelope. Throws an `EventsProxyError` carrying the offending path on rejection.
|
||||
- `decodeEnvelope(raw)` Zod-validates an inbound frame.
|
||||
- `probeStructuredClone(value)` returns the offending path or `null`. Detects functions, symbols, `WeakMap`/`WeakSet`, class instances, cycles, and over-deep graphs (limit `MAX_PROBE_DEPTH = 64`).
|
||||
- `DEFAULT_BACKPRESSURE_HIGH_WATER = 1024` — the default per-subscription queue cap.
|
||||
|
||||
## Usage: Basic
|
||||
|
||||
```ts
|
||||
import {
|
||||
encodeEnvelope,
|
||||
decodeEnvelope,
|
||||
} from "@suaveplan/api-events-worker-proxy/payload-codec";
|
||||
|
||||
const wire = encodeEnvelope("billing.invoice.created", { invoiceId: "i_1" });
|
||||
const decoded = decodeEnvelope(wire);
|
||||
console.log(decoded.topic, decoded.payload);
|
||||
```
|
||||
|
||||
## Usage: Advanced
|
||||
|
||||
```ts
|
||||
import {
|
||||
probeStructuredClone,
|
||||
encodeEnvelope,
|
||||
} from "@suaveplan/api-events-worker-proxy/payload-codec";
|
||||
import {
|
||||
EventsProxyError,
|
||||
EVENTS_PROXY_CODES,
|
||||
} from "@suaveplan/api-events-worker-proxy/error-codes";
|
||||
|
||||
const payload = {
|
||||
invoiceId: "i_1",
|
||||
whenIssued: new Date(),
|
||||
handler: () => undefined,
|
||||
};
|
||||
const offender = probeStructuredClone(payload);
|
||||
if (offender !== null) {
|
||||
throw new EventsProxyError(
|
||||
EVENTS_PROXY_CODES.UNSERIALISABLE_PAYLOAD,
|
||||
`payload rejected at ${offender}`
|
||||
);
|
||||
}
|
||||
const wire = encodeEnvelope("invoice.flagged", payload);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `EventEnvelopeSchema` — `z.object({ topic: z.string().min(1), traceId: z.string().optional(), payload: z.unknown() })`.
|
||||
- `WireEventEnvelope` — `z.infer<typeof EventEnvelopeSchema>`.
|
||||
- `encodeEnvelope(topic: string, payload: unknown, traceId?: string): WireEventEnvelope`.
|
||||
- `decodeEnvelope(raw: unknown): WireEventEnvelope`.
|
||||
- `probeStructuredClone(value: unknown): string | null` — returns the offending JSON-path-like string or `null`.
|
||||
- `MAX_PROBE_DEPTH: number` — `64`.
|
||||
- `DEFAULT_BACKPRESSURE_HIGH_WATER: number` — `1024`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The probe walks the object graph synchronously with a `WeakSet` cycle detector, accumulating the offending JSON path. It deliberately rejects class instances (`Object.getPrototypeOf(value) !== Object.prototype`) because the receiving side reconstructs them as plain objects, breaking `instanceof`. `Date`, `RegExp`, `Map`, `Set`, `ArrayBuffer`, and `ArrayBuffer.isView`-typed values are recognised explicitly because they ARE structured-clone safe even though they have non-`Object` prototypes. The depth bound of 64 is generous for legitimate payloads but cuts off pathological inputs. Cycles are detected by `WeakSet`; the probe reports the path that re-entered the visited set rather than recursing infinitely.
|
||||
Reference in New Issue
Block a user