export const TriggerPriority = { Min: 0, Begin: 10000, Pre: 20000, Body: 30000, Post: 40000, Final: 50000, } as const; export type TriggerPriority = (typeof TriggerPriority)[keyof typeof TriggerPriority]; export interface Trigger, TArg = unknown> { readonly priority: number; readonly uniqueId: string; action(context: TContext, env: TEnv, arg?: TArg): TEnv; } export class TriggerCaller, TArg = unknown> { private readonly triggersByPriority = new Map>>(); constructor(...triggers: Array>) { if (triggers.length === 0) { return; } for (const trigger of triggers) { this.append(trigger); } } isEmpty(): boolean { return this.triggersByPriority.size === 0; } append(trigger: Trigger): this { const priority = trigger.priority; const uniqueId = trigger.uniqueId; const bucket = this.triggersByPriority.get(priority) ?? new Map(); bucket.set(uniqueId, trigger); this.triggersByPriority.set(priority, bucket); return this; } merge(other: TriggerCaller | null | undefined): this { if (!other) { return this; } for (const [priority, otherBucket] of other.triggersByPriority.entries()) { const bucket = this.triggersByPriority.get(priority) ?? new Map(); for (const [uniqueId, trigger] of otherBucket.entries()) { bucket.set(uniqueId, trigger); } this.triggersByPriority.set(priority, bucket); } return this; } fire(context: TContext, env: TEnv, arg?: TArg): TEnv { let currentEnv = env; const priorities = Array.from(this.triggersByPriority.keys()).sort((a, b) => a - b); for (const priority of priorities) { const bucket = this.triggersByPriority.get(priority); if (!bucket) { continue; } for (const trigger of bucket.values()) { currentEnv = trigger.action(context, currentEnv, arg); } } return currentEnv; } }