| 1 | import type * as eventbridge from "@distilled.cloud/aws/eventbridge"; |
| 2 | import type * as Effect from "effect/Effect"; |
| 3 | import type * as Sink from "effect/Sink"; |
| 4 | import * as Binding from "../../Binding.ts"; |
| 5 | import type { BatchRetryExhaustedError } from "../internal/BatchedSink.ts"; |
| 6 | import type { EventBus } from "./EventBus.ts"; |
| 7 | |
| 8 | /** |
| 9 | * A raw `PutEventsRequestEntry` minus `EventBusName`, which the sink derives |
| 10 | * from the bus it is bound to. Callers stay in control of `Source`, |
| 11 | * `DetailType`, `Detail`, `Time`, `Resources`, and `TraceHeader` — no |
| 12 | * auto-marshalling. |
| 13 | */ |
| 14 | export interface BusSinkEntry extends Omit< |
| 15 | eventbridge.PutEventsRequestEntry, |
| 16 | "EventBusName" |
| 17 | > {} |
| 18 | |
| 19 | export type BusSinkError = |
| 20 | | eventbridge.PutEventsError |
| 21 | | BatchRetryExhaustedError<BusSinkEntry>; |
| 22 | |
| 23 | /** |
| 24 | * A batching sink over EventBridge `PutEvents` (10 entries / 256 KiB per |
| 25 | * call). Per-entry failures with transient error codes (`ThrottlingException`, |
| 26 | * `InternalFailure`) are re-submitted on a bounded schedule; any other |
| 27 | * per-entry `ErrorCode` (e.g. `MalformedDetail`) is permanent — those entries |
| 28 | * are dropped and surfaced via a logged warning. Exhausting retries fails the |
| 29 | * sink with a typed `BatchRetryExhaustedError` carrying the stranded entries. |
| 30 | * |
| 31 | * Omit the bus argument to publish to the account's default event bus. |
| 32 | * |
| 33 | * ### Streaming Events |
| 34 | * **Example:** Run a Stream of Entries into the Bus |
| 35 | * ```typescript |
| 36 | * // init — bind the sink (provide AWS.EventBridge.BusSinkHttp on the Function) |
| 37 | * const sink = yield* AWS.EventBridge.BusSink(bus); |
| 38 | * |
| 39 | * return { |
| 40 | * fetch: Effect.gen(function* () { |
| 41 | * // runtime — publish a stream of raw PutEvents entries |
| 42 | * const entries: AWS.EventBridge.BusSinkEntry[] = markers.map((marker) => ({ |
| 43 | * Source: "my.app", |
| 44 | * DetailType: "MarkerSeen", |
| 45 | * Detail: JSON.stringify({ marker }), |
| 46 | * })); |
| 47 | * yield* Stream.fromIterable(entries).pipe(Stream.run(sink)); |
| 48 | * return HttpServerResponse.json({ ok: true }); |
| 49 | * }), |
| 50 | * }; |
| 51 | * ``` |
| 52 | * |
| 53 | * @binding |
| 54 | */ |
| 55 | export interface BusSink extends Binding.Service< |
| 56 | BusSink, |
| 57 | "AWS.EventBridge.BusSink", |
| 58 | ( |
| 59 | bus?: EventBus, |
| 60 | ) => Effect.Effect< |
| 61 | Sink.Sink<void, BusSinkEntry, readonly BusSinkEntry[], BusSinkError> |
| 62 | > |
| 63 | > {} |
| 64 | |
| 65 | export const BusSink = Binding.Service<BusSink>("AWS.EventBridge.BusSink"); |