woltage
Version:
A CQRS and Event-Sourcing Framework
198 lines (197 loc) • 13 kB
JavaScript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var _Woltage_instances, _a, _Woltage_importModules, _Woltage_constructAggregateMap, _Woltage_constructProjectorMap, _Woltage_aggregateMap, _Woltage_projectorMap, _Woltage_readModelMap, _Woltage_store, _Woltage_getStore, _Woltage_projections, _Woltage_init, _Woltage_loadProjections, _Woltage_saveProjections, _Woltage_createProjection, _Woltage_execute;
import fs from 'node:fs/promises';
import path from 'node:path';
import Aggregate from "./write/Aggregate.js";
import EventStore from "./EventStore.js";
import Event from "./Event.js";
import { registerEventClasses } from "./eventMap.js";
import Projector from "./read/Projector.js";
import { z } from 'zod/v4';
import { createStore, createStoreFactory } from "./StoreFactory.js";
import Projection from "./read/Projection.js";
import ConflictError from "./errors/ConflictError.js";
import ProjectionMap from "./ProjectionMap.js";
import ReadModel from "./read/ReadModel.js";
import NotFoundError from "./errors/NotFoundError.js";
import { executionStorage } from "./localStorages.js";
const projectionConfigSchema = {
projections: {
key: z.object({
id: z.string()
}),
schema: z.object({
map: z.record(z.string(), z.object({
activeVersion: z.int().optional(),
versions: z.record(z.string(), z.object({
projectionId: z.string(),
name: z.string(),
version: z.int(),
projectorName: z.string(),
projectorVersion: z.int(),
storeName: z.string(),
}))
}))
})
}
};
class Woltage {
static async create(config) {
const instance = new this(config);
await __classPrivateFieldGet(instance, _Woltage_instances, "m", _Woltage_init).call(instance);
if (config.autostart !== false)
await instance.start();
return instance;
}
constructor(config) {
_Woltage_instances.add(this);
_Woltage_aggregateMap.set(this, {});
_Woltage_projectorMap.set(this, {});
_Woltage_readModelMap.set(this, {});
_Woltage_store.set(this, void 0);
_Woltage_getStore.set(this, void 0);
_Woltage_projections.set(this, void 0);
this.config = config;
__classPrivateFieldSet(this, _Woltage_store, createStore(this.config.internalStore, '_woltage_config'), "f");
__classPrivateFieldGet(this, _Woltage_store, "f").defineTables(projectionConfigSchema);
__classPrivateFieldSet(this, _Woltage_getStore, createStoreFactory(this.config.stores ?? {}), "f");
__classPrivateFieldSet(this, _Woltage_projections, new ProjectionMap(), "f");
}
async addProjection(projectionName, projectionVersion, projectorName, projectorVersion, storeName) {
const projection = await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_createProjection).call(this, projectionName, projectionVersion, projectorName, projectorVersion, storeName);
__classPrivateFieldGet(this, _Woltage_projections, "f").add(projection);
await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_saveProjections).call(this);
}
async setProjectionActive(projectionName, projectionVersion, force = false) {
__classPrivateFieldGet(this, _Woltage_projections, "f").setActive(projectionName, projectionVersion, force);
await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_saveProjections).call(this);
}
getProjections() {
return Object.fromEntries(__classPrivateFieldGet(this, _Woltage_projections, "f").idMap);
}
getProjection(projectionName, projectionVersion) {
return __classPrivateFieldGet(this, _Woltage_projections, "f").get(projectionName, projectionVersion);
}
async removeProjection(projectionName, projectionVersion, force = false) {
await __classPrivateFieldGet(this, _Woltage_projections, "f").remove(projectionName, projectionVersion, force);
await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_saveProjections).call(this);
}
async executeCommand(aggregateName, aggregateId, commandName, payload, context) {
const aggregate = __classPrivateFieldGet(this, _Woltage_aggregateMap, "f")[aggregateName];
if (!aggregate)
throw new NotFoundError(`Aggregate '${aggregateName}' not found.`);
await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_execute).call(this, () => aggregate.executeCommand(aggregateId, commandName, payload), context);
}
async executeQuery(readModelName, handlerName, query, context) {
if (typeof readModelName !== 'string')
readModelName = readModelName.toString();
const readModel = __classPrivateFieldGet(this, _Woltage_readModelMap, "f")[ReadModel.getName(readModelName)];
if (!readModel)
throw new NotFoundError(`Read model '${readModelName}' not found.`);
return await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_execute).call(this, () => readModel.call(handlerName, query), context);
}
async start() {
await EventStore.init(new this.config.eventStore.adapter(...(this.config.eventStore.args ?? [])));
await __classPrivateFieldGet(this, _Woltage_projections, "f").init();
}
async stop() {
await __classPrivateFieldGet(this, _Woltage_projections, "f").stop();
await EventStore.close();
}
}
_a = Woltage, _Woltage_aggregateMap = new WeakMap(), _Woltage_projectorMap = new WeakMap(), _Woltage_readModelMap = new WeakMap(), _Woltage_store = new WeakMap(), _Woltage_getStore = new WeakMap(), _Woltage_projections = new WeakMap(), _Woltage_instances = new WeakSet(), _Woltage_importModules = async function _Woltage_importModules(dirPath, filter) {
const modules = [];
await Promise.all((await fs.readdir(dirPath, { withFileTypes: true, recursive: true }))
.filter(dirent => dirent.isFile() && ['ts', 'js'].includes(dirent.name.split('.').pop() ?? ''))
.map(async (dirent) => {
const { default: module } = await import(path.join(dirent.parentPath, dirent.name));
if (filter(module))
modules.push(module);
}));
return modules;
}, _Woltage_constructAggregateMap = function _Woltage_constructAggregateMap(aggregates) {
return aggregates.reduce((map, aggregate) => {
if (map[aggregate.name])
throw new Error(`Duplicate aggregate found. Aggregate '${aggregate.name}' already exists.`);
map[aggregate.name] = aggregate;
return map;
}, {});
}, _Woltage_constructProjectorMap = function _Woltage_constructProjectorMap(projectorClasses) {
return projectorClasses.reduce((map, ProjectorClass) => {
map[ProjectorClass.name] ??= {};
if (map[ProjectorClass.name][ProjectorClass.version])
throw new Error(`Duplicate projector class found. Projector '${ProjectorClass.name}@${ProjectorClass.version}' already exists.`);
map[ProjectorClass.name][ProjectorClass.version] = ProjectorClass;
return map;
}, {});
}, _Woltage_init = async function _Woltage_init() {
registerEventClasses(typeof this.config.eventClasses === 'string'
? await __classPrivateFieldGet(_a, _a, "m", _Woltage_importModules).call(_a, this.config.eventClasses, module => module.prototype instanceof Event)
: this.config.eventClasses);
__classPrivateFieldSet(this, _Woltage_aggregateMap, __classPrivateFieldGet(_a, _a, "m", _Woltage_constructAggregateMap).call(_a, typeof this.config.aggregates === 'string'
? await __classPrivateFieldGet(_a, _a, "m", _Woltage_importModules).call(_a, this.config.aggregates, module => module instanceof Aggregate)
: this.config.aggregates), "f");
__classPrivateFieldSet(this, _Woltage_projectorMap, __classPrivateFieldGet(_a, _a, "m", _Woltage_constructProjectorMap).call(_a, typeof this.config.projectorClasses === 'string'
? await __classPrivateFieldGet(_a, _a, "m", _Woltage_importModules).call(_a, this.config.projectorClasses, module => module.prototype instanceof Projector)
: this.config.projectorClasses), "f");
__classPrivateFieldSet(this, _Woltage_readModelMap, Object.fromEntries((typeof this.config.readModelClasses === 'string'
? await __classPrivateFieldGet(_a, _a, "m", _Woltage_importModules).call(_a, this.config.readModelClasses, module => module.prototype instanceof ReadModel)
: this.config.readModelClasses ?? [])
.map(ReadModelClass => [ReadModelClass.toString(), new ReadModelClass()])), "f");
await __classPrivateFieldGet(this, _Woltage_store, "f").connect();
await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_loadProjections).call(this);
}, _Woltage_loadProjections = async function _Woltage_loadProjections() {
const projectionMap = (await __classPrivateFieldGet(this, _Woltage_store, "f").tables.projections.get({ id: 'status' }))?.map ?? {};
await Promise.all(Object.entries(projectionMap)
.map(async ([name, { activeVersion, versions }]) => {
await Promise.all(Object.entries(versions)
.map(async ([, { version, projectorName, projectorVersion, storeName }]) => __classPrivateFieldGet(this, _Woltage_projections, "f").add(await __classPrivateFieldGet(this, _Woltage_instances, "m", _Woltage_createProjection).call(this, name, version, projectorName, projectorVersion, storeName))));
if (activeVersion !== undefined)
__classPrivateFieldGet(this, _Woltage_projections, "f").setActive(name, activeVersion, true);
}));
}, _Woltage_saveProjections = async function _Woltage_saveProjections() {
const map = {};
for (const projection of __classPrivateFieldGet(this, _Woltage_projections, "f").idMap.values()) {
map[projection.name] ??= { activeVersion: undefined, versions: {} };
map[projection.name].versions[projection.version] = {
projectionId: projection.id,
name: projection.name,
version: projection.version,
projectorName: projection.projector.constructor.name,
projectorVersion: projection.projector.constructor.version,
storeName: projection.storeName ?? ''
};
}
for (const projection of __classPrivateFieldGet(this, _Woltage_projections, "f").activeProjectionMap.values())
map[projection.name].activeVersion = projection.version;
await __classPrivateFieldGet(this, _Woltage_store, "f").tables.projections.set({ id: 'status', map });
}, _Woltage_createProjection = async function _Woltage_createProjection(projectionName, projectionVersion, projectorName, projectorVersion, storeName) {
const projectionId = Projection.getId(projectionName, projectionVersion);
if (__classPrivateFieldGet(this, _Woltage_projections, "f").get(projectionId))
throw new ConflictError('Projection already exists');
const ProjectorClass = __classPrivateFieldGet(this, _Woltage_projectorMap, "f")[projectorName]?.[projectorVersion];
if (!ProjectorClass)
throw new NotFoundError(`Projector '${projectorName}@${projectorVersion}' not found`);
const store = __classPrivateFieldGet(this, _Woltage_getStore, "f").call(this, storeName, projectionId);
const projection = new Projection(projectionName, projectionVersion, ProjectorClass, store);
projection.storeName = storeName;
await store.connect();
return projection;
}, _Woltage_execute = async function _Woltage_execute(routine, context) {
return await executionStorage.run({
readModelMap: __classPrivateFieldGet(this, _Woltage_readModelMap, "f"),
projectionMap: new Map(__classPrivateFieldGet(this, _Woltage_projections, "f").activeProjectionMap),
context
}, routine);
};
export default Woltage.create.bind(Woltage);