# segment-refresher _(no submodules)_ ## Purpose `segment-refresher.ts` orchestrates re-evaluation of one or many segments and persists the resulting `contactIds` back to the `SegmentStore` cache. It exposes a typed `EventBus` so downstream consumers (broadcast schedulers, real-time dashboards, OTel exporters) can subscribe to `segment:refreshed` without coupling to the refresher's internals. The refresher is intentionally driver-agnostic — it does not own a timer; the consumer drives `refresh` / `refreshAll` from whatever cadence policy (cron, push, manual button) it prefers. ## Features - `refresh(segmentId)` — single-segment re-evaluation; returns `undefined` for an unknown ID without emitting. - `refreshAll(tenantId)` — multi-segment fan-out; emits once per refreshed segment. - Typed `EventBus` — `events.on("segment:refreshed", handler)` receives `{ segmentId, result }`. - Cache write through `store.updateCache(segmentId, contactIds)` on every refresh, updating `cachedContactIds`, `cachedCount`, `lastRefreshedAt`, `updatedAt`. - Pure composition of `SegmentEvaluator` + `SegmentStore` — both are constructor-injected so test doubles wire in cleanly. ## Usage: Basic Example ```ts import { InMemorySegmentStore, SegmentEvaluator, SegmentRefresher, } from "@suaveplan/audience-rules"; const store = new InMemorySegmentStore(); const evaluator = new SegmentEvaluator(async () => [ { id: "c1", tags: ["vip"] }, ]); const refresher = new SegmentRefresher(store, evaluator); const segment = await store.create({ tenantId: "t1", name: "VIPs", rules: [{ field: "tags", operator: "contains", value: "vip" }], refreshMode: "manual", }); const result = await refresher.refresh(segment.id); ``` ## Usage: Advanced Example ```ts import { InMemorySegmentStore, SegmentEvaluator, SegmentRefresher, } from "@suaveplan/audience-rules"; const refresher = new SegmentRefresher( new InMemorySegmentStore(), new SegmentEvaluator(async () => []) ); refresher.events.on("segment:refreshed", ({ segmentId, result }) => { // Forward to OTel, dashboard, downstream pub/sub, etc. console.log( `refreshed ${segmentId}: ${result.total} (${(result.matchRate * 100).toFixed(1)}%)` ); }); await refresher.refreshAll("tenant-1"); ``` ## API Reference - `interface SegmentRefresherEventMap` — `"segment:refreshed": { segmentId, result }`. - `class SegmentRefresher` — constructor `(store: SegmentStore, evaluator: SegmentEvaluator)`. - `refresher.events: EventBus` — typed event bus from `@suaveplan/events`; `on` / `off` / `emit` / `use` / `clear`. - `refresher.refresh(segmentId: string): Promise` - `refresher.refreshAll(tenantId: string): Promise>` ## Implementation Notes - The migration swaps upstream's untyped `EventEmitter` for the suaveplan `EventBus`. The consumer API (`on("segment:refreshed", handler)`) is preserved verbatim; only the type signature of `handler` becomes precise. - `refresh` returns `undefined` for a missing segment because the absence is a normal (not exceptional) outcome — the consumer typically retries with a corrected ID or surfaces a `404` at the API layer. - `refreshAll` iterates sequentially; concurrent fan-out is intentionally left to the caller (Promise.all over selected segment IDs) so the refresher does not need to own a concurrency policy. - The `events` field is publicly readable but the `EventBus` is mutable — consumers are expected to subscribe, not replace.