@robinmalfait/event-source 0.0.17 → 0.0.18

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/README.md CHANGED
@@ -49,7 +49,7 @@ function depositMoney(accountId: string, amount: number) {
49
49
  Aggregates are domain entities that emit events and rebuild their state from event history. They extend the base `Aggregate` class and define `apply` handlers for each event type.
50
50
 
51
51
  ```typescript
52
- import { Aggregate, abort } from '@robinmalfait/event-source'
52
+ import { Aggregate, type ApplyEvents, abort } from '@robinmalfait/event-source'
53
53
 
54
54
  class Account extends Aggregate {
55
55
  private owner: string = ''
@@ -70,7 +70,9 @@ class Account extends Aggregate {
70
70
  }
71
71
 
72
72
  // Apply handlers rebuild state from events
73
- apply = {
73
+ apply: ApplyEvents<
74
+ typeof accountOpened | typeof moneyDeposited | typeof accountClosed
75
+ > = {
74
76
  ACCOUNT_OPENED: (event) => {
75
77
  this.owner = event.payload.owner
76
78
  },
@@ -151,16 +153,28 @@ See the [`examples/mysql-event-store`](./examples/mysql-event-store) directory f
151
153
 
152
154
  ### Projectors
153
155
 
154
- Projectors build read models from events. They process events sequentially and maintain derived state:
156
+ Projectors build read models from events. They process events sequentially and maintain derived state.
157
+
158
+ **Lifecycle:**
159
+
160
+ - **Normal operation:** When events are persisted via `es.persist()`, each projector's `apply` handlers are called for the new events only.
161
+ - **Full rebuild:** When `es.resetProjections()` is called, `reset()` is called first (to clear existing state), then all events are replayed through `apply`.
162
+
163
+ This means projectors work well in serverless environments - projections are persisted to your database and don't need rebuilding on every cold start.
155
164
 
156
165
  ```typescript
157
- import { Projector, createEventMapper } from '@robinmalfait/event-source'
166
+ import { Projector, type ApplyEvents } from '@robinmalfait/event-source'
158
167
 
159
168
  class BalanceProjector extends Projector {
160
169
  name = 'balance-projector'
161
170
  private balances = new Map<string, number>()
162
171
 
163
- apply = createEventMapper({
172
+ async reset() {
173
+ // Called only during resetProjections() - clear state before full rebuild
174
+ this.balances.clear()
175
+ }
176
+
177
+ apply: ApplyEvents<typeof accountOpened | typeof moneyDeposited> = {
164
178
  ACCOUNT_OPENED: (event) => {
165
179
  this.balances.set(event.aggregateId, 0)
166
180
  },
@@ -168,15 +182,6 @@ class BalanceProjector extends Projector {
168
182
  let current = this.balances.get(event.aggregateId) ?? 0
169
183
  this.balances.set(event.aggregateId, current + event.payload.amount)
170
184
  },
171
- })
172
-
173
- async initializer() {
174
- // Load initial state if needed
175
- }
176
-
177
- async project(event: EventType) {
178
- // Called for each new event
179
- this.apply(event)
180
185
  }
181
186
 
182
187
  getBalance(accountId: string) {
package/dist/index.cjs CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";var L=Object.create;var h=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var _=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var R=(n,e)=>{for(var t in e)h(n,t,{get:e[t],enumerable:!0})},H=(n,e,t,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of I(e))!J.call(n,a)&&a!==t&&h(n,a,{get:()=>e[a],enumerable:!(r=D(e,a))||r.enumerable});return n};var B=(n,e,t)=>(t=n!=null?L(_(n)):{},H(e||!n||!n.__esModule?h(t,"default",{value:n,enumerable:!0}):t,n)),Q=n=>H(h({},"__esModule",{value:!0}),n);var W={};R(W,{Aggregate:()=>A,Command:()=>V,Event:()=>Y,EventSource:()=>E,Projector:()=>m,abort:()=>c,createEventMapper:()=>C,createTestEventStore:()=>U,objectToYaml:()=>f});module.exports=Q(W);function M(n,e){let t=Object.assign(new Error(n),e);return t.stack,Error.captureStackTrace&&Error.captureStackTrace(t,M),t}function c(n,e){let t=M(n,e);throw Error.captureStackTrace&&Error.captureStackTrace(t,c),t}function P(n){Object.freeze(n);for(let e of Object.getOwnPropertyNames(n))n.hasOwnProperty(e)&&n[e]!==null&&(typeof n[e]=="object"||typeof n[e]=="function")&&!Object.isFrozen(n[e])&&P(n[e]);return n}var F={NODE_ENV:typeof process<"u"?process.env?.NODE_ENV:void 0},A=class{#e=0;#t=[];replayEvents(e=[]){for(let t of e)this.applyAnEvent(t);return this}applyAnEvent(e){F.NODE_ENV==="test"&&P(e);let t=this.apply[e.eventName];return t==null&&(e.eventName.match(/^[$A-Z_][0-9A-Z_$]*$/i)?c(`Aggregate "${this.constructor.name}" has no method:
1
+ "use strict";var z=Object.create;var h=Object.defineProperty;var D=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var _=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var R=(r,e)=>{for(var t in e)h(r,t,{get:e[t],enumerable:!0})},H=(r,e,t,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let a of I(e))!J.call(r,a)&&a!==t&&h(r,a,{get:()=>e[a],enumerable:!(n=D(e,a))||n.enumerable});return r};var B=(r,e,t)=>(t=r!=null?z(_(r)):{},H(e||!r||!r.__esModule?h(t,"default",{value:r,enumerable:!0}):t,r)),Q=r=>H(h({},"__esModule",{value:!0}),r);var W={};R(W,{Aggregate:()=>A,Command:()=>V,Event:()=>Y,EventSource:()=>E,Projector:()=>m,abort:()=>c,createEventMapper:()=>C,createTestEventStore:()=>U,objectToYaml:()=>f});module.exports=Q(W);function M(r,e){let t=Object.assign(new Error(r),e);return t.stack,Error.captureStackTrace&&Error.captureStackTrace(t,M),t}function c(r,e){let t=M(r,e);throw Error.captureStackTrace&&Error.captureStackTrace(t,c),t}function P(r){Object.freeze(r);for(let e of Object.getOwnPropertyNames(r))r.hasOwnProperty(e)&&r[e]!==null&&(typeof r[e]=="object"||typeof r[e]=="function")&&!Object.isFrozen(r[e])&&P(r[e]);return r}var F={NODE_ENV:typeof process<"u"?process.env?.NODE_ENV:void 0},A=class{#e=0;#t=[];replayEvents(e=[]){for(let t of e)this.applyAnEvent(t);return this}applyAnEvent(e){F.NODE_ENV==="test"&&P(e);let t=this.apply[e.eventName];return t==null&&(e.eventName.match(/^[$A-Z_][0-9A-Z_$]*$/i)?c(`Aggregate "${this.constructor.name}" has no method:
2
2
 
3
3
  apply = {
4
4
  ${e.eventName}(event) {
@@ -12,13 +12,13 @@ apply = {
12
12
  // Code goes here...
13
13
  }
14
14
  // ...
15
- }`)),t(e),this.#e++,this}recordThat(e){let t={...e,version:this.#e};return this.applyAnEvent(t),this.#t.push(t),this}releaseEvents(){return this.#t.splice(0)}};function V(n,e=null){return{type:n,payload:e}}function Y(n,e,t=null,r=null){return{aggregateId:e,eventId:globalThis.crypto.randomUUID(),eventName:n,payload:t,metadata:r,recordedAt:new Date,version:-1}}var k=B(require("yamlify-object"),1),q={indent:" ",colors:{date:v,error:v,symbol:v,string:v,number:v,boolean:v,null:v,undefined:v}};function v(n){return n}function f(n){return n instanceof Error?f({...n}):(0,k.default)(n,q).split(`
15
+ }`)),t(e),this.#e++,this}recordThat(e){let t={...e,version:this.#e};return this.applyAnEvent(t),this.#t.push(t),this}releaseEvents(){return this.#t.splice(0)}};function V(r,e=null){return{type:r,payload:e}}function Y(r,e,t=null,n=null){return{aggregateId:e,eventId:globalThis.crypto.randomUUID(),eventName:r,payload:t,metadata:n,recordedAt:new Date,version:-1}}var k=B(require("yamlify-object"),1),q={indent:" ",colors:{date:v,error:v,symbol:v,string:v,number:v,boolean:v,null:v,undefined:v}};function v(r){return r}function f(r){return r instanceof Error?f({...r}):(0,k.default)(r,q).split(`
16
16
  `).slice(1).join(`
17
- `)}var T=new WeakMap;function g(n,e){if(T.has(n)){let t=T.get(n);for(let r in e)t[r]=e[r]}else T.set(n,e)}function x(n){return T.get(n)}var b=class{constructor(){g(this,{jobs:[],state:0})}get length(){return x(this).jobs.length}async start(){let{state:e,jobs:t}=x(this);if(!(e===1||t.length<=0)){for(g(this,{state:1});t.length>0;){let r=t.shift();await Promise.resolve().then(r.handle).then(r.resolve,r.reject)}g(this,{state:0})}}push(e){return new Promise((t,r)=>{let{jobs:a}=x(this);a.push({handle:e,resolve:t,reject:r}),queueMicrotask(()=>this.start())})}};var m=class{apply;initializer(){}#e=new b;async init(e){this.initializer&&await this.#e.push(()=>this.initializer?.());for(let t of e)await this.applyEvent(t)}async applyEvent(e){await this.#e.push(()=>this.apply?.[e.eventName]?.(e)),await this.#e.push(()=>this.project(e))}project(e){}},E=class n{constructor(e,t,r,a,o){this.store=e;this.commandHandlers=t;this.projectors=r;this.eventHandlers=a;this.eventMetadataEnhancers=o}static builder(e){return new j(e)}static new(e,t,r,a,o){return new n(e,t,r,a,o)}async resetProjections(){let e=await this.store.loadEvents();await Promise.all(this.projectors.map(t=>t.init(e)))}async dispatch(e){return this.commandHandlers.has(e.type)||c(`There is no command handler for the "${e.type}" command`),await this.commandHandlers.get(e.type)(e,this),e}async loadEvents(){return this.store.loadEvents()}async load(e,t){let r=await this.store.load(t);return r.length<=0&&c(`Aggregate(${e.constructor.name}) with ID(${t}) does not exist.`,{aggregate:e.constructor.name,aggregateId:t}),e.replayEvents(r)}async persist(e){let t=e.releaseEvents();if(this.eventMetadataEnhancers.length>0){let r={};for(let a of this.eventMetadataEnhancers)Object.assign(r,await a());for(let a of t){let o=a.metadata??{};a.metadata=Object.assign({},r,o)}}await this.store.persist(t);for(let r of t)await Promise.all(this.projectors.map(async a=>{try{await a.applyEvent(r)}catch(o){throw o instanceof Error&&console.error(`An error occurred in one of your projections: ${a.name}, given an event`,o.stack?.split(`
17
+ `)}var T=new WeakMap;function g(r,e){if(T.has(r)){let t=T.get(r);for(let n in e)t[n]=e[n]}else T.set(r,e)}function x(r){return T.get(r)}var b=class{constructor(){g(this,{jobs:[],state:0})}get length(){return x(this).jobs.length}async start(){let{state:e,jobs:t}=x(this);if(!(e===1||t.length<=0)){for(g(this,{state:1});t.length>0;){let n=t.shift();await Promise.resolve().then(n.handle).then(n.resolve,n.reject)}g(this,{state:0})}}push(e){return new Promise((t,n)=>{let{jobs:a}=x(this);a.push({handle:e,resolve:t,reject:n}),queueMicrotask(()=>this.start())})}};var m=class{apply;reset(){}#e=new b;async init(e){this.reset&&await this.#e.push(()=>this.reset?.());for(let t of e)await this.applyEvent(t)}async applyEvent(e){await this.#e.push(()=>this.apply?.[e.eventName]?.(e)),await this.#e.push(()=>this.project(e))}project(e){}},E=class r{constructor(e,t,n,a,o){this.store=e;this.commandHandlers=t;this.projectors=n;this.eventHandlers=a;this.eventMetadataEnhancers=o}static builder(e){return new j(e)}static new(e,t,n,a,o){return new r(e,t,n,a,o)}async resetProjections(){let e=await this.store.loadEvents();await Promise.all(this.projectors.map(t=>t.init(e)))}async dispatch(e){return this.commandHandlers.has(e.type)||c(`There is no command handler for the "${e.type}" command`),await this.commandHandlers.get(e.type)(e,this),e}async loadEvents(){return this.store.loadEvents()}async load(e,t){let n=await this.store.load(t);return n.length<=0&&c(`Aggregate(${e.constructor.name}) with ID(${t}) does not exist.`,{aggregate:e.constructor.name,aggregateId:t}),e.replayEvents(n)}async persist(e){let t=e.releaseEvents();if(this.eventMetadataEnhancers.length>0){let n={};for(let a of this.eventMetadataEnhancers)Object.assign(n,await a());for(let a of t){let o=a.metadata??{};a.metadata=Object.assign({},n,o)}}await this.store.persist(t);for(let n of t)await Promise.all(this.projectors.map(async a=>{try{await a.applyEvent(n)}catch(o){throw o instanceof Error&&console.error(`An error occurred in one of your projections: ${a.name}, given an event`,o.stack?.split(`
18
18
  `).map(y=>` ${y}`).join(`
19
- `)),o}})),await Promise.all(this.eventHandlers.map(async a=>{try{await a(r,this)}catch(o){throw o instanceof Error&&console.error(`An error occurred in one of your event handlers: ${a.name}, given an event`,o.stack?.split(`
19
+ `)),o}})),await Promise.all(this.eventHandlers.map(async a=>{try{await a(n,this)}catch(o){throw o instanceof Error&&console.error(`An error occurred in one of your event handlers: ${a.name}, given an event`,o.stack?.split(`
20
20
  `).map(y=>` ${y}`).join(`
21
- `)),o}}))}async loadPersist(e,t,r){return await this.load(e,t),await r(e),this.persist(e)}},j=class{constructor(e){this.store=e}commandHandlers=new Map;projectors=[];eventHandlers=[];eventMetadataEnhancers=[];build(){return E.new(this.store,this.commandHandlers,this.projectors,this.eventHandlers,this.eventMetadataEnhancers)}addCommandHandler(e,t){return this.commandHandlers.has(e)&&c(`A command handler for the "${e}" command already exists`),this.commandHandlers.set(e,t),this}addProjector(e){return this.projectors.push(e),this}addEventHandler(e){return this.eventHandlers.push(e),this}metadata(e){return this.eventMetadataEnhancers.push(e),this}};function C(n){return(e,t)=>n[e.eventName]?.(e,t)}var S=Symbol("__placeholder__");function z(n,e){try{return n()}catch(t){throw Error.captureStackTrace&&t instanceof Error&&Error.captureStackTrace(t,e),t}}var N=class extends m{constructor(t=[],r=[]){super();this.db=t;this.producedEvents=r}apply={};name="test-recording-projector";initializer(){this.db.splice(0)}project(t){this.producedEvents.push(t)}};function U(n,e=[]){let t=new N,r=E.builder({load(s){return t.db.filter(i=>i.aggregateId===s)},loadEvents(){return t.db},persist(s){t.db.push(...s)}});for(let s of e)r.addProjector(s);r.addProjector(t);for(let[s,i]of Object.entries(n))r.addCommandHandler(s,i);let a=r.build(),o,y={___:S,async given(s=[]){t.db.push(...s)},async when(s){try{return await a.dispatch(typeof s=="function"?s():s)}catch(i){return i instanceof Error&&(o=i),i}},async then(s){if(s instanceof Error){let u=s;z(()=>{if(o?.message!==u?.message)throw new Error(`Expected error message to be:
21
+ `)),o}}))}async loadPersist(e,t,n){return await this.load(e,t),await n(e),this.persist(e)}},j=class{constructor(e){this.store=e}commandHandlers=new Map;projectors=[];eventHandlers=[];eventMetadataEnhancers=[];build(){return E.new(this.store,this.commandHandlers,this.projectors,this.eventHandlers,this.eventMetadataEnhancers)}addCommandHandler(e,t){return this.commandHandlers.has(e)&&c(`A command handler for the "${e}" command already exists`),this.commandHandlers.set(e,t),this}addProjector(e){return this.projectors.push(e),this}addEventHandler(e){return this.eventHandlers.push(e),this}metadata(e){return this.eventMetadataEnhancers.push(e),this}};function C(r){return(e,t)=>r[e.eventName]?.(e,t)}var S=Symbol("__placeholder__");function L(r,e){try{return r()}catch(t){throw Error.captureStackTrace&&t instanceof Error&&Error.captureStackTrace(t,e),t}}var N=class extends m{constructor(t=[],n=[]){super();this.db=t;this.producedEvents=n}apply={};name="test-recording-projector";reset(){this.db.splice(0)}project(t){this.producedEvents.push(t)}};function U(r,e=[]){let t=new N,n=E.builder({load(s){return t.db.filter(i=>i.aggregateId===s)},loadEvents(){return t.db},persist(s){t.db.push(...s)}});for(let s of e)n.addProjector(s);n.addProjector(t);for(let[s,i]of Object.entries(r))n.addCommandHandler(s,i);let a=n.build(),o,y={___:S,async given(s=[]){t.db.push(...s)},async when(s){try{return await a.dispatch(typeof s=="function"?s():s)}catch(i){return i instanceof Error&&(o=i),i}},async then(s){if(s instanceof Error){let u=s;L(()=>{if(o?.message!==u?.message)throw new Error(`Expected error message to be:
22
22
 
23
23
  ${u.message}
24
24
 
@@ -30,4 +30,4 @@ ${f(o)}
30
30
  ---
31
31
 
32
32
  `].join(`
33
- `)),o;z(()=>{if(i.length!==t.producedEvents.length)throw new Error(`Expected ${i.length} events, but got ${t.producedEvents.length} events.`);for(let[u,p]of i.entries()){let{aggregateId:$,eventName:O,payload:l}=t.producedEvents[u];if(p.aggregateId===S)throw new Error("Expected an `aggregateId`, but got `___` instead.");if(p.aggregateId!==$)throw new Error(`Expected aggregateId to be ${p.aggregateId}, but got ${$}.`);if(p.eventName!==O)throw new Error(`Expected eventName to be ${p.eventName}, but got ${O}.`);if((p.payload===null||p.payload===void 0)&&JSON.stringify(p.payload)!==JSON.stringify(l))throw new Error(`Expected payload to be ${JSON.stringify(p.payload)}, but got ${JSON.stringify(l)}.`);for(let d in p.payload){let w=p.payload[d];if(w===S){if(!(d in l))throw new Error(`Expected payload to have property ${d}, but it does not.`);if(l[d]===null||l[d]===void 0)throw new Error(`Expected payload to have property ${d}, but it is ${l[d]}.`)}else if(l[d]!==w)throw new Error(`Expected payload.${d} to be ${w}, but got ${l[d]}.`)}}},y.then)}};return y}0&&(module.exports={Aggregate,Command,Event,EventSource,Projector,abort,createEventMapper,createTestEventStore,objectToYaml});
33
+ `)),o;L(()=>{if(i.length!==t.producedEvents.length)throw new Error(`Expected ${i.length} events, but got ${t.producedEvents.length} events.`);for(let[u,p]of i.entries()){let{aggregateId:$,eventName:O,payload:l}=t.producedEvents[u];if(p.aggregateId===S)throw new Error("Expected an `aggregateId`, but got `___` instead.");if(p.aggregateId!==$)throw new Error(`Expected aggregateId to be ${p.aggregateId}, but got ${$}.`);if(p.eventName!==O)throw new Error(`Expected eventName to be ${p.eventName}, but got ${O}.`);if((p.payload===null||p.payload===void 0)&&JSON.stringify(p.payload)!==JSON.stringify(l))throw new Error(`Expected payload to be ${JSON.stringify(p.payload)}, but got ${JSON.stringify(l)}.`);for(let d in p.payload){let w=p.payload[d];if(w===S){if(!(d in l))throw new Error(`Expected payload to have property ${d}, but it does not.`);if(l[d]===null||l[d]===void 0)throw new Error(`Expected payload to have property ${d}, but it is ${l[d]}.`)}else if(l[d]!==w)throw new Error(`Expected payload.${d} to be ${w}, but got ${l[d]}.`)}}},y.then)}};return y}0&&(module.exports={Aggregate,Command,Event,EventSource,Projector,abort,createEventMapper,createTestEventStore,objectToYaml});
package/dist/index.d.cts CHANGED
@@ -87,12 +87,12 @@ declare abstract class Projector<T extends ApplyProjectorEvents<any> = any> {
87
87
  */
88
88
  apply?: T;
89
89
  /**
90
- * Initialize the projector.
90
+ * Reset the projector state.
91
91
  *
92
- * Executed before loading all existing events to get the projection up to
93
- * date.
92
+ * Called before replaying all events during a full rebuild. Use this to
93
+ * clear any existing projection data (e.g., truncate tables).
94
94
  */
95
- initializer?(): void | Promise<void>;
95
+ reset?(): void | Promise<void>;
96
96
  init(events: EventType[]): Promise<void>;
97
97
  applyEvent(event: EventType): Promise<void>;
98
98
  project(event: EventType): void | Promise<void>;
package/dist/index.d.ts CHANGED
@@ -87,12 +87,12 @@ declare abstract class Projector<T extends ApplyProjectorEvents<any> = any> {
87
87
  */
88
88
  apply?: T;
89
89
  /**
90
- * Initialize the projector.
90
+ * Reset the projector state.
91
91
  *
92
- * Executed before loading all existing events to get the projection up to
93
- * date.
92
+ * Called before replaying all events during a full rebuild. Use this to
93
+ * clear any existing projection data (e.g., truncate tables).
94
94
  */
95
- initializer?(): void | Promise<void>;
95
+ reset?(): void | Promise<void>;
96
96
  init(events: EventType[]): Promise<void>;
97
97
  applyEvent(event: EventType): Promise<void>;
98
98
  project(event: EventType): void | Promise<void>;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- function b(n,e){let t=Object.assign(new Error(n),e);return t.stack,Error.captureStackTrace&&Error.captureStackTrace(t,b),t}function l(n,e){let t=b(n,e);throw Error.captureStackTrace&&Error.captureStackTrace(t,l),t}function w(n){Object.freeze(n);for(let e of Object.getOwnPropertyNames(n))n.hasOwnProperty(e)&&n[e]!==null&&(typeof n[e]=="object"||typeof n[e]=="function")&&!Object.isFrozen(n[e])&&w(n[e]);return n}var H={NODE_ENV:typeof process<"u"?process.env?.NODE_ENV:void 0},$=class{#e=0;#t=[];replayEvents(e=[]){for(let t of e)this.applyAnEvent(t);return this}applyAnEvent(e){H.NODE_ENV==="test"&&w(e);let t=this.apply[e.eventName];return t==null&&(e.eventName.match(/^[$A-Z_][0-9A-Z_$]*$/i)?l(`Aggregate "${this.constructor.name}" has no method:
1
+ function b(r,e){let t=Object.assign(new Error(r),e);return t.stack,Error.captureStackTrace&&Error.captureStackTrace(t,b),t}function l(r,e){let t=b(r,e);throw Error.captureStackTrace&&Error.captureStackTrace(t,l),t}function w(r){Object.freeze(r);for(let e of Object.getOwnPropertyNames(r))r.hasOwnProperty(e)&&r[e]!==null&&(typeof r[e]=="object"||typeof r[e]=="function")&&!Object.isFrozen(r[e])&&w(r[e]);return r}var H={NODE_ENV:typeof process<"u"?process.env?.NODE_ENV:void 0},$=class{#e=0;#t=[];replayEvents(e=[]){for(let t of e)this.applyAnEvent(t);return this}applyAnEvent(e){H.NODE_ENV==="test"&&w(e);let t=this.apply[e.eventName];return t==null&&(e.eventName.match(/^[$A-Z_][0-9A-Z_$]*$/i)?l(`Aggregate "${this.constructor.name}" has no method:
2
2
 
3
3
  apply = {
4
4
  ${e.eventName}(event) {
@@ -12,13 +12,13 @@ apply = {
12
12
  // Code goes here...
13
13
  }
14
14
  // ...
15
- }`)),t(e),this.#e++,this}recordThat(e){let t={...e,version:this.#e};return this.applyAnEvent(t),this.#t.push(t),this}releaseEvents(){return this.#t.splice(0)}};function Q(n,e=null){return{type:n,payload:e}}function V(n,e,t=null,r=null){return{aggregateId:e,eventId:globalThis.crypto.randomUUID(),eventName:n,payload:t,metadata:r,recordedAt:new Date,version:-1}}import k from"yamlify-object";var C={indent:" ",colors:{date:v,error:v,symbol:v,string:v,number:v,boolean:v,null:v,undefined:v}};function v(n){return n}function M(n){return n instanceof Error?M({...n}):k(n,C).split(`
15
+ }`)),t(e),this.#e++,this}recordThat(e){let t={...e,version:this.#e};return this.applyAnEvent(t),this.#t.push(t),this}releaseEvents(){return this.#t.splice(0)}};function Q(r,e=null){return{type:r,payload:e}}function V(r,e,t=null,n=null){return{aggregateId:e,eventId:globalThis.crypto.randomUUID(),eventName:r,payload:t,metadata:n,recordedAt:new Date,version:-1}}import k from"yamlify-object";var C={indent:" ",colors:{date:v,error:v,symbol:v,string:v,number:v,boolean:v,null:v,undefined:v}};function v(r){return r}function M(r){return r instanceof Error?M({...r}):k(r,C).split(`
16
16
  `).slice(1).join(`
17
- `)}var u=new WeakMap;function h(n,e){if(u.has(n)){let t=u.get(n);for(let r in e)t[r]=e[r]}else u.set(n,e)}function f(n){return u.get(n)}var T=class{constructor(){h(this,{jobs:[],state:0})}get length(){return f(this).jobs.length}async start(){let{state:e,jobs:t}=f(this);if(!(e===1||t.length<=0)){for(h(this,{state:1});t.length>0;){let r=t.shift();await Promise.resolve().then(r.handle).then(r.resolve,r.reject)}h(this,{state:0})}}push(e){return new Promise((t,r)=>{let{jobs:o}=f(this);o.push({handle:e,resolve:t,reject:r}),queueMicrotask(()=>this.start())})}};var g=class{apply;initializer(){}#e=new T;async init(e){this.initializer&&await this.#e.push(()=>this.initializer?.());for(let t of e)await this.applyEvent(t)}async applyEvent(e){await this.#e.push(()=>this.apply?.[e.eventName]?.(e)),await this.#e.push(()=>this.project(e))}project(e){}},E=class n{constructor(e,t,r,o,a){this.store=e;this.commandHandlers=t;this.projectors=r;this.eventHandlers=o;this.eventMetadataEnhancers=a}static builder(e){return new P(e)}static new(e,t,r,o,a){return new n(e,t,r,o,a)}async resetProjections(){let e=await this.store.loadEvents();await Promise.all(this.projectors.map(t=>t.init(e)))}async dispatch(e){return this.commandHandlers.has(e.type)||l(`There is no command handler for the "${e.type}" command`),await this.commandHandlers.get(e.type)(e,this),e}async loadEvents(){return this.store.loadEvents()}async load(e,t){let r=await this.store.load(t);return r.length<=0&&l(`Aggregate(${e.constructor.name}) with ID(${t}) does not exist.`,{aggregate:e.constructor.name,aggregateId:t}),e.replayEvents(r)}async persist(e){let t=e.releaseEvents();if(this.eventMetadataEnhancers.length>0){let r={};for(let o of this.eventMetadataEnhancers)Object.assign(r,await o());for(let o of t){let a=o.metadata??{};o.metadata=Object.assign({},r,a)}}await this.store.persist(t);for(let r of t)await Promise.all(this.projectors.map(async o=>{try{await o.applyEvent(r)}catch(a){throw a instanceof Error&&console.error(`An error occurred in one of your projections: ${o.name}, given an event`,a.stack?.split(`
17
+ `)}var u=new WeakMap;function h(r,e){if(u.has(r)){let t=u.get(r);for(let n in e)t[n]=e[n]}else u.set(r,e)}function f(r){return u.get(r)}var T=class{constructor(){h(this,{jobs:[],state:0})}get length(){return f(this).jobs.length}async start(){let{state:e,jobs:t}=f(this);if(!(e===1||t.length<=0)){for(h(this,{state:1});t.length>0;){let n=t.shift();await Promise.resolve().then(n.handle).then(n.resolve,n.reject)}h(this,{state:0})}}push(e){return new Promise((t,n)=>{let{jobs:o}=f(this);o.push({handle:e,resolve:t,reject:n}),queueMicrotask(()=>this.start())})}};var g=class{apply;reset(){}#e=new T;async init(e){this.reset&&await this.#e.push(()=>this.reset?.());for(let t of e)await this.applyEvent(t)}async applyEvent(e){await this.#e.push(()=>this.apply?.[e.eventName]?.(e)),await this.#e.push(()=>this.project(e))}project(e){}},E=class r{constructor(e,t,n,o,a){this.store=e;this.commandHandlers=t;this.projectors=n;this.eventHandlers=o;this.eventMetadataEnhancers=a}static builder(e){return new P(e)}static new(e,t,n,o,a){return new r(e,t,n,o,a)}async resetProjections(){let e=await this.store.loadEvents();await Promise.all(this.projectors.map(t=>t.init(e)))}async dispatch(e){return this.commandHandlers.has(e.type)||l(`There is no command handler for the "${e.type}" command`),await this.commandHandlers.get(e.type)(e,this),e}async loadEvents(){return this.store.loadEvents()}async load(e,t){let n=await this.store.load(t);return n.length<=0&&l(`Aggregate(${e.constructor.name}) with ID(${t}) does not exist.`,{aggregate:e.constructor.name,aggregateId:t}),e.replayEvents(n)}async persist(e){let t=e.releaseEvents();if(this.eventMetadataEnhancers.length>0){let n={};for(let o of this.eventMetadataEnhancers)Object.assign(n,await o());for(let o of t){let a=o.metadata??{};o.metadata=Object.assign({},n,a)}}await this.store.persist(t);for(let n of t)await Promise.all(this.projectors.map(async o=>{try{await o.applyEvent(n)}catch(a){throw a instanceof Error&&console.error(`An error occurred in one of your projections: ${o.name}, given an event`,a.stack?.split(`
18
18
  `).map(y=>` ${y}`).join(`
19
- `)),a}})),await Promise.all(this.eventHandlers.map(async o=>{try{await o(r,this)}catch(a){throw a instanceof Error&&console.error(`An error occurred in one of your event handlers: ${o.name}, given an event`,a.stack?.split(`
19
+ `)),a}})),await Promise.all(this.eventHandlers.map(async o=>{try{await o(n,this)}catch(a){throw a instanceof Error&&console.error(`An error occurred in one of your event handlers: ${o.name}, given an event`,a.stack?.split(`
20
20
  `).map(y=>` ${y}`).join(`
21
- `)),a}}))}async loadPersist(e,t,r){return await this.load(e,t),await r(e),this.persist(e)}},P=class{constructor(e){this.store=e}commandHandlers=new Map;projectors=[];eventHandlers=[];eventMetadataEnhancers=[];build(){return E.new(this.store,this.commandHandlers,this.projectors,this.eventHandlers,this.eventMetadataEnhancers)}addCommandHandler(e,t){return this.commandHandlers.has(e)&&l(`A command handler for the "${e}" command already exists`),this.commandHandlers.set(e,t),this}addProjector(e){return this.projectors.push(e),this}addEventHandler(e){return this.eventHandlers.push(e),this}metadata(e){return this.eventMetadataEnhancers.push(e),this}};function z(n){return(e,t)=>n[e.eventName]?.(e,t)}var A=Symbol("__placeholder__");function O(n,e){try{return n()}catch(t){throw Error.captureStackTrace&&t instanceof Error&&Error.captureStackTrace(t,e),t}}var j=class extends g{constructor(t=[],r=[]){super();this.db=t;this.producedEvents=r}apply={};name="test-recording-projector";initializer(){this.db.splice(0)}project(t){this.producedEvents.push(t)}};function oe(n,e=[]){let t=new j,r=E.builder({load(s){return t.db.filter(i=>i.aggregateId===s)},loadEvents(){return t.db},persist(s){t.db.push(...s)}});for(let s of e)r.addProjector(s);r.addProjector(t);for(let[s,i]of Object.entries(n))r.addCommandHandler(s,i);let o=r.build(),a,y={___:A,async given(s=[]){t.db.push(...s)},async when(s){try{return await o.dispatch(typeof s=="function"?s():s)}catch(i){return i instanceof Error&&(a=i),i}},async then(s){if(s instanceof Error){let m=s;O(()=>{if(a?.message!==m?.message)throw new Error(`Expected error message to be:
21
+ `)),a}}))}async loadPersist(e,t,n){return await this.load(e,t),await n(e),this.persist(e)}},P=class{constructor(e){this.store=e}commandHandlers=new Map;projectors=[];eventHandlers=[];eventMetadataEnhancers=[];build(){return E.new(this.store,this.commandHandlers,this.projectors,this.eventHandlers,this.eventMetadataEnhancers)}addCommandHandler(e,t){return this.commandHandlers.has(e)&&l(`A command handler for the "${e}" command already exists`),this.commandHandlers.set(e,t),this}addProjector(e){return this.projectors.push(e),this}addEventHandler(e){return this.eventHandlers.push(e),this}metadata(e){return this.eventMetadataEnhancers.push(e),this}};function L(r){return(e,t)=>r[e.eventName]?.(e,t)}var A=Symbol("__placeholder__");function O(r,e){try{return r()}catch(t){throw Error.captureStackTrace&&t instanceof Error&&Error.captureStackTrace(t,e),t}}var j=class extends g{constructor(t=[],n=[]){super();this.db=t;this.producedEvents=n}apply={};name="test-recording-projector";reset(){this.db.splice(0)}project(t){this.producedEvents.push(t)}};function oe(r,e=[]){let t=new j,n=E.builder({load(s){return t.db.filter(i=>i.aggregateId===s)},loadEvents(){return t.db},persist(s){t.db.push(...s)}});for(let s of e)n.addProjector(s);n.addProjector(t);for(let[s,i]of Object.entries(r))n.addCommandHandler(s,i);let o=n.build(),a,y={___:A,async given(s=[]){t.db.push(...s)},async when(s){try{return await o.dispatch(typeof s=="function"?s():s)}catch(i){return i instanceof Error&&(a=i),i}},async then(s){if(s instanceof Error){let m=s;O(()=>{if(a?.message!==m?.message)throw new Error(`Expected error message to be:
22
22
 
23
23
  ${m.message}
24
24
 
@@ -30,4 +30,4 @@ ${M(a)}
30
30
  ---
31
31
 
32
32
  `].join(`
33
- `)),a;O(()=>{if(i.length!==t.producedEvents.length)throw new Error(`Expected ${i.length} events, but got ${t.producedEvents.length} events.`);for(let[m,p]of i.entries()){let{aggregateId:S,eventName:N,payload:c}=t.producedEvents[m];if(p.aggregateId===A)throw new Error("Expected an `aggregateId`, but got `___` instead.");if(p.aggregateId!==S)throw new Error(`Expected aggregateId to be ${p.aggregateId}, but got ${S}.`);if(p.eventName!==N)throw new Error(`Expected eventName to be ${p.eventName}, but got ${N}.`);if((p.payload===null||p.payload===void 0)&&JSON.stringify(p.payload)!==JSON.stringify(c))throw new Error(`Expected payload to be ${JSON.stringify(p.payload)}, but got ${JSON.stringify(c)}.`);for(let d in p.payload){let x=p.payload[d];if(x===A){if(!(d in c))throw new Error(`Expected payload to have property ${d}, but it does not.`);if(c[d]===null||c[d]===void 0)throw new Error(`Expected payload to have property ${d}, but it is ${c[d]}.`)}else if(c[d]!==x)throw new Error(`Expected payload.${d} to be ${x}, but got ${c[d]}.`)}}},y.then)}};return y}export{$ as Aggregate,Q as Command,V as Event,E as EventSource,g as Projector,l as abort,z as createEventMapper,oe as createTestEventStore,M as objectToYaml};
33
+ `)),a;O(()=>{if(i.length!==t.producedEvents.length)throw new Error(`Expected ${i.length} events, but got ${t.producedEvents.length} events.`);for(let[m,p]of i.entries()){let{aggregateId:S,eventName:N,payload:c}=t.producedEvents[m];if(p.aggregateId===A)throw new Error("Expected an `aggregateId`, but got `___` instead.");if(p.aggregateId!==S)throw new Error(`Expected aggregateId to be ${p.aggregateId}, but got ${S}.`);if(p.eventName!==N)throw new Error(`Expected eventName to be ${p.eventName}, but got ${N}.`);if((p.payload===null||p.payload===void 0)&&JSON.stringify(p.payload)!==JSON.stringify(c))throw new Error(`Expected payload to be ${JSON.stringify(p.payload)}, but got ${JSON.stringify(c)}.`);for(let d in p.payload){let x=p.payload[d];if(x===A){if(!(d in c))throw new Error(`Expected payload to have property ${d}, but it does not.`);if(c[d]===null||c[d]===void 0)throw new Error(`Expected payload to have property ${d}, but it is ${c[d]}.`)}else if(c[d]!==x)throw new Error(`Expected payload.${d} to be ${x}, but got ${c[d]}.`)}}},y.then)}};return y}export{$ as Aggregate,Q as Command,V as Event,E as EventSource,g as Projector,l as abort,L as createEventMapper,oe as createTestEventStore,M as objectToYaml};
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.17",
2
+ "version": "0.0.18",
3
3
  "name": "@robinmalfait/event-source",
4
4
  "repository": {
5
5
  "type": "git",