1import * as eventbridge from "@distilled.cloud/aws/eventbridge";
2import * as Effect from "effect/Effect";
3import * as Binding from "../../Binding.ts";
4import * as Layer from "effect/Layer";
5import * as Output from "../../Output.ts";
6import { AWSEnvironment } from "../Environment.ts";
7import { isBindingHost } from "../Lambda/Function.ts";
8import type { EventBus } from "./EventBus.ts";
9import { PutEvents, type PutEventsRequest } from "./PutEvents.ts";
10
11/**
12 * HTTP implementation of {@link PutEvents}. At deploy time it grants
13 * `events:PutEvents` on the bound bus (or the default bus); at runtime it
14 * calls the EventBridge API with the host Function's credentials. Provide
15 * this layer on the Function using the binding.
16 */
17export const PutEventsHttp = Layer.effect(
18 PutEvents,
19 Effect.gen(function* () {
20 const putEvents = yield* eventbridge.putEvents;
21
22 return Effect.fn(function* (bus?: EventBus) {
23 const EventBusName = bus ? yield* bus.eventBusName : undefined;
24 if (!globalThis.__ALCHEMY_RUNTIME__) {
25 const host = yield* Binding.Host;
26 if (isBindingHost(host)) {
27 const { accountId, region } =
28 yield* AWSEnvironment.current as unknown as Effect.Effect<{
29 accountId: string;
30 region: string;
31 }>;
32 // Pass the ARN as an unresolved Output — binding data is resolved
33 // by the engine before the host reconciles. Eagerly yielding here
34 // (during plan) produces a deferred object that serializes into an
35 // invalid IAM policy (MalformedPolicyDocumentException).
36 const resource = bus
37 ? Output.interpolate`${bus.eventBusArn}`
38 : (`arn:aws:events:${region}:${accountId}:event-bus/default` as const);
39
40 yield* host.bind`Allow(${host}, AWS.EventBridge.PutEvents(${bus ?? "default"}))`(
41 {
42 policyStatements: [
43 {
44 Effect: "Allow",
45 Action: ["events:PutEvents"],
46 Resource: [resource],
47 },
48 ],
49 },
50 );
51 }
52 }
53 return Effect.fn(`AWS.EventBridge.PutEvents(${bus?.LogicalId})`)(
54 function* (request: PutEventsRequest) {
55 const eventBusName = EventBusName ? yield* EventBusName : undefined;
56 return yield* putEvents({
57 ...request,
58 Entries: request.Entries.map((entry) => ({
59 ...entry,
60 EventBusName:
61 eventBusName && eventBusName !== "default"
62 ? eventBusName
63 : undefined,
64 })),
65 });
66 },
67 );
68 });
69 }),
70);