@composurecdk/lambda 0.7.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/README.md +138 -6
  2. package/dist/{alarm-config.d.ts → commonjs/alarm-config.d.ts} +30 -0
  3. package/dist/commonjs/alarm-config.d.ts.map +1 -0
  4. package/dist/commonjs/alarm-config.js +3 -0
  5. package/dist/{alarm-config.js.map → commonjs/alarm-config.js.map} +1 -1
  6. package/dist/{alarm-defaults.d.ts → commonjs/alarm-defaults.d.ts} +2 -0
  7. package/dist/commonjs/alarm-defaults.d.ts.map +1 -0
  8. package/dist/commonjs/alarm-defaults.js +61 -0
  9. package/dist/commonjs/alarm-defaults.js.map +1 -0
  10. package/dist/commonjs/defaults.d.ts.map +1 -0
  11. package/dist/commonjs/defaults.js +23 -0
  12. package/dist/commonjs/defaults.js.map +1 -0
  13. package/dist/commonjs/event-sources/composure-event-source.d.ts +76 -0
  14. package/dist/commonjs/event-sources/composure-event-source.d.ts.map +1 -0
  15. package/dist/commonjs/event-sources/composure-event-source.js +36 -0
  16. package/dist/commonjs/event-sources/composure-event-source.js.map +1 -0
  17. package/dist/commonjs/event-sources/sqs-event-source.d.ts +54 -0
  18. package/dist/commonjs/event-sources/sqs-event-source.d.ts.map +1 -0
  19. package/dist/commonjs/event-sources/sqs-event-source.js +80 -0
  20. package/dist/commonjs/event-sources/sqs-event-source.js.map +1 -0
  21. package/dist/{function-alarms.d.ts → commonjs/function-alarms.d.ts} +5 -2
  22. package/dist/commonjs/function-alarms.d.ts.map +1 -0
  23. package/dist/commonjs/function-alarms.js +191 -0
  24. package/dist/commonjs/function-alarms.js.map +1 -0
  25. package/dist/commonjs/function-builder.d.ts +252 -0
  26. package/dist/commonjs/function-builder.d.ts.map +1 -0
  27. package/dist/commonjs/function-builder.js +233 -0
  28. package/dist/commonjs/function-builder.js.map +1 -0
  29. package/dist/{index.d.ts → commonjs/index.d.ts} +2 -0
  30. package/dist/commonjs/index.d.ts.map +1 -0
  31. package/dist/commonjs/index.js +13 -0
  32. package/dist/commonjs/index.js.map +1 -0
  33. package/dist/commonjs/package.json +3 -0
  34. package/dist/esm/alarm-config.d.ts +114 -0
  35. package/dist/esm/alarm-config.d.ts.map +1 -0
  36. package/dist/esm/alarm-config.js.map +1 -0
  37. package/dist/esm/alarm-defaults.d.ts +19 -0
  38. package/dist/esm/alarm-defaults.d.ts.map +1 -0
  39. package/dist/{alarm-defaults.js → esm/alarm-defaults.js} +14 -0
  40. package/dist/esm/alarm-defaults.js.map +1 -0
  41. package/dist/esm/defaults.d.ts +8 -0
  42. package/dist/esm/defaults.d.ts.map +1 -0
  43. package/dist/esm/defaults.js.map +1 -0
  44. package/dist/esm/event-sources/composure-event-source.d.ts +76 -0
  45. package/dist/esm/event-sources/composure-event-source.d.ts.map +1 -0
  46. package/dist/esm/event-sources/composure-event-source.js +31 -0
  47. package/dist/esm/event-sources/composure-event-source.js.map +1 -0
  48. package/dist/esm/event-sources/sqs-event-source.d.ts +54 -0
  49. package/dist/esm/event-sources/sqs-event-source.d.ts.map +1 -0
  50. package/dist/esm/event-sources/sqs-event-source.js +76 -0
  51. package/dist/esm/event-sources/sqs-event-source.js.map +1 -0
  52. package/dist/esm/function-alarms.d.ts +31 -0
  53. package/dist/esm/function-alarms.d.ts.map +1 -0
  54. package/dist/{function-alarms.js → esm/function-alarms.js} +69 -4
  55. package/dist/esm/function-alarms.js.map +1 -0
  56. package/dist/esm/function-builder.d.ts +252 -0
  57. package/dist/esm/function-builder.d.ts.map +1 -0
  58. package/dist/esm/function-builder.js +230 -0
  59. package/dist/esm/function-builder.js.map +1 -0
  60. package/dist/esm/index.d.ts +7 -0
  61. package/dist/esm/index.d.ts.map +1 -0
  62. package/dist/{index.js → esm/index.js} +1 -0
  63. package/dist/esm/index.js.map +1 -0
  64. package/dist/esm/package.json +3 -0
  65. package/package.json +39 -19
  66. package/dist/alarm-config.d.ts.map +0 -1
  67. package/dist/alarm-defaults.d.ts.map +0 -1
  68. package/dist/alarm-defaults.js.map +0 -1
  69. package/dist/defaults.d.ts.map +0 -1
  70. package/dist/defaults.js.map +0 -1
  71. package/dist/function-alarms.d.ts.map +0 -1
  72. package/dist/function-alarms.js.map +0 -1
  73. package/dist/function-builder.d.ts +0 -140
  74. package/dist/function-builder.d.ts.map +0 -1
  75. package/dist/function-builder.js +0 -67
  76. package/dist/function-builder.js.map +0 -1
  77. package/dist/index.d.ts.map +0 -1
  78. package/dist/index.js.map +0 -1
  79. /package/dist/{defaults.d.ts → commonjs/defaults.d.ts} +0 -0
  80. /package/dist/{alarm-config.js → esm/alarm-config.js} +0 -0
  81. /package/dist/{defaults.js → esm/defaults.js} +0 -0
