@composurecdk/s3 0.1.2 → 0.3.0

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.
package/README.md ADDED
@@ -0,0 +1,226 @@
1
+ # @composurecdk/s3
2
+
3
+ S3 builders for [ComposureCDK](../../README.md).
4
+
5
+ This package provides fluent builders for S3 buckets and bucket deployments with secure, AWS-recommended defaults. It wraps the CDK [Bucket](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.Bucket.html) and [BucketDeployment](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeployment.html) constructs — refer to the CDK documentation for the full set of configurable properties.
6
+
7
+ ## Bucket Builder
8
+
9
+ ```ts
10
+ import { createBucketBuilder } from "@composurecdk/s3";
11
+
12
+ const site = createBucketBuilder().bucketName("my-website-bucket").build(stack, "SiteBucket");
13
+ ```
14
+
15
+ Every [BucketProps](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3.BucketProps.html) property is available as a fluent setter on the builder.
16
+
17
+ ### Secure Defaults
18
+
19
+ `createBucketBuilder` applies the following defaults. Each can be overridden via the builder's fluent API.
20
+
21
+ | Property | Default | Rationale |
22
+ | ------------------- | ------------ | ---------------------------------------------------------------- |
23
+ | `accessLogging` | `true` | Auto-creates a logging bucket for server access log audit trail. |
24
+ | `accessLogsPrefix` | `"logs/"` | Default prefix for access log object keys. |
25
+ | `blockPublicAccess` | `BLOCK_ALL` | Prevents public access unless explicitly required. |
26
+ | `encryption` | `S3_MANAGED` | Enables server-side encryption with S3-managed keys (SSE-S3). |
27
+ | `enforceSSL` | `true` | Requires SSL/TLS for all requests to the bucket. |
28
+ | `versioned` | `true` | Protects against accidental deletions and supports rollback. |
29
+ | `removalPolicy` | `RETAIN` | Retains the bucket on stack deletion to prevent data loss. |
30
+
31
+ These defaults are guided by the [AWS Well-Architected Security Pillar](https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/protecting-data-at-rest.html).
32
+
33
+ The defaults are exported as `BUCKET_DEFAULTS` for visibility and testing:
34
+
35
+ ```ts
36
+ import { BUCKET_DEFAULTS } from "@composurecdk/s3";
37
+ ```
38
+
39
+ ### Overriding defaults
40
+
41
+ ```ts
42
+ import { RemovalPolicy } from "aws-cdk-lib";
43
+ import { BlockPublicAccess } from "aws-cdk-lib/aws-s3";
44
+
45
+ const bucket = createBucketBuilder()
46
+ .blockPublicAccess(BlockPublicAccess.BLOCK_ACLS)
47
+ .versioned(false)
48
+ .removalPolicy(RemovalPolicy.DESTROY)
49
+ .build(stack, "MyBucket");
50
+ ```
51
+
52
+ When `removalPolicy` is set to `DESTROY`, the builder automatically enables `autoDeleteObjects` (unless explicitly set to `false`) so that non-empty buckets can be cleanly removed during stack deletion.
53
+
54
+ ### Access logging
55
+
56
+ By default, the builder creates a dedicated logging bucket with secure defaults and configures it as the server access logs destination. The created bucket is returned in the build result:
57
+
58
+ ```ts
59
+ const result = createBucketBuilder().build(stack, "MyBucket");
60
+
61
+ result.bucket; // Bucket
62
+ result.accessLogsBucket; // Bucket | undefined
63
+ ```
64
+
65
+ To provide your own destination instead, set `serverAccessLogsBucket` — the auto-created logging bucket is skipped. To disable access logging entirely, set `.accessLogging(false)`.
66
+
67
+ ## Recommended Alarms
68
+
69
+ The builder creates [AWS-recommended CloudWatch alarms](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3) automatically when [CloudWatch request metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-request-metrics-bucket.html) are configured on the bucket via `.metrics()`. One alarm per metric is created for each metrics configuration entry. No alarm actions are configured — access alarms from the build result to add SNS topics or other actions.
70
+
71
+ | Alarm | Metric | Default threshold | Created when |
72
+ | -------------- | ---------------------- | ----------------- | -------------------------- |
73
+ | `serverErrors` | 5xxErrors (Sum, 5 min) | > 0 | `.metrics()` is configured |
74
+ | `clientErrors` | 4xxErrors (Sum, 5 min) | > 0 | `.metrics()` is configured |
75
+
76
+ Alarm keys include the metrics filter ID (e.g. `serverErrors:EntireBucket`). When multiple metrics configurations are provided, alarms are created for each one.
77
+
78
+ The defaults are exported as `BUCKET_ALARM_DEFAULTS` for visibility and testing:
79
+
80
+ ```ts
81
+ import { BUCKET_ALARM_DEFAULTS } from "@composurecdk/s3";
82
+ ```
83
+
84
+ ### Enabling alarms
85
+
86
+ Configure request metrics on the bucket — alarms are created automatically:
87
+
88
+ ```ts
89
+ const site = createBucketBuilder().metrics([{ id: "EntireBucket" }]);
90
+ ```
91
+
92
+ ### Multiple metrics configurations
93
+
94
+ Alarms are created for each metrics configuration entry:
95
+
96
+ ```ts
97
+ const site = createBucketBuilder().metrics([
98
+ { id: "EntireBucket" },
99
+ { id: "UploadsOnly", prefix: "uploads/" },
100
+ ]);
101
+
102
+ // Creates: serverErrors:EntireBucket, clientErrors:EntireBucket,
103
+ // serverErrors:UploadsOnly, clientErrors:UploadsOnly
104
+ ```
105
+
106
+ ### Customizing thresholds
107
+
108
+ Override individual alarm properties via `recommendedAlarms`. Unspecified fields keep their defaults.
109
+
110
+ ```ts
111
+ builder.metrics([{ id: "EntireBucket" }]).recommendedAlarms({
112
+ serverErrors: { threshold: 5, evaluationPeriods: 3 },
113
+ clientErrors: { threshold: 50 },
114
+ });
115
+ ```
116
+
117
+ ### Disabling alarms
118
+
119
+ Disable all recommended alarms:
120
+
121
+ ```ts
122
+ builder.recommendedAlarms(false);
123
+ // or
124
+ builder.recommendedAlarms({ enabled: false });
125
+ ```
126
+
127
+ Disable individual alarms:
128
+
129
+ ```ts
130
+ builder.metrics([{ id: "EntireBucket" }]).recommendedAlarms({ clientErrors: false });
131
+ ```
132
+
133
+ ### Custom alarms
134
+
135
+ Add custom alarms alongside the recommended ones via `addAlarm`. The callback receives an `AlarmDefinitionBuilder` typed to the S3 `Bucket`, so the metric factory has access to the bucket's metric helpers.
136
+
137
+ ```ts
138
+ import { Metric } from "aws-cdk-lib/aws-cloudwatch";
139
+
140
+ const site = createBucketBuilder()
141
+ .metrics([{ id: "EntireBucket" }])
142
+ .addAlarm("lowTraffic", (alarm) =>
143
+ alarm
144
+ .metric(
145
+ (bucket) =>
146
+ new Metric({
147
+ namespace: "AWS/S3",
148
+ metricName: "GetRequests",
149
+ dimensionsMap: {
150
+ BucketName: bucket.bucketName,
151
+ FilterId: "EntireBucket",
152
+ },
153
+ period: Duration.minutes(5),
154
+ }),
155
+ )
156
+ .threshold(10)
157
+ .lessThan()
158
+ .description("Bucket traffic has dropped below expected level"),
159
+ );
160
+ ```
161
+
162
+ ### Applying alarm actions
163
+
164
+ Alarms are returned in the build result as `Record<string, Alarm>`:
165
+
166
+ ```ts
167
+ const result = site.build(stack, "SiteBucket");
168
+
169
+ const alertTopic = new Topic(stack, "AlertTopic");
170
+ for (const alarm of Object.values(result.alarms)) {
171
+ alarm.addAlarmAction(new SnsAction(alertTopic));
172
+ }
173
+ ```
174
+
175
+ ## Bucket Deployment Builder
176
+
177
+ Deploys local assets to an S3 bucket with optional CloudFront cache invalidation.
178
+
179
+ ```ts
180
+ import { createBucketDeploymentBuilder } from "@composurecdk/s3";
181
+ import { Source } from "aws-cdk-lib/aws-s3-deployment";
182
+
183
+ const deploy = createBucketDeploymentBuilder()
184
+ .sources([Source.asset("./site")])
185
+ .destinationBucket(myBucket)
186
+ .build(stack, "Deploy");
187
+ ```
188
+
189
+ The `destinationBucket` and `distribution` methods accept `Ref` values for cross-component wiring:
190
+
191
+ ```ts
192
+ import { compose, ref } from "@composurecdk/core";
193
+
194
+ const deploy = createBucketDeploymentBuilder()
195
+ .sources([Source.asset("./site")])
196
+ .destinationBucket(ref("site", (r) => r.bucket))
197
+ .distribution(ref("cdn", (r) => r.distribution));
198
+
199
+ compose(
200
+ { site: createBucketBuilder(), cdn: createDistributionBuilder(), deploy },
201
+ { site: [], cdn: ["site"], deploy: ["site", "cdn"] },
202
+ ).build(stack, "Website");
203
+ ```
204
+
205
+ ### Secure Defaults
206
+
207
+ `createBucketDeploymentBuilder` applies the following defaults. Each can be overridden via the builder's fluent API.
208
+
209
+ | Property | Default | Rationale |
210
+ | ------------------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
211
+ | `prune` | `true` | Removes stale files from the destination, keeping it in sync with the source. |
212
+ | `memoryLimit` | `256` | Allocates 256 MiB to the deployment Lambda (CDK default of 128 MiB is insufficient for larger deployments). |
213
+ | `retainOnDelete` | `false` | Does not retain deployed files on stack deletion, consistent with prune semantics. |
214
+ | `distributionPaths` | `["/*"]` | Invalidates all CloudFront paths so content is immediately visible. Only applied when a distribution is configured. |
215
+
216
+ The builder also auto-creates a managed CloudWatch LogGroup (using `@composurecdk/logs` with its secure defaults) for the deployment's backing Lambda, preventing the auto-created log group with infinite retention.
217
+
218
+ The defaults are exported as `BUCKET_DEPLOYMENT_DEFAULTS` for visibility and testing:
219
+
220
+ ```ts
221
+ import { BUCKET_DEPLOYMENT_DEFAULTS } from "@composurecdk/s3";
222
+ ```
223
+
224
+ ## Examples
225
+
226
+ - [StaticWebsiteStack](../examples/src/static-website/app.ts) — S3 + CloudFront static website with OAC, error pages, and content deployment
@@ -0,0 +1,48 @@
1
+ import type { AlarmConfig } from "@composurecdk/cloudwatch";
2
+ /**
3
+ * Controls which recommended alarms are created for an S3 bucket.
4
+ * All applicable alarms are enabled by default with AWS-recommended thresholds.
5
+ * Set individual alarms to `false` to disable them, or provide an
6
+ * {@link AlarmConfig} to tune thresholds.
7
+ *
8
+ * S3 request metric alarms (5xxErrors, 4xxErrors) require
9
+ * [CloudWatch request metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-request-metrics-bucket.html)
10
+ * to be enabled on the bucket. Alarms are automatically created for each
11
+ * entry in the bucket's {@link BucketProps.metrics} array, keyed by the
12
+ * metrics configuration ID (e.g. `serverErrors:EntireBucket`).
13
+ *
14
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
15
+ */
16
+ export interface BucketAlarmConfig {
17
+ /**
18
+ * Master switch: set to `false` to disable all recommended alarms.
19
+ * Individual alarms can also be disabled via their own entry.
20
+ * @default true
21
+ */
22
+ enabled?: boolean;
23
+ /**
24
+ * Alarm when S3 returns server-side errors (5xx HTTP status codes).
25
+ *
26
+ * Metric: `AWS/S3 5xxErrors`, statistic Sum, period 5 minutes.
27
+ * Default threshold: > 0 errors.
28
+ *
29
+ * Only created when the bucket has request metrics configured via
30
+ * {@link BucketProps.metrics}. One alarm per metrics configuration.
31
+ *
32
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
33
+ */
34
+ serverErrors?: AlarmConfig | false;
35
+ /**
36
+ * Alarm when S3 returns client-side errors (4xx HTTP status codes).
37
+ *
38
+ * Metric: `AWS/S3 4xxErrors`, statistic Sum, period 5 minutes.
39
+ * Default threshold: > 0 errors.
40
+ *
41
+ * Only created when the bucket has request metrics configured via
42
+ * {@link BucketProps.metrics}. One alarm per metrics configuration.
43
+ *
44
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
45
+ */
46
+ clientErrors?: AlarmConfig | false;
47
+ }
48
+ //# sourceMappingURL=alarm-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alarm-config.d.ts","sourceRoot":"","sources":["../src/alarm-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;IAEnC;;;;;;;;;;OAUG;IACH,YAAY,CAAC,EAAE,WAAW,GAAG,KAAK,CAAC;CACpC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=alarm-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alarm-config.js","sourceRoot":"","sources":["../src/alarm-config.ts"],"names":[],"mappings":""}
@@ -0,0 +1,14 @@
1
+ import type { AlarmConfig } from "@composurecdk/cloudwatch";
2
+ interface BucketAlarmDefaults {
3
+ enabled: true;
4
+ serverErrors: Required<AlarmConfig>;
5
+ clientErrors: Required<AlarmConfig>;
6
+ }
7
+ /**
8
+ * AWS-recommended default alarm configuration for S3 buckets.
9
+ *
10
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
11
+ */
12
+ export declare const BUCKET_ALARM_DEFAULTS: BucketAlarmDefaults;
13
+ export {};
14
+ //# sourceMappingURL=alarm-defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alarm-defaults.d.ts","sourceRoot":"","sources":["../src/alarm-defaults.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,UAAU,mBAAmB;IAC3B,OAAO,EAAE,IAAI,CAAC;IACd,YAAY,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;IACpC,YAAY,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,mBAkBnC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
2
+ /**
3
+ * AWS-recommended default alarm configuration for S3 buckets.
4
+ *
5
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
6
+ */
7
+ export const BUCKET_ALARM_DEFAULTS = {
8
+ enabled: true,
9
+ /** Any server-side error is worth investigating; threshold 0. */
10
+ serverErrors: {
11
+ threshold: 0,
12
+ evaluationPeriods: 1,
13
+ datapointsToAlarm: 1,
14
+ treatMissingData: TreatMissingData.NOT_BREACHING,
15
+ },
16
+ /** Any client-side error pattern is worth investigating; threshold 0. */
17
+ clientErrors: {
18
+ threshold: 0,
19
+ evaluationPeriods: 1,
20
+ datapointsToAlarm: 1,
21
+ treatMissingData: TreatMissingData.NOT_BREACHING,
22
+ },
23
+ };
24
+ //# sourceMappingURL=alarm-defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alarm-defaults.js","sourceRoot":"","sources":["../src/alarm-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAS9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAwB;IACxD,OAAO,EAAE,IAAI;IAEb,iEAAiE;IACjE,YAAY,EAAE;QACZ,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,gBAAgB,CAAC,aAAa;KACjD;IAED,yEAAyE;IACzE,YAAY,EAAE;QACZ,SAAS,EAAE,CAAC;QACZ,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,gBAAgB,CAAC,aAAa;KACjD;CACF,CAAC"}
@@ -0,0 +1,34 @@
1
+ import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
2
+ import type { Bucket, BucketMetrics } from "aws-cdk-lib/aws-s3";
3
+ import type { IConstruct } from "constructs";
4
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
5
+ import type { AlarmDefinition } from "@composurecdk/cloudwatch";
6
+ import type { BucketAlarmConfig } from "./alarm-config.js";
7
+ /**
8
+ * Resolves the recommended alarm configuration into fully-resolved
9
+ * {@link AlarmDefinition}s for an S3 bucket.
10
+ *
11
+ * Creates alarms for each entry in `metricsConfigs`, keyed as
12
+ * `{alarmType}:{filterId}` (e.g. `serverErrors:EntireBucket`).
13
+ */
14
+ export declare function resolveBucketAlarmDefinitions(bucket: Bucket, config: BucketAlarmConfig | undefined, metricsConfigs: BucketMetrics[]): AlarmDefinition[];
15
+ /**
16
+ * Creates AWS-recommended CloudWatch alarms for an S3 bucket,
17
+ * merging recommended definitions with any custom alarm builders.
18
+ *
19
+ * Alarms are created for each entry in `metricsConfigs` (from
20
+ * {@link BucketProps.metrics}). If no metrics configurations are
21
+ * provided, only custom alarms are created.
22
+ *
23
+ * @param scope - CDK construct scope for creating alarm constructs.
24
+ * @param id - Base identifier for alarm construct ids.
25
+ * @param bucket - The S3 bucket to create alarms for.
26
+ * @param config - User-provided alarm configuration, or `false` to disable all.
27
+ * @param metricsConfigs - The bucket's request metrics configurations.
28
+ * @param customAlarms - Custom alarm builders added via `addAlarm()`.
29
+ * @returns A record mapping alarm keys to their created Alarm constructs.
30
+ *
31
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
32
+ */
33
+ export declare function createBucketAlarms(scope: IConstruct, id: string, bucket: Bucket, config: BucketAlarmConfig | false | undefined, metricsConfigs: BucketMetrics[], customAlarms?: AlarmDefinitionBuilder<Bucket>[]): Record<string, Alarm>;
34
+ //# sourceMappingURL=bucket-alarms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-alarms.d.ts","sourceRoot":"","sources":["../src/bucket-alarms.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAqC,MAAM,4BAA4B,CAAC;AAC3F,OAAO,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAoC,MAAM,0BAA0B,CAAC;AACpG,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AA2B3D;;;;;;GAMG;AACH,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,iBAAiB,GAAG,SAAS,EACrC,cAAc,EAAE,aAAa,EAAE,GAC9B,eAAe,EAAE,CAuCnB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,iBAAiB,GAAG,KAAK,GAAG,SAAS,EAC7C,cAAc,EAAE,aAAa,EAAE,EAC/B,YAAY,GAAE,sBAAsB,CAAC,MAAM,CAAC,EAAO,GAClD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAUvB"}
@@ -0,0 +1,94 @@
1
+ import { Duration } from "aws-cdk-lib";
2
+ import { ComparisonOperator, Metric, Stats } from "aws-cdk-lib/aws-cloudwatch";
3
+ import { createAlarms, resolveAlarmConfig } from "@composurecdk/cloudwatch";
4
+ import { BUCKET_ALARM_DEFAULTS } from "./alarm-defaults.js";
5
+ const METRIC_PERIOD = Duration.minutes(5);
6
+ const METRIC_PERIOD_LABEL = `${String(METRIC_PERIOD.toMinutes())} minutes`;
7
+ /**
8
+ * Creates an S3 request metric with the correct namespace and dimensions.
9
+ */
10
+ function s3RequestMetric(bucket, filterId, metricName, statistic) {
11
+ return new Metric({
12
+ namespace: "AWS/S3",
13
+ metricName,
14
+ dimensionsMap: {
15
+ BucketName: bucket.bucketName,
16
+ FilterId: filterId,
17
+ },
18
+ statistic,
19
+ period: METRIC_PERIOD,
20
+ });
21
+ }
22
+ /**
23
+ * Resolves the recommended alarm configuration into fully-resolved
24
+ * {@link AlarmDefinition}s for an S3 bucket.
25
+ *
26
+ * Creates alarms for each entry in `metricsConfigs`, keyed as
27
+ * `{alarmType}:{filterId}` (e.g. `serverErrors:EntireBucket`).
28
+ */
29
+ export function resolveBucketAlarmDefinitions(bucket, config, metricsConfigs) {
30
+ if (config?.enabled === false)
31
+ return [];
32
+ if (metricsConfigs.length === 0)
33
+ return [];
34
+ const definitions = [];
35
+ for (const metrics of metricsConfigs) {
36
+ const filterId = metrics.id;
37
+ if (config?.serverErrors !== false) {
38
+ const cfg = resolveAlarmConfig(config?.serverErrors, BUCKET_ALARM_DEFAULTS.serverErrors);
39
+ definitions.push({
40
+ key: `serverErrors:${filterId}`,
41
+ metric: s3RequestMetric(bucket, filterId, "5xxErrors", Stats.SUM),
42
+ threshold: cfg.threshold,
43
+ comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
44
+ evaluationPeriods: cfg.evaluationPeriods,
45
+ datapointsToAlarm: cfg.datapointsToAlarm,
46
+ treatMissingData: cfg.treatMissingData,
47
+ description: `S3 bucket is returning server-side errors (filter: ${filterId}). Threshold: > ${String(cfg.threshold)} 5xx errors in ${METRIC_PERIOD_LABEL}.`,
48
+ });
49
+ }
50
+ if (config?.clientErrors !== false) {
51
+ const cfg = resolveAlarmConfig(config?.clientErrors, BUCKET_ALARM_DEFAULTS.clientErrors);
52
+ definitions.push({
53
+ key: `clientErrors:${filterId}`,
54
+ metric: s3RequestMetric(bucket, filterId, "4xxErrors", Stats.SUM),
55
+ threshold: cfg.threshold,
56
+ comparisonOperator: ComparisonOperator.GREATER_THAN_THRESHOLD,
57
+ evaluationPeriods: cfg.evaluationPeriods,
58
+ datapointsToAlarm: cfg.datapointsToAlarm,
59
+ treatMissingData: cfg.treatMissingData,
60
+ description: `S3 bucket is returning client-side errors (filter: ${filterId}). Threshold: > ${String(cfg.threshold)} 4xx errors in ${METRIC_PERIOD_LABEL}.`,
61
+ });
62
+ }
63
+ }
64
+ return definitions;
65
+ }
66
+ /**
67
+ * Creates AWS-recommended CloudWatch alarms for an S3 bucket,
68
+ * merging recommended definitions with any custom alarm builders.
69
+ *
70
+ * Alarms are created for each entry in `metricsConfigs` (from
71
+ * {@link BucketProps.metrics}). If no metrics configurations are
72
+ * provided, only custom alarms are created.
73
+ *
74
+ * @param scope - CDK construct scope for creating alarm constructs.
75
+ * @param id - Base identifier for alarm construct ids.
76
+ * @param bucket - The S3 bucket to create alarms for.
77
+ * @param config - User-provided alarm configuration, or `false` to disable all.
78
+ * @param metricsConfigs - The bucket's request metrics configurations.
79
+ * @param customAlarms - Custom alarm builders added via `addAlarm()`.
80
+ * @returns A record mapping alarm keys to their created Alarm constructs.
81
+ *
82
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
83
+ */
84
+ export function createBucketAlarms(scope, id, bucket, config, metricsConfigs, customAlarms = []) {
85
+ if (config === false)
86
+ return {};
87
+ const enabled = config?.enabled ?? BUCKET_ALARM_DEFAULTS.enabled;
88
+ if (!enabled)
89
+ return {};
90
+ const recommended = resolveBucketAlarmDefinitions(bucket, config, metricsConfigs);
91
+ const custom = customAlarms.map((b) => b.resolve(bucket));
92
+ return createAlarms(scope, id, [...recommended, ...custom]);
93
+ }
94
+ //# sourceMappingURL=bucket-alarms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-alarms.js","sourceRoot":"","sources":["../src/bucket-alarms.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
+ import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
1
2
  import { Bucket, type BucketProps } from "aws-cdk-lib/aws-s3";
