diff --git a/packages/core/src/lib/message/command.ts b/packages/core/src/lib/message/command.ts index e008abd..3f2015f 100644 --- a/packages/core/src/lib/message/command.ts +++ b/packages/core/src/lib/message/command.ts @@ -110,42 +110,44 @@ export const commandSchema: CommandSchemaType = z.object({ /** * Input for creating a command. + * + * When a specific command type is provided via the generic parameter, + * the input is validated against that type. This means fields like + * `type` and `data` must match the narrower types of `TCommand`. */ -export type CreateCommandInput = Partial> & { - type: string; - source: string; - data: unknown; -}; +export type CreateCommandInput = + & Partial< + Pick< + TCommand, + | 'id' + | 'correlationid' + | 'time' + | 'subject' + | 'datacontenttype' + | 'dataschema' + > + > + & Pick; /** * Creates a command based on input data with the convenience * to skip properties and use the defaults for the rest. */ export const createCommand = ( - { - id, - correlationid, - time, - source, - type, - subject, - data, - datacontenttype, - dataschema, - }: CreateCommandInput, + input: CreateCommandInput, ): TCommand => { const command = { - specversion: '1.0', - id: id ?? ulid(), - correlationid: correlationid ?? ulid(), - time: time ?? new Date().toISOString(), - source, - type, - ...(subject && { subject }), - data, - datacontenttype: datacontenttype ?? 'application/json', - ...(dataschema && { dataschema }), - } as TCommand; + specversion: '1.0' as const, + id: input.id ?? ulid(), + correlationid: input.correlationid ?? ulid(), + time: input.time ?? new Date().toISOString(), + source: input.source, + type: input.type, + ...(input.subject && { subject: input.subject }), + data: input.data, + datacontenttype: input.datacontenttype ?? 'application/json', + ...(input.dataschema && { dataschema: input.dataschema }), + }; - return command; + return command as TCommand; }; diff --git a/packages/core/src/lib/message/event.ts b/packages/core/src/lib/message/event.ts index ce8464e..6f97c89 100644 --- a/packages/core/src/lib/message/event.ts +++ b/packages/core/src/lib/message/event.ts @@ -87,7 +87,7 @@ export type EventSchemaType = z.ZodObject<{ /** * The Zod schema matching the Event type. * - * Zod is the default for validating incomming messages. + * Zod is the default for validating incoming messages. * * We do not infer the Event type from this schema because of * slow type issues see https://jsr.io/docs/about-slow-types for more details. @@ -113,43 +113,39 @@ export const eventSchema: EventSchemaType = z.object({ /** * Input for creating an event. + * + * When a specific event type is provided via the generic parameter, + * the input is validated against that type. This means fields like + * `type` and `data` must match the narrower types of `TEvent`. */ -export type CreateEventInput = Partial> & { - type: string; - source: string; - subject: string; - data: unknown; -}; +export type CreateEventInput = + & Partial< + Pick< + TEvent, + 'id' | 'correlationid' | 'time' | 'datacontenttype' | 'dataschema' + > + > + & Pick; /** * Creates an event based on input data with the convenience * to skip properties and use the defaults for the rest. */ export const createEvent = ( - { - id, - correlationid, - time, - source, - type, - subject, - data, - datacontenttype, - dataschema, - }: CreateEventInput, + input: CreateEventInput, ): TEvent => { const event = { - specversion: '1.0', - id: id ?? ulid(), - correlationid: correlationid ?? ulid(), - time: time ?? new Date().toISOString(), - source, - type, - subject, - data, - datacontenttype: datacontenttype ?? 'application/json', - ...(dataschema && { dataschema }), - } as TEvent; + specversion: '1.0' as const, + id: input.id ?? ulid(), + correlationid: input.correlationid ?? ulid(), + time: input.time ?? new Date().toISOString(), + source: input.source, + type: input.type, + subject: input.subject, + data: input.data, + datacontenttype: input.datacontenttype ?? 'application/json', + ...(input.dataschema && { dataschema: input.dataschema }), + }; - return event; + return event as TEvent; }; diff --git a/packages/core/src/lib/message/query.ts b/packages/core/src/lib/message/query.ts index c5ac52d..8b45431 100644 --- a/packages/core/src/lib/message/query.ts +++ b/packages/core/src/lib/message/query.ts @@ -104,40 +104,38 @@ export const querySchema: QuerySchemaType = z.object({ /** * Input for creating a query. + * + * When a specific query type is provided via the generic parameter, + * the input is validated against that type. This means fields like + * `type` and `data` must match the narrower types of `TQuery`. */ -export type CreateQueryInput = Partial> & { - type: string; - source: string; - data: unknown; -}; +export type CreateQueryInput = + & Partial< + Pick< + TQuery, + 'id' | 'correlationid' | 'time' | 'datacontenttype' | 'dataschema' + > + > + & Pick; /** * Creates a query based on input data with the convenience * to skip properties and use the defaults for the rest. */ export const createQuery = ( - { - id, - correlationid, - time, - source, - type, - data, - datacontenttype, - dataschema, - }: CreateQueryInput, + input: CreateQueryInput, ): TQuery => { const query = { - specversion: '1.0', - id: id ?? ulid(), - correlationid: correlationid ?? ulid(), - time: time ?? new Date().toISOString(), - source, - type, - data, - datacontenttype: datacontenttype ?? 'application/json', - ...(dataschema && { dataschema }), - } as TQuery; + specversion: '1.0' as const, + id: input.id ?? ulid(), + correlationid: input.correlationid ?? ulid(), + time: input.time ?? new Date().toISOString(), + source: input.source, + type: input.type, + data: input.data, + datacontenttype: input.datacontenttype ?? 'application/json', + ...(input.dataschema && { dataschema: input.dataschema }), + }; - return query; + return query as TQuery; };