@composurecdk/s3 0.1.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.
@@ -0,0 +1,103 @@
1
+ import { Bucket, type BucketProps } from "aws-cdk-lib/aws-s3";
2
+ import { type IConstruct } from "constructs";
3
+ import { type IBuilder, type Lifecycle } from "@composurecdk/core";
4
+ /**
5
+ * Configuration properties for the S3 bucket builder.
6
+ *
7
+ * Extends the CDK {@link BucketProps} with additional builder-specific options.
8
+ */
9
+ export interface BucketBuilderProps extends BucketProps {
10
+ /**
11
+ * Whether to automatically create an S3 access logging bucket.
12
+ *
13
+ * When `true`, the builder creates a dedicated logging bucket using
14
+ * {@link createBucketBuilder} (with secure defaults appropriate for log
15
+ * storage) and configures it as the server access logs destination. The
16
+ * logging bucket inherits secure defaults (block public access, encryption,
17
+ * enforceSSL, versioning for log integrity) with `removalPolicy: RETAIN`
18
+ * and access logging disabled to avoid recursion. The created logging
19
+ * bucket is returned in the build result as `accessLogsBucket`.
20
+ *
21
+ * When `false`, no logging bucket is created. You can still provide your
22
+ * own destination via `serverAccessLogsBucket`.
23
+ *
24
+ * This setting is ignored when `serverAccessLogsBucket` is provided — the
25
+ * user-supplied destination takes precedence.
26
+ */
27
+ accessLogging?: boolean;
28
+ /**
29
+ * The prefix applied to server access log object keys when the builder
30
+ * auto-creates a logging bucket.
31
+ *
32
+ * This setting is only used when {@link accessLogging} is `true` and no
33
+ * user-provided `serverAccessLogsBucket` is set.
34
+ *
35
+ * @default "logs/"
36
+ */
37
+ accessLogsPrefix?: string;
38
+ }
39
+ /**
40
+ * The build output of a {@link IBucketBuilder}. Contains the CDK constructs
41
+ * created during {@link Lifecycle.build}, keyed by role.
42
+ */
43
+ export interface BucketBuilderResult {
44
+ /** The S3 bucket construct created by the builder. */
45
+ bucket: Bucket;
46
+ /**
47
+ * The S3 bucket created for access logging, or `undefined` if access
48
+ * logging was disabled or the user provided their own destination.
49
+ */
50
+ accessLogsBucket?: Bucket;
51
+ }
52
+ /**
53
+ * A fluent builder for configuring and creating an Amazon S3 bucket.
54
+ *
55
+ * Each configuration property from the CDK {@link BucketProps} is exposed
56
+ * as an overloaded method: call with a value to set it (returns the builder
57
+ * for chaining), or call with no arguments to read the current value.
58
+ *
59
+ * The builder implements {@link Lifecycle}, so it can be used directly as a
60
+ * component in a {@link compose | composed system}. When built, it creates
61
+ * an S3 bucket with the configured properties and returns a
62
+ * {@link BucketBuilderResult}.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const site = createBucketBuilder()
67
+ * .bucketName("my-website-bucket")
68
+ * .versioned(false);
69
+ * ```
70
+ */
71
+ export type IBucketBuilder = IBuilder<BucketBuilderProps, BucketBuilder>;
72
+ declare class BucketBuilder implements Lifecycle<BucketBuilderResult> {
73
+ props: Partial<BucketBuilderProps>;
74
+ build(scope: IConstruct, id: string): BucketBuilderResult;
75
+ }
76
+ /**
77
+ * Creates a new {@link IBucketBuilder} for configuring an Amazon S3 bucket.
78
+ *
79
+ * This is the entry point for defining an S3 bucket component. The returned
80
+ * builder exposes every {@link BucketProps} property as a fluent setter/getter
81
+ * and implements {@link Lifecycle} for use with {@link compose}.
82
+ *
83
+ * @returns A fluent builder for an Amazon S3 bucket.
84
+ *
85
+ * @example
86
+ * ```ts
87
+ * const site = createBucketBuilder()
88
+ * .bucketName("my-site")
89
+ * .versioned(false);
90
+ *
91
+ * // Use standalone:
92
+ * const result = site.build(stack, "SiteBucket");
93
+ *
94
+ * // Or compose into a system:
95
+ * const system = compose(
96
+ * { site, cdn: createDistributionBuilder() },
97
+ * { site: [], cdn: ["site"] },
98
+ * );
99
+ * ```
100
+ */
101
+ export declare function createBucketBuilder(): IBucketBuilder;
102
+ export {};
103
+ //# sourceMappingURL=bucket-builder.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,83 @@
1
+ import { RemovalPolicy } from "aws-cdk-lib";
2
+ import { Bucket } from "aws-cdk-lib/aws-s3";
3
+ import { Builder } from "@composurecdk/core";
4
+ import { BUCKET_DEFAULTS } from "./defaults.js";
5
+ class BucketBuilder {
6
+ props = {};
7
+ build(scope, id) {
8
+ const { accessLogging, accessLogsPrefix, ...bucketProps } = this.props;
9
+ const { accessLogging: defaultAccessLogging, accessLogsPrefix: defaultLogsPrefix, ...cdkDefaults } = BUCKET_DEFAULTS;
10
+ const autoAccessLog = (accessLogging ?? defaultAccessLogging) && !bucketProps.serverAccessLogsBucket;
11
+ if (accessLogsPrefix !== undefined && !autoAccessLog) {
12
+ throw new Error("Cannot set 'accessLogsPrefix' when access logging is disabled or " +
13
+ "'serverAccessLogsBucket' is provided. Set 'serverAccessLogsPrefix' " +
14
+ "directly when using your own logging bucket.");
15
+ }
16
+ let accessLogsBucket;
17
+ let accessLogProps = {};
18
+ if (autoAccessLog) {
19
+ accessLogsBucket = createBucketBuilder()
20
+ .accessLogging(false)
21
+ .removalPolicy(RemovalPolicy.RETAIN)
22
+ .build(scope, `${id}AccessLogs`).bucket;
23
+ accessLogProps = {
24
+ serverAccessLogsBucket: accessLogsBucket,
25
+ serverAccessLogsPrefix: accessLogsPrefix ?? defaultLogsPrefix,
26
+ };
27
+ }
28
+ const mergedProps = {
29
+ ...cdkDefaults,
30
+ ...accessLogProps,
31
+ ...bucketProps,
32
+ ...autoDeleteProps(bucketProps, BUCKET_DEFAULTS),
33
+ };
34
+ return {
35
+ bucket: new Bucket(scope, id, mergedProps),
36
+ accessLogsBucket,
37
+ };
38
+ }
39
+ }
40
+ /**
41
+ * Returns `{ autoDeleteObjects: true }` when the effective removal policy is
42
+ * `DESTROY` and the user has not explicitly set `autoDeleteObjects`.
43
+ *
44
+ * CDK requires `autoDeleteObjects` to be paired with `removalPolicy: DESTROY`,
45
+ * but forgetting it causes a non-empty-bucket error on stack deletion. This
46
+ * helper bridges that gap so that switching to `DESTROY` Just Works.
47
+ */
48
+ function autoDeleteProps(userProps, defaults) {
49
+ const effectivePolicy = userProps.removalPolicy ?? defaults.removalPolicy;
50
+ if (effectivePolicy === RemovalPolicy.DESTROY && userProps.autoDeleteObjects === undefined) {
51
+ return { autoDeleteObjects: true };
52
+ }
53
+ return {};
54
+ }
55
+ /**
56
+ * Creates a new {@link IBucketBuilder} for configuring an Amazon S3 bucket.
57
+ *
58
+ * This is the entry point for defining an S3 bucket component. The returned
59
+ * builder exposes every {@link BucketProps} property as a fluent setter/getter
60
+ * and implements {@link Lifecycle} for use with {@link compose}.
61
+ *
62
+ * @returns A fluent builder for an Amazon S3 bucket.
63
+ *
64
+ * @example
65
+ * ```ts
66
+ * const site = createBucketBuilder()
67
+ * .bucketName("my-site")
68
+ * .versioned(false);
69
+ *
70
+ * // Use standalone:
71
+ * const result = site.build(stack, "SiteBucket");
72
+ *
73
+ * // Or compose into a system:
74
+ * const system = compose(
75
+ * { site, cdn: createDistributionBuilder() },
76
+ * { site: [], cdn: ["site"] },
77
+ * );
78
+ * ```
79
+ */
80
+ export function createBucketBuilder() {
81
+ return Builder(BucketBuilder);
82
+ }
83
+ //# sourceMappingURL=bucket-builder.js.map
@@ -0,0 +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"}
@@ -0,0 +1,112 @@
1
+ import { BucketDeployment, type BucketDeploymentProps, type ISource } from "aws-cdk-lib/aws-s3-deployment";
2
+ import { type IBucket } from "aws-cdk-lib/aws-s3";
3
+ import { type IDistribution } from "aws-cdk-lib/aws-cloudfront";
4
+ import { type IConstruct } from "constructs";
5
+ 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
+ }
22
+ /**
23
+ * The build output of a {@link IBucketDeploymentBuilder}.
24
+ */
25
+ export interface BucketDeploymentBuilderResult {
26
+ /** The CDK BucketDeployment construct created by the builder. */
27
+ deployment: BucketDeployment;
28
+ }
29
+ /**
30
+ * A fluent builder for configuring and creating an S3 BucketDeployment.
31
+ *
32
+ * Each configuration property from the CDK {@link BucketDeploymentProps} is
33
+ * exposed as an overloaded method: call with a value to set it (returns the
34
+ * builder for chaining), or call with no arguments to read the current value.
35
+ *
36
+ * The `destinationBucket` and `distribution` are set via dedicated methods
37
+ * that accept {@link Resolvable} values for cross-component wiring.
38
+ *
39
+ * The builder implements {@link Lifecycle}, so it can be used directly as a
40
+ * component in a {@link compose | composed system}.
41
+ *
42
+ * @example
43
+ * ```ts
44
+ * const deploy = createBucketDeploymentBuilder()
45
+ * .sources([Source.asset("./site")])
46
+ * .destinationBucket(ref("site", (r: BucketBuilderResult) => r.bucket))
47
+ * .distribution(ref("cdn", (r: DistributionBuilderResult) => r.distribution))
48
+ * .distributionPaths(["/*"]);
49
+ * ```
50
+ */
51
+ export type IBucketDeploymentBuilder = IBuilder<BucketDeploymentBuilderProps, BucketDeploymentBuilder>;
52
+ declare class BucketDeploymentBuilder implements Lifecycle<BucketDeploymentBuilderResult> {
53
+ props: Partial<BucketDeploymentBuilderProps>;
54
+ private _destinationBucket?;
55
+ private _distribution?;
56
+ /**
57
+ * Sets the destination bucket for the deployment.
58
+ *
59
+ * Accepts a concrete {@link IBucket} or a {@link Ref} that resolves to one
60
+ * at build time.
61
+ *
62
+ * @param bucket - The bucket or a Ref to one.
63
+ * @returns This builder for chaining.
64
+ */
65
+ destinationBucket(bucket: Resolvable<IBucket>): this;
66
+ /**
67
+ * Sets the CloudFront distribution to invalidate on deployment.
68
+ *
69
+ * Accepts a concrete {@link IDistribution} or a {@link Ref} that resolves
70
+ * to one at build time. This is optional — deployments can target a bucket
71
+ * without CloudFront invalidation.
72
+ *
73
+ * @param distribution - The distribution or a Ref to one.
74
+ * @returns This builder for chaining.
75
+ */
76
+ distribution(distribution: Resolvable<IDistribution>): this;
77
+ build(scope: IConstruct, id: string, context?: Record<string, object>): BucketDeploymentBuilderResult;
78
+ }
79
+ /**
80
+ * Creates a new {@link IBucketDeploymentBuilder} for deploying content to an
81
+ * S3 bucket with optional CloudFront cache invalidation.
82
+ *
83
+ * This is the entry point for defining an S3 deployment component. The
84
+ * returned builder exposes {@link BucketDeploymentBuilderProps} properties as
85
+ * fluent setters/getters, plus {@link IBucketDeploymentBuilder.destinationBucket | destinationBucket()}
86
+ * and {@link IBucketDeploymentBuilder.distribution | distribution()} for
87
+ * cross-component wiring with Ref support. It implements {@link Lifecycle}
88
+ * for use with {@link compose}.
89
+ *
90
+ * @returns A fluent builder for an S3 BucketDeployment.
91
+ *
92
+ * @example
93
+ * ```ts
94
+ * const deploy = createBucketDeploymentBuilder()
95
+ * .sources([Source.asset("./site")])
96
+ * .destinationBucket(ref("site", (r: BucketBuilderResult) => r.bucket))
97
+ * .distribution(ref("cdn", (r: DistributionBuilderResult) => r.distribution))
98
+ * .distributionPaths(["/*"]);
99
+ *
100
+ * // Use standalone:
101
+ * const result = deploy.build(stack, "Deploy");
102
+ *
103
+ * // Or compose into a system:
104
+ * const system = compose(
105
+ * { site: createBucketBuilder(), cdn: createDistributionBuilder(), deploy },
106
+ * { site: [], cdn: ["site"], deploy: ["site", "cdn"] },
107
+ * );
108
+ * ```
109
+ */
110
+ export declare function createBucketDeploymentBuilder(): IBucketDeploymentBuilder;
111
+ export {};
112
+ //# sourceMappingURL=bucket-deployment-builder.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,104 @@
1
+ import { BucketDeployment, } from "aws-cdk-lib/aws-s3-deployment";
2
+ import { Builder, resolve, } from "@composurecdk/core";
3
+ import { BUCKET_DEPLOYMENT_DEFAULTS } from "./bucket-deployment-defaults.js";
4
+ class BucketDeploymentBuilder {
5
+ props = {};
6
+ _destinationBucket;
7
+ _distribution;
8
+ /**
9
+ * Sets the destination bucket for the deployment.
10
+ *
11
+ * Accepts a concrete {@link IBucket} or a {@link Ref} that resolves to one
12
+ * at build time.
13
+ *
14
+ * @param bucket - The bucket or a Ref to one.
15
+ * @returns This builder for chaining.
16
+ */
17
+ destinationBucket(bucket) {
18
+ this._destinationBucket = bucket;
19
+ return this;
20
+ }
21
+ /**
22
+ * Sets the CloudFront distribution to invalidate on deployment.
23
+ *
24
+ * Accepts a concrete {@link IDistribution} or a {@link Ref} that resolves
25
+ * to one at build time. This is optional — deployments can target a bucket
26
+ * without CloudFront invalidation.
27
+ *
28
+ * @param distribution - The distribution or a Ref to one.
29
+ * @returns This builder for chaining.
30
+ */
31
+ distribution(distribution) {
32
+ this._distribution = distribution;
33
+ return this;
34
+ }
35
+ build(scope, id, context) {
36
+ const ctx = context ?? {};
37
+ const resolvedBucket = this._destinationBucket
38
+ ? resolve(this._destinationBucket, ctx)
39
+ : undefined;
40
+ if (!resolvedBucket) {
41
+ throw new Error(`BucketDeploymentBuilder "${id}" requires a destination bucket. ` +
42
+ `Call .destinationBucket() with an IBucket or a Ref to one.`);
43
+ }
44
+ const { sources, ...deployProps } = this.props;
45
+ if (!sources || sources.length === 0) {
46
+ throw new Error(`BucketDeploymentBuilder "${id}" requires at least one source. ` +
47
+ `Call .sources() with an array of ISource.`);
48
+ }
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;
58
+ const mergedProps = {
59
+ ...effectiveDefaults,
60
+ ...deployProps,
61
+ ...(resolvedDistribution ? { distribution: resolvedDistribution } : {}),
62
+ sources,
63
+ destinationBucket: resolvedBucket,
64
+ };
65
+ return {
66
+ deployment: new BucketDeployment(scope, id, mergedProps),
67
+ };
68
+ }
69
+ }
70
+ /**
71
+ * Creates a new {@link IBucketDeploymentBuilder} for deploying content to an
72
+ * S3 bucket with optional CloudFront cache invalidation.
73
+ *
74
+ * This is the entry point for defining an S3 deployment component. The
75
+ * returned builder exposes {@link BucketDeploymentBuilderProps} properties as
76
+ * fluent setters/getters, plus {@link IBucketDeploymentBuilder.destinationBucket | destinationBucket()}
77
+ * and {@link IBucketDeploymentBuilder.distribution | distribution()} for
78
+ * cross-component wiring with Ref support. It implements {@link Lifecycle}
79
+ * for use with {@link compose}.
80
+ *
81
+ * @returns A fluent builder for an S3 BucketDeployment.
82
+ *
83
+ * @example
84
+ * ```ts
85
+ * const deploy = createBucketDeploymentBuilder()
86
+ * .sources([Source.asset("./site")])
87
+ * .destinationBucket(ref("site", (r: BucketBuilderResult) => r.bucket))
88
+ * .distribution(ref("cdn", (r: DistributionBuilderResult) => r.distribution))
89
+ * .distributionPaths(["/*"]);
90
+ *
91
+ * // Use standalone:
92
+ * const result = deploy.build(stack, "Deploy");
93
+ *
94
+ * // Or compose into a system:
95
+ * const system = compose(
96
+ * { site: createBucketBuilder(), cdn: createDistributionBuilder(), deploy },
97
+ * { site: [], cdn: ["site"], deploy: ["site", "cdn"] },
98
+ * );
99
+ * ```
100
+ */
101
+ export function createBucketDeploymentBuilder() {
102
+ return Builder(BucketDeploymentBuilder);
103
+ }
104
+ //# sourceMappingURL=bucket-deployment-builder.js.map
@@ -0,0 +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"}
@@ -0,0 +1,8 @@
1
+ import type { BucketDeploymentBuilderProps } from "./bucket-deployment-builder.js";
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.
6
+ */
7
+ export declare const BUCKET_DEPLOYMENT_DEFAULTS: Partial<BucketDeploymentBuilderProps>;
8
+ //# sourceMappingURL=bucket-deployment-defaults.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,20 @@
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.
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: ["/*"],
13
+ /**
14
+ * 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
17
+ */
18
+ prune: true,
19
+ };
20
+ //# sourceMappingURL=bucket-deployment-defaults.js.map
@@ -0,0 +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"}
@@ -0,0 +1,8 @@
1
+ import type { BucketBuilderProps } from "./bucket-builder.js";
2
+ /**
3
+ * Secure, AWS-recommended defaults applied to every S3 bucket built
4
+ * with {@link createBucketBuilder}. Each property can be individually
5
+ * overridden via the builder's fluent API.
6
+ */
7
+ export declare const BUCKET_DEFAULTS: Partial<BucketBuilderProps>;
8
+ //# sourceMappingURL=defaults.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.d.ts","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAE9D;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,kBAAkB,CAqDvD,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { BlockPublicAccess, BucketEncryption } from "aws-cdk-lib/aws-s3";
2
+ import { RemovalPolicy } from "aws-cdk-lib";
3
+ /**
4
+ * Secure, AWS-recommended defaults applied to every S3 bucket built
5
+ * with {@link createBucketBuilder}. Each property can be individually
6
+ * overridden via the builder's fluent API.
7
+ */
8
+ export const BUCKET_DEFAULTS = {
9
+ /**
10
+ * Automatically create an access logging bucket for S3 server access logs.
11
+ * Access logging provides an audit trail of all object-level operations for
12
+ * security monitoring and troubleshooting.
13
+ * @see https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/sec_detect_investigate_events_app_service_logging.html
14
+ */
15
+ accessLogging: true,
16
+ /**
17
+ * Default prefix for server access log object keys in the auto-created
18
+ * logging bucket.
19
+ */
20
+ accessLogsPrefix: "logs/",
21
+ /**
22
+ * Block all public access to the bucket. S3 buckets should not be
23
+ * publicly accessible unless explicitly required (e.g. static website
24
+ * hosting via CloudFront OAC).
25
+ * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html
26
+ */
27
+ blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
28
+ /**
29
+ * Enable server-side encryption with S3-managed keys (SSE-S3).
30
+ * This is the default and lowest-cost encryption option.
31
+ * @see https://docs.aws.amazon.com/wellarchitected/latest/security-pillar/protecting-data-at-rest.html
32
+ */
33
+ encryption: BucketEncryption.S3_MANAGED,
34
+ /**
35
+ * Enforce SSL/TLS for all requests to the bucket.
36
+ * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html
37
+ */
38
+ enforceSSL: true,
39
+ /**
40
+ * Enable versioning to protect against accidental deletions and
41
+ * support rollback.
42
+ * @see https://docs.aws.amazon.com/AmazonS3/latest/userguide/Versioning.html
43
+ */
44
+ versioned: true,
45
+ /**
46
+ * Retain the bucket on stack deletion to prevent data loss.
47
+ *
48
+ * When overridden to `RemovalPolicy.DESTROY`, the builder automatically
49
+ * enables `autoDeleteObjects` (unless explicitly set to `false`) so that
50
+ * non-empty buckets can be cleanly removed during stack deletion.
51
+ *
52
+ * @see https://docs.aws.amazon.com/cdk/v2/guide/resources.html#resources_removal
53
+ */
54
+ removalPolicy: RemovalPolicy.RETAIN,
55
+ };
56
+ //# sourceMappingURL=defaults.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../src/defaults.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACzE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAgC;IAC1D;;;;;OAKG;IACH,aAAa,EAAE,IAAI;IAEnB;;;OAGG;IACH,gBAAgB,EAAE,OAAO;IAEzB;;;;;OAKG;IACH,iBAAiB,EAAE,iBAAiB,CAAC,SAAS;IAE9C;;;;OAIG;IACH,UAAU,EAAE,gBAAgB,CAAC,UAAU;IAEvC;;;OAGG;IACH,UAAU,EAAE,IAAI;IAEhB;;;;OAIG;IACH,SAAS,EAAE,IAAI;IAEf;;;;;;;;OAQG;IACH,aAAa,EAAE,aAAa,CAAC,MAAM;CACpC,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { createBucketBuilder, type BucketBuilderResult, type IBucketBuilder, } from "./bucket-builder.js";
2
+ export { BUCKET_DEFAULTS } from "./defaults.js";
3
+ //# 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,mBAAmB,EACnB,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createBucketBuilder, } from "./bucket-builder.js";
2
+ export { BUCKET_DEFAULTS } from "./defaults.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@composurecdk/s3",
3
+ "version": "0.1.2",
4
+ "description": "Composable S3 bucket builder with well-architected defaults",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/laazyj/composureCDK",
8
+ "directory": "packages/s3"
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 --passWithNoTests",
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/core": "^0.1.0",
39
+ "aws-cdk-lib": "^2.0.0",
40
+ "constructs": "^10.0.0"
41
+ },
42
+ "devDependencies": {
43
+ "@types/node": "^25.5.0",
44
+ "aws-cdk-lib": "^2.245.0",
45
+ "constructs": "^10.6.0",
46
+ "typescript": "^6.0.2",
47
+ "vitest": "^4.1.2"
48
+ }
49
+ }