1import type * as Effect from "effect/Effect";
2import type * as Stream from "effect/Stream";
3import {
4 consumeBusEvents,
5 type EventRecord,
6 type EventRouteProps,
7} from "../EventBridge/EventSource.ts";
8
9/**
10 * The `detail` payload Amazon Fraud Detector delivers to EventBridge when
11 * event orchestration is enabled on an event type and a prediction is
12 * generated. The payload mirrors the `GetEventPrediction` inputs and results;
13 * fields are optional because the schema grows over time.
14 */
15export interface PredictionEventDetail {
16 /** Id of the evaluated event. */
17 eventId?: string;
18 /** Name of the event type the event belongs to. */
19 eventTypeName?: string;
20 /** Id of the detector that produced the prediction. */
21 detectorId?: string;
22 /** Version of the detector that produced the prediction. */
23 detectorVersionId?: string;
24 /** ISO-8601 timestamp of the evaluated event. */
25 eventTimestamp?: string;
26 /** Entities the event was recorded against. */
27 entities?: ReadonlyArray<Record<string, unknown>>;
28 /** Variable values the prediction was computed from. */
29 eventVariables?: Record<string, unknown>;
30 /** Matched-rule results, including the returned outcomes. */
31 ruleResults?: ReadonlyArray<Record<string, unknown>>;
32 /** Model scores, when the detector uses models. */
33 modelScores?: ReadonlyArray<Record<string, unknown>>;
34 /** Additional event fields (the schema grows over time). */
35 [key: string]: unknown;
36}
37
38/** A Fraud Detector prediction EventBridge event delivered to the handler. */
39export type PredictionEvent = EventRecord<PredictionEventDetail>;
40
41export interface PredictionEventSourceProps extends EventRouteProps {
42 /**
43 * Logical id for the backing EventBridge rule.
44 * @default "FraudDetectorPredictions"
45 */
46 id?: string;
47 /**
48 * Restrict to predictions for specific event types (matched against the
49 * event's `eventTypeName`).
50 */
51 eventTypeNames?: readonly string[];
52 /**
53 * Restrict to predictions produced by specific detectors (matched against
54 * the event's `detectorId`).
55 */
56 detectorIds?: readonly string[];
57}
58
59/**
60 * Event source connecting Amazon Fraud Detector prediction results to the
61 * hosting compute. When event orchestration is enabled on an event type
62 * (`EventType` with `eventBridgeEnabled: true`), every `GetEventPrediction`
63 * result is published to the account's default EventBridge bus (source
64 * `aws.frauddetector`, detail-type `Event Prediction Result Returned`); this
65 * subscribes the host Function to those events so it can trigger downstream
66 * workflows — queue a manual review, block an account, notify the customer.
67 *
68 * Fraud Detector publishes to EventBridge automatically — no additional
69 * resource is created besides the EventBridge rule targeting the host.
70 * Provide the host-specific implementation layer (e.g.
71 * `AWS.Lambda.EventSource`) on the Function effect.
72 *
73 * ### Consuming Prediction Results
74 * **Example:** React To High-Risk Predictions
75 * ```typescript
76 * import * as AWS from "alchemy/AWS";
77 *
78 * export default ReviewFunction.make(
79 * { main: import.meta.url },
80 * Effect.gen(function* () {
81 * yield* AWS.FraudDetector.consumePredictionEvents(
82 * { eventTypeNames: ["purchase"] },
83 * (events) =>
84 * Stream.runForEach(events, (event) =>
85 * Effect.log(
86 * `prediction for ${event.detail.eventId}: ` +
87 * JSON.stringify(event.detail.ruleResults),
88 * ),
89 * ),
90 * );
91 * return {};
92 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
93 * );
94 * ```
95 */
96export const consumePredictionEvents = <StreamReq = never, Req = never>(
97 props: PredictionEventSourceProps,
98 process: (
99 events: Stream.Stream<PredictionEvent, never, StreamReq>,
100 ) => Effect.Effect<void, never, Req>,
101) =>
102 consumeBusEvents(
103 props.id ?? "FraudDetectorPredictions",
104 {
105 source: ["aws.frauddetector"],
106 "detail-type": ["Event Prediction Result Returned"],
107 ...(props.eventTypeNames !== undefined || props.detectorIds !== undefined
108 ? {
109 detail: {
110 ...(props.eventTypeNames !== undefined
111 ? { eventTypeName: [...props.eventTypeNames] }
112 : {}),
113 ...(props.detectorIds !== undefined
114 ? { detectorId: [...props.detectorIds] }
115 : {}),
116 },
117 }
118 : {}),
119 },
120 { description: props.description, state: props.state },
121 process,
122 );