- New lib/event-bus.ts: lightweight typed pub/sub with error isolation via Promise.allSettled. Supports note.created, note.updated, note.deleted, task.created, workspace.created events. - notes/repository.ts: emit events on create, update, and delete. - .env.example: add TELEMETRY_ENABLED, FEATURE_FLAGS_ENABLED, FIELD_ENCRYPT_ENABLED, PALACE_ENABLED, PALACE_EXTRACTION_ENABLED.
85 lines
2.0 KiB
TypeScript
85 lines
2.0 KiB
TypeScript
/**
|
|
* Domain event bus singleton for NoteLett backend.
|
|
*
|
|
* Lightweight typed pub/sub for domain events. Handlers run via
|
|
* Promise.allSettled — a failing handler never blocks others.
|
|
*/
|
|
|
|
export interface NoteCreatedEvent {
|
|
noteId: string;
|
|
workspaceId: string;
|
|
userId: string;
|
|
title: string;
|
|
}
|
|
|
|
export interface NoteUpdatedEvent {
|
|
noteId: string;
|
|
workspaceId: string;
|
|
userId: string;
|
|
title: string;
|
|
}
|
|
|
|
export interface NoteDeletedEvent {
|
|
noteId: string;
|
|
workspaceId: string;
|
|
userId: string;
|
|
}
|
|
|
|
export interface TaskCreatedEvent {
|
|
taskId: string;
|
|
noteId: string;
|
|
workspaceId: string;
|
|
userId: string;
|
|
title: string;
|
|
}
|
|
|
|
export interface WorkspaceCreatedEvent {
|
|
workspaceId: string;
|
|
userId: string;
|
|
name: string;
|
|
}
|
|
|
|
export type NoteLettEventMap = {
|
|
'note.created': NoteCreatedEvent;
|
|
'note.updated': NoteUpdatedEvent;
|
|
'note.deleted': NoteDeletedEvent;
|
|
'task.created': TaskCreatedEvent;
|
|
'workspace.created': WorkspaceCreatedEvent;
|
|
};
|
|
|
|
type Handler<T> = (payload: T) => void | Promise<void>;
|
|
|
|
class DomainEventBus {
|
|
private handlers = new Map<string, Set<Handler<unknown>>>();
|
|
|
|
on<K extends keyof NoteLettEventMap>(event: K, handler: Handler<NoteLettEventMap[K]>): () => void {
|
|
if (!this.handlers.has(event)) this.handlers.set(event, new Set());
|
|
this.handlers.get(event)!.add(handler as Handler<unknown>);
|
|
return () => { this.handlers.get(event)?.delete(handler as Handler<unknown>); };
|
|
}
|
|
|
|
async emit<K extends keyof NoteLettEventMap>(event: K, payload: NoteLettEventMap[K]): Promise<void> {
|
|
const fns = this.handlers.get(event);
|
|
if (!fns || fns.size === 0) return;
|
|
await Promise.allSettled([...fns].map(fn => fn(payload)));
|
|
}
|
|
|
|
removeAll(): void {
|
|
this.handlers.clear();
|
|
}
|
|
}
|
|
|
|
let _bus: DomainEventBus | null = null;
|
|
|
|
export function getEventBus(): DomainEventBus {
|
|
if (!_bus) {
|
|
_bus = new DomainEventBus();
|
|
}
|
|
return _bus;
|
|
}
|
|
|
|
/** @internal — for testing only. */
|
|
export function _resetEventBus(): void {
|
|
_bus = null;
|
|
}
|