atomservices 0.14.1 → 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=e=>{let{EventStore:t,Projections:n,Reactions:r,onReactionError:i}=e,a=n.reduce((e,t)=>{let n=e[t.name]||[];return n.push(t),e[t.name]=n,e},{}),o=r.reduce((e,t)=>{let n=e[t.name]||[];return n.push(t),e[t.name]=n,e},{});return{async dispatch(e,n){try{await t.append(e);let r=a[e.name]||[];await Promise.all(r.map(t=>t.project(e))),(o[e.name]||[]).forEach(t=>{t.on(e).catch(n=>{let r=n instanceof Error?n:Error(String(n));i&&i({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}}}},r=(...e)=>e.flatMap(e=>Object.values(e)),i=()=>(0,e.uuidv7)();exports.composeEventBuilder=t,exports.createService=n,exports.generateID=i,exports.remap=r;
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/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/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\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 // Group Projections by Event Name (Supports One-to-Many)\r\n const PROJECTION_MAP = Projections.reduce((result, projection) => {\r\n const list = result[projection.name] || [];\r\n list.push(projection);\r\n result[projection.name] = list;\r\n\r\n return result;\r\n }, {} as Record<string, IProjection[]>);\r\n\r\n const REACTION_MAP = Reactions.reduce((result, reaction) => {\r\n const list = result[reaction.name] || [];\r\n list.push(reaction);\r\n result[reaction.name] = list;\r\n\r\n return result;\r\n }, {} as Record<string, IReaction[]>);\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 = PROJECTION_MAP[event.name] || [];\r\n await Promise.all(projections.map((p) => p.project(event)));\r\n\r\n const reactions = REACTION_MAP[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,GC7BU,EAAiB,GAKd,CACd,GAAM,CAAE,aAAY,cAAa,YAAW,mBAAoB,EAG1D,EAAiB,EAAY,QAAQ,EAAQ,IAAe,CAChE,IAAM,EAAO,EAAO,EAAW,OAAS,EAAE,CAI1C,OAHA,EAAK,KAAK,EAAW,CACrB,EAAO,EAAW,MAAQ,EAEnB,GACN,EAAE,CAAkC,CAEjC,EAAe,EAAU,QAAQ,EAAQ,IAAa,CAC1D,IAAM,EAAO,EAAO,EAAS,OAAS,EAAE,CAIxC,OAHA,EAAK,KAAK,EAAS,CACnB,EAAO,EAAS,MAAQ,EAEjB,GACN,EAAE,CAAgC,CAErC,MAAO,CACL,MAAM,SAAS,EAAO,EAA6B,CACjD,GAAI,CACF,MAAM,EAAW,OAAO,EAAM,CAE9B,IAAM,EAAc,EAAe,EAAM,OAAS,EAAE,CACpD,MAAM,QAAQ,IAAI,EAAY,IAAK,GAAM,EAAE,QAAQ,EAAM,CAAC,CAAC,EAEzC,EAAa,EAAM,OAAS,EAAE,EACtC,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,EC9DU,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,9 +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> {
45
+ interface IProjection<Event extends IEvent = IEvent> {
26
46
  name: string;
27
- project: (event: E) => Promise<void>;
47
+ aggregateType: string;
48
+ project: (event: Event) => Promise<void>;
28
49
  }
29
50
  //#endregion
30
51
  //#region src/core/IReaction.d.ts
@@ -33,9 +54,10 @@ interface IProjection<E extends IEvent = IEvent> {
33
54
  * Reactions are asynchronous, non-blocking listeners to events.
34
55
  * They do NOT update state directly; they trigger external actions or new events.
35
56
  */
36
- interface IReaction<E extends IEvent = IEvent> {
57
+ interface IReaction<Event extends IEvent = IEvent> {
37
58
  name: string;
38
- on: (event: E) => Promise<void>;
59
+ aggregateType: string;
60
+ on: (event: Event) => Promise<void>;
39
61
  }
40
62
  //#endregion
41
63
  //#region src/core/IReactionError.d.ts
@@ -52,42 +74,57 @@ interface IReactionError {
52
74
  * Must calculate the new state immediately without side effects.
53
75
  * Used for high-performance Aggregate Rehydration.
54
76
  */
55
- interface IReducer<T> {
56
- (state: T, event: IEvent): T;
77
+ interface IReducer<State> {
78
+ (state: State, event: IEvent): State;
57
79
  }
58
80
  //#endregion
59
81
  //#region src/IService.d.ts
60
82
  interface IService {
61
83
  /**
62
- * Persists an event to the Store and triggers the handler in the background.
63
- * * @param event The event to dispatch.
64
- * @param afterHandler Optional callback triggered after background processing is complete.
65
- * @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.
66
93
  */
67
- dispatch: (event: IEvent, afterHandler?: (error?: Error) => void) => Promise<void>;
94
+ dispatch: (command: ICommand, afterHandler?: (error?: Error) => void) => Promise<void>;
68
95
  }
69
96
  //#endregion
70
- //#region src/composeEventBuilder.d.ts
71
- declare const composeEventBuilder: <T extends IEvent>(definitions: {
72
- EventName: string;
73
- AggregateType: string;
74
- AggregateIdentifier?: () => string;
75
- EventIdentifier?: () => string;
76
- }) => ({
77
- aggregateID,
78
- payloads,
79
- _metadata,
80
- _version,
81
- _createdBy,
82
- ...others
83
- }: Omit<T, "_id" | "name" | "aggregateID" | "aggregateType" | "_createdAt" | "_metadata"> & {
84
- aggregateID?: string;
85
- _metadata?: T["_metadata"];
86
- }) => 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
+ };
87
123
  //#endregion
88
124
  //#region src/createService.d.ts
89
125
  declare const createService: (definition: {
90
126
  EventStore: IEventStore;
127
+ CommandHandlers: ICommandHandler[];
91
128
  Projections: IProjection[];
92
129
  Reactions: IReaction[];
93
130
  onReactionError?: (context: IReactionError) => void;
@@ -97,5 +134,5 @@ declare const createService: (definition: {
97
134
  declare const remap: <T>(...objs: Record<string, T>[]) => T[];
98
135
  declare const generateID: () => string;
99
136
  //#endregion
100
- 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 };
101
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,OAAA,GAAU,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;AFTzB;;;;;AAAA,UGKiB,SAAA,WAAoB,MAAA,GAAS,MAAA;EAC5C,IAAA;EACA,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;UCTH,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;;;cClBO,aAAA,GAAiB,UAAA;EAC5B,UAAA,EAAY,WAAA;EACZ,WAAA,EAAa,WAAA;EACb,SAAA,EAAW,SAAA;EACX,eAAA,IAAmB,OAAA,EAAS,cAAA;AAAA,MAC1B,QAAA;;;cCXS,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,9 +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> {
45
+ interface IProjection<Event extends IEvent = IEvent> {
26
46
  name: string;
27
- project: (event: E) => Promise<void>;
47
+ aggregateType: string;
48
+ project: (event: Event) => Promise<void>;
28
49
  }
29
50
  //#endregion
30
51
  //#region src/core/IReaction.d.ts
@@ -33,9 +54,10 @@ interface IProjection<E extends IEvent = IEvent> {
33
54
  * Reactions are asynchronous, non-blocking listeners to events.
34
55
  * They do NOT update state directly; they trigger external actions or new events.
35
56
  */
36
- interface IReaction<E extends IEvent = IEvent> {
57
+ interface IReaction<Event extends IEvent = IEvent> {
37
58
  name: string;
38
- on: (event: E) => Promise<void>;
59
+ aggregateType: string;
60
+ on: (event: Event) => Promise<void>;
39
61
  }
40
62
  //#endregion
41
63
  //#region src/core/IReactionError.d.ts
@@ -52,42 +74,57 @@ interface IReactionError {
52
74
  * Must calculate the new state immediately without side effects.
53
75
  * Used for high-performance Aggregate Rehydration.
54
76
  */
55
- interface IReducer<T> {
56
- (state: T, event: IEvent): T;
77
+ interface IReducer<State> {
78
+ (state: State, event: IEvent): State;
57
79
  }
58
80
  //#endregion
59
81
  //#region src/IService.d.ts
60
82
  interface IService {
61
83
  /**
62
- * Persists an event to the Store and triggers the handler in the background.
63
- * * @param event The event to dispatch.
64
- * @param afterHandler Optional callback triggered after background processing is complete.
65
- * @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.
66
93
  */
67
- dispatch: (event: IEvent, afterHandler?: (error?: Error) => void) => Promise<void>;
94
+ dispatch: (command: ICommand, afterHandler?: (error?: Error) => void) => Promise<void>;
68
95
  }
69
96
  //#endregion
70
- //#region src/composeEventBuilder.d.ts
71
- declare const composeEventBuilder: <T extends IEvent>(definitions: {
72
- EventName: string;
73
- AggregateType: string;
74
- AggregateIdentifier?: () => string;
75
- EventIdentifier?: () => string;
76
- }) => ({
77
- aggregateID,
78
- payloads,
79
- _metadata,
80
- _version,
81
- _createdBy,
82
- ...others
83
- }: Omit<T, "_id" | "name" | "aggregateID" | "aggregateType" | "_createdAt" | "_metadata"> & {
84
- aggregateID?: string;
85
- _metadata?: T["_metadata"];
86
- }) => 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
+ };
87
123
  //#endregion
88
124
  //#region src/createService.d.ts
89
125
  declare const createService: (definition: {
90
126
  EventStore: IEventStore;
127
+ CommandHandlers: ICommandHandler[];
91
128
  Projections: IProjection[];
92
129
  Reactions: IReaction[];
93
130
  onReactionError?: (context: IReactionError) => void;
@@ -97,5 +134,5 @@ declare const createService: (definition: {
97
134
  declare const remap: <T>(...objs: Record<string, T>[]) => T[];
98
135
  declare const generateID: () => string;
99
136
  //#endregion
100
- 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 };
101
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,OAAA,GAAU,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;AFTzB;;;;;AAAA,UGKiB,SAAA,WAAoB,MAAA,GAAS,MAAA;EAC5C,IAAA;EACA,EAAA,GAAK,KAAA,EAAO,CAAA,KAAM,OAAA;AAAA;;;UCTH,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;;;cClBO,aAAA,GAAiB,UAAA;EAC5B,UAAA,EAAY,WAAA;EACZ,WAAA,EAAa,WAAA;EACb,SAAA,EAAW,SAAA;EACX,eAAA,IAAmB,OAAA,EAAS,cAAA;AAAA,MAC1B,QAAA;;;cCXS,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=e=>{let{EventStore:t,Projections:n,Reactions:r,onReactionError:i}=e,a=n.reduce((e,t)=>{let n=e[t.name]||[];return n.push(t),e[t.name]=n,e},{}),o=r.reduce((e,t)=>{let n=e[t.name]||[];return n.push(t),e[t.name]=n,e},{});return{async dispatch(e,n){try{await t.append(e);let r=a[e.name]||[];await Promise.all(r.map(t=>t.project(e))),(o[e.name]||[]).forEach(t=>{t.on(e).catch(n=>{let r=n instanceof Error?n:Error(String(n));i&&i({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}}}},r=(...e)=>e.flatMap(e=>Object.values(e)),i=()=>e();export{t as composeEventBuilder,n as createService,i as generateID,r 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/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/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\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 // Group Projections by Event Name (Supports One-to-Many)\r\n const PROJECTION_MAP = Projections.reduce((result, projection) => {\r\n const list = result[projection.name] || [];\r\n list.push(projection);\r\n result[projection.name] = list;\r\n\r\n return result;\r\n }, {} as Record<string, IProjection[]>);\r\n\r\n const REACTION_MAP = Reactions.reduce((result, reaction) => {\r\n const list = result[reaction.name] || [];\r\n list.push(reaction);\r\n result[reaction.name] = list;\r\n\r\n return result;\r\n }, {} as Record<string, IReaction[]>);\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 = PROJECTION_MAP[event.name] || [];\r\n await Promise.all(projections.map((p) => p.project(event)));\r\n\r\n const reactions = REACTION_MAP[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,GC7BU,EAAiB,GAKd,CACd,GAAM,CAAE,aAAY,cAAa,YAAW,mBAAoB,EAG1D,EAAiB,EAAY,QAAQ,EAAQ,IAAe,CAChE,IAAM,EAAO,EAAO,EAAW,OAAS,EAAE,CAI1C,OAHA,EAAK,KAAK,EAAW,CACrB,EAAO,EAAW,MAAQ,EAEnB,GACN,EAAE,CAAkC,CAEjC,EAAe,EAAU,QAAQ,EAAQ,IAAa,CAC1D,IAAM,EAAO,EAAO,EAAS,OAAS,EAAE,CAIxC,OAHA,EAAK,KAAK,EAAS,CACnB,EAAO,EAAS,MAAQ,EAEjB,GACN,EAAE,CAAgC,CAErC,MAAO,CACL,MAAM,SAAS,EAAO,EAA6B,CACjD,GAAI,CACF,MAAM,EAAW,OAAO,EAAM,CAE9B,IAAM,EAAc,EAAe,EAAM,OAAS,EAAE,CACpD,MAAM,QAAQ,IAAI,EAAY,IAAK,GAAM,EAAE,QAAQ,EAAM,CAAC,CAAC,EAEzC,EAAa,EAAM,OAAS,EAAE,EACtC,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,EC9DU,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.1",
3
+ "version": "0.15.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",