@go-to-k/cdkd 0.280.22 → 0.280.24

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 requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-DObU-2va.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-60FIug-P.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-DVoWhe5E.js";
4
+ import { t as ASGProvider } from "./asg-provider-Dpx4B6UA.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";
@@ -4984,7 +4984,14 @@ var S3BucketProvider = class S3BucketProvider {
4984
4984
  /**
4985
4985
  * Apply versioning configuration if specified
4986
4986
  */
4987
- async applyVersioning(bucketName, versioningConfig) {
4987
+ async applyVersioning(bucketName, versioningConfig, onUnusable) {
4988
+ if (onUnusable) {
4989
+ const refusal = configStringRefusal(versioningConfig, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
4990
+ if (refusal !== void 0) {
4991
+ 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;
4993
+ }
4994
+ }
4988
4995
  const status = readConfigString(versioningConfig, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
4989
4996
  await this.s3Client.send(new PutBucketVersioningCommand({
4990
4997
  Bucket: bucketName,
@@ -5262,7 +5269,14 @@ var S3BucketProvider = class S3BucketProvider {
5262
5269
  * - LogFilePrefix -> SDK TargetPrefix
5263
5270
  * SDK: PutBucketLogging with BucketLoggingStatus.LoggingEnabled
5264
5271
  */
5265
- async applyLoggingConfiguration(bucketName, loggingConfig) {
5272
+ async applyLoggingConfiguration(bucketName, loggingConfig, onUnusable) {
5273
+ if (onUnusable) {
5274
+ const refusal = configStringRefusal(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") ?? configStringRefusal(loggingConfig, "LogFilePrefix", "", "AWS::S3::Bucket LoggingConfiguration");
5275
+ if (refusal !== void 0) {
5276
+ 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;
5278
+ }
5279
+ }
5266
5280
  const destinationBucket = readConfigString(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration");
5267
5281
  if (!loggingConfig || destinationBucket === "") {
5268
5282
  await this.s3Client.send(new PutBucketLoggingCommand({
@@ -5589,11 +5603,20 @@ var S3BucketProvider = class S3BucketProvider {
5589
5603
  const s3Dest = this.resolveS3BucketDestination(rawDest, inventoryDestPath, onUnusable);
5590
5604
  if (s3Dest === void 0 && rawDest != null) continue;
5591
5605
  const destPath = s3BucketDestinationPath(rawDest, inventoryDestPath);
5606
+ const scheduleFrequencyRefusal = onUnusable ? configStringRefusal(config, "ScheduleFrequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[]") : void 0;
5607
+ if (scheduleFrequencyRefusal !== void 0 && config["Schedule"] == null) {
5608
+ 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`);
5609
+ continue;
5610
+ }
5611
+ if (scheduleFrequencyRefusal !== void 0) onUnusable?.(`${scheduleFrequencyRefusal}. Falling back to Schedule.Frequency; the same value is REFUSED on a template-path create`);
5612
+ 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;
5614
+ const frequency = useScheduleFrequency ? requireConfigString(config["ScheduleFrequency"], "Weekly", "AWS::S3::Bucket InventoryConfigurations[].ScheduleFrequency") : readConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule");
5592
5615
  const inventoryConfig = {
5593
5616
  Id: id,
5594
5617
  IsEnabled: config["Enabled"] ?? true,
5595
5618
  IncludedObjectVersions: readConfigString(config, "IncludedObjectVersions", "All", "AWS::S3::Bucket InventoryConfigurations[]"),
5596
- Schedule: { Frequency: config["ScheduleFrequency"] !== void 0 ? requireConfigString(config["ScheduleFrequency"], "Weekly", "AWS::S3::Bucket InventoryConfigurations[].ScheduleFrequency") : readConfigString(config["Schedule"], "Frequency", "Weekly", "AWS::S3::Bucket InventoryConfigurations[].Schedule") },
5619
+ Schedule: { Frequency: frequency },
5597
5620
  Destination: { S3BucketDestination: s3Dest ? {
5598
5621
  Bucket: s3Dest["BucketArn"] ?? s3Dest["Bucket"],
5599
5622
  AccountId: s3Dest["BucketAccountId"],
@@ -5762,9 +5785,10 @@ var S3BucketProvider = class S3BucketProvider {
5762
5785
  * in both places would double-PUT.
5763
5786
  */
5764
5787
  async applyConfiguration(bucketName, properties, options = {}) {
5765
- const { skipTags = false, skipDiffManaged = false } = options;
5788
+ const { skipTags = false, skipDiffManaged = false, context } = options;
5789
+ const replayOnUnusable = replayWarn(this.logger, context).onUnusable;
5766
5790
  const versioningConfig = properties["VersioningConfiguration"];
5767
- if (!skipDiffManaged && versioningConfig != null) await this.applyVersioning(bucketName, versioningConfig);
5791
+ if (!skipDiffManaged && versioningConfig != null) await this.applyVersioning(bucketName, versioningConfig, replayOnUnusable);
5768
5792
  const tags = properties["Tags"];
5769
5793
  if (!skipTags && tags && Array.isArray(tags) && tags.length > 0) await this.applyTags(bucketName, tags);
5770
5794
  const ownershipControls = properties["OwnershipControls"];
@@ -5897,8 +5921,10 @@ var S3BucketProvider = class S3BucketProvider {
5897
5921
  const previousVersioning = previousProperties["VersioningConfiguration"];
5898
5922
  const nextVersioning = properties["VersioningConfiguration"];
5899
5923
  const versioningChanged = JSON.stringify(previousVersioning ?? null) !== JSON.stringify(nextVersioning ?? null);
5900
- const desiredVersioningStatus = readConfigString(nextVersioning, "Status", "Suspended", "AWS::S3::Bucket VersioningConfiguration");
5901
- if (versioningChanged && nextVersioning != null && desiredVersioningStatus !== "Suspended") await this.applyVersioning(bucketName, nextVersioning);
5924
+ 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`);
5926
+ 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));
5902
5928
  await this.diffSubConfig(bucketName, S3BucketProvider.emptyListConfigToUndefined(previousProperties["OwnershipControls"], "Rules"), S3BucketProvider.emptyListConfigToUndefined(properties["OwnershipControls"], "Rules"), async (cfg) => this.applyOwnershipControls(bucketName, cfg), async () => {
5903
5929
  await this.s3Client.send(new DeleteBucketOwnershipControlsCommand({ Bucket: bucketName }));
5904
5930
  this.logger.debug(`Deleted ownership controls on bucket ${bucketName}`);
@@ -5925,7 +5951,7 @@ var S3BucketProvider = class S3BucketProvider {
5925
5951
  await this.s3Client.send(new DeleteBucketWebsiteCommand({ Bucket: bucketName }));
5926
5952
  this.logger.debug(`Deleted website configuration on bucket ${bucketName}`);
5927
5953
  });
5928
- await this.diffSubConfig(bucketName, previousProperties["LoggingConfiguration"], properties["LoggingConfiguration"], async (cfg) => this.applyLoggingConfiguration(bucketName, cfg), async () => this.applyLoggingConfiguration(bucketName, void 0));
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));
5929
5955
  await this.diffSubConfig(bucketName, previousProperties["NotificationConfiguration"], properties["NotificationConfiguration"], async (cfg) => this.applyNotificationConfiguration(bucketName, cfg), async () => this.applyNotificationConfiguration(bucketName, void 0));
5930
5956
  await this.diffSubConfig(bucketName, previousProperties["ReplicationConfiguration"], properties["ReplicationConfiguration"], async (cfg) => this.applyReplicationConfiguration(bucketName, cfg, (m) => this.logger.warn(m)), async () => {
5931
5957
  await this.s3Client.send(new DeleteBucketReplicationCommand({ Bucket: bucketName }));
@@ -5989,7 +6015,9 @@ var S3BucketProvider = class S3BucketProvider {
5989
6015
  const lifecycleConfig = properties["LifecycleConfiguration"];
5990
6016
  if (lifecycleConfig?.Rules && Array.isArray(lifecycleConfig.Rules) && lifecycleConfig.Rules.length > 0) await this.applyLifecycleConfiguration(bucketName, lifecycleConfig, replayOnUnusable);
5991
6017
  const loggingConfig = properties["LoggingConfiguration"];
5992
- if (readConfigString(loggingConfig, "DestinationBucketName", "", "AWS::S3::Bucket LoggingConfiguration") !== "") await this.applyLoggingConfiguration(bucketName, loggingConfig);
6018
+ 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);
5993
6021
  const websiteConfig = properties["WebsiteConfiguration"];
5994
6022
  if (websiteConfig) await this.applyWebsiteConfiguration(bucketName, websiteConfig);
5995
6023
  const accelerateConfig = properties["AccelerateConfiguration"];
@@ -6032,7 +6060,7 @@ var S3BucketProvider = class S3BucketProvider {
6032
6060
  else throw createError;
6033
6061
  }
6034
6062
  try {
6035
- await this.applyConfiguration(bucketName, properties);
6063
+ await this.applyConfiguration(bucketName, properties, { ...context ? { context } : {} });
6036
6064
  await this.applyAllSubConfigsForCreate(bucketName, properties, context);
6037
6065
  } catch (innerError) {
6038
6066
  if (createdNewBucket) try {
@@ -17921,6 +17949,47 @@ var EventBridgeBusProvider = class {
17921
17949
  //#endregion
17922
17950
  //#region src/provisioning/providers/ec2-provider.ts
17923
17951
  /**
17952
+ * The `AWS::EC2::Route` destination keys, in CloudFormation's own precedence
17953
+ * order — which is also the order `createRoute`'s
17954
+ * `DestinationCidrBlock || DestinationIpv6CidrBlock || DestinationPrefixListId`
17955
+ * chain resolves them.
17956
+ */
17957
+ const ROUTE_DESTINATION_KEYS = [
17958
+ "DestinationCidrBlock",
17959
+ "DestinationIpv6CidrBlock",
17960
+ "DestinationPrefixListId"
17961
+ ];
17962
+ /**
17963
+ * Split a Route property bag into the destination keys it DECLARES and the bag
17964
+ * carrying only the one that would actually be sent (issue #1591).
17965
+ *
17966
+ * ONE source for a decision two very different call sites have to agree on: the
17967
+ * provisioning path (`createRoute`, which narrows and warns) and the DIFF path
17968
+ * (the engine's canonicalizer, which must not report the losers as a change).
17969
+ * If those two disagreed the fix would be worse than the bug — state and
17970
+ * template would each be narrowed to a different key.
17971
+ *
17972
+ * The winner is simply `declared[0]`. That is not a shortcut: the list is built
17973
+ * in the same order and with the same truthiness predicate as the `||` chain,
17974
+ * so the first declared key IS what the chain resolves to. The predicate is
17975
+ * `Boolean`, not a hand-listed `undefined | null | ''` set, because the bag is
17976
+ * `unknown`-valued at runtime — an unquoted YAML `0` must be skipped by the
17977
+ * narrowing exactly as the chain skips it, or the two disagree on which key
17978
+ * was sent.
17979
+ *
17980
+ * `narrowed` is always a fresh object: the caller's bag is the engine's
17981
+ * `resolvedProps`, which it records for every resource that does NOT narrow.
17982
+ */
17983
+ function narrowRouteDestinations(properties) {
17984
+ const declared = ROUTE_DESTINATION_KEYS.filter((key) => Boolean(properties[key]));
17985
+ const narrowed = { ...properties };
17986
+ for (const losingKey of declared.slice(1)) delete narrowed[losingKey];
17987
+ return {
17988
+ declared,
17989
+ narrowed
17990
+ };
17991
+ }
17992
+ /**
17924
17993
  * AWS EC2 Networking Provider
17925
17994
  *
17926
17995
  * Implements resource provisioning for EC2 networking resources:
@@ -18768,15 +18837,15 @@ var EC2Provider = class {
18768
18837
  const destinationPrefixListId = properties["DestinationPrefixListId"];
18769
18838
  const destination = destinationCidrBlock || destinationIpv6CidrBlock || destinationPrefixListId;
18770
18839
  if (!routeTableId || !destination) throw new ProvisioningError(`RouteTableId and one of DestinationCidrBlock/DestinationIpv6CidrBlock/DestinationPrefixListId are required for Route ${logicalId}`, resourceType, logicalId);
18771
- const declaredDestinations = [
18772
- ["DestinationCidrBlock", destinationCidrBlock],
18773
- ["DestinationIpv6CidrBlock", destinationIpv6CidrBlock],
18774
- ["DestinationPrefixListId", destinationPrefixListId]
18775
- ].filter(([, value]) => Boolean(value)).map(([key]) => key);
18840
+ const { declared: declaredDestinations, narrowed: narrowedProperties } = narrowRouteDestinations(properties);
18841
+ let effectiveProperties;
18776
18842
  if (declaredDestinations.length > 1) {
18777
18843
  const message = `Route ${logicalId} declares more than one destination (${declaredDestinations.join(", ")}). CloudFormation and EC2 accept exactly one of DestinationCidrBlock/DestinationIpv6CidrBlock/DestinationPrefixListId; remove the extra keys from the template.`;
18778
- if (onMultipleDestinations) onMultipleDestinations(`${message} Continuing with ${destination === destinationCidrBlock ? "DestinationCidrBlock" : destination === destinationIpv6CidrBlock ? "DestinationIpv6CidrBlock" : "DestinationPrefixListId"} and ignoring the rest.`);
18779
- else throw new ProvisioningError(message, resourceType, logicalId);
18844
+ if (onMultipleDestinations) {
18845
+ const usedKey = declaredDestinations[0];
18846
+ effectiveProperties = narrowedProperties;
18847
+ onMultipleDestinations(`${message} Continuing with ${usedKey} and ignoring the rest.`);
18848
+ } else throw new ProvisioningError(message, resourceType, logicalId);
18780
18849
  }
18781
18850
  try {
18782
18851
  await this.ec2Client.send(new CreateRouteCommand({
@@ -18798,7 +18867,8 @@ var EC2Provider = class {
18798
18867
  this.logger.debug(`Successfully created Route ${logicalId}: ${physicalId}`);
18799
18868
  return {
18800
18869
  physicalId,
18801
- attributes: {}
18870
+ attributes: {},
18871
+ ...effectiveProperties && { effectiveProperties }
18802
18872
  };
18803
18873
  } catch (error) {
18804
18874
  const cause = error instanceof Error ? error : void 0;
@@ -18817,7 +18887,8 @@ var EC2Provider = class {
18817
18887
  return {
18818
18888
  physicalId: createResult.physicalId,
18819
18889
  wasReplaced: true,
18820
- ...createResult.attributes && { attributes: createResult.attributes }
18890
+ ...createResult.attributes && { attributes: createResult.attributes },
18891
+ ...createResult.effectiveProperties && { effectiveProperties: createResult.effectiveProperties }
18821
18892
  };
18822
18893
  } catch (error) {
18823
18894
  if (error instanceof CdkdError) throw error;
@@ -20221,6 +20292,26 @@ var EC2Provider = class {
20221
20292
  default: return [];
20222
20293
  }
20223
20294
  }
20295
+ /**
20296
+ * The diff-side half of the Route multi-destination narrowing (issue #1591).
20297
+ *
20298
+ * `createRoute` sends exactly one destination key and reports the narrowed
20299
+ * bag via `effectiveProperties`, so state carries one key. Without the same
20300
+ * narrowing here the template's extra keys read as an ADDED property on the
20301
+ * next deploy — and every destination key is create-only in the registry
20302
+ * schema, so the diff would classify a REPLACEMENT and the engine's
20303
+ * replacement create (which passes no context, and so gets no
20304
+ * `onMultipleDestinations` downgrade) would hit the #1566 refusal. A
20305
+ * previously-green no-op deploy would start failing.
20306
+ *
20307
+ * Shares `narrowRouteDestinations` with the provisioning path so the two
20308
+ * cannot disagree about which key survives.
20309
+ */
20310
+ canonicalizeDesiredProperties(resourceType, properties) {
20311
+ if (resourceType !== "AWS::EC2::Route") return properties;
20312
+ const { declared, narrowed } = narrowRouteDestinations(properties);
20313
+ return declared.length > 1 ? narrowed : properties;
20314
+ }
20224
20315
  async readVpcCurrentState(physicalId) {
20225
20316
  const vpc = (await this.ec2Client.send(new DescribeVpcsCommand({ VpcIds: [physicalId] }))).Vpcs?.[0];
20226
20317
  if (!vpc) return void 0;
@@ -51246,7 +51337,7 @@ async function loadStateOrEmpty(stackName, region, stateBackend) {
51246
51337
  * Pure with respect to AWS state mutation — only reads state (the resolver
51247
51338
  * may read producer state for `Fn::ImportValue` / `Fn::GetStackOutput`).
51248
51339
  */
51249
- async function computeStackDiff(currentState, template, region, stackName, stateBackend, diffCalculator, parameters) {
51340
+ async function computeStackDiff(currentState, template, region, stackName, stateBackend, diffCalculator, parameters, canonicalizeProperties) {
51250
51341
  const intrinsicResolver = new IntrinsicFunctionResolver(region);
51251
51342
  let mergedParameters = parameters;
51252
51343
  let parametersBound = false;
@@ -51286,7 +51377,7 @@ async function computeStackDiff(currentState, template, region, stackName, state
51286
51377
  ...mergedParameters && { parameters: mergedParameters },
51287
51378
  ...conditions && { conditions }
51288
51379
  });
51289
- return diffCalculator.calculateDiff(currentState, effectiveTemplate, resolveFn);
51380
+ return diffCalculator.calculateDiff(currentState, effectiveTemplate, resolveFn, canonicalizeProperties);
51290
51381
  }
51291
51382
  /**
51292
51383
  * Resolve a nested-stack child's input `Parameters` (declared on the parent's
@@ -51347,13 +51438,13 @@ async function resolveChildStackParameters(parentStackRow, parentTemplate, paren
51347
51438
  * fail at deploy time.
51348
51439
  */
51349
51440
  async function buildDiffTree(args) {
51350
- const { stackName, displayName, region, template, nestedTemplates, recursive, stateBackend, diffCalculator, parameters, assetRedirect } = args;
51441
+ const { stackName, displayName, region, template, nestedTemplates, recursive, stateBackend, diffCalculator, parameters, canonicalizeProperties, assetRedirect } = args;
51351
51442
  const state = await loadStateOrEmpty(stackName, region, stateBackend);
51352
51443
  const node = {
51353
51444
  stackName,
51354
51445
  displayName,
51355
51446
  region,
51356
- changes: await computeStackDiff(state, template, region, stackName, stateBackend, diffCalculator, parameters),
51447
+ changes: await computeStackDiff(state, template, region, stackName, stateBackend, diffCalculator, parameters, canonicalizeProperties),
51357
51448
  ccApiRoutes: collectCcApiRoutes(template, state),
51358
51449
  children: []
51359
51450
  };
@@ -51379,6 +51470,7 @@ async function buildDiffTree(args) {
51379
51470
  stateBackend,
51380
51471
  diffCalculator,
51381
51472
  parameters: childParameters,
51473
+ ...canonicalizeProperties && { canonicalizeProperties },
51382
51474
  ...assetRedirect && { assetRedirect }
51383
51475
  }));
51384
51476
  }
@@ -51736,6 +51828,9 @@ async function diffCommand(stacks, options) {
51736
51828
  ...options.profile && { profile: options.profile }
51737
51829
  });
51738
51830
  const diffCalculator = new DiffCalculator();
51831
+ const diffProviderRegistry = new ProviderRegistry();
51832
+ registerAllProviders(diffProviderRegistry);
51833
+ const canonicalizeProperties = makeCanonicalizePropertiesFn(diffProviderRegistry);
51739
51834
  const recursive = options.recursive ?? false;
51740
51835
  const resolveAssetRedirect = createAssetRedirectResolver({
51741
51836
  stateBackend,
@@ -51762,6 +51857,7 @@ async function diffCommand(stacks, options) {
51762
51857
  recursive,
51763
51858
  stateBackend,
51764
51859
  diffCalculator,
51860
+ canonicalizeProperties,
51765
51861
  ...assetRedirect && { assetRedirect }
51766
51862
  }));
51767
51863
  }
@@ -70245,7 +70341,7 @@ function createMigrateCommand() {
70245
70341
  */
70246
70342
  function buildProgram() {
70247
70343
  const program = new Command();
70248
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.22");
70344
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.24");
70249
70345
  program.hook("preAction", (_thisCommand, actionCommand) => {
70250
70346
  const { profile } = actionCommand.optsWithGlobals();
70251
70347
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;