@go-to-k/cdkd 0.278.21 → 0.278.22

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,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { $ as isThrottlingError, $t as setAwsClients, A as red, At as getLegacyStateBucketName, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_BODY_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerForeground, D as cyan, Dt as Synthesizer, E as bold, Et as getDockerImageBySourceHash, F as ProviderRegistry, Ft as resolveStateBucketWithDefault, G as normalizeAwsTagsToCfn, Gt as expectedOwnerParam, H as cfnRefValueFromPhysicalId, Ht as MIGRATE_TMP_PREFIX, I as findActionableSilentDrops, It as resolveStateBucketWithDefaultAndSource, J as applyRoleArnIfSet, K as resolveExplicitPhysicalId, Kt as AssemblyReader, L as CloudControlProvider, Lt as resolveUseCdkBootstrapAssets, M as IAMRoleProvider, Mt as resolveAutoAssetStorage, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveCaptureObservedState, O as gray, Ot as synthesisStatusMessage, P as clearOnUpdateRemoval, Pt as resolveSkipPrefix, Q as isRetryableTransientError, Qt as resetAwsClients, R as slowCcOperationTimeoutMs, Rt as stateBucketExistenceConfirmed, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as withErrorHandling, St as getDockerCmd, T as formatResourceLine, Tt as AssetManifestLoader, U as refStateLookupFromResource, Ut as findLargeInlineResources, V as IntrinsicFunctionResolver, Vt as CFN_TEMPLATE_URL_LIMIT, W as WAFv2WebACLProvider, Wt as uploadCfnTemplate, X as describeTypeWithThrottleRetry, Xt as AwsClients, Y as DiffCalculator, Yt as resolveBucketRegion, Z as withRetry, Zt as getAwsClients, _ as createPreDeleteFinalSnapshot, _t as parseBootstrapMarker, a as DeploymentEventsStore, an as LocalInvokeBuildError$1, at as shouldRetainResource, b as unsupportedFinalSnapshotError, bt as buildDockerImage, c as replayFailedOperations, ct as WorkGraph, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PartialFailureError, dt as loadPublishableAssetManifest, et as DagBuilder, f as computeImplicitDeleteEdges, fn as ProvisioningError, ft as rewriteTemplateAssetReferences, g as ccRoutedFinalSnapshotError, gn as StackTerminationProtectionError, gt as getBootstrapMarkerKey, h as buildFinalSnapshotIdentifier, hn as StackHasActiveImportsError, ht as ensureAssetStorage, i as DeploymentEventsReader, in as DeployCancelledError, it as rebuildClientForBucketRegion, j as yellow, jt as resolveApp, k as green, kt as getDefaultStateBucketName, l as replayRollback, ln as MissingCdkCliError, lt as buildAssetRedirectMap, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ResourceUpdateNotSupportedError, mt as BOOTSTRAP_MARKER_PREFIX, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LockManager, o as planFailedOps, on as LocalMigrateError, ot as AssetPublisher, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceTimeoutError, pt as AssetModeResolver, q as assertRegionMatch, qt as processStackMessages, r as DeployEngine, rt as S3StateBackend, s as planRollback, sn as LocalStartServiceError, st as stringifyValue, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as CdkdError, tt as TemplateParser, u as withResourceDeadline, un as NestedStackChildDirectDestroyError, ut as createAssetRedirectResolver, v as isFinalSnapshotError, vt as validateAssetBucketName, w as renderStatefulReason, wt as runDockerStreaming, x as extractDeploymentEventError, xn as normalizeAwsError, xt as formatDockerLoginError, y as refusesFinalSnapshot, yt as validateContainerRepoName, z as disableInstanceApiTermination, zt as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-dvS1K_ET.js";
2
+ import { $ as isThrottlingError, $t as setAwsClients, A as red, At as getLegacyStateBucketName, B as isTerminationProtectionPropagationError, Bt as CFN_TEMPLATE_BODY_LIMIT, C as isStatefulRecreateTargetSync, Ct as runDockerForeground, D as cyan, Dt as Synthesizer, E as bold, Et as getDockerImageBySourceHash, F as ProviderRegistry, Ft as resolveStateBucketWithDefault, G as normalizeAwsTagsToCfn, Gt as expectedOwnerParam, H as cfnRefValueFromPhysicalId, Ht as MIGRATE_TMP_PREFIX, I as findActionableSilentDrops, It as resolveStateBucketWithDefaultAndSource, J as applyRoleArnIfSet, K as resolveExplicitPhysicalId, Kt as AssemblyReader, L as CloudControlProvider, Lt as resolveUseCdkBootstrapAssets, M as IAMRoleProvider, Mt as resolveAutoAssetStorage, N as collectInlinePolicyNamesManagedBySiblings, Nt as resolveCaptureObservedState, O as gray, Ot as synthesisStatusMessage, P as clearOnUpdateRemoval, Pt as resolveSkipPrefix, Q as isRetryableTransientError, Qt as resetAwsClients, R as slowCcOperationTimeoutMs, Rt as stateBucketExistenceConfirmed, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as withErrorHandling, St as getDockerCmd, T as formatResourceLine, Tt as AssetManifestLoader, U as refStateLookupFromResource, Ut as findLargeInlineResources, V as IntrinsicFunctionResolver, Vt as CFN_TEMPLATE_URL_LIMIT, W as WAFv2WebACLProvider, Wt as uploadCfnTemplate, X as describeTypeWithThrottleRetry, Xt as AwsClients, Y as DiffCalculator, Yt as resolveBucketRegion, Z as withRetry, Zt as getAwsClients, _ as createPreDeleteFinalSnapshot, _t as parseBootstrapMarker, a as DeploymentEventsStore, an as LocalInvokeBuildError$1, at as shouldRetainResource, b as unsupportedFinalSnapshotError, bt as buildDockerImage, c as replayFailedOperations, ct as WorkGraph, d as IMPLICIT_DELETE_DEPENDENCIES, dn as PartialFailureError, dt as loadPublishableAssetManifest, et as DagBuilder, f as computeImplicitDeleteEdges, fn as ProvisioningError, ft as rewriteTemplateAssetReferences, g as ccRoutedFinalSnapshotError, gn as StackTerminationProtectionError, gt as getBootstrapMarkerKey, h as buildFinalSnapshotIdentifier, hn as StackHasActiveImportsError, ht as ensureAssetStorage, i as DeploymentEventsReader, in as DeployCancelledError, it as rebuildClientForBucketRegion, j as yellow, jt as resolveApp, k as green, kt as getDefaultStateBucketName, l as replayRollback, ln as MissingCdkCliError, lt as buildAssetRedirectMap, m as PRE_DELETE_SNAPSHOT_TYPES, mn as ResourceUpdateNotSupportedError, mt as BOOTSTRAP_MARKER_PREFIX, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as LockManager, o as planFailedOps, on as LocalMigrateError, ot as AssetPublisher, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as ResourceTimeoutError, pt as AssetModeResolver, q as assertRegionMatch, qt as processStackMessages, r as DeployEngine, rt as S3StateBackend, s as planRollback, sn as LocalStartServiceError, st as stringifyValue, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as CdkdError, tt as TemplateParser, u as withResourceDeadline, un as NestedStackChildDirectDestroyError, ut as createAssetRedirectResolver, v as isFinalSnapshotError, vt as validateAssetBucketName, w as renderStatefulReason, wt as runDockerStreaming, x as extractDeploymentEventError, xn as normalizeAwsError, xt as formatDockerLoginError, y as refusesFinalSnapshot, yt as validateContainerRepoName, z as disableInstanceApiTermination, zt as warnDeprecatedNoPrefixCliFlag } from "./deploy-engine-CkuG2VD5.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-BN1QMZlz.js";
4
+ import { t as ASGProvider } from "./asg-provider-BBsW05cu.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
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";
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";
8
8
  import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachRolePolicyCommand, AttachUserPolicyCommand, CreateAccessKeyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreatePolicyCommand, CreatePolicyVersionCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeletePolicyCommand, DeletePolicyVersionCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachRolePolicyCommand, DetachUserPolicyCommand, GetAccessKeyLastUsedCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetPolicyCommand, GetPolicyVersionCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListEntitiesForPolicyCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListPoliciesCommand, ListPolicyTagsCommand, ListPolicyVersionsCommand, ListUserPoliciesCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagPolicyCommand, TagUserCommand, UntagPolicyCommand, UntagUserCommand, UpdateAccessKeyCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
9
9
  import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, GetQueueUrlCommand, ListQueueTagsCommand, QueueDoesNotExist, SQSClient, SetQueueAttributesCommand, TagQueueCommand, UntagQueueCommand } from "@aws-sdk/client-sqs";