2
3
  import { type IConstruct } from "constructs";
3
4
  import { type IBuilder, type Lifecycle } from "@composurecdk/core";
5
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
6
+ import type { BucketAlarmConfig } from "./alarm-config.js";
4
7
  /**
5
8
  * Configuration properties for the S3 bucket builder.
6
9
  *
@@ -35,6 +38,21 @@ export interface BucketBuilderProps extends BucketProps {
35
38
  * @default "logs/"
36
39
  */
37
40
  accessLogsPrefix?: string;
41
+ /**
42
+ * Configuration for AWS-recommended CloudWatch alarms.
43
+ *
44
+ * S3 request metric alarms (5xxErrors, 4xxErrors) require
45
+ * [CloudWatch request metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/configure-request-metrics-bucket.html)
46
+ * to be enabled on the bucket. Set {@link BucketAlarmConfig.requestMetricsFilterId}
47
+ * to the ID of the request metrics configuration to create these alarms.
48
+ *
49
+ * No alarm actions are configured by default since notification
50
+ * methods are user-specific. Access alarms from the build result
51
+ * or use an `afterBuild` hook to apply actions.
52
+ *
53
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
54
+ */
55
+ recommendedAlarms?: BucketAlarmConfig | false;
38
56
  }
39
57
  /**
40
58
  * The build output of a {@link IBucketBuilder}. Contains the CDK constructs
@@ -48,6 +66,19 @@ export interface BucketBuilderResult {
48
66
  * logging was disabled or the user provided their own destination.
49
67
  */
