@go-to-k/cdkd 0.275.1 → 0.275.3

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as WorkGraph, A as slowCcOperationTimeoutMs, At as warnDeprecatedNoPrefixCliFlag, B as applyRoleArnIfSet, Bt as resolveBucketRegion, C as yellow, Ct as resolveAutoAssetStorage, D as ProviderRegistry, Dt as resolveStateBucketWithDefaultAndSource, E as clearOnUpdateRemoval, Et as resolveStateBucketWithDefault, F as refStateLookupFromResource, Ft as uploadCfnTemplate, G as DagBuilder, H as describeTypeWithThrottleRetry, Ht as getAwsClients, I as WAFv2WebACLProvider, It as expectedOwnerParam, J as S3StateBackend, K as TemplateParser, Kt as CdkdError, L as normalizeAwsTagsToCfn, Lt as AssemblyReader, M as isTerminationProtectionPropagationError, Mt as CFN_TEMPLATE_URL_LIMIT, N as IntrinsicFunctionResolver, Nt as MIGRATE_TMP_PREFIX, O as findActionableSilentDrops, Ot as resolveUseCdkBootstrapAssets, P as cfnRefValueFromPhysicalId, Pt as findLargeInlineResources, Q as stringifyValue, Qt as LocalStartServiceError, R as resolveExplicitPhysicalId, Rt as processStackMessages, S as red, St as resolveApp, T as collectInlinePolicyNamesManagedBySiblings, Tt as resolveSkipPrefix, U as withRetry, Ut as resetAwsClients, V as DiffCalculator, Vt as AwsClients, W as isRetryableTransientError, Wt as setAwsClients, X as shouldRetainResource, Xt as LocalInvokeBuildError$1, Y as rebuildClientForBucketRegion, Yt as DeployCancelledError, Z as AssetPublisher, Zt as LocalMigrateError, _ as formatResourceLine, _t as getDockerImageBySourceHash, a as DeploymentEventsStore, an as ResourceUpdateNotSupportedError, at as BOOTSTRAP_MARKER_PREFIX, b as gray, bt as getDefaultStateBucketName, c as replayFailedOperations, ct as parseBootstrapMarker, d as IMPLICIT_DELETE_DEPENDENCIES, dt as buildDockerImage, en as MissingCdkCliError, et as buildAssetRedirectMap, f as computeImplicitDeleteEdges, fn as normalizeAwsError, ft as formatDockerLoginError, g as renderStatefulReason, gt as AssetManifestLoader, h as isStatefulRecreateTargetSync, ht as runDockerStreaming, i as DeploymentEventsReader, in as ResourceTimeoutError, it as AssetModeResolver, j as disableInstanceApiTermination, jt as CFN_TEMPLATE_BODY_LIMIT, k as CloudControlProvider, kt as stateBucketExistenceConfirmed, l as replayRollback, lt as validateAssetBucketName, m as MULTI_REGION_RECREATE_BLOCKED_TYPES, mt as runDockerForeground, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as PartialFailureError, nt as loadPublishableAssetManifest, o as planFailedOps, on as StackHasActiveImportsError, ot as ensureAssetStorage, p as extractDeploymentEventError, pn as withErrorHandling, pt as getDockerCmd, q as LockManager, r as DeployEngine, rn as ProvisioningError, rt as rewriteTemplateAssetReferences, s as planRollback, sn as StackTerminationProtectionError, st as getBootstrapMarkerKey, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as NestedStackChildDirectDestroyError, tt as createAssetRedirectResolver, u as withResourceDeadline, ut as validateContainerRepoName, v as bold, vt as Synthesizer, w as IAMRoleProvider, wt as resolveCaptureObservedState, x as green, xt as getLegacyStateBucketName, y as cyan, yt as synthesisStatusMessage, z as assertRegionMatch } from "./deploy-engine-C44YX-go.js";
2
+ import { $ as WorkGraph, A as slowCcOperationTimeoutMs, At as warnDeprecatedNoPrefixCliFlag, B as applyRoleArnIfSet, Bt as resolveBucketRegion, C as yellow, Ct as resolveAutoAssetStorage, D as ProviderRegistry, Dt as resolveStateBucketWithDefaultAndSource, E as clearOnUpdateRemoval, Et as resolveStateBucketWithDefault, F as refStateLookupFromResource, Ft as uploadCfnTemplate, G as DagBuilder, H as describeTypeWithThrottleRetry, Ht as getAwsClients, I as WAFv2WebACLProvider, It as expectedOwnerParam, J as S3StateBackend, K as TemplateParser, Kt as CdkdError, L as normalizeAwsTagsToCfn, Lt as AssemblyReader, M as isTerminationProtectionPropagationError, Mt as CFN_TEMPLATE_URL_LIMIT, N as IntrinsicFunctionResolver, Nt as MIGRATE_TMP_PREFIX, O as findActionableSilentDrops, Ot as resolveUseCdkBootstrapAssets, P as cfnRefValueFromPhysicalId, Pt as findLargeInlineResources, Q as stringifyValue, Qt as LocalStartServiceError, R as resolveExplicitPhysicalId, Rt as processStackMessages, S as red, St as resolveApp, T as collectInlinePolicyNamesManagedBySiblings, Tt as resolveSkipPrefix, U as withRetry, Ut as resetAwsClients, V as DiffCalculator, Vt as AwsClients, W as isRetryableTransientError, Wt as setAwsClients, X as shouldRetainResource, Xt as LocalInvokeBuildError$1, Y as rebuildClientForBucketRegion, Yt as DeployCancelledError, Z as AssetPublisher, Zt as LocalMigrateError, _ as formatResourceLine, _t as getDockerImageBySourceHash, a as DeploymentEventsStore, an as ResourceUpdateNotSupportedError, at as BOOTSTRAP_MARKER_PREFIX, b as gray, bt as getDefaultStateBucketName, c as replayFailedOperations, ct as parseBootstrapMarker, d as IMPLICIT_DELETE_DEPENDENCIES, dt as buildDockerImage, en as MissingCdkCliError, et as buildAssetRedirectMap, f as computeImplicitDeleteEdges, fn as normalizeAwsError, ft as formatDockerLoginError, g as renderStatefulReason, gt as AssetManifestLoader, h as isStatefulRecreateTargetSync, ht as runDockerStreaming, i as DeploymentEventsReader, in as ResourceTimeoutError, it as AssetModeResolver, j as disableInstanceApiTermination, jt as CFN_TEMPLATE_BODY_LIMIT, k as CloudControlProvider, kt as stateBucketExistenceConfirmed, l as replayRollback, lt as validateAssetBucketName, m as MULTI_REGION_RECREATE_BLOCKED_TYPES, mt as runDockerForeground, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as PartialFailureError, nt as loadPublishableAssetManifest, o as planFailedOps, on as StackHasActiveImportsError, ot as ensureAssetStorage, p as extractDeploymentEventError, pn as withErrorHandling, pt as getDockerCmd, q as LockManager, r as DeployEngine, rn as ProvisioningError, rt as rewriteTemplateAssetReferences, s as planRollback, sn as StackTerminationProtectionError, st as getBootstrapMarkerKey, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as NestedStackChildDirectDestroyError, tt as createAssetRedirectResolver, u as withResourceDeadline, ut as validateContainerRepoName, v as bold, vt as Synthesizer, w as IAMRoleProvider, wt as resolveCaptureObservedState, x as green, xt as getLegacyStateBucketName, y as cyan, yt as synthesisStatusMessage, z as assertRegionMatch } from "./deploy-engine-_U0eVWNU.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
4
- import { t as ASGProvider } from "./asg-provider-c0TMaGXW.js";
4
+ import { t as ASGProvider } from "./asg-provider-UUnRzwMx.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -33,7 +33,7 @@ import { AssociateVPCWithHostedZoneCommand, ChangeResourceRecordSetsCommand, Cha
33
33
  import { AddTagsCommand, CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, DeleteListenerCommand, DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DescribeListenerAttributesCommand, DescribeListenersCommand, DescribeLoadBalancerAttributesCommand, DescribeLoadBalancersCommand, DescribeTagsCommand, DescribeTargetGroupsCommand, ElasticLoadBalancingV2Client, ModifyListenerAttributesCommand, ModifyListenerCommand, ModifyLoadBalancerAttributesCommand, ModifyTargetGroupCommand, RemoveTagsCommand, SetIpAddressTypeCommand, SetSecurityGroupsCommand, SetSubnetsCommand, waitUntilLoadBalancerAvailable } from "@aws-sdk/client-elastic-load-balancing-v2";
