11import z from "zod"
2- import type { ZodObject } from "zod"
32import { Schema , Types } from "effect"
43import { Database , eq } from "@/storage"
54import { GlobalBus } from "@/bus/global"
@@ -12,22 +11,34 @@ import { EventID } from "./schema"
1211import { Flag } from "@/flag/flag"
1312import { zod } from "@/util/effect-zod"
1413
14+ type StructLike < Fields extends Schema . Struct . Fields > = Fields | Schema . Struct < Fields >
15+
1516export type Definition = {
1617 type : string
1718 version : number
1819 aggregate : string
19- schema : z . ZodObject
20+ schema : Schema . Top
21+ busSchema : Schema . Top
22+ properties : z . ZodTypeAny
23+ }
24+
25+ type MutableType < S extends Schema . Top > = Types . DeepMutable < Schema . Schema . Type < S > >
2026
21- // This is temporary and only exists for compatibility with bus
22- // event definitions
23- properties : z . ZodObject
27+ type DefinedEvent < Type extends string , Agg extends string , SchemaDef extends Schema . Top , BusDef extends Schema . Top > = Definition & {
28+ type : Type
29+ aggregate : Agg
30+ schema : SchemaDef
31+ busSchema : BusDef
32+ properties : z . ZodType < MutableType < BusDef > >
2433}
2534
35+ type Data < Def extends Definition > = MutableType < Def [ "schema" ] >
36+
2637export type Event < Def extends Definition = Definition > = {
2738 id : string
2839 seq : number
2940 aggregateID : string
30- data : z . infer < Def [ "schema" ] >
41+ data : Data < Def >
3142}
3243
3344export type SerializedEvent < Def extends Definition = Definition > = Event < Def > & { type : string }
@@ -56,7 +67,7 @@ export function init(input: { projectors: Array<[Definition, ProjectorFunc]>; co
5667 for ( let [ type , version ] of versions . entries ( ) ) {
5768 let def = registry . get ( versionedType ( type , version ) ) !
5869
59- BusEvent . define ( def . type , def . properties || def . schema )
70+ BusEvent . define ( def . type , def . properties )
6071 }
6172
6273 // Freeze the system so it clearly errors if events are defined
@@ -71,58 +82,46 @@ export function versionedType(type: string, version?: number) {
7182 return version ? `${ type } .${ version } ` : type
7283}
7384
74- type SchemaLike < Agg extends string > =
75- | ZodObject < Record < Agg , z . ZodType < string > > >
76- | Schema . Struct < Record < Agg , Schema . Top > >
77-
78- type BusSchemaLike = ZodObject | Schema . Struct < Schema . Struct . Fields >
79-
80- type Mutable < T > = Types . DeepMutable < T >
81- type ToZodObject < S > = S extends Schema . Top
82- ? z . ZodObject < { [ K in keyof Mutable < Schema . Schema . Type < S > > ] : z . ZodType < Mutable < Schema . Schema . Type < S > > [ K ] > } >
83- : S
85+ function struct < Fields extends Schema . Struct . Fields > ( value : StructLike < Fields > ) {
86+ return ( Schema . isSchema ( value ) ? value : Schema . Struct ( value as Fields ) ) as Schema . Struct < Fields >
87+ }
8488
85- /**
86- * Define a sync event. Accepts either a Zod schema or an Effect Schema for
87- * both `schema` and `busSchema`. Effect Schemas are converted to Zod via the
88- * `effect-zod` walker since the sync pipeline uses Zod for validation and
89- * JSON Schema generation.
90- */
9189export function define <
9290 Type extends string ,
9391 Agg extends string ,
94- S extends SchemaLike < Agg > ,
95- B extends BusSchemaLike = S ,
96- > ( input : { type : Type ; version : number ; aggregate : Agg ; schema : S ; busSchema ?: B } ) {
92+ Fields extends Schema . Struct . Fields & Record < Agg , Schema . Top > ,
93+ BusFields extends Schema . Struct . Fields = Fields ,
94+ > ( input : {
95+ type : Type
96+ version : number
97+ aggregate : Agg
98+ schema : StructLike < Fields >
99+ busSchema ?: StructLike < BusFields >
100+ } ) : DefinedEvent < Type , Agg , Schema . Struct < Fields > , Schema . Struct < BusFields > > {
97101 if ( frozen ) {
98102 throw new Error ( "Error defining sync event: sync system has been frozen" )
99103 }
100104
101- const schema = toZodObject ( input . schema ) as ToZodObject < S >
102- const properties = ( input . busSchema ? toZodObject ( input . busSchema ) : schema ) as ToZodObject < B >
105+ const schema = struct ( input . schema )
106+ const busSchema = ( input . busSchema ? struct ( input . busSchema ) : schema ) as Schema . Struct < BusFields >
107+ const properties = zod ( busSchema ) as unknown as z . ZodType < MutableType < typeof busSchema > >
103108
104- const def = {
109+ const def : DefinedEvent < Type , Agg , typeof schema , typeof busSchema > = {
105110 type : input . type ,
106111 version : input . version ,
107112 aggregate : input . aggregate ,
108113 schema,
114+ busSchema,
109115 properties,
110116 }
111117
112118 versions . set ( def . type , Math . max ( def . version , versions . get ( def . type ) || 0 ) )
113119
114- registry . set ( versionedType ( def . type , def . version ) , def as unknown as Definition )
120+ registry . set ( versionedType ( def . type , def . version ) , def )
115121
116122 return def
117123}
118124
119- function toZodObject ( value : ZodObject | Schema . Top ) : z . ZodObject {
120- if ( Schema . isSchema ( value ) ) {
121- return zod ( value as Schema . Top ) as unknown as z . ZodObject
122- }
123- return value as z . ZodObject
124- }
125-
126125export function project < Def extends Definition > (
127126 def : Def ,
128127 func : ( db : Database . TxOrDb , data : Event < Def > [ "data" ] ) => void ,
@@ -172,10 +171,10 @@ function process<Def extends Definition>(def: Def, event: Event<Def>, options: {
172171 const result = convertEvent ( def . type , event . data )
173172 if ( result instanceof Promise ) {
174173 void result . then ( ( data ) => {
175- void ProjectBus . publish ( { type : def . type , properties : def . schema } , data )
174+ void ProjectBus . publish ( { type : def . type , properties : def . properties } , data )
176175 } )
177176 } else {
178- void ProjectBus . publish ( { type : def . type , properties : def . schema } , result )
177+ void ProjectBus . publish ( { type : def . type , properties : def . properties } , result )
179178 }
180179
181180 GlobalBus . emit ( "event" , {
@@ -295,7 +294,7 @@ export function payloads() {
295294 id : z . string ( ) ,
296295 seq : z . number ( ) ,
297296 aggregateID : z . literal ( def . aggregate ) ,
298- data : def . schema ,
297+ data : zod ( def . schema ) ,
299298 } )
300299 . meta ( {
301300 ref : `SyncEvent.${ def . type } ` ,
0 commit comments