@@ -0,0 +1,191 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveFunctionAlarmDefinitions = resolveFunctionAlarmDefinitions;
4
+ exports.createFunctionAlarms = createFunctionAlarms;
5
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
6
+ const aws_cloudwatch_1 = require("aws-cdk-lib/aws-cloudwatch");
7
+ const cloudwatch_1 = require("@composurecdk/cloudwatch");
8
+ const alarm_defaults_js_1 = require("./alarm-defaults.js");
9
+ const METRIC_PERIOD = aws_cdk_lib_1.Duration.minutes(1);
10
+ const METRIC_PERIOD_LABEL = `${String(METRIC_PERIOD.toMinutes())} minute`;
11
+ /**
12
+ * Contextual alarm specs keyed by source kind: each attached event source
13
+ * of a kind contributes one alarm per spec, dimensioned on its mapping UUID.
14
+ *
15
+ * @see https://aws.amazon.com/blogs/compute/introducing-new-event-source-mapping-esm-metrics-for-aws-lambda/
16
+ */
17
+ const EVENT_SOURCE_ALARM_SPECS = {
18
+ sqs: [
19
+ {
20
+ keySuffix: "FailedInvocations",
21
+ configKey: "eventSourceFailedInvocations",
22
+ metricName: "FailedInvokeEventCount",
23
+ describe: (key, threshold) => `Lambda event source "${key}" is failing to invoke the function. ` +
24
+ `Threshold: > ${String(threshold)} failed invocations in ${METRIC_PERIOD_LABEL}.`,
25
+ },
26
+ {
27
+ keySuffix: "DroppedEvents",
28
+ configKey: "eventSourceDroppedEvents",
29
+ metricName: "DroppedEventCount",
30
+ describe: (key, threshold) => `Lambda event source "${key}" is dropping events after exhausting retries or TTL. ` +
31
+ `Threshold: > ${String(threshold)} dropped events in ${METRIC_PERIOD_LABEL}.`,
32
+ },
33
+ ],
34
+ unknown: [],
35
+ };
36
+ /**
37
+ * Resolves the recommended alarm configuration into fully-resolved
38
+ * {@link AlarmDefinition}s, applying contextual logic for timeout-based
39
+ * duration and reserved-concurrency-based concurrent execution alarms.
40
+ */
41
+ function resolveFunctionAlarmDefinitions(fn, config, props, eventSources = []) {
42
+ if (config?.enabled === false)
43
+ return [];
44
+ const definitions = [];
45
+ if (config?.errors !== false) {
46
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(config?.errors, alarm_defaults_js_1.FUNCTION_ALARM_DEFAULTS.errors);
47
+ definitions.push({
48
+ key: "errors",
49
+ alarmName: cfg.alarmName,
50
+ metric: fn.metricErrors({ period: METRIC_PERIOD }),
51
+ threshold: cfg.threshold,
52
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
53
+ evaluationPeriods: cfg.evaluationPeriods,
54
+ datapointsToAlarm: cfg.datapointsToAlarm,
55
+ treatMissingData: cfg.treatMissingData,
56
+ description: `Lambda function is producing invocation errors. Threshold: > ${String(cfg.threshold)} errors in ${METRIC_PERIOD_LABEL}.`,
57
+ });
58
+ }
59
+ if (config?.throttles !== false) {
60
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(config?.throttles, alarm_defaults_js_1.FUNCTION_ALARM_DEFAULTS.throttles);
61
+ definitions.push({
62
+ key: "throttles",
63
+ alarmName: cfg.alarmName,
64
+ metric: fn.metricThrottles({ period: METRIC_PERIOD }),
65
+ threshold: cfg.threshold,
66
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
67
+ evaluationPeriods: cfg.evaluationPeriods,
68
+ datapointsToAlarm: cfg.datapointsToAlarm,
69
+ treatMissingData: cfg.treatMissingData,
70
+ description: `Lambda function invocations are being throttled. Threshold: > ${String(cfg.threshold)} throttles in ${METRIC_PERIOD_LABEL}.`,
71
+ });
72
+ }
73
+ const timeoutMs = props.timeout?.toMilliseconds();
74
+ if (config?.duration !== false && timeoutMs !== undefined) {
75
+ const cfg = resolvePercentageAlarmConfig(config?.duration, alarm_defaults_js_1.FUNCTION_ALARM_DEFAULTS.duration);
76
+ const threshold = Math.round(timeoutMs * cfg.thresholdPercent);
77
+ definitions.push({
78
+ key: "duration",
79
+ alarmName: cfg.alarmName,
80
+ metric: fn.metricDuration({ period: METRIC_PERIOD, statistic: aws_cloudwatch_1.Stats.percentile(99) }),
81
+ threshold,
82
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
83
+ evaluationPeriods: cfg.evaluationPeriods,
84
+ datapointsToAlarm: cfg.datapointsToAlarm,
85
+ treatMissingData: cfg.treatMissingData,
86
+ description: `Lambda function p99 duration is approaching the configured timeout. Threshold: > ${String(threshold)}ms (${String(cfg.thresholdPercent * 100)}% of ${String(timeoutMs)}ms timeout).`,
87
+ });
88
+ }
89
+ const reservedConcurrency = props.reservedConcurrentExecutions;
90
+ if (config?.concurrentExecutions !== false && reservedConcurrency !== undefined) {
91
+ const cfg = resolvePercentageAlarmConfig(config?.concurrentExecutions, alarm_defaults_js_1.FUNCTION_ALARM_DEFAULTS.concurrentExecutions);
92
+ const threshold = Math.round(reservedConcurrency * cfg.thresholdPercent);
93
+ definitions.push({
94
+ key: "concurrentExecutions",
95
+ alarmName: cfg.alarmName,
96
+ metric: fn.metric("ConcurrentExecutions", {
97
+ period: METRIC_PERIOD,
98
+ statistic: aws_cloudwatch_1.Stats.MAXIMUM,
99
+ }),
100
+ threshold,
101
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
102
+ evaluationPeriods: cfg.evaluationPeriods,
103
+ datapointsToAlarm: cfg.datapointsToAlarm,
104
+ treatMissingData: cfg.treatMissingData,
105
+ description: `Lambda function concurrent executions approaching reserved concurrency limit. Threshold: >= ${String(threshold)} (${String(cfg.thresholdPercent * 100)}% of ${String(reservedConcurrency)} reserved).`,
106
+ });
107
+ }
108
+ for (const eventSource of eventSources) {
109
+ // The ESM metrics that back these alarms are dimensioned on the mapping
110
+ // UUID; without it (e.g. a bare escape-hatch source) there is nothing to
111
+ // alarm on.
112
+ if (eventSource.eventSourceMappingId === undefined)
113
+ continue;
114
+ for (const spec of EVENT_SOURCE_ALARM_SPECS[eventSource.kind]) {
115
+ const userConfig = config?.[spec.configKey];
116
+ if (userConfig === false)
117
+ continue;
118
+ const cfg = (0, cloudwatch_1.resolveAlarmConfig)(userConfig, alarm_defaults_js_1.FUNCTION_ALARM_DEFAULTS[spec.configKey]);
119
+ definitions.push({
120
+ key: `${eventSource.key}${spec.keySuffix}`,
121
+ alarmName: cfg.alarmName,
122
+ metric: eventSourceMetric(eventSource.eventSourceMappingId, spec.metricName),
123
+ threshold: cfg.threshold,
124
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
125
+ evaluationPeriods: cfg.evaluationPeriods,
126
+ datapointsToAlarm: cfg.datapointsToAlarm,
127
+ treatMissingData: cfg.treatMissingData,
128
+ description: spec.describe(eventSource.key, cfg.threshold),
129
+ });
130
+ }
131
+ }
132
+ return definitions;
133
+ }
134
+ /**
135
+ * Builds an `AWS/Lambda` event source mapping metric. These per-mapping ESM
136
+ * metrics are dimensioned on `EventSourceMappingUUID` rather than
137
+ * `FunctionName`, so they cannot use the function's built-in metric helpers.
138
+ */
139
+ function eventSourceMetric(eventSourceMappingId, metricName) {
140
+ return new aws_cloudwatch_1.Metric({
141
+ namespace: "AWS/Lambda",
142
+ metricName,
143
+ dimensionsMap: { EventSourceMappingUUID: eventSourceMappingId },
144
+ statistic: aws_cloudwatch_1.Stats.SUM,
145
+ period: METRIC_PERIOD,
146
+ });
147
+ }
148
+ /**
149
+ * Creates AWS-recommended CloudWatch alarms for a Lambda function,
150
+ * merging recommended definitions with any custom alarm builders.
151
+ *
152
+ * @param scope - CDK construct scope for creating alarm constructs.
153
+ * @param id - Base identifier for alarm construct ids.
154
+ * @param fn - The Lambda function to create alarms for.
155
+ * @param config - User-provided alarm configuration, or `false` to disable all.
156
+ * @param props - The merged function props, used for contextual alarm thresholds.
157
+ * @param eventSources - Event sources attached to the function, used for
158
+ * per-event-source contextual alarms.
159
+ * @param customAlarms - Custom alarm builders added via `addAlarm()`.
160
+ * @returns A record mapping alarm keys to their created Alarm constructs.
161
+ *
162
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#Lambda
163
+ */
164
+ function createFunctionAlarms(scope, id, fn, config, props, eventSources = [], customAlarms = []) {
165
+ if (config === false)
166
+ return {};
167
+ const enabled = config?.enabled ?? alarm_defaults_js_1.FUNCTION_ALARM_DEFAULTS.enabled;
168
+ if (!enabled)
169
+ return {};
170
+ const recommended = resolveFunctionAlarmDefinitions(fn, config, props, eventSources);
171
+ const custom = customAlarms.map((b) => b.resolve(fn));
172
+ return (0, cloudwatch_1.createAlarms)(scope, id, [...recommended, ...custom]);
173
+ }
174
+ /**
175
+ * Resolves a percentage-based alarm config by layering user overrides
176
+ * onto the defaults.
177
+ */
178
+ function resolvePercentageAlarmConfig(userConfig, defaults) {
179
+ const thresholdPercent = userConfig?.thresholdPercent ?? defaults.thresholdPercent;
180
+ if (thresholdPercent <= 0 || thresholdPercent > 1) {
181
+ throw new Error(`thresholdPercent must be between 0 (exclusive) and 1 (inclusive), got ${String(thresholdPercent)}.`);
182
+ }
183
+ return {
184
+ alarmName: userConfig?.alarmName,
185
+ thresholdPercent,
186
+ evaluationPeriods: userConfig?.evaluationPeriods ?? defaults.evaluationPeriods,
187
+ datapointsToAlarm: userConfig?.datapointsToAlarm ?? defaults.datapointsToAlarm,
188
+ treatMissingData: userConfig?.treatMissingData ?? defaults.treatMissingData,
189
+ };
190
+ }
191
+ //# sourceMappingURL=function-alarms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-alarms.js","sourceRoot":"","sources":["../../src/function-alarms.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,252 @@
1
+ import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
2
+ import { type IRole } from "aws-cdk-lib/aws-iam";
3
+ import { Function as LambdaFunction, type FunctionProps, type IEventSource } from "aws-cdk-lib/aws-lambda";
4
+ import type { LogGroup } from "aws-cdk-lib/aws-logs";
5
+ import { type IConstruct } from "constructs";
6
+ import { COPY_STATE, type Lifecycle, type Resolvable } from "@composurecdk/core";
7
+ import { type ITaggedBuilder } from "@composurecdk/cloudformation";
8
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
9
+ import { type IRoleBuilder } from "@composurecdk/iam";
10
+ import type { FunctionAlarmConfig } from "./alarm-config.js";
11
+ import { type ComposureEventSource } from "./event-sources/composure-event-source.js";
12
+ /**
13
+ * Configuration properties for the Lambda function builder.
14
+ *
15
+ * Extends the CDK {@link FunctionProps} with builder-specific options. The
16
+ * `role` prop is widened to {@link Resolvable} so a role built by a sibling
17
+ * component can be referenced via `ref(...)` at configuration time.
18
+ */
19
+ export interface FunctionBuilderProps extends Omit<FunctionProps, "role"> {
20
+ /**
21
+ * The IAM execution role to attach to the function. When set, the builder
22
+ * skips creating its own role and the auto-created `LogsWriter` inline
23
+ * policy is **not** added — the caller is fully responsible for the role's
24
+ * permissions.
25
+ *
26
+ * Accepts a concrete {@link IRole} or a {@link Resolvable} for
27
+ * cross-component wiring (e.g. `ref("sharedRole", r => r.role)`).
28
+ *
29
+ * Mutually exclusive with {@link IFunctionBuilder.configureRole} and
30
+ * {@link IFunctionBuilder.useCdkAutoRole}.
31
+ */
32
+ role?: Resolvable<IRole>;
33
+ /**
34
+ * Configuration for AWS-recommended CloudWatch alarms.
35
+ *
36
+ * By default, the builder creates recommended alarms with sensible
37
+ * thresholds for every applicable metric. Individual alarms can be
38
+ * customized or disabled. Set to `false` to disable all alarms.
39
+ *
40
+ * No alarm actions are configured by default since notification
41
+ * methods are user-specific. Access alarms from the build result
42
+ * or use an `afterBuild` hook to apply actions.
43
+ *
44
+ * Contextual alarms are only created when the corresponding function
45
+ * configuration is present: `duration` when `timeout` is set,
46
+ * `concurrentExecutions` when `reservedConcurrentExecutions` is set, and
47
+ * the event-source alarms (`eventSourceFailedInvocations`,
48
+ * `eventSourceDroppedEvents`) once an event source is attached via
49
+ * {@link IFunctionBuilder.addEventSource}.
50
+ *
51
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#Lambda
52
+ */
53
+ recommendedAlarms?: FunctionAlarmConfig | false;
54
+ }
55
+ /**
56
+ * The build output of a {@link IFunctionBuilder}. Contains the CDK constructs
57
+ * created during {@link Lifecycle.build}, keyed by role.
58
+ */
59
+ export interface FunctionBuilderResult {
60
+ /** The Lambda function construct created by the builder. */
61
+ function: LambdaFunction;
62
+ /**
63
+ * The IAM execution role attached to the function. Always populated:
64
+ * - if the caller supplied a role via {@link IFunctionBuilder.role}, this
65
+ * is that role;
66
+ * - if {@link IFunctionBuilder.useCdkAutoRole} was called, this is CDK's
67
+ * auto-created role;
68
+ * - otherwise, this is the explicit role the builder constructed via
69
+ * `@composurecdk/iam`'s `createServiceRoleBuilder`, with an inline
70
+ * `LogsWriter` policy scoped to the function's auto-created log group.
71
+ */
72
+ role: IRole;
73
+ /**
74
+ * The CloudWatch LogGroup created for the function, or `undefined` if
75
+ * the user provided their own via the `logGroup` property.
76
+ *
77
+ * By default the builder creates a managed LogGroup using
78
+ * {@link createLogGroupBuilder} with well-architected defaults (retention
79
+ * policy, removal policy). This follows AWS CDK guidance to create a
80
+ * `LogGroup` explicitly rather than relying on the auto-created default,
81
+ * which cannot be configured via CDK.
82
+ *
83
+ * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html
84
+ * @see https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs-loggroups.html
85
+ */
86
+ logGroup?: LogGroup;
87
+ /**
88
+ * CloudWatch alarms created for the function, keyed by alarm name.
89
+ *
90
+ * Includes both AWS-recommended alarms and any custom alarms added
91
+ * via {@link IFunctionBuilder.addAlarm}. Access individual alarms
92
+ * by key (e.g., `result.alarms.errors`).
93
+ *
94
+ * No alarm actions are configured — apply them via the result or an
95
+ * `afterBuild` hook.
96
+ *
97
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#Lambda
98
+ */
99
+ alarms: Record<string, Alarm>;
100
+ /**
101
+ * Event sources attached to the function via
102
+ * {@link IFunctionBuilder.addEventSource}, keyed by the key passed to that
103
+ * call. Each value is the resolved CDK {@link IEventSource} — for sources
104
+ * built by a ComposureCDK factory (e.g. {@link sqsEventSource}) the
105
+ * concrete type is preserved, so callers can read back post-`bind()` state
106
+ * such as `eventSourceMappingId`.
107
+ *
108
+ * Always present — `{}` when no event sources were added.
109
+ */
110
+ eventSources: Record<string, IEventSource>;
111
+ }
112
+ /**
113
+ * A fluent builder for configuring and creating an AWS Lambda function.
114
+ *
115
+ * Each configuration property from the CDK {@link FunctionProps} is exposed
116
+ * as an overloaded method: call with a value to set it (returns the builder
117
+ * for chaining), or call with no arguments to read the current value.
118
+ *
119
+ * The builder implements {@link Lifecycle}, so it can be used directly as a
120
+ * component in a {@link compose | composed system}. When built, it creates
121
+ * a Lambda function with the configured properties and returns a
122
+ * {@link FunctionBuilderResult}.
123
+ *
124
+ * Unless a user-supplied `logGroup` is provided, the builder automatically
125
+ * creates a managed CloudWatch LogGroup via {@link createLogGroupBuilder}
126
+ * with well-architected defaults (retention, removal policy) and wires it
127
+ * to the function. This ensures full control over log lifecycle and follows
128
+ * AWS CDK guidance to create a LogGroup explicitly.
129
+ *
130
+ * ## Execution role
131
+ *
132
+ * By default the builder creates an explicit IAM role via
133
+ * `@composurecdk/iam`'s `createServiceRoleBuilder("lambda.amazonaws.com")`,
134
+ * with an inline `LogsWriter` policy granting `logs:CreateLogStream` and
135
+ * `logs:PutLogEvents` scoped to the function's auto-created log group.
136
+ * This replaces CDK's default auto-role (which attaches the
137
+ * `AWSLambdaBasicExecutionRole` managed policy granting wildcard log
138
+ * access) with a least-privilege role.
139
+ *
140
+ * Three override seams are available, in order of preference:
141
+ *
142
+ * 1. {@link IFunctionBuilder.configureRole} — extend the default role
143
+ * builder with additional inline policies, etc.
144
+ * 2. {@link IFunctionBuilder.role} — supply a fully external role; no
145
+ * `LogsWriter` policy is added.
146
+ * 3. {@link IFunctionBuilder.useCdkAutoRole} — opt back into CDK's
147
+ * auto-created role with `AWSLambdaBasicExecutionRole`.
148
+ *
149
+ * The seams are mutually exclusive; combining any two throws at build time.
150
+ *
151
+ * The builder also creates AWS-recommended CloudWatch alarms by default.
152
+ * Alarms can be customized or disabled via the `recommendedAlarms` property.
153
+ * Custom alarms can be added via the {@link addAlarm} method.
154
+ *
155
+ * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_lambda-readme.html
156
+ *
157
+ * @example
158
+ * ```ts
159
+ * const handler = createFunctionBuilder()
160
+ * .runtime(Runtime.NODEJS_22_X)
161
+ * .handler("index.handler")
162
+ * .code(Code.fromAsset("lambda"))
163
+ * .memorySize(256)
164
+ * .timeout(Duration.seconds(30));
165
+ * ```
166
+ */
167
+ export type IFunctionBuilder = ITaggedBuilder<FunctionBuilderProps, FunctionBuilder>;
168
+ declare class FunctionBuilder implements Lifecycle<FunctionBuilderResult> {
169
+ #private;
170
+ props: Partial<FunctionBuilderProps>;
171
+ addAlarm(key: string, configure: (alarm: AlarmDefinitionBuilder<LambdaFunction>) => AlarmDefinitionBuilder<LambdaFunction>): this;
172
+ /**
173
+ * Register an event source to be attached to the function at build time.
174
+ *
175
+ * A Lambda function can have many event sources of mixed types, so this
176
+ * hook is repeatable and typed to the common {@link IEventSource}. Pass a
177
+ * {@link ComposureEventSource} from a ComposureCDK factory (e.g. {@link sqsEventSource})
178
+ * or a bare concrete {@link IEventSource}.
179
+ *
180
+ * At build time the source is resolved and attached *after* the function
181
+ * (and its least-privilege execution role) exist, so the `source.bind(fn)`
182
+ * that `addEventSource` performs grants the consume permission onto the
183
+ * builder's role. The resolved source is exposed on
184
+ * {@link FunctionBuilderResult.eventSources} under `key`.
185
+ *
186
+ * Sources built by a recognised factory also enable contextual alarms —
187
+ * see {@link FunctionBuilderProps.recommendedAlarms}.
188
+ *
189
+ * @throws If `key` was already used by a previous `addEventSource` call.
190
+ */
191
+ addEventSource(key: string, source: Resolvable<ComposureEventSource | IEventSource>): this;
192
+ /**
193
+ * Extend the default execution-role builder with additional configuration
194
+ * (inline policies, managed-policy attachments, description, etc.).
195
+ *
196
+ * The callback receives the internal {@link IRoleBuilder} that the function
197
+ * builder will use to construct the role. Calling `configureRole` more than
198
+ * once replaces the previous callback. The default `LogsWriter` inline
199
+ * policy is added before the callback runs; supplying another inline
200
+ * policy with the name `LogsWriter` throws at build time.
201
+ *
202
+ * Mutually exclusive with {@link role} and {@link useCdkAutoRole}.
203
+ */
204
+ configureRole(fn: (rb: IRoleBuilder) => unknown): this;
205
+ /**
206
+ * Opt back into CDK's auto-created execution role attached to the
207
+ * `AWSLambdaBasicExecutionRole` managed policy.
208
+ *
209
+ * **Not the recommended path.** The default builder-created role grants
210
+ * `logs:CreateLogStream` and `logs:PutLogEvents` scoped to the function's
211
+ * own log group; CDK's auto-role grants those actions on `*` and also
212
+ * permits `logs:CreateLogGroup` arbitrarily. Use this escape hatch only
213
+ * when matching an existing stack's logical IDs during a phased migration
214
+ * or when the wildcard log surface is a deliberate trade-off.
215
+ *
216
+ * Mutually exclusive with {@link role} and {@link configureRole}.
217
+ */
218
+ useCdkAutoRole(): this;
219
+ /** @internal — see ADR-0005. */
220
+ [COPY_STATE](target: FunctionBuilder): void;
221
+ build(scope: IConstruct, id: string, context?: Record<string, object>): FunctionBuilderResult;
222
+ }
223
+ /**
224
+ * Creates a new {@link IFunctionBuilder} for configuring an AWS Lambda function.
225
+ *
226
+ * This is the entry point for defining a Lambda function component. The returned
227
+ * builder exposes every {@link FunctionBuilderProps} property as a fluent setter/getter
228
+ * and implements {@link Lifecycle} for use with {@link compose}.
229
+ *
230
+ * @returns A fluent builder for an AWS Lambda function.
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * const handler = createFunctionBuilder()
235
+ * .runtime(Runtime.NODEJS_22_X)
236
+ * .handler("index.handler")
237
+ * .code(Code.fromAsset("lambda"))
238
+ * .timeout(Duration.seconds(30));
239
+ *
240
+ * // Use standalone:
241
+ * const result = handler.build(stack, "MyFunction");
242
+ *
243
+ * // Or compose into a system:
244
+ * const system = compose(
245
+ * { handler, table: createTableBuilder() },
246
+ * { handler: ["table"], table: [] },
247
+ * );
248
+ * ```
249
+ */
250
+ export declare function createFunctionBuilder(): IFunctionBuilder;
251
+ export {};
252
+ //# sourceMappingURL=function-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"function-builder.d.ts","sourceRoot":"","sources":["../../src/function-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,KAAK,KAAK,EAAiB,MAAM,qBAAqB,CAAC;AAChE,OAAO,EACL,QAAQ,IAAI,cAAc,EAC1B,KAAK,aAAa,EAClB,KAAK,YAAY,EAClB,MAAM,wBAAwB,CAAC;AAChC,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,UAAU,EAAE,KAAK,SAAS,EAAW,KAAK,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAC1F,OAAO,EAAE,KAAK,cAAc,EAAiB,MAAM,8BAA8B,CAAC;AAClF,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,mBAAmB,CAAC;AAE3B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAG7D,OAAO,EAEL,KAAK,oBAAoB,EAG1B,MAAM,2CAA2C,CAAC;AAInD;;;;;;GAMG;AACH,MAAM,WAAW,oBAAqB,SAAQ,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC;IACvE;;;;;;;;;;;OAWG;IACH,IAAI,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC;IAEzB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,iBAAiB,CAAC,EAAE,mBAAmB,GAAG,KAAK,CAAC;CACjD;AAED;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,4DAA4D;IAC5D,QAAQ,EAAE,cAAc,CAAC;IAEzB;;;;;;;;;OASG;IACH,IAAI,EAAE,KAAK,CAAC;IAEZ;;;;;;;;;;;;OAYG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAE9B;;;;;;;;;OASG;IACH,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC5C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AACH,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,oBAAoB,EAAE,eAAe,CAAC,CAAC;AAOrF,cAAM,eAAgB,YAAW,SAAS,CAAC,qBAAqB,CAAC;;IAC/D,KAAK,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAM;IAM1C,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CACT,KAAK,EAAE,sBAAsB,CAAC,cAAc,CAAC,KAC1C,sBAAsB,CAAC,cAAc,CAAC,GAC1C,IAAI;IAKP;;;;;;;;;;;;;;;;;;OAkBG;IACH,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,oBAAoB,GAAG,YAAY,CAAC,GAAG,IAAI;IAU1F;;;;;;;;;;;OAWG;IACH,aAAa,CAAC,EAAE,EAAE,CAAC,EAAE,EAAE,YAAY,KAAK,OAAO,GAAG,IAAI;IAKtD;;;;;;;;;;;;OAYG;IACH,cAAc,IAAI,IAAI;IAKtB,gCAAgC;IAChC,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI;IAO3C,KAAK,CACH,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACnC,qBAAqB;CAyHzB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,qBAAqB,IAAI,gBAAgB,CAExD"}