@go-to-k/cdkd 0.280.35 → 0.280.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 requireConfigObject, $t as AssemblyReader, A as green, At as runDockerForeground, B as disableInstanceApiTermination, Bt as resolveCaptureObservedState, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackTerminationProtectionError, Ct as getBootstrapMarkerKey, D as bold, Dt as buildDockerImage, E as formatResourceLine, Et as validateContainerRepoName, F as clearOnUpdateRemoval, Ft as synthesisStatusMessage, G as WAFv2WebACLProvider, Gt as stateBucketExistenceConfirmed, H as IntrinsicFunctionResolver, Ht as resolveStateBucketWithDefault, I as ProviderRegistry, It as getDefaultStateBucketName, J as assertRegionMatch, Jt as CFN_TEMPLATE_URL_LIMIT, K as normalizeAwsTagsToCfn, Kt as warnDeprecatedNoPrefixCliFlag, L as findActionableSilentDrops, Lt as getLegacyStateBucketName, M as yellow, Mt as AssetManifestLoader, N as IAMRoleProvider, Nt as getDockerImageBySourceHash, O as cyan, On as normalizeAwsError, Ot as formatDockerLoginError, P as collectInlinePolicyNamesManagedBySiblings, Pt as Synthesizer, Q as requireConfigArray, Qt as expectedOwnerParam, R as CloudControlProvider, Rt as resolveApp, S as extractDeploymentEventError, Sn as StackHasActiveImportsError, St as ensureAssetStorage, T as renderStatefulReason, Tt as validateAssetBucketName, U as cfnRefValueFromPhysicalId, Ut as resolveStateBucketWithDefaultAndSource, V as isTerminationProtectionPropagationError, Vt as resolveSkipPrefix, W as refStateLookupFromResource, Wt as resolveUseCdkBootstrapAssets, X as readConfigString, Xt as findLargeInlineResources, Y as configStringRefusal, Yt as MIGRATE_TMP_PREFIX, Z as replayWarn, Zt as uploadCfnTemplate, _ as createPreDeleteFinalSnapshot, _n as NestedStackChildDirectDestroyError, _t as createAssetRedirectResolver, a as DeploymentEventsStore, an as resetAwsClients, at as isRetryableTransientError, b as unsupportedFinalSnapshotError, bn as ResourceTimeoutError, bt as AssetModeResolver, c as replayFailedOperations, cn as CdkdError, ct as TemplateParser, d as IMPLICIT_DELETE_DEPENDENCIES, dn as DeployCancelledError, dt as rebuildClientForBucketRegion, en as processStackMessages, et as requireConfigString, f as computeImplicitDeleteEdges, fn as LocalInvokeBuildError$1, ft as shouldRetainResource, g as ccRoutedFinalSnapshotError, gn as MissingCdkCliError, gt as buildAssetRedirectMap, h as buildFinalSnapshotIdentifier, ht as WorkGraph, i as DeploymentEventsReader, in as getAwsClients, it as withRetry, j as red, jt as runDockerStreaming, k as gray, kn as withErrorHandling, kt as getDockerCmd, l as replayRollback, lt as LockManager, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalStartServiceError, mt as stringifyValue, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveBucketRegion, nt as DiffCalculator, o as planFailedOps, on as setAwsClients, ot as isThrottlingError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalMigrateError, pt as AssetPublisher, q as resolveExplicitPhysicalId, qt as CFN_TEMPLATE_BODY_LIMIT, r as DeployEngine, rn as AwsClients, rt as describeTypeWithThrottleRetry, s as planRollback, st as DagBuilder, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as applyRoleArnIfSet, u as withResourceDeadline, ut as S3StateBackend, v as isFinalSnapshotError, vn as PartialFailureError, vt as loadPublishableAssetManifest, w as isStatefulRecreateTargetSync, wt as parseBootstrapMarker, x as makeCanonicalizePropertiesFn, xn as ResourceUpdateNotSupportedError, xt as BOOTSTRAP_MARKER_PREFIX, y as refusesFinalSnapshot, yn as ProvisioningError, yt as rewriteTemplateAssetReferences, z as slowCcOperationTimeoutMs, zt as resolveAutoAssetStorage } from "./deploy-engine-DjdMx6fC.js";
2
+ import { $ as requireConfigObject, $t as AssemblyReader, A as green, At as runDockerForeground, B as disableInstanceApiTermination, Bt as resolveCaptureObservedState, C as MULTI_REGION_RECREATE_BLOCKED_TYPES, Cn as StackTerminationProtectionError, Ct as getBootstrapMarkerKey, D as bold, Dt as buildDockerImage, E as formatResourceLine, Et as validateContainerRepoName, F as clearOnUpdateRemoval, Ft as synthesisStatusMessage, G as WAFv2WebACLProvider, Gt as stateBucketExistenceConfirmed, H as IntrinsicFunctionResolver, Ht as resolveStateBucketWithDefault, I as ProviderRegistry, It as getDefaultStateBucketName, J as assertRegionMatch, Jt as CFN_TEMPLATE_URL_LIMIT, K as normalizeAwsTagsToCfn, Kt as warnDeprecatedNoPrefixCliFlag, L as findActionableSilentDrops, Lt as getLegacyStateBucketName, M as yellow, Mt as AssetManifestLoader, N as IAMRoleProvider, Nt as getDockerImageBySourceHash, O as cyan, On as normalizeAwsError, Ot as formatDockerLoginError, P as collectInlinePolicyNamesManagedBySiblings, Pt as Synthesizer, Q as requireConfigArray, Qt as expectedOwnerParam, R as CloudControlProvider, Rt as resolveApp, S as extractDeploymentEventError, Sn as StackHasActiveImportsError, St as ensureAssetStorage, T as renderStatefulReason, Tt as validateAssetBucketName, U as cfnRefValueFromPhysicalId, Ut as resolveStateBucketWithDefaultAndSource, V as isTerminationProtectionPropagationError, Vt as resolveSkipPrefix, W as refStateLookupFromResource, Wt as resolveUseCdkBootstrapAssets, X as readConfigString, Xt as findLargeInlineResources, Y as configStringRefusal, Yt as MIGRATE_TMP_PREFIX, Z as replayWarn, Zt as uploadCfnTemplate, _ as createPreDeleteFinalSnapshot, _n as NestedStackChildDirectDestroyError, _t as createAssetRedirectResolver, a as DeploymentEventsStore, an as resetAwsClients, at as isRetryableTransientError, b as unsupportedFinalSnapshotError, bn as ResourceTimeoutError, bt as AssetModeResolver, c as replayFailedOperations, cn as CdkdError, ct as TemplateParser, d as IMPLICIT_DELETE_DEPENDENCIES, dn as DeployCancelledError, dt as rebuildClientForBucketRegion, en as processStackMessages, et as requireConfigString, f as computeImplicitDeleteEdges, fn as LocalInvokeBuildError$1, ft as shouldRetainResource, g as ccRoutedFinalSnapshotError, gn as MissingCdkCliError, gt as buildAssetRedirectMap, h as buildFinalSnapshotIdentifier, ht as WorkGraph, i as DeploymentEventsReader, in as getAwsClients, it as withRetry, j as red, jt as runDockerStreaming, k as gray, kn as withErrorHandling, kt as getDockerCmd, l as replayRollback, lt as LockManager, m as PRE_DELETE_SNAPSHOT_TYPES, mn as LocalStartServiceError, mt as stringifyValue, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as resolveBucketRegion, nt as DiffCalculator, o as planFailedOps, on as setAwsClients, ot as isThrottlingError, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalMigrateError, pt as AssetPublisher, q as resolveExplicitPhysicalId, qt as CFN_TEMPLATE_BODY_LIMIT, r as DeployEngine, rn as AwsClients, rt as describeTypeWithThrottleRetry, s as planRollback, st as DagBuilder, t as DEFAULT_RESOURCE_TIMEOUT_MS, tt as applyRoleArnIfSet, u as withResourceDeadline, ut as S3StateBackend, v as isFinalSnapshotError, vn as PartialFailureError, vt as loadPublishableAssetManifest, w as isStatefulRecreateTargetSync, wt as parseBootstrapMarker, x as makeCanonicalizePropertiesFn, xn as ResourceUpdateNotSupportedError, xt as BOOTSTRAP_MARKER_PREFIX, y as refusesFinalSnapshot, yn as ProvisioningError, yt as rewriteTemplateAssetReferences, z as slowCcOperationTimeoutMs, zt as resolveAutoAssetStorage } from "./deploy-engine-BcorBRVN.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, h as withStackName, i as runStackBuffered, m as withSkipPrefix, n as getLogger, o as PATTERN_B_NAME_OPTIONS, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-zRrlbaQt.js";
4
- import { t as ASGProvider } from "./asg-provider-CL2zlhEi.js";
4
+ import { t as ASGProvider } from "./asg-provider-DeR16ts4.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";
@@ -4820,6 +4820,23 @@ function isTruthyCfnBoolean(value) {
4820
4820
  * single transition entry makes S3 answer `MalformedXML` for the WHOLE
4821
4821
  * lifecycle configuration, so the legacy singular readers screen with this.
4822
4822
  */
4823
+ /**
4824
+ * The `Id` of one per-item configuration, normalized to a string.
4825
+ *
4826
+ * `diffArrayConfigById` accepts an `Id` under a TRUTHINESS test, so an
4827
+ * unquoted-YAML `Id: 1` is a NUMBER that reaches the Put and lands in the
4828
+ * skipped-id set as a number. A `typeof id === 'string'` predicate on the other
4829
+ * side then reads that item as NOT skipped and records the never-applied
4830
+ * desired value — reviving, for exactly that shape, the phantom drift issue
4831
+ * #1612 exists to remove. Both sides go through this helper so they cannot
4832
+ * disagree about what an `Id` is.
4833
+ */
4834
+ function configItemId(item) {
4835
+ if (!isPlainObject$1(item)) return void 0;
4836
+ const id = item["Id"];
4837
+ if (typeof id === "string") return id;
4838
+ if (typeof id === "number" && Number.isFinite(id)) return String(id);
4839
+ }
4823
4840
  function isPlainObject$1(value) {
4824
4841
  return typeof value === "object" && value !== null && !Array.isArray(value);
4825
4842
  }
@@ -4984,12 +5001,17 @@ var S3BucketProvider = class S3BucketProvider {
4984
5001
  /**
4985
5002
  * Apply versioning configuration if specified
4986
5003
  */
5004
+ /**
5005
+ * @returns `true` when `PutBucketVersioning` was issued, `false` when the
5006
+ * replay downgrade SKIPPED it. The caller records that skip in
5007
+ * `effectiveProperties` so state describes what AWS holds (issue #1612).
5008
+ */
4987
5009
  async applyVersioning(bucketName, versioningConfig, onUnusable) {
4988
5010
  if (onUnusable) {
4989
5011
  const refusal = configStringRefusal(versioningConfig, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
4990
5012
  if (refusal !== void 0) {
4991
5013
  onUnusable(`${refusal}. No PutBucketVersioning is issued rather than applying the Suspended default, which would turn object versioning OFF on an existing bucket (on a replayed create it means the bucket is left unversioned); the same value is REFUSED on a template-path create`);
4992
- return;
5014
+ return false;
4993
5015
  }
4994
5016
  }
4995
5017
  const status = readConfigString(versioningConfig, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
@@ -4998,6 +5020,7 @@ var S3BucketProvider = class S3BucketProvider {
4998
5020
  VersioningConfiguration: { Status: status }
4999
5021
  }));
5000
5022
  this.logger.debug(`Applied versioning (${status}) to bucket ${bucketName}`);
5023
+ return true;
5001
5024
  }
5002
5025
  /**
5003
5026
  * Apply tags if specified
@@ -5115,16 +5138,20 @@ var S3BucketProvider = class S3BucketProvider {
5115
5138
  * - CFn uses TagFilters, SDK uses Tag/Tags in Filter
5116
5139
  * - CFn Transition.TransitionInDays -> SDK Transition.Days
5117
5140
  * - CFn Transition.TransitionDate -> SDK Transition.Date
5141
+ *
5142
+ * @returns `true` when the Put was issued, `false` when a replay downgrade
5143
+ * SKIPPED the whole configuration (issue #1612 — the caller records that in
5144
+ * `effectiveProperties`).
5118
5145
  */
5119
5146
  async applyLifecycleConfiguration(bucketName, lifecycleConfig, onUnusable) {
5120
5147
  for (const rule of lifecycleConfig.Rules) {
5121
5148
  const rawFilter = rule["Filter"];
5122
- if (rawFilter != null && requireConfigObject(rawFilter, "AWS::S3::Bucket LifecycleConfiguration.Rules[].Filter", { ...onUnusable ? { onUnusable } : {} }) === void 0) return;
5149
+ if (rawFilter != null && requireConfigObject(rawFilter, "AWS::S3::Bucket LifecycleConfiguration.Rules[].Filter", { ...onUnusable ? { onUnusable } : {} }) === void 0) return false;
5123
5150
  const rawTagFilters = rawFilter?.["TagFilters"] ?? rule["TagFilters"];
5124
5151
  if (rawTagFilters != null) {
5125
- if (requireConfigArray(rawTagFilters, "AWS::S3::Bucket LifecycleConfiguration.Rules[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) return;
5152
+ if (requireConfigArray(rawTagFilters, "AWS::S3::Bucket LifecycleConfiguration.Rules[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) return false;
5126
5153
  }
5127
- if (this.skipOnUnusableConfigString(rule, "Status", "Enabled", "AWS::S3::Bucket LifecycleConfiguration.Rules[]", "Leaving the whole lifecycle configuration unapplied here", onUnusable)) return;
5154
+ if (this.skipOnUnusableConfigString(rule, "Status", "Enabled", "AWS::S3::Bucket LifecycleConfiguration.Rules[]", "Leaving the whole lifecycle configuration unapplied here", onUnusable)) return false;
5128
5155
  }
5129
5156
  const gatherScope = (rule) => {
5130
5157
  const filter = rule["Filter"];
@@ -5211,6 +5238,7 @@ var S3BucketProvider = class S3BucketProvider {
5211
5238
  TransitionDefaultMinimumObjectSize: lifecycleConfig["TransitionDefaultMinimumObjectSize"]
5212
5239
  }));
5213
5240
  this.logger.debug(`Applied lifecycle configuration to bucket ${bucketName}`);
5241
+ return true;
5214
5242
  }
5215
5243
  /**
5216
5244
  * Apply public access block configuration
@@ -5269,12 +5297,16 @@ var S3BucketProvider = class S3BucketProvider {
5269
5297
  * - LogFilePrefix -> SDK TargetPrefix
5270
5298
  * SDK: PutBucketLogging with BucketLoggingStatus.LoggingEnabled
5271
5299
  */
5300
+ /**
5301
+ * @returns `true` when a `PutBucketLogging` (set or clear) was issued,
5302
+ * `false` when the replay downgrade SKIPPED it (issue #1612).
5303
+ */
5272
5304
  async applyLoggingConfiguration(bucketName, loggingConfig, onUnusable) {
5273
5305
  if (onUnusable) {
5274
5306
  const refusal = configStringRefusal(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") ?? configStringRefusal(loggingConfig, "LogFilePrefix", "", "AWS::S3::Bucket LoggingConfiguration");
5275
5307
  if (refusal !== void 0) {
5276
5308
  onUnusable(`${refusal}. No PutBucketLogging is issued rather than clearing access logging, which would turn it OFF on an existing bucket (on a replayed create it means the bucket comes up with no access logging); the same value is REFUSED on a template-path create`);
5277
- return;
5309
+ return false;
5278
5310
  }
5279
5311
  }
5280
5312
  const destinationBucket = readConfigString(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration");
@@ -5284,7 +5316,7 @@ var S3BucketProvider = class S3BucketProvider {
5284
5316
  BucketLoggingStatus: {}
5285
5317
  }));
5286
5318
  this.logger.debug(`Cleared logging configuration on bucket ${bucketName}`);
5287
- return;
5319
+ return true;
5288
5320
  }
5289
5321
  const loggingEnabled = {
5290
5322
  TargetBucket: destinationBucket,
@@ -5303,6 +5335,7 @@ var S3BucketProvider = class S3BucketProvider {
5303
5335
  BucketLoggingStatus: { LoggingEnabled: loggingEnabled }
5304
5336
  }));
5305
5337
  this.logger.debug(`Applied logging configuration to bucket ${bucketName}`);
5338
+ return true;
5306
5339
  }
5307
5340
  /**
5308
5341
  * Apply website configuration
@@ -5423,15 +5456,24 @@ var S3BucketProvider = class S3BucketProvider {
5423
5456
  *
5424
5457
  * CFn property: MetricsConfigurations[] (array of configurations)
5425
5458
  * SDK: PutBucketMetricsConfiguration (one per configuration, keyed by Id)
5459
+ *
5460
+ * @returns the indexes (into `configs`) of the items a replay downgrade
5461
+ * SKIPPED — the per-item unit of `effectiveProperties` (issue #1612).
5426
5462
  */
5427
5463
  async applyMetricsConfigurations(bucketName, configs, onUnusable) {
5464
+ const skipped = [];
5465
+ let index = -1;
5428
5466
  for (const config of configs) {
5467
+ index += 1;
5429
5468
  const id = config["Id"];
5430
5469
  const metricsConfig = { Id: id };
5431
5470
  const prefix = config["Prefix"];
5432
5471
  const accessPointArn = config["AccessPointArn"];
5433
5472
  const tagFilters = config["TagFilters"];
5434
- if (tagFilters != null && requireConfigArray(tagFilters, "AWS::S3::Bucket MetricsConfigurations[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) continue;
5473
+ if (tagFilters != null && requireConfigArray(tagFilters, "AWS::S3::Bucket MetricsConfigurations[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
5474
+ skipped.push(index);
5475
+ continue;
5476
+ }
5435
5477
  const tagCount = tagFilters?.length ?? 0;
5436
5478
  if ((prefix ? 1 : 0) + tagCount + (accessPointArn ? 1 : 0) > 1) metricsConfig.Filter = { And: {
5437
5479
  Prefix: prefix,
@@ -5447,7 +5489,8 @@ var S3BucketProvider = class S3BucketProvider {
5447
5489
  MetricsConfiguration: metricsConfig
5448
5490
  }));
5449
5491
  }
5450
- this.logger.debug(`Applied ${configs.length} metrics configuration(s) to bucket ${bucketName}`);
5492
+ this.logger.debug(`Applied ${configs.length - skipped.length} metrics configuration(s) to bucket ${bucketName}`);
5493
+ return skipped;
5451
5494
  }
5452
5495
  /**
5453
5496
  * Pick the `S3BucketDestination` bag out of an analytics / inventory
@@ -5503,12 +5546,21 @@ var S3BucketProvider = class S3BucketProvider {
5503
5546
  *
5504
5547
  * CFn property: AnalyticsConfigurations[] (array of configurations)
5505
5548
  * SDK: PutBucketAnalyticsConfiguration (one per configuration, keyed by Id)
5549
+ *
5550
+ * @returns the indexes (into `configs`) of the items a replay downgrade
5551
+ * SKIPPED — the per-item unit of `effectiveProperties` (issue #1612).
5506
5552
  */
5507
5553
  async applyAnalyticsConfigurations(bucketName, configs, onUnusable) {
5554
+ const skipped = [];
5555
+ let index = -1;
5508
5556
  for (const config of configs) {
5557
+ index += 1;
5509
5558
  const id = config["Id"];
5510
5559
  const rawStorageClassAnalysis = config["StorageClassAnalysis"];
5511
- if (rawStorageClassAnalysis != null && requireConfigObject(rawStorageClassAnalysis, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis", { ...onUnusable ? { onUnusable } : {} }) === void 0) continue;
5560
+ if (rawStorageClassAnalysis != null && requireConfigObject(rawStorageClassAnalysis, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
5561
+ skipped.push(index);
5562
+ continue;
5563
+ }
5512
5564
  const storageClassAnalysis = rawStorageClassAnalysis;
5513
5565
  const analyticsConfig = {
5514
5566
  Id: id,
@@ -5516,7 +5568,10 @@ var S3BucketProvider = class S3BucketProvider {
5516
5568
  };
5517
5569
  const prefix = config["Prefix"];
5518
5570
  const tagFilters = config["TagFilters"];
5519
- if (tagFilters != null && requireConfigArray(tagFilters, "AWS::S3::Bucket AnalyticsConfigurations[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) continue;
5571
+ if (tagFilters != null && requireConfigArray(tagFilters, "AWS::S3::Bucket AnalyticsConfigurations[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
5572
+ skipped.push(index);
5573
+ continue;
5574
+ }
5520
5575
  const tagCount = tagFilters?.length ?? 0;
5521
5576
  if ((prefix ? 1 : 0) + tagCount > 1) analyticsConfig.Filter = { And: {
5522
5577
  Prefix: prefix,
@@ -5526,12 +5581,18 @@ var S3BucketProvider = class S3BucketProvider {
5526
5581
  else if (tagFilters && tagFilters.length === 1) analyticsConfig.Filter = { Tag: tagFilters[0] };
5527
5582
  if (storageClassAnalysis?.["DataExport"] != null) {
5528
5583
  const rawDataExport = storageClassAnalysis["DataExport"];
5529
- if (requireConfigObject(rawDataExport, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis.DataExport", { ...onUnusable ? { onUnusable } : {} }) === void 0) continue;
5584
+ if (requireConfigObject(rawDataExport, "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis.DataExport", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
5585
+ skipped.push(index);
5586
+ continue;
5587
+ }
5530
5588
  const dataExport = rawDataExport;
5531
5589
  const analyticsDestPath = "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis.DataExport.Destination";
5532
5590
  const rawDest = dataExport["Destination"];
5533
5591
  const s3Dest = this.resolveS3BucketDestination(rawDest, analyticsDestPath, onUnusable);
5534
- if (s3Dest === void 0 && rawDest != null) continue;
5592
+ if (s3Dest === void 0 && rawDest != null) {
5593
+ skipped.push(index);
5594
+ continue;
5595
+ }
5535
5596
  const destPath = s3BucketDestinationPath(rawDest, analyticsDestPath);
5536
5597
  analyticsConfig.StorageClassAnalysis = { DataExport: {
5537
5598
  OutputSchemaVersion: readConfigString(dataExport, "OutputSchemaVersion", "V_1", "AWS::S3::Bucket AnalyticsConfigurations[].StorageClassAnalysis.DataExport", { ...onUnusable ? { onUnusable } : {} }),
@@ -5549,18 +5610,28 @@ var S3BucketProvider = class S3BucketProvider {
5549
5610
  AnalyticsConfiguration: analyticsConfig
5550
5611
  }));
5551
5612
  }
5552
- this.logger.debug(`Applied ${configs.length} analytics configuration(s) to bucket ${bucketName}`);
5613
+ this.logger.debug(`Applied ${configs.length - skipped.length} analytics configuration(s) to bucket ${bucketName}`);
5614
+ return skipped;
5553
5615
  }
5554
5616
  /**
5555
5617
  * Apply intelligent tiering configurations
5556
5618
  *
5557
5619
  * CFn property: IntelligentTieringConfigurations[]
5558
5620
  * SDK: PutBucketIntelligentTieringConfiguration (one per configuration, keyed by Id)
5621
+ *
5622
+ * @returns the indexes (into `configs`) of the items a replay downgrade
5623
+ * SKIPPED — the per-item unit of `effectiveProperties` (issue #1612).
5559
5624
  */
5560
5625
  async applyIntelligentTieringConfigurations(bucketName, configs, onUnusable) {
5626
+ const skipped = [];
5627
+ let index = -1;
5561
5628
  for (const config of configs) {
5629
+ index += 1;
5562
5630
  const id = config["Id"];
5563
- if (this.skipOnUnusableConfigString(config, "Status", "Enabled", "AWS::S3::Bucket IntelligentTieringConfigurations[]", "Skipping this intelligent tiering configuration", onUnusable)) continue;
5631
+ if (this.skipOnUnusableConfigString(config, "Status", "Enabled", "AWS::S3::Bucket IntelligentTieringConfigurations[]", "Skipping this intelligent tiering configuration", onUnusable)) {
5632
+ skipped.push(index);
5633
+ continue;
5634
+ }
5564
5635
  const tierings = config["Tierings"];
5565
5636
  const itConfig = {
5566
5637
  Id: id,
@@ -5572,7 +5643,10 @@ var S3BucketProvider = class S3BucketProvider {
5572
5643
  };
5573
5644
  const prefix = config["Prefix"];
5574
5645
  const tagFilters = config["TagFilters"];
5575
- if (tagFilters != null && requireConfigArray(tagFilters, "AWS::S3::Bucket IntelligentTieringConfigurations[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) continue;
5646
+ if (tagFilters != null && requireConfigArray(tagFilters, "AWS::S3::Bucket IntelligentTieringConfigurations[].TagFilters", { ...onUnusable ? { onUnusable } : {} }) === void 0) {
5647
+ skipped.push(index);
5648
+ continue;
5649
+ }
5576
5650
  const tagCount = tagFilters?.length ?? 0;
5577
5651
  if ((prefix ? 1 : 0) + tagCount > 1) itConfig.Filter = { And: {
5578
5652
  Prefix: prefix,
@@ -5586,31 +5660,48 @@ var S3BucketProvider = class S3BucketProvider {
5586
5660
  IntelligentTieringConfiguration: itConfig
5587
5661
  }));
5588
5662
  }
5589
- this.logger.debug(`Applied ${configs.length} intelligent tiering configuration(s) to bucket ${bucketName}`);
5663
+ this.logger.debug(`Applied ${configs.length - skipped.length} intelligent tiering configuration(s) to bucket ${bucketName}`);
5664
+ return skipped;
5590
5665
  }
5591
5666
  /**
5592
5667
  * Apply inventory configurations
5593
5668
  *
5594
5669
  * CFn property: InventoryConfigurations[]
5595
5670
  * SDK: PutBucketInventoryConfiguration (one per configuration, keyed by Id)
5671
+ *
5672
+ * @returns the indexes (into `configs`) of the items a replay downgrade
5673
+ * SKIPPED — the per-item unit of `effectiveProperties` (issue #1612).
5596
5674
  */
5597
5675
  async applyInventoryConfigurations(bucketName, configs, onUnusable) {
5676
+ const skipped = [];
5677
+ let index = -1;
5598
5678
  for (const config of configs) {
5679
+ index += 1;
5599
5680
  const id = config["Id"];
5600
- if (this.skipOnUnusableConfigString(config, "IncludedObjectVersions", "All", "AWS::S3::Bucket InventoryConfigurations[]", "Skipping this inventory configuration", onUnusable)) continue;
5681
+ if (this.skipOnUnusableConfigString(config, "IncludedObjectVersions", "All", "AWS::S3::Bucket InventoryConfigurations[]", "Skipping this inventory configuration", onUnusable)) {
5682
+ skipped.push(index);
5683
+ continue;
5684
+ }
5601
5685
  const inventoryDestPath = "AWS::S3::Bucket InventoryConfigurations[].Destination";
5602
5686
  const rawDest = config["Destination"];
5603
5687
  const s3Dest = this.resolveS3BucketDestination(rawDest, inventoryDestPath, onUnusable);
5604
- if (s3Dest === void 0 && rawDest != null) continue;
5688
+ if (s3Dest === void 0 && rawDest != null) {
5689
+ skipped.push(index);
5690
+ continue;
5691
+ }
5605
5692
  const destPath = s3BucketDestinationPath(rawDest, inventoryDestPath);
5606
5693
  const scheduleFrequencyRefusal = onUnusable ? configStringRefusal(config, "ScheduleFrequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[]") : void 0;
5607
5694
  if (scheduleFrequencyRefusal !== void 0 && config["Schedule"] == null) {
5608
5695
  onUnusable?.(`${scheduleFrequencyRefusal}. Skipping this inventory configuration: there is no Schedule.Frequency to fall back to, and defaulting to Weekly would silently re-cadence a live inventory report; the same value is REFUSED on a template-path create`);
5696
+ skipped.push(index);
5609
5697
  continue;
5610
5698
  }
5611
5699
  if (scheduleFrequencyRefusal !== void 0) onUnusable?.(`${scheduleFrequencyRefusal}. Falling back to Schedule.Frequency; the same value is REFUSED on a template-path create`);
5612
5700
  const useScheduleFrequency = config["ScheduleFrequency"] !== void 0 && scheduleFrequencyRefusal === void 0;
5613
- if (!useScheduleFrequency && this.skipOnUnusableConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule", "Skipping this inventory configuration", onUnusable)) continue;
5701
+ if (!useScheduleFrequency && this.skipOnUnusableConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule", "Skipping this inventory configuration", onUnusable)) {
5702
+ skipped.push(index);
5703
+ continue;
5704
+ }
5614
5705
  const frequency = useScheduleFrequency ? requireConfigString(config["ScheduleFrequency"], "Weekly", "AWS::S3::Bucket InventoryConfigurations[].ScheduleFrequency") : readConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule");
5615
5706
  const inventoryConfig = {
5616
5707
  Id: id,
@@ -5632,7 +5723,8 @@ var S3BucketProvider = class S3BucketProvider {
5632
5723
  InventoryConfiguration: inventoryConfig
5633
5724
  }));
5634
5725
  }
5635
- this.logger.debug(`Applied ${configs.length} inventory configuration(s) to bucket ${bucketName}`);
5726
+ this.logger.debug(`Applied ${configs.length - skipped.length} inventory configuration(s) to bucket ${bucketName}`);
5727
+ return skipped;
5636
5728
  }
5637
5729
  /**
5638
5730
  * Apply replication configuration
@@ -5690,12 +5782,16 @@ var S3BucketProvider = class S3BucketProvider {
5690
5782
  }
5691
5783
  return sdkDest;
5692
5784
  }
5785
+ /**
5786
+ * @returns `true` when the Put was issued, `false` when a replay downgrade
5787
+ * SKIPPED the whole configuration (issue #1612).
5788
+ */
5693
5789
  async applyReplicationConfiguration(bucketName, replConfig, onUnusable) {
5694
5790
  const rules = replConfig["Rules"];
5695
5791
  for (const rule of rules || []) {
5696
5792
  const rawFilter = rule["Filter"];
5697
- if (rawFilter != null && requireConfigObject(rawFilter, "AWS::S3::Bucket ReplicationConfiguration.Rules[].Filter", { ...onUnusable ? { onUnusable } : {} }) === void 0) return;
5698
- if (this.skipOnUnusableConfigString(rule, "Status", "Enabled", "AWS::S3::Bucket ReplicationConfiguration.Rules[]", "Leaving the whole replication configuration unapplied here", onUnusable)) return;
5793
+ if (rawFilter != null && requireConfigObject(rawFilter, "AWS::S3::Bucket ReplicationConfiguration.Rules[].Filter", { ...onUnusable ? { onUnusable } : {} }) === void 0) return false;
5794
+ if (this.skipOnUnusableConfigString(rule, "Status", "Enabled", "AWS::S3::Bucket ReplicationConfiguration.Rules[]", "Leaving the whole replication configuration unapplied here", onUnusable)) return false;
5699
5795
  }
5700
5796
  await this.s3Client.send(new PutBucketReplicationCommand({
5701
5797
  Bucket: bucketName,
@@ -5744,6 +5840,7 @@ var S3BucketProvider = class S3BucketProvider {
5744
5840
  }
5745
5841
  }));
5746
5842
  this.logger.debug(`Applied replication configuration to bucket ${bucketName}`);
5843
+ return true;
5747
5844
  }
5748
5845
  /**
5749
5846
  * Apply object lock configuration
@@ -5786,9 +5883,12 @@ var S3BucketProvider = class S3BucketProvider {
5786
5883
  */
5787
5884
  async applyConfiguration(bucketName, properties, options = {}) {
5788
5885
  const { skipTags = false, skipDiffManaged = false, context } = options;
5886
+ const overrides = /* @__PURE__ */ new Map();
5789
5887
  const replayOnUnusable = replayWarn(this.logger, context).onUnusable;
5790
5888
  const versioningConfig = properties["VersioningConfiguration"];
5791
- if (!skipDiffManaged && versioningConfig != null) await this.applyVersioning(bucketName, versioningConfig, replayOnUnusable);
5889
+ if (!skipDiffManaged && versioningConfig != null) {
5890
+ if (!await this.applyVersioning(bucketName, versioningConfig, replayOnUnusable)) overrides.set("VersioningConfiguration", void 0);
5891
+ }
5792
5892
  const tags = properties["Tags"];
5793
5893
  if (!skipTags && tags && Array.isArray(tags) && tags.length > 0) await this.applyTags(bucketName, tags);
5794
5894
  const ownershipControls = properties["OwnershipControls"];
@@ -5798,6 +5898,7 @@ var S3BucketProvider = class S3BucketProvider {
5798
5898
  if (publicAccessBlock) await this.applyPublicAccessBlockConfiguration(bucketName, publicAccessBlock);
5799
5899
  const normalizedEncryption = S3BucketProvider.emptyListConfigToUndefined(properties["BucketEncryption"], "ServerSideEncryptionConfiguration");
5800
5900
  if (!skipDiffManaged && normalizedEncryption !== void 0) await this.applyBucketEncryption(bucketName, normalizedEncryption);
5901
+ return overrides;
5801
5902
  }
5802
5903
  /**
5803
5904
  * CFn property: OwnershipControls
@@ -5857,6 +5958,74 @@ var S3BucketProvider = class S3BucketProvider {
5857
5958
  return config;
5858
5959
  }
5859
5960
  /**
5961
+ * Turn a set of skipped-property overrides into the `effectiveProperties`
5962
+ * bag the deploy engine records in place of the desired one (issue #1612).
5963
+ *
5964
+ * A warn-and-skip arm makes the deploy SUCCEED while a declared
5965
+ * configuration never reaches AWS, so recording the desired bag verbatim
5966
+ * writes a value AWS does not hold: `readCurrentState` can never match it,
5967
+ * every later `cdkd drift` re-reports the difference, and `drift --revert`
5968
+ * re-issues the same skipped call — the permanent phantom drift
5969
+ * `.claude/rules/providers.md` records for the EC2 Route warn arm (#1591),
5970
+ * reached here through a skip instead of a narrowing.
5971
+ *
5972
+ * A `undefined` override value means the key is ABSENT from the effective
5973
+ * bag, which is why this walks the map rather than spreading it: spreading
5974
+ * would leave an explicit `undefined` that `JSON.stringify` drops on one
5975
+ * side of the next diff but not the other.
5976
+ *
5977
+ * @returns the complete replacement bag, or `undefined` when nothing was
5978
+ * skipped — the engine then records the desired properties unchanged.
5979
+ */
5980
+ static applyEffectiveOverrides(properties, overrides) {
5981
+ if (overrides.size === 0) return void 0;
5982
+ const effective = { ...properties };
5983
+ for (const [key, value] of overrides) if (value === void 0) delete effective[key];
5984
+ else effective[key] = value;
5985
+ return effective;
5986
+ }
5987
+ /**
5988
+ * The effective value of a per-item array property some of whose items a
5989
+ * replay downgrade SKIPPED (issue #1612).
5990
+ *
5991
+ * The Put for these four properties is per-`Id`, so the skip unit is one
5992
+ * configuration ITEM and the array must be rebuilt member by member: a
5993
+ * skipped CHANGE keeps the previously-applied item (that IS what AWS still
5994
+ * holds), a skipped ADD is dropped (AWS holds nothing for that `Id`), and
5995
+ * every unskipped item stays as declared.
5996
+ *
5997
+ * The DESIRED order is preserved deliberately — `DiffCalculator` compares
5998
+ * arrays positionally, so an effective array that merely reorders the same
5999
+ * items would manufacture a fresh phantom drift while removing the one this
6000
+ * exists to fix.
6001
+ *
6002
+ * @param previous the previously-recorded array, or `undefined` on the
6003
+ * replay-CREATE path, where nothing was ever applied and there is no
6004
+ * previous item to substitute.
6005
+ * @returns the effective array, or `undefined` when the key should be ABSENT
6006
+ * from the effective bag (nothing survived AND the previous side declared
6007
+ * nothing, so the property was never applied at all).
6008
+ */
6009
+ static buildEffectiveItemArray(desired, previous, isSkipped) {
6010
+ const previousById = /* @__PURE__ */ new Map();
6011
+ for (const item of previous ?? []) {
6012
+ const id = configItemId(item);
6013
+ if (id !== void 0) previousById.set(id, item);
6014
+ }
6015
+ const effective = [];
6016
+ desired.forEach((item, index) => {
6017
+ if (!isSkipped(item, index)) {
6018
+ effective.push(item);
6019
+ return;
6020
+ }
6021
+ const id = configItemId(item);
6022
+ const retained = id !== void 0 ? previousById.get(id) : void 0;
6023
+ if (retained !== void 0) effective.push(retained);
6024
+ });
6025
+ if (effective.length === 0 && previous === void 0) return void 0;
6026
+ return effective;
6027
+ }
6028
+ /**
5860
6029
  * Diff CFn-shape sub-config values between previous and new state.
5861
6030
  *
5862
6031
  * Four transitions:
@@ -5916,15 +6085,47 @@ var S3BucketProvider = class S3BucketProvider {
5916
6085
  * silent no-op (issue #1466): the same A/B showed CFn suspends versioning,
5917
6086
  * deletes ownership controls, and resets encryption to the SSE-S3 default.
5918
6087
  * They are now diffed here like every other Put/Delete pair.
6088
+ *
6089
+ * @returns the per-property overrides for `effectiveProperties` (issue
6090
+ * #1612): on THIS path a skipped Put means AWS still holds the
6091
+ * previously-applied configuration, so the override is the PREVIOUS value —
6092
+ * `undefined` when the previous side declared none, which removes the key.
6093
+ * Dropping the key unconditionally instead would be wrong in the other
6094
+ * direction: a later template that REMOVES the block would then derive no
6095
+ * removal and the live configuration would survive forever.
5919
6096
  */
5920
6097
  async applySubConfigDiffs(bucketName, properties, previousProperties) {
6098
+ const overrides = /* @__PURE__ */ new Map();
6099
+ /** Record a WHOLE-BLOCK skip: the effective value is the previous one. */
6100
+ const retainPrevious = (key) => {
6101
+ overrides.set(key, previousProperties[key]);
6102
+ };
6103
+ /** Record a PER-ITEM skip set against the desired / previous arrays. */
6104
+ const retainPreviousItems = (key, skippedIds) => {
6105
+ if (skippedIds.size === 0) return;
6106
+ const desired = properties[key];
6107
+ if (!Array.isArray(desired)) {
6108
+ this.logger.debug(`Bucket ${bucketName}: ${skippedIds.size} skipped ${key} item(s) could not be recorded — the desired value is not an array`);
6109
+ return;
6110
+ }
6111
+ const previous = previousProperties[key];
6112
+ overrides.set(key, S3BucketProvider.buildEffectiveItemArray(desired, Array.isArray(previous) ? previous : void 0, (item) => {
6113
+ const id = configItemId(item);
6114
+ return id !== void 0 && skippedIds.has(id);
6115
+ }));
6116
+ };
5921
6117
  const previousVersioning = previousProperties["VersioningConfiguration"];
5922
6118
  const nextVersioning = properties["VersioningConfiguration"];
5923
6119
  const versioningChanged = JSON.stringify(previousVersioning ?? null) !== JSON.stringify(nextVersioning ?? null);
5924
6120
  const versioningRefusal = configStringRefusal(nextVersioning, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
5925
- if (versioningRefusal !== void 0 && versioningChanged) this.logger.warn(`Bucket ${bucketName}: ${versioningRefusal}. Leaving the bucket's LIVE versioning state unchanged — neither enabling nor suspending it; the same value is REFUSED on a template-path create`);
6121
+ if (versioningRefusal !== void 0 && versioningChanged) {
6122
+ this.logger.warn(`Bucket ${bucketName}: ${versioningRefusal}. Leaving the bucket's LIVE versioning state unchanged — neither enabling nor suspending it; the same value is REFUSED on a template-path create`);
6123
+ retainPrevious("VersioningConfiguration");
6124
+ }
5926
6125
  const desiredVersioningStatus = versioningRefusal === void 0 ? readConfigString(nextVersioning, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration") : void 0;
5927
- if (versioningRefusal === void 0 && versioningChanged && nextVersioning != null && desiredVersioningStatus !== "Suspended") await this.applyVersioning(bucketName, nextVersioning, (m) => this.logger.warn(m));
6126
+ if (versioningRefusal === void 0 && versioningChanged && nextVersioning != null && desiredVersioningStatus !== "Suspended") {
6127
+ if (!await this.applyVersioning(bucketName, nextVersioning, (m) => this.logger.warn(m))) retainPrevious("VersioningConfiguration");
6128
+ }
5928
6129
  await this.diffSubConfig(bucketName, S3BucketProvider.emptyListConfigToUndefined(previousProperties["OwnershipControls"], "Rules"), S3BucketProvider.emptyListConfigToUndefined(properties["OwnershipControls"], "Rules"), async (cfg) => this.applyOwnershipControls(bucketName, cfg), async () => {
5929
6130
  await this.s3Client.send(new DeleteBucketOwnershipControlsCommand({ Bucket: bucketName }));
5930
6131
  this.logger.debug(`Deleted ownership controls on bucket ${bucketName}`);
@@ -5935,7 +6136,7 @@ var S3BucketProvider = class S3BucketProvider {
5935
6136
  });
5936
6137
  await this.diffSubConfig(bucketName, previousProperties["LifecycleConfiguration"], properties["LifecycleConfiguration"], async (cfg) => {
5937
6138
  if (!cfg.Rules || !Array.isArray(cfg.Rules) || cfg.Rules.length === 0) return;
5938
- await this.applyLifecycleConfiguration(bucketName, cfg, (m) => this.logger.warn(m));
6139
+ if (!await this.applyLifecycleConfiguration(bucketName, cfg, (m) => this.logger.warn(m))) retainPrevious("LifecycleConfiguration");
5939
6140
  }, async () => {
5940
6141
  await this.s3Client.send(new DeleteBucketLifecycleCommand({ Bucket: bucketName }));
5941
6142
  this.logger.debug(`Deleted lifecycle configuration on bucket ${bucketName}`);
@@ -5951,9 +6152,15 @@ var S3BucketProvider = class S3BucketProvider {
5951
6152
  await this.s3Client.send(new DeleteBucketWebsiteCommand({ Bucket: bucketName }));
5952
6153
  this.logger.debug(`Deleted website configuration on bucket ${bucketName}`);
5953
6154
  });
5954
- await this.diffSubConfig(bucketName, previousProperties["LoggingConfiguration"], properties["LoggingConfiguration"], async (cfg) => this.applyLoggingConfiguration(bucketName, cfg, (m) => this.logger.warn(m)), async () => this.applyLoggingConfiguration(bucketName, void 0));
6155
+ await this.diffSubConfig(bucketName, previousProperties["LoggingConfiguration"], properties["LoggingConfiguration"], async (cfg) => {
6156
+ if (!await this.applyLoggingConfiguration(bucketName, cfg, (m) => this.logger.warn(m))) retainPrevious("LoggingConfiguration");
6157
+ }, async () => {
6158
+ await this.applyLoggingConfiguration(bucketName, void 0);
6159
+ });
5955
6160
  await this.diffSubConfig(bucketName, previousProperties["NotificationConfiguration"], properties["NotificationConfiguration"], async (cfg) => this.applyNotificationConfiguration(bucketName, cfg), async () => this.applyNotificationConfiguration(bucketName, void 0));
5956
- await this.diffSubConfig(bucketName, previousProperties["ReplicationConfiguration"], properties["ReplicationConfiguration"], async (cfg) => this.applyReplicationConfiguration(bucketName, cfg, (m) => this.logger.warn(m)), async () => {
6161
+ await this.diffSubConfig(bucketName, previousProperties["ReplicationConfiguration"], properties["ReplicationConfiguration"], async (cfg) => {
6162
+ if (!await this.applyReplicationConfiguration(bucketName, cfg, (m) => this.logger.warn(m))) retainPrevious("ReplicationConfiguration");
6163
+ }, async () => {
5957
6164
  await this.s3Client.send(new DeleteBucketReplicationCommand({ Bucket: bucketName }));
5958
6165
  this.logger.debug(`Deleted replication configuration on bucket ${bucketName}`);
5959
6166
  });
@@ -5965,75 +6172,117 @@ var S3BucketProvider = class S3BucketProvider {
5965
6172
  this.logger.debug(`Cleared object lock rule on bucket ${bucketName}`);
5966
6173
  });
5967
6174
  await this.diffSubConfig(bucketName, previousProperties["AccelerateConfiguration"], properties["AccelerateConfiguration"], async (cfg) => this.applyAccelerateConfiguration(bucketName, cfg), async () => this.applyAccelerateConfiguration(bucketName, { AccelerationStatus: "Suspended" }));
5968
- await this.diffArrayConfigById(bucketName, previousProperties["MetricsConfigurations"], properties["MetricsConfigurations"], async (_id, cfg) => this.applyMetricsConfigurations(bucketName, [cfg], (m) => this.logger.warn(m)), async (id) => {
6175
+ const skippedMetricsIds = /* @__PURE__ */ new Set();
6176
+ await this.diffArrayConfigById(bucketName, previousProperties["MetricsConfigurations"], properties["MetricsConfigurations"], async (id, cfg) => {
6177
+ if ((await this.applyMetricsConfigurations(bucketName, [cfg], (m) => this.logger.warn(m))).length > 0) skippedMetricsIds.add(String(id));
6178
+ }, async (id) => {
5969
6179
  await this.s3Client.send(new DeleteBucketMetricsConfigurationCommand({
5970
6180
  Bucket: bucketName,
5971
6181
  Id: id
5972
6182
  }));
5973
6183
  this.logger.debug(`Deleted metrics configuration ${id} on bucket ${bucketName}`);
5974
6184
  });
5975
- await this.diffArrayConfigById(bucketName, previousProperties["AnalyticsConfigurations"], properties["AnalyticsConfigurations"], async (_id, cfg) => this.applyAnalyticsConfigurations(bucketName, [cfg], (m) => this.logger.warn(m)), async (id) => {
6185
+ retainPreviousItems("MetricsConfigurations", skippedMetricsIds);
6186
+ const skippedAnalyticsIds = /* @__PURE__ */ new Set();
6187
+ await this.diffArrayConfigById(bucketName, previousProperties["AnalyticsConfigurations"], properties["AnalyticsConfigurations"], async (id, cfg) => {
6188
+ if ((await this.applyAnalyticsConfigurations(bucketName, [cfg], (m) => this.logger.warn(m))).length > 0) skippedAnalyticsIds.add(String(id));
6189
+ }, async (id) => {
5976
6190
  await this.s3Client.send(new DeleteBucketAnalyticsConfigurationCommand({
5977
6191
  Bucket: bucketName,
5978
6192
  Id: id
5979
6193
  }));
5980
6194
  this.logger.debug(`Deleted analytics configuration ${id} on bucket ${bucketName}`);
5981
6195
  });
5982
- await this.diffArrayConfigById(bucketName, previousProperties["IntelligentTieringConfigurations"], properties["IntelligentTieringConfigurations"], async (_id, cfg) => this.applyIntelligentTieringConfigurations(bucketName, [cfg], (m) => this.logger.warn(m)), async (id) => {
6196
+ retainPreviousItems("AnalyticsConfigurations", skippedAnalyticsIds);
6197
+ const skippedIntelligentTieringIds = /* @__PURE__ */ new Set();
6198
+ await this.diffArrayConfigById(bucketName, previousProperties["IntelligentTieringConfigurations"], properties["IntelligentTieringConfigurations"], async (id, cfg) => {
6199
+ if ((await this.applyIntelligentTieringConfigurations(bucketName, [cfg], (m) => this.logger.warn(m))).length > 0) skippedIntelligentTieringIds.add(String(id));
6200
+ }, async (id) => {
5983
6201
  await this.s3Client.send(new DeleteBucketIntelligentTieringConfigurationCommand({
5984
6202
  Bucket: bucketName,
5985
6203
  Id: id
5986
6204
  }));
5987
6205
  this.logger.debug(`Deleted intelligent tiering configuration ${id} on bucket ${bucketName}`);
5988
6206
  });
5989
- await this.diffArrayConfigById(bucketName, previousProperties["InventoryConfigurations"], properties["InventoryConfigurations"], async (_id, cfg) => this.applyInventoryConfigurations(bucketName, [cfg], (m) => this.logger.warn(m)), async (id) => {
6207
+ retainPreviousItems("IntelligentTieringConfigurations", skippedIntelligentTieringIds);
6208
+ const skippedInventoryIds = /* @__PURE__ */ new Set();
6209
+ await this.diffArrayConfigById(bucketName, previousProperties["InventoryConfigurations"], properties["InventoryConfigurations"], async (id, cfg) => {
6210
+ if ((await this.applyInventoryConfigurations(bucketName, [cfg], (m) => this.logger.warn(m))).length > 0) skippedInventoryIds.add(String(id));
6211
+ }, async (id) => {
5990
6212
  await this.s3Client.send(new DeleteBucketInventoryConfigurationCommand({
5991
6213
  Bucket: bucketName,
5992
6214
  Id: id
5993
6215
  }));
5994
6216
  this.logger.debug(`Deleted inventory configuration ${id} on bucket ${bucketName}`);
5995
6217
  });
6218
+ retainPreviousItems("InventoryConfigurations", skippedInventoryIds);
5996
6219
  if (versioningChanged && desiredVersioningStatus === "Suspended") {
5997
6220
  const hasObjectLock = (p) => {
5998
6221
  if (p["ObjectLockEnabled"] === true || p["ObjectLockEnabled"] === "true") return true;
5999
6222
  return p["ObjectLockConfiguration"]?.["ObjectLockEnabled"] === "Enabled";
6000
6223
  };
6001
- 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.`);
6002
- else await this.applyVersioning(bucketName, { Status: "Suspended" });
6224
+ if (hasObjectLock(properties) || hasObjectLock(previousProperties)) {
6225
+ 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.`);
6226
+ retainPrevious("VersioningConfiguration");
6227
+ } else await this.applyVersioning(bucketName, { Status: "Suspended" });
6003
6228
  }
6229
+ return overrides;
6004
6230
  }
6005
6231
  /**
6006
6232
  * Apply ALL sub-configs unconditionally on initial create. Used by
6007
6233
  * `create()` so the bucket starts out matching the template.
6234
+ *
6235
+ * @returns the per-property overrides for `effectiveProperties` (issue
6236
+ * #1612). Every skip reachable here is on the REPLAY-create path (the
6237
+ * rollback executor's reverse-replacement arm), where the bucket is brand
6238
+ * new and nothing was applied — so the override DROPS the key rather than
6239
+ * retaining a previous value, because there is no previous value to keep.
6240
+ * The per-item arrays keep the items that DID apply.
6008
6241
  */
6009
6242
  async applyAllSubConfigsForCreate(bucketName, properties, context) {
6243
+ const overrides = /* @__PURE__ */ new Map();
6244
+ /** Record a per-ITEM skip set: nothing to substitute on a fresh bucket. */
6245
+ const dropSkippedItems = (key, configs, skippedIndexes) => {
6246
+ if (skippedIndexes.length === 0) return;
6247
+ const skipped = new Set(skippedIndexes);
6248
+ overrides.set(key, S3BucketProvider.buildEffectiveItemArray(configs, void 0, (_item, index) => skipped.has(index)));
6249
+ };
6010
6250
  const replayOnUnusable = replayWarn(this.logger, context).onUnusable;
6011
6251
  const notifConfig = properties["NotificationConfiguration"];
6012
6252
  if (notifConfig) await this.applyNotificationConfiguration(bucketName, notifConfig);
6013
6253
  const corsConfig = properties["CorsConfiguration"];
6014
6254
  if (corsConfig?.CorsRules && Array.isArray(corsConfig.CorsRules) && corsConfig.CorsRules.length > 0) await this.applyCorsConfiguration(bucketName, corsConfig);
6015
6255
  const lifecycleConfig = properties["LifecycleConfiguration"];
6016
- if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable);
6256
+ if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) {
6257
+ if (!await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable)) overrides.set("LifecycleConfiguration", void 0);
6258
+ }
6017
6259
  const loggingConfig = properties["LoggingConfiguration"];
6018
6260
  const loggingRefusal = replayOnUnusable ? configStringRefusal(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") : void 0;
6019
- if (loggingRefusal !== void 0) replayOnUnusable?.(`${loggingRefusal}. Skipping the logging configuration; the same value is REFUSED on a template-path create`);
6020
- else if (readConfigString(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") !== "") await this.applyLoggingConfiguration(bucketName, loggingConfig, replayOnUnusable);
6261
+ if (loggingRefusal !== void 0) {
6262
+ replayOnUnusable?.(`${loggingRefusal}. Skipping the logging configuration; the same value is REFUSED on a template-path create`);
6263
+ overrides.set("LoggingConfiguration", void 0);
6264
+ } else if (readConfigString(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") !== "") {
6265
+ if (!await this.applyLoggingConfiguration(bucketName, loggingConfig, replayOnUnusable)) overrides.set("LoggingConfiguration", void 0);
6266
+ }
6021
6267
  const websiteConfig = properties["WebsiteConfiguration"];
6022
6268
  if (websiteConfig) await this.applyWebsiteConfiguration(bucketName, websiteConfig);
6023
6269
  const accelerateConfig = properties["AccelerateConfiguration"];
6024
6270
  if (accelerateConfig) await this.applyAccelerateConfiguration(bucketName, accelerateConfig);
6025
6271
  const metricsConfigs = properties["MetricsConfigurations"];
6026
- if (metricsConfigs && Array.isArray(metricsConfigs) && metricsConfigs.length > 0) await this.applyMetricsConfigurations(bucketName, metricsConfigs, replayOnUnusable);
6272
+ if (metricsConfigs && Array.isArray(metricsConfigs) && metricsConfigs.length > 0) dropSkippedItems("MetricsConfigurations", metricsConfigs, await this.applyMetricsConfigurations(bucketName, metricsConfigs, replayOnUnusable));
6027
6273
  const analyticsConfigs = properties["AnalyticsConfigurations"];
6028
- if (analyticsConfigs && Array.isArray(analyticsConfigs) && analyticsConfigs.length > 0) await this.applyAnalyticsConfigurations(bucketName, analyticsConfigs, replayOnUnusable);
6274
+ if (analyticsConfigs && Array.isArray(analyticsConfigs) && analyticsConfigs.length > 0) dropSkippedItems("AnalyticsConfigurations", analyticsConfigs, await this.applyAnalyticsConfigurations(bucketName, analyticsConfigs, replayOnUnusable));
6029
6275
  const itConfigs = properties["IntelligentTieringConfigurations"];
6030
- if (itConfigs && Array.isArray(itConfigs) && itConfigs.length > 0) await this.applyIntelligentTieringConfigurations(bucketName, itConfigs, replayOnUnusable);
6276
+ if (itConfigs && Array.isArray(itConfigs) && itConfigs.length > 0) dropSkippedItems("IntelligentTieringConfigurations", itConfigs, await this.applyIntelligentTieringConfigurations(bucketName, itConfigs, replayOnUnusable));
6031
6277
  const inventoryConfigs = properties["InventoryConfigurations"];
6032
- if (inventoryConfigs && Array.isArray(inventoryConfigs) && inventoryConfigs.length > 0) await this.applyInventoryConfigurations(bucketName, inventoryConfigs, replayOnUnusable);
6278
+ if (inventoryConfigs && Array.isArray(inventoryConfigs) && inventoryConfigs.length > 0) dropSkippedItems("InventoryConfigurations", inventoryConfigs, await this.applyInventoryConfigurations(bucketName, inventoryConfigs, replayOnUnusable));
6033
6279
  const replConfig = properties["ReplicationConfiguration"];
6034
- if (replConfig) await this.applyReplicationConfiguration(bucketName, replConfig, replayOnUnusable);
6280
+ if (replConfig) {
6281
+ if (!await this.applyReplicationConfiguration(bucketName, replConfig, replayOnUnusable)) overrides.set("ReplicationConfiguration", void 0);
6282
+ }
6035
6283
  const objectLockConfig = properties["ObjectLockConfiguration"];
6036
6284
  if (objectLockConfig) await this.applyObjectLockConfiguration(bucketName, objectLockConfig);
6285
+ return overrides;
6037
6286
  }
6038
6287
  /**
6039
6288
  * Create an S3 bucket
@@ -6059,9 +6308,10 @@ var S3BucketProvider = class S3BucketProvider {
6059
6308
  if (createError instanceof Error && (createError.name === "BucketAlreadyOwnedByYou" || createError.message.includes("you already own it"))) this.logger.debug(`S3 bucket ${bucketName} already exists and is owned by you`);
6060
6309
  else throw createError;
6061
6310
  }
6311
+ let effectiveOverrides;
6062
6312
  try {
6063
- await this.applyConfiguration(bucketName, properties, { ...context ? { context } : {} });
6064
- await this.applyAllSubConfigsForCreate(bucketName, properties, context);
6313
+ effectiveOverrides = await this.applyConfiguration(bucketName, properties, { ...context ? { context } : {} });
6314
+ for (const [key, value] of await this.applyAllSubConfigsForCreate(bucketName, properties, context)) effectiveOverrides.set(key, value);
6065
6315
  } catch (innerError) {
6066
6316
  if (createdNewBucket) try {
6067
6317
  await this.s3Client.send(new DeleteBucketCommand({ Bucket: bucketName }));
@@ -6073,9 +6323,11 @@ var S3BucketProvider = class S3BucketProvider {
6073
6323
  }
6074
6324
  const attributes = await this.buildAttributes(bucketName);
6075
6325
  this.logger.debug(`Successfully created S3 bucket ${logicalId}: ${bucketName}`);
6326
+ const effectiveProperties = S3BucketProvider.applyEffectiveOverrides(properties, effectiveOverrides);
6076
6327
  return {
6077
6328
  physicalId: bucketName,
6078
- attributes
6329
+ attributes,
6330
+ ...effectiveProperties ? { effectiveProperties } : {}
6079
6331
  };
6080
6332
  } catch (error) {
6081
6333
  const cause = error instanceof Error ? error : void 0;
@@ -6096,18 +6348,20 @@ var S3BucketProvider = class S3BucketProvider {
6096
6348
  };
6097
6349
  }
6098
6350
  try {
6099
- await this.applyConfiguration(physicalId, properties, {
6351
+ const effectiveOverrides = await this.applyConfiguration(physicalId, properties, {
6100
6352
  skipTags: true,
6101
6353
  skipDiffManaged: true
6102
6354
  });
6103
- await this.applySubConfigDiffs(physicalId, properties, previousProperties);
6355
+ for (const [key, value] of await this.applySubConfigDiffs(physicalId, properties, previousProperties)) effectiveOverrides.set(key, value);
6104
6356
  await this.applyTagDiff(physicalId, previousProperties["Tags"], properties["Tags"]);
6105
6357
  const attributes = await this.buildAttributes(physicalId);
6106
6358
  this.logger.debug(`Successfully updated S3 bucket ${logicalId}`);
6359
+ const effectiveProperties = S3BucketProvider.applyEffectiveOverrides(properties, effectiveOverrides);
6107
6360
  return {
6108
6361
  physicalId,
6109
6362
  wasReplaced: false,
6110
- attributes
6363
+ attributes,
6364
+ ...effectiveProperties ? { effectiveProperties } : {}
6111
6365
  };
6112
6366
  } catch (error) {
6113
6367
  const cause = error instanceof Error ? error : void 0;
@@ -71263,7 +71517,7 @@ function createMigrateCommand() {
71263
71517
  */
71264
71518
  function buildProgram() {
71265
71519
  const program = new Command();
71266
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.35");
71520
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.36");
71267
71521
  program.hook("preAction", (_thisCommand, actionCommand) => {
71268
71522
  const { profile } = actionCommand.optsWithGlobals();
71269
71523
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;