32 lines
1.2 KiB
TypeScript
32 lines
1.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Alert rule catalog. Each entry is a pure async function that takes a
|
||
|
|
* `portId` and returns an array of `AlertCandidate` rows the engine should
|
||
|
|
* upsert into `alerts`. Skeleton: signatures only — implementations land
|
||
|
|
* in PR2.
|
||
|
|
*/
|
||
|
|
|
||
|
|
import type { AlertCandidate } from './alerts.service';
|
||
|
|
import { ALERT_RULES, type AlertRuleId } from '@/lib/db/schema/insights';
|
||
|
|
|
||
|
|
type RuleEvaluator = (portId: string) => Promise<AlertCandidate[]>;
|
||
|
|
|
||
|
|
/** Empty implementations — every evaluator returns no candidates. PR2
|
||
|
|
* fills these in; the cron dispatcher in PR2 walks `RULE_REGISTRY`. */
|
||
|
|
export const RULE_REGISTRY: Record<AlertRuleId, RuleEvaluator> = {
|
||
|
|
'reservation.no_agreement': async () => [],
|
||
|
|
'interest.stale': async () => [],
|
||
|
|
'document.expiring_soon': async () => [],
|
||
|
|
'document.signer_overdue': async () => [],
|
||
|
|
'berth.under_offer_stalled': async () => [],
|
||
|
|
'expense.duplicate': async () => [],
|
||
|
|
'expense.unscanned': async () => [],
|
||
|
|
'interest.high_value_silent': async () => [],
|
||
|
|
'eoi.unsigned_long': async () => [],
|
||
|
|
'audit.suspicious_login': async () => [],
|
||
|
|
};
|
||
|
|
|
||
|
|
/** Sanity check: catalog matches the ALERT_RULES literal type. */
|
||
|
|
export function listRuleIds(): readonly AlertRuleId[] {
|
||
|
|
return ALERT_RULES;
|
||
|
|
}
|