import { ObjectId, CheckInMethod, EngagementLevel, OperationContext, AttendanceStats } from './types.js'; /** * ClockIn Event System * * Type-safe event bus for attendance operations * Inspired by Revenue library's event architecture * * @module @classytic/clockin/core/events */ /** Base event interface */ interface BaseEvent { /** Event timestamp */ timestamp: Date; /** Request ID for tracing */ requestId?: string; } /** Member info in events */ interface EventMemberInfo { id: ObjectId; name?: string; } /** Check-in recorded event */ interface CheckInRecordedEvent extends BaseEvent { type: 'checkIn:recorded'; data: { checkIn: { id: ObjectId; timestamp: Date; method: CheckInMethod; }; member: EventMemberInfo; targetModel: string; stats: { totalVisits: number; currentStreak: number; engagementLevel: EngagementLevel; }; context?: OperationContext; }; } /** Check-in failed event */ interface CheckInFailedEvent extends BaseEvent { type: 'checkIn:failed'; data: { member?: EventMemberInfo; targetModel: string; error: { code: string; message: string; }; context?: OperationContext; }; } /** Check-out recorded event */ interface CheckOutRecordedEvent extends BaseEvent { type: 'checkOut:recorded'; data: { checkIn: { id: ObjectId; checkInTime: Date; checkOutTime: Date; }; member: EventMemberInfo; targetModel: string; duration: number; context?: OperationContext; }; } /** Check-out failed event */ interface CheckOutFailedEvent extends BaseEvent { type: 'checkOut:failed'; data: { member?: EventMemberInfo; targetModel: string; checkInId?: ObjectId; error: { code: string; message: string; }; context?: OperationContext; }; } /** Milestone achieved event */ interface MilestoneAchievedEvent extends BaseEvent { type: 'milestone:achieved'; data: { member: EventMemberInfo; milestone: { type: 'visits' | 'streak'; value: number; message: string; }; stats: AttendanceStats; }; } /** Engagement changed event */ interface EngagementChangedEvent extends BaseEvent { type: 'engagement:changed'; data: { member: EventMemberInfo; engagement: { from: EngagementLevel; to: EngagementLevel; }; stats: AttendanceStats; }; } /** Stats updated event */ interface StatsUpdatedEvent extends BaseEvent { type: 'stats:updated'; data: { member: EventMemberInfo; stats: AttendanceStats; }; } /** Member at-risk event */ interface MemberAtRiskEvent extends BaseEvent { type: 'member:atRisk'; data: { member: EventMemberInfo; stats: AttendanceStats; daysSinceLastVisit: number; }; } /** Member inactive event */ interface MemberInactiveEvent extends BaseEvent { type: 'member:inactive'; data: { member: EventMemberInfo; stats: AttendanceStats; daysSinceLastVisit: number; }; } /** Session expired event */ interface SessionExpiredEvent extends BaseEvent { type: 'session:expired'; data: { member: EventMemberInfo; checkInId: ObjectId; checkInTime: Date; autoCheckedOut: boolean; }; } /** All event types */ type ClockInEventPayload = CheckInRecordedEvent | CheckInFailedEvent | CheckOutRecordedEvent | CheckOutFailedEvent | MilestoneAchievedEvent | EngagementChangedEvent | StatsUpdatedEvent | MemberAtRiskEvent | MemberInactiveEvent | SessionExpiredEvent; /** Event type names */ type ClockInEventType = ClockInEventPayload['type']; /** Event map for type-safe listeners */ interface ClockInEventMap { 'checkIn:recorded': CheckInRecordedEvent; 'checkIn:failed': CheckInFailedEvent; 'checkOut:recorded': CheckOutRecordedEvent; 'checkOut:failed': CheckOutFailedEvent; 'milestone:achieved': MilestoneAchievedEvent; 'engagement:changed': EngagementChangedEvent; 'stats:updated': StatsUpdatedEvent; 'member:atRisk': MemberAtRiskEvent; 'member:inactive': MemberInactiveEvent; 'session:expired': SessionExpiredEvent; } /** * Type-safe Event Bus * * @example * ```typescript * const events = createEventBus(); * * events.on('checkIn:recorded', (event) => { * console.log(`${event.data.member.name} checked in!`); * }); * * events.on('milestone:achieved', (event) => { * sendNotification(event.data.member, event.data.milestone.message); * }); * ``` */ /** Unsubscribe function returned by on/once */ type Unsubscribe = () => void; declare class EventBus { private emitter; private _isDestroyed; constructor(maxListeners?: number); /** * Check if the event bus has been destroyed */ get isDestroyed(): boolean; /** * Subscribe to an event. * Returns an unsubscribe function for cleanup. * * @example * ```typescript * const unsubscribe = events.on('checkIn:recorded', handler); * // Later, to clean up: * unsubscribe(); * ``` */ on(event: E, handler: (payload: ClockInEventMap[E]) => void | Promise): Unsubscribe; /** * Subscribe once (auto-unsubscribes after first event). * Returns an unsubscribe function for cleanup before event fires. */ once(event: E, handler: (payload: ClockInEventMap[E]) => void | Promise): Unsubscribe; /** * Unsubscribe from an event */ off(event: E, handler: (payload: ClockInEventMap[E]) => void | Promise): this; /** * Emit an event */ emit(event: E, payload: Omit): void; /** * Remove all listeners and mark as destroyed. * After calling this, new subscriptions will be no-ops. */ clear(): void; /** * Destroy the event bus and prevent further use. * This should be called when the ClockIn instance is destroyed. */ destroy(): void; /** * Get listener count for an event */ listenerCount(event: ClockInEventType): number; /** * Get total listener count across all events */ totalListenerCount(): number; } /** * Create a new event bus instance */ declare function createEventBus(): EventBus; /** * ClockIn Plugin System * * Extensible hooks for attendance operations * Inspired by Revenue library's plugin architecture * * @module @classytic/clockin/core/plugin */ /** Plugin logger interface */ interface PluginLogger { debug(message: string, data?: unknown): void; info(message: string, data?: unknown): void; warn(message: string, data?: unknown): void; error(message: string, data?: unknown): void; } /** Plugin context passed to hooks */ interface PluginContext { /** Event bus for emitting events */ events: EventBus; /** Logger instance */ logger: PluginLogger; /** Get value from container */ get(key: string): T; /** Per-request storage */ storage: Map; /** Request metadata */ meta: { requestId: string; timestamp: Date; }; } interface BeforeCheckInHookData { memberId: ObjectId; targetModel: string; } interface BeforeCheckOutHookData { memberId: ObjectId; checkInId: ObjectId; } /** Check-in data for plugin hooks */ interface CheckInHookData { memberId: ObjectId; memberName?: string; targetModel: string; checkInId: ObjectId; timestamp: Date; method: string; stats: AttendanceStats; } /** Check-out data for plugin hooks */ interface CheckOutHookData { memberId: ObjectId; memberName?: string; targetModel: string; checkInId: ObjectId; duration: number; timestamp: Date; } /** Milestone data for plugin hooks */ interface MilestoneHookData { memberId: ObjectId; memberName?: string; type: 'visits' | 'streak'; value: number; message: string; stats: AttendanceStats; } /** Engagement change data for plugin hooks */ interface EngagementHookData { memberId: ObjectId; memberName?: string; from: EngagementLevel; to: EngagementLevel; stats: AttendanceStats; } /** Plugin hooks interface */ interface PluginHooks { /** Called when ClockIn is initialized */ onInit?(ctx: PluginContext): void | Promise; /** Called before check-in */ beforeCheckIn?(ctx: PluginContext, data: BeforeCheckInHookData): void | Promise; /** Called after successful check-in */ afterCheckIn?(ctx: PluginContext, data: CheckInHookData): void | Promise; /** Called before check-out */ beforeCheckOut?(ctx: PluginContext, data: BeforeCheckOutHookData): void | Promise; /** Called after successful check-out */ afterCheckOut?(ctx: PluginContext, data: CheckOutHookData): void | Promise; /** Called when milestone is achieved */ onMilestone?(ctx: PluginContext, data: MilestoneHookData): void | Promise; /** Called when engagement level changes */ onEngagementChange?(ctx: PluginContext, data: EngagementHookData): void | Promise; /** Called when ClockIn is destroyed */ onDestroy?(ctx: PluginContext): void | Promise; } /** Plugin definition */ interface ClockInPlugin extends PluginHooks { /** Unique plugin name */ name: string; /** Plugin version */ version?: string; } type HookDataMap = { beforeCheckIn: BeforeCheckInHookData; afterCheckIn: CheckInHookData; beforeCheckOut: BeforeCheckOutHookData; afterCheckOut: CheckOutHookData; onMilestone: MilestoneHookData; onEngagementChange: EngagementHookData; }; type HookWithDataKey = keyof HookDataMap; /** * Plugin error that wraps the original error with plugin context */ declare class PluginError extends Error { readonly pluginName: string; readonly hook: string; readonly originalError: Error; constructor(pluginName: string, hook: string, originalError: Error); } /** * Plugin Manager options */ interface PluginManagerOptions { /** * If true, throw an error when any plugin hook fails. * If false (default), log the error and continue with other plugins. */ failFast?: boolean; } /** * Plugin Manager * * Manages plugin lifecycle and hook execution */ declare class PluginManager { private plugins; private initialized; private readonly failFast; private _lastErrors; constructor(options?: PluginManagerOptions); /** * Get the last errors from hook execution. * Cleared on each runHook call. */ get lastErrors(): readonly PluginError[]; /** * Register a plugin */ register(plugin: ClockInPlugin): this; /** * Get all registered plugins */ getPlugins(): readonly ClockInPlugin[]; /** * Initialize all plugins */ init(ctx: PluginContext): Promise; /** * Run a hook on all plugins. * * In fail-fast mode, throws PluginError on first failure. * Otherwise, logs errors and stores them in lastErrors. */ runHook(hook: K, ctx: PluginContext, data: HookDataMap[K]): Promise; /** * Destroy all plugins */ destroy(ctx: PluginContext): Promise; } /** * Create a plugin with type safety */ declare function definePlugin(plugin: ClockInPlugin): ClockInPlugin; /** * Logging plugin - logs all attendance operations */ declare function loggingPlugin(options?: { level?: 'debug' | 'info'; }): ClockInPlugin; /** * Metrics plugin - tracks attendance metrics */ declare function metricsPlugin(options: { onMetric: (metric: { name: string; value: number; tags: Record; }) => void; }): ClockInPlugin; /** * Notification plugin - sends notifications on events */ declare function notificationPlugin(options: { onMilestone?: (data: MilestoneHookData) => void | Promise; onEngagementChange?: (data: EngagementHookData) => void | Promise; onAtRisk?: (data: EngagementHookData) => void | Promise; }): ClockInPlugin; export { PluginManager as A, type BeforeCheckInHookData as B, type ClockInPlugin as C, type PluginLogger as D, type EngagementHookData as E, type MilestoneHookData as M, PluginError as P, type StatsUpdatedEvent as S, type Unsubscribe as U, type PluginContext as a, type PluginHooks as b, type PluginManagerOptions as c, definePlugin as d, type BeforeCheckOutHookData as e, type CheckInHookData as f, type CheckOutHookData as g, EventBus as h, createEventBus as i, type ClockInEventPayload as j, type ClockInEventMap as k, loggingPlugin as l, metricsPlugin as m, notificationPlugin as n, type ClockInEventType as o, type BaseEvent as p, type EventMemberInfo as q, type CheckInRecordedEvent as r, type CheckInFailedEvent as s, type CheckOutRecordedEvent as t, type CheckOutFailedEvent as u, type MilestoneAchievedEvent as v, type EngagementChangedEvent as w, type MemberAtRiskEvent as x, type MemberInactiveEvent as y, type SessionExpiredEvent as z };