34
34
  import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCommand, DisableKeyCommand, DisableKeyRotationCommand, EnableKeyCommand, EnableKeyRotationCommand, GetKeyPolicyCommand, GetKeyRotationStatusCommand, KMSClient, ListAliasesCommand, ListResourceTagsCommand, NotFoundException as NotFoundException$2, PutKeyPolicyCommand, ScheduleKeyDeletionCommand, TagResourceCommand as TagResourceCommand$11, UntagResourceCommand as UntagResourceCommand$11, UpdateAliasCommand, UpdateKeyDescriptionCommand } from "@aws-sdk/client-kms";
35
35
  import { join as join$1 } from "path";
36
- import { BatchDeleteImageCommand, CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$8, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$12 } from "@aws-sdk/client-ecr";
36
+ import { BatchDeleteImageCommand, CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeImagesCommand as DescribeImagesCommand$1, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$8, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotEmptyException, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$12, UntagResourceCommand as UntagResourceCommand$12 } from "@aws-sdk/client-ecr";
37
37
  import graphlib from "graphlib";
38
38
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBProxyCommand, CreateDBProxyEndpointCommand, CreateDBSubnetGroupCommand, DBProxyEndpointNotFoundFault, DBProxyNotFoundFault, DBProxyTargetGroupNotFoundFault, DBProxyTargetNotFoundFault, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBProxyCommand, DeleteDBProxyEndpointCommand, DeleteDBSubnetGroupCommand, DeregisterDBProxyTargetsCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBProxiesCommand, DescribeDBProxyEndpointsCommand, DescribeDBProxyTargetGroupsCommand, DescribeDBProxyTargetsCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$9, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBProxyCommand, ModifyDBProxyEndpointCommand, ModifyDBProxyTargetGroupCommand, ModifyDBSubnetGroupCommand, RDSClient, RegisterDBProxyTargetsCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
39
39
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$2, CreateCacheClusterCommand, CreateCacheSubnetGroupCommand, DeleteCacheClusterCommand, DeleteCacheSubnetGroupCommand, DescribeCacheClustersCommand, DescribeCacheSubnetGroupsCommand, ElastiCacheClient, ListTagsForResourceCommand as ListTagsForResourceCommand$10, ModifyCacheClusterCommand, ModifyCacheSubnetGroupCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$2 } from "@aws-sdk/client-elasticache";
@@ -4701,6 +4701,53 @@ var IAMUserGroupProvider = class {
4701
4701
  }