50
68
  accessLogsBucket?: Bucket;
69
+ /**
70
+ * CloudWatch alarms created for the bucket, keyed by alarm name.
71
+ *
72
+ * Includes both AWS-recommended alarms and any custom alarms added
73
+ * via {@link IBucketBuilder.addAlarm}. Access individual alarms
74
+ * by key (e.g., `result.alarms.serverErrors`).
75
+ *
76
+ * No alarm actions are configured — apply them via the result or an
77
+ * `afterBuild` hook.
78
+ *
79
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#S3
80
+ */
81
+ alarms: Record<string, Alarm>;
51
82
  }
52
83
  /**
53
84
  * A fluent builder for configuring and creating an Amazon S3 bucket.
@@ -71,6 +102,8 @@ export interface BucketBuilderResult {
71
102
  export type IBucketBuilder = IBuilder<BucketBuilderProps, BucketBuilder>;
72
103
  declare class BucketBuilder implements Lifecycle<BucketBuilderResult> {
73
104
  props: Partial<BucketBuilderProps>;
105
+ private readonly customAlarms;
106
+ addAlarm(key: string, configure: (alarm: AlarmDefinitionBuilder<Bucket>) => AlarmDefinitionBuilder<Bucket>): this;
74
107
  build(scope: IConstruct, id: string): BucketBuilderResult;
75
108
  }
76
109
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"bucket-builder.d.ts","sourceRoot":"","sources":["../src/bucket-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAW,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAG5E;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;AAEzE,cAAM,aAAc,YAAW,SAAS,CAAC,mBAAmB,CAAC;IAC3D,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAM;IAExC,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,mBAAmB;CA4C1D;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAEpD"}
1
+ {"version":3,"file":"bucket-builder.d.ts","sourceRoot":"","sources":["../src/bucket-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,KAAK,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAW,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAI3D;;;;GAIG;AACH,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD;;;;;;;;;;;;;;;;OAgBG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IAExB;;;;;;;;OAQG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAC;CAC/C;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IAEf;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,kBAAkB,EAAE,aAAa,CAAC,CAAC;AAEzE,cAAM,aAAc,YAAW,SAAS,CAAC,mBAAmB,CAAC;IAC3D,KAAK,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAM;IACxC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAwC;IAErE,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CAAC,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,KAAK,sBAAsB,CAAC,MAAM,CAAC,GACnF,IAAI;IAKP,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,GAAG,mBAAmB;CA6D1D;AAqBD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,mBAAmB,IAAI,cAAc,CAEpD"}
@@ -1,11 +1,18 @@
1
1
  import { RemovalPolicy } from "aws-cdk-lib";
2
2
  import { Bucket } from "aws-cdk-lib/aws-s3";
3
3
  import { Builder } from "@composurecdk/core";
4
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
5
+ import { createBucketAlarms } from "./bucket-alarms.js";
4
6
  import { BUCKET_DEFAULTS } from "./defaults.js";
5
7
  class BucketBuilder {
6
8
  props = {};
9
+ customAlarms = [];
10
+ addAlarm(key, configure) {
11
+ this.customAlarms.push(configure(new AlarmDefinitionBuilder(key)));
12
+ return this;
13
+ }
7
14
  build(scope, id) {
8
- const { accessLogging, accessLogsPrefix, ...bucketProps } = this.props;
15
+ const { accessLogging, accessLogsPrefix, recommendedAlarms: alarmConfig, ...bucketProps } = this.props;
9
16
  const { accessLogging: defaultAccessLogging, accessLogsPrefix: defaultLogsPrefix, ...cdkDefaults } = BUCKET_DEFAULTS;
10
17
  const autoAccessLog = (accessLogging ?? defaultAccessLogging) && !bucketProps.serverAccessLogsBucket;
11
18
  if (accessLogsPrefix !== undefined && !autoAccessLog) {
@@ -31,9 +38,12 @@ class BucketBuilder {
31
38
  ...bucketProps,
32
39
  ...autoDeleteProps(bucketProps, BUCKET_DEFAULTS),
33
40
  };
41
+ const bucket = new Bucket(scope, id, mergedProps);
42
+ const alarms = createBucketAlarms(scope, id, bucket, alarmConfig, bucketProps.metrics ?? [], this.customAlarms);
34
43
  return {
35
- bucket: new Bucket(scope, id, mergedProps),
44
+ bucket,
36
45
  accessLogsBucket,
46
+ alarms,
37
47
  };
38
48
  }
39
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bucket-builder.js","sourceRoot":"","sources":["../src/bucket-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,MAAM,EAAoB,MAAM,oBAAoB,CAAC;AAE9D,OAAO,EAAE,OAAO,EAAiC,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AA2EhD,MAAM,aAAa;IACjB,KAAK,GAAgC,EAAE,CAAC;IAExC,KAAK,CAAC,KAAiB,EAAE,EAAU;QACjC,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QACvE,MAAM,EACJ,aAAa,EAAE,oBAAoB,EACnC,gBAAgB,EAAE,iBAAiB,EACnC,GAAG,WAAW,EACf,GAAG,eAAe,CAAC;QACpB,MAAM,aAAa,GACjB,CAAC,aAAa,IAAI,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC;QAEjF,IAAI,gBAAgB,KAAK,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,mEAAmE;gBACjE,qEAAqE;gBACrE,8CAA8C,CACjD,CAAC;QACJ,CAAC;QAED,IAAI,gBAAoC,CAAC;QACzC,IAAI,cAAc,GAAG,EAAE,CAAC;QAExB,IAAI,aAAa,EAAE,CAAC;YAClB,gBAAgB,GAAG,mBAAmB,EAAE;iBACrC,aAAa,CAAC,KAAK,CAAC;iBACpB,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC;iBACnC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC;YAC1C,cAAc,GAAG;gBACf,sBAAsB,EAAE,gBAAgB;gBACxC,sBAAsB,EAAE,gBAAgB,IAAI,iBAAiB;aAC9D,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG;YAClB,GAAG,WAAW;YACd,GAAG,cAAc;YACjB,GAAG,WAAW;YACd,GAAG,eAAe,CAAC,WAAW,EAAE,eAAe,CAAC;SAClC,CAAC;QAEjB,OAAO;YACL,MAAM,EAAE,IAAI,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC;YAC1C,gBAAgB;SACjB,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CACtB,SAA+B,EAC/B,QAAqC;IAErC,MAAM,eAAe,GAAG,SAAS,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC;IAC1E,IAAI,eAAe,KAAK,aAAa,CAAC,OAAO,IAAI,SAAS,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QAC3F,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,OAAO,CAAoC,aAAa,CAAC,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"bucket-builder.js","sourceRoot":"","sources":["../src/bucket-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAoB,MAAM,oBAAoB,CAAC;AAE9D,OAAO,EAAE,OAAO,EAAiC,MAAM,oBAAoB,CAAC;AAC5E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAyGhD,MAAM,aAAa;IACjB,KAAK,GAAgC,EAAE,CAAC;IACvB,YAAY,GAAqC,EAAE,CAAC;IAErE,QAAQ,CACN,GAAW,EACX,SAAoF;QAEpF,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,sBAAsB,CAAS,GAAG,CAAC,CAAC,CAAC,CAAC;QAC3E,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAiB,EAAE,EAAU;QACjC,MAAM,EACJ,aAAa,EACb,gBAAgB,EAChB,iBAAiB,EAAE,WAAW,EAC9B,GAAG,WAAW,EACf,GAAG,IAAI,CAAC,KAAK,CAAC;QACf,MAAM,EACJ,aAAa,EAAE,oBAAoB,EACnC,gBAAgB,EAAE,iBAAiB,EACnC,GAAG,WAAW,EACf,GAAG,eAAe,CAAC;QACpB,MAAM,aAAa,GACjB,CAAC,aAAa,IAAI,oBAAoB,CAAC,IAAI,CAAC,WAAW,CAAC,sBAAsB,CAAC;QAEjF,IAAI,gBAAgB,KAAK,SAAS,IAAI,CAAC,aAAa,EAAE,CAAC;YACrD,MAAM,IAAI,KAAK,CACb,mEAAmE;gBACjE,qEAAqE;gBACrE,8CAA8C,CACjD,CAAC;QACJ,CAAC;QAED,IAAI,gBAAoC,CAAC;QACzC,IAAI,cAAc,GAAG,EAAE,CAAC;QAExB,IAAI,aAAa,EAAE,CAAC;YAClB,gBAAgB,GAAG,mBAAmB,EAAE;iBACrC,aAAa,CAAC,KAAK,CAAC;iBACpB,aAAa,CAAC,aAAa,CAAC,MAAM,CAAC;iBACnC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,YAAY,CAAC,CAAC,MAAM,CAAC;YAC1C,cAAc,GAAG;gBACf,sBAAsB,EAAE,gBAAgB;gBACxC,sBAAsB,EAAE,gBAAgB,IAAI,iBAAiB;aAC9D,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG;YAClB,GAAG,WAAW;YACd,GAAG,cAAc;YACjB,GAAG,WAAW;YACd,GAAG,eAAe,CAAC,WAAW,EAAE,eAAe,CAAC;SAClC,CAAC;QAEjB,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,kBAAkB,CAC/B,KAAK,EACL,EAAE,EACF,MAAM,EACN,WAAW,EACX,WAAW,CAAC,OAAO,IAAI,EAAE,EACzB,IAAI,CAAC,YAAY,CAClB,CAAC;QAEF,OAAO;YACL,MAAM;YACN,gBAAgB;YAChB,MAAM;SACP,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;GAOG;AACH,SAAS,eAAe,CACtB,SAA+B,EAC/B,QAAqC;IAErC,MAAM,eAAe,GAAG,SAAS,CAAC,aAAa,IAAI,QAAQ,CAAC,aAAa,CAAC;IAC1E,IAAI,eAAe,KAAK,aAAa,CAAC,OAAO,IAAI,SAAS,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QAC3F,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC;IACrC,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,mBAAmB;IACjC,OAAO,OAAO,CAAoC,aAAa,CAAC,CAAC;AACnE,CAAC"}
@@ -1,30 +1,29 @@
1
- import { BucketDeployment, type BucketDeploymentProps, type ISource } from "aws-cdk-lib/aws-s3-deployment";
1
+ import { BucketDeployment } from "aws-cdk-lib/aws-s3-deployment";
2
2
  import { type IBucket } from "aws-cdk-lib/aws-s3";
3
3
  import { type IDistribution } from "aws-cdk-lib/aws-cloudfront";
4
+ import type { LogGroup } from "aws-cdk-lib/aws-logs";
4
5
  import { type IConstruct } from "constructs";
5
6
  import { type IBuilder, type Lifecycle, type Resolvable } from "@composurecdk/core";
6
- /**
7
- * Configuration properties for the S3 bucket deployment builder.
8
- *
9
- * Extends the CDK {@link BucketDeploymentProps} but replaces
10
- * `destinationBucket` and `distribution` with builder-managed fields that
11
- * support {@link Resolvable} for cross-component wiring via {@link ref}.
12
- *
13
- * `sources` is set via the builder's fluent API rather than the constructor.
14
- */
15
- export interface BucketDeploymentBuilderProps extends Omit<BucketDeploymentProps, "sources" | "destinationBucket" | "distribution"> {
16
- /**
17
- * The sources from which to deploy content. Typically created with
18
- * `Source.asset("./path")` or `Source.data("key", "content")`.
19
- */
20
- sources?: ISource[];
21
- }
7
+ import { type BucketDeploymentBuilderProps } from "./bucket-deployment-props.js";
22
8
  /**
23
9
  * The build output of a {@link IBucketDeploymentBuilder}.
24
10
  */
