/** * @module event-builder * @category Internal * * Everything `act().build()` derives from the registered events. * * This is the build-time half of the event lifecycle. `internal/sensitive.ts` * owns the other two halves — the `sensitive(...)` marker itself, and the * runtime transforms (`pii_gate`, `pii_strip`, `pii_split`) that a reader is * composed from. Nothing here runs per event at runtime; everything here runs * once, at build, and hands the orchestrator a prebuilt function. * * Three things are derived, all in ONE walk over the registered events: * * - **validation** — a reaction may not target a lane nobody declared, nor a * target a projection already serves. Both checks skip dynamic resolvers, * because a `.to(fn)` target is unknowable until an event arrives. * - **resolution** — each event's schema is read once into {@link EventTags}: * which fields are sensitive, and how to revive the dates in a stored * payload. Working out where the dates are is a Zod concern, so it lives in * `internal/date-reviver.ts`; this module composes what that returns. * - **composition** — the per-surface readers, each a single {@link EventGate} * that types the payload and applies disclosure in one call. * * Deliberately NOT here: deprecation. `Foo` is deprecated only because * `Foo_v2` exists beside it, so it is derived from event *names across a whole * state* rather than from one event's schema — and it governs *emitting* (a * static `.emit()` throws at build), not reading. Replay of a deprecated event * stays silent by design. * * @internal */ import { z } from "zod"; import { type EventGate } from "../internal/sensitive.js"; import type { Actor, LaneConfig, Registry } from "../types/index.js"; /** What one pass over an event's schema resolves. @internal */ export type EventTags = { /** Keys marked `sensitive(...)`, top level (and across union variants). */ readonly sensitive: readonly string[]; /** * Revives the dates in a stored `data` payload, or `undefined` when the * schema declares none. */ readonly date_reviver: ((data: unknown) => unknown) | undefined; /** * Revives the dates in a stored `pii` sidecar, or `undefined` when no * sensitive field is a date. */ readonly pii_date_reviver: ((pii: unknown) => unknown) | undefined; }; /** * Resolve an event's schema in a single pass. * * Sensitive markers are read at the top level only — the documented carve-out * (`sensitive.ts`), since the write path splits whole top-level keys. A union * event has no top-level shape, so its variants are walked and unioned: a key * sensitive in any variant must be split, because the stored payload could be * that variant ([#1417](https://github.com/Rotorsoft/act-root/issues/1417)). * * @internal */ export declare function event_tags(schema: z.ZodType): EventTags; /** * What the reader should do about sensitive fields. * * - `redact` — substitute `[REDACTED]` unless `disclose` authorizes the actor, * and drop the `pii` sidecar. The read surfaces (`query`, `query_array`, * `load`). * - `strip` — remove the keys entirely. Reaction and projection handlers, * which never see PII by framework rule, and shouldn't structurally observe * the keys either. * * @internal */ export type Disclosure = "redact" | "strip"; /** * Compose one event's typing and disclosure into a single gate. * * Returns `undefined` when the event needs neither, so the caller can fall * back to the shared {@link IDENTITY_GATE} and the common path allocates * nothing. * * @internal */ export declare function make_event_reader(tags: EventTags, disclosure: Disclosure, predicate?: ((event: never, actor: Actor) => boolean) | null): EventGate | undefined; /** * Exactly what the registry serves, keyed by event name — no intermediates. * * The per-state `view` is installed directly on each state rather than * returned: it is event-derived wiring like the rest, and handing it back for * the caller to install would put the composition back where it came from. * * @internal */ export type BuiltEvents = { /** Backs `registry.sensitive_fields`. */ readonly sensitive: Map; /** Backs `registry.query_gate` — the actor-less read surfaces. */ readonly query_readers: Map; /** Readers for handlers — sensitive keys removed, payload typed. */ readonly handler_readers: Map; }; /** What validation needs to know about the projections already registered. */ export type TargetOwners = { readonly batch_handlers: ReadonlyMap; readonly fold_targets: ReadonlySet; /** A projection's own reactions legitimately target it — excluded by identity. */ readonly projection_reactions: ReadonlySet; }; /** * Validate every static reaction and wire every event, in a single walk. * * Ownership conflicts throw as they are found; lane violations are collected * and thrown afterwards. That ordering is deliberate — it preserves the * precedence the two separate passes had, where the ownership guard ran to * completion before lane references were checked, so a config violating both * reports the same error it always did. * * @internal */ export declare function build_events(registry: Registry, states: ReadonlyMap, lanes: ReadonlyArray, owners: TargetOwners): BuiltEvents; //# sourceMappingURL=event-builder.d.ts.map