1import type * as aas from "@distilled.cloud/aws/application-auto-scaling";
2import type * as Effect from "effect/Effect";
3import type * as Stream from "effect/Stream";
4import {
5 consumeBusEvents,
6 type EventRecord,
7} from "../EventBridge/EventSource.ts";
8import type { ScalableTarget } from "./ScalableTarget.ts";
9
10/**
11 * The `detail` payload Application Auto Scaling delivers to EventBridge for a
12 * `Application Auto Scaling Scaling Activity State Change` event. Emitted
13 * when a scale-out reaches the scalable target's maximum capacity
14 * (`scaledToMax: true`) — further demand cannot be absorbed until the
15 * target's `maxCapacity` is raised.
16 */
17export interface ScalingActivityEventDetail {
18 /**
19 * ISO-8601 time the scaling activity started.
20 */
21 startTime: string;
22 /**
23 * ISO-8601 time the scaling activity ended.
24 */
25 endTime: string;
26 /**
27 * Desired capacity after the scaling activity.
28 */
29 newDesiredCapacity: number;
30 /**
31 * Desired capacity before the scaling activity.
32 */
33 oldDesiredCapacity: number;
34 /**
35 * Minimum capacity of the scalable target.
36 */
37 minCapacity: number;
38 /**
39 * Maximum capacity of the scalable target.
40 */
41 maxCapacity: number;
42 /**
43 * Identifier of the scaled resource, e.g. `table/my-table`.
44 */
45 resourceId: string;
46 /**
47 * Scalable dimension of the target, e.g.
48 * `dynamodb:table:WriteCapacityUnits`.
49 */
50 scalableDimension: aas.ScalableDimension;
51 /**
52 * Namespace of the AWS service that provides the resource, e.g. `dynamodb`.
53 */
54 serviceNamespace: aas.ServiceNamespace;
55 /**
56 * Final status of the scaling activity (e.g. `Successful`).
57 */
58 statusCode: aas.ScalingActivityStatusCode;
59 /**
60 * Whether the activity scaled the target out to its maximum size limit.
61 */
62 scaledToMax: boolean;
63 /**
64 * Direction of the scaling activity (`scale-out` or `scale-in`).
65 */
66 direction?: string;
67}
68
69/** A scaling-activity EventBridge event delivered to the handler. */
70export type ScalingActivityEvent = EventRecord<ScalingActivityEventDetail>;
71
72export interface ScalingActivityEventSourceProps {
73 /**
74 * Logical id prefix for the EventBridge rule. Defaults to the scalable
75 * target's logical id.
76 */
77 id?: string;
78}
79
80/**
81 * Deliver Application Auto Scaling scaling-activity state-change events to
82 * the host Function via EventBridge. Application Auto Scaling emits these
83 * events when a scale-out pins a scalable target at its maximum capacity
84 * (`detail.scaledToMax`), e.g. to alert that demand exceeds the configured
85 * ceiling or to raise `maxCapacity` automatically.
86 *
87 * The EventBridge pattern matches every scaling-activity event in the
88 * account; inspect `event.detail.resourceId` / `event.detail.serviceNamespace`
89 * in the handler if multiple targets share the Function.
90 *
91 * ### Reacting to Scaling Activity
92 * **Example:** Alert When a Target Is Pinned at Max Capacity
93 * ```typescript
94 * yield* consumeScalingActivityEvents(target, {}, (events) =>
95 * Stream.runForEach(events, (event) =>
96 * event.detail.scaledToMax
97 * ? Effect.log(
98 * `${event.detail.resourceId} is pinned at max capacity ${event.detail.maxCapacity}`,
99 * )
100 * : Effect.void,
101 * ),
102 * );
103 * ```
104 */
105export const consumeScalingActivityEvents = <StreamReq = never, Req = never>(
106 target: ScalableTarget,
107 props: ScalingActivityEventSourceProps,
108 process: (
109 events: Stream.Stream<ScalingActivityEvent, never, StreamReq>,
110 ) => Effect.Effect<void, never, Req>,
111) =>
112 // The pattern uses only literal `source` + `detail-type` so it round-trips
113 // through both the deploy-time rule and the runtime matcher (Output values
114 // cannot appear in the pattern — they don't resolve inside the deployed
115 // bundle).
116 consumeBusEvents(
117 `${props.id ?? target.LogicalId}-ScalingActivityEvents`,
118 {
119 source: ["aws.application-autoscaling"],
120 "detail-type": ["Application Auto Scaling Scaling Activity State Change"],
121 },
122 process,
123 );