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 AWS Elemental MediaConvert delivers to EventBridge
11 * when a transcode job changes state (`MediaConvert Job State Change`).
12 * Which fields are present depends on the event `status` (e.g.
13 * `outputGroupDetails` only on `COMPLETE`, `errorCode`/`errorMessage` only
14 * on `ERROR`), so everything event-specific is optional.
15 */
16export interface MediaConvertJobEventDetail {
17 /** The job's id (`arn:…:jobs/{id}`). */
18 jobId?: string;
19 /** The event kind within the state change stream: `SUBMITTED`, `PROGRESSING`, `INPUT_INFORMATION`, `STATUS_UPDATE`, `NEW_WARNING`, `QUEUE_HOP`, `COMPLETE`, `ERROR`, or `CANCELED`. */
20 status?: string;
21 /** Timestamp (epoch millis) the event was emitted. */
22 timestamp?: number;
23 /** The account the job ran in. */
24 accountId?: string;
25 /** ARN of the queue the job ran in. */
26 queue?: string;
27 /** The `userMetadata` key/value pairs supplied at job submission. */
28 userMetadata?: Record<string, string>;
29 /** Per-output-group details (output file paths, durations) on `COMPLETE`. */
30 outputGroupDetails?: unknown[];
31 /** Integer error code on `ERROR` events. */
32 errorCode?: number;
33 /** Human-readable error message on `ERROR` events. */
34 errorMessage?: string;
35 /** Additional event fields (the schema grows over time). */
36 [key: string]: unknown;
37}
38
39/** A MediaConvert EventBridge event delivered to the handler. */
40export type MediaConvertJobEvent = EventRecord<MediaConvertJobEventDetail>;
41
42export interface JobEventSourceProps extends EventRouteProps {
43 /**
44 * Logical id for the backing EventBridge rule.
45 * @default "MediaConvertJobEvents"
46 */
47 id?: string;
48 /**
49 * Only deliver events with these `detail.status` values (e.g.
50 * `["COMPLETE", "ERROR"]`).
51 * @default all statuses
52 */
53 statuses?: readonly string[];
54}
55
56/**
57 * Event source connecting AWS Elemental MediaConvert job state changes to
58 * the hosting compute. MediaConvert publishes every job state transition
59 * (`MediaConvert Job State Change`, source `aws.mediaconvert`) to the
60 * account's default EventBridge bus; this subscribes the host Function to
61 * those events so it can chain post-transcode automation or alert on
62 * `ERROR` jobs.
63 *
64 * MediaConvert publishes to EventBridge automatically — no additional
65 * resource is created besides the EventBridge rule targeting the host.
66 * Provide the host-specific implementation layer (e.g.
67 * `AWS.Lambda.EventSource`) on the Function effect.
68 *
69 * ### Consuming Job Events
70 * **Example:** React To Finished Transcodes
71 * ```typescript
72 * import * as AWS from "alchemy/AWS";
73 *
74 * export default TranscodeReactor.make(
75 * { main: import.meta.url },
76 * Effect.gen(function* () {
77 * yield* AWS.MediaConvert.consumeJobEvents(
78 * { statuses: ["COMPLETE", "ERROR"] },
79 * (events) =>
80 * Stream.runForEach(events, (event) =>
81 * event.detail.status === "ERROR"
82 * ? Effect.log(`job ${event.detail.jobId} failed: ${event.detail.errorMessage}`)
83 * : Effect.log(`job ${event.detail.jobId} complete`),
84 * ),
85 * );
86 * return {};
87 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
88 * );
89 * ```
90 */
91export const consumeJobEvents = <StreamReq = never, Req = never>(
92 props: JobEventSourceProps,
93 process: (
94 events: Stream.Stream<MediaConvertJobEvent, never, StreamReq>,
95 ) => Effect.Effect<void, never, Req>,
96) =>
97 consumeBusEvents(
98 props.id ?? "MediaConvertJobEvents",
99 {
100 source: ["aws.mediaconvert"],
101 "detail-type": ["MediaConvert Job State Change"],
102 ...(props.statuses ? { detail: { status: [...props.statuses] } } : {}),
103 },
104 { description: props.description, state: props.state },
105 process,
106 );