4702
4702
  };
4703
4703
 
4704
+ //#endregion
4705
+ //#region src/provisioning/data-delete-intent.ts
4706
+ /**
4707
+ * Shared helpers for detecting a template-borne "destroy my data" opt-in on
4708
+ * resources whose delete API fails by default while they still hold data
4709
+ * (issue #1340).
4710
+ *
4711
+ * CloudFormation fails the delete of a non-empty S3 bucket / image-carrying
4712
+ * ECR repository and the user opts into force-cleanup explicitly (CDK's
4713
+ * `autoDeleteObjects` custom resource + tag, `emptyOnDelete` /
4714
+ * `EmptyOnDelete: true`). cdkd honors exactly those signals — plus the
4715
+ * deploy engine's `--force-stateful-recreation` consent, threaded via
4716
+ * `DeleteContext.forceDataDelete` — instead of force-cleaning every
4717
+ * resource unconditionally.
4718
+ */
4719
+ /**
4720
+ * Tag key CDK's `aws-s3.Bucket` sets when `autoDeleteObjects: true`.
4721
+ * The tag is what the AutoDeleteObjects custom-resource handler itself
4722
+ * checks before emptying, so it is the canonical opt-in marker.
4723
+ */
4724
+ const S3_AUTO_DELETE_OBJECTS_TAG = "aws-cdk:auto-delete-objects";
4725
+ /**
4726
+ * Tag key CDK's `aws-ecr.Repository` sets when `autoDeleteImages: true`
4727
+ * (the pre-`emptyOnDelete` opt-in; both signals are honored).
4728
+ */
4729
+ const ECR_AUTO_DELETE_IMAGES_TAG = "aws-cdk:auto-delete-images";
4730
+ /**
4731
+ * True when the resource's CFn `Tags` list carries `tagKey` with a truthy
4732
+ * value. Tolerates the CFn boolean-as-string shape (`"true"`).
4733
+ */
4734
+ function hasCdkAutoDeleteTag(properties, tagKey) {
4735
+ const tags = properties?.["Tags"];
4736
+ if (!Array.isArray(tags)) return false;
4737
+ return tags.some((tag) => {
4738
+ if (typeof tag !== "object" || tag === null) return false;
4739
+ const entry = tag;
4740
+ return entry.Key === tagKey && isTruthyCfnBoolean(entry.Value);
4741
+ });
4742
+ }
4743
+ /**
4744
+ * CFn booleans arrive as `true` or the string `"true"` depending on how the
4745
+ * template was authored / resolved; treat both as true.
4746
+ */
4747
+ function isTruthyCfnBoolean(value) {
4748
+ return value === true || value === "true";
4749
+ }
4750
+
4704
4751
  //#endregion