25
11
  export interface BucketDeploymentBuilderResult {
26
12
  /** The CDK BucketDeployment construct created by the builder. */
27
13
  deployment: BucketDeployment;
14
+ /**
15
+ * The CloudWatch LogGroup created for the deployment's backing Lambda,
16
+ * or `undefined` if the user provided their own via the `logGroup`
17
+ * property.
18
+ *
19
+ * By default the builder creates a managed LogGroup using
20
+ * {@link createLogGroupBuilder} with well-architected defaults (retention
21
+ * policy, removal policy). This prevents the backing Lambda from
22
+ * creating an auto-managed log group with infinite retention.
23
+ *
24
+ * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeploymentProps.html#loggroup
25
+ */
26
+ logGroup?: LogGroup;
28
27
  }
29
28
  /**
30
29
  * A fluent builder for configuring and creating an S3 BucketDeployment.
@@ -1 +1 @@
1
- {"version":3,"file":"bucket-deployment-builder.d.ts","sourceRoot":"","sources":["../src/bucket-deployment-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACb,MAAM,+BAA+B,CAAC;AACvC,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAEd,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAG5B;;;;;;;;GAQG;AACH,MAAM,WAAW,4BACf,SAAQ,IAAI,CAAC,qBAAqB,EAAE,SAAS,GAAG,mBAAmB,GAAG,cAAc,CAAC;IACrF;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,iEAAiE;IACjE,UAAU,EAAE,gBAAgB,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAC7C,4BAA4B,EAC5B,uBAAuB,CACxB,CAAC;AAEF,cAAM,uBAAwB,YAAW,SAAS,CAAC,6BAA6B,CAAC;IAC/E,KAAK,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAM;IAClD,OAAO,CAAC,kBAAkB,CAAC,CAAsB;IACjD,OAAO,CAAC,aAAa,CAAC,CAA4B;IAElD;;;;;;;;OAQG;IACH,iBAAiB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI;IAKpD;;;;;;;;;OASG;IACH,YAAY,CAAC,YAAY,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,IAAI;IAK3D,KAAK,CACH,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,6BAA6B;CA8CjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,6BAA6B,IAAI,wBAAwB,CAExE"}
1
+ {"version":3,"file":"bucket-deployment-builder.d.ts","sourceRoot":"","sources":["../src/bucket-deployment-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAA8B,MAAM,+BAA+B,CAAC;AAC7F,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,KAAK,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAEd,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,KAAK,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAEjF;;GAEG;AACH,MAAM,WAAW,6BAA6B;IAC5C,iEAAiE;IACjE,UAAU,EAAE,gBAAgB,CAAC;IAE7B;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAC7C,4BAA4B,EAC5B,uBAAuB,CACxB,CAAC;AAEF,cAAM,uBAAwB,YAAW,SAAS,CAAC,6BAA6B,CAAC;IAC/E,KAAK,EAAE,OAAO,CAAC,4BAA4B,CAAC,CAAM;IAClD,OAAO,CAAC,kBAAkB,CAAC,CAAsB;IACjD,OAAO,CAAC,aAAa,CAAC,CAA4B;IAElD;;;;;;;;OAQG;IACH,iBAAiB,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,GAAG,IAAI;IAKpD;;;;;;;;;OASG;IACH,YAAY,CAAC,YAAY,EAAE,UAAU,CAAC,aAAa,CAAC,GAAG,IAAI;IAK3D,KAAK,CACH,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,6BAA6B;CAiDjC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,6BAA6B,IAAI,wBAAwB,CAExE"}
@@ -1,6 +1,7 @@
1
- import { BucketDeployment, } from "aws-cdk-lib/aws-s3-deployment";
1
+ import { BucketDeployment } from "aws-cdk-lib/aws-s3-deployment";
2
2
  import { Builder, resolve, } from "@composurecdk/core";
3
- import { BUCKET_DEPLOYMENT_DEFAULTS } from "./bucket-deployment-defaults.js";
3
+ import { createLogGroupBuilder } from "@composurecdk/logs";
4
+ import { effectiveDefaults } from "./bucket-deployment-defaults.js";
4
5
  class BucketDeploymentBuilder {
5
6
  props = {};
6
7
  _destinationBucket;
@@ -46,17 +47,18 @@ class BucketDeploymentBuilder {
46
47
  throw new Error(`BucketDeploymentBuilder "${id}" requires at least one source. ` +
47
48
  `Call .sources() with an array of ISource.`);
48
49
  }
49
- const resolvedDistribution = this._distribution
50
- ? resolve(this._distribution, ctx)
51
- : undefined;
52
- // Only apply distributionPaths default when a distribution is present;
53
- // CDK throws if distributionPaths is set without a distribution.
54
- const { distributionPaths: _distPaths, ...defaultsWithoutPaths } = BUCKET_DEPLOYMENT_DEFAULTS;
55
- const effectiveDefaults = resolvedDistribution
56
- ? BUCKET_DEPLOYMENT_DEFAULTS
57
- : defaultsWithoutPaths;
50
+ const resolvedDistribution = this._distribution ? resolve(this._distribution, ctx) : undefined;
51
+ // Auto-create a managed LogGroup for the deployment's backing Lambda
52
+ // unless the user supplied their own, matching the Lambda builder pattern.
53
+ let logGroup;
54
+ let logGroupProps = {};
55
+ if (!deployProps.logGroup) {
56
+ logGroup = createLogGroupBuilder().build(scope, `${id}LogGroup`).logGroup;
57
+ logGroupProps = { logGroup };
58
+ }
58
59
  const mergedProps = {
59
- ...effectiveDefaults,
60
+ ...effectiveDefaults(!!resolvedDistribution),
61
+ ...logGroupProps,
60
62
  ...deployProps,
61
63
  ...(resolvedDistribution ? { distribution: resolvedDistribution } : {}),
62
64
  sources,
@@ -64,6 +66,7 @@ class BucketDeploymentBuilder {
64
66
  };
65
67
  return {
66
68
  deployment: new BucketDeployment(scope, id, mergedProps),
69
+ logGroup,
67
70
  };
68
71
  }
69
72
  }
@@ -1 +1 @@
1
- {"version":3,"file":"bucket-deployment-builder.js","sourceRoot":"","sources":["../src/bucket-deployment-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,GAGjB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,EACL,OAAO,EAGP,OAAO,GAER,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC;AAuD7E,MAAM,uBAAuB;IAC3B,KAAK,GAA0C,EAAE,CAAC;IAC1C,kBAAkB,CAAuB;IACzC,aAAa,CAA6B;IAElD;;;;;;;;OAQG;IACH,iBAAiB,CAAC,MAA2B;QAC3C,IAAI,CAAC,kBAAkB,GAAG,MAAM,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;OASG;IACH,YAAY,CAAC,YAAuC;QAClD,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CACH,KAAiB,EACjB,EAAU,EACV,OAAgC;QAEhC,MAAM,GAAG,GAAG,OAAO,IAAI,EAAE,CAAC;QAE1B,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB;YAC5C,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC;YACvC,CAAC,CAAC,SAAS,CAAC;QAEd,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,4BAA4B,EAAE,mCAAmC;gBAC/D,4DAA4D,CAC/D,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAE/C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CACb,4BAA4B,EAAE,kCAAkC;gBAC9D,2CAA2C,CAC9C,CAAC;QACJ,CAAC;QAED,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa;YAC7C,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC;YAClC,CAAC,CAAC,SAAS,CAAC;QAEd,uEAAuE;QACvE,iEAAiE;QACjE,MAAM,EAAE,iBAAiB,EAAE,UAAU,EAAE,GAAG,oBAAoB,EAAE,GAAG,0BAA0B,CAAC;QAC9F,MAAM,iBAAiB,GAAG,oBAAoB;YAC5C,CAAC,CAAC,0BAA0B;YAC5B,CAAC,CAAC,oBAAoB,CAAC;QAEzB,MAAM,WAAW,GAAG;YAClB,GAAG,iBAAiB;YACpB,GAAG,WAAW;YACd,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,OAAO;YACP,iBAAiB,EAAE,cAAc;SACT,CAAC;QAE3B,OAAO;YACL,UAAU,EAAE,IAAI,gBAAgB,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC;SACzD,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,6BAA6B;IAC3C,OAAO,OAAO,CAAwD,uBAAuB,CAAC,CAAC;AACjG,CAAC"}
1
+ {"version":3,"file":"bucket-deployment-builder.js","sourceRoot":"","sources":["../src/bucket-deployment-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAA8B,MAAM,+BAA+B,CAAC;AAK7F,OAAO,EACL,OAAO,EAGP,OAAO,GAER,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAC;AAoDpE,MAAM,uBAAuB;IAC3B,KAAK,GAA0C,EAAE,CAAC;IAC1C,kBAAkB,CAAuB;IACzC,aAAa,CAA6B;IAElD;;;;;;;;OAQG;IACH,iBAAiB,CAAC,MAA2B;QAC3C,IAAI,CAAC,kBAAkB,GAAG,MAAM,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;OASG;IACH,YAAY,CAAC,YAAuC;QAClD,IAAI,CAAC,aAAa,GAAG,YAAY,CAAC;QAClC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CACH,KAAiB,EACjB,EAAU,EACV,OAAgC;QAEhC,MAAM,GAAG,GAAG,OAAO,IAAI,EAAE,CAAC;QAE1B,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB;YAC5C,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,CAAC;YACvC,CAAC,CAAC,SAAS,CAAC;QAEd,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CACb,4BAA4B,EAAE,mCAAmC;gBAC/D,4DAA4D,CAC/D,CAAC;QACJ,CAAC;QAED,MAAM,EAAE,OAAO,EAAE,GAAG,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC;QAE/C,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,KAAK,CACb,4BAA4B,EAAE,kCAAkC;gBAC9D,2CAA2C,CAC9C,CAAC;QACJ,CAAC;QAED,MAAM,oBAAoB,GAAG,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE/F,qEAAqE;QACrE,2EAA2E;QAC3E,IAAI,QAA8B,CAAC;QACnC,IAAI,aAAa,GAAG,EAAE,CAAC;QAEvB,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;YAC1B,QAAQ,GAAG,qBAAqB,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,QAAQ,CAAC;YAC1E,aAAa,GAAG,EAAE,QAAQ,EAAE,CAAC;QAC/B,CAAC;QAED,MAAM,WAAW,GAAG;YAClB,GAAG,iBAAiB,CAAC,CAAC,CAAC,oBAAoB,CAAC;YAC5C,GAAG,aAAa;YAChB,GAAG,WAAW;YACd,GAAG,CAAC,oBAAoB,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,oBAAoB,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvE,OAAO;YACP,iBAAiB,EAAE,cAAc;SACT,CAAC;QAE3B,OAAO;YACL,UAAU,EAAE,IAAI,gBAAgB,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC;YACxD,QAAQ;SACT,CAAC;IACJ,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,6BAA6B;IAC3C,OAAO,OAAO,CAAwD,uBAAuB,CAAC,CAAC;AACjG,CAAC"}
@@ -1,8 +1,51 @@
1
- import type { BucketDeploymentBuilderProps } from "./bucket-deployment-builder.js";
1
+ import type { BucketDeploymentBuilderProps } from "./bucket-deployment-props.js";
2
2
  /**
3
- * Sensible defaults applied to every S3 BucketDeployment built with
4
- * {@link createBucketDeploymentBuilder}. Each property can be individually
5
- * overridden via the builder's fluent API.
3
+ * Secure, AWS-recommended defaults applied to every S3 BucketDeployment
4
+ * built with {@link createBucketDeploymentBuilder}. Each property can be
5
+ * individually overridden via the builder's fluent API.
6
6
  */
