typebus-cqrs
Version:
Simple, type-safe CQRS library with auto-registration and minimal boilerplate (typebus-cqrs)
842 lines (834 loc) • 31.1 kB
JavaScript
/**
* Factory class for creating message instances.
* Handles the creation of commands, queries, and events with proper typing.
*/
class MessageFactory {
/**
* Creates a command message.
* @template TCommandMap - Command map type
* @template T - Command type key
* @param {T} type - The command type.
* @param {CommandData<TCommandMap, T>} data - The command data.
* @param {string} aggregateId - The aggregate ID.
* @param {Record<string, any>} [metadata] - Optional metadata.
* @returns {ICommand<TCommandMap, T>} The created command.
*/
createCommand(type, data, aggregateId, metadata) {
return {
id: this.generateId(),
type,
timestamp: new Date(),
data,
aggregateId,
metadata
};
}
/**
* Creates a query message.
* @template TQueryMap - Query map type
* @template T - Query type key
* @param {T} type - The query type.
* @param {QueryParams<TQueryMap, T>} params - The query parameters.
* @param {Record<string, any>} [metadata] - Optional metadata.
* @returns {IQuery<TQueryMap, T>} The created query.
*/
createQuery(type, params, metadata) {
return {
id: this.generateId(),
type,
timestamp: new Date(),
params,
metadata
};
}
/**
* Creates an event message.
* @template TEventMap - Event map type
* @template T - Event type key
* @param {T} type - The event type.
* @param {EventData<TEventMap, T>} data - The event data.
* @param {string} aggregateId - The aggregate ID.
* @param {number} version - The event version.
* @param {Record<string, any>} [metadata] - Optional metadata.
* @returns {IEvent<TEventMap, T>} The created event.
*/
createEvent(type, data, aggregateId, version, metadata) {
return {
id: this.generateId(),
type,
timestamp: new Date(),
data,
aggregateId,
version,
metadata
};
}
/**
* Generates a unique message ID.
* @returns {string} A unique message ID.
*/
generateId() {
return `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}
}
/**
* Main class of the TypeBus-CQRS library. Implements the IMessageBus interface.
* Handles registration and execution of commands, queries, and events with middleware support.
* @template TCommandMap - Command map type
* @template TQueryMap - Query map type
* @template TEventMap - Event map type
* @implements {IMessageBus<TCommandMap, TQueryMap, TEventMap>}
*/
class TypeBus {
/**
* Creates a new TypeBus instance.
* @param {TypeBusConfig} [config]
*/
constructor(config = {}) {
this.commandHandlers = new Map();
this.queryHandlers = new Map();
this.eventHandlers = new Map();
this.middlewares = [];
this.messageFactory = new MessageFactory();
this.config = {
enableMetrics: true,
enableLogging: true,
logLevel: 'info',
maxMiddleware: 10,
commandTimeout: 30000,
queryTimeout: 10000,
...config
};
}
// ================================================================================
// Middleware Management
// ================================================================================
/**
* Registers a middleware to the bus.
* @param {IMiddleware} middleware
*/
use(middleware) {
if (this.middlewares.length >= this.config.maxMiddleware) {
throw new Error(`Maximum number of middleware (${this.config.maxMiddleware}) exceeded`);
}
this.middlewares.push(middleware);
}
// ================================================================================
// Handler Registration
// ================================================================================
/**
* Registers a command handler for a specific command type.
* @template T
* @param {T} commandType
* @param {IMessageHandler<any, CommandResult<TCommandMap, T>>} handler
*/
registerCommandHandler(commandType, handler) {
if (this.commandHandlers.has(commandType)) {
throw new Error(`Command handler for '${commandType}' already registered`);
}
this.commandHandlers.set(commandType, handler);
if (this.config.enableLogging && this.config.logLevel === 'debug') {
console.log(`📝 Registered command handler: ${commandType}`);
}
}
/**
* Registers a query handler for a specific query type.
* @template T
* @param {T} queryType
* @param {IMessageHandler<any, QueryResult<TQueryMap, T>>} handler
*/
registerQueryHandler(queryType, handler) {
if (this.queryHandlers.has(queryType)) {
throw new Error(`Query handler for '${queryType}' already registered`);
}
this.queryHandlers.set(queryType, handler);
if (this.config.enableLogging && this.config.logLevel === 'debug') {
console.log(`📖 Registered query handler: ${queryType}`);
}
}
/**
* Registers an event handler for a specific event type.
* @template T
* @param {T} eventType
* @param {IMessageHandler<any, void>} handler
*/
registerEventHandler(eventType, handler) {
if (!this.eventHandlers.has(eventType)) {
this.eventHandlers.set(eventType, []);
}
this.eventHandlers.get(eventType).push(handler);
if (this.config.enableLogging && this.config.logLevel === 'debug') {
const count = this.eventHandlers.get(eventType).length;
console.log(`📢 Registered event handler: ${eventType} (${count} total)`);
}
}
// ================================================================================
// Message Execution
// ================================================================================
/**
* Executes a command message.
* @template T
* @param {T} type
* @param {CommandData<TCommandMap, T>} data
* @param {string} aggregateId
* @param {Record<string, any>} [metadata]
* @returns {Promise<CommandResult<TCommandMap, T>>}
*/
async executeCommand(type, data, aggregateId, metadata) {
const command = this.messageFactory.createCommand(type, data, aggregateId, metadata);
const handler = this.commandHandlers.get(type);
if (!handler) {
throw new Error(`No handler registered for command: ${type}`);
}
return await this.executeWithMiddleware(command, handler, this.config.commandTimeout);
}
/**
* Executes a query message.
* @template T
* @param {T} type
* @param {QueryParams<TQueryMap, T>} params
* @param {Record<string, any>} [metadata]
* @returns {Promise<QueryResult<TQueryMap, T>>}
*/
async executeQuery(type, params, metadata) {
const query = this.messageFactory.createQuery(type, params, metadata);
const handler = this.queryHandlers.get(type);
if (!handler) {
throw new Error(`No handler registered for query: ${type}`);
}
return await this.executeWithMiddleware(query, handler, this.config.queryTimeout);
}
/**
* Publishes an event message to all registered handlers.
* @template T
* @param {T} type
* @param {EventData<TEventMap, T>} data
* @param {string} aggregateId
* @param {number} version
* @param {Record<string, any>} [metadata]
* @returns {Promise<void>}
*/
async publishEvent(type, data, aggregateId, version, metadata) {
const event = this.messageFactory.createEvent(type, data, aggregateId, version, metadata);
const handlers = this.eventHandlers.get(type) || [];
if (handlers.length === 0) {
if (this.config.enableLogging && this.config.logLevel === 'debug') {
console.log(`📢 No handlers registered for event: ${type}`);
}
return;
}
// Execute all handlers in parallel
const promises = handlers.map(handler => this.executeWithMiddleware(event, handler, this.config.commandTimeout));
await Promise.all(promises);
}
// ================================================================================
// Private Methods
// ================================================================================
/**
* Executes a message through the middleware pipeline.
* @template T, R
* @param {T} message
* @param {IMessageHandler<T, R>} handler
* @param {number} timeout
* @returns {Promise<R>}
*/
async executeWithMiddleware(message, handler, timeout) {
// Create middleware chain
const dispatch = async (msg) => {
return await handler.handle(msg);
};
// Apply middleware in reverse order
let chain = dispatch;
for (let i = this.middlewares.length - 1; i >= 0; i--) {
const middleware = this.middlewares[i];
const next = chain;
chain = async (msg) => middleware.execute(msg, next);
}
return await this.withTimeout(chain(message), timeout, message.type);
}
/**
* Executes a promise with a timeout.
* @template T
* @param {Promise<T>} promise
* @param {number} timeoutMs
* @param {string} operationType
* @returns {Promise<T>}
*/
async withTimeout(promise, timeoutMs, operationType) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`${operationType} timed out after ${timeoutMs}ms`));
}, timeoutMs);
});
return Promise.race([promise, timeoutPromise]);
}
// ================================================================================
// Utility Methods
// ================================================================================
/**
* Clears all registered handlers and middleware.
*/
clear() {
this.commandHandlers.clear();
this.queryHandlers.clear();
this.eventHandlers.clear();
this.middlewares = [];
if (this.config.enableLogging) {
console.log('🧹 TypeBus cleared all handlers and middleware');
}
}
/**
* Gets statistics about the bus.
* @returns {object}
*/
getStats() {
return {
commandHandlers: this.commandHandlers.size,
queryHandlers: this.queryHandlers.size,
eventHandlers: Array.from(this.eventHandlers.values()).reduce((sum, handlers) => sum + handlers.length, 0),
middleware: this.middlewares.length,
totalHandlers: this.commandHandlers.size + this.queryHandlers.size + Array.from(this.eventHandlers.values()).reduce((sum, handlers) => sum + handlers.length, 0)
};
}
/**
* Gets all registered handlers.
* @returns {object}
*/
getRegisteredHandlers() {
return {
commands: Array.from(this.commandHandlers.keys()),
queries: Array.from(this.queryHandlers.keys()),
events: Array.from(this.eventHandlers.keys())
};
}
}
/** ================================================================================
* Builder for creating and registering command handlers with TypeBus-CQRS.
================================================================================ */
class TypedCommandBuilder {
/**
* Creates and registers a command handler.
* @template TCommandMap - Command map type
* @template T - Command type key
* @param {TypeBus<TCommandMap, any, any>} bus - The TypeBus-CQRS instance.
* @param {T} commandType - The command type.
* @param {(data: CommandData<TCommandMap, T>, aggregateId: string, metadata?: Record<string, any>) => Promise<CommandResult<TCommandMap, T>>} handlerLogic - The handler logic.
* @returns {object} Command executor and handler meta.
*/
static create(bus, commandType, handlerLogic) {
const handler = {
async handle(command) {
return await handlerLogic(command.data, command.aggregateId, command.metadata);
}
};
bus.registerCommandHandler(commandType, handler);
return {
async execute(data, aggregateId, metadata) {
return await bus.executeCommand(commandType, data, aggregateId, metadata);
},
type: commandType,
handler
};
}
}
/** ================================================================================
* Builder for creating and registering query handlers with TypeBus-CQRS.
================================================================================ */
class TypedQueryBuilder {
/**
* Creates and registers a query handler.
* @template TQueryMap - Query map type
* @template T - Query type key
* @param {TypeBus<any, TQueryMap, any>} bus - The TypeBus-CQRS instance.
* @param {T} queryType - The query type.
* @param {(params: QueryParams<TQueryMap, T>, metadata?: Record<string, any>) => Promise<QueryResult<TQueryMap, T>>} handlerLogic - The handler logic.
* @returns {object} Query executor and handler meta.
*/
static create(bus, queryType, handlerLogic) {
const handler = {
async handle(query) {
return await handlerLogic(query.params, query.metadata);
}
};
bus.registerQueryHandler(queryType, handler);
return {
async execute(params, metadata) {
return await bus.executeQuery(queryType, params, metadata);
},
type: queryType,
handler
};
}
}
/** ================================================================================
* Builder for creating and registering event handlers with TypeBus-CQRS.
================================================================================ */
class TypedEventBuilder {
/**
* Creates and registers an event handler.
* @template TEventMap - Event map type
* @template T - Event type key
* @param {TypeBus<any, any, TEventMap>} bus - The TypeBus-CQRS instance.
* @param {T} eventType - The event type.
* @param {(data: EventData<TEventMap, T>, aggregateId: string, version: number, metadata?: Record<string, any>) => Promise<void>} handlerLogic - The handler logic.
* @returns {object} Event publisher and handler meta.
*/
static create(bus, eventType, handlerLogic) {
const handler = {
async handle(event) {
return await handlerLogic(event.data, event.aggregateId, event.version, event.metadata);
}
};
bus.registerEventHandler(eventType, handler);
return {
async publish(data, aggregateId, version, metadata) {
return await bus.publishEvent(eventType, data, aggregateId, version, metadata);
},
type: eventType,
handler
};
}
}
/** ================================================================================
* Builder for creating a batch of related commands, queries, and events.
================================================================================ */
class BatchBuilder {
constructor(bus) {
this.bus = bus;
this.items = [];
}
/**
* Adds a command to the batch.
* @template T - Command type key
* @param {string} name - The name of the command.
* @param {T} commandType - The command type.
* @param {(data: CommandData<TCommandMap, T>, aggregateId: string, metadata?: Record<string, any>) => Promise<CommandResult<TCommandMap, T>>} handlerLogic - The handler logic.
* @returns {BatchBuilder<TCommandMap, TQueryMap, TEventMap>}
*/
addCommand(name, commandType, handlerLogic) {
const executor = TypedCommandBuilder.create(this.bus, commandType, handlerLogic);
this.items.push({ type: 'command', executor, name });
return this;
}
/**
* Adds a query to the batch.
* @template T - Query type key
* @param {string} name - The name of the query.
* @param {T} queryType - The query type.
* @param {(params: QueryParams<TQueryMap, T>, metadata?: Record<string, any>) => Promise<QueryResult<TQueryMap, T>>} handlerLogic - The handler logic.
* @returns {BatchBuilder<TCommandMap, TQueryMap, TEventMap>}
*/
addQuery(name, queryType, handlerLogic) {
const executor = TypedQueryBuilder.create(this.bus, queryType, handlerLogic);
this.items.push({ type: 'query', executor, name });
return this;
}
/**
* Adds an event handler to the batch.
* @template T - Event type key
* @param {string} name - The name of the event handler.
* @param {T} eventType - The event type.
* @param {(data: EventData<TEventMap, T>, aggregateId: string, version: number, metadata?: Record<string, any>) => Promise<void>} handlerLogic - The handler logic.
* @returns {BatchBuilder<TCommandMap, TQueryMap, TEventMap>}
*/
addEventHandler(name, eventType, handlerLogic) {
const executor = TypedEventBuilder.create(this.bus, eventType, handlerLogic);
this.items.push({ type: 'event', executor, name });
return this;
}
/**
* Builds the batch and returns an object with all executors.
* @returns {object}
*/
build() {
const result = {};
this.items.forEach(item => {
result[item.name] = item.executor;
});
return result;
}
}
/** ================================================================================
* Fluent API builder for TypeBus-CQRS.
================================================================================ */
class FluentBuilder {
constructor(bus) {
this.bus = bus;
}
/**
* Creates a command with fluent API.
* @template T - Command type key
* @param {T} commandType - The command type.
* @returns {object} Fluent command builder.
*/
command(commandType) {
return {
handle: (handlerLogic) => {
return TypedCommandBuilder.create(this.bus, commandType, handlerLogic);
}
};
}
/**
* Creates a query with fluent API.
* @template T - Query type key
* @param {T} queryType - The query type.
* @returns {object} Fluent query builder.
*/
query(queryType) {
return {
handle: (handlerLogic) => {
return TypedQueryBuilder.create(this.bus, queryType, handlerLogic);
}
};
}
/**
* Creates an event handler with fluent API.
* @template T - Event type key
* @param {T} eventType - The event type.
* @returns {object} Fluent event builder.
*/
event(eventType) {
return {
handle: (handlerLogic) => {
return TypedEventBuilder.create(this.bus, eventType, handlerLogic);
}
};
}
/**
* Creates a batch builder.
* @returns {BatchBuilder<TCommandMap, TQueryMap, TEventMap>}
*/
batch() {
return new BatchBuilder(this.bus);
}
}
// ================================================================================
// Factory Functions
// ================================================================================
/**
* Creates and registers a command handler.
* @template TCommandMap - Command map type
* @template T - Command type key
* @param {TypeBus<TCommandMap, any, any>} bus - The TypeBus-CQRS instance.
* @param {T} commandType - The command type.
* @param {(data: CommandData<TCommandMap, T>, aggregateId: string, metadata?: Record<string, any>) => Promise<CommandResult<TCommandMap, T>>} handlerLogic - The handler logic.
* @returns {object} Command executor.
*/
function createCommand(bus, commandType, handlerLogic) {
return TypedCommandBuilder.create(bus, commandType, handlerLogic);
}
/**
* Creates and registers a query handler.
* @template TQueryMap - Query map type
* @template T - Query type key
* @param {TypeBus<any, TQueryMap, any>} bus - The TypeBus-CQRS instance.
* @param {T} queryType - The query type.
* @param {(params: QueryParams<TQueryMap, T>, metadata?: Record<string, any>) => Promise<QueryResult<TQueryMap, T>>} handlerLogic - The handler logic.
* @returns {object} Query executor.
*/
function createQuery(bus, queryType, handlerLogic) {
return TypedQueryBuilder.create(bus, queryType, handlerLogic);
}
/**
* Creates and registers an event handler.
* @template TEventMap - Event map type
* @template T - Event type key
* @param {TypeBus<any, any, TEventMap>} bus - The TypeBus-CQRS instance.
* @param {T} eventType - The event type.
* @param {(data: EventData<TEventMap, T>, aggregateId: string, version: number, metadata?: Record<string, any>) => Promise<void>} handlerLogic - The handler logic.
* @returns {object} Event publisher.
*/
function createEventHandler(bus, eventType, handlerLogic) {
return TypedEventBuilder.create(bus, eventType, handlerLogic);
}
/**
* Creates a fluent API builder.
* @template TCommandMap - Command map type
* @template TQueryMap - Query map type
* @template TEventMap - Event map type
* @param {TypeBus<TCommandMap, TQueryMap, TEventMap>} bus - The TypeBus-CQRS instance.
* @returns {FluentBuilder<TCommandMap, TQueryMap, TEventMap>}
*/
function createFluentBuilder(bus) {
return new FluentBuilder(bus);
}
/**
* Middleware for logging message execution in TypeBus-CQRS.
* @implements {IMiddleware}
*/
class LoggingMiddleware {
/**
* Creates a new LoggingMiddleware instance.
* @param {LoggingOptions} [options]
*/
constructor(options = {}) {
this.options = {
logLevel: 'info',
includeData: false,
includeMetadata: false,
colorOutput: true,
maxDataLength: 200,
...options
};
}
/**
* Executes the middleware logic for logging.
* @template T, R
* @param {T} message - The message to process.
* @param {(message: T) => Promise<R>} next - The next middleware or handler.
* @returns {Promise<R>}
*/
async execute(message, next) {
const startTime = process.hrtime.bigint();
const icon = this.getMessageIcon(message.type);
if (this.shouldLogStart()) {
console.log(this.colorize(`${icon} START: ${message.type}`, 'blue'), {
id: message.id,
timestamp: message.timestamp.toISOString(),
...this.getExtraLogData(message)
});
}
try {
const result = await next(message);
const duration = this.getDuration(startTime);
const color = duration > 1000 ? 'yellow' : 'green';
console.log(this.colorize(`${icon} SUCCESS: ${message.type}`, color), {
id: message.id,
duration: `${duration.toFixed(2)}ms`,
...this.getResultLogData(result)
});
return result;
}
catch (error) {
const duration = this.getDuration(startTime);
console.error(this.colorize(`${icon} ERROR: ${message.type}`, 'red'), {
id: message.id,
duration: `${duration.toFixed(2)}ms`,
error: error instanceof Error ? error.message : String(error),
...this.getStackTrace(error)
});
throw error;
}
}
/**
* Determines if the start of message processing should be logged.
* @returns {boolean}
*/
shouldLogStart() {
return this.options.logLevel === 'verbose' || this.options.logLevel === 'debug';
}
/**
* Extracts extra log data from the message (data, params, metadata).
* @param {IMessage} message
* @returns {object}
*/
getExtraLogData(message) {
const extra = {};
if (this.options.includeData) {
const data = message.data || message.params;
if (data) {
extra.data = this.sanitizeData(data);
}
}
if (this.options.includeMetadata && message.metadata) {
extra.metadata = message.metadata;
}
return extra;
}
/**
* Extracts result log data if logLevel is 'debug'.
* @param {any} result
* @returns {object}
*/
getResultLogData(result) {
if (this.options.logLevel !== 'debug')
return {};
return {
result: this.sanitizeResult(result)
};
}
/**
* Extracts stack trace from error if logLevel is 'debug'.
* @param {any} error
* @returns {object}
*/
getStackTrace(error) {
if (this.options.logLevel !== 'debug')
return {};
return {
stack: error instanceof Error ? error.stack : undefined
};
}
/**
* Calculates the duration in milliseconds from the given start time.
* @param {bigint} startTime
* @returns {number}
*/
getDuration(startTime) {
const endTime = process.hrtime.bigint();
return Number(endTime - startTime) / 1000000;
}
/**
* Returns an icon based on the message type.
* @param {string} type
* @returns {string}
*/
getMessageIcon(type) {
if (this.isCommand(type))
return '📤';
if (this.isQuery(type))
return '📥';
if (this.isEvent(type))
return '📢';
return '💬';
}
/**
* Determines if the message type is a command.
* @param {string} type
* @returns {boolean}
*/
isCommand(type) {
return (type.includes('Command') ||
type.includes('.Create') ||
type.includes('.Update') ||
type.includes('.Delete') ||
type.includes('.Change'));
}
/**
* Determines if the message type is a query.
* @param {string} type
* @returns {boolean}
*/
isQuery(type) {
return (type.includes('Query') ||
type.includes('.Get') ||
type.includes('.Search') ||
type.includes('.Find'));
}
/**
* Determines if the message type is an event.
* @param {string} type
* @returns {boolean}
*/
isEvent(type) {
return (type.includes('Event') ||
type.includes('.Created') ||
type.includes('.Updated') ||
type.includes('.Changed'));
}
/**
* Adds color to the log output if enabled.
* @param {string} text
* @param {string} color
* @returns {string}
*/
colorize(text, color) {
if (!this.options.colorOutput)
return text;
const colors = {
red: '\x1b[31m',
green: '\x1b[32m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
magenta: '\x1b[35m',
cyan: '\x1b[36m',
reset: '\x1b[0m'
};
return `${colors[color] || ''}${text}${colors.reset}`;
}
/**
* Sanitizes data for logging, hiding sensitive fields and limiting length.
* @param {any} data
* @returns {any}
*/
sanitizeData(data) {
if (!data || typeof data !== 'object')
return data;
const sanitized = { ...data };
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth', 'authorization'];
for (const field of sensitiveFields) {
if (sanitized[field]) {
sanitized[field] = '***HIDDEN***';
}
}
const stringified = JSON.stringify(sanitized);
if (stringified.length > this.options.maxDataLength) {
return `${stringified.substring(0, this.options.maxDataLength)}...`;
}
return sanitized;
}
/**
* Sanitizes result for logging, limiting length.
* @param {any} result
* @returns {any}
*/
sanitizeResult(result) {
if (!result || typeof result !== 'object')
return result;
const stringified = JSON.stringify(result);
if (stringified.length > this.options.maxDataLength) {
return `${stringified.substring(0, this.options.maxDataLength)}...`;
}
return result;
}
}
// middleware/index.ts - Экспорт всех middleware
/**
* Helper function to quickly set up logging middleware.
* @param {Object} [options]
* @param {'info'|'debug'|'verbose'} [options.level]
* @param {boolean} [options.includeData]
* @returns {LoggingMiddleware}
*/
function withLogging(options) {
return new LoggingMiddleware({
logLevel: options?.level || 'info',
includeData: options?.includeData || false,
includeMetadata: true,
colorOutput: true
});
}
// factory.ts - Фабричные функции для TypeBus-CQRS
/**
* Factory function for creating a configured TypeBus-CQRS instance.
* @template TCommandMap - Command map type
* @template TQueryMap - Query map type
* @template TEventMap - Event map type
* @param {Object} [config] - Optional configuration for the TypeBus-CQRS instance.
* @param {boolean} [config.enableLogging] - Enable logging middleware.
* @param {'info'|'debug'|'verbose'} [config.logLevel] - Logging level.
* @returns {TypeBus<TCommandMap, TQueryMap, TEventMap>} TypeBus-CQRS instance
*/
function createTypeBus(config) {
const bus = new TypeBus({
enableLogging: config?.enableLogging ?? true,
logLevel: config?.logLevel ?? 'info'
});
if (config?.enableLogging) {
bus.use(withLogging({
level: config.logLevel,
includeData: config.logLevel === 'debug'
}));
}
return bus;
}
/**
* Utility function for creating a fluent API builder for TypeBus-CQRS.
* @template TCommandMap - Command map type
* @template TQueryMap - Query map type
* @template TEventMap - Event map type
* @param {TypeBus<TCommandMap, TQueryMap, TEventMap>} bus - The TypeBus-CQRS instance.
* @returns {ReturnType<typeof createFluentBuilder<TCommandMap, TQueryMap, TEventMap>>}
*/
function fluent(bus) {
return createFluentBuilder(bus);
}
// index.ts - Main export file for TypeBus library
// Export all modules
/**
* Library version string.
* @type {string}
*/
const VERSION = '0.2.0';
export { BatchBuilder, FluentBuilder, LoggingMiddleware, MessageFactory, TypeBus, TypedCommandBuilder, TypedEventBuilder, TypedQueryBuilder, VERSION, createCommand, createEventHandler, createFluentBuilder, createQuery, createTypeBus, fluent, withLogging };
//# sourceMappingURL=index.esm.js.map