@go-to-k/cdkd 0.278.35 → 0.278.36

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 describeTypeWithThrottleRetry, $t as AwsClients, A as red, At as Synthesizer, B as isTerminationProtectionPropagationError, Bt as resolveUseCdkBootstrapAssets, C as isStatefulRecreateTargetSync, Ct as buildDockerImage, D as cyan, Dt as runDockerStreaming, E as bold, Et as runDockerForeground, F as ProviderRegistry, Ft as resolveAutoAssetStorage, G as normalizeAwsTagsToCfn, Gt as MIGRATE_TMP_PREFIX, H as cfnRefValueFromPhysicalId, Ht as warnDeprecatedNoPrefixCliFlag, I as findActionableSilentDrops, It as resolveCaptureObservedState, J as readConfigString, Jt as expectedOwnerParam, K as resolveExplicitPhysicalId, Kt as findLargeInlineResources, L as CloudControlProvider, Lt as resolveSkipPrefix, M as IAMRoleProvider, Mt as getDefaultStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as getLegacyStateBucketName, O as gray, Ot as AssetManifestLoader, P as clearOnUpdateRemoval, Pt as resolveApp, Q as DiffCalculator, Qt as resolveBucketRegion, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefault, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateContainerRepoName, T as formatResourceLine, Tn as withErrorHandling, Tt as getDockerCmd, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_BODY_LIMIT, V as IntrinsicFunctionResolver, Vt as stateBucketExistenceConfirmed, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_URL_LIMIT, X as requireConfigString, Xt as processStackMessages, Y as requireConfigArray, Yt as AssemblyReader, Z as applyRoleArnIfSet, _ as createPreDeleteFinalSnapshot, _n as ResourceUpdateNotSupportedError, _t as BOOTSTRAP_MARKER_PREFIX, a as DeploymentEventsStore, at as LockManager, b as unsupportedFinalSnapshotError, bt as parseBootstrapMarker, c as replayFailedOperations, cn as LocalInvokeBuildError$1, ct as shouldRetainResource, d as IMPLICIT_DELETE_DEPENDENCIES, dt as WorkGraph, en as getAwsClients, et as withRetry, f as computeImplicitDeleteEdges, fn as MissingCdkCliError, ft as buildAssetRedirectMap, g as ccRoutedFinalSnapshotError, gn as ResourceTimeoutError, gt as AssetModeResolver, h as buildFinalSnapshotIdentifier, hn as ProvisioningError, ht as rewriteTemplateAssetReferences, i as DeploymentEventsReader, in as CdkdError, it as TemplateParser, j as yellow, jt as synthesisStatusMessage, k as green, kt as getDockerImageBySourceHash, l as replayRollback, ln as LocalMigrateError, lt as AssetPublisher, m as PRE_DELETE_SNAPSHOT_TYPES, mn as PartialFailureError, mt as loadPublishableAssetManifest, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as setAwsClients, nt as isThrottlingError, o as planFailedOps, ot as S3StateBackend, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as NestedStackChildDirectDestroyError, pt as createAssetRedirectResolver, q as assertRegionMatch, qt as uploadCfnTemplate, r as DeployEngine, rt as DagBuilder, s as planRollback, sn as DeployCancelledError, st as rebuildClientForBucketRegion, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resetAwsClients, tt as isRetryableTransientError, u as withResourceDeadline, un as LocalStartServiceError, ut as stringifyValue, v as isFinalSnapshotError, vn as StackHasActiveImportsError, vt as ensureAssetStorage, w as renderStatefulReason, wn as normalizeAwsError, wt as formatDockerLoginError, x as extractDeploymentEventError, xt as validateAssetBucketName, y as refusesFinalSnapshot, yn as StackTerminationProtectionError, yt as getBootstrapMarkerKey, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefaultAndSource } from "./deploy-engine-CWZlo2Il.js";
2
+ import { $ as describeTypeWithThrottleRetry, $t as AwsClients, A as red, At as Synthesizer, B as isTerminationProtectionPropagationError, Bt as resolveUseCdkBootstrapAssets, C as isStatefulRecreateTargetSync, Ct as buildDockerImage, D as cyan, Dt as runDockerStreaming, E as bold, Et as runDockerForeground, F as ProviderRegistry, Ft as resolveAutoAssetStorage, G as normalizeAwsTagsToCfn, Gt as MIGRATE_TMP_PREFIX, H as cfnRefValueFromPhysicalId, Ht as warnDeprecatedNoPrefixCliFlag, I as findActionableSilentDrops, It as resolveCaptureObservedState, J as readConfigString, Jt as expectedOwnerParam, K as resolveExplicitPhysicalId, Kt as findLargeInlineResources, L as CloudControlProvider, Lt as resolveSkipPrefix, M as IAMRoleProvider, Mt as getDefaultStateBucketName, N as collectInlinePolicyNamesManagedBySiblings, Nt as getLegacyStateBucketName, O as gray, Ot as AssetManifestLoader, P as clearOnUpdateRemoval, Pt as resolveApp, Q as DiffCalculator, Qt as resolveBucketRegion, R as slowCcOperationTimeoutMs, Rt as resolveStateBucketWithDefault, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, St as validateContainerRepoName, T as formatResourceLine, Tn as withErrorHandling, Tt as getDockerCmd, U as refStateLookupFromResource, Ut as CFN_TEMPLATE_BODY_LIMIT, V as IntrinsicFunctionResolver, Vt as stateBucketExistenceConfirmed, W as WAFv2WebACLProvider, Wt as CFN_TEMPLATE_URL_LIMIT, X as requireConfigString, Xt as processStackMessages, Y as requireConfigArray, Yt as AssemblyReader, Z as applyRoleArnIfSet, _ as createPreDeleteFinalSnapshot, _n as ResourceUpdateNotSupportedError, _t as BOOTSTRAP_MARKER_PREFIX, a as DeploymentEventsStore, at as LockManager, b as unsupportedFinalSnapshotError, bt as parseBootstrapMarker, c as replayFailedOperations, cn as LocalInvokeBuildError$1, ct as shouldRetainResource, d as IMPLICIT_DELETE_DEPENDENCIES, dt as WorkGraph, en as getAwsClients, et as withRetry, f as computeImplicitDeleteEdges, fn as MissingCdkCliError, ft as buildAssetRedirectMap, g as ccRoutedFinalSnapshotError, gn as ResourceTimeoutError, gt as AssetModeResolver, h as buildFinalSnapshotIdentifier, hn as ProvisioningError, ht as rewriteTemplateAssetReferences, i as DeploymentEventsReader, in as CdkdError, it as TemplateParser, j as yellow, jt as synthesisStatusMessage, k as green, kt as getDockerImageBySourceHash, l as replayRollback, ln as LocalMigrateError, lt as AssetPublisher, m as PRE_DELETE_SNAPSHOT_TYPES, mn as PartialFailureError, mt as loadPublishableAssetManifest, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as setAwsClients, nt as isThrottlingError, o as planFailedOps, ot as S3StateBackend, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as NestedStackChildDirectDestroyError, pt as createAssetRedirectResolver, q as assertRegionMatch, qt as uploadCfnTemplate, r as DeployEngine, rt as DagBuilder, s as planRollback, sn as DeployCancelledError, st as rebuildClientForBucketRegion, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resetAwsClients, tt as isRetryableTransientError, u as withResourceDeadline, un as LocalStartServiceError, ut as stringifyValue, v as isFinalSnapshotError, vn as StackHasActiveImportsError, vt as ensureAssetStorage, w as renderStatefulReason, wn as normalizeAwsError, wt as formatDockerLoginError, x as extractDeploymentEventError, xt as validateAssetBucketName, y as refusesFinalSnapshot, yn as StackTerminationProtectionError, yt as getBootstrapMarkerKey, z as disableInstanceApiTermination, zt as resolveStateBucketWithDefaultAndSource } from "./deploy-engine-iHGY-Q_U.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-8XxBHtzK.js";
4
+ import { t as ASGProvider } from "./asg-provider-5hYG8VHd.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, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, 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";
@@ -66,7 +66,8 @@ import { CodeCommitClient, CreateCommitCommand, CreateRepositoryCommand as Creat
66
66
  import AdmZip from "adm-zip";
67
67
  import { CreateLifecyclePolicyCommand, DLMClient, DeleteLifecyclePolicyCommand as DeleteLifecyclePolicyCommand$1, GetLifecyclePolicyCommand as GetLifecyclePolicyCommand$1, ResourceNotFoundException as ResourceNotFoundException$14, TagResourceCommand as TagResourceCommand$21, UntagResourceCommand as UntagResourceCommand$21, UpdateLifecyclePolicyCommand } from "@aws-sdk/client-dlm";
68
68
  import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$17, S3VectorsClient, TagResourceCommand as TagResourceCommand$22, UntagResourceCommand as UntagResourceCommand$22 } from "@aws-sdk/client-s3vectors";
