UNPKG

woltage

Version:

A CQRS and Event-Sourcing Framework

82 lines (81 loc) 3.08 kB
import { randomUUID } from 'crypto'; import { z } from 'zod/v4'; import { projectionStorage } from "./localStorages.js"; import { getEventClass } from "./eventMap.js"; class Event { static toString() { return this.name.replace(/\W+/g, ' ') .split(/ |\B(?=[A-Z])/) .map(word => word.toLowerCase()) .join('.'); } static get type() { return this.toString(); } static get identity() { return JSON.stringify({ type: this.type, version: this.version, }); } static validate(payload) { return this.schema.parse(payload); } static fromJSON(data, shouldValidate = false) { return new (getEventClass(data.type, data.version))(data, shouldValidate); } constructor(data, shouldValidate = true) { if (typeof this.version !== 'number' || this.version <= 0) throw new Error('Event\'s version property must be a number > 0.'); if (typeof this.constructor.schema?.parse !== 'function') throw new Error(`Missing schema for event class '${this.type}@${this.version}'.`); if ('type' in data && this.type !== data.type) throw new Error(`Event type does not match event data ("${this.type}" not equal "${data.type}").`); if ('version' in data && this.version !== data.version) throw new Error(`Event version does not match event data ("${this.version}" not equal "${data.version}").`); const { id = randomUUID(), timestamp = Date.now(), aggregateId, payload, correlationId, causationId, meta = {}, position = -1n } = data; this.id = id; this.timestamp = timestamp; this.aggregateId = aggregateId; this.payload = payload; const currentEvent = projectionStorage.getStore()?.currentEvent; this.correlationId = correlationId ?? currentEvent?.correlationId ?? id; this.causationId = causationId ?? currentEvent?.id ?? null; this.meta = meta; this.position = position; this.payload = shouldValidate ? this.constructor.validate(data.payload) : data.payload; } toString() { return this.constructor.toString(); } get type() { return this.toString(); } get version() { return this.constructor.version; } get identity() { return this.constructor.identity; } toJSON() { if (!this.aggregateId) throw new Error(`Invalid event state. Missing aggregate ID for '${this.type}@${this.version}' event.`); return { id: this.id, type: this.type, version: this.version, timestamp: this.timestamp, aggregateId: this.aggregateId, payload: structuredClone(this.payload), correlationId: this.correlationId, causationId: this.causationId, meta: this.meta, position: this.position }; } } Event.schema = z.any(); Event.version = -1; export default Event;