10
10
  import { CreateTopicCommand, DeleteTopicCommand, GetSubscriptionAttributesCommand, GetTopicAttributesCommand, InvalidParameterException, ListSubscriptionsByTopicCommand, ListTagsForResourceCommand, ListTopicsCommand, NotFoundException, SNSClient, SetTopicAttributesCommand, SubscribeCommand, TagResourceCommand, UnsubscribeCommand, UntagResourceCommand } from "@aws-sdk/client-sns";
@@ -4868,7 +4868,7 @@ function coerceCfnBoolean(value) {
4868
4868
  * S3's CreateBucket is synchronous - no polling needed, unlike CC API which
4869
4869
  * requires async polling (1s→1.5s→2.25s...) adding seconds per resource.
4870
4870
  */
4871
- var S3BucketProvider = class {
4871
+ var S3BucketProvider = class S3BucketProvider {
4872
4872
  s3Client;
4873
4873
  logger = getLogger().child("S3BucketProvider");
4874
4874
  handledProperties = /* @__PURE__ */ new Map([["AWS::S3::Bucket", /* @__PURE__ */ new Set([
@@ -5142,6 +5142,7 @@ var S3BucketProvider = class {
5142
5142
  * - BucketKeyEnabled
5143
5143
  */
5144
5144
  async applyBucketEncryption(bucketName, encryptionConfig) {
5145
+ if (!Array.isArray(encryptionConfig?.ServerSideEncryptionConfiguration)) throw new Error(`BucketEncryption.ServerSideEncryptionConfiguration must be an array (got ${encryptionConfig?.ServerSideEncryptionConfiguration === void 0 ? "undefined" : typeof encryptionConfig.ServerSideEncryptionConfiguration}) — check for an unresolved intrinsic or a mis-nested template value`);
5145
5146
  const rules = encryptionConfig.ServerSideEncryptionConfiguration.map((rule) => {
5146
5147
  const byDefault = rule["ServerSideEncryptionByDefault"];
5147
5148
  return {
@@ -5525,30 +5526,91 @@ var S3BucketProvider = class {
5525
5526
  this.logger.debug(`Applied object lock configuration to bucket ${bucketName}`);
5526
5527
  }
5527
5528
  /**
5528
- * Apply additional bucket configuration after creation
5529
+ * Apply additional bucket configuration after creation.
5530
+ *
5531
+ * `options.skipDiffManaged` is passed by `update()` for the three
5532
+ * sub-configs that moved onto the diff path (`VersioningConfiguration` /
5533
+ * `OwnershipControls` / `BucketEncryption`, issue #1466). They stay here for
5534
+ * CREATE — where there is no previous side to diff against — but on UPDATE
5535
+ * they must go through `applySubConfigDiffs` so a REMOVED property is reset
5536
+ * to the CloudFormation default instead of silently surviving. Applying them
5537
+ * in both places would double-PUT.
5529
5538
  */
5530
- async applyConfiguration(bucketName, properties, skipTags = false) {
5539
+ async applyConfiguration(bucketName, properties, options = {}) {
5540
+ const { skipTags = false, skipDiffManaged = false } = options;
5531
5541
  const versioningConfig = properties["VersioningConfiguration"];
5532
- if (versioningConfig) await this.applyVersioning(bucketName, versioningConfig);
5542
+ if (!skipDiffManaged && versioningConfig) await this.applyVersioning(bucketName, versioningConfig);
5533
5543
  const tags = properties["Tags"];
5534
5544
  if (!skipTags && tags && Array.isArray(tags) && tags.length > 0) await this.applyTags(bucketName, tags);
5535
5545
  const ownershipControls = properties["OwnershipControls"];
5536
- if (ownershipControls?.Rules) {
5537
- await this.s3Client.send(new PutBucketOwnershipControlsCommand({
5538
- Bucket: bucketName,
5539
- OwnershipControls: { Rules: ownershipControls.Rules.map((r) => ({ ObjectOwnership: r.ObjectOwnership })) }
5540
- }));
5541
- this.logger.debug(`Applied ownership controls to bucket ${bucketName}`);
5542
- }
5546
+ const normalizedOwnership = S3BucketProvider.emptyListConfigToUndefined(ownershipControls, "Rules");
5547
+ if (!skipDiffManaged && normalizedOwnership !== void 0) await this.applyOwnershipControls(bucketName, normalizedOwnership);
5543
5548
  const publicAccessBlock = properties["PublicAccessBlockConfiguration"];
5544
5549
  if (publicAccessBlock) await this.applyPublicAccessBlockConfiguration(bucketName, publicAccessBlock);
5545
- const bucketEncryption = properties["BucketEncryption"];
5546
- if (bucketEncryption?.ServerSideEncryptionConfiguration && Array.isArray(bucketEncryption.ServerSideEncryptionConfiguration) && bucketEncryption.ServerSideEncryptionConfiguration.length > 0) await this.applyBucketEncryption(bucketName, bucketEncryption);
5550
+ const normalizedEncryption = S3BucketProvider.emptyListConfigToUndefined(properties["BucketEncryption"], "ServerSideEncryptionConfiguration");
5551
+ if (!skipDiffManaged && normalizedEncryption !== void 0) await this.applyBucketEncryption(bucketName, normalizedEncryption);
5552
+ }
5553
+ /**
5554
+ * CFn property: OwnershipControls
5555
+ * SDK: PutBucketOwnershipControls
5556
+ */
5557
+ async applyOwnershipControls(bucketName, ownershipControls) {
5558
+ if (!Array.isArray(ownershipControls?.Rules)) throw new Error(`OwnershipControls.Rules must be an array (got ${ownershipControls?.Rules === void 0 ? "undefined" : typeof ownershipControls.Rules}) — check for an unresolved intrinsic or a mis-nested template value`);
5559
+ await this.s3Client.send(new PutBucketOwnershipControlsCommand({
5560
+ Bucket: bucketName,
5561
+ OwnershipControls: { Rules: ownershipControls.Rules.map((r) => ({ ObjectOwnership: r.ObjectOwnership })) }
5562
+ }));
5563
+ this.logger.debug(`Applied ownership controls to bucket ${bucketName}`);
5564
+ }
5565
+ /**
5566
+ * Normalize an "empty container" sub-config value to `undefined` so the diff
5567
+ * path cannot mistake a placeholder for a real value.
5568
+ *
5569
+ * `readCurrentState` always-emits placeholder shapes for un-configured
5570
+ * features (`BucketEncryption: { ServerSideEncryptionConfiguration: [] }`,
5571
+ * and the `OwnershipControls: { Rules: [] }` added with issue #1466), and a
5572
+ * `cdkd drift --revert` round-trip feeds those back through `update()`.
5573
+ *
5574
+ * The rule is "empty container == not declared", applied to BOTH sides. Per
5575
+ * pairing that means:
5576
+ * empty desired + absent previous -> no call (without this, a Put with an
5577
+ * empty list, which both APIs reject)
5578
+ * absent desired + empty previous -> no call (without this, a Delete
5579
+ * against a bucket that never had it)
5580
+ * empty desired + REAL previous -> Delete. This one is NOT suppressed
5581
+ * and must not be: it is a genuine
5582
+ * declared -> not-declared transition,
5583
+ * which is exactly what CloudFormation
5584
+ * removes.
5585
+ *
5586
+ * "Empty" is deliberately NARROW. Only two shapes fold to `undefined`:
5587
+ * a block with NO keys at all (`{}`), and a block whose list key holds an
5588
+ * empty array. Everything else — a non-object, an array where the object
5589
+ * belongs (wrong nesting), a block whose list key is absent but which
5590
+ * carries OTHER keys, or a non-array list (an unresolved intrinsic) — is
5591
+ * MALFORMED, not empty, and passes through so the apply call refuses it by
5592
+ * name. Folding any of those to `undefined` would emit a Delete and
5593
+ * silently downgrade a live bucket (KMS -> AES256) while the template still
5594
+ * declares the config.
5595
+ *
5596
+ * NOTE this helper does NOT cover `VersioningConfiguration` (it has no list
5597
+ * key), and that property is NOT malformed-shape-refused anywhere: a
5598
+ * malformed value there still resolves to 'Suspended', matching `main`.
5599
+ * That pre-existing gap is tracked in issue #1471 — see the note in
5600
+ * `applySubConfigDiffs` for why guarding it is not as simple as it looks.
5601
+ */
5602
+ static emptyListConfigToUndefined(config, listKey) {
5603
+ if (config === void 0 || config === null) return void 0;
5604
+ if (typeof config !== "object" || Array.isArray(config)) return config;
5605
+ const list = config[listKey];
5606
+ if (list === void 0 || list === null) return Object.keys(config).length === 0 ? void 0 : config;
5607
+ if (Array.isArray(list) && list.length === 0) return void 0;
5608
+ return config;
5547
5609
  }
5548
5610
  /**
5549
5611
  * Diff CFn-shape sub-config values between previous and new state.
5550
5612
  *
5551
- * Three transitions:
5613
+ * Four transitions:
5552
5614
  * - undefined -> defined (value differs from previous, OR previous undefined): Put
5553
5615
  * - defined -> undefined: Delete
5554
5616
  * - defined -> defined (different): Put
@@ -5593,13 +5655,33 @@ var S3BucketProvider = class {
5593
5655
  * Apply the diff between previous and new sub-configs, issuing Put / Delete
5594
5656
  * SDK calls only for differing keys. Called from `update()`.
5595
5657
  *
5596
- * Versioning / PublicAccessBlock / Tags / OwnershipControls / BucketEncryption
5597
- * stay on `applyConfiguration` (the unconditional always-PUT path) because
5598
- * their AWS APIs don't have a clean "delete" counterpart and they
5599
- * round-trip safely as no-ops when state == AWS-current. The 12 sub-configs
5600
- * below DO have proper Put/Delete pairs so the diff path is preferable.
5658
+ * PublicAccessBlockConfiguration and Tags stay on `applyConfiguration` /
5659
+ * `applyTagDiff`. `PublicAccessBlockConfiguration` is deliberately NOT on the
5660
+ * diff path: a live CloudFormation A/B (issue #1466) confirmed CFn ALSO
5661
+ * leaves the block in place when the property is removed, so cdkd is at
5662
+ * parity and adding an `onRemove` here would CREATE a divergence.
5663
+ *
5664
+ * VersioningConfiguration / OwnershipControls / BucketEncryption used to sit
5665
+ * on that always-PUT path too, on the assumption that their APIs had no clean
5666
+ * "delete" counterpart. That was wrong in all three cases and made removal a
5667
+ * silent no-op (issue #1466): the same A/B showed CFn suspends versioning,
5668
+ * deletes ownership controls, and resets encryption to the SSE-S3 default.
5669
+ * They are now diffed here like every other Put/Delete pair.
5601
5670
  */
5602
5671
  async applySubConfigDiffs(bucketName, properties, previousProperties) {
5672
+ const previousVersioning = previousProperties["VersioningConfiguration"];
5673
+ const nextVersioning = properties["VersioningConfiguration"];
5674
+ const versioningChanged = JSON.stringify(previousVersioning ?? null) !== JSON.stringify(nextVersioning ?? null);
5675
+ const desiredVersioningStatus = nextVersioning == null ? "Suspended" : nextVersioning["Status"] || "Suspended";
5676
+ if (versioningChanged && nextVersioning != null && desiredVersioningStatus !== "Suspended") await this.applyVersioning(bucketName, nextVersioning);
5677
+ await this.diffSubConfig(bucketName, S3BucketProvider.emptyListConfigToUndefined(previousProperties["OwnershipControls"], "Rules"), S3BucketProvider.emptyListConfigToUndefined(properties["OwnershipControls"], "Rules"), async (cfg) => this.applyOwnershipControls(bucketName, cfg), async () => {
5678
+ await this.s3Client.send(new DeleteBucketOwnershipControlsCommand({ Bucket: bucketName }));
5679
+ this.logger.debug(`Deleted ownership controls on bucket ${bucketName}`);
5680
+ });
5681
+ await this.diffSubConfig(bucketName, S3BucketProvider.emptyListConfigToUndefined(previousProperties["BucketEncryption"], "ServerSideEncryptionConfiguration"), S3BucketProvider.emptyListConfigToUndefined(properties["BucketEncryption"], "ServerSideEncryptionConfiguration"), async (cfg) => this.applyBucketEncryption(bucketName, cfg), async () => {
5682
+ await this.s3Client.send(new DeleteBucketEncryptionCommand({ Bucket: bucketName }));
5683
+ this.logger.debug(`Deleted bucket encryption on bucket ${bucketName}`);
5684
+ });
5603
5685
  await this.diffSubConfig(bucketName, previousProperties["LifecycleConfiguration"], properties["LifecycleConfiguration"], async (cfg) => {
5604
5686
  if (!cfg.Rules || !Array.isArray(cfg.Rules) || cfg.Rules.length === 0) return;
5605
5687
  await this.applyLifecycleConfiguration(bucketName, cfg);
@@ -5660,6 +5742,14 @@ var S3BucketProvider = class {
5660
5742
  }));
5661
5743
  this.logger.debug(`Deleted inventory configuration ${id} on bucket ${bucketName}`);
5662
5744
  });
5745
+ if (versioningChanged && desiredVersioningStatus === "Suspended") {
5746
+ const hasObjectLock = (p) => {
5747
+ if (p["ObjectLockEnabled"] === true || p["ObjectLockEnabled"] === "true") return true;
5748
+ return p["ObjectLockConfiguration"]?.["ObjectLockEnabled"] === "Enabled";
5749
+ };
5750
+ if (hasObjectLock(properties) || hasObjectLock(previousProperties)) this.logger.warn(`Bucket ${bucketName}: versioning would be suspended (VersioningConfiguration removed or set to Suspended), but the bucket has Object Lock enabled and S3 does not allow suspending versioning on it. Leaving versioning enabled.`);
5751
+ else await this.applyVersioning(bucketName, { Status: "Suspended" });
5752
+ }
5663
5753
  }
5664
5754
  /**
5665
5755
  * Apply ALL sub-configs unconditionally on initial create. Used by
@@ -5752,7 +5842,10 @@ var S3BucketProvider = class {
5752
5842
  };
5753
5843
  }
5754
5844
  try {
5755
- await this.applyConfiguration(physicalId, properties, true);
5845
+ await this.applyConfiguration(physicalId, properties, {
5846
+ skipTags: true,
5847
+ skipDiffManaged: true
5848
+ });
5756
5849
  await this.applySubConfigDiffs(physicalId, properties, previousProperties);
5757
5850
  await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
5758
5851
  const attributes = await this.buildAttributes(physicalId);
@@ -5804,7 +5897,8 @@ var S3BucketProvider = class {
5804
5897
  * returns `undefined`.
5805
5898
  *
5806
5899
  * Coverage: `BucketName`, `VersioningConfiguration`, `BucketEncryption`,
5807
- * `PublicAccessBlockConfiguration`, `Tags`, plus all 12 sub-configs:
5900
+ * `OwnershipControls`, `PublicAccessBlockConfiguration`, `Tags`, plus all 12
5901
+ * sub-configs:
5808
5902
  * `LifecycleConfiguration`, `CorsConfiguration`, `WebsiteConfiguration`,
5809
5903
  * `LoggingConfiguration`, `NotificationConfiguration`,
5810
5904
  * `ReplicationConfiguration`, `ObjectLockConfiguration`,
@@ -5820,9 +5914,10 @@ var S3BucketProvider = class {
5820
5914
  if (err instanceof NoSuchBucket || e.name === "NotFound" || e.name === "NoSuchBucket" || e.$metadata?.httpStatusCode === 404) return;
5821
5915
  throw err;
5822
5916
  }
5823
- const [versioning, encryption, pab, tags, lifecycle, cors, website, logging, notification, replication, objectLock, accelerate, metrics, analytics, intelligentTier, inventory] = await Promise.all([
5917
+ const [versioning, encryption, ownership, pab, tags, lifecycle, cors, website, logging, notification, replication, objectLock, accelerate, metrics, analytics, intelligentTier, inventory] = await Promise.all([
5824
5918
  this.readVersioning(physicalId),
5825
5919
  this.readEncryption(physicalId),
5920
+ this.readOwnershipControls(physicalId),
5826
5921
  this.readPublicAccessBlock(physicalId),
5827
5922
  this.readTags(physicalId),
5828
5923
  this.readLifecycle(physicalId),
@@ -5842,6 +5937,7 @@ var S3BucketProvider = class {
5842
5937
  BucketName: physicalId,
5843
5938
  VersioningConfiguration: versioning,
5844
5939
  BucketEncryption: encryption,
5940
+ OwnershipControls: ownership,
5845
5941
  PublicAccessBlockConfiguration: pab,
5846
5942
  Tags: tags,
5847
5943
  LifecycleConfiguration: lifecycle,
@@ -5861,6 +5957,22 @@ var S3BucketProvider = class {
5861
5957
  async readVersioning(bucket) {
5862
5958
  return { Status: (await this.s3Client.send(new GetBucketVersioningCommand({ Bucket: bucket }))).Status ?? "Suspended" };
5863
5959
  }
5960
+ /**
5961
+ * OwnershipControls { Rules: [{ ObjectOwnership }] }. Always emit a
5962
+ * placeholder (empty `Rules`) so a console-side change on a bucket that
5963
+ * declares the property surfaces as drift. Added with issue #1466 — without
5964
+ * a read here, `cdkd drift` could not see ownership controls at all, which
5965
+ * is why the removal bug that issue fixes produced no signal from ANY
5966
+ * command.
5967
+ */
5968
+ async readOwnershipControls(bucket) {
5969
+ try {
5970
+ return { Rules: ((await this.s3Client.send(new GetBucketOwnershipControlsCommand({ Bucket: bucket }))).OwnershipControls?.Rules ?? []).map((r) => ({ ObjectOwnership: r.ObjectOwnership })) };
5971
+ } catch (err) {
5972
+ if (err.name === "OwnershipControlsNotFoundError") return { Rules: [] };
5973
+ throw err;
5974
+ }
5975
+ }
5864
5976
  async readEncryption(bucket) {
5865
5977
  try {
5866
5978
  return { ServerSideEncryptionConfiguration: ((await this.s3Client.send(new GetBucketEncryptionCommand({ Bucket: bucket }))).ServerSideEncryptionConfiguration?.Rules ?? []).map((rule) => {
@@ -65486,7 +65598,7 @@ function createMigrateCommand() {
65486
65598
  */
65487
65599
  function buildProgram() {
65488
65600
  const program = new Command();
65489
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.21");
65601
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.278.22");
65490
65602
  program.hook("preAction", (_thisCommand, actionCommand) => {
65491
65603
  const { profile } = actionCommand.optsWithGlobals();
65492
65604
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;