atomservices 0.14.2 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`uuidv7`);const t=t=>{let{AggregateType:n,EventName:r,AggregateIdentifier:i=()=>(0,e.uuidv7)(),EventIdentifier:a=()=>(0,e.uuidv7)()}=t;return({aggregateID:e,payloads:t,_metadata:o,_version:s,_createdBy:c,...l})=>({...l,_id:a(),name:r,aggregateType:n,aggregateID:e??i(),payloads:t,_version:s,_createdBy:c,_createdAt:new Date,_metadata:o??{}})},n=()=>{let e=new Map,t=(e,t)=>`${e}:${t}`;return{register:n=>{let r=t(n.type,n.name),i=e.get(r)||[];e.set(r,[...i,n])},resolve:(n,r)=>e.get(t(n,r))||[]}},r=e=>{let{EventStore:t,Projections:r,Reactions:i,onReactionError:a}=e,o=n(),s=n();return r.forEach(e=>o.register(e)),i.forEach(e=>s.register(e)),{async dispatch(e,n){try{await t.append(e);let r=o.resolve(e.aggregateType,e.name);await Promise.all(r.map(t=>t.project(e))),s.resolve(e.aggregateType,e.name).forEach(t=>{t.on(e).catch(n=>{let r=n instanceof Error?n:Error(String(n));a&&a({reactionName:t.name,eventName:e.name,eventID:e._id,error:r})})}),n?.()}catch(e){let t=e instanceof Error?e:Error(String(e));throw n?.(t),t}}}},i=(...e)=>e.flatMap(e=>Object.values(e)),a=()=>(0,e.uuidv7)();exports.composeEventBuilder=t,exports.createService=r,exports.generateID=a,exports.remap=i;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`uuidv7`);const t=t=>{let{name:n,aggregateType:r}=t,i=({aggregateID:t,payloads:i,_version:a,_createdBy:o,_metadata:s})=>({_id:(0,e.uuidv7)(),name:n,aggregateID:t??(0,e.uuidv7)(),aggregateType:r,payloads:i,_version:a,_createdAt:new Date,_createdBy:o,_metadata:s??{}});return{transform:e=>({action:i,handler:{name:n,aggregateType:r,transform:e}})}},n=e=>{let t={};return e.forEach(e=>{let{aggregateType:n,name:r}=e;t[n]||(t[n]={}),t[n][r]||(t[n][r]=[]),t[n][r].push(e)}),{resolve:(e,n)=>t[e]?.[n]||[]}},r=e=>{let t={};return e.forEach(e=>{if(t[e.aggregateType]||(t[e.aggregateType]={}),t[e.aggregateType][e.name])throw Error(`Duplicate command handler for ${e.aggregateType} and ${e.name}`);t[e.aggregateType][e.name]=e}),{transform:e=>{let n=t[e.aggregateType]?.[e.name];if(!n)throw Error(`No command handler found for ${e.aggregateType} and ${e.name}`);return n.transform(e)}}},i=e=>{let{EventStore:t,CommandHandlers:i,Projections:a,Reactions:o,onReactionError:s}=e,c=n(a),l=n(o),u=r(i);return{async dispatch(e,n){try{let r=u.transform(e);await t.append(r);let i=c.resolve(r.aggregateType,r.name);await Promise.all(i.map(e=>e.project(r))),l.resolve(r.aggregateType,r.name).forEach(e=>{e.on(r).catch(t=>{let n=t instanceof Error?t:Error(String(t));s&&s({reactionName:e.name,eventName:r.name,eventID:r._id,error:n})})}),n?.()}catch(e){let t=e instanceof Error?e:Error(String(e));throw n?.(t),t}}}},a=(...e)=>e.flatMap(e=>Object.values(e)),o=()=>(0,e.uuidv7)();exports.createService=i,exports.defineCommand=t,exports.generateID=o,exports.remap=a;
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":[],"sources":["../src/composeEventBuilder.ts","../src/common/Mapper.ts","../src/createService.ts","../src/util.ts"],"sourcesContent":["import { uuidv7 } from \"uuidv7\";\r\nimport { IEvent } from \"./core/IEvent\";\r\n\r\nexport const composeEventBuilder = <T extends IEvent>(definitions: {\r\n EventName: string;\r\n AggregateType: string;\r\n AggregateIdentifier?: () => string;\r\n EventIdentifier?: () => string;\r\n}) => {\r\n const {\r\n AggregateType,\r\n EventName,\r\n AggregateIdentifier = () => uuidv7(),\r\n EventIdentifier = () => uuidv7(),\r\n } = definitions;\r\n\r\n return ({\r\n aggregateID,\r\n payloads,\r\n _metadata,\r\n _version,\r\n _createdBy,\r\n ...others\r\n }: Omit<T, \"_id\" | \"name\" | \"aggregateID\" | \"aggregateType\" | \"_createdAt\" | \"_metadata\"> & {\r\n aggregateID?: string;\r\n _metadata?: T[\"_metadata\"];\r\n }): T => ({\r\n ...others,\r\n _id: EventIdentifier(),\r\n name: EventName,\r\n aggregateType: AggregateType,\r\n aggregateID: aggregateID ?? AggregateIdentifier(),\r\n payloads,\r\n _version,\r\n _createdBy,\r\n _createdAt: new Date(),\r\n _metadata: _metadata ?? ({} as T[\"_metadata\"]),\r\n } as T);\r\n};\r\n","// src/common/Mapper.ts\r\n\r\nexport const Mapper = <T extends { type: string; name: string; }>(): {\r\n register: (component: T) => void;\r\n resolve: (type: string, name: string) => T[];\r\n} => {\r\n const registry = new Map<string, T[]>();\r\n const toKey = (type: string, name: string): string => `${type}:${name}`;\r\n\r\n return {\r\n register: (component: T) => {\r\n const key = toKey(component.type, component.name);\r\n const others = registry.get(key) || [];\r\n registry.set(key, [...others, component]);\r\n },\r\n resolve: (type: string, name: string): T[] =>\r\n registry.get(toKey(type, name)) || [],\r\n };\r\n};\r\n","// src/createService.ts\r\n\r\nimport { IEventStore } from \"./core/IEventStore\";\r\nimport { IProjection } from \"./core/IProjection\";\r\nimport { IReaction } from \"./core/IReaction\";\r\nimport { IReactionError } from \"./core/IReactionError\";\r\nimport { IService } from \"./IService\";\r\nimport { Mapper } from \"./common/Mapper\";\r\n\r\nexport const createService = (definition: {\r\n EventStore: IEventStore;\r\n Projections: IProjection[];\r\n Reactions: IReaction[];\r\n onReactionError?: (context: IReactionError) => void;\r\n}): IService => {\r\n const { EventStore, Projections, Reactions, onReactionError } = definition;\r\n\r\n // Initialize Mappers inside the service to handle the internal grouping\r\n const ProjectionMapper = Mapper<IProjection>();\r\n const ReactionMapper = Mapper<IReaction>();\r\n\r\n // Register all provided components\r\n Projections.forEach((p) => ProjectionMapper.register(p));\r\n Reactions.forEach((r) => ReactionMapper.register(r));\r\n\r\n return {\r\n async dispatch(event, afterHandler): Promise<void> {\r\n try {\r\n await EventStore.append(event);\r\n\r\n const projections = ProjectionMapper.resolve(event.aggregateType, event.name);\r\n await Promise.all(projections.map((p) => p.project(event)));\r\n\r\n const reactions = ReactionMapper.resolve(event.aggregateType, event.name);\r\n reactions.forEach((r) => {\r\n r.on(event).catch((error) => {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n if (onReactionError) {\r\n onReactionError({\r\n reactionName: r.name,\r\n eventName: event.name,\r\n eventID: event._id,\r\n error: wrappedError,\r\n });\r\n }\r\n });\r\n });\r\n\r\n // Resolve successful dispatch immediately after state is sync'd\r\n afterHandler?.();\r\n } catch (error) {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n afterHandler?.(wrappedError);\r\n throw wrappedError;\r\n }\r\n },\r\n };\r\n};\r\n","import { uuidv7 } from \"uuidv7\";\r\n\r\nexport const remap = <T>(...objs: Record<string, T>[]): T[] =>\r\n objs.flatMap(obj => Object.values(obj));\r\n\r\nexport const generateID = (): string => uuidv7();\r\n"],"mappings":"2FAGA,MAAa,EAAyC,GAKhD,CACJ,GAAM,CACJ,gBACA,YACA,2BAAA,EAAA,EAAA,SAAoC,CACpC,uBAAA,EAAA,EAAA,SAAgC,EAC9B,EAEJ,OAAQ,CACN,cACA,WACA,YACA,WACA,aACA,GAAG,MAIK,CACR,GAAG,EACH,IAAK,GAAiB,CACtB,KAAM,EACN,cAAe,EACf,YAAa,GAAe,GAAqB,CACjD,WACA,WACA,aACA,WAAY,IAAI,KAChB,UAAW,GAAc,EAAE,CAC5B,GCnCU,MAGR,CACH,IAAM,EAAW,IAAI,IACf,GAAS,EAAc,IAAyB,GAAG,EAAK,GAAG,IAEjE,MAAO,CACL,SAAW,GAAiB,CAC1B,IAAM,EAAM,EAAM,EAAU,KAAM,EAAU,KAAK,CAC3C,EAAS,EAAS,IAAI,EAAI,EAAI,EAAE,CACtC,EAAS,IAAI,EAAK,CAAC,GAAG,EAAQ,EAAU,CAAC,EAE3C,SAAU,EAAc,IACtB,EAAS,IAAI,EAAM,EAAM,EAAK,CAAC,EAAI,EAAE,CACxC,ECRU,EAAiB,GAKd,CACd,GAAM,CAAE,aAAY,cAAa,YAAW,mBAAoB,EAG1D,EAAmB,GAAqB,CACxC,EAAiB,GAAmB,CAM1C,OAHA,EAAY,QAAS,GAAM,EAAiB,SAAS,EAAE,CAAC,CACxD,EAAU,QAAS,GAAM,EAAe,SAAS,EAAE,CAAC,CAE7C,CACL,MAAM,SAAS,EAAO,EAA6B,CACjD,GAAI,CACF,MAAM,EAAW,OAAO,EAAM,CAE9B,IAAM,EAAc,EAAiB,QAAQ,EAAM,cAAe,EAAM,KAAK,CAC7E,MAAM,QAAQ,IAAI,EAAY,IAAK,GAAM,EAAE,QAAQ,EAAM,CAAC,CAAC,CAEzC,EAAe,QAAQ,EAAM,cAAe,EAAM,KAC3D,CAAC,QAAS,GAAM,CACvB,EAAE,GAAG,EAAM,CAAC,MAAO,GAAU,CAC3B,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAC1E,GACF,EAAgB,CACd,aAAc,EAAE,KAChB,UAAW,EAAM,KACjB,QAAS,EAAM,IACf,MAAO,EACR,CAAC,EAEJ,EACF,CAGF,KAAgB,OACT,EAAO,CACd,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAE9E,MADA,IAAe,EAAa,CACtB,IAGX,ECtDU,GAAY,GAAG,IAC1B,EAAK,QAAQ,GAAO,OAAO,OAAO,EAAI,CAAC,CAE5B,OAAA,EAAA,EAAA,SAAmC"}
1
+ {"version":3,"file":"index.cjs","names":[],"sources":["../src/defineCommand.ts","../src/common/composeMapper.ts","../src/common/composeCommandHandlers.ts","../src/createService.ts","../src/util.ts"],"sourcesContent":["import { uuidv7 } from \"uuidv7\";\r\nimport { ICommand } from \"./core/ICommand\";\r\nimport { IEvent } from \"./core/IEvent\";\r\n\r\nexport const defineCommand = <C extends ICommand, E extends IEvent>(definitions: {\r\n name: string;\r\n aggregateType: string;\r\n}) => {\r\n const { name, aggregateType } = definitions;\r\n\r\n const action = ({\r\n aggregateID,\r\n payloads,\r\n _version,\r\n _createdBy,\r\n _metadata,\r\n }: {\r\n aggregateID?: string;\r\n payloads: C[\"payloads\"];\r\n _version: C[\"_version\"];\r\n _createdBy: C[\"_createdBy\"];\r\n _metadata?: C[\"_metadata\"];\r\n }): C => ({\r\n _id: uuidv7(),\r\n name,\r\n aggregateID: aggregateID ?? uuidv7(),\r\n aggregateType,\r\n payloads,\r\n _version,\r\n _createdAt: new Date(),\r\n _createdBy,\r\n _metadata: _metadata ?? ({} as C[\"_metadata\"]),\r\n } as unknown as C);\r\n\r\n return {\r\n transform: (fn: (command: C) => E) => ({\r\n action,\r\n handler: {\r\n name,\r\n aggregateType,\r\n transform: fn,\r\n },\r\n })\r\n };\r\n};\r\n","// src/common/composeMapper.ts\r\n\r\n// A generic utility to compose Mappers for Projections and Reactions based on aggregateType and name\r\nexport const composeMapper = <T extends { aggregateType: string; name: string; }>(components: T[]): {\r\n resolve: (aggregateType: string, name: string) => T[];\r\n} => {\r\n const Registry = {} as Record<string, Record<string, T[]>>;\r\n\r\n components.forEach((component) => {\r\n const { aggregateType, name } = component;\r\n if (!Registry[aggregateType]) {\r\n Registry[aggregateType] = {};\r\n }\r\n\r\n if (!Registry[aggregateType][name]) {\r\n Registry[aggregateType][name] = [];\r\n }\r\n\r\n Registry[aggregateType][name].push(component);\r\n });\r\n\r\n return {\r\n // Resolve returns an array of components matching the aggregateType and name, or an empty array if none found\r\n resolve: (aggregateType: string, name: string): T[] =>\r\n Registry[aggregateType]?.[name] || [],\r\n };\r\n};\r\n","// src/common/composeCommandHandlers.ts\r\n\r\nimport { ICommand } from \"../core/ICommand\";\r\nimport { ICommandHandler } from \"../core/ICommandHandler\";\r\n\r\nexport const composeCommandHandlers = (handlers: ICommandHandler[]) => {\r\n const Registry = {} as Record<string, Record<string, ICommandHandler>>;\r\n\r\n handlers.forEach(handler => {\r\n if (!Registry[handler.aggregateType]) {\r\n Registry[handler.aggregateType] = {};\r\n }\r\n\r\n if (Registry[handler.aggregateType][handler.name]) {\r\n throw new Error(`Duplicate command handler for ${handler.aggregateType} and ${handler.name}`);\r\n }\r\n\r\n Registry[handler.aggregateType][handler.name] = handler;\r\n });\r\n\r\n return {\r\n transform: (command: ICommand) => {\r\n const handler = Registry[command.aggregateType]?.[command.name];\r\n\r\n if (!handler) {\r\n throw new Error(`No command handler found for ${command.aggregateType} and ${command.name}`);\r\n }\r\n\r\n return handler.transform(command);\r\n },\r\n };\r\n};\r\n","// src/createService.ts\r\n\r\nimport { composeMapper } from \"./common/composeMapper\";\r\nimport { composeCommandHandlers } from \"./common/composeCommandHandlers\";\r\nimport { IEventStore } from \"./core/IEventStore\";\r\nimport { ICommandHandler } from \"./core/ICommandHandler\";\r\nimport { IProjection } from \"./core/IProjection\";\r\nimport { IReaction } from \"./core/IReaction\";\r\nimport { IReactionError } from \"./core/IReactionError\";\r\nimport { IService } from \"./IService\";\r\n\r\nexport const createService = (definition: {\r\n EventStore: IEventStore;\r\n CommandHandlers: ICommandHandler[];\r\n Projections: IProjection[];\r\n Reactions: IReaction[];\r\n onReactionError?: (context: IReactionError) => void;\r\n}): IService => {\r\n const { EventStore, CommandHandlers, Projections, Reactions, onReactionError } = definition;\r\n\r\n // Initialize Mappers\r\n const _ProjectionMapper = composeMapper<IProjection>(Projections);\r\n const _ReactionMapper = composeMapper<IReaction>(Reactions);\r\n const _CommandHandler = composeCommandHandlers(CommandHandlers);\r\n\r\n return {\r\n async dispatch(command, afterHandler): Promise<void> {\r\n try {\r\n const event = _CommandHandler.transform(command);\r\n await EventStore.append(event);\r\n\r\n const projections = _ProjectionMapper.resolve(event.aggregateType, event.name);\r\n await Promise.all(projections.map((p) => p.project(event)));\r\n\r\n const reactions = _ReactionMapper.resolve(event.aggregateType, event.name);\r\n reactions.forEach((r) => {\r\n r.on(event).catch((error) => {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n if (onReactionError) {\r\n onReactionError({\r\n reactionName: r.name,\r\n eventName: event.name,\r\n eventID: event._id,\r\n error: wrappedError,\r\n });\r\n }\r\n });\r\n });\r\n\r\n // Resolve successful dispatch immediately after state is sync'd\r\n afterHandler?.();\r\n } catch (error) {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n afterHandler?.(wrappedError);\r\n throw wrappedError;\r\n }\r\n },\r\n };\r\n};\r\n","import { uuidv7 } from \"uuidv7\";\r\n\r\nexport const remap = <T>(...objs: Record<string, T>[]): T[] =>\r\n objs.flatMap(obj => Object.values(obj));\r\n\r\nexport const generateID = (): string => uuidv7();\r\n"],"mappings":"2FAIA,MAAa,EAAuD,GAG9D,CACJ,GAAM,CAAE,OAAM,iBAAkB,EAE1B,GAAU,CACd,cACA,WACA,WACA,aACA,gBAOQ,CACR,KAAA,EAAA,EAAA,SAAa,CACb,OACA,YAAa,IAAA,EAAA,EAAA,SAAuB,CACpC,gBACA,WACA,WACA,WAAY,IAAI,KAChB,aACA,UAAW,GAAc,EAAE,CAC5B,EAED,MAAO,CACL,UAAY,IAA2B,CACrC,SACA,QAAS,CACP,OACA,gBACA,UAAW,EACZ,CACF,EACF,ECxCU,EAAqE,GAE7E,CACH,IAAM,EAAW,EAAE,CAenB,OAbA,EAAW,QAAS,GAAc,CAChC,GAAM,CAAE,gBAAe,QAAS,EAC3B,EAAS,KACZ,EAAS,GAAiB,EAAE,EAGzB,EAAS,GAAe,KAC3B,EAAS,GAAe,GAAQ,EAAE,EAGpC,EAAS,GAAe,GAAM,KAAK,EAAU,EAC7C,CAEK,CAEL,SAAU,EAAuB,IAC/B,EAAS,KAAiB,IAAS,EAAE,CACxC,ECpBU,EAA0B,GAAgC,CACrE,IAAM,EAAW,EAAE,CAcnB,OAZA,EAAS,QAAQ,GAAW,CAK1B,GAJK,EAAS,EAAQ,iBACpB,EAAS,EAAQ,eAAiB,EAAE,EAGlC,EAAS,EAAQ,eAAe,EAAQ,MAC1C,MAAU,MAAM,iCAAiC,EAAQ,cAAc,OAAO,EAAQ,OAAO,CAG/F,EAAS,EAAQ,eAAe,EAAQ,MAAQ,GAChD,CAEK,CACL,UAAY,GAAsB,CAChC,IAAM,EAAU,EAAS,EAAQ,iBAAiB,EAAQ,MAE1D,GAAI,CAAC,EACH,MAAU,MAAM,gCAAgC,EAAQ,cAAc,OAAO,EAAQ,OAAO,CAG9F,OAAO,EAAQ,UAAU,EAAQ,EAEpC,ECnBU,EAAiB,GAMd,CACd,GAAM,CAAE,aAAY,kBAAiB,cAAa,YAAW,mBAAoB,EAG3E,EAAoB,EAA2B,EAAY,CAC3D,EAAkB,EAAyB,EAAU,CACrD,EAAkB,EAAuB,EAAgB,CAE/D,MAAO,CACL,MAAM,SAAS,EAAS,EAA6B,CACnD,GAAI,CACF,IAAM,EAAQ,EAAgB,UAAU,EAAQ,CAChD,MAAM,EAAW,OAAO,EAAM,CAE9B,IAAM,EAAc,EAAkB,QAAQ,EAAM,cAAe,EAAM,KAAK,CAC9E,MAAM,QAAQ,IAAI,EAAY,IAAK,GAAM,EAAE,QAAQ,EAAM,CAAC,CAAC,CAEzC,EAAgB,QAAQ,EAAM,cAAe,EAAM,KAC5D,CAAC,QAAS,GAAM,CACvB,EAAE,GAAG,EAAM,CAAC,MAAO,GAAU,CAC3B,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAC1E,GACF,EAAgB,CACd,aAAc,EAAE,KAChB,UAAW,EAAM,KACjB,QAAS,EAAM,IACf,MAAO,EACR,CAAC,EAEJ,EACF,CAGF,KAAgB,OACT,EAAO,CACd,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAE9E,MADA,IAAe,EAAa,CACtB,IAGX,ECvDU,GAAY,GAAG,IAC1B,EAAK,QAAQ,GAAO,OAAO,OAAO,EAAI,CAAC,CAE5B,OAAA,EAAA,EAAA,SAAmC"}
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/core/IEvent.d.ts
2
- interface IEvent<Payloads = any, Metadata extends Record<string, unknown> = {}> {
2
+ interface IEvent<Payloads extends Record<string, unknown> = {}, Metadata extends Record<string, unknown> = {}> {
3
3
  _id: string;
4
4
  name: string;
5
5
  aggregateID: string;
@@ -11,6 +11,26 @@ interface IEvent<Payloads = any, Metadata extends Record<string, unknown> = {}>
11
11
  _metadata: Metadata;
12
12
  }
13
13
  //#endregion
14
+ //#region src/core/ICommand.d.ts
15
+ interface ICommand<E extends IEvent = IEvent, Payloads = E["payloads"], Metadata = E["_metadata"]> {
16
+ _id: string;
17
+ name: string;
18
+ aggregateID: string;
19
+ aggregateType: string;
20
+ payloads: Payloads;
21
+ _version: number;
22
+ _createdAt: Date;
23
+ _createdBy: string;
24
+ _metadata: Metadata;
25
+ }
26
+ //#endregion
27
+ //#region src/core/ICommandHandler.d.ts
28
+ interface ICommandHandler<Command extends ICommand = ICommand, Event extends IEvent = IEvent> {
29
+ name: string;
30
+ aggregateType: string;
31
+ transform: (command: Command) => Event;
32
+ }
33
+ //#endregion
14
34
  //#region src/core/IEventStore.d.ts
15
35
  interface IEventStore {
16
36
  append: (event: IEvent) => Promise<void>;
@@ -22,10 +42,10 @@ interface IEventStore {
22
42
  * Responsibility: Update the Read Model.
23
43
  * Must be idempotent and focused solely on state representation.
24
44
  */
25
- interface IProjection<E extends IEvent = IEvent> {
26
- type: string;
45
+ interface IProjection<Event extends IEvent = IEvent> {
27
46
  name: string;
28
- project: (event: E) => Promise<void>;
47
+ aggregateType: string;
48
+ project: (event: Event) => Promise<void>;
29
49
  }
30
50
  //#endregion
31
51
  //#region src/core/IReaction.d.ts
@@ -34,10 +54,10 @@ interface IProjection<E extends IEvent = IEvent> {
34
54
  * Reactions are asynchronous, non-blocking listeners to events.
35
55
  * They do NOT update state directly; they trigger external actions or new events.
36
56
  */
37
- interface IReaction<E extends IEvent = IEvent> {
38
- type: string;
57
+ interface IReaction<Event extends IEvent = IEvent> {
39
58
  name: string;
40
- on: (event: E) => Promise<void>;
59
+ aggregateType: string;
60
+ on: (event: Event) => Promise<void>;
41
61
  }
42
62
  //#endregion
43
63
  //#region src/core/IReactionError.d.ts
@@ -54,42 +74,57 @@ interface IReactionError {
54
74
  * Must calculate the new state immediately without side effects.
55
75
  * Used for high-performance Aggregate Rehydration.
56
76
  */
57
- interface IReducer<T> {
58
- (state: T, event: IEvent): T;
77
+ interface IReducer<State> {
78
+ (state: State, event: IEvent): State;
59
79
  }
60
80
  //#endregion
61
81
  //#region src/IService.d.ts
62
82
  interface IService {
63
83
  /**
64
- * Persists an event to the Store and triggers the handler in the background.
65
- * * @param event The event to dispatch.
66
- * @param afterHandler Optional callback triggered after background processing is complete.
67
- * @returns A promise that resolves as soon as the event is successfully stored.
84
+ * Processes a user intent (Command) through the system lifecycle.
85
+ * * Lifecycle:
86
+ * 1. Resolve: Find the 1-to-1 Command Handler for the given command name and type.
87
+ * 2. Transform: Convert the Command into an Event using a pure handler function.
88
+ * 3. Persist: Append the resulting Event to the Event Store.
89
+ * 4. Project: Update the State Store (Read Models) via Projections.
90
+ * 5. React: Trigger side-effects (e.g., emails, external APIs) via Reactions.
91
+ * * @param command - The ICommand object representing the intent.
92
+ * @param afterHandler - Optional callback executed after the state is synchronized.
68
93
  */
69
- dispatch: (event: IEvent, afterHandler?: (error?: Error) => void) => Promise<void>;
94
+ dispatch: (command: ICommand, afterHandler?: (error?: Error) => void) => Promise<void>;
70
95
  }
71
96
  //#endregion
72
- //#region src/composeEventBuilder.d.ts
73
- declare const composeEventBuilder: <T extends IEvent>(definitions: {
74
- EventName: string;
75
- AggregateType: string;
76
- AggregateIdentifier?: () => string;
77
- EventIdentifier?: () => string;
78
- }) => ({
79
- aggregateID,
80
- payloads,
81
- _metadata,
82
- _version,
83
- _createdBy,
84
- ...others
85
- }: Omit<T, "_id" | "name" | "aggregateID" | "aggregateType" | "_createdAt" | "_metadata"> & {
86
- aggregateID?: string;
87
- _metadata?: T["_metadata"];
88
- }) => T;
97
+ //#region src/defineCommand.d.ts
98
+ declare const defineCommand: <C extends ICommand, E extends IEvent>(definitions: {
99
+ name: string;
100
+ aggregateType: string;
101
+ }) => {
102
+ transform: (fn: (command: C) => E) => {
103
+ action: ({
104
+ aggregateID,
105
+ payloads,
106
+ _version,
107
+ _createdBy,
108
+ _metadata
109
+ }: {
110
+ aggregateID?: string;
111
+ payloads: C["payloads"];
112
+ _version: C["_version"];
113
+ _createdBy: C["_createdBy"];
114
+ _metadata?: C["_metadata"];
115
+ }) => C;
116
+ handler: {
117
+ name: string;
118
+ aggregateType: string;
119
+ transform: (command: C) => E;
120
+ };
121
+ };
122
+ };
89
123
  //#endregion
90
124
  //#region src/createService.d.ts
91
125
  declare const createService: (definition: {
92
126
  EventStore: IEventStore;
127
+ CommandHandlers: ICommandHandler[];
93
128
  Projections: IProjection[];
94
129
  Reactions: IReaction[];
95
130
  onReactionError?: (context: IReactionError) => void;
@@ -99,5 +134,5 @@ declare const createService: (definition: {
99
134
  declare const remap: <T>(...objs: Record<string, T>[]) => T[];
100
135
  declare const generateID: () => string;
101
136
  //#endregion
102
- export { type IEvent, type IEventStore, type IProjection, type IReaction, type IReactionError, type IReducer, type IService, composeEventBuilder, createService, generateID, remap };
137
+ export { type ICommand, type ICommandHandler, type IEvent, type IEventStore, type IProjection, type IReaction, type IReactionError, type IReducer, type IService, createService, defineCommand, generateID, remap };
103
138
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/core/IEvent.ts","../src/core/IEventStore.ts","../src/core/IProjection.ts","../src/core/IReaction.ts","../src/core/IReactionError.ts","../src/core/IReducer.ts","../src/IService.ts","../src/composeEventBuilder.ts","../src/createService.ts","../src/util.ts"],"mappings":";UAEiB,MAAA,kCAAwC,MAAA;EACvD,GAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;EACA,UAAA,EAAY,IAAA;EACZ,UAAA;EACA,SAAA,EAAW,QAAA;AAAA;;;UCPI,WAAA;EACf,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,OAAA;AAAA;;;ADH7B;;;;;AAAA,UEOiB,WAAA,WAAsB,MAAA,GAAS,MAAA;EAC9C,IAAA;EACA,IAAA;EACA,OAAA,GAAU,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;AFVzB;;;;;AAAA,UGKiB,SAAA,WAAoB,MAAA,GAAS,MAAA;EAC5C,IAAA;EACA,IAAA;EACA,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;UCVH,cAAA;EACf,YAAA;EACA,SAAA;EACA,OAAA;EACA,KAAA,EAAO,KAAA;AAAA;;;AJFT;;;;;AAAA,UKOiB,QAAA;EAAA,CACd,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,MAAA,GAAS,CAAA;AAAA;;;UCNZ,QAAA;ENFM;;;;;;EMSrB,QAAA,GAAW,KAAA,EAAO,MAAA,EAAQ,YAAA,IAAgB,KAAA,GAAQ,KAAA,cAAmB,OAAA;AAAA;;;cCR1D,mBAAA,aAAiC,MAAA,EAAQ,WAAA;EACpD,SAAA;EACA,aAAA;EACA,mBAAA;EACA,eAAA;AAAA;EASQ,WAAA;EAAA,QAAA;EAAA,SAAA;EAAA,QAAA;EAAA,UAAA;EAAA,GAAA;AAAA,GAOL,IAAA,CAAK,CAAA;EACN,WAAA;EACA,SAAA,GAAY,CAAA;AAAA,MACV,CAAA;;;cCjBO,aAAA,GAAiB,UAAA;EAC5B,UAAA,EAAY,WAAA;EACZ,WAAA,EAAa,WAAA;EACb,SAAA,EAAW,SAAA;EACX,eAAA,IAAmB,OAAA,EAAS,cAAA;AAAA,MAC1B,QAAA;;;cCZS,KAAA,SAAe,IAAA,EAAM,MAAA,SAAe,CAAA,QAAO,CAAA;AAAA,cAG3C,UAAA"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/core/IEvent.ts","../src/core/ICommand.ts","../src/core/ICommandHandler.ts","../src/core/IEventStore.ts","../src/core/IProjection.ts","../src/core/IReaction.ts","../src/core/IReactionError.ts","../src/core/IReducer.ts","../src/IService.ts","../src/defineCommand.ts","../src/createService.ts","../src/util.ts"],"mappings":";UAEiB,MAAA,kBAAwB,MAAA,yCAA+C,MAAA;EACtF,GAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;EACA,UAAA,EAAY,IAAA;EACZ,UAAA;EACA,SAAA,EAAW,QAAA;AAAA;;;UCPI,QAAA,WAAmB,MAAA,GAAS,MAAA,aAAmB,CAAA,yBAA0B,CAAA;EACxF,GAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;EACA,UAAA,EAAY,IAAA;EACZ,UAAA;EACA,SAAA,EAAW,QAAA;AAAA;;;UCVI,eAAA,iBAAgC,QAAA,GAAW,QAAA,gBAAwB,MAAA,GAAS,MAAA;EAC3F,IAAA;EACA,aAAA;EACA,SAAA,GAAY,OAAA,EAAS,OAAA,KAAY,KAAA;AAAA;;;UCFlB,WAAA;EACf,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,OAAA;AAAA;;;AHH7B;;;;;AAAA,UIOiB,WAAA,eAA0B,MAAA,GAAS,MAAA;EAClD,IAAA;EACA,aAAA;EACA,OAAA,GAAU,KAAA,EAAO,KAAA,KAAU,OAAA;AAAA;;;AJV7B;;;;;AAAA,UKKiB,SAAA,eAAwB,MAAA,GAAS,MAAA;EAChD,IAAA;EACA,aAAA;EACA,EAAA,GAAK,KAAA,EAAO,KAAA,KAAU,OAAA;AAAA;;;UCVP,cAAA;EACf,YAAA;EACA,SAAA;EACA,OAAA;EACA,KAAA,EAAO,KAAA;AAAA;;;ANFT;;;;;AAAA,UOOiB,QAAA;EAAA,CACd,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,GAAS,KAAA;AAAA;;;UCNhB,QAAA;ERFM;;;;;;;;;;;EQcrB,QAAA,GAAW,OAAA,EAAS,QAAA,EAAU,YAAA,IAAgB,KAAA,GAAQ,KAAA,cAAmB,OAAA;AAAA;;;cCZ9D,aAAA,aAA2B,QAAA,YAAoB,MAAA,EAAQ,WAAA;EAClE,IAAA;EACA,aAAA;AAAA;mBA6BmB,OAAA,EAAS,CAAA,KAAM,CAAA;;;;;;;;MAlBhC,WAAA;MACA,QAAA,EAAU,CAAA;MACV,QAAA,EAAU,CAAA;MACV,UAAA,EAAY,CAAA;MACZ,SAAA,GAAY,CAAA;IAAA,MACV,CAAA;;;;2BAawB,CAAA,KAAM,CAAA;IAAA;EAAA;AAAA;;;cCxBvB,aAAA,GAAiB,UAAA;EAC5B,UAAA,EAAY,WAAA;EACZ,eAAA,EAAiB,eAAA;EACjB,WAAA,EAAa,WAAA;EACb,SAAA,EAAW,SAAA;EACX,eAAA,IAAmB,OAAA,EAAS,cAAA;AAAA,MAC1B,QAAA;;;cCfS,KAAA,SAAe,IAAA,EAAM,MAAA,SAAe,CAAA,QAAO,CAAA;AAAA,cAG3C,UAAA"}
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region src/core/IEvent.d.ts
2
- interface IEvent<Payloads = any, Metadata extends Record<string, unknown> = {}> {
2
+ interface IEvent<Payloads extends Record<string, unknown> = {}, Metadata extends Record<string, unknown> = {}> {
3
3
  _id: string;
4
4
  name: string;
5
5
  aggregateID: string;
@@ -11,6 +11,26 @@ interface IEvent<Payloads = any, Metadata extends Record<string, unknown> = {}>
11
11
  _metadata: Metadata;
12
12
  }
13
13
  //#endregion
14
+ //#region src/core/ICommand.d.ts
15
+ interface ICommand<E extends IEvent = IEvent, Payloads = E["payloads"], Metadata = E["_metadata"]> {
16
+ _id: string;
17
+ name: string;
18
+ aggregateID: string;
19
+ aggregateType: string;
20
+ payloads: Payloads;
21
+ _version: number;
22
+ _createdAt: Date;
23
+ _createdBy: string;
24
+ _metadata: Metadata;
25
+ }
26
+ //#endregion
27
+ //#region src/core/ICommandHandler.d.ts
28
+ interface ICommandHandler<Command extends ICommand = ICommand, Event extends IEvent = IEvent> {
29
+ name: string;
30
+ aggregateType: string;
31
+ transform: (command: Command) => Event;
32
+ }
33
+ //#endregion
14
34
  //#region src/core/IEventStore.d.ts
15
35
  interface IEventStore {
16
36
  append: (event: IEvent) => Promise<void>;
@@ -22,10 +42,10 @@ interface IEventStore {
22
42
  * Responsibility: Update the Read Model.
23
43
  * Must be idempotent and focused solely on state representation.
24
44
  */
25
- interface IProjection<E extends IEvent = IEvent> {
26
- type: string;
45
+ interface IProjection<Event extends IEvent = IEvent> {
27
46
  name: string;
28
- project: (event: E) => Promise<void>;
47
+ aggregateType: string;
48
+ project: (event: Event) => Promise<void>;
29
49
  }
30
50
  //#endregion
31
51
  //#region src/core/IReaction.d.ts
@@ -34,10 +54,10 @@ interface IProjection<E extends IEvent = IEvent> {
34
54
  * Reactions are asynchronous, non-blocking listeners to events.
35
55
  * They do NOT update state directly; they trigger external actions or new events.
36
56
  */
37
- interface IReaction<E extends IEvent = IEvent> {
38
- type: string;
57
+ interface IReaction<Event extends IEvent = IEvent> {
39
58
  name: string;
40
- on: (event: E) => Promise<void>;
59
+ aggregateType: string;
60
+ on: (event: Event) => Promise<void>;
41
61
  }
42
62
  //#endregion
43
63
  //#region src/core/IReactionError.d.ts
@@ -54,42 +74,57 @@ interface IReactionError {
54
74
  * Must calculate the new state immediately without side effects.
55
75
  * Used for high-performance Aggregate Rehydration.
56
76
  */
57
- interface IReducer<T> {
58
- (state: T, event: IEvent): T;
77
+ interface IReducer<State> {
78
+ (state: State, event: IEvent): State;
59
79
  }
60
80
  //#endregion
61
81
  //#region src/IService.d.ts
62
82
  interface IService {
63
83
  /**
64
- * Persists an event to the Store and triggers the handler in the background.
65
- * * @param event The event to dispatch.
66
- * @param afterHandler Optional callback triggered after background processing is complete.
67
- * @returns A promise that resolves as soon as the event is successfully stored.
84
+ * Processes a user intent (Command) through the system lifecycle.
85
+ * * Lifecycle:
86
+ * 1. Resolve: Find the 1-to-1 Command Handler for the given command name and type.
87
+ * 2. Transform: Convert the Command into an Event using a pure handler function.
88
+ * 3. Persist: Append the resulting Event to the Event Store.
89
+ * 4. Project: Update the State Store (Read Models) via Projections.
90
+ * 5. React: Trigger side-effects (e.g., emails, external APIs) via Reactions.
91
+ * * @param command - The ICommand object representing the intent.
92
+ * @param afterHandler - Optional callback executed after the state is synchronized.
68
93
  */
69
- dispatch: (event: IEvent, afterHandler?: (error?: Error) => void) => Promise<void>;
94
+ dispatch: (command: ICommand, afterHandler?: (error?: Error) => void) => Promise<void>;
70
95
  }
71
96
  //#endregion
72
- //#region src/composeEventBuilder.d.ts
73
- declare const composeEventBuilder: <T extends IEvent>(definitions: {
74
- EventName: string;
75
- AggregateType: string;
76
- AggregateIdentifier?: () => string;
77
- EventIdentifier?: () => string;
78
- }) => ({
79
- aggregateID,
80
- payloads,
81
- _metadata,
82
- _version,
83
- _createdBy,
84
- ...others
85
- }: Omit<T, "_id" | "name" | "aggregateID" | "aggregateType" | "_createdAt" | "_metadata"> & {
86
- aggregateID?: string;
87
- _metadata?: T["_metadata"];
88
- }) => T;
97
+ //#region src/defineCommand.d.ts
98
+ declare const defineCommand: <C extends ICommand, E extends IEvent>(definitions: {
99
+ name: string;
100
+ aggregateType: string;
101
+ }) => {
102
+ transform: (fn: (command: C) => E) => {
103
+ action: ({
104
+ aggregateID,
105
+ payloads,
106
+ _version,
107
+ _createdBy,
108
+ _metadata
109
+ }: {
110
+ aggregateID?: string;
111
+ payloads: C["payloads"];
112
+ _version: C["_version"];
113
+ _createdBy: C["_createdBy"];
114
+ _metadata?: C["_metadata"];
115
+ }) => C;
116
+ handler: {
117
+ name: string;
118
+ aggregateType: string;
119
+ transform: (command: C) => E;
120
+ };
121
+ };
122
+ };
89
123
  //#endregion
90
124
  //#region src/createService.d.ts
91
125
  declare const createService: (definition: {
92
126
  EventStore: IEventStore;
127
+ CommandHandlers: ICommandHandler[];
93
128
  Projections: IProjection[];
94
129
  Reactions: IReaction[];
95
130
  onReactionError?: (context: IReactionError) => void;
@@ -99,5 +134,5 @@ declare const createService: (definition: {
99
134
  declare const remap: <T>(...objs: Record<string, T>[]) => T[];
100
135
  declare const generateID: () => string;
101
136
  //#endregion
102
- export { type IEvent, type IEventStore, type IProjection, type IReaction, type IReactionError, type IReducer, type IService, composeEventBuilder, createService, generateID, remap };
137
+ export { type ICommand, type ICommandHandler, type IEvent, type IEventStore, type IProjection, type IReaction, type IReactionError, type IReducer, type IService, createService, defineCommand, generateID, remap };
103
138
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/IEvent.ts","../src/core/IEventStore.ts","../src/core/IProjection.ts","../src/core/IReaction.ts","../src/core/IReactionError.ts","../src/core/IReducer.ts","../src/IService.ts","../src/composeEventBuilder.ts","../src/createService.ts","../src/util.ts"],"mappings":";UAEiB,MAAA,kCAAwC,MAAA;EACvD,GAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;EACA,UAAA,EAAY,IAAA;EACZ,UAAA;EACA,SAAA,EAAW,QAAA;AAAA;;;UCPI,WAAA;EACf,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,OAAA;AAAA;;;ADH7B;;;;;AAAA,UEOiB,WAAA,WAAsB,MAAA,GAAS,MAAA;EAC9C,IAAA;EACA,IAAA;EACA,OAAA,GAAU,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;AFVzB;;;;;AAAA,UGKiB,SAAA,WAAoB,MAAA,GAAS,MAAA;EAC5C,IAAA;EACA,IAAA;EACA,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;UCVH,cAAA;EACf,YAAA;EACA,SAAA;EACA,OAAA;EACA,KAAA,EAAO,KAAA;AAAA;;;AJFT;;;;;AAAA,UKOiB,QAAA;EAAA,CACd,KAAA,EAAO,CAAA,EAAG,KAAA,EAAO,MAAA,GAAS,CAAA;AAAA;;;UCNZ,QAAA;ENFM;;;;;;EMSrB,QAAA,GAAW,KAAA,EAAO,MAAA,EAAQ,YAAA,IAAgB,KAAA,GAAQ,KAAA,cAAmB,OAAA;AAAA;;;cCR1D,mBAAA,aAAiC,MAAA,EAAQ,WAAA;EACpD,SAAA;EACA,aAAA;EACA,mBAAA;EACA,eAAA;AAAA;EASQ,WAAA;EAAA,QAAA;EAAA,SAAA;EAAA,QAAA;EAAA,UAAA;EAAA,GAAA;AAAA,GAOL,IAAA,CAAK,CAAA;EACN,WAAA;EACA,SAAA,GAAY,CAAA;AAAA,MACV,CAAA;;;cCjBO,aAAA,GAAiB,UAAA;EAC5B,UAAA,EAAY,WAAA;EACZ,WAAA,EAAa,WAAA;EACb,SAAA,EAAW,SAAA;EACX,eAAA,IAAmB,OAAA,EAAS,cAAA;AAAA,MAC1B,QAAA;;;cCZS,KAAA,SAAe,IAAA,EAAM,MAAA,SAAe,CAAA,QAAO,CAAA;AAAA,cAG3C,UAAA"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/core/IEvent.ts","../src/core/ICommand.ts","../src/core/ICommandHandler.ts","../src/core/IEventStore.ts","../src/core/IProjection.ts","../src/core/IReaction.ts","../src/core/IReactionError.ts","../src/core/IReducer.ts","../src/IService.ts","../src/defineCommand.ts","../src/createService.ts","../src/util.ts"],"mappings":";UAEiB,MAAA,kBAAwB,MAAA,yCAA+C,MAAA;EACtF,GAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;EACA,UAAA,EAAY,IAAA;EACZ,UAAA;EACA,SAAA,EAAW,QAAA;AAAA;;;UCPI,QAAA,WAAmB,MAAA,GAAS,MAAA,aAAmB,CAAA,yBAA0B,CAAA;EACxF,GAAA;EACA,IAAA;EACA,WAAA;EACA,aAAA;EACA,QAAA,EAAU,QAAA;EACV,QAAA;EACA,UAAA,EAAY,IAAA;EACZ,UAAA;EACA,SAAA,EAAW,QAAA;AAAA;;;UCVI,eAAA,iBAAgC,QAAA,GAAW,QAAA,gBAAwB,MAAA,GAAS,MAAA;EAC3F,IAAA;EACA,aAAA;EACA,SAAA,GAAY,OAAA,EAAS,OAAA,KAAY,KAAA;AAAA;;;UCFlB,WAAA;EACf,MAAA,GAAS,KAAA,EAAO,MAAA,KAAW,OAAA;AAAA;;;AHH7B;;;;;AAAA,UIOiB,WAAA,eAA0B,MAAA,GAAS,MAAA;EAClD,IAAA;EACA,aAAA;EACA,OAAA,GAAU,KAAA,EAAO,KAAA,KAAU,OAAA;AAAA;;;AJV7B;;;;;AAAA,UKKiB,SAAA,eAAwB,MAAA,GAAS,MAAA;EAChD,IAAA;EACA,aAAA;EACA,EAAA,GAAK,KAAA,EAAO,KAAA,KAAU,OAAA;AAAA;;;UCVP,cAAA;EACf,YAAA;EACA,SAAA;EACA,OAAA;EACA,KAAA,EAAO,KAAA;AAAA;;;ANFT;;;;;AAAA,UOOiB,QAAA;EAAA,CACd,KAAA,EAAO,KAAA,EAAO,KAAA,EAAO,MAAA,GAAS,KAAA;AAAA;;;UCNhB,QAAA;ERFM;;;;;;;;;;;EQcrB,QAAA,GAAW,OAAA,EAAS,QAAA,EAAU,YAAA,IAAgB,KAAA,GAAQ,KAAA,cAAmB,OAAA;AAAA;;;cCZ9D,aAAA,aAA2B,QAAA,YAAoB,MAAA,EAAQ,WAAA;EAClE,IAAA;EACA,aAAA;AAAA;mBA6BmB,OAAA,EAAS,CAAA,KAAM,CAAA;;;;;;;;MAlBhC,WAAA;MACA,QAAA,EAAU,CAAA;MACV,QAAA,EAAU,CAAA;MACV,UAAA,EAAY,CAAA;MACZ,SAAA,GAAY,CAAA;IAAA,MACV,CAAA;;;;2BAawB,CAAA,KAAM,CAAA;IAAA;EAAA;AAAA;;;cCxBvB,aAAA,GAAiB,UAAA;EAC5B,UAAA,EAAY,WAAA;EACZ,eAAA,EAAiB,eAAA;EACjB,WAAA,EAAa,WAAA;EACb,SAAA,EAAW,SAAA;EACX,eAAA,IAAmB,OAAA,EAAS,cAAA;AAAA,MAC1B,QAAA;;;cCfS,KAAA,SAAe,IAAA,EAAM,MAAA,SAAe,CAAA,QAAO,CAAA;AAAA,cAG3C,UAAA"}
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import{uuidv7 as e}from"uuidv7";const t=t=>{let{AggregateType:n,EventName:r,AggregateIdentifier:i=()=>e(),EventIdentifier:a=()=>e()}=t;return({aggregateID:e,payloads:t,_metadata:o,_version:s,_createdBy:c,...l})=>({...l,_id:a(),name:r,aggregateType:n,aggregateID:e??i(),payloads:t,_version:s,_createdBy:c,_createdAt:new Date,_metadata:o??{}})},n=()=>{let e=new Map,t=(e,t)=>`${e}:${t}`;return{register:n=>{let r=t(n.type,n.name),i=e.get(r)||[];e.set(r,[...i,n])},resolve:(n,r)=>e.get(t(n,r))||[]}},r=e=>{let{EventStore:t,Projections:r,Reactions:i,onReactionError:a}=e,o=n(),s=n();return r.forEach(e=>o.register(e)),i.forEach(e=>s.register(e)),{async dispatch(e,n){try{await t.append(e);let r=o.resolve(e.aggregateType,e.name);await Promise.all(r.map(t=>t.project(e))),s.resolve(e.aggregateType,e.name).forEach(t=>{t.on(e).catch(n=>{let r=n instanceof Error?n:Error(String(n));a&&a({reactionName:t.name,eventName:e.name,eventID:e._id,error:r})})}),n?.()}catch(e){let t=e instanceof Error?e:Error(String(e));throw n?.(t),t}}}},i=(...e)=>e.flatMap(e=>Object.values(e)),a=()=>e();export{t as composeEventBuilder,r as createService,a as generateID,i as remap};
1
+ import{uuidv7 as e}from"uuidv7";const t=t=>{let{name:n,aggregateType:r}=t,i=({aggregateID:t,payloads:i,_version:a,_createdBy:o,_metadata:s})=>({_id:e(),name:n,aggregateID:t??e(),aggregateType:r,payloads:i,_version:a,_createdAt:new Date,_createdBy:o,_metadata:s??{}});return{transform:e=>({action:i,handler:{name:n,aggregateType:r,transform:e}})}},n=e=>{let t={};return e.forEach(e=>{let{aggregateType:n,name:r}=e;t[n]||(t[n]={}),t[n][r]||(t[n][r]=[]),t[n][r].push(e)}),{resolve:(e,n)=>t[e]?.[n]||[]}},r=e=>{let t={};return e.forEach(e=>{if(t[e.aggregateType]||(t[e.aggregateType]={}),t[e.aggregateType][e.name])throw Error(`Duplicate command handler for ${e.aggregateType} and ${e.name}`);t[e.aggregateType][e.name]=e}),{transform:e=>{let n=t[e.aggregateType]?.[e.name];if(!n)throw Error(`No command handler found for ${e.aggregateType} and ${e.name}`);return n.transform(e)}}},i=e=>{let{EventStore:t,CommandHandlers:i,Projections:a,Reactions:o,onReactionError:s}=e,c=n(a),l=n(o),u=r(i);return{async dispatch(e,n){try{let r=u.transform(e);await t.append(r);let i=c.resolve(r.aggregateType,r.name);await Promise.all(i.map(e=>e.project(r))),l.resolve(r.aggregateType,r.name).forEach(e=>{e.on(r).catch(t=>{let n=t instanceof Error?t:Error(String(t));s&&s({reactionName:e.name,eventName:r.name,eventID:r._id,error:n})})}),n?.()}catch(e){let t=e instanceof Error?e:Error(String(e));throw n?.(t),t}}}},a=(...e)=>e.flatMap(e=>Object.values(e)),o=()=>e();export{i as createService,t as defineCommand,o as generateID,a as remap};
2
2
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/composeEventBuilder.ts","../src/common/Mapper.ts","../src/createService.ts","../src/util.ts"],"sourcesContent":["import { uuidv7 } from \"uuidv7\";\r\nimport { IEvent } from \"./core/IEvent\";\r\n\r\nexport const composeEventBuilder = <T extends IEvent>(definitions: {\r\n EventName: string;\r\n AggregateType: string;\r\n AggregateIdentifier?: () => string;\r\n EventIdentifier?: () => string;\r\n}) => {\r\n const {\r\n AggregateType,\r\n EventName,\r\n AggregateIdentifier = () => uuidv7(),\r\n EventIdentifier = () => uuidv7(),\r\n } = definitions;\r\n\r\n return ({\r\n aggregateID,\r\n payloads,\r\n _metadata,\r\n _version,\r\n _createdBy,\r\n ...others\r\n }: Omit<T, \"_id\" | \"name\" | \"aggregateID\" | \"aggregateType\" | \"_createdAt\" | \"_metadata\"> & {\r\n aggregateID?: string;\r\n _metadata?: T[\"_metadata\"];\r\n }): T => ({\r\n ...others,\r\n _id: EventIdentifier(),\r\n name: EventName,\r\n aggregateType: AggregateType,\r\n aggregateID: aggregateID ?? AggregateIdentifier(),\r\n payloads,\r\n _version,\r\n _createdBy,\r\n _createdAt: new Date(),\r\n _metadata: _metadata ?? ({} as T[\"_metadata\"]),\r\n } as T);\r\n};\r\n","// src/common/Mapper.ts\r\n\r\nexport const Mapper = <T extends { type: string; name: string; }>(): {\r\n register: (component: T) => void;\r\n resolve: (type: string, name: string) => T[];\r\n} => {\r\n const registry = new Map<string, T[]>();\r\n const toKey = (type: string, name: string): string => `${type}:${name}`;\r\n\r\n return {\r\n register: (component: T) => {\r\n const key = toKey(component.type, component.name);\r\n const others = registry.get(key) || [];\r\n registry.set(key, [...others, component]);\r\n },\r\n resolve: (type: string, name: string): T[] =>\r\n registry.get(toKey(type, name)) || [],\r\n };\r\n};\r\n","// src/createService.ts\r\n\r\nimport { IEventStore } from \"./core/IEventStore\";\r\nimport { IProjection } from \"./core/IProjection\";\r\nimport { IReaction } from \"./core/IReaction\";\r\nimport { IReactionError } from \"./core/IReactionError\";\r\nimport { IService } from \"./IService\";\r\nimport { Mapper } from \"./common/Mapper\";\r\n\r\nexport const createService = (definition: {\r\n EventStore: IEventStore;\r\n Projections: IProjection[];\r\n Reactions: IReaction[];\r\n onReactionError?: (context: IReactionError) => void;\r\n}): IService => {\r\n const { EventStore, Projections, Reactions, onReactionError } = definition;\r\n\r\n // Initialize Mappers inside the service to handle the internal grouping\r\n const ProjectionMapper = Mapper<IProjection>();\r\n const ReactionMapper = Mapper<IReaction>();\r\n\r\n // Register all provided components\r\n Projections.forEach((p) => ProjectionMapper.register(p));\r\n Reactions.forEach((r) => ReactionMapper.register(r));\r\n\r\n return {\r\n async dispatch(event, afterHandler): Promise<void> {\r\n try {\r\n await EventStore.append(event);\r\n\r\n const projections = ProjectionMapper.resolve(event.aggregateType, event.name);\r\n await Promise.all(projections.map((p) => p.project(event)));\r\n\r\n const reactions = ReactionMapper.resolve(event.aggregateType, event.name);\r\n reactions.forEach((r) => {\r\n r.on(event).catch((error) => {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n if (onReactionError) {\r\n onReactionError({\r\n reactionName: r.name,\r\n eventName: event.name,\r\n eventID: event._id,\r\n error: wrappedError,\r\n });\r\n }\r\n });\r\n });\r\n\r\n // Resolve successful dispatch immediately after state is sync'd\r\n afterHandler?.();\r\n } catch (error) {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n afterHandler?.(wrappedError);\r\n throw wrappedError;\r\n }\r\n },\r\n };\r\n};\r\n","import { uuidv7 } from \"uuidv7\";\r\n\r\nexport const remap = <T>(...objs: Record<string, T>[]): T[] =>\r\n objs.flatMap(obj => Object.values(obj));\r\n\r\nexport const generateID = (): string => uuidv7();\r\n"],"mappings":"gCAGA,MAAa,EAAyC,GAKhD,CACJ,GAAM,CACJ,gBACA,YACA,0BAA4B,GAAQ,CACpC,sBAAwB,GAAQ,EAC9B,EAEJ,OAAQ,CACN,cACA,WACA,YACA,WACA,aACA,GAAG,MAIK,CACR,GAAG,EACH,IAAK,GAAiB,CACtB,KAAM,EACN,cAAe,EACf,YAAa,GAAe,GAAqB,CACjD,WACA,WACA,aACA,WAAY,IAAI,KAChB,UAAW,GAAc,EAAE,CAC5B,GCnCU,MAGR,CACH,IAAM,EAAW,IAAI,IACf,GAAS,EAAc,IAAyB,GAAG,EAAK,GAAG,IAEjE,MAAO,CACL,SAAW,GAAiB,CAC1B,IAAM,EAAM,EAAM,EAAU,KAAM,EAAU,KAAK,CAC3C,EAAS,EAAS,IAAI,EAAI,EAAI,EAAE,CACtC,EAAS,IAAI,EAAK,CAAC,GAAG,EAAQ,EAAU,CAAC,EAE3C,SAAU,EAAc,IACtB,EAAS,IAAI,EAAM,EAAM,EAAK,CAAC,EAAI,EAAE,CACxC,ECRU,EAAiB,GAKd,CACd,GAAM,CAAE,aAAY,cAAa,YAAW,mBAAoB,EAG1D,EAAmB,GAAqB,CACxC,EAAiB,GAAmB,CAM1C,OAHA,EAAY,QAAS,GAAM,EAAiB,SAAS,EAAE,CAAC,CACxD,EAAU,QAAS,GAAM,EAAe,SAAS,EAAE,CAAC,CAE7C,CACL,MAAM,SAAS,EAAO,EAA6B,CACjD,GAAI,CACF,MAAM,EAAW,OAAO,EAAM,CAE9B,IAAM,EAAc,EAAiB,QAAQ,EAAM,cAAe,EAAM,KAAK,CAC7E,MAAM,QAAQ,IAAI,EAAY,IAAK,GAAM,EAAE,QAAQ,EAAM,CAAC,CAAC,CAEzC,EAAe,QAAQ,EAAM,cAAe,EAAM,KAC3D,CAAC,QAAS,GAAM,CACvB,EAAE,GAAG,EAAM,CAAC,MAAO,GAAU,CAC3B,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAC1E,GACF,EAAgB,CACd,aAAc,EAAE,KAChB,UAAW,EAAM,KACjB,QAAS,EAAM,IACf,MAAO,EACR,CAAC,EAEJ,EACF,CAGF,KAAgB,OACT,EAAO,CACd,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAE9E,MADA,IAAe,EAAa,CACtB,IAGX,ECtDU,GAAY,GAAG,IAC1B,EAAK,QAAQ,GAAO,OAAO,OAAO,EAAI,CAAC,CAE5B,MAA2B,GAAQ"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/defineCommand.ts","../src/common/composeMapper.ts","../src/common/composeCommandHandlers.ts","../src/createService.ts","../src/util.ts"],"sourcesContent":["import { uuidv7 } from \"uuidv7\";\r\nimport { ICommand } from \"./core/ICommand\";\r\nimport { IEvent } from \"./core/IEvent\";\r\n\r\nexport const defineCommand = <C extends ICommand, E extends IEvent>(definitions: {\r\n name: string;\r\n aggregateType: string;\r\n}) => {\r\n const { name, aggregateType } = definitions;\r\n\r\n const action = ({\r\n aggregateID,\r\n payloads,\r\n _version,\r\n _createdBy,\r\n _metadata,\r\n }: {\r\n aggregateID?: string;\r\n payloads: C[\"payloads\"];\r\n _version: C[\"_version\"];\r\n _createdBy: C[\"_createdBy\"];\r\n _metadata?: C[\"_metadata\"];\r\n }): C => ({\r\n _id: uuidv7(),\r\n name,\r\n aggregateID: aggregateID ?? uuidv7(),\r\n aggregateType,\r\n payloads,\r\n _version,\r\n _createdAt: new Date(),\r\n _createdBy,\r\n _metadata: _metadata ?? ({} as C[\"_metadata\"]),\r\n } as unknown as C);\r\n\r\n return {\r\n transform: (fn: (command: C) => E) => ({\r\n action,\r\n handler: {\r\n name,\r\n aggregateType,\r\n transform: fn,\r\n },\r\n })\r\n };\r\n};\r\n","// src/common/composeMapper.ts\r\n\r\n// A generic utility to compose Mappers for Projections and Reactions based on aggregateType and name\r\nexport const composeMapper = <T extends { aggregateType: string; name: string; }>(components: T[]): {\r\n resolve: (aggregateType: string, name: string) => T[];\r\n} => {\r\n const Registry = {} as Record<string, Record<string, T[]>>;\r\n\r\n components.forEach((component) => {\r\n const { aggregateType, name } = component;\r\n if (!Registry[aggregateType]) {\r\n Registry[aggregateType] = {};\r\n }\r\n\r\n if (!Registry[aggregateType][name]) {\r\n Registry[aggregateType][name] = [];\r\n }\r\n\r\n Registry[aggregateType][name].push(component);\r\n });\r\n\r\n return {\r\n // Resolve returns an array of components matching the aggregateType and name, or an empty array if none found\r\n resolve: (aggregateType: string, name: string): T[] =>\r\n Registry[aggregateType]?.[name] || [],\r\n };\r\n};\r\n","// src/common/composeCommandHandlers.ts\r\n\r\nimport { ICommand } from \"../core/ICommand\";\r\nimport { ICommandHandler } from \"../core/ICommandHandler\";\r\n\r\nexport const composeCommandHandlers = (handlers: ICommandHandler[]) => {\r\n const Registry = {} as Record<string, Record<string, ICommandHandler>>;\r\n\r\n handlers.forEach(handler => {\r\n if (!Registry[handler.aggregateType]) {\r\n Registry[handler.aggregateType] = {};\r\n }\r\n\r\n if (Registry[handler.aggregateType][handler.name]) {\r\n throw new Error(`Duplicate command handler for ${handler.aggregateType} and ${handler.name}`);\r\n }\r\n\r\n Registry[handler.aggregateType][handler.name] = handler;\r\n });\r\n\r\n return {\r\n transform: (command: ICommand) => {\r\n const handler = Registry[command.aggregateType]?.[command.name];\r\n\r\n if (!handler) {\r\n throw new Error(`No command handler found for ${command.aggregateType} and ${command.name}`);\r\n }\r\n\r\n return handler.transform(command);\r\n },\r\n };\r\n};\r\n","// src/createService.ts\r\n\r\nimport { composeMapper } from \"./common/composeMapper\";\r\nimport { composeCommandHandlers } from \"./common/composeCommandHandlers\";\r\nimport { IEventStore } from \"./core/IEventStore\";\r\nimport { ICommandHandler } from \"./core/ICommandHandler\";\r\nimport { IProjection } from \"./core/IProjection\";\r\nimport { IReaction } from \"./core/IReaction\";\r\nimport { IReactionError } from \"./core/IReactionError\";\r\nimport { IService } from \"./IService\";\r\n\r\nexport const createService = (definition: {\r\n EventStore: IEventStore;\r\n CommandHandlers: ICommandHandler[];\r\n Projections: IProjection[];\r\n Reactions: IReaction[];\r\n onReactionError?: (context: IReactionError) => void;\r\n}): IService => {\r\n const { EventStore, CommandHandlers, Projections, Reactions, onReactionError } = definition;\r\n\r\n // Initialize Mappers\r\n const _ProjectionMapper = composeMapper<IProjection>(Projections);\r\n const _ReactionMapper = composeMapper<IReaction>(Reactions);\r\n const _CommandHandler = composeCommandHandlers(CommandHandlers);\r\n\r\n return {\r\n async dispatch(command, afterHandler): Promise<void> {\r\n try {\r\n const event = _CommandHandler.transform(command);\r\n await EventStore.append(event);\r\n\r\n const projections = _ProjectionMapper.resolve(event.aggregateType, event.name);\r\n await Promise.all(projections.map((p) => p.project(event)));\r\n\r\n const reactions = _ReactionMapper.resolve(event.aggregateType, event.name);\r\n reactions.forEach((r) => {\r\n r.on(event).catch((error) => {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n if (onReactionError) {\r\n onReactionError({\r\n reactionName: r.name,\r\n eventName: event.name,\r\n eventID: event._id,\r\n error: wrappedError,\r\n });\r\n }\r\n });\r\n });\r\n\r\n // Resolve successful dispatch immediately after state is sync'd\r\n afterHandler?.();\r\n } catch (error) {\r\n const wrappedError = error instanceof Error ? error : new Error(String(error));\r\n afterHandler?.(wrappedError);\r\n throw wrappedError;\r\n }\r\n },\r\n };\r\n};\r\n","import { uuidv7 } from \"uuidv7\";\r\n\r\nexport const remap = <T>(...objs: Record<string, T>[]): T[] =>\r\n objs.flatMap(obj => Object.values(obj));\r\n\r\nexport const generateID = (): string => uuidv7();\r\n"],"mappings":"gCAIA,MAAa,EAAuD,GAG9D,CACJ,GAAM,CAAE,OAAM,iBAAkB,EAE1B,GAAU,CACd,cACA,WACA,WACA,aACA,gBAOQ,CACR,IAAK,GAAQ,CACb,OACA,YAAa,GAAe,GAAQ,CACpC,gBACA,WACA,WACA,WAAY,IAAI,KAChB,aACA,UAAW,GAAc,EAAE,CAC5B,EAED,MAAO,CACL,UAAY,IAA2B,CACrC,SACA,QAAS,CACP,OACA,gBACA,UAAW,EACZ,CACF,EACF,ECxCU,EAAqE,GAE7E,CACH,IAAM,EAAW,EAAE,CAenB,OAbA,EAAW,QAAS,GAAc,CAChC,GAAM,CAAE,gBAAe,QAAS,EAC3B,EAAS,KACZ,EAAS,GAAiB,EAAE,EAGzB,EAAS,GAAe,KAC3B,EAAS,GAAe,GAAQ,EAAE,EAGpC,EAAS,GAAe,GAAM,KAAK,EAAU,EAC7C,CAEK,CAEL,SAAU,EAAuB,IAC/B,EAAS,KAAiB,IAAS,EAAE,CACxC,ECpBU,EAA0B,GAAgC,CACrE,IAAM,EAAW,EAAE,CAcnB,OAZA,EAAS,QAAQ,GAAW,CAK1B,GAJK,EAAS,EAAQ,iBACpB,EAAS,EAAQ,eAAiB,EAAE,EAGlC,EAAS,EAAQ,eAAe,EAAQ,MAC1C,MAAU,MAAM,iCAAiC,EAAQ,cAAc,OAAO,EAAQ,OAAO,CAG/F,EAAS,EAAQ,eAAe,EAAQ,MAAQ,GAChD,CAEK,CACL,UAAY,GAAsB,CAChC,IAAM,EAAU,EAAS,EAAQ,iBAAiB,EAAQ,MAE1D,GAAI,CAAC,EACH,MAAU,MAAM,gCAAgC,EAAQ,cAAc,OAAO,EAAQ,OAAO,CAG9F,OAAO,EAAQ,UAAU,EAAQ,EAEpC,ECnBU,EAAiB,GAMd,CACd,GAAM,CAAE,aAAY,kBAAiB,cAAa,YAAW,mBAAoB,EAG3E,EAAoB,EAA2B,EAAY,CAC3D,EAAkB,EAAyB,EAAU,CACrD,EAAkB,EAAuB,EAAgB,CAE/D,MAAO,CACL,MAAM,SAAS,EAAS,EAA6B,CACnD,GAAI,CACF,IAAM,EAAQ,EAAgB,UAAU,EAAQ,CAChD,MAAM,EAAW,OAAO,EAAM,CAE9B,IAAM,EAAc,EAAkB,QAAQ,EAAM,cAAe,EAAM,KAAK,CAC9E,MAAM,QAAQ,IAAI,EAAY,IAAK,GAAM,EAAE,QAAQ,EAAM,CAAC,CAAC,CAEzC,EAAgB,QAAQ,EAAM,cAAe,EAAM,KAC5D,CAAC,QAAS,GAAM,CACvB,EAAE,GAAG,EAAM,CAAC,MAAO,GAAU,CAC3B,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAC1E,GACF,EAAgB,CACd,aAAc,EAAE,KAChB,UAAW,EAAM,KACjB,QAAS,EAAM,IACf,MAAO,EACR,CAAC,EAEJ,EACF,CAGF,KAAgB,OACT,EAAO,CACd,IAAM,EAAe,aAAiB,MAAQ,EAAY,MAAM,OAAO,EAAM,CAAC,CAE9E,MADA,IAAe,EAAa,CACtB,IAGX,ECvDU,GAAY,GAAG,IAC1B,EAAK,QAAQ,GAAO,OAAO,OAAO,EAAI,CAAC,CAE5B,MAA2B,GAAQ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atomservices",
3
- "version": "0.14.2",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",