1import * as eventbridge from "@distilled.cloud/aws/eventbridge";
2import * as Effect from "effect/Effect";
3import * as Layer from "effect/Layer";
4import * as Binding from "../../Binding.ts";
5import { AWSEnvironment } from "../Environment.ts";
6import { isBindingHost } from "../Lambda/Function.ts";
7import type { Archive } from "./Archive.ts";
8import { StartReplay, type StartReplayRequest } from "./StartReplay.ts";
9
10/**
11 * HTTP implementation of {@link StartReplay}. At deploy time it grants
12 * `events:StartReplay` on the account's replays (replay names are chosen at
13 * runtime, so the grant is the `replay/*` wildcard); at runtime it calls the
14 * EventBridge API with the host Function's credentials, injecting the bound
15 * archive's ARN as the `EventSourceArn`. Provide this layer on the Function
16 * using the binding.
17 */
18export const StartReplayHttp = Layer.effect(
19 StartReplay,
20 Effect.gen(function* () {
21 const startReplay = yield* eventbridge.startReplay;
22
23 return Effect.fn(function* (archive: Archive) {
24 const ArchiveArn = yield* archive.archiveArn;
25 const EventSourceArn = yield* archive.eventSourceArn;
26 if (!globalThis.__ALCHEMY_RUNTIME__) {
27 const host = yield* Binding.Host;
28 if (isBindingHost(host)) {
29 const { accountId, region } =
30 yield* AWSEnvironment.current as unknown as Effect.Effect<{
31 accountId: string;
32 region: string;
33 }>;
34 yield* host.bind`Allow(${host}, AWS.EventBridge.StartReplay(${archive}))`(
35 {
36 policyStatements: [
37 {
38 Effect: "Allow",
39 Action: ["events:StartReplay"],
40 Resource: [
41 `arn:aws:events:${region}:${accountId}:replay/*`,
42 // StartReplay also authorizes against the source archive.
43 archive.archiveArn,
44 // ...and against the DESTINATION event bus. EventBridge
45 // only allows replaying to the archive's source bus, so
46 // that bus ARN covers every legal destination.
47 archive.eventSourceArn,
48 ],
49 },
50 ],
51 },
52 );
53 }
54 }
55 return Effect.fn(`AWS.EventBridge.StartReplay(${archive.LogicalId})`)(
56 function* (request: StartReplayRequest) {
57 return yield* startReplay({
58 ...request,
59 EventSourceArn: yield* ArchiveArn,
60 // Default the destination to the archive's source bus — the only
61 // destination EventBridge accepts for a replay.
62 Destination: request.Destination ?? {
63 Arn: yield* EventSourceArn,
64 },
65 });
66 },
67 );
68 });
69 }),
70);