From 8869199c3e9b647fbf3bd509ae83e5740bce3211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Go=CC=88rdes?= Date: Mon, 9 Feb 2026 21:03:17 +0100 Subject: [PATCH 1/3] feat(core): enhance command, event, and query input types for better type safety and validation --- packages/core/src/lib/message/command.ts | 53 +++++++++++------------- packages/core/src/lib/message/event.ts | 51 ++++++++++------------- packages/core/src/lib/message/query.ts | 47 ++++++++++----------- 3 files changed, 68 insertions(+), 83 deletions(-) diff --git a/packages/core/src/lib/message/command.ts b/packages/core/src/lib/message/command.ts index e008abd..896ae3f 100644 --- a/packages/core/src/lib/message/command.ts +++ b/packages/core/src/lib/message/command.ts @@ -110,42 +110,39 @@ 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..856e857 100644 --- a/packages/core/src/lib/message/event.ts +++ b/packages/core/src/lib/message/event.ts @@ -113,43 +113,36 @@ 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 + > + & 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..7895fb8 100644 --- a/packages/core/src/lib/message/query.ts +++ b/packages/core/src/lib/message/query.ts @@ -104,40 +104,35 @@ 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 + > + & 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; }; From 46ba1c5b6f19e7042105cfa73e2b54c000262020 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Go=CC=88rdes?= Date: Mon, 9 Feb 2026 21:04:34 +0100 Subject: [PATCH 2/3] fix(core): correct typo in event schema documentation --- packages/core/src/lib/message/event.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/core/src/lib/message/event.ts b/packages/core/src/lib/message/event.ts index 856e857..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. @@ -120,7 +120,10 @@ export const eventSchema: EventSchemaType = z.object({ */ export type CreateEventInput = & Partial< - Pick + Pick< + TEvent, + 'id' | 'correlationid' | 'time' | 'datacontenttype' | 'dataschema' + > > & Pick; From d94706831b97bcac97fb67251b2715f8d56e233e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Go=CC=88rdes?= Date: Mon, 9 Feb 2026 21:10:40 +0100 Subject: [PATCH 3/3] fixes formatting --- packages/core/src/lib/message/command.ts | 7 ++++++- packages/core/src/lib/message/query.ts | 5 ++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/core/src/lib/message/command.ts b/packages/core/src/lib/message/command.ts index 896ae3f..3f2015f 100644 --- a/packages/core/src/lib/message/command.ts +++ b/packages/core/src/lib/message/command.ts @@ -119,7 +119,12 @@ export type CreateCommandInput = & Partial< Pick< TCommand, - 'id' | 'correlationid' | 'time' | 'subject' | 'datacontenttype' | 'dataschema' + | 'id' + | 'correlationid' + | 'time' + | 'subject' + | 'datacontenttype' + | 'dataschema' > > & Pick; diff --git a/packages/core/src/lib/message/query.ts b/packages/core/src/lib/message/query.ts index 7895fb8..8b45431 100644 --- a/packages/core/src/lib/message/query.ts +++ b/packages/core/src/lib/message/query.ts @@ -111,7 +111,10 @@ export const querySchema: QuerySchemaType = z.object({ */ export type CreateQueryInput = & Partial< - Pick + Pick< + TQuery, + 'id' | 'correlationid' | 'time' | 'datacontenttype' | 'dataschema' + > > & Pick;