import type { z, ZodType } from "zod"; /** * Workspace alias for "any zod schema". Zod 4 dropped the `ZodTypeAny` * export; this alias keeps the rest of the codebase reading naturally * without a hundred `ZodType` sprinkled inline. * * `ZodType` preserves bivariance so concrete schemas (e.g. * `ZodObject<{...}>`) assign to `ZodTypeAny` without complaint — the same * behaviour zod 3 gave us out of the box. */ export type ZodTypeAny = ZodType; export interface CommandDefinition { readonly name: string; readonly schema: TSchema; readonly description?: string; } /** * Event visibility — controls whether the event is part of the public, * cross-bounded-context API of its module. * * - `'public'` (default) — exported as part of the module's public surface; * other modules MAY listen to it via `when(SomeEvent, ...)`. The * `@nwire/messages` workspace package re-exports public events for * external consumers (frontends, BFFs, sync services). * - `'internal'` — the event is module-internal. Other modules MUST NOT * subscribe; the framework can warn or refuse cross-module listeners. * Use for transient implementation-detail events. */ export type EventVisibility = "public" | "internal"; /** * Domain outcome an event represents. Lets Studio score success rates and * surface warnings/failures without parsing event names. Default `'success'`. * - `'success'` — a positive thing happened (default) * - `'failure'` — something failed in a domain-meaningful way (not a system error) * - `'milestone'` — significant progress event (lesson completed, onboarding finished) * - `'warning'` — anomaly that's not yet a failure (low confidence, manual review needed) */ export type EventOutcome = "success" | "failure" | "milestone" | "warning"; /** * Event scope — the structural distinction between domain and integration. * * - `'domain'` — internal to a bounded context. Folded by actors, projections, * reactions inside the BC. Never bus-eligible. Refactor freely. * - `'integration'` — public contract on the bus. Versioned, audience-tagged, * slim. Other BCs and services subscribe to these, not to domain events. * * `scope` is structural and complements `visibility`: * - `scope: "domain"` → implies `visibility: "internal"` unless overridden * - `scope: "integration"` → implies `visibility: "public"` * * If `scope` is omitted, the framework infers it from `visibility`. */ export type EventScope = "domain" | "integration"; export interface EventDefinitionMeta { readonly name: TName; readonly schema: TSchema; readonly description?: string; /** Defaults to `'public'`. */ readonly visibility?: EventVisibility; /** Domain outcome semantics. Defaults to `'success'`. */ readonly outcome?: EventOutcome; /** * Relative business weight (e.g. `100` for money-flow events, `1` for * telemetry chatter). Studio uses this to weight dashboards so a single * payment-failed event doesn't get drowned out by a thousand pings. */ readonly businessWeight?: number; /** * Free-form audience tags — who cares about this event outside engineering? * `['product']`, `['ops', 'support']`, `['finance']`. */ readonly audience?: readonly string[]; /** * Domain vs integration scope. Domain events are BC-internal; integration * events are versioned public contracts on the bus. If omitted, inferred * from `visibility` (public → integration, internal → domain). */ readonly scope?: EventScope; /** * Integer version of an integration event's schema (`v1`, `v2`, ...). * Required for `scope: "integration"`. Ignored for domain events. */ readonly version?: number; } /** Shape returned when an event definition is called as a factory. */ export interface EventMessageOf { readonly eventName: string; readonly payload: z.output; } /** * `defineEvent` returns a callable factory that *is* the event definition. * Call it to mint a typed `EventMessage` (validates against the zod schema), * or read its meta fields (`name`, `schema`, `outcome`, …) directly. * * const AnswerWasSubmitted = defineEvent({ name: "...", schema: ... }) * AnswerWasSubmitted.name // "submissions.answer-was-submitted" * AnswerWasSubmitted({ ... }) // { eventName, payload } * * Domain→integration translation lives in `defineWorkflow`. Subscribe with * `on(SourceEvent, async (e) => { await publish(TargetEvent({ ... })); })`. */ export type EventDefinition = EventDefinitionMeta & ((payload: z.output) => EventMessageOf) & { /** * Kind discriminator — `defineEvent` always sets it, so the app registry * can route events by kind. Optional in the type so hand-shaped * event-likes (tests, adapters) stay assignable. */ readonly $kind?: "event"; /** Module-manifest visibility marker. See `defineWorkflow.public()` notes. */ readonly $public?: true; /** Return a callable event clone with `$public: true`. */ public: () => EventDefinition & { readonly $public: true; }; }; export declare function defineCommand(def: CommandDefinition): CommandDefinition; export declare function defineEvent(def: EventDefinitionMeta): EventDefinition; export type CommandPayload = C extends CommandDefinition ? z.output : never; export type EventPayload = E extends EventDefinition ? z.output : never;