69
- import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, NotFoundException as NotFoundException$6, S3TablesClient, TagResourceCommand as TagResourceCommand$23, UntagResourceCommand as UntagResourceCommand$23 } from "@aws-sdk/client-s3tables";
69
+ import { ListTagsForResourceCommand as ListTagsForResourceCommand$18, S3ControlClient, TagResourceCommand as TagResourceCommand$23, UntagResourceCommand as UntagResourceCommand$23 } from "@aws-sdk/client-s3-control";
70
+ import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$19, NotFoundException as NotFoundException$6, S3TablesClient, TagResourceCommand as TagResourceCommand$24, UntagResourceCommand as UntagResourceCommand$24 } from "@aws-sdk/client-s3tables";
70
71
  import { Document, Pair, Scalar, YAMLMap, YAMLSeq, parse as parse$1, stringify } from "yaml";
71
72
  import { createLocalStartAgentCoreCommand, createLocalStartCloudFrontCommand, createLocalStateProvider, getEmbedConfig, isCfnFlagPresent, listTargets, rejectExplicitCfnStackWithMultipleStacks, resolveCfnFallbackRegion, setEmbedConfig, substituteAgainstState, substituteAgainstStateAsync, substituteEnvVarsFromState, substituteEnvVarsFromStateAsync } from "cdk-local";
