@composurecdk/acm 0.3.2

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,144 @@
1
+ # @composurecdk/acm
2
+
3
+ AWS Certificate Manager builder for [ComposureCDK](../../README.md).
4
+
5
+ This package provides a fluent builder for ACM certificates with secure, AWS-recommended defaults, first-class DNS validation wiring, and a recommended `DaysToExpiry` alarm. It wraps the CDK [Certificate](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_certificatemanager.Certificate.html) construct — refer to the CDK documentation for the full set of configurable properties.
6
+
7
+ ## Certificate Builder
8
+
9
+ ```ts
10
+ import { createCertificateBuilder } from "@composurecdk/acm";
11
+ import { HostedZone } from "aws-cdk-lib/aws-route53";
12
+
13
+ const zone = HostedZone.fromLookup(stack, "Zone", { domainName: "example.com" });
14
+
15
+ const { certificate } = createCertificateBuilder()
16
+ .domainName("example.com")
17
+ .subjectAlternativeNames(["www.example.com"])
18
+ .validationZone(zone)
19
+ .build(stack, "SiteCert");
20
+ ```
21
+
22
+ Every [CertificateProps](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_certificatemanager.CertificateProps.html) property is available as a fluent setter on the builder.
23
+
24
+ ## Secure Defaults
25
+
26
+ `createCertificateBuilder` applies the following defaults. Each can be overridden via the builder's fluent API.
27
+
28
+ | Property | Default | Rationale |
29
+ | ---------------------------- | ----------------------- | ----------------------------------------------------------------------------- |
30
+ | `keyAlgorithm` | `KeyAlgorithm.RSA_2048` | Broadest client and AWS service compatibility (CloudFront, API Gateway, ALB). |
31
+ | `transparencyLoggingEnabled` | `true` | Required by modern browsers; enables public detection of mis-issuance. |
32
+
33
+ These defaults are guided by the [AWS ACM Best Practices](https://docs.aws.amazon.com/acm/latest/userguide/acm-bestpractices.html).
34
+
35
+ The defaults are exported as `CERTIFICATE_DEFAULTS` for visibility and testing:
36
+
37
+ ```ts
38
+ import { CERTIFICATE_DEFAULTS } from "@composurecdk/acm";
39
+ ```
40
+
41
+ ## DNS Validation
42
+
43
+ Email-based validation is not used by default — it blocks stack creation until a human clicks a link in an email. The builder requires one of:
44
+
45
+ - `validationZone(zone)` — the hosted zone that owns every domain on the certificate.
46
+ - `validationZones({ "apex.com": apexZone, "alt.net": altZone })` — when domains span multiple zones.
47
+ - `validation(CertificateValidation.fromEmail())` — explicit opt-in to email validation (not recommended).
48
+
49
+ `validationZone` / `validationZones` accept a `Resolvable<IHostedZone>`, so a hosted zone produced by a sibling component can be wired via `ref()`.
50
+
51
+ ## Certificate Lifetime
52
+
53
+ ACM-issued public certificates are valid for [395 days](https://docs.aws.amazon.com/acm/latest/userguide/acm-certificate.html) and auto-renew starting ~60 days before expiry, provided the DNS validation records remain published. Renewal is therefore the happy path — the `daysToExpiry` alarm below is a safety net for the cases where it can't complete (records removed, zone delegation broken, etc.). For imported certificates, which do not auto-renew, `daysToExpiry` is the primary expiry control.
54
+
55
+ ## Recommended Alarms
56
+
57
+ The builder creates [AWS-recommended CloudWatch alarms](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager) by default. No alarm actions are configured — access alarms from the build result to add SNS topics or other actions.
58
+
59
+ | Alarm | Metric | Default threshold |
60
+ | -------------- | ----------------------------- | ----------------- |
61
+ | `daysToExpiry` | DaysToExpiry (Minimum, 1 day) | ≤ 45 days |
62
+
63
+ `treatMissingData` defaults to `notBreaching`: once a certificate has effectively expired, ACM stops emitting `DaysToExpiry`, and there is nothing left to alarm about.
64
+
65
+ The defaults are exported as `CERTIFICATE_ALARM_DEFAULTS` for visibility and testing:
66
+
67
+ ```ts
68
+ import { CERTIFICATE_ALARM_DEFAULTS } from "@composurecdk/acm";
69
+ ```
70
+
71
+ ### Customizing thresholds
72
+
73
+ Override individual alarm properties via `recommendedAlarms`. Unspecified fields keep their defaults.
74
+
75
+ ```ts
76
+ const cert = createCertificateBuilder()
77
+ .domainName("example.com")
78
+ .validationZone(zone)
79
+ .recommendedAlarms({
80
+ daysToExpiry: { threshold: 30 },
81
+ });
82
+ ```
83
+
84
+ ### Disabling alarms
85
+
86
+ Disable all recommended alarms:
87
+
88
+ ```ts
89
+ builder.recommendedAlarms(false);
90
+ // or
91
+ builder.recommendedAlarms({ enabled: false });
92
+ ```
93
+
94
+ Disable the daysToExpiry alarm individually:
95
+
96
+ ```ts
97
+ builder.recommendedAlarms({ daysToExpiry: false });
98
+ ```
99
+
100
+ ### Custom alarms
101
+
102
+ Add custom alarms alongside the recommended ones via `addAlarm`. The callback receives an `AlarmDefinitionBuilder` typed to `ICertificate`, so the metric factory has access to the certificate at build time.
103
+
104
+ ```ts
105
+ import { Metric } from "aws-cdk-lib/aws-cloudwatch";
106
+ import { Duration } from "aws-cdk-lib";
107
+
108
+ const cert = createCertificateBuilder()
109
+ .domainName("example.com")
110
+ .validationZone(zone)
111
+ .addAlarm("urgentExpiry", (alarm) =>
112
+ alarm
113
+ .metric(
114
+ (c) =>
115
+ new Metric({
116
+ namespace: "AWS/CertificateManager",
117
+ metricName: "DaysToExpiry",
118
+ dimensionsMap: { CertificateArn: c.certificateArn },
119
+ statistic: "Minimum",
120
+ period: Duration.days(1),
121
+ }),
122
+ )
123
+ .threshold(10)
124
+ .lessThanOrEqual()
125
+ .description("Certificate very close to expiry — page oncall"),
126
+ );
127
+ ```
128
+
129
+ ### Applying alarm actions
130
+
131
+ Alarms are returned in the build result as `Record<string, Alarm>`:
132
+
133
+ ```ts
134
+ const result = cert.build(stack, "SiteCert");
135
+
136
+ const alertTopic = new Topic(stack, "AlertTopic");
137
+ for (const alarm of Object.values(result.alarms)) {
138
+ alarm.addAlarmAction(new SnsAction(alertTopic));
139
+ }
140
+ ```
141
+
142
+ ## CloudFront certificates
143
+
144
+ CloudFront viewer certificates must live in `us-east-1`. The ACM builder cannot enforce this on its own — certificates in other regions are perfectly valid for ALB, API Gateway, and other services. The constraint is enforced by CloudFront at association time, so if your certificate is for CloudFront, place the builder in a stack that targets `us-east-1` (e.g. via [`createStackBuilder`](../cloudformation/README.md) with an `env`).
@@ -0,0 +1,34 @@
1
+ import type { AlarmConfig } from "@composurecdk/cloudwatch";
2
+ /**
3
+ * Controls which recommended alarms are created for an ACM certificate.
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
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
9
+ */
10
+ export interface CertificateAlarmConfig {
11
+ /**
12
+ * Master switch: set to `false` to disable all recommended alarms.
13
+ * Individual alarms can also be disabled via their own entry.
14
+ * @default true
15
+ */
16
+ enabled?: boolean;
17
+ /**
18
+ * Alarm when the certificate is approaching expiry.
19
+ *
20
+ * ACM public certificates auto-renew, so this alarm is primarily a
21
+ * safety net for the edge cases where renewal cannot complete (for
22
+ * example, when DNS validation records have been removed from the
23
+ * zone). For imported certificates, which do not auto-renew, this
24
+ * alarm is the primary expiry control.
25
+ *
26
+ * Metric: `AWS/CertificateManager DaysToExpiry`, statistic Minimum,
27
+ * period 1 day, dimension `CertificateArn`.
28
+ * Default threshold: &le; 45 days.
29
+ *
30
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
31
+ */
32
+ daysToExpiry?: AlarmConfig | false;
33
+ }
34
+ //# 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;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAElB;;;;;;;;;;;;;;OAcG;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,13 @@
1
+ import type { AlarmConfig } from "@composurecdk/cloudwatch";
2
+ interface CertificateAlarmDefaults {
3
+ enabled: true;
4
+ daysToExpiry: Required<AlarmConfig>;
5
+ }
6
+ /**
7
+ * AWS-recommended default alarm configuration for ACM certificates.
8
+ *
9
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
10
+ */
11
+ export declare const CERTIFICATE_ALARM_DEFAULTS: CertificateAlarmDefaults;
12
+ export {};
13
+ //# 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,wBAAwB;IAChC,OAAO,EAAE,IAAI,CAAC;IACd,YAAY,EAAE,QAAQ,CAAC,WAAW,CAAC,CAAC;CACrC;AAED;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,EAAE,wBAmBxC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { TreatMissingData } from "aws-cdk-lib/aws-cloudwatch";
2
+ /**
3
+ * AWS-recommended default alarm configuration for ACM certificates.
4
+ *
5
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
6
+ */
7
+ export const CERTIFICATE_ALARM_DEFAULTS = {
8
+ enabled: true,
9
+ /**
10
+ * Alarm 45 days before expiry — AWS's recommended threshold. For public
11
+ * certificates, ACM begins auto-renewal attempts around 60 days out, so
12
+ * 45 days leaves a two-week window to investigate renewal failures
13
+ * before the certificate expires.
14
+ *
15
+ * `treatMissingData: notBreaching` avoids false alarms after a
16
+ * certificate has effectively expired — at that point ACM stops
17
+ * emitting DaysToExpiry, and there is nothing left to alarm about.
18
+ */
19
+ daysToExpiry: {
20
+ threshold: 45,
21
+ evaluationPeriods: 1,
22
+ datapointsToAlarm: 1,
23
+ treatMissingData: TreatMissingData.NOT_BREACHING,
24
+ },
25
+ };
26
+ //# 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;AAQ9D;;;;GAIG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAA6B;IAClE,OAAO,EAAE,IAAI;IAEb;;;;;;;;;OASG;IACH,YAAY,EAAE;QACZ,SAAS,EAAE,EAAE;QACb,iBAAiB,EAAE,CAAC;QACpB,iBAAiB,EAAE,CAAC;QACpB,gBAAgB,EAAE,gBAAgB,CAAC,aAAa;KACjD;CACF,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
2
+ import type { ICertificate } from "aws-cdk-lib/aws-certificatemanager";
3
+ import type { IConstruct } from "constructs";
4
+ import type { AlarmDefinition } from "@composurecdk/cloudwatch";
5
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
6
+ import type { CertificateAlarmConfig } from "./alarm-config.js";
7
+ /**
8
+ * Resolves the recommended alarm configuration into fully-resolved
9
+ * {@link AlarmDefinition}s for an ACM certificate.
10
+ */
11
+ export declare function resolveCertificateAlarmDefinitions(certificate: ICertificate, config: CertificateAlarmConfig | undefined): AlarmDefinition[];
12
+ /**
13
+ * Creates AWS-recommended CloudWatch alarms for an ACM certificate,
14
+ * merging recommended definitions with any custom alarm builders.
15
+ *
16
+ * @param scope - CDK construct scope for creating alarm constructs.
17
+ * @param id - Base identifier for alarm construct ids.
18
+ * @param certificate - The ACM certificate to create alarms for.
19
+ * @param config - User-provided alarm configuration, or `false` to disable all.
20
+ * @param customAlarms - Custom alarm builders added via `addAlarm()`.
21
+ * @returns A record mapping alarm keys to their created Alarm constructs.
22
+ *
23
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
24
+ */
25
+ export declare function createCertificateAlarms(scope: IConstruct, id: string, certificate: ICertificate, config: CertificateAlarmConfig | false | undefined, customAlarms?: AlarmDefinitionBuilder<ICertificate>[]): Record<string, Alarm>;
26
+ //# sourceMappingURL=certificate-alarms.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate-alarms.d.ts","sourceRoot":"","sources":["../src/certificate-alarms.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,KAAK,EAAsB,MAAM,4BAA4B,CAAC;AAC5E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAoC,MAAM,0BAA0B,CAAC;AACpG,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAKhE;;;GAGG;AACH,wBAAgB,kCAAkC,CAChD,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,sBAAsB,GAAG,SAAS,GACzC,eAAe,EAAE,CAoBnB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EACjB,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,YAAY,EACzB,MAAM,EAAE,sBAAsB,GAAG,KAAK,GAAG,SAAS,EAClD,YAAY,GAAE,sBAAsB,CAAC,YAAY,CAAC,EAAO,GACxD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAUvB"}
@@ -0,0 +1,52 @@
1
+ import { Duration } from "aws-cdk-lib";
2
+ import { ComparisonOperator } from "aws-cdk-lib/aws-cloudwatch";
3
+ import { createAlarms, resolveAlarmConfig } from "@composurecdk/cloudwatch";
4
+ import { CERTIFICATE_ALARM_DEFAULTS } from "./alarm-defaults.js";
5
+ const METRIC_PERIOD = Duration.days(1);
6
+ /**
7
+ * Resolves the recommended alarm configuration into fully-resolved
8
+ * {@link AlarmDefinition}s for an ACM certificate.
9
+ */
10
+ export function resolveCertificateAlarmDefinitions(certificate, config) {
11
+ if (config?.enabled === false)
12
+ return [];
13
+ const definitions = [];
14
+ if (config?.daysToExpiry !== false) {
15
+ const cfg = resolveAlarmConfig(config?.daysToExpiry, CERTIFICATE_ALARM_DEFAULTS.daysToExpiry);
16
+ definitions.push({
17
+ key: "daysToExpiry",
18
+ metric: certificate.metricDaysToExpiry({ period: METRIC_PERIOD }),
19
+ threshold: cfg.threshold,
20
+ comparisonOperator: ComparisonOperator.LESS_THAN_OR_EQUAL_TO_THRESHOLD,
21
+ evaluationPeriods: cfg.evaluationPeriods,
22
+ datapointsToAlarm: cfg.datapointsToAlarm,
23
+ treatMissingData: cfg.treatMissingData,
24
+ description: `ACM certificate is approaching expiry. Threshold: <= ${String(cfg.threshold)} days remaining.`,
25
+ });
26
+ }
27
+ return definitions;
28
+ }
29
+ /**
30
+ * Creates AWS-recommended CloudWatch alarms for an ACM certificate,
31
+ * merging recommended definitions with any custom alarm builders.
32
+ *
33
+ * @param scope - CDK construct scope for creating alarm constructs.
34
+ * @param id - Base identifier for alarm construct ids.
35
+ * @param certificate - The ACM certificate to create alarms for.
36
+ * @param config - User-provided alarm configuration, or `false` to disable all.
37
+ * @param customAlarms - Custom alarm builders added via `addAlarm()`.
38
+ * @returns A record mapping alarm keys to their created Alarm constructs.
39
+ *
40
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
41
+ */
42
+ export function createCertificateAlarms(scope, id, certificate, config, customAlarms = []) {
43
+ if (config === false)
44
+ return {};
45
+ const enabled = config?.enabled ?? CERTIFICATE_ALARM_DEFAULTS.enabled;
46
+ if (!enabled)
47
+ return {};
48
+ const recommended = resolveCertificateAlarmDefinitions(certificate, config);
49
+ const custom = customAlarms.map((b) => b.resolve(certificate));
50
+ return createAlarms(scope, id, [...recommended, ...custom]);
51
+ }
52
+ //# sourceMappingURL=certificate-alarms.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate-alarms.js","sourceRoot":"","sources":["../src/certificate-alarms.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAc,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAI5E,OAAO,EAA0B,YAAY,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAEpG,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC;AAEjE,MAAM,aAAa,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEvC;;;GAGG;AACH,MAAM,UAAU,kCAAkC,CAChD,WAAyB,EACzB,MAA0C;IAE1C,IAAI,MAAM,EAAE,OAAO,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAEzC,MAAM,WAAW,GAAsB,EAAE,CAAC;IAE1C,IAAI,MAAM,EAAE,YAAY,KAAK,KAAK,EAAE,CAAC;QACnC,MAAM,GAAG,GAAG,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,0BAA0B,CAAC,YAAY,CAAC,CAAC;QAC9F,WAAW,CAAC,IAAI,CAAC;YACf,GAAG,EAAE,cAAc;YACnB,MAAM,EAAE,WAAW,CAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;YACjE,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,kBAAkB,EAAE,kBAAkB,CAAC,+BAA+B;YACtE,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;YACxC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;YACxC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;YACtC,WAAW,EAAE,wDAAwD,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,kBAAkB;SAC7G,CAAC,CAAC;IACL,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAiB,EACjB,EAAU,EACV,WAAyB,EACzB,MAAkD,EAClD,eAAuD,EAAE;IAEzD,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,EAAE,CAAC;IAEhC,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,IAAI,0BAA0B,CAAC,OAAO,CAAC;IACtE,IAAI,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IAExB,MAAM,WAAW,GAAG,kCAAkC,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC5E,MAAM,MAAM,GAAG,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC;IAE/D,OAAO,YAAY,CAAC,KAAK,EAAE,EAAE,EAAE,CAAC,GAAG,WAAW,EAAE,GAAG,MAAM,CAAC,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,136 @@
1
+ import { Certificate, type CertificateProps, type ICertificate } from "aws-cdk-lib/aws-certificatemanager";
2
+ import { type Alarm } from "aws-cdk-lib/aws-cloudwatch";
3
+ import type { IHostedZone } from "aws-cdk-lib/aws-route53";
4
+ import { type IConstruct } from "constructs";
5
+ import { type IBuilder, type Lifecycle, type Resolvable } from "@composurecdk/core";
6
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
7
+ import type { CertificateAlarmConfig } from "./alarm-config.js";
8
+ /**
9
+ * Configuration properties for the ACM certificate builder.
10
+ *
11
+ * Extends the CDK {@link CertificateProps} with additional builder-specific
12
+ * options. The `validation` field is augmented by {@link validationZone} /
13
+ * {@link validationZones}, which accept {@link Resolvable} hosted zones so
14
+ * the validation zone can come from a composed component.
15
+ *
16
+ * If neither `validation`, `validationZone`, nor `validationZones` is set,
17
+ * the builder fails fast — falling back to ACM's email-based validation would
18
+ * stall stack creation waiting on a human to click a link.
19
+ */
20
+ interface CertificateBuilderProps extends CertificateProps {
21
+ /**
22
+ * The hosted zone used to automatically create DNS validation records
23
+ * for every domain on the certificate. Accepts a {@link Resolvable} so
24
+ * a zone produced by a sibling component can be wired in via `ref`.
25
+ *
26
+ * Mutually exclusive with {@link validationZones} and {@link CertificateProps.validation}.
27
+ * When set, the builder configures
28
+ * {@link CertificateValidation.fromDns | CertificateValidation.fromDns(zone)}.
29
+ */
30
+ validationZone?: Resolvable<IHostedZone>;
31
+ /**
32
+ * A map of domain name to hosted zone, used when the apex and subject
33
+ * alternative names live in different zones. Each value accepts a
34
+ * {@link Resolvable}. When set, the builder configures
35
+ * {@link CertificateValidation.fromDnsMultiZone}.
36
+ *
37
+ * Mutually exclusive with {@link validationZone} and {@link CertificateProps.validation}.
38
+ */
39
+ validationZones?: Record<string, Resolvable<IHostedZone>>;
40
+ /**
41
+ * Configuration for AWS-recommended CloudWatch alarms.
42
+ *
43
+ * By default, the builder creates a recommended `daysToExpiry` alarm
44
+ * at 45 days. The alarm can be customized or disabled. Set to `false`
45
+ * to disable all alarms.
46
+ *
47
+ * No alarm actions are configured by default since notification
48
+ * methods are user-specific. Access alarms from the build result
49
+ * or use an `afterBuild` hook to apply actions.
50
+ *
51
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
52
+ */
53
+ recommendedAlarms?: CertificateAlarmConfig | false;
54
+ }
55
+ /**
56
+ * The build output of an {@link ICertificateBuilder}. Contains the CDK
57
+ * constructs created during {@link Lifecycle.build}, keyed by role.
58
+ */
59
+ export interface CertificateBuilderResult {
60
+ /** The ACM certificate construct created by the builder. */
61
+ certificate: Certificate;
62
+ /**
63
+ * CloudWatch alarms created for the certificate, keyed by alarm name.
64
+ *
65
+ * Includes both AWS-recommended alarms and any custom alarms added
66
+ * via {@link ICertificateBuilder.addAlarm}. Access individual alarms
67
+ * by key (e.g., `result.alarms.daysToExpiry`).
68
+ *
69
+ * No alarm actions are configured — apply them via the result or an
70
+ * `afterBuild` hook.
71
+ *
72
+ * @see https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/Best_Practice_Recommended_Alarms_AWS_Services.html#CertificateManager
73
+ */
74
+ alarms: Record<string, Alarm>;
75
+ }
76
+ /**
77
+ * A fluent builder for configuring and creating an AWS Certificate Manager
78
+ * certificate.
79
+ *
80
+ * Each configuration property from the CDK {@link CertificateProps} is
81
+ * exposed as an overloaded method: call with a value to set it (returns the
82
+ * builder for chaining), or call with no arguments to read the current value.
83
+ *
84
+ * Validation is DNS-based by default — set
85
+ * {@link CertificateBuilderProps.validationZone | validationZone} (or
86
+ * {@link CertificateBuilderProps.validationZones | validationZones}) with the
87
+ * hosted zone(s) that own the certificate's domains. Accepts a
88
+ * {@link Resolvable} so zones produced by a composed component can be
89
+ * wired in via `ref`.
90
+ *
91
+ * The builder implements {@link Lifecycle}, so it can be used directly as a
92
+ * component in a {@link compose | composed system}. When built, it creates
93
+ * an ACM certificate with the configured properties and returns a
94
+ * {@link CertificateBuilderResult}.
95
+ *
96
+ * The builder also creates AWS-recommended CloudWatch alarms by default
97
+ * (`daysToExpiry` at 45 days). Alarms can be customized or disabled via the
98
+ * `recommendedAlarms` property.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * const cert = createCertificateBuilder()
103
+ * .domainName("example.com")
104
+ * .subjectAlternativeNames(["www.example.com"])
105
+ * .validationZone(zone);
106
+ * ```
107
+ */
108
+ export type ICertificateBuilder = IBuilder<CertificateBuilderProps, CertificateBuilder>;
109
+ declare class CertificateBuilder implements Lifecycle<CertificateBuilderResult> {
110
+ props: Partial<CertificateBuilderProps>;
111
+ private readonly customAlarms;
112
+ addAlarm(key: string, configure: (alarm: AlarmDefinitionBuilder<ICertificate>) => AlarmDefinitionBuilder<ICertificate>): this;
113
+ build(scope: IConstruct, id: string, context?: Record<string, object>): CertificateBuilderResult;
114
+ }
115
+ /**
116
+ * Creates a new {@link ICertificateBuilder} for configuring an ACM certificate.
117
+ *
118
+ * This is the entry point for defining an ACM certificate component. The
119
+ * returned builder exposes every {@link CertificateBuilderProps} property as
120
+ * a fluent setter/getter and implements {@link Lifecycle} for use with
121
+ * {@link compose}.
122
+ *
123
+ * @returns A fluent builder for an ACM certificate.
124
+ *
125
+ * @example
126
+ * ```ts
127
+ * const cert = createCertificateBuilder()
128
+ * .domainName("example.com")
129
+ * .validationZone(zone);
130
+ *
131
+ * const result = cert.build(stack, "SiteCert");
132
+ * ```
133
+ */
134
+ export declare function createCertificateBuilder(): ICertificateBuilder;
135
+ export {};
136
+ //# sourceMappingURL=certificate-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate-builder.d.ts","sourceRoot":"","sources":["../src/certificate-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EAEX,KAAK,gBAAgB,EACrB,KAAK,YAAY,EAClB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,KAAK,KAAK,EAAE,MAAM,4BAA4B,CAAC;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAEL,KAAK,QAAQ,EACb,KAAK,SAAS,EAEd,KAAK,UAAU,EAChB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAIhE;;;;;;;;;;;GAWG;AACH,UAAU,uBAAwB,SAAQ,gBAAgB;IACxD;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC;IAEzC;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC;IAE1D;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,EAAE,sBAAsB,GAAG,KAAK,CAAC;CACpD;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC,4DAA4D;IAC5D,WAAW,EAAE,WAAW,CAAC;IAEzB;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,MAAM,mBAAmB,GAAG,QAAQ,CAAC,uBAAuB,EAAE,kBAAkB,CAAC,CAAC;AAExF,cAAM,kBAAmB,YAAW,SAAS,CAAC,wBAAwB,CAAC;IACrE,KAAK,EAAE,OAAO,CAAC,uBAAuB,CAAC,CAAM;IAC7C,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA8C;IAE3E,QAAQ,CACN,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,CACT,KAAK,EAAE,sBAAsB,CAAC,YAAY,CAAC,KACxC,sBAAsB,CAAC,YAAY,CAAC,GACxC,IAAI;IAKP,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,wBAAwB;CA4DjG;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,IAAI,mBAAmB,CAE9D"}
@@ -0,0 +1,77 @@
1
+ import { Certificate, CertificateValidation, } from "aws-cdk-lib/aws-certificatemanager";
2
+ import { Builder, resolve, } from "@composurecdk/core";
3
+ import { AlarmDefinitionBuilder } from "@composurecdk/cloudwatch";
4
+ import { createCertificateAlarms } from "./certificate-alarms.js";
5
+ import { CERTIFICATE_DEFAULTS } from "./defaults.js";
6
+ class CertificateBuilder {
7
+ props = {};
8
+ customAlarms = [];
9
+ addAlarm(key, configure) {
10
+ this.customAlarms.push(configure(new AlarmDefinitionBuilder(key)));
11
+ return this;
12
+ }
13
+ build(scope, id, context) {
14
+ const { validationZone, validationZones, validation: userValidation, recommendedAlarms: alarmConfig, ...certProps } = this.props;
15
+ if (!certProps.domainName) {
16
+ throw new Error(`CertificateBuilder "${id}" requires a domainName. ` +
17
+ `Call .domainName() with the fully-qualified domain.`);
18
+ }
19
+ if ([userValidation, validationZone, validationZones].filter(Boolean).length > 1) {
20
+ throw new Error(`CertificateBuilder "${id}": 'validation', 'validationZone', and 'validationZones' ` +
21
+ `are mutually exclusive. Set exactly one.`);
22
+ }
23
+ const resolvedContext = context ?? {};
24
+ let validation;
25
+ if (userValidation) {
26
+ validation = userValidation;
27
+ }
28
+ else if (validationZones) {
29
+ const resolvedZones = Object.fromEntries(Object.entries(validationZones).map(([domain, zone]) => [
30
+ domain,
31
+ resolve(zone, resolvedContext),
32
+ ]));
33
+ validation = CertificateValidation.fromDnsMultiZone(resolvedZones);
34
+ }
35
+ else if (validationZone) {
36
+ validation = CertificateValidation.fromDns(resolve(validationZone, resolvedContext));
37
+ }
38
+ else {
39
+ throw new Error(`CertificateBuilder "${id}" requires DNS validation to be configured. ` +
40
+ `Call .validationZone() with the hosted zone for the certificate's domain, ` +
41
+ `or .validationZones() when domains span multiple zones, ` +
42
+ `or .validation() to configure an explicit CertificateValidation. ` +
43
+ `Email validation is not enabled by default because it blocks stack creation.`);
44
+ }
45
+ const mergedProps = {
46
+ ...CERTIFICATE_DEFAULTS,
47
+ ...certProps,
48
+ validation,
49
+ };
50
+ const certificate = new Certificate(scope, id, mergedProps);
51
+ const alarms = createCertificateAlarms(scope, id, certificate, alarmConfig, this.customAlarms);
52
+ return { certificate, alarms };
53
+ }
54
+ }
55
+ /**
56
+ * Creates a new {@link ICertificateBuilder} for configuring an ACM certificate.
57
+ *
58
+ * This is the entry point for defining an ACM certificate component. The
59
+ * returned builder exposes every {@link CertificateBuilderProps} property as
60
+ * a fluent setter/getter and implements {@link Lifecycle} for use with
61
+ * {@link compose}.
62
+ *
63
+ * @returns A fluent builder for an ACM certificate.
64
+ *
65
+ * @example
66
+ * ```ts
67
+ * const cert = createCertificateBuilder()
68
+ * .domainName("example.com")
69
+ * .validationZone(zone);
70
+ *
71
+ * const result = cert.build(stack, "SiteCert");
72
+ * ```
73
+ */
74
+ export function createCertificateBuilder() {
75
+ return Builder(CertificateBuilder);
76
+ }
77
+ //# sourceMappingURL=certificate-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"certificate-builder.js","sourceRoot":"","sources":["../src/certificate-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,WAAW,EACX,qBAAqB,GAGtB,MAAM,oCAAoC,CAAC;AAI5C,OAAO,EACL,OAAO,EAGP,OAAO,GAER,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,OAAO,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AA6GrD,MAAM,kBAAkB;IACtB,KAAK,GAAqC,EAAE,CAAC;IAC5B,YAAY,GAA2C,EAAE,CAAC;IAE3E,QAAQ,CACN,GAAW,EACX,SAEyC;QAEzC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,sBAAsB,CAAe,GAAG,CAAC,CAAC,CAAC,CAAC;QACjF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,KAAiB,EAAE,EAAU,EAAE,OAAgC;QACnE,MAAM,EACJ,cAAc,EACd,eAAe,EACf,UAAU,EAAE,cAAc,EAC1B,iBAAiB,EAAE,WAAW,EAC9B,GAAG,SAAS,EACb,GAAG,IAAI,CAAC,KAAK,CAAC;QAEf,IAAI,CAAC,SAAS,CAAC,UAAU,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,uBAAuB,EAAE,2BAA2B;gBAClD,qDAAqD,CACxD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,cAAc,EAAE,cAAc,EAAE,eAAe,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACjF,MAAM,IAAI,KAAK,CACb,uBAAuB,EAAE,2DAA2D;gBAClF,0CAA0C,CAC7C,CAAC;QACJ,CAAC;QAED,MAAM,eAAe,GAAG,OAAO,IAAI,EAAE,CAAC;QACtC,IAAI,UAAiC,CAAC;QAEtC,IAAI,cAAc,EAAE,CAAC;YACnB,UAAU,GAAG,cAAc,CAAC;QAC9B,CAAC;aAAM,IAAI,eAAe,EAAE,CAAC;YAC3B,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CACtC,MAAM,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC;gBACtD,MAAM;gBACN,OAAO,CAAC,IAAI,EAAE,eAAe,CAAC;aAC/B,CAAC,CACH,CAAC;YACF,UAAU,GAAG,qBAAqB,CAAC,gBAAgB,CAAC,aAAa,CAAC,CAAC;QACrE,CAAC;aAAM,IAAI,cAAc,EAAE,CAAC;YAC1B,UAAU,GAAG,qBAAqB,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC,CAAC;QACvF,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,uBAAuB,EAAE,8CAA8C;gBACrE,4EAA4E;gBAC5E,0DAA0D;gBAC1D,mEAAmE;gBACnE,8EAA8E,CACjF,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GAAG;YAClB,GAAG,oBAAoB;YACvB,GAAG,SAAS;YACZ,UAAU;SACS,CAAC;QAEtB,MAAM,WAAW,GAAG,IAAI,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,CAAC,CAAC;QAE5D,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,EAAE,EAAE,EAAE,WAAW,EAAE,WAAW,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;QAE/F,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,CAAC;IACjC,CAAC;CACF;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,wBAAwB;IACtC,OAAO,OAAO,CAA8C,kBAAkB,CAAC,CAAC;AAClF,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { type CertificateProps } from "aws-cdk-lib/aws-certificatemanager";
2
+ /**
3
+ * Secure, AWS-recommended defaults applied to every ACM certificate built
4
+ * with {@link createCertificateBuilder}. Each property can be individually
5
+ * overridden via the builder's fluent API.
6
+ */
7
+ export declare const CERTIFICATE_DEFAULTS: Partial<CertificateProps>;
8
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAEzF;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,EAAE,OAAO,CAAC,gBAAgB,CAiB1D,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { KeyAlgorithm } from "aws-cdk-lib/aws-certificatemanager";
2
+ /**
3
+ * Secure, AWS-recommended defaults applied to every ACM certificate built
4
+ * with {@link createCertificateBuilder}. Each property can be individually
5
+ * overridden via the builder's fluent API.
6
+ */
7
+ export const CERTIFICATE_DEFAULTS = {
8
+ /**
9
+ * Use RSA-2048 as the key algorithm. Widest client/CDN compatibility
10
+ * (CloudFront, API Gateway, ALB) and sufficient for TLS 1.2 and 1.3.
11
+ * For newer workloads, `KeyAlgorithm.EC_PRIME256V1` offers smaller
12
+ * signatures at comparable security — override via `.keyAlgorithm()`.
13
+ * @see https://docs.aws.amazon.com/acm/latest/userguide/acm-certificate.html#algorithms.title
14
+ */
15
+ keyAlgorithm: KeyAlgorithm.RSA_2048,
16
+ /**
17
+ * Publish certificates to the public Certificate Transparency (CT) logs.
18
+ * CT logging is required by modern browsers to trust a certificate and
19
+ * enables detection of mis-issuance.
20
+ * @see https://docs.aws.amazon.com/acm/latest/userguide/acm-bestpractices.html#best-practices-transparency
21
+ */
22
+ transparencyLoggingEnabled: true,
23
+ };
24
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAyB,MAAM,oCAAoC,CAAC;AAEzF;;;;GAIG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA8B;IAC7D;;;;;;OAMG;IACH,YAAY,EAAE,YAAY,CAAC,QAAQ;IAEnC;;;;;OAKG;IACH,0BAA0B,EAAE,IAAI;CACjC,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { createCertificateBuilder, type CertificateBuilderResult, type ICertificateBuilder, } from "./certificate-builder.js";
2
+ export { CERTIFICATE_DEFAULTS } from "./defaults.js";
3
+ export { type CertificateAlarmConfig } from "./alarm-config.js";
4
+ export { CERTIFICATE_ALARM_DEFAULTS } from "./alarm-defaults.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,GACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { createCertificateBuilder, } from "./certificate-builder.js";
2
+ export { CERTIFICATE_DEFAULTS } from "./defaults.js";
3
+ export { CERTIFICATE_ALARM_DEFAULTS } from "./alarm-defaults.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,GAGzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAErD,OAAO,EAAE,0BAA0B,EAAE,MAAM,qBAAqB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@composurecdk/acm",
3
+ "version": "0.3.2",
4
+ "description": "Composable AWS Certificate Manager builder with well-architected defaults",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/laazyj/composureCDK",
8
+ "directory": "packages/acm"
9
+ },
10
+ "main": "./dist/index.js",
11
+ "types": "./dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.js",
15
+ "types": "./dist/index.d.ts"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md",
21
+ "LICENSE"
22
+ ],
23
+ "scripts": {
24
+ "clean": "rm -rf dist",
25
+ "build": "tsc -p tsconfig.build.json",
26
+ "typecheck": "tsc --noEmit",
27
+ "test": "vitest run",
28
+ "test:watch": "vitest"
29
+ },
30
+ "keywords": [],
31
+ "author": "Jason Duffett (https://github.com/laazyj)",
32
+ "license": "MIT",
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "type": "module",
37
+ "peerDependencies": {
38
+ "@composurecdk/cloudwatch": "^0.3.0",
39
+ "@composurecdk/core": "^0.3.0",
40
+ "aws-cdk-lib": "^2.0.0",
41
+ "constructs": "^10.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^25.6.0",
45
+ "aws-cdk-lib": "^2.250.0",
46
+ "constructs": "^10.6.0",
47
+ "typescript": "^6.0.3",
48
+ "vitest": "^4.1.4"
49
+ }
50
+ }