4705
4752
  //#region src/provisioning/providers/s3-bucket-provider.ts
4706
4753
  /**
@@ -5599,10 +5646,11 @@ var S3BucketProvider = class {
5599
5646
  *
5600
5647
  * Note: The bucket must be empty before deletion.
5601
5648
  */
5602
- async delete(logicalId, physicalId, resourceType, _properties, context) {
5649
+ async delete(logicalId, physicalId, resourceType, properties, context) {
5603
5650
  this.logger.debug(`Deleting S3 bucket ${logicalId}: ${physicalId}`);
5651
+ const allowAutoEmpty = context?.forceDataDelete === true || hasCdkAutoDeleteTag(properties, "aws-cdk:auto-delete-objects");
5604
5652
  try {
5605
- await this.deleteBucketWithEmptyRetry(logicalId, physicalId);
5653
+ await this.deleteBucketWithEmptyRetry(logicalId, physicalId, allowAutoEmpty);
5606
5654
  } catch (error) {
5607
5655
  if (error instanceof NoSuchBucket) {
5608
5656
  assertRegionMatch(await this.s3Client.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
@@ -6134,11 +6182,21 @@ var S3BucketProvider = class {
6134
6182
  return null;
6135
6183
  }
6136
6184
  /**
6137
- * Delete a bucket, emptying it first if not empty.
6138
- * Handles the race condition where objects (e.g., ALB logs) are written
6139
- * after CustomResource cleanup but before bucket deletion.
6185
+ * Delete a bucket, emptying it first if not empty — but ONLY when
6186
+ * `allowAutoEmpty` is set (issue #1340).
6187
+ *
6188
+ * With `allowAutoEmpty` (CDK `autoDeleteObjects` tag present, or
6189
+ * `--force-stateful-recreation` consent via
6190
+ * {@link DeleteContext.forceDataDelete}), the retry loop handles the race
6191
+ * condition where objects (e.g., ALB logs) are written after
6192
+ * CustomResource cleanup but before bucket deletion.
6193
+ *
6194
+ * Without it, a not-empty bucket refuses deletion with an actionable
6195
+ * error — matching CloudFormation's DELETE_FAILED, which is the safety
6196
+ * net users rely on to keep `cdkd destroy` from silently destroying data
6197
+ * they never opted into losing.
6140
6198
  */
6141
- async deleteBucketWithEmptyRetry(logicalId, bucketName) {
6199
+ async deleteBucketWithEmptyRetry(logicalId, bucketName, allowAutoEmpty) {
6142
6200
  const maxAttempts = 3;
6143
6201
  for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
6144
6202
  await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
@@ -6147,7 +6205,8 @@ var S3BucketProvider = class {
6147
6205
  } catch (error) {
6148
6206
  const msg = error instanceof Error ? error.message : String(error);
6149
6207
  if (msg.includes("not empty") || msg.includes("BucketNotEmpty")) {
6150
- this.logger.debug(`Bucket ${bucketName} not empty (attempt ${attempt}/${maxAttempts}), emptying...`);
6208
+ if (!allowAutoEmpty) throw new Error(`bucket ${bucketName} is not empty. Matching CloudFormation, cdkd does not delete a non-empty bucket unless it opted into automatic emptying (CDK's autoDeleteObjects: true, i.e. the '${S3_AUTO_DELETE_OBJECTS_TAG}' tag). Either empty the bucket first (delete all objects — and for versioned buckets all object versions and delete markers), or redeploy with autoDeleteObjects: true and destroy again.`);
6209
+ this.logger.info(`Bucket ${bucketName} not empty (attempt ${attempt}/${maxAttempts}), emptying (auto-delete opt-in present)...`);
6151
6210
  await this.emptyBucket(bucketName);
6152
6211
  continue;
6153
6212
  }
@@ -41327,16 +41386,33 @@ var S3DirectoryBucketProvider = class {
41327
41386
  /**
41328
41387
  * Delete an S3 Express Directory Bucket
41329
41388
  *
41330
- * Must empty the bucket before deletion. Directory buckets do not support
41331
- * versioning, so only current objects need to be deleted.
41389
+ * CloudFormation-parity data guard (issue #1344, sibling of #1340): a
41390
+ * non-empty directory bucket is only auto-emptied when the user opted in —
41391
+ * the `aws-cdk:auto-delete-objects` tag on the bucket (settable only via
41392
+ * `--allow-unsupported-properties` today, since `Tags` is not yet a
41393
+ * handled property for this type) or the deploy engine's
41394
+ * `--force-stateful-recreation` consent on a replacement delete
41395
+ * (`DeleteContext.forceDataDelete`). Otherwise the not-empty error
41396
+ * surfaces exactly like CloudFormation's DELETE_FAILED (live-A/B-verified
41397
+ * 2026-08-03: CFn fails a non-empty DirectoryBucket delete with "The
41398
+ * bucket you tried to delete is not empty", 409).
41399
+ *
41400
+ * Directory buckets do not support versioning, so the opted-in auto-empty
41401
+ * only needs to delete current objects.
41332
41402
  */
41333
- async delete(logicalId, physicalId, resourceType, _properties, context) {
41403
+ async delete(logicalId, physicalId, resourceType, properties, context) {
41334
41404
  this.logger.debug(`Deleting S3 Express Directory Bucket ${logicalId}: ${physicalId}`);
41405
+ const allowAutoEmpty = context?.forceDataDelete === true || hasCdkAutoDeleteTag(properties, "aws-cdk:auto-delete-objects");
41335
41406
  try {
41336
- await this.emptyBucket(physicalId);
41407
+ if (allowAutoEmpty) {
41408
+ this.logger.info(`Emptying directory bucket ${physicalId} before deletion (auto-delete opt-in present)`);
41409
+ await this.emptyBucket(physicalId);
41410
+ }
41337
41411
  await this.s3Client.send(new DeleteBucketCommand({ Bucket: physicalId }));
41338
41412
  this.logger.debug(`Successfully deleted S3 Express Directory Bucket ${logicalId}`);
41339
41413
  } catch (error) {
41414
+ const notEmptyMsg = error instanceof Error ? error.message : String(error);
41415
+ if (!allowAutoEmpty && (notEmptyMsg.includes("not empty") || notEmptyMsg.includes("BucketNotEmpty"))) throw new ProvisioningError(`Failed to delete S3 Express Directory Bucket ${logicalId}: bucket ${physicalId} is not empty. Matching CloudFormation, cdkd does not delete a non-empty directory bucket without an explicit opt-in. Delete all objects first (e.g. aws s3 rm s3://${physicalId} --recursive) and destroy again.`, resourceType, logicalId, physicalId, error instanceof Error ? error : void 0);
41340
41416
  if (error instanceof Error && (error.name === "NoSuchBucket" || error.name === "BucketNotFound")) {
41341
41417
  assertRegionMatch(await this.s3Client.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
41342
41418
  this.logger.debug(`Bucket ${physicalId} does not exist, skipping deletion`);
@@ -42015,8 +42091,11 @@ var S3TablesProvider = class {
42015
42091
  *
42016
42092
  * Implements resource provisioning for AWS::ECR::Repository using the ECR SDK.
42017
42093
  * WHY: The CC API cannot force-delete repositories that contain images.
42018
- * This SDK provider uses DeleteRepositoryCommand with `force: true` to delete
42019
- * repositories along with all their images, supporting CDK's `emptyOnDelete: true`.
42094
+ * This SDK provider implements CDK's `emptyOnDelete: true` by passing
42095
+ * `force: true` to DeleteRepositoryCommand — gated on the user's opt-in
42096
+ * (`EmptyOnDelete: true`, the `aws-cdk:auto-delete-images` tag, or
42097
+ * `--force-stateful-recreation` on a replacement), never unconditionally
42098
+ * (issue #1340; CloudFormation parity for repositories without the opt-in).
42020
42099
  */
42021
42100
  var ECRProvider = class {
42022
42101
  client;
@@ -42208,18 +42287,27 @@ var ECRProvider = class {
42208
42287
  /**
42209
42288
  * Delete an ECR Repository
42210
42289
  *
42211
- * Uses `force: true` to delete the repository even if it contains images.
42212
- * This supports CDK's `emptyOnDelete: true` / `removalPolicy: DESTROY` pattern.
42290
+ * `force: true` (delete the repository along with any images it still
42291
+ * contains) is applied ONLY when the user opted in (issue #1340):
42292
+ * `EmptyOnDelete: true` (CDK `emptyOnDelete`), CDK's legacy
42293
+ * `autoDeleteImages` tag, or the deploy engine's
42294
+ * `--force-stateful-recreation` consent on a replacement delete.
42295
+ * Without an opt-in, a repository that still contains images refuses
42296
+ * deletion with an actionable error — matching CloudFormation's
42297
+ * DELETE_FAILED (`RepositoryNotEmptyException`).
42213
42298
  */
42214
- async delete(logicalId, physicalId, resourceType, _properties, context) {
42299
+ async delete(logicalId, physicalId, resourceType, properties, context) {
42215
42300
  this.logger.debug(`Deleting ECR Repository ${logicalId}: ${physicalId}`);
42301
+ const force = context?.forceDataDelete === true || isTruthyCfnBoolean(properties?.["EmptyOnDelete"]) || hasCdkAutoDeleteTag(properties, "aws-cdk:auto-delete-images");
42216
42302
  try {
42303
+ if (force) this.logger.debug(`Force-deleting ECR Repository ${logicalId} (empty-on-delete opt-in present)`);
42217
42304
  await this.getClient().send(new DeleteRepositoryCommand({
42218
42305
  repositoryName: physicalId,
42219
- force: true
42306
+ force
42220
42307
  }));
42221
42308
  this.logger.debug(`Successfully deleted ECR Repository ${logicalId}`);
42222
42309
  } catch (error) {
42310
+ if (error instanceof RepositoryNotEmptyException) throw new ProvisioningError(`Failed to delete ECR Repository ${logicalId}: repository ${physicalId} still contains images. Matching CloudFormation, cdkd does not force-delete an image-carrying repository unless it opted in via EmptyOnDelete: true (CDK's emptyOnDelete) or the '${ECR_AUTO_DELETE_IMAGES_TAG}' tag (CDK's autoDeleteImages). Either delete the images first (aws ecr batch-delete-image), or redeploy with emptyOnDelete: true and destroy again.`, resourceType, logicalId, physicalId, error);
42223
42311
  if (error instanceof RepositoryNotFoundException) {
42224
42312
  assertRegionMatch(await this.getClient().config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
42225
42313
  this.logger.debug(`ECR Repository ${physicalId} does not exist, skipping deletion`);
@@ -63483,7 +63571,7 @@ function createMigrateCommand() {
63483
63571
  */
63484
63572
  function buildProgram() {
63485
63573
  const program = new Command();
63486
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.275.1");
63574
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.275.3");
63487
63575
  program.hook("preAction", (_thisCommand, actionCommand) => {
63488
63576
  const { profile } = actionCommand.optsWithGlobals();
63489
63577
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;