72
73
  import { A2A_CONTAINER_PORT, A2A_PATH, AGENTCORE_A2A_PROTOCOL, AGENTCORE_AGUI_PROTOCOL, AGENTCORE_MCP_PROTOCOL, ConnectionRegistry, EcsTaskResolutionError, HOST_GATEWAY_MIN_VERSION, LocalInvokeBuildError, MCP_CONTAINER_PORT, MCP_PATH, a2aInvokeOnce, addAlbSpecificOptions, addCommonEcsServiceOptions, addStartServiceSpecificOptions, albStrategy, architectureToPlatform, attachAuthorizers, attachStageContext, availableApiIdentifiers, bufferToBody, buildAgentCoreCodeImage, buildCognitoJwksUrl, buildConnectEvent, buildContainerImage, buildCorsConfigByApiId, buildCorsConfigFromCloudFrontChain, buildDisconnectEvent, buildJwksUrlFromIssuer, buildMessageEvent, buildMgmtEndpointEnvUrl, buildStageMap, classifySourceChange, createAuthorizerCache, createFileWatcher, createFileWatcher as createFileWatcher$1, createJwksCache, createWatchPredicates, defaultCredentialsLoader, derivePseudoParametersFromRegion, discoverRoutes, discoverWebSocketApis, downloadAndExtractS3Bundle, filterRoutesByApiIdentifier, groupRoutesByServer, handleConnectionsRequest, invokeAgentCore, invokeAgentCoreWs, materializeLayerFromArn, mcpInvokeOnce, parseConnectionsPath, parseSelectionExpressionPath, pickAgentCoreCandidateStack, pickAgentCoreCandidateStack as pickAgentCoreCandidateStack$1, probeHostGatewaySupport, readMtlsMaterialsFromDisk, resolveAgentCoreTarget, resolveEnvVars, resolveHostGatewayExtraHosts, resolveRuntimeCodeMountPath, resolveRuntimeFileExtension, resolveRuntimeImage, resolveSingleTarget, resolveWatchConfig, runEcsServiceEmulator, signAgentCoreInvocation, startApiServer, substituteImagePlaceholders, tryResolveImageFnJoin, verifyJwtViaDiscovery, waitForAgentCorePing } from "cdk-local/internal";
