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 MediaPackage v2 delivers to
11 * EventBridge when a live-to-VOD harvest job succeeds or fails
12 * (detail-type `MediaPackageV2 HarvestJob Notification`).
13 */
14export interface MediaPackageV2HarvestJobEventDetail {
15 /** The harvest job the notification is about. */
16 harvestJob: {
17 /** Name of the harvest job. */
18 harvestJobName: string;
19 /** ARN of the harvest job. */
20 arn: string;
21 /** Terminal status: `COMPLETED` or `FAILED`. */
22 status: string;
23 /** Channel group the harvested endpoint belongs to. */
24 channelGroupName: string;
25 /** Channel the harvested endpoint belongs to. */
26 channelName: string;
27 /** Origin endpoint the content was harvested from. */
28 originEndpointName: string;
29 /** Human-readable outcome (e.g. the failure reason). */
30 message?: string;
31 /** The harvested time window. */
32 scheduleConfiguration?: { startTime?: string; endTime?: string };
33 /** Where the VOD asset was written. */
34 destination?: {
35 s3Destination?: { bucketName?: string; destinationPath?: string };
36 };
37 /** Additional fields (the schema grows over time). */
38 [key: string]: unknown;
39 };
40}
41
42/** A MediaPackage v2 harvest-job EventBridge event delivered to the handler. */
43export type MediaPackageV2HarvestJobEvent =
44 EventRecord<MediaPackageV2HarvestJobEventDetail>;
45
46export interface HarvestJobEventSourceProps extends EventRouteProps {
47 /**
48 * Logical id for the backing EventBridge rule.
49 * @default "MediaPackageV2HarvestJobEvents"
50 */
51 id?: string;
52 /**
53 * Restrict to notifications about specific harvest jobs (matched against
54 * the event's top-level `resources`, which contains the harvest job ARN
55 * `{endpointArn}/harvestJob/{name}`). Prefix patterns are not supported
56 * here — filter in the handler for per-endpoint routing.
57 */
58 harvestJobArns?: readonly string[];
59}
60
61/**
62 * Event source connecting AWS Elemental MediaPackage v2 harvest-job
63 * notifications to the hosting compute. MediaPackage publishes an event to
64 * the account's default EventBridge bus (source `aws.mediapackagev2`,
65 * detail-type `MediaPackageV2 HarvestJob Notification`) when a live-to-VOD
66 * harvest job completes or fails; this subscribes the host Function to
67 * those events so it can publish the exported clip — or alert on a failed
68 * export — without polling `GetHarvestJob`.
69 *
70 * MediaPackage publishes to EventBridge automatically — no additional
71 * resource is created besides the EventBridge rule targeting the host.
72 * Provide the host-specific implementation layer (e.g.
73 * `AWS.Lambda.EventSource`) on the Function effect.
74 *
75 * ### Consuming Harvest Job Events
76 * **Example:** Publish a Clip When Its Harvest Job Completes
77 * ```typescript
78 * import * as AWS from "alchemy/AWS";
79 *
80 * export default ClipPublisher.make(
81 * { main: import.meta.url },
82 * Effect.gen(function* () {
83 * yield* AWS.MediaPackageV2.consumeHarvestJobEvents({}, (events) =>
84 * Stream.runForEach(events, (event) =>
85 * event.detail.harvestJob.status === "COMPLETED"
86 * ? Effect.log(
87 * `clip ready: ${event.detail.harvestJob.destination?.s3Destination?.destinationPath}`,
88 * )
89 * : Effect.log(`harvest failed: ${event.detail.harvestJob.message}`),
90 * ),
91 * );
92 * return {};
93 * }).pipe(Effect.provide(AWS.Lambda.EventSource)),
94 * );
95 * ```
96 */
97export const consumeHarvestJobEvents = <StreamReq = never, Req = never>(
98 props: HarvestJobEventSourceProps,
99 process: (
100 events: Stream.Stream<MediaPackageV2HarvestJobEvent, never, StreamReq>,
101 ) => Effect.Effect<void, never, Req>,
102) =>
103 consumeBusEvents(
104 props.id ?? "MediaPackageV2HarvestJobEvents",
105 {
106 source: ["aws.mediapackagev2"],
107 "detail-type": ["MediaPackageV2 HarvestJob Notification"],
108 ...(props.harvestJobArns !== undefined
109 ? { resources: [...props.harvestJobArns] }
110 : {}),
111 },
112 { description: props.description, state: props.state },
113 process,
114 );