7
- export declare const BUCKET_DEPLOYMENT_DEFAULTS: Partial<BucketDeploymentBuilderProps>;
7
+ export declare const BUCKET_DEPLOYMENT_DEFAULTS: {
8
+ sources?: import("aws-cdk-lib/aws-s3-deployment").ISource[];
9
+ accessControl?: import("aws-cdk-lib/aws-s3").BucketAccessControl;
10
+ destinationKeyPrefix?: string;
11
+ extract?: boolean;
12
+ exclude?: string[];
13
+ include?: string[];
14
+ prune?: boolean;
15
+ retainOnDelete?: boolean;
16
+ distributionPaths?: string[];
17
+ waitForDistributionInvalidation?: boolean;
18
+ logRetention?: import("aws-cdk-lib/aws-logs").RetentionDays;
19
+ logGroup?: import("aws-cdk-lib/aws-logs").ILogGroupRef;
20
+ memoryLimit?: number;
21
+ ephemeralStorageSize?: import("aws-cdk-lib").Size;
22
+ useEfs?: boolean;
23
+ role?: import("aws-cdk-lib/aws-iam").IRole;
24
+ metadata?: {
25
+ [key: string]: string;
26
+ };
27
+ cacheControl?: import("aws-cdk-lib/aws-s3-deployment").CacheControl[];
28
+ contentDisposition?: string;
29
+ contentEncoding?: string;
30
+ contentLanguage?: string;
31
+ contentType?: string;
32
+ expires?: import("aws-cdk-lib").Expiration;
33
+ serverSideEncryption?: import("aws-cdk-lib/aws-s3-deployment").ServerSideEncryption;
34
+ storageClass?: import("aws-cdk-lib/aws-s3-deployment").StorageClass;
35
+ websiteRedirectLocation?: string;
36
+ serverSideEncryptionAwsKmsKeyId?: string;
37
+ serverSideEncryptionCustomerAlgorithm?: string;
38
+ vpc?: import("aws-cdk-lib/aws-ec2").IVpc;
39
+ vpcSubnets?: import("aws-cdk-lib/aws-ec2").SubnetSelection;
40
+ signContent?: boolean;
41
+ outputObjectKeys?: boolean;
42
+ securityGroups?: import("aws-cdk-lib/aws-ec2").ISecurityGroup[];
43
+ };
44
+ /**
45
+ * Returns the appropriate defaults based on whether a CloudFront
46
+ * distribution is present. CDK throws if `distributionPaths` is set
47
+ * without a distribution, so distribution-specific defaults are only
48
+ * included when applicable.
49
+ */
50
+ export declare function effectiveDefaults(hasDistribution: boolean): Partial<BucketDeploymentBuilderProps>;
8
51
  //# sourceMappingURL=bucket-deployment-defaults.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bucket-deployment-defaults.d.ts","sourceRoot":"","sources":["../src/bucket-deployment-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,gCAAgC,CAAC;AAEnF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,OAAO,CAAC,4BAA4B,CAc5E,CAAC"}
