@@ -78,6 +78,30 @@ export interface EventClientOptions {
7878 onResponse ?: ( event : EventResponseEvent ) => void ;
7979}
8080
81+ // ── Schema types ──────────────────────────────────────────────────────────────
82+
83+ /** A value parser. Compatible with Zod schemas and any object with `.parse`. */
84+ export type Schema < T > = { parse : ( value : unknown ) => T } ;
85+
86+ /** Maps routing key patterns (including globs) to their payload schemas. */
87+ export type EventSchemaMap = Record < string , Schema < unknown > > ;
88+
89+ type GlobMatch < K extends string , P extends string > = P extends
90+ `${infer Pre } *${infer Suf } ` ? K extends `${Pre } ${string } ${Suf } ` ? true : false
91+ : K extends P ? true
92+ : false ;
93+
94+ type MatchedPattern < K extends string , Map extends EventSchemaMap > = {
95+ [ P in keyof Map & string ] : GlobMatch < K , P > extends true ? P : never ;
96+ } [ keyof Map & string ] ;
97+
98+ export type EventPayloadType < K extends string , Map extends EventSchemaMap > =
99+ [ MatchedPattern < K , Map > ] extends [ never ] ? JsonValue
100+ : Map [ MatchedPattern < K , Map > & keyof Map ] extends Schema < infer T > ? T
101+ : JsonValue ;
102+
103+ // ── Client interfaces ─────────────────────────────────────────────────────────
104+
81105export interface EventClient {
82106 /**
83107 * Publish a message to a routing key. All subscriptions whose pattern
@@ -110,6 +134,21 @@ export interface EventClient {
110134 close ( ) : Promise < void > ;
111135}
112136
137+ /**
138+ * An event client with schema-aware payload types. Returned by `createEventClient`
139+ * when a `schema` map is provided. Routing keys matching a schema pattern get typed
140+ * payloads; unmatched keys fall back to `JsonValue`.
141+ */
142+ export interface EventSchemaClient < Map extends EventSchemaMap >
143+ extends Omit < EventClient , "publish" >
144+ {
145+ publish < K extends string > (
146+ routingKey : K ,
147+ payload : EventPayloadType < K , Map > ,
148+ opts ?: PublishOptions ,
149+ ) : EventResult < PublishResult > ;
150+ }
151+
113152// ── Internal helpers ──────────────────────────────────────────────────────────
114153
115154function toEventError ( error : unknown , response : Response ) : EventError {
@@ -184,22 +223,29 @@ function buildFetch(
184223
185224// ── Factory ───────────────────────────────────────────────────────────────────
186225
226+ /** Creates a schema-aware event client. Publish payload types are inferred from the schema map. */
227+ export function createEventClient < Map extends EventSchemaMap > (
228+ opts : EventClientOptions & { schema : Map } ,
229+ ) : EventSchemaClient < Map > ;
187230/** Creates an event client backed by the beyond-queue HTTP API. */
188- export function createEventClient ( opts : EventClientOptions = { } ) : EventClient {
189- const url = opts . url ?? env [ "BEYOND_EVENTS_URL" ] ;
231+ export function createEventClient ( opts ?: EventClientOptions ) : EventClient ;
232+ export function createEventClient (
233+ opts ?: EventClientOptions & { schema ?: EventSchemaMap } ,
234+ ) : EventClient {
235+ const url = opts ?. url ?? env [ "BEYOND_EVENTS_URL" ] ;
190236 if ( ! url ) {
191237 throw new Error (
192238 "BEYOND_EVENTS_URL is required (pass `url` or set the BEYOND_EVENTS_URL env var)" ,
193239 ) ;
194240 }
195241 const base = url . replace ( / \/ + $ / , "" ) ;
196- const token = opts . token ?? env [ "BEYOND_EVENTS_TOKEN" ] ;
197- const { onRequest, onResponse } = opts ;
242+ const token = opts ? .token ?? env [ "BEYOND_EVENTS_TOKEN" ] ;
243+ const { onRequest, onResponse } = opts ?? { } ;
198244
199245 const client = createFetchClient < paths > ( {
200246 baseUrl : base ,
201247 headers : { Authorization : `Bearer ${ token ?? "anon" } ` } ,
202- fetch : buildFetch ( opts . fetch , opts . retries ?? 2 , opts . timeout ) ,
248+ fetch : buildFetch ( opts ? .fetch , opts ? .retries ?? 2 , opts ? .timeout ) ,
203249 } ) ;
204250
205251 function cmd < A extends unknown [ ] , R > (
0 commit comments