@@ -43910,9 +43911,11 @@ var S3DirectoryBucketProvider = class {
43910
43911
  handledProperties = /* @__PURE__ */ new Map([["AWS::S3Express::DirectoryBucket", /* @__PURE__ */ new Set([
43911
43912
  "DataRedundancy",
43912
43913
  "LocationName",
43913
- "BucketName"
43914
+ "BucketName",
43915
+ "Tags"
43914
43916
  ])]]);
43915
43917
  ec2Client;
43918
+ s3ControlClient;
43916
43919
  providerRegion = process.env["AWS_REGION"];
43917
43920
  constructor() {
43918
43921
  const awsClients = getAwsClients();
@@ -43924,6 +43927,17 @@ var S3DirectoryBucketProvider = class {
43924
43927
  return this.ec2Client;
43925
43928
  }
43926
43929
  /**
43930
+ * Directory-bucket tags live on the S3 control plane: `PutBucketTagging`
43931
+ * is documented "not supported for directory buckets", and the tag CRUD
43932
+ * after create is S3 Control's `TagResource` / `UntagResource` /
43933
+ * `ListTagsForResource` (requiring the `s3express:TagResource` /
43934
+ * `s3express:UntagResource` / `s3express:ListTagsForResource` permissions).
43935
+ */
43936
+ getS3ControlClient() {
43937
+ if (!this.s3ControlClient) this.s3ControlClient = new S3ControlClient(this.providerRegion ? { region: this.providerRegion } : {});
43938
+ return this.s3ControlClient;
43939
+ }
43940
+ /**
43927
43941
  * Convert AZ name (us-east-1a) to AZ ID (use1-az1) via EC2 DescribeAvailabilityZones
43928
43942
  */
43929
43943
  async getAzId(azName) {
@@ -43954,7 +43968,16 @@ var S3DirectoryBucketProvider = class {
43954
43968
  * Build attributes for a directory bucket
43955
43969
  */
43956
43970
  async buildAttributes(bucketName) {
43957
- return { Arn: `arn:aws:s3express:${await this.getRegion()}:${await this.getAccountId()}:bucket/${bucketName}` };
43971
+ return { Arn: await this.buildBucketArn(bucketName) };
43972
+ }
43973
+ /**
43974
+ * Directory bucket ARN: `arn:aws:s3express:{region}:{account}:bucket/{name}`.
43975
+ * Also the `ResourceArn` for the S3 Control tag operations. Callers that
43976
+ * already resolved the account (for the S3 Control `AccountId` parameter)
43977
+ * pass it in so the STS lookup runs once per operation.
43978
+ */
43979
+ async buildBucketArn(bucketName, accountId) {
43980
+ return `arn:aws:s3express:${await this.getRegion()}:${accountId ?? await this.getAccountId()}:bucket/${bucketName}`;
43958
43981
  }
43959
43982
  /**
43960
43983
  * Create an S3 Express Directory Bucket
@@ -43969,6 +43992,8 @@ var S3DirectoryBucketProvider = class {
43969
43992
  maxLength: 64,
43970
43993
  lowercase: true
43971
43994
  })}--${azId}--x-s3`;
43995
+ const tags = properties["Tags"];
43996
+ const includeTags = tags != null && !(Array.isArray(tags) && tags.length === 0);
43972
43997
  try {
43973
43998
  await this.s3Client.send(new CreateBucketCommand({
43974
43999
  Bucket: bucketName,
@@ -43980,7 +44005,8 @@ var S3DirectoryBucketProvider = class {
43980
44005
  Location: {
43981
44006
  Name: azId,
43982
44007
  Type: "AvailabilityZone"
43983
- }
44008
+ },
44009
+ ...includeTags ? { Tags: tags } : {}
43984
44010
  }
43985
44011
  }));
43986
44012
  this.logger.debug(`Created S3 Express Directory Bucket: ${bucketName}`);
@@ -43997,28 +44023,60 @@ var S3DirectoryBucketProvider = class {
43997
44023
  /**
43998
44024
  * Update an S3 Express Directory Bucket
43999
44025
  *
44000
- * Most properties are immutable, so this is a no-op.
44026
+ * `BucketName` / `DataRedundancy` / `LocationName` are create-only (the
44027
+ * registry-schema fallback classifies changes to them as replacement), so
44028
+ * the only in-place mutable property is `Tags` — applied via S3 Control's
44029
+ * `TagResource` / `UntagResource` (the directory-bucket tag CRUD; the
44030
+ * bucket-level `PutBucketTagging` is not supported for directory buckets).
44031
+ * `TagResource` is additive-only, so a tag dropped from the template
44032
+ * (partial removal) — or the entire `Tags` property removed (full removal,
44033
+ * `newTags === undefined`) — would survive on AWS unless the removed keys
44034
+ * are explicitly `UntagResource`d.
44001
44035
  */
44002
- update(logicalId, physicalId, _resourceType, _properties, _previousProperties) {
44003
- this.logger.debug(`Update for S3 Express Directory Bucket ${logicalId} is a no-op (immutable properties)`);
44004
- return Promise.resolve({
44005
- physicalId,
44006
- wasReplaced: false
44007
- });
44036
+ async update(logicalId, physicalId, resourceType, properties, previousProperties) {
44037
+ try {
44038
+ const newTags = properties["Tags"];
44039
+ const oldTags = previousProperties["Tags"];
44040
+ const malformedNewTags = newTags != null && !Array.isArray(newTags);
44041
+ const newKeys = new Set((Array.isArray(newTags) ? newTags : []).map((t) => t.Key).filter((k) => !!k));
44042
+ const removedKeys = malformedNewTags ? [] : (Array.isArray(oldTags) ? oldTags : []).map((t) => t.Key).filter((k) => !!k && !newKeys.has(k));
44043
+ const tagsToApply = malformedNewTags || Array.isArray(newTags) && newTags.length > 0 ? newTags : void 0;
44044
+ if (JSON.stringify(newTags) !== JSON.stringify(oldTags) && (removedKeys.length > 0 || tagsToApply !== void 0)) {
44045
+ const accountId = await this.getAccountId();
44046
+ const resourceArn = await this.buildBucketArn(physicalId, accountId);
44047
+ if (removedKeys.length > 0) await this.getS3ControlClient().send(new UntagResourceCommand$23({
44048
+ AccountId: accountId,
44049
+ ResourceArn: resourceArn,
44050
+ TagKeys: removedKeys
44051
+ }));
44052
+ if (tagsToApply) await this.getS3ControlClient().send(new TagResourceCommand$23({
44053
+ AccountId: accountId,
44054
+ ResourceArn: resourceArn,
44055
+ Tags: tagsToApply
44056
+ }));
44057
+ this.logger.debug(`Updated tags for S3 Express Directory Bucket ${physicalId}`);
44058
+ } else this.logger.debug(`Update for S3 Express Directory Bucket ${logicalId} is a no-op (no tag changes; other properties are immutable)`);
44059
+ return {
44060
+ physicalId,
44061
+ wasReplaced: false
44062
+ };
44063
+ } catch (error) {
44064
+ const cause = error instanceof Error ? error : void 0;
44065
+ throw new ProvisioningError(`Failed to update S3 Express Directory Bucket ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, physicalId, cause);
44066
+ }
44008
44067
  }
44009
44068
  /**
44010
44069
  * Delete an S3 Express Directory Bucket
44011
44070
  *
44012
44071
  * CloudFormation-parity data guard (issue #1344, sibling of #1340): a
44013
44072
  * non-empty directory bucket is only auto-emptied when the user opted in —
44014
- * the `aws-cdk:auto-delete-objects` tag on the bucket (settable only via
44015
- * `--allow-unsupported-properties` today, since `Tags` is not yet a
44016
- * handled property for this type) or the deploy engine's
44017
- * `--force-stateful-recreation` consent on a replacement delete
44018
- * (`DeleteContext.forceDataDelete`). Otherwise the not-empty error
44019
- * surfaces exactly like CloudFormation's DELETE_FAILED (live-A/B-verified
44020
- * 2026-08-03: CFn fails a non-empty DirectoryBucket delete with "The
44021
- * bucket you tried to delete is not empty", 409).
44073
+ * the `aws-cdk:auto-delete-objects` tag on the bucket (a first-class
44074
+ * template `Tags` entry now that `Tags` is a handled property, issue #609)
44075
+ * or the deploy engine's `--force-stateful-recreation` consent on a
44076
+ * replacement delete (`DeleteContext.forceDataDelete`). Otherwise the
44077
+ * not-empty error surfaces exactly like CloudFormation's DELETE_FAILED
44078
+ * (live-A/B-verified 2026-08-03: CFn fails a non-empty DirectoryBucket
44079
+ * delete with "The bucket you tried to delete is not empty", 409).
44022
44080
  *
44023
44081
  * Directory buckets do not support versioning, so the opted-in auto-empty
44024
44082
  * only needs to delete current objects.
@@ -44027,15 +44085,8 @@ var S3DirectoryBucketProvider = class {
44027
44085
  this.logger.debug(`Deleting S3 Express Directory Bucket ${logicalId}: ${physicalId}`);
44028
44086
  const allowAutoEmpty = context?.forceDataDelete === true || hasCdkAutoDeleteTag(properties, "aws-cdk:auto-delete-objects");
44029
44087
  try {
44030
- if (allowAutoEmpty) {
44031
- this.logger.info(`Emptying directory bucket ${physicalId} before deletion (auto-delete opt-in present)`);
44032
- await this.emptyBucket(physicalId);
44033
- }
44034
- await this.s3Client.send(new DeleteBucketCommand({ Bucket: physicalId }));
44035
- this.logger.debug(`Successfully deleted S3 Express Directory Bucket ${logicalId}`);
44088
+ await this.deleteBucketWithEmptyRetry(logicalId, physicalId, allowAutoEmpty);
44036
44089
  } catch (error) {
44037
- const notEmptyMsg = error instanceof Error ? error.message : String(error);
44038
- 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);
44039
44090
  if (error instanceof Error && (error.name === "NoSuchBucket" || error.name === "BucketNotFound")) {
44040
44091
  assertRegionMatch(await this.s3Client.config.region(), context?.expectedRegion, resourceType, logicalId, physicalId);
44041
44092
  this.logger.debug(`Bucket ${physicalId} does not exist, skipping deletion`);
@@ -44046,6 +44097,35 @@ var S3DirectoryBucketProvider = class {
44046
44097
  }
44047
44098
  }
44048
44099
  /**
44100
+ * Delete the bucket, absorbing the write race on an opted-in auto-empty:
44101
+ * an object written between the empty pass and the delete makes AWS reject
44102
+ * the delete as not-empty, so re-empty and retry (bounded) — the same
44103
+ * bounded loop as the standard-bucket sibling, ported per issue #609's
44104
+ * "Tags becomes a handled property" note (the tag opt-in is mainstream
44105
+ * now, so the ~zero-opt-in rationale for skipping the loop in PR #1347 no
44106
+ * longer holds). Without the opt-in, the first not-empty error surfaces
44107
+ * as the CloudFormation-parity guard (issue #1344).
44108
+ */
44109
+ async deleteBucketWithEmptyRetry(logicalId, bucketName, allowAutoEmpty) {
44110
+ const maxAttempts = 3;
44111
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) try {
44112
+ await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
44113
+ this.logger.debug(`Successfully deleted S3 Express Directory Bucket ${logicalId}`);
44114
+ return;
44115
+ } catch (error) {
44116
+ const msg = error instanceof Error ? error.message : String(error);
44117
+ if (msg.includes("not empty") || msg.includes("BucketNotEmpty")) {
44118
+ if (!allowAutoEmpty) throw new Error(`bucket ${bucketName} 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://${bucketName} --recursive) and destroy again.`);
44119
+ this.logger.info(`Directory bucket ${bucketName} not empty (attempt ${attempt}/${maxAttempts}), emptying (auto-delete opt-in present)...`);
44120
+ await this.emptyBucket(bucketName);
44121
+ continue;
44122
+ }
44123
+ throw error;
44124
+ }
44125
+ await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
44126
+ this.logger.debug(`Successfully deleted S3 Express Directory Bucket ${logicalId}`);
44127
+ }
44128
+ /**
44049
44129
  * Empty all objects from a directory bucket.
44050
44130
  * Lists and deletes objects in batches of 1000.
44051
44131
  */
@@ -44076,10 +44156,11 @@ var S3DirectoryBucketProvider = class {
44076
44156
  * CFn-property shape.
44077
44157
  *
44078
44158
  * Issues `HeadBucket` to verify existence and surfaces `BucketName`
44079
- * (the physicalId). `DataRedundancy` and `LocationName` are not exposed
44080
- * by any cheap S3 Express API — the only way to inspect them is by
44081
- * parsing the bucket name's `--<az-id>--x-s3` suffix, which is best
44082
- * effort. We surface them when they are recoverable from the bucket
44159
+ * (the physicalId) plus `Tags` (via S3 Control `ListTagsForResource`,
44160
+ * the directory-bucket tag read). `DataRedundancy` and `LocationName`
44161
+ * are not exposed by any cheap S3 Express API — the only way to inspect
44162
+ * them is by parsing the bucket name's `--<az-id>--x-s3` suffix, which is
44163
+ * best effort. We surface them when they are recoverable from the bucket
44083
44164
  * name to give the comparator a chance to detect mismatches.
44084
44165
  *
44085
44166
  * `--<az-id>--x-s3` suffix parsing: directory bucket names follow the
@@ -44105,8 +44186,17 @@ var S3DirectoryBucketProvider = class {
44105
44186
  }
44106
44187
  const result = { BucketName: physicalId };
44107
44188
  if (physicalId.endsWith("--x-s3")) result["DataRedundancy"] = "SingleAvailabilityZone";
44189
+ result["Tags"] = await this.readTags(physicalId);
44108
44190
  return result;
44109
44191
  }
44192
+ async readTags(bucketName) {
44193
+ const accountId = await this.getAccountId();
44194
+ const resourceArn = await this.buildBucketArn(bucketName, accountId);
44195
+ return normalizeAwsTagsToCfn((await this.getS3ControlClient().send(new ListTagsForResourceCommand$18({
44196
+ AccountId: accountId,
44197
+ ResourceArn: resourceArn
44198
+ }))).Tags);
44199
+ }
44110
44200
  /**
44111
44201
  * Adopt an existing S3 Express Directory Bucket into cdkd state.
44112
44202
  *
@@ -44592,7 +44682,7 @@ var S3TablesProvider = class {
44592
44682
  */
44593
44683
  async readTagsBestEffort(resourceArn) {
44594
44684
  try {
44595
- const tags = (await this.getClient().send(new ListTagsForResourceCommand$18({ resourceArn }))).tags ?? {};
44685
+ const tags = (await this.getClient().send(new ListTagsForResourceCommand$19({ resourceArn }))).tags ?? {};
44596
44686
  const out = [];
44597
44687
  for (const [Key, Value] of Object.entries(tags)) out.push({
44598
44688
  Key,
@@ -44646,7 +44736,7 @@ var S3TablesProvider = class {
44646
44736
  const resourceArn = await this.lookupTableArn(tableBucketARN, namespace, name);
44647
44737
  if (!resourceArn) throw new ProvisioningError(`applyTableTagsDiff: GetTable returned no tableARN for ${physicalId} — table is gone or state is out-of-sync. Refusing to silently drop the tag update (run 'cdkd state orphan ${logicalId}' to clean up if the table was deleted out-of-band).`, resourceType, logicalId, physicalId);
44648
44738
  if (removedKeys.length > 0) try {
44649
- await this.getClient().send(new UntagResourceCommand$23({
44739
+ await this.getClient().send(new UntagResourceCommand$24({
44650
44740
  resourceArn,
44651
44741
  tagKeys: removedKeys
44652
44742
  }));
@@ -44655,7 +44745,7 @@ var S3TablesProvider = class {
44655
44745
  throw new ProvisioningError(`applyTableTagsDiff: UntagResource failed for ${resourceArn} (keys: ${removedKeys.join(", ")}): ${err instanceof Error ? err.message : String(err)}. State has NOT been updated so the next deploy will retry the tag-diff.`, resourceType, logicalId, physicalId, cause);
44656
44746
  }
44657
44747
  if (Object.keys(upserts).length > 0) try {
44658
- await this.getClient().send(new TagResourceCommand$23({
44748
+ await this.getClient().send(new TagResourceCommand$24({
44659
44749
  resourceArn,
44660
44750
  tags: upserts
44661
44751
  }));
@@ -44687,7 +44777,7 @@ var S3TablesProvider = class {
44687
44777
  if (removedKeys.length === 0 && Object.keys(upserts).length === 0) return;
44688
44778
  const resourceArn = physicalId;
44689
44779
  if (removedKeys.length > 0) try {
44690
- await this.getClient().send(new UntagResourceCommand$23({
44780
+ await this.getClient().send(new UntagResourceCommand$24({
44691
44781
  resourceArn,
44692
44782
  tagKeys: removedKeys
44693
44783
  }));
@@ -44696,7 +44786,7 @@ var S3TablesProvider = class {
44696
44786
  throw new ProvisioningError(`applyTableBucketTagsDiff: UntagResource failed for ${resourceArn} (keys: ${removedKeys.join(", ")}): ${err instanceof Error ? err.message : String(err)}. State has NOT been updated so the next deploy will retry the tag-diff.`, resourceType, logicalId, physicalId, cause);
44697
44787
  }
44698
44788
  if (Object.keys(upserts).length > 0) try {
44699
- await this.getClient().send(new TagResourceCommand$23({
44789
+ await this.getClient().send(new TagResourceCommand$24({
44700
44790
  resourceArn,
44701
44791
  tags: upserts
44702
44792
  }));
@@ -66616,7 +66706,7 @@ function createMigrateCommand() {
66616
66706
  */
66617
66707
  function buildProgram() {
66618
66708
  const program = new Command();
66619
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.35");
66709
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.36");
66620
66710
  program.hook("preAction", (_thisCommand, actionCommand) => {
66621
66711
  const { profile } = actionCommand.optsWithGlobals();
66622
66712
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;