1
+ {"version":3,"file":"bucket-deployment-defaults.d.ts","sourceRoot":"","sources":["../src/bucket-deployment-defaults.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAmDjF;;;;GAIG;AACH,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAGtC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,eAAe,EAAE,OAAO,GAAG,OAAO,CAAC,4BAA4B,CAAC,CAEjG"}
@@ -1,20 +1,64 @@
1
1
  /**
2
- * Sensible defaults applied to every S3 BucketDeployment built with
3
- * {@link createBucketDeploymentBuilder}. Each property can be individually
4
- * overridden via the builder's fluent API.
2
+ * Defaults that apply regardless of whether a CloudFront distribution is
3
+ * present. Split from distribution-specific defaults so the builder can
4
+ * merge without runtime filtering.
5
5
  */
6
- export const BUCKET_DEPLOYMENT_DEFAULTS = {
7
- /**
8
- * Invalidate all paths by default so that deployed content is immediately
9
- * visible through CloudFront.
10
- * @see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html
11
- */
12
- distributionPaths: ["/*"],
6
+ const BASE_DEFAULTS = {
13
7
  /**
14
8
  * Remove files from the destination bucket that are not present in the
15
- * source, keeping the bucket in sync with the deployed assets.
16
- * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeploymentProps.html#prune
9
+ * source, keeping the bucket in sync with the deployed assets and
10
+ * preventing stale content from being served.
11
+ * @see https://docs.aws.amazon.com/wellarchitected/latest/operational-excellence-pillar/ops_evolve_ops_learned_from_experience.html
17
12
  */
18
13
  prune: true,
14
+ /**
15
+ * Allocate 256 MiB to the deployment Lambda. The CDK default of 128 MiB
16
+ * is insufficient for deployments with more than a handful of files and
17
+ * can cause silent failures or timeouts.
18
+ * @see https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_withstand_component_failures_avoid_hard_coded_limits.html
19
+ * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment-readme.html#memory-limit
20
+ */
21
+ memoryLimit: 256,
22
+ /**
23
+ * Do not retain deployed files when the stack is deleted. This aligns
24
+ * with `prune: true` semantics — the deployment keeps the bucket in
25
+ * sync with the source, so retaining stale files on deletion is
26
+ * inconsistent. The destination bucket's own removal policy governs
27
+ * whether the bucket itself is retained.
28
+ * @see https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_s3_deployment.BucketDeploymentProps.html#retainondelete
29
+ */
30
+ retainOnDelete: false,
31
+ };
32
+ /**
33
+ * Defaults that only apply when a CloudFront distribution is configured.
34
+ * CDK throws if `distributionPaths` is set without a distribution.
35
+ */
36
+ const DISTRIBUTION_DEFAULTS = {
37
+ /**
38
+ * Invalidate all paths by default so that deployed content is immediately
39
+ * visible through CloudFront. Uses a wildcard path which counts as a
40
+ * single invalidation path. For deployments that only change a subset of
41
+ * files, override with specific paths to reduce origin fetches.
42
+ * @see https://docs.aws.amazon.com/wellarchitected/latest/reliability-pillar/rel_withstand_component_failures_static_stability.html
43
+ */
44
+ distributionPaths: ["/*"],
19
45
  };
46
+ /**
47
+ * Secure, AWS-recommended defaults applied to every S3 BucketDeployment
48
+ * built with {@link createBucketDeploymentBuilder}. Each property can be
49
+ * individually overridden via the builder's fluent API.
50
+ */
51
+ export const BUCKET_DEPLOYMENT_DEFAULTS = {
52
+ ...BASE_DEFAULTS,
53
+ ...DISTRIBUTION_DEFAULTS,
54
+ };
55
+ /**
56
+ * Returns the appropriate defaults based on whether a CloudFront
57
+ * distribution is present. CDK throws if `distributionPaths` is set
58
+ * without a distribution, so distribution-specific defaults are only
59
+ * included when applicable.
60
+ */
61
+ export function effectiveDefaults(hasDistribution) {
62
+ return hasDistribution ? { ...BASE_DEFAULTS, ...DISTRIBUTION_DEFAULTS } : BASE_DEFAULTS;
63
+ }
20
64
  //# sourceMappingURL=bucket-deployment-defaults.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"bucket-deployment-defaults.js","sourceRoot":"","sources":["../src/bucket-deployment-defaults.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAA0C;IAC/E;;;;OAIG;IACH,iBAAiB,EAAE,CAAC,IAAI,CAAC;IAEzB;;;;OAIG;IACH,KAAK,EAAE,IAAI;CACZ,CAAC"}
1
+ {"version":3,"file":"bucket-deployment-defaults.js","sourceRoot":"","sources":["../src/bucket-deployment-defaults.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,MAAM,aAAa,GAA0C;IAC3D;;;;;OAKG;IACH,KAAK,EAAE,IAAI;IAEX;;;;;;OAMG;IACH,WAAW,EAAE,GAAG;IAEhB;;;;;;;OAOG;IACH,cAAc,EAAE,KAAK;CACtB,CAAC;AAEF;;;GAGG;AACH,MAAM,qBAAqB,GAA0C;IACnE;;;;;;OAMG;IACH,iBAAiB,EAAE,CAAC,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG;IACxC,GAAG,aAAa;IAChB,GAAG,qBAAqB;CACzB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAAC,eAAwB;IACxD,OAAO,eAAe,CAAC,CAAC,CAAC,EAAE,GAAG,aAAa,EAAE,GAAG,qBAAqB,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC;AAC1F,CAAC"}
@@ -0,0 +1,18 @@
1
+ import type { BucketDeploymentProps, ISource } from "aws-cdk-lib/aws-s3-deployment";
2
+ /**
3
+ * Configuration properties for the S3 bucket deployment builder.
4
+ *
5
+ * Extends the CDK {@link BucketDeploymentProps} but replaces
6
+ * `destinationBucket` and `distribution` with builder-managed fields that
7
+ * support {@link Resolvable} for cross-component wiring via {@link ref}.
8
+ *
9
+ * `sources` is set via the builder's fluent API rather than the constructor.
10
+ */
11
+ export interface BucketDeploymentBuilderProps extends Omit<BucketDeploymentProps, "sources" | "destinationBucket" | "distribution"> {
12
+ /**
13
+ * The sources from which to deploy content. Typically created with
14
+ * `Source.asset("./path")` or `Source.data("key", "content")`.
15
+ */
16
+ sources?: ISource[];
17
+ }
18
+ //# sourceMappingURL=bucket-deployment-props.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-deployment-props.d.ts","sourceRoot":"","sources":["../src/bucket-deployment-props.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAEpF;;;;;;;;GAQG;AACH,MAAM,WAAW,4BAA6B,SAAQ,IAAI,CACxD,qBAAqB,EACrB,SAAS,GAAG,mBAAmB,GAAG,cAAc,CACjD;IACC;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,EAAE,CAAC;CACrB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=bucket-deployment-props.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bucket-deployment-props.js","sourceRoot":"","sources":["../src/bucket-deployment-props.ts"],"names":[],"mappings":""}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,7 @@
1
1
  export { createBucketBuilder, type BucketBuilderResult, type IBucketBuilder, } from "./bucket-builder.js";
2
2
  export { BUCKET_DEFAULTS } from "./defaults.js";
3
+ export { type BucketAlarmConfig } from "./alarm-config.js";
4
+ export { BUCKET_ALARM_DEFAULTS } from "./alarm-defaults.js";
5
+ export { createBucketDeploymentBuilder, type BucketDeploymentBuilderResult, type IBucketDeploymentBuilder, } from "./bucket-deployment-builder.js";
6
+ export { BUCKET_DEPLOYMENT_DEFAULTS } from "./bucket-deployment-defaults.js";
3
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAC3D,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,GAC9B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
1
  export { createBucketBuilder, } from "./bucket-builder.js";
2
2
  export { BUCKET_DEFAULTS } from "./defaults.js";
3
+ export { BUCKET_ALARM_DEFAULTS } from "./alarm-defaults.js";
4
+ export { createBucketDeploymentBuilder, } from "./bucket-deployment-builder.js";
5
+ export { BUCKET_DEPLOYMENT_DEFAULTS } from "./bucket-deployment-defaults.js";
3
6
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAGpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,GAGpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EACL,6BAA6B,GAG9B,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,0BAA0B,EAAE,MAAM,iCAAiC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@composurecdk/s3",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "Composable S3 bucket builder with well-architected defaults",
5
5
  "repository": {
6
6
  "type": "git",
@@ -35,7 +35,9 @@
35
35
  },
36
36
  "type": "module",
37
37
  "peerDependencies": {
38
- "@composurecdk/core": "^0.1.0",
38
+ "@composurecdk/cloudwatch": "^0.3.0",
39
+ "@composurecdk/core": "^0.3.0",
40
+ "@composurecdk/logs": "^0.3.0",
39
41
  "aws-cdk-lib": "^2.0.0",
40
42
  "constructs": "^10.0